Merge master.kernel.org:/pub/scm/gitk/gitk
authorLinus Torvalds <torvalds@g5.osdl.org>
Wed, 6 Jul 2005 02:00:26 +0000 (19:00 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Wed, 6 Jul 2005 02:00:26 +0000 (19:00 -0700)
224 files changed:
COPYING [new file with mode: 0644]
Documentation/Makefile [new file with mode: 0644]
Documentation/cvs-migration.txt [new file with mode: 0644]
Documentation/diff-format.txt [new file with mode: 0644]
Documentation/diffcore.txt [new file with mode: 0644]
Documentation/git-apply-patch-script.txt [new file with mode: 0644]
Documentation/git-apply.txt [new file with mode: 0644]
Documentation/git-cat-file.txt [new file with mode: 0644]
Documentation/git-check-files.txt [new file with mode: 0644]
Documentation/git-checkout-cache.txt [new file with mode: 0644]
Documentation/git-commit-tree.txt [new file with mode: 0644]
Documentation/git-convert-cache.txt [new file with mode: 0644]
Documentation/git-cvsimport-script.txt [new file with mode: 0644]
Documentation/git-diff-cache.txt [new file with mode: 0644]
Documentation/git-diff-files.txt [new file with mode: 0644]
Documentation/git-diff-helper.txt [new file with mode: 0644]
Documentation/git-diff-stages.txt [new file with mode: 0644]
Documentation/git-diff-tree.txt [new file with mode: 0644]
Documentation/git-export.txt [new file with mode: 0644]
Documentation/git-fsck-cache.txt [new file with mode: 0644]
Documentation/git-http-pull.txt [new file with mode: 0644]
Documentation/git-init-db.txt [new file with mode: 0644]
Documentation/git-local-pull.txt [new file with mode: 0644]
Documentation/git-ls-files.txt [new file with mode: 0644]
Documentation/git-ls-tree.txt [new file with mode: 0644]
Documentation/git-merge-base.txt [new file with mode: 0644]
Documentation/git-merge-cache.txt [new file with mode: 0644]
Documentation/git-merge-one-file-script.txt [new file with mode: 0644]
Documentation/git-mktag.txt [new file with mode: 0644]
Documentation/git-prune-script.txt [new file with mode: 0644]
Documentation/git-pull-script.txt [new file with mode: 0644]
Documentation/git-read-tree.txt [new file with mode: 0644]
Documentation/git-resolve-script.txt [new file with mode: 0644]
Documentation/git-rev-list.txt [new file with mode: 0644]
Documentation/git-rev-tree.txt [new file with mode: 0644]
Documentation/git-ssh-pull.txt [new file with mode: 0644]
Documentation/git-ssh-push.txt [new file with mode: 0644]
Documentation/git-tag-script.txt [new file with mode: 0644]
Documentation/git-tar-tree.txt [new file with mode: 0644]
Documentation/git-unpack-file.txt [new file with mode: 0644]
Documentation/git-update-cache.txt [new file with mode: 0644]
Documentation/git-verify-pack.txt [new file with mode: 0644]
Documentation/git-write-blob.txt [new file with mode: 0644]
Documentation/git-write-tree.txt [new file with mode: 0644]
Documentation/git.txt [new file with mode: 0644]
Documentation/tutorial.txt [new file with mode: 0644]
INSTALL [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
apply.c [new file with mode: 0644]
blob.c [new file with mode: 0644]
blob.h [new file with mode: 0644]
cache.h [new file with mode: 0644]
cat-file.c [new file with mode: 0644]
check-files.c [new file with mode: 0644]
checkout-cache.c [new file with mode: 0644]
clone-pack.c [new file with mode: 0644]
commit-tree.c [new file with mode: 0644]
commit.c [new file with mode: 0644]
commit.h [new file with mode: 0644]
connect.c [new file with mode: 0644]
convert-cache.c [new file with mode: 0644]
count-delta.c [new file with mode: 0644]
count-delta.h [new file with mode: 0644]
csum-file.c [new file with mode: 0644]
csum-file.h [new file with mode: 0644]
date.c [new file with mode: 0644]
delta.h [new file with mode: 0644]
diff-cache.c [new file with mode: 0644]
diff-delta.c [new file with mode: 0644]
diff-files.c [new file with mode: 0644]
diff-helper.c [new file with mode: 0644]
diff-stages.c [new file with mode: 0644]
diff-tree.c [new file with mode: 0644]
diff.c [new file with mode: 0644]
diff.h [new file with mode: 0644]
diffcore-break.c [new file with mode: 0644]
diffcore-order.c [new file with mode: 0644]
diffcore-pathspec.c [new file with mode: 0644]
diffcore-pickaxe.c [new file with mode: 0644]
diffcore-rename.c [new file with mode: 0644]
diffcore.h [new file with mode: 0644]
entry.c [new file with mode: 0644]
epoch.c [new file with mode: 0644]
epoch.h [new file with mode: 0644]
export.c [new file with mode: 0644]
fetch-pack.c [new file with mode: 0644]
fsck-cache.c [new file with mode: 0644]
get-tar-commit-id.c [new file with mode: 0644]
git [new file with mode: 0755]
git-add-script [new file with mode: 0755]
git-apply-patch-script [new file with mode: 0755]
git-checkout-script [new file with mode: 0755]
git-cherry [new file with mode: 0755]
git-clone-script [new file with mode: 0755]
git-commit-script [new file with mode: 0755]
git-cvsimport-script [new file with mode: 0755]
git-diff-script [new file with mode: 0755]
git-external-diff-script [new file with mode: 0755]
git-fetch-script [new file with mode: 0755]
git-log-script [new file with mode: 0755]
git-merge-one-file-script [new file with mode: 0755]
git-prune-script [new file with mode: 0755]
git-pull-script [new file with mode: 0755]
git-rebase-script [new file with mode: 0755]
git-relink-script [new file with mode: 0644]
git-repack-script [new file with mode: 0644]
git-reset-script [new file with mode: 0755]
git-resolve-script [new file with mode: 0755]
git-shortlog [new file with mode: 0755]
git-status-script [new file with mode: 0755]
git-tag-script [new file with mode: 0755]
git-verify-tag-script [new file with mode: 0755]
git-whatchanged [new file with mode: 0755]
gitenv.c [new file with mode: 0644]
http-pull.c [new file with mode: 0644]
index.c [new file with mode: 0644]
init-db.c [new file with mode: 0644]
local-pull.c [new file with mode: 0644]
ls-files.c [new file with mode: 0644]
ls-tree.c [new file with mode: 0644]
merge-base.c [new file with mode: 0644]
merge-cache.c [new file with mode: 0644]
mktag.c [new file with mode: 0644]
mozilla-sha1/sha1.c [new file with mode: 0644]
mozilla-sha1/sha1.h [new file with mode: 0644]
object.c [new file with mode: 0644]
object.h [new file with mode: 0644]
pack-check.c [new file with mode: 0644]
pack-objects.c [new file with mode: 0644]
pack.h [new file with mode: 0644]
patch-delta.c [new file with mode: 0644]
patch-id.c [new file with mode: 0644]
pkt-line.c [new file with mode: 0644]
pkt-line.h [new file with mode: 0644]
ppc/sha1.c [new file with mode: 0644]
ppc/sha1.h [new file with mode: 0644]
ppc/sha1ppc.S [new file with mode: 0644]
prune-packed.c [new file with mode: 0644]
pull.c [new file with mode: 0644]
pull.h [new file with mode: 0644]
read-cache.c [new file with mode: 0644]
read-tree.c [new file with mode: 0644]
receive-pack.c [new file with mode: 0644]
refs.c [new file with mode: 0644]
refs.h [new file with mode: 0644]
rev-list.c [new file with mode: 0644]
rev-parse.c [new file with mode: 0644]
rev-tree.c [new file with mode: 0644]
rsh.c [new file with mode: 0644]
rsh.h [new file with mode: 0644]
send-pack.c [new file with mode: 0644]
sha1_file.c [new file with mode: 0644]
show-index.c [new file with mode: 0644]
ssh-pull.c [new file with mode: 0644]
ssh-push.c [new file with mode: 0644]
strbuf.c [new file with mode: 0644]
strbuf.h [new file with mode: 0644]
stripspace.c [new file with mode: 0644]
t/Makefile [new file with mode: 0644]
t/README [new file with mode: 0644]
t/diff-lib.sh [new file with mode: 0644]
t/lib-read-tree-m-3way.sh [new file with mode: 0644]
t/t0000-basic.sh [new file with mode: 0755]
t/t0100-environment-names.sh [new file with mode: 0755]
t/t0110-environment-names-old.sh [new file with mode: 0755]
t/t1000-read-tree-m-3way.sh [new file with mode: 0755]
t/t1001-read-tree-m-2way.sh [new file with mode: 0755]
t/t1002-read-tree-m-u-2way.sh [new file with mode: 0755]
t/t1005-read-tree-m-2way-emu23.sh [new file with mode: 0644]
t/t1100-commit-tree-options.sh [new file with mode: 0644]
t/t2000-checkout-cache-clash.sh [new file with mode: 0755]
t/t2001-checkout-cache-clash.sh [new file with mode: 0755]
t/t2002-checkout-cache-u.sh [new file with mode: 0755]
t/t2003-checkout-cache-mkdir.sh [new file with mode: 0644]
t/t2100-update-cache-badpath.sh [new file with mode: 0755]
t/t3000-ls-files-others.sh [new file with mode: 0755]
t/t3010-ls-files-killed.sh [new file with mode: 0755]
t/t3100-ls-tree-restrict.sh [new file with mode: 0644]
t/t4000-diff-format.sh [new file with mode: 0755]
t/t4001-diff-rename.sh [new file with mode: 0755]
t/t4002-diff-basic.sh [new file with mode: 0644]
t/t4003-diff-rename-1.sh [new file with mode: 0644]
t/t4004-diff-rename-symlink.sh [new file with mode: 0644]
t/t4005-diff-rename-2.sh [new file with mode: 0644]
t/t4006-diff-mode.sh [new file with mode: 0644]
t/t4007-rename-3.sh [new file with mode: 0644]
t/t4008-diff-break-rewrite.sh [new file with mode: 0644]
t/t4009-diff-rename-4.sh [new file with mode: 0644]
t/t4010-diff-pathspec.sh [new file with mode: 0644]
t/t4100-apply-stat.sh [new file with mode: 0644]
t/t4100/t-apply-1.expect [new file with mode: 0644]
t/t4100/t-apply-1.patch [new file with mode: 0644]
t/t4100/t-apply-2.expect [new file with mode: 0644]
t/t4100/t-apply-2.patch [new file with mode: 0644]
t/t4100/t-apply-3.expect [new file with mode: 0644]
t/t4100/t-apply-3.patch [new file with mode: 0644]
t/t4100/t-apply-4.expect [new file with mode: 0644]
t/t4100/t-apply-4.patch [new file with mode: 0644]
t/t4100/t-apply-5.expect [new file with mode: 0644]
t/t4100/t-apply-5.patch [new file with mode: 0644]
t/t4100/t-apply-6.expect [new file with mode: 0644]
t/t4100/t-apply-6.patch [new file with mode: 0644]
t/t4100/t-apply-7.expect [new file with mode: 0644]
t/t4100/t-apply-7.patch [new file with mode: 0644]
t/t5000-tar-tree.sh [new file with mode: 0644]
t/t5300-pack-object.sh [new file with mode: 0755]
t/t6001-rev-list-merge-order.sh [new file with mode: 0755]
t/test-lib.sh [new file with mode: 0755]
tag.c [new file with mode: 0644]
tag.h [new file with mode: 0644]
tar-tree.c [new file with mode: 0644]
test-date.c [new file with mode: 0644]
test-delta.c [new file with mode: 0644]
tree.c [new file with mode: 0644]
tree.h [new file with mode: 0644]
unpack-file.c [new file with mode: 0644]
unpack-objects.c [new file with mode: 0644]
update-cache.c [new file with mode: 0644]
upload-pack.c [new file with mode: 0644]
usage.c [new file with mode: 0644]
verify-pack.c [new file with mode: 0644]
write-blob.c [new file with mode: 0644]
write-tree.c [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..6ff87c4
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,361 @@
+
+ Note that the only valid version of the GPL as far as this project
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+ HOWEVER, in order to allow a migration to GPLv3 if that seems like
+ a good idea, I also ask that people involved with the project make
+ their preferences known. In particular, if you trust me to make that
+ decision, you might note so in your copyright message, ie something
+ like
+
+       This file is licensed under the GPL v2, or a later version
+       at the discretion of Linus.
+
+  might avoid issues. But we can also just decide to synchronize and
+  contact all copyright holders on record if/when the occasion arises.
+
+                       Linus Torvalds
+
+----------------------------------------
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  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 Library 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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+
+  <signature of Ty Coon>, 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 Library General
+Public License instead of this License.
diff --git a/Documentation/Makefile b/Documentation/Makefile
new file mode 100644 (file)
index 0000000..e3844f2
--- /dev/null
@@ -0,0 +1,41 @@
+MAN1_TXT=$(wildcard git-*.txt)
+MAN7_TXT=git.txt
+
+DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN7_TXT))
+
+DOC_MAN1=$(patsubst %.txt,%.1,$(MAN1_TXT))
+DOC_MAN7=$(patsubst %.txt,%.7,$(MAN7_TXT))
+
+#
+# Please note that there is a minor bug in asciidoc.
+# The version after 6.0.3 _will_ include the patch found here:
+#   http://marc.theaimsgroup.com/?l=git&m=111558757202243&w=2
+#
+# Until that version is released you may have to apply the patch
+# yourself - yes, all 6 characters of it!
+#
+
+all: html man
+
+html: $(DOC_HTML)
+
+
+man: man1 man7
+man1: $(DOC_MAN1)
+man7: $(DOC_MAN7)
+
+# 'include' dependencies
+git-diff-%.txt: diff-format.txt
+       touch $@
+
+clean:
+       rm -f *.xml *.html *.1 *.7
+
+%.html : %.txt
+       asciidoc -b css-embedded -d manpage $<
+
+%.1 %.7 : %.xml
+       xmlto man $<
+
+%.xml : %.txt
+       asciidoc -b docbook -d manpage $<
diff --git a/Documentation/cvs-migration.txt b/Documentation/cvs-migration.txt
new file mode 100644 (file)
index 0000000..e39b829
--- /dev/null
@@ -0,0 +1,232 @@
+Git for CVS users
+=================
+
+Ok, so you're a CVS user. That's ok, it's a treatable condition, and the
+first step to recovery is admitting you have a problem. The fact that
+you are reading this file means that you may be well on that path
+already.
+
+The thing about CVS is that it absolutely sucks as a source control
+manager, and you'll thus be happy with almost anything else. Git,
+however, may be a bit _too_ different (read: "good") for your taste, and
+does a lot of things differently. 
+
+One particular suckage of CVS is very hard to work around: CVS is
+basically a tool for tracking _file_ history, while git is a tool for
+tracking _project_ history.  This sometimes causes problems if you are
+used to doing very strange things in CVS, in particular if you're doing
+things like making branches of just a subset of the project.  Git can't
+track that, since git never tracks things on the level of an individual
+file, only on the whole project level. 
+
+The good news is that most people don't do that, and in fact most sane
+people think it's a bug in CVS that makes it tag (and check in changes)
+one file at a time.  So most projects you'll ever see will use CVS
+_as_if_ it was sane.  In which case you'll find it very easy indeed to
+move over to Git. 
+
+First off: this is not a git tutorial. See Documentation/tutorial.txt
+for how git actually works. This is more of a random collection of
+gotcha's and notes on converting from CVS to git.
+
+Second: CVS has the notion of a "repository" as opposed to the thing
+that you're actually working in (your working directory, or your
+"checked out tree").  Git does not have that notion at all, and all git
+working directories _are_ the repositories.  However, you can easily
+emulate the CVS model by having one special "global repository", which
+people can synchronize with.  See details later, but in the meantime
+just keep in mind that with git, every checked out working tree will
+have a full revision control history of its own.
+
+
+Importing a CVS archive
+-----------------------
+
+Ok, you have an old project, and you want to at least give git a chance
+to see how it performs. The first thing you want to do (after you've
+gone through the git tutorial, and generally familiarized yourself with
+how to commit stuff etc in git) is to create a git'ified version of your
+CVS archive.
+
+Happily, that's very easy indeed. Git will do it for you, although git
+will need the help of a program called "cvsps":
+
+       http://www.cobite.com/cvsps/
+
+which is not actually related to git at all, but which makes CVS usage
+look almost sane (ie you almost certainly want to have it even if you
+decide to stay with CVS). However, git will want at _least_ version 2.1
+of cvsps (available at the address above), and in fact will currently
+refuse to work with anything else.
+
+Once you've gotten (and installed) cvsps, you may or may not want to get
+any more familiar with it, but make sure it is in your path. After that,
+the magic command line is
+
+       git cvsimport -v -d <cvsroot> <module> <destination>
+
+which will do exactly what you'd think it does: it will create a git
+archive of the named CVS module. The new archive will be created in the
+subdirectory named <destination>; it'll be created if it doesn't exist.
+Default is the local directory.
+
+It can take some time to actually do the conversion for a large archive
+since it involves checking out from CVS every revision of every file,
+and the conversion script is reasonably chatty unless you omit the '-v'
+option, but on some not very scientific tests it averaged about twenty
+revisions per second, so a medium-sized project should not take more
+than a couple of minutes.  For larger projects or remote repositories,
+the process may take longer.
+
+After the (initial) import is done, the CVS archive's current head
+revision will be checked out -- thus, you can start adding your own
+changes right away.
+
+The import is incremental, i.e. if you call it again next month it'll
+fetch any CVS updates that have been happening in the meantime. The
+cut-off is date-based, so don't change the branches that were imported
+from CVS.
+
+You can merge those updates (or, in fact, a different CVS branch) into
+your main branch:
+
+       cg-merge <branch>
+
+The HEAD revision from CVS is named "origin", not "HEAD", because git
+already uses "HEAD". (If you don't like 'origin', use cvsimport's
+'-o' option to change it.)
+
+
+Emulating CVS behaviour
+-----------------------
+
+
+FIXME! Talk about setting up several repositories, and pulling and
+pushing between them. Talk about merging, and branches. Some of this
+needs to be in the tutorial too.
+
+
+
+CVS annotate
+------------
+
+So, something has gone wrong, and you don't know whom to blame, and
+you're an ex-CVS user and used to do "cvs annotate" to see who caused
+the breakage. You're looking for the "git annotate", and it's just
+claiming not to find such a script. You're annoyed.
+
+Yes, that's right.  Core git doesn't do "annotate", although it's
+technically possible, and there are at least two specialized scripts out
+there that can be used to get equivalent information (see the git
+mailing list archives for details). 
+
+Git has a couple of alternatives, though, that you may find sufficient
+or even superior depending on your use.  One is called "git-whatchanged"
+(for obvious reasons) and the other one is called "pickaxe" ("a tool for
+the software archeologist"). 
+
+The "git-whatchanged" script is a truly trivial script that can give you
+a good overview of what has changed in a file or a directory (or an
+arbitrary list of files or directories).  The "pickaxe" support is an
+additional layer that can be used to further specify exactly what you're
+looking for, if you already know the specific area that changed.
+
+Let's step back a bit and think about the reason why you would
+want to do "cvs annotate a-file.c" to begin with.
+
+You would use "cvs annotate" on a file when you have trouble
+with a function (or even a single "if" statement in a function)
+that happens to be defined in the file, which does not do what
+you want it to do.  And you would want to find out why it was
+written that way, because you are about to modify it to suit
+your needs, and at the same time you do not want to break its
+current callers.  For that, you are trying to find out why the
+original author did things that way in the original context.
+
+Many times, it may be enough to see the commit log messages of
+commits that touch the file in question, possibly along with the
+patches themselves, like this:
+
+       $ git-whatchanged -p a-file.c
+
+This will show log messages and patches for each commit that
+touches a-file.
+
+This, however, may not be very useful when this file has many
+modifications that are not related to the piece of code you are
+interested in.  You would see many log messages and patches that
+do not have anything to do with the piece of code you are
+interested in.  As an example, assuming that you have this piece
+code that you are interested in in the HEAD version:
+
+       if (frotz) {
+               nitfol();
+       }
+
+you would use git-rev-list and git-diff-tree like this:
+
+       $ git-rev-list HEAD |
+         git-diff-tree --stdin -v -p -S'if (frotz) {
+               nitfol();
+       }'
+
+We have already talked about the "--stdin" form of git-diff-tree
+command that reads the list of commits and compares each commit
+with its parents.  The git-whatchanged command internally runs
+the equivalent of the above command, and can be used like this:
+
+       $ git-whatchanged -p -S'if (frotz) {
+               nitfol();
+       }'
+
+When the -S option is used, git-diff-tree command outputs
+differences between two commits only if one tree has the
+specified string in a file and the corresponding file in the
+other tree does not.  The above example looks for a commit that
+has the "if" statement in it in a file, but its parent commit
+does not have it in the same shape in the corresponding file (or
+the other way around, where the parent has it and the commit
+does not), and the differences between them are shown, along
+with the commit message (thanks to the -v flag).  It does not
+show anything for commits that do not touch this "if" statement.
+
+Also, in the original context, the same statement might have
+appeared at first in a different file and later the file was
+renamed to "a-file.c".  CVS annotate would not help you to go
+back across such a rename, but GIT would still help you in such
+a situation.  For that, you can give the -C flag to
+git-diff-tree, like this:
+
+       $ git-whatchanged -p -C -S'if (frotz) {
+               nitfol();
+       }'
+
+When the -C flag is used, file renames and copies are followed.
+So if the "if" statement in question happens to be in "a-file.c"
+in the current HEAD commit, even if the file was originally
+called "o-file.c" and then renamed in an earlier commit, or if
+the file was created by copying an existing "o-file.c" in an
+earlier commit, you will not lose track.  If the "if" statement
+did not change across such rename or copy, then the commit that
+does rename or copy would not show in the output, and if the
+"if" statement was modified while the file was still called
+"o-file.c", it would find the commit that changed the statement
+when it was in "o-file.c".
+
+[ BTW, the current versions of "git-diff-tree -C" is not eager
+  enough to find copies, and it will miss the fact that a-file.c
+  was created by copying o-file.c unless o-file.c was somehow
+  changed in the same commit.]
+
+You can use the --pickaxe-all flag in addition to the -S flag.
+This causes the differences from all the files contained in
+those two commits, not just the differences between the files
+that contain this changed "if" statement:
+
+       $ git-whatchanged -p -C -S'if (frotz) {
+               nitfol();
+       }' --pickaxe-all
+
+[ Side note.  This option is called "--pickaxe-all" because -S
+  option is internally called "pickaxe", a tool for software
+  archaeologists.]
diff --git a/Documentation/diff-format.txt b/Documentation/diff-format.txt
new file mode 100644 (file)
index 0000000..d6ce035
--- /dev/null
@@ -0,0 +1,135 @@
+The output format from "git-diff-cache", "git-diff-tree" and
+"git-diff-files" is very similar.
+
+These commands all compare two sets of things; what are
+compared are different:
+
+git-diff-cache <tree-ish>::
+        compares the <tree-ish> and the files on the filesystem.
+
+git-diff-cache --cached <tree-ish>::
+        compares the <tree-ish> and the cache.
+
+git-diff-tree [-r] <tree-ish-1> <tree-ish-2> [<pattern>...]::
+        compares the trees named by the two arguments.
+
+git-diff-files [<pattern>...]::
+        compares the cache and the files on the filesystem.
+
+
+An output line is formatted this way:
+
+in-place edit  :100644 100644 bcd1234... 0123456... M file0
+copy-edit      :100644 100644 abcd123... 1234567... C68 file1 file2
+rename-edit    :100644 100644 abcd123... 1234567... R86 file1 file3
+create         :000000 100644 0000000... 1234567... N file4
+delete         :100644 000000 1234567... 0000000... D file5
+unmerged       :000000 000000 0000000... 0000000... U file6
+
+That is, from the left to the right:
+
+  (1) a colon.
+  (2) mode for "src"; 000000 if creation or unmerged.
+  (3) a space.
+  (4) mode for "dst"; 000000 if deletion or unmerged.
+  (5) a space.
+  (6) sha1 for "src"; 0{40} if creation or unmerged.
+  (7) a space.
+  (8) sha1 for "dst"; 0{40} if creation, unmerged or "look at work tree".
+  (9) a space.
+ (10) status, followed by optional "score" number.
+ (11) a tab or a NUL when '-z' option is used.
+ (12) path for "src"
+ (13) a tab or a NUL when '-z' option is used; only exists for C or R.
+ (14) path for "dst"; only exists for C or R.
+ (15) an LF or a NUL when '-z' option is used, to terminate the record.
+
+<sha1> is shown as all 0's if new is a file on the filesystem
+and it is out of sync with the cache.  Example:
+
+  :100644 100644 5be4a4...... 000000...... M file.c
+
+Generating patches with -p
+--------------------------
+
+When "git-diff-cache", "git-diff-tree", or "git-diff-files" are run
+with a '-p' option, they do not produce the output described above;
+instead they produce a patch file.
+
+The patch generation can be customized at two levels.  This
+customization also applies to "git-diff-helper".
+
+1. When the environment variable 'GIT_EXTERNAL_DIFF' is not set,
+   these commands internally invoke "diff" like this:
+
+      diff -L a/<path> -L a/<path> -pu <old> <new>
++
+For added files, `/dev/null` is used for <old>.  For removed
+files, `/dev/null` is used for <new>
++
+The "diff" formatting options can be customized via the
+environment variable 'GIT_DIFF_OPTS'.  For example, if you
+prefer context diff:
+
+      GIT_DIFF_OPTS=-c git-diff-cache -p $(cat .git/HEAD)
+
+
+2. When the environment variable 'GIT_EXTERNAL_DIFF' is set, the
+   program named by it is called, instead of the diff invocation
+   described above.
++
+For a path that is added, removed, or modified,
+'GIT_EXTERNAL_DIFF' is called with 7 parameters:
+
+     path old-file old-hex old-mode new-file new-hex new-mode
++
+where:
+
+     <old|new>-file:: are files GIT_EXTERNAL_DIFF can use to read the
+                     contents of <old|ne>,
+     <old|new>-hex:: are the 40-hexdigit SHA1 hashes,
+     <old|new>-mode:: are the octal representation of the file modes.
+
++ 
+The file parameters can point at the user's working file
+(e.g. `new-file` in "git-diff-files"), `/dev/null` (e.g. `old-file`
+when a new file is added), or a temporary file (e.g. `old-file` in the
+cache).  'GIT_EXTERNAL_DIFF' should not worry about unlinking the
+temporary file --- it is removed when 'GIT_EXTERNAL_DIFF' exits.
+
+For a path that is unmerged, 'GIT_EXTERNAL_DIFF' is called with 1
+parameter, <path>.
+
+
+Git specific extention to diff format
+-------------------------------------
+
+What -p option produces is slightly different from the
+traditional diff format.
+
+ (1) It is preceeded with a "git diff" header, that looks like
+     this:
+
+     diff --git a/file1 b/file2
+
+     The a/ and b/ filenames are the same unless rename/copy is
+     involved.  Especially, even for a creation or a deletion,
+     /dev/null is _not_ used in place of a/ or b/ filename.
+
+     When rename/copy is involved, file1 and file2 shows the
+     name of the source file of the rename/copy and the name of
+     the file that rename/copy produces, respectively.
+
+ (2) It is followed by extended header lines that are one or
+     more of:
+
+       old mode <mode>
+       new mode <mode>
+       deleted file mode <mode>
+       new file mode <mode>
+       copy from <path>
+       copy to <path>
+       rename from <path>
+       rename to <path>
+       similarity index <number>
+       dissimilarity index <number>
diff --git a/Documentation/diffcore.txt b/Documentation/diffcore.txt
new file mode 100644 (file)
index 0000000..6c474d1
--- /dev/null
@@ -0,0 +1,248 @@
+Tweaking diff output
+====================
+June 2005
+
+
+Introduction
+------------
+
+The diff commands git-diff-cache, git-diff-files, and
+git-diff-tree can be told to manipulate differences they find
+in unconventional ways before showing diff(1) output.  The
+manipulation is collectively called "diffcore transformation".
+This short note describes what they are and how to use them to
+produce diff outputs that are easier to understand than the
+conventional kind.
+
+
+The chain of operation
+----------------------
+
+The git-diff-* family works by first comparing two sets of
+files:
+
+ - git-diff-cache compares contents of a "tree" object and the
+   working directory (when --cached flag is not used) or a
+   "tree" object and the index file (when --cached flag is
+   used);
+
+ - git-diff-files compares contents of the index file and the
+   working directory;
+
+ - git-diff-tree compares contents of two "tree" objects.
+
+In all of these cases, the commands themselves compare
+corresponding paths in the two sets of files.  The result of
+comparison is passed from these commands to what is internally
+called "diffcore", in a format similar to what is output when
+the -p option is not used.  E.g.
+
+    in-place edit  :100644 100644 bcd1234... 0123456... M file0
+    create         :000000 100644 0000000... 1234567... N file4
+    delete         :100644 000000 1234567... 0000000... D file5
+    unmerged       :000000 000000 0000000... 0000000... U file6
+
+The diffcore mechanism is fed a list of such comparison results
+(each of which is called "filepair", although at this point each
+of them talks about a single file), and transforms such a list
+into another list.  There are currently 6 such transformations:
+
+ - diffcore-pathspec
+ - diffcore-break
+ - diffcore-rename
+ - diffcore-merge-broken
+ - diffcore-pickaxe
+ - diffcore-order
+
+These are applied in sequence.  The set of filepairs git-diff-*
+commands find are used as the input to diffcore-pathspec, and
+the output from diffcore-pathspec is used as the input to the
+next transformation.  The final result is then passed to the
+output routine and generates either diff-raw format (see Output
+format sections of the manual for git-diff-* commands) or
+diff-patch format.
+
+
+diffcore-pathspec
+-----------------
+
+The first transformation in the chain is diffcore-pathspec, and
+is controlled by giving the pathname parameters to the
+git-diff-* commands on the command line.  The pathspec is used
+to limit the world diff operates in.  It removes the filepairs
+outside the specified set of pathnames.
+
+Implementation note.  For performance reasons, git-diff-tree
+uses the pathname parameters on the command line to cull set of
+filepairs it feeds the diffcore mechanism itself, and does not
+use diffcore-pathspec, but the end result is the same.
+
+
+diffcore-break
+--------------
+
+The second transformation in the chain is diffcore-break, and is
+controlled by the -B option to the git-diff-* commands.  This is
+used to detect a filepair that represents "complete rewrite" and
+break such filepair into two filepairs that represent delete and
+create.  E.g.  If the input contained this filepair:
+
+    :100644 100644 bcd1234... 0123456... M file0
+
+and if it detects that the file "file0" is completely rewritten,
+it changes it to:
+
+    :100644 000000 bcd1234... 0000000... D file0
+    :000000 100644 0000000... 0123456... N file0
+
+For the purpose of breaking a filepair, diffcore-break examines
+the extent of changes between the contents of the files before
+and after modification (i.e. the contents that have "bcd1234..."
+and "0123456..." as their SHA1 content ID, in the above
+example).  The amount of deletion of original contents and
+insertion of new material are added together, and if it exceeds
+the "break score", the filepair is broken into two.  The break
+score defaults to 50% of the size of the smaller of the original
+and the result (i.e. if the edit shrinks the file, the size of
+the result is used; if the edit lengthens the file, the size of
+the original is used), and can be customized by giving a number
+after "-B" option (e.g. "-B75" to tell it to use 75%).
+
+
+diffcore-rename
+---------------
+
+This transformation is used to detect renames and copies, and is
+controlled by the -M option (to detect renames) and the -C option
+(to detect copies as well) to the git-diff-* commands.  If the
+input contained these filepairs:
+
+    :100644 000000 0123456... 0000000... D fileX
+    :000000 100644 0000000... 0123456... N file0
+
+and the contents of the deleted file fileX is similar enough to
+the contents of the created file file0, then rename detection
+merges these filepairs and creates:
+
+    :100644 100644 0123456... 0123456... R100 fileX file0
+
+When the "-C" option is used, the original contents of modified
+files and contents of unchanged files are considered as
+candidates of the source files in rename/copy operation, in
+addition to the deleted files.  If the input were like these
+filepairs, that talk about a modified file fileY and a newly
+created file file0:
+
+    :100644 100644 0123456... 1234567... M fileY
+    :000000 100644 0000000... 0123456... N file0
+
+the original contents of fileY and the resulting contents of
+file0 are compared, and if they are similar enough, they are
+changed to:
+
+    :100644 100644 0123456... 1234567... M fileY
+    :100644 100644 0123456... 0123456... C100 fileY file0
+
+In both rename and copy detection, the same "extent of changes"
+algorithm used in diffcore-break is used to determine if two
+files are "similar enough", and can be customized to use
+similarity score different from the default 50% by giving a
+number after "-M" or "-C" option (e.g. "-M8" to tell it to use
+8/10 = 80%).
+
+Note.  When the "-C" option is used with --find-copies-harder
+option, git-diff-* commands feed unmodified filepairs to
+diffcore mechanism as well as modified ones.  This lets the copy
+detector consider unmodified files as copy source candidates at
+the expense of making it slower.  Without --find-copies-harder,
+git-diff-* commands can detect copies only if the file that was
+copied happened to have been modified in the same changeset.
+
+
+diffcore-merge-broken
+---------------------
+
+This transformation is used to merge filepairs broken by
+diffcore-break, and were not transformed into rename/copy by
+diffcore-rename, back into a single modification.  This always
+runs when diffcore-break is used.
+
+For the purpose of merging broken filepairs back, it uses a
+different "extent of changes" computation from the ones used by
+diffcore-break and diffcore-rename.  It counts only the deletion
+from the original, and does not count insertion.  If you removed
+only 10 lines from a 100-line document, even if you added 910
+new lines to make a new 1000-line document, you did not do a
+complete rewrite.  diffcore-break breaks such a case in order to
+help diffcore-rename to consider such filepairs as candidate of
+rename/copy detection, but if filepairs broken that way were not
+matched with other filepairs to create rename/copy, then this
+transformation merges them back into the original
+"modification".
+
+The "extent of changes" parameter can be tweaked from the
+default 80% (that is, unless more than 80% of the original
+material is deleted, the broken pairs are merged back into a
+single modification) by giving a second number to -B option,
+like these:
+
+       -B50/60 (give 50% "break score" to diffcore-break, use
+                 60% for diffcore-merge-broken).
+       -B/60   (the same as above, since diffcore-break defautls to
+                50%).
+
+Note that earlier implementation left a broken pair as a separate
+creation and deletion patches.  This was unnecessary hack and
+the latest implementation always merges all the broken pairs
+back into modifications, but the resulting patch output is
+formatted differently to still let the reviewing easier for such
+a complete rewrite by showing the entire contents of old version
+prefixed with '-', followed by the entire contents of new
+version prefixed with '+'.
+
+
+diffcore-pickaxe
+----------------
+
+This transformation is used to find filepairs that represent
+changes that touch a specified string, and is controlled by the
+-S option and the --pickaxe-all option to the git-diff-*
+commands.
+
+When diffcore-pickaxe is in use, it checks if there are
+filepairs whose "original" side has the specified string and
+whose "result" side does not.  Such a filepair represents "the
+string appeared in this changeset".  It also checks for the
+opposite case that loses the specified string.
+
+When --pickaxe-all is not in effect, diffcore-pickaxe leaves
+only such filepairs that touches the specified string in its
+output.  When --pickaxe-all is used, diffcore-pickaxe leaves all
+filepairs intact if there is such a filepair, or makes the
+output empty otherwise.  The latter behaviour is designed to
+make reviewing of the changes in the context of the whole
+changeset easier.
+
+
+diffcore-order
+--------------
+
+This is used to reorder the filepairs according to the user's
+(or project's) taste, and is controlled by the -O option to the
+git-diff-* commands.
+
+This takes a text file each of whose line is a shell glob
+pattern.  Filepairs that match a glob pattern on an earlier line
+in the file are output before ones that match a later line, and
+filepairs that do not match any glob pattern are output last.
+
+As an example, typical orderfile for the core GIT probably
+should look like this:
+
+    README
+    Makefile
+    Documentation
+    *.h
+    *.c
+    t
+
diff --git a/Documentation/git-apply-patch-script.txt b/Documentation/git-apply-patch-script.txt
new file mode 100644 (file)
index 0000000..a6f860d
--- /dev/null
@@ -0,0 +1,32 @@
+git-apply-patch-script(1)
+=========================
+v0.1, May 2005
+
+NAME
+----
+git-apply-patch-script - Sample script to apply the diffs from git-diff-*
+
+
+SYNOPSIS
+--------
+'git-apply-patch-script'
+
+DESCRIPTION
+-----------
+This is a sample script to be used via the 'GIT_EXTERNAL_DIFF'
+environment variable to apply the differences that the "git-diff-*"
+family of commands report to the current work tree.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-apply.txt b/Documentation/git-apply.txt
new file mode 100644 (file)
index 0000000..81eb155
--- /dev/null
@@ -0,0 +1,72 @@
+git-apply(1)
+============
+v0.1, June 2005
+
+NAME
+----
+git-apply - Apply patch on a GIT index file and a work tree
+
+
+SYNOPSIS
+--------
+'git-apply' [--no-merge] [--stat] [--summary] [--check]
+[--index] [--show-files] [-] [<file>...]
+
+DESCRIPTION
+-----------
+Reads supplied diff output and applies it on a GIT index file
+and a work tree.
+
+OPTIONS
+-------
+<file>...::
+       The files to read patch from.
+
+-::
+       Instead of reading from a file, read from standard input.
+
+--no-merge::
+       The default mode of operation is the merge behaviour
+       which is not quite implemented yet.  This flag
+       explicitly tells the program not to use the merge
+       behaviour.
+
+--stat::
+       Instead of applying the patch, output diffstat for the
+       input.
+
+--summary::
+       Instead of applying the patch, output a condensed
+       summary of information obtained from git diff extended
+       headers, such as creations, renames and mode changes.
+
+--check::
+       Instead of applying the patch, see if the patch is
+       applicable to the current work tree and/or the index
+       file and detects errors.
+
+--index::
+       When --check is in effect, or when applying the patch
+       (which is the default when none of the options that
+       disables it is in effect), make sure the patch is
+       applicable to what the current index file records.  If
+       the file to be patched in the work tree is not
+       up-to-date, it is flagged as an error.  This flag also
+       causes the index file to be updated.
+
+--show-files::
+       Show summary of files that are affected by the patch.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by Junio C Hamano
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt
new file mode 100644 (file)
index 0000000..2131a29
--- /dev/null
@@ -0,0 +1,61 @@
+git-cat-file(1)
+===============
+v0.1, May 2005
+
+NAME
+----
+git-cat-file - Provide content or type information for repository objects
+
+
+SYNOPSIS
+--------
+'git-cat-file' (-t | -s | <type>) <object>
+
+DESCRIPTION
+-----------
+Provides content or type of objects in the repository. The type
+is required unless '-t' is used to find the object type,
+or '-s' is used to find the object size.
+
+OPTIONS
+-------
+<object>::
+       The sha1 identifier of the object.
+
+-t::
+       Instead of the content, show the object type identified by
+       <object>.
+
+-s::
+       Instead of the content, show the object size identified by
+       <object>.
+
+<type>::
+       Typically this matches the real type of <object> but asking
+       for a type that can trivially dereferenced from the given
+       <object> is also permitted.  An example is to ask for a
+       "tree" with <object> being a commit object that contains it,
+       or to ask for a "blob" with <object> being a tag object that
+       points at it.
+
+OUTPUT
+------
+If '-t' is specified, one of the <type>.  If '-s' is specified,
+the size of the <object> in bytes.
+
+Otherwise the raw (though uncompressed) contents of the <object> will
+be returned.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-check-files.txt b/Documentation/git-check-files.txt
new file mode 100644 (file)
index 0000000..6146098
--- /dev/null
@@ -0,0 +1,50 @@
+git-check-files(1)
+==================
+v0.1, May 2005
+
+NAME
+----
+git-check-files - Verify a list of files are up-to-date
+
+
+
+SYNOPSIS
+--------
+'git-check-files' <file>...
+
+DESCRIPTION
+-----------
+Check that a list of files are up-to-date between the filesystem and
+the cache. Used to verify a patch target before doing a patch.
+
+Files that do not exist on the filesystem are considered up-to-date
+(whether or not they are in the cache).
+
+Emits an error message on failure:
+
+preparing to update existing file <file> not in cache::
+         <file> exists but is not in the cache
+
+preparing to update file <file> not uptodate in cache::
+         <file> on disk is not up-to-date with the cache
+
+Exits with a status code indicating success if all files are
+up-to-date.
+
+See Also
+--------
+link:git-update-cache.html[git-update-cache]
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-checkout-cache.txt b/Documentation/git-checkout-cache.txt
new file mode 100644 (file)
index 0000000..321a00c
--- /dev/null
@@ -0,0 +1,106 @@
+git-checkout-cache(1)
+=====================
+v0.1, May 2005
+
+NAME
+----
+git-checkout-cache - Copy files from the cache to the working directory
+
+
+SYNOPSIS
+--------
+'git-checkout-cache' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>]
+                  [--] <file>...
+
+DESCRIPTION
+-----------
+Will copy all files listed from the cache to the working directory
+(not overwriting existing files).
+
+OPTIONS
+-------
+-u::
+       update stat information for the checked out entries in
+       the cache file.
+
+-q::
+       be quiet if files exist or are not in the cache
+
+-f::
+       forces overwrite of existing files
+
+-a::
+       checks out all files in the cache (will then continue to
+       process listed files).
+
+-n::
+       Don't checkout new files, only refresh files already checked
+       out.
+
+--prefix=<string>::
+       When creating files, prepend <string> (usually a directory
+       including a trailing /)
+
+--::
+       Do not interpret any more arguments as options.
+
+Note that the order of the flags matters:
+
+     git-checkout-cache -a -f file.c
+
+will first check out all files listed in the cache (but not overwrite
+any old ones), and then force-checkout `file.c` a second time (ie that
+one *will* overwrite any old contents with the same filename).
+
+Also, just doing "git-checkout-cache" does nothing. You probably meant
+"git-checkout-cache -a". And if you want to force it, you want
+"git-checkout-cache -f -a".
+
+Intuitiveness is not the goal here. Repeatability is. The reason for
+the "no arguments means no work" thing is that from scripts you are
+supposed to be able to do things like:
+
+       find . -name '*.h' -print0 | xargs -0 git-checkout-cache -f --
+
+which will force all existing `*.h` files to be replaced with their
+cached copies. If an empty command line implied "all", then this would
+force-refresh everything in the cache, which was not the point.
+
+To update and refresh only the files already checked out:
+
+        git-checkout-cache -n -f -a && git-update-cache --ignore-missing --refresh
+
+Oh, and the "--" is just a good idea when you know the rest will be
+filenames. Just so that you wouldn't have a filename of "-a" causing
+problems (not possible in the above example, but get used to it in
+scripting!).
+
+The prefix ability basically makes it trivial to use
+git-checkout-cache as an "export as tree" function. Just read the
+desired tree into the index, and do a
+  
+        git-checkout-cache --prefix=git-export-dir/ -a
+  
+and git-checkout-cache will "export" the cache into the specified
+directory.
+  
+NOTE The final "/" is important. The exported name is literally just
+prefixed with the specified string, so you can also do something like
+
+    git-checkout-cache --prefix=.merged- Makefile
+
+to check out the currently cached copy of `Makefile` into the file
+`.merged-Makefile`
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-commit-tree.txt b/Documentation/git-commit-tree.txt
new file mode 100644 (file)
index 0000000..c0dc1f4
--- /dev/null
@@ -0,0 +1,88 @@
+git-commit-tree(1)
+==================
+v0.1, May 2005
+
+NAME
+----
+git-commit-tree - Creates a new commit object
+
+
+SYNOPSIS
+--------
+'git-commit-tree' <tree> [-p <parent commit>]\   < changelog
+
+DESCRIPTION
+-----------
+Creates a new commit object based on the provided tree object and
+emits the new commit object id on stdout. If no parent is given then
+it is considered to be an initial tree.
+
+A commit object usually has 1 parent (a commit after a change) or up
+to 16 parents.  More than one parent represents a merge of branches
+that led to them.
+
+While a tree represents a particular directory state of a working
+directory, a commit represents that state in "time", and explains how
+to get there.
+
+Normally a commit would identify a new "HEAD" state, and while git
+doesn't care where you save the note about that state, in practice we
+tend to just write the result to the file `.git/HEAD`, so that we can
+always see what the last committed state was.
+
+OPTIONS
+-------
+<tree>::
+       An existing tree object
+
+-p <parent commit>::
+       Each '-p' indicates a the id of a parent commit object.
+       
+
+Commit Information
+------------------
+
+A commit encapsulates:
+
+- all parent object ids
+- author name, email and date
+- committer name and email and the commit time.
+
+If not provided, "git-commit-tree" uses your name, hostname and domain to
+provide author and committer info. This can be overridden using the
+following environment variables.
+
+       GIT_AUTHOR_NAME
+       GIT_AUTHOR_EMAIL
+       GIT_AUTHOR_DATE
+       GIT_COMMITTER_NAME
+       GIT_COMMITTER_EMAIL
+
+(nb <,> and '\n's are stripped)
+
+A commit comment is read from stdin (max 999 chars). If a changelog
+entry is not provided via '<' redirection, "git-commit-tree" will just wait
+for one to be entered and terminated with ^D
+
+Diagnostics
+-----------
+You don't exist. Go away!::
+    The passwd(5) gecos field couldn't be read
+
+See Also
+--------
+link:git-write-tree.html[git-write-tree]
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-convert-cache.txt b/Documentation/git-convert-cache.txt
new file mode 100644 (file)
index 0000000..66d7fe7
--- /dev/null
@@ -0,0 +1,30 @@
+git-convert-cache(1)
+====================
+v0.1, May 2005
+
+NAME
+----
+git-convert-cache - Converts old-style GIT repository
+
+
+SYNOPSIS
+--------
+'git-convert-cache'
+
+DESCRIPTION
+-----------
+Converts old-style GIT repository to the latest format
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-cvsimport-script.txt b/Documentation/git-cvsimport-script.txt
new file mode 100644 (file)
index 0000000..4eb0f43
--- /dev/null
@@ -0,0 +1,75 @@
+git-cvsimport-script(1)
+=======================
+v0.1, July 2005
+
+NAME
+----
+git-cvsimport-script - Import a CVS repository into git
+
+
+SYNOPSIS
+--------
+'git-cvsimport-script' [ -o <branch-for-HEAD> ] [ -h ] [ -v ]
+                       [ -d <CVSROOT> ] [ -p <options-for-cvsps> ]
+                       [ -C <GIT_repository> ] [ <CVS_module> ]
+
+
+DESCRIPTION
+-----------
+Imports a CVS repository into git. It will either create a new
+repository, or incrementally import into an existing one.
+
+Splitting the CVS log into patch sets is done by 'cvsps'.
+At least version 2.1 is required.
+
+OPTIONS
+-------
+-d <CVSROOT>::
+       The root of the CVS archive. May be local (a simple path) or remote;
+       currently, only the :local:, :ext: and :pserver: access methods 
+       are supported.
+
+-o <branch-for-HEAD>::
+       The 'HEAD' branch from CVS is imported to the 'origin' branch within
+       the git repository, as 'HEAD' already has a special meaning for git.
+       Use this option if you want to import into a different branch.
+
+       Use '-o master' for continuing an import that was initially done by
+       the old cvs2git tool.
+
+-p <options-for-cvsps>::
+       Additional options for cvsps.
+       The options '-x' and '-A' are implicit and should not be used here.
+
+       If you need to pass multiple options, separate them with a comma.
+
+-v::
+       Verbosity: let 'cvsimport' report what it is doing.
+
+<CVS_module>::
+       The CVS module you want to import. Relative to <CVSROOT>.
+
+-h::
+       Print a short usage message and exit.
+
+OUTPUT
+------
+If '-v' is specified, the script reports what it is doing.
+
+Otherwise, success is indicated the Unix way, i.e. by simply exiting with
+a zero exit status.
+
+
+Author
+------
+Written by Matthias Urlichs <smurf@smurf.noris.de>, with help from
+various participants of the git-list <git@vger.kernel.org>.
+
+Documentation
+--------------
+Documentation by Matthias Urlichs <smurf@smurf.noris.de>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-diff-cache.txt b/Documentation/git-diff-cache.txt
new file mode 100644 (file)
index 0000000..f6dd703
--- /dev/null
@@ -0,0 +1,176 @@
+git-diff-cache(1)
+=================
+v0.1, May 2005
+
+NAME
+----
+git-diff-cache - Compares content and mode of blobs between the cache and repository
+
+
+SYNOPSIS
+--------
+'git-diff-cache' [-p] [-r] [-z] [-m] [--cached] [-R] [-B] [-M] [-C] [--find-copies-harder] [-O<orderfile>] [-S<string>] [--pickaxe-all] <tree-ish> [<path>...]
+
+DESCRIPTION
+-----------
+Compares the content and mode of the blobs found via a tree
+object with the content of the current cache and, optionally
+ignoring the stat state of the file on disk.  When paths are
+specified, compares only those named paths.  Otherwise all
+entries in the cache are compared.
+
+OPTIONS
+-------
+<tree-ish>::
+       The id of a tree object to diff against.
+
+-p::
+       Generate patch (see section on generating patches)
+
+-r::
+       This flag does not mean anything.  It is there only to match
+       "git-diff-tree".  Unlike "git-diff-tree", "git-diff-cache"
+       always looks at all the subdirectories.
+
+-z::
+       \0 line termination on output
+
+-B::
+       Break complete rewrite changes into pairs of delete and create.
+
+-M::
+       Detect renames.
+
+-C::
+       Detect copies as well as renames.
+
+--find-copies-harder::
+       By default, -C option finds copies only if the original
+       file of the copy was modified in the same changeset for
+       performance reasons.  This flag makes the command
+       inspect unmodified files as candidates for the source of
+       copy.  This is a very expensive operation for large
+       projects, so use it with caution.
+
+-S<string>::
+       Look for differences that contains the change in <string>.
+
+--pickaxe-all::
+       When -S finds a change, show all the changes in that
+       changeset, not just the files that contains the change
+       in <string>.
+
+-O<orderfile>::
+       Output the patch in the order specified in the
+       <orderfile>, which has one shell glob pattern per line.
+
+-R::
+       Swap two inputs; that is, show differences from cache or
+       on-disk file to tree contents.
+
+--cached::
+       do not consider the on-disk file at all
+
+-m::
+       By default, files recorded in the index but not checked
+       out are reported as deleted.  This flag makes
+       "git-diff-cache" say that all non-checked-out files are up
+       to date.
+
+Output format
+-------------
+include::diff-format.txt[]
+
+Operating Modes
+---------------
+You can choose whether you want to trust the index file entirely
+(using the '--cached' flag) or ask the diff logic to show any files
+that don't match the stat state as being "tentatively changed".  Both
+of these operations are very useful indeed.
+
+Cached Mode
+-----------
+If '--cached' is specified, it allows you to ask:
+
+       show me the differences between HEAD and the current index
+       contents (the ones I'd write with a "git-write-tree")
+
+For example, let's say that you have worked on your index file, and are
+ready to commit. You want to see eactly *what* you are going to commit is
+without having to write a new tree object and compare it that way, and to
+do that, you just do
+
+       git-diff-cache --cached $(cat .git/HEAD)
+
+Example: let's say I had renamed `commit.c` to `git-commit.c`, and I had
+done an "git-update-cache" to make that effective in the index file.
+"git-diff-files" wouldn't show anything at all, since the index file
+matches my working directory. But doing a "git-diff-cache" does:
+
+  torvalds@ppc970:~/git> git-diff-cache --cached $(cat .git/HEAD)
+  -100644 blob    4161aecc6700a2eb579e842af0b7f22b98443f74        commit.c
+  +100644 blob    4161aecc6700a2eb579e842af0b7f22b98443f74        git-commit.c
+
+You can trivially see that the above is a rename.
+
+In fact, "git-diff-cache --cached" *should* always be entirely equivalent to
+actually doing a "git-write-tree" and comparing that. Except this one is much
+nicer for the case where you just want to check where you are.
+
+So doing a "git-diff-cache --cached" is basically very useful when you are 
+asking yourself "what have I already marked for being committed, and 
+what's the difference to a previous tree".
+
+Non-cached Mode
+---------------
+The "non-cached" mode takes a different approach, and is potentially
+the more useful of the two in that what it does can't be emulated with
+a "git-write-tree" + "git-diff-tree". Thus that's the default mode.
+The non-cached version asks the question:
+
+   show me the differences between HEAD and the currently checked out
+   tree - index contents _and_ files that aren't up-to-date
+
+which is obviously a very useful question too, since that tells you what
+you *could* commit. Again, the output matches the "git-diff-tree -r"
+output to a tee, but with a twist.
+
+The twist is that if some file doesn't match the cache, we don't have
+a backing store thing for it, and we use the magic "all-zero" sha1 to
+show that. So let's say that you have edited `kernel/sched.c`, but
+have not actually done a "git-update-cache" on it yet - there is no
+"object" associated with the new state, and you get:
+
+  torvalds@ppc970:~/v2.6/linux> git-diff-cache $(cat .git/HEAD )
+  *100644->100664 blob    7476bb......->000000......      kernel/sched.c
+
+ie it shows that the tree has changed, and that `kernel/sched.c` has is
+not up-to-date and may contain new stuff. The all-zero sha1 means that to
+get the real diff, you need to look at the object in the working directory
+directly rather than do an object-to-object diff.
+
+NOTE! As with other commands of this type, "git-diff-cache" does not
+actually look at the contents of the file at all. So maybe
+`kernel/sched.c` hasn't actually changed, and it's just that you
+touched it. In either case, it's a note that you need to
+"git-upate-cache" it to make the cache be in sync.
+
+NOTE 2! You can have a mixture of files show up as "has been updated"
+and "is still dirty in the working directory" together. You can always
+tell which file is in which state, since the "has been updated" ones
+show a valid sha1, and the "not in sync with the index" ones will
+always have the special all-zero sha1.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-diff-files.txt b/Documentation/git-diff-files.txt
new file mode 100644 (file)
index 0000000..32e9a1e
--- /dev/null
@@ -0,0 +1,84 @@
+git-diff-files(1)
+=================
+v0.1, May 2005
+
+NAME
+----
+git-diff-files - Compares files in the working tree and the cache
+
+
+SYNOPSIS
+--------
+'git-diff-files' [-p] [-q] [-r] [-z] [-R] [-B] [-M] [-C] [--find-copies-harder] [-O<orderfile>] [-S<string>] [--pickaxe-all] [<path>...]
+
+DESCRIPTION
+-----------
+Compares the files in the working tree and the cache.  When paths
+are specified, compares only those named paths.  Otherwise all
+entries in the cache are compared.  The output format is the
+same as "git-diff-cache" and "git-diff-tree".
+
+OPTIONS
+-------
+-p::
+       generate patch (see section on generating patches).
+
+-q::
+       Remain silent even on nonexisting files
+
+-R::
+       Swap two inputs; that is, show differences from on-disk files
+       to cache contents.
+
+-B::
+       Break complete rewrite changes into pairs of delete and create.
+
+-M::
+       Detect renames.
+
+-C::
+       Detect copies as well as renames.
+
+--find-copies-harder::
+       By default, -C option finds copies only if the original
+       file of the copy was modified in the same changeset for
+       performance reasons.  This flag makes the command
+       inspect unmodified files as candidates for the source of
+       copy.  This is a very expensive operation for large
+       projects, so use it with caution.
+
+-S<string>::
+       Look for differences that contains the change in <string>.
+
+--pickaxe-all::
+       When -S finds a change, show all the changes in that
+       changeset, not just the files that contains the change
+       in <string>.
+
+-O<orderfile>::
+       Output the patch in the order specified in the
+       <orderfile>, which has one shell glob pattern per line.
+
+-r::
+       This flag does not mean anything.  It is there only to match
+       git-diff-tree.  Unlike git-diff-tree, git-diff-files always looks
+       at all the subdirectories.
+
+
+Output format
+-------------
+include::diff-format.txt[]
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-diff-helper.txt b/Documentation/git-diff-helper.txt
new file mode 100644 (file)
index 0000000..d826deb
--- /dev/null
@@ -0,0 +1,53 @@
+git-diff-helper(1)
+==================
+v0.1, May 2005
+
+NAME
+----
+git-diff-helper - Generates patch format output for git-diff-*
+
+
+SYNOPSIS
+--------
+'git-diff-helper' [-z] [-S<string>] [-O<orderfile>]
+
+DESCRIPTION
+-----------
+Reads output from "git-diff-cache", "git-diff-tree" and "git-diff-files" and
+generates patch format output.
+
+OPTIONS
+-------
+-z::
+       \0 line termination on input
+
+-S<string>::
+       Look for differences that contains the change in <string>.
+
+--pickaxe-all::
+       When -S finds a change, show all the changes in that
+       changeset, not just the files that contains the change
+       in <string>.
+
+-O<orderfile>::
+       Output the patch in the order specified in the
+       <orderfile>, which has one shell glob pattern per line.
+
+See Also
+--------
+The section on generating patches in link:git-diff-cache.html[git-diff-cache]
+
+
+Author
+------
+Written by Junio C Hamano <junkio@cox.net>
+
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-diff-stages.txt b/Documentation/git-diff-stages.txt
new file mode 100644 (file)
index 0000000..e7e59c8
--- /dev/null
@@ -0,0 +1,83 @@
+git-diff-stages(1)
+==================
+v0.1, June 2005
+
+NAME
+----
+git-diff-stages - Compares content and mode of blobs between stages in an unmerged index file.
+
+
+SYNOPSIS
+--------
+'git-diff-stages' [-p] [-r] [-z] [-R] [-B] [-M] [-C] [--find-copies-harder] [-O<orderfile>] [-S<string>] [--pickaxe-all] <stage1> <stage2> [<path>...]
+
+DESCRIPTION
+-----------
+Compares the content and mode of the blobs in two stages in an
+unmerged index file.
+
+OPTIONS
+-------
+<stage1>,<stage2>::
+       The stage number to be compared.
+
+-p::
+       Generate patch (see section on generating patches)
+
+-r::
+       This flag does not mean anything.  It is there only to match
+       "git-diff-tree".  Unlike "git-diff-tree", "git-diff-stages"
+       always looks at all the subdirectories.
+
+-z::
+       \0 line termination on output
+
+-B::
+       Break complete rewrite changes into pairs of delete and create.
+
+-M::
+       Detect renames.
+
+-C::
+       Detect copies as well as renames.
+
+--find-copies-harder::
+       By default, -C option finds copies only if the original
+       file of the copy was modified in the same changeset for
+       performance reasons.  This flag makes the command
+       inspect unmodified files as candidates for the source of
+       copy.  This is a very expensive operation for large
+       projects, so use it with caution.
+
+-S<string>::
+       Look for differences that contains the change in <string>.
+
+--pickaxe-all::
+       When -S finds a change, show all the changes in that
+       changeset, not just the files that contains the change
+       in <string>.
+
+-O<orderfile>::
+       Output the patch in the order specified in the
+       <orderfile>, which has one shell glob pattern per line.
+
+-R::
+       Swap two inputs; that is, show differences from <stage2> to
+       <stage1>.
+
+Output format
+-------------
+include::diff-format.txt[]
+
+
+Author
+------
+Written by Junio C Hamano <junkio@cox.net>
+
+Documentation
+--------------
+Documentation by Junio C Hamano.
+
+GIT
+---
+Part of the link:git.html[git] suite
diff --git a/Documentation/git-diff-tree.txt b/Documentation/git-diff-tree.txt
new file mode 100644 (file)
index 0000000..ab0ba4f
--- /dev/null
@@ -0,0 +1,170 @@
+git-diff-tree(1)
+================
+v0.1, May 2005
+
+NAME
+----
+git-diff-tree - Compares the content and mode of blobs found via two tree objects
+
+
+SYNOPSIS
+--------
+'git-diff-tree' [-p] [-r] [-z] [--stdin] [-m] [-s] [-v] [-t] [-R] [-B] [-M] [-C] [--find-copies-harder] [-O<orderfile>] [-S<string>] [--pickaxe-all] <tree-ish> <tree-ish> [<path>...]
+
+DESCRIPTION
+-----------
+Compares the content and mode of the blobs found via two tree objects.
+
+Note that "git-diff-tree" can use the tree encapsulated in a commit object.
+
+OPTIONS
+-------
+<tree-ish>::
+       The id of a tree object.
+
+<path>...::
+       If provided, the results are limited to a subset of files
+       matching one of these prefix strings.
+       ie file matches `/^<pattern1>|<pattern2>|.../`
+       Note that this parameter does not provide any wildcard or regexp
+       features.
+
+-p::
+       generate patch (see section on generating patches).  For
+       git-diff-tree, this flag implies '-r' as well.
+
+-B::
+       Break complete rewrite changes into pairs of delete and create.
+
+-M::
+       Detect renames.
+
+-C::
+       Detect copies as well as renames.
+
+--find-copies-harder::
+       By default, -C option finds copies only if the original
+       file of the copy was modified in the same changeset for
+       performance reasons.  This flag makes the command
+       inspect unmodified files as candidates for the source of
+       copy.  This is a very expensive operation for large
+       projects, so use it with caution.
+
+-R::
+       Swap two input trees.
+
+-S<string>::
+       Look for differences that contains the change in <string>.
+
+--pickaxe-all::
+       When -S finds a change, show all the changes in that
+       changeset, not just the files that contains the change
+       in <string>.
+
+-O<orderfile>::
+       Output the patch in the order specified in the
+       <orderfile>, which has one shell glob pattern per line.
+
+-r::
+       recurse
+
+-t::
+       show tree entry itself as well as subtrees.  Implies -r.
+
+-z::
+       \0 line termination on output
+
+--root::
+       When '--root' is specified the initial commit will be showed as a big
+       creation event. This is equivalent to a diff against the NULL tree.
+
+--stdin::
+       When '--stdin' is specified, the command does not take
+       <tree-ish> arguments from the command line.  Instead, it
+       reads either one <commit> or a pair of <tree-ish>
+       separated with a single space from its standard input.
++
+When a single commit is given on one line of such input, it compares
+the commit with its parents.  The following flags further affects its
+behaviour.  This does not apply to the case where two <tree-ish>
+separated with a single space are given.
+
+-m::
+       By default, "git-diff-tree --stdin" does not show
+       differences for merge commits.  With this flag, it shows
+       differences to that commit from all of its parents.
+
+-s::
+       By default, "git-diff-tree --stdin" shows differences,
+       either in machine-readable form (without '-p') or in patch
+       form (with '-p').  This output can be supressed.  It is
+       only useful with '-v' flag.
+
+-v::
+       This flag causes "git-diff-tree --stdin" to also show
+       the commit message before the differences.
+
+--pretty[=(raw|medium|short)]::
+       This is used to control "pretty printing" format of the
+       commit message.  Without "=<style>", it defaults to
+       medium.
+
+
+Limiting Output
+---------------
+If you're only interested in differences in a subset of files, for
+example some architecture-specific files, you might do:
+
+       git-diff-tree -r <tree-ish> <tree-ish> arch/ia64 include/asm-ia64
+
+and it will only show you what changed in those two directories.
+
+Or if you are searching for what changed in just `kernel/sched.c`, just do
+
+       git-diff-tree -r <tree-ish> <tree-ish> kernel/sched.c
+
+and it will ignore all differences to other files.
+
+The pattern is always the prefix, and is matched exactly.  There are no
+wildcards.  Even stricter, it has to match complete path comonent.
+I.e. "foo" does not pick up `foobar.h`.  "foo" does match `foo/bar.h`
+so it can be used to name subdirectories.
+
+An example of normal usage is:
+
+  torvalds@ppc970:~/git> git-diff-tree 5319e4......
+  *100664->100664 blob    ac348b.......->a01513.......      git-fsck-cache.c
+
+which tells you that the last commit changed just one file (it's from
+this one:
+
+  commit 3c6f7ca19ad4043e9e72fa94106f352897e651a8
+  tree 5319e4d609cdd282069cc4dce33c1db559539b03
+  parent b4e628ea30d5ab3606119d2ea5caeab141d38df7
+  author Linus Torvalds <torvalds@ppc970.osdl.org> Sat Apr 9 12:02:30 2005
+  committer Linus Torvalds <torvalds@ppc970.osdl.org> Sat Apr 9 12:02:30 2005
+
+  Make "git-fsck-cache" print out all the root commits it finds.
+
+  Once I do the reference tracking, I'll also make it print out all the
+  HEAD commits it finds, which is even more interesting.
+
+in case you care).
+
+Output format
+-------------
+include::diff-format.txt[]
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-export.txt b/Documentation/git-export.txt
new file mode 100644 (file)
index 0000000..d2d0dc4
--- /dev/null
@@ -0,0 +1,31 @@
+git-export(1)
+=============
+v0.1, May 2005
+
+NAME
+----
+git-export - Exports each commit and a diff against each of its parents
+
+
+SYNOPSIS
+--------
+'git-export' top [base]
+
+DESCRIPTION
+-----------
+Exports each commit and diff against each of its parents, between
+top and base.  If base is not specified it exports everything.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-fsck-cache.txt b/Documentation/git-fsck-cache.txt
new file mode 100644 (file)
index 0000000..f1c18c1
--- /dev/null
@@ -0,0 +1,141 @@
+git-fsck-cache(1)
+=================
+v0.1, May 2005
+
+NAME
+----
+git-fsck-cache - Verifies the connectivity and validity of the objects in the database
+
+
+SYNOPSIS
+--------
+'git-fsck-cache' [--tags] [--root] [--unreachable] [--cache] [--standalone | --full] [<object>*]
+
+DESCRIPTION
+-----------
+Verifies the connectivity and validity of the objects in the database.
+
+OPTIONS
+-------
+<object>::
+       An object to treat as the head of an unreachability trace.
+
+       If no objects are given, git-fsck-cache defaults to using the
+       index file and all SHA1 references in .git/refs/* as heads.
+
+--unreachable::
+       Print out objects that exist but that aren't readable from any
+       of the reference nodes.
+
+--root::
+       Report root nodes.
+
+--tags::
+       Report tags.
+
+--cache::
+       Consider any object recorded in the cache also as a head node for
+       an unreachability trace.
+
+--standalone::
+       Limit checks to the contents of GIT_OBJECT_DIRECTORY
+       (.git/objects), making sure that it is consistent and
+       complete without referring to objects found in alternate
+       object pools listed in GIT_ALTERNATE_OBJECT_DIRECTORIES,
+       nor packed GIT archives found in .git/objects/pack;
+       cannot be used with --full.
+
+--full::
+       Check not just objects in GIT_OBJECT_DIRECTORY
+       (.git/objects), but also the ones found in alternate
+       object pools listed in GIT_ALTERNATE_OBJECT_DIRECTORIES,
+       and in packed GIT archives found in .git/objects/pack
+       and corresponding pack subdirectories in alternate
+       object pools; cannot be used with --standalone.
+
+It tests SHA1 and general object sanity, and it does full tracking of
+the resulting reachability and everything else. It prints out any
+corruption it finds (missing or bad objects), and if you use the
+'--unreachable' flag it will also print out objects that exist but
+that aren't readable from any of the specified head nodes.
+
+So for example
+
+       git-fsck-cache --unreachable $(cat .git/HEAD)
+
+or, for Cogito users:
+
+       git-fsck-cache --unreachable $(cat .git/refs/heads/*)
+
+will do quite a _lot_ of verification on the tree. There are a few
+extra validity tests to be added (make sure that tree objects are
+sorted properly etc), but on the whole if "git-fsck-cache" is happy, you
+do have a valid tree.
+
+Any corrupt objects you will have to find in backups or other archives
+(ie you can just remove them and do an "rsync" with some other site in
+the hopes that somebody else has the object you have corrupted).
+
+Of course, "valid tree" doesn't mean that it wasn't generated by some
+evil person, and the end result might be crap. Git is a revision
+tracking system, not a quality assurance system ;)
+
+Extracted Diagnostics
+---------------------
+
+expect dangling commits - potential heads - due to lack of head information::
+       You haven't specified any nodes as heads so it won't be
+       possible to differentiate between un-parented commits and
+       root nodes.
+
+missing sha1 directory '<dir>'::
+       The directory holding the sha1 objects is missing.
+
+unreachable <type> <object>::
+       The <type> object <object>, isn't actually referred to directly
+       or indirectly in any of the trees or commits seen. This can
+       mean that there's another root node that you're not specifying
+       or that the tree is corrupt. If you haven't missed a root node
+       then you might as well delete unreachable nodes since they
+       can't be used.
+
+missing <type> <object>::
+       The <type> object <object>, is referred to but isn't present in
+       the database.
+
+dangling <type> <object>::
+       The <type> object <object>, is present in the database but never
+       'directly' used. A dangling commit could be a root node.
+
+warning: git-fsck-cache: tree <tree> has full pathnames in it::
+       And it shouldn't...
+
+sha1 mismatch <object>::
+       The database has an object who's sha1 doesn't match the
+       database value.
+       This indicates a serious data integrity problem.
+       (note: this error occured during early git development when
+       the database format changed.)
+
+Environment Variables
+---------------------
+
+GIT_OBJECT_DIRECTORY::
+       used to specify the object database root (usually .git/objects)
+
+GIT_INDEX_FILE::
+       used to specify the cache
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-http-pull.txt b/Documentation/git-http-pull.txt
new file mode 100644 (file)
index 0000000..431ef7c
--- /dev/null
@@ -0,0 +1,41 @@
+git-http-pull(1)
+================
+v0.1, May 2005
+
+NAME
+----
+git-http-pull - Downloads a remote GIT repository via HTTP
+
+
+SYNOPSIS
+--------
+'git-http-pull' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
+
+DESCRIPTION
+-----------
+Downloads a remote GIT repository via HTTP.
+
+-c::
+       Get the commit objects.
+-t::
+       Get trees associated with the commit objects.
+-a::
+       Get all the objects.
+-v::
+       Report what is downloaded.
+-w::
+        Writes the commit-id into the filename under $GIT_DIR/refs/ on
+        the local end after the transfer is complete.
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-init-db.txt b/Documentation/git-init-db.txt
new file mode 100644 (file)
index 0000000..99f96f7
--- /dev/null
@@ -0,0 +1,40 @@
+git-init-db(1)
+==============
+v0.1, May 2005
+
+NAME
+----
+git-init-db - Creates an empty git object database
+
+
+SYNOPSIS
+--------
+'git-init-db'
+
+DESCRIPTION
+-----------
+This simply creates an empty git object database - basically a `.git`
+directory and `.git/object/??/` directories.
+
+If the 'GIT_DIR' environment variable is set then it specifies a path
+to use instead of `./.git` for the base of the repository.
+
+If the object storage directory is specified via the 'GIT_OBJECT_DIRECTORY'
+environment variable then the sha1 directories are created underneath -
+otherwise the default `.git/objects` directory is used.
+
+"git-init-db" won't hurt an existing repository.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-local-pull.txt b/Documentation/git-local-pull.txt
new file mode 100644 (file)
index 0000000..cb95334
--- /dev/null
@@ -0,0 +1,43 @@
+git-local-pull(1)
+=================
+v0.1, May 2005
+
+NAME
+----
+git-local-pull - Duplicates another GIT repository on a local system
+
+
+SYNOPSIS
+--------
+'git-local-pull' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] [-l] [-s] [-n] commit-id path
+
+DESCRIPTION
+-----------
+Duplicates another GIT repository on a local system.
+
+OPTIONS
+-------
+-c::
+       Get the commit objects.
+-t::
+       Get trees associated with the commit objects.
+-a::
+       Get all the objects.
+-v::
+       Report what is downloaded.
+-w::
+        Writes the commit-id into the filename under $GIT_DIR/refs/ on
+        the local end after the transfer is complete.
+
+Author
+------
+Written by Junio C Hamano <junkio@cox.net>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-ls-files.txt b/Documentation/git-ls-files.txt
new file mode 100644 (file)
index 0000000..be83ab1
--- /dev/null
@@ -0,0 +1,108 @@
+git-ls-files(1)
+===============
+v0.1, May 2005
+
+NAME
+----
+git-ls-files - Information about files in the cache/working directory
+
+
+SYNOPSIS
+--------
+'git-ls-files' [-z] [-t]
+               (--[cached|deleted|others|ignored|stage|unmerged|killed])\*
+               (-[c|d|o|i|s|u|k])\*
+               [-x <pattern>|--exclude=<pattern>]
+               [-X <file>|--exclude-from=<file>]
+
+DESCRIPTION
+-----------
+This merges the file listing in the directory cache index with the
+actual working directory list, and shows different combinations of the
+two.
+
+One or more of the options below may be used to determine the files
+shown:
+
+OPTIONS
+-------
+-c|--cached::
+       Show cached files in the output (default)
+
+-d|--deleted::
+       Show deleted files in the output
+
+-o|--others::
+       Show other files in the output
+
+-i|--ignored::
+       Show ignored files in the output
+       Note the this also reverses any exclude list present.
+
+-s|--stage::
+       Show stage files in the output
+
+-u|--unmerged::
+       Show unmerged files in the output (forces --stage)
+
+-k|--killed::
+       Show files on the filesystem that need to be removed due
+       to file/directory conflicts for checkout-cache to
+       succeed.
+
+-z::
+       \0 line termination on output
+
+-x|--exclude=<pattern>::
+       Skips files matching pattern.
+       Note that pattern is a shell wildcard pattern.
+
+-X|--exclude-from=<file>::
+       exclude patterns are read from <file>; 1 per line.
+       Allows the use of the famous dontdiff file as follows to find
+       out about uncommitted files just as dontdiff is used with
+       the diff command:
+            git-ls-files --others --exclude-from=dontdiff
+
+-t::
+       Identify the file status with the following tags (followed by
+       a space) at the start of each line:
+       H       cached
+       M       unmerged
+       R       removed/deleted
+       K       to be killed
+       ?       other
+
+Output
+------
+show files just outputs the filename unless '--stage' is specified in
+which case it outputs:
+
+        [<tag> ]<mode> <object> <stage> <file>
+
+"git-ls-files --unmerged" and "git-ls-files --stage" can be used to examine
+detailed information on unmerged paths.
+
+For an unmerged path, instead of recording a single mode/SHA1 pair,
+the dircache records up to three such pairs; one from tree O in stage
+1, A in stage 2, and B in stage 3.  This information can be used by
+the user (or Cogito) to see what should eventually be recorded at the
+path. (see read-cache for more information on state)
+
+See Also
+--------
+link:read-cache.html[read-cache]
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-ls-tree.txt b/Documentation/git-ls-tree.txt
new file mode 100644 (file)
index 0000000..958b56d
--- /dev/null
@@ -0,0 +1,55 @@
+git-ls-tree(1)
+==============
+v0.1, May 2005
+
+NAME
+----
+git-ls-tree - Lists the contents of a tree object.
+
+
+SYNOPSIS
+--------
+'git-ls-tree' [-d] [-r] [-z] <tree-ish> [paths...]
+
+DESCRIPTION
+-----------
+Lists the contents of a tree object, like what "/bin/ls -a" does
+in the current working directory.
+
+OPTIONS
+-------
+<tree-ish>::
+       Id of a tree.
+
+-d::
+       show only the named tree entry itself, not its children
+
+-r::
+       recurse into sub-trees
+
+-z::
+       \0 line termination on output
+
+paths::
+       When paths are given, shows them.  Otherwise implicitly
+       uses the root level of the tree as the sole path argument.
+
+
+Output Format
+-------------
+        <mode> SP <type> SP <object> TAB <file>
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+Completely rewritten from scratch by Junio C Hamano <junkio@cox.net>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-merge-base.txt b/Documentation/git-merge-base.txt
new file mode 100644 (file)
index 0000000..1e27bf2
--- /dev/null
@@ -0,0 +1,34 @@
+git-merge-base(1)
+=================
+v0.1, May 2005
+
+NAME
+----
+git-merge-base - Finds as good a common ancestor as possible for a merge
+
+
+SYNOPSIS
+--------
+'git-merge-base' <commit> <commit>
+
+DESCRIPTION
+-----------
+"git-merge-base" finds as good a common ancestor as possible. Given a
+selection of equally good common ancestors it should not be relied on
+to decide in any particular way.
+
+The "git-merge-base" algorithm is still in flux - use the source...
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-merge-cache.txt b/Documentation/git-merge-cache.txt
new file mode 100644 (file)
index 0000000..3fb4d49
--- /dev/null
@@ -0,0 +1,84 @@
+git-merge-cache(1)
+==================
+v0.1, May 2005
+
+NAME
+----
+git-merge-cache - Runs a merge for files needing merging
+
+
+SYNOPSIS
+--------
+'git-merge-cache' [-o] <merge-program> (-a | -- | <file>\*) 
+
+DESCRIPTION
+-----------
+This looks up the <file>(s) in the cache and, if there are any merge
+entries, passes the SHA1 hash for those files as arguments 1, 2, 3 (empty
+argument if no file), and <file> as argument 4.  File modes for the three
+files are passed as arguments 5, 6 and 7.
+
+OPTIONS
+-------
+--::
+       Interpret all future arguments as filenames.
+
+-a::
+       Run merge against all files in the cache that need merging.
+
+-o::
+       Instead of stopping at the first failed merge, do all of them
+       in one shot - continue with merging even when previous merges
+       returned errors, and only return the error code after all the
+       merges are over.
+
+If "git-merge-cache" is called with multiple <file>s (or -a) then it
+processes them in turn only stopping if merge returns a non-zero exit
+code.
+
+Typically this is run with the a script calling the merge command from
+the RCS package.
+
+A sample script called "git-merge-one-file-script" is included in the
+ditribution.
+
+ALERT ALERT ALERT! The git "merge object order" is different from the
+RCS "merge" program merge object order. In the above ordering, the
+original is first. But the argument order to the 3-way merge program
+"merge" is to have the original in the middle. Don't ask me why.
+
+Examples:
+
+  torvalds@ppc970:~/merge-test> git-merge-cache cat MM
+  This is MM from the original tree.                   # original
+  This is modified MM in the branch A.                 # merge1
+  This is modified MM in the branch B.                 # merge2
+  This is modified MM in the branch B.                 # current contents
+
+or 
+
+  torvalds@ppc970:~/merge-test> git-merge-cache cat AA MM
+  cat: : No such file or directory
+  This is added AA in the branch A.
+  This is added AA in the branch B.
+  This is added AA in the branch B.
+  fatal: merge program failed
+
+where the latter example shows how "git-merge-cache" will stop trying to
+merge once anything has returned an error (ie "cat" returned an error
+for the AA file, because it didn't exist in the original, and thus
+"git-merge-cache" didn't even try to merge the MM thing).
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+One-shot merge by Petr Baudis <pasky@ucw.cz>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-merge-one-file-script.txt b/Documentation/git-merge-one-file-script.txt
new file mode 100644 (file)
index 0000000..387601d
--- /dev/null
@@ -0,0 +1,30 @@
+git-merge-one-file-script(1)
+============================
+v0.1, May 2005
+
+NAME
+----
+git-merge-one-file-script - The standard helper program to use with "git-merge-cache"
+
+
+SYNOPSIS
+--------
+'git-merge-one-file-script'
+
+DESCRIPTION
+-----------
+This is the standard helper program to use with "git-merge-cache"
+to resolve a merge after the trivial merge done with "git-read-tree -m".
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-mktag.txt b/Documentation/git-mktag.txt
new file mode 100644 (file)
index 0000000..708f4ef
--- /dev/null
@@ -0,0 +1,48 @@
+git-mktag(1)
+============
+v0.1, May 2005
+
+NAME
+----
+git-mktag - Creates a tag object
+
+
+SYNOPSIS
+--------
+'git-mktag' < signature_file
+
+DESCRIPTION
+-----------
+Reads a tag contents on standard input and creates a tag object
+that can also be used to sign other objects.
+
+The output is the new tag's <object> identifier.
+
+Tag Format
+----------
+A tag signature file has a very simple fixed format: three lines of
+
+  object <sha1>
+  type <typename>
+  tag <tagname>
+
+followed by some 'optional' free-form signature that git itself
+doesn't care about, but that can be verified with gpg or similar.
+
+The size of the full object is artificially limited to 8kB.  (Just
+because I'm a lazy bastard, and if you can't fit a signature in that
+size, you're doing something wrong)
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-prune-script.txt b/Documentation/git-prune-script.txt
new file mode 100644 (file)
index 0000000..537b790
--- /dev/null
@@ -0,0 +1,32 @@
+git-prune-script(1)
+===================
+v0.1, May 2005
+
+NAME
+----
+git-prune-script - Prunes all unreachable objects from the object database
+
+
+SYNOPSIS
+--------
+'git-prune-script'
+
+DESCRIPTION
+-----------
+This runs "git-fsck-cache --unreachable" program using the heads specified
+on the command line (or `.git/refs/heads/\*` and `.git/refs/tags/\*` if none is
+specified), and prunes all unreachable objects from the object database.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-pull-script.txt b/Documentation/git-pull-script.txt
new file mode 100644 (file)
index 0000000..44fd09a
--- /dev/null
@@ -0,0 +1,31 @@
+git-pull-script(1)
+==================
+v0.1, May 2005
+
+NAME
+----
+git-pull-script - Script used by Linus to pull and merge a remote repository
+
+
+SYNOPSIS
+--------
+'git-pull-script'
+
+DESCRIPTION
+-----------
+This script is used by Linus to pull from a remote repository and perform
+a merge.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-read-tree.txt b/Documentation/git-read-tree.txt
new file mode 100644 (file)
index 0000000..7665946
--- /dev/null
@@ -0,0 +1,268 @@
+git-read-tree(1)
+================
+v0.1, May 2005
+
+NAME
+----
+git-read-tree - Reads tree information into the directory cache
+
+
+SYNOPSIS
+--------
+'git-read-tree' (<tree-ish> | [-m [-u]] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
+
+
+DESCRIPTION
+-----------
+Reads the tree information given by <tree-ish> into the directory cache,
+but does not actually *update* any of the files it "caches". (see:
+git-checkout-cache)
+
+Optionally, it can merge a tree into the cache, perform a
+fast-forward (i.e. 2-way) merge, or a 3-way merge, with the -m
+flag.  When used with -m, the -u flag causes it to also update
+the files in the work tree with the result of the merge.
+
+Trivial merges are done by "git-read-tree" itself.  Only conflicting paths
+will be in unmerged state when "git-read-tree" returns.
+
+OPTIONS
+-------
+-m::
+       Perform a merge, not just a read.
+
+-u::
+       After a successful merge, update the files in the work
+       tree with the result of the merge.
+
+<tree-ish#>::
+       The id of the tree object(s) to be read/merged.
+
+
+Merging
+-------
+If '-m' is specified, "git-read-tree" can performs 3 kinds of
+merge, a single tree merge if only 1 tree is given, a
+fast-forward merge with 2 trees, or a 3-way merge if 3 trees are
+provided.
+
+
+Single Tree Merge
+~~~~~~~~~~~~~~~~~
+If only 1 tree is specified, git-read-tree operates as if the user did not
+specify '-m', except that if the original cache has an entry for a
+given pathname; and the contents of the path matches with the tree
+being read, the stat info from the cache is used. (In other words, the
+cache's stat()s take precedence over the merged tree's)
+
+That means that if you do a "git-read-tree -m <newtree>" followed by a
+"git-checkout-cache -f -u -a", the "git-checkout-cache" only checks out
+the stuff that really changed.
+
+This is used to avoid unnecessary false hits when "git-diff-files" is
+run after git-read-tree.
+
+
+Two Tree Merge
+~~~~~~~~~~~~~~
+
+Typically, this is invoked as "git-read-tree -m $H $M", where $H
+is the head commit of the current repository, and $M is the head
+of a foreign tree, which is simply ahead of $H (i.e. we are in a
+fast forward situation).
+
+When two trees are specified, the user is telling git-read-tree
+the following:
+
+    (1) The current index and work tree is derived from $H, but
+        the user may have local changes in them since $H;
+
+    (2) The user wants to fast-forward to $M.
+
+In this case, the "git-read-tree -m $H $M" command makes sure
+that no local change is lost as the result of this "merge".
+Here are the "carry forward" rules:
+
+        I (index)           H        M        Result
+       -------------------------------------------------------
+      0 nothing             nothing  nothing  (does not happen)
+      1 nothing             nothing  exists   use M
+      2 nothing             exists   nothing  remove path from cache
+      3 nothing             exists   exists   use M
+
+        clean I==H  I==M
+       ------------------
+      4 yes   N/A   N/A     nothing  nothing  keep index
+      5 no    N/A   N/A     nothing  nothing  keep index
+
+      6 yes   N/A   yes     nothing  exists   keep index
+      7 no    N/A   yes     nothing  exists   keep index
+      8 yes   N/A   no      nothing  exists   fail
+      9 no    N/A   no      nothing  exists   fail
+
+     10 yes   yes   N/A     exists   nothing  remove path from cache
+     11 no    yes   N/A     exists   nothing  fail
+     12 yes   no    N/A     exists   nothing  fail
+     13 no    no    N/A     exists   nothing  fail
+
+        clean (H=M)
+       ------
+     14 yes                 exists   exists   keep index
+     15 no                  exists   exists   keep index
+
+        clean I==H  I==M (H!=M)
+       ------------------
+     16 yes   no    no      exists   exists   fail
+     17 no    no    no      exists   exists   fail
+     18 yes   no    yes     exists   exists   keep index
+     19 no    no    yes     exists   exists   keep index
+     20 yes   yes   no      exists   exists   use M
+     21 no    yes   no      exists   exists   fail
+
+In all "keep index" cases, the cache entry stays as in the
+original index file.  If the entry were not up to date,
+git-read-tree keeps the copy in the work tree intact when
+operating under the -u flag.
+
+When this form of git-read-tree returns successfully, you can
+see what "local changes" you made are carried forward by running
+"git-diff-cache --cached $M".  Note that this does not
+necessarily match "git-diff-cache --cached $H" would have
+produced before such a two tree merge.  This is because of cases
+18 and 19 --- if you already had the changes in $M (e.g. maybe
+you picked it up via e-mail in a patch form), "git-diff-cache
+--cached $H" would have told you about the change before this
+merge, but it would not show in "git-diff-cache --cached $M"
+output after two-tree merge.
+
+
+3-Way Merge
+~~~~~~~~~~~
+Each "index" entry has two bits worth of "stage" state. stage 0 is the
+normal one, and is the only one you'd see in any kind of normal use.
+
+However, when you do "git-read-tree" with three trees, the "stage"
+starts out at 1.
+
+This means that you can do
+
+       git-read-tree -m <tree1> <tree2> <tree3>
+
+and you will end up with an index with all of the <tree1> entries in
+"stage1", all of the <tree2> entries in "stage2" and all of the
+<tree3> entries in "stage3".
+
+Furthermore, "git-read-tree" has special-case logic that says: if you see
+a file that matches in all respects in the following states, it
+"collapses" back to "stage0":
+
+   - stage 2 and 3 are the same; take one or the other (it makes no
+     difference - the same work has been done on stage 2 and 3)
+
+   - stage 1 and stage 2 are the same and stage 3 is different; take
+     stage 3 (some work has been done on stage 3)
+
+   - stage 1 and stage 3 are the same and stage 2 is different take
+     stage 2 (some work has been done on stage 2)
+
+The "git-write-tree" command refuses to write a nonsensical tree, and it
+will complain about unmerged entries if it sees a single entry that is not
+stage 0.
+
+Ok, this all sounds like a collection of totally nonsensical rules,
+but it's actually exactly what you want in order to do a fast
+merge. The different stages represent the "result tree" (stage 0, aka
+"merged"), the original tree (stage 1, aka "orig"), and the two trees
+you are trying to merge (stage 2 and 3 respectively).
+
+The order of stages 1, 2 and 3 (hence the order of three
+<tree-ish> command line arguments) are significant when you
+start a 3-way merge with an index file that is already
+populated.  Here is an outline of how the algorithm works:
+
+- if a file exists in identical format in all three trees, it will
+  automatically collapse to "merged" state by git-read-tree.
+
+- a file that has _any_ difference what-so-ever in the three trees
+  will stay as separate entries in the index. It's up to "script
+  policy" to determine how to remove the non-0 stages, and insert a
+  merged version.
+
+- the index file saves and restores with all this information, so you
+  can merge things incrementally, but as long as it has entries in
+  stages 1/2/3 (ie "unmerged entries") you can't write the result. So
+  now the merge algorithm ends up being really simple:
+
+  * you walk the index in order, and ignore all entries of stage 0,
+    since they've already been done.
+
+  * if you find a "stage1", but no matching "stage2" or "stage3", you
+    know it's been removed from both trees (it only existed in the
+    original tree), and you remove that entry.
+
+  * if you find a matching "stage2" and "stage3" tree, you remove one
+    of them, and turn the other into a "stage0" entry. Remove any
+    matching "stage1" entry if it exists too.  .. all the normal
+    trivial rules ..
+
+You would normally use "git-merge-cache" with supplied
+"git-merge-one-file-script" to do this last step.  The script
+does not touch the files in the work tree, and the entire merge
+happens in the index file.  In other words, there is no need to
+worry about what is in the working directory, since it is never
+shown and never used.
+
+When you start a 3-way merge with an index file that is already
+populated, it is assumed that it represents the state of the
+files in your work tree, and you can even have files with
+changes unrecorded in the index file.  It is further assumed
+that this state is "derived" from the stage 2 tree.  The 3-way
+merge refuses to run if it finds an entry in the original index
+file that does not match stage 2.
+
+This is done to prevent you from losing your work-in-progress
+changes.  To illustrate, suppose you start from what has been
+commited last to your repository:
+
+    $ JC=`cat .git/HEAD`
+    $ git-checkout-cache -f -u -a $JC
+
+You do random edits, without running git-update-cache.  And then
+you notice that the tip of your "upstream" tree has advanced
+since you pulled from him:
+
+    $ git-fetch-script rsync://.... linus
+    $ LT=`cat .git/MERGE_HEAD`
+
+Your work tree is still based on your HEAD ($JC), but you have
+some edits since.  Three-way merge makes sure that you have not
+added or modified cache entries since $JC, and if you haven't,
+then does the right thing.  So with the following sequence:
+
+    $ git-read-tree -m -u `git-merge-base $JC $LT` $JC $LT
+    $ git-merge-cache git-merge-one-file-script -a
+    $ echo "Merge with Linus" | \
+      git-commit-tree `git-write-tree` -p $JC -p $LT
+
+what you would commit is a pure merge between $JC and LT without
+your work-in-progress changes, and your work tree would be
+updated to the result of the merge.
+
+
+See Also
+--------
+link:git-write-tree.html[git-write-tree]; link:git-ls-files.html[git-ls-files]
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-resolve-script.txt b/Documentation/git-resolve-script.txt
new file mode 100644 (file)
index 0000000..8dd84a3
--- /dev/null
@@ -0,0 +1,30 @@
+git-resolve-script(1)
+=====================
+v0.1, May 2005
+
+NAME
+----
+git-resolve-script - Script used to merge two trees
+
+
+SYNOPSIS
+--------
+'git-resolve-script'
+
+DESCRIPTION
+-----------
+This script is used by Linus to merge two trees.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
new file mode 100644 (file)
index 0000000..fe86c9c
--- /dev/null
@@ -0,0 +1,75 @@
+git-rev-list(1)
+===============
+v0.1, May 2005
+
+NAME
+----
+git-rev-list - Lists commit objects in reverse chronological order
+
+
+SYNOPSIS
+--------
+'git-rev-list' [ *--max-count*=number ] [ *--max-age*=timestamp ] [ *--min-age*=timestamp ] [ *--merge-order* [ *--show-breaks* ] ] <commit>
+
+DESCRIPTION
+-----------
+Lists commit objects in reverse chronological order starting at the
+given commit, taking ancestry relationship into account.  This is
+useful to produce human-readable log output.
+
+If *--merge-order* is specified, the commit history is decomposed into a
+unique sequence of minimal, non-linear epochs and maximal, linear epochs.
+Non-linear epochs are then linearised by sorting them into merge order, which
+is described below.
+
+Maximal, linear epochs correspond to periods of sequential development.
+Minimal, non-linear epochs correspond to periods of divergent development
+followed by a converging merge. The theory of epochs is described in more
+detail at
+link:http://blackcubes.dyndns.org/epoch/[http://blackcubes.dyndns.org/epoch/].
+
+The merge order for a non-linear epoch is defined as a linearisation for which
+the following invariants are true:
+
+    1. if a commit P is reachable from commit N, commit P sorts after commit N
+       in the linearised list.
+    2. if Pi and Pj are any two parents of a merge M (with i < j), then any
+       commit N, such that N is reachable from Pj but not reachable from Pi,
+       sorts before all commits reachable from Pi.
+
+Invariant 1 states that later commits appear before earlier commits they are
+derived from.
+
+Invariant 2 states that commits unique to "later" parents in a merge, appear
+before all commits from "earlier" parents of a merge.
+
+If *--show-breaks* is specified, each item of the list is output with a
+2-character prefix consisting of one of: (|), (^), (=) followed by a space.
+
+Commits marked with (=) represent the boundaries of minimal, non-linear epochs
+and correspond either to the start of a period of divergent development or to
+the end of such a period.
+
+Commits marked with (|) are direct parents of commits immediately preceding
+the marked commit in the list.
+
+Commits marked with (^) are not parents of the immediately preceding commit.
+These "breaks" represent necessary discontinuities implied by trying to
+represent an arbtirary DAG in a linear form.
+
+*--show-breaks* is only valid if *--merge-order* is also specified.
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Original *--merge-order* logic by Jon Seymour <jon.seymour@gmail.com>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-rev-tree.txt b/Documentation/git-rev-tree.txt
new file mode 100644 (file)
index 0000000..2ec7ed0
--- /dev/null
@@ -0,0 +1,88 @@
+git-rev-tree(1)
+===============
+v0.1, May 2005
+
+NAME
+----
+git-rev-tree - Provides the revision tree for one or more commits
+
+
+SYNOPSIS
+--------
+'git-rev-tree' [--edges] [--cache <cache-file>] [^]<commit> [[^]<commit>]
+
+DESCRIPTION
+-----------
+Provides the revision tree for one or more commits.
+
+OPTIONS
+-------
+--edges::
+       Show edges (ie places where the marking changes between parent
+       and child)
+
+--cache <cache-file>::
+       Use the specified file as a cache from a previous git-rev-list run
+       to speed things up.  Note that this "cache" is totally different
+       concept from the directory index.  Also this option is not
+       implemented yet.
+
+[^]<commit>::
+       The commit id to trace (a leading caret means to ignore this
+       commit-id and below)
+
+Output
+------
+
+        <date> <commit>:<flags> [<parent-commit>:<flags> ]\*
+
+<date>::
+       Date in 'seconds since epoch'
+
+<commit>::
+       id of commit object
+
+<parent-commit>::
+       id of each parent commit object (>1 indicates a merge)
+
+<flags>::
+
+       The flags are read as a bitmask representing each commit
+       provided on the commandline. eg: given the command:
+
+                $ git-rev-tree <com1> <com2> <com3>
+
+       The output:
+
+           <date> <commit>:5
+
+        means that <commit> is reachable from <com1>(1) and <com3>(4)
+       
+A revtree can get quite large. "git-rev-tree" will eventually allow
+you to cache previous state so that you don't have to follow the whole
+thing down.
+
+So the change difference between two commits is literally
+
+       git-rev-tree [commit-id1]  > commit1-revtree
+       git-rev-tree [commit-id2]  > commit2-revtree
+       join -t : commit1-revtree commit2-revtree > common-revisions
+
+(this is also how to find the most common parent - you'd look at just
+the head revisions - the ones that aren't referred to by other
+revisions - in "common-revision", and figure out the best one. I
+think.)
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-ssh-pull.txt b/Documentation/git-ssh-pull.txt
new file mode 100644 (file)
index 0000000..ac3fb34
--- /dev/null
@@ -0,0 +1,52 @@
+git-ssh-pull(1)
+===============
+v0.1, May 2005
+
+NAME
+----
+git-ssh-pull - Pulls from a remote repository over ssh connection
+
+
+
+SYNOPSIS
+--------
+'git-ssh-pull' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
+
+DESCRIPTION
+-----------
+Pulls from a remote repository over ssh connection, invoking
+git-ssh-push on the other end. It functions identically to
+git-ssh-push, aside from which end you run it on.
+
+
+OPTIONS
+-------
+commit-id::
+        Either the hash or the filename under [URL]/refs/ to
+        pull.
+
+-c::
+       Get the commit objects.
+-t::
+       Get trees associated with the commit objects.
+-a::
+       Get all the objects.
+-v::
+       Report what is downloaded.
+-w::
+        Writes the commit-id into the filename under $GIT_DIR/refs/ on
+        the local end after the transfer is complete.
+
+
+Author
+------
+Written by Daniel Barkalow <barkalow@iabervon.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-ssh-push.txt b/Documentation/git-ssh-push.txt
new file mode 100644 (file)
index 0000000..e38679d
--- /dev/null
@@ -0,0 +1,48 @@
+git-ssh-push(1)
+===============
+v0.1, Jun 2005
+
+NAME
+----
+git-ssh-push - Pushes to a remote repository over ssh connection
+
+
+SYNOPSIS
+--------
+'git-ssh-push' [-c] [-t] [-a] [-d] [-v] [-w filename] [--recover] commit-id url
+
+DESCRIPTION
+-----------
+Pushes from a remote repository over ssh connection, invoking
+git-ssh-pull on the other end. It functions identically to
+git-ssh-pull, aside from which end you run it on.
+
+OPTIONS
+-------
+commit-id::
+        Id of commit to push.
+
+-c::
+        Get the commit objects.
+-t::
+        Get tree associated with the requested commit object.
+-a::
+        Get all the objects.
+-v::
+        Report what is uploaded.
+-w::
+        Writes the commit-id into the filename under [URL]/refs/ on
+        the remote end after the transfer is complete.
+
+Author
+------
+Written by Daniel Barkalow <barkalow@iabervon.org>
+
+Documentation
+--------------
+Documentation by Daniel Barkalow
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-tag-script.txt b/Documentation/git-tag-script.txt
new file mode 100644 (file)
index 0000000..daf350b
--- /dev/null
@@ -0,0 +1,32 @@
+git-tag-script(1)
+=================
+v0.1, May 2005
+
+NAME
+----
+git-tag-script - An example script to create a tag object signed with GPG
+
+
+
+SYNOPSIS
+--------
+'git-tag-script'
+
+DESCRIPTION
+-----------
+This is an example script that uses "git-mktag" to create a tag object
+signed with GPG.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-tar-tree.txt b/Documentation/git-tar-tree.txt
new file mode 100644 (file)
index 0000000..dc8d0fa
--- /dev/null
@@ -0,0 +1,39 @@
+git-tar-tree(1)
+===============
+v0.1, May 2005
+
+NAME
+----
+git-tar-tree - Creates a tar archive of the files in the named tree
+
+
+SYNOPSIS
+--------
+'git-tar-tree' <tree-ish> [ <base> ]
+
+DESCRIPTION
+-----------
+Creates a tar archive containing the tree structure for the named tree.
+When <base> is specified it is added as a leading path as the files in the
+generated tar archive.
+
+git-tar-tree behaves differently when given a tree ID versus when given
+a commit ID or tag ID.  In the first case the current time is used as
+modification time of each file in the archive.  In the latter case the
+commit time as recorded in the referenced commit object is used instead.
+Additionally the commit ID is stored in a global extended pax header.
+It can be extracted using git-get-tar-commit-id.
+
+
+Author
+------
+Written by Rene Scharfe.
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-unpack-file.txt b/Documentation/git-unpack-file.txt
new file mode 100644 (file)
index 0000000..2f2130d
--- /dev/null
@@ -0,0 +1,37 @@
+git-unpack-file(1)
+==================
+v0.1, May 2005
+
+NAME
+----
+git-unpack-file - Creates a temporary file with a blob's contents
+
+
+
+SYNOPSIS
+--------
+'git-unpack-file' <blob>
+
+DESCRIPTION
+-----------
+Creates a file holding the contents of the blob specified by sha1. It
+returns the name of the temporary file in the following format:
+       .merge_file_XXXXX
+
+OPTIONS
+-------
+<blob>::
+       Must be a blob id
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-update-cache.txt b/Documentation/git-update-cache.txt
new file mode 100644 (file)
index 0000000..947f2bd
--- /dev/null
@@ -0,0 +1,108 @@
+git-update-cache(1)
+===================
+v0.1, May 2005
+
+NAME
+----
+git-update-cache - Modifies the index or directory cache
+
+
+SYNOPSIS
+--------
+'git-update-cache'
+            [--add] [--remove] [--refresh] [--replace]
+            [--ignore-missing]
+            [--force-remove]
+            [--cacheinfo <mode> <object> <file>]\*
+            [--] [<file>]\*
+
+DESCRIPTION
+-----------
+Modifies the index or directory cache. Each file mentioned is updated
+into the cache and any 'unmerged' or 'needs updating' state is
+cleared.
+
+The way "git-update-cache" handles files it is told about can be modified
+using the various options:
+
+OPTIONS
+-------
+--add::
+       If a specified file isn't in the cache already then it's
+       added.
+       Default behaviour is to ignore new files.
+
+--remove::
+       If a specified file is in the cache but is missing then it's
+       removed.
+       Default behaviour is to ignore removed file.
+
+--refresh::
+       Looks at the current cache and checks to see if merges or
+       updates are needed by checking stat() information.
+
+--ignore-missing::
+       Ignores missing files during a --refresh
+
+--cacheinfo <mode> <object> <path>::
+       Directly insert the specified info into the cache.
+       
+--force-remove::
+       Remove the file from the index even when the working directory
+       still has such a file. (Implies --remove.)
+
+--replace::
+       By default, when a file `path` exists in the index,
+       git-update-cache refuses an attempt to add `path/file`.
+       Similarly if a file `path/file` exists, a file `path`
+       cannot be added.  With --replace flag, existing entries
+       that conflicts with the entry being added are
+       automatically removed with warning messages.
+
+--::
+       Do not interpret any more arguments as options.
+
+<file>::
+       Files to act on.
+       Note that files begining with '.' are discarded. This includes
+       `./file` and `dir/./file`. If you don't want this, then use     
+       cleaner names.
+       The same applies to directories ending '/' and paths with '//'
+
+Using --refresh
+---------------
+'--refresh' does not calculate a new sha1 file or bring the cache
+up-to-date for mode/content changes. But what it *does* do is to
+"re-match" the stat information of a file with the cache, so that you
+can refresh the cache for a file that hasn't been changed but where
+the stat entry is out of date.
+
+For example, you'd want to do this after doing a "git-read-tree", to link
+up the stat cache details with the proper files.
+
+Using --cacheinfo
+-----------------
+'--cacheinfo' is used to register a file that is not in the current
+working directory.  This is useful for minimum-checkout merging.
+
+To pretend you have a file with mode and sha1 at path, say:
+
+ $ git-update-cache --cacheinfo mode sha1 path
+
+To update and refresh only the files already checked out:
+
+   git-checkout-cache -n -f -a && git-update-cache --ignore-missing --refresh
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-verify-pack.txt b/Documentation/git-verify-pack.txt
new file mode 100644 (file)
index 0000000..2df77de
--- /dev/null
@@ -0,0 +1,38 @@
+git-verify-pack(1)
+==================
+v0.1, June 2005
+
+NAME
+----
+git-verify-pack - Validate packed GIT archive files.
+
+
+SYNOPSIS
+--------
+'git-verify-pack' <pack>.idx ...
+
+
+DESCRIPTION
+-----------
+Reads given idx file for packed GIT archive created with
+git-pack-objects command and verifies idx file and the
+corresponding pack file.
+
+OPTIONS
+-------
+<pack>.idx ...::
+       The idx files to verify.
+
+
+Author
+------
+Written by Junio C Hamano <junkio@cox.net>
+
+Documentation
+--------------
+Documentation by Junio C Hamano
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-write-blob.txt b/Documentation/git-write-blob.txt
new file mode 100644 (file)
index 0000000..22d7555
--- /dev/null
@@ -0,0 +1,33 @@
+git-write-blob(1)
+=================
+v0.1, May 2005
+
+NAME
+----
+git-write-blob - Creates a blob from a file
+
+
+SYNOPSIS
+--------
+'git-write-blob' <any-file-on-the-filesystem>
+
+DESCRIPTION
+-----------
+Writes the contents of the named file (which can be outside of the work
+tree) as a blob into the object database, and reports its object ID to its
+standard output.  This is used by "git-merge-one-file-script" to update the
+cache without modifying files in the work tree.
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git-write-tree.txt b/Documentation/git-write-tree.txt
new file mode 100644 (file)
index 0000000..458d97a
--- /dev/null
@@ -0,0 +1,52 @@
+git-write-tree(1)
+=================
+v0.1, May 2005
+
+NAME
+----
+git-write-tree - Creates a tree from the current cache
+
+
+SYNOPSIS
+--------
+'git-write-tree'
+
+DESCRIPTION
+-----------
+Creates a tree object using the current cache.
+
+The cache must be merged.
+
+Conceptually, "git-write-tree" sync()s the current directory cache contents
+into a set of tree files.
+In order to have that match what is actually in your directory right
+now, you need to have done a "git-update-cache" phase before you did the
+"git-write-tree".
+
+
+
+
+////////////////////////////////////////////////////////////////
+
+Producing man pages and html
+
+To create a set of html pages run:
+  perl split-docs.pl -html < core-git.txt
+
+To create a set of man pages run:
+  perl split-docs.pl -man < core-git.txt
+
+
+////////////////////////////////////////////////////////////////
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/git.txt b/Documentation/git.txt
new file mode 100644 (file)
index 0000000..797eb33
--- /dev/null
@@ -0,0 +1,308 @@
+git(7)
+======
+May 2005
+
+NAME
+----
+git - the stupid content tracker
+
+
+SYNOPSIS
+--------
+'git-<command>' <args>
+
+DESCRIPTION
+-----------
+
+This is reference information for the core git commands.
+
+The Discussion section below contains much useful definition and
+clarification info - read that first.  And of the commands, I suggest
+reading link:git-update-cache.html[git-update-cache] and
+link:git-read-tree.html[git-read-tree] first - I wish I had!
+
+David Greaves <david@dgreaves.com>
+08/05/05
+
+Updated by Junio C Hamano <junkio@cox.net> on 2005-05-05 to
+reflect recent changes.
+
+Commands Overview
+-----------------
+The git commands can helpfully be split into those that manipulate
+the repository, the cache and the working fileset and those that
+interrogate and compare them.
+
+There are also some ancilliary programs that can be viewed as useful
+aids for using the core commands but which are unlikely to be used by
+SCMs layered over git.
+
+Manipulation commands
+~~~~~~~~~~~~~~~~~~~~~
+link:git-checkout-cache.html[git-checkout-cache]::
+       Copy files from the cache to the working directory
+
+link:git-commit-tree.html[git-commit-tree]::
+       Creates a new commit object
+
+link:git-init-db.html[git-init-db]::
+       Creates an empty git object database
+
+link:git-merge-base.html[git-merge-base]::
+       Finds as good a common ancestor as possible for a merge
+
+link:git-mktag.html[git-mktag]::
+       Creates a tag object
+
+link:git-read-tree.html[git-read-tree]::
+       Reads tree information into the directory cache
+
+link:git-update-cache.html[git-update-cache]::
+       Modifies the index or directory cache
+
+link:git-write-blob.html[git-write-blob]::
+       Creates a blob from a file
+
+link:git-write-tree.html[git-write-tree]::
+       Creates a tree from the current cache
+
+Interrogation commands
+~~~~~~~~~~~~~~~~~~~~~~
+link:git-cat-file.html[git-cat-file]::
+       Provide content or type information for repository objects
+
+link:git-check-files.html[git-check-files]::
+       Verify a list of files are up-to-date
+
+link:git-diff-cache.html[git-diff-cache]::
+       Compares content and mode of blobs between the cache and repository
+
+link:git-diff-files.html[git-diff-files]::
+       Compares files in the working tree and the cache
+
+link:git-diff-tree.html[git-diff-tree]::
+       Compares the content and mode of blobs found via two tree objects
+
+link:git-export.html[git-export]::
+       Exports each commit and a diff against each of its parents
+
+link:git-fsck-cache.html[git-fsck-cache]::
+       Verifies the connectivity and validity of the objects in the database
+
+link:git-ls-files.html[git-ls-files]::
+       Information about files in the cache/working directory
+
+link:git-ls-tree.html[git-ls-tree]::
+       Displays a tree object in human readable form
+
+link:git-merge-cache.html[git-merge-cache]::
+       Runs a merge for files needing merging
+
+link:git-rev-list.html[git-rev-list]::
+       Lists commit objects in reverse chronological order
+
+link:git-rev-tree.html[git-rev-tree]::
+       Provides the revision tree for one or more commits
+
+link:git-tar-tree.html[git-tar-tree]::
+       Creates a tar archive of the files in the named tree
+
+link:git-unpack-file.html[git-unpack-file]::
+       Creates a temporary file with a blob's contents
+
+link:git-verify-pack.html[git-verify-pack]::
+       Validates packed GIT archive files
+
+The interrogate commands may create files - and you can force them to
+touch the working file set - but in general they don't
+
+
+Ancilliary Commands
+-------------------
+Manipulators:
+
+link:git-apply-patch-script.html[git-apply-patch-script]::
+       Sample script to apply the diffs from git-diff-*
+
+link:git-convert-cache.html[git-convert-cache]::
+       Converts old-style GIT repository
+
+link:git-http-pull.html[git-http-pull]::
+       Downloads a remote GIT repository via HTTP
+
+link:git-local-pull.html[git-local-pull]::
+       Duplicates another GIT repository on a local system
+
+link:git-merge-one-file-script.html[git-merge-one-file-script]::
+       The standard helper program to use with "git-merge-cache"
+
+link:git-pull-script.html[git-pull-script]::
+       Script used by Linus to pull and merge a remote repository
+
+link:git-prune-script.html[git-prune-script]::
+       Prunes all unreachable objects from the object database
+
+link:git-resolve-script.html[git-resolve-script]::
+       Script used to merge two trees
+
+link:git-tag-script.html[git-tag-script]::
+       An example script to create a tag object signed with GPG
+
+link:git-ssh-pull.html[git-ssh-pull]::
+       Pulls from a remote repository over ssh connection
+
+Interogators:
+
+link:git-diff-helper.html[git-diff-helper]::
+       Generates patch format output for git-diff-*
+
+link:git-ssh-push.html[git-ssh-push]::
+       Helper "server-side" program used by git-ssh-pull
+
+
+
+Identifier Terminology
+----------------------
+<object>::
+       Indicates the sha1 identifier for any type of object
+
+<blob>::
+       Indicates a blob object sha1 identifier
+
+<tree>::
+       Indicates a tree object sha1 identifier
+
+<commit>::
+       Indicates a commit object sha1 identifier
+
+<tree-ish>::
+       Indicates a tree, commit or tag object sha1 identifier.  A
+       command that takes a <tree-ish> argument ultimately wants to
+       operate on a <tree> object but automatically dereferences
+       <commit> and <tag> objects that point at a <tree>.
+
+<type>::
+       Indicates that an object type is required.
+       Currently one of: blob/tree/commit/tag
+
+<file>::
+       Indicates a filename - always relative to the root of
+       the tree structure GIT_INDEX_FILE describes.
+
+Symbolic Identifiers
+--------------------
+Any git comand accepting any <object> can also use the following
+symbolic notation:
+
+HEAD::
+       indicates the head of the repository (ie the contents of
+       `$GIT_DIR/HEAD`)
+<tag>::
+       a valid tag 'name'+
+       (ie the contents of `$GIT_DIR/refs/tags/<tag>`)
+<head>::
+       a valid head 'name'+
+       (ie the contents of `$GIT_DIR/refs/heads/<head>`)
+<snap>::
+       a valid snapshot 'name'+
+       (ie the contents of `$GIT_DIR/refs/snap/<snap>`)
+
+
+File/Directory Structure
+------------------------
+The git-core manipulates the following areas in the directory:
+
+ .git/        The base (overridden with $GIT_DIR)
+   objects/    The object base (overridden with $GIT_OBJECT_DIRECTORY)
+     ??/       'First 2 chars of object' directories
+
+It can interrogate (but never updates) the following areas:
+
+   refs/       Directories containing symbolic names for objects
+              (each file contains the hex SHA1 + newline)
+     heads/    Commits which are heads of various sorts
+     tags/     Tags, by the tag name (or some local renaming of it)
+     snap/     ????
+   ...         Everything else isn't shared
+   HEAD        Symlink to refs/heads/<something>
+
+Higher level SCMs may provide and manage additional information in the
+GIT_DIR.
+
+Terminology
+-----------
+Each line contains terms which you may see used interchangeably
+
+ object database, .git directory
+ directory cache, index
+ id, sha1, sha1-id, sha1 hash
+ type, tag
+
+
+Environment Variables
+---------------------
+Various git commands use the following environment variables:
+
+The git Repository
+~~~~~~~~~~~~~~~~~~
+These environment variables apply to 'all' core git commands. Nb: it
+is worth noting that they may be used/overridden by SCMS sitting above
+git so take care if using Cogito etc
+
+'GIT_INDEX_FILE'::
+       This environment allows the specification of an alternate
+       cache/index file. If not specified, the default of
+       `$GIT_DIR/index` is used.
+
+'GIT_OBJECT_DIRECTORY'::
+       If the object storage directory is specified via this
+       environment variable then the sha1 directories are created
+       underneath - otherwise the default `$GIT_DIR/objects`
+       directory is used.
+
+'GIT_ALTERNATE_OBJECT_DIRECTORIES'::
+       Due to the immutable nature of git objects, old objects can be
+       archived into shared, read-only directories. This variable
+       specifies a ":" seperated list of git object directories which
+       can be used to search for git objects. New objects will not be
+       written to these directories.
+
+'GIT_DIR'::
+       If the 'GIT_DIR' environment variable is set then it specifies
+       a path to use instead of `./.git` for the base of the
+       repository.
+
+git Commits
+~~~~~~~~~~~
+'GIT_AUTHOR_NAME'::
+'GIT_AUTHOR_EMAIL'::
+'GIT_AUTHOR_DATE'::
+'GIT_COMMITTER_NAME'::
+'GIT_COMMITTER_EMAIL'::
+       see link:git-commit-tree.html[git-commit-tree]
+
+git Diffs
+~~~~~~~~~
+'GIT_DIFF_OPTS'::
+'GIT_EXTERNAL_DIFF'::
+       see the "generating patches" section in :
+       link:git-diff-cache.html[git-diff-cache];
+       link:git-diff-files.html[git-diff-files];
+       link:git-diff-tree.html[git-diff-tree]
+
+Discussion
+----------
+include::../README[]
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org> and the git-list <git@vger.kernel.org>.
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the link:git.html[git] suite
+
diff --git a/Documentation/tutorial.txt b/Documentation/tutorial.txt
new file mode 100644 (file)
index 0000000..957ea96
--- /dev/null
@@ -0,0 +1,538 @@
+A short git tutorial
+====================
+May 2005
+
+
+Introduction
+------------
+
+This is trying to be a short tutorial on setting up and using a git
+archive, mainly because being hands-on and using explicit examples is
+often the best way of explaining what is going on.
+
+In normal life, most people wouldn't use the "core" git programs
+directly, but rather script around them to make them more palatable. 
+Understanding the core git stuff may help some people get those scripts
+done, though, and it may also be instructive in helping people
+understand what it is that the higher-level helper scripts are actually
+doing. 
+
+The core git is often called "plumbing", with the prettier user
+interfaces on top of it called "porcelain".  You may not want to use the
+plumbing directly very often, but it can be good to know what the
+plumbing does for when the porcelain isn't flushing... 
+
+
+Creating a git archive
+----------------------
+
+Creating a new git archive couldn't be easier: all git archives start
+out empty, and the only thing you need to do is find yourself a
+subdirectory that you want to use as a working tree - either an empty
+one for a totally new project, or an existing working tree that you want
+to import into git. 
+
+For our first example, we're going to start a totally new archive from
+scratch, with no pre-existing files, and we'll call it "git-tutorial".
+To start up, create a subdirectory for it, change into that
+subdirectory, and initialize the git infrastructure with "git-init-db":
+
+       mkdir git-tutorial
+       cd git-tutorial
+       git-init-db 
+
+to which git will reply
+
+       defaulting to local storage area
+
+which is just git's way of saying that you haven't been doing anything
+strange, and that it will have created a local .git directory setup for
+your new project. You will now have a ".git" directory, and you can
+inspect that with "ls". For your new empty project, ls should show you
+three entries:
+
+ - a symlink called HEAD, pointing to "refs/heads/master"
+
+   Don't worry about the fact that the file that the HEAD link points to
+   doesn't even exist yet - you haven't created the commit that will
+   start your HEAD development branch yet.
+
+ - a subdirectory called "objects", which will contain all the git SHA1
+   objects of your project. You should never have any real reason to
+   look at the objects directly, but you might want to know that these
+   objects are what contains all the real _data_ in your repository.
+
+ - a subdirectory called "refs", which contains references to objects.
+
+   In particular, the "refs" subdirectory will contain two other
+   subdirectories, named "heads" and "tags" respectively.  They do
+   exactly what their names imply: they contain references to any number
+   of different "heads" of development (aka "branches"), and to any
+   "tags" that you have created to name specific versions of your
+   repository. 
+
+   One note: the special "master" head is the default branch, which is
+   why the .git/HEAD file was created as a symlink to it even if it
+   doesn't yet exist. Basically, the HEAD link is supposed to always
+   point to the branch you are working on right now, and you always
+   start out expecting to work on the "master" branch.
+
+   However, this is only a convention, and you can name your branches
+   anything you want, and don't have to ever even _have_ a "master"
+   branch.  A number of the git tools will assume that .git/HEAD is
+   valid, though.
+
+   [ Implementation note: an "object" is identified by its 160-bit SHA1
+   hash, aka "name", and a reference to an object is always the 40-byte
+   hex representation of that SHA1 name. The files in the "refs"
+   subdirectory are expected to contain these hex references (usually
+   with a final '\n' at the end), and you should thus expect to see a
+   number of 41-byte files containing these references in this refs
+   subdirectories when you actually start populating your tree ]
+
+You have now created your first git archive. Of course, since it's
+empty, that's not very useful, so let's start populating it with data.
+
+
+       Populating a git archive
+       ------------------------
+
+We'll keep this simple and stupid, so we'll start off with populating a
+few trivial files just to get a feel for it.
+
+Start off with just creating any random files that you want to maintain
+in your git archive. We'll start off with a few bad examples, just to
+get a feel for how this works:
+
+       echo "Hello World" > a
+       echo "Silly example" > b
+
+you have now created two files in your working directory, but to
+actually check in your hard work, you will have to go through two steps:
+
+ - fill in the "cache" aka "index" file with the information about your
+   working directory state
+
+ - commit that index file as an object.
+
+The first step is trivial: when you want to tell git about any changes
+to your working directory, you use the "git-update-cache" program.  That
+program normally just takes a list of filenames you want to update, but
+to avoid trivial mistakes, it refuses to add new entries to the cache
+(or remove existing ones) unless you explicitly tell it that you're
+adding a new entry with the "--add" flag (or removing an entry with the
+"--remove") flag. 
+
+So to populate the index with the two files you just created, you can do
+
+       git-update-cache --add a b
+
+and you have now told git to track those two files.
+
+In fact, as you did that, if you now look into your object directory,
+you'll notice that git will have added two new objects to the object
+store.  If you did exactly the steps above, you should now be able to do
+
+       ls .git/objects/??/*
+
+and see two files:
+
+       .git/objects/55/7db03de997c86a4a028e1ebd3a1ceb225be238 
+       .git/objects/f2/4c74a2e500f5ee1332c86b94199f52b1d1d962
+
+which correspond with the object with SHA1 names of 557db... and f24c7..
+respectively.
+
+If you want to, you can use "git-cat-file" to look at those objects, but
+you'll have to use the object name, not the filename of the object:
+
+       git-cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
+
+where the "-t" tells git-cat-file to tell you what the "type" of the
+object is. Git will tell you that you have a "blob" object (ie just a
+regular file), and you can see the contents with
+
+       git-cat-file "blob" 557db03de997c86a4a028e1ebd3a1ceb225be238
+
+which will print out "Hello World".  The object 557db...  is nothing
+more than the contents of your file "a". 
+
+[ Digression: don't confuse that object with the file "a" itself.  The
+  object is literally just those specific _contents_ of the file, and
+  however much you later change the contents in file "a", the object we
+  just looked at will never change.  Objects are immutable.  ]
+
+Anyway, as we mentioned previously, you normally never actually take a
+look at the objects themselves, and typing long 40-character hex SHA1
+names is not something you'd normally want to do.  The above digression
+was just to show that "git-update-cache" did something magical, and
+actually saved away the contents of your files into the git content
+store. 
+
+Updating the cache did something else too: it created a ".git/index"
+file.  This is the index that describes your current working tree, and
+something you should be very aware of.  Again, you normally never worry
+about the index file itself, but you should be aware of the fact that
+you have not actually really "checked in" your files into git so far,
+you've only _told_ git about them.
+
+However, since git knows about them, you can now start using some of the
+most basic git commands to manipulate the files or look at their status. 
+
+In particular, let's not even check in the two files into git yet, we'll
+start off by adding another line to "a" first:
+
+       echo "It's a new day for git" >> a
+
+and you can now, since you told git about the previous state of "a", ask
+git what has changed in the tree compared to your old index, using the
+"git-diff-files" command:
+
+       git-diff-files 
+
+oops.  That wasn't very readable.  It just spit out its own internal
+version of a "diff", but that internal version really just tells you
+that it has noticed that "a" has been modified, and that the old object
+contents it had have been replaced with something else.
+
+To make it readable, we can tell git-diff-files to output the
+differences as a patch, using the "-p" flag:
+
+       git-diff-files -p
+
+which will spit out
+
+       diff --git a/a b/a
+       --- a/a
+       +++ b/a
+       @@ -1 +1,2 @@
+        Hello World
+       +It's a new day for git
+
+ie the diff of the change we caused by adding another line to "a".
+
+In other words, git-diff-files always shows us the difference between
+what is recorded in the index, and what is currently in the working
+tree. That's very useful.
+
+
+       Committing git state
+       --------------------
+
+Now, we want to go to the next stage in git, which is to take the files
+that git knows about in the index, and commit them as a real tree. We do
+that in two phases: creating a "tree" object, and committing that "tree"
+object as a "commit" object together with an explanation of what the
+tree was all about, along with information of how we came to that state.
+
+Creating a tree object is trivial, and is done with "git-write-tree". 
+There are no options or other input: git-write-tree will take the
+current index state, and write an object that describes that whole
+index.  In other words, we're now tying together all the different
+filenames with their contents (and their permissions), and we're
+creating the equivalent of a git "directory" object:
+
+       git-write-tree
+
+and this will just output the name of the resulting tree, in this case
+(if you have does exactly as I've described) it should be
+
+       3ede4ed7e895432c0a247f09d71a76db53bd0fa4
+
+which is another incomprehensible object name. Again, if you want to,
+you can use "git-cat-file -t 3ede4.." to see that this time the object
+is not a "blob" object, but a "tree" object (you can also use
+git-cat-file to actually output the raw object contents, but you'll see
+mainly a binary mess, so that's less interesting).
+
+However - normally you'd never use "git-write-tree" on its own, because
+normally you always commit a tree into a commit object using the
+"git-commit-tree" command. In fact, it's easier to not actually use
+git-write-tree on its own at all, but to just pass its result in as an
+argument to "git-commit-tree".
+
+"git-commit-tree" normally takes several arguments - it wants to know
+what the _parent_ of a commit was, but since this is the first commit
+ever in this new archive, and it has no parents, we only need to pass in
+the tree ID. However, git-commit-tree also wants to get a commit message
+on its standard input, and it will write out the resulting ID for the
+commit to its standard output.
+
+And this is where we start using the .git/HEAD file. The HEAD file is
+supposed to contain the reference to the top-of-tree, and since that's
+exactly what git-commit-tree spits out, we can do this all with a simple
+shell pipeline:
+
+       echo "Initial commit" | git-commit-tree $(git-write-tree) > .git/HEAD
+
+which will say:
+
+       Committing initial tree 3ede4ed7e895432c0a247f09d71a76db53bd0fa4
+
+just to warn you about the fact that it created a totally new commit
+that is not related to anything else. Normally you do this only _once_
+for a project ever, and all later commits will be parented on top of an
+earlier commit, and you'll never see this "Committing initial tree"
+message ever again.
+
+
+       Making a change
+       ---------------
+
+Remember how we did the "git-update-cache" on file "a" and then we
+changed "a" afterward, and could compare the new state of "a" with the
+state we saved in the index file? 
+
+Further, remember how I said that "git-write-tree" writes the contents
+of the _index_ file to the tree, and thus what we just committed was in
+fact the _original_ contents of the file "a", not the new ones. We did
+that on purpose, to show the difference between the index state, and the
+state in the working directory, and how they don't have to match, even
+when we commit things.
+
+As before, if we do "git-diff-files -p" in our git-tutorial project,
+we'll still see the same difference we saw last time: the index file
+hasn't changed by the act of committing anything.  However, now that we
+have committed something, we can also learn to use a new command:
+"git-diff-cache".
+
+Unlike "git-diff-files", which showed the difference between the index
+file and the working directory, "git-diff-cache" shows the differences
+between a committed _tree_ and either the the index file or the working
+directory.  In other words, git-diff-cache wants a tree to be diffed
+against, and before we did the commit, we couldn't do that, because we
+didn't have anything to diff against. 
+
+But now we can do 
+
+       git-diff-cache -p HEAD
+
+(where "-p" has the same meaning as it did in git-diff-files), and it
+will show us the same difference, but for a totally different reason. 
+Now we're comparing the working directory not against the index file,
+but against the tree we just wrote.  It just so happens that those two
+are obviously the same, so we get the same result.
+
+In other words, "git-diff-cache" normally compares a tree against the
+working directory, but when given the "--cached" flag, it is told to
+instead compare against just the index cache contents, and ignore the
+current working directory state entirely.  Since we just wrote the index
+file to HEAD, doing "git-diff-cache --cached -p HEAD" should thus return
+an empty set of differences, and that's exactly what it does. 
+
+[ Digression: "git-diff-cache" really always uses the index for its
+  comparisons, and saying that it compares a tree against the working
+  directory is thus not strictly accurate. In particular, the list of
+  files to compare (the "meta-data") _always_ comes from the index file,
+  regardless of whether the --cached flag is used or not. The --cached
+  flag really only determines whether the file _contents_ to be compared
+  come from the working directory or not.
+
+  This is not hard to understand, as soon as you realize that git simply
+  never knows (or cares) about files that it is not told about
+  explicitly. Git will never go _looking_ for files to compare, it
+  expects you to tell it what the files are, and that's what the index
+  is there for.  ]
+
+However, our next step is to commit the _change_ we did, and again, to
+understand what's going on, keep in mind the difference between "working
+directory contents", "index file" and "committed tree".  We have changes
+in the working directory that we want to commit, and we always have to
+work through the index file, so the first thing we need to do is to
+update the index cache:
+
+       git-update-cache a
+
+(note how we didn't need the "--add" flag this time, since git knew
+about the file already).
+
+Note what happens to the different git-diff-xxx versions here.  After
+we've updated "a" in the index, "git-diff-files -p" now shows no
+differences, but "git-diff-cache -p HEAD" still _does_ show that the
+current state is different from the state we committed.  In fact, now
+"git-diff-cache" shows the same difference whether we use the "--cached"
+flag or not, since now the index is coherent with the working directory. 
+
+Now, since we've updated "a" in the index, we can commit the new
+version. We could do it by writing the tree by hand, and committing the
+tree (this time we'd have to use the "-p HEAD" flag to tell commit that
+the HEAD was the _parent_ of the new commit, and that this wasn't an
+initial commit any more), but the fact is, git has a simple helper
+script for doing all of the non-initial commits that does all of this
+for you, and starts up an editor to let you write your commit message
+yourself, so let's just use that:
+
+       git commit
+
+Write whatever message you want, and all the lines that start with '#'
+will be pruned out, and the rest will be used as the commit message for
+the change. If you decide you don't want to commit anything after all at
+this point (you can continue to edit things and update the cache), you
+can just leave an empty message. Otherwise git-commit-script will commit
+the change for you.
+
+You've now made your first real git commit. And if you're interested in
+looking at what git-commit-script really does, feel free to investigate:
+it's a few very simple shell scripts to generate the helpful (?) commit
+message headers, and a few one-liners that actually do the commit itself.
+
+
+       Checking it out
+       ---------------
+
+While creating changes is useful, it's even more useful if you can tell
+later what changed.  The most useful command for this is another of the
+"diff" family, namely "git-diff-tree". 
+
+git-diff-tree can be given two arbitrary trees, and it will tell you the
+differences between them. Perhaps even more commonly, though, you can
+give it just a single commit object, and it will figure out the parent
+of that commit itself, and show the difference directly. Thus, to get
+the same diff that we've already seen several times, we can now do
+
+       git-diff-tree -p HEAD
+
+(again, "-p" means to show the difference as a human-readable patch),
+and it will show what the last commit (in HEAD) actually changed.
+
+More interestingly, you can also give git-diff-tree the "-v" flag, which
+tells it to also show the commit message and author and date of the
+commit, and you can tell it to show a whole series of diffs.
+Alternatively, you can tell it to be "silent", and not show the diffs at
+all, but just show the actual commit message.
+
+In fact, together with the "git-rev-list" program (which generates a
+list of revisions), git-diff-tree ends up being a veritable fount of
+changes. A trivial (but very useful) script called "git-whatchanged" is
+included with git which does exactly this, and shows a log of recent
+activity.
+
+To see the whole history of our pitiful little git-tutorial project, you
+can do
+
+       git log
+
+which shows just the log messages, or if we want to see the log together
+with the associated patches use the more complex (and much more
+powerful)
+
+       git-whatchanged -p --root
+
+and you will see exactly what has changed in the repository over its
+short history. 
+
+[ Side note: the "--root" flag is a flag to git-diff-tree to tell it to
+  show the initial aka "root" commit too.  Normally you'd probably not
+  want to see the initial import diff, but since the tutorial project
+  was started from scratch and is so small, we use it to make the result
+  a bit more interesting ]
+
+With that, you should now be having some inkling of what git does, and
+can explore on your own.
+
+
+       Copying archives
+       -----------------
+
+Git archives are normally totally self-sufficient, and it's worth noting
+that unlike CVS, for example, there is no separate notion of
+"repository" and "working tree".  A git repository normally _is_ the
+working tree, with the local git information hidden in the ".git"
+subdirectory.  There is nothing else.  What you see is what you got. 
+
+[ Side note: you can tell git to split the git internal information from
+  the directory that it tracks, but we'll ignore that for now: it's not
+  how normal projects work, and it's really only meant for special uses.
+  So the mental model of "the git information is always tied directly to
+  the working directory that it describes" may not be technically 100%
+  accurate, but it's a good model for all normal use ]
+
+This has two implications: 
+
+ - if you grow bored with the tutorial archive you created (or you've
+   made a mistake and want to start all over), you can just do simple
+
+       rm -rf git-tutorial
+
+   and it will be gone. There's no external repository, and there's no
+   history outside of the project you created.
+
+ - if you want to move or duplicate a git archive, you can do so. There
+   is no "git clone" command: if you want to create a copy of your
+   archive (with all the full history that went along with it), you can
+   do so with a regular "cp -a git-tutorial new-git-tutorial".
+
+   Note that when you've moved or copied a git archive, your git index
+   file (which caches various information, notably some of the "stat"
+   information for the files involved) will likely need to be refreshed.
+   So after you do a "cp -a" to create a new copy, you'll want to do
+
+       git-update-cache --refresh
+
+   to make sure that the index file is up-to-date in the new one. 
+
+Note that the second point is true even across machines.  You can
+duplicate a remote git archive with _any_ regular copy mechanism, be it
+"scp", "rsync" or "wget". 
+
+When copying a remote repository, you'll want to at a minimum update the
+index cache when you do this, and especially with other peoples
+repositories you often want to make sure that the index cache is in some
+known state (you don't know _what_ they've done and not yet checked in),
+so usually you'll precede the "git-update-cache" with a
+
+       git-read-tree --reset HEAD
+       git-update-cache --refresh
+
+which will force a total index re-build from the tree pointed to by HEAD
+(it resets the index contents to HEAD, and then the git-update-cache
+makes sure to match up all index entries with the checked-out files). 
+
+The above can also be written as simply
+
+       git reset
+
+and in fact a lot of the common git command combinations can be scripted
+with the "git xyz" interfaces, and you can learn things by just looking
+at what the git-*-script scripts do ("git reset" is the above two lines
+implemented in "git-reset-script", but some things like "git status" and
+"git commit" are slightly more complex scripts around the basic git
+commands). 
+
+NOTE! Many (most?) public remote repositories will not contain any of
+the checked out files or even an index file, and will _only_ contain the
+actual core git files.  Such a repository usually doesn't even have the
+".git" subdirectory, but has all the git files directly in the
+repository. 
+
+To create your own local live copy of such a "raw" git repository, you'd
+first create your own subdirectory for the project, and then copy the
+raw repository contents into the ".git" directory. For example, to
+create your own copy of the git repository, you'd do the following
+
+       mkdir my-git
+       cd my-git
+       rsync -rL rsync://rsync.kernel.org/pub/scm/linux/kernel/git/torvalds/git.git/ .git
+
+followed by 
+
+       git-read-tree HEAD
+
+to populate the index. However, now you have populated the index, and
+you have all the git internal files, but you will notice that you don't
+actually have any of the _working_directory_ files to work on. To get
+those, you'd check them out with
+
+       git-checkout-cache -u -a
+
+where the "-u" flag means that you want the checkout to keep the index
+up-to-date (so that you don't have to refresh it afterward), and the
+"-a" file means "check out all files" (if you have a stale copy or an
+older version of a checked out tree you may also need to add the "-f"
+file first, to tell git-checkout-cache to _force_ overwriting of any old
+files). 
+
+You have now successfully copied somebody else's (mine) remote
+repository, and checked it out. 
+
+[ to be continued.. cvs2git, tagging versions, branches, merging.. ]
diff --git a/INSTALL b/INSTALL
new file mode 100644 (file)
index 0000000..101485d
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,64 @@
+
+               Git installation
+
+Normally you can just do "make" followed by "make install", and that
+will install the git programs in your own ~/bin/ directory.  If you want
+to do a global install, you can do
+
+       make prefix=/usr install
+
+(or prefix=/usr/local, of course).  Some day somebody may send me a RPM
+spec file or something, and you can do "make rpm" or whatever.
+
+Issues of note:
+
+ - git normally installs a helper script wrapper called "git", which
+   conflicts with a similarly named "GNU interactive tools" program.
+
+   Tough.  Either don't use the wrapper script, or delete the old GNU
+   interactive tools.  None of the core git stuff needs the wrapper,
+   it's just a convenient shorthand and while it is documented in some
+   places, you can always replace "git commit" with "git-commit-script"
+   instead. 
+
+   But let's face it, most of us don't have GNU interactive tools, and
+   even if we had it, we wouldn't know what it does.  I don't think it
+   has been actively developed since 1997, and people have moved over to
+   graphical file managers.
+
+ - Git is reasonably self-sufficient, but does depend on a few external
+   programs and libraries:
+
+       - "zlib", the compression library. Git won't build without it.
+
+       - "openssl".  The git-rev-list program uses bignum support from
+         openssl, and unless you specify otherwise, you'll also get the
+         SHA1 library from here.
+
+         If you don't have openssl, you can use one of the SHA1 libraries
+         that come with git (git includes the one from Mozilla, and has
+         its own PowerPC-optimized one too - see the Makefile), and you
+         can avoid the bignum support by excising git-rev-list support
+         for "--merge-order" (by hand).
+
+       - "libcurl".  git-http-pull uses this.  You can disable building of
+         that program if you just want to get started. 
+
+       - "GNU diff" to generate patches.  Of course, you don't _have_ to
+         generate patches if you don't want to, but let's face it, you'll
+         be wanting to. Or why did you get git in the first place?
+
+         Non-GNU versions of the diff/patch programs don't generally support
+         the unified patch format (which is the one git uses), so you
+         really do want to get the GNU one.  Trust me, you will want to
+         do that even if it wasn't for git.  There's no point in living
+         in the dark ages any more. 
+
+       - "merge", the standard UNIX three-way merge program.  It usually
+         comes with the "rcs" package on most Linux distributions, so if
+         you have a developer install you probably have it already, but a
+         "graphical user desktop" install might have left it out.
+
+         You'll only need the merge program if you do development using
+         git, and if you only use git to track other peoples work you'll
+         never notice the lack of it. 
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..5dcfc24
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,175 @@
+# -DCOLLISION_CHECK if you believe that SHA1's
+# 1461501637330902918203684832716283019655932542976 hashes do not give you
+# enough guarantees about no collisions between objects ever hapenning.
+#
+# -DUSE_NSEC if you want git to care about sub-second file mtimes and ctimes.
+# -DUSE_STDEV if you want git to care about st_dev changing
+#
+# Note that you need some new glibc (at least >2.2.4) for this, and it will
+# BREAK YOUR LOCAL DIFFS! show-diff and anything using it will likely randomly
+# break unless your underlying filesystem supports those sub-second times
+# (my ext3 doesn't).
+COPTS=-O2
+CFLAGS=-g $(COPTS) -Wall
+
+prefix=$(HOME)
+bin=$(prefix)/bin
+# dest=
+
+CC=gcc
+AR=ar
+INSTALL=install
+
+#
+# sparse is architecture-neutral, which means that we need to tell it
+# explicitly what architecture to check for. Fix this up for yours..
+#
+SPARSE_FLAGS=-D__BIG_ENDIAN__ -D__powerpc__
+
+SCRIPTS=git git-apply-patch-script git-merge-one-file-script git-prune-script \
+       git-pull-script git-tag-script git-resolve-script git-whatchanged \
+       git-fetch-script git-status-script git-commit-script \
+       git-log-script git-shortlog git-cvsimport-script git-diff-script \
+       git-reset-script git-add-script git-checkout-script git-clone-script \
+       gitk git-cherry git-rebase-script git-relink-script git-repack-script
+
+PROG=   git-update-cache git-diff-files git-init-db git-write-tree \
+       git-read-tree git-commit-tree git-cat-file git-fsck-cache \
+       git-checkout-cache git-diff-tree git-rev-tree git-ls-files \
+       git-check-files git-ls-tree git-merge-base git-merge-cache \
+       git-unpack-file git-export git-diff-cache git-convert-cache \
+       git-http-pull git-ssh-push git-ssh-pull git-rev-list git-mktag \
+       git-diff-helper git-tar-tree git-local-pull git-write-blob \
+       git-get-tar-commit-id git-apply git-stripspace \
+       git-diff-stages git-rev-parse git-patch-id git-pack-objects \
+       git-unpack-objects git-verify-pack git-receive-pack git-send-pack \
+       git-prune-packed git-fetch-pack git-upload-pack git-clone-pack \
+       git-show-index
+
+all: $(PROG)
+
+install: $(PROG) $(SCRIPTS)
+       $(INSTALL) $(PROG) $(SCRIPTS) $(dest)$(bin)
+
+LIB_OBJS=read-cache.o sha1_file.o usage.o object.o commit.o tree.o blob.o \
+        tag.o date.o index.o diff-delta.o patch-delta.o entry.o \
+        epoch.o refs.o csum-file.o pack-check.o pkt-line.o connect.o
+LIB_FILE=libgit.a
+LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.h csum-file.h \
+       pack.h pkt-line.h refs.h
+
+LIB_H += strbuf.h
+LIB_OBJS += strbuf.o
+
+LIB_H += diff.h count-delta.h
+LIB_OBJS += diff.o diffcore-rename.o diffcore-pickaxe.o diffcore-pathspec.o \
+       count-delta.o diffcore-break.o diffcore-order.o
+
+LIB_OBJS += gitenv.o
+
+LIBS = $(LIB_FILE)
+LIBS += -lz
+
+ifdef MOZILLA_SHA1
+  SHA1_HEADER="mozilla-sha1/sha1.h"
+  LIB_OBJS += mozilla-sha1/sha1.o
+else
+ifdef PPC_SHA1
+  SHA1_HEADER="ppc/sha1.h"
+  LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
+else
+  SHA1_HEADER=<openssl/sha.h>
+  LIBS += -lcrypto
+endif
+endif
+
+CFLAGS += '-DSHA1_HEADER=$(SHA1_HEADER)'
+
+$(LIB_FILE): $(LIB_OBJS)
+       $(AR) rcs $@ $(LIB_OBJS)
+
+check:
+       for i in *.c; do sparse $(CFLAGS) $(SPARSE_FLAGS) $$i; done
+
+test-date: test-date.c date.o
+       $(CC) $(CFLAGS) -o $@ test-date.c date.o
+
+test-delta: test-delta.c diff-delta.o patch-delta.o
+       $(CC) $(CFLAGS) -o $@ $^
+
+git-%: %.c $(LIB_FILE)
+       $(CC) $(CFLAGS) -o $@ $(filter %.c,$^) $(LIBS)
+
+git-update-cache: update-cache.c
+git-diff-files: diff-files.c
+git-init-db: init-db.c
+git-write-tree: write-tree.c
+git-read-tree: read-tree.c
+git-commit-tree: commit-tree.c
+git-cat-file: cat-file.c
+git-fsck-cache: fsck-cache.c
+git-checkout-cache: checkout-cache.c
+git-diff-tree: diff-tree.c
+git-rev-tree: rev-tree.c
+git-ls-files: ls-files.c
+git-check-files: check-files.c
+git-ls-tree: ls-tree.c
+git-merge-base: merge-base.c
+git-merge-cache: merge-cache.c
+git-unpack-file: unpack-file.c
+git-export: export.c
+git-diff-cache: diff-cache.c
+git-convert-cache: convert-cache.c
+git-http-pull: http-pull.c pull.c
+git-local-pull: local-pull.c pull.c
+git-ssh-push: rsh.c
+git-ssh-pull: rsh.c pull.c
+git-rev-list: rev-list.c
+git-mktag: mktag.c
+git-diff-helper: diff-helper.c
+git-tar-tree: tar-tree.c
+git-write-blob: write-blob.c
+git-stripspace: stripspace.c
+git-diff-stages: diff-stages.c
+git-rev-parse: rev-parse.c
+git-patch-id: patch-id.c
+git-pack-objects: pack-objects.c
+git-unpack-objects: unpack-objects.c
+git-verify-pack: verify-pack.c
+git-receive-pack: receive-pack.c
+git-send-pack: send-pack.c
+git-prune-packed: prune-packed.c
+git-fetch-pack: fetch-pack.c
+
+git-http-pull: LIBS += -lcurl
+git-rev-list: LIBS += -lssl
+
+# Library objects..
+blob.o: $(LIB_H)
+tree.o: $(LIB_H)
+commit.o: $(LIB_H)
+tag.o: $(LIB_H)
+object.o: $(LIB_H)
+read-cache.o: $(LIB_H)
+sha1_file.o: $(LIB_H)
+usage.o: $(LIB_H)
+strbuf.o: $(LIB_H)
+gitenv.o: $(LIB_H)
+entry.o: $(LIB_H)
+diff.o: $(LIB_H) diffcore.h
+diffcore-rename.o : $(LIB_H) diffcore.h
+diffcore-pathspec.o : $(LIB_H) diffcore.h
+diffcore-pickaxe.o : $(LIB_H) diffcore.h
+diffcore-break.o : $(LIB_H) diffcore.h
+diffcore-order.o : $(LIB_H) diffcore.h
+epoch.o: $(LIB_H)
+
+test: all
+       $(MAKE) -C t/ all
+
+clean:
+       rm -f *.o mozilla-sha1/*.o ppc/*.o $(PROG) $(LIB_FILE)
+       $(MAKE) -C Documentation/ clean
+
+backup: clean
+       cd .. ; tar czvf dircache.tar.gz dir-cache
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..1b8d9b8
--- /dev/null
+++ b/README
@@ -0,0 +1,482 @@
+////////////////////////////////////////////////////////////////
+
+       GIT - the stupid content tracker
+
+////////////////////////////////////////////////////////////////
+"git" can mean anything, depending on your mood.
+
+ - random three-letter combination that is pronounceable, and not
+   actually used by any common UNIX command.  The fact that it is a
+   mispronunciation of "get" may or may not be relevant.
+ - stupid. contemptible and despicable. simple. Take your pick from the
+   dictionary of slang.
+ - "global information tracker": you're in a good mood, and it actually
+   works for you. Angels sing, and a light suddenly fills the room. 
+ - "goddamn idiotic truckload of sh*t": when it breaks
+
+This is a stupid (but extremely fast) directory content manager.  It
+doesn't do a whole lot, but what it _does_ do is track directory
+contents efficiently. 
+
+There are two object abstractions: the "object database", and the
+"current directory cache" aka "index".
+
+The Object Database
+~~~~~~~~~~~~~~~~~~~
+The object database is literally just a content-addressable collection
+of objects.  All objects are named by their content, which is
+approximated by the SHA1 hash of the object itself.  Objects may refer
+to other objects (by referencing their SHA1 hash), and so you can
+build up a hierarchy of objects.
+
+All objects have a statically determined "type" aka "tag", which is
+determined at object creation time, and which identifies the format of
+the object (i.e. how it is used, and how it can refer to other
+objects).  There are currently four different object types: "blob",
+"tree", "commit" and "tag".
+
+A "blob" object cannot refer to any other object, and is, like the tag
+implies, a pure storage object containing some user data.  It is used to
+actually store the file data, i.e. a blob object is associated with some
+particular version of some file. 
+
+A "tree" object is an object that ties one or more "blob" objects into a
+directory structure. In addition, a tree object can refer to other tree
+objects, thus creating a directory hierarchy. 
+
+A "commit" object ties such directory hierarchies together into
+a DAG of revisions - each "commit" is associated with exactly one tree
+(the directory hierarchy at the time of the commit). In addition, a
+"commit" refers to one or more "parent" commit objects that describe the
+history of how we arrived at that directory hierarchy.
+
+As a special case, a commit object with no parents is called the "root"
+object, and is the point of an initial project commit.  Each project
+must have at least one root, and while you can tie several different
+root objects together into one project by creating a commit object which
+has two or more separate roots as its ultimate parents, that's probably
+just going to confuse people.  So aim for the notion of "one root object
+per project", even if git itself does not enforce that. 
+
+A "tag" object symbolically identifies and can be used to sign other
+objects. It contains the identifier and type of another object, a
+symbolic name (of course!) and, optionally, a signature.
+
+Regardless of object type, all objects share the following
+characteristics: they are all deflated with zlib, and have a header
+that not only specifies their tag, but also provides size information
+about the data in the object.  It's worth noting that the SHA1 hash
+that is used to name the object is the hash of the original data.
+(Historical note: in the dawn of the age of git the hash
+was the sha1 of the _compressed_ object)
+
+As a result, the general consistency of an object can always be tested
+independently of the contents or the type of the object: all objects can
+be validated by verifying that (a) their hashes match the content of the
+file and (b) the object successfully inflates to a stream of bytes that
+forms a sequence of <ascii tag without space> + <space> + <ascii decimal
+size> + <byte\0> + <binary object data>. 
+
+The structured objects can further have their structure and
+connectivity to other objects verified. This is generally done with
+the "git-fsck-cache" program, which generates a full dependency graph
+of all objects, and verifies their internal consistency (in addition
+to just verifying their superficial consistency through the hash).
+
+The object types in some more detail:
+
+Blob Object
+~~~~~~~~~~~
+A "blob" object is nothing but a binary blob of data, and doesn't
+refer to anything else.  There is no signature or any other
+verification of the data, so while the object is consistent (it _is_
+indexed by its sha1 hash, so the data itself is certainly correct), it
+has absolutely no other attributes.  No name associations, no
+permissions.  It is purely a blob of data (i.e. normally "file
+contents").
+
+In particular, since the blob is entirely defined by its data, if two
+files in a directory tree (or in multiple different versions of the
+repository) have the same contents, they will share the same blob
+object. The object is totally independent of it's location in the
+directory tree, and renaming a file does not change the object that
+file is associated with in any way.
+
+A blob is created with link:git-write-blob.html[git-write-blob] and
+it's data can be accessed by link:git-cat-file.html[git-cat-file]
+
+Tree Object
+~~~~~~~~~~~
+The next hierarchical object type is the "tree" object.  A tree object
+is a list of mode/name/blob data, sorted by name.  Alternatively, the
+mode data may specify a directory mode, in which case instead of
+naming a blob, that name is associated with another TREE object.
+
+Like the "blob" object, a tree object is uniquely determined by the
+set contents, and so two separate but identical trees will always
+share the exact same object. This is true at all levels, i.e. it's
+true for a "leaf" tree (which does not refer to any other trees, only
+blobs) as well as for a whole subdirectory.
+
+For that reason a "tree" object is just a pure data abstraction: it
+has no history, no signatures, no verification of validity, except
+that since the contents are again protected by the hash itself, we can
+trust that the tree is immutable and its contents never change.
+
+So you can trust the contents of a tree to be valid, the same way you
+can trust the contents of a blob, but you don't know where those
+contents _came_ from.
+
+Side note on trees: since a "tree" object is a sorted list of
+"filename+content", you can create a diff between two trees without
+actually having to unpack two trees.  Just ignore all common parts,
+and your diff will look right.  In other words, you can effectively
+(and efficiently) tell the difference between any two random trees by
+O(n) where "n" is the size of the difference, rather than the size of
+the tree.
+
+Side note 2 on trees: since the name of a "blob" depends entirely and
+exclusively on its contents (i.e. there are no names or permissions
+involved), you can see trivial renames or permission changes by
+noticing that the blob stayed the same.  However, renames with data
+changes need a smarter "diff" implementation.
+
+A tree is created with link:git-write-tree.html[git-write-tree] and
+it's data can be accessed by link:git-ls-tree.html[git-ls-tree]
+
+Commit Object
+~~~~~~~~~~~~~
+The "commit" object is an object that introduces the notion of
+history into the picture.  In contrast to the other objects, it
+doesn't just describe the physical state of a tree, it describes how
+we got there, and why.
+
+A "commit" is defined by the tree-object that it results in, the
+parent commits (zero, one or more) that led up to that point, and a
+comment on what happened.  Again, a commit is not trusted per se:
+the contents are well-defined and "safe" due to the cryptographically
+strong signatures at all levels, but there is no reason to believe
+that the tree is "good" or that the merge information makes sense.
+The parents do not have to actually have any relationship with the
+result, for example.
+
+Note on commits: unlike real SCM's, commits do not contain
+rename information or file mode chane information.  All of that is
+implicit in the trees involved (the result tree, and the result trees
+of the parents), and describing that makes no sense in this idiotic
+file manager.
+
+A commit is created with link:git-commit-tree.html[git-commit-tree] and
+it's data can be accessed by link:git-cat-file.html[git-cat-file]
+
+Trust
+~~~~~
+An aside on the notion of "trust". Trust is really outside the scope
+of "git", but it's worth noting a few things.  First off, since
+everything is hashed with SHA1, you _can_ trust that an object is
+intact and has not been messed with by external sources.  So the name
+of an object uniquely identifies a known state - just not a state that
+you may want to trust.
+
+Furthermore, since the SHA1 signature of a commit refers to the
+SHA1 signatures of the tree it is associated with and the signatures
+of the parent, a single named commit specifies uniquely a whole set
+of history, with full contents.  You can't later fake any step of the
+way once you have the name of a commit.
+
+So to introduce some real trust in the system, the only thing you need
+to do is to digitally sign just _one_ special note, which includes the
+name of a top-level commit.  Your digital signature shows others
+that you trust that commit, and the immutability of the history of
+commits tells others that they can trust the whole history.
+
+In other words, you can easily validate a whole archive by just
+sending out a single email that tells the people the name (SHA1 hash)
+of the top commit, and digitally sign that email using something
+like GPG/PGP.
+
+To assist in this, git also provides the tag object...
+
+Tag Object
+~~~~~~~~~~
+Git provides the "tag" object to simplify creating, managing and
+exchanging symbolic and signed tokens.  The "tag" object at its
+simplest simply symbolically identifies another object by containing
+the sha1, type and symbolic name.
+
+However it can optionally contain additional signature information
+(which git doesn't care about as long as there's less than 8k of
+it). This can then be verified externally to git.
+
+Note that despite the tag features, "git" itself only handles content
+integrity; the trust framework (and signature provision and
+verification) has to come from outside.
+
+A tag is created with link:git-mktag.html[git-mktag] and
+it's data can be accessed by link:git-cat-file.html[git-cat-file]
+
+
+The "index" aka "Current Directory Cache"
+-----------------------------------------
+The index is a simple binary file, which contains an efficient
+representation of a virtual directory content at some random time.  It
+does so by a simple array that associates a set of names, dates,
+permissions and content (aka "blob") objects together.  The cache is
+always kept ordered by name, and names are unique (with a few very
+specific rules) at any point in time, but the cache has no long-term
+meaning, and can be partially updated at any time.
+
+In particular, the index certainly does not need to be consistent with
+the current directory contents (in fact, most operations will depend on
+different ways to make the index _not_ be consistent with the directory
+hierarchy), but it has three very important attributes:
+
+'(a) it can re-generate the full state it caches (not just the
+directory structure: it contains pointers to the "blob" objects so
+that it can regenerate the data too)'
+
+As a special case, there is a clear and unambiguous one-way mapping
+from a current directory cache to a "tree object", which can be
+efficiently created from just the current directory cache without
+actually looking at any other data.  So a directory cache at any one
+time uniquely specifies one and only one "tree" object (but has
+additional data to make it easy to match up that tree object with what
+has happened in the directory)
+
+'(b) it has efficient methods for finding inconsistencies between that
+cached state ("tree object waiting to be instantiated") and the
+current state.'
+
+'(c) it can additionally efficiently represent information about merge
+conflicts between different tree objects, allowing each pathname to be
+associated with sufficient information about the trees involved that
+you can create a three-way merge between them.'
+
+Those are the three ONLY things that the directory cache does.  It's a
+cache, and the normal operation is to re-generate it completely from a
+known tree object, or update/compare it with a live tree that is being
+developed.  If you blow the directory cache away entirely, you generally
+haven't lost any information as long as you have the name of the tree
+that it described. 
+
+At the same time, the directory index is at the same time also the
+staging area for creating new trees, and creating a new tree always
+involves a controlled modification of the index file.  In particular,
+the index file can have the representation of an intermediate tree that
+has not yet been instantiated.  So the index can be thought of as a
+write-back cache, which can contain dirty information that has not yet
+been written back to the backing store.
+
+
+
+The Workflow
+------------
+Generally, all "git" operations work on the index file. Some operations
+work *purely* on the index file (showing the current state of the
+index), but most operations move data to and from the index file. Either
+from the database or from the working directory. Thus there are four
+main combinations: 
+
+1) working directory -> index
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You update the index with information from the working directory with
+the link:git-update-cache.html[git-update-cache] command.  You
+generally update the index information by just specifying the filename
+you want to update, like so:
+
+       git-update-cache filename
+
+but to avoid common mistakes with filename globbing etc, the command
+will not normally add totally new entries or remove old entries,
+i.e. it will normally just update existing cache entries.
+
+To tell git that yes, you really do realize that certain files no
+longer exist in the archive, or that new files should be added, you
+should use the "--remove" and "--add" flags respectively.
+
+NOTE! A "--remove" flag does _not_ mean that subsequent filenames will
+necessarily be removed: if the files still exist in your directory
+structure, the index will be updated with their new status, not
+removed. The only thing "--remove" means is that update-cache will be
+considering a removed file to be a valid thing, and if the file really
+does not exist any more, it will update the index accordingly.
+
+As a special case, you can also do "git-update-cache --refresh", which
+will refresh the "stat" information of each index to match the current
+stat information. It will _not_ update the object status itself, and
+it will only update the fields that are used to quickly test whether
+an object still matches its old backing store object.
+
+2) index -> object database
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You write your current index file to a "tree" object with the program
+
+       git-write-tree
+
+that doesn't come with any options - it will just write out the
+current index into the set of tree objects that describe that state,
+and it will return the name of the resulting top-level tree. You can
+use that tree to re-generate the index at any time by going in the
+other direction:
+
+3) object database -> index
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You read a "tree" file from the object database, and use that to
+populate (and overwrite - don't do this if your index contains any
+unsaved state that you might want to restore later!) your current
+index.  Normal operation is just
+
+               git-read-tree <sha1 of tree>
+
+and your index file will now be equivalent to the tree that you saved
+earlier. However, that is only your _index_ file: your working
+directory contents have not been modified.
+
+4) index -> working directory
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+You update your working directory from the index by "checking out"
+files. This is not a very common operation, since normally you'd just
+keep your files updated, and rather than write to your working
+directory, you'd tell the index files about the changes in your
+working directory (i.e. "git-update-cache").
+
+However, if you decide to jump to a new version, or check out somebody
+else's version, or just restore a previous tree, you'd populate your
+index file with read-tree, and then you need to check out the result
+with
+               git-checkout-cache filename
+
+or, if you want to check out all of the index, use "-a".
+
+NOTE! git-checkout-cache normally refuses to overwrite old files, so
+if you have an old version of the tree already checked out, you will
+need to use the "-f" flag (_before_ the "-a" flag or the filename) to
+_force_ the checkout.
+
+
+Finally, there are a few odds and ends which are not purely moving
+from one representation to the other:
+
+5) Tying it all together
+~~~~~~~~~~~~~~~~~~~~~~~~
+To commit a tree you have instantiated with "git-write-tree", you'd
+create a "commit" object that refers to that tree and the history
+behind it - most notably the "parent" commits that preceded it in
+history.
+
+Normally a "commit" has one parent: the previous state of the tree
+before a certain change was made. However, sometimes it can have two
+or more parent commits, in which case we call it a "merge", due to the
+fact that such a commit brings together ("merges") two or more
+previous states represented by other commits.
+
+In other words, while a "tree" represents a particular directory state
+of a working directory, a "commit" represents that state in "time",
+and explains how we got there.
+
+You create a commit object by giving it the tree that describes the
+state at the time of the commit, and a list of parents:
+
+       git-commit-tree <tree> -p <parent> [-p <parent2> ..]
+
+and then giving the reason for the commit on stdin (either through
+redirection from a pipe or file, or by just typing it at the tty).
+
+git-commit-tree will return the name of the object that represents
+that commit, and you should save it away for later use. Normally,
+you'd commit a new "HEAD" state, and while git doesn't care where you
+save the note about that state, in practice we tend to just write the
+result to the file ".git/HEAD", so that we can always see what the
+last committed state was.
+
+6) Examining the data
+~~~~~~~~~~~~~~~~~~~~~
+
+You can examine the data represented in the object database and the
+index with various helper tools. For every object, you can use
+link:git-cat-file.html[git-cat-file] to examine details about the
+object:
+
+               git-cat-file -t <objectname>
+
+shows the type of the object, and once you have the type (which is
+usually implicit in where you find the object), you can use
+
+               git-cat-file blob|tree|commit <objectname>
+
+to show its contents. NOTE! Trees have binary content, and as a result
+there is a special helper for showing that content, called
+"git-ls-tree", which turns the binary content into a more easily
+readable form.
+
+It's especially instructive to look at "commit" objects, since those
+tend to be small and fairly self-explanatory. In particular, if you
+follow the convention of having the top commit name in ".git/HEAD",
+you can do
+
+               git-cat-file commit $(cat .git/HEAD)
+
+to see what the top commit was.
+
+7) Merging multiple trees
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Git helps you do a three-way merge, which you can expand to n-way by
+repeating the merge procedure arbitrary times until you finally
+"commit" the state.  The normal situation is that you'd only do one
+three-way merge (two parents), and commit it, but if you like to, you
+can do multiple parents in one go.
+
+To do a three-way merge, you need the two sets of "commit" objects
+that you want to merge, use those to find the closest common parent (a
+third "commit" object), and then use those commit objects to find the
+state of the directory ("tree" object) at these points.
+
+To get the "base" for the merge, you first look up the common parent
+of two commits with
+
+               git-merge-base <commit1> <commit2>
+
+which will return you the commit they are both based on.  You should
+now look up the "tree" objects of those commits, which you can easily
+do with (for example)
+
+               git-cat-file commit <commitname> | head -1
+
+since the tree object information is always the first line in a commit
+object.
+
+Once you know the three trees you are going to merge (the one
+"original" tree, aka the common case, and the two "result" trees, aka
+the branches you want to merge), you do a "merge" read into the
+index. This will throw away your old index contents, so you should
+make sure that you've committed those - in fact you would normally
+always do a merge against your last commit (which should thus match
+what you have in your current index anyway).
+
+To do the merge, do
+
+               git-read-tree -m <origtree> <target1tree> <target2tree>
+
+which will do all trivial merge operations for you directly in the
+index file, and you can just write the result out with
+"git-write-tree".
+
+NOTE! Because the merge is done in the index file, and not in your
+working directory, your working directory will no longer match your
+index. You can use "git-checkout-cache -f -a" to make the effect of
+the merge be seen in your working directory.
+
+NOTE2! Sadly, many merges aren't trivial. If there are files that have
+been added.moved or removed, or if both branches have modified the
+same file, you will be left with an index tree that contains "merge
+entries" in it. Such an index tree can _NOT_ be written out to a tree
+object, and you will have to resolve any such merge clashes using
+other tools before you can write out the result.
+
+
+[ fixme: talk about resolving merges here ]
diff --git a/apply.c b/apply.c
new file mode 100644 (file)
index 0000000..701c01a
--- /dev/null
+++ b/apply.c
@@ -0,0 +1,1454 @@
+/*
+ * apply.c
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ *
+ * This applies patches on top of some (arbitrary) version of the SCM.
+ *
+ * NOTE! It does all its work in the index file, and only cares about
+ * the files in the working directory if you tell it to "merge" the
+ * patch apply.
+ *
+ * Even when merging it always takes the source from the index, and
+ * uses the working tree as a "branch" for a 3-way merge.
+ */
+#include <ctype.h>
+
+#include "cache.h"
+
+// We default to the merge behaviour, since that's what most people would
+// expect.
+//
+//  --check turns on checking that the working tree matches the
+//    files that are being modified, but doesn't apply the patch
+//  --stat does just a diffstat, and doesn't actually apply
+//  --show-files shows the directory changes
+//
+static int merge_patch = 1;
+static int check_index = 0;
+static int write_index = 0;
+static int diffstat = 0;
+static int summary = 0;
+static int check = 0;
+static int apply = 1;
+static int show_files = 0;
+static const char apply_usage[] = "git-apply [--stat] [--summary] [--check] [--show-files] <patch>";
+
+/*
+ * For "diff-stat" like behaviour, we keep track of the biggest change
+ * we've seen, and the longest filename. That allows us to do simple
+ * scaling.
+ */
+static int max_change, max_len;
+
+/*
+ * Various "current state", notably line numbers and what
+ * file (and how) we're patching right now.. The "is_xxxx"
+ * things are flags, where -1 means "don't know yet".
+ */
+static int linenr = 1;
+
+struct fragment {
+       unsigned long oldpos, oldlines;
+       unsigned long newpos, newlines;
+       const char *patch;
+       int size;
+       struct fragment *next;
+};
+
+struct patch {
+       char *new_name, *old_name, *def_name;
+       unsigned int old_mode, new_mode;
+       int is_rename, is_copy, is_new, is_delete;
+       int lines_added, lines_deleted;
+       int score;
+       struct fragment *fragments;
+       char *result;
+       unsigned long resultsize;
+       struct patch *next;
+};
+
+#define CHUNKSIZE (8192)
+#define SLOP (16)
+
+static void *read_patch_file(int fd, unsigned long *sizep)
+{
+       unsigned long size = 0, alloc = CHUNKSIZE;
+       void *buffer = xmalloc(alloc);
+
+       for (;;) {
+               int nr = alloc - size;
+               if (nr < 1024) {
+                       alloc += CHUNKSIZE;
+                       buffer = xrealloc(buffer, alloc);
+                       nr = alloc - size;
+               }
+               nr = read(fd, buffer + size, nr);
+               if (!nr)
+                       break;
+               if (nr < 0) {
+                       if (errno == EAGAIN)
+                               continue;
+                       die("git-apply: read returned %s", strerror(errno));
+               }
+               size += nr;
+       }
+       *sizep = size;
+
+       /*
+        * Make sure that we have some slop in the buffer
+        * so that we can do speculative "memcmp" etc, and
+        * see to it that it is NUL-filled.
+        */
+       if (alloc < size + SLOP)
+               buffer = xrealloc(buffer, size + SLOP);
+       memset(buffer + size, 0, SLOP);
+       return buffer;
+}
+
+static unsigned long linelen(const char *buffer, unsigned long size)
+{
+       unsigned long len = 0;
+       while (size--) {
+               len++;
+               if (*buffer++ == '\n')
+                       break;
+       }
+       return len;
+}
+
+static int is_dev_null(const char *str)
+{
+       return !memcmp("/dev/null", str, 9) && isspace(str[9]);
+}
+
+#define TERM_SPACE     1
+#define TERM_TAB       2
+
+static int name_terminate(const char *name, int namelen, int c, int terminate)
+{
+       if (c == ' ' && !(terminate & TERM_SPACE))
+               return 0;
+       if (c == '\t' && !(terminate & TERM_TAB))
+               return 0;
+
+       return 1;
+}
+
+static char * find_name(const char *line, char *def, int p_value, int terminate)
+{
+       int len;
+       const char *start = line;
+       char *name;
+
+       for (;;) {
+               char c = *line;
+
+               if (isspace(c)) {
+                       if (c == '\n')
+                               break;
+                       if (name_terminate(start, line-start, c, terminate))
+                               break;
+               }
+               line++;
+               if (c == '/' && !--p_value)
+                       start = line;
+       }
+       if (!start)
+               return def;
+       len = line - start;
+       if (!len)
+               return def;
+
+       /*
+        * Generally we prefer the shorter name, especially
+        * if the other one is just a variation of that with
+        * something else tacked on to the end (ie "file.orig"
+        * or "file~").
+        */
+       if (def) {
+               int deflen = strlen(def);
+               if (deflen < len && !strncmp(start, def, deflen))
+                       return def;
+       }
+
+       name = xmalloc(len + 1);
+       memcpy(name, start, len);
+       name[len] = 0;
+       free(def);
+       return name;
+}
+
+/*
+ * Get the name etc info from the --/+++ lines of a traditional patch header
+ *
+ * NOTE! This hardcodes "-p1" behaviour in filename detection.
+ *
+ * FIXME! The end-of-filename heuristics are kind of screwy. For existing
+ * files, we can happily check the index for a match, but for creating a
+ * new file we should try to match whatever "patch" does. I have no idea.
+ */
+static void parse_traditional_patch(const char *first, const char *second, struct patch *patch)
+{
+       int p_value = 1;
+       char *name;
+
+       first += 4;     // skip "--- "
+       second += 4;    // skip "+++ "
+       if (is_dev_null(first)) {
+               patch->is_new = 1;
+               patch->is_delete = 0;
+               name = find_name(second, NULL, p_value, TERM_SPACE | TERM_TAB);
+               patch->new_name = name;
+       } else if (is_dev_null(second)) {
+               patch->is_new = 0;
+               patch->is_delete = 1;
+               name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
+               patch->old_name = name;
+       } else {
+               name = find_name(first, NULL, p_value, TERM_SPACE | TERM_TAB);
+               name = find_name(second, name, p_value, TERM_SPACE | TERM_TAB);
+               patch->old_name = patch->new_name = name;
+       }
+       if (!name)
+               die("unable to find filename in patch at line %d", linenr);
+}
+
+static int gitdiff_hdrend(const char *line, struct patch *patch)
+{
+       return -1;
+}
+
+/*
+ * We're anal about diff header consistency, to make
+ * sure that we don't end up having strange ambiguous
+ * patches floating around.
+ *
+ * As a result, gitdiff_{old|new}name() will check
+ * their names against any previous information, just
+ * to make sure..
+ */
+static char *gitdiff_verify_name(const char *line, int isnull, char *orig_name, const char *oldnew)
+{
+       int len;
+       const char *name;
+
+       if (!orig_name && !isnull)
+               return find_name(line, NULL, 1, 0);
+
+       name = "/dev/null";
+       len = 9;
+       if (orig_name) {
+               name = orig_name;
+               len = strlen(name);
+               if (isnull)
+                       die("git-apply: bad git-diff - expected /dev/null, got %s on line %d", name, linenr);
+       }
+
+       if (*name == '/')
+               goto absolute_path;
+
+       for (;;) {
+               char c = *line++;
+               if (c == '\n')
+                       break;
+               if (c != '/')
+                       continue;
+absolute_path:
+               if (memcmp(line, name, len) || line[len] != '\n')
+                       break;
+               return orig_name;
+       }
+       die("git-apply: bad git-diff - inconsistent %s filename on line %d", oldnew, linenr);
+       return NULL;
+}
+
+static int gitdiff_oldname(const char *line, struct patch *patch)
+{
+       patch->old_name = gitdiff_verify_name(line, patch->is_new, patch->old_name, "old");
+       return 0;
+}
+
+static int gitdiff_newname(const char *line, struct patch *patch)
+{
+       patch->new_name = gitdiff_verify_name(line, patch->is_delete, patch->new_name, "new");
+       return 0;
+}
+
+static int gitdiff_oldmode(const char *line, struct patch *patch)
+{
+       patch->old_mode = strtoul(line, NULL, 8);
+       return 0;
+}
+
+static int gitdiff_newmode(const char *line, struct patch *patch)
+{
+       patch->new_mode = strtoul(line, NULL, 8);
+       return 0;
+}
+
+static int gitdiff_delete(const char *line, struct patch *patch)
+{
+       patch->is_delete = 1;
+       patch->old_name = patch->def_name;
+       return gitdiff_oldmode(line, patch);
+}
+
+static int gitdiff_newfile(const char *line, struct patch *patch)
+{
+       patch->is_new = 1;
+       patch->new_name = patch->def_name;
+       return gitdiff_newmode(line, patch);
+}
+
+static int gitdiff_copysrc(const char *line, struct patch *patch)
+{
+       patch->is_copy = 1;
+       patch->old_name = find_name(line, NULL, 0, 0);
+       return 0;
+}
+
+static int gitdiff_copydst(const char *line, struct patch *patch)
+{
+       patch->is_copy = 1;
+       patch->new_name = find_name(line, NULL, 0, 0);
+       return 0;
+}
+
+static int gitdiff_renamesrc(const char *line, struct patch *patch)
+{
+       patch->is_rename = 1;
+       patch->old_name = find_name(line, NULL, 0, 0);
+       return 0;
+}
+
+static int gitdiff_renamedst(const char *line, struct patch *patch)
+{
+       patch->is_rename = 1;
+       patch->new_name = find_name(line, NULL, 0, 0);
+       return 0;
+}
+
+static int gitdiff_similarity(const char *line, struct patch *patch)
+{
+       if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
+               patch->score = 0;
+       return 0;
+}
+
+static int gitdiff_dissimilarity(const char *line, struct patch *patch)
+{
+       if ((patch->score = strtoul(line, NULL, 10)) == ULONG_MAX)
+               patch->score = 0;
+       return 0;
+}
+
+/*
+ * This is normal for a diff that doesn't change anything: we'll fall through
+ * into the next diff. Tell the parser to break out.
+ */
+static int gitdiff_unrecognized(const char *line, struct patch *patch)
+{
+       return -1;
+}
+
+static char *git_header_name(char *line)
+{
+       int len;
+       char *name, *second;
+
+       /*
+        * Find the first '/'
+        */
+       name = line;
+       for (;;) {
+               char c = *name++;
+               if (c == '\n')
+                       return NULL;
+               if (c == '/')
+                       break;
+       }
+
+       /*
+        * We don't accept absolute paths (/dev/null) as possibly valid
+        */
+       if (name == line+1)
+               return NULL;
+
+       /*
+        * Accept a name only if it shows up twice, exactly the same
+        * form.
+        */
+       for (len = 0 ; ; len++) {
+               char c = name[len];
+
+               switch (c) {
+               default:
+                       continue;
+               case '\n':
+                       break;
+               case '\t': case ' ':
+                       second = name+len;
+                       for (;;) {
+                               char c = *second++;
+                               if (c == '\n')
+                                       return NULL;
+                               if (c == '/')
+                                       break;
+                       }
+                       if (second[len] == '\n' && !memcmp(name, second, len)) {
+                               char *ret = xmalloc(len + 1);
+                               memcpy(ret, name, len);
+                               ret[len] = 0;
+                               return ret;
+                       }
+               }
+       }
+       return NULL;
+}
+
+/* Verify that we recognize the lines following a git header */
+static int parse_git_header(char *line, int len, unsigned int size, struct patch *patch)
+{
+       unsigned long offset;
+
+       /* A git diff has explicit new/delete information, so we don't guess */
+       patch->is_new = 0;
+       patch->is_delete = 0;
+
+       /*
+        * Some things may not have the old name in the
+        * rest of the headers anywhere (pure mode changes,
+        * or removing or adding empty files), so we get
+        * the default name from the header.
+        */
+       patch->def_name = git_header_name(line + strlen("diff --git "));
+
+       line += len;
+       size -= len;
+       linenr++;
+       for (offset = len ; size > 0 ; offset += len, size -= len, line += len, linenr++) {
+               static const struct opentry {
+                       const char *str;
+                       int (*fn)(const char *, struct patch *);
+               } optable[] = {
+                       { "@@ -", gitdiff_hdrend },
+                       { "--- ", gitdiff_oldname },
+                       { "+++ ", gitdiff_newname },
+                       { "old mode ", gitdiff_oldmode },
+                       { "new mode ", gitdiff_newmode },
+                       { "deleted file mode ", gitdiff_delete },
+                       { "new file mode ", gitdiff_newfile },
+                       { "copy from ", gitdiff_copysrc },
+                       { "copy to ", gitdiff_copydst },
+                       { "rename old ", gitdiff_renamesrc },
+                       { "rename new ", gitdiff_renamedst },
+                       { "rename from ", gitdiff_renamesrc },
+                       { "rename to ", gitdiff_renamedst },
+                       { "similarity index ", gitdiff_similarity },
+                       { "dissimilarity index ", gitdiff_dissimilarity },
+                       { "", gitdiff_unrecognized },
+               };
+               int i;
+
+               len = linelen(line, size);
+               if (!len || line[len-1] != '\n')
+                       break;
+               for (i = 0; i < sizeof(optable) / sizeof(optable[0]); i++) {
+                       const struct opentry *p = optable + i;
+                       int oplen = strlen(p->str);
+                       if (len < oplen || memcmp(p->str, line, oplen))
+                               continue;
+                       if (p->fn(line + oplen, patch) < 0)
+                               return offset;
+                       break;
+               }
+       }
+
+       return offset;
+}
+
+static int parse_num(const char *line, unsigned long *p)
+{
+       char *ptr;
+
+       if (!isdigit(*line))
+               return 0;
+       *p = strtoul(line, &ptr, 10);
+       return ptr - line;
+}
+
+static int parse_range(const char *line, int len, int offset, const char *expect,
+                       unsigned long *p1, unsigned long *p2)
+{
+       int digits, ex;
+
+       if (offset < 0 || offset >= len)
+               return -1;
+       line += offset;
+       len -= offset;
+
+       digits = parse_num(line, p1);
+       if (!digits)
+               return -1;
+
+       offset += digits;
+       line += digits;
+       len -= digits;
+
+       *p2 = *p1;
+       if (*line == ',') {
+               digits = parse_num(line+1, p2);
+               if (!digits)
+                       return -1;
+
+               offset += digits+1;
+               line += digits+1;
+               len -= digits+1;
+       }
+
+       ex = strlen(expect);
+       if (ex > len)
+               return -1;
+       if (memcmp(line, expect, ex))
+               return -1;
+
+       return offset + ex;
+}
+
+/*
+ * Parse a unified diff fragment header of the
+ * form "@@ -a,b +c,d @@"
+ */
+static int parse_fragment_header(char *line, int len, struct fragment *fragment)
+{
+       int offset;
+
+       if (!len || line[len-1] != '\n')
+               return -1;
+
+       /* Figure out the number of lines in a fragment */
+       offset = parse_range(line, len, 4, " +", &fragment->oldpos, &fragment->oldlines);
+       offset = parse_range(line, len, offset, " @@", &fragment->newpos, &fragment->newlines);
+
+       return offset;
+}
+
+static int find_header(char *line, unsigned long size, int *hdrsize, struct patch *patch)
+{
+       unsigned long offset, len;
+
+       patch->is_rename = patch->is_copy = 0;
+       patch->is_new = patch->is_delete = -1;
+       patch->old_mode = patch->new_mode = 0;
+       patch->old_name = patch->new_name = NULL;
+       for (offset = 0; size > 0; offset += len, size -= len, line += len, linenr++) {
+               unsigned long nextlen;
+
+               len = linelen(line, size);
+               if (!len)
+                       break;
+
+               /* Testing this early allows us to take a few shortcuts.. */
+               if (len < 6)
+                       continue;
+
+               /*
+                * Make sure we don't find any unconnected patch fragmants.
+                * That's a sign that we didn't find a header, and that a
+                * patch has become corrupted/broken up.
+                */
+               if (!memcmp("@@ -", line, 4)) {
+                       struct fragment dummy;
+                       if (parse_fragment_header(line, len, &dummy) < 0)
+                               continue;
+                       error("patch fragment without header at line %d: %.*s", linenr, len-1, line);
+               }
+
+               if (size < len + 6)
+                       break;
+
+               /*
+                * Git patch? It might not have a real patch, just a rename
+                * or mode change, so we handle that specially
+                */
+               if (!memcmp("diff --git ", line, 11)) {
+                       int git_hdr_len = parse_git_header(line, len, size, patch);
+                       if (git_hdr_len <= len)
+                               continue;
+                       if (!patch->old_name && !patch->new_name) {
+                               if (!patch->def_name)
+                                       die("git diff header lacks filename information (line %d)", linenr);
+                               patch->old_name = patch->new_name = patch->def_name;
+                       }
+                       *hdrsize = git_hdr_len;
+                       return offset;
+               }
+
+               /** --- followed by +++ ? */
+               if (memcmp("--- ", line,  4) || memcmp("+++ ", line + len, 4))
+                       continue;
+
+               /*
+                * We only accept unified patches, so we want it to
+                * at least have "@@ -a,b +c,d @@\n", which is 14 chars
+                * minimum
+                */
+               nextlen = linelen(line + len, size - len);
+               if (size < nextlen + 14 || memcmp("@@ -", line + len + nextlen, 4))
+                       continue;
+
+               /* Ok, we'll consider it a patch */
+               parse_traditional_patch(line, line+len, patch);
+               *hdrsize = len + nextlen;
+               linenr += 2;
+               return offset;
+       }
+       return -1;
+}
+
+/*
+ * Parse a unified diff. Note that this really needs
+ * to parse each fragment separately, since the only
+ * way to know the difference between a "---" that is
+ * part of a patch, and a "---" that starts the next
+ * patch is to look at the line counts..
+ */
+static int parse_fragment(char *line, unsigned long size, struct patch *patch, struct fragment *fragment)
+{
+       int added, deleted;
+       int len = linelen(line, size), offset;
+       unsigned long oldlines, newlines;
+
+       offset = parse_fragment_header(line, len, fragment);
+       if (offset < 0)
+               return -1;
+       oldlines = fragment->oldlines;
+       newlines = fragment->newlines;
+
+       if (patch->is_new < 0) {
+               patch->is_new =  !oldlines;
+               if (!oldlines)
+                       patch->old_name = NULL;
+       }
+       if (patch->is_delete < 0) {
+               patch->is_delete = !newlines;
+               if (!newlines)
+                       patch->new_name = NULL;
+       }
+
+       if (patch->is_new != !oldlines)
+               return error("new file depends on old contents");
+       if (patch->is_delete != !newlines) {
+               if (newlines)
+                       return error("deleted file still has contents");
+               fprintf(stderr, "** warning: file %s becomes empty but is not deleted\n", patch->new_name);
+       }
+
+       /* Parse the thing.. */
+       line += len;
+       size -= len;
+       linenr++;
+       added = deleted = 0;
+       for (offset = len; size > 0; offset += len, size -= len, line += len, linenr++) {
+               if (!oldlines && !newlines)
+                       break;
+               len = linelen(line, size);
+               if (!len || line[len-1] != '\n')
+                       return -1;
+               switch (*line) {
+               default:
+                       return -1;
+               case ' ':
+                       oldlines--;
+                       newlines--;
+                       break;
+               case '-':
+                       deleted++;
+                       oldlines--;
+                       break;
+               case '+':
+                       added++;
+                       newlines--;
+                       break;
+               /* We allow "\ No newline at end of file" */
+               case '\\':
+                       if (len < 12 || memcmp(line, "\\ No newline", 12))
+                               return -1;
+                       break;
+               }
+       }
+       patch->lines_added += added;
+       patch->lines_deleted += deleted;
+       return offset;
+}
+
+static int parse_single_patch(char *line, unsigned long size, struct patch *patch)
+{
+       unsigned long offset = 0;
+       struct fragment **fragp = &patch->fragments;
+
+       while (size > 4 && !memcmp(line, "@@ -", 4)) {
+               struct fragment *fragment;
+               int len;
+
+               fragment = xmalloc(sizeof(*fragment));
+               memset(fragment, 0, sizeof(*fragment));
+               len = parse_fragment(line, size, patch, fragment);
+               if (len <= 0)
+                       die("corrupt patch at line %d", linenr);
+
+               fragment->patch = line;
+               fragment->size = len;
+
+               *fragp = fragment;
+               fragp = &fragment->next;
+
+               offset += len;
+               line += len;
+               size -= len;
+       }
+       return offset;
+}
+
+static int parse_chunk(char *buffer, unsigned long size, struct patch *patch)
+{
+       int hdrsize, patchsize;
+       int offset = find_header(buffer, size, &hdrsize, patch);
+
+       if (offset < 0)
+               return offset;
+
+       patchsize = parse_single_patch(buffer + offset + hdrsize, size - offset - hdrsize, patch);
+
+       return offset + hdrsize + patchsize;
+}
+
+static const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+static const char minuses[]= "----------------------------------------------------------------------";
+
+static void show_stats(struct patch *patch)
+{
+       char *name = patch->new_name;
+       int len, max, add, del, total;
+
+       if (!name)
+               name = patch->old_name;
+
+       /*
+        * "scale" the filename
+        */
+       len = strlen(name);
+       max = max_len;
+       if (max > 50)
+               max = 50;
+       if (len > max)
+               name += len - max;
+       len = max;
+
+       /*
+        * scale the add/delete
+        */
+       max = max_change;
+       if (max + len > 70)
+               max = 70 - len;
+
+       add = patch->lines_added;
+       del = patch->lines_deleted;
+       total = add + del;
+
+       if (max_change > 0) {
+               total = (total * max + max_change / 2) / max_change;
+               add = (add * max + max_change / 2) / max_change;
+               del = total - add;
+       }
+       printf(" %-*s |%5d %.*s%.*s\n",
+               len, name, patch->lines_added + patch->lines_deleted,
+               add, pluses, del, minuses);
+}
+
+static int read_old_data(struct stat *st, const char *path, void *buf, unsigned long size)
+{
+       int fd;
+       unsigned long got;
+
+       switch (st->st_mode & S_IFMT) {
+       case S_IFLNK:
+               return readlink(path, buf, size);
+       case S_IFREG:
+               fd = open(path, O_RDONLY);
+               if (fd < 0)
+                       return error("unable to open %s", path);
+               got = 0;
+               for (;;) {
+                       int ret = read(fd, buf + got, size - got);
+                       if (ret < 0) {
+                               if (errno == EAGAIN)
+                                       continue;
+                               break;
+                       }
+                       if (!ret)
+                               break;
+                       got += ret;
+               }
+               close(fd);
+               return got;
+
+       default:
+               return -1;
+       }
+}
+
+static int find_offset(const char *buf, unsigned long size, const char *fragment, unsigned long fragsize, int line)
+{
+       int i;
+       unsigned long start, backwards, forwards;
+
+       if (fragsize > size)
+               return -1;
+
+       start = 0;
+       if (line > 1) {
+               unsigned long offset = 0;
+               i = line-1;
+               while (offset + fragsize <= size) {
+                       if (buf[offset++] == '\n') {
+                               start = offset;
+                               if (!--i)
+                                       break;
+                       }
+               }
+       }
+
+       /* Exact line number? */
+       if (!memcmp(buf + start, fragment, fragsize))
+               return start;
+
+       /*
+        * There's probably some smart way to do this, but I'll leave
+        * that to the smart and beautiful people. I'm simple and stupid.
+        */
+       backwards = start;
+       forwards = start;
+       for (i = 0; ; i++) {
+               unsigned long try;
+               int n;
+
+               /* "backward" */
+               if (i & 1) {
+                       if (!backwards) {
+                               if (forwards + fragsize > size)
+                                       break;
+                               continue;
+                       }
+                       do {
+                               --backwards;
+                       } while (backwards && buf[backwards-1] != '\n');
+                       try = backwards;
+               } else {
+                       while (forwards + fragsize <= size) {
+                               if (buf[forwards++] == '\n')
+                                       break;
+                       }
+                       try = forwards;
+               }
+
+               if (try + fragsize > size)
+                       continue;
+               if (memcmp(buf + try, fragment, fragsize))
+                       continue;
+               n = (i >> 1)+1;
+               if (i & 1)
+                       n = -n;
+               return try;
+       }
+
+       /*
+        * We should start searching forward and backward.
+        */
+       return -1;
+}
+
+struct buffer_desc {
+       char *buffer;
+       unsigned long size;
+       unsigned long alloc;
+};
+
+static int apply_one_fragment(struct buffer_desc *desc, struct fragment *frag)
+{
+       char *buf = desc->buffer;
+       const char *patch = frag->patch;
+       int offset, size = frag->size;
+       char *old = xmalloc(size);
+       char *new = xmalloc(size);
+       int oldsize = 0, newsize = 0;
+
+       while (size > 0) {
+               int len = linelen(patch, size);
+               int plen;
+
+               if (!len)
+                       break;
+
+               /*
+                * "plen" is how much of the line we should use for
+                * the actual patch data. Normally we just remove the
+                * first character on the line, but if the line is
+                * followed by "\ No newline", then we also remove the
+                * last one (which is the newline, of course).
+                */
+               plen = len-1;
+               if (len > size && patch[len] == '\\')
+                       plen--;
+               switch (*patch) {
+               case ' ':
+               case '-':
+                       memcpy(old + oldsize, patch + 1, plen);
+                       oldsize += plen;
+                       if (*patch == '-')
+                               break;
+               /* Fall-through for ' ' */
+               case '+':
+                       memcpy(new + newsize, patch + 1, plen);
+                       newsize += plen;
+                       break;
+               case '@': case '\\':
+                       /* Ignore it, we already handled it */
+                       break;
+               default:
+                       return -1;
+               }
+               patch += len;
+               size -= len;
+       }
+
+       offset = find_offset(buf, desc->size, old, oldsize, frag->newpos);
+       if (offset >= 0) {
+               int diff = newsize - oldsize;
+               unsigned long size = desc->size + diff;
+               unsigned long alloc = desc->alloc;
+
+               if (size > alloc) {
+                       alloc = size + 8192;
+                       desc->alloc = alloc;
+                       buf = xrealloc(buf, alloc);
+                       desc->buffer = buf;
+               }
+               desc->size = size;
+               memmove(buf + offset + newsize, buf + offset + oldsize, size - offset - newsize);
+               memcpy(buf + offset, new, newsize);
+               offset = 0;
+       }
+
+       free(old);
+       free(new);
+       return offset;
+}
+
+static int apply_fragments(struct buffer_desc *desc, struct patch *patch)
+{
+       struct fragment *frag = patch->fragments;
+
+       while (frag) {
+               if (apply_one_fragment(desc, frag) < 0)
+                       return error("patch failed: %s:%d", patch->old_name, frag->oldpos);
+               frag = frag->next;
+       }
+       return 0;
+}
+
+static int apply_data(struct patch *patch, struct stat *st)
+{
+       char *buf;
+       unsigned long size, alloc;
+       struct buffer_desc desc;
+
+       size = 0;
+       alloc = 0;
+       buf = NULL;
+       if (patch->old_name) {
+               size = st->st_size;
+               alloc = size + 8192;
+               buf = xmalloc(alloc);
+               if (read_old_data(st, patch->old_name, buf, alloc) != size)
+                       return error("read of %s failed", patch->old_name);
+       }
+
+       desc.size = size;
+       desc.alloc = alloc;
+       desc.buffer = buf;
+       if (apply_fragments(&desc, patch) < 0)
+               return -1;
+       patch->result = desc.buffer;
+       patch->resultsize = desc.size;
+
+       if (patch->is_delete && patch->resultsize)
+               return error("removal patch leaves file contents");
+
+       return 0;
+}
+
+static int check_patch(struct patch *patch)
+{
+       struct stat st;
+       const char *old_name = patch->old_name;
+       const char *new_name = patch->new_name;
+
+       if (old_name) {
+               int changed;
+
+               if (lstat(old_name, &st) < 0)
+                       return error("%s: %s", old_name, strerror(errno));
+               if (check_index) {
+                       int pos = cache_name_pos(old_name, strlen(old_name));
+                       if (pos < 0)
+                               return error("%s: does not exist in index", old_name);
+                       changed = ce_match_stat(active_cache[pos], &st);
+                       if (changed)
+                               return error("%s: does not match index", old_name);
+               }
+               if (patch->is_new < 0)
+                       patch->is_new = 0;
+               st.st_mode = ntohl(create_ce_mode(st.st_mode));
+               if (!patch->old_mode)
+                       patch->old_mode = st.st_mode;
+               if ((st.st_mode ^ patch->old_mode) & S_IFMT)
+                       return error("%s: wrong type", old_name);
+               if (st.st_mode != patch->old_mode)
+                       fprintf(stderr, "warning: %s has type %o, expected %o\n",
+                               old_name, st.st_mode, patch->old_mode);
+       }
+
+       if (new_name && (patch->is_new | patch->is_rename | patch->is_copy)) {
+               if (check_index && cache_name_pos(new_name, strlen(new_name)) >= 0)
+                       return error("%s: already exists in index", new_name);
+               if (!lstat(new_name, &st))
+                       return error("%s: already exists in working directory", new_name);
+               if (errno != ENOENT)
+                       return error("%s: %s", new_name, strerror(errno));
+               if (!patch->new_mode)
+                       patch->new_mode = S_IFREG | 0644;
+       }
+
+       if (new_name && old_name) {
+               int same = !strcmp(old_name, new_name);
+               if (!patch->new_mode)
+                       patch->new_mode = patch->old_mode;
+               if ((patch->old_mode ^ patch->new_mode) & S_IFMT)
+                       return error("new mode (%o) of %s does not match old mode (%o)%s%s",
+                               patch->new_mode, new_name, patch->old_mode,
+                               same ? "" : " of ", same ? "" : old_name);
+       }       
+
+       if (apply_data(patch, &st) < 0)
+               return error("%s: patch does not apply", old_name);
+       return 0;
+}
+
+static int check_patch_list(struct patch *patch)
+{
+       int error = 0;
+
+       for (;patch ; patch = patch->next)
+               error |= check_patch(patch);
+       return error;
+}
+
+static void show_file(int c, unsigned int mode, const char *name)
+{
+       printf("%c %o %s\n", c, mode, name);
+}
+
+static void show_file_list(struct patch *patch)
+{
+       for (;patch ; patch = patch->next) {
+               if (patch->is_rename) {
+                       show_file('-', patch->old_mode, patch->old_name);
+                       show_file('+', patch->new_mode, patch->new_name);
+                       continue;
+               }
+               if (patch->is_copy || patch->is_new) {
+                       show_file('+', patch->new_mode, patch->new_name);
+                       continue;
+               }
+               if (patch->is_delete) {
+                       show_file('-', patch->old_mode, patch->old_name);
+                       continue;
+               }
+               if (patch->old_mode && patch->new_mode && patch->old_mode != patch->new_mode) {
+                       printf("M %o:%o %s\n", patch->old_mode, patch->new_mode, patch->old_name);
+                       continue;
+               }
+               printf("M %o %s\n", patch->old_mode, patch->old_name);
+       }
+}
+
+static void stat_patch_list(struct patch *patch)
+{
+       int files, adds, dels;
+
+       for (files = adds = dels = 0 ; patch ; patch = patch->next) {
+               files++;
+               adds += patch->lines_added;
+               dels += patch->lines_deleted;
+               show_stats(patch);
+       }
+
+       printf(" %d files changed, %d insertions(+), %d deletions(-)\n", files, adds, dels);
+}
+
+static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name)
+{
+       if (mode)
+               printf(" %s mode %06o %s\n", newdelete, mode, name);
+       else
+               printf(" %s %s\n", newdelete, name);
+}
+
+static void show_mode_change(struct patch *p, int show_name)
+{
+       if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) {
+               if (show_name)
+                       printf(" mode change %06o => %06o %s\n",
+                              p->old_mode, p->new_mode, p->new_name);
+               else
+                       printf(" mode change %06o => %06o\n",
+                              p->old_mode, p->new_mode);
+       }
+}
+
+static void show_rename_copy(struct patch *p)
+{
+       const char *renamecopy = p->is_rename ? "rename" : "copy";
+       const char *old, *new;
+
+       /* Find common prefix */
+       old = p->old_name;
+       new = p->new_name;
+       while (1) {
+               const char *slash_old, *slash_new;
+               slash_old = strchr(old, '/');
+               slash_new = strchr(new, '/');
+               if (!slash_old ||
+                   !slash_new ||
+                   slash_old - old != slash_new - new ||
+                   memcmp(old, new, slash_new - new))
+                       break;
+               old = slash_old + 1;
+               new = slash_new + 1;
+       }
+       /* p->old_name thru old is the common prefix, and old and new
+        * through the end of names are renames
+        */
+       if (old != p->old_name)
+               printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
+                      old - p->old_name, p->old_name,
+                      old, new, p->score);
+       else
+               printf(" %s %s => %s (%d%%)\n", renamecopy,
+                      p->old_name, p->new_name, p->score);
+       show_mode_change(p, 0);
+}
+
+static void summary_patch_list(struct patch *patch)
+{
+       struct patch *p;
+
+       for (p = patch; p; p = p->next) {
+               if (p->is_new)
+                       show_file_mode_name("create", p->new_mode, p->new_name);
+               else if (p->is_delete)
+                       show_file_mode_name("delete", p->old_mode, p->old_name);
+               else {
+                       if (p->is_rename || p->is_copy)
+                               show_rename_copy(p);
+                       else {
+                               if (p->score) {
+                                       printf(" rewrite %s (%d%%)\n",
+                                              p->new_name, p->score);
+                                       show_mode_change(p, 0);
+                               }
+                               else
+                                       show_mode_change(p, 1);
+                       }
+               }
+       }
+}
+
+static void patch_stats(struct patch *patch)
+{
+       int lines = patch->lines_added + patch->lines_deleted;
+
+       if (lines > max_change)
+               max_change = lines;
+       if (patch->old_name) {
+               int len = strlen(patch->old_name);
+               if (len > max_len)
+                       max_len = len;
+       }
+       if (patch->new_name) {
+               int len = strlen(patch->new_name);
+               if (len > max_len)
+                       max_len = len;
+       }
+}
+
+static void remove_file(struct patch *patch)
+{
+       if (write_index) {
+               if (remove_file_from_cache(patch->old_name) < 0)
+                       die("unable to remove %s from index", patch->old_name);
+       }
+       unlink(patch->old_name);
+}
+
+static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)
+{
+       struct stat st;
+       struct cache_entry *ce;
+       int namelen = strlen(path);
+       unsigned ce_size = cache_entry_size(namelen);
+
+       if (!write_index)
+               return;
+
+       ce = xmalloc(ce_size);
+       memset(ce, 0, ce_size);
+       memcpy(ce->name, path, namelen);
+       ce->ce_mode = create_ce_mode(mode);
+       ce->ce_flags = htons(namelen);
+       if (lstat(path, &st) < 0)
+               die("unable to stat newly created file %s", path);
+       fill_stat_cache_info(ce, &st);
+       if (write_sha1_file(buf, size, "blob", ce->sha1) < 0)
+               die("unable to create backing store for newly created file %s", path);
+       if (add_cache_entry(ce, ADD_CACHE_OK_TO_ADD) < 0)
+               die("unable to add cache entry for %s", path);
+}
+
+static void create_subdirectories(const char *path)
+{
+       int len = strlen(path);
+       char *buf = xmalloc(len + 1);
+       const char *slash = path;
+
+       while ((slash = strchr(slash+1, '/')) != NULL) {
+               len = slash - path;
+               memcpy(buf, path, len);
+               buf[len] = 0;
+               if (mkdir(buf, 0755) < 0) {
+                       if (errno != EEXIST)
+                               break;
+               }
+       }
+       free(buf);
+}
+
+/*
+ * We optimistically assume that the directories exist,
+ * which is true 99% of the time anyway. If they don't,
+ * we create them and try again.
+ */
+static int create_regular_file(const char *path, unsigned int mode)
+{
+       int ret = open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+
+       if (ret < 0 && errno == ENOENT) {
+               create_subdirectories(path);
+               ret = open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+       }
+       return ret;
+}
+
+static int create_symlink(const char *buf, const char *path)
+{
+       int ret = symlink(buf, path);
+
+       if (ret < 0 && errno == ENOENT) {
+               create_subdirectories(path);
+               ret = symlink(buf, path);
+       }
+       return ret;
+}
+
+static void create_file(struct patch *patch)
+{
+       const char *path = patch->new_name;
+       unsigned mode = patch->new_mode;
+       unsigned long size = patch->resultsize;
+       char *buf = patch->result;
+
+       if (!mode)
+               mode = S_IFREG | 0644;
+       if (S_ISREG(mode)) {
+               int fd;
+               mode = (mode & 0100) ? 0777 : 0666;
+               fd = create_regular_file(path, mode);
+               if (fd < 0)
+                       die("unable to create file %s (%s)", path, strerror(errno));
+               if (write(fd, buf, size) != size)
+                       die("unable to write file %s", path);
+               close(fd);
+               add_index_file(path, mode, buf, size);
+               return;
+       }
+       if (S_ISLNK(mode)) {
+               if (size && buf[size-1] == '\n')
+                       size--;
+               buf[size] = 0;
+               if (create_symlink(buf, path) < 0)
+                       die("unable to write symlink %s", path);
+               add_index_file(path, mode, buf, size);
+               return;
+       }
+       die("unable to write file mode %o", mode);
+}
+
+static void write_out_one_result(struct patch *patch)
+{
+       if (patch->is_delete > 0) {
+               remove_file(patch);
+               return;
+       }
+       if (patch->is_new > 0 || patch->is_copy) {
+               create_file(patch);
+               return;
+       }
+       /*
+        * Rename or modification boils down to the same
+        * thing: remove the old, write the new
+        */
+       remove_file(patch);
+       create_file(patch);
+}
+
+static void write_out_results(struct patch *list)
+{
+       if (!list)
+               die("No changes");
+
+       while (list) {
+               write_out_one_result(list);
+               list = list->next;
+       }
+}
+
+static struct cache_file cache_file;
+
+static int apply_patch(int fd)
+{
+       int newfd;
+       unsigned long offset, size;
+       char *buffer = read_patch_file(fd, &size);
+       struct patch *list = NULL, **listp = &list;
+
+       if (!buffer)
+               return -1;
+       offset = 0;
+       while (size > 0) {
+               struct patch *patch;
+               int nr;
+
+               patch = xmalloc(sizeof(*patch));
+               memset(patch, 0, sizeof(*patch));
+               nr = parse_chunk(buffer + offset, size, patch);
+               if (nr < 0)
+                       break;
+               patch_stats(patch);
+               *listp = patch;
+               listp = &patch->next;
+               offset += nr;
+               size -= nr;
+       }
+
+       newfd = -1;
+       write_index = check_index && apply;
+       if (write_index)
+               newfd = hold_index_file_for_update(&cache_file, get_index_file());
+       if (check_index) {
+               if (read_cache() < 0)
+                       die("unable to read index file");
+       }
+
+       if ((check || apply) && check_patch_list(list) < 0)
+               exit(1);
+
+       if (apply)
+               write_out_results(list);
+
+       if (write_index) {
+               if (write_cache(newfd, active_cache, active_nr) ||
+                   commit_index_file(&cache_file))
+                       die("Unable to write new cachefile");
+       }
+
+       if (show_files)
+               show_file_list(list);
+
+       if (diffstat)
+               stat_patch_list(list);
+
+       if (summary)
+               summary_patch_list(list);
+
+       free(buffer);
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       int i;
+       int read_stdin = 1;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               int fd;
+
+               if (!strcmp(arg, "-")) {
+                       apply_patch(0);
+                       read_stdin = 0;
+                       continue;
+               }
+               if (!strcmp(arg, "--no-merge")) {
+                       merge_patch = 0;
+                       continue;
+               }
+               if (!strcmp(arg, "--stat")) {
+                       apply = 0;
+                       diffstat = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--summary")) {
+                       apply = 0;
+                       summary = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--check")) {
+                       apply = 0;
+                       check = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--index")) {
+                       check_index = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--apply")) {
+                       apply = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--show-files")) {
+                       show_files = 1;
+                       continue;
+               }
+               fd = open(arg, O_RDONLY);
+               if (fd < 0)
+                       usage(apply_usage);
+               read_stdin = 0;
+               apply_patch(fd);
+               close(fd);
+       }
+       if (read_stdin)
+               apply_patch(0);
+       return 0;
+}
diff --git a/blob.c b/blob.c
new file mode 100644 (file)
index 0000000..ea52ad5
--- /dev/null
+++ b/blob.c
@@ -0,0 +1,52 @@
+#include "blob.h"
+#include "cache.h"
+#include <stdlib.h>
+
+const char *blob_type = "blob";
+
+struct blob *lookup_blob(const unsigned char *sha1)
+{
+       struct object *obj = lookup_object(sha1);
+       if (!obj) {
+               struct blob *ret = xmalloc(sizeof(struct blob));
+               memset(ret, 0, sizeof(struct blob));
+               created_object(sha1, &ret->object);
+               ret->object.type = blob_type;
+               return ret;
+       }
+       if (!obj->type)
+               obj->type = blob_type;
+       if (obj->type != blob_type) {
+               error("Object %s is a %s, not a blob", 
+                     sha1_to_hex(sha1), obj->type);
+               return NULL;
+       }
+       return (struct blob *) obj;
+}
+
+int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size)
+{
+       item->object.parsed = 1;
+       return 0;
+}
+
+int parse_blob(struct blob *item)
+{
+        char type[20];
+        void *buffer;
+        unsigned long size;
+       int ret;
+
+        if (item->object.parsed)
+                return 0;
+        buffer = read_sha1_file(item->object.sha1, type, &size);
+        if (!buffer)
+                return error("Could not read %s",
+                             sha1_to_hex(item->object.sha1));
+        if (strcmp(type, blob_type))
+                return error("Object %s not a blob",
+                             sha1_to_hex(item->object.sha1));
+       ret = parse_blob_buffer(item, buffer, size);
+       free(buffer);
+       return ret;
+}
diff --git a/blob.h b/blob.h
new file mode 100644 (file)
index 0000000..ea5d9e9
--- /dev/null
+++ b/blob.h
@@ -0,0 +1,18 @@
+#ifndef BLOB_H
+#define BLOB_H
+
+#include "object.h"
+
+extern const char *blob_type;
+
+struct blob {
+       struct object object;
+};
+
+struct blob *lookup_blob(const unsigned char *sha1);
+
+int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
+
+int parse_blob(struct blob *item);
+
+#endif /* BLOB_H */
diff --git a/cache.h b/cache.h
new file mode 100644 (file)
index 0000000..cc7b6db
--- /dev/null
+++ b/cache.h
@@ -0,0 +1,278 @@
+#ifndef CACHE_H
+#define CACHE_H
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include SHA1_HEADER
+#include <zlib.h>
+
+#if ZLIB_VERNUM < 0x1200
+#define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
+#endif
+
+#ifdef DT_UNKNOWN
+#define DTYPE(de)      ((de)->d_type)
+#else
+#define DT_UNKNOWN     0
+#define DT_DIR         1
+#define DT_REG         2
+#define DT_LNK         3
+#define DTYPE(de)      DT_UNKNOWN
+#endif
+
+#ifdef __GNUC__
+#define NORETURN __attribute__((__noreturn__))
+#else
+#define NORETURN
+#endif
+
+/*
+ * Environment variables transition.
+ * We accept older names for now but warn.
+ */
+extern char *gitenv_bc(const char *);
+#define gitenv(e) (getenv(e) ? : gitenv_bc(e))
+
+/*
+ * Basic data structures for the directory cache
+ */
+
+#define CACHE_SIGNATURE 0x44495243     /* "DIRC" */
+struct cache_header {
+       unsigned int hdr_signature;
+       unsigned int hdr_version;
+       unsigned int hdr_entries;
+};
+
+/*
+ * The "cache_time" is just the low 32 bits of the
+ * time. It doesn't matter if it overflows - we only
+ * check it for equality in the 32 bits we save.
+ */
+struct cache_time {
+       unsigned int sec;
+       unsigned int nsec;
+};
+
+/*
+ * dev/ino/uid/gid/size are also just tracked to the low 32 bits
+ * Again - this is just a (very strong in practice) heuristic that
+ * the inode hasn't changed.
+ *
+ * We save the fields in big-endian order to allow using the
+ * index file over NFS transparently.
+ */
+struct cache_entry {
+       struct cache_time ce_ctime;
+       struct cache_time ce_mtime;
+       unsigned int ce_dev;
+       unsigned int ce_ino;
+       unsigned int ce_mode;
+       unsigned int ce_uid;
+       unsigned int ce_gid;
+       unsigned int ce_size;
+       unsigned char sha1[20];
+       unsigned short ce_flags;
+       char name[0];
+};
+
+#define CE_NAMEMASK  (0x0fff)
+#define CE_STAGEMASK (0x3000)
+#define CE_UPDATE    (0x4000)
+#define CE_STAGESHIFT 12
+
+#define create_ce_flags(len, stage) htons((len) | ((stage) << CE_STAGESHIFT))
+#define ce_namelen(ce) (CE_NAMEMASK & ntohs((ce)->ce_flags))
+#define ce_size(ce) cache_entry_size(ce_namelen(ce))
+#define ce_stage(ce) ((CE_STAGEMASK & ntohs((ce)->ce_flags)) >> CE_STAGESHIFT)
+
+#define ce_permissions(mode) (((mode) & 0100) ? 0755 : 0644)
+static inline unsigned int create_ce_mode(unsigned int mode)
+{
+       if (S_ISLNK(mode))
+               return htonl(S_IFLNK);
+       return htonl(S_IFREG | ce_permissions(mode));
+}
+
+#define cache_entry_size(len) ((offsetof(struct cache_entry,name) + (len) + 8) & ~7)
+
+extern struct cache_entry **active_cache;
+extern unsigned int active_nr, active_alloc, active_cache_changed;
+
+#define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
+#define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
+#define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
+
+extern char *get_object_directory(void);
+extern char *get_refs_directory(void);
+extern char *get_index_file(void);
+
+#define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
+
+#define alloc_nr(x) (((x)+16)*3/2)
+
+/* Initialize and use the cache information */
+extern int read_cache(void);
+extern int write_cache(int newfd, struct cache_entry **cache, int entries);
+extern int cache_name_pos(const char *name, int namelen);
+#define ADD_CACHE_OK_TO_ADD 1          /* Ok to add */
+#define ADD_CACHE_OK_TO_REPLACE 2      /* Ok to replace file/directory */
+#define ADD_CACHE_SKIP_DFCHECK 4       /* Ok to skip DF conflict checks */
+extern int add_cache_entry(struct cache_entry *ce, int option);
+extern int remove_cache_entry_at(int pos);
+extern int remove_file_from_cache(char *path);
+extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
+extern int ce_match_stat(struct cache_entry *ce, struct stat *st);
+extern int index_fd(unsigned char *sha1, int fd, struct stat *st);
+extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
+
+struct cache_file {
+       struct cache_file *next;
+       char lockfile[PATH_MAX];
+};
+extern int hold_index_file_for_update(struct cache_file *, const char *path);
+extern int commit_index_file(struct cache_file *);
+extern void rollback_index_file(struct cache_file *);
+
+#define MTIME_CHANGED  0x0001
+#define CTIME_CHANGED  0x0002
+#define OWNER_CHANGED  0x0004
+#define MODE_CHANGED    0x0008
+#define INODE_CHANGED   0x0010
+#define DATA_CHANGED    0x0020
+#define TYPE_CHANGED    0x0040
+
+/* Return a statically allocated filename matching the sha1 signature */
+extern char *git_path(const char *fmt, ...);
+extern char *sha1_file_name(const unsigned char *sha1);
+
+/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
+extern void * map_sha1_file(const unsigned char *sha1, unsigned long *size);
+extern int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size);
+extern int parse_sha1_header(char *hdr, char *type, unsigned long *sizep);
+extern int sha1_object_info(const unsigned char *, char *, unsigned long *);
+extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
+extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
+extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
+
+extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
+
+/* Read a tree into the cache */
+extern int read_tree(void *buffer, unsigned long size, int stage);
+
+extern int write_sha1_from_fd(const unsigned char *sha1, int fd);
+
+extern int has_sha1_pack(const unsigned char *sha1);
+extern int has_sha1_file(const unsigned char *sha1);
+
+/* Convert to/from hex/sha1 representation */
+extern int get_sha1(const char *str, unsigned char *sha1);
+extern int get_sha1_hex(const char *hex, unsigned char *sha1);
+extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
+
+/* General helper functions */
+extern void usage(const char *err) NORETURN;
+extern void die(const char *err, ...) NORETURN;
+extern int error(const char *err, ...);
+
+extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
+extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
+
+extern void *read_object_with_reference(const unsigned char *sha1,
+                                       const char *required_type,
+                                       unsigned long *size,
+                                       unsigned char *sha1_ret);
+
+const char *show_date(unsigned long time, int timezone);
+void parse_date(char *date, char *buf, int bufsize);
+void datestamp(char *buf, int bufsize);
+
+static inline void *xmalloc(size_t size)
+{
+       void *ret = malloc(size);
+       if (!ret)
+               die("Out of memory, malloc failed");
+       return ret;
+}
+
+static inline void *xrealloc(void *ptr, size_t size)
+{
+       void *ret = realloc(ptr, size);
+       if (!ret)
+               die("Out of memory, realloc failed");
+       return ret;
+}
+
+static inline void *xcalloc(size_t nmemb, size_t size)
+{
+       void *ret = calloc(nmemb, size);
+       if (!ret)
+               die("Out of memory, calloc failed");
+       return ret;
+}
+
+struct checkout {
+       const char *base_dir;
+       int base_dir_len;
+       unsigned force:1,
+                quiet:1,
+                not_new:1,
+                refresh_cache:1;
+};
+
+extern int checkout_entry(struct cache_entry *ce, struct checkout *state);
+
+extern struct alternate_object_database {
+       char *base;
+       char *name;
+} *alt_odb;
+extern void prepare_alt_odb(void);
+
+extern struct packed_git {
+       struct packed_git *next;
+       unsigned long index_size;
+       unsigned long pack_size;
+       unsigned int *index_base;
+       void *pack_base;
+       unsigned int pack_last_used;
+       unsigned int pack_use_cnt;
+       char pack_name[0]; /* something like ".git/objects/pack/xxxxx.pack" */
+} *packed_git;
+
+struct pack_entry {
+       unsigned int offset;
+       unsigned char sha1[20];
+       struct packed_git *p;
+};
+
+extern int git_connect(int fd[2], char *url, const char *prog);
+extern int finish_connect(pid_t pid);
+extern int path_match(const char *path, int nr, char **match);
+extern int get_ack(int fd, unsigned char *result_sha1);
+
+extern void prepare_packed_git(void);
+extern int use_packed_git(struct packed_git *);
+extern void unuse_packed_git(struct packed_git *);
+extern struct packed_git *add_packed_git(char *, int);
+extern int num_packed_objects(const struct packed_git *p);
+extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
+extern int find_pack_entry_one(const unsigned char *, struct pack_entry *, struct packed_git *);
+extern void *unpack_entry_gently(struct pack_entry *, char *, unsigned long *);
+extern void packed_object_info_detail(struct pack_entry *, char *, unsigned long *, unsigned long *, int *, unsigned char *);
+
+#endif /* CACHE_H */
diff --git a/cat-file.c b/cat-file.c
new file mode 100644 (file)
index 0000000..fa0bb72
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+
+int main(int argc, char **argv)
+{
+       unsigned char sha1[20];
+       char type[20];
+       void *buf;
+       unsigned long size;
+
+       if (argc != 3 || get_sha1(argv[2], sha1))
+               usage("git-cat-file [-t | -s | tagname] <sha1>");
+
+       if (!strcmp("-t", argv[1]) || !strcmp("-s", argv[1])) {
+               if (!sha1_object_info(sha1, type,
+                                     argv[1][1] == 's' ? &size : NULL)) {
+                       switch (argv[1][1]) {
+                       case 't':
+                               printf("%s\n", type);
+                               break;
+                       case 's':
+                               printf("%lu\n", size);
+                               break;
+                       }
+                       return 0;
+               }
+               buf = NULL;
+       } else {
+               buf = read_object_with_reference(sha1, argv[1], &size, NULL);
+       }
+
+       if (!buf)
+               die("git-cat-file %s: bad file", argv[2]);
+
+       while (size > 0) {
+               long ret = write(1, buf, size);
+               if (ret < 0) {
+                       if (errno == EAGAIN)
+                               continue;
+                       /* Ignore epipe */
+                       if (errno == EPIPE)
+                               break;
+                       die("git-cat-file: %s", strerror(errno));
+               } else if (!ret) {
+                       die("git-cat-file: disk full?");
+               }
+               size -= ret;
+               buf += ret;
+       }
+       return 0;
+}
diff --git a/check-files.c b/check-files.c
new file mode 100644 (file)
index 0000000..6fd69e7
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * check-files.c
+ *
+ * Check that a set of files are up-to-date in the filesystem or
+ * do not exist. Used to verify a patch target before doing a patch.
+ *
+ * Copyright (C) 2005 Linus Torvalds
+ */
+#include "cache.h"
+
+static void check_file(const char *path)
+{
+       int fd = open(path, O_RDONLY);
+       struct cache_entry *ce;
+       struct stat st;
+       int pos, changed;
+
+       /* Nonexistent is fine */
+       if (fd < 0) {
+               if (errno != ENOENT)
+                       die("%s: %s", path, strerror(errno));
+               return;
+       }
+
+       /* Exists but is not in the cache is not fine */
+       pos = cache_name_pos(path, strlen(path));
+       if (pos < 0)
+               die("preparing to update existing file '%s' not in cache", path);
+       ce = active_cache[pos];
+
+       if (lstat(path, &st) < 0)
+               die("lstat(%s): %s", path, strerror(errno));
+
+       changed = ce_match_stat(ce, &st);
+       if (changed)
+               die("preparing to update file '%s' not uptodate in cache", path);
+}
+
+int main(int argc, char **argv)
+{
+       int i;
+
+       read_cache();
+       for (i = 1; i < argc ; i++)
+               check_file(argv[i]);
+       return 0;
+}
diff --git a/checkout-cache.c b/checkout-cache.c
new file mode 100644 (file)
index 0000000..82ddbe4
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Check-out files from the "current cache directory"
+ *
+ * Copyright (C) 2005 Linus Torvalds
+ *
+ * Careful: order of argument flags does matter. For example,
+ *
+ *     git-checkout-cache -a -f file.c
+ *
+ * Will first check out all files listed in the cache (but not
+ * overwrite any old ones), and then force-checkout "file.c" a
+ * second time (ie that one _will_ overwrite any old contents
+ * with the same filename).
+ *
+ * Also, just doing "git-checkout-cache" does nothing. You probably
+ * meant "git-checkout-cache -a". And if you want to force it, you
+ * want "git-checkout-cache -f -a".
+ *
+ * Intuitiveness is not the goal here. Repeatability is. The
+ * reason for the "no arguments means no work" thing is that
+ * from scripts you are supposed to be able to do things like
+ *
+ *     find . -name '*.h' -print0 | xargs -0 git-checkout-cache -f --
+ *
+ * which will force all existing *.h files to be replaced with
+ * their cached copies. If an empty command line implied "all",
+ * then this would force-refresh everything in the cache, which
+ * was not the point.
+ *
+ * Oh, and the "--" is just a good idea when you know the rest
+ * will be filenames. Just so that you wouldn't have a filename
+ * of "-a" causing problems (not possible in the above example,
+ * but get used to it in scripting!).
+ */
+#include "cache.h"
+
+static struct checkout state = {
+       .base_dir = "",
+       .base_dir_len = 0,
+       .force = 0,
+       .quiet = 0,
+       .not_new = 0,
+       .refresh_cache = 0,
+};
+
+static int checkout_file(const char *name)
+{
+       int pos = cache_name_pos(name, strlen(name));
+       if (pos < 0) {
+               if (!state.quiet) {
+                       pos = -pos - 1;
+                       fprintf(stderr,
+                               "git-checkout-cache: %s is %s.\n",
+                               name,
+                               (pos < active_nr &&
+                                !strcmp(active_cache[pos]->name, name)) ?
+                               "unmerged" : "not in the cache");
+               }
+               return -1;
+       }
+       return checkout_entry(active_cache[pos], &state);
+}
+
+static int checkout_all(void)
+{
+       int i;
+
+       for (i = 0; i < active_nr ; i++) {
+               struct cache_entry *ce = active_cache[i];
+               if (ce_stage(ce))
+                       continue;
+               if (checkout_entry(ce, &state) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+int main(int argc, char **argv)
+{
+       int i, force_filename = 0;
+       struct cache_file cache_file;
+       int newfd = -1;
+
+       if (read_cache() < 0) {
+               die("invalid cache");
+       }
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!force_filename) {
+                       if (!strcmp(arg, "-a")) {
+                               checkout_all();
+                               continue;
+                       }
+                       if (!strcmp(arg, "--")) {
+                               force_filename = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "-f")) {
+                               state.force = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "-q")) {
+                               state.quiet = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "-n")) {
+                               state.not_new = 1;
+                               continue;
+                       }
+                       if (!strcmp(arg, "-u")) {
+                               state.refresh_cache = 1;
+                               if (newfd < 0)
+                                       newfd = hold_index_file_for_update
+                                               (&cache_file,
+                                                get_index_file());
+                               if (newfd < 0)
+                                       die("cannot open index.lock file.");
+                               continue;
+                       }
+                       if (!memcmp(arg, "--prefix=", 9)) {
+                               state.base_dir = arg+9;
+                               state.base_dir_len = strlen(state.base_dir);
+                               continue;
+                       }
+               }
+               if (state.base_dir_len) {
+                       /* when --prefix is specified we do not
+                        * want to update cache.
+                        */
+                       if (state.refresh_cache) {
+                               close(newfd); newfd = -1;
+                               rollback_index_file(&cache_file);
+                       }
+                       state.refresh_cache = 0;
+               }
+               checkout_file(arg);
+       }
+
+       if (0 <= newfd &&
+           (write_cache(newfd, active_cache, active_nr) ||
+            commit_index_file(&cache_file)))
+               die("Unable to write new cachefile");
+       return 0;
+}
diff --git a/clone-pack.c b/clone-pack.c
new file mode 100644 (file)
index 0000000..0337cec
--- /dev/null
@@ -0,0 +1,208 @@
+#include "cache.h"
+#include "refs.h"
+#include "pkt-line.h"
+#include <sys/wait.h>
+
+static const char clone_pack_usage[] = "git-clone-pack [host:]directory [heads]*";
+static const char *exec = "git-upload-pack";
+
+struct ref {
+       struct ref *next;
+       unsigned char sha1[20];
+       char name[0];
+};
+
+struct ref *get_remote_refs(int fd, int nr_match, char **match)
+{
+       struct ref *ref_list = NULL, **next_ref = &ref_list;
+
+       for (;;) {
+               static char line[1000];
+               unsigned char sha1[20];
+               struct ref *ref;
+               char *refname;
+               int len;
+
+               len = packet_read_line(fd, line, sizeof(line));
+               if (!len)
+                       break;
+               if (line[len-1] == '\n')
+                       line[--len] = 0;
+               if (len < 42 || get_sha1_hex(line, sha1))
+                       die("git-fetch-pack: protocol error - expected ref descriptor, got '%s¤'", line);
+               refname = line+41;
+               len = len-40;
+               if (nr_match && !path_match(refname, nr_match, match))
+                       continue;
+               ref = xmalloc(sizeof(struct ref) + len);
+               ref->next = NULL;
+               memcpy(ref->sha1, sha1, 20);
+               memcpy(ref->name, refname, len);
+               *next_ref = ref;
+               next_ref = &ref->next;
+       }
+       return ref_list;
+}
+
+static void clone_handshake(int fd[2], struct ref *ref)
+{
+       unsigned char sha1[20];
+
+       while (ref) {
+               packet_write(fd[1], "want %s\n", sha1_to_hex(ref->sha1));
+               ref = ref->next;
+       }
+       packet_flush(fd[1]);
+
+       /* We don't have nuttin' */
+       packet_write(fd[1], "done\n");
+       if (get_ack(fd[0], sha1))
+               error("Huh! git-clone-pack got positive ack for %s", sha1_to_hex(sha1));
+}
+
+static int is_master(struct ref *ref)
+{
+       return !strcmp(ref->name, "refs/heads/master");
+}
+
+static void write_one_ref(struct ref *ref)
+{
+       char *path = git_path(ref->name);
+       int fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666);
+       char *hex;
+
+       if (fd < 0)
+               die("unable to create ref %s", ref->name);
+       hex = sha1_to_hex(ref->sha1);
+       hex[40] = '\n';
+       if (write(fd, hex, 41) != 41)
+               die("unable to write ref %s", ref->name);
+       close(fd);
+}
+
+static void write_refs(struct ref *ref)
+{
+       struct ref *head = NULL, *head_ptr, *master_ref;
+       char *head_path;
+
+       if (!strcmp(ref->name, "HEAD")) {
+               head = ref;
+               ref = ref->next;
+       }
+       head_ptr = NULL;
+       master_ref = NULL;
+       while (ref) {
+               if (is_master(ref))
+                       master_ref = ref;
+               if (head && !memcmp(ref->sha1, head->sha1, 20)) {
+                       if (!head_ptr || ref == master_ref)
+                               head_ptr = ref;
+               }
+               write_one_ref(ref);
+               ref = ref->next;
+       }
+       if (!head)
+               return;
+
+       head_path = git_path("HEAD");
+       if (!head_ptr) {
+               /*
+                * If we had a master ref, and it wasn't HEAD, we need to undo the
+                * symlink, and write a standalone HEAD. Give a warning, because that's
+                * really really wrong.
+                */
+               if (master_ref) {
+                       error("HEAD doesn't point to any refs! Making standalone HEAD");
+                       unlink(head_path);
+               }
+               write_one_ref(head);
+               return;
+       }
+
+       /* We reset to the master branch if it's available */
+       if (master_ref)
+               return;
+
+       /*
+        * Uhhuh. Other end didn't have master. We start HEAD off with
+        * the first branch with the same value.
+        */
+       unlink(head_path);
+       if (symlink(head_ptr->name, head_path) < 0)
+               die("unable to link HEAD to %s", head_ptr->name);
+}
+
+static int clone_pack(int fd[2], int nr_match, char **match)
+{
+       struct ref *refs;
+       int status;
+       pid_t pid;
+
+       refs = get_remote_refs(fd[0], nr_match, match);
+       if (!refs) {
+               packet_flush(fd[1]);
+               die("no matching remote head");
+       }
+       clone_handshake(fd, refs);
+       pid = fork();
+       if (pid < 0)
+               die("git-clone-pack: unable to fork off git-unpack-objects");
+       if (!pid) {
+               close(fd[1]);
+               dup2(fd[0], 0);
+               close(fd[0]);
+               execlp("git-unpack-objects", "git-unpack-objects", NULL);
+               die("git-unpack-objects exec failed");
+       }
+       close(fd[0]);
+       close(fd[1]);
+       while (waitpid(pid, &status, 0) < 0) {
+               if (errno != EINTR)
+                       die("waiting for git-unpack-objects: %s", strerror(errno));
+       }
+       if (WIFEXITED(status)) {
+               int code = WEXITSTATUS(status);
+               if (code)
+                       die("git-unpack-objects died with error code %d", code);
+               write_refs(refs);
+               return 0;
+       }
+       if (WIFSIGNALED(status)) {
+               int sig = WTERMSIG(status);
+               die("git-unpack-objects died of signal %d", sig);
+       }
+       die("Sherlock Holmes! git-unpack-objects died of unnatural causes %d!", status);
+}
+
+int main(int argc, char **argv)
+{
+       int i, ret, nr_heads;
+       char *dest = NULL, **heads;
+       int fd[2];
+       pid_t pid;
+
+       nr_heads = 0;
+       heads = NULL;
+       for (i = 1; i < argc; i++) {
+               char *arg = argv[i];
+
+               if (*arg == '-') {
+                       /* Arguments go here */
+                       usage(clone_pack_usage);
+               }
+               dest = arg;
+               heads = argv + i + 1;
+               nr_heads = argc - i - 1;
+               break;
+       }
+       if (!dest)
+               usage(clone_pack_usage);
+       pid = git_connect(fd, dest, exec);
+       if (pid < 0)
+               return 1;
+       ret = clone_pack(fd, nr_heads, heads);
+       close(fd[0]);
+       close(fd[1]);
+       finish_connect(pid);
+       return ret;
+}
diff --git a/commit-tree.c b/commit-tree.c
new file mode 100644 (file)
index 0000000..0d504e7
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+#include "cache.h"
+
+#include <pwd.h>
+#include <time.h>
+#include <ctype.h>
+
+#define BLOCKING (1ul << 14)
+
+/*
+ * FIXME! Share the code with "write-tree.c"
+ */
+static void init_buffer(char **bufp, unsigned int *sizep)
+{
+       char *buf = xmalloc(BLOCKING);
+       *sizep = 0;
+       *bufp = buf;
+}
+
+static void add_buffer(char **bufp, unsigned int *sizep, const char *fmt, ...)
+{
+       char one_line[2048];
+       va_list args;
+       int len;
+       unsigned long alloc, size, newsize;
+       char *buf;
+
+       va_start(args, fmt);
+       len = vsnprintf(one_line, sizeof(one_line), fmt, args);
+       va_end(args);
+       size = *sizep;
+       newsize = size + len;
+       alloc = (size + 32767) & ~32767;
+       buf = *bufp;
+       if (newsize > alloc) {
+               alloc = (newsize + 32767) & ~32767;
+               buf = xrealloc(buf, alloc);
+               *bufp = buf;
+       }
+       *sizep = newsize;
+       memcpy(buf + size, one_line, len);
+}
+
+static void remove_special(char *p)
+{
+       char c;
+       char *dst = p, *src = p;
+
+       for (;;) {
+               c = *src;
+               src++;
+               switch(c) {
+               case '\n': case '<': case '>':
+                       continue;
+               }
+               *dst++ = c;
+               if (!c)
+                       break;
+       }
+
+       /*
+        * Go back, and remove crud from the end: some people
+        * have commas etc in their gecos field
+        */
+       dst--;
+       while (--dst >= p) {
+               unsigned char c = *dst;
+               switch (c) {
+               case ',': case ';': case '.':
+                       *dst = 0;
+                       continue;
+               }
+               break;
+       }
+}
+
+static void check_valid(unsigned char *sha1, const char *expect)
+{
+       void *buf;
+       char type[20];
+       unsigned long size;
+
+       buf = read_sha1_file(sha1, type, &size);
+       if (!buf || strcmp(type, expect))
+               die("%s is not a valid '%s' object", sha1_to_hex(sha1), expect);
+       free(buf);
+}
+
+/*
+ * Having more than two parents is not strange at all, and this is
+ * how multi-way merges are represented.
+ */
+#define MAXPARENT (16)
+static unsigned char parent_sha1[MAXPARENT][20];
+
+static char *commit_tree_usage = "git-commit-tree <sha1> [-p <sha1>]* < changelog";
+
+static int new_parent(int idx)
+{
+       int i;
+       unsigned char *sha1 = parent_sha1[idx];
+       for (i = 0; i < idx; i++) {
+               if (!memcmp(parent_sha1[i], sha1, 20)) {
+                       error("duplicate parent %s ignored", sha1_to_hex(sha1));
+                       return 0;
+               }
+       }
+       return 1;
+}
+
+int main(int argc, char **argv)
+{
+       int i, len;
+       int parents = 0;
+       unsigned char tree_sha1[20];
+       unsigned char commit_sha1[20];
+       char *gecos, *realgecos, *commitgecos;
+       char *email, *commitemail, realemail[1000];
+       char date[50], realdate[50];
+       char *audate, *cmdate;
+       char comment[1000];
+       struct passwd *pw;
+       char *buffer;
+       unsigned int size;
+
+       if (argc < 2 || get_sha1_hex(argv[1], tree_sha1) < 0)
+               usage(commit_tree_usage);
+
+       check_valid(tree_sha1, "tree");
+       for (i = 2; i < argc; i += 2) {
+               char *a, *b;
+               a = argv[i]; b = argv[i+1];
+               if (!b || strcmp(a, "-p") || get_sha1(b, parent_sha1[parents]))
+                       usage(commit_tree_usage);
+               check_valid(parent_sha1[parents], "commit");
+               if (new_parent(parents))
+                       parents++;
+       }
+       if (!parents)
+               fprintf(stderr, "Committing initial tree %s\n", argv[1]);
+       pw = getpwuid(getuid());
+       if (!pw)
+               die("You don't exist. Go away!");
+       realgecos = pw->pw_gecos;
+       len = strlen(pw->pw_name);
+       memcpy(realemail, pw->pw_name, len);
+       realemail[len] = '@';
+       gethostname(realemail+len+1, sizeof(realemail)-len-1);
+       if (!strchr(realemail+len+1, '.')) {
+               strcat(realemail, ".");
+               getdomainname(realemail+strlen(realemail), sizeof(realemail)-strlen(realemail)-1);
+       }
+
+       datestamp(realdate, sizeof(realdate));
+       strcpy(date, realdate);
+
+       commitgecos = gitenv("GIT_COMMITTER_NAME") ? : realgecos;
+       commitemail = gitenv("GIT_COMMITTER_EMAIL") ? : realemail;
+       gecos = gitenv("GIT_AUTHOR_NAME") ? : realgecos;
+       email = gitenv("GIT_AUTHOR_EMAIL") ? : realemail;
+       audate = gitenv("GIT_AUTHOR_DATE");
+       if (audate)
+               parse_date(audate, date, sizeof(date));
+       cmdate = gitenv("GIT_COMMITTER_DATE");
+       if (cmdate)
+               parse_date(cmdate, realdate, sizeof(realdate));
+
+       remove_special(gecos); remove_special(realgecos); remove_special(commitgecos);
+       remove_special(email); remove_special(realemail); remove_special(commitemail);
+
+       init_buffer(&buffer, &size);
+       add_buffer(&buffer, &size, "tree %s\n", sha1_to_hex(tree_sha1));
+
+       /*
+        * NOTE! This ordering means that the same exact tree merged with a
+        * different order of parents will be a _different_ changeset even
+        * if everything else stays the same.
+        */
+       for (i = 0; i < parents; i++)
+               add_buffer(&buffer, &size, "parent %s\n", sha1_to_hex(parent_sha1[i]));
+
+       /* Person/date information */
+       add_buffer(&buffer, &size, "author %s <%s> %s\n", gecos, email, date);
+       add_buffer(&buffer, &size, "committer %s <%s> %s\n\n", commitgecos, commitemail, realdate);
+
+       /* And add the comment */
+       while (fgets(comment, sizeof(comment), stdin) != NULL)
+               add_buffer(&buffer, &size, "%s", comment);
+
+       write_sha1_file(buffer, size, "commit", commit_sha1);
+       printf("%s\n", sha1_to_hex(commit_sha1));
+       return 0;
+}
diff --git a/commit.c b/commit.c
new file mode 100644 (file)
index 0000000..03f8ca8
--- /dev/null
+++ b/commit.c
@@ -0,0 +1,348 @@
+#include <ctype.h>
+#include "tag.h"
+#include "commit.h"
+#include "cache.h"
+
+const char *commit_type = "commit";
+
+enum cmit_fmt get_commit_format(const char *arg)
+{
+       if (!*arg)
+               return CMIT_FMT_DEFAULT;
+       if (!strcmp(arg, "=raw"))
+               return CMIT_FMT_RAW;
+       if (!strcmp(arg, "=medium"))
+               return CMIT_FMT_MEDIUM;
+       if (!strcmp(arg, "=short"))
+               return CMIT_FMT_SHORT;
+       if (!strcmp(arg, "=full"))
+               return CMIT_FMT_FULL;
+       die("invalid --pretty format");
+}
+
+static struct commit *check_commit(struct object *obj, const unsigned char *sha1)
+{
+       if (obj->type != commit_type) {
+               error("Object %s is a %s, not a commit", 
+                     sha1_to_hex(sha1), obj->type);
+               return NULL;
+       }
+       return (struct commit *) obj;
+}
+
+struct commit *lookup_commit_reference(const unsigned char *sha1)
+{
+       struct object *obj = parse_object(sha1);
+
+       if (!obj)
+               return NULL;
+       if (obj->type == tag_type)
+               obj = ((struct tag *)obj)->tagged;
+       return check_commit(obj, sha1);
+}
+
+struct commit *lookup_commit(const unsigned char *sha1)
+{
+       struct object *obj = lookup_object(sha1);
+       if (!obj) {
+               struct commit *ret = xmalloc(sizeof(struct commit));
+               memset(ret, 0, sizeof(struct commit));
+               created_object(sha1, &ret->object);
+               ret->object.type = commit_type;
+               return ret;
+       }
+       if (!obj->type)
+               obj->type = commit_type;
+       return check_commit(obj, sha1);
+}
+
+static unsigned long parse_commit_date(const char *buf)
+{
+       unsigned long date;
+
+       if (memcmp(buf, "author", 6))
+               return 0;
+       while (*buf++ != '\n')
+               /* nada */;
+       if (memcmp(buf, "committer", 9))
+               return 0;
+       while (*buf++ != '>')
+               /* nada */;
+       date = strtoul(buf, NULL, 10);
+       if (date == ULONG_MAX)
+               date = 0;
+       return date;
+}
+
+int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size)
+{
+       void *bufptr = buffer;
+       unsigned char parent[20];
+       struct commit_list **pptr;
+
+       if (item->object.parsed)
+               return 0;
+       item->object.parsed = 1;
+       get_sha1_hex(bufptr + 5, parent);
+       item->tree = lookup_tree(parent);
+       if (item->tree)
+               add_ref(&item->object, &item->tree->object);
+       bufptr += 46; /* "tree " + "hex sha1" + "\n" */
+       pptr = &item->parents;
+       while (!memcmp(bufptr, "parent ", 7) &&
+              !get_sha1_hex(bufptr + 7, parent)) {
+               struct commit *new_parent = lookup_commit(parent);
+               if (new_parent) {
+                       pptr = &commit_list_insert(new_parent, pptr)->next;
+                       add_ref(&item->object, &new_parent->object);
+               }
+               bufptr += 48;
+       }
+       item->date = parse_commit_date(bufptr);
+       return 0;
+}
+
+int parse_commit(struct commit *item)
+{
+       char type[20];
+       void *buffer;
+       unsigned long size;
+       int ret;
+
+       if (item->object.parsed)
+               return 0;
+       buffer = read_sha1_file(item->object.sha1, type, &size);
+       if (!buffer)
+               return error("Could not read %s",
+                            sha1_to_hex(item->object.sha1));
+       if (strcmp(type, commit_type)) {
+               free(buffer);
+               return error("Object %s not a commit",
+                            sha1_to_hex(item->object.sha1));
+       }
+       ret = parse_commit_buffer(item, buffer, size);
+       if (!ret) {
+               item->buffer = buffer;
+               return 0;
+       }
+       free(buffer);
+       return ret;
+}
+
+struct commit_list *commit_list_insert(struct commit *item, struct commit_list **list_p)
+{
+       struct commit_list *new_list = xmalloc(sizeof(struct commit_list));
+       new_list->item = item;
+       new_list->next = *list_p;
+       *list_p = new_list;
+       return new_list;
+}
+
+void free_commit_list(struct commit_list *list)
+{
+       while (list) {
+               struct commit_list *temp = list;
+               list = temp->next;
+               free(temp);
+       }
+}
+
+void insert_by_date(struct commit_list **list, struct commit *item)
+{
+       struct commit_list **pp = list;
+       struct commit_list *p;
+       while ((p = *pp) != NULL) {
+               if (p->item->date < item->date) {
+                       break;
+               }
+               pp = &p->next;
+       }
+       commit_list_insert(item, pp);
+}
+
+       
+void sort_by_date(struct commit_list **list)
+{
+       struct commit_list *ret = NULL;
+       while (*list) {
+               insert_by_date(&ret, (*list)->item);
+               *list = (*list)->next;
+       }
+       *list = ret;
+}
+
+struct commit *pop_most_recent_commit(struct commit_list **list,
+                                     unsigned int mark)
+{
+       struct commit *ret = (*list)->item;
+       struct commit_list *parents = ret->parents;
+       struct commit_list *old = *list;
+
+       *list = (*list)->next;
+       free(old);
+
+       while (parents) {
+               struct commit *commit = parents->item;
+               parse_commit(commit);
+               if (!(commit->object.flags & mark)) {
+                       commit->object.flags |= mark;
+                       insert_by_date(list, commit);
+               }
+               parents = parents->next;
+       }
+       return ret;
+}
+
+/*
+ * Generic support for pretty-printing the header
+ */
+static int get_one_line(const char *msg, unsigned long len)
+{
+       int ret = 0;
+
+       while (len--) {
+               char c = *msg++;
+               ret++;
+               if (c == '\n')
+                       break;
+               if (!c)
+                       return 0;
+       }
+       return ret;
+}
+
+static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, const char *line)
+{
+       char *date;
+       unsigned int namelen;
+       unsigned long time;
+       int tz, ret;
+
+       date = strchr(line, '>');
+       if (!date)
+               return 0;
+       namelen = ++date - line;
+       time = strtoul(date, &date, 10);
+       tz = strtol(date, NULL, 10);
+
+       ret = sprintf(buf, "%s: %.*s\n", what, namelen, line);
+       if (fmt == CMIT_FMT_MEDIUM)
+               ret += sprintf(buf + ret, "Date:   %s\n", show_date(time, tz));
+       return ret;
+}
+
+static int is_empty_line(const char *line, int len)
+{
+       while (len && isspace(line[len-1]))
+               len--;
+       return !len;
+}
+
+static int add_parent_info(enum cmit_fmt fmt, char *buf, const char *line, int parents)
+{
+       int offset = 0;
+       switch (parents) {
+       case 1:
+               break;
+       case 2:
+               /* Go back to the previous line: 40 characters of previous parent, and one '\n' */
+               offset = sprintf(buf, "Merge: %.40s\n", line-41);
+               /* Fallthrough */
+       default:
+               /* Replace the previous '\n' with a space */
+               buf[offset-1] = ' ';
+               offset += sprintf(buf + offset, "%.40s\n", line+7);
+       }
+       return offset;
+}
+
+unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space)
+{
+       int hdr = 1, body = 0;
+       unsigned long offset = 0;
+       int parents = 0;
+
+       for (;;) {
+               const char *line = msg;
+               int linelen = get_one_line(msg, len);
+
+               if (!linelen)
+                       break;
+
+               /*
+                * We want some slop for indentation and a possible
+                * final "...". Thus the "+ 20".
+                */
+               if (offset + linelen + 20 > space) {
+                       memcpy(buf + offset, "    ...\n", 8);
+                       offset += 8;
+                       break;
+               }
+
+               msg += linelen;
+               len -= linelen;
+               if (hdr) {
+                       if (linelen == 1) {
+                               hdr = 0;
+                               buf[offset++] = '\n';
+                               continue;
+                       }
+                       if (fmt == CMIT_FMT_RAW) {
+                               memcpy(buf + offset, line, linelen);
+                               offset += linelen;
+                               continue;
+                       }
+                       if (!memcmp(line, "parent ", 7)) {
+                               if (linelen != 48)
+                                       die("bad parent line in commit");
+                               offset += add_parent_info(fmt, buf + offset, line, ++parents);
+                       }
+                       if (!memcmp(line, "author ", 7))
+                               offset += add_user_info("Author", fmt, buf + offset, line + 7);
+                       if (fmt == CMIT_FMT_FULL) {
+                               if (!memcmp(line, "committer ", 10))
+                                       offset += add_user_info("Commit", fmt, buf + offset, line + 10);
+                       }
+                       continue;
+               }
+
+               if (is_empty_line(line, linelen)) {
+                       if (!body)
+                               continue;
+                       if (fmt == CMIT_FMT_SHORT)
+                               break;
+               } else {
+                       body = 1;
+               }
+               memset(buf + offset, ' ', 4);
+               memcpy(buf + offset + 4, line, linelen);
+               offset += linelen + 4;
+       }
+       /* Make sure there is an EOLN */
+       if (buf[offset - 1] != '\n')
+               buf[offset++] = '\n';
+       buf[offset] = '\0';
+       return offset;
+}
+
+struct commit *pop_commit(struct commit_list **stack)
+{
+       struct commit_list *top = *stack;
+       struct commit *item = top ? top->item : NULL;
+
+       if (top) {
+               *stack = top->next;
+               free(top);
+       }
+       return item;
+}
+
+int count_parents(struct commit * commit)
+{
+        int count = 0;
+        struct commit_list * parents = commit->parents;
+        for (count=0;parents; parents=parents->next,count++)
+          ;
+        return count;
+}
+
diff --git a/commit.h b/commit.h
new file mode 100644 (file)
index 0000000..1339231
--- /dev/null
+++ b/commit.h
@@ -0,0 +1,58 @@
+#ifndef COMMIT_H
+#define COMMIT_H
+
+#include "object.h"
+#include "tree.h"
+
+struct commit_list {
+       struct commit *item;
+       struct commit_list *next;
+};
+
+struct commit {
+       struct object object;
+       unsigned long date;
+       struct commit_list *parents;
+       struct tree *tree;
+       char *buffer;
+};
+
+extern const char *commit_type;
+
+struct commit *lookup_commit(const unsigned char *sha1);
+struct commit *lookup_commit_reference(const unsigned char *sha1);
+
+int parse_commit_buffer(struct commit *item, void *buffer, unsigned long size);
+
+int parse_commit(struct commit *item);
+
+struct commit_list * commit_list_insert(struct commit *item, struct commit_list **list_p);
+
+void free_commit_list(struct commit_list *list);
+
+void sort_by_date(struct commit_list **list);
+
+/* Commit formats */
+enum cmit_fmt {
+       CMIT_FMT_RAW,
+       CMIT_FMT_MEDIUM,
+       CMIT_FMT_DEFAULT = CMIT_FMT_MEDIUM,
+       CMIT_FMT_SHORT,
+       CMIT_FMT_FULL,
+};
+
+extern enum cmit_fmt get_commit_format(const char *arg);
+extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space);
+
+void insert_by_date(struct commit_list **list, struct commit *item);
+
+/** Removes the first commit from a list sorted by date, and adds all
+ * of its parents.
+ **/
+struct commit *pop_most_recent_commit(struct commit_list **list, 
+                                     unsigned int mark);
+
+struct commit *pop_commit(struct commit_list **stack);
+
+int count_parents(struct commit * commit);
+#endif /* COMMIT_H */
diff --git a/connect.c b/connect.c
new file mode 100644 (file)
index 0000000..075683e
--- /dev/null
+++ b/connect.c
@@ -0,0 +1,126 @@
+#include "cache.h"
+#include "pkt-line.h"
+#include <sys/wait.h>
+
+int get_ack(int fd, unsigned char *result_sha1)
+{
+       static char line[1000];
+       int len = packet_read_line(fd, line, sizeof(line));
+
+       if (!len)
+               die("git-fetch-pack: expected ACK/NAK, got EOF");
+       if (line[len-1] == '\n')
+               line[--len] = 0;
+       if (!strcmp(line, "NAK"))
+               return 0;
+       if (!strncmp(line, "ACK ", 3)) {
+               if (!get_sha1_hex(line+4, result_sha1))
+                       return 1;
+       }
+       die("git-fetch_pack: expected ACK/NAK, got '%s'", line);
+}
+
+int path_match(const char *path, int nr, char **match)
+{
+       int i;
+       int pathlen = strlen(path);
+
+       for (i = 0; i < nr; i++) {
+               char *s = match[i];
+               int len = strlen(s);
+
+               if (!len || len > pathlen)
+                       continue;
+               if (memcmp(path + pathlen - len, s, len))
+                       continue;
+               if (pathlen > len && path[pathlen - len - 1] != '/')
+                       continue;
+               *s = 0;
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * First, make it shell-safe.  We do this by just disallowing any
+ * special characters. Somebody who cares can do escaping and let
+ * through the rest. But since we're doing to feed this to ssh as
+ * a command line, we're going to be pretty damn anal for now.
+ */
+static char *shell_safe(char *url)
+{
+       char *n = url;
+       unsigned char c;
+       static const char flags[256] = {
+               ['0'...'9'] = 1,
+               ['a'...'z'] = 1,
+               ['A'...'Z'] = 1,
+               ['.'] = 1, ['/'] = 1,
+               ['-'] = 1, ['+'] = 1,
+               [':'] = 1
+       };
+
+       while ((c = *n++) != 0) {
+               if (flags[c] != 1)
+                       die("I don't like '%c'. Sue me.", c);
+       }
+       return url;
+}
+
+/*
+ * Yeah, yeah, fixme. Need to pass in the heads etc.
+ */
+int git_connect(int fd[2], char *url, const char *prog)
+{
+       char command[1024];
+       const char *host, *path;
+       char *colon;
+       int pipefd[2][2];
+       pid_t pid;
+
+       url = shell_safe(url);
+       host = NULL;
+       path = url;
+       colon = strchr(url, ':');
+       if (colon) {
+               *colon = 0;
+               host = url;
+               path = colon+1;
+       }
+       snprintf(command, sizeof(command), "%s %s", prog, path);
+       if (pipe(pipefd[0]) < 0 || pipe(pipefd[1]) < 0)
+               die("unable to create pipe pair for communication");
+       pid = fork();
+       if (!pid) {
+               dup2(pipefd[1][0], 0);
+               dup2(pipefd[0][1], 1);
+               close(pipefd[0][0]);
+               close(pipefd[0][1]);
+               close(pipefd[1][0]);
+               close(pipefd[1][1]);
+               if (host)
+                       execlp("ssh", "ssh", host, command, NULL);
+               else
+                       execlp("sh", "sh", "-c", command, NULL);
+               die("exec failed");
+       }               
+       fd[0] = pipefd[0][0];
+       fd[1] = pipefd[1][1];
+       close(pipefd[0][1]);
+       close(pipefd[1][0]);
+       return pid;
+}
+
+int finish_connect(pid_t pid)
+{
+       int ret;
+
+       for (;;) {
+               ret = waitpid(pid, NULL, 0);
+               if (!ret)
+                       break;
+               if (errno != EINTR)
+                       break;
+       }
+       return ret;
+}
diff --git a/convert-cache.c b/convert-cache.c
new file mode 100644 (file)
index 0000000..77f8bff
--- /dev/null
@@ -0,0 +1,312 @@
+#define _XOPEN_SOURCE /* glibc2 needs this */
+#include <time.h>
+#include <ctype.h>
+#include "cache.h"
+
+struct entry {
+       unsigned char old_sha1[20];
+       unsigned char new_sha1[20];
+       int converted;
+};
+
+#define MAXOBJECTS (1000000)
+
+static struct entry *convert[MAXOBJECTS];
+static int nr_convert;
+
+static struct entry * convert_entry(unsigned char *sha1);
+
+static struct entry *insert_new(unsigned char *sha1, int pos)
+{
+       struct entry *new = xmalloc(sizeof(struct entry));
+       memset(new, 0, sizeof(*new));
+       memcpy(new->old_sha1, sha1, 20);
+       memmove(convert + pos + 1, convert + pos, (nr_convert - pos) * sizeof(struct entry *));
+       convert[pos] = new;
+       nr_convert++;
+       if (nr_convert == MAXOBJECTS)
+               die("you're kidding me - hit maximum object limit");
+       return new;
+}
+
+static struct entry *lookup_entry(unsigned char *sha1)
+{
+       int low = 0, high = nr_convert;
+
+       while (low < high) {
+               int next = (low + high) / 2;
+               struct entry *n = convert[next];
+               int cmp = memcmp(sha1, n->old_sha1, 20);
+               if (!cmp)
+                       return n;
+               if (cmp < 0) {
+                       high = next;
+                       continue;
+               }
+               low = next+1;
+       }
+       return insert_new(sha1, low);
+}
+
+static void convert_binary_sha1(void *buffer)
+{
+       struct entry *entry = convert_entry(buffer);
+       memcpy(buffer, entry->new_sha1, 20);
+}
+
+static void convert_ascii_sha1(void *buffer)
+{
+       unsigned char sha1[20];
+       struct entry *entry;
+
+       if (get_sha1_hex(buffer, sha1))
+               die("bad sha1");
+       entry = convert_entry(sha1);
+       memcpy(buffer, sha1_to_hex(entry->new_sha1), 40);
+}
+
+static int write_subdirectory(void *buffer, unsigned long size, const char *base, int baselen, unsigned char *result_sha1)
+{
+       char *new = xmalloc(size);
+       unsigned long newlen = 0;
+       unsigned long used;
+
+       used = 0;
+       while (size) {
+               int len = 21 + strlen(buffer);
+               char *path = strchr(buffer, ' ');
+               unsigned char *sha1;
+               unsigned int mode;
+               char *slash, *origpath;
+
+               if (!path || sscanf(buffer, "%o", &mode) != 1)
+                       die("bad tree conversion");
+               path++;
+               if (memcmp(path, base, baselen))
+                       break;
+               origpath = path;
+               path += baselen;
+               slash = strchr(path, '/');
+               if (!slash) {
+                       newlen += sprintf(new + newlen, "%o %s", mode, path);
+                       new[newlen++] = '\0';
+                       memcpy(new + newlen, buffer + len - 20, 20);
+                       newlen += 20;
+
+                       used += len;
+                       size -= len;
+                       buffer += len;
+                       continue;
+               }
+
+               newlen += sprintf(new + newlen, "%o %.*s", S_IFDIR, (int)(slash - path), path);
+               new[newlen++] = 0;
+               sha1 = (unsigned char *)(new + newlen);
+               newlen += 20;
+
+               len = write_subdirectory(buffer, size, origpath, slash-origpath+1, sha1);
+
+               used += len;
+               size -= len;
+               buffer += len;
+       }
+
+       write_sha1_file(new, newlen, "tree", result_sha1);
+       free(new);
+       return used;
+}
+
+static void convert_tree(void *buffer, unsigned long size, unsigned char *result_sha1)
+{
+       void *orig_buffer = buffer;
+       unsigned long orig_size = size;
+
+       while (size) {
+               int len = 1+strlen(buffer);
+
+               convert_binary_sha1(buffer + len);
+
+               len += 20;
+               if (len > size)
+                       die("corrupt tree object");
+               size -= len;
+               buffer += len;
+       }
+
+       write_subdirectory(orig_buffer, orig_size, "", 0, result_sha1);
+}
+
+static unsigned long parse_oldstyle_date(const char *buf)
+{
+       char c, *p;
+       char buffer[100];
+       struct tm tm;
+       const char *formats[] = {
+               "%c",
+               "%a %b %d %T",
+               "%Z",
+               "%Y",
+               " %Y",
+               NULL
+       };
+       /* We only ever did two timezones in the bad old format .. */
+       const char *timezones[] = {
+               "PDT", "PST", "CEST", NULL
+       };
+       const char **fmt = formats;
+
+       p = buffer;
+       while (isspace(c = *buf))
+               buf++;
+       while ((c = *buf++) != '\n')
+               *p++ = c;
+       *p++ = 0;
+       buf = buffer;
+       memset(&tm, 0, sizeof(tm));
+       do {
+               const char *next = strptime(buf, *fmt, &tm);
+               if (next) {
+                       if (!*next)
+                               return mktime(&tm);
+                       buf = next;
+               } else {
+                       const char **p = timezones;
+                       while (isspace(*buf))
+                               buf++;
+                       while (*p) {
+                               if (!memcmp(buf, *p, strlen(*p))) {
+                                       buf += strlen(*p);
+                                       break;
+                               }
+                               p++;
+                       }
+               }
+               fmt++;
+       } while (*buf && *fmt);
+       printf("left: %s\n", buf);
+       return mktime(&tm);                             
+}
+
+static int convert_date_line(char *dst, void **buf, unsigned long *sp)
+{
+       unsigned long size = *sp;
+       char *line = *buf;
+       char *next = strchr(line, '\n');
+       char *date = strchr(line, '>');
+       int len;
+
+       if (!next || !date)
+               die("missing or bad author/committer line %s", line);
+       next++; date += 2;
+
+       *buf = next;
+       *sp = size - (next - line);
+
+       len = date - line;
+       memcpy(dst, line, len);
+       dst += len;
+
+       /* Is it already in new format? */
+       if (isdigit(*date)) {
+               int datelen = next - date;
+               memcpy(dst, date, datelen);
+               return len + datelen;
+       }
+
+       /*
+        * Hacky hacky: one of the sparse old-style commits does not have
+        * any date at all, but we can fake it by using the committer date.
+        */
+       if (*date == '\n' && strchr(next, '>'))
+               date = strchr(next, '>')+2;
+
+       return len + sprintf(dst, "%lu -0700\n", parse_oldstyle_date(date));
+}
+
+static void convert_date(void *buffer, unsigned long size, unsigned char *result_sha1)
+{
+       char *new = xmalloc(size + 100);
+       unsigned long newlen = 0;
+       
+       // "tree <sha1>\n"
+       memcpy(new + newlen, buffer, 46);
+       newlen += 46;
+       buffer += 46;
+       size -= 46;
+
+       // "parent <sha1>\n"
+       while (!memcmp(buffer, "parent ", 7)) {
+               memcpy(new + newlen, buffer, 48);
+               newlen += 48;
+               buffer += 48;
+               size -= 48;
+       }
+
+       // "author xyz <xyz> date"
+       newlen += convert_date_line(new + newlen, &buffer, &size);
+       // "committer xyz <xyz> date"
+       newlen += convert_date_line(new + newlen, &buffer, &size);
+
+       // Rest
+       memcpy(new + newlen, buffer, size);
+       newlen += size;
+
+       write_sha1_file(new, newlen, "commit", result_sha1);
+       free(new);      
+}
+
+static void convert_commit(void *buffer, unsigned long size, unsigned char *result_sha1)
+{
+       void *orig_buffer = buffer;
+       unsigned long orig_size = size;
+
+       convert_ascii_sha1(buffer+5);
+       buffer += 46;    /* "tree " + "hex sha1" + "\n" */
+       while (!memcmp(buffer, "parent ", 7)) {
+               convert_ascii_sha1(buffer+7);
+               buffer += 48;
+       }
+       convert_date(orig_buffer, orig_size, result_sha1);
+}
+
+static struct entry * convert_entry(unsigned char *sha1)
+{
+       struct entry *entry = lookup_entry(sha1);
+       char type[20];
+       void *buffer, *data;
+       unsigned long size;
+
+       if