[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/gited 5138dee 01/73: Add new library gited.el

From: Stefan Monnier
Subject: [elpa] externals/gited 5138dee 01/73: Add new library gited.el
Date: Sun, 29 Nov 2020 00:00:27 -0500 (EST)

branch: externals/gited
commit 5138deece91b461b50864b2696bc07a68b0d6c38
Author: Tino Calancha <tino.calancha@gmail.com>
Commit: Tino Calancha <tino.calancha@gmail.com>

    Add new library gited.el
 .travis.yml    |   22 +
 LICENSE        |  674 ++++++++++++
 Makefile       |   16 +
 README.md      |   24 +
 gited-tests.el |   88 ++
 gited.el       | 3240 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 4064 insertions(+)

diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..098eca7
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,22 @@
+language: emacs-lisp
+  email:
+    on_success: never
+    on_failure: always
+  - EVM_EMACS=emacs-24.3-travis
+  - EVM_EMACS=emacs-24.4-travis
+  - EVM_EMACS=emacs-24.5-travis
+  - EVM_EMACS=emacs-git-snapshot-travis
+  - git clone https://github.com/rejeep/evm.git $HOME/.evm
+  - export PATH=$HOME/.evm/bin:$PATH
+  - evm config path /tmp
+  - evm install $EVM_EMACS --use --skip
+  - make test
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9cecc1d
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+                            Preamble
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+  The precise terms and conditions for copying, distribution and
+modification follow.
+                       TERMS AND CONDITIONS
+  0. Definitions.
+  "This License" refers to version 3 of the GNU General Public License.
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+  1. Source Code.
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+  The Corresponding Source for a work in source code form is that
+same work.
+  2. Basic Permissions.
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+  4. Conveying Verbatim Copies.
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+  5. Conveying Modified Source Versions.
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+  6. Conveying Non-Source Forms.
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+  7. Additional Terms.
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+  8. Termination.
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+  9. Acceptance Not Required for Having Copies.
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+  10. Automatic Licensing of Downstream Recipients.
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+  11. Patents.
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+  12. No Surrender of Others' Freedom.
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+  13. Use with the GNU Affero General Public License.
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+  14. Revised Versions of this License.
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+  15. Disclaimer of Warranty.
+  16. Limitation of Liability.
+  17. Interpretation of Sections 15 and 16.
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+                     END OF TERMS AND CONDITIONS
+            How to Apply These Terms to Your New Programs
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+    {one line to give the program's name and a brief idea of what it does.}
+    Copyright (C) {year}  {name of author}
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    GNU General Public License for more details.
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+Also add information on how to contact you by electronic and paper mail.
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+    {project}  Copyright (C) {year}  {fullname}
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..68f1376
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+emacs ?= emacs
+LOAD = -l gited.el
+all: test
+       $(emacs) -batch $(LOAD) -l gited-tests.el -f 
+       $(emacs) -batch --eval "(progn (add-to-list 'load-path 
default-directory) (byte-compile-file \"gited.el\"))"
+       rm -f *.elc
+.PHONY: all compile clean test
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..5a72246
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
+# Gited: Operate on Git branches like dired
+This library lists the branches in a Git repository.  Then you can
+operate on them with a dired-like interface.
+The command **gited-list-branches** prompts for the kind of branch
+(local branches, remote branches or tags) and lists them.
+This command is used quite often, thus it might be convenient
+to give it a key binding.  For instance, if *gited.el* is in
+your *load-path*, then you can bind it to **C-x C-g** in Dired buffers
+by adding the following lines into your .emacs file:
+(require 'gited)
+(define-key dired-mode-map "\C-x\C-g" 'gited-list-branches)
+If you are familiar with Dired, then you already know how to use
+Gited; that's because most of the Gited commands with a Dired equivalent
+share same keybindings.
+For instance *gited-rename-branch* is bound to 'R' as *dired-do-rename*.
+Similarly, *gited-mark* is bound to 'm' as *dired-mark*.
diff --git a/gited-tests.el b/gited-tests.el
new file mode 100644
index 0000000..bf07e6b
--- /dev/null
+++ b/gited-tests.el
@@ -0,0 +1,88 @@
+;;; gited-tests.el --- Tests for gited.el  -*- lexical-binding: t; -*-
+;; Copyright (C) 2017 Free Software Foundation, Inc.
+;; Author: Tino Calancha <tino.calancha@gmail.com>,
+;; Keywords:
+;; This file is part of GNU Emacs.
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; GNU General Public License for more details.
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+;;; Commentary:
+;;; code:
+(require 'ert)
+(require 'gited)
+(ert-deftest gited-test1 ()
+  (when (executable-find vc-git-program)
+    (let* ((dir (make-temp-file "gited" 'dir))
+           (file (expand-file-name "foo" dir))
+           (gited-expert t))
+      (unwind-protect
+          (let ((str "Initialize repository."))
+            (write-region "Test file" nil file)
+               (dired dir)
+            (gited-git-command '("init"))
+               (gited-git-command '("config" "user.email" 
+               (gited-git-command '("config" "user.name" "John Doe"))
+            (gited-git-command '("add" "foo"))
+            (gited-git-command `("commit" "-m" ,str))
+            (gited-list-branches "local")
+            (should (gited-dir-under-Git-control-p))
+            (should (gited-buffer-p))
+            (should (equal str (gited-last-commit-title)))
+            (should (equal "master" (gited-current-branch)))
+            (should-not (gited-branch-exists-p "foo"))
+            (gited-copy-branch "master" "foo")
+            (should (gited-branch-exists-p "foo"))
+            (gited-toggle-marks)
+            (should (= 2 (gited-number-marked)))
+            (gited-unmark-all-marks)
+            (should (= 0 (gited-number-marked)))
+            (gited-with-current-branch "foo"
+              ;; (gited-checkout-branch "foo")
+              (write-region "Changed this file" nil file)
+              (gited-git-command '("add" "foo"))
+              (gited-git-command '("commit" "-m" "Update file"))
+              (let ((hash
+                     (with-temp-buffer
+                       (gited-git-command
+                        '("rev-parse" "HEAD") (current-buffer))
+                       (buffer-substring 1 (1- (point-max))))))
+                (gited-mark-branches-containing-commit hash)
+                (should (= 1 (gited-number-marked))))
+              (gited-unmark-all-marks)
+              (gited-mark-branches-regexp "foo")
+              (should (= 1 (gited-number-marked)))
+              (gited-unmark-all-marks)
+              (gited-mark-branches-containing-regexp "Update")
+              (should (= 1 (gited-number-marked)))
+              (gited-unmark-all-marks))
+            (gited-copy-branch "foo" "bar")
+            (gited-delete-branch "foo" 'force)
+            (gited-update)
+            (should-not (gited-branch-exists-p "foo"))
+            (gited-rename-branch "bar" "foo") ; Asynchronous.
+            (while gited-branch-after-op
+              (sit-for 0.05))
+            (should (gited-branch-exists-p "foo")))
+        (delete-directory dir 'recursive)))))
+(provide 'gited-tests)
+;;; gited-tests.el ends here
diff --git a/gited.el b/gited.el
new file mode 100644
index 0000000..4927571
--- /dev/null
+++ b/gited.el
@@ -0,0 +1,3240 @@
+;;; gited.el --- Operate on Git branches like dired  -*- lexical-binding:t -*-
+;; Copyright (C) 2016-2017 Free Software Foundation, Inc.
+;; Author: Tino Calancha <tino.calancha@gmail.com>
+;; Maintainer: Tino Calancha <tino.calancha@gmail.com>
+;; Keywords: git, vc, convenience
+;; Created: Wed Oct 26 01:28:54 JST 2016
+;; Compatibility: GNU Emacs: 24.4
+;; Version: 0.2.0
+;; Package-Requires: ((emacs "24.4") (cl-lib "0.5"))
+;; Last-Updated: Fri Jun 02 09:30:00 JST 2017
+;;           By: calancha
+;;     Update #: 634
+;; This file is part of GNU Emacs.
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; GNU General Public License for more details.
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+;;; Commentary:
+;; This library lists the branches in a Git repository.  Then you can
+;; operate on them with a dired-like interface.
+;; The command `gited-list-branches' prompts for the kind of branch
+;; (local branches, remote branches or tags) and lists them.
+;; This command is used quite often, thus it might be convenient
+;; to give it a key binding.  For instance, if `gited.el' is in
+;; your `load-path', then you can bind it to `C-x C-g' in Dired buffers
+;; by adding the following lines into your .emacs file:
+;; (require 'gited)
+;; (define-key dired-mode-map "\C-x\C-g" 'gited-list-branches)
+;; If you are familiar with Dired, then you already know how to use
+;; Gited; that's because most of the Gited commands with a Dired equivalent
+;; share same keybindings.
+;; For instance `gited-rename-branch' is bound to `R' as `dired-do-rename'.
+;; Similarly, `gited-mark' is bound to `m' as `dired-mark'.
+;;  Internal variables defined here:
+;;   `gited--hide-details-set', `gited--op',
+;;   `gited--revert-commit', `gited--running-async-op',
+;;   `gited-actual-switches', `gited-after-change-hook',
+;;   `gited-author-face', `gited-author-idx',
+;;   `gited-bisect-buf-name', `gited-bisect-buffer',
+;;   `gited-bisect-buffer', `gited-bisect-output-name',
+;;   `gited-branch-after-op', `gited-branch-alist',
+;;   `gited-branch-idx', `gited-branch-name-face',
+;;   `gited-buffer', `gited-buffer-name',
+;;   `gited-commit-idx', `gited-commit-msg-face',
+;;   `gited-current-branch', `gited-date-idx',
+;;   `gited-date-regexp', `gited-date-time-face',
+;;   `gited-del-char', `gited-deletion-branch-face',
+;;   `gited-deletion-face', `gited-edit-commit-mode-map',
+;;   `gited-flag-mark-face', `gited-flag-mark-line-face',
+;;   `gited-header', `gited-list-format',
+;;   `gited-list-refs-format-command', `gited-log-buffer',
+;;   `gited-mark-col-size', `gited-mark-face',
+;;   `gited-mark-idx', `gited-marker-char',
+;;   `gited-mode', `gited-mode-map',
+;;   `gited-modified-branch', `gited-new-or-deleted-files-re',
+;;   `gited-op-string', `gited-original-buffer',
+;;   `gited-output-buffer', `gited-output-buffer-name',
+;;   `gited-re-mark', `gited-ref-kind',
+;;   `gited-section-highlight-face', `gited-toplevel-dir'.
+;;  Coustom variables defined here:
+;;   `gited-add-untracked-files', `gited-author-col-size',
+;;   `gited-branch-col-size', `gited-commit-col-size',
+;;   `gited-current-branch-face', `gited-date-col-size',
+;;   `gited-date-format', `gited-delete-unmerged-branches',
+;;   `gited-expert', `gited-patch-options',
+;;   `gited-patch-program', `gited-protected-branches',
+;;   `gited-reset-mode', `gited-short-log-cmd',
+;;   `gited-show-commit-hash', `gited-switches',
+;;   `gited-use-header-line', `gited-verbose'.
+;;  Macros defined here:
+;;   `gited-map-over-marks', `gited-mark-if',
+;;   `gited-with-current-branch'.
+;;  Commands defined here:
+;;   `gited--mark-merged-branches-spec', `gited--mark-unmerged-branches-spec',
+;;   `gited-add-patched-files', `gited-apply-add-and-commit-patch',
+;;   `gited-apply-patch', `gited-async-operation',
+;;   `gited-bisect', `gited-branch-clear',
+;;   `gited-checkout-branch', `gited-commit',
+;;   `gited-copy-branch', `gited-copy-branchname-as-kill',
+;;   `gited-delete-branch', `gited-diff',
+;;   `gited-do-delete', `gited-do-flagged-delete',
+;;   `gited-do-kill-lines', `gited-edit-commit-mode',
+;;   `gited-extract-patches', `gited-finish-commit-edit',
+;;   `gited-flag-branch-deletion', `gited-goto-branch',
+;;   `gited-goto-first-branch', `gited-goto-last-branch',
+;;   `gited-kill-line', `gited-list-branches',
+;;   `gited-log', `gited-log-last-n-commits',
+;;   `gited-mark', `gited-mark-branches-containing-commit',
+;;   `gited-mark-branches-containing-regexp', `gited-mark-branches-regexp',
+;;   `gited-mark-merged-branches', `gited-mark-unmerged-branches',
+;;   `gited-merge-branch', `gited-move-to-author',
+;;   `gited-move-to-branchname', `gited-move-to-date',
+;;   `gited-move-to-end-of-author', `gited-move-to-end-of-branchname',
+;;   `gited-move-to-end-of-date', `gited-next-line',
+;;   `gited-next-marked-branch', `gited-number-marked',
+;;   `gited-origin', `gited-prev-line',
+;;   `gited-prev-marked-branch', `gited-pull',
+;;   `gited-push', `gited-rename-branch',
+;;   `gited-reset-branch', `gited-revert-commit',
+;;   `gited-set-branch-upstream', `gited-show-commit',
+;;   `gited-stash', `gited-stash-apply',
+;;   `gited-stash-branch', `gited-stash-drop',
+;;   `gited-stash-pop', `gited-status',
+;;   `gited-summary', `gited-sync-with-trunk',
+;;   `gited-toggle-marks', `gited-unmark',
+;;   `gited-unmark-all-branches', `gited-unmark-all-marks',
+;;   `gited-unmark-backward', `gited-update',
+;;   `gited-visit-branch-sources', `gited-why'.
+;;  Non-interactive functions defined here:
+;;   `gited--bisect-after-run', `gited--bisect-executable-p',
+;;   `gited--case-ref-kind', `gited--check-unmerged-marked-branches',
+;;   `gited--clean-previous-patches', `gited--fill-branch-alist',
+;;   `gited--fontify-current-row', `gited--get-branches-from-command',
+;;   `gited--get-column', `gited--get-merged-branches',
+;;   `gited--get-patch-or-commit-buffers', `gited--get-unmerged-branches',
+;;   `gited--goto-column', `gited--goto-first-branch',
+;;   `gited--handle-new-or-delete-files', `gited--list-format-init',
+;;   `gited--mark-branches-in-region',
+;;   `gited--mark-merged-or-unmerged-branches',
+;;   `gited--mark-merged-or-unmerged-branches-spec', `gited--merged-branch-p',
+;;   `gited--move-to-end-of-column', `gited--output-buffer',
+;;   `gited--patch-or-commit-buffer', `gited--set-output-buffer-mode',
+;;   `gited--stash-branch', `gited--update-padding',
+;;   `gited--valid-ref-p', `gited-all-branches',
+;;   `gited-async-operation-sentinel', `gited-at-header-line-p',
+;;   `gited-bisecting-p', `gited-branch-exists-p',
+;;   `gited-buffer-p', `gited-commit-title',
+;;   `gited-current-branch', `gited-current-branches-with-marks',
+;;   `gited-current-state-list', `gited-dir-under-Git-control-p',
+;;   `gited-edit-commit', `gited-fontify-current-branch',
+;;   `gited-fontify-marked-branch-name', `gited-format-columns-of-files',
+;;   `gited-get-branchname', `gited-get-commit',
+;;   `gited-get-date', `gited-get-element-in-row',
+;;   `gited-get-last-commit-time', `gited-get-mark',
+;;   `gited-get-marked-branches', `gited-git-command',
+;;   `gited-git-command-on-region',
+;;   `gited-hide-details-update-invisibility-spec',
+;;   `gited-insert-marker-char', `gited-internal-do-deletions',
+;;   `gited-last-commit-title', `gited-listed-branches',
+;;   `gited-log-msg', `gited-log-summary',
+;;   `gited-map-lines', `gited-mark-pop-up',
+;;   `gited-mark-remembered', `gited-modified-files',
+;;   `gited-modified-files-p', `gited-next-branch',
+;;   `gited-number-of-commits', `gited-prev-branch',
+;;   `gited-print-entry', `gited-remember-marks',
+;;   `gited-remote-repository-p', `gited-repeat-over-lines',
+;;   `gited-stashes', `gited-tabulated-list-entries',
+;;   `gited-trunk-branches', `gited-untracked-files'.
+;;  Faces defined here:
+;;   `gited-author', `gited-branch-name',
+;;   `gited-commit-msg', `gited-date-time',
+;;   `gited-deletion', `gited-deletion-branch',
+;;   `gited-flag-mark', `gited-flag-mark-line',
+;;   `gited-header', `gited-mark',
+;;   `gited-modified-branch', `gited-section-highlight',
+;;   `gited-status-branch-local', `gited-status-tag'.
+;;; Code:
+(require 'cl-lib)
+(require 'tabulated-list)
+(require 'dired)
+(require 'vc-git)
+(require 'find-func) ; `find-library-name'
+(defgroup gited nil
+  "Git branch editing."
+  :version "26.1"
+  :group 'vc)
+(defvar-local gited-mode nil
+  "Variable saving the status of `gited-mode'.")
+(put 'gited-mode 'permanent-local t)
+(defvar-local gited-current-branch nil
+  "The branch currently checked out.")
+(put 'gited-current-branch 'permanent-local t)
+(defvar-local gited-toplevel-dir nil
+  "Absolute path of the top-level directory for the current repository.")
+(put 'gited-toplevel-dir 'permanent-local t)
+;; Stolen from ediff-ptch.el
+(defcustom gited-patch-program "patch"
+  "Name of the program that applies patches.
+It is recommended to use GNU-compatible versions."
+  :type 'string
+  :group 'gited)
+;; Stolen from ediff-ptch.el
+(defcustom gited-patch-options "-f"
+  "Options to pass to `gited-patch-program'.
+It is recommended to pass the `-f' option to the patch program, so it won't ask
+questions.  However, some implementations don't accept this option, in which
+case the default value for this variable should be changed."
+  :type 'string
+  :group 'gited)
+(defcustom gited-use-header-line t
+  "If non-nil, use the header line to display Gited column titles."
+  :type 'boolean
+  :group 'gited)
+(defcustom gited-verbose nil
+  "If non-nil, show the author name and commit date."
+  :type 'boolean
+  :group 'gited)
+(defvar-local gited--hide-details-set nil)
+(defvar-local gited-ref-kind nil
+  "The kind of Git reference displayed in `gited-buffer'.
+It must be `local', `remote' or `tags'.")
+(put 'gited-ref-kind 'permanent-local t)
+(defvar gited-mark-idx 0
+  "Position of marker char in array `tabulated-list-entries'.")
+(defvar gited-author-idx 1
+  "Position of author in array `tabulated-list-entries'.")
+(defvar gited-date-idx 2
+  "Position of date in array `tabulated-list-entries'.")
+(defvar gited-branch-idx 3
+  "Position of branch name in array `tabulated-list-entries'.")
+(defvar gited-commit-idx 4
+  "Position of newest commit tittle in array `tabulated-list-entries'.")
+(defvar gited-after-change-hook nil
+  "Hook run after make a change in the list of branches.")
+(defvar gited-branch-after-op nil
+  "Branch where to set point after an asynchronous operation.")
+(defvar gited-op-string nil
+  "String to describe the actual operation.")
+(defvar gited-marker-char ?*
+  "In gited, the current mark character.
+This is what the do-commands look for, and what the mark-commands store.")
+(defvar gited-del-char ?D
+  "Character used to flag branches for deletion.")
+;; "Regexp matching a marked line.
+(defvar gited-re-mark "^[^ \n]")
+(defvar-local gited-branch-alist nil
+INDEX identify each row in the table.
+MARK is the mark character shown in the table for that row.
+TIME is the time of the last commit in that branch.
+BRANCH-NAME is the name of the branch.
+AUTHOR-NAME is the author of the last commit in that branch.
+TITLE is the title of the last commit.")
+(defvar gited-buffer-name "*gited*"
+  "Name of the buffer where to list the repository branches.")
+(defvar-local gited-buffer nil
+  "Buffer where to list the repository branches.")
+(put 'gited-buffer 'permanent-local t)
+(defvar gited-output-buffer-name "*gited-output*"
+  "Name of the buffer where to show output from Git commands.")
+(defvar-local gited-output-buffer nil
+  "Output buffer for Git commands.")
+(put 'gited-output-buffer 'permanent-local t)
+(defvar gited-bisect-buf-name "*gited-bisect*"
+  "Name of the buffer where to show bisect output.")
+(defvar-local gited-bisect-buffer nil
+  "Output buffer for Git bisects.")
+(put 'gited-bisect-buffer 'permanent-local t)
+(defvar gited-bisect-output-name "*gited-bisect*"
+  "Name of the output buffer for Git bisects.")
+(defvar-local gited-bisect-buffer nil
+  "Output buffer for Git bisects.")
+(put 'gited-bisect-buffer 'permanent-local t)
+(defvar gited-list-refs-format-command
+  '("for-each-ref" "--format='(%(authordate:raw) \
+\"%(refname:short)\" \"%(authorname)\")'" "refs/%s")
+  "Format strings to build a Git command to list references.")
+(defvar gited-date-regexp (concat "\\("
+                                  (substring
+                                   directory-listing-before-filename-regexp
+                                   (length "\\([0-9][BkKMGTPEZY]? ")))
+  "Regular expression to match a date in `gited-buffer' buffer.")
+(defcustom gited-switches "-g"
+  "Control how to sort `gited-branch-alist'.
+Option -r reverse order while sorting.
+Option -g do not show the author name."
+  :type '(choice
+          (const :tag "Unset" nil) string)
+  :group 'gited)
+(defvar-local gited-actual-switches gited-switches
+  "Switches used on this buffer.")
+(put 'gited-actual-switches 'permanent-local t)
+(defvar-local gited-list-format nil
+  "Format of the columns in the branch list.")
+(defcustom gited-reset-mode "mixed"
+  "Default mode of a Git reset."
+  :type '(choice
+          (const :tag "soft" "soft")
+          (const :tag "mixed" "mixed")
+          (const :tag "hard" "hard")
+          (const :tag "merge" "merge")
+          (const :tag "keep" "keep"))
+  :group 'gited)
+(defvar gited-mark-col-size 2
+  "Size of mark column.")
+(defcustom gited-author-col-size 16
+  "Size of author column."
+  :type 'integer
+  :group 'gited)
+(defcustom gited-date-col-size 17
+  "Size of date column."
+  :type '(choice
+          (const :tag "Short" 17)
+          (const :tag "Full" 24))
+  :group 'gited)
+(defcustom gited-branch-col-size 50
+  "Size of branch column."
+  :type 'integer
+  :group 'gited)
+(defcustom gited-commit-col-size 65
+  "Size of commit column."
+  :type 'integer
+  :group 'gited)
+;; Must be parseable by `date-to-time'.
+(defcustom gited-date-format "%F %R"
+  "Format to display the date in `gited-buffer'.
+If you change this option, then you might want to change
+`gited-date-col-size' as well."
+  :type '(choice
+          (const :tag "Short" "%F %R")
+          (const :tag "Full" "%FT%T%z"))
+  :group 'gited)
+(defun gited--list-format-init (&optional col-names col-sizes)
+  "Initialize `gited-list-format'.
+Optional arguments COL-NAMES and COL-SIZES are the column names
+and sizes."
+  (setq gited-actual-switches gited-switches)
+  (setq gited-list-format
+        (vector `(,(if col-names (nth 0 col-names) "M")
+                  ,(if col-sizes (nth 0 col-sizes) gited-mark-col-size) t)
+                `(,(if col-names (nth 1 col-names) "Authors")
+                  ,(if col-sizes (nth 1 col-sizes) gited-author-col-size) t)
+                `(,(if col-names (nth 2 col-names) "Date")
+                  ,(if col-sizes (nth 2 col-sizes) gited-date-col-size)
+                  (lambda (row1 row2)
+                    (let* ((reverse-order
+                            (member "-r" (split-string gited-actual-switches)))
+                           (t1 (aref (cadr row1) gited-date-idx))
+                           (t2 (aref (cadr row2) gited-date-idx))
+                           (earlierp
+                            (time-less-p (date-to-time t1) (date-to-time t2))))
+                      (if reverse-order
+                          earlierp
+                        (not earlierp)))))
+                `(,(if col-names (nth 3 col-names) "Branches")
+                  ,(if col-sizes (nth 3 col-sizes) gited-branch-col-size) t)
+                `(,(if col-names (nth 4 col-names) "Last Commit")
+                  ,(if col-sizes (nth 4 col-sizes) gited-commit-col-size) t))))
+(setq gited-list-format (gited--list-format-init))
+(defcustom gited-expert nil
+  "If non-nil, don't ask for confirmation for some operations on branches."
+  :type 'boolean
+  :group 'gited)
+(defcustom gited-add-untracked-files nil
+  "If non-nil, `gited-add-patched-files' adds untracked files as well."
+  :type 'boolean
+  :group 'gited)
+(defcustom gited-show-commit-hash t
+  "If non-nil, show the SHA1 in the 'Last Commit' column'."
+  :type 'boolean
+  :group 'gited)
+(defcustom gited-delete-unmerged-branches nil
+  "If non-nil, `gited-do-delete' delete non-fully merged branches.
+Otherwise, deletion of unmerged branches require call `gited-do-delete'
+with a prefix."
+  :type 'boolean
+  :group 'gited)
+(defcustom gited-current-branch-face 'font-lock-keyword-face
+  "Face used for displaying current checkout branch."
+  :type 'face
+  :group 'gited)
+(defface gited-section-highlight
+  '((((class color) (background light)) :background "grey95")
+    (((class color) (background  dark)) :background "grey20"))
+  "Face for highlighting the current branch."
+  :group 'gited)
+(defvar gited-section-highlight-face 'gited-section-highlight)
+(defface gited-flag-mark-line
+  '((((background dark)) (:background "#787831311414")) ; ~ dark red brown
+    (t                   (:background "Skyblue")))
+  "Face used for flagged and marked lines in Gited buffers."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-flag-mark-line-face 'gited-flag-mark-line)
+(defface gited-flag-mark
+  '((((background dark))
+     (:foreground "Blue" :background "#7575D4D41D1D")) ; ~ olive green
+    (t                   (:foreground "Yellow" :background "Blueviolet")))
+  "Face used for flags and marks (except D) in Gited buffers."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-flag-mark-face 'gited-flag-mark)
+(defface gited-deletion-branch
+  '((t (:foreground "Red")))
+  "Face used for branches flagged for deletion in Gited buffers."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-deletion-branch-face 'gited-deletion-branch)
+(defface gited-deletion
+  '((t (:foreground "Yellow" :background "Red")))
+  "Face used for deletion flags (D) in Gited buffers."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-deletion-face 'gited-deletion)
+(defface gited-header
+  '((t (:inherit font-lock-type-face)))
+  "Face used used for header when listing Git branches."
+  :group 'gited)
+(defface gited-status-branch-local ; Same as magit-branch-local.
+  '((((class color) (background light)) :foreground "SkyBlue4")
+    (((class color) (background  dark)) :foreground "LightSkyBlue1"))
+  "Face for local branches in status."
+  :group 'gited)
+(defface gited-status-tag ; Same as gited-tag.
+  '((((class color) (background light)) :foreground "Goldenrod4")
+    (((class color) (background  dark)) :foreground "LightGoldenrod2"))
+  "Face for tag labels shown in log buffer."
+  :group 'gited)
+(defcustom gited-protected-branches nil
+  "Name of protected branches.
+These branches cannot be deleted or renamed."
+  :type '(repeat (string :tag "Branch name"))
+  :group 'gited)
+(defcustom gited-short-log-cmd
+  '("log" "--pretty=format:'%h %an %ad | %s%d'" "--graph" "--date=short")
+  "Default short format for Git log."
+  :type 'string
+  :group 'gited)
+(defvar gited-header 'gited-header
+  "Face name used for header when listing Git branches.")
+(defface gited-modified-branch
+  '((((background dark)) (:foreground "green1"))
+    (t                   (:foreground "red")))
+  "*Face used for branches with unstaged/uncommitted changes."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-modified-branch 'gited-modified-branch)
+(defface gited-mark
+  '((t (:inherit font-lock-constant-face)))
+  "Face used for Gited marks."
+  :group 'gited-faces)
+(defvar gited-mark-face 'gited-mark
+  "Face name used for Gited marks.")
+(defface gited-author
+  '((((background dark)) (:foreground "orange"))
+    (t                   (:foreground "black")))
+  "*Face used for AUTHOR in Gited buffers."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-author-face 'gited-author)
+(defface gited-date-time
+  '((((background dark)) (:foreground "#74749A9AF7F7")) ; ~ med blue
+    (t                   (:foreground "DarkGoldenrod4")))
+  "Face used for date and time in Gited buffers."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-date-time-face 'gited-date-time)
+(defface gited-branch-name
+  '((((background dark)) (:foreground "Yellow"))
+    (t                   (:foreground "Blue")))
+  "Face used for branch names  in Gited buffers."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-branch-name-face 'gited-branch-name)
+(defface gited-commit-msg ; Same as magit-branch-remote.
+  '((((class color) (background light)) :foreground "DarkOliveGreen4")
+    (((class color) (background  dark)) :foreground "DarkSeaGreen2"))
+  "Face used for commit-msg  in Gited buffers."
+  :group 'gited :group 'font-lock-highlighting-faces)
+(defvar gited-commit-msg-face 'gited-commit-msg)
+;;; Macros.
+(defmacro gited-with-current-branch (branch &rest body)
+  "Set BRANCH temporarily current and execute forms in BODY.
+BRANCH must be the name of an existing branch.
+The value returned is the value of the last form in BODY."
+  (declare (indent 1) (debug t))
+  (let ((cur-branch (make-symbol "cur-branch")))
+    `(let ((,cur-branch gited-current-branch))
+       (unwind-protect
+           (progn
+             (vc-git-checkout nil ,branch)
+             (setq gited-current-branch ,branch)
+             ,@body)
+         ;; Restore original current branch.
+         (vc-git-checkout nil ,cur-branch)
+         (setq gited-current-branch ,cur-branch)))))
+;;; Map over marks.
+(defmacro gited-map-over-marks (body arg)
+  "Eval BODY with point on each marked line.  Return a list of BODY's results.
+If no marked branch could be found, execute BODY on the current
+line.  ARG, if non-nil, specifies the branches to use instead of the
+marked branches.
+If ARG is an integer, use the next ARG (or previous -ARG, if
+ARG<0) files.  In that case, point is dragged along.  This is so
+that commands on the next ARG (instead of the marked) files can
+be chained easily.
+For any other non-nil value of ARG, use the current file.
+Search starts at the beginning of the buffer, thus the car of the
+list corresponds to the line nearest to the buffer's bottom.
+This is also true for (positive and negative) integer values of
+BODY should not be too long as it is expanded four times.
+If DISTINGUISH-ONE-MARKED is non-nil, then if we find just one
+marked file, return (t FILENAME) instead of (FILENAME)."
+  `(prog1
+       (let ((inhibit-read-only t) case-fold-search found results)
+         (if ,arg
+             (if (integerp ,arg)
+                 (progn ;; no save-excursion, want to move point.
+                   (gited-repeat-over-lines
+                    ,arg
+                    (lambda ()
+                      (setq results (cons ,body results))))
+                   (if (< ,arg 0)
+                       (nreverse results)
+                     results))
+               ;; non-nil, non-integer ARG means use current file:
+               (list ,body))
+           (let ((regexp
+                  (concat "^"
+                          (regexp-quote
+                           (char-to-string gited-marker-char))))
+                 next-position)
+             (save-excursion
+               (goto-char (point-min))
+               ;; remember position of next marked file before BODY
+               ;; can insert lines before the just found file,
+               ;; confusing us by finding the same marked file again
+               ;; and again and...
+               (setq next-position (and (re-search-forward regexp nil t)
+                                        (point-marker))
+                     found (not (null next-position)))
+               (while next-position
+                 (goto-char next-position)
+                 (setq results (cons ,body results))
+                 ;; move after last match
+                 (goto-char next-position)
+                 (forward-line)
+                 (set-marker next-position nil)
+                 (setq next-position (and (re-search-forward regexp nil t)
+                                          (point-marker)))))
+             (if found
+                 results
+               (list ,body)))))
+     ;; save-excursion loses, again
+     (gited-move-to-branchname)))
+(defmacro gited-mark-if (predicate msg)
+  "Mark all branches for which PREDICATE eval to non-nil.
+PREDICATE is evaluated on each line, with point at beginning of line.
+MSG is a noun phrase for the type of branches being marked.
+It should end with a noun that can be pluralized by adding `s'.
+Return value is the number of files marked, or nil if none were marked."
+  `(let ((inhibit-read-only t) count)
+     (save-excursion
+       (setq count 0)
+       (when ,msg
+         (message "%s %ss%s..."
+                  (cond ((eq gited-marker-char ?\s) "Unmarking")
+                        ((eq gited-marker-char gited-del-char)
+                         "Flagging")
+                        (t "Marking"))
+                  ,msg
+                  (if (eq gited-marker-char gited-del-char)
+                      " for deletion"
+                    "")))
+       (gited--goto-first-branch)
+       (while (not (eobp))
+         (if ,predicate
+             (progn
+               (gited-insert-marker-char)
+               (gited-fontify-marked-branch-name)
+               (cl-incf count)))
+         (forward-line))
+       (if ,msg (message "%s %s%s %s%s."
+                         count
+                         ,msg
+                         (gited-plural-s count)
+                         (if (eq gited-marker-char ?\s) "un" "")
+                         (if (eq gited-marker-char gited-del-char)
+                             "flagged" "marked"))))
+     (and (> count 0) count)))
+;;; Convenience functions.
+;; Following functions are stolen from Dired.
+;; To avoid code duplication we just bind `dired-log-buffer' to
+;; `gited-log-buffer' and use the original definitions in dired.el.
+(defvar gited-log-buffer "*Gited log*")
+(defalias 'gited-plural-s 'dired-plural-s)
+(defun gited-why ()
+  "Pop up a buffer with error log output from Gited.
+A group of errors from a single command ends with a formfeed.
+Thus, use \\[backward-page] to find the beginning of a group of errors."
+  (interactive)
+  (let ((dired-log-buffer gited-log-buffer))
+    (dired-why)))
+(defun gited-summary ()
+  "Summarize basic Gited commands and show recent Gited errors."
+  (interactive)
+  (gited-why)
+  (message
+   "d-elete, u-ndelete, x-punge, f-ind, o-rigin, R-ename, C-opy, h-elp"))
+(defun gited-log-msg (log &rest args)
+  (let ((dired-log-buffer gited-log-buffer))
+    (dired-log log args)))
+(defun gited-log-summary (string failures)
+  (let ((dired-log-buffer gited-log-buffer))
+    (dired-log-summary string failures)))
+(defun gited-format-columns-of-files (branches)
+  (let ((beg (point)))
+    (completion--insert-strings branches)
+    (put-text-property beg (point) 'mouse-face nil)))
+(defun gited-next-branch ()
+  "Return name of next branch."
+  (save-excursion
+    (forward-line)
+    (ignore-errors
+      (gited-get-branchname))))
+(defun gited-prev-branch ()
+  "Return name of previous branch."
+  (save-excursion
+    (forward-line -1)
+    (ignore-errors
+      (gited-get-branchname))))
+(defun gited-current-branch ()
+  "Return name of current branch."
+  (car (vc-git-branches)))
+(defun gited-get-last-commit-time (branch)
+  "Return last commit time of BRANCH."
+  (date-to-time
+   (cadr
+    (cdr
+     (nth
+      (cl-position-if
+       (lambda (x) (equal branch (nth 3 x))) gited-branch-alist)
+      gited-branch-alist)))))
+(defun gited--get-column (col)
+  (mapcar (lambda (x)
+            (elt (cadr x) col))
+          tabulated-list-entries))
+(defun gited-listed-branches ()
+  "Return a list with all listed branches/tags names."
+  (gited--get-column gited-branch-idx))
+(defalias 'gited-get-branches 'gited-listed-branches)
+(defun gited-git-command (args &optional buffer display unquote)
+  "Execute a Git command with arguments ARGS.
+Optional arg BUFFER is the output buffer.
+Optional arg DISPLAY means redisplay buffer as output is inserted.
+Optional arg UNQUOTE removes single quotes from the output."
+  (prog1
+      (apply #'call-process vc-git-program nil buffer display args)
+    (when (and unquote buffer (buffer-live-p buffer))
+      (with-current-buffer buffer
+        ;; Drop single quotes.
+        (save-excursion
+          (goto-char (point-min))
+          (while (re-search-forward "^'\\(.*\\)'$" nil t)
+            (replace-match "\\1")))))))
+(defun gited-git-command-on-region (args &optional buffer display)
+  "Execute a Git command with arguments ARGS and region as input.
+Optional arg BUFFER is the output buffer.
+Optional arg DISPLAY means redisplay buffer as output is inserted."
+  (apply #'call-process-region nil nil vc-git-program nil buffer display args))
+(defun gited-all-branches ()
+  "Return a list with all (local and remote) branches and tags."
+  (let ((args '("for-each-ref" "--format='%(refname:short)'" "refs/heads"
+                "refs/remotes" "refs/tags")))
+    (with-temp-buffer
+      (gited-git-command args (current-buffer) nil 'unquote)
+      (split-string (buffer-string) "\n" 'omit-nulls))))
+(defun gited-copy-branchname-as-kill ()
+  "Copy names of marked branches into the kill ring.
+The names are separated by a space.
+If on a subdir headerline, use absolute subdirname instead;
+prefix arg and marked files are ignored in this case.
+You can then feed the file name(s) to other commands with \\[yank]."
+  (interactive)
+  (let ((string
+         (mapconcat (function identity)
+                    (or (gited-get-marked-branches)
+                        (list (gited-get-branchname)))
+                    " ")))
+    (unless (string= string "")
+      (if (eq last-command 'kill-region)
+          (kill-append string nil)
+        (kill-new string))
+      (message "%s" string))))
+(defun gited--output-buffer (&optional buf-name)
+  (unless buf-name
+    (setq buf-name gited-output-buffer-name))
+  (let* ((buf
+          (cond ((equal buf-name gited-bisect-buf-name)
+                 (and (buffer-live-p gited-bisect-buffer) gited-bisect-buffer))
+                (t
+                 (and (buffer-live-p gited-output-buffer) 
+         (res (or (and (buffer-live-p buf)
+                       (equal default-directory
+                              (buffer-local-value 'default-directory buf))
+                       buf)
+                  (generate-new-buffer buf-name))))
+    (if (equal buf-name gited-bisect-buf-name)
+        (setq gited-bisect-buffer res)
+      (setq gited-output-buffer res))))
+;;; Predicates.
+(defun gited--list-files (regexp)
+  "List modified or untracked files ccording with REGEXP."
+  (let ((case-fold-search) res)
+    (with-temp-buffer
+      (gited-git-command '("status" "--porcelain") (current-buffer))
+      (goto-char (point-min))
+      (while (re-search-forward regexp nil t)
+        (push (match-string 1) res)))
+    (nreverse res)))
+(defun gited-modified-files ()
+  "Return a list with all unstaged files."
+  (gited--list-files
+   "^\\(?:[[:blank:]][MAUD]\\|[MAU]+\\)[[:blank:]]*\\(.+\\)"))
+(defun gited-modified-files-p ()
+  "Return non-nil if there are unstaged changes."
+  (and (gited-modified-files) t))
+(defun gited-dir-under-Git-control-p ()
+  "Return non-nil if current directory is under Git version control."
+  (zerop (gited-git-command '("status"))))
+(defun gited-branch-exists-p (branch)
+  "Return non-nil if BRANCH exists."
+  (member branch (gited-listed-branches)))
+(defun gited-buffer-p ()
+  "Return non-nil if current buffer is a gited buffer."
+  (string-prefix-p gited-buffer-name
+                   (buffer-name (current-buffer))))
+(defun gited-at-header-line-p ()
+  "Return non-nil if point is at header line."
+  (and (not gited-use-header-line)
+       (= 1 (line-number-at-pos (point)))))
+(defun gited-remote-repository-p ()
+  "Return non-nil if current repository is remote."
+  (let ((regexp "^remote.origin"))
+    (with-temp-buffer
+      (gited-git-command  '("config" "--local" "--list") (current-buffer))
+      (goto-char 1)
+      (and (re-search-forward regexp nil t) t))))
+;;; Operations on branches (copy, merge, ...).
+(defun gited--get-branches-from-command (cmd)
+  (with-temp-buffer
+    (gited-git-command cmd (current-buffer) nil 'unquote)
+    (goto-char (point-min))
+    (while (re-search-forward "^\\(  \\|\\* \\)" nil t)
+      (replace-match ""))
+    (split-string (buffer-string) "\n" 'omit-nulls)))
+(defun gited-trunk-branches ()
+  "Return a list with branch names tracked from a remote repository."
+  (let ((regexp "^branch\.\\([^.]+\\)\.merge=")
+        res)
+    (with-temp-buffer
+      (gited-git-command  '("config" "--local" "--list") (current-buffer))
+      (goto-char 1)
+      (while (re-search-forward regexp nil t)
+        (push (match-string 1) res))
+      (nreverse res))))
+(defun gited--get-unmerged-branches ()
+  (let ((args `("branch" "--no-merged" ,(car (gited-trunk-branches)))))
+    (gited--get-branches-from-command args)))
+(defun gited--get-merged-branches ()
+  (let ((args `("branch" "--merged" ,(car (gited-trunk-branches)))))
+    (gited--get-branches-from-command args)))
+(defun gited--check-unmerged-marked-branches (&optional marker)
+  (let ((marked-branches (or (gited-get-marked-branches marker)
+                             (list (gited-get-branchname)))))
+    (dolist (b marked-branches)
+      (let ((unmerged (ignore-errors (gited--get-unmerged-branches))))
+        (dolist (x unmerged)
+          (when (string= b x)
+            (error "Cannot delete unmerged branches.  Try C-u %s"
+                   (substitute-command-keys (this-command-keys)))))))))
+(defun gited--merged-branch-p (branch)
+  (and (member branch (gited--get-merged-branches))
+       t))
+(defun gited-untracked-files ()
+  "Return a list with all untracked files."
+  (gited--list-files "^[?]\\{2\\}[[:blank:]]+\\(.+\\)"))
+(defun gited-stashes ()
+  "Return a list with all the stashes."
+  (let ((args '("stash" "list"))
+        res)
+    (with-temp-buffer
+      (gited-git-command args (current-buffer))
+      (goto-char (point-min))
+      (while (not (eobp))
+        (push (buffer-substring (point-at-bol) (point-at-eol)) res)
+        (forward-line)))
+    (nreverse res)))
+(defun gited-commit-title (commit)
+  "Return title of COMMIT, a string."
+  (let ((args `("log" "--pretty=format:'%s'" "-n1" ,commit)))
+    (with-temp-buffer
+      (gited-git-command args (current-buffer) nil 'unquote)
+      (buffer-string))))
+(defun gited-last-commit-title ()
+  "Return title of the last commit."
+  (gited-commit-title "HEAD"))
+;; Non-nil while running an asynchronous Gited subprocess.
+(defvar-local gited--running-async-op nil)
+(defun gited-async-operation (command &optional remote-op-p buffer)
+  "Run COMMAND asynchronously.
+COMMAND perform a branch operation, i.e., rename or delete a branch.
+Optional arg REMOTE-OP-P, means the operation modify the remote
+repository.  Otherwise, the operation just change local branches.
+Optional arg BUFFER is the output buffer for the operation.  Otherwise,
+use `gited-output-buffer'."
+  (interactive)
+  (if gited--running-async-op
+      (error "Cannot run 2 Gited async process in parallel")
+    (let* ((gited-buf (current-buffer))
+           (out-buf (or buffer (gited--output-buffer)))
+           (directory default-directory)
+           proc)
+      (with-current-buffer out-buf
+        ;; Always display out-buf for remote operations.  This is to prompt
+        ;; for Git user/password.
+        (when remote-op-p
+          (display-buffer out-buf '(nil (allow-no-window . t))))
+        (setq default-directory directory
+              proc (start-process-shell-command
+                    "*gited-async-operation*" out-buf command)
+              mode-line-process '(":%s"))
+        (with-current-buffer gited-buf
+          (setq gited--running-async-op t))
+        (with-no-warnings
+          (require 'shell) (shell-mode))
+        (set-process-sentinel proc 'gited-async-operation-sentinel)
+        (set-process-filter proc 'comint-output-filter)
+        ;; Associate out-buf with gited-buf; this is used in the sentinel.
+        (setq gited-buffer gited-buf)))))
+(defun gited-async-operation-sentinel (proc state)
+  "Sentinel for asynchronous operations on branches.
+PROC is the process.
+STATE is the state of process PROC."
+  (let* ((buf (process-buffer proc))
+         (gited-buf (and (buffer-live-p buf)
+                         (buffer-local-value 'gited-buffer buf)))
+         (op-string gited-op-string)
+         (inhibit-read-only t))
+    (when (memq (process-status proc) '(exit signal))
+      (with-current-buffer gited-buf
+        (setq gited--running-async-op nil)
+        (when (gited-bisecting-p)
+          (gited--bisect-after-run (process-buffer proc)))))
+    (when (buffer-live-p gited-buf)
+      (set-buffer gited-buf)
+      (run-hooks 'gited-after-change-hook)
+      (when gited-branch-after-op
+        (gited-goto-branch gited-branch-after-op)
+        (setq gited-branch-after-op nil
+              gited-op-string nil))
+      ;; State is 'finished\n' when the process exit with code 0.
+      (if (string-prefix-p "finished" state)
+          (message "%s done!" op-string)
+        (beep)
+        (message "Process exited with non-zero status.  Please check")
+        (display-buffer gited-output-buffer)))))
+(define-minor-mode gited-hide-details-mode
+  "Toggle visibility of detailed information in current Gited buffer.
+When this minor mode is enabled, details such as last commit author and
+date are hidden from view."
+  :group 'gited
+  (unless (derived-mode-p 'gited-mode)
+    (error "Not a Gited buffer"))
+  (gited-hide-details-update-invisibility-spec))
+(put 'gited-hide-details-mode 'permanent-local t)
+(defun gited-update ()
+  "Update `gited-branch-alist' and redisplay the list of branches."
+  (interactive)
+  (unless (derived-mode-p major-mode 'gited-mode)
+    (error "Cannot enable Gited mode in this buffer"))
+  (let ((target-br (ignore-errors (gited-get-branchname)))
+        (at-headr-p (gited-at-header-line-p))
+        (hide-details gited-hide-details-mode))
+    (gited-list-branches gited-ref-kind nil 'update)
+    (gited-hide-details-mode (if hide-details 1 0))
+    (if (not at-headr-p)
+        (gited-goto-branch target-br)
+      (gited-goto-branch gited-current-branch))))
+(add-hook 'gited-after-change-hook 'gited-update)
+(defun gited-rename-branch (old-name new-name)
+  "Rename branch OLD-NAME to NEW-NAME."
+  (interactive
+   (let* ((old (gited-get-branchname))
+          (new (read-string
+                (format "Rename %s to: " old)
+                nil nil old)))
+     (list old new)))
+  (when (member old-name gited-protected-branches)
+    (error "Cannot rename a protected branch"))
+  (let ((buf (gited--output-buffer))
+        (inhibit-read-only t) remote-op-p)
+    (setq gited-output-buffer buf)
+    (with-current-buffer buf (erase-buffer))
+    (if (and (not gited-expert)
+             (not (y-or-n-p
+                   (format "Rename branch '%s' to '%s'? "
+                           old-name new-name))))
+        (message "OK, rename canceled")
+      (if (gited-async-operation
+           (pcase gited-ref-kind
+             ("remote"
+              (let ((old (substring old-name (length "origin/")))
+                    (new (if (string-prefix-p "origin/" new-name)
+                             (substring new-name (length "origin/"))
+                           new-name)))
+                (setq remote-op-p t)
+                (format "%s push origin origin/%s:refs/heads/%s :%s"
+                        vc-git-program old new old)))
+             ("local" (format "%s branch --move %s %s"
+                              vc-git-program old-name new-name))
+             ("tags" (error "Rename tags not implemented!"))
+             (_ (error "Unsupported gited-ref-kind: must be \
+local, remote or tags")))
+           remote-op-p)
+          (progn
+            (setq gited-branch-after-op new-name
+                  gited-op-string
+                  (format "Rename branch '%s' to '%s'" old-name new-name)))
+        (error "Cannot rename branch '%s' to '%s'" old-name new-name)))))
+(defun gited-merge-branch (branch)
+  "Merge BRANCH with another one.
+That means, changes from another branch are added into BRANCH."
+  (interactive
+   (list (gited-get-branchname)))
+  (let ((branch-new
+         (completing-read
+          (format "Merge %s with: " branch)
+          (gited-listed-branches)
+          nil t)))
+    (gited-with-current-branch branch
+      (if (zerop (gited-git-command `("merge" ,branch-new)))
+          (message "Merged %s into %s!" branch-new branch)
+        (error "Cannot merge '%s' into '%s'" branch-new branch)))))
+(defun gited-reset-branch (commit &optional mode)
+  "Reset current branch to an earlier state.
+COMMIT is a SHA1 string or HEAD~N, to reset BRANCH to that commit.
+Interactively prompt for the limit commit: 0 means HEAD,
+ 1 means HEAD~, and so on.
+Interactively with a prefix argument prompts for the reset mode.
+ Defaults to `gited-reset-mode'."
+  (interactive
+   (let* ((alist
+           '(("s" . "soft") ("m" . "mixed") ("h" . "hard")
+             ("g" . "merged") ("k" . "keep")))
+          (mode
+           (if current-prefix-arg
+               (cdr (assoc
+                     (completing-read
+                      "Reset mode (s = soft, m = mixed, h = hard, \
+g = merged, k = keep): "
+                      '("s" "m" "h" "g" "k")
+                      nil 'mustmatch nil nil "h")
+                     alist))
+             gited-reset-mode))
+          (input
+           (read-string
+            (format "Reset --%s to commit (0 = HEAD, \
+1 = HEAD~1, ... or SHA1): " mode)
+            nil nil "0")))
+     (list (if (gited--valid-ref-p input)
+               input
+             (concat "HEAD~" input))
+           mode)))
+  (unless mode (setq mode gited-reset-mode))
+  (let ((branch (gited-current-branch))
+        (args `("reset" ,(concat "--" mode) ,commit)))
+    (if (not (y-or-n-p
+              (format "Reset --%s '%s' to '%s'? " mode branch commit)))
+        (message "OK, reset canceled")
+      (if (zerop (gited-git-command args))
+          (message "Reseted --%s '%s' to '%s'!" mode branch commit)
+        (error "Cannot reset --%s '%s' to '%s'" mode branch commit)))))
+(defun gited-delete-branch (branch &optional force)
+  "Delete branch BRANCH.
+BRANCH default to the branch at current line.
+Optional arg FORCE, if non-nil then delete non-fully merged branches
+as well."
+  (interactive
+   (list (gited-get-branchname) current-prefix-arg))
+  (let ((br-after (or (gited-next-branch)
+                      (gited-prev-branch)
+                      (gited-current-branch)))
+        (buf (gited--output-buffer))
+        (force (or gited-delete-unmerged-branches force))
+        (inhibit-read-only t))
+    (setq gited-output-buffer buf)
+    (with-current-buffer buf (erase-buffer))
+    (when (string= branch gited-current-branch)
+      (error "Cannot delete the current branch"))
+    (when (member branch gited-protected-branches)
+      (error "Cannot delete a protected branch"))
+    (if (and (not gited-expert)
+             (not (y-or-n-p (format "Delete branch '%s'? " branch))))
+        (message "OK, deletion canceled")
+      (pcase gited-ref-kind
+        ("tags" (error "Delete tags not implemented!"))
+        ("local"
+         (if (zerop (gited-git-command
+                     ;; --delete --force as shortcut of -D doesn't exist
+                     ;; in old Git versions.
+                     `("branch"
+                       ,(if force "-D" "--delete")
+                       ,branch)
+                     buf))
+             (progn
+               (gited-goto-branch br-after)
+               (message "Delete branch '%s'!" branch))
+           (error "Cannot delete unmerged branch '%s'.  Try C-u %s"
+                  branch
+                  (substitute-command-keys "\\[gited-do-flagged-delete\]"))))
+        ("remote"
+         (gited-async-operation
+          (format "%s push origin :%s"
+                  vc-git-program
+                  (substring branch (length "origin/"))) 'remote-op-p)
+         (setq gited-branch-after-op br-after
+               gited-op-string (format "Delete branch '%s'" branch)))
+        (_ (error "Unsupported gited-ref-kind: must be \
+local, remote or tags"))))))
+(defun gited-do-delete (&optional arg force)
+  "Delete all marked (or next ARG) branches.
+Optional arg FORCE, if non-nil then delete non-fully merged branches
+as well."
+  (interactive
+   (let ((prefix current-prefix-arg))
+     (list prefix (equal prefix '(4)))))
+  (unless force
+    (gited--check-unmerged-marked-branches gited-del-char))
+  (gited-internal-do-deletions
+   ;; this may move point if ARG is an integer
+   (gited-map-over-marks (cons (or (gited-get-branchname)
+                                   (error "No branch on this line"))
+                               (point))
+                         arg)
+   arg force)
+  (run-hooks 'gited-after-change-hook))
+(defun gited-mark-pop-up (buffer-or-name op-symbol branches function &rest 
+  "Return FUNCTION's result on ARGS after showing which branches are marked.
+Displays the branch names in a window showing a buffer named
+BUFFER-OR-NAME; the default name being \" *Marked Branches*\".  The
+window is not shown if there is just one branch, `gited-no-confirm'
+is t, or OP-SYMBOL is a member of the list in `gited-no-confirm'.
+By default, Gited shrinks the display buffer to fit the marked branches.
+To disable this, use the Customization interface to add a new rule
+to `display-buffer-alist' where condition regexp is
+\"^ \\*Marked Branches\\*$\",
+action argument symbol is `window-height' and its value is nil.
+BRANCHES is the list of marked branches.  It can also be (t BRANCHNAME)
+in the case of one marked branch, to distinguish that from using
+just the current branch.
+FUNCTION should not manipulate branches, just read input (an
+argument or confirmation)."
+  (if (= (length branches) 1)
+      (apply function args)
+    (let ((buffer (get-buffer-create (or buffer-or-name " *Marked Branches*")))
+          ;; Mark *Marked Branches* window as softly-dedicated, to prevent
+          ;; other buffers e.g. *Completions* from reusing it (bug#17554).
+          (display-buffer-mark-dedicated 'soft))
+      (ignore op-symbol) ; ignore unused symbol.
+      (with-current-buffer buffer
+        (with-current-buffer-window
+         buffer
+         (cons 'display-buffer-below-selected
+               '((window-height . fit-window-to-buffer)))
+         #'(lambda (window _value)
+             (with-selected-window window
+               (unwind-protect
+                   (apply function args)
+                 (when (window-live-p window)
+                   (quit-restore-window window 'kill)))))
+         (gited-format-columns-of-files
+          branches)
+         (remove-text-properties (point-min) (point-max)
+                                 '(mouse-face nil help-echo nil)))))))
+(defun gited-internal-do-deletions (l arg &optional force)
+  ;; L is an alist of branches to delete, with their buffer positions.
+  ;; ARG is the prefix arg.
+  ;; FORCE, if non-nil then delete non-fully merged branches as well.
+  ;; (car L) *must* be the *last* (bottommost) branch in the gited buffer.
+  ;; That way as changes are made in the buffer they do not shift the
+  ;; lines still to be changed, so the (point) values in L stay valid.
+  ;; Also, for subdirs in natural order, a subdir's branches are deleted
+  ;; before the subdir itself - the other way around would not work.
+  (let* ((branches (mapcar (function car) l))
+         (count (length l))
+         (succ 0))
+    ;; canonicalize branch list for pop up
+    (if (gited-mark-pop-up
+         " *Deletions*" 'delete branches 'y-or-n-p
+         (format "%s %s "
+                 "Delete"
+                 (replace-regexp-in-string "files"
+                                           "branches"
+                                           (dired-mark-prompt arg branches))))
+        (save-excursion
+          (let ((progress-reporter
+                 (make-progress-reporter
+                  "Deleting..."
+                  succ count))
+                failures) ;; branches better be in reverse order for this loop!
+            (while l
+              (goto-char (cdar l))
+              (let ((inhibit-read-only t))
+                (condition-case err
+                    (let ((fn (caar l))
+                          (gited-expert t)) ;; Don't ask confirmation again.
+                      (gited-delete-branch fn force)
+                      ;; if we get here, removing worked
+                      (cl-incf succ)
+                      (progress-reporter-update progress-reporter succ))
+                  (error ;; catch errors from failed deletions
+                   (gited-log-msg "%s\n" err)
+                   (setq failures (cons (caar l) failures)))))
+              (setq l (cdr l)))
+            (if (not failures)
+                (progress-reporter-done progress-reporter)
+              (gited-log-summary
+               (format "%d of %d deletion%s failed"
+                       (length failures) count
+                       (gited-plural-s count))
+               failures))))
+      (message "(No deletions performed)")))
+  (gited-goto-branch gited-current-branch))
+(defun gited-do-flagged-delete (&optional force)
+  "In Gited, delete the branches flagged for deletion.
+Optional arg FORCE, if non-nil then delete non-fully merged branches
+as well."
+  (interactive "P")
+  (let* ((gited-marker-char gited-del-char)
+         (regexp (concat "^" (regexp-quote (char-to-string 
+         (force (or gited-delete-unmerged-branches force))
+         case-fold-search)
+    (unless force
+      (gited--check-unmerged-marked-branches gited-del-char))
+    (if (save-excursion (goto-char (point-min))
+                        (re-search-forward regexp nil t))
+        (progn
+          (gited-internal-do-deletions
+           ;; this can't move point since ARG is nil
+           (gited-map-over-marks (cons (gited-get-branchname) (point))
+                                 nil)
+           nil force)
+          (run-hooks 'gited-after-change-hook))
+      (message "(No deletions requested)"))))
+(defun gited-copy-branch (old new &optional commit)
+  "Copy branch OLD to branch NEW.
+OLD default to branch at current line.
+Optional arg COMMIT, if non-nil then is a SHA1 string or
+HEAD~N, to copy OLD until that commit, inclusive.
+When called with a prefix, prompt for the limit commit: 0 means HEAD,
+1 means HEAD~, and so on."
+  (interactive
+   (let* ((old-br (gited-get-branchname))
+          (new-br (read-string
+                   (format "Copy %s to: " old-br)
+                   nil nil old-br))
+          (ask current-prefix-arg)
+          (num-or-sha1
+           (if ask
+               (read-string "Show commit (0 = HEAD, 1 = HEAD~1, ... or SHA1): "
+                            nil nil "0")
+             "HEAD")))
+     (list old-br new-br num-or-sha1)))
+  (unless commit (setq commit "HEAD"))
+  (unless (gited--valid-ref-p commit)
+    (setq commit (concat old "~" commit)))
+  (let ((cmd1 `("checkout" ,old))
+        (cmd2 `("checkout" "-b" ,new ,commit))
+        (cmd3 `("checkout" ,gited-current-branch)))
+    (if (and (zerop (gited-git-command cmd1))
+             (zerop (gited-git-command cmd2))
+             (zerop (gited-git-command cmd3)))
+        (progn
+          (message "Copied %s to %s!" old new)
+          (run-hooks 'gited-after-change-hook)
+          (gited-goto-branch new))
+      (error "Cannot copy '%s' to '%s'" old new))))
+;;; Checkout/visit sources.
+(defun gited-visit-branch-sources (&optional other-window)
+  "Visit source code for current revision.
+If optional arg OTHER-WINDOW is non-nil, then use another window."
+  (interactive "P")
+  (when (and (gited-modified-files-p)
+             (not (equal gited-current-branch (gited-get-branchname))))
+    (error "Cannot checkout a new branch: there are modified files"))
+  (let* ((branch (gited-get-branchname))
+         (visit-sources
+          (y-or-n-p (format "Visit '%s' branch sources? " branch))))
+    (if (not visit-sources)
+        (error "OK, canceled")
+      (let ((gited-expert visit-sources))
+        (gited-checkout-branch branch)
+        (if other-window
+            (dired-other-window default-directory)
+          (dired default-directory))
+        (dired-revert)))))
+(defun gited--fontify-current-row ()
+  (remove-text-properties
+   (point-at-bol) (point-at-eol) '(face))
+  (let ((inhibit-read-only t) pos)
+    (save-excursion
+      (gited-move-to-author)
+      (setq pos (point))
+      (gited-move-to-end-of-author)
+      (put-text-property
+       pos (point) 'face gited-author-face)
+      (gited-move-to-date)
+      (setq pos (point))
+      (gited-move-to-end-of-date)
+      (put-text-property
+       pos (point) 'face gited-date-time-face)
+      (gited-move-to-branchname)
+      (setq pos (point))
+      (gited-move-to-end-of-branchname)
+      (put-text-property
+       pos (point) 'face gited-branch-name-face)
+      (setq pos (point))
+      (put-text-property
+       pos (point-at-eol) 'face gited-commit-msg-face))))
+(defun gited-checkout-branch (branch)
+  "Checkout BRANCH.
+If the gited buffer lists local branches and BRANCH is not
+local, then prompt for a branch name where to check out BRANCH."
+  (interactive
+   (list (completing-read "Checkout branch: "
+                          (gited-all-branches)
+                          nil 'mustmatch (gited-get-branchname) nil)))
+  (when (and (gited-modified-files-p)
+             (not (equal gited-current-branch (gited-get-branchname))))
+    (error "Cannot checkout a new branch: there are modified files"))
+  (let* ((cur-br gited-current-branch)
+         (new-branch-p (and (equal gited-ref-kind "local")
+                            (not (member branch (gited-get-branches)))))
+         (inhibit-read-only t)
+         (local-branch (if new-branch-p
+                           (read-string
+                            "Checkout in local branch: "
+                            nil nil (file-name-nondirectory branch))
+                         branch)))
+    (save-excursion
+      (gited-goto-branch cur-br)
+      (gited--fontify-current-row)
+      (if (not new-branch-p)
+          (vc-git-checkout nil branch)
+        (gited-git-command `("checkout" "-b" ,local-branch ,branch))
+        (run-hooks 'gited-after-change-hook))
+      (setq gited-current-branch local-branch))
+    (gited-fontify-current-branch)
+    (when new-branch-p
+      (gited-goto-branch gited-current-branch)))
+  (message "Current branch is %s" gited-current-branch))
+;;; Apply patches.
+(defun gited--patch-or-commit-buffer (&optional commit)
+  (let ((regexp
+         (if commit
+             "\\`\\*gited-commit-\\([0-9]+\\)\\*\\'"
+           "\\`\\*gited-patch-\\([0-9]+\\)\\*\\'")))
+    (get-buffer
+     (completing-read
+      (if commit
+          "Use commit message in buffer: "
+        "Apply patch from buffer: ")
+      (cl-delete-if-not
+       (lambda (x) (string-match regexp x))
+       (mapcar #'buffer-name (buffer-list)))))))
+(defconst gited-new-or-deleted-files-re
+  (format "^\\(%s\\|%s\\|%s\\|%s\\|%s\\|%s\\)"
+          "deleted file mode"
+          "new file mode"
+          "copy from"
+          "copy to"
+          "rename from"
+          "rename to")
+  "Regexp to match new or deleted files in a Git diff.")
+;; Currently just handle new files.
+(defun gited--handle-new-or-delete-files (patch-buf)
+  (let ((new-files))
+    (goto-char 1)
+    (with-current-buffer patch-buf
+      (while (re-search-forward gited-new-or-deleted-files-re nil t)
+        (unless (or (string= "new file mode" (match-string-no-properties 0))
+                    (string= "deleted file mode" (match-string-no-properties 
+          (error "Only creation/deletion of files is implemented: %s"
+                 (match-string-no-properties 0)))
+        (let* ((str (buffer-substring-no-properties
+                     (point-at-bol 0) (point-at-eol 0)))
+               (file
+                (progn
+                  (string-match "diff --git a/\\(.*\\) b/.*" str)
+                  (match-string-no-properties 1 str))))
+          (push file new-files))))
+    (if (zerop (gited-git-command (nconc '("add") new-files)))
+        (message "Sucessfully staged new files: %s"
+                 (mapconcat #'shell-quote-argument new-files " "))
+      (error "Cannot stage some new files.  Please check"))))
+(defun gited-apply-patch (buf-patch &optional update)
+  "Apply patch at BUF-PATCH into current branch.
+If optional arg UPDATE is non-nil, then call `gited-update'
+after checkout."
+  (interactive
+   (list (gited--patch-or-commit-buffer)
+         current-prefix-arg))
+  (let ((toplevel gited-toplevel-dir)
+        create-or-del-files-p)
+    (with-temp-buffer
+      ;; Apply patches from top-level dir.
+      (setq default-directory (file-name-as-directory toplevel))
+      (insert-buffer-substring-no-properties buf-patch)
+      (goto-char 1)
+      (setq create-or-del-files-p
+            (re-search-forward gited-new-or-deleted-files-re nil t))
+      (if (not (zerop (gited-git-command-on-region '("apply" "--check"))))
+          (error "Cannot apply patch at '%s'.  Please check"
+                 (buffer-name buf-patch))
+        (gited-git-command-on-region '("apply"))
+        (when create-or-del-files-p
+          (gited--handle-new-or-delete-files (current-buffer)))
+        (and update (gited-update))
+        (message "Patch applied successfully!")))))
+(defun gited-add-patched-files (files &optional _untracked-ok ask)
+  "Stage FILES for next commit.
+If optional arg UNTRACKED-OK is non-nil, then stage untracked
+files.  Otherwise ignore them.
+To always stage untracked files, you can customize the option
+If optional arg ASK is non-nil, then prompt the user before
+to add every hunk, and display the output buffer in other window.
+Interactively, with a prefix C-u stage the untracked files as well.
+Interactively, with 2 prefices C-u C-u set arg ASK non-nil."
+  (interactive
+   (let* ((prefix current-prefix-arg)
+          (untracked-ok (equal '(4) prefix))
+          (ask (equal '(16) prefix))
+          (files
+           (if (or untracked-ok gited-add-untracked-files)
+               (nconc (gited-modified-files) (gited-untracked-files))
+             (gited-modified-files))))
+     (list files untracked-ok ask)))
+  (if (not files)
+      (let ((untracked (gited-untracked-files)))
+        (beep)
+        (if untracked
+            (message "Only untracked files.  Call 'C-u %s' to add them."
+                     (substitute-command-keys "\\[gited-add-patched-files]"))
+          (message "No modified files")))
+    (let ((buf (gited--output-buffer)))
+      (cond (ask
+             ;; Output buffer must be editable.
+             (with-current-buffer buf
+               (setq buffer-read-only nil)
+               (erase-buffer))
+             (gited-async-operation (format "%s add --patch" vc-git-program)
+                                    nil buf)
+             (setq gited-op-string "add --patch")
+             (display-buffer buf))
+            (t
+             (let ((toplevel gited-toplevel-dir))
+               (with-temp-buffer
+                 ;; Add files from top-level dir.
+                 (setq default-directory (file-name-as-directory toplevel))
+                 (if (not (zerop (gited-git-command (nconc '("add") files))))
+                     (error "Cannot add files.  Please check")
+                   (message "Successfully added files: %s"
+                            (mapconcat #'shell-quote-argument files " 
+(defun gited-commit (comment &optional author)
+  "Commit latest changes using COMMENT as the message.
+Optional argument AUTHOR is the author of the commit.
+A prefix argument prompts for AUTHOR."
+  (interactive
+   (let ((_files (or (gited-modified-files) (error "No changes to commit")))
+         (name (and current-prefix-arg (read-string "Author: ")))
+         (msg (read-string "Message: ")))
+     (list msg name)))
+  (or (gited-modified-files) (error "No changes to commit"))
+  (let* ((buf (generate-new-buffer "*git-commit*"))
+         (args
+          (delete ""
+                  `("commit" ,(if author (format "--author=\"%s\"" author) "")
+                    "-m"
+                    ,comment))))
+    (unless (zerop (gited-git-command args buf))
+      (display-buffer buf)
+      (error "Commit failt: please check %s" (buffer-name buf)))
+    (with-current-buffer buf
+      (insert (format "\nCommit successfully with message:\n\n\"%s\"" 
+    (gited--set-output-buffer-mode buf)
+    (display-buffer buf)))
+(defun gited-apply-add-and-commit-patch (buf-patch buf-commit)
+  "Apply patch at BUF-PATCH, stage it and commit it with message BUF-COMMIT."
+  (interactive
+   (list
+    (gited--patch-or-commit-buffer)
+    (gited--patch-or-commit-buffer 'commit)))
+  (if (null (ignore-errors (gited-apply-patch buf-patch)))
+      (error "Cannot apply patch at %s" (buffer-name buf-patch))
+    (let ((cur-buf (current-buffer)))
+      ;; Stage changes.
+      (gited-add-patched-files (gited-modified-files)) ; Switch to another buf.
+      (switch-to-buffer cur-buf)
+      (let* ((commit-msg
+              (with-current-buffer buf-commit
+                (buffer-string)))
+             (args `("commit" "-m" ,commit-msg)))
+        (if (zerop (gited-git-command args))
+            (message "Patch applied and committed successfully!")
+          (error "Cannot commit patch at %s" (buffer-name buf-patch)))))))
+;;; Revert a commit.
+;; Following is inspired `edit-kbd-macro'.
+(defvar gited-original-buffer)
+(defvar gited--revert-commit)
+(defvar gited-edit-commit-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\C-c\C-c" 'gited-finish-commit-edit)
+    map))
+(defun gited-finish-commit-edit ()
+  (interactive)
+  (unless (eq major-mode 'gited-edit-commit-mode)
+    (error
+     "This command is valid only in buffers created by 
+  (goto-char 1)
+  (while (re-search-forward "^#.*
+?" nil t)
+    (replace-match ""))
+  (unless (zerop (gited-git-command `("revert" "--no-commit" 
+    (error "Cannot revert commit %s" gited--revert-commit))
+  (unless (zerop (gited-git-command `("commit" "-m" ,(buffer-string))))
+    (error "Reverted commit %s but cannot commit the revert" 
+  (switch-to-buffer gited-original-buffer)
+  (run-hooks 'gited-after-change-hook))
+(defun gited-edit-commit-mode ()
+  (interactive)
+  (error "This mode can be enabled only by `gited-edit-commit'"))
+(put 'gited-edit-commit-mode 'mode-class 'special)
+(defun gited-edit-commit (commit)
+  "Edit message to revert a commit."
+  (let ((string (format "Revert '%s'\n\nThis reverts commit %s\n\n%s\n%s\n%s\n"
+                        (gited-commit-title commit)
+                        commit
+"# Please enter the commit message for your changes. Lines starting"
+"# with '#' will be ignored, and an empty message aborts the commit."
+"# Press 'C-c C-c' once done to commit.  Press  C-x k RET to cancel.")))
+    (let ((oldbuf (current-buffer))
+              (buf (get-buffer-create "*Edit Commit*")))
+         (switch-to-buffer buf)
+         (kill-all-local-variables)
+         (use-local-map gited-edit-commit-mode-map)
+         (setq buffer-read-only nil)
+      (setq major-mode 'gited-edit-commit-mode)
+         (setq mode-name "Edit Commit")
+         (set (make-local-variable 'gited-original-buffer) oldbuf)
+         (set (make-local-variable 'gited--revert-commit) commit)
+         (erase-buffer)
+      (insert string)
+         (recenter '(4)))))
+(defun gited-revert-commit (commit)
+  (interactive
+   (let ((last-commit
+          (with-temp-buffer
+            (gited-git-command '("rev-parse" "HEAD") (current-buffer))
+            (buffer-substring 1 (1- (point-max))))))
+     (list (read-string "Revert commit: " last-commit))))
+  (gited-edit-commit commit))
+;;; Common operations on Git repositiores: pull, diff, log, etc.
+(defun gited-number-of-commits ()
+  "Return number of Git commits in current buffer."
+  (let ((regexp "^commit[:]? \\([[:xdigit:]]+\\)"))
+    (save-excursion
+      (goto-char (point-min))
+      (let ((count 0))
+        (while (re-search-forward regexp nil t)
+          (cl-incf count))
+        (if (called-interactively-p 'interactive)
+            (message "%d commits in current buffer" count)
+          count)))))
+(defun gited--case-ref-kind ()
+  (pcase gited-ref-kind
+    ("remote" "remotes/")
+    ("local" "heads/")
+    ("tags" "tags/")
+    (_ (error "Unsupported gited-ref-kind: must be local, remote or tags"))))
+(defun gited--set-output-buffer-mode (buffer &optional mode editable)
+  (let ((win (get-buffer-window buffer)))
+    (when win (set-window-point win (point-min))))
+  (with-current-buffer buffer
+    (goto-char (point-min))
+    (set-buffer-modified-p nil)
+    (or editable (setq buffer-read-only t))
+    (pcase mode
+      (`diff (diff-mode))
+      (`outline (outline-mode))
+      (_ (fundamental-mode)))))
+(defun gited-diff (ref)
+  "Show diff of REF with another ref.
+REF defaults to the branch at current line.
+The actual diff run is:
+diff OLD-REF REF."
+  (interactive
+   (list (gited-get-branchname)))
+  (let ((old-ref
+         (completing-read (format "Diff %s with: " ref)
+                          (gited-listed-branches)
+                          nil nil (if (equal ref gited-current-branch)
+                                      (concat gited-current-branch "^")
+                                    gited-current-branch)))
+        (buf (gited--output-buffer)))
+    (setq gited-output-buffer buf)
+    (with-current-buffer buf
+      (let ((inhibit-read-only t))
+        (erase-buffer)
+        (gited-git-command `("diff" ,old-ref ,ref)
+                           (current-buffer)))
+      (display-buffer buf))
+    (gited--set-output-buffer-mode buf 'diff)))
+(defun gited--valid-ref-p (str)
+  (let ((args `("rev-parse" ,str)))
+    (zerop (gited-git-command args))))
+(defun gited-show-commit (branch &optional commit)
+  "Show a commit of BRANCH.
+BRANCH default to the branch at current line.
+Optional arg COMMIT, if non-nil then is a SHA1 string or
+HEAD~N, to indicate which commit to display.
+Interactively prompt for the limit commit: 0 means HEAD,
+1 means HEAD~, and so on."
+  (interactive
+   (let* ((branch (gited-get-branchname))
+          (num-or-sha1
+           (read-string "Show commit (0 = HEAD, 1 = HEAD~1, ... or SHA1): "
+                        nil nil "0")))
+     (list branch num-or-sha1)))
+  (let ((buf (gited--output-buffer))
+        (args (if (gited--valid-ref-p commit)
+                  `("show" ,commit)
+                (list "show" (concat (gited--case-ref-kind)
+                                     branch "~" commit)))))
+    (setq gited-output-buffer buf)
+    (with-current-buffer buf
+      (let ((inhibit-read-only t))
+        (erase-buffer)
+        (gited-git-command args (current-buffer)))
+      (display-buffer buf))
+    (gited--set-output-buffer-mode buf 'diff)))
+(defun gited-status ()
+  "Show repository status.
+If `magit' is installed, then this calls `magit-status'.  Otherwise,
+show similar info as that command."
+  (interactive)
+  (let ((buf (gited--output-buffer))
+        (branch gited-current-branch)
+        (gited-buf gited-buffer))
+    (setq gited-output-buffer buf)
+    (if (ignore-errors (find-library-name "magit"))
+        (with-no-warnings (magit-status-internal default-directory))
+      (with-current-buffer buf
+        (let ((inhibit-read-only t)
+              (untracked (gited-untracked-files))
+              (unstaged (gited-modified-files))
+              (bisectingp (gited-bisecting-p))
+              (stashes (gited-stashes)))
+          (erase-buffer)
+          ;; (outline-mode)
+          (insert (format "Head:     %s %s\n"
+                          (propertize branch
+                                      'font-lock-face 
+                          (gited-last-commit-title)))
+          ;; (insert (format "Tag:     %s (%s)\n" tag tag-id))
+          (when bisectingp
+            (insert (format "\n%s\nCall C-u C-u %s to reset\n"
+                            (propertize
+                             "You are bisecting:"
+                             'font-lock-face 'gited-status-tag)
+                            (with-current-buffer gited-buf
+                              (substitute-command-keys "\\[gited-bisect]"))))
+            (let ((nentries -1)
+                  bad-commit res)
+              (with-temp-buffer
+                (gited-git-command '("bisect" "log") (current-buffer))
+                (goto-char (point-min))
+                (save-excursion
+                  (when (re-search-forward
+                         "# first bad commit: \\[\\([[:xdigit:]]+\\)\\]"
+                         nil t)
+                    (setq bad-commit (match-string 1))))
+                (while (re-search-forward "^[^#]+$" nil t)
+                  (push (buffer-substring-no-properties (point-at-bol) (point))
+                        res))
+                (setq res (nreverse res) nentries (length res)))
+              (insert (format "\n%s (%d)\n"
+                              (propertize
+                               "Bisect Log:"
+                               'font-lock-face 'gited-status-tag)
+                              (1+ nentries)))
+              (while res
+                (insert (concat (pop res) "\n")))
+              (when bad-commit
+                (insert (concat bad-commit " is the first bad commit\n")))))
+          (when untracked
+            (insert (format "\n%s (%d)\n"
+                            (propertize "Untracked files"
+                                        'font-lock-face 'gited-status-tag)
+                            (length untracked)))
+            (insert (mapconcat 'identity untracked "\n")))
+          (when unstaged
+            (insert (format "\n\n%s (%d)\n"
+                            (propertize "Unstaged changes"
+                                        'font-lock-face 'gited-status-tag)
+                            (length unstaged)))
+            (insert (mapconcat 'identity unstaged "\n")))
+          (when stashes
+            (insert (format "\n\n%s (%d)\n"
+                            (propertize "Stashes"
+                                        'font-lock-face 'gited-status-tag)
+                            (length stashes)))
+            (insert (mapconcat 'identity stashes "\n")))
+          (display-buffer buf)
+          (gited--set-output-buffer-mode buf 'outline)))
+      )))
+(defun gited-pull ()
+  "Run git pull in current branch."
+  (interactive)
+  (let ((branch gited-current-branch))
+    (if (not (or gited-expert
+                 (y-or-n-p (format "Pull on '%s' branch? " branch))))
+        (message "OK, pull canceled")
+      (let ((buf (gited--output-buffer))
+            (cmd (format "%s pull" vc-git-program))
+            (inhibit-read-only t))
+        (setq gited-output-buffer buf
+              gited-op-string cmd)
+        (with-current-buffer buf (erase-buffer))
+        (gited-async-operation cmd 'remote-op-p)))))
+(defun gited-push (&optional force-with-lease)
+  "Run git push in current branch.
+If optional arg FORCE-WITH-LEASE is non-nil, then use Git flag
+--force-with-lease.  Otherwise, reject the pull if the remote
+ref is not ancestor of the local ref."
+  (interactive "P")
+  (if (not (or gited-expert
+               (y-or-n-p (format "Push '%s' branch? "
+                                 gited-current-branch))))
+      (message "OK, push canceled")
+    (let ((buf (gited--output-buffer))
+          (cmd (format "%s push %s" vc-git-program
+                       (if force-with-lease "--force-with-lease" ""))))
+      (setq gited-output-buffer buf
+            gited-op-string cmd)
+      (with-current-buffer buf
+        (setq buffer-read-only nil) ; Editable, they can ask username.
+        (erase-buffer))
+      (gited-async-operation cmd 'remote-op-p))))
+(defun gited-set-branch-upstream (branch)
+  "Push a local BRANCH to origin."
+  (interactive
+   (list (gited-get-branchname)))
+  (unless (string= gited-ref-kind "local")
+    (error "Gited should be listing local branches"))
+  (if (not (or gited-expert
+               (y-or-n-p (format "Push '%s' branch up stream? "
+                                 branch))))
+      (message "OK, push canceled")
+    (let ((buf (gited--output-buffer))
+          (cmd (format "%s push --set-upstream origin %s"
+                       vc-git-program branch))
+          (inhibit-read-only t))
+      (setq gited-output-buffer buf
+            gited-op-string (format "Set branch '%s' upstream" branch))
+      (with-current-buffer buf (erase-buffer))
+      (gited-async-operation cmd 'remote-op-p))))
+(defun gited-origin (branch &optional no-display)
+  "Run git log origin..BRANCH.
+BRANCH defaults to the branch at point.
+If optional arg NO-DISPLAY is non-nil, then don't display the
+output buffer.
+Return output buffer.
+Note that this command only has sense if you have a remote branch
+called origin in your Git configuration.  Otherwise, if you wish to
+see the newest N commits then use `\\[gited-log-last-n-commits\]'."
+  (interactive
+   (list (gited-get-branchname) current-prefix-arg))
+  (unless (string= gited-ref-kind "local")
+    (error "Not listing local branches"))
+  (unless (gited-remote-repository-p)
+    (error "Not a remote repository.  Try '%s' or '%s'"
+           (substitute-command-keys "\\[gited-log\]")
+           (substitute-command-keys "\\[gited-log-last-n-commits\]")))
+  (let ((buf (gited--output-buffer))
+        (args (list "log" (concat "origin.." (gited--case-ref-kind) branch)))
+        (gited-buf gited-buffer) count)
+    (setq gited-output-buffer buf)
+    (with-current-buffer buf
+      (let ((inhibit-read-only t) res)
+        (erase-buffer)
+        (setq res (gited-git-command args (current-buffer)))
+        (unless (zerop res)
+          (with-current-buffer gited-buf
+            (error "Command 'git log origin..BRANCH' fails.  Try '%s' or '%s'"
+                   (substitute-command-keys "\\[gited-log\]")
+                   (substitute-command-keys "\\[gited-log-last-n-commits\]"))))
+        (goto-char (point-min)))
+      (setq count (gited-number-of-commits))
+      (or no-display (display-buffer buf)))
+    (gited--set-output-buffer-mode buf 'diff)
+    (message "%d commit%s in '%s'"
+             count (if (> count 1) "s" "") (buffer-name buf))
+    buf))
+(defun gited-log (branch start-date end-date &optional short)
+  "Show Git log for BRANCH between START-DATE and END-DATE.
+If optional arg SHORT is non-nil, then use a short format.
+Interactively, prompt for START-DATE and END-DATE."
+  (interactive
+   (let* ((branch (gited-get-branchname))
+          (last-commit-time
+           (gited-get-last-commit-time branch))
+          (default
+            (format-time-string
+             "%F"
+             (time-subtract
+              last-commit-time
+              (days-to-time 7))))
+          (start   (read-string "start-date: " default nil nil nil))
+          (default (format-time-string "%F"))
+          (end     (read-string "end-date: " default nil nil nil)))
+     (list branch start end current-prefix-arg)))
+  (let ((buf (gited--output-buffer))
+        (args (append (if short gited-short-log-cmd '("log"))
+                      (list (concat (gited--case-ref-kind) branch)))))
+    (setq gited-output-buffer buf)
+    (with-current-buffer buf
+      (let ((inhibit-read-only t))
+        (erase-buffer)
+        (setq args (append args
+                           (list (format "--after=\"%s\"" start-date))
+                           (list (format "--before=\"%s\"" end-date))))
+        (gited-git-command args buf))
+      (display-buffer buf))
+    (gited--set-output-buffer-mode buf 'outline)))
+(defun gited-log-last-n-commits (branch n &optional short)
+  "Show newest N log entries for BRANCH.
+When N is of the form N1-N2, then skip the N1 newest log entries
+and show the remaining newest N2 entries.
+The actual command run in this case is as follows:
+git-log --skip=N1 --max-count=N2.
+If optional arg SHORT is non-nil use a short format."
+  (interactive
+   (list (gited-get-branchname)
+         (read-string "Show newest N commits, or those in (N1, N1 + N2]: "
+                      nil nil "1")
+         current-prefix-arg))
+  (let ((buf (gited--output-buffer)))
+    (setq gited-output-buffer buf)
+    (with-current-buffer buf
+      (let* ((inhibit-read-only t)
+             (skip (and (string-match "\\([0-9]+\\)-\\([0-9]+\\)" n)
+                        (string-to-number (match-string 1 n))))
+             (max (string-to-number (if skip (match-string 2 n) n)))
+             (args (append (if short gited-short-log-cmd '("log"))
+                           (and skip (list (format "--skip=%d" skip)))
+                           (list (format "--max-count=%d" max))
+                           (list branch))))
+        (erase-buffer)
+        (gited-git-command args buf))
+      (display-buffer buf))
+    (gited--set-output-buffer-mode buf 'outline)))
+;;; Extract patches
+(defun gited--clean-previous-patches ()
+  (mapc (lambda (x)
+          (when (buffer-live-p x)
+            (kill-buffer x)))
+        (nconc
+         (gited--get-patch-or-commit-buffers)
+         (gited--get-patch-or-commit-buffers 'commit))))
+(defun gited-extract-patches (n &optional origin write-file)
+  "Extract the patches from the N newest commits.
+Optional arg ORIGIN, means extract the patches from all commits accesible
+from the trunk, and not being in the trunk.
+Optional arg WRITE-FILE if non-nil, then write the patches to disk."
+  (interactive
+   (let* ((prefix current-prefix-arg)
+          (num (unless prefix
+                 (read-string "Extract N newest patches: "
+                              nil nil "1")))
+          (from-origin (and prefix (equal prefix '(4))))
+          (write (and prefix (equal prefix '(16)))))
+     (list num from-origin write)))
+  (let* ((branch (gited-get-branchname))
+         (buffer (if origin
+                     (gited-origin branch 'no-display)
+                   (gited-log-last-n-commits branch n)
+                   gited-output-buffer))
+         num-commits count)
+    (with-current-buffer buffer
+      (if (zerop (buffer-size))
+          (error "No new patches")
+        ;; Previous patch buffers must be deleted.
+        (gited--clean-previous-patches)
+        (save-excursion
+          (goto-char (point-min))
+          (setq num-commits (gited-number-of-commits)
+                count num-commits))))
+    ;; Following form must be evalled with branch temporary current.
+    (gited-with-current-branch branch
+      (dotimes (i num-commits)
+        (let ((buf-patch (get-buffer-create (format "*gited-patch-%d*" count)))
+              (buf-commit (get-buffer-create
+                           (format "*gited-commit-%d*" count))))
+          (with-current-buffer buf-patch
+            (gited-git-command
+             `("format-patch" "-1" ,(format "HEAD~%d" i) "--stdout")
+                               (current-buffer))
+            (gited--set-output-buffer-mode (current-buffer) 'diff 'editable))
+          (with-current-buffer buf-commit
+            (gited-git-command `("show" "-s" "--format=%B" ,(format "HEAD~%d" 
+                               (current-buffer))
+            (while (looking-at "^$") ; Delete empty lines.
+              (delete-char -1)))
+          (when write-file
+            (with-temp-file (expand-file-name
+                             (substring (buffer-name buf-patch) 1 -1)
+                             temporary-file-directory)
+              (insert
+               (with-current-buffer buf-patch
+                 (buffer-string)))))
+          (cl-decf count))))
+    (if write-file
+        (message "Extracted %d patches and saved in %s"
+                 num-commits temporary-file-directory)
+      (message "Extracted %d patches" num-commits))) t)
+(defun gited--get-patch-or-commit-buffers (&optional commit)
+  (let ((regexp
+         (if commit
+             "\\`\\*gited-commit-\\([0-9]+\\)\\*\\'"
+           "\\`\\*gited-patch-\\([0-9]+\\)\\*\\'")))
+    (sort
+     (cl-delete-if-not
+      (lambda (x)
+        (string-match regexp (buffer-name x)))
+      (buffer-list))
+     (lambda (x y)
+       (string< (buffer-name x) (buffer-name y)))
+     )))
+(defun gited-sync-with-trunk (branch-target)
+  "Extract latest patches in branch at point and apply then into BRANCH-TARGET.
+BRANCH-TARGET is a new branch copied from (car (gited-trunk-branches)).
+The effect is similar than merge the branch at point with the trunk;
+one difference is that we don't modify the trunk, instead we copy it;
+another difference that we don't get a 'Merge branch...' commit in the log.
+this command set BRANCH-TARGET current."
+  (interactive
+   (let* ((br (gited-get-branchname))
+          (prompt
+           (format "Syncronized '%s' into new branch: " br))
+          (def (if (string-match "-new\\([0-9]*\\)\\'" br)
+                   (format "%s%d" (substring br 0 (match-beginning 1))
+                           (1+ (string-to-number (match-string 1 br))))
+                 (concat br "-new1"))))
+     (list
+      (completing-read prompt
+                       (gited-listed-branches)
+                       nil nil def))))
+  ;; Previous patch buffers must be deleted.
+  (gited--clean-previous-patches)
+  (unless (gited-remote-repository-p)
+    (error "This command only works for repositories \
+tracking a remote repository"))
+  (if (null (ignore-errors (gited-extract-patches nil t)))
+      (error "No new patches to apply")
+    ;; If branch-target doesn't exists create it as copy of master.
+    (unless (member branch-target (gited-listed-branches))
+      (cond ((gited-trunk-branches)
+             (gited-copy-branch (car (gited-trunk-branches)) branch-target))
+            (t (error "I don't know what is your master branch"))))
+    (let (num-commits)
+      (gited-with-current-branch branch-target
+        (let* ((buf-patches
+                (gited--get-patch-or-commit-buffers))
+               (buf-commits
+                (gited--get-patch-or-commit-buffers 'commit)))
+          (setq num-commits (length buf-patches))
+          (while buf-patches
+            (gited-apply-add-and-commit-patch (car buf-patches)
+                                              (car buf-commits))
+            (setq buf-patches (cdr buf-patches)
+                  buf-commits (cdr buf-commits)))))
+      (gited-checkout-branch branch-target)
+      (gited-update)
+      (message "Successfully applied and committed %d commits!"
+               num-commits))))
+(defun gited-bisecting-p ()
+  "Return non-nil if a Git bisect is on process."
+  (zerop (gited-git-command '("bisect" "log"))))
+(defun gited--bisect-executable-p (command)
+  (let ((file (car (split-string command))))
+    (unless (file-executable-p file)
+      (error "File '%s' not executable" file))))
+(defun gited--bisect-after-run (buffer)
+  (let ((regexp "^[[:xdigit:]]+ is the first bad commit")
+        pos window)
+    (gited--set-output-buffer-mode buffer)
+    (with-current-buffer buffer
+      (save-excursion
+        (goto-char (point-min))
+        (when (re-search-forward regexp nil t)
+          (setq pos (match-beginning 0))
+          (put-text-property (match-beginning 0)
+                             (match-end 0)
+                             'font-lock-face 'gited-status-tag))))
+    (setq window (display-buffer buffer))
+    (when pos (set-window-point window pos))))
+(defun gited-bisect (&optional script reset)
+  "Execute a Git bisect.
+Optional arg SCRIPT if non-nil, then is a script to pass to
+git bisect run.
+Optional arg RESET if non-nil, then means abort the current bisect.
+Interactively, a prefix 'C-u' prompts for SCRIPT; a prefix 'C-u C-u'
+set RESET non-nil."
+  (interactive
+   (let ((prefix current-prefix-arg))
+     (list (equal prefix '(4))
+           (equal prefix '(16)))))
+  (let ((bisectingp (gited-bisecting-p))
+        (obuf (gited--output-buffer gited-bisect-buf-name))
+        (branch (gited-get-branchname)))
+    ;; Ensure output buffer is editable.
+    (with-current-buffer obuf (setq buffer-read-only nil))
+    (cond (reset
+           (if (not bisectingp)
+               (message "Not bisecting.  Nothing to do")
+             (setq gited--running-async-op nil)
+             (gited-git-command '("bisect" "reset") obuf)
+             (message "Successfully reseted git bisect!")))
+          ((not bisectingp)
+           (with-current-buffer obuf
+             (erase-buffer))
+           (let ((bad (read-string "Start bisect with bad/new revision: "
+                                   nil nil branch))
+                 (good (read-string "Good/Old revision: " nil nil branch))
+                 (cmd (and script
+                           (read-shell-command "Bisect shell command: "))))
+             (and cmd
+                  (gited--bisect-executable-p cmd)) ; File must be executable.
+             (gited-git-command `("bisect" "start" ,bad ,good) obuf)
+             (when cmd
+               ;; (when (zerop (gited-git-command `("bisect" "run" ,cmd) obuf))
+               ;;   (gited--bisect-after-run obuf))
+               (gited-async-operation
+                (format "%s bisect run %s" vc-git-program cmd) nil obuf)
+               (setq gited-op-string "bisect run"))
+             (display-buffer obuf)))
+          ((and bisectingp script)
+           (let ((cmd
+                  (and script (read-shell-command "Bisect shell command: "))))
+             (and cmd
+                  (gited--bisect-executable-p cmd)) ; File must be executable.
+             ;; (when (zerop (gited-git-command `("bisect" "run" ,cmd) obuf))
+             ;;   (gited--bisect-after-run obuf))
+             (gited-async-operation
+              (format "%s bisect run %s" vc-git-program cmd) nil obuf)
+             (setq gited-op-string "bisect run"))
+           (display-buffer obuf))
+          (t
+           (let* ((is-badp (y-or-n-p "Is current revision Bad/New? "))
+                  (is-goodp (and (not is-badp)
+                                 (y-or-n-p "Is current revision Good/Old? ")))
+                  (skip (and (not (or is-badp is-goodp))
+                             (y-or-n-p "Do you want to skip this commit? ")))
+                  (args
+                   (cond (is-badp '("bisect" "bad"))
+                         (is-goodp '("bisect" "good"))
+                         (skip '("bisect" "skip"))
+                         (t (error "Commit should be either bad, \
+good or skip")))))
+             (gited-git-command args obuf)
+             (display-buffer obuf))))))
+;;; Git Stash commands.
+(defun gited-stash (&optional untracked)
+  "Stash the changes in a dirty working directory away.
+If called with prefix argument, then include untracked files.  With two
+prefix arguments includes the ignored files as well."
+  (interactive
+   (let ((prefix current-prefix-arg))
+     (list (cond ((and prefix (equal prefix '(4))) "--include-untracked")
+                 ((and prefix (equal prefix '(16))) "--all")))))
+  (let* ((msg (read-string
+               "Stash message: "
+               (format "WIP on %s: " (gited-current-branch))))
+         (args (append '("stash") '("save") `(,msg)
+                       (and untracked `(,untracked)))))
+    (gited-git-command args)))
+(defun gited-stash-apply ()
+  "Apply a stash to the working tree."
+  (interactive)
+  (if (null (gited-stashes))
+      (message "Empty stash list")
+    (let* ((stash
+            (read-string "Apply stash: " nil nil "stash@{0}"))
+           (args `("stash" "apply" ,stash)))
+      (gited-git-command args))))
+(defun gited-stash-pop ()
+  "Apply a stash to the working tree and remove it from stash list."
+  (interactive)
+  (if (null (gited-stashes))
+      (message "Empty stash list")
+    (let* ((stash
+            (read-string "Apply pop: " nil nil "stash@{0}"))
+           (args `("stash" "pop" ,stash)))
+      (gited-git-command args))))
+(defun gited--stash-branch ()
+  (cond ((null (gited-stashes))
+         (error "Empty stash list"))
+        ((gited-modified-files)
+         (error "Commit your local changes before you switch branches"))
+        (t)))
+(defun gited-stash-branch (branch stash)
+  "Create and checkout a new BRANCH from STASH."
+  (interactive
+   (let ((stash
+          (and (gited--stash-branch)
+               (read-string "Branch stash: " nil nil "stash@{0}")))
+         (br (read-string "Branch name: ")))
+     (list br stash)))
+  (when (gited--stash-branch)
+    (let ((args `("stash" "branch" ,branch ,stash)))
+      (gited-git-command args))))
+(defun gited-stash-drop ()
+  "Remove a stash from the stash list."
+  (interactive)
+  (if (null (gited-stashes))
+      (message "Empty stash list")
+    (let* ((stash
+            (read-string "Drop stash: " nil nil "stash@{0}"))
+           (args `("stash" "drop" ,stash)))
+      (gited-git-command args))))
+(defun gited-branch-clear ()
+  "Remove all stashes from the stash list."
+  (interactive)
+  (if (null (gited-stashes))
+      (message "Empty stash list")
+    (if (y-or-n-p "Remove all stashes? ")
+        (gited-git-command '("stash" "clear"))
+      (error "OK, canceled"))))
+(defalias 'gited-delete-all-stashes 'gited-branch-clear)
+;;; Moving around.
+(defun gited-next-line (&optional arg)
+  "Go to start of branch name in next ARG lines."
+  (interactive "p")
+  (forward-line arg)
+  (condition-case nil
+      (gited-move-to-branchname)
+    (error
+     (forward-line -1)
+     (gited-move-to-branchname)
+     (message "At last branch!")
+     (ding)
+     (sit-for 1)
+     (message nil))))
+(defun gited-prev-line (&optional arg)
+  "Go to start of branch name in previous ARG lines."
+  (interactive "p")
+  (let ((oline (line-number-at-pos))
+        nb-line)
+    (when (natnump arg) (setq arg (- arg)))
+    (forward-line arg)
+    (gited-move-to-branchname)
+    (setq nb-line (line-number-at-pos))
+    (when (or (= oline nb-line)
+              (< (- oline (abs arg)) nb-line))
+      (message "At first branch!")
+      (ding)
+      (sit-for 1)
+      (message nil))))
+(defun gited--goto-first-branch ()
+  (goto-char (point-min))
+  (when (overlays-at (point-min))
+    (forward-line)))
+(defun gited-goto-first-branch ()
+  "Go to first branch in current Gited buffer."
+  (interactive)
+  (gited--goto-first-branch)
+  (gited-move-to-branchname))
+(defun gited-goto-last-branch ()
+  "Go to last branch in current Gited buffer."
+  (interactive)
+  (goto-char (point-max))
+  (forward-line -1)
+  (gited-move-to-branchname))
+(defun gited--goto-column (col)
+  (forward-line 0)
+  (dotimes (_ (1- col))
+    (goto-char
+     (next-single-property-change
+      (point)
+      'tabulated-list-column-name))))
+(defun gited-goto-branch (branch)
+  "Go to line describing BRANCH in this Gited buffer.
+Return buffer position on success, else nil."
+  (interactive
+   (let ((cur-branch gited-current-branch))
+     (list (completing-read "Jump to branch: "
+                            (gited-listed-branches)
+                            nil t nil nil cur-branch))))
+  (let ((row (cl-position branch (gited-listed-branches) :test #'equal)))
+    (goto-char (point-min))
+    (forward-line (if (overlays-at (point-min)) (1+ row) row))
+    (gited--goto-column (1+ gited-branch-idx))))
+(defun gited-next-marked-branch (arg &optional wrap opoint)
+  "Move to the next ARG marked branch.
+If WRAP is non-nil, wrap around to the beginning of the buffer if
+we reach the end."
+  (interactive "p\np")
+  (or opoint (setq opoint (point)));; return to where interactively started
+  (if (if (> arg 0)
+          (re-search-forward gited-re-mark nil t arg)
+        (beginning-of-line)
+        (re-search-backward gited-re-mark nil t (- arg)))
+      (gited-move-to-branchname)
+    (if (null wrap)
+        (progn
+          (goto-char opoint)
+          (error "No next marked branch"))
+      (message "(Wraparound for next marked branch)")
+      (goto-char (if (> arg 0) (point-min) (point-max)))
+      (gited-next-marked-branch arg nil opoint))))
+(defun gited-prev-marked-branch (arg &optional wrap)
+  "Move to the previous ARG marked branch.
+If WRAP is non-nil, wrap around to the end of the buffer if we
+reach the beginning of the buffer."
+  (interactive "p\np")
+  (gited-next-marked-branch (- arg) wrap))
+;; Fill and return `gited-branch-alist'.
+(defun gited-get-element-in-row (idx)
+  (let ((row (tabulated-list-get-entry)))
+    (if row
+        (aref row idx)
+      (error "No branch at point"))))
+(defun gited-get-branchname ()
+  (gited-get-element-in-row gited-branch-idx))
+(defun gited-get-mark ()
+  (gited-get-element-in-row gited-mark-idx))
+(defun gited-get-date ()
+  (gited-get-element-in-row gited-date-idx))
+(defun gited-get-commit ()
+  (gited-get-element-in-row gited-commit-idx))
+(defalias 'gited--move-to-column #'gited--goto-column)
+(defun gited--move-to-end-of-column (col)
+  (gited--move-to-column col)
+  (goto-char (next-single-property-change (point) 
+(defun gited--fill-branch-alist (&optional pattern)
+  (let* ((args (append (butlast gited-list-refs-format-command)
+                       (list (format (car (last 
+                                     (if pattern
+                                         (pcase pattern
+                                           ("local" "heads")
+                                           ("remote" "remotes")
+                                           (_ pattern))
+                                       "heads")))))
+         (alist
+          (with-temp-buffer
+            (insert "(\n")
+            (unless (zerop (gited-git-command args (current-buffer)
+                                              nil 'unquote))
+              (error "No Git repository in current directory"))
+            (insert ")")
+            (mapcar (lambda (x)
+                      (when (stringp (car x)) ; No time: set it to beginning
+                                              ; of epoch.
+                        (push 0 x))
+                      (when (= (length x) 4) ; Group time an time zone within 
+                        (setf (car x) (cons (car x) (cadr x)))
+                        (setf (cdr x) (cddr x)))
+                      (when (and (stringp (car (last x))) ; If no Author, set
+                                                          ; it Unknown.
+                                 (string= "" (car (last x))))
+                        (setf (car (last x)) "Unknown"))
+                      x)
+                    (car (read-from-string (buffer-string))))))
+         (prep
+          (make-progress-reporter
+           "Collecting brach info..."
+           0 (length alist))))
+    (cl-flet ((format-time-fn (time-secs &optional zone)
+                              (let ((zone-ok (>= emacs-major-version 25)))
+                                (cond (zone-ok
+                                       (format-time-string
+                                        gited-date-format
+                                        (apply #'encode-time
+                                               (decode-time
+                                                (seconds-to-time time-secs) 
+                                        ;; FIXME: Would work for  a zone as: 
+0530 ?
+                                        (and zone (* 36 zone)))) 
+                                      (t ;; HACK: Workaround for Emacs 
versions < 25 that don't accept
+                                         ;; ZONE arg in functions like 
+                                         ;; or `format-time-string', where 
ZONE has less general meaning.
+                                       (let ((time (decode-time
+                                                    (seconds-to-time 
+                                             (gited-date-format
+                                              (if (string= gited-date-format 
"%F %R")
+                                                  "%F %R"
+                                                "%FT%T"))
+                                             date-str)
+                                         (when zone
+                                           (setf (car (last time)) (* 3600 
+                                         (setq date-str
+                                               (format-time-string
+                                                gited-date-format
+                                                (apply #'encode-time time)))
+                                         (when (and (not (string= 
gited-date-format "%F %R")) zone)
+                                           (if (= 0 zone)
+                                               (setq date-str (format 
"%s+0000" date-str))
+                                             (setq date-str (format "%s%s%s%d"
+                                                                    date-str
+                                                                    (if (> 
zone 0) "+" "-")
+                                                                    (if (> 
(abs zone) 999) "" "0")
+                                                                    (abs 
+                                         date-str)))))
+              (get-mark-fn (x)
+                           (let ((table
+                                  (save-excursion
+                                    (gited--goto-first-branch)
+                                    (ignore-errors (gited-get-mark)))))
+                             (if (and table (ignore-errors
+                                              (gited-goto-branch (nth 1 x))))
+                                 (cons (gited-get-mark) nil)
+                               '(" ")))))
+      ;; Get title of first commit for each listed branch.
+      (setq gited-branch-alist
+            (cl-loop for entry the elements of alist using (index idx) collect
+                     (progn
+                       (progress-reporter-update prep idx)
+                       (let* ((args (list "log"
+                                          (if gited-show-commit-hash
+                                              "--pretty=format:%h | %s"
+                                            "--pretty=format:%s")
+                                          (cadr entry) "-n1" "--"))
+                              (str (with-temp-buffer
+                                     (gited-git-command args
+                                                        (current-buffer) nil)
+                                     (buffer-string))))
+                         ;; Format time in seconds as `gited-date-format'.
+                         (setf (car entry) (format-time-fn (caar entry) (cdar 
+                         (append `(,(1+ idx)) (get-mark-fn entry)
+                                 entry `(,str)))))))
+    (progress-reporter-done prep)
+    gited-branch-alist))
+;;; Toggles.
+(defun gited-hide-details-update-invisibility-spec ()
+  (let ((col-names
+         (if gited-hide-details-mode
+             '("M" "Branches" "" "" "Last Commit")
+           '("M" "Authors" "Date" "Branches" "Last Commit")))
+        (col-sizes
+         (if gited-hide-details-mode
+             (list gited-mark-col-size gited-branch-col-size
+                   -1 -1 gited-commit-col-size)
+           (list gited-mark-col-size gited-author-col-size gited-date-col-size
+               gited-branch-col-size gited-commit-col-size))))
+    (gited--list-format-init col-names col-sizes)
+    (setq tabulated-list-format gited-list-format)
+    (funcall (if gited-hide-details-mode
+                 'add-to-invisibility-spec
+               'remove-from-invisibility-spec)
+             'gited-hide-details-author)
+    (funcall (if gited-hide-details-mode
+                 'add-to-invisibility-spec
+               'remove-from-invisibility-spec)
+             'gited-hide-details-date)
+    (tabulated-list-init-header)
+    (gited--update-padding (not gited-hide-details-mode))))
+(defun gited--update-padding (undo)
+  "Update columns padding after `gited-hide-details-mode'."
+  (let ((inhibit-read-only t)
+        (align-to (if undo
+                      (+ gited-mark-col-size
+                         gited-author-col-size
+                         gited-date-col-size
+                         gited-branch-col-size 4)
+                    (+ gited-mark-col-size gited-branch-col-size 2))))
+    (save-excursion
+      (gited-goto-first-branch)
+      (while (not (eobp))
+        (gited-move-to-end-of-branchname)
+        (skip-chars-backward " \t")
+        (forward-char 1)
+        (unless (= (current-column) align-to)
+          (put-text-property (point) (1+ (point))
+                             'display `(space :align-to ,align-to))
+          (delete-region (1+ (point))
+                         (gited-move-to-end-of-branchname)))
+        (forward-line 1))) nil))
+;;; Marked branches.
+(defun gited-map-lines (fn)
+  "Map FN on each Gited line."
+  (let (br-name mark)
+    (save-excursion
+      (gited--goto-first-branch)
+      (while (ignore-errors
+               (setq br-name (gited-get-branchname)
+                     mark  (gited-get-mark)))
+        (funcall fn br-name mark)
+        (forward-line)))))
+(defun gited-get-marked-branches (&optional marker)
+  "Return a list of branches currently marked."
+  (delq nil
+        (mapcar (lambda (e)
+                  (when (equal (string-to-char (cdr e))
+                               (or marker gited-marker-char))
+                    (car e)))
+                (gited-current-state-list))))
+(defun gited-current-state-list (&optional pos)
+  "Return a list like (BRANCH . MARK) of all branches in an Gited buffer.
+If POS is non-nil, return a list like (BRANCH MARK POINT), where POINT is
+the value of point at the beginning of the line for that buffer."
+  (let ((gited-current-state-list-tmp '()))
+    (if pos
+        (gited-map-lines
+         (lambda (branch mark)
+           (push (list branch mark (point))
+                 gited-current-state-list-tmp)))
+      (gited-map-lines
+       (lambda (branch mark)
+         (push (cons branch mark) gited-current-state-list-tmp))))
+    (nreverse gited-current-state-list-tmp)))
+(defun gited-current-branches-with-marks ()
+  "Return a list like (BRANCH . MARK) of all listed branches."
+  (let ((branches (gited-current-state-list)))
+    (mapcar (lambda (x)
+              (let ((e (assoc x branches)))
+                (if e
+                    e
+                  (cons x ?\s))))
+            (gited-listed-branches))))
+ ;;; Mark/unmark.
+(defun gited-remember-marks (beg end)
+  "Return alist of branches and their marks, from BEG to END."
+  (if selective-display         ; must unhide to make this work.
+      (let ((inhibit-read-only t))
+        (subst-char-in-region beg end ?\r ?\n)))
+  (let (branch chr alist)
+    (save-excursion
+      (goto-char beg)
+      (while (re-search-forward gited-re-mark end t)
+        (when (setq branch (gited-get-branchname))
+          (setq chr (preceding-char)
+                alist (cons (cons branch chr) alist)))))
+    alist))
+(defun gited-mark-remembered (alist)
+  "Mark all branches remembered in ALIST.
+Each element of ALIST looks like (BRANCH . MARKERCHAR)."
+  (let (elt branch chr)
+    (save-excursion
+      (while alist
+        (setq elt (car alist)
+              alist (cdr alist)
+              branch (car elt)
+              chr (cdr elt))
+        (when (gited-goto-branch branch)
+          (beginning-of-line)
+          (delete-char 1)
+          (insert chr))))))
+(defun gited-fontify-current-branch ()
+  "Set font for current branch."
+  (let ((inhibit-read-only t))
+    (save-excursion
+      (gited-goto-branch gited-current-branch)
+      (when (string= " " (gited-get-mark))
+        (remove-text-properties
+         (point-at-bol) (point-at-eol) '(face))
+        (put-text-property
+         (point)
+         (gited-move-to-end-of-branchname)
+         'face gited-current-branch-face)
+        (put-text-property
+         (point-at-bol)
+         (point-at-eol)
+         'face gited-section-highlight-face)))))
+(defun gited-fontify-marked-branch-name (&optional mark)
+  "Set font for a marked branch."
+  (let ((marker (or mark (string-to-char (gited-get-mark))))
+        (inhibit-read-only t))
+    (gited-move-to-branchname)
+    (remove-text-properties
+     (point-at-bol) (point-at-eol) '(face))
+    (cond ((eq marker ?\s)
+           (if (string= (gited-get-branchname)
+                        gited-current-branch)
+               (put-text-property
+                (point-at-bol)
+                (point-at-eol)
+                'face gited-section-highlight-face)
+             (gited--fontify-current-row)))
+          ((eq marker gited-marker-char)
+           (put-text-property
+            (point-at-bol) (1+ (point-at-bol)) 'face gited-flag-mark-face)
+           (put-text-property
+            (1+ (point-at-bol))
+            (point-at-eol)
+            'face gited-flag-mark-line-face))
+          ((eq marker gited-del-char)
+           (put-text-property
+            (point-at-bol) (1+ (point-at-bol)) 'face gited-deletion-face)
+           (put-text-property
+            (1+ (point-at-bol))
+            (point-at-eol)
+            'face gited-deletion-branch-face)))))
+(defun gited-insert-marker-char (&optional marker)
+  (tabulated-list-set-col gited-mark-idx
+                          (char-to-string (or marker gited-marker-char))
+                          'change))
+(defun gited-flag-branch-deletion (arg &optional interactive)
+  "In Gited, flag the branch at current line (or next ARG) for deletion.
+If the region is active, flag all branches in the region.
+Otherwise, with a prefix arg, flag branches on the next ARG lines.
+If the region is active in Transient Mark mode, flag all branches
+in the active region."
+  (interactive (list current-prefix-arg t))
+  (gited-mark arg gited-del-char interactive))
+(defun gited-toggle-marks ()
+  "Toggle marks: marked branches become unmarked, and vice versa.
+Branches marked with other flags (such as `D') are not affected.
+As always, hidden subdirs are not affected."
+  (interactive)
+  (save-excursion
+    (gited--goto-first-branch)
+    (while (not (eobp))
+      (let* ((mark (string-to-char (gited-get-mark)))
+             (flag
+              (cond ((eq ?\s mark) gited-marker-char)
+                    ((eq gited-marker-char mark) ?\s)
+                    (t nil))))
+        (when flag
+          (gited-insert-marker-char flag)
+          (gited-fontify-marked-branch-name flag)))
+      (forward-line))))
+(defun gited-kill-line (&optional arg)
+  "Kill the current line or next ARG lines (not the branches).
+With a prefix argument, kill that many lines starting with the current line.
+\(A negative argument kills backward.)"
+  (interactive "P")
+  (setq arg (prefix-numeric-value arg))
+  (let (buffer-read-only branch)
+    (while (/= 0 arg)
+      (setq branch (gited-get-branchname))
+      (if (not branch)
+          (error "Can only kill branch lines")
+        (setq tabulated-list-entries
+              (assq-delete-all
+               (car (tabulated-list-delete-entry))
+               tabulated-list-entries))
+        (if (> arg 0)
+            (cl-decf arg)
+          (cl-incf arg)
+          (forward-line -1))))
+    (gited-move-to-branchname)))
+(defun gited-do-kill-lines (&optional arg fmt)
+  "Kill all marked lines (not the branches).
+With a prefix argument, kill that many lines starting with the current line.
+\(A negative argument kills backward.)"
+  ;; Returns count of killed lines.  FMT="" suppresses message.
+  (interactive "P")
+  (if arg
+      (gited-kill-line arg)
+    (save-excursion
+      (gited--goto-first-branch)
+      (let (buffer-read-only
+            (count 0)
+            (regexp
+             (concat "^" (regexp-quote (char-to-string gited-marker-char)))))
+        (while (and (not (eobp))
+                    (re-search-forward regexp nil t))
+          (cl-incf count)
+          (setq tabulated-list-entries
+                (assq-delete-all
+                 (car (tabulated-list-delete-entry))
+                 tabulated-list-entries)))
+        (or (equal "" fmt)
+            (message (or fmt "Killed %d line%s.") count (gited-plural-s 
+        count))))
+(defun gited-mark-branches-regexp (regexp &optional marker-char)
+  "Mark all files matching REGEXP for use in later commands.
+A prefix argument means to unmark them instead.
+REGEXP is an Emacs regexp, not a shell wildcard.  Thus, use `\\.o$' for
+object files--just `.o' will mark more than you might think."
+  (interactive
+   (list (read-regexp (concat (if current-prefix-arg "Unmark" "Mark")
+                              " branches (regexp): ")
+                      nil)
+         (and current-prefix-arg ?\s)))
+  (let ((gited-marker-char (or marker-char gited-marker-char)))
+    (gited-mark-if
+     (and (not (eolp))          ; empty line
+          (let ((fn (gited-get-branchname)))
+            (and fn (string-match-p regexp fn))))
+     "matching branch")))
+(defun gited-mark-branches-containing-regexp (regexp &optional marker-char 
+  "Mark all branches containing REGEXP in some commit message.
+A prefix argument means to unmark them instead.
+Optional arg DAYS, if non-nil then limit the search to DAYS before the
+newest commit.  Otherwise, limit the search to commits until 1 month earlier
+than the newest commit.
+In interactive calls, a prefix C-u C-u prompts for DAYS."
+  (interactive
+   (let ((def current-prefix-arg)
+         regex marker interval)
+     (pcase def
+       (`(16) (setq interval
+                    (string-to-number
+                     (read-string "Number of days before newest commit: "
+                                  nil nil "30"))
+                    marker (pcase (read-number "Mark (0) or Unmark (1): " 0)
+                             (0 gited-marker-char)
+                             (1 ?\s)
+                             (_ gited-marker-char))
+                    regex (read-string
+                           (format "%s branches containing regexp: "
+                                   (if (char-equal marker gited-marker-char)
+                                       "Mark" "UNmark")))))
+       (_ (setq interval 30
+                marker (if current-prefix-arg
+                           ?\s
+                         gited-marker-char)
+                regex (read-string
+                       (format "%s branches containing regexp: "
+                               (if (char-equal marker gited-marker-char)
+                                   "Mark" "UNmark"))))))
+     (list regex marker interval)))
+  (let ((gited-marker-char (or marker-char gited-marker-char)))
+    (gited-mark-if
+     (and (not (eolp))
+          (gited-get-branchname)
+          (let* ((fn (gited-get-branchname))
+                 (time-max
+                 (date-to-time
+                  (car
+                   (cddr
+                    (cl-find-if
+                     (lambda (x)
+                       (string= (nth 3 x) fn)) gited-branch-alist)))))
+                 (time-min (time-subtract time-max (days-to-time (or days 
+                 (args (list "log"
+                             (format "--after=%s"
+                                     (format-time-string "%F" time-min))
+                             (format "--before=%s"
+                                     (format-time-string "%F" time-max))
+                             (format "--grep=%s" regexp)
+                             fn "--")))
+            (with-temp-buffer
+              (gited-git-command args (current-buffer))
+              (not (string= "" (buffer-string))))))
+     "matching branch")))
+(defun gited-mark-branches-containing-commit (commit &optional marker-char)
+  "Mark all branches containing COMMIT.
+A prefix argument means to unmark them instead.
+COMMIT is the sha1 of the commit."
+  (interactive
+   (list (read-string (format "%s branches containing sha1 commit: "
+                              (if current-prefix-arg "UNmark" "Mark")))
+         (and current-prefix-arg ?\s)))
+  (let* ((args `("branch" ,(concat "--contains=" commit)))
+         (branches (gited--get-branches-from-command args))
+         (gited-marker-char (or marker-char gited-marker-char)))
+    (gited-mark-if
+     (and (not (eolp))
+          (let ((fn (gited-get-branchname)))
+            (and fn (member fn branches))))
+     "matching branch")))
+(defvar gited--op nil)
+(defun gited--mark-merged-or-unmerged-branches-spec (op-val)
+  (setq gited--op op-val)
+  (read-string (format "%s branches %s with branch: "
+                       (if current-prefix-arg "UNmark" "Mark")
+                       (pcase gited--op
+                         (`merged "merged")
+                         (_ "unmerged")))
+               nil nil gited-current-branch))
+(defun gited--mark-merged-branches-spec (branch prefix)
+  (interactive "i\nP")
+  (ignore branch prefix)
+  (list
+   (gited--mark-merged-or-unmerged-branches-spec 'merged)
+   (if current-prefix-arg ?\s gited-marker-char)))
+(defun gited--mark-unmerged-branches-spec (branch prefix)
+  (interactive "i\nP")
+  (ignore branch prefix)
+  (list
+   (gited--mark-merged-or-unmerged-branches-spec 'unmerged)
+   (if current-prefix-arg ?\s gited-marker-char)))
+(defun gited--mark-merged-or-unmerged-branches (branch op marker-char)
+  (let* ((args
+          (list "branch"
+                (if (string= op "merged") "--merged" "--no-merged")
+                branch))
+         (branches (gited--get-branches-from-command args))
+         (gited-marker-char (or marker-char gited-marker-char)))
+    (gited-mark-if
+     (and (not (eolp))
+          (let ((fn (gited-get-branchname)))
+            (and fn (member fn branches))))
+     "matching branch")))
+(defun gited-mark-merged-branches (branch &optional marker-char)
+  "Mark all merged branches with BRANCH.
+A prefix argument means to unmark them instead."
+  (interactive
+   (call-interactively #'gited--mark-merged-branches-spec))
+  (gited--mark-merged-or-unmerged-branches branch "merged" marker-char))
+(defun gited-mark-unmerged-branches (branch &optional marker-char)
+  "Mark all unmerged branches with BRANCH.
+A prefix argument means to unmark them instead."
+  (interactive
+   (call-interactively #'gited--mark-unmerged-branches-spec))
+  (gited--mark-merged-or-unmerged-branches branch "unmerged" marker-char))
+(defun gited-repeat-over-lines (arg function)
+  ;; This version skips non-file lines.
+  (let ((pos (make-marker)))
+    (beginning-of-line)
+    (while (and (> arg 0) (not (eobp)))
+      (cl-decf arg)
+      (beginning-of-line)
+      (when (gited-at-header-line-p) (forward-line))
+      (save-excursion
+        (forward-line)
+        (move-marker pos (1+ (point))))
+      (save-excursion (funcall function))
+      ;; Advance to the next line--actually, to the line that *was* next.
+      ;; (If FUNCTION inserted some new lines in between, skip them.)
+      (goto-char pos))
+    (while (and (< arg 0) (not (bobp)))
+      (cl-incf arg)
+      (forward-line -1)
+      (while (not (gited-at-header-line-p)) (forward-line -1))
+      (beginning-of-line)
+      (save-excursion (funcall function)))
+    (move-marker pos nil)
+    (ignore-errors (gited-move-to-branchname))
+    (when (eobp)
+      (forward-line -1)
+      (ignore-errors (gited-move-to-branchname)))))
+(defun gited-mark (arg mark &optional interactive)
+  "Mark the branch at point in the Gited buffer with MARK.
+If the region is active, mark all branches in the region.
+Otherwise, with a prefix arg, mark branches on the next ARG lines.
+Use \\[gited-unmark-all-branches] to remove all marks
+and \\[gited-unmark] on a subdir to remove the marks in
+this subdir."
+  (interactive (list current-prefix-arg gited-marker-char t))
+  (cond
+   ;; Mark branches in the active region.
+   ((and interactive (use-region-p))
+    (save-excursion
+      (let ((beg (region-beginning))
+            (end (region-end)))
+        (gited--mark-branches-in-region
+         (progn (goto-char beg) (point-at-bol))
+         (progn (goto-char end) (point-at-bol))
+         mark))))
+   ;; Mark the current (or next ARG) branches.
+   (t
+    (let ((inhibit-read-only t))
+      (gited-repeat-over-lines
+       (prefix-numeric-value arg)
+       (lambda ()
+         (when (ignore-errors (gited-get-branchname))
+           (gited-insert-marker-char mark)
+           (gited-fontify-marked-branch-name mark))))))))
+(defun gited--mark-branches-in-region (start end mark)
+  (when (> start end)
+    (error "Wrong input values: start, end, <"))
+  (goto-char start) ; assumed at beginning of line
+  (while (< (point) end)
+    (when (gited-get-branchname)
+      (gited-insert-marker-char mark)
+      (gited-fontify-marked-branch-name mark))
+    (forward-line)))
+(defun gited-unmark-backward ()
+  "Unmark the branches in the region, or ARG branches."
+  (interactive)
+  (forward-line -1)
+  (when (gited-get-branchname)
+    (gited-insert-marker-char ?\s)
+    (gited-fontify-marked-branch-name ?\s)
+    (gited-move-to-branchname)))
+(defun gited-unmark-all-branches (mark)
+  "Remove all branches with flag MARK."
+  (interactive "cRemove marks (RET means all):")
+  (save-excursion
+    (gited--goto-first-branch)
+    (while (not (eobp))
+      (let ((str (aref (tabulated-list-get-entry)
+                       gited-mark-idx)))
+        (when (and (gited-get-branchname)
+                   (or (and (eq mark ?\r) (not (string= str " ")))
+                       (string= (char-to-string mark) str)))
+          (gited-insert-marker-char ?\s)
+          (gited-fontify-marked-branch-name ?\s))
+        (forward-line)))))
+(defun gited-unmark-all-marks ()
+  "Remove all marks from all marked branches in the Gited buffer."
+  (interactive)
+  (gited-unmark-all-branches ?\r))
+(defun gited-move-to-branchname ()
+  (interactive)
+  (gited--move-to-column (1+ gited-branch-idx)))
+;; Return point.
+(defun gited-move-to-end-of-branchname ()
+  (interactive)
+  (gited--move-to-end-of-column (1+ gited-branch-idx)))
+(defun gited-move-to-author ()
+  (interactive)
+  (gited--move-to-column (1+ gited-author-idx)))
+(defun gited-move-to-end-of-author ()
+  (interactive)
+  (gited--move-to-end-of-column (1+ gited-author-idx)))
+(defun gited-move-to-date ()
+  (interactive)
+  (gited--move-to-column (1+ gited-date-idx)))
+(defun gited-move-to-end-of-date ()
+  (interactive)
+  (gited--move-to-end-of-column (1+ gited-date-idx)))
+(defun gited-unmark (arg &optional interactive)
+  "Unmark the branch at point in the Gited buffer.
+If the region is active, unmark all branches in the region.
+Otherwise, with a prefix arg, unmark branches on the next ARG lines.
+If the region is active in Transient Mark mode, unmark all branches
+in the active region."
+  (interactive (list current-prefix-arg t))
+  (gited-mark arg ?\s interactive))
+(defun gited-number-marked ()
+  "Return number of marked files."
+  (interactive)
+  (save-excursion
+    (goto-char (point-min))
+    (let ((count 0))
+      (while (not (eobp))
+        (unless (looking-at "^[[:blank:]]")
+          (cl-incf count))
+        (forward-line))
+      (prog1 count
+        (if (zerop count)
+            (message "No marked branches")
+          (message "%d marked %s"
+                   count
+                   (if (> count 1)
+                       "branches" "branch")))))))
+;;; Mode map.
+(defvar gited-mode-map
+  (let ((map (make-keymap)))
+    (define-key map (kbd "t") 'gited-toggle-marks)
+    (define-key map (kbd "(") 'gited-hide-details-mode)
+    (define-key map (kbd "u") 'gited-unmark)
+    (define-key map (kbd "=") 'gited-diff)
+    (define-key map (kbd "j") 'gited-goto-branch)
+    (define-key map (kbd "DEL") 'gited-unmark-backward)
+    (define-key map (kbd "U") 'gited-unmark-all-marks)
+    (define-key map (kbd "M-DEL") 'gited-unmark-all-branches)
+    (define-key map (kbd "x") 'gited-do-flagged-delete)
+    ;; moving
+    (define-key map (kbd "<") 'gited-goto-first-branch)
+    (define-key map (kbd ">") 'gited-goto-last-branch)
+    (define-key map (kbd "M-<") 'gited-goto-first-branch)
+    (define-key map (kbd "M->") 'gited-goto-last-branch)
+    (define-key map (kbd "n") 'gited-next-line)
+    (define-key map (kbd "SPC") 'next-line)
+    (define-key map (kbd "p") 'gited-prev-line)
+    (define-key map (kbd "M-}") 'gited-next-marked-branch)
+    (define-key map (kbd "M-{") 'gited-prev-marked-branch)
+    ;; immediate operations
+    (define-key map (kbd "a") 'gited-apply-patch)
+    (define-key map (kbd "A") 'gited-add-patched-files)
+    (define-key map (kbd "B") 'gited-bisect)
+    (define-key map (kbd "C-c c") 'gited-commit)
+    (define-key map (kbd "w") 'gited-copy-branchname-as-kill)
+    (define-key map (kbd "e") 'gited-extract-patches)
+    (define-key map (kbd "T") 'gited-sync-with-trunk)
+    (define-key map (kbd "M") 'gited-merge-branch)
+    (define-key map (kbd "c") 'gited-checkout-branch)
+    (define-key map (kbd "v") 'gited-visit-branch-sources)
+    (define-key map (kbd "f") 'gited-visit-branch-sources)
+    (define-key map (kbd "s") 'gited-show-commit)
+    (define-key map (kbd "S") 'gited-status)
+    (define-key map (kbd "RET") 'gited-visit-branch-sources)
+    (define-key map (kbd "g") 'gited-update)
+    (define-key map "\C-x\C-g" 'gited-list-branches)
+    (define-key map (kbd "k") 'gited-do-kill-lines)
+    (define-key map (kbd "P") 'gited-pull)
+    (define-key map (kbd "r") 'gited-reset-branch)
+    (define-key map (kbd "* p") 'gited-set-branch-upstream)
+    (define-key map (kbd "* <") 'gited-pull)
+    (define-key map (kbd "* >") 'gited-push)
+    (define-key map (kbd "o") 'gited-origin)
+    (define-key map (kbd "l") 'gited-log)
+    (define-key map (kbd "L") 'gited-log-last-n-commits)
+    ;; marking banches
+    (define-key map (kbd "m") 'gited-mark)
+    (define-key map (kbd "% n") 'gited-mark-branches-regexp)
+    (define-key map (kbd "% c") 'gited-mark-branches-containing-commit)
+    (define-key map (kbd "% g") 'gited-mark-branches-containing-regexp)
+    (define-key map (kbd "% m") 'gited-mark-merged-branches)
+    (define-key map (kbd "% M") 'gited-mark-unmerged-branches)
+    (define-key map (kbd "d") 'gited-flag-branch-deletion)
+    ;; Git stash
+    (define-key map (kbd "* s") 'gited-stash)
+    (define-key map (kbd "* a") 'gited-stash-apply)
+    (define-key map (kbd "* A") 'gited-stash-pop)
+    (define-key map (kbd "* b") 'gited-stash-branch)
+    (define-key map (kbd "* d") 'gited-stash-drop)
+    ;; marked operations
+    (define-key map (kbd "* N") 'gited-number-marked)
+    (define-key map (kbd "R") 'gited-rename-branch)
+    (define-key map (kbd "C") 'gited-copy-branch)
+    (define-key map (kbd "D") 'gited-do-delete)
+    (define-key map (kbd "?") 'gited-summary)
+    map))
+(defun gited-list-branches (&optional pattern other-window update)
+  "List all branches with the time of its last commit."
+  (interactive
+   (progn
+     (unless (gited-dir-under-Git-control-p)
+       (error "No Git repository in current directory"))
+     (let* ((opts '("local" "remote" "tags"))
+            (patt (completing-read
+                   "List (local, remote, tags): "
+                   opts nil t nil nil "local")))
+       (list patt current-prefix-arg nil))))
+  (if (and (buffer-live-p gited-buffer)
+           (not update)
+           (or (not pattern)
+               (equal pattern gited-ref-kind)))
+      (switch-to-buffer gited-buffer)
+    (unless (gited-dir-under-Git-control-p)
+      (error "No Git repository in current directory"))
+    (let ((buf (or (and (buffer-live-p gited-buffer) gited-buffer)
+                   (setq gited-buffer (generate-new-buffer 
+      (unless (equal pattern gited-ref-kind)
+        (setq gited-ref-kind pattern))
+      (if other-window
+          (switch-to-buffer-other-window buf)
+        (switch-to-buffer buf))
+      (or gited-mode (gited-mode))
+      ;; Set `gited-toplevel-dir' if not set yet.
+      (unless gited-toplevel-dir
+        (setq gited-toplevel-dir
+              (with-temp-buffer
+                (gited-git-command '("rev-parse" "--show-toplevel")
+                                   (current-buffer))
+                (file-name-as-directory
+                 (buffer-substring 1 (1- (point-max)))))))
+      (setq tabulated-list-use-header-line gited-use-header-line
+            gited-buffer buf
+            gited-ref-kind pattern
+            gited-current-branch (gited-current-branch)
+            tabulated-list-printer #'gited-print-entry)
+      ;; Ignore dired-hide-details-* value of invisible text property by 
+      (when (eq buffer-invisibility-spec t)
+        (setq buffer-invisibility-spec (list t)))
+      (gited-tabulated-list-entries)
+      (tabulated-list-print)
+      (gited-goto-branch gited-current-branch)
+      (gited-fontify-current-branch)
+      (unless gited--hide-details-set
+        (or gited-verbose (gited-hide-details-mode 1))
+        (setq gited--hide-details-set t)))))
+(defun gited-print-entry (id cols)
+  "Insert a Gited entry at point.
+ID is a Lisp object identifying the entry to print, and COLS is a vector
+of column descriptors."
+  (let ((beg   (point))
+        (x     (max tabulated-list-padding 0))
+        (ncols (length tabulated-list-format))
+        (inhibit-read-only t))
+    (if (> tabulated-list-padding 0)
+        (insert (make-string x ?\s)))
+    (dotimes (n ncols)
+      (let ((pos (point)))
+        (setq x (tabulated-list-print-col n (aref cols n) x))
+        (cond
+         ((= n gited-author-idx)
+          (add-text-properties
+           pos (point)
+           `(invisible gited-hide-details-author
+                       font-lock-face ,gited-author-face)))
+         ((= n gited-date-idx)
+          (add-text-properties
+           pos (point)
+           `(invisible gited-hide-details-date
+                       font-lock-face ,gited-date-time-face)))
+         ((= n gited-branch-idx)
+          (put-text-property
+           pos (point)
+           'font-lock-face gited-branch-name-face))
+         ((= n gited-commit-idx)
+          (put-text-property
+           pos (point)
+           'font-lock-face gited-commit-msg-face))
+         (t nil))
+        ))
+    (insert ?\n)
+    ;; Ever so slightly faster than calling `put-text-property' twice.
+    (add-text-properties
+     beg (point)
+     `(tabulated-list-id ,id tabulated-list-entry ,cols))))
+(defun gited-tabulated-list-entries ()
+  (let ((alist (gited--fill-branch-alist gited-ref-kind))
+        mark author date branch commit id values result)
+    (while alist
+      (setq id (caar alist)
+            values (cdr (car alist))
+            mark (nth 0 values)
+            author (nth 3 values)
+            date (nth 1 values)
+            branch (nth 2 values)
+            commit (nth 4 values)
+            alist (cdr alist))
+      (push (list id (vector mark author date branch commit)) result))
+    (setq tabulated-list-entries (nreverse result))
+    (tabulated-list-init-header)))
+;;; Define minor mode.
+(define-derived-mode gited-mode tabulated-list-mode "Gited"
+  "Toggle gited-mode.
+Interactively with no argument, this command toggles the mode.
+A positive prefix argument enables the mode, any other prefix
+argument disables it.  From Lisp, argument omitted or nil enables
+the mode, `toggle' toggles the state.
+Mode to edit Git branches as Dired."
+  (unless (gited-buffer-p)
+    (error "Gited mode cannot be enabled in this buffer"))
+  (gited--list-format-init)
+  (setq tabulated-list-format gited-list-format)
+  (add-hook 'tabulated-list-revert-hook 'gited-tabulated-list-entries nil t)
+  (setq tabulated-list-sort-key '("Date")))
+(provide 'gited)
+;;; gited.el ends here

reply via email to

[Prev in Thread] Current Thread [Next in Thread]