From a6f973f6c2ec6a518927cd42a9bf769b9a67b818 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Fri, 11 Dec 2009 17:03:09 +0100 Subject: added magit for emacs (better git handling) --- emacs.d/lisp/magit/.gitignore | 37 + emacs.d/lisp/magit/50magit.el | 3 + emacs.d/lisp/magit/AUTHORS | 6 + emacs.d/lisp/magit/COPYING | 676 ++++++++ emacs.d/lisp/magit/ChangeLog | 1 + emacs.d/lisp/magit/Makefile.am | 24 + emacs.d/lisp/magit/NEWS | 93 ++ emacs.d/lisp/magit/README | 44 + emacs.d/lisp/magit/autogen.sh | 2 + emacs.d/lisp/magit/configure.ac | 8 + emacs.d/lisp/magit/fdl.texi | 451 +++++ emacs.d/lisp/magit/magit-pkg.el.in | 1 + emacs.d/lisp/magit/magit.el | 3244 ++++++++++++++++++++++++++++++++++++ emacs.d/lisp/magit/magit.texi | 639 +++++++ emacs.d/lisp/magit/test/BAR.2 | 1 + emacs.d/lisp/magit/test/FOO.2 | 1 + 16 files changed, 5231 insertions(+) create mode 100644 emacs.d/lisp/magit/.gitignore create mode 100644 emacs.d/lisp/magit/50magit.el create mode 100644 emacs.d/lisp/magit/AUTHORS create mode 100644 emacs.d/lisp/magit/COPYING create mode 100644 emacs.d/lisp/magit/ChangeLog create mode 100644 emacs.d/lisp/magit/Makefile.am create mode 100644 emacs.d/lisp/magit/NEWS create mode 100644 emacs.d/lisp/magit/README create mode 100755 emacs.d/lisp/magit/autogen.sh create mode 100644 emacs.d/lisp/magit/configure.ac create mode 100644 emacs.d/lisp/magit/fdl.texi create mode 100644 emacs.d/lisp/magit/magit-pkg.el.in create mode 100644 emacs.d/lisp/magit/magit.el create mode 100644 emacs.d/lisp/magit/magit.texi create mode 100644 emacs.d/lisp/magit/test/BAR.2 create mode 100644 emacs.d/lisp/magit/test/FOO.2 (limited to 'emacs.d/lisp/magit') diff --git a/emacs.d/lisp/magit/.gitignore b/emacs.d/lisp/magit/.gitignore new file mode 100644 index 0000000..aee86f2 --- /dev/null +++ b/emacs.d/lisp/magit/.gitignore @@ -0,0 +1,37 @@ +/INSTALL +/Makefile +/Makefile.in +/aclocal.m4 +/build-stamp +/config.log +/config.status +/configure +/debian/debhelper.log +/debian/files +/debian/magit.debhelper.log +/debian/magit.postinst.debhelper +/debian/magit.prerm.debhelper +/install-sh +/install-stamp +/magit-0.5.tar.gz +/magit.info +/missing +/texinfo.tex +/debian/magit/ +/autom4te.cache/ +/magit-0.6.tar.gz +*.elc +*~ +/magit.aux +/magit.cp +/magit.dvi +/magit.fn +/magit.ky +/magit.log +/magit.pg +/magit.toc +/magit.tp +/magit.vr +/magit.pdf +/magit.ps +/magit-pkg.el diff --git a/emacs.d/lisp/magit/50magit.el b/emacs.d/lisp/magit/50magit.el new file mode 100644 index 0000000..b04034d --- /dev/null +++ b/emacs.d/lisp/magit/50magit.el @@ -0,0 +1,3 @@ +;;; Autoloads for magit + +(autoload 'magit-status "magit" nil t) diff --git a/emacs.d/lisp/magit/AUTHORS b/emacs.d/lisp/magit/AUTHORS new file mode 100644 index 0000000..af133ff --- /dev/null +++ b/emacs.d/lisp/magit/AUTHORS @@ -0,0 +1,6 @@ +Marius Vollmer +Linh Dang +Alex Ott +Marcin Bachry +Alexey Voinov diff --git a/emacs.d/lisp/magit/COPYING b/emacs.d/lisp/magit/COPYING new file mode 100644 index 0000000..4432540 --- /dev/null +++ b/emacs.d/lisp/magit/COPYING @@ -0,0 +1,676 @@ + + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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 +Source. + + 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 +measures. + + 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. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 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. + + + Copyright (C) + + 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 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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 . + +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: + + Copyright (C) + 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/emacs.d/lisp/magit/ChangeLog b/emacs.d/lisp/magit/ChangeLog new file mode 100644 index 0000000..d65ee7e --- /dev/null +++ b/emacs.d/lisp/magit/ChangeLog @@ -0,0 +1 @@ +There is no ChangeLog. diff --git a/emacs.d/lisp/magit/Makefile.am b/emacs.d/lisp/magit/Makefile.am new file mode 100644 index 0000000..8ae4192 --- /dev/null +++ b/emacs.d/lisp/magit/Makefile.am @@ -0,0 +1,24 @@ +lispdir = $(datadir)/emacs/site-lisp +sitestartdir = $(sysconfdir)/emacs/site-start.d + +lisp_DATA = magit.el magit.elc +sitestart_DATA = 50magit.el + +info_TEXINFOS = magit.texi + +CLEANFILES = magit.elc +EXTRA_DIST = magit.el 50magit.el + +%.elc: %.el + @if [ $(builddir) != $(srcdir) ]; then ln $(srcdir)/$*.el .; fi + emacs --batch --eval '(byte-compile-file "$*.el")' + @if [ $(builddir) != $(srcdir) ]; then rm -f $*.el; fi + + +elpa: magit-pkg.el info + -@rm -rf magit-$(VERSION) + mkdir magit-$(VERSION) + cp magit.el magit-pkg.el magit.info magit-$(VERSION) + (cd magit-$(VERSION); ginstall-info --dir-file=dir magit.info) + tar cf magit-$(VERSION).tar magit-$(VERSION) + @rm -rf magit-$(VERSION) diff --git a/emacs.d/lisp/magit/NEWS b/emacs.d/lisp/magit/NEWS new file mode 100644 index 0000000..9906dac --- /dev/null +++ b/emacs.d/lisp/magit/NEWS @@ -0,0 +1,93 @@ +Changes in magit 0.8: + +* By setting magit-repo-dirs, you can get better repo completion. + Magit will offer all subdirectories (upto magit-repo-dirs level + deep) of the listed directories when magit-status asks for a + directory to work on. + + You can get the old behavior with a double prefix to magit-status. + +* Hitting 'c' or 'C' while resolving a conflict in the middle of a + rebase will offer to continue the rebase instead of trying to commit + your changes. + +* Pulling will ask which branch to pull from when you don't have a + default branch configured. + +* Switching to a remote branch will offer to create a local tracking + branch for it. + +* Hitting C-c C-s while editing a commit message will toggle the + "--signoff" option. + +* Hitting 's' on the "Untracked files" section title will stage all + untracked files. + +* Hitting 'C-u S' will stage all untracked and tracked files. + +* Performance improvements in the status buffer. + +* Bug fixes to make Magit work better with newer Gits. + +Changes in magit 0.7: + +* Tagging, on 't' and 'T'. + +* Stashing, on 'z' and 'Z'. + +* Wazzup, on 'w'. Wazzup gives you an overview over how other + branches relate to the current one. + +* There is more control over pushing. 'P' now takes a prefix argument + and pushing a branch without a default remote will ask for one. + +* Logs have changed a bit: 'l' shows the traditional brief log, and + 'L' shows a more verbose log. Use the prefix arg to specify the + range of the log. + +* M-x magit-status doesn't prompt anymore for a directory when invoked + from within a Git repository. Use C-u to force a prompt. + +* When you have nothing staged, 'c' will now explicitly ask whether to + commit everything instead of just going ahead and do it. This can + be customized. + +* The digit keys '1', '2', '3', and '4' now show sections on the + respective level and hide everything below. With Meta, they work on + all sections; without, they work only on sections that are a parent + or child of the current section. + +* Typing '+' and '-' will change the size of hunks, via the "-U" + option to git diff. '0' resets hunks to their default size. + +* Typing 'k' on the "Untracked files" section title will offer to + delete all untracked files. + +* Magit understands a bit of git-svn: the status buffer shows unpushed + and unpulled commits, 'N r' runs git svn rebase, and 'N c' runs git + svn dcommit. + +* Magit now also works when the direcory is accessed via tramp. + +* M-x magit-status can also create new repositories when given a + directory that is not a Git repository. + +* Magit works better with oldish Gits that don't understand "--graph", + for example. + +* The name of the Git program and common options for it can be + customized. + +Changes in magit 0.6: + +Almost everything has changed. Please read the manual again. Some +highlights: + +* Magit now works with Git 1.6.0. (John Wiegley) + +* Support for interactive rewriting. + +* Sections can be shown and hidden. + +* Staging, unstaging, applying, reverting and discarding changes can + now be done line-by-line, not only hunk-by-hunk. diff --git a/emacs.d/lisp/magit/README b/emacs.d/lisp/magit/README new file mode 100644 index 0000000..b3e41d3 --- /dev/null +++ b/emacs.d/lisp/magit/README @@ -0,0 +1,44 @@ +It's Magit! A Emacs mode for Git. + +I started to write Magit to learn about Git and to figure out how I +would be using Git in a 'natural' way. Magit will grow and hopefully +become more coherent as I learn more about Git and good ways to use +it. Feedback is welcome! + +* Installing + +Magit can be installed with the popular recipe of + + $ ./autogen.sh # If you got the sources directly from Git + $ ./configure + $ make install + +This will put magit.el into /usr/local/share/emacs/site-lisp, where +Emacs should be able to find it. Then add + + (require 'magit) + +to your .emacs file. + +* Getting started + +To get started with Magit, open any file in a Git repository in Emacs +and run 'M-x magit-status'. Read the online help of magit-mode ('C-h +m' in the Magit buffer), make some changes to your files, and try to +commit them. + +* Learning more + +The Magit User Manual describes things with more words than the online +help. You can read it in Emacs with 'C-u C-h i magit.info' for +example, or on the web at + + http://zagadka.vm.bytemark.co.uk/magit/magit.html + +If you have questions, please use the mailing list at + + http://groups.google.com/group/magit/ + +Magit's web home is currently at + + http://zagadka.vm.bytemark.co.uk/magit/ diff --git a/emacs.d/lisp/magit/autogen.sh b/emacs.d/lisp/magit/autogen.sh new file mode 100755 index 0000000..f586638 --- /dev/null +++ b/emacs.d/lisp/magit/autogen.sh @@ -0,0 +1,2 @@ +#! /bin/sh +autoreconf --install diff --git a/emacs.d/lisp/magit/configure.ac b/emacs.d/lisp/magit/configure.ac new file mode 100644 index 0000000..da592b9 --- /dev/null +++ b/emacs.d/lisp/magit/configure.ac @@ -0,0 +1,8 @@ +AC_INIT(magit, 0.8) +AC_CONFIG_SRCDIR([magit.el]) +AM_INIT_AUTOMAKE + +AC_CONFIG_FILES([Makefile + magit-pkg.el]) + +AC_OUTPUT diff --git a/emacs.d/lisp/magit/fdl.texi b/emacs.d/lisp/magit/fdl.texi new file mode 100644 index 0000000..96ce74e --- /dev/null +++ b/emacs.d/lisp/magit/fdl.texi @@ -0,0 +1,451 @@ +@c The GNU Free Documentation License. +@center Version 1.2, November 2002 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 2000,2001,2002 Free Software Foundation, Inc. +51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document is void, and will +automatically terminate your rights under this License. However, +parties who have received copies, or rights, from you under this +License will not have their licenses terminated so long as such +parties remain in full compliance. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with@dots{}Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/emacs.d/lisp/magit/magit-pkg.el.in b/emacs.d/lisp/magit/magit-pkg.el.in new file mode 100644 index 0000000..59335e5 --- /dev/null +++ b/emacs.d/lisp/magit/magit-pkg.el.in @@ -0,0 +1 @@ +(define-package "magit" "@VERSION@" "Control Git from Emacs.") diff --git a/emacs.d/lisp/magit/magit.el b/emacs.d/lisp/magit/magit.el new file mode 100644 index 0000000..bfbfe9b --- /dev/null +++ b/emacs.d/lisp/magit/magit.el @@ -0,0 +1,3244 @@ +;;; Magit -- control Git from Emacs. + +;; Copyright (C) 2008, 2009 Marius Vollmer +;; Copyright (C) 2008 Linh Dang +;; Copyright (C) 2008 Alex Ott +;; Copyright (C) 2008 Marcin Bachry +;; Copyright (C) 2009 Alexey Voinov +;; Copyright (C) 2009 John Wiegley +;; +;; Magit is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 3, or (at your option) +;; any later version. +;; +;; Magit is distributed in the hope that it will be useful, but WITHOUT +;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +;; License for more details. +;; +;; You should have received a copy of the GNU General Public License +;; along with Magit. If not, see . + +;;; Commentary + +;; Invoking the magit-status function will show a buffer with the +;; status of the current git repository and its working tree. That +;; buffer offers key bindings for manipulating the status in simple +;; ways. +;; +;; The status buffer mainly shows the difference between the working +;; tree and the index, and the difference between the index and the +;; current HEAD. You can add individual hunks from the working tree +;; to the index, and you can commit the index. +;; +;; See the Magit User Manual for more information. + +;;; TODO + +;; For 0.8: +;; +;; - Fix display of unmerged files. +;; - Fix performance problems with large status buffers. +;; - Handle the case where remote and local branches have different names. +;; +;; Later: +;; +;; - Queuing of asynchronous commands. +;; - Good email integration. +;; - Showing tags. +;; - Visiting from staged hunks doesn't always work since the line +;; numbers don't refer to the working tree. Fix that somehow. +;; - Figure out how to discard staged changes for files that also have +;; unstaged changes. +;; - Get current defun from removed lines in a diff +;; - Amending commits other than HEAD. +;; - 'Subsetting', only looking at a subset of all files. + +(require 'cl) +(require 'parse-time) +(require 'log-edit) +(require 'easymenu) +(require 'diff-mode) + +(defgroup magit nil + "Controlling Git from Emacs." + :prefix "magit-" + :group 'tools) + +(defcustom magit-git-executable "git" + "The name of the Git executable." + :group 'magit + :type 'string) + +(defcustom magit-git-standard-options '("--no-pager") + "Standard options when running Git." + :group 'magit + :type '(repeat string)) + +(defcustom magit-repo-dirs nil + "Directories containing Git repositories. +Magit will look into these directories for Git repositories and offers them as choices for magit-status." + :group 'magit + :type '(repeat string)) + +(defcustom magit-repo-dirs-depth 3 + "When looking for Git repositors below the directories in magit-repo-dirs, Magit will only descend this many levels deep." + :group 'magit + :type 'integer) + +(defcustom magit-save-some-buffers t + "Non-nil means that \\[magit-status] will save modified buffers before running. +Setting this to t will ask which buffers to save, setting it to 'dontask will +save all modified buffers without asking." + :group 'magit + :type '(choice (const :tag "Never" nil) + (const :tag "Ask" t) + (const :tag "Save without asking" dontask))) + +(defcustom magit-commit-all-when-nothing-staged 'ask + "Determines what \\[magit-log-edit] does when nothing is staged. +Setting this to nil will make it do nothing, setting it to t will arrange things so that the actual commit command will use the \"--all\" option, setting it to 'ask will first ask for confirmation whether to do this, and setting it to 'ask-stage will cause all changes to be staged, after a confirmation." + :group 'magit + :type '(choice (const :tag "No" nil) + (const :tag "Always" t) + (const :tag "Ask" ask) + (const :tag "Ask to stage everything" ask-stage))) + +(defcustom magit-commit-signoff nil + "When performing git commit adds --signoff" + :group 'magit + :type 'boolean) + +(defcustom magit-log-cutoff-length 100 + "The maximum number of commits to show in the log and whazzup buffers" + :group 'magit + :type 'integer) + +(defcustom magit-process-popup-time -1 + "Popup the process buffer if a command takes longer than this many seconds." + :group 'magit + :type '(choice (const :tag "Never" -1) + (const :tag "Immediately" 0) + (integer :tag "After this many seconds"))) + +(defcustom magit-log-edit-confirm-cancellation nil + "Require acknowledgement before cancelling the log edit buffer." + :group 'magit + :type 'boolean) + +(defface magit-header + '((t)) + "Face for generic header lines. + +Many Magit faces inherit from this one by default." + :group 'magit) + +(defface magit-section-title + '((t :weight bold :inherit magit-header)) + "Face for section titles." + :group 'magit) + +(defface magit-branch + '((t :weight bold :inherit magit-header)) + "Face for the current branch." + :group 'magit) + +(defface magit-diff-file-header + '((t :inherit magit-header)) + "Face for diff file header lines." + :group 'magit) + +(defface magit-diff-hunk-header + '((t :slant italic :inherit magit-header)) + "Face for diff hunk header lines." + :group 'magit) + +(defface magit-diff-add + '((((class color) (background light)) + :foreground "blue1") + (((class color) (background dark)) + :foreground "white")) + "Face for lines in a diff that have been added." + :group 'magit) + +(defface magit-diff-none + '((t)) + "Face for lines in a diff that are unchanged." + :group 'magit) + +(defface magit-diff-del + '((((class color) (background light)) + :foreground "red") + (((class color) (background dark)) + :foreground "OrangeRed")) + "Face for lines in a diff that have been deleted." + :group 'magit) + +(defface magit-item-highlight + '((((class color) (background light)) + :background "gray95") + (((class color) (background dark)) + :background "dim gray")) + "Face for highlighting the current item." + :group 'magit) + +(defface magit-item-mark + '((((class color) (background light)) + :foreground "red") + (((class color) (background dark)) + :foreground "orange")) + "Face for highlighting marked item." + :group 'magit) + +(defface magit-log-tag-label + '((((class color) (background light)) + :background "LightGoldenRod") + (((class color) (background dark)) + :background "DarkGoldenRod")) + "Face for git tag labels shown in log buffer." + :group 'magit) + +(defface magit-log-head-label + '((((class color) (background light)) + :background "spring green") + (((class color) (background dark)) + :background "DarkGreen")) + "Face for branch head labels shown in log buffer." + :group 'magit) + +;;; Macros + +(defmacro magit-with-refresh (&rest body) + (declare (indent 0)) + `(magit-refresh-wrapper (lambda () ,@body))) + +;;; Utilities + +(defun magit-use-region-p () + (if (fboundp 'use-region-p) + (use-region-p) + (and transient-mark-mode mark-active))) + +(defun magit-goto-line (line) + ;; Like goto-line but doesn't set the mark. + (save-restriction + (widen) + (goto-char 1) + (forward-line (1- line)))) + +(defun magit-trim-line (str) + (if (string= str "") + nil + (if (equal (elt str (- (length str) 1)) ?\n) + (substring str 0 (- (length str) 1)) + str))) + +(defun magit-split-lines (str) + (if (string= str "") + nil + (let ((lines (nreverse (split-string str "\n")))) + (if (string= (car lines) "") + (setq lines (cdr lines))) + (nreverse lines)))) + +(defun magit-git-insert (args) + (apply #'process-file + magit-git-executable + nil (list t nil) nil + (append magit-git-standard-options args))) + +(defun magit-git-output (args) + (with-output-to-string + (with-current-buffer + standard-output + (magit-git-insert args)))) + +(defun magit-git-string (&rest args) + (magit-trim-line (magit-git-output args))) + +(defun magit-git-lines (&rest args) + (magit-split-lines (magit-git-output args))) + +(defun magit-git-exit-code (&rest args) + (apply #'process-file magit-git-executable nil nil nil + (append magit-git-standard-options args))) + +(defun magit-file-lines (file) + (when (file-exists-p file) + (with-temp-buffer + (insert-file-contents file) + (let ((rev (nreverse (split-string (buffer-string) "\n")))) + (nreverse (if (equal (car rev) "") + (cdr rev) + rev)))))) + +(defun magit-write-file-lines (file lines) + (with-temp-buffer + (dolist (l lines) + (insert l "\n")) + (write-file file))) + +(defun magit-concat-with-delim (delim seqs) + (cond ((null seqs) + nil) + ((null (cdr seqs)) + (car seqs)) + (t + (concat (car seqs) delim (magit-concat-with-delim delim (cdr seqs)))))) + +(defun magit-get (&rest keys) + (magit-git-string "config" (magit-concat-with-delim "." keys))) + +(defun magit-set (val &rest keys) + (if val + (magit-git-string "config" (magit-concat-with-delim "." keys) val) + (magit-git-string "config" "--unset" (magit-concat-with-delim "." keys)))) + +(defun magit-remove-conflicts (alist) + (let ((dict (make-hash-table :test 'equal)) + (result nil)) + (dolist (a alist) + (puthash (car a) (cons (cdr a) (gethash (car a) dict)) + dict)) + (maphash (lambda (key value) + (if (= (length value) 1) + (push (cons key (car value)) result) + (let ((sub (magit-remove-conflicts + (mapcar (lambda (entry) + (let ((dir (directory-file-name + (subseq entry 0 (- (length key)))))) + (cons (concat (file-name-nondirectory dir) "/" key) + entry))) + value)))) + (setq result (append result sub))))) + dict) + result)) + +(defun magit-git-repo-p (dir) + (file-exists-p (expand-file-name ".git" dir))) + +(defun magit-list-repos* (dir level) + (if (magit-git-repo-p dir) + (list dir) + (apply #'append + (mapcar (lambda (entry) + (unless (or (string= (substring entry -3) "/..") + (string= (substring entry -2) "/.")) + (magit-list-repos* entry (+ level 1)))) + (and (file-directory-p dir) + (< level magit-repo-dirs-depth) + (directory-files dir t nil t)))))) + +(defun magit-list-repos (dirs) + (magit-remove-conflicts + (apply #'append + (mapcar (lambda (dir) + (mapcar #'(lambda (repo) + (cons (file-name-nondirectory repo) + repo)) + (magit-list-repos* dir 0))) + dirs)))) + +(defun magit-get-top-dir (cwd) + (let ((cwd (expand-file-name cwd))) + (and (file-directory-p cwd) + (let* ((default-directory cwd) + (magit-dir + (magit-git-string "rev-parse" "--git-dir"))) + (and magit-dir + (file-name-as-directory + (or (file-name-directory magit-dir) cwd))))))) + +(defun magit-get-ref (ref) + (magit-git-string "symbolic-ref" "-q" ref)) + +(defun magit-get-current-branch () + (let* ((head (magit-get-ref "HEAD")) + (pos (and head (string-match "^refs/heads/" head)))) + (if pos + (substring head 11) + nil))) + +(defun magit-ref-exists-p (ref) + (= (magit-git-exit-code "show-ref" "--verify" ref) 0)) + +(defun magit-read-top-dir (rawp) + (if (and (not rawp) magit-repo-dirs) + (let* ((repos (magit-list-repos magit-repo-dirs)) + (reply (completing-read "Git repository: " + (magit-list-repos magit-repo-dirs)))) + (file-name-as-directory + (cdr (assoc reply repos)))) + (file-name-as-directory + (read-directory-name "Git repository: " + (or (magit-get-top-dir default-directory) + default-directory))))) + +(defun magit-name-rev (rev) + (and rev + (let ((name (magit-git-string "name-rev" "--name-only" rev))) + (if (or (not name) (string= name "undefined")) + rev + name)))) + +(defun magit-put-line-property (prop val) + (put-text-property (line-beginning-position) (line-beginning-position 2) + prop val)) + +(defun magit-format-commit (commit format) + (magit-git-string "log" "--max-count=1" + (concat "--pretty=format:" format) + commit)) + +(defun magit-current-line () + (buffer-substring-no-properties (line-beginning-position) + (line-end-position))) + +(defun magit-insert-region (beg end buf) + (let ((text (buffer-substring-no-properties beg end))) + (with-current-buffer buf + (insert text)))) + +(defun magit-insert-current-line (buf) + (let ((text (buffer-substring-no-properties + (line-beginning-position) (line-beginning-position 2)))) + (with-current-buffer buf + (insert text)))) + +(defun magit-file-uptodate-p (file) + (eq (magit-git-exit-code "diff" "--quiet" "--" file) 0)) + +(defun magit-anything-staged-p () + (not (eq (magit-git-exit-code "diff" "--quiet" "--cached") 0))) + +(defun magit-everything-clean-p () + (and (not (magit-anything-staged-p)) + (eq (magit-git-exit-code "diff" "--quiet") 0))) + +(defun magit-commit-parents (commit) + (cdr (split-string (magit-git-string "rev-list" "-1" "--parents" commit)))) + +;; XXX - let the user choose the parent + +(defun magit-choose-parent-id (commit op) + (let* ((parents (magit-commit-parents commit))) + (if (> (length parents) 1) + (error "Can't %s merge commits." op) + nil))) + +;;; Revisions and ranges + +(defun magit-list-interesting-refs () + (let ((refs ())) + (dolist (line (magit-git-lines "show-ref")) + (if (string-match "[^ ]+ +\\(.*\\)" line) + (let ((ref (match-string 1 line))) + (cond ((string-match "refs/heads/\\(.*\\)" ref) + (let ((branch (match-string 1 ref))) + (push (cons branch branch) refs))) + ((string-match "refs/tags/\\(.*\\)" ref) + (push (cons (format "%s (tag)" (match-string 1 ref)) ref) + refs)) + ((string-match "refs/remotes/\\([^/]+\\)/\\(.+\\)" ref) + (push (cons (format "%s (%s)" + (match-string 2 ref) + (match-string 1 ref)) + ref) + refs)))))) + refs)) + +(defun magit-read-rev (prompt &optional def) + (let* ((prompt (if def + (format "%s (default %s): " prompt def) + (format "%s: " prompt))) + (interesting-refs (magit-list-interesting-refs)) + (reply (completing-read prompt interesting-refs + nil nil nil nil def)) + (rev (or (cdr (assoc reply interesting-refs)) reply))) + (if (string= rev "") + nil + rev))) + +(defun magit-read-rev-range (op &optional def-beg def-end) + (let ((beg (magit-read-rev (format "%s start" op) + def-beg))) + (if (not beg) + nil + (let ((end (magit-read-rev (format "%s end" op) def-end))) + (cons beg end))))) + +(defun magit-rev-to-git (rev) + (or rev + (error "No revision specified")) + (if (string= rev ".") + (magit-marked-commit) + rev)) + +(defun magit-rev-range-to-git (range) + (or range + (error "No revision range specified")) + (if (stringp range) + range + (if (cdr range) + (format "%s..%s" + (magit-rev-to-git (car range)) + (magit-rev-to-git (cdr range))) + (format "%s" (magit-rev-to-git (car range)))))) + +(defun magit-rev-describe (rev) + (or rev + (error "No revision specified")) + (if (string= rev ".") + "mark" + (magit-name-rev rev))) + +(defun magit-rev-range-describe (range things) + (or range + (error "No revision range specified")) + (if (stringp range) + (format "%s in %s" things range) + (if (cdr range) + (format "%s from %s to %s" things + (magit-rev-describe (car range)) + (magit-rev-describe (cdr range))) + (format "%s at %s" things (magit-rev-describe (car range)))))) + +(defun magit-default-rev () + (magit-name-rev (magit-commit-at-point t))) + +;;; Sections + +;; A buffer in magit-mode is organized into hierarchical sections. +;; These sections are used for navigation and for hiding parts of the +;; buffer. +;; +;; Most sections also represent the objects that Magit works with, +;; such as files, diffs, hunks, commits, etc. The 'type' of a section +;; identifies what kind of object it represents (if any), and the +;; parent and grand-parent, etc provide the context. + +(defstruct magit-section + parent title beginning end children hidden type info + needs-refresh-on-show) + +(defvar magit-top-section nil) +(make-variable-buffer-local 'magit-top-section) +(put 'magit-top-section 'permanent-local t) + +(defvar magit-old-top-section nil) + +(defvar magit-section-hidden-default nil) + +(defun magit-new-section (title type) + (let* ((s (make-magit-section :parent magit-top-section + :title title + :type type + :hidden magit-section-hidden-default)) + (old (and magit-old-top-section + (magit-find-section (magit-section-path s) + magit-old-top-section)))) + (if magit-top-section + (setf (magit-section-children magit-top-section) + (cons s (magit-section-children magit-top-section))) + (setq magit-top-section s)) + (if old + (setf (magit-section-hidden s) (magit-section-hidden old))) + s)) + +(defun magit-cancel-section (section) + (delete-region (magit-section-beginning section) + (magit-section-end section)) + (let ((parent (magit-section-parent section))) + (if parent + (setf (magit-section-children parent) + (delq section (magit-section-children parent))) + (setq magit-top-section nil)))) + +(defmacro magit-with-section (title type &rest body) + (declare (indent 2)) + (let ((s (gensym))) + `(let* ((,s (magit-new-section ,title ,type)) + (magit-top-section ,s)) + (setf (magit-section-beginning ,s) (point)) + ,@body + (setf (magit-section-end ,s) (point)) + (setf (magit-section-children ,s) + (nreverse (magit-section-children ,s))) + ,s))) + +(defun magit-set-section-info (info &optional section) + (setf (magit-section-info (or section magit-top-section)) info)) + +(defun magit-set-section-needs-refresh-on-show (flag &optional section) + (setf (magit-section-needs-refresh-on-show + (or section magit-top-section)) + flag)) + +(defmacro magit-create-buffer-sections (&rest body) + (declare (indent 0)) + `(let ((inhibit-read-only t)) + (erase-buffer) + (let ((magit-old-top-section magit-top-section)) + (setq magit-top-section nil) + ,@body + (when (null magit-top-section) + (magit-with-section 'top nil + (insert "(empty)\n"))) + (magit-propertize-section magit-top-section) + (magit-section-set-hidden magit-top-section + (magit-section-hidden magit-top-section))))) + +(defun magit-propertize-section (section) + (put-text-property (magit-section-beginning section) + (magit-section-end section) + 'magit-section section) + (dolist (s (magit-section-children section)) + (magit-propertize-section s))) + +(defun magit-find-section (path top) + (if (null path) + top + (let ((sec (find-if (lambda (s) (equal (car path) + (magit-section-title s))) + (magit-section-children top)))) + (if sec + (magit-find-section (cdr path) sec) + nil)))) + +(defun magit-section-path (section) + (if (not (magit-section-parent section)) + '() + (append (magit-section-path (magit-section-parent section)) + (list (magit-section-title section))))) + +(defun magit-find-section-at (pos secs) + (while (and secs + (not (and (<= (magit-section-beginning (car secs)) pos) + (< pos (magit-section-end (car secs)))))) + (setq secs (cdr secs))) + (if secs + (or (magit-find-section-at pos (magit-section-children (car secs))) + (car secs)) + nil)) + +(defun magit-find-section-after (pos secs) + (while (and secs + (not (> (magit-section-beginning (car secs)) pos))) + (setq secs (cdr secs))) + (car secs)) + +(defun magit-find-section-before (pos secs) + (let ((prev nil)) + (while (and secs + (not (> (magit-section-beginning (car secs)) pos))) + (setq prev (car secs)) + (setq secs (cdr secs))) + prev)) + +(defun magit-current-section () + (or (get-text-property (point) 'magit-section) + magit-top-section)) + +(defun magit-insert-section (section-title-and-type + buffer-title washer cmd &rest args) + (let* ((body-beg nil) + (section-title (if (consp section-title-and-type) + (car section-title-and-type) + section-title-and-type)) + (section-type (if (consp section-title-and-type) + (cdr section-title-and-type) + nil)) + (section + (magit-with-section section-title section-type + (if buffer-title + (insert (propertize buffer-title 'face 'magit-section-title) + "\n")) + (setq body-beg (point)) + (apply 'process-file cmd nil t nil (append magit-git-standard-options args)) + (if (not (eq (char-before) ?\n)) + (insert "\n")) + (if washer + (save-restriction + (narrow-to-region body-beg (point)) + (goto-char (point-min)) + (funcall washer) + (goto-char (point-max))))))) + (if (= body-beg (point)) + (magit-cancel-section section) + (insert "\n")) + section)) + +(defun magit-git-section (section-title-and-type + buffer-title washer &rest args) + (apply #'magit-insert-section + section-title-and-type + buffer-title + washer + magit-git-executable + (append magit-git-standard-options args))) + +(defun magit-next-section (section) + (let ((parent (magit-section-parent section))) + (if parent + (let ((next (cadr (memq section + (magit-section-children parent))))) + (or next + (magit-next-section parent)))))) + +(defun magit-goto-next-section () + (interactive) + (let* ((section (magit-current-section)) + (next (or (and (not (magit-section-hidden section)) + (magit-section-children section) + (magit-find-section-after (point) + (magit-section-children + section))) + (magit-next-section section)))) + + (if next + (progn + (goto-char (magit-section-beginning next)) + (if (memq magit-submode '(log reflog)) + (magit-show-commit next)) + (if (not (magit-section-hidden next)) + (let ((offset (- (line-number-at-pos + (magit-section-beginning next)) + (line-number-at-pos + (magit-section-end next))))) + (if (< offset (window-height)) + (recenter offset))))) + (message "No next section")))) + +(defun magit-prev-section (section) + (let ((parent (magit-section-parent section))) + (if parent + (let ((prev (cadr (memq section + (reverse (magit-section-children parent)))))) + (cond (prev + (while (and (not (magit-section-hidden prev)) + (magit-section-children prev)) + (setq prev (car (reverse (magit-section-children prev))))) + prev) + (t + parent)))))) + +(defun magit-goto-previous-section () + (interactive) + (let ((section (magit-current-section))) + (cond ((= (point) (magit-section-beginning section)) + (let ((prev (magit-prev-section (magit-current-section)))) + (if prev + (progn + (if (memq magit-submode '(log reflog)) + (magit-show-commit (or prev section))) + (goto-char (magit-section-beginning prev))) + (message "No previous section")))) + (t + (let ((prev (magit-find-section-before (point) + (magit-section-children + section)))) + (if (memq magit-submode '(log reflog)) + (magit-show-commit (or prev section))) + (goto-char (magit-section-beginning (or prev section)))))))) + +(defun magit-goto-section (path) + (let ((sec (magit-find-section path magit-top-section))) + (if sec + (goto-char (magit-section-beginning sec)) + (message "No such section")))) + +(defun magit-for-all-sections (func &optional top) + (let ((section (or top magit-top-section))) + (when section + (funcall func section) + (dolist (c (magit-section-children section)) + (magit-for-all-sections func c))))) + +(defun magit-section-set-hidden (section hidden) + (setf (magit-section-hidden section) hidden) + (if (and (not hidden) + (magit-section-needs-refresh-on-show section)) + (magit-refresh) + (let ((inhibit-read-only t) + (beg (save-excursion + (goto-char (magit-section-beginning section)) + (forward-line) + (point))) + (end (magit-section-end section))) + (put-text-property beg end 'invisible hidden)) + (if (not hidden) + (dolist (c (magit-section-children section)) + (magit-section-set-hidden c (magit-section-hidden c)))))) + +(defun magit-section-any-hidden (section) + (or (magit-section-hidden section) + (some #'magit-section-any-hidden (magit-section-children section)))) + +(defun magit-section-collapse (section) + (dolist (c (magit-section-children section)) + (setf (magit-section-hidden c) t)) + (magit-section-set-hidden section nil)) + +(defun magit-section-expand (section) + (dolist (c (magit-section-children section)) + (setf (magit-section-hidden c) nil)) + (magit-section-set-hidden section nil)) + +(defun magit-section-expand-all-aux (section) + (dolist (c (magit-section-children section)) + (setf (magit-section-hidden c) nil) + (magit-section-expand-all-aux c))) + +(defun magit-section-expand-all (section) + (magit-section-expand-all-aux section) + (magit-section-set-hidden section nil)) + +(defun magit-section-hideshow (flag-or-func) + (let ((section (magit-current-section))) + (cond ((magit-section-parent section) + (goto-char (magit-section-beginning section)) + (if (functionp flag-or-func) + (funcall flag-or-func section) + (magit-section-set-hidden section flag-or-func)))))) + +(defun magit-show-section () + (interactive) + (magit-section-hideshow nil)) + +(defun magit-hide-section () + (interactive) + (magit-section-hideshow t)) + +(defun magit-collapse-section () + (interactive) + (magit-section-hideshow #'magit-section-collapse)) + +(defun magit-expand-section () + (interactive) + (magit-section-hideshow #'magit-section-expand)) + +(defun magit-toggle-section () + (interactive) + (magit-section-hideshow + (lambda (s) + (magit-section-set-hidden s (not (magit-section-hidden s)))))) + +(defun magit-expand-collapse-section () + (interactive) + (magit-section-hideshow + (lambda (s) + (cond ((magit-section-any-hidden s) + (magit-section-expand-all s)) + (t + (magit-section-collapse s)))))) + +(defun magit-cycle-section () + (interactive) + (magit-section-hideshow + (lambda (s) + (cond ((magit-section-hidden s) + (magit-section-collapse s)) + ((notany #'magit-section-hidden (magit-section-children s)) + (magit-section-set-hidden s t)) + (t + (magit-section-expand s)))))) + +(defun magit-section-lineage (s) + (and s (cons s (magit-section-lineage (magit-section-parent s))))) + +(defun magit-section-show-level (section level threshold path) + (magit-section-set-hidden section (>= level threshold)) + (when (< level threshold) + (if path + (magit-section-show-level (car path) (1+ level) threshold (cdr path)) + (dolist (c (magit-section-children section)) + (magit-section-show-level c (1+ level) threshold nil))))) + +(defun magit-show-level (level all) + (magit-with-refresh + (if all + (magit-section-show-level magit-top-section 0 level nil) + (let ((path (reverse (magit-section-lineage (magit-current-section))))) + (magit-section-show-level (car path) 0 level (cdr path)))))) + +(defun magit-show-only-files () + (interactive) + (if (eq magit-submode 'status) + (call-interactively 'magit-show-level-2) + (call-interactively 'magit-show-level-1))) + +(defun magit-show-only-files-all () + (interactive) + (if (eq magit-submode 'status) + (call-interactively 'magit-show-level-2-all) + (call-interactively 'magit-show-level-1-all))) + +(defmacro magit-define-level-shower-1 (level all) + (let ((fun (intern (format "magit-show-level-%s%s" + level (if all "-all" "")))) + (doc (format "Show sections on level %s." level))) + `(defun ,fun () + ,doc + (interactive) + (magit-show-level ,level ,all)))) + +(defmacro magit-define-level-shower (level) + `(progn + (magit-define-level-shower-1 ,level nil) + (magit-define-level-shower-1 ,level t))) + +(defmacro magit-define-section-jumper (sym title) + (let ((fun (intern (format "magit-jump-to-%s" sym))) + (doc (format "Jump to section `%s'." title))) + `(defun ,fun () + ,doc + (interactive) + (magit-goto-section '(,sym))))) + +(defvar magit-highlight-overlay nil) + +(defvar magit-highlighted-section nil) + +(defun magit-highlight-section () + (let ((section (magit-current-section))) + (when (not (eq section magit-highlighted-section)) + (setq magit-highlighted-section section) + (if (not magit-highlight-overlay) + (let ((ov (make-overlay 1 1))) + (overlay-put ov 'face 'magit-item-highlight) + (setq magit-highlight-overlay ov))) + (if (and section (magit-section-type section)) + (move-overlay magit-highlight-overlay + (magit-section-beginning section) + (magit-section-end section) + (current-buffer)) + (delete-overlay magit-highlight-overlay))))) + +(defun magit-section-context-type (section) + (if (null section) + '() + (let ((c (or (magit-section-type section) + (if (symbolp (magit-section-title section)) + (magit-section-title section))))) + (if c + (cons c (magit-section-context-type + (magit-section-parent section))) + '())))) + +(defun magit-prefix-p (prefix list) + ;;; Very schemish... + (or (null prefix) + (if (eq (car prefix) '*) + (or (magit-prefix-p (cdr prefix) list) + (and (not (null list)) + (magit-prefix-p prefix (cdr list)))) + (and (not (null list)) + (equal (car prefix) (car list)) + (magit-prefix-p (cdr prefix) (cdr list)))))) + +(defmacro magit-section-case (head &rest clauses) + (declare (indent 1)) + (let ((section (car head)) + (info (cadr head)) + (type (gensym)) + (context (gensym)) + (opname (caddr head))) + `(let* ((,section (magit-current-section)) + (,info (magit-section-info ,section)) + (,type (magit-section-type ,section)) + (,context (magit-section-context-type ,section))) + (cond ,@(mapcar (lambda (clause) + (if (eq (car clause) t) + clause + (let ((prefix (reverse (car clause))) + (body (cdr clause))) + `((magit-prefix-p ',prefix ,context) + ,@body)))) + clauses) + ,@(if opname + `(((not ,type) + (error "Nothing to %s here." ,opname)) + (t + (error "Can't %s a %s." + ,opname + (or (get ,type 'magit-description) + ,type))))))))) + +(defmacro magit-section-action (head &rest clauses) + (declare (indent 1)) + `(magit-with-refresh + (magit-section-case ,head ,@clauses))) + +(defun magit-wash-sequence (func) + (while (and (not (eobp)) + (funcall func)))) + +;;; Running commands + +(defun magit-set-mode-line-process (str) + (let ((pr (if str (concat " " str) ""))) + (save-excursion + (magit-for-all-buffers (lambda () + (setq mode-line-process pr)))))) + +(defun magit-process-indicator-from-command (comps) + (if (magit-prefix-p (cons magit-git-executable magit-git-standard-options) + comps) + (setq comps (nthcdr (+ (length magit-git-standard-options) 1) comps))) + (cond ((or (null (cdr comps)) + (not (member (car comps) '("remote")))) + (car comps)) + (t + (concat (car comps) " " (cadr comps))))) + +(defvar magit-process nil) +(defvar magit-process-client-buffer nil) + +(defun magit-run* (cmd-and-args + &optional logline noerase noerror nowait input) + (if (and magit-process + (get-buffer "*magit-process*")) + (error "Git is already running.")) + (let ((cmd (car cmd-and-args)) + (args (cdr cmd-and-args)) + (dir default-directory) + (buf (get-buffer-create "*magit-process*")) + (successp nil)) + (magit-set-mode-line-process + (magit-process-indicator-from-command cmd-and-args)) + (setq magit-process-client-buffer (current-buffer)) + (save-excursion + (set-buffer buf) + (setq buffer-read-only t) + (let ((inhibit-read-only t)) + (setq default-directory dir) + (if noerase + (goto-char (point-max)) + (erase-buffer)) + (insert "$ " (or logline + (magit-concat-with-delim " " cmd-and-args)) + "\n") + (cond (nowait + (setq magit-process + (apply 'start-file-process cmd buf cmd args)) + (set-process-sentinel magit-process 'magit-process-sentinel) + (set-process-filter magit-process 'magit-process-filter) + (when input + (with-current-buffer input + (process-send-region magit-process + (point-min) (point-max))) + (process-send-eof magit-process) + (sit-for 0.1 t)) + (cond ((= magit-process-popup-time 0) + (pop-to-buffer (process-buffer magit-process))) + ((> magit-process-popup-time 0) + (run-with-timer + magit-process-popup-time nil + (function + (lambda (buf) + (with-current-buffer buf + (when magit-process + (display-buffer (process-buffer magit-process)) + (goto-char (point-max)))))) + (current-buffer)))) + (setq successp t)) + (input + (with-current-buffer input + (setq default-directory dir) + (setq successp + (equal (apply 'call-process-region + (point-min) (point-max) + cmd nil buf nil args) 0))) + (magit-set-mode-line-process nil) + (magit-need-refresh magit-process-client-buffer)) + (t + (setq successp + (equal (apply 'process-file cmd nil buf nil args) 0)) + (magit-set-mode-line-process nil) + (magit-need-refresh magit-process-client-buffer)))) + (or successp + noerror + (error "Git failed.")) + successp))) + +(defun magit-process-sentinel (process event) + (let ((msg (format "Git %s." (substring event 0 -1))) + (successp (string-match "^finished" event))) + (with-current-buffer (process-buffer process) + (let ((inhibit-read-only t)) + (goto-char (point-max)) + (insert msg "\n") + (message msg))) + (setq magit-process nil) + (magit-set-mode-line-process nil) + (magit-refresh-buffer magit-process-client-buffer))) + +(defun magit-process-filter (proc string) + (save-current-buffer + (set-buffer (process-buffer proc)) + (let ((inhibit-read-only t)) + (goto-char (process-mark proc)) + ;; Find last ^M in string. If one was found, ignore everything + ;; before it and delete the current line. + (let ((ret-pos (position ?\r string :from-end t))) + (cond (ret-pos + (goto-char (line-beginning-position)) + (delete-region (point) (line-end-position)) + (insert (substring string (+ ret-pos 1)))) + (t + (insert string)))) + (set-marker (process-mark proc) (point))))) + +(defun magit-run (cmd &rest args) + (magit-with-refresh + (magit-run* (cons cmd args)))) + +(defun magit-run-git (&rest args) + (magit-with-refresh + (magit-run* (append (cons magit-git-executable + magit-git-standard-options) + args)))) + +(defun magit-run-with-input (input cmd &rest args) + (magit-with-refresh + (magit-run* (cons cmd args) nil nil nil nil input))) + +(defun magit-run-git-with-input (input &rest args) + (magit-with-refresh + (magit-run* (append (cons magit-git-executable + magit-git-standard-options) + args) + nil nil nil nil input))) + +(defun magit-run-git-async (&rest args) + (magit-run* (append (cons magit-git-executable + magit-git-standard-options) + args) + nil nil nil t)) + +(defun magit-run-async-with-input (input cmd &rest args) + (magit-run* (cons cmd args) nil nil nil t input)) + +(defun magit-display-process () + (interactive) + (display-buffer "*magit-process*")) + +;;; Mode + +;; We define individual functions (instead of using lambda etc) so +;; that the online help can show something meaningful. + +(magit-define-section-jumper untracked "Untracked files") +(magit-define-section-jumper unstaged "Unstaged changes") +(magit-define-section-jumper staged "Staged changes") +(magit-define-section-jumper unpushed "Unpushed commits") +(magit-define-section-jumper svn-unpushed "Unpushed commits (SVN)") + +(magit-define-level-shower 1) +(magit-define-level-shower 2) +(magit-define-level-shower 3) +(magit-define-level-shower 4) + +(defvar magit-mode-map + (let ((map (make-keymap))) + (suppress-keymap map t) + (define-key map (kbd "n") 'magit-goto-next-section) + (define-key map (kbd "p") 'magit-goto-previous-section) + (define-key map (kbd "TAB") 'magit-toggle-section) + (define-key map (kbd "") 'magit-expand-collapse-section) + (define-key map (kbd "1") 'magit-show-level-1) + (define-key map (kbd "2") 'magit-show-level-2) + (define-key map (kbd "3") 'magit-show-level-3) + (define-key map (kbd "4") 'magit-show-level-4) + (define-key map (kbd "M-1") 'magit-show-level-1-all) + (define-key map (kbd "M-2") 'magit-show-level-2-all) + (define-key map (kbd "M-3") 'magit-show-level-3-all) + (define-key map (kbd "M-4") 'magit-show-level-4-all) + (define-key map (kbd "M-h") 'magit-show-only-files) + (define-key map (kbd "M-H") 'magit-show-only-files-all) + (define-key map (kbd "M-s") 'magit-show-level-4) + (define-key map (kbd "M-S") 'magit-show-level-4-all) + (define-key map (kbd "g") 'magit-refresh) + (define-key map (kbd "G") 'magit-refresh-all) + (define-key map (kbd "s") 'magit-stage-item) + (define-key map (kbd "S") 'magit-stage-all) + (define-key map (kbd "u") 'magit-unstage-item) + (define-key map (kbd "U") 'magit-unstage-all) + (define-key map (kbd "i") 'magit-ignore-item) + (define-key map (kbd "I") 'magit-ignore-item-locally) + (define-key map (kbd "?") 'magit-describe-item) + (define-key map (kbd ".") 'magit-mark-item) + (define-key map (kbd "=") 'magit-diff-with-mark) + (define-key map (kbd "-") 'magit-diff-smaller-hunks) + (define-key map (kbd "+") 'magit-diff-larger-hunks) + (define-key map (kbd "0") 'magit-diff-default-hunks) + (define-key map (kbd "l") 'magit-log) + (define-key map (kbd "L") 'magit-log-long) + (define-key map (kbd "h") 'magit-reflog-head) + (define-key map (kbd "H") 'magit-reflog) + (define-key map (kbd "d") 'magit-diff-working-tree) + (define-key map (kbd "D") 'magit-diff) + (define-key map (kbd "a") 'magit-apply-item) + (define-key map (kbd "A") 'magit-cherry-pick-item) + (define-key map (kbd "v") 'magit-revert-item) + (define-key map (kbd "x") 'magit-reset-head) + (define-key map (kbd "X") 'magit-reset-working-tree) + (define-key map (kbd "k") 'magit-discard-item) + (define-key map (kbd "!") 'magit-shell-command) + (define-key map (kbd "RET") 'magit-visit-item) + (define-key map (kbd "SPC") 'magit-show-item-or-scroll-up) + (define-key map (kbd "DEL") 'magit-show-item-or-scroll-down) + (define-key map (kbd "C-w") 'magit-copy-item-as-kill) + (define-key map (kbd "b") 'magit-checkout) + (define-key map (kbd "B") 'magit-create-branch) + (define-key map (kbd "m") 'magit-manual-merge) + (define-key map (kbd "M") 'magit-automatic-merge) + (define-key map (kbd "e") 'magit-interactive-resolve-item) + (define-key map (kbd "N r") 'magit-svn-rebase) + (define-key map (kbd "N c") 'magit-svn-dcommit) + (define-key map (kbd "R") 'magit-rebase-step) + (define-key map (kbd "r s") 'magit-rewrite-start) + (define-key map (kbd "r t") 'magit-rewrite-stop) + (define-key map (kbd "r a") 'magit-rewrite-abort) + (define-key map (kbd "r f") 'magit-rewrite-finish) + (define-key map (kbd "r *") 'magit-rewrite-set-unused) + (define-key map (kbd "r .") 'magit-rewrite-set-used) + (define-key map (kbd "P") 'magit-push) + (define-key map (kbd "f") 'magit-remote-update) + (define-key map (kbd "F") 'magit-pull) + (define-key map (kbd "c") 'magit-log-edit) + (define-key map (kbd "C") 'magit-add-log) + (define-key map (kbd "t") 'magit-tag) + (define-key map (kbd "T") 'magit-annotated-tag) + (define-key map (kbd "z") 'magit-stash) + (define-key map (kbd "Z") 'magit-stash-snapshot) + (define-key map (kbd "w") 'magit-wazzup) + (define-key map (kbd "$") 'magit-display-process) + (define-key map (kbd "E") 'magit-interactive-rebase) + (define-key map (kbd "V") 'magit-show-branches) + (define-key map (kbd "q") 'quit-window) + map)) + +(easy-menu-define magit-mode-menu magit-mode-map + "Magit menu" + '("Magit" + ["Refresh" magit-refresh t] + ["Refresh all" magit-refresh-all t] + "---" + ["Stage" magit-stage-item t] + ["Stage all" magit-stage-all t] + ["Unstage" magit-unstage-item t] + ["Unstage all" magit-unstage-all t] + ["Commit" magit-log-edit t] + ["Add log entry" magit-add-log t] + ["Tag" magit-tag t] + ["Annotated tag" magit-annotated-tag t] + "---" + ["Diff working tree" magit-diff-working-tree t] + ["Diff" magit-diff t] + ["Log" magit-log t] + ["Long Log" magit-log-long t] + ["Reflog head" magit-reflog-head t] + ["Reflog" magit-reflog t] + "---" + ["Cherry pick" magit-cherry-pick-item t] + ["Apply" magit-apply-item t] + ["Revert" magit-revert-item t] + "---" + ["Ignore" magit-ignore-item t] + ["Ignore locally" magit-ignore-item-locally t] + ["Discard" magit-discard-item t] + ["Reset head" magit-reset-head t] + ["Reset working tree" magit-reset-working-tree t] + ["Stash" magit-stash t] + ["Snapshot" magit-stash-snapshot t] + "---" + ["Switch branch" magit-checkout t] + ["Create branch" magit-create-branch t] + ["Merge" magit-automatic-merge t] + ["Merge (no commit)" magit-manual-merge t] + ["Interactive resolve" magit-interactive-resolve-item t] + ["Rebase" magit-rebase-step t] + ("Git SVN" + ["Rebase" magit-svn-rebase (magit-svn-enabled)] + ["Commit" magit-svn-dcommit (magit-svn-enabled)] + ) + ("Rewrite" + ["Start" magit-rewrite-start t] + ["Stop" magit-rewrite-stop t] + ["Finish" magit-rewrite-finish t] + ["Abort" magit-rewrite-abort t] + ["Set used" magit-rewrite-set-used t] + ["Set unused" magit-rewrite-set-unused t]) + "---" + ["Push" magit-push t] + ["Pull" magit-pull t] + ["Remote update" magit-remote-update t] + "---" + ["Display Git output" magit-display-process t] + ["Quit Magit" quit-window t])) + +(defvar magit-mode-hook nil) + +(put 'magit-mode 'mode-class 'special) + +(defvar magit-submode nil) +(make-variable-buffer-local 'magit-submode) +(put 'magit-submode 'permanent-local t) + +(defvar magit-refresh-function nil) +(make-variable-buffer-local 'magit-refresh-function) +(put 'magit-refresh-function 'permanent-local t) + +(defvar magit-refresh-args nil) +(make-variable-buffer-local 'magit-refresh-args) +(put 'magit-refresh-args 'permanent-local t) + +(defvar last-point) + +(defun magit-remember-point () + (setq last-point (point))) + +(defun magit-invisible-region-end (pos) + (while (and (not (= pos (point-max))) (invisible-p pos)) + (setq pos (next-char-property-change pos))) + pos) + +(defun magit-invisible-region-start (pos) + (while (and (not (= pos (point-min))) (invisible-p pos)) + (setq pos (1- (previous-char-property-change pos)))) + pos) + +(defun magit-correct-point-after-command () + ;; Emacs often leaves point in invisible regions, it seems. To fix + ;; this, we move point ourselves and never let Emacs do its own + ;; adjustements. + ;; + ;; When point has to be moved out of an invisible region, it can be + ;; moved to its end or its beginning. We usually move it to its + ;; end, except when that would move point back to where it was + ;; before the last command. + ;; + (if (invisible-p (point)) + (let ((end (magit-invisible-region-end (point)))) + (goto-char (if (= end last-point) + (magit-invisible-region-start (point)) + end)))) + (setq disable-point-adjustment t)) + +(defun magit-post-command-hook () + (magit-correct-point-after-command) + (magit-highlight-section)) + +(defun magit-mode () + "Review the status of a git repository and act on it. + +Please see the manual for a complete description of Magit. + +\\{magit-mode-map}" + (kill-all-local-variables) + (buffer-disable-undo) + (setq buffer-read-only t) + (make-local-variable 'line-move-visual) + (setq major-mode 'magit-mode + mode-name "Magit" + mode-line-process "" + truncate-lines t + line-move-visual nil) + (add-hook 'pre-command-hook #'magit-remember-point nil t) + (add-hook 'post-command-hook #'magit-post-command-hook t t) + (use-local-map magit-mode-map) + (run-mode-hooks 'magit-mode-hook)) + +(defun magit-mode-init (dir submode refresh-func &rest refresh-args) + (setq default-directory dir + magit-submode submode + magit-refresh-function refresh-func + magit-refresh-args refresh-args) + (magit-mode) + (magit-refresh-buffer)) + +(defun magit-find-buffer (submode &optional dir) + (let ((topdir (magit-get-top-dir (or dir default-directory)))) + (dolist (buf (buffer-list)) + (if (save-excursion + (set-buffer buf) + (and default-directory + (equal (expand-file-name default-directory) topdir) + (eq major-mode 'magit-mode) + (eq magit-submode submode))) + (return buf))))) + +(defun magit-find-status-buffer (&optional dir) + (magit-find-buffer 'status dir)) + +(defun magit-for-all-buffers (func &optional dir) + (dolist (buf (buffer-list)) + (save-excursion + (set-buffer buf) + (if (and (eq major-mode 'magit-mode) + (or (null dir) + (equal default-directory dir))) + (funcall func))))) + +(defun magit-refresh-buffer (&optional buffer) + (with-current-buffer (or buffer (current-buffer)) + (let* ((old-line (line-number-at-pos)) + (old-section (magit-current-section)) + (old-path (and old-section + (magit-section-path (magit-current-section)))) + (section-line (and old-section + (count-lines + (magit-section-beginning old-section) + (point))))) + (if magit-refresh-function + (apply magit-refresh-function + magit-refresh-args)) + (magit-refresh-marked-commits-in-buffer) + (let ((s (and old-path (magit-find-section old-path magit-top-section)))) + (cond (s + (goto-char (magit-section-beginning s)) + (forward-line section-line)) + (t + (magit-goto-line old-line))) + (dolist (w (get-buffer-window-list (current-buffer))) + (set-window-point w (point))) + (magit-highlight-section))))) + +(defun magit-string-has-prefix-p (string prefix) + (eq (compare-strings string nil (length prefix) prefix nil nil) t)) + +(defun magit-revert-buffers (dir) + (dolist (buffer (buffer-list)) + (when (and buffer + (buffer-file-name buffer) + (magit-string-has-prefix-p (buffer-file-name buffer) dir) + (not (verify-visited-file-modtime buffer)) + (not (buffer-modified-p buffer))) + (with-current-buffer buffer + (ignore-errors + (revert-buffer t t nil)))))) + +(defvar magit-refresh-needing-buffers nil) +(defvar magit-refresh-pending nil) + +(defun magit-refresh-wrapper (func) + (if magit-refresh-pending + (funcall func) + (let* ((dir default-directory) + (status-buffer (magit-find-buffer 'status dir)) + (magit-refresh-needing-buffers nil) + (magit-refresh-pending t)) + (unwind-protect + (funcall func) + (when magit-refresh-needing-buffers + (magit-revert-buffers dir) + (dolist (b (adjoin status-buffer + magit-refresh-needing-buffers)) + (magit-refresh-buffer b))))))) + +(defun magit-need-refresh (&optional buffer) + (let ((buffer (or buffer (current-buffer)))) + (when (not (memq buffer magit-refresh-needing-buffers)) + (setq magit-refresh-needing-buffers + (cons buffer magit-refresh-needing-buffers))))) + +(defun magit-refresh () + (interactive) + (magit-with-refresh + (magit-need-refresh))) + +(defun magit-refresh-all () + (interactive) + (magit-for-all-buffers #'magit-refresh-buffer default-directory)) + +;;; Untracked files + +(defun magit-wash-untracked-file () + (if (looking-at "^? \\(.*\\)$") + (let ((file (match-string-no-properties 1))) + (delete-region (point) (+ (line-end-position) 1)) + (magit-with-section file 'file + (magit-set-section-info file) + (insert "\t" file "\n")) + t) + nil)) + +(defun magit-wash-untracked-files () + ;; Setting magit-old-top-section to nil speeds up washing: no time + ;; is wasted looking up the old visibility, which doesn't matter for + ;; untracked files. + ;; + ;; XXX - speed this up in a more general way. + ;; + (let ((magit-old-top-section nil)) + (magit-wash-sequence #'magit-wash-untracked-file))) + +(defun magit-insert-untracked-files () + (magit-git-section 'untracked "Untracked files:" + 'magit-wash-untracked-files + "ls-files" "-t" "--others" "--exclude-standard")) + +;;; Diffs and Hunks + +(defvar magit-diff-context-lines 3) + +(defun magit-diff-U-arg () + (format "-U%d" magit-diff-context-lines)) + +(defun magit-diff-smaller-hunks (&optional count) + (interactive "p") + (setq magit-diff-context-lines (max 0 (- magit-diff-context-lines count))) + (magit-refresh)) + +(defun magit-diff-larger-hunks (&optional count) + (interactive "p") + (setq magit-diff-context-lines (+ magit-diff-context-lines count)) + (magit-refresh)) + +(defun magit-diff-default-hunks () + (interactive "") + (setq magit-diff-context-lines 3) + (magit-refresh)) + +(defun magit-diff-line-file () + (cond ((looking-at "^diff --git ./\\(.*\\) ./\\(.*\\)$") + (match-string-no-properties 2)) + ((looking-at "^diff --cc +\\(.*\\)$") + (match-string-no-properties 1)) + (t + nil))) + +(defun magit-wash-diffs () + (magit-wash-sequence #'magit-wash-diff-or-other-file)) + +(defun magit-wash-diff-or-other-file () + (or (magit-wash-diff) + (magit-wash-other-file))) + +(defun magit-wash-other-file () + (if (looking-at "^? \\(.*\\)$") + (let ((file (match-string-no-properties 1))) + (delete-region (point) (+ (line-end-position) 1)) + (magit-with-section file 'file + (magit-set-section-info file) + (insert "\tNew " file "\n")) + t) + nil)) + +(defvar magit-hide-diffs nil) + +(defun magit-insert-diff-title (status file file2) + (let ((status-text (case status + ((unmerged) + (format "Unmerged %s" file)) + ((new) + (format "New %s" file)) + ((deleted) + (format "Deleted %s" file)) + ((renamed) + (format "Renamed %s (from %s)" + file file2)) + ((modified) + (format "Modified %s" file)) + (t + (format "? %s" file))))) + (insert "\t" status-text "\n"))) + +(defun magit-wash-diff-section () + (cond ((looking-at "^\\* Unmerged path \\(.*\\)") + (let ((file (match-string-no-properties 1))) + (delete-region (point) (line-end-position)) + (insert "\tUnmerged " file "\n") + (magit-set-section-info (list 'unmerged file nil)) + t)) + ((looking-at "^diff") + (let ((file (magit-diff-line-file)) + (end (save-excursion + (forward-line) ;; skip over "diff" line + (if (search-forward-regexp "^diff\\|^@@" nil t) + (goto-char (match-beginning 0)) + (goto-char (point-max))) + (point-marker)))) + (let* ((status (cond + ((looking-at "^diff --cc") + 'unmerged) + ((save-excursion + (search-forward-regexp "^new file" end t)) + 'new) + ((save-excursion + (search-forward-regexp "^deleted" end t)) + 'deleted) + ((save-excursion + (search-forward-regexp "^rename" end t)) + 'renamed) + (t + 'modified))) + (file2 (cond + ((save-excursion + (search-forward-regexp "^rename from \\(.*\\)" + end t)) + (match-string-no-properties 1))))) + (magit-set-section-info (list status file file2)) + (magit-insert-diff-title status file file2) + (goto-char end) + (let ((magit-section-hidden-default nil)) + (magit-wash-sequence #'magit-wash-hunk)))) + t) + (t + nil))) + +(defun magit-wash-diff () + (let ((magit-section-hidden-default magit-hide-diffs)) + (magit-with-section (magit-current-line) 'diff + (magit-wash-diff-section)))) + +(defun magit-diff-item-kind (diff) + (car (magit-section-info diff))) + +(defun magit-diff-item-file (diff) + (cadr (magit-section-info diff))) + +(defun magit-diff-item-file2 (diff) + (caddr (magit-section-info diff))) + +(defun magit-wash-hunk () + (cond ((looking-at "\\(^@+\\)[^@]*@+") + (let ((n-columns (1- (length (match-string 1)))) + (head (match-string 0))) + (magit-with-section head 'hunk + (magit-put-line-property 'face 'magit-diff-hunk-header) + (forward-line) + (while (not (or (eobp) + (looking-at "^diff\\|^@@"))) + (let ((prefix (buffer-substring-no-properties + (point) (min (+ (point) n-columns) (point-max))))) + (cond ((string-match "\\+" prefix) + (magit-put-line-property 'face 'magit-diff-add)) + ((string-match "-" prefix) + (magit-put-line-property 'face 'magit-diff-del)) + (t + (magit-put-line-property 'face 'magit-diff-none)))) + (forward-line)))) + t) + (t + nil))) + +(defvar magit-diff-options nil) + +(defun magit-insert-diff (file) + (let ((cmd magit-git-executable) + (args (append (list "diff") + (list (magit-diff-U-arg)) + magit-diff-options + (list "--" file)))) + (let ((p (point))) + (magit-git-insert args) + (if (not (eq (char-before) ?\n)) + (insert "\n")) + (save-restriction + (narrow-to-region p (point)) + (goto-char p) + (magit-wash-diff-section) + (goto-char (point-max)))))) + +(defvar magit-last-raw-diff nil) +(defvar magit-ignore-unmerged-raw-diffs nil) + +(defun magit-wash-raw-diffs () + (let ((magit-last-raw-diff nil)) + (magit-wash-sequence #'magit-wash-raw-diff))) + +(defun magit-wash-raw-diff () + (if (looking-at + ":\\([0-7]+\\) \\([0-7]+\\) [0-9a-f]+ [0-9a-f]+ \\(.\\)[0-9]*\t\\([^\t\n]+\\)$") + (let ((old-perm (match-string-no-properties 1)) + (new-perm (match-string-no-properties 2)) + (status (case (string-to-char (match-string-no-properties 3)) + (?A 'new) + (?D 'deleted) + (?M 'modified) + (?U 'unmerged) + (?T 'new-type) + (t nil))) + (file (match-string-no-properties 4))) + ;; If this is for the same file as the last diff, ignore it. + ;; Unmerged files seem to get two entries. + ;; We also ignore unmerged files when told so. + (if (or (equal file magit-last-raw-diff) + (and magit-ignore-unmerged-raw-diffs (eq status 'unmerged))) + (delete-region (point) (+ (line-end-position) 1)) + (setq magit-last-raw-diff file) + ;; The 'diff' section that is created here will not work with + ;; magit-insert-diff-item-patch etc when we leave it empty. + ;; Luckily, raw diffs are only produced for staged and + ;; unstaged changes, and we never call + ;; magit-insert-diff-item-patch on them. This is a bit + ;; brittle, of course. + (let ((magit-section-hidden-default magit-hide-diffs)) + (magit-with-section file 'diff + (delete-region (point) (+ (line-end-position) 1)) + (if (not (magit-section-hidden magit-top-section)) + (magit-insert-diff file) + (magit-set-section-info (list status file nil)) + (magit-set-section-needs-refresh-on-show t) + (magit-insert-diff-title status file nil))))) + t) + nil)) + +(defun magit-hunk-item-diff (hunk) + (let ((diff (magit-section-parent hunk))) + (or (eq (magit-section-type diff) 'diff) + (error "Huh? Parent of hunk not a diff.")) + diff)) + +(defun magit-diff-item-insert-header (diff buf) + (let ((beg (save-excursion + (goto-char (magit-section-beginning diff)) + (forward-line) + (point))) + (end (if (magit-section-children diff) + (magit-section-beginning (car (magit-section-children diff))) + (magit-section-end diff)))) + (magit-insert-region beg end buf))) + +(defun magit-insert-diff-item-patch (diff buf) + (let ((beg (save-excursion + (goto-char (magit-section-beginning diff)) + (forward-line) + (point))) + (end (magit-section-end diff))) + (magit-insert-region beg end buf))) + +(defun magit-insert-hunk-item-patch (hunk buf) + (magit-diff-item-insert-header (magit-hunk-item-diff hunk) buf) + (magit-insert-region (magit-section-beginning hunk) (magit-section-end hunk) + buf)) + +(defun magit-insert-hunk-item-region-patch (hunk reverse beg end buf) + (magit-diff-item-insert-header (magit-hunk-item-diff hunk) buf) + (save-excursion + (goto-char (magit-section-beginning hunk)) + (magit-insert-current-line buf) + (forward-line) + (let ((copy-op (if reverse "+" "-"))) + (while (< (point) (magit-section-end hunk)) + (if (and (<= beg (point)) (< (point) end)) + (magit-insert-current-line buf) + (cond ((looking-at " ") + (magit-insert-current-line buf)) + ((looking-at copy-op) + (let ((text (buffer-substring-no-properties + (+ (point) 1) (line-beginning-position 2)))) + (with-current-buffer buf + (insert " " text)))))) + (forward-line)))) + (with-current-buffer buf + (diff-fixup-modifs (point-min) (point-max)))) + +(defun magit-hunk-item-is-conflict-p (hunk) + ;;; XXX - Using the title is a bit too clever... + (string-match "^diff --cc" + (magit-section-title (magit-hunk-item-diff hunk)))) + +(defun magit-hunk-item-target-line (hunk) + (save-excursion + (beginning-of-line) + (let ((line (line-number-at-pos))) + (if (looking-at "-") + (error "Can't visit removed lines.")) + (goto-char (magit-section-beginning hunk)) + (if (not (looking-at "@@+ .* \\+\\([0-9]+\\),[0-9]+ @@+")) + (error "Hunk header not found.")) + (let ((target (parse-integer (match-string 1)))) + (forward-line) + (while (< (line-number-at-pos) line) + ;; XXX - deal with combined diffs + (if (not (looking-at "-")) + (setq target (+ target 1))) + (forward-line)) + target)))) + +(defun magit-apply-diff-item (diff &rest args) + (when (zerop magit-diff-context-lines) + (setq args (cons "--unidiff-zero" args))) + (let ((tmp (get-buffer-create "*magit-tmp*"))) + (with-current-buffer tmp + (erase-buffer)) + (magit-insert-diff-item-patch diff "*magit-tmp*") + (apply #'magit-run-git-with-input tmp + "apply" (append args (list "-"))))) + +(defun magit-apply-hunk-item* (hunk reverse &rest args) + (when (zerop magit-diff-context-lines) + (setq args (cons "--unidiff-zero" args))) + (let ((tmp (get-buffer-create "*magit-tmp*"))) + (with-current-buffer tmp + (erase-buffer)) + (if (magit-use-region-p) + (magit-insert-hunk-item-region-patch + hunk reverse (region-beginning) (region-end) tmp) + (magit-insert-hunk-item-patch hunk tmp)) + (apply #'magit-run-git-with-input tmp + "apply" (append args (list "-"))))) + +(defun magit-apply-hunk-item (hunk &rest args) + (apply #'magit-apply-hunk-item* hunk nil args)) + +(defun magit-apply-hunk-item-reverse (hunk &rest args) + (apply #'magit-apply-hunk-item* hunk t (cons "--reverse" args))) + +(defun magit-insert-unstaged-changes (title) + (let ((magit-hide-diffs t)) + (let ((magit-diff-options '())) + (magit-git-section 'unstaged title 'magit-wash-raw-diffs + "diff-files")))) + +(defun magit-insert-staged-changes (no-commit) + (let ((magit-hide-diffs t) + (base (if no-commit + (magit-git-string "mktree") + "HEAD"))) + (let ((magit-diff-options '("--cached")) + (magit-ignore-unmerged-raw-diffs t)) + (magit-git-section 'staged "Staged changes:" 'magit-wash-raw-diffs + "diff-index" "--cached" + base)))) + +;;; Logs and Commits + +(defun magit-parse-log-ref (refname) + "Return shortened and propertized version of full REFNAME, like +\"refs/remotes/origin/master\"." + (let ((face 'magit-log-head-label)) + (cond ((string-match "^\\(tag: +\\)?refs/tags/\\(.+\\)" refname) + (setq refname (match-string 2 refname) + face 'magit-log-tag-label)) + ((string-match "^refs/remotes/\\(.+\\)" refname) + (setq refname (match-string 1 refname))) + ((string-match "[^/]+$" refname) + (setq refname (match-string 0 refname)))) + (propertize refname 'face face))) + +(defun magit-parse-log-refs (refstring) + "Parse REFSTRING annotation from `git log --decorate' +output (for example: \"refs/remotes/origin/master, +refs/heads/master\") and return prettified string for displaying +in log buffer." + (mapconcat 'identity + (mapcar 'magit-parse-log-ref + (remove-if (lambda (refname) + (string-match "/HEAD$" refname)) + (reverse (split-string refstring ", *" t)))) + " - ")) + +(defun magit-wash-log-line () + (if (and (search-forward-regexp "[0-9a-fA-F]\\{40\\}" (line-end-position) t) + (goto-char (match-beginning 0)) + (not (looking-back "commit "))) + (let ((commit (match-string-no-properties 0))) + (delete-region (match-beginning 0) (match-end 0)) + (fixup-whitespace) + (goto-char (line-beginning-position)) + (when (search-forward-regexp "^[|*\\/ ]+\\((\\(tag:.+?\\|refs/.+?\\))\\)" + (line-end-position) t) + (let ((refstring (match-string-no-properties 2))) + (delete-region (match-beginning 1) (match-end 1)) + (insert (magit-parse-log-refs refstring))) + (goto-char (line-beginning-position))) + (magit-with-section commit 'commit + (magit-set-section-info commit) + (forward-line))) + (forward-line)) + t) + +(defun magit-wash-log () + (let ((magit-old-top-section nil)) + (magit-wash-sequence #'magit-wash-log-line))) + +(defvar magit-currently-shown-commit nil) + +(defun magit-wash-commit () + (cond ((search-forward-regexp "^diff" nil t) + (goto-char (match-beginning 0)) + (magit-wash-diffs)))) + +(defun magit-refresh-commit-buffer (commit) + (magit-create-buffer-sections + (magit-git-section nil nil + 'magit-wash-commit + "log" "--max-count=1" + "--pretty=medium" + "--cc" "-p" commit))) + +(defun magit-show-commit (commit &optional scroll) + (when (magit-section-p commit) + (setq commit (magit-section-info commit))) + (let ((dir default-directory) + (buf (get-buffer-create "*magit-commit*"))) + (cond ((equal magit-currently-shown-commit commit) + (let ((win (get-buffer-window buf))) + (cond ((not win) + (display-buffer buf)) + (scroll + (with-selected-window win + (funcall scroll)))))) + (t + (setq magit-currently-shown-commit commit) + (display-buffer buf) + (with-current-buffer buf + (set-buffer buf) + (goto-char (point-min)) + (magit-mode-init dir 'commit + #'magit-refresh-commit-buffer commit)))))) + +(defvar magit-marked-commit nil) + +(defvar magit-mark-overlay nil) +(make-variable-buffer-local 'magit-mark-overlay) +(put 'magit-mark-overlay 'permanent-local t) + +(defun magit-refresh-marked-commits () + (magit-for-all-buffers #'magit-refresh-marked-commits-in-buffer)) + +(defun magit-refresh-marked-commits-in-buffer () + (if (not magit-mark-overlay) + (let ((ov (make-overlay 1 1))) + (overlay-put ov 'face 'magit-item-mark) + (setq magit-mark-overlay ov))) + (delete-overlay magit-mark-overlay) + (magit-for-all-sections + (lambda (section) + (when (and (eq (magit-section-type section) 'commit) + (equal (magit-section-info section) + magit-marked-commit)) + (move-overlay magit-mark-overlay + (magit-section-beginning section) + (magit-section-end section) + (current-buffer)))))) + +(defun magit-set-marked-commit (commit) + (setq magit-marked-commit commit) + (magit-refresh-marked-commits)) + +(defun magit-marked-commit () + (or magit-marked-commit + (error "No commit marked"))) + +(defun magit-insert-unpulled-commits (remote branch) + (magit-git-section 'unpulled + "Unpulled commits:" 'magit-wash-log + "log" "--pretty=format:* %H %s" + (format "HEAD..%s/%s" remote branch))) + +(defun magit-insert-unpushed-commits (remote branch) + (magit-git-section 'unpushed + "Unpushed commits:" 'magit-wash-log + "log" "--pretty=format:* %H %s" + (format "%s/%s..HEAD" remote branch))) + +(defun magit-insert-unpulled-svn-commits () + (magit-git-section 'svn-unpulled + "Unpulled commits (SVN):" 'magit-wash-log + "log" "--pretty=format:* %H %s" + (format "HEAD..%s" (magit-get-svn-ref)))) + +(defun magit-insert-unpushed-svn-commits () + (magit-git-section 'svn-unpushed + "Unpushed commits (SVN):" 'magit-wash-log + "log" "--pretty=format:* %H %s" + (format "%s..HEAD" (magit-get-svn-ref)))) + +;;; Status + +(defun magit-refresh-status () + (magit-create-buffer-sections + (magit-with-section 'status nil + (let* ((branch (magit-get-current-branch)) + (remote (and branch (magit-get "branch" branch "remote"))) + (svn-enabled (magit-svn-enabled)) + (head (magit-git-string + "log" "--max-count=1" "--abbrev-commit" "--pretty=oneline")) + (no-commit (not head))) + (if remote + (insert (format "Remote: %s %s\n" + remote (magit-get "remote" remote "url")))) + (insert (format "Local: %s %s\n" + (propertize (or branch "(detached)") + 'face 'magit-branch) + (abbreviate-file-name default-directory))) + (insert (format "Head: %s\n" + (if no-commit "nothing commited (yet)" head))) + (let ((merge-heads (magit-file-lines ".git/MERGE_HEAD"))) + (if merge-heads + (insert (format "Merging: %s\n" + (magit-concat-with-delim + ", " + (mapcar 'magit-name-rev merge-heads)))))) + (let ((rebase (magit-rebase-info))) + (if rebase + (insert (apply 'format "Rebasing: %s (%s of %s)\n" rebase)))) + (insert "\n") + (magit-git-exit-code "update-index" "--refresh") + (magit-insert-untracked-files) + (magit-insert-stashes) + (magit-insert-topics) + (magit-insert-pending-changes) + (magit-insert-pending-commits) + (when remote + (magit-insert-unpulled-commits remote branch)) + (when svn-enabled + (magit-insert-unpulled-svn-commits)) + (let ((staged (or no-commit (magit-anything-staged-p)))) + (magit-insert-unstaged-changes + (if staged "Unstaged changes:" "Changes:")) + (if staged + (magit-insert-staged-changes no-commit))) + (when remote + (magit-insert-unpushed-commits remote branch)) + (when svn-enabled + (magit-insert-unpushed-svn-commits)))))) + +(defun magit-init (dir) + "Initialize git repository in specified directory" + (interactive (list (read-directory-name "Directory for Git repository: "))) + (let ((topdir (magit-get-top-dir dir))) + (when (or (not topdir) + (yes-or-no-p + (format + (if (string-equal topdir (expand-file-name dir)) + "There is already a Git repository in %s. Reinitialize? " + "There is a Git repository in %s. Create another in %s? ") + topdir dir))) + (unless (file-directory-p dir) + (and (y-or-n-p (format "Directory %s does not exists. Create it? " dir)) + (make-directory dir))) + (let ((default-directory dir)) + (magit-run* (list "git" "init")))))) + +;;;###autoload +(defun magit-status (dir) + (interactive (list (or (and (not current-prefix-arg) + (magit-get-top-dir default-directory)) + (magit-read-top-dir (and (consp current-prefix-arg) + (> (car current-prefix-arg) 4)))))) + (if magit-save-some-buffers + (save-some-buffers (eq magit-save-some-buffers 'dontask))) + (let ((topdir (magit-get-top-dir dir))) + (unless topdir + (when (y-or-n-p (format "There is no Git repository in %S. Create one? " + dir)) + (magit-init dir) + (setq topdir (magit-get-top-dir dir)))) + (when topdir + (let ((buf (or (magit-find-buffer 'status topdir) + (switch-to-buffer + (get-buffer-create + (concat "*magit: " + (file-name-nondirectory + (directory-file-name topdir)) "*")))))) + (switch-to-buffer buf) + (magit-mode-init topdir 'status #'magit-refresh-status))))) + +;;; Staging and Unstaging + +(defun magit-stage-item () + "Add the item at point to the staging area." + (interactive) + (magit-section-action (item info "stage") + ((untracked file) + (magit-run-git "add" info)) + ((untracked) + (apply #'magit-run-git "add" "--" + (magit-git-lines "ls-files" "--other" "--exclude-standard"))) + ((unstaged diff hunk) + (if (magit-hunk-item-is-conflict-p item) + (error (concat "Can't stage individual resolution hunks. " + "Please stage the whole file."))) + (magit-apply-hunk-item item "--cached")) + ((unstaged diff) + (magit-run-git "add" "-u" (magit-diff-item-file item))) + ((staged *) + (error "Already staged")) + ((hunk) + (error "Can't stage this hunk")) + ((diff) + (error "Can't stage this diff")))) + +(defun magit-unstage-item () + "Remove the item at point from the staging area." + (interactive) + (magit-section-action (item info "unstage") + ((staged diff hunk) + (magit-apply-hunk-item-reverse item "--cached")) + ((staged diff) + (if (eq (car info) 'unmerged) + (error "Can't unstage a unmerged file. Resolve it first.")) + (magit-run-git "reset" "-q" "HEAD" "--" (magit-diff-item-file item))) + ((unstaged *) + (error "Already unstaged")) + ((hunk) + (error "Can't unstage this hunk")) + ((diff) + (error "Can't unstage this diff")))) + +(defun magit-stage-all (&optional also-untracked-p) + (interactive "P") + (if also-untracked-p + (magit-run-git "add" ".") + (magit-run-git "add" "-u" "."))) + +(defun magit-unstage-all () + (interactive) + (magit-run-git "reset" "HEAD")) + +;;; Branches + +(defun magit-maybe-create-local-tracking-branch (rev) + (if (string-match "^refs/remotes/\\([^/]+\\)/\\(.+\\)" rev) + (let ((remote (match-string 1 rev)) + (branch (match-string 2 rev))) + (when (and (not (magit-ref-exists-p (concat "refs/heads/" branch))) + (yes-or-no-p + (format "Create local tracking branch for %s? " branch))) + (magit-run-git "checkout" "-b" branch rev) + t)) + nil)) + +(defun magit-checkout (rev) + (interactive (list (magit-read-rev "Switch to" (magit-default-rev)))) + (if rev + (if (not (magit-maybe-create-local-tracking-branch rev)) + (magit-run-git "checkout" (magit-rev-to-git rev))))) + +(defun magit-read-create-branch-args () + (let* ((cur-branch (magit-get-current-branch)) + (branch (read-string "Create branch: ")) + (parent (magit-read-rev "Parent" cur-branch))) + (list branch parent))) + +(defun magit-create-branch (branch parent) + (interactive (magit-read-create-branch-args)) + (if (and branch (not (string= branch "")) + parent) + (magit-run-git "checkout" "-b" + branch + (magit-rev-to-git parent)))) + +;;; Merging + +(defun magit-guess-branch () + (let ((sec (magit-current-section))) + (if (and sec (eq (magit-section-type sec) 'wazzup)) + (magit-section-info sec)))) + +(defun magit-manual-merge (rev) + (interactive (list (magit-read-rev "Manually merge" (magit-guess-branch)))) + (if rev + (magit-run-git "merge" "--no-ff" "--no-commit" + (magit-rev-to-git rev)))) + +(defun magit-automatic-merge (rev) + (interactive (list (magit-read-rev "Merge" (magit-guess-branch)))) + (if rev + (magit-run-git "merge" (magit-rev-to-git rev)))) + +;;; Rebasing + +(defun magit-rebase-info () + (cond ((file-exists-p ".git/rebase-apply") + (list (magit-name-rev + (car (magit-file-lines ".git/rebase-apply/onto"))) + (car (magit-file-lines ".git/rebase-apply/next")) + (car (magit-file-lines ".git/rebase-apply/last")))) + ((file-exists-p ".dotest") + (list (magit-name-rev (car (magit-file-lines ".dotest/onto"))) + (car (magit-file-lines ".dotest/next")) + (car (magit-file-lines ".dotest/last")))) + ((file-exists-p ".git/.dotest-merge") + (list (car (magit-file-lines ".git/.dotest-merge/onto_name")) + (car (magit-file-lines ".git/.dotest-merge/msgnum")) + (car (magit-file-lines ".git/.dotest-merge/end")))) + (t + nil))) + +(defun magit-rebase-step () + (interactive) + (let ((info (magit-rebase-info))) + (if (not info) + (let ((rev (magit-read-rev "Rebase to"))) + (if rev + (magit-run-git "rebase" (magit-rev-to-git rev)))) + (let ((cursor-in-echo-area t) + (message-log-max nil)) + (message "Rebase in progress. Abort, Skip, or Continue? ") + (let ((reply (read-event))) + (case reply + ((?A ?a) + (magit-run-git "rebase" "--abort")) + ((?S ?s) + (magit-run-git "rebase" "--skip")) + ((?C ?c) + (magit-run-git "rebase" "--continue")))))))) + +;; git svn commands + +(defun magit-svn-rebase () + (interactive) + (magit-run-git-async "svn" "rebase")) + +(defun magit-svn-dcommit () + (interactive) + (magit-run-git-async "svn" "dcommit")) + +(defun magit-svn-enabled () + (not (null (magit-get-svn-ref)))) + +(defun magit-get-svn-ref () + (cond ((magit-ref-exists-p "refs/remotes/git-svn") + "refs/remotes/git-svn") + ((magit-ref-exists-p "refs/remotes/trunk") + "refs/remotes/trunk") + (t + nil))) + +;;; Resetting + +(defun magit-reset-head (rev &optional hard) + (interactive (list (magit-read-rev (format "%s head to" + (if current-prefix-arg + "Hard reset" + "Reset")) + (or (magit-default-rev) + "HEAD^")) + current-prefix-arg)) + (if rev + (magit-run-git "reset" (if hard "--hard" "--soft") + (magit-rev-to-git rev)))) + +(defun magit-reset-working-tree () + (interactive) + (if (yes-or-no-p "Discard all uncommitted changes? ") + (magit-run-git "reset" "--hard"))) + +;;; Rewriting + +(defun magit-read-rewrite-info () + (when (file-exists-p ".git/magit-rewrite-info") + (with-temp-buffer + (insert-file-contents ".git/magit-rewrite-info") + (goto-char (point-min)) + (read (current-buffer))))) + +(defun magit-write-rewrite-info (info) + (with-temp-file ".git/magit-rewrite-info" + (prin1 info (current-buffer)) + (princ "\n" (current-buffer)))) + +(defun magit-insert-pending-commits () + (let* ((info (magit-read-rewrite-info)) + (pending (cdr (assq 'pending info)))) + (when pending + (magit-with-section 'pending nil + (insert (propertize "Pending commits:\n" + 'face 'magit-section-title)) + (dolist (p pending) + (let* ((commit (car p)) + (properties (cdr p)) + (used (plist-get properties 'used))) + (magit-with-section commit 'commit + (magit-set-section-info commit) + (insert (magit-git-string + "log" "--max-count=1" + (if used + "--pretty=format:. %s" + "--pretty=format:* %s") + commit "--") + "\n"))))) + (insert "\n")))) + +(defun magit-rewrite-set-commit-property (commit prop value) + (let* ((info (magit-read-rewrite-info)) + (pending (cdr (assq 'pending info))) + (p (assoc commit pending))) + (when p + (setf (cdr p) (plist-put (cdr p) prop value)) + (magit-write-rewrite-info info) + (magit-need-refresh)))) + +(defun magit-rewrite-set-used () + (interactive) + (magit-section-action (item info) + ((pending commit) + (magit-rewrite-set-commit-property info 'used t)))) + +(defun magit-rewrite-set-unused () + (interactive) + (magit-section-action (item info) + ((pending commit) + (magit-rewrite-set-commit-property info 'used nil)))) + +(defun magit-insert-pending-changes () + (let* ((info (magit-read-rewrite-info)) + (orig (cadr (assq 'orig info)))) + (when orig + (let ((magit-hide-diffs t)) + (magit-git-section 'pending-changes + "Pending changes" + 'magit-wash-diffs + "diff" (magit-diff-U-arg) "-R" orig))))) + +(defun magit-rewrite-start (from &optional onto) + (interactive (list (magit-read-rev "Rewrite from" (magit-default-rev)))) + (or (magit-everything-clean-p) + (error "You have uncommitted changes.")) + (or (not (magit-read-rewrite-info)) + (error "Rewrite in progress.")) + (let* ((orig (magit-git-string "rev-parse" "HEAD")) + (base (or (car (magit-commit-parents from)) + (error "Can't rewrite a commit without a parent, sorry."))) + (pending (magit-git-lines "rev-list" (concat base "..")))) + (magit-write-rewrite-info `((orig ,orig) + (pending ,@(mapcar #'list pending)))) + (magit-run-git "reset" "--hard" base))) + +(defun magit-rewrite-stop (&optional noconfirm) + (interactive) + (let* ((info (magit-read-rewrite-info))) + (or info + (error "No rewrite in progress.")) + (when (or noconfirm + (yes-or-no-p "Stop rewrite? ")) + (magit-write-rewrite-info nil) + (magit-need-refresh)))) + +(defun magit-rewrite-abort () + (interactive) + (let* ((info (magit-read-rewrite-info)) + (orig (cadr (assq 'orig info)))) + (or info + (error "No rewrite in progress.")) + (or (magit-everything-clean-p) + (error "You have uncommitted changes.")) + (when (yes-or-no-p "Abort rewrite? ") + (magit-write-rewrite-info nil) + (magit-run-git "reset" "--hard" orig)))) + +(defun magit-rewrite-finish () + (interactive) + (magit-with-refresh + (magit-rewrite-finish-step t))) + +(defun magit-rewrite-finish-step (first-p) + (let ((info (magit-read-rewrite-info))) + (or info + (error "No rewrite in progress.")) + (let* ((pending (cdr (assq 'pending info))) + (first-unused (find-if (lambda (p) + (not (plist-get (cdr p) 'used))) + pending + :from-end t)) + (commit (car first-unused))) + (cond ((not first-unused) + (magit-rewrite-stop t)) + ((magit-apply-commit commit t (not first-p)) + (magit-rewrite-set-commit-property commit 'used t) + (magit-rewrite-finish-step nil)))))) + +;;; Updating, pull, and push + +(defun magit-remote-update () + (interactive) + (if (magit-svn-enabled) + (magit-run-git-async "svn" "fetch") + (magit-run-git-async "remote" "update"))) + +(defun magit-pull () + (interactive) + (let* ((branch (magit-get-current-branch)) + (config-branch (and branch (magit-get "branch" branch "merge"))) + (merge-branch (or config-branch + (magit-read-rev (format "Pull from"))))) + (if (and branch (not config-branch)) + (magit-set merge-branch "branch" branch "merge")) + (magit-run-git-async "pull" "-v"))) + +(defun magit-shell-command (command) + (interactive "sCommand: ") + (require 'pcomplete) + (let ((args (car (with-temp-buffer + (insert command) + (pcomplete-parse-buffer-arguments)))) + (magit-process-popup-time 0)) + (magit-run* args nil nil nil t))) + +(defun magit-read-remote (prompt def) + (completing-read (if def + (format "%s (default %s): " prompt def) + (format "%s: " prompt)) + (magit-git-lines "remote") + nil nil nil nil def)) + +(defun magit-push () + (interactive) + (let* ((branch (or (magit-get-current-branch) + (error "Don't push a detached head. That's gross."))) + (branch-remote (magit-get "branch" branch "remote")) + (push-remote (if (or current-prefix-arg + (not branch-remote)) + (magit-read-remote (format "Push %s to" branch) + branch-remote) + branch-remote))) + (if (and (not branch-remote) + (not current-prefix-arg)) + (magit-set push-remote "branch" branch "remote")) + (magit-run-git-async "push" "-v" push-remote branch))) + +;;; Log edit mode + +(defvar magit-log-edit-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "C-c C-c") 'magit-log-edit-commit) + (define-key map (kbd "C-c C-a") 'magit-log-edit-toggle-amending) + (define-key map (kbd "C-c C-s") 'magit-log-edit-toggle-signoff) + (define-key map (kbd "M-p") 'log-edit-previous-comment) + (define-key map (kbd "M-n") 'log-edit-next-comment) + (define-key map (kbd "C-c C-k") 'magit-log-edit-cancel-log-message) + map)) + +(defvar magit-pre-log-edit-window-configuration nil) + +(defun magit-log-fill-paragraph (&optional justify) + "Fill the paragraph, but preserve open parentheses at beginning of lines. +Prefix arg means justify as well." + (interactive "P") + ;; Add lines starting with a left paren or an asterisk. + (let ((paragraph-start (concat paragraph-start "\\|*\\|("))) + (let ((end (progn (forward-paragraph) (point))) + (beg (progn (backward-paragraph) (point))) + (adaptive-fill-mode nil)) + (fill-region beg end justify) + t))) + +(define-derived-mode magit-log-edit-mode text-mode "Magit Log Edit" + (set (make-local-variable 'fill-paragraph-function) + 'magit-log-fill-paragraph) + (use-local-map magit-log-edit-map)) + +(defun magit-log-edit-cleanup () + (save-excursion + (goto-char (point-min)) + (flush-lines "^#") + (goto-char (point-min)) + (if (re-search-forward "[ \t\n]*\\'" nil t) + (replace-match "\n" nil nil)))) + +(defun magit-log-edit-append (str) + (save-excursion + (set-buffer (get-buffer-create "*magit-log-edit*")) + (goto-char (point-max)) + (insert str "\n"))) + +(defconst magit-log-header-end "-- End of Magit header --\n") + +(defun magit-log-edit-get-fields () + (let ((buf (get-buffer "*magit-log-edit*")) + (result nil)) + (if buf + (save-excursion + (set-buffer buf) + (goto-char (point-min)) + (while (looking-at "^\\([A-Za-z0-9-_]+\\): *\\(.*\\)$") + (setq result (acons (intern (downcase (match-string 1))) + (match-string 2) + result)) + (forward-line)) + (if (not (looking-at (regexp-quote magit-log-header-end))) + (setq result nil)))) + (nreverse result))) + +(defun magit-log-edit-set-fields (fields) + (let ((buf (get-buffer-create "*magit-log-edit*"))) + (save-excursion + (set-buffer buf) + (goto-char (point-min)) + (if (search-forward-regexp (format "^\\([A-Za-z0-9-_]+:.*\n\\)+%s" + (regexp-quote magit-log-header-end)) + nil t) + (delete-region (match-beginning 0) (match-end 0))) + (goto-char (point-min)) + (when fields + (while fields + (insert (capitalize (symbol-name (caar fields))) ": " + (cdar fields) "\n") + (setq fields (cdr fields))) + (insert magit-log-header-end))))) + +(defun magit-log-edit-set-field (name value) + (let* ((fields (magit-log-edit-get-fields)) + (cell (assq name fields))) + (cond (cell + (if value + (rplacd cell value) + (setq fields (delq cell fields)))) + (t + (if value + (setq fields (append fields (list (cons name value))))))) + (magit-log-edit-set-fields fields))) + +(defun magit-log-edit-get-field (name) + (cdr (assq name (magit-log-edit-get-fields)))) + +(defun magit-log-edit-setup-author-env (author) + (cond (author + ;; XXX - this is a bit strict, probably. + (or (string-match "\\(.*\\) <\\(.*\\)>, \\(.*\\)" author) + (error "Can't parse author string.")) + ;; Shucks, setenv destroys the match data. + (let ((name (match-string 1 author)) + (email (match-string 2 author)) + (date (match-string 3 author))) + (setenv "GIT_AUTHOR_NAME" name) + (setenv "GIT_AUTHOR_EMAIL" email) + (setenv "GIT_AUTHOR_DATE" date))) + (t + (setenv "GIT_AUTHOR_NAME") + (setenv "GIT_AUTHOR_EMAIL") + (setenv "GIT_AUTHOR_DATE")))) + +(defun magit-log-edit-push-to-comment-ring (comment) + (when (or (ring-empty-p log-edit-comment-ring) + (not (equal comment (ring-ref log-edit-comment-ring 0)))) + (ring-insert log-edit-comment-ring comment))) + +(defun magit-log-edit-commit () + (interactive) + (let* ((fields (magit-log-edit-get-fields)) + (amend (equal (cdr (assq 'amend fields)) "yes")) + (commit-all (equal (cdr (assq 'commit-all fields)) "yes")) + (sign-off-field (assq 'sign-off fields)) + (sign-off (if sign-off-field + (equal (cdr sign-off-field) "yes") + magit-commit-signoff)) + (tag (cdr (assq 'tag fields))) + (author (cdr (assq 'author fields)))) + (magit-log-edit-push-to-comment-ring (buffer-string)) + (magit-log-edit-setup-author-env author) + (magit-log-edit-set-fields nil) + (magit-log-edit-cleanup) + (if (= (buffer-size) 0) + (insert "(Empty description)\n")) + (let ((commit-buf (current-buffer))) + (with-current-buffer (magit-find-buffer 'status default-directory) + (cond (tag + (magit-run-git-with-input commit-buf "tag" tag "-a" "-F" "-")) + (t + (apply #'magit-run-async-with-input commit-buf + magit-git-executable + (append magit-git-standard-options + (list "commit" "-F" "-") + (if commit-all '("--all") '()) + (if amend '("--amend") '()) + (if sign-off '("--signoff") '()))))))) + (erase-buffer) + (bury-buffer) + (when (file-exists-p ".git/MERGE_MSG") + (delete-file ".git/MERGE_MSG")) + (when magit-pre-log-edit-window-configuration + (set-window-configuration magit-pre-log-edit-window-configuration) + (setq magit-pre-log-edit-window-configuration nil)))) + +(defun magit-log-edit-cancel-log-message () + (interactive) + (when (or (not magit-log-edit-confirm-cancellation) + (yes-or-no-p + "Really cancel editing the log (any changes will be lost)?")) + (erase-buffer) + (bury-buffer) + (when magit-pre-log-edit-window-configuration + (set-window-configuration magit-pre-log-edit-window-configuration) + (setq magit-pre-log-edit-window-configuration nil)))) + +(defun magit-log-edit-toggle-amending () + (interactive) + (let* ((fields (magit-log-edit-get-fields)) + (cell (assq 'amend fields))) + (if cell + (rplacd cell (if (equal (cdr cell) "yes") "no" "yes")) + (setq fields (acons 'amend "yes" fields)) + (magit-log-edit-append + (magit-format-commit "HEAD" "%s%n%n%b"))) + (magit-log-edit-set-fields fields))) + +(defun magit-log-edit-toggle-signoff () + (interactive) + (let* ((fields (magit-log-edit-get-fields)) + (cell (assq 'sign-off fields))) + (if cell + (rplacd cell (if (equal (cdr cell) "yes") "no" "yes")) + (setq fields (acons 'sign-off (if magit-commit-signoff "no" "yes") + fields))) + (magit-log-edit-set-fields fields))) + +(defun magit-pop-to-log-edit (operation) + (let ((dir default-directory) + (buf (get-buffer-create "*magit-log-edit*"))) + (setq magit-pre-log-edit-window-configuration + (current-window-configuration)) + (pop-to-buffer buf) + (when (file-exists-p ".git/MERGE_MSG") + (insert-file-contents ".git/MERGE_MSG")) + (setq default-directory dir) + (magit-log-edit-mode) + (message "Type C-c C-c to %s (C-c C-k to cancel)." operation))) + +(defun magit-log-edit () + (interactive) + (cond ((magit-rebase-info) + (if (y-or-n-p "Rebase in progress. Continue it? ") + (magit-run-git "rebase" "--continue"))) + (t + (magit-log-edit-set-field 'tag nil) + (when (and magit-commit-all-when-nothing-staged + (not (magit-anything-staged-p))) + (cond ((eq magit-commit-all-when-nothing-staged 'ask-stage) + (if (and (not (magit-everything-clean-p)) + (y-or-n-p "Nothing staged. Stage everything now? ")) + (magit-stage-all))) + ((not (magit-log-edit-get-field 'commit-all)) + (magit-log-edit-set-field + 'commit-all + (if (or (eq magit-commit-all-when-nothing-staged t) + (y-or-n-p + "Nothing staged. Commit all unstaged changes? ")) + "yes" "no"))))) + (magit-pop-to-log-edit "commit")))) + +(defun magit-add-log () + (interactive) + (cond ((magit-rebase-info) + (if (y-or-n-p "Rebase in progress. Continue it? ") + (magit-run-git "rebase" "--continue"))) + (t + (let ((section (magit-current-section))) + (let ((fun (if (eq (magit-section-type section) 'hunk) + (save-window-excursion + (save-excursion + (magit-visit-item) + (add-log-current-defun))) + nil)) + (file (magit-diff-item-file + (cond ((eq (magit-section-type section) 'hunk) + (magit-hunk-item-diff section)) + ((eq (magit-section-type section) 'diff) + section) + (t + (error "No change at point")))))) + (magit-log-edit) + (goto-char (point-min)) + (cond ((not (search-forward-regexp + (format "^\\* %s" (regexp-quote file)) nil t)) + ;; No entry for file, create it. + (goto-char (point-max)) + (insert (format "\n* %s" file)) + (if fun + (insert (format " (%s)" fun))) + (insert ": ")) + (fun + ;; found entry for file, look for fun + (let ((limit (or (save-excursion + (and (search-forward-regexp "^\\* " + nil t) + (match-beginning 0))) + (point-max)))) + (cond ((search-forward-regexp (format "(.*\\<%s\\>.*):" + (regexp-quote fun)) + limit t) + ;; found it, goto end of current entry + (if (search-forward-regexp "^(" limit t) + (backward-char 2) + (goto-char limit))) + (t + ;; not found, insert new entry + (goto-char limit) + (if (bolp) + (open-line 1) + (newline)) + (insert (format "(%s): " fun)))))))))))) + +;;; Tags + +(defun magit-tag (name) + (interactive "sNew tag name: ") + (magit-run-git "tag" name)) + +(defun magit-annotated-tag (name) + (interactive "sNew tag name: ") + (magit-log-edit-set-field 'tag name) + (magit-pop-to-log-edit "tag")) + +;;; Stashing + +(defun magit-wash-stash () + (if (search-forward-regexp "stash@{\\(.*\\)}" (line-end-position) t) + (let ((stash (match-string-no-properties 0)) + (name (match-string-no-properties 1))) + (delete-region (match-beginning 0) (match-end 0)) + (goto-char (match-beginning 0)) + (fixup-whitespace) + (goto-char (line-beginning-position)) + (insert name) + (goto-char (line-beginning-position)) + (magit-with-section stash 'stash + (magit-set-section-info stash) + (forward-line))) + (forward-line)) + t) + +(defun magit-wash-stashes () + (let ((magit-old-top-section nil)) + (magit-wash-sequence #'magit-wash-stash))) + +(defun magit-insert-stashes () + (magit-git-section 'stashes + "Stashes:" 'magit-wash-stashes + "stash" "list")) + +(defun magit-stash (description) + (interactive "sStash description: ") + (magit-run-git "stash" "save" description)) + +(defun magit-stash-snapshot () + (interactive) + (magit-with-refresh + (magit-run-git "stash" "save" + (format-time-string "Snapshot taken at %Y-%m-%d %H:%M:%S" + (current-time))) + (magit-run-git "stash" "apply" "stash@{0}"))) + +(defvar magit-currently-shown-stash nil) + +(defun magit-show-stash (stash &optional scroll) + (when (magit-section-p stash) + (setq stash (magit-section-info stash))) + (let ((dir default-directory) + (buf (get-buffer-create "*magit-stash*"))) + (cond ((equal magit-currently-shown-stash stash) + (let ((win (get-buffer-window buf))) + (cond ((not win) + (display-buffer buf)) + (scroll + (with-selected-window win + (funcall scroll)))))) + (t + (setq magit-currently-shown-stash stash) + (display-buffer buf) + (with-current-buffer buf + (set-buffer buf) + (goto-char (point-min)) + (let* ((range (cons (concat stash "^2^") stash)) + (args (magit-rev-range-to-git range))) + (magit-mode-init dir 'diff #'magit-refresh-diff-buffer + range args))))))) + +;;; Topic branches (using topgit) + +(defun magit-wash-topic () + (if (search-forward-regexp "^..\\(t/\\S-+\\)\\s-+\\(\\S-+\\)\\s-+\\(\\S-+\\)" + (line-end-position) t) + (let ((topic (match-string 1))) + (delete-region (match-beginning 2) (match-end 2)) + (goto-char (line-beginning-position)) + (delete-char 4) + (insert "\t") + (goto-char (line-beginning-position)) + (magit-with-section topic 'topic + (magit-set-section-info topic) + (forward-line))) + (delete-region (line-beginning-position) (1+ (line-end-position)))) + t) + +(defun magit-wash-topics () + (let ((magit-old-top-section nil)) + (magit-wash-sequence #'magit-wash-topic))) + +(defun magit-insert-topics () + (magit-git-section 'topics + "Topics:" 'magit-wash-topics + "branch" "-v")) + +;;; Commits + +(defun magit-commit-at-point (&optional nil-ok-p) + (let* ((section (magit-current-section)) + (commit (and (eq (magit-section-type section) 'commit) + (magit-section-info section)))) + (if nil-ok-p + commit + (or commit + (error "No commit at point."))))) + +(defun magit-apply-commit (commit &optional docommit noerase revert) + (let* ((parent-id (magit-choose-parent-id commit "cherry-pick")) + (success (magit-run* `(,magit-git-executable + ,@magit-git-standard-options + ,(if revert "revert" "cherry-pick") + ,@(if parent-id + (list "-m" (number-to-string parent-id))) + ,@(if (not docommit) (list "--no-commit")) + ,commit) + nil noerase))) + (when (or (not docommit) success) + (cond (revert + (magit-log-edit-append + (magit-format-commit commit "Reverting \"%s\""))) + (t + (magit-log-edit-append + (magit-format-commit commit "%s%n%n%b")) + (magit-log-edit-set-field + 'author + (magit-format-commit commit "%an <%ae>, %ai"))))) + success)) + +(defun magit-apply-item () + (interactive) + (magit-section-action (item info "apply") + ((pending commit) + (magit-apply-commit info) + (magit-rewrite-set-commit-property info 'used t)) + ((commit) + (magit-apply-commit info)) + ((unstaged *) + (error "Change is already in your working tree")) + ((staged *) + (error "Change is already in your working tree")) + ((hunk) + (magit-apply-hunk-item item)) + ((diff) + (magit-apply-diff-item item)) + ((stash) + (magit-run-git "stash" "apply" info)))) + +(defun magit-cherry-pick-item () + (interactive) + (magit-section-action (item info "cherry-pick") + ((pending commit) + (magit-apply-commit info t) + (magit-rewrite-set-commit-property info 'used t)) + ((commit) + (magit-apply-commit info t)) + ((stash) + (magit-run-git "stash" "pop" info)))) + +(defun magit-revert-item () + (interactive) + (magit-section-action (item info "revert") + ((pending commit) + (magit-apply-commit info nil nil t) + (magit-rewrite-set-commit-property info 'used nil)) + ((commit) + (magit-apply-commit info nil nil t)) + ((hunk) + (magit-apply-hunk-item-reverse item)) + ((diff) + (magit-apply-diff-item item "--reverse")))) + +(defvar magit-have-graph 'unset) +(defvar magit-have-decorate 'unset) +(make-variable-buffer-local 'magit-have-graph) +(put 'magit-have-graph 'permanent-local t) +(make-variable-buffer-local 'magit-have-decorate) +(put 'magit-have-decorate 'permanent-local t) + +(defun magit-configure-have-graph () + (if (eq magit-have-graph 'unset) + (let ((res (magit-git-exit-code "log" "--graph" "--max-count=0"))) + (setq magit-have-graph (eq res 0))))) + +(defun magit-configure-have-decorate () + (if (eq magit-have-decorate 'unset) + (let ((res (magit-git-exit-code "log" "--decorate=full" "--max-count=0"))) + (setq magit-have-decorate (eq res 0))))) + +(defun magit-refresh-log-buffer (range style args) + (magit-configure-have-graph) + (magit-configure-have-decorate) + (magit-create-buffer-sections + (apply #'magit-git-section nil + (magit-rev-range-describe range "Commits") + 'magit-wash-log + `("log" + ,(format "--max-count=%s" magit-log-cutoff-length) + ,style + ,@(if magit-have-decorate (list "--decorate=full")) + ,@(if magit-have-graph (list "--graph")) + ,args "--")))) + +(defun magit-log (&optional arg) + (interactive "P") + (let* ((range (if arg + (magit-read-rev-range "Log" "HEAD") + "HEAD")) + (topdir (magit-get-top-dir default-directory)) + (args (magit-rev-range-to-git range))) + (switch-to-buffer "*magit-log*") + (magit-mode-init topdir 'log #'magit-refresh-log-buffer range + "--pretty=oneline" args))) + +(defun magit-log-long (&optional arg) + (interactive "P") + (let* ((range (if arg + (magit-read-rev-range "Long log" "HEAD") + "HEAD")) + (topdir (magit-get-top-dir default-directory)) + (args (magit-rev-range-to-git range))) + (switch-to-buffer "*magit-log*") + (magit-mode-init topdir 'log #'magit-refresh-log-buffer range + "--stat" args))) + +;;; Reflog + +(defun magit-refresh-reflog-buffer (head args) + (magit-create-buffer-sections + (magit-git-section 'reflog + (format "Local history of head %s" head) + 'magit-wash-log + "log" "--walk-reflogs" + (format "--max-count=%s" magit-log-cutoff-length) + "--pretty=oneline" + args))) + +(defun magit-reflog (head) + (interactive (list (magit-read-rev "Reflog of" "HEAD"))) + (if head + (let* ((topdir (magit-get-top-dir default-directory)) + (args (magit-rev-to-git head))) + (switch-to-buffer "*magit-reflog*") + (magit-mode-init topdir 'reflog + #'magit-refresh-reflog-buffer head args)))) + +(defun magit-reflog-head () + (interactive) + (magit-reflog "HEAD")) + +;;; Diffing + +(defun magit-refresh-diff-buffer (range args) + (magit-create-buffer-sections + (magit-git-section 'diffbuf + (magit-rev-range-describe range "Changes") + 'magit-wash-diffs + "diff" (magit-diff-U-arg) args))) + +(defun magit-diff (range) + (interactive (list (magit-read-rev-range "Diff"))) + (if range + (let* ((dir default-directory) + (args (magit-rev-range-to-git range)) + (buf (get-buffer-create "*magit-diff*"))) + (display-buffer buf) + (save-excursion + (set-buffer buf) + (magit-mode-init dir 'diff #'magit-refresh-diff-buffer range args))))) + +(defun magit-diff-working-tree (rev) + (interactive (list (magit-read-rev "Diff with (default HEAD)"))) + (magit-diff (or rev "HEAD"))) + +(defun magit-diff-with-mark () + (interactive) + (magit-diff (cons (magit-marked-commit) + (magit-commit-at-point)))) + +;;; Wazzup + +(defun magit-wazzup-toggle-ignore (branch edit) + (let ((ignore-file ".git/info/wazzup-exclude")) + (if edit + (setq branch (read-string "Branch to ignore for wazzup: " branch))) + (let ((ignored (magit-file-lines ignore-file))) + (cond ((member branch ignored) + (when (or (not edit) + (y-or-n-p "Branch %s is already ignored. Unignore?")) + (setq ignored (delete branch ignored)))) + (t + (setq ignored (append ignored (list branch))))) + (magit-write-file-lines ignore-file ignored) + (magit-need-refresh)))) + +(defun magit-refresh-wazzup-buffer (head all) + (magit-create-buffer-sections + (magit-with-section 'wazzupbuf nil + (insert (format "Wazzup, %s\n\n" head)) + (let* ((excluded (magit-file-lines ".git/info/wazzup-exclude")) + (all-branches (magit-list-interesting-refs)) + (branches (if all all-branches + (remove-if (lambda (b) (member (cdr b) excluded)) + all-branches))) + (reported (make-hash-table :test #'equal))) + (dolist (branch branches) + (let* ((name (car branch)) + (ref (cdr branch)) + (hash (magit-git-string "rev-parse" ref)) + (reported-branch (gethash hash reported))) + (unless (or (and reported-branch + (string= (file-name-nondirectory ref) + reported-branch)) + (not (magit-git-string "merge-base" head ref))) + (puthash hash (file-name-nondirectory ref) reported) + (let* ((n (length (magit-git-lines "log" "--pretty=oneline" + (concat head ".." ref)))) + (section + (let ((magit-section-hidden-default t)) + (magit-git-section + (cons ref 'wazzup) + (format "%s unmerged commits in %s%s" + n name + (if (member ref excluded) + " (normally ignored)" + "")) + 'magit-wash-log + "log" + (format "--max-count=%s" magit-log-cutoff-length) + "--pretty=oneline" + (format "%s..%s" head ref) + "--")))) + (magit-set-section-info ref section))))))))) + +(defun magit-wazzup (&optional all) + (interactive "P") + (let* ((topdir (magit-get-top-dir default-directory))) + (switch-to-buffer "*magit-wazzup*") + (magit-mode-init topdir 'wazzup + #'magit-refresh-wazzup-buffer + (magit-get-current-branch) all))) + +;;; Miscellaneous + +(defun magit-ignore-file (file edit local) + (let ((ignore-file (if local ".git/info/exclude" ".gitignore"))) + (if edit + (setq file (read-string "File to ignore: " file))) + (append-to-file (concat "/" file "\n") nil ignore-file) + (magit-need-refresh))) + +(defun magit-ignore-item () + (interactive) + (magit-section-action (item info "ignore") + ((untracked file) + (magit-ignore-file info current-prefix-arg nil)) + ((wazzup) + (magit-wazzup-toggle-ignore info current-prefix-arg)))) + +(defun magit-ignore-item-locally () + (interactive) + (magit-section-action (item info "ignore") + ((untracked file) + (magit-ignore-file info current-prefix-arg t)))) + +(defun magit-discard-diff (diff stagedp) + (let ((kind (magit-diff-item-kind diff)) + (file (magit-diff-item-file diff))) + (cond ((eq kind 'deleted) + (when (yes-or-no-p (format "Resurrect %s? " file)) + (magit-run-git "reset" "-q" "--" file) + (magit-run-git "checkout" "--" file))) + ((eq kind 'new) + (if (yes-or-no-p (format "Delete %s? " file)) + (magit-run-git "rm" "-f" "--" file))) + (t + (if (yes-or-no-p (format "Discard changes to %s? " file)) + (if stagedp + (magit-run-git "checkout" "HEAD" "--" file) + (magit-run-git "checkout" "--" file))))))) + +(defun magit-discard-item () + (interactive) + (magit-section-action (item info "discard") + ((untracked file) + (if (yes-or-no-p (format "Delete %s? " info)) + (magit-run "rm" info))) + ((untracked) + (if (yes-or-no-p "Delete all untracked files and directories? ") + (magit-run "git" "clean" "-df"))) + ((unstaged diff hunk) + (when (yes-or-no-p (if (magit-use-region-p) + "Discard changes in region? " + "Discard hunk? ")) + (magit-apply-hunk-item-reverse item))) + ((staged diff hunk) + (if (magit-file-uptodate-p (magit-diff-item-file + (magit-hunk-item-diff item))) + (when (yes-or-no-p (if (magit-use-region-p) + "Discard changes in region? " + "Discard hunk? ")) + (magit-apply-hunk-item-reverse item "--index")) + (error "Can't discard this hunk. Please unstage it first."))) + ((unstaged diff) + (magit-discard-diff item nil)) + ((staged diff) + (if (magit-file-uptodate-p (magit-diff-item-file item)) + (magit-discard-diff item t) + (error "Can't discard staged changes to this file. Please unstage it first."))) + ((hunk) + (error "Can't discard this hunk")) + ((diff) + (error "Can't discard this diff")) + ((stash) + (when (yes-or-no-p "Discard stash? ") + (magit-run-git "stash" "drop" info))))) + +(defun magit-visit-item () + (interactive) + (magit-section-action (item info "visit") + ((untracked file) + (find-file info)) + ((diff) + (find-file (magit-diff-item-file item))) + ((hunk) + (let ((file (magit-diff-item-file (magit-hunk-item-diff item))) + (line (magit-hunk-item-target-line item))) + (find-file file) + (goto-line line))) + ((commit) + (magit-show-commit info) + (pop-to-buffer "*magit-commit*")) + ((stash) + (magit-show-stash info) + (pop-to-buffer "*magit-diff*")) + ((topic) + (magit-checkout info)))) + +(defun magit-show-item-or-scroll-up () + (interactive) + (magit-section-action (item info) + ((commit) + (magit-show-commit info #'scroll-up)) + ((stash) + (magit-show-stash info #'scroll-up)) + (t + (scroll-up)))) + +(defun magit-show-item-or-scroll-down () + (interactive) + (magit-section-action (item info) + ((commit) + (magit-show-commit info #'scroll-down)) + ((stash) + (magit-show-stash info #'scroll-down)) + (t + (scroll-down)))) + +(defun magit-mark-item (&optional unmark) + (interactive "P") + (if unmark + (magit-set-marked-commit nil) + (magit-section-action (item info "mark") + ((commit) + (magit-set-marked-commit (if (eq magit-marked-commit info) + nil + info)))))) + +(defun magit-describe-item () + (interactive) + (let ((section (magit-current-section))) + (message "Section: %s %s-%s %S %S %S" + (magit-section-type section) + (magit-section-beginning section) + (magit-section-end section) + (magit-section-title section) + (magit-section-info section) + (magit-section-context-type section)))) + +(defun magit-copy-item-as-kill () + "Copy sha1 of commit at point into kill ring." + (interactive) + (magit-section-action (item info "copy") + ((commit) + (kill-new info) + (message "%s" info)))) + +(defun magit-interactive-rebase () + "Start a git rebase -i session, old school-style." + (interactive) + (server-start) + (let* ((section (get-text-property (point) 'magit-section)) + (commit (and (member 'commit (magit-section-context-type section)) + (magit-section-info section))) + (old-editor (getenv "GIT_EDITOR"))) + (setenv "GIT_EDITOR" (expand-file-name "emacsclient" exec-directory)) + (unwind-protect + (magit-run-git-async "rebase" "-i" + (or (and commit (concat commit "^")) + (read-string "Interactively rebase to: "))) + (if old-editor + (setenv "GIT_EDITOR" old-editor))))) + +(defun magit-show-branches () + "Show all of the current branches in other-window." + (interactive) + (save-selected-window + (switch-to-buffer-other-window "*magit-branches*") + (erase-buffer) + (insert (magit-git-string "branch" "-va")) + (insert "\n"))) + +(defvar magit-ediff-file) +(defvar magit-ediff-windows) + +(defun magit-interactive-resolve (file) + (let ((merge-status (magit-git-string "ls-files" "-u" "--" file)) + (base-buffer (generate-new-buffer (concat file ".base"))) + (our-buffer (generate-new-buffer (concat file ".current"))) + (their-buffer (generate-new-buffer (concat file ".merged"))) + (windows (current-window-configuration))) + (if (null merge-status) + (error "Cannot resolve %s" file)) + (with-current-buffer base-buffer + (if (string-match "^[0-9]+ [0-9a-f]+ 1" merge-status) + (insert (magit-git-string "cat-file" "blob" + (concat ":1:" file))))) + (with-current-buffer our-buffer + (if (string-match "^[0-9]+ [0-9a-f]+ 2" merge-status) + (insert (magit-git-string "cat-file" "blob" + (concat ":2:" file))))) + (with-current-buffer their-buffer + (if (string-match "^[0-9]+ [0-9a-f]+ 3" merge-status) + (insert (magit-git-string "cat-file" "blob" + (concat ":3:" file))))) + ;; We have now created the 3 buffer with ours, theirs and the ancestor files + (with-current-buffer (ediff-merge-buffers-with-ancestor our-buffer their-buffer base-buffer) + (make-local-variable 'magit-ediff-file) + (setq magit-ediff-file file) + (make-local-variable 'magit-ediff-windows) + (setq magit-ediff-windows windows) + (make-local-variable 'ediff-quit-hook) + (add-hook 'ediff-quit-hook + (lambda () + (let ((buffer-A ediff-buffer-A) + (buffer-B ediff-buffer-B) + (buffer-C ediff-buffer-C) + (buffer-Ancestor ediff-ancestor-buffer) + (file magit-ediff-file) + (windows magit-ediff-windows)) + (ediff-cleanup-mess) + (find-file file) + (erase-buffer) + (insert-buffer-substring buffer-C) + (kill-buffer buffer-A) + (kill-buffer buffer-B) + (kill-buffer buffer-C) + (when (bufferp buffer-Ancestor) (kill-buffer buffer-Ancestor)) + (set-window-configuration windows) + (message "Conflict resolution finished; you may save the buffer"))))))) + + +(defun magit-interactive-resolve-item () + (interactive) + (magit-section-action (item info "resolv") + ((diff) + (magit-interactive-resolve (cadr info))))) + + +(provide 'magit) diff --git a/emacs.d/lisp/magit/magit.texi b/emacs.d/lisp/magit/magit.texi new file mode 100644 index 0000000..4930d92 --- /dev/null +++ b/emacs.d/lisp/magit/magit.texi @@ -0,0 +1,639 @@ +\input texinfo.tex @c -*-texinfo-*- +@c %**start of header +@setfilename magit.info +@settitle Magit User Manual +@c %**end of header + +@dircategory Emacs +@direntry +* Magit: (magit). Using Git from Emacs with Magit. +@end direntry + +@copying +Copyright @copyright{} 2008, 2009 Marius Vollmer + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.2 or +any later version published by the Free Software Foundation; with no +Invariant Sections, with no Front-Cover Texts, and with no Back-Cover +Texts. +@end quotation +@end copying + +@node Top +@top Magit User Manual + +Magit is an interface to the version control system Git, implemented +as an extension to Emacs. + +@menu +* Introduction:: +* Acknowledgments:: +* Sections:: +* Status:: +* Untracked files:: +* Staging and Committing:: +* History:: +* Reflogs:: +* Diffing:: +* Tagging:: +* Resetting:: +* Stashing:: +* Branching:: +* Wazzup:: +* Merging:: +* Rebasing:: +* Rewriting:: +* Pushing and Pulling:: +* Interfacing with Subversion:: +@end menu + +@node Introduction +@chapter Introduction + +With Magit, you can inspect and modify your Git repositories with +Emacs. You can review and commit the changes you have made to the +tracked files, for example, and you can browse the history of past +changes. There is support for cherry picking, reverting, merging, +rebasing, and other common Git operations. + +Magit is not a complete interface to Git; it just aims to make the +most common Git operations convenient. Thus, Magit will likely not +save you from learning Git itself. + +This manual provides a tour of all Magit features. It does not give a +introduction to version control in general, or to Git in particular. + +The main entry point to Magit is @kbd{M-x magit-status}, which will +put you in Magit's status buffer. You will be using it frequently, so +it is probably a good idea to bind @code{magit-status} to a key of +your choice. + +In addition to the status buffer, Magit will also create buffers that +show lists of commits, buffers with diffs, and other kinds of buffers. +All these buffers are in @code{magit-mode} and have the same key +bindings. Not all commands make sense in all contexts, but a given +key will always do the same thing in all Magit buffers. + +Naturally, Magit runs the @code{git} command to do most of the work. +The @code{*magit-process*} buffer contains the transcript of the most +recent command. You can switch to it with @kbd{$}. + +@node Acknowledgments +@chapter Acknowledgments + +From day one of the first Magit announcement, John Wiegley has +contributed numerous fixes, UI improvements, and new features. +Thanks! + +Linh Dang and Christian Neukirchen also contributed from day one. +Thanks! + +Phil Hagelberg joined a few days later. Thanks! + +Alex Ott contributed support for git svn. Thanks! + +Marcin Bachry contributed bug fixes and support for decorated logs. +Thanks! + +Alexey Voinov fixed bugs. Thanks! + +RĂ©mi Vanicat helped with Tramp support. Thanks! + +@node Sections +@chapter Sections + +All Magit buffers are structured into nested 'sections'. These +sections can be hidden and shown individually. When a section is +hidden, only its first line is shown and all its children are +completely invisible. + +The most fine-grained way to control the visibility of sections is the +@kbd{TAB} key. It will to toggle the current section (the section +that contains point) between being hidden and being shown. + +Typing @kbd{S-TAB} toggles the visibility of the children of the +current section. When all of them are shown, they will all be hidden. +Otherwise, when some or all are hidden, they will all be shown. + +The digit keys @kbd{1}, @kbd{2}, @kbd{3}, and @kbd{4} control the +visibility of sections based on levels. Hitting @kbd{2}, for example, +will show sections on levels one and two, and will hide sections on +level 3. However, only sections that are a parent or child of the +current section are affected. + +For example, when the current section is on level 3 and you hit +@kbd{1}, the grand-parent of the current section (which is on level +one) will be shown, and the parent of the current section (level 2) +will be hidden. The visibility of no other section will be changed. + +This sounds a bit complicated, but you'll figure it out. + +Using @kbd{M-1}, @kbd{M-2}, @kbd{M-3}, and @kbd{M-4} is similar to the +unmodified digits, but now all sections on the respective level are +affected, regardless of whether or not they are related to the current +section. + +For example, @kbd{M-1} will only show the first lines of the top-level +sections and will hide everything else. Typing @kbd{M-4} on the other +hand will show everything. + +Because of the way the status buffer is set up, some changes to +section visibility are more common than others. Files are on level 2 +and diff hunks are on level 4. Thus, you can type @kbd{2} to collapse +the diff of the current file, and @kbd{M-2} to collapse all files. +This returns the status buffer to its default setup and is a quick way +to unclutter it after drilling down into the modified files. + +Because @kbd{2} and @kbd{M-2} are so common in the status buffer, they +are bound to additional, more mnemonic keys: @kbd{M-h} (hide) and +@kbd{M-H} (hide all). Likewise @kbd{4} and @kbd{M-4} are also +available as @kbd{M-s} (show) and @kbd{M-S} (show all). + +In other buffers than the status buffer, @kbd{M-h}, @kbd{M-H}, +@kbd{M-s}, and @kbd{M-S} might work on different levels than on 2 and +4, but they keep their general meaning: @kbd{M-H} hides all detail, +and @kbd{M-S} shows everything. + +@node Status +@chapter Status + +Running @kbd{M-x magit-status} displays the main interface of Magit, +the status buffer. You can have multiple status buffers active at the +same time, each associated with its own Git repository. + +When invoking @kbd{M-x magit-status} from within a Git repository, it +will switch to the status buffer of that repository. Otherwise, it +will prompt for a directory. With a prefix argument, it will always +prompt. + +You can set @code{magit-repo-dirs} to customize how +@code{magit-status} asks for the repository to work on. When +@code{magit-repo-dirs} is nil, @code{magit-status} will simply ask for +a directory. + +If you specify a directory that is not a Git repository, @kbd{M-x +magit-status} will offer to initialize it as one. + +When @code{magit-repo-dirs} is not nil, it is treated as a list of +directory names, and @code{magit-status} will find all Git +repositories in those directories and offer them for completion. +(Magit will only look @code{magit-repo-dirs-depth} levels deep, +however.) + +With two prefix arguments, @code{magit-status} will always prompt for +a raw directory. + +Thus, you would normally set @code{magit-repo-dirs} to the places +where you keep most of your Git repositories and switch between them +with @kbd{C-u M-x magit-status}. If you want to go to a repository +outside of your normal working areas, or if you want to create a new +repository, you would use @kbd{C-u C-u M-x magit-status}. + +You need to explicitly refresh the status buffer when you have made +changes to the repository from outside of Emacs. You can type @kbd{g} +in the status buffer itself, or just use @kbd{M-x magit-status} +instead of @kbd{C-x b} when switching to it. You also need to refresh +the status buffer in this way after saving a file in Emacs. + +The header at the top of the status buffer shows a short summary of +the repository state: where it is located, which branch is checked +out, etc. Below the header are a number of sections that show details +about the working tree and the staging area. You can hide and show +them as described in the previous section. + +The first section shows @emph{Untracked files}, if there are any. See +@ref{Untracked files} for more details. + +Two section show your local changes. They are explained fully in the +next chapter, @ref{Staging and Committing}. + +If the current branch is associated with a remote tracking branch, the +status buffer shows the differences between the current branch and the +tracking branch. See @ref{Pushing and Pulling} for more information. + +During a history rewriting session, the status buffer shows the +@emph{Pending changes} and @emph{Pending commits} sections. See +@ref{Rewriting} for more details. + +@node Untracked files +@chapter Untracked files + +Untracked files are shown in the @emph{Untracked files} section. + +You can add a untracked file to the staging area with @kbd{s}. If +point is on the @emph{Untracked files} section title when you it +@kbd{s}, all untracked files are staged. + +Typing @kbd{C-u S} anywhere will also stage all untracked files, +together with all changes to the tracked files. + +You can instruct Git to ignore them by typing @kbd{i}. This will add +the filename to the @code{.gitignore} file. Typing @kbd{C-u i} will +ask you for the name of the file to ignore. This is useful to ignore +whole directories, for example. The @kbd{I} command is similar to +@kbd{i} but will add the file to @code{.git/info/exclude} instead. + +To delete a untracked file forever, use @kbd{k}. If point is on the +@emph{Untracked files} section title when you it @kbd{k}, all +untracked files are deleted. + +@node Staging and Committing +@chapter Staging and Committing + +Comitting with Git is a two step process: first you add the changes +you want to commit to a 'staging area', and then you commit them to +the repository. This allows you to only commit a subset of your local +changes. + +Magit allows you to ignore the staging area if you wish. As long as +your staging area is unused, Magit will show your uncomitted changes +in a section named @emph{Changes}. + +When the staging area is in use, Magit uses two sections: +@emph{Unstaged changes} and @emph{Staged changes}. The @emph{Staged +changes} section shows the changes that will be included in the next +commit, while the @emph{Unstaged changes} section shows the changes +that will be left out. + +To move a unstaged hunk into the staging area, move point into the +hunk and type @kbd{s}. Likewise, to unstage a hunk, move point into +it and type @kbd{u}. If point is in a diff header when you type +@kbd{s} or @kbd{u}, all hunks belonging to that diff are moved at the +same time. + +If the region is active when you type @kbd{s} or @kbd{u}, only the +changes in the region are staged or unstaged. (This works line by +line: if the beginning of a line is in the region it is included in +the changes, otherwise it is not.) + +To move all hunks of all diffs into the staging area in one go, type +@kbd{S}. To unstage everything, type @kbd{U}. + +Typing @kbd{C-u S} will stage all untracked files in addition to the +changes to tracked files. + +You can discard uncommitted changes by moving point into a hunk and +typing @kbd{k}. The changes to discard are selected as with @kbd{s} +and @kbd{u}. + +Before committing, you should write a short description of the +changes. + +Type @kbd{c} to pop up a buffer where you can write your change +description. Once you are happy with the description, type @kbd{C-c +C-c} in that buffer to perform the commit. + +Typing @kbd{c} when the staging area is unused is a special situation. +Normally, the next commit would be empty, but you can configure Magit +to do something more useful by customizing the +@code{magit-commit-all-when-nothing-staged} variable. One choice is +to instruct the subsequent @kbd{C-c C-c} to commit all changes. +Another choice is stage everything at the time of hitting @kbd{c}. + +You can type @kbd{C-c C-a} in the buffer with the change description +to toggle a flag that determines whether the next commit will +@emph{amend} the current commit in HEAD. + +Typing @kbd{C-c C-s} will toggle the @code{--signoff} option. The +default is determined by the @code{magit-commit-signoff} customization +variable. + +If you change your mind and don't want to go ahead with your commit +while you are in the @code{*magit-log-edit*} buffer, you can just +switch to another buffer, continue editing there, staging and +unstaging things until you are happy, and then return to the +@code{*magit-log-edit*} buffer, maybe via @kbd{C-x b}, or by hitting +@kbd{c} again in a Magit buffer. + +If you want to erase the @code{*magit-log-edit*} buffer and bury it, +you can hit @kbd{C-c C-k} in it. + +Typing @kbd{C} will also pop up the change description buffer, but in +addition, it will try to insert a ChangeLog-style entry for the change +that point is in. + +@node History +@chapter History + +To show the repository history of your current head, type @kbd{l}. A +new buffer will be shown that displays the history in a terse form. +The first paragraph of each commit message is displayed, next to a +representation of the relationships between commits. + +Giving a prefix argument to @kbd{l} will ask for the starting and end +point of the history. This can be used to show the commits that are +in one branch, but not in another, for example. + +Typing @kbd{L} (or @kbd{C-u L}) will show the log in a more verbose +form. + +You can move point to a commit and then cause various things to happen +with it. (The following commands work in any list of commit, such as +the one shown in the @emph{Unpushed commits} section.) + +Typing @kbd{RET} will pop up more information about the current commit +and move point into the new buffer. Typing @kbd{SPC} and @kbd{DEL} +will also show the information, but will scroll the new buffer up or +down (respectively) when typed again. + +Typing @kbd{a} will apply the current commit to your current branch. +This is useful when you are browsing the history of some other branch +and you want to `cherry-pick' some changes from it. A typical +situation is applying selected bug fixes from the development version +of a program to a release branch. The cherry-picked changes will not +be committed automatically; you need to do that explicitly. + +Typing @kbd{A} will cherry-pick the current commit and will also +commit the changes automatically when there have not been any +conflicts. + +Typing @kbd{v} will revert the current commit. Thus, it will apply +the changes made by that commit in reverse. This is obviously useful +to cleanly undo changes that turned out to be wrong. As with @kbd{a}, +you need to commit the changes explicitly. + +Typing @kbd{C-w} will copy the sha1 of the current commit into the +kill ring. + +Typing @kbd{=} will show the differences from the current commit to +the @dfn{marked} commit. + +You can mark the current commit by typing @kbd{.}. When the current +commit is already marked, typing @kbd{.} will unmark it. To unmark +the marked commit no matter where point is, use @kbd{C-u .}. + +Some commands, such as @kbd{=}, will use the current commit and the +marked commit as implicit arguments. Other commands will offer the +marked commit as a default when prompting for their arguments. + +@node Reflogs +@chapter Reflogs + +You can use @kbd{h} and @kbd{H} to browse your @emph{reflog}, the +local history of changes made to your repository heads. Typing +@kbd{H} will ask for a head, while @kbd{h} will show the reflog of +@code{HEAD}. + +The resulting buffer is just like the buffer produced by @kbd{l} and +@kbd{L} that shows the commit history. + +@node Diffing +@chapter Diffing + +To show the changes from you working tree to another revision, type +@kbd{d}. To show the changes between two arbitrary revisions, type +@kbd{D}. + +You can use @kbd{a} within the diff output to apply the changes to +your working tree. As usual when point is in a diff header for a +file, all changes for that file are applied, and when it is in a hunk, +only that hunk is. When the region is active, the applied changes are +restricted to that region. + +Typing @kbd{v} will apply the selected changes in reverse. + +@node Tagging +@chapter Tagging + +Typing @kbd{t} will make a lighweight tag. Typing @kbd{T} will make a +annotated tag. It will put you in the normal @code{*magit-log-edit} +buffer for writing commit messages, but typing @kbd{C-c C-c} in it +will make the tag instead. This is controlled by the @code{Tag} field +that will be added to the @code{*magit-log-edit*} buffer. You can +edit it, if you like. + +@node Resetting +@chapter Resetting + +Once you have added a commit to your local repository, you can not +change that commit anymore in any way. But you can reset your current +head to an earlier commit and start over. + +If you have published your history already, rewriting it in this way +can be confusing and should be avoided. However, rewriting your local +history is fine and it is often cleaner to fix mistakes this way than +by reverting commits (with @kbd{v}, for example). + +Typing @kbd{x} will ask for a revision and reset your current head to +it. No changes will be made to your working tree and staging area. +Thus, the @emph{Staged changes} section in the status buffer will show +the changes that you have removed from your commit history. You can +commit the changes again as if you had just made them, thus rewriting +history. + +Typing @kbd{x} while point is in a line that describes a commit will +offer this commit as the default revision to reset to. Thus, you can +move point to one of the commits in the @emph{Unpushed commits} +section and hit @kbd{x RET} to reset your current head to it. + +Type @kbd{X} to reset your working tree and staging area to the most +recently committed state. This will discard your local modifications, +so be careful. + +You can give a prefix to @kbd{x} if you want to reset both the current +head and your working tree to a given commit. This is the same as +first using an unprefixed @kbd{x} to reset only the head, and then +using @kbd{X}. + +@node Stashing +@chapter Stashing + +You can create a new stash with @kbd{z}. Your stashes will be listed +in the status buffer, and you can apply them with @kbd{a} and pop them +with @kbd{A}. To drop a stash, use @kbd{k}. + +Typing @kbd{Z} will create a stash just like @kbd{z}, but will leave +the changes in your working tree and index. + +You can visit and show stashes in the usual way: Typing @kbd{SPC} and +@kbd{DEL} will pop up a buffer with the description of the stash and +scroll it, typing @kbd{RET} will move point into that buffer. + +@node Branching +@chapter Branching + +The current branch is indicated in the header of the status buffer. +You can switch to a different branch by typing @kbd{b}. This will +immediately checkout the branch into your working copy, so you +shouldn't have any local modifications when switching branches. + +If you try to switch to a remote branch, Magit will offer to create a +local tracking branch for it instead. This way, you can easily start +working on new branches that have appeared in a remote repository. + +Similar to @kbd{x}, typing @kbd{b} while point is at a commit +description will offer that commit as the default to switch to. +This will result in a detached head. + +To create a new branch and switch to it immediately, type @kbd{B}. + +@node Wazzup +@chapter Wazzup + +Typing @kbd{w} will show a summary of how your other branches relate +to the current branch. + +For each branch, you will get a section that lists the commits in that +branch that are not in the current branch. The sections are initially +collapsed; you need to explicitly open them with @kbd{TAB} (or +similar) to show the lists of commits. + +When point is on a @emph{N unmerged commits in ...} title, the +corresponding branch will be offered as the default for a merge. + +Hitting @kbd{i} on a branch title will ignore this branch in the +wazzup view. You can use @kbd{C-u w} to show all branches, including +the ignored ones. Hitting @kbd{i} on an already ignored branch in +that view will unignore it. + +@node Merging +@chapter Merging + +Magit offers two ways to merge branches: manually and automatic. A +manual merge will apply all changes to your working tree and staging +area, but will not commit them, while a automatic merge will go ahead +and commit them immediately. + +Type @kbd{m} to initiate a manual merge, and type @kbd{M} for a +automatic merge. + +A manual merge is useful when carefully merging a new feature that you +want to review and test before even committing it. A automatic merge +is appropriate when you are on a feature branch and want to catch up +with the master, say. + +After initiating a manual merge, the header of the status buffer will +remind you that the next commit will be a merge commit (with more than +one parent). If you want to abort a manual merge, just do a hard +reset to HEAD with @kbd{X}. + +Merges can fail if the two branches you merge want to introduce +conflicting changes. In that case, the automatic merge stops before the +commit, essentially falling back to a manual merge. You need to resolve +the conflicts for example with @kbd{e} and stage the resolved files, for +example with @kbd{S}. + +You can not stage individual hunks one by one as you resolve them, you +can only stage whole files once all conflicts in them have been +resolved. + +@node Rebasing +@chapter Rebasing + +Typing @kbd{R} in the status buffer will initiate a rebase or, if one +is already in progress, ask you how to continue. + +When a rebase is stopped in the middle because of a conflict, the +header of the status buffer will indicate how far along you are in the +series of commits that are being replayed. When that happens, you +should resolve the conflicts and stage everything and hit @kbd{R c} to +continue the rebase. Alternatively, hitting @kbd{c} or @kbd{C} while +in the middle of a rebase will also ask you whether to continue the +rebase. + +Of course, you can initiate a rebase in any number of ways, by +configuring @code{git pull} to rebase instead of merge, for example. +Such a rebase can be finished with @kbd{R} as well. + +@node Rewriting +@chapter Rewriting + +As hinted at earlier, you can rewrite your commit history. For +example, you can reset he current head to an earlier commit with +@kbd{x}. This leaves the working tree unchanged, and the status +buffer will show all the changes that have been made since that new +value of the current head. You can commit these changes again, +possibly splitting them into multiple commits as you go along. + +Amending your last commit is a common special case of rewriting +history like this. + +Another common way to rewrite history is to reset the head to an +earlier commit, and then to cherry pick the previous commits in a +different order. You could pick them from the reflog, for example. + +Magit has several commands that can simplify the book keeping +associated with rewriting. These commands all start with the @kbd{r} +prefix key. + +Typing @kbd{r s} will start a rewrite operation. You will be prompted +for a @emph{base} commit, and all commits between the current head and +this commit are put in a list of @emph{Pending commits} (including the +base commit). The current head will then be reset to the parent of +the base commit. + +You would then typically use @kbd{a} and @kbd{A} to cherry pick +commits from the list of pending commits in the desired order, until +all have been applied. Magit shows which commits have been applied by +changing their marker from @code{*} to @code{.}. + +Using @kbd{A} will immediately commit the commit (as usual). If you +want to combine multiple previous commits into a single new one, use +@kbd{a} to apply them all to your working tree, and then commit them +together. + +Magit has no explicit support for rewriting merge commits. It will +happily include merge commits in the list of pending commits, but +there is no way of replaying them automatically. You have to redo the +merge explicitly. + +You can also use @kbd{v} to revert a commit when you have changed your +mind. This will change the @code{.} mark back to @code{*}. + +Once you are done with the rewrite, type @kbd{r t} to remove the book +keeping information from the status buffer. + +If you rather wish to start over, type @kbd{r a}. This will abort the +rewriting, resetting the current head back to the value it had before +the rewrite was started with @kbd{r s}. + +Typing @kbd{r f} will @emph{finish} the rewrite: it will apply all +unused commits one after the other, as if you would us @kbd{A} with +all of them. + +You can change the @kbd{*} and @kbd{.} marks of a pending commit +explicitly with @kbd{r *} and @kbd{r .}. + +In addition to a list of pending commits, the status buffer will show +the @emph{Pending changes}. This section shows the diff between the +original head and the current head. You can use it to review the +changes that you still need to rewrite, and you can apply hunks from +it, like from any other diff. + +@node Pushing and Pulling +@chapter Pushing and Pulling + +Magit will run @code{git push} when you type @kbd{P}. If you give a +prefix argument to @kbd{P}, you will be prompted for the repository to +push to. When no default remote repositor has been configured yet for +the current branch, you will be prompted as well. Typing @kbd{P} will +only push the current branch to the remote. In other words, it will +run @code{git push }. + +Typing @kbd{f} will run @code{git remote update} and @kbd{F} will run +@code{git pull}. When you don't have a default branch configured to +be pulled into the current one, you will be asked for it. + +If there is a default remote repository for the current branch, Magit +will show that repository in the status buffer header. + +In this case, the status buffer will also have a @emph{Unpushed +commits} section that shows the commits on your current head that are +not in the branch named @code{/}. This section works +just like the history buffer: you can see details about a commit with +@kbd{RET}, compare two of them with @kbd{.} and @kbd{=}, and you can +reset your current head to one of them with @kbd{x}, for example. + +When the remote branch has changes that are not in the current branch, +Magit shows them in a section called @emph{Unpulled changes}. Typing +@kbd{F} will merge them into the current branch. + +@node Interfacing with Subversion +@chapter Interfacing with Subversion + +Typing @kbd{N r} runs @code{git svn rebase} and typing @kbd{N c} runs +@code{git svn dcommit}. + +@bye diff --git a/emacs.d/lisp/magit/test/BAR.2 b/emacs.d/lisp/magit/test/BAR.2 new file mode 100644 index 0000000..2614c5e --- /dev/null +++ b/emacs.d/lisp/magit/test/BAR.2 @@ -0,0 +1 @@ +Hi Ho. diff --git a/emacs.d/lisp/magit/test/FOO.2 b/emacs.d/lisp/magit/test/FOO.2 new file mode 100644 index 0000000..10ddd6d --- /dev/null +++ b/emacs.d/lisp/magit/test/FOO.2 @@ -0,0 +1 @@ +Hello! -- cgit v1.2.3