From: David A. Greene Date: Tue, 10 Apr 2012 01:22:55 +0000 (-0500) Subject: Add 'contrib/subtree/' from commit 'd3a04e06c77d57978bb5230361c64946232cc346' X-Git-Tag: v1.7.11-rc0~176 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/634392b26275fe5436c0ea131bc89b46476aa4ae Add 'contrib/subtree/' from commit 'd3a04e06c77d57978bb5230361c64946232cc346' git-subtree-dir: contrib/subtree git-subtree-mainline: e8dde3e5f9ddb7cf95a6ff3cea6cf07c3a2db80d git-subtree-split: d3a04e06c77d57978bb5230361c64946232cc346 --- 634392b26275fe5436c0ea131bc89b46476aa4ae diff --cc contrib/subtree/.gitignore index 0000000000,0000000000..7e77c9d022 new file mode 100644 --- /dev/null +++ b/contrib/subtree/.gitignore @@@ -1,0 -1,0 +1,5 @@@ ++*~ ++git-subtree.xml ++git-subtree.1 ++mainline ++subproj diff --cc contrib/subtree/COPYING index 0000000000,0000000000..d511905c16 new file mode 100644 --- /dev/null +++ b/contrib/subtree/COPYING @@@ -1,0 -1,0 +1,339 @@@ ++ GNU GENERAL PUBLIC LICENSE ++ Version 2, June 1991 ++ ++ Copyright (C) 1989, 1991 Free Software Foundation, Inc., ++ 51 Franklin Street, 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. ++ ++ Preamble ++ ++ The licenses for most software are designed to take away your ++freedom to share and change it. By contrast, the GNU General Public ++License is intended to guarantee your freedom to share and change free ++software--to make sure the software is free for all its users. This ++General Public License applies to most of the Free Software ++Foundation's software and to any other program whose authors commit to ++using it. (Some other Free Software Foundation software is covered by ++the GNU Lesser General Public License instead.) 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 ++this service 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 make restrictions that forbid ++anyone to deny you these rights or to ask you to surrender the rights. ++These restrictions translate to certain responsibilities for you if you ++distribute copies of the software, or if you modify it. ++ ++ For example, if you distribute copies of such a program, whether ++gratis or for a fee, you must give the recipients all the rights that ++you have. 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. ++ ++ We protect your rights with two steps: (1) copyright the software, and ++(2) offer you this license which gives you legal permission to copy, ++distribute and/or modify the software. ++ ++ Also, for each author's protection and ours, we want to make certain ++that everyone understands that there is no warranty for this free ++software. If the software is modified by someone else and passed on, we ++want its recipients to know that what they have is not the original, so ++that any problems introduced by others will not reflect on the original ++authors' reputations. ++ ++ Finally, any free program is threatened constantly by software ++patents. We wish to avoid the danger that redistributors of a free ++program will individually obtain patent licenses, in effect making the ++program proprietary. To prevent this, we have made it clear that any ++patent must be licensed for everyone's free use or not licensed at all. ++ ++ The precise terms and conditions for copying, distribution and ++modification follow. ++ ++ GNU GENERAL PUBLIC LICENSE ++ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION ++ ++ 0. This License applies to any program or other work which contains ++a notice placed by the copyright holder saying it may be distributed ++under the terms of this General Public License. The "Program", below, ++refers to any such program or work, and a "work based on the Program" ++means either the Program or any derivative work under copyright law: ++that is to say, a work containing the Program or a portion of it, ++either verbatim or with modifications and/or translated into another ++language. (Hereinafter, translation is included without limitation in ++the term "modification".) Each licensee is addressed as "you". ++ ++Activities other than copying, distribution and modification are not ++covered by this License; they are outside its scope. The act of ++running the Program is not restricted, and the output from the Program ++is covered only if its contents constitute a work based on the ++Program (independent of having been made by running the Program). ++Whether that is true depends on what the Program does. ++ ++ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the ++notices that refer to this License and to the absence of any warranty; ++and give any other recipients of the Program a copy of this License ++along with the Program. ++ ++You may charge a fee for the physical act of transferring a copy, and ++you may at your option offer warranty protection in exchange for a fee. ++ ++ 2. You may modify your copy or copies of the Program or any portion ++of it, thus forming a work based on the Program, and copy and ++distribute such modifications or work under the terms of Section 1 ++above, provided that you also meet all of these conditions: ++ ++ a) You must cause the modified files to carry prominent notices ++ stating that you changed the files and the date of any change. ++ ++ b) You must cause any work that you distribute or publish, that in ++ whole or in part contains or is derived from the Program or any ++ part thereof, to be licensed as a whole at no charge to all third ++ parties under the terms of this License. ++ ++ c) If the modified program normally reads commands interactively ++ when run, you must cause it, when started running for such ++ interactive use in the most ordinary way, to print or display an ++ announcement including an appropriate copyright notice and a ++ notice that there is no warranty (or else, saying that you provide ++ a warranty) and that users may redistribute the program under ++ these conditions, and telling the user how to view a copy of this ++ License. (Exception: if the Program itself is interactive but ++ does not normally print such an announcement, your work based on ++ the Program is not required to print an announcement.) ++ ++These requirements apply to the modified work as a whole. If ++identifiable sections of that work are not derived from the Program, ++and can be reasonably considered independent and separate works in ++themselves, then this License, and its terms, do not apply to those ++sections when you distribute them as separate works. But when you ++distribute the same sections as part of a whole which is a work based ++on the Program, the distribution of the whole must be on the terms of ++this License, whose permissions for other licensees extend to the ++entire whole, and thus to each and every part regardless of who wrote it. ++ ++Thus, it is not the intent of this section to claim rights or contest ++your rights to work written entirely by you; rather, the intent is to ++exercise the right to control the distribution of derivative or ++collective works based on the Program. ++ ++In addition, mere aggregation of another work not based on the Program ++with the Program (or with a work based on the Program) on a volume of ++a storage or distribution medium does not bring the other work under ++the scope of this License. ++ ++ 3. You may copy and distribute the Program (or a work based on it, ++under Section 2) in object code or executable form under the terms of ++Sections 1 and 2 above provided that you also do one of the following: ++ ++ a) Accompany it with the complete corresponding machine-readable ++ source code, which must be distributed under the terms of Sections ++ 1 and 2 above on a medium customarily used for software interchange; or, ++ ++ b) Accompany it with a written offer, valid for at least three ++ years, to give any third party, for a charge no more than your ++ cost of physically performing source distribution, a complete ++ machine-readable copy of the corresponding source code, to be ++ distributed under the terms of Sections 1 and 2 above on a medium ++ customarily used for software interchange; or, ++ ++ c) Accompany it with the information you received as to the offer ++ to distribute corresponding source code. (This alternative is ++ allowed only for noncommercial distribution and only if you ++ received the program in object code or executable form with such ++ an offer, in accord with Subsection b above.) ++ ++The source code for a work means the preferred form of the work for ++making modifications to it. For an executable work, complete source ++code means all the source code for all modules it contains, plus any ++associated interface definition files, plus the scripts used to ++control compilation and installation of the executable. However, as a ++special exception, the source code distributed need not include ++anything that is normally distributed (in either source or binary ++form) with the major components (compiler, kernel, and so on) of the ++operating system on which the executable runs, unless that component ++itself accompanies the executable. ++ ++If distribution of executable or object code is made by offering ++access to copy from a designated place, then offering equivalent ++access to copy the source code from the same place counts as ++distribution of the source code, even though third parties are not ++compelled to copy the source along with the object code. ++ ++ 4. You may not copy, modify, sublicense, or distribute the Program ++except as expressly provided under this License. Any attempt ++otherwise to copy, modify, sublicense or distribute the Program 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. ++ ++ 5. You are not required to accept this License, since you have not ++signed it. However, nothing else grants you permission to modify or ++distribute the Program or its derivative works. These actions are ++prohibited by law if you do not accept this License. Therefore, by ++modifying or distributing the Program (or any work based on the ++Program), you indicate your acceptance of this License to do so, and ++all its terms and conditions for copying, distributing or modifying ++the Program or works based on it. ++ ++ 6. Each time you redistribute the Program (or any work based on the ++Program), the recipient automatically receives a license from the ++original licensor to copy, distribute or modify the Program subject to ++these terms and conditions. You may not impose any further ++restrictions on the recipients' exercise of the rights granted herein. ++You are not responsible for enforcing compliance by third parties to ++this License. ++ ++ 7. If, as a consequence of a court judgment or allegation of patent ++infringement or for any other reason (not limited to patent issues), ++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 ++distribute so as to satisfy simultaneously your obligations under this ++License and any other pertinent obligations, then as a consequence you ++may not distribute the Program at all. For example, if a patent ++license would not permit royalty-free redistribution of the Program by ++all those who receive copies directly or indirectly through you, then ++the only way you could satisfy both it and this License would be to ++refrain entirely from distribution of the Program. ++ ++If any portion of this section is held invalid or unenforceable under ++any particular circumstance, the balance of the section is intended to ++apply and the section as a whole is intended to apply in other ++circumstances. ++ ++It is not the purpose of this section to induce you to infringe any ++patents or other property right claims or to contest validity of any ++such claims; this section has the sole purpose of protecting the ++integrity of the free software distribution system, which is ++implemented by public license practices. Many people have made ++generous contributions to the wide range of software distributed ++through that system in reliance on consistent application of that ++system; it is up to the author/donor to decide if he or she is willing ++to distribute software through any other system and a licensee cannot ++impose that choice. ++ ++This section is intended to make thoroughly clear what is believed to ++be a consequence of the rest of this License. ++ ++ 8. If the distribution and/or use of the Program is restricted in ++certain countries either by patents or by copyrighted interfaces, the ++original copyright holder who places the Program under this License ++may add an explicit geographical distribution limitation excluding ++those countries, so that distribution is permitted only in or among ++countries not thus excluded. In such case, this License incorporates ++the limitation as if written in the body of this License. ++ ++ 9. The Free Software Foundation may publish revised and/or new versions ++of the 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 a version number of this License which applies to it and "any ++later version", you have the option of following the terms and conditions ++either of that version or of any later version published by the Free ++Software Foundation. If the Program does not specify a version number of ++this License, you may choose any version ever published by the Free Software ++Foundation. ++ ++ 10. If you wish to incorporate parts of the Program into other free ++programs whose distribution conditions are different, write to the author ++to ask for permission. For software which is copyrighted by the Free ++Software Foundation, write to the Free Software Foundation; we sometimes ++make exceptions for this. Our decision will be guided by the two goals ++of preserving the free status of all derivatives of our free software and ++of promoting the sharing and reuse of software generally. ++ ++ NO WARRANTY ++ ++ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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. ++ ++ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING ++WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR ++REDISTRIBUTE 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. ++ ++ 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 ++convey 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 2 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, write to the Free Software Foundation, Inc., ++ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ ++Also add information on how to contact you by electronic and paper mail. ++ ++If the program is interactive, make it output a short notice like this ++when it starts in an interactive mode: ++ ++ Gnomovision version 69, Copyright (C) year name of author ++ Gnomovision 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, the commands you use may ++be called something other than `show w' and `show c'; they could even be ++mouse-clicks or menu items--whatever suits your program. ++ ++You should also get your employer (if you work as a programmer) or your ++school, if any, to sign a "copyright disclaimer" for the program, if ++necessary. Here is a sample; alter the names: ++ ++ Yoyodyne, Inc., hereby disclaims all copyright interest in the program ++ `Gnomovision' (which makes passes at compilers) written by James Hacker. ++ ++ , 1 April 1989 ++ Ty Coon, President of Vice ++ ++This 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. diff --cc contrib/subtree/INSTALL index 0000000000,0000000000..81ac702ad2 new file mode 100644 --- /dev/null +++ b/contrib/subtree/INSTALL @@@ -1,0 -1,0 +1,22 @@@ ++ ++HOW TO INSTALL git-subtree ++========================== ++ ++You simply need to copy the file 'git-subtree.sh' to where ++the rest of the git scripts are stored. ++ ++From the Git bash window just run: ++ ++install.sh ++ ++Or if you have the full Cygwin installed, you can use make: ++ ++make install ++ ++That will make a 'git subtree' (note: space instead of dash) command ++available. See the file git-subtree.txt for more. ++ ++You can also install the man page by doing: ++ ++ make doc ++ cp git-subtree.1 /usr/share/man/man1/ diff --cc contrib/subtree/Makefile index 0000000000,0000000000..91e0cc08ea new file mode 100644 --- /dev/null +++ b/contrib/subtree/Makefile @@@ -1,0 -1,0 +1,45 @@@ ++prefix ?= /usr/local ++mandir ?= $(prefix)/share/man ++gitdir ?= $(shell git --exec-path) ++ ++gitver ?= $(word 3,$(shell git --version)) ++ ++# this should be set to a 'standard' bsd-type install program ++INSTALL ?= install ++INSTALL_DATA = $(INSTALL) -c -m 0644 ++INSTALL_EXE = $(INSTALL) -c -m 0755 ++INSTALL_DIR = $(INSTALL) -c -d -m 0755 ++ ++default: ++ @echo "git-subtree doesn't need to be built." ++ @echo "Just copy it somewhere on your PATH, like /usr/local/bin." ++ @echo ++ @echo "Try: make doc" ++ @echo " or: make test" ++ @false ++ ++install: install-exe install-doc ++ ++install-exe: git-subtree.sh ++ $(INSTALL_DIR) $(DESTDIR)/$(gitdir) ++ $(INSTALL_EXE) $< $(DESTDIR)/$(gitdir)/git-subtree ++ ++install-doc: git-subtree.1 ++ $(INSTALL_DIR) $(DESTDIR)/$(mandir)/man1/ ++ $(INSTALL_DATA) $< $(DESTDIR)/$(mandir)/man1/ ++ ++doc: git-subtree.1 ++ ++%.1: %.xml ++ xmlto -m manpage-normal.xsl man $^ ++ ++%.xml: %.txt ++ asciidoc -b docbook -d manpage -f asciidoc.conf \ ++ -agit_version=$(gitver) $^ ++ ++test: ++ ./test.sh ++ ++clean: ++ rm -f *~ *.xml *.html *.1 ++ rm -rf subproj mainline diff --cc contrib/subtree/README index 0000000000,0000000000..c686b4a69b new file mode 100644 --- /dev/null +++ b/contrib/subtree/README @@@ -1,0 -1,0 +1,8 @@@ ++ ++Please read git-subtree.txt for documentation. ++ ++Please don't contact me using github mail; it's slow, ugly, and worst of ++all, redundant. Email me instead at apenwarr@gmail.com and I'll be happy to ++help. ++ ++Avery diff --cc contrib/subtree/asciidoc.conf index 0000000000,0000000000..dc76e7f073 new file mode 100644 --- /dev/null +++ b/contrib/subtree/asciidoc.conf @@@ -1,0 -1,0 +1,91 @@@ ++## linkgit: macro ++# ++# Usage: linkgit:command[manpage-section] ++# ++# Note, {0} is the manpage section, while {target} is the command. ++# ++# Show GIT link as: (
); if section is defined, else just show ++# the command. ++ ++[macros] ++(?su)[\\]?(?Plinkgit):(?P\S*?)\[(?P.*?)\]= ++ ++[attributes] ++asterisk=* ++plus=+ ++caret=^ ++startsb=[ ++endsb=] ++tilde=~ ++ ++ifdef::backend-docbook[] ++[linkgit-inlinemacro] ++{0%{target}} ++{0#} ++{0#{target}{0}} ++{0#} ++endif::backend-docbook[] ++ ++ifdef::backend-docbook[] ++ifndef::git-asciidoc-no-roff[] ++# "unbreak" docbook-xsl v1.68 for manpages. v1.69 works with or without this. ++# v1.72 breaks with this because it replaces dots not in roff requests. ++[listingblock] ++{title} ++ ++ifdef::doctype-manpage[] ++ .ft C ++endif::doctype-manpage[] ++| ++ifdef::doctype-manpage[] ++ .ft ++endif::doctype-manpage[] ++ ++{title#} ++endif::git-asciidoc-no-roff[] ++ ++ifdef::git-asciidoc-no-roff[] ++ifdef::doctype-manpage[] ++# The following two small workarounds insert a simple paragraph after screen ++[listingblock] ++{title} ++ ++| ++ ++{title#} ++ ++[verseblock] ++{title} ++{title%} ++{title#} ++| ++ ++{title#} ++{title%} ++endif::doctype-manpage[] ++endif::git-asciidoc-no-roff[] ++endif::backend-docbook[] ++ ++ifdef::doctype-manpage[] ++ifdef::backend-docbook[] ++[header] ++template::[header-declarations] ++ ++ ++{mantitle} ++{manvolnum} ++Git ++{git_version} ++Git Manual ++ ++ ++ {manname} ++ {manpurpose} ++ ++endif::backend-docbook[] ++endif::doctype-manpage[] ++ ++ifdef::backend-xhtml11[] ++[linkgit-inlinemacro] ++{target}{0?({0})} ++endif::backend-xhtml11[] diff --cc contrib/subtree/git-subtree index 0000000000,0000000000..7d7539894e new file mode 120000 --- /dev/null +++ b/contrib/subtree/git-subtree @@@ -1,0 -1,0 +1,1 @@@ ++git-subtree.sh diff --cc contrib/subtree/git-subtree.sh index 0000000000,0000000000..920c664bb7 new file mode 100755 --- /dev/null +++ b/contrib/subtree/git-subtree.sh @@@ -1,0 -1,0 +1,712 @@@ ++#!/bin/bash ++# ++# git-subtree.sh: split/join git repositories in subdirectories of this one ++# ++# Copyright (C) 2009 Avery Pennarun ++# ++if [ $# -eq 0 ]; then ++ set -- -h ++fi ++OPTS_SPEC="\ ++git subtree add --prefix= ++git subtree merge --prefix= ++git subtree pull --prefix= ++git subtree push --prefix= ++git subtree split --prefix= ++-- ++h,help show the help ++q quiet ++d show debug messages ++P,prefix= the name of the subdir to split out ++m,message= use the given message as the commit message for the merge commit ++ options for 'split' ++annotate= add a prefix to commit message of new commits ++b,branch= create a new branch from the split subtree ++ignore-joins ignore prior --rejoin commits ++onto= try connecting new tree to an existing one ++rejoin merge the new branch back into HEAD ++ options for 'add', 'merge', 'pull' and 'push' ++squash merge subtree changes as a single commit ++" ++eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)" ++ ++PATH=$PATH:$(git --exec-path) ++. git-sh-setup ++ ++require_work_tree ++ ++quiet= ++branch= ++debug= ++command= ++onto= ++rejoin= ++ignore_joins= ++annotate= ++squash= ++message= ++ ++debug() ++{ ++ if [ -n "$debug" ]; then ++ echo "$@" >&2 ++ fi ++} ++ ++say() ++{ ++ if [ -z "$quiet" ]; then ++ echo "$@" >&2 ++ fi ++} ++ ++assert() ++{ ++ if "$@"; then ++ : ++ else ++ die "assertion failed: " "$@" ++ fi ++} ++ ++ ++#echo "Options: $*" ++ ++while [ $# -gt 0 ]; do ++ opt="$1" ++ shift ++ case "$opt" in ++ -q) quiet=1 ;; ++ -d) debug=1 ;; ++ --annotate) annotate="$1"; shift ;; ++ --no-annotate) annotate= ;; ++ -b) branch="$1"; shift ;; ++ -P) prefix="$1"; shift ;; ++ -m) message="$1"; shift ;; ++ --no-prefix) prefix= ;; ++ --onto) onto="$1"; shift ;; ++ --no-onto) onto= ;; ++ --rejoin) rejoin=1 ;; ++ --no-rejoin) rejoin= ;; ++ --ignore-joins) ignore_joins=1 ;; ++ --no-ignore-joins) ignore_joins= ;; ++ --squash) squash=1 ;; ++ --no-squash) squash= ;; ++ --) break ;; ++ *) die "Unexpected option: $opt" ;; ++ esac ++done ++ ++command="$1" ++shift ++case "$command" in ++ add|merge|pull) default= ;; ++ split|push) default="--default HEAD" ;; ++ *) die "Unknown command '$command'" ;; ++esac ++ ++if [ -z "$prefix" ]; then ++ die "You must provide the --prefix option." ++fi ++ ++case "$command" in ++ add) [ -e "$prefix" ] && ++ die "prefix '$prefix' already exists." ;; ++ *) [ -e "$prefix" ] || ++ die "'$prefix' does not exist; use 'git subtree add'" ;; ++esac ++ ++dir="$(dirname "$prefix/.")" ++ ++if [ "$command" != "pull" -a "$command" != "add" -a "$command" != "push" ]; then ++ revs=$(git rev-parse $default --revs-only "$@") || exit $? ++ dirs="$(git rev-parse --no-revs --no-flags "$@")" || exit $? ++ if [ -n "$dirs" ]; then ++ die "Error: Use --prefix instead of bare filenames." ++ fi ++fi ++ ++debug "command: {$command}" ++debug "quiet: {$quiet}" ++debug "revs: {$revs}" ++debug "dir: {$dir}" ++debug "opts: {$*}" ++debug ++ ++cache_setup() ++{ ++ cachedir="$GIT_DIR/subtree-cache/$$" ++ rm -rf "$cachedir" || die "Can't delete old cachedir: $cachedir" ++ mkdir -p "$cachedir" || die "Can't create new cachedir: $cachedir" ++ mkdir -p "$cachedir/notree" || die "Can't create new cachedir: $cachedir/notree" ++ debug "Using cachedir: $cachedir" >&2 ++} ++ ++cache_get() ++{ ++ for oldrev in $*; do ++ if [ -r "$cachedir/$oldrev" ]; then ++ read newrev <"$cachedir/$oldrev" ++ echo $newrev ++ fi ++ done ++} ++ ++cache_miss() ++{ ++ for oldrev in $*; do ++ if [ ! -r "$cachedir/$oldrev" ]; then ++ echo $oldrev ++ fi ++ done ++} ++ ++check_parents() ++{ ++ missed=$(cache_miss $*) ++ for miss in $missed; do ++ if [ ! -r "$cachedir/notree/$miss" ]; then ++ debug " incorrect order: $miss" ++ fi ++ done ++} ++ ++set_notree() ++{ ++ echo "1" > "$cachedir/notree/$1" ++} ++ ++cache_set() ++{ ++ oldrev="$1" ++ newrev="$2" ++ if [ "$oldrev" != "latest_old" \ ++ -a "$oldrev" != "latest_new" \ ++ -a -e "$cachedir/$oldrev" ]; then ++ die "cache for $oldrev already exists!" ++ fi ++ echo "$newrev" >"$cachedir/$oldrev" ++} ++ ++rev_exists() ++{ ++ if git rev-parse "$1" >/dev/null 2>&1; then ++ return 0 ++ else ++ return 1 ++ fi ++} ++ ++rev_is_descendant_of_branch() ++{ ++ newrev="$1" ++ branch="$2" ++ branch_hash=$(git rev-parse $branch) ++ match=$(git rev-list -1 $branch_hash ^$newrev) ++ ++ if [ -z "$match" ]; then ++ return 0 ++ else ++ return 1 ++ fi ++} ++ ++# if a commit doesn't have a parent, this might not work. But we only want ++# to remove the parent from the rev-list, and since it doesn't exist, it won't ++# be there anyway, so do nothing in that case. ++try_remove_previous() ++{ ++ if rev_exists "$1^"; then ++ echo "^$1^" ++ fi ++} ++ ++find_latest_squash() ++{ ++ debug "Looking for latest squash ($dir)..." ++ dir="$1" ++ sq= ++ main= ++ sub= ++ git log --grep="^git-subtree-dir: $dir/*\$" \ ++ --pretty=format:'START %H%n%s%n%n%b%nEND%n' HEAD | ++ while read a b junk; do ++ debug "$a $b $junk" ++ debug "{{$sq/$main/$sub}}" ++ case "$a" in ++ START) sq="$b" ;; ++ git-subtree-mainline:) main="$b" ;; ++ git-subtree-split:) sub="$b" ;; ++ END) ++ if [ -n "$sub" ]; then ++ if [ -n "$main" ]; then ++ # a rejoin commit? ++ # Pretend its sub was a squash. ++ sq="$sub" ++ fi ++ debug "Squash found: $sq $sub" ++ echo "$sq" "$sub" ++ break ++ fi ++ sq= ++ main= ++ sub= ++ ;; ++ esac ++ done ++} ++ ++find_existing_splits() ++{ ++ debug "Looking for prior splits..." ++ dir="$1" ++ revs="$2" ++ main= ++ sub= ++ git log --grep="^git-subtree-dir: $dir/*\$" \ ++ --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs | ++ while read a b junk; do ++ case "$a" in ++ START) sq="$b" ;; ++ git-subtree-mainline:) main="$b" ;; ++ git-subtree-split:) sub="$b" ;; ++ END) ++ debug " Main is: '$main'" ++ if [ -z "$main" -a -n "$sub" ]; then ++ # squash commits refer to a subtree ++ debug " Squash: $sq from $sub" ++ cache_set "$sq" "$sub" ++ fi ++ if [ -n "$main" -a -n "$sub" ]; then ++ debug " Prior: $main -> $sub" ++ cache_set $main $sub ++ cache_set $sub $sub ++ try_remove_previous "$main" ++ try_remove_previous "$sub" ++ fi ++ main= ++ sub= ++ ;; ++ esac ++ done ++} ++ ++copy_commit() ++{ ++ # We're going to set some environment vars here, so ++ # do it in a subshell to get rid of them safely later ++ debug copy_commit "{$1}" "{$2}" "{$3}" ++ git log -1 --pretty=format:'%an%n%ae%n%ad%n%cn%n%ce%n%cd%n%s%n%n%b' "$1" | ++ ( ++ read GIT_AUTHOR_NAME ++ read GIT_AUTHOR_EMAIL ++ read GIT_AUTHOR_DATE ++ read GIT_COMMITTER_NAME ++ read GIT_COMMITTER_EMAIL ++ read GIT_COMMITTER_DATE ++ export GIT_AUTHOR_NAME \ ++ GIT_AUTHOR_EMAIL \ ++ GIT_AUTHOR_DATE \ ++ GIT_COMMITTER_NAME \ ++ GIT_COMMITTER_EMAIL \ ++ GIT_COMMITTER_DATE ++ (echo -n "$annotate"; cat ) | ++ git commit-tree "$2" $3 # reads the rest of stdin ++ ) || die "Can't copy commit $1" ++} ++ ++add_msg() ++{ ++ dir="$1" ++ latest_old="$2" ++ latest_new="$3" ++ if [ -n "$message" ]; then ++ commit_message="$message" ++ else ++ commit_message="Add '$dir/' from commit '$latest_new'" ++ fi ++ cat <<-EOF ++ $commit_message ++ ++ git-subtree-dir: $dir ++ git-subtree-mainline: $latest_old ++ git-subtree-split: $latest_new ++ EOF ++} ++ ++add_squashed_msg() ++{ ++ if [ -n "$message" ]; then ++ echo "$message" ++ else ++ echo "Merge commit '$1' as '$2'" ++ fi ++} ++ ++rejoin_msg() ++{ ++ dir="$1" ++ latest_old="$2" ++ latest_new="$3" ++ if [ -n "$message" ]; then ++ commit_message="$message" ++ else ++ commit_message="Split '$dir/' into commit '$latest_new'" ++ fi ++ cat <<-EOF ++ $commit_message ++ ++ git-subtree-dir: $dir ++ git-subtree-mainline: $latest_old ++ git-subtree-split: $latest_new ++ EOF ++} ++ ++squash_msg() ++{ ++ dir="$1" ++ oldsub="$2" ++ newsub="$3" ++ newsub_short=$(git rev-parse --short "$newsub") ++ ++ if [ -n "$oldsub" ]; then ++ oldsub_short=$(git rev-parse --short "$oldsub") ++ echo "Squashed '$dir/' changes from $oldsub_short..$newsub_short" ++ echo ++ git log --pretty=tformat:'%h %s' "$oldsub..$newsub" ++ git log --pretty=tformat:'REVERT: %h %s' "$newsub..$oldsub" ++ else ++ echo "Squashed '$dir/' content from commit $newsub_short" ++ fi ++ ++ echo ++ echo "git-subtree-dir: $dir" ++ echo "git-subtree-split: $newsub" ++} ++ ++toptree_for_commit() ++{ ++ commit="$1" ++ git log -1 --pretty=format:'%T' "$commit" -- || exit $? ++} ++ ++subtree_for_commit() ++{ ++ commit="$1" ++ dir="$2" ++ git ls-tree "$commit" -- "$dir" | ++ while read mode type tree name; do ++ assert [ "$name" = "$dir" ] ++ assert [ "$type" = "tree" -o "$type" = "commit" ] ++ [ "$type" = "commit" ] && continue # ignore submodules ++ echo $tree ++ break ++ done ++} ++ ++tree_changed() ++{ ++ tree=$1 ++ shift ++ if [ $# -ne 1 ]; then ++ return 0 # weird parents, consider it changed ++ else ++ ptree=$(toptree_for_commit $1) ++ if [ "$ptree" != "$tree" ]; then ++ return 0 # changed ++ else ++ return 1 # not changed ++ fi ++ fi ++} ++ ++new_squash_commit() ++{ ++ old="$1" ++ oldsub="$2" ++ newsub="$3" ++ tree=$(toptree_for_commit $newsub) || exit $? ++ if [ -n "$old" ]; then ++ squash_msg "$dir" "$oldsub" "$newsub" | ++ git commit-tree "$tree" -p "$old" || exit $? ++ else ++ squash_msg "$dir" "" "$newsub" | ++ git commit-tree "$tree" || exit $? ++ fi ++} ++ ++copy_or_skip() ++{ ++ rev="$1" ++ tree="$2" ++ newparents="$3" ++ assert [ -n "$tree" ] ++ ++ identical= ++ nonidentical= ++ p= ++ gotparents= ++ for parent in $newparents; do ++ ptree=$(toptree_for_commit $parent) || exit $? ++ [ -z "$ptree" ] && continue ++ if [ "$ptree" = "$tree" ]; then ++ # an identical parent could be used in place of this rev. ++ identical="$parent" ++ else ++ nonidentical="$parent" ++ fi ++ ++ # sometimes both old parents map to the same newparent; ++ # eliminate duplicates ++ is_new=1 ++ for gp in $gotparents; do ++ if [ "$gp" = "$parent" ]; then ++ is_new= ++ break ++ fi ++ done ++ if [ -n "$is_new" ]; then ++ gotparents="$gotparents $parent" ++ p="$p -p $parent" ++ fi ++ done ++ ++ if [ -n "$identical" ]; then ++ echo $identical ++ else ++ copy_commit $rev $tree "$p" || exit $? ++ fi ++} ++ ++ensure_clean() ++{ ++ if ! git diff-index HEAD --exit-code --quiet 2>&1; then ++ die "Working tree has modifications. Cannot add." ++ fi ++ if ! git diff-index --cached HEAD --exit-code --quiet 2>&1; then ++ die "Index has modifications. Cannot add." ++ fi ++} ++ ++cmd_add() ++{ ++ if [ -e "$dir" ]; then ++ die "'$dir' already exists. Cannot add." ++ fi ++ ++ ensure_clean ++ ++ if [ $# -eq 1 ]; then ++ "cmd_add_commit" "$@" ++ elif [ $# -eq 2 ]; then ++ "cmd_add_repository" "$@" ++ else ++ say "error: parameters were '$@'" ++ die "Provide either a refspec or a repository and refspec." ++ fi ++} ++ ++cmd_add_repository() ++{ ++ echo "git fetch" "$@" ++ repository=$1 ++ refspec=$2 ++ git fetch "$@" || exit $? ++ revs=FETCH_HEAD ++ set -- $revs ++ cmd_add_commit "$@" ++} ++ ++cmd_add_commit() ++{ ++ revs=$(git rev-parse $default --revs-only "$@") || exit $? ++ set -- $revs ++ rev="$1" ++ ++ debug "Adding $dir as '$rev'..." ++ git read-tree --prefix="$dir" $rev || exit $? ++ git checkout -- "$dir" || exit $? ++ tree=$(git write-tree) || exit $? ++ ++ headrev=$(git rev-parse HEAD) || exit $? ++ if [ -n "$headrev" -a "$headrev" != "$rev" ]; then ++ headp="-p $headrev" ++ else ++ headp= ++ fi ++ ++ if [ -n "$squash" ]; then ++ rev=$(new_squash_commit "" "" "$rev") || exit $? ++ commit=$(add_squashed_msg "$rev" "$dir" | ++ git commit-tree $tree $headp -p "$rev") || exit $? ++ else ++ commit=$(add_msg "$dir" "$headrev" "$rev" | ++ git commit-tree $tree $headp -p "$rev") || exit $? ++ fi ++ git reset "$commit" || exit $? ++ ++ say "Added dir '$dir'" ++} ++ ++cmd_split() ++{ ++ debug "Splitting $dir..." ++ cache_setup || exit $? ++ ++ if [ -n "$onto" ]; then ++ debug "Reading history for --onto=$onto..." ++ git rev-list $onto | ++ while read rev; do ++ # the 'onto' history is already just the subdir, so ++ # any parent we find there can be used verbatim ++ debug " cache: $rev" ++ cache_set $rev $rev ++ done ++ fi ++ ++ if [ -n "$ignore_joins" ]; then ++ unrevs= ++ else ++ unrevs="$(find_existing_splits "$dir" "$revs")" ++ fi ++ ++ # We can't restrict rev-list to only $dir here, because some of our ++ # parents have the $dir contents the root, and those won't match. ++ # (and rev-list --follow doesn't seem to solve this) ++ grl='git rev-list --topo-order --reverse --parents $revs $unrevs' ++ revmax=$(eval "$grl" | wc -l) ++ revcount=0 ++ createcount=0 ++ eval "$grl" | ++ while read rev parents; do ++ revcount=$(($revcount + 1)) ++ say -n "$revcount/$revmax ($createcount) " ++ debug "Processing commit: $rev" ++ exists=$(cache_get $rev) ++ if [ -n "$exists" ]; then ++ debug " prior: $exists" ++ continue ++ fi ++ createcount=$(($createcount + 1)) ++ debug " parents: $parents" ++ newparents=$(cache_get $parents) ++ debug " newparents: $newparents" ++ ++ tree=$(subtree_for_commit $rev "$dir") ++ debug " tree is: $tree" ++ ++ check_parents $parents ++ ++ # ugly. is there no better way to tell if this is a subtree ++ # vs. a mainline commit? Does it matter? ++ if [ -z $tree ]; then ++ set_notree $rev ++ if [ -n "$newparents" ]; then ++ cache_set $rev $rev ++ fi ++ continue ++ fi ++ ++ newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $? ++ debug " newrev is: $newrev" ++ cache_set $rev $newrev ++ cache_set latest_new $newrev ++ cache_set latest_old $rev ++ done || exit $? ++ latest_new=$(cache_get latest_new) ++ if [ -z "$latest_new" ]; then ++ die "No new revisions were found" ++ fi ++ ++ if [ -n "$rejoin" ]; then ++ debug "Merging split branch into HEAD..." ++ latest_old=$(cache_get latest_old) ++ git merge -s ours \ ++ -m "$(rejoin_msg $dir $latest_old $latest_new)" \ ++ $latest_new >&2 || exit $? ++ fi ++ if [ -n "$branch" ]; then ++ if rev_exists "refs/heads/$branch"; then ++ if ! rev_is_descendant_of_branch $latest_new $branch; then ++ die "Branch '$branch' is not an ancestor of commit '$latest_new'." ++ fi ++ action='Updated' ++ else ++ action='Created' ++ fi ++ git update-ref -m 'subtree split' "refs/heads/$branch" $latest_new || exit $? ++ say "$action branch '$branch'" ++ fi ++ echo $latest_new ++ exit 0 ++} ++ ++cmd_merge() ++{ ++ revs=$(git rev-parse $default --revs-only "$@") || exit $? ++ ensure_clean ++ ++ set -- $revs ++ if [ $# -ne 1 ]; then ++ die "You must provide exactly one revision. Got: '$revs'" ++ fi ++ rev="$1" ++ ++ if [ -n "$squash" ]; then ++ first_split="$(find_latest_squash "$dir")" ++ if [ -z "$first_split" ]; then ++ die "Can't squash-merge: '$dir' was never added." ++ fi ++ set $first_split ++ old=$1 ++ sub=$2 ++ if [ "$sub" = "$rev" ]; then ++ say "Subtree is already at commit $rev." ++ exit 0 ++ fi ++ new=$(new_squash_commit "$old" "$sub" "$rev") || exit $? ++ debug "New squash commit: $new" ++ rev="$new" ++ fi ++ ++ version=$(git version) ++ if [ "$version" \< "git version 1.7" ]; then ++ if [ -n "$message" ]; then ++ git merge -s subtree --message="$message" $rev ++ else ++ git merge -s subtree $rev ++ fi ++ else ++ if [ -n "$message" ]; then ++ git merge -Xsubtree="$prefix" --message="$message" $rev ++ else ++ git merge -Xsubtree="$prefix" $rev ++ fi ++ fi ++} ++ ++cmd_pull() ++{ ++ ensure_clean ++ git fetch "$@" || exit $? ++ revs=FETCH_HEAD ++ set -- $revs ++ cmd_merge "$@" ++} ++ ++cmd_push() ++{ ++ if [ $# -ne 2 ]; then ++ die "You must provide " ++ fi ++ if [ -e "$dir" ]; then ++ repository=$1 ++ refspec=$2 ++ echo "git push using: " $repository $refspec ++ git push $repository $(git subtree split --prefix=$prefix):refs/heads/$refspec ++ else ++ die "'$dir' must already exist. Try 'git subtree add'." ++ fi ++} ++ ++"cmd_$command" "$@" diff --cc contrib/subtree/git-subtree.txt index 0000000000,0000000000..0c44fda011 new file mode 100644 --- /dev/null +++ b/contrib/subtree/git-subtree.txt @@@ -1,0 -1,0 +1,366 @@@ ++git-subtree(1) ++============== ++ ++NAME ++---- ++git-subtree - Merge subtrees together and split repository into subtrees ++ ++ ++SYNOPSIS ++-------- ++[verse] ++'git subtree' add -P ++'git subtree' pull -P ++'git subtree' push -P ++'git subtree' merge -P ++'git subtree' split -P [OPTIONS] [] ++ ++ ++DESCRIPTION ++----------- ++Subtrees allow subprojects to be included within a subdirectory ++of the main project, optionally including the subproject's ++entire history. ++ ++For example, you could include the source code for a library ++as a subdirectory of your application. ++ ++Subtrees are not to be confused with submodules, which are meant for ++the same task. Unlike submodules, subtrees do not need any special ++constructions (like .gitmodule files or gitlinks) be present in ++your repository, and do not force end-users of your ++repository to do anything special or to understand how subtrees ++work. A subtree is just a subdirectory that can be ++committed to, branched, and merged along with your project in ++any way you want. ++ ++They are also not to be confused with using the subtree merge ++strategy. The main difference is that, besides merging ++the other project as a subdirectory, you can also extract the ++entire history of a subdirectory from your project and make it ++into a standalone project. Unlike the subtree merge strategy ++you can alternate back and forth between these ++two operations. If the standalone library gets updated, you can ++automatically merge the changes into your project; if you ++update the library inside your project, you can "split" the ++changes back out again and merge them back into the library ++project. ++ ++For example, if a library you made for one application ends up being ++useful elsewhere, you can extract its entire history and publish ++that as its own git repository, without accidentally ++intermingling the history of your application project. ++ ++[TIP] ++In order to keep your commit messages clean, we recommend that ++people split their commits between the subtrees and the main ++project as much as possible. That is, if you make a change that ++affects both the library and the main application, commit it in ++two pieces. That way, when you split the library commits out ++later, their descriptions will still make sense. But if this ++isn't important to you, it's not *necessary*. git subtree will ++simply leave out the non-library-related parts of the commit ++when it splits it out into the subproject later. ++ ++ ++COMMANDS ++-------- ++add:: ++ Create the subtree by importing its contents ++ from the given or and remote . ++ A new commit is created automatically, joining the imported ++ project's history with your own. With '--squash', imports ++ only a single commit from the subproject, rather than its ++ entire history. ++ ++merge:: ++ Merge recent changes up to into the ++ subtree. As with normal 'git merge', this doesn't ++ remove your own local changes; it just merges those ++ changes into the latest . With '--squash', ++ creates only one commit that contains all the changes, ++ rather than merging in the entire history. ++ ++ If you use '--squash', the merge direction doesn't ++ always have to be forward; you can use this command to ++ go back in time from v2.5 to v2.4, for example. If your ++ merge introduces a conflict, you can resolve it in the ++ usual ways. ++ ++pull:: ++ Exactly like 'merge', but parallels 'git pull' in that ++ it fetches the given commit from the specified remote ++ repository. ++ ++push:: ++ Does a 'split' (see above) using the supplied ++ and then does a 'git push' to push the result to the ++ repository and refspec. This can be used to push your ++ subtree to different branches of the remote repository. ++ ++split:: ++ Extract a new, synthetic project history from the ++ history of the subtree. The new history ++ includes only the commits (including merges) that ++ affected , and each of those commits now has the ++ contents of at the root of the project instead ++ of in a subdirectory. Thus, the newly created history ++ is suitable for export as a separate git repository. ++ ++ After splitting successfully, a single commit id is ++ printed to stdout. This corresponds to the HEAD of the ++ newly created tree, which you can manipulate however you ++ want. ++ ++ Repeated splits of exactly the same history are ++ guaranteed to be identical (ie. to produce the same ++ commit ids). Because of this, if you add new commits ++ and then re-split, the new commits will be attached as ++ commits on top of the history you generated last time, ++ so 'git merge' and friends will work as expected. ++ ++ Note that if you use '--squash' when you merge, you ++ should usually not just '--rejoin' when you split. ++ ++ ++OPTIONS ++------- ++-q:: ++--quiet:: ++ Suppress unnecessary output messages on stderr. ++ ++-d:: ++--debug:: ++ Produce even more unnecessary output messages on stderr. ++ ++-P :: ++--prefix=:: ++ Specify the path in the repository to the subtree you ++ want to manipulate. This option is mandatory ++ for all commands. ++ ++-m :: ++--message=:: ++ This option is only valid for add, merge and pull (unsure). ++ Specify as the commit message for the merge commit. ++ ++ ++OPTIONS FOR add, merge, push, pull ++---------------------------------- ++--squash:: ++ This option is only valid for add, merge, push and pull ++ commands. ++ ++ Instead of merging the entire history from the subtree ++ project, produce only a single commit that contains all ++ the differences you want to merge, and then merge that ++ new commit into your project. ++ ++ Using this option helps to reduce log clutter. People ++ rarely want to see every change that happened between ++ v1.0 and v1.1 of the library they're using, since none of the ++ interim versions were ever included in their application. ++ ++ Using '--squash' also helps avoid problems when the same ++ subproject is included multiple times in the same ++ project, or is removed and then re-added. In such a ++ case, it doesn't make sense to combine the histories ++ anyway, since it's unclear which part of the history ++ belongs to which subtree. ++ ++ Furthermore, with '--squash', you can switch back and ++ forth between different versions of a subtree, rather ++ than strictly forward. 'git subtree merge --squash' ++ always adjusts the subtree to match the exactly ++ specified commit, even if getting to that commit would ++ require undoing some changes that were added earlier. ++ ++ Whether or not you use '--squash', changes made in your ++ local repository remain intact and can be later split ++ and send upstream to the subproject. ++ ++ ++OPTIONS FOR split ++----------------- ++--annotate=:: ++ This option is only valid for the split command. ++ ++ When generating synthetic history, add as a ++ prefix to each commit message. Since we're creating new ++ commits with the same commit message, but possibly ++ different content, from the original commits, this can help ++ to differentiate them and avoid confusion. ++ ++ Whenever you split, you need to use the same ++ , or else you don't have a guarantee that ++ the new re-created history will be identical to the old ++ one. That will prevent merging from working correctly. ++ git subtree tries to make it work anyway, particularly ++ if you use --rejoin, but it may not always be effective. ++ ++-b :: ++--branch=:: ++ This option is only valid for the split command. ++ ++ After generating the synthetic history, create a new ++ branch called that contains the new history. ++ This is suitable for immediate pushing upstream. ++ must not already exist. ++ ++--ignore-joins:: ++ This option is only valid for the split command. ++ ++ If you use '--rejoin', git subtree attempts to optimize ++ its history reconstruction to generate only the new ++ commits since the last '--rejoin'. '--ignore-join' ++ disables this behaviour, forcing it to regenerate the ++ entire history. In a large project, this can take a ++ long time. ++ ++--onto=:: ++ This option is only valid for the split command. ++ ++ If your subtree was originally imported using something ++ other than git subtree, its history may not match what ++ git subtree is expecting. In that case, you can specify ++ the commit id that corresponds to the first ++ revision of the subproject's history that was imported ++ into your project, and git subtree will attempt to build ++ its history from there. ++ ++ If you used 'git subtree add', you should never need ++ this option. ++ ++--rejoin:: ++ This option is only valid for the split command. ++ ++ After splitting, merge the newly created synthetic ++ history back into your main project. That way, future ++ splits can search only the part of history that has ++ been added since the most recent --rejoin. ++ ++ If your split commits end up merged into the upstream ++ subproject, and then you want to get the latest upstream ++ version, this will allow git's merge algorithm to more ++ intelligently avoid conflicts (since it knows these ++ synthetic commits are already part of the upstream ++ repository). ++ ++ Unfortunately, using this option results in 'git log' ++ showing an extra copy of every new commit that was ++ created (the original, and the synthetic one). ++ ++ If you do all your merges with '--squash', don't use ++ '--rejoin' when you split, because you don't want the ++ subproject's history to be part of your project anyway. ++ ++ ++EXAMPLE 1. Add command ++---------------------- ++Let's assume that you have a local repository that you would like ++to add an external vendor library to. In this case we will add the ++git-subtree repository as a subdirectory of your already existing ++git-extensions repository in ~/git-extensions/: ++ ++ $ git subtree add --prefix=git-subtree --squash \ ++ git://github.com/apenwarr/git-subtree.git master ++ ++'master' needs to be a valid remote ref and can be a different branch ++name ++ ++You can omit the --squash flag, but doing so will increase the number ++of commits that are incldued in your local repository. ++ ++We now have a ~/git-extensions/git-subtree directory containing code ++from the master branch of git://github.com/apenwarr/git-subtree.git ++in our git-extensions repository. ++ ++EXAMPLE 2. Extract a subtree using commit, merge and pull ++--------------------------------------------------------- ++Let's use the repository for the git source code as an example. ++First, get your own copy of the git.git repository: ++ ++ $ git clone git://git.kernel.org/pub/scm/git/git.git test-git ++ $ cd test-git ++ ++gitweb (commit 1130ef3) was merged into git as of commit ++0a8f4f0, after which it was no longer maintained separately. ++But imagine it had been maintained separately, and we wanted to ++extract git's changes to gitweb since that time, to share with ++the upstream. You could do this: ++ ++ $ git subtree split --prefix=gitweb --annotate='(split) ' \ ++ 0a8f4f0^.. --onto=1130ef3 --rejoin \ ++ --branch gitweb-latest ++ $ gitk gitweb-latest ++ $ git push git@github.com:whatever/gitweb.git gitweb-latest:master ++ ++(We use '0a8f4f0^..' because that means "all the changes from ++0a8f4f0 to the current version, including 0a8f4f0 itself.") ++ ++If gitweb had originally been merged using 'git subtree add' (or ++a previous split had already been done with --rejoin specified) ++then you can do all your splits without having to remember any ++weird commit ids: ++ ++ $ git subtree split --prefix=gitweb --annotate='(split) ' --rejoin \ ++ --branch gitweb-latest2 ++ ++And you can merge changes back in from the upstream project just ++as easily: ++ ++ $ git subtree pull --prefix=gitweb \ ++ git@github.com:whatever/gitweb.git master ++ ++Or, using '--squash', you can actually rewind to an earlier ++version of gitweb: ++ ++ $ git subtree merge --prefix=gitweb --squash gitweb-latest~10 ++ ++Then make some changes: ++ ++ $ date >gitweb/myfile ++ $ git add gitweb/myfile ++ $ git commit -m 'created myfile' ++ ++And fast forward again: ++ ++ $ git subtree merge --prefix=gitweb --squash gitweb-latest ++ ++And notice that your change is still intact: ++ ++ $ ls -l gitweb/myfile ++ ++And you can split it out and look at your changes versus ++the standard gitweb: ++ ++ git log gitweb-latest..$(git subtree split --prefix=gitweb) ++ ++EXAMPLE 3. Extract a subtree using branch ++----------------------------------------- ++Suppose you have a source directory with many files and ++subdirectories, and you want to extract the lib directory to its own ++git project. Here's a short way to do it: ++ ++First, make the new repository wherever you want: ++ ++ $ ++ $ git init --bare ++ ++Back in your original directory: ++ ++ $ git subtree split --prefix=lib --annotate="(split)" -b split ++ ++Then push the new branch onto the new empty repository: ++ ++ $ git push split:master ++ ++ ++AUTHOR ++------ ++Written by Avery Pennarun ++ ++ ++GIT ++--- ++Part of the linkgit:git[1] suite diff --cc contrib/subtree/install.sh index 0000000000,0000000000..1f87a62434 new file mode 100644 --- /dev/null +++ b/contrib/subtree/install.sh @@@ -1,0 -1,0 +1,2 @@@ ++# copy Git to where the rest of the Git scripts are found. ++cp git-subtree.sh "$(git --exec-path)"/git-subtree diff --cc contrib/subtree/manpage-base.xsl index 0000000000,0000000000..a264fa6160 new file mode 100644 --- /dev/null +++ b/contrib/subtree/manpage-base.xsl @@@ -1,0 -1,0 +1,35 @@@ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ sp ++ ++ ++ ++ ++ ++ ++ ++ br ++ ++ ++ diff --cc contrib/subtree/manpage-normal.xsl index 0000000000,0000000000..a48f5b11f3 new file mode 100644 --- /dev/null +++ b/contrib/subtree/manpage-normal.xsl @@@ -1,0 -1,0 +1,13 @@@ ++ ++ ++ ++ ++ ++ ++\ ++. ++ ++ diff --cc contrib/subtree/shellopts.sh index 0000000000,0000000000..42644cd019 new file mode 100644 --- /dev/null +++ b/contrib/subtree/shellopts.sh @@@ -1,0 -1,0 +1,1 @@@ ++export PATH=$PWD:$PATH diff --cc contrib/subtree/t/t7900-subtree.sh index 0000000000,0000000000..585f8d5751 new file mode 100755 --- /dev/null +++ b/contrib/subtree/t/t7900-subtree.sh @@@ -1,0 -1,0 +1,506 @@@ ++#!/bin/sh ++# ++# Copyright (c) 2012 Avery Pennaraum ++# ++test_description='Basic porcelain support for subtrees ++ ++This test verifies the basic operation of the merge, pull, add ++and split subcommands of git subtree. ++' ++ ++. ./test-lib.sh ++ ++create() ++{ ++ echo "$1" >"$1" ++ git add "$1" ++} ++ ++ ++check_equal() ++{ ++ test_debug 'echo' ++ test_debug "echo \"check a:\" \"{$1}\"" ++ test_debug "echo \" b:\" \"{$2}\"" ++ if [ "$1" = "$2" ]; then ++ return 0 ++ else ++ return 1 ++ fi ++} ++ ++fixnl() ++{ ++ t="" ++ while read x; do ++ t="$t$x " ++ done ++ echo $t ++} ++ ++multiline() ++{ ++ while read x; do ++ set -- $x ++ for d in "$@"; do ++ echo "$d" ++ done ++ done ++} ++ ++undo() ++{ ++ git reset --hard HEAD~ ++} ++ ++last_commit_message() ++{ ++ git log --pretty=format:%s -1 ++} ++ ++# 1 ++test_expect_success 'init subproj' ' ++ test_create_repo subproj ++' ++ ++# To the subproject! ++cd subproj ++ ++# 2 ++test_expect_success 'add sub1' ' ++ create sub1 && ++ git commit -m "sub1" && ++ git branch sub1 && ++ git branch -m master subproj ++' ++ ++# 3 ++test_expect_success 'add sub2' ' ++ create sub2 && ++ git commit -m "sub2" && ++ git branch sub2 ++' ++ ++# 4 ++test_expect_success 'add sub3' ' ++ create sub3 && ++ git commit -m "sub3" && ++ git branch sub3 ++' ++ ++# Back to mainline ++cd .. ++ ++# 5 ++test_expect_success 'add main4' ' ++ create main4 && ++ git commit -m "main4" && ++ git branch -m master mainline && ++ git branch subdir ++' ++ ++# 6 ++test_expect_success 'fetch subproj history' ' ++ git fetch ./subproj sub1 && ++ git branch sub1 FETCH_HEAD ++' ++ ++# 7 ++test_expect_success 'no subtree exists in main tree' ' ++ test_must_fail git subtree merge --prefix=subdir sub1 ++' ++ ++# 8 ++test_expect_success 'no pull from non-existant subtree' ' ++ test_must_fail git subtree pull --prefix=subdir ./subproj sub1 ++' ++ ++# 9 ++test_expect_success 'check if --message works for add' ' ++ git subtree add --prefix=subdir --message="Added subproject" sub1 && ++ check_equal ''"$(last_commit_message)"'' "Added subproject" && ++ undo ++' ++ ++# 10 ++test_expect_success 'check if --message works as -m and --prefix as -P' ' ++ git subtree add -P subdir -m "Added subproject using git subtree" sub1 && ++ check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" && ++ undo ++' ++ ++# 11 ++test_expect_success 'check if --message works with squash too' ' ++ git subtree add -P subdir -m "Added subproject with squash" --squash sub1 && ++ check_equal ''"$(last_commit_message)"'' "Added subproject with squash" && ++ undo ++' ++ ++# 12 ++test_expect_success 'add subproj to mainline' ' ++ git subtree add --prefix=subdir/ FETCH_HEAD && ++ check_equal ''"$(last_commit_message)"'' "Add '"'subdir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'" ++' ++ ++# 13 ++# this shouldn't actually do anything, since FETCH_HEAD is already a parent ++test_expect_success 'merge fetched subproj' ' ++ git merge -m "merge -s -ours" -s ours FETCH_HEAD ++' ++ ++# 14 ++test_expect_success 'add main-sub5' ' ++ create subdir/main-sub5 && ++ git commit -m "main-sub5" ++' ++ ++# 15 ++test_expect_success 'add main6' ' ++ create main6 && ++ git commit -m "main6 boring" ++' ++ ++# 16 ++test_expect_success 'add main-sub7' ' ++ create subdir/main-sub7 && ++ git commit -m "main-sub7" ++' ++ ++# 17 ++test_expect_success 'fetch new subproj history' ' ++ git fetch ./subproj sub2 && ++ git branch sub2 FETCH_HEAD ++' ++ ++# 18 ++test_expect_success 'check if --message works for merge' ' ++ git subtree merge --prefix=subdir -m "Merged changes from subproject" sub2 && ++ check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" && ++ undo ++' ++ ++# 19 ++test_expect_success 'check if --message for merge works with squash too' ' ++ git subtree merge --prefix subdir -m "Merged changes from subproject using squash" --squash sub2 && ++ check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" && ++ undo ++' ++ ++# 20 ++test_expect_success 'merge new subproj history into subdir' ' ++ git subtree merge --prefix=subdir FETCH_HEAD && ++ git branch pre-split && ++ check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline" ++' ++ ++# 21 ++test_expect_success 'Check that prefix argument is required for split' ' ++ echo "You must provide the --prefix option." > expected && ++ test_must_fail git subtree split > actual 2>&1 && ++ test_debug "echo -n expected: " && ++ test_debug "cat expected" && ++ test_debug "echo -n actual: " && ++ test_debug "cat actual" && ++ test_cmp expected actual && ++ rm -f expected actual ++' ++ ++# 22 ++test_expect_success 'Check that the exists for a split' ' ++ echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected && ++ test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 && ++ test_debug "echo -n expected: " && ++ test_debug "cat expected" && ++ test_debug "echo -n actual: " && ++ test_debug "cat actual" && ++ test_cmp expected actual ++# rm -f expected actual ++' ++ ++# 23 ++test_expect_success 'check if --message works for split+rejoin' ' ++ spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && ++ git branch spl1 "$spl1" && ++ check_equal ''"$(last_commit_message)"'' "Split & rejoin" && ++ undo ++' ++ ++# 24 ++test_expect_success 'check split with --branch' ' ++ spl1=$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin) && ++ undo && ++ git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr1 && ++ check_equal ''"$(git rev-parse splitbr1)"'' "$spl1" ++' ++ ++# 25 ++test_expect_success 'check split with --branch for an existing branch' ' ++ spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && ++ undo && ++ git branch splitbr2 sub1 && ++ git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --branch splitbr2 && ++ check_equal ''"$(git rev-parse splitbr2)"'' "$spl1" ++' ++ ++# 26 ++test_expect_success 'check split with --branch for an incompatible branch' ' ++ test_must_fail git subtree split --prefix subdir --onto FETCH_HEAD --branch subdir ++' ++ ++ ++# 27 ++test_expect_success 'check split+rejoin' ' ++ spl1=''"$(git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' && ++ undo && ++ git subtree split --annotate='"'*'"' --prefix subdir --onto FETCH_HEAD --rejoin && ++ check_equal ''"$(last_commit_message)"'' "Split '"'"'subdir/'"'"' into commit '"'"'"$spl1"'"'"'" ++' ++ ++# 28 ++test_expect_success 'add main-sub8' ' ++ create subdir/main-sub8 && ++ git commit -m "main-sub8" ++' ++ ++# To the subproject! ++cd ./subproj ++ ++# 29 ++test_expect_success 'merge split into subproj' ' ++ git fetch .. spl1 && ++ git branch spl1 FETCH_HEAD && ++ git merge FETCH_HEAD ++' ++ ++# 30 ++test_expect_success 'add sub9' ' ++ create sub9 && ++ git commit -m "sub9" ++' ++ ++# Back to mainline ++cd .. ++ ++# 31 ++test_expect_success 'split for sub8' ' ++ split2=''"$(git subtree split --annotate='"'*'"' --prefix subdir/ --rejoin)"'' ++ git branch split2 "$split2" ++' ++ ++# 32 ++test_expect_success 'add main-sub10' ' ++ create subdir/main-sub10 && ++ git commit -m "main-sub10" ++' ++ ++# 33 ++test_expect_success 'split for sub10' ' ++ spl3=''"$(git subtree split --annotate='"'*'"' --prefix subdir --rejoin)"'' && ++ git branch spl3 "$spl3" ++' ++ ++# To the subproject! ++cd ./subproj ++ ++# 34 ++test_expect_success 'merge split into subproj' ' ++ git fetch .. spl3 && ++ git branch spl3 FETCH_HEAD && ++ git merge FETCH_HEAD && ++ git branch subproj-merge-spl3 ++' ++ ++chkm="main4 main6" ++chkms="main-sub10 main-sub5 main-sub7 main-sub8" ++chkms_sub=$(echo $chkms | multiline | sed 's,^,subdir/,' | fixnl) ++chks="sub1 sub2 sub3 sub9" ++chks_sub=$(echo $chks | multiline | sed 's,^,subdir/,' | fixnl) ++ ++# 35 ++test_expect_success 'make sure exactly the right set of files ends up in the subproj' ' ++ subfiles=''"$(git ls-files | fixnl)"'' && ++ check_equal "$subfiles" "$chkms $chks" ++' ++ ++# 36 ++test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' ' ++ allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' && ++ check_equal "$allchanges" "$chkms $chks" ++' ++ ++# Back to mainline ++cd .. ++ ++# 37 ++test_expect_success 'pull from subproj' ' ++ git fetch ./subproj subproj-merge-spl3 && ++ git branch subproj-merge-spl3 FETCH_HEAD && ++ git subtree pull --prefix=subdir ./subproj subproj-merge-spl3 ++' ++ ++# 38 ++test_expect_success 'make sure exactly the right set of files ends up in the mainline' ' ++ mainfiles=''"$(git ls-files | fixnl)"'' && ++ check_equal "$mainfiles" "$chkm $chkms_sub $chks_sub" ++' ++ ++# 39 ++test_expect_success 'make sure each filename changed exactly once in the entire history' ' ++ # main-sub?? and /subdir/main-sub?? both change, because those are the ++ # changes that were split into their own history. And subdir/sub?? never ++ # change, since they were *only* changed in the subtree branch. ++ allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | fixnl)"'' && ++ check_equal "$allchanges" ''"$(echo $chkms $chkm $chks $chkms_sub | multiline | sort | fixnl)"'' ++' ++ ++# 40 ++test_expect_success 'make sure the --rejoin commits never make it into subproj' ' ++ check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' "" ++' ++ ++# 41 ++test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' ' ++ # They are meaningless to subproj since one side of the merge refers to the mainline ++ check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' "" ++' ++ ++# prepare second pair of repositories ++mkdir test2 ++cd test2 ++ ++# 42 ++test_expect_success 'init main' ' ++ test_create_repo main ++' ++ ++cd main ++ ++# 43 ++test_expect_success 'add main1' ' ++ create main1 && ++ git commit -m "main1" ++' ++ ++cd .. ++ ++# 44 ++test_expect_success 'init sub' ' ++ test_create_repo sub ++' ++ ++cd sub ++ ++# 45 ++test_expect_success 'add sub2' ' ++ create sub2 && ++ git commit -m "sub2" ++' ++ ++cd ../main ++ ++# check if split can find proper base without --onto ++ ++# 46 ++test_expect_success 'add sub as subdir in main' ' ++ git fetch ../sub master && ++ git branch sub2 FETCH_HEAD && ++ git subtree add --prefix subdir sub2 ++' ++ ++cd ../sub ++ ++# 47 ++test_expect_success 'add sub3' ' ++ create sub3 && ++ git commit -m "sub3" ++' ++ ++cd ../main ++ ++# 48 ++test_expect_success 'merge from sub' ' ++ git fetch ../sub master && ++ git branch sub3 FETCH_HEAD && ++ git subtree merge --prefix subdir sub3 ++' ++ ++# 49 ++test_expect_success 'add main-sub4' ' ++ create subdir/main-sub4 && ++ git commit -m "main-sub4" ++' ++ ++# 50 ++test_expect_success 'split for main-sub4 without --onto' ' ++ git subtree split --prefix subdir --branch mainsub4 ++' ++ ++# at this point, the new commit parent should be sub3 if it is not, ++# something went wrong (the "newparent" of "master~" commit should ++# have been sub3, but it was not, because its cache was not set to ++# itself) ++ ++# 51 ++test_expect_success 'check that the commit parent is sub3' ' ++ check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"'' ++' ++ ++# 52 ++test_expect_success 'add main-sub5' ' ++ mkdir subdir2 && ++ create subdir2/main-sub5 && ++ git commit -m "main-sub5" ++' ++ ++# 53 ++test_expect_success 'split for main-sub5 without --onto' ' ++ # also test that we still can split out an entirely new subtree ++ # if the parent of the first commit in the tree is not empty, ++ # then the new subtree has accidently been attached to something ++ git subtree split --prefix subdir2 --branch mainsub5 && ++ check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' "" ++' ++ ++# make sure no patch changes more than one file. The original set of commits ++# changed only one file each. A multi-file change would imply that we pruned ++# commits too aggressively. ++joincommits() ++{ ++ commit= ++ all= ++ while read x y; do ++ #echo "{$x}" >&2 ++ if [ -z "$x" ]; then ++ continue ++ elif [ "$x" = "commit:" ]; then ++ if [ -n "$commit" ]; then ++ echo "$commit $all" ++ all= ++ fi ++ commit="$y" ++ else ++ all="$all $y" ++ fi ++ done ++ echo "$commit $all" ++} ++ ++# 54 ++test_expect_success 'verify one file change per commit' ' ++ x= && ++ list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' && ++# test_debug "echo HERE" && ++# test_debug "echo ''"$list"''" && ++ (git log --pretty=format:'"'commit: %H'"' | joincommits | ++ ( while read commit a b; do ++ test_debug "echo Verifying commit "''"$commit"'' ++ test_debug "echo a: "''"$a"'' ++ test_debug "echo b: "''"$b"'' ++ check_equal "$b" "" ++ x=1 ++ done ++ check_equal "$x" 1 ++ )) ++' ++ ++test_done diff --cc contrib/subtree/todo index 0000000000,0000000000..7e44b0024f new file mode 100644 --- /dev/null +++ b/contrib/subtree/todo @@@ -1,0 -1,0 +1,50 @@@ ++ ++ delete tempdir ++ ++ 'git subtree rejoin' option to do the same as --rejoin, eg. after a ++ rebase ++ ++ --prefix doesn't force the subtree correctly in merge/pull: ++ "-s subtree" should be given an explicit subtree option? ++ There doesn't seem to be a way to do this. We'd have to ++ patch git-merge-subtree. Ugh. ++ (but we could avoid this problem by generating squashes with ++ exactly the right subtree structure, rather than using ++ subtree merge...) ++ ++ add a 'push' subcommand to parallel 'pull' ++ ++ add a 'log' subcommand to see what's new in a subtree? ++ ++ add to-submodule and from-submodule commands ++ ++ automated tests for --squash stuff ++ ++ "add" command non-obviously requires a commitid; would be easier if ++ it had a "pull" sort of mode instead ++ ++ "pull" and "merge" commands should fail if you've never merged ++ that --prefix before ++ ++ docs should provide an example of "add" ++ ++ note that the initial split doesn't *have* to have a commitid ++ specified... that's just an optimization ++ ++ if you try to add (or maybe merge?) with an invalid commitid, you ++ get a misleading "prefix must end with /" message from ++ one of the other git tools that git-subtree calls. Should ++ detect this situation and print the *real* problem. ++ ++ "pull --squash" should do fetch-synthesize-merge, but instead just ++ does "pull" directly, which doesn't work at all. ++ ++ make a 'force-update' that does what 'add' does even if the subtree ++ already exists. That way we can help people who imported ++ subtrees "incorrectly" (eg. by just copying in the files) in ++ the past. ++ ++ guess --prefix automatically if possible based on pwd ++ ++ make a 'git subtree grafts' that automatically expands --squash'd ++ commits so you can see the full history if you want it.