Do a cross-project merge of Paul Mackerras' gitk visualizer
authorLinus Torvalds <torvalds@ppc970.osdl.org>
Wed, 22 Jun 2005 21:05:02 +0000 (14:05 -0700)
committerLinus Torvalds <torvalds@ppc970.osdl.org>
Wed, 22 Jun 2005 21:05:02 +0000 (14:05 -0700)
gitk is really quite incredibly cool, and is great for visualizing what
is going on in a git repository.  It's especially useful when you are
looking at what has changed since a particular version, since it
gracefully handles partial trees (and this also avoids the expense of
looking at _all_ changes in a big project).

For example, to see what changed in a merge after a "git pull", do

gitk ORIG_HEAD..

to see only the new things.  Or you can simply do "gitk v2.6.12.." to
see what has changed since the v2.6.12 tag etc.

This merge itself is pretty interesting too, since it shows off a
feature of git itself that is incredibly cool: you can merge a
_separate_ git project into another git project.  Not only does this
keep all the history of the original project, it also makes it possible
to continue to merge with the original project and the union of the two
projects.

I don't think anybody else can do that.

203 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-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-mkdelta.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-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]
commit-tree.c [new file with mode: 0644]
commit.c [new file with mode: 0644]
commit.h [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]
cvs2git.c [new file with mode: 0644]
date.c [new file with mode: 0644]
delta.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]
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-commit-script [new file with mode: 0755]
git-cvsimport-script [new file with mode: 0755]
git-deltafy-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-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-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]
mkdelta.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]
patch-delta.c [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]
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]
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]
sha1_file.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/t5100-delta-pull.sh [new file with mode: 0644]
t/t6001-rev-list-merge-order.sh [new file with mode: 0644]
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]
update-cache.c [new file with mode: 0644]
usage.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..4e2c452
--- /dev/null
@@ -0,0 +1,212 @@
+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 <cvsroot> <module>
+
+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 a
+subdirectory named <module>.
+
+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 can be reasonably chatty, but on some not very
+scientific tests it averaged about eight 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.
+
+
+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..48fb377
--- /dev/null
@@ -0,0 +1,55 @@
+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 | <type>) <object>
+
+DESCRIPTION
+-----------
+Provides content or type of objects in the repository. The type
+is required if '-t' is not being used to find the object type.
+
+OPTIONS
+-------
+<object>::
+       The sha1 identifier of the object.
+
+-t::
+       Instead of the content, show the object type 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>.
+
+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-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..ff97504
--- /dev/null
@@ -0,0 +1,128 @@
+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] [--delta-depth] [--unreachable] [--cache] [<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.
+
+--delta-depth::
+       Report back the length of the longest delta chain found.
+
+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..e4b7b37
--- /dev/null
@@ -0,0 +1,46 @@
+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] [-v] [-d] [--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.
+-d::
+       Do not check for delta base objects (use this option
+       only when you know the remote repository is not
+       deltified).
+--recover::
+       Check dependency of deltified object more carefully than
+       usual, to recover after earlier pull that was interrupted.
+-v::
+       Report what is downloaded.
+
+
+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..777bdbd
--- /dev/null
@@ -0,0 +1,50 @@
+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.
+-d::
+       Do not check for delta base objects (use this option
+       only when you know the remote repository is not
+       deltified).
+--recover::
+       Check dependency of deltified object more carefully than
+       usual, to recover after earlier pull that was interrupted.
+-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-mkdelta.txt b/Documentation/git-mkdelta.txt
new file mode 100644 (file)
index 0000000..240d59a
--- /dev/null
@@ -0,0 +1,45 @@
+git-mkdelta(1)
+==============
+May 2005
+
+NAME
+----
+git-mkdelta - Creates a delta object
+
+
+SYNOPSIS
+--------
+'git-mkdelta' [-v] [-d N | --max-depth=N ] <reference_object> <target_object> [ <next_object> ... ]
+
+DESCRIPTION
+-----------
+Creates a delta object to replace <reference_object> by using an
+ordered list of potential objects to deltafy against earlier objects
+in the list.
+
+A cap on the depth of delta references can be provided as well,
+otherwise the default is to not have any limit.  A limit of 0 will
+also undeltafy a given object.
+
+
+OPTIONS
+-------
+-v::
+       Verbose
+
+-d|--max-depth::
+       limit the number of delta references in a chain
+       If 0 then all objects are undeltafied.
+       
+Author
+------
+Git is 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/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..3397fba
--- /dev/null
@@ -0,0 +1,59 @@
+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.
+-d::
+       Do not check for delta base objects (use this option
+       only when you know the remote repository is not
+       deltified).
+--recover::
+       Check dependency of deltified object more carefully than
+       usual, to recover after earlier pull that was interrupted.
+-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..c55ce7d
--- /dev/null
@@ -0,0 +1,55 @@
+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::
+        Either the hash or the filename under $GIT_DIR/refs/ to push.
+
+-c::
+        Get the commit objects.
+-t::
+        Get tree associated with the requested commit object.
+-a::
+        Get all the objects.
+-d::
+        Do not check for delta base objects (use this option
+        only when you know the local repository is not
+        deltified).
+--recover::
+        Check dependency of deltified object more carefully than
+        usual, to recover after earlier push that was interrupted.
+-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-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..971012b
--- /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-mkdelta.html[git-mkdelta]::
+       Creates a delta object
+
+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
+
+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..3372269
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,155 @@
+# -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
+
+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-deltafy-script 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
+
+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-mkdelta git-apply git-stripspace \
+       git-cvs2git git-diff-stages git-rev-parse
+
+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 delta.o date.o index.o diff-delta.o patch-delta.o entry.o \
+        epoch.o refs.o
+LIB_FILE=libgit.a
+LIB_H=cache.h object.h blob.h tree.h commit.h tag.h delta.h epoch.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)
+
+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-mkdelta: mkdelta.c
+git-stripspace: stripspace.c
+git-cvs2git: cvs2git.c
+git-diff-stages: diff-stages.c
+git-rev-parse: rev-parse.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..f5deac7
--- /dev/null
+++ b/README
@@ -0,0 +1,509 @@
+////////////////////////////////////////////////////////////////
+
+       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 five different object types: "blob",
+"tree", "commit", "tag" and "delta"
+
+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.
+
+A "delta" object is used internally by the object database to minimise
+disk usage. Instead of storing the entire contents of a revision, git
+can behave in a similar manner to RCS et al and simply store a delta.
+
+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 or
+the delta. (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]
+
+Delta Object
+~~~~~~~~~~~~
+
+The "delta" object is used internally by the object database to
+minimise storage usage by using xdeltas (byte level diffs). Deltas can
+form chains of arbitrary length as RCS does (although this is
+configureable at creation time). Most operations won't see or even be
+aware of delta objects as they are automatically 'applied' and appear
+as 'real' git objects In other words, if you write your own routines
+to look at the contents of the object database then you need to know
+about this - otherwise you don't. Actually, that's not quite true -
+one important area where deltas are likely to prove very valuable is
+in reducing bandwidth loads - so the more sophisticated network tools
+for git repositories will be aware of them too.
+
+Finally, git repositories can (and must) be deltafied in the
+background - the work to calculate the differences does not take place
+automatically at commit time.
+
+A delta can be created (or undeltafied) with
+link:git-mkdelta.html[git-mkdelta] it's raw data cannot be accessed at
+present.
+
+
+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..31f134c
--- /dev/null
+++ b/apply.c
@@ -0,0 +1,1451 @@
+/*
+ * 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;
+}
+
+const char pluses[] = "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";
+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;
+               fprintf(stderr, "Fragment applied at offset %d\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, "--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..6a7525a
--- /dev/null
+++ b/cache.h
@@ -0,0 +1,236 @@
+#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 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 */
+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 *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_delta_base(const unsigned char *, unsigned char *);
+extern int sha1_file_size(const unsigned 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_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);
+
+#endif /* CACHE_H */
diff --git a/cat-file.c b/cat-file.c
new file mode 100644 (file)
index 0000000..be41f51
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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 | tagname] <sha1>");
+
+       if (!strcmp("-t", argv[1])) {
+               buf = read_sha1_file(sha1, type, &size);
+               if (buf) {
+                       buf = type;
+                       size = strlen(type);
+                       type[size] = '\n';
+                       size++;
+               }
+       } 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/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..738590f
--- /dev/null
+++ b/commit.c
@@ -0,0 +1,330 @@
+#include <ctype.h>
+#include "tag.h"
+#include "commit.h"
+#include "cache.h"
+
+const char *commit_type = "commit";
+
+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_author_info(enum cmit_fmt fmt, char *buf, const char *line, int len)
+{
+       char *date;
+       unsigned int namelen;
+       unsigned long time;
+       int tz, ret;
+
+       line += strlen("author ");
+       date = strchr(line, '>');
+       if (!date)
+               return 0;
+       namelen = ++date - line;
+       time = strtoul(date, &date, 10);
+       tz = strtol(date, NULL, 10);
+
+       ret = sprintf(buf, "Author: %.*s\n", 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_author_info(fmt, buf + offset, line, linelen);
+                       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..57c1b62
--- /dev/null
+++ b/commit.h
@@ -0,0 +1,56 @@
+#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
+};
+
+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/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 (entry->converted)
+               return entry;
+       data = read_sha1_file(sha1, type, &size);
+       if (!data)
+               die("unable to read object %s", sha1_to_hex(sha1));
+
+       buffer = xmalloc(size);
+       memcpy(buffer, data, size);
+       
+       if (!strcmp(type, "blob")) {
+               write_sha1_file(buffer, size, "blob", entry->new_sha1);
+       } else if (!strcmp(type, "tree"))
+               convert_tree(buffer, size, entry->new_sha1);
+       else if (!strcmp(type, "commit"))
+               convert_commit(buffer, size, entry->new_sha1);
+       else
+               die("unknown object type '%s' in %s", type, sha1_to_hex(sha1));
+       entry->converted = 1;
+       free(buffer);
+       return entry;
+}
+
+int main(int argc, char **argv)
+{
+       unsigned char sha1[20];
+       struct entry *entry;
+
+       if (argc != 2 || get_sha1(argv[1], sha1))
+               usage("git-convert-cache <sha1>");
+
+       entry = convert_entry(sha1);
+       printf("new sha1: %s\n", sha1_to_hex(entry->new_sha1));
+       return 0;
+}
diff --git a/count-delta.c b/count-delta.c
new file mode 100644 (file)
index 0000000..c7f3767
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2005 Junio C Hamano
+ * The delta-parsing part is almost straight copy of patch-delta.c
+ * which is (C) 2005 Nicolas Pitre <nico@cam.org>.
+ */
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "count-delta.h"
+
+static unsigned long get_hdr_size(const unsigned char **datap)
+{
+       const unsigned char *data = *datap;
+       unsigned long size;
+       unsigned char cmd;
+       int i;
+       size = i = 0;
+       cmd = *data++;
+       while (cmd) {
+               if (cmd & 1)
+                       size |= *data++ << i;
+               i += 8;
+               cmd >>= 1;
+       }
+       *datap = data;
+       return size;
+}
+
+/*
+ * NOTE.  We do not _interpret_ delta fully.  As an approximation, we
+ * just count the number of bytes that are copied from the source, and
+ * the number of literal data bytes that are inserted.
+ *
+ * Number of bytes that are _not_ copied from the source is deletion,
+ * and number of inserted literal bytes are addition, so sum of them
+ * is the extent of damage.  xdelta can express an edit that copies
+ * data inside of the destination which originally came from the
+ * source.  We do not count that in the following routine, so we are
+ * undercounting the source material that remains in the final output
+ * that way.
+ */
+int count_delta(void *delta_buf, unsigned long delta_size,
+               unsigned long *src_copied, unsigned long *literal_added)
+{
+       unsigned long copied_from_source, added_literal;
+       const unsigned char *data, *top;
+       unsigned char cmd;
+       unsigned long src_size, dst_size, out;
+
+       /* the smallest delta size possible is 6 bytes */
+       if (delta_size < 6)
+               return -1;
+
+       data = delta_buf;
+       top = delta_buf + delta_size;
+
+       src_size = get_hdr_size(&data);
+       dst_size = get_hdr_size(&data);
+
+       added_literal = copied_from_source = out = 0;
+       while (data < top) {
+               cmd = *data++;
+               if (cmd & 0x80) {
+                       unsigned long cp_off = 0, cp_size = 0;
+                       if (cmd & 0x01) cp_off = *data++;
+                       if (cmd & 0x02) cp_off |= (*data++ << 8);
+                       if (cmd & 0x04) cp_off |= (*data++ << 16);
+                       if (cmd & 0x08) cp_off |= (*data++ << 24);
+                       if (cmd & 0x10) cp_size = *data++;
+                       if (cmd & 0x20) cp_size |= (*data++ << 8);
+                       if (cp_size == 0) cp_size = 0x10000;
+
+                       if (cmd & 0x40)
+                               /* copy from dst */
+                               ;
+                       else
+                               copied_from_source += cp_size;
+                       out += cp_size;
+               } else {
+                       /* write literal into dst */
+                       added_literal += cmd;
+                       out += cmd;
+                       data += cmd;
+               }
+       }
+
+       /* sanity check */
+       if (data != top || out != dst_size)
+               return -1;
+
+       /* delete size is what was _not_ copied from source.
+        * edit size is that and literal additions.
+        */
+       *src_copied = copied_from_source;
+       *literal_added = added_literal;
+       return 0;
+}
diff --git a/count-delta.h b/count-delta.h
new file mode 100644 (file)
index 0000000..7359629
--- /dev/null
@@ -0,0 +1,10 @@
+/*
+ * Copyright (C) 2005 Junio C Hamano
+ */
+#ifndef COUNT_DELTA_H
+#define COUNT_DELTA_H
+
+int count_delta(void *, unsigned long,
+               unsigned long *src_copied, unsigned long *literal_added);
+
+#endif
diff --git a/cvs2git.c b/cvs2git.c
new file mode 100644 (file)
index 0000000..ab05908
--- /dev/null
+++ b/cvs2git.c
@@ -0,0 +1,329 @@
+/*
+ * cvs2git
+ *
+ * Copyright (C) Linus Torvalds 2005
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static int verbose = 0;
+
+/*
+ * This is a really stupid program that takes cvsps output, and
+ * generates a a long _shell_script_ that will create the GIT archive
+ * from it. 
+ *
+ * You've been warned. I told you it was stupid.
+ *
+ * NOTE NOTE NOTE! In order to do branches correctly, this needs
+ * the fixed cvsps that has the "Ancestor branch" tag output.
+ * Hopefully David Mansfield will update his distribution soon
+ * enough (he's the one who wrote the patch, so at least we don't
+ * have to figt maintainer issues ;)
+ *
+ * Usage:
+ *
+ *     TZ=UTC cvsps -A |
+ *             git-cvs2git --cvsroot=[root] --module=[module] > script
+ *
+ * Creates a shell script that will generate the .git archive of
+ * the names CVS repository.
+ *
+ *     TZ=UTC cvsps -s 1234- -A |
+ *             git-cvs2git -u --cvsroot=[root] --module=[module] > script
+ *
+ * Creates a shell script that will update the .git archive with
+ * CVS changes from patchset 1234 until the last one.
+ *
+ * IMPORTANT NOTE ABOUT "cvsps"! This requires version 2.1 or better,
+ * and the "TZ=UTC" and the "-A" flag is required for sane results!
+ */
+enum state {
+       Header,
+       Log,
+       Members
+};
+
+static const char *cvsroot;
+static const char *cvsmodule;
+
+static char date[100];
+static char author[100];
+static char branch[100];
+static char ancestor[100];
+static char tag[100];
+static char log[32768];
+static int loglen = 0;
+static int initial_commit = 1;
+
+static void lookup_author(char *n, char **name, char **email)
+{
+       /*
+        * FIXME!!! I'm lazy and stupid.
+        *
+        * This could be something like
+        *
+        *      printf("lookup_author '%s'\n", n);
+        *      *name = "$author_name";
+        *      *email = "$author_email";
+        *
+        * and that would allow the script to do its own
+        * lookups at run-time.
+        */
+       *name = n;
+       *email = n;
+}
+
+static void prepare_commit(void)
+{
+       char *author_name, *author_email;
+       char *src_branch;
+
+       lookup_author(author, &author_name, &author_email);
+
+       printf("export GIT_COMMITTER_NAME=%s\n", author_name);
+       printf("export GIT_COMMITTER_EMAIL=%s\n", author_email);
+       printf("export GIT_COMMITTER_DATE='+0000 %s'\n", date);
+
+       printf("export GIT_AUTHOR_NAME=%s\n", author_name);
+       printf("export GIT_AUTHOR_EMAIL=%s\n", author_email);
+       printf("export GIT_AUTHOR_DATE='+0000 %s'\n", date);
+
+       if (initial_commit)
+               return;
+
+       src_branch = *ancestor ? ancestor : branch;
+       if (!strcmp(src_branch, "HEAD"))
+               src_branch = "master";
+       printf("ln -sf refs/heads/'%s' .git/HEAD\n", src_branch);
+
+       /*
+        * Even if cvsps claims an ancestor, we'll let the new
+        * branch name take precedence if it already exists
+        */
+       if (*ancestor) {
+               src_branch = branch;
+               if (!strcmp(src_branch, "HEAD"))
+                       src_branch = "master";
+               printf("[ -e .git/refs/heads/'%s' ] && ln -sf refs/heads/'%s' .git/HEAD\n",
+                       src_branch, src_branch);
+       }
+
+       printf("git-read-tree -m HEAD || exit 1\n");
+       printf("git-checkout-cache -f -u -a\n");
+}
+
+static void commit(void)
+{
+       const char *cmit_parent = initial_commit ? "" : "-p HEAD";
+       const char *dst_branch;
+       char *space;
+       int i;
+
+       printf("tree=$(git-write-tree)\n");
+       printf("cat > .cmitmsg <<EOFMSG\n");
+
+       /* Escape $ characters, and remove control characters */
+       for (i = 0; i < loglen; i++) {
+               unsigned char c = log[i];
+
+               switch (c) {
+               case '$':
+               case '\\':
+               case '`':
+                       putchar('\\');
+                       break;
+               case 0 ... 31:
+                       if (c == '\n' || c == '\t')
+                               break;
+               case 128 ... 159:
+                       continue;
+               }
+               putchar(c);
+       }
+       printf("\nEOFMSG\n");
+       printf("commit=$(cat .cmitmsg | git-commit-tree $tree %s)\n", cmit_parent);
+
+       dst_branch = branch;
+       if (!strcmp(dst_branch, "HEAD"))
+               dst_branch = "master";
+
+       printf("echo $commit > .git/refs/heads/'%s'\n", dst_branch);
+
+       space = strchr(tag, ' ');
+       if (space)
+               *space = 0;
+       if (strcmp(tag, "(none)"))
+               printf("echo $commit > .git/refs/tags/'%s'\n", tag);
+
+       printf("echo 'Committed (to %s):' ; cat .cmitmsg; echo\n", dst_branch);
+
+       *date = 0;
+       *author = 0;
+       *branch = 0;
+       *ancestor = 0;
+       *tag = 0;
+       loglen = 0;
+
+       initial_commit = 0;
+}
+
+static void update_file(char *line)
+{
+       char *name, *version;
+       char *dir;
+
+       while (isspace(*line))
+               line++;
+       name = line;
+       line = strchr(line, ':');
+       if (!line)
+               return;
+       *line++ = 0;
+       line = strchr(line, '>');
+       if (!line)
+               return;
+       *line++ = 0;
+       version = line;
+       line = strchr(line, '(');
+       if (line) {     /* "(DEAD)" */
+               printf("git-update-cache --force-remove '%s'\n", name);
+               return;
+       }
+
+       dir = strrchr(name, '/');
+       if (dir)
+               printf("mkdir -p %.*s\n", (int)(dir - name), name);
+
+       printf("cvs -q -d %s checkout -d .git-tmp -r%s '%s/%s'\n", 
+               cvsroot, version, cvsmodule, name);
+       printf("mv -f .git-tmp/%s %s\n", dir ? dir+1 : name, name);
+       printf("rm -rf .git-tmp\n");
+       printf("git-update-cache --add -- '%s'\n", name);
+}
+
+struct hdrentry {
+       const char *name;
+       char *dest;
+} hdrs[] = {
+       { "Date:", date },
+       { "Author:", author },
+       { "Branch:", branch },
+       { "Ancestor branch:", ancestor },
+       { "Tag:", tag },
+       { "Log:", NULL },
+       { NULL, NULL }
+};
+
+int main(int argc, char **argv)
+{
+       static char line[1000];
+       enum state state = Header;
+       int i;
+
+       for (i = 1; i < argc; i++) {
+               const char *arg = argv[i];
+               if (!memcmp(arg, "--cvsroot=", 10)) {
+                       cvsroot = arg + 10;
+                       continue;
+               }
+               if (!memcmp(arg, "--module=", 9)) {
+                       cvsmodule = arg+9;
+                       continue;
+               } 
+               if (!strcmp(arg, "-v")) {
+                       verbose = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "-u")) {
+                       initial_commit = 0;
+                       continue;
+               }
+       }
+
+
+       if (!cvsroot)
+               cvsroot = getenv("CVSROOT");
+
+       if (!cvsmodule || !cvsroot) {
+               fprintf(stderr, "I need a CVSROOT and module name\n");
+               exit(1);
+       }
+
+       if (initial_commit) {
+               printf("[ -d .git ] && exit 1\n");
+                   printf("git-init-db\n");
+               printf("mkdir -p .git/refs/heads\n");
+               printf("mkdir -p .git/refs/tags\n");
+               printf("ln -sf refs/heads/master .git/HEAD\n");
+       }
+
+       while (fgets(line, sizeof(line), stdin) != NULL) {
+               int linelen = strlen(line);
+
+               while (linelen && isspace(line[linelen-1]))
+                       line[--linelen] = 0;
+
+               switch (state) {
+               struct hdrentry *entry;
+
+               case Header:
+                       if (verbose)
+                               printf("# H: %s\n", line);
+                       for (entry = hdrs ; entry->name ; entry++) {
+                               int len = strlen(entry->name);
+                               char *val;
+
+                               if (memcmp(entry->name, line, len))
+                                       continue;
+                               if (!entry->dest) {
+                                       state = Log;
+                                       break;
+                               }
+                               val = line + len;
+                               linelen -= len;
+                               while (isspace(*val)) {
+                                       val++;
+                                       linelen--;
+                               }
+                               memcpy(entry->dest, val, linelen+1);
+                               break;
+                       }
+                       continue;
+
+               case Log:
+                       if (verbose)
+                               printf("# L: %s\n", line);
+                       if (!strcmp(line, "Members:")) {
+                               while (loglen && isspace(log[loglen-1]))
+                                       log[--loglen] = 0;
+                               prepare_commit();
+                               state = Members;
+                               continue;
+                       }
+                               
+                       if (loglen + linelen + 5 > sizeof(log))
+                               continue;
+                       memcpy(log + loglen, line, linelen);
+                       loglen += linelen;
+                       log[loglen++] = '\n';
+                       continue;
+
+               case Members:
+                       if (verbose)
+                               printf("# M: %s\n", line);
+                       if (!linelen) {
+                               commit();
+                               state = Header;
+                               continue;
+                       }
+                       update_file(line);
+                       continue;
+               }
+       }
+       return 0;
+}
diff --git a/date.c b/date.c
new file mode 100644 (file)
index 0000000..ff922fe
--- /dev/null
+++ b/date.c
@@ -0,0 +1,455 @@
+/*
+ * GIT - The information manager from hell
+ *
+ * Copyright (C) Linus Torvalds, 2005
+ */
+
+#include <ctype.h>
+#include <time.h>
+
+#include "cache.h"
+
+static time_t my_mktime(struct tm *tm)
+{
+       static const int mdays[] = {
+           0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+       };
+       int year = tm->tm_year - 70;
+       int month = tm->tm_mon;
+       int day = tm->tm_mday;
+
+       if (year < 0 || year > 129) /* algo only works for 1970-2099 */
+               return -1;
+       if (month < 0 || month > 11) /* array bounds */
+               return -1;
+       if (month < 2 || (year + 2) % 4)
+               day--;
+       return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
+               tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
+}
+
+static const char *month_names[] = {
+       "January", "February", "March", "April", "May", "June",
+       "July", "August", "September", "October", "November", "December"
+};
+
+static const char *weekday_names[] = {