Merge fixes up to GIT 1.1.6
authorJunio C Hamano <junkio@cox.net>
Mon, 30 Jan 2006 01:56:45 +0000 (17:56 -0800)
committerJunio C Hamano <junkio@cox.net>
Mon, 30 Jan 2006 01:56:45 +0000 (17:56 -0800)
75 files changed:
Documentation/.gitignore
Documentation/Makefile
Documentation/core-tutorial.txt [new file with mode: 0644]
Documentation/diff-format.txt
Documentation/fetch-options.txt
Documentation/git-checkout.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-cvsimport.txt
Documentation/git-diff-files.txt
Documentation/git-diff-tree.txt
Documentation/git-grep.txt
Documentation/git-ls-files.txt
Documentation/git-push.txt
Documentation/git-reset.txt
Documentation/git-rev-list.txt
Documentation/git-show-branch.txt
Documentation/git-svnimport.txt
Documentation/git.txt
Documentation/howto/revert-branch-rebase.txt
Documentation/howto/using-topic-branches.txt
Documentation/repository-layout.txt
Documentation/tutorial.txt
GIT-VERSION-GEN
INSTALL
Makefile
cache.h
combine-diff.c [new file with mode: 0644]
commit.c
commit.h
compat/unsetenv.c [new file with mode: 0644]
connect.c
daemon.c
describe.c
diff-files.c
diff-index.c
diff-tree.c
diff.c
diff.h
exec_cmd.c [new file with mode: 0644]
exec_cmd.h [new file with mode: 0644]
fetch-clone.c
fsck-objects.c
git-checkout.sh
git-clone.sh
git-compat-util.h
git-cvsimport.perl
git-fetch.sh
git-format-patch.sh
git-grep.sh
git-ls-remote.sh
git-merge-octopus.sh
git-merge.sh
git-push.sh
git-whatchanged.sh
git.c
ls-tree.c
mailsplit.c
receive-pack.c
refs.c
rev-list.c
rev-parse.c
run-command.c
run-command.h
send-pack.c
sha1_name.c
shell.c
show-branch.c
t/t1200-tutorial.sh
t/t4011-diff-symlink.sh
t/t6101-rev-parse-parents.sh
templates/hooks--update
tree.c
tree.h
upload-pack.c

index 9fef490..c87c61a 100644 (file)
@@ -4,3 +4,4 @@
 *.7
 howto-index.txt
 doc.dep
+README
index a1ff2c2..a3bca86 100644 (file)
@@ -4,6 +4,7 @@ MAN7_TXT=git.txt
 DOC_HTML=$(patsubst %.txt,%.html,$(MAN1_TXT) $(MAN7_TXT))
 
 ARTICLES = tutorial
+ARTICLES += core-tutorial
 ARTICLES += cvs-migration
 ARTICLES += diffcore
 ARTICLES += howto-index
@@ -61,11 +62,14 @@ doc.dep : $(wildcard *.txt) build-docdep.perl
 
 -include doc.dep
 
-git.7: ../README
+git.7: README
+
+README: ../README
+       cp $< $@
 
 
 clean:
-       rm -f *.xml *.html *.1 *.7 howto-index.txt howto/*.html doc.dep
+       rm -f *.xml *.html *.1 *.7 howto-index.txt howto/*.html doc.dep README
 
 %.html : %.txt
        asciidoc -b xhtml11 -d manpage -f asciidoc.conf $<
@@ -76,7 +80,7 @@ clean:
 %.xml : %.txt
        asciidoc -b docbook -d manpage -f asciidoc.conf $<
 
-git.html: git.txt ../README
+git.html: git.txt README
 
 glossary.html : glossary.txt sort_glossary.pl
        cat $< | \
diff --git a/Documentation/core-tutorial.txt b/Documentation/core-tutorial.txt
new file mode 100644 (file)
index 0000000..35579cc
--- /dev/null
@@ -0,0 +1,1841 @@
+A short git tutorial
+====================
+
+Introduction
+------------
+
+This is trying to be a short tutorial on setting up and using a git
+repository, 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.
+
+The material presented here often goes deep describing how things
+work internally.  If you are mostly interested in using git as a
+SCM, you can skip them during your first pass.
+
+[NOTE]
+And those "too deep" descriptions are often marked as Note.
+
+[NOTE]
+If you are already familiar with another version control system,
+like CVS, you may want to take a look at
+link:everyday.html[Everyday GIT in 20 commands or so] first
+before reading this.
+
+
+Creating a git repository
+-------------------------
+
+Creating a new git repository couldn't be easier: all git repositories 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 repository 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, it should show you
+three entries, among other things:
+
+ - a symlink called `HEAD`, pointing to `refs/heads/master` (if your
+   platform does not have native symlinks, it is a file containing the
+   line "ref: 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
+   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 in 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.
+
+[NOTE]
+An 'object' is identified by its 160-bit SHA1 hash, aka 'object 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 these `refs` subdirectories when you actually start
+populating your tree.
+
+[NOTE]
+An advanced user may want to take a look at the
+link:repository-layout.html[repository layout] document
+after finishing this tutorial.
+
+You have now created your first git repository. Of course, since it's
+empty, that's not very useful, so let's start populating it with data.
+
+
+Populating a git repository
+---------------------------
+
+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 repository. We'll start off with a few bad examples, just to
+get a feel for how this works:
+
+------------------------------------------------
+$ echo "Hello World" >hello
+$ echo "Silly example" >example
+------------------------------------------------
+
+you have now created two files in your working tree (aka 'working directory'), but to
+actually check in your hard work, you will have to go through two steps:
+
+ - fill in the 'index' file (aka 'cache') with the information about your
+   working tree 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 tree, you use the `git-update-index` 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 index
+(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-index --add hello example
+------------------------------------------------
+
+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
+database. 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 objects with 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" 557db03
+----------------
+
+which will print out "Hello World". The object 557db03 is nothing
+more than the contents of your file `hello`.
+
+[NOTE]
+Don't confuse that object with the file `hello` itself. The
+object is literally just those specific *contents* of the file, and
+however much you later change the contents in file `hello`, the object
+we just looked at will never change. Objects are immutable.
+
+[NOTE]
+The second example demonstrates that you can
+abbreviate the object name to only the first several
+hexadecimal digits in most places.
+
+Anyway, as we mentioned previously, you normally never actually take a
+look at the objects themselves, and typing long 40-character hex
+names is not something you'd normally want to do. The above digression
+was just to show that `git-update-index` did something magical, and
+actually saved away the contents of your files into the git object
+database.
+
+Updating the index 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 `hello` first:
+
+------------------------------------------------
+$ echo "It's a new day for git" >>hello
+------------------------------------------------
+
+and you can now, since you told git about the previous state of `hello`, 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 "hello" 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
+diff --git a/hello b/hello
+index 557db03..263414f 100644
+--- a/hello
++++ b/hello
+@@ -1 +1,2 @@
+ Hello World
++It's a new day for git
+----
+
+i.e. the diff of the change we caused by adding another line to `hello`.
+
+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.
+
+A common shorthand for `git-diff-files -p` is to just write `git
+diff`, which will do the same thing.
+
+------------
+$ git diff
+diff --git a/hello b/hello
+index 557db03..263414f 100644
+--- a/hello
++++ b/hello
+@@ -1 +1,2 @@
+ Hello World
++It's a new day for git
+------------
+
+
+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 done exactly as I've described) it should be
+
+----------------
+8988da15d077d4829fc51d8544c097def6644dbb
+----------------
+
+which is another incomprehensible object name. Again, if you want to,
+you can use `git-cat-file -t 8988d\...` 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 repository, and it has no parents, we only need to pass in
+the object name of the tree. However, `git-commit-tree`
+also wants to get a commit message
+on its standard input, and it will write out the resulting object name for the
+commit to its standard output.
+
+And this is where we create the `.git/refs/heads/master` file
+which is pointed at by `HEAD`. This file is supposed to contain
+the reference to the top-of-tree of the master branch, and since
+that's exactly what `git-commit-tree` spits out, we can do this
+all with a sequence of simple shell commands:
+
+------------------------------------------------
+$ tree=$(git-write-tree)
+$ commit=$(echo 'Initial commit' | git-commit-tree $tree)
+$ git-update-ref HEAD $commit
+------------------------------------------------
+
+which will say:
+
+----------------
+Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb
+----------------
+
+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.
+
+Again, normally you'd never actually do this by hand. There is a
+helpful script called `git commit` that will do all of this for you. So
+you could have just written `git commit`
+instead, and it would have done the above magic scripting for you.
+
+
+Making a change
+---------------
+
+Remember how we did the `git-update-index` on file `hello` and then we
+changed `hello` afterward, and could compare the new state of `hello` 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 `hello`, not the new ones. We did
+that on purpose, to show the difference between the index state, and the
+state in the working tree, 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-index`.
+
+Unlike `git-diff-files`, which showed the difference between the index
+file and the working tree, `git-diff-index` shows the differences
+between a committed *tree* and either the index file or the working
+tree. In other words, `git-diff-index` 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-index -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 tree 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.
+
+Again, because this is a common operation, you can also just shorthand
+it with
+
+----------------
+$ git diff HEAD
+----------------
+
+which ends up doing the above for you.
+
+In other words, `git-diff-index` normally compares a tree against the
+working tree, but when given the `\--cached` flag, it is told to
+instead compare against just the index cache contents, and ignore the
+current working tree state entirely. Since we just wrote the index
+file to HEAD, doing `git-diff-index \--cached -p HEAD` should thus return
+an empty set of differences, and that's exactly what it does. 
+
+[NOTE]
+================
+`git-diff-index` really always uses the index for its
+comparisons, and saying that it compares a tree against the working
+tree 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 tree 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
+tree contents", "index file" and "committed tree". We have changes
+in the working tree 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-index hello
+------------------------------------------------
+
+(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-\*` versions here. After
+we've updated `hello` in the index, `git-diff-files -p` now shows no
+differences, but `git-diff-index -p HEAD` still *does* show that the
+current state is different from the state we committed. In fact, now
+`git-diff-index` shows the same difference whether we use the `--cached`
+flag or not, since now the index is coherent with the working tree.
+
+Now, since we've updated `hello` in the index, we can commit the new
+version. We could do it by writing the tree by hand again, 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 you've done that once
+already, so let's just use the helpful script this time:
+
+------------------------------------------------
+$ git commit
+------------------------------------------------
+
+which starts an editor for you to write the commit message and tells you
+a bit about what you have done.
+
+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 index), you
+can just leave an empty message. Otherwise `git commit` 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` 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 (`git-commit`).
+
+
+Inspecting Changes
+------------------
+
+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.
+
+[NOTE]
+============
+Here is an ASCII art by Jon Loeliger that illustrates how
+various diff-\* commands compare things.
+
+                      diff-tree
+                       +----+
+                       |    |
+                       |    |
+                       V    V
+                    +-----------+
+                    | Object DB |
+                    |  Backing  |
+                    |   Store   |
+                    +-----------+
+                      ^    ^
+                      |    |
+                      |    |  diff-index --cached
+                      |    |
+          diff-index  |    V
+                      |  +-----------+
+                      |  |   Index   |
+                      |  |  "cache"  |
+                      |  +-----------+
+                      |    ^
+                      |    |
+                      |    |  diff-files
+                      |    |
+                      V    V
+                    +-----------+
+                    |  Working  |
+                    | Directory |
+                    +-----------+
+============
+
+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
+activities.
+
+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. 
+
+[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.
+
+[NOTE]
+Most likely, you are not directly using the core
+git Plumbing commands, but using Porcelain like Cogito on top
+of it. Cogito works a bit differently and you usually do not
+have to run `git-update-index` yourself for changed files (you
+do tell underlying git about additions and removals via
+`cg-add` and `cg-rm` commands). Just before you make a commit
+with `cg-commit`, Cogito figures out which files you modified,
+and runs `git-update-index` on them for you.
+
+
+Tagging a version
+-----------------
+
+In git, there are two kinds of tags, a "light" one, and an "annotated tag".
+
+A "light" tag is technically nothing more than a branch, except we put
+it in the `.git/refs/tags/` subdirectory instead of calling it a `head`.
+So the simplest form of tag involves nothing more than
+
+------------------------------------------------
+$ git tag my-first-tag
+------------------------------------------------
+
+which just writes the current `HEAD` into the `.git/refs/tags/my-first-tag`
+file, after which point you can then use this symbolic name for that
+particular state. You can, for example, do
+
+----------------
+$ git diff my-first-tag
+----------------
+
+to diff your current state against that tag (which at this point will
+obviously be an empty diff, but if you continue to develop and commit
+stuff, you can use your tag as an "anchor-point" to see what has changed
+since you tagged it.
+
+An "annotated tag" is actually a real git object, and contains not only a
+pointer to the state you want to tag, but also a small tag name and
+message, along with optionally a PGP signature that says that yes,
+you really did
+that tag. You create these annotated tags with either the `-a` or
+`-s` flag to `git tag`:
+
+----------------
+$ git tag -s <tagname>
+----------------
+
+which will sign the current `HEAD` (but you can also give it another
+argument that specifies the thing to tag, ie you could have tagged the
+current `mybranch` point by using `git tag <tagname> mybranch`).
+
+You normally only do signed tags for major releases or things
+like that, while the light-weight tags are useful for any marking you
+want to do -- any time you decide that you want to remember a certain
+point, just create a private tag for it, and you have a nice symbolic
+name for the state at that point.
+
+
+Copying repositories
+--------------------
+
+git repositories are normally totally self-sufficient and relocatable
+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.
+
+[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 tree 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 repository 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 the project you created.
+
+ - if you want to move or duplicate a git repository, you can do so. There
+   is `git clone` command, but if all you want to do is just to
+   create a copy of your repository (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 repository, 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-index --refresh
+----------------
++
+in the new repository to make sure that the index file is up-to-date.
+
+Note that the second point is true even across machines. You can
+duplicate a remote git repository 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-index` with a
+
+----------------
+$ git-read-tree --reset HEAD
+$ git-update-index --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-index`
+makes sure to match up all index entries with the checked-out files.
+If the original repository had uncommitted changes in its
+working tree, `git-update-index --refresh` notices them and
+tells you they need to be updated.
+
+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.  You can learn things by just looking
+at what the various git scripts do.  For example, `git reset` is the
+above two lines implemented in `git-reset`, but some things like
+`git status` and `git commit` are slightly more complex scripts around
+the basic git commands.
+
+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/git/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 tree files to work on. To get
+those, you'd check them out with
+
+----------------
+$ git-checkout-index -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` flag 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`
+flag first, to tell git-checkout-index to *force* overwriting of any old
+files). 
+
+Again, this can all be simplified with
+
+----------------
+$ git clone rsync://rsync.kernel.org/pub/scm/git/git.git/ my-git
+$ cd my-git
+$ git checkout
+----------------
+
+which will end up doing all of the above for you.
+
+You have now successfully copied somebody else's (mine) remote
+repository, and checked it out. 
+
+
+Creating a new branch
+---------------------
+
+Branches in git are really nothing more than pointers into the git
+object database from within the `.git/refs/` subdirectory, and as we
+already discussed, the `HEAD` branch is nothing but a symlink to one of
+these object pointers. 
+
+You can at any time create a new branch by just picking an arbitrary
+point in the project history, and just writing the SHA1 name of that
+object into a file under `.git/refs/heads/`. You can use any filename you
+want (and indeed, subdirectories), but the convention is that the
+"normal" branch is called `master`. That's just a convention, though,
+and nothing enforces it. 
+
+To show that as an example, let's go back to the git-tutorial repository we
+used earlier, and create a branch in it. You do that by simply just
+saying that you want to check out a new branch:
+
+------------
+$ git checkout -b mybranch
+------------
+
+will create a new branch based at the current `HEAD` position, and switch
+to it. 
+
+[NOTE]
+================================================
+If you make the decision to start your new branch at some
+other point in the history than the current `HEAD`, you can do so by
+just telling `git checkout` what the base of the checkout would be.
+In other words, if you have an earlier tag or branch, you'd just do
+
+------------
+$ git checkout -b mybranch earlier-commit
+------------
+
+and it would create the new branch `mybranch` at the earlier commit,
+and check out the state at that time.
+================================================
+
+You can always just jump back to your original `master` branch by doing
+
+------------
+$ git checkout master
+------------
+
+(or any other branch-name, for that matter) and if you forget which
+branch you happen to be on, a simple
+
+------------
+$ ls -l .git/HEAD
+------------
+
+will tell you where it's pointing (Note that on platforms with bad or no
+symlink support, you have to execute
+
+------------
+$ cat .git/HEAD
+------------
+
+instead). To get the list of branches you have, you can say
+
+------------
+$ git branch
+------------
+
+which is nothing more than a simple script around `ls .git/refs/heads`.
+There will be asterisk in front of the branch you are currently on.
+
+Sometimes you may wish to create a new branch _without_ actually
+checking it out and switching to it. If so, just use the command
+
+------------
+$ git branch <branchname> [startingpoint]
+------------
+
+which will simply _create_ the branch, but will not do anything further. 
+You can then later -- once you decide that you want to actually develop
+on that branch -- switch to that branch with a regular `git checkout`
+with the branchname as the argument.
+
+
+Merging two branches
+--------------------
+
+One of the ideas of having a branch is that you do some (possibly
+experimental) work in it, and eventually merge it back to the main
+branch. So assuming you created the above `mybranch` that started out
+being the same as the original `master` branch, let's make sure we're in
+that branch, and do some work there.
+
+------------------------------------------------
+$ git checkout mybranch
+$ echo "Work, work, work" >>hello
+$ git commit -m 'Some work.' hello
+------------------------------------------------
+
+Here, we just added another line to `hello`, and we used a shorthand for
+doing both `git-update-index hello` and `git commit` by just giving the
+filename directly to `git commit`. The `-m` flag is to give the
+commit log message from the command line.
+
+Now, to make it a bit more interesting, let's assume that somebody else
+does some work in the original branch, and simulate that by going back
+to the master branch, and editing the same file differently there:
+
+------------
+$ git checkout master
+------------
+
+Here, take a moment to look at the contents of `hello`, and notice how they
+don't contain the work we just did in `mybranch` -- because that work
+hasn't happened in the `master` branch at all. Then do
+
+------------
+$ echo "Play, play, play" >>hello
+$ echo "Lots of fun" >>example
+$ git commit -m 'Some fun.' hello example
+------------
+
+since the master branch is obviously in a much better mood.
+
+Now, you've got two branches, and you decide that you want to merge the
+work done. Before we do that, let's introduce a cool graphical tool that
+helps you view what's going on:
+
+----------------
+$ gitk --all
+----------------
+
+will show you graphically both of your branches (that's what the `\--all`
+means: normally it will just show you your current `HEAD`) and their
+histories. You can also see exactly how they came to be from a common
+source. 
+
+Anyway, let's exit `gitk` (`^Q` or the File menu), and decide that we want
+to merge the work we did on the `mybranch` branch into the `master`
+branch (which is currently our `HEAD` too). To do that, there's a nice
+script called `git merge`, which wants to know which branches you want
+to resolve and what the merge is all about:
+
+------------
+$ git merge "Merge work in mybranch" HEAD mybranch
+------------
+
+where the first argument is going to be used as the commit message if
+the merge can be resolved automatically.
+
+Now, in this case we've intentionally created a situation where the
+merge will need to be fixed up by hand, though, so git will do as much
+of it as it can automatically (which in this case is just merge the `example`
+file, which had no differences in the `mybranch` branch), and say:
+
+----------------
+       Trying really trivial in-index merge...
+       fatal: Merge requires file-level merging
+       Nope.
+       ...
+       Auto-merging hello 
+       CONFLICT (content): Merge conflict in hello 
+       Automatic merge failed/prevented; fix up by hand
+----------------
+
+which is way too verbose, but it basically tells you that it failed the
+really trivial merge ("Simple merge") and did an "Automatic merge"
+instead, but that too failed due to conflicts in `hello`.
+
+Not to worry. It left the (trivial) conflict in `hello` in the same form you
+should already be well used to if you've ever used CVS, so let's just
+open `hello` in our editor (whatever that may be), and fix it up somehow.
+I'd suggest just making it so that `hello` contains all four lines:
+
+------------
+Hello World
+It's a new day for git
+Play, play, play
+Work, work, work
+------------
+
+and once you're happy with your manual merge, just do a
+
+------------
+$ git commit hello
+------------
+
+which will very loudly warn you that you're now committing a merge
+(which is correct, so never mind), and you can write a small merge
+message about your adventures in git-merge-land.
+
+After you're done, start up `gitk \--all` to see graphically what the
+history looks like. Notice that `mybranch` still exists, and you can
+switch to it, and continue to work with it if you want to. The
+`mybranch` branch will not contain the merge, but next time you merge it
+from the `master` branch, git will know how you merged it, so you'll not
+have to do _that_ merge again.
+
+Another useful tool, especially if you do not always work in X-Window
+environment, is `git show-branch`.
+
+------------------------------------------------
+$ git show-branch master mybranch
+* [master] Merge work in mybranch
+ ! [mybranch] Some work.
+--
+-  [master] Merge work in mybranch
+*+ [mybranch] Some work.
+------------------------------------------------
+
+The first two lines indicate that it is showing the two branches
+and the first line of the commit log message from their
+top-of-the-tree commits, you are currently on `master` branch
+(notice the asterisk `*` character), and the first column for
+the later output lines is used to show commits contained in the
+`master` branch, and the second column for the `mybranch`
+branch. Three commits are shown along with their log messages.
+All of them have non blank characters in the first column (`*`
+shows an ordinary commit on the current branch, `.` is a merge commit), which
+means they are now part of the `master` branch. Only the "Some
+work" commit has the plus `+` character in the second column,
+because `mybranch` has not been merged to incorporate these
+commits from the master branch.  The string inside brackets
+before the commit log message is a short name you can use to
+name the commit.  In the above example, 'master' and 'mybranch'
+are branch heads.  'master~1' is the first parent of 'master'
+branch head.  Please see 'git-rev-parse' documentation if you
+see more complex cases.
+
+Now, let's pretend you are the one who did all the work in
+`mybranch`, and the fruit of your hard work has finally been merged
+to the `master` branch. Let's go back to `mybranch`, and run
+resolve to get the "upstream changes" back to your branch.
+
+------------
+$ git checkout mybranch
+$ git merge "Merge upstream changes." HEAD master
+------------
+
+This outputs something like this (the actual commit object names
+would be different)
+
+----------------
+Updating from ae3a2da... to a80b4aa....
+ example |    1 +
+ hello   |    1 +
+ 2 files changed, 2 insertions(+), 0 deletions(-)
+----------------
+
+Because your branch did not contain anything more than what are
+already merged into the `master` branch, the resolve operation did
+not actually do a merge. Instead, it just updated the top of
+the tree of your branch to that of the `master` branch. This is
+often called 'fast forward' merge.
+
+You can run `gitk \--all` again to see how the commit ancestry
+looks like, or run `show-branch`, which tells you this.
+
+------------------------------------------------
+$ git show-branch master mybranch
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
+--
+-- [master] Merge work in mybranch
+------------------------------------------------
+
+
+Merging external work
+---------------------
+
+It's usually much more common that you merge with somebody else than
+merging with your own branches, so it's worth pointing out that git
+makes that very easy too, and in fact, it's not that different from
+doing a `git merge`. In fact, a remote merge ends up being nothing
+more than "fetch the work from a remote repository into a temporary tag"
+followed by a `git merge`.
+
+Fetching from a remote repository is done by, unsurprisingly,
+`git fetch`:
+
+----------------
+$ git fetch <remote-repository>
+----------------
+
+One of the following transports can be used to name the
+repository to download from:
+
+Rsync::
+       `rsync://remote.machine/path/to/repo.git/`
++
+Rsync transport is usable for both uploading and downloading,
+but is completely unaware of what git does, and can produce
+unexpected results when you download from the public repository
+while the repository owner is uploading into it via `rsync`
+transport.  Most notably, it could update the files under
+`refs/` which holds the object name of the topmost commits
+before uploading the files in `objects/` -- the downloader would
+obtain head commit object name while that object itself is still
+not available in the repository.  For this reason, it is
+considered deprecated.
+
+SSH::
+       `remote.machine:/path/to/repo.git/` or
++
+`ssh://remote.machine/path/to/repo.git/`
++
+This transport can be used for both uploading and downloading,
+and requires you to have a log-in privilege over `ssh` to the
+remote machine.  It finds out the set of objects the other side
+lacks by exchanging the head commits both ends have and
+transfers (close to) minimum set of objects.  It is by far the
+most efficient way to exchange git objects between repositories.
+
+Local directory::
+       `/path/to/repo.git/`
++
+This transport is the same as SSH transport but uses `sh` to run
+both ends on the local machine instead of running other end on
+the remote machine via `ssh`.
+
+git Native::
+       `git://remote.machine/path/to/repo.git/`
++
+This transport was designed for anonymous downloading.  Like SSH
+transport, it finds out the set of objects the downstream side
+lacks and transfers (close to) minimum set of objects.
+
+HTTP(S)::
+       `http://remote.machine/path/to/repo.git/`
++
+Downloader from http and https URL
+first obtains the topmost commit object name from the remote site
+by looking at the specified refname under `repo.git/refs/` directory,
+and then tries to obtain the
+commit object by downloading from `repo.git/objects/xx/xxx\...`
+using the object name of that commit object.  Then it reads the
+commit object to find out its parent commits and the associate
+tree object; it repeats this process until it gets all the
+necessary objects.  Because of this behaviour, they are
+sometimes also called 'commit walkers'.
++
+The 'commit walkers' are sometimes also called 'dumb
+transports', because they do not require any git aware smart
+server like git Native transport does.  Any stock HTTP server
+that does not even support directory index would suffice.  But
+you must prepare your repository with `git-update-server-info`
+to help dumb transport downloaders.
++
+There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
+programs, which are 'commit walkers'; they outlived their
+usefulness when git Native and SSH transports were introduced,
+and not used by `git pull` or `git push` scripts.
+
+Once you fetch from the remote repository, you `resolve` that
+with your current branch.
+
+However -- it's such a common thing to `fetch` and then
+immediately `resolve`, that it's called `git pull`, and you can
+simply do
+
+----------------
+$ git pull <remote-repository>
+----------------
+
+and optionally give a branch-name for the remote end as a second
+argument.
+
+[NOTE]
+You could do without using any branches at all, by
+keeping as many local repositories as you would like to have
+branches, and merging between them with `git pull`, just like
+you merge between branches. The advantage of this approach is
+that it lets you keep set of files for each `branch` checked
+out and you may find it easier to switch back and forth if you
+juggle multiple lines of development simultaneously. Of
+course, you will pay the price of more disk usage to hold
+multiple working trees, but disk space is cheap these days.
+
+[NOTE]
+You could even pull from your own repository by
+giving '.' as <remote-repository> parameter to `git pull`.  This
+is useful when you want to merge a local branch (or more, if you
+are making an Octopus) into the current branch.
+
+It is likely that you will be pulling from the same remote
+repository from time to time. As a short hand, you can store
+the remote repository URL in a file under .git/remotes/
+directory, like this:
+
+------------------------------------------------
+$ mkdir -p .git/remotes/
+$ cat >.git/remotes/linus <<\EOF
+URL: http://www.kernel.org/pub/scm/git/git.git/
+EOF
+------------------------------------------------
+
+and use the filename to `git pull` instead of the full URL.
+The URL specified in such file can even be a prefix
+of a full URL, like this:
+
+------------------------------------------------
+$ cat >.git/remotes/jgarzik <<\EOF
+URL: http://www.kernel.org/pub/scm/linux/git/jgarzik/
+EOF
+------------------------------------------------
+
+
+Examples.
+
+. `git pull linus`
+. `git pull linus tag v0.99.1`
+. `git pull jgarzik/netdev-2.6.git/ e100`
+
+the above are equivalent to:
+
+. `git pull http://www.kernel.org/pub/scm/git/git.git/ HEAD`
+. `git pull http://www.kernel.org/pub/scm/git/git.git/ tag v0.99.1`
+. `git pull http://www.kernel.org/pub/.../jgarzik/netdev-2.6.git e100`
+
+
+How does the merge work?
+------------------------
+
+We said this tutorial shows what plumbing does to help you cope
+with the porcelain that isn't flushing, but we so far did not
+talk about how the merge really works.  If you are following
+this tutorial the first time, I'd suggest to skip to "Publishing
+your work" section and come back here later.
+
+OK, still with me?  To give us an example to look at, let's go
+back to the earlier repository with "hello" and "example" file,
+and bring ourselves back to the pre-merge state:
+
+------------
+$ git show-branch --more=3 master mybranch
+! [master] Merge work in mybranch
+ * [mybranch] Merge work in mybranch
+--
+-- [master] Merge work in mybranch
++* [master^2] Some work.
++* [master^] Some fun.
+------------
+
+Remember, before running `git merge`, our `master` head was at
+"Some fun." commit, while our `mybranch` head was at "Some
+work." commit.
+
+------------
+$ git checkout mybranch
+$ git reset --hard master^2
+$ git checkout master
+$ git reset --hard master^
+------------
+
+After rewinding, the commit structure should look like this:
+
+------------
+$ git show-branch
+* [master] Some fun.
+ ! [mybranch] Some work.
+--
+ + [mybranch] Some work.
+*  [master] Some fun.
+*+ [mybranch^] New day.
+------------
+
+Now we are ready to experiment with the merge by hand.
+
+`git merge` command, when merging two branches, uses 3-way merge
+algorithm.  First, it finds the common ancestor between them.
+The command it uses is `git-merge-base`:
+
+------------
+$ mb=$(git-merge-base HEAD mybranch)
+------------
+
+The command writes the commit object name of the common ancestor
+to the standard output, so we captured its output to a variable,
+because we will be using it in the next step.  BTW, the common
+ancestor commit is the "New day." commit in this case.  You can
+tell it by:
+
+------------
+$ git-name-rev $mb
+my-first-tag
+------------
+
+After finding out a common ancestor commit, the second step is
+this:
+
+------------
+$ git-read-tree -m -u $mb HEAD mybranch
+------------
+
+This is the same `git-read-tree` command we have already seen,
+but it takes three trees, unlike previous examples.  This reads
+the contents of each tree into different 'stage' in the index
+file (the first tree goes to stage 1, the second stage 2,
+etc.).  After reading three trees into three stages, the paths
+that are the same in all three stages are 'collapsed' into stage
+0.  Also paths that are the same in two of three stages are
+collapsed into stage 0, taking the SHA1 from either stage 2 or
+stage 3, whichever is different from stage 1 (i.e. only one side
+changed from the common ancestor).
+
+After 'collapsing' operation, paths that are different in three
+trees are left in non-zero stages.  At this point, you can
+inspect the index file with this command:
+
+------------
+$ git-ls-files --stage
+100644 7f8b141b65fdcee47321e399a2598a235a032422 0      example
+100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
+100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
+------------
+
+In our example of only two files, we did not have unchanged
+files so only 'example' resulted in collapsing, but in real-life
+large projects, only small number of files change in one commit,
+and this 'collapsing' tends to trivially merge most of the paths
+fairly quickly, leaving only a handful the real changes in non-zero
+stages.
+
+To look at only non-zero stages, use `\--unmerged` flag:
+
+------------
+$ git-ls-files --unmerged
+100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
+100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
+------------
+
+The next step of merging is to merge these three versions of the
+file, using 3-way merge.  This is done by giving
+`git-merge-one-file` command as one of the arguments to
+`git-merge-index` command:
+
+------------
+$ git-merge-index git-merge-one-file hello
+Auto-merging hello.
+merge: warning: conflicts during merge
+ERROR: Merge conflict in hello.
+fatal: merge program failed
+------------
+
+`git-merge-one-file` script is called with parameters to
+describe those three versions, and is responsible to leave the
+merge results in the working tree.
+It is a fairly straightforward shell script, and
+eventually calls `merge` program from RCS suite to perform a
+file-level 3-way merge.  In this case, `merge` detects
+conflicts, and the merge result with conflict marks is left in
+the working tree..  This can be seen if you run `ls-files
+--stage` again at this point:
+
+------------
+$ git-ls-files --stage
+100644 7f8b141b65fdcee47321e399a2598a235a032422 0      example
+100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
+100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
+100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
+------------
+
+This is the state of the index file and the working file after
+`git merge` returns control back to you, leaving the conflicting
+merge for you to resolve.  Notice that the path `hello` is still
+unmerged, and what you see with `git diff` at this point is
+differences since stage 2 (i.e. your version).
+
+
+Publishing your work
+--------------------
+
+So we can use somebody else's work from a remote repository; but
+how can *you* prepare a repository to let other people pull from
+it?
+
+Your do your real work in your working tree that has your
+primary repository hanging under it as its `.git` subdirectory.
+You *could* make that repository accessible remotely and ask
+people to pull from it, but in practice that is not the way
+things are usually done. A recommended way is to have a public
+repository, make it reachable by other people, and when the
+changes you made in your primary working tree are in good shape,
+update the public repository from it. This is often called
+'pushing'.
+
+[NOTE]
+This public repository could further be mirrored, and that is
+how git repositories at `kernel.org` are managed.
+
+Publishing the changes from your local (private) repository to
+your remote (public) repository requires a write privilege on
+the remote machine. You need to have an SSH account there to
+run a single command, `git-receive-pack`.
+
+First, you need to create an empty repository on the remote
+machine that will house your public repository. This empty
+repository will be populated and be kept up-to-date by pushing
+into it later. Obviously, this repository creation needs to be
+done only once.
+
+[NOTE]
+`git push` uses a pair of programs,
+`git-send-pack` on your local machine, and `git-receive-pack`
+on the remote machine. The communication between the two over
+the network internally uses an SSH connection.
+
+Your private repository's git directory is usually `.git`, but
+your public repository is often named after the project name,
+i.e. `<project>.git`. Let's create such a public repository for
+project `my-git`. After logging into the remote machine, create
+an empty directory:
+
+------------
+$ mkdir my-git.git
+------------
+
+Then, make that directory into a git repository by running
+`git init-db`, but this time, since its name is not the usual
+`.git`, we do things slightly differently:
+
+------------
+$ GIT_DIR=my-git.git git-init-db
+------------
+
+Make sure this directory is available for others you want your
+changes to be pulled by via the transport of your choice. Also
+you need to make sure that you have the `git-receive-pack`
+program on the `$PATH`.
+
+[NOTE]
+Many installations of sshd do not invoke your shell as the login
+shell when you directly run programs; what this means is that if
+your login shell is `bash`, only `.bashrc` is read and not
+`.bash_profile`. As a workaround, make sure `.bashrc` sets up
+`$PATH` so that you can run `git-receive-pack` program.
+
+[NOTE]
+If you plan to publish this repository to be accessed over http,
+you should do `chmod +x my-git.git/hooks/post-update` at this
+point.  This makes sure that every time you push into this
+repository, `git-update-server-info` is run.
+
+Your "public repository" is now ready to accept your changes.
+Come back to the machine you have your private repository. From
+there, run this command:
+
+------------
+$ git push <public-host>:/path/to/my-git.git master
+------------
+
+This synchronizes your public repository to match the named
+branch head (i.e. `master` in this case) and objects reachable
+from them in your current repository.
+
+As a real example, this is how I update my public git
+repository. Kernel.org mirror network takes care of the
+propagation to other publicly visible machines:
+
+------------
+$ git push master.kernel.org:/pub/scm/git/git.git/ 
+------------
+
+
+Packing your repository
+-----------------------
+
+Earlier, we saw that one file under `.git/objects/??/` directory
+is stored for each git object you create. This representation
+is efficient to create atomically and safely, but
+not so convenient to transport over the network. Since git objects are
+immutable once they are created, there is a way to optimize the
+storage by "packing them together". The command
+
+------------
+$ git repack
+------------
+
+will do it for you. If you followed the tutorial examples, you
+would have accumulated about 17 objects in `.git/objects/??/`
+directories by now. `git repack` tells you how many objects it
+packed, and stores the packed file in `.git/objects/pack`
+directory.
+
+[NOTE]
+You will see two files, `pack-\*.pack` and `pack-\*.idx`,
+in `.git/objects/pack` directory. They are closely related to
+each other, and if you ever copy them by hand to a different
+repository for whatever reason, you should make sure you copy
+them together. The former holds all the data from the objects
+in the pack, and the latter holds the index for random
+access.
+
+If you are paranoid, running `git-verify-pack` command would
+detect if you have a corrupt pack, but do not worry too much.
+Our programs are always perfect ;-).
+
+Once you have packed objects, you do not need to leave the
+unpacked objects that are contained in the pack file anymore.
+
+------------
+$ git prune-packed
+------------
+
+would remove them for you.
+
+You can try running `find .git/objects -type f` before and after
+you run `git prune-packed` if you are curious.  Also `git
+count-objects` would tell you how many unpacked objects are in
+your repository and how much space they are consuming.
+
+[NOTE]
+`git pull` is slightly cumbersome for HTTP transport, as a
+packed repository may contain relatively few objects in a
+relatively large pack. If you expect many HTTP pulls from your
+public repository you might want to repack & prune often, or
+never.
+
+If you run `git repack` again at this point, it will say
+"Nothing to pack". Once you continue your development and
+accumulate the changes, running `git repack` again will create a
+new pack, that contains objects created since you packed your
+repository the last time. We recommend that you pack your project
+soon after the initial import (unless you are starting your
+project from scratch), and then run `git repack` every once in a
+while, depending on how active your project is.
+
+When a repository is synchronized via `git push` and `git pull`
+objects packed in the source repository are usually stored
+unpacked in the destination, unless rsync transport is used.
+While this allows you to use different packing strategies on
+both ends, it also means you may need to repack both
+repositories every once in a while.
+
+
+Working with Others
+-------------------
+
+Although git is a truly distributed system, it is often
+convenient to organize your project with an informal hierarchy
+of developers. Linux kernel development is run this way. There
+is a nice illustration (page 17, "Merges to Mainline") in Randy
+Dunlap's presentation (`http://tinyurl.com/a2jdg`).
+
+It should be stressed that this hierarchy is purely *informal*.
+There is nothing fundamental in git that enforces the "chain of
+patch flow" this hierarchy implies. You do not have to pull
+from only one remote repository.
+
+A recommended workflow for a "project lead" goes like this:
+
+1. Prepare your primary repository on your local machine. Your
+   work is done there.
+
+2. Prepare a public repository accessible to others.
++
+If other people are pulling from your repository over dumb
+transport protocols (HTTP), you need to keep this repository
+'dumb transport friendly'.  After `git init-db`,
+`$GIT_DIR/hooks/post-update` copied from the standard templates
+would contain a call to `git-update-server-info` but the
+`post-update` hook itself is disabled by default -- enable it
+with `chmod +x post-update`.  This makes sure `git-update-server-info`
+keeps the necessary files up-to-date.
+
+3. Push into the public repository from your primary
+   repository.
+
+4. `git repack` the public repository. This establishes a big
+   pack that contains the initial set of objects as the
+   baseline, and possibly `git prune` if the transport
+   used for pulling from your repository supports packed
+   repositories.
+
+5. Keep working in your primary repository. Your changes
+   include modifications of your own, patches you receive via
+   e-mails, and merges resulting from pulling the "public"
+   repositories of your "subsystem maintainers".
++
+You can repack this private repository whenever you feel like.
+
+6. Push your changes to the public repository, and announce it
+   to the public.
+
+7. Every once in a while, "git repack" the public repository.
+   Go back to step 5. and continue working.
+
+
+A recommended work cycle for a "subsystem maintainer" who works
+on that project and has an own "public repository" goes like this:
+
+1. Prepare your work repository, by `git clone` the public
+   repository of the "project lead". The URL used for the
+   initial cloning is stored in `.git/remotes/origin`.
+
+2. Prepare a public repository accessible to others, just like
+   the "project lead" person does.
+
+3. Copy over the packed files from "project lead" public
+   repository to your public repository, unless the "project
+   lead" repository lives on the same machine as yours.  In the
+   latter case, you can use `objects/info/alternates` file to
+   point at the repository you are borrowing from.
+
+4. Push into the public repository from your primary
+   repository. Run `git repack`, and possibly `git prune` if the
+   transport used for pulling from your repository supports
+   packed repositories.
+
+5. Keep working in your primary repository. Your changes
+   include modifications of your own, patches you receive via
+   e-mails, and merges resulting from pulling the "public"
+   repositories of your "project lead" and possibly your
+   "sub-subsystem maintainers".
++
+You can repack this private repository whenever you feel
+like.
+
+6. Push your changes to your public repository, and ask your
+   "project lead" and possibly your "sub-subsystem
+   maintainers" to pull from it.
+
+7. Every once in a while, `git repack` the public repository.
+   Go back to step 5. and continue working.
+
+
+A recommended work cycle for an "individual developer" who does
+not have a "public" repository is somewhat different. It goes
+like this:
+
+1. Prepare your work repository, by `git clone` the public
+   repository of the "project lead" (or a "subsystem
+   maintainer", if you work on a subsystem). The URL used for
+   the initial cloning is stored in `.git/remotes/origin`.
+
+2. Do your work in your repository on 'master' branch.
+
+3. Run `git fetch origin` from the public repository of your
+   upstream every once in a while. This does only the first
+   half of `git pull` but does not merge. The head of the
+   public repository is stored in `.git/refs/heads/origin`.
+
+4. Use `git cherry origin` to see which ones of your patches
+   were accepted, and/or use `git rebase origin` to port your
+   unmerged changes forward to the updated upstream.
+
+5. Use `git format-patch origin` to prepare patches for e-mail
+   submission to your upstream and send it out. Go back to
+   step 2. and continue.
+
+
+Working with Others, Shared Repository Style
+--------------------------------------------
+
+If you are coming from CVS background, the style of cooperation
+suggested in the previous section may be new to you. You do not
+have to worry. git supports "shared public repository" style of
+cooperation you are probably more familiar with as well.
+
+For this, set up a public repository on a machine that is
+reachable via SSH by people with "commit privileges".  Put the
+committers in the same user group and make the repository
+writable by that group.  Make sure their umasks are set up to
+allow group members to write into directories other members
+have created.
+
+You, as an individual committer, then:
+
+- First clone the shared repository to a local repository:
+------------------------------------------------
+$ git clone repo.shared.xz:/pub/scm/project.git/ my-project
+$ cd my-project
+$ hack away
+------------------------------------------------
+
+- Merge the work others might have done while you were hacking
+  away:
+------------------------------------------------
+$ git pull origin
+$ test the merge result
+------------------------------------------------
+[NOTE]
+================================
+The first `git clone` would have placed the following in
+`my-project/.git/remotes/origin` file, and that's why this and
+the next step work.
+------------
+URL: repo.shared.xz:/pub/scm/project.git/ my-project
+Pull: master:origin
+------------
+================================
+
+- push your work as the new head of the shared
+  repository.
+------------------------------------------------
+$ git push origin master
+------------------------------------------------
+If somebody else pushed into the same shared repository while
+you were working locally, `git push` in the last step would
+complain, telling you that the remote `master` head does not
+fast forward.  You need to pull and merge those other changes
+back before you push your work when it happens.
+
+The `git push` command without any explicit refspec parameter
+pushes the refs that exist both in the local repository and the
+remote repository.  So the last `push` can be done with either
+one of these:
+------------
+$ git push origin
+$ git push repo.shared.xz:/pub/scm/project.git/
+------------
+as long as the shared repository does not have any branches
+other than `master`.
+[NOTE]
+============
+If you created your shared repository by cloning from somewhere
+else, you may have the `origin` branch.  Your developers
+typically do not use that branch; remove it.  Otherwise, that
+would be pushed back by the `git push origin` because your
+developers' repository would surely have `origin` branch to keep
+track of the shared repository, and would be counted as "exist
+on both ends".
+============
+
+Advanced Shared Repository Management
+-------------------------------------
+
+Being able to push into a shared repository means being able to
+write into it.  If your developers are coming over the network,
+this means you, as the repository administrator, need to give
+each of them an SSH access to the shared repository machine.
+
+In some cases, though, you may not want to give a normal shell
+account to them, but want to restrict them to be able to only
+do `git push` into the repository and nothing else.
+
+You can achieve this by setting the login shell of your
+developers on the shared repository host to `git-shell` program.
+
+[NOTE]
+Most likely you would also need to list `git-shell` program in
+`/etc/shells` file.
+
+This restricts the set of commands that can be run from incoming
+SSH connection for these users to only `receive-pack` and
+`upload-pack`, so the only thing they can do are `git fetch` and
+`git push`.
+
+You still need to create UNIX user accounts for each developer,
+and put them in the same group.  Make sure that the repository
+shared among these developers is writable by that group.
+
+. Initializing the shared repository with `git-init-db --shared`
+helps somewhat.
+
+. Run the following in the shared repository:
++
+------------
+$ chgrp -R $group repo.git
+$ find repo.git -type d -print | xargs chmod ug+rwx,g+s
+$ GIT_DIR=repo.git git repo-config core.sharedrepository true
+------------
+
+The above measures make sure that directories lazily created in
+`$GIT_DIR` are writable by group members.  You, as the
+repository administrator, are still responsible to make sure
+your developers belong to that shared repository group and set
+their umask to a value no stricter than 027 (i.e. at least allow
+reading and searching by group members).
+
+You can implement finer grained branch policies using update
+hooks.  There is a document ("control access to branches") in
+Documentation/howto by Carl Baldwin and JC outlining how to (1)
+limit access to branch per user, (2) forbid overwriting existing
+tags.
+
+
+Bundling your work together
+---------------------------
+
+It is likely that you will be working on more than one thing at
+a time.  It is easy to manage those more-or-less independent tasks
+using branches with git.
+
+We have already seen how branches work previously,
+with "fun and work" example using two branches.  The idea is the
+same if there are more than two branches.  Let's say you started
+out from "master" head, and have some new code in the "master"
+branch, and two independent fixes in the "commit-fix" and
+"diff-fix" branches:
+
+------------
+$ git show-branch
+! [commit-fix] Fix commit message normalization.
+ ! [diff-fix] Fix rename detection.
+  * [master] Release candidate #1
+---
+ +  [diff-fix] Fix rename detection.
+ +  [diff-fix~1] Better common substring algorithm.
++   [commit-fix] Fix commit message normalization.
+  * [master] Release candidate #1
+++* [diff-fix~2] Pretty-print messages.
+------------
+
+Both fixes are tested well, and at this point, you want to merge
+in both of them.  You could merge in 'diff-fix' first and then
+'commit-fix' next, like this:
+
+------------
+$ git merge 'Merge fix in diff-fix' master diff-fix
+$ git merge 'Merge fix in commit-fix' master commit-fix
+------------
+
+Which would result in:
+
+------------
+$ git show-branch
+! [commit-fix] Fix commit message normalization.
+ ! [diff-fix] Fix rename detection.
+  * [master] Merge fix in commit-fix
+---
+  - [master] Merge fix in commit-fix
++ * [commit-fix] Fix commit message normalization.
+  - [master~1] Merge fix in diff-fix
+ +* [diff-fix] Fix rename detection.
+ +* [diff-fix~1] Better common substring algorithm.
+  * [master~2] Release candidate #1
+++* [master~3] Pretty-print messages.
+------------
+
+However, there is no particular reason to merge in one branch
+first and the other next, when what you have are a set of truly
+independent changes (if the order mattered, then they are not
+independent by definition).  You could instead merge those two
+branches into the current branch at once.  First let's undo what
+we just did and start over.  We would want to get the master
+branch before these two merges by resetting it to 'master~2':
+
+------------
+$ git reset --hard master~2
+------------
+
+You can make sure 'git show-branch' matches the state before
+those two 'git merge' you just did.  Then, instead of running
+two 'git merge' commands in a row, you would pull these two
+branch heads (this is known as 'making an Octopus'):
+
+------------
+$ git pull . commit-fix diff-fix
+$ git show-branch
+! [commit-fix] Fix commit message normalization.
+ ! [diff-fix] Fix rename detection.
+  * [master] Octopus merge of branches 'diff-fix' and 'commit-fix'
+---
+  - [master] Octopus merge of branches 'diff-fix' and 'commit-fix'
++ * [commit-fix] Fix commit message normalization.
+ +* [diff-fix] Fix rename detection.
+ +* [diff-fix~1] Better common substring algorithm.
+  * [master~1] Release candidate #1
+++* [master~2] Pretty-print messages.
+------------
+
+Note that you should not do Octopus because you can.  An octopus
+is a valid thing to do and often makes it easier to view the
+commit history if you are pulling more than two independent
+changes at the same time.  However, if you have merge conflicts
+with any of the branches you are merging in and need to hand
+resolve, that is an indication that the development happened in
+those branches were not independent after all, and you should
+merge two at a time, documenting how you resolved the conflicts,
+and the reason why you preferred changes made in one side over
+the other.  Otherwise it would make the project history harder
+to follow, not easier.
+
+[ to be continued.. cvsimports ]
index 0398b40..617d8f5 100644 (file)
@@ -146,3 +146,52 @@ the file that rename/copy produces, respectively.
 
 3.  TAB, LF, and backslash characters in pathnames are
     represented as `\t`, `\n`, and `\\`, respectively.
+
+
+combined diff format
+--------------------
+
+git-diff-tree and git-diff-files can take '-c' or '--cc' option
+to produce 'combined diff', which looks like this:
+
+------------
+diff --combined describe.c
+@@@ +98,7 @@@
+   return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
+  }
+
+- static void describe(char *arg)
+ -static void describe(struct commit *cmit, int last_one)
+++static void describe(char *arg, int last_one)
+  {
+ +     unsigned char sha1[20];
+ +     struct commit *cmit;
+------------
+
+Unlike the traditional 'unified' diff format, which shows two
+files A and B with a single column that has `-` (minus --
+appears in A but removed in B), `+` (plus -- missing in A but
+added to B), or ` ` (space -- unchanged) prefix, this format
+compares two or more files file1, file2,... with one file X, and
+shows how X differs from each of fileN.  One column for each of
+fileN is prepended to the output line to note how X's line is
+different from it.
+
+A `-` character in the column N means that the line appears in
+fileN but it does not appear in the last file.  A `+` character
+in the column N means that the line appears in the last file,
+and fileN does not have that line.
+
+In the above example output, the function signature was changed
+from both files (hence two `-` removals from both file1 and
+file2, plus `++` to mean one line that was added does not appear
+in either file1 nor file2).  Also two other lines are the same
+from file1 but do not appear in file2 (hence prefixed with ` +`).
+
+When shown by `git diff-tree -c`, it compares the parents of a
+merge commit with the merge result (i.e. file1..fileN are the
+parents).  When shown by `git diff-files -c`, it compares the
+two unresolved merge parents with the working tree file
+(i.e. file1 is stage 2 aka "our version", file2 is stage 3 aka
+"their version").
+
index 1fe8423..13f34d3 100644 (file)
@@ -3,6 +3,12 @@
        existing contents of `.git/FETCH_HEAD`.  Without this
        option old data in `.git/FETCH_HEAD` will be overwritten.
 
+\--upload-pack <upload-pack>::
+        When given, and the repository to fetch from is handled
+        by 'git-fetch-pack', '--exec=<upload-pack>' is passed to
+        the command to specify non-default path for the command
+        run on the other end.
+
 -f, \--force::
        When `git-fetch` is used with `<rbranch>:<lbranch>`
        refspec, it refuses to update the local branch
@@ -24,6 +30,9 @@
        flag lets all tags and their associated objects be
        downloaded.
 
+-k, \--keep::
+       Keep downloaded pack.
+
 -u, \--update-head-ok::
        By default `git-fetch` refuses to update the head which
        corresponds to the current branch.  This flag disables the
index 9442c66..df9a618 100644 (file)
@@ -7,7 +7,7 @@ git-checkout - Checkout and switch to a branch.
 
 SYNOPSIS
 --------
-'git-checkout' [-f] [-b <new_branch>] [<branch>] [<paths>...]
+'git-checkout' [-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]
 
 DESCRIPTION
 -----------
@@ -34,6 +34,19 @@ OPTIONS
 -b::
        Create a new branch and start it at <branch>.
 
+-m::
+       If you have local modifications to a file that is
+       different between the current branch and the branch you
+       are switching to, the command refuses to switch
+       branches, to preserve your modifications in context.
+       With this option, a three-way merge between the current
+       branch, your working tree contents, and the new branch
+       is done, and you will be on the new branch.
++
+When a merge conflict happens, the index entries for conflicting
+paths are left unmerged, and you need to resolve the conflicts
+and mark the resolved paths with `git update-index`.
+
 <new_branch>::
        Name for the new branch.
 
@@ -42,13 +55,13 @@ OPTIONS
        commit. Defaults to HEAD.
 
 
-EXAMPLE
--------
+EXAMPLES
+--------
 
-The following sequence checks out the `master` branch, reverts
+The following sequence checks out the `master` branch, reverts
 the `Makefile` to two revisions back, deletes hello.c by
 mistake, and gets it back from the index.
-
++
 ------------
 $ git checkout master <1>
 $ git checkout master~2 Makefile <2>
@@ -59,15 +72,64 @@ $ git checkout hello.c <3>
 <2> take out a file out of other commit
 <3> or "git checkout -- hello.c", as in the next example.
 ------------
-
++
 If you have an unfortunate branch that is named `hello.c`, the
 last step above would be confused as an instruction to switch to
 that branch.  You should instead write:
-
++
 ------------
 $ git checkout -- hello.c
 ------------
 
+. After working in a wrong branch, switching to the correct
+branch you would want to is done with:
++
+------------
+$ git checkout mytopic
+------------
++
+However, your "wrong" branch and correct "mytopic" branch may
+differ in files that you have locally modified, in which case,
+the above checkout would fail like this:
++
+------------
+$ git checkout mytopic
+fatal: Entry 'frotz' not uptodate. Cannot merge.
+------------
++
+You can give the `-m` flag to the command, which would try a
+three-way merge:
++
+------------
+$ git checkout -m mytopic
+Auto-merging frotz
+------------
++
+After this three-way merge, the local modifications are _not_
+registered in your index file, so `git diff` would show you what
+changes you made since the tip of the new branch.
+
+. When a merge conflict happens during switching branches with
+the `-m` option, you would see something like this:
++
+------------
+$ git checkout -m mytopic
+Auto-merging frotz
+merge: warning: conflicts during merge
+ERROR: Merge conflict in frotz
+fatal: merge program failed
+------------
++
+At this point, `git diff` shows the changes cleanly merged as in
+the previous example, as well as the changes in the conflicted
+files.  Edit and resolve the conflict and mark it resolved with
+`git update-index` as usual:
++
+------------
+$ edit frotz
+$ git update-index frotz
+------------
+
 
 Author
 ------
index 790b87b..684e4bd 100644 (file)
@@ -9,7 +9,7 @@ git-clone - Clones a repository.
 SYNOPSIS
 --------
 [verse]
-'git-clone' [-l [-s]] [-q] [-n] [-o <name>] [-u <upload-pack>]
+'git-clone' [-l [-s]] [-q] [-n] [--bare] [-o <name>] [-u <upload-pack>]
          <repository> [<directory>]
 
 DESCRIPTION
@@ -58,6 +58,14 @@ OPTIONS
 -n::
        No checkout of HEAD is performed after the clone is complete.
 
+--bare::
+       Make a 'bare' GIT repository.  That is, instead of
+       creating `<directory>` and placing the administrative
+       files in `<directory>/.git`, make the `<directory>`
+       itself the `$GIT_DIR`. This implies `-n` option.  When
+       this option is used, neither the `origin` branch nor the
+       default `remotes/origin` file is created.
+
 -o <name>::
        Instead of using the branch name 'origin' to keep track
        of the upstream repository, use <name> instead.  Note
@@ -103,6 +111,22 @@ $ cd copy
 $ git show-branch
 ------------
 
+
+Create a bare repository to publish your changes to the public::
++
+------------
+$ git clone --bare -l /home/proj/.git /pub/scm/proj.git
+------------
+
+
+Create a repository on the kernel.org machine that borrows from Linus::
++
+------------
+$ git clone --bare -l -s /pub/scm/.../torvalds/linux-2.6.git \
+    /pub/scm/.../me/subsys-2.6.git
+------------
+
+
 Author
 ------
 Written by Linus Torvalds <torvalds@osdl.org>
index e35984d..72f96fc 100644 (file)
@@ -27,7 +27,7 @@ OPTIONS
 -a|--all::
        Update all paths in the index file.  This flag notices
        files that have been modified and deleted, but new files
-       you have not told about git are not affected.
+       you have not told git about are not affected.
 
 -c or -C <commit>::
        Take existing commit object, and reuse the log message
index 01ca7ef..dfe86ce 100644 (file)
@@ -89,6 +89,26 @@ If you need to pass multiple options, separate them with a comma.
 -s <subst>::
        Substitute the character "/" in branch names with <subst>
 
+-A <author-conv-file>::
+       CVS by default uses the unix username when writing its
+       commit logs. Using this option and an author-conv-file
+       in this format
+
+       exon=Andreas Ericsson <ae@op5.se>
+       spawn=Simon Pawn <spawn@frog-pond.org>
+
+       git-cvsimport will make it appear as those authors had
+       their GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL set properly
+       all along.
+
+       For convenience, this data is saved to $GIT_DIR/cvs-authors
+       each time the -A option is provided and read from that same
+       file each time git-cvsimport is run.
+
+       It is not recommended to use this feature if you intend to
+       export changes back to CVS again later with
+       git-link[1]::git-cvsexportcommit.
+
 OUTPUT
 ------
 If '-v' is specified, the script reports what it is doing.
index 67f5126..481b8b3 100644 (file)
@@ -8,7 +8,7 @@ git-diff-files - Compares files in the working tree and the index
 
 SYNOPSIS
 --------
-'git-diff-files' [-q] [<common diff options>] [<path>...]
+'git-diff-files' [-q] [-0|-1|-2|-3|-c|--cc] [<common diff options>] [<path>...]
 
 DESCRIPTION
 -----------
@@ -30,6 +30,12 @@ The default is to diff against our branch (-2) and the
 cleanly resolved paths.  The option -0 can be given to
 omit diff output for unmerged entries and just show "Unmerged".
 
+-c,--cc::
+       This compares stage 2 (our branch), stage 3 (their
+       branch) and the working tree file and outputs a combined
+       diff, similar to the way 'diff-tree' shows a merge
+       commit with these flags.
+
 -q::
        Remain silent even on nonexisting files
 
index 91fb130..dd46d4f 100644 (file)
@@ -9,8 +9,9 @@ git-diff-tree - Compares the content and mode of blobs found via two tree object
 SYNOPSIS
 --------
 [verse]
-'git-diff-tree' [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty] [-t] [-r]
-             [--root] [<common diff options>] <tree-ish> [<tree-ish>] [<path>...]
+'git-diff-tree' [--stdin] [-m] [-s] [-v] [--no-commit-id] [--pretty]
+             [-t] [-r] [-c | --cc] [--root] [<common diff options>]
+             <tree-ish> [<tree-ish>] [<path>...]
 
 DESCRIPTION
 -----------
@@ -80,6 +81,20 @@ separated with a single space are given.
        git-diff-tree outputs a line with the commit ID when
        applicable.  This flag suppressed the commit ID output.
 
+-c,--cc::
+       These flags change the way a merge commit is displayed
+       (which means it is useful only when the command is given
+       one <tree-ish>, or '--stdin').  It shows the differences
+       from each of the parents to the merge result
+       simultaneously, instead of showing pairwise diff between
+       a parent and the result one at a time, which '-m' option
+       output does.  '--cc' further compresses the output by
+       omiting hunks that show differences from only one
+       parent, or show the same change from all but one parent
+       for an Octopus merge.  When this optimization makes all
+       hunks disappear, the commit itself and the commit log
+       message is not shown, unless '-m' is specified.
+
 
 Limiting Output
 ---------------
index 2bfd8ed..bf4b592 100644 (file)
@@ -8,7 +8,7 @@ git-grep - print lines matching a pattern
 
 SYNOPSIS
 --------
-'git-grep' [<option>...] <pattern> [<path>...]
+'git-grep' [<option>...] [-e] <pattern> [--] [<path>...]
 
 DESCRIPTION
 -----------
@@ -18,13 +18,24 @@ containing a match to the given pattern.
 
 OPTIONS
 -------
+`--`::
+       Signals the end of options; the rest of the parameters
+       are <path> limiters.
+
 <option>...::
        Either an option to pass to `grep` or `git-ls-files`.
-       Some `grep` options, such as `-C` and `-m`, that take
-       parameters are known to `git-grep`.
+
+       The following are the specific `git-ls-files` options
+       that may be given: `-o`, `--cached`, `--deleted`, `--others`,
+       `--killed`, `--ignored`, `--modified`, `--exclude=*`,
+       `--exclude-from=*`, and `--exclude-per-directory=*`.
+
+       All other options will be passed to `grep`.
 
 <pattern>::
-       The pattern to look for.
+       The pattern to look for.  The first non option is taken
+       as the pattern; if your pattern begins with a dash, use
+       `-e <pattern>`.
 
 <path>...::
        Optional paths to limit the set of files to be searched;
index e42af5e..e433407 100644 (file)
@@ -46,6 +46,10 @@ OPTIONS
 -s|--stage::
        Show stage files in the output
 
+--directory::
+       If a whole directory is classified as "other", show just its
+       name (with a trailing slash) and not its whole contents.
+
 -u|--unmerged::
        Show unmerged files in the output (forces --stage)
 
index 29fa307..a0ef61d 100644 (file)
@@ -8,7 +8,7 @@ git-push - Update remote refs along with associated objects.
 
 SYNOPSIS
 --------
-'git-push' [--all] [--force] <repository> <refspec>...
+'git-push' [--all] [--tags] [--force] <repository> <refspec>...
 
 DESCRIPTION
 -----------
@@ -29,6 +29,11 @@ include::pull-fetch-param.txt[]
        Instead of naming each ref to push, specifies all refs
        to be pushed.
 
+\--tags::
+       All refs under `$GIT_DIR/refs/tags` are pushed, in
+       addition to refspecs explicitly listed on the command
+       line.
+
 -f, \--force::
        Usually, the command refuses to update a remote ref that is
        not a descendent of the local ref used to overwrite it.
index 315683a..b4e737e 100644 (file)
@@ -147,8 +147,8 @@ and resets the tip of the branch to that commit.
 
 Interrupted workflow::
 +
-You can get interrupted by an ungent fix request while you are
-still in the middle of a large change.  The files in your
+Suppose you are interrupted by an urgent fix request while you
+are in the middle of a large change.  The files in your
 working tree are not in any shape to be committed yet, but you
 need to get to the other branch for a quick bugfix.
 +
@@ -164,8 +164,8 @@ $ git reset --soft HEAD^ ;# go back to WIP state <2>
 $ git reset <3>
 
 <1> This commit will get blown away so a throw-away log message is OK.
-<2> This removes the 'WIP' commit from the commit history, and makes
-    your working tree in the state just before you made that snapshot.
+<2> This removes the 'WIP' commit from the commit history, and sets
+    your working tree to the state just before you made that snapshot.
 <3> After <2>, the index file still has all the WIP changes you
     committed in <1>.  This sets it to the last commit you were
     basing the WIP changes on.
index f9146f1..1c6146c 100644 (file)
@@ -14,6 +14,7 @@ SYNOPSIS
             [ \--min-age=timestamp ]
             [ \--sparse ]
             [ \--no-merges ]
+            [ \--remove-empty ]
             [ \--all ]
             [ [ \--merge-order [ \--show-breaks ] ] | [ \--topo-order ] ]
             [ \--parents ]
@@ -80,6 +81,9 @@ OPTIONS
        (still subject to count and age limitation), but apply
        merge simplification nevertheless.
 
+--remove-empty::
+       Stop when a given path disappears from the tree.
+
 --all::
        Pretend as if all the refs in `$GIT_DIR/refs/` are
        listed on the command line as <commit>.
index 5b76f3b..7b1a9c9 100644 (file)
@@ -7,7 +7,10 @@ git-show-branch - Show branches and their commits.
 
 SYNOPSIS
 --------
-'git-show-branch [--all] [--heads] [--tags] [--topo-order] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [<rev> | <glob>]...'
+[verse]
+git-show-branch [--all] [--heads] [--tags] [--topo-order] [--current]
+       [--more=<n> | --list | --independent | --merge-base]
+       [--no-name | --sha1-name] [<rev> | <glob>]...
 
 DESCRIPTION
 -----------
@@ -18,6 +21,9 @@ and/or $GIT_DIR/refs/tags) semi-visually.
 
 It cannot show more than 29 branches and commits at a time.
 
+It uses `showbranch.default` multi-valued configuration items if
+no <rev> nor <glob> is given on the command line.
+
 
 OPTIONS
 -------
@@ -35,6 +41,11 @@ OPTIONS
        Show all refs under $GIT_DIR/refs, $GIT_DIR/refs/heads,
        and $GIT_DIR/refs/tags, respectively.
 
+--current::
+       With this option, the command includes the current
+       branch to the list of revs to be shown when it is not
+       given on the command line.
+
 --topo-order::
         By default, the branches and their commits are shown in
         reverse chronological order.  This option makes them
@@ -50,7 +61,7 @@ OPTIONS
        tree.
 
 --list::
-       Synomym to `--more=-1`
+       Synonym to `--more=-1`
 
 --merge-base::
        Instead of showing the commit list, just act like the
@@ -78,13 +89,14 @@ OUTPUT
 ------
 Given N <references>, the first N lines are the one-line
 description from their commit message.  The branch head that is
-pointed at by $GIT_DIR/HEAD is prefixed with an asterisk '*'
-character while other heads are prefixed with a '!' character.
+pointed at by $GIT_DIR/HEAD is prefixed with an asterisk `*`
+character while other heads are prefixed with a `!` character.
 
 Following these N lines, one-line log for each commit is
 displayed, indented N places.  If a commit is on the I-th
-branch, the I-th indentation character shows a '+' sign;
-otherwise it shows a space.  Each commit shows a short name that
+branch, the I-th indentation character shows a `+` sign;
+otherwise it shows a space.  Merge commits are denoted by
+a `-` sign.  Each commit shows a short name that
 can be used as an extended SHA1 to name that commit.
 
 The following example shows three branches, "master", "fixes"
@@ -92,7 +104,7 @@ and "mhf":
 
 ------------------------------------------------
 $ git show-branch master fixes mhf
-! [master] Add 'git show-branch'.
+* [master] Add 'git show-branch'.
  ! [fixes] Introduce "reset type" flag to "git reset"
   ! [mhf] Allow "+remote:local" refspec to cause --force when fetching.
 ---
@@ -106,13 +118,33 @@ $ git show-branch master fixes mhf
   + [mhf~6] Retire git-parse-remote.
   + [mhf~7] Multi-head fetch.
   + [mhf~8] Start adding the $GIT_DIR/remotes/ support.
-+++ [master] Add 'git show-branch'.
+*++ [master] Add 'git show-branch'.
 ------------------------------------------------
 
 These three branches all forked from a common commit, [master],
 whose commit message is "Add 'git show-branch'.  "fixes" branch
 adds one commit 'Introduce "reset type"'.  "mhf" branch has many
-other commits.
+other commits.  The current branch is "master".
+
+
+EXAMPLE
+-------
+
+If you keep your primary branches immediately under
+`$GIT_DIR/refs/heads`, and topic branches in subdirectories of
+it, having the following in the configuration file may help:
+
+------------
+[showbranch]
+       default = --topo-order
+       default = heads/*
+
+------------
+
+With this,`git show-branch` without extra parameters would show
+only the primary branches.  In addition, if you happen to be on
+your topic branch, it is shown as well.
+
 
 
 Author
index db1ce38..63e28b8 100644 (file)
@@ -21,7 +21,7 @@ DESCRIPTION
 Imports a SVN repository into git. It will either create a new
 repository, or incrementally import into an existing one.
 
-SVN access is done by the SVN:: Perl module.
+SVN access is done by the SVN::Perl module.
 
 git-svnimport assumes that SVN repositories are organized into one
 "trunk" directory where the main development happens, "branch/FOO"
@@ -74,8 +74,8 @@ When importing incrementally, you might need to edit the .git/svn2git file.
 -l <max_rev>::
        Specify a maximum revision number to pull.
 
-       Formerly, this option controlled how many revisions to pull, due to
-       SVN memory leaks. (These have been worked around.)
+       Formerly, this option controlled how many revisions to pull,
+       due to SVN memory leaks. (These have been worked around.)
 
 -v::
        Verbosity: let 'svnimport' report what it is doing.
@@ -100,11 +100,7 @@ with a 40x error pretty quickly.
 If you're using the "-d" or "-D" option, this is the URL of the SVN
 repository itself; it usually ends in "/svn".
 
-<SVN_repository_URL>::
-       The URL of the SVN module you want to import. For local
-       repositories, use "file:///absolute/path".
-
-<path>
+<path>::
        The path to the module you want to check out.
 
 -h::
index 92cfe0e..e8ef3ef 100644 (file)
@@ -43,12 +43,13 @@ people.  The <<Discussion>> section below contains much useful
 definition and clarification - read that first.
 
 If you are interested in using git to manage (version control)
-projects, use link:everyday.html[Everyday GIT] as a guide to the
+projects, use link:tutorial.html[The Tutorial] to get you started,
+and then link:everyday.html[Everyday GIT] as a guide to the
 minimum set of commands you need to know for day-to-day work.
 Most likely, that will get you started, and you can go a long
 way without knowing the low level details too much.
 
-The link:tutorial.html[tutorial] document covers how things
+The link:core-tutorial.html[Core tutorial] document covers how things
 internally work.
 
 If you are migrating from CVS, link:cvs-migration.html[cvs
@@ -585,7 +586,7 @@ git Diffs
 
 Discussion[[Discussion]]
 ------------------------
-include::../README[]
+include::README[]
 
 Authors
 -------
index 5a7e0cf..d10476b 100644 (file)
@@ -32,16 +32,16 @@ merge introduced 5 commits or so:
 
 ------------------------------------------------
 $ git show-branch --more=4 master master^2 | head
-! [master] Merge refs/heads/portable from http://www.cs.berkeley....
+* [master] Merge refs/heads/portable from http://www.cs.berkeley....
  ! [master^2] Replace C99 array initializers with code.
 --
-+  [master] Merge refs/heads/portable from http://www.cs.berkeley....
-++ [master^2] Replace C99 array initializers with code.
-++ [master^2~1] Replace unsetenv() and setenv() with older putenv().
-++ [master^2~2] Include sys/time.h in daemon.c.
-++ [master^2~3] Fix ?: statements.
-++ [master^2~4] Replace zero-length array decls with [].
-+  [master~1] tutorial note about git branch
+-  [master] Merge refs/heads/portable from http://www.cs.berkeley....
+*+ [master^2] Replace C99 array initializers with code.
+*+ [master^2~1] Replace unsetenv() and setenv() with older putenv().
+*+ [master^2~2] Include sys/time.h in daemon.c.
+*+ [master^2~3] Fix ?: statements.
+*+ [master^2~4] Replace zero-length array decls with [].
+*  [master~1] tutorial note about git branch
 ------------------------------------------------
 
 The '--more=4' above means "after we reach the merge base of refs,
@@ -193,8 +193,8 @@ $ git show-branch --more=1 master pu rc
  +  [pu~4] Document "git cherry-pick" and "git revert"
  +  [pu~5] Remove git-apply-patch-script.
  +  [pu~6] Redo "revert" using three-way merge machinery.
-  + [rc] Merge refs/heads/master from .
-+++ [master] Revert "Replace zero-length array decls with []."
-  + [rc~1] Merge refs/heads/master from .
-+++ [master~1] Merge refs/heads/portable from http://www.cs.berkeley....
+  - [rc] Merge refs/heads/master from .
+++* [master] Revert "Replace zero-length array decls with []."
+  - [rc~1] Merge refs/heads/master from .
+... [master~1] Merge refs/heads/portable from http://www.cs.berkeley....
 ------------------------------------------------
index b3d592f..2c98194 100644 (file)
@@ -9,7 +9,7 @@ GIT as a Linux subsystem maintainer.
 
 -Tony
 
-Last updated w.r.t. GIT 0.99.9f
+Last updated w.r.t. GIT 1.1
 
 Linux subsystem maintenance using GIT
 -------------------------------------
@@ -92,6 +92,14 @@ These can be easily kept up to date by merging from the "linus" branch:
  $ git checkout test && git merge "Auto-update from upstream" test linus
  $ git checkout release && git merge "Auto-update from upstream" release linus
 
+Important note!  If you have any local changes in these branches, then
+this merge will create a commit object in the history (with no local
+changes git will simply do a "Fast forward" merge).  Many people dislike
+the "noise" that this creates in the Linux history, so you should avoid
+doing this capriciously in the "release" branch, as these noisy commits
+will become part of the permanent history when you ask Linus to pull
+from the release branch.
+
 Set up so that you can push upstream to your public tree (you need to
 log-in to the remote system and create an empty tree there before the
 first push).
index 0347cab..1f19bf8 100644 (file)
@@ -3,7 +3,7 @@ git repository layout
 
 You may find these things in your git repository (`.git`
 directory for a repository associated with your working tree, or
-`'project'.git` directory for a public 'naked' repository).
+`'project'.git` directory for a public 'bare' repository).
 
 objects::
        Object store associated with this repository.  Usually
@@ -73,7 +73,7 @@ HEAD::
        A symlink of the form `refs/heads/'name'` to point at
        the current branch, if exists.  It does not mean much if
        the repository is not associated with any working tree
-       (i.e. 'naked' repository), but a valid git repository
+       (i.e. a 'bare' repository), but a valid git repository
        *must* have such a symlink here.  It is legal if the
        named branch 'name' does not (yet) exist.
 
@@ -92,7 +92,7 @@ hooks::
 
 index::
        The current index file for the repository.  It is
-       usually not found in a naked repository.
+       usually not found in a bare repository.
 
 info::
        Additional information about the repository is recorded
index edd91cb..66680d7 100644 (file)
-A short git tutorial
-====================
+A tutorial introduction to git
+==============================
 
-Introduction
-------------
+This tutorial explains how to import a new project into git, make
+changes to it, and share changes with other developers.
 
-This is trying to be a short tutorial on setting up and using a git
-repository, mainly because being hands-on and using explicit examples is
-often the best way of explaining what is going on.
+First, note that you can get documentation for a command such as "git
+diff" with:
 
-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.
-
-The material presented here often goes deep describing how things
-work internally.  If you are mostly interested in using git as a
-SCM, you can skip them during your first pass.
-
-[NOTE]
-And those "too deep" descriptions are often marked as Note.
-
-[NOTE]
-If you are already familiar with another version control system,
-like CVS, you may want to take a look at
-link:everyday.html[Everyday GIT in 20 commands or so] first
-before reading this.
-
-
-Creating a git repository
--------------------------
+------------------------------------------------
+$ man git-diff
+------------------------------------------------
 
-Creating a new git repository couldn't be easier: all git repositories 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. 
+Importing a new project
+-----------------------
 
-For our first example, we're going to start a totally new repository 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`:
+Assume you have a tarball project.tar.gz with your initial work.  You
+can place it under git revision control as follows.
 
 ------------------------------------------------
-$ mkdir git-tutorial
-$ cd git-tutorial
-$ git-init-db
+$ tar xzf project.tar.gz
+$ cd project
+$ git init-db
 ------------------------------------------------
 
-to which git will reply
+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, it should show you
-three entries, among other things:
-
- - a symlink called `HEAD`, pointing to `refs/heads/master` (if your
-   platform does not have native symlinks, it is a file containing the
-   line "ref: 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
-   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 in 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.
-
-[NOTE]
-An 'object' is identified by its 160-bit SHA1 hash, aka 'object 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 these `refs` subdirectories when you actually start
-populating your tree.
-
-[NOTE]
-An advanced user may want to take a look at the
-link:repository-layout.html[repository layout] document
-after finishing this tutorial.
-
-You have now created your first git repository. Of course, since it's
-empty, that's not very useful, so let's start populating it with data.
-
-
-Populating a git repository
----------------------------
-
-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 repository. We'll start off with a few bad examples, just to
-get a feel for how this works:
+You've now initialized the working directory--you may notice a new
+directory created, named ".git".  Tell git that you want it to track
+every file under the current directory with
 
 ------------------------------------------------
-$ echo "Hello World" >hello
-$ echo "Silly example" >example
+$ git add .
 ------------------------------------------------
 
-you have now created two files in your working tree (aka 'working directory'), but to
-actually check in your hard work, you will have to go through two steps:
-
- - fill in the 'index' file (aka 'cache') with the information about your
-   working tree state.
+Finally,
 
- - commit that index file as an object.
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
 
-The first step is trivial: when you want to tell git about any changes
-to your working tree, you use the `git-update-index` 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 index
-(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.
+will prompt you for a commit message, then record the current state
+of all the files to the repository.
 
-So to populate the index with the two files you just created, you can do
+Try modifying some files, then run
 
 ------------------------------------------------
-$ git-update-index --add hello example
+$ git diff
 ------------------------------------------------
 
-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
-database. If you did exactly the steps above, you should now be able to do
+to review your changes.  When you're done,
 
+------------------------------------------------
+$ git commit -a
+------------------------------------------------
 
-----------------
-$ ls .git/objects/??/*
-----------------
+will again prompt your for a message describing the change, and then
+record the new versions of the modified files.
 
-and see two files:
+A note on commit messages: Though not required, it's a good idea to
+begin the commit message with a single short (less than 50 character)
+line summarizing the change, followed by a blank line and then a more
+thorough description.  Tools that turn commits into email, for
+example, use the first line on the Subject line and the rest of the
+commit in the body.
 
-----------------
-.git/objects/55/7db03de997c86a4a028e1ebd3a1ceb225be238 
-.git/objects/f2/4c74a2e500f5ee1332c86b94199f52b1d1d962
-----------------
+To add a new file, first create the file, then
 
-which correspond with the objects with names of 557db... and f24c7..
-respectively.
+------------------------------------------------
+$ git add path/to/new/file
+------------------------------------------------
 
-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:
+then commit as usual.  No special command is required when removing a
+file; just remove it, then commit.
 
-----------------
-$ git-cat-file -t 557db03de997c86a4a028e1ebd3a1ceb225be238
-----------------
+At any point you can view the history of your changes using
 
-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 whatchanged
+------------------------------------------------
 
-----------------
-$ git-cat-file "blob" 557db03
-----------------
+If you also want to see complete diffs at each step, use
 
-which will print out "Hello World". The object 557db03 is nothing
-more than the contents of your file `hello`.
+------------------------------------------------
+$ git whatchanged -p
+------------------------------------------------
 
-[NOTE]
-Don't confuse that object with the file `hello` itself. The
-object is literally just those specific *contents* of the file, and
-however much you later change the contents in file `hello`, the object
-we just looked at will never change. Objects are immutable.
+Managing branches
+-----------------
 
-[NOTE]
-The second example demonstrates that you can
-abbreviate the object name to only the first several
-hexadecimal digits in most places.
+A single git repository can maintain multiple branches of
+development.  To create a new branch named "experimental", use
 
-Anyway, as we mentioned previously, you normally never actually take a
-look at the objects themselves, and typing long 40-character hex
-names is not something you'd normally want to do. The above digression
-was just to show that `git-update-index` did something magical, and
-actually saved away the contents of your files into the git object
-database.
+------------------------------------------------
+$ git branch experimental
+------------------------------------------------
 
-Updating the index 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.
+If you now run
 
-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. 
+------------------------------------------------
+$ git branch
+------------------------------------------------
 
-In particular, let's not even check in the two files into git yet, we'll
-start off by adding another line to `hello` first:
+you'll get a list of all existing branches:
 
 ------------------------------------------------
-$ echo "It's a new day for git" >>hello
+  experimental
+* master
 ------------------------------------------------
 
-and you can now, since you told git about the previous state of `hello`, ask
-git what has changed in the tree compared to your old index, using the
-`git-diff-files` command:
+The "experimental" branch is the one you just created, and the
+"master" branch is a default branch that was created for you
+automatically.  The asterisk marks the branch you are currently on;
+type
 
-------------
-$ git-diff-files
-------------
+------------------------------------------------
+$ git checkout experimental
+------------------------------------------------
 
-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 "hello" has been modified, and that the old object
-contents it had have been replaced with something else.
+to switch to the experimental branch.  Now edit a file, commit the
+change, and switch back to the master branch:
 
-To make it readable, we can tell git-diff-files to output the
-differences as a patch, using the `-p` flag:
+------------------------------------------------
+(edit file)
+$ git commit -a
+$ git checkout master
+------------------------------------------------
 
-------------
-$ git-diff-files -p
-diff --git a/hello b/hello
-index 557db03..263414f 100644
---- a/hello
-+++ b/hello
-@@ -1 +1,2 @@
- Hello World
-+It's a new day for git
-----
+Check that the change you made is no longer visible, since it was
+made on the experimental branch and you're back on the master branch.
 
-i.e. the diff of the change we caused by adding another line to `hello`.
+You can make a different change on the master branch:
 
-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.
+------------------------------------------------
+(edit file)
+$ git commit -a
+------------------------------------------------
 
-A common shorthand for `git-diff-files -p` is to just write `git
-diff`, which will do the same thing.
+at this point the two branches have diverged, with different changes
+made in each.  To merge the changes made in the two branches, run
 
-------------
-$ git diff
-diff --git a/hello b/hello
-index 557db03..263414f 100644
---- a/hello
-+++ b/hello
-@@ -1 +1,2 @@
- Hello World
-+It's a new day for git
-------------
-
-
-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 pull . experimental
+------------------------------------------------
+
+If the changes don't conflict, you're done.  If there are conflicts,
+markers will be left in the problematic files showing the conflict;
 
 ------------------------------------------------
-$ git-write-tree
+$ git diff
 ------------------------------------------------
 
-and this will just output the name of the resulting tree, in this case
-(if you have done exactly as I've described) it should be
-
-----------------
-8988da15d077d4829fc51d8544c097def6644dbb
-----------------
-
-which is another incomprehensible object name. Again, if you want to,
-you can use `git-cat-file -t 8988d\...` 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 repository, and it has no parents, we only need to pass in
-the object name of the tree. However, `git-commit-tree`
-also wants to get a commit message
-on its standard input, and it will write out the resulting object name for the
-commit to its standard output.
-
-And this is where we create the `.git/refs/heads/master` file
-which is pointed at by `HEAD`. This file is supposed to contain
-the reference to the top-of-tree of the master branch, and since
-that's exactly what `git-commit-tree` spits out, we can do this
-all with a sequence of simple shell commands:
+will show this.  Once you've edited the files to resolve the
+conflicts,
 
 ------------------------------------------------
-$ tree=$(git-write-tree)
-$ commit=$(echo 'Initial commit' | git-commit-tree $tree)
-$ git-update-ref HEAD $commit
+$ git commit -a
 ------------------------------------------------
 
-which will say:
-
-----------------
-Committing initial tree 8988da15d077d4829fc51d8544c097def6644dbb
-----------------
-
-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.
-
-Again, normally you'd never actually do this by hand. There is a
-helpful script called `git commit` that will do all of this for you. So
-you could have just written `git commit`
-instead, and it would have done the above magic scripting for you.
-
-
-Making a change
----------------
-
-Remember how we did the `git-update-index` on file `hello` and then we
-changed `hello` afterward, and could compare the new state of `hello` 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 `hello`, not the new ones. We did
-that on purpose, to show the difference between the index state, and the
-state in the working tree, 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-index`.
-
-Unlike `git-diff-files`, which showed the difference between the index
-file and the working tree, `git-diff-index` shows the differences
-between a committed *tree* and either the index file or the working
-tree. In other words, `git-diff-index` 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-index -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 tree 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.
-
-Again, because this is a common operation, you can also just shorthand
-it with
-
-----------------
-$ git diff HEAD
-----------------
-
-which ends up doing the above for you.
-
-In other words, `git-diff-index` normally compares a tree against the
-working tree, but when given the `\--cached` flag, it is told to
-instead compare against just the index cache contents, and ignore the
-current working tree state entirely. Since we just wrote the index
-file to HEAD, doing `git-diff-index \--cached -p HEAD` should thus return
-an empty set of differences, and that's exactly what it does. 
-
-[NOTE]
-================
-`git-diff-index` really always uses the index for its
-comparisons, and saying that it compares a tree against the working
-tree 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 tree 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
-tree contents", "index file" and "committed tree". We have changes
-in the working tree 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:
+will commit the result of the merge. Finally,
 
 ------------------------------------------------
-$ git-update-index hello
+$ gitk
 ------------------------------------------------
 
-(note how we didn't need the `\--add` flag this time, since git knew
-about the file already).
+will show a nice graphical representation of the resulting history.
 
-Note what happens to the different `git-diff-\*` versions here. After
-we've updated `hello` in the index, `git-diff-files -p` now shows no
-differences, but `git-diff-index -p HEAD` still *does* show that the
-current state is different from the state we committed. In fact, now
-`git-diff-index` shows the same difference whether we use the `--cached`
-flag or not, since now the index is coherent with the working tree.
+If you develop on a branch crazy-idea, then regret it, you can always
+delete the branch with
 
-Now, since we've updated `hello` in the index, we can commit the new
-version. We could do it by writing the tree by hand again, 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 you've done that once
-already, so let's just use the helpful script this time:
+-------------------------------------
+$ git branch -D crazy-idea
+-------------------------------------
 
-------------------------------------------------
-$ git commit
-------------------------------------------------
+Branches are cheap and easy, so this is a good way to try something
+out.
 
-which starts an editor for you to write the commit message and tells you
-a bit about what you have done.
-
-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 index), you
-can just leave an empty message. Otherwise `git commit` 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` 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 (`git-commit`).
-
-
-Inspecting Changes
-------------------
-
-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.
-
-[NOTE]
-============
-Here is an ASCII art by Jon Loeliger that illustrates how
-various diff-\* commands compare things.
-
-                      diff-tree
-                       +----+
-                       |    |
-                       |    |
-                       V    V
-                    +-----------+
-                    | Object DB |
-                    |  Backing  |
-                    |   Store   |
-                    +-----------+
-                      ^    ^
-                      |    |
-                      |    |  diff-index --cached
-                      |    |
-          diff-index  |    V
-                      |  +-----------+
-                      |  |   Index   |
-                      |  |  "cache"  |
-                      |  +-----------+
-                      |    ^
-                      |    |
-                      |    |  diff-files
-                      |    |
-                      V    V
-                    +-----------+
-                    |  Working  |
-                    | Directory |
-                    +-----------+
-============
-
-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
-activities.
-
-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. 
-
-[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.
-
-[NOTE]
-Most likely, you are not directly using the core
-git Plumbing commands, but using Porcelain like Cogito on top
-of it. Cogito works a bit differently and you usually do not
-have to run `git-update-index` yourself for changed files (you
-do tell underlying git about additions and removals via
-`cg-add` and `cg-rm` commands). Just before you make a commit
-with `cg-commit`, Cogito figures out which files you modified,
-and runs `git-update-index` on them for you.
-
-
-Tagging a version
------------------
+Using git for collaboration
+---------------------------
 
-In git, there are two kinds of tags, a "light" one, and an "annotated tag".
+Suppose that Alice has started a new project with a git repository in
+/home/alice/project, and that Bob, who has a home directory on the
+same machine, wants to contribute.
 
-A "light" tag is technically nothing more than a branch, except we put
-it in the `.git/refs/tags/` subdirectory instead of calling it a `head`.
-So the simplest form of tag involves nothing more than
+Bob begins with:
 
 ------------------------------------------------
-$ git tag my-first-tag
+$ git clone /home/alice/project myrepo
 ------------------------------------------------
 
-which just writes the current `HEAD` into the `.git/refs/tags/my-first-tag`
-file, after which point you can then use this symbolic name for that
-particular state. You can, for example, do
-
-----------------
-$ git diff my-first-tag
-----------------
-
-to diff your current state against that tag (which at this point will
-obviously be an empty diff, but if you continue to develop and commit
-stuff, you can use your tag as an "anchor-point" to see what has changed
-since you tagged it.
-
-An "annotated tag" is actually a real git object, and contains not only a
-pointer to the state you want to tag, but also a small tag name and
-message, along with optionally a PGP signature that says that yes,
-you really did
-that tag. You create these annotated tags with either the `-a` or
-`-s` flag to `git tag`:
-
-----------------
-$ git tag -s <tagname>
-----------------
-
-which will sign the current `HEAD` (but you can also give it another
-argument that specifies the thing to tag, ie you could have tagged the
-current `mybranch` point by using `git tag <tagname> mybranch`).
-
-You normally only do signed tags for major releases or things
-like that, while the light-weight tags are useful for any marking you
-want to do -- any time you decide that you want to remember a certain
-point, just create a private tag for it, and you have a nice symbolic
-name for the state at that point.
-
-
-Copying repositories
---------------------
-
-git repositories are normally totally self-sufficient and relocatable
-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.
-
-[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 tree 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 repository 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 the project you created.
-
- - if you want to move or duplicate a git repository, you can do so. There
-   is `git clone` command, but if all you want to do is just to
-   create a copy of your repository (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 repository, 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-index --refresh
-----------------
-+
-in the new repository to make sure that the index file is up-to-date.
-
-Note that the second point is true even across machines. You can
-duplicate a remote git repository 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-index` with a
-
-----------------
-$ git-read-tree --reset HEAD
-$ git-update-index --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-index`
-makes sure to match up all index entries with the checked-out files.
-If the original repository had uncommitted changes in its
-working tree, `git-update-index --refresh` notices them and
-tells you they need to be updated.
-
-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.  You can learn things by just looking
-at what the various git scripts do.  For example, `git reset` is the
-above two lines implemented in `git-reset`, but some things like
-`git status` and `git commit` are slightly more complex scripts around
-the basic git commands.
-
-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/git/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 tree files to work on. To get
-those, you'd check them out with
-
-----------------
-$ git-checkout-index -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` flag 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`
-flag first, to tell git-checkout-index to *force* overwriting of any old
-files). 
-
-Again, this can all be simplified with
-
-----------------
-$ git clone rsync://rsync.kernel.org/pub/scm/git/git.git/ my-git
-$ cd my-git
-$ git checkout
-----------------
-
-which will end up doing all of the above for you.
-
-You have now successfully copied somebody else's (mine) remote
-repository, and checked it out. 
-
-
-Creating a new branch
----------------------
-
-Branches in git are really nothing more than pointers into the git
-object database from within the `.git/refs/` subdirectory, and as we
-already discussed, the `HEAD` branch is nothing but a symlink to one of
-these object pointers. 
-
-You can at any time create a new branch by just picking an arbitrary
-point in the project history, and just writing the SHA1 name of that
-object into a file under `.git/refs/heads/`. You can use any filename you
-want (and indeed, subdirectories), but the convention is that the
-"normal" branch is called `master`. That's just a convention, though,
-and nothing enforces it. 
-
-To show that as an example, let's go back to the git-tutorial repository we
-used earlier, and create a branch in it. You do that by simply just
-saying that you want to check out a new branch:
-
-------------
-$ git checkout -b mybranch
-------------
-
-will create a new branch based at the current `HEAD` position, and switch
-to it. 
-
-[NOTE]
-================================================
-If you make the decision to start your new branch at some
-other point in the history than the current `HEAD`, you can do so by
-just telling `git checkout` what the base of the checkout would be.
-In other words, if you have an earlier tag or branch, you'd just do
-
-------------
-$ git checkout -b mybranch earlier-commit
-------------
-
-and it would create the new branch `mybranch` at the earlier commit,
-and check out the state at that time.
-================================================
-
-You can always just jump back to your original `master` branch by doing
-
-------------
-$ git checkout master
-------------
+This creates a new directory "myrepo" containing a clone of Alice's
+repository.  The clone is on an equal footing with the original
+project, posessing its own copy of the original project's history.
+
+Bob then makes some changes and commits them:
 
-(or any other branch-name, for that matter) and if you forget which
-branch you happen to be on, a simple
+------------------------------------------------
+(edit files)
+$ git commit -a
+(repeat as necessary)
+------------------------------------------------
 
-------------
-$ ls -l .git/HEAD
-------------
+When he's ready, he tells Alice to pull changes from the repository
+at /home/bob/myrepo.  She does this with:
 
-will tell you where it's pointing (Note that on platforms with bad or no
-symlink support, you have to execute
+------------------------------------------------
+$ cd /home/alice/project
+$ git pull /home/bob/myrepo
+------------------------------------------------
 
-------------
-$ cat .git/HEAD
-------------
+This actually pulls changes from the branch in Bob's repository named
+"master".  Alice could request a different branch by adding the name
+of the branch to the end of the git pull command line.
 
-instead). To get the list of branches you have, you can say
+This merges Bob's changes into her repository; "git whatchanged" will
+now show the new commits.  If Alice has made her own changes in the
+meantime, then Bob's changes will be merged in, and she will need to
+manually fix any conflicts.
 
-------------
-$ git branch
-------------
+A more cautious Alice might wish to examine Bob's changes before
+pulling them.  She can do this by creating a temporary branch just
+for the purpose of studying Bob's changes:
 
-which is nothing more than a simple script around `ls .git/refs/heads`.
-There will be asterisk in front of the branch you are currently on.
+-------------------------------------
+$ git fetch /home/bob/myrepo master:bob-incoming
+-------------------------------------
 
-Sometimes you may wish to create a new branch _without_ actually
-checking it out and switching to it. If so, just use the command
+which fetches the changes from Bob's master branch into a new branch
+named bob-incoming.  (Unlike git pull, git fetch just fetches a copy
+of Bob's line of development without doing any merging).  Then
 
-------------
-$ git branch <branchname> [startingpoint]
-------------
+-------------------------------------
+$ git whatchanged -p master..bob-incoming
+-------------------------------------
 
-which will simply _create_ the branch, but will not do anything further. 
-You can then later -- once you decide that you want to actually develop
-on that branch -- switch to that branch with a regular `git checkout`
-with the branchname as the argument.
+shows a list of all the changes that Bob made since he branched from
+Alice's master branch.
 
+After examing those changes, and possibly fixing things, Alice can
+pull the changes into her master branch:
 
-Merging two branches
---------------------
+-------------------------------------
+$ git checkout master
+$ git pull . bob-incoming
+-------------------------------------
 
-One of the ideas of having a branch is that you do some (possibly
-experimental) work in it, and eventually merge it back to the main
-branch. So assuming you created the above `mybranch` that started out
-being the same as the original `master` branch, let's make sure we're in
-that branch, and do some work there.
+The last command is a pull from the "bob-incoming" branch in Alice's
+own repository.
 
-------------------------------------------------
-$ git checkout mybranch
-$ echo "Work, work, work" >>hello
-$ git commit -m 'Some work.' hello
-------------------------------------------------
+Later, Bob can update his repo with Alice's latest changes using
 
-Here, we just added another line to `hello`, and we used a shorthand for
-doing both `git-update-index hello` and `git commit` by just giving the
-filename directly to `git commit`. The `-m` flag is to give the
-commit log message from the command line.
+-------------------------------------
+$ git pull
+-------------------------------------
 
-Now, to make it a bit more interesting, let's assume that somebody else
-does some work in the original branch, and simulate that by going back
-to the master branch, and editing the same file differently there:
+Note that he doesn't need to give the path to Alice's repository;
+when Bob cloned Alice's repository, git stored the location of her
+repository in the file .git/remotes/origin, and that location is used
+as the default for pulls.
 
-------------
-$ git checkout master
-------------
-
-Here, take a moment to look at the contents of `hello`, and notice how they
-don't contain the work we just did in `mybranch` -- because that work
-hasn't happened in the `master` branch at all. Then do
-
-------------
-$ echo "Play, play, play" >>hello
-$ echo "Lots of fun" >>example
-$ git commit -m 'Some fun.' hello example
-------------
-
-since the master branch is obviously in a much better mood.
-
-Now, you've got two branches, and you decide that you want to merge the
-work done. Before we do that, let's introduce a cool graphical tool that
-helps you view what's going on:
-
-----------------
-$ gitk --all
-----------------
-
-will show you graphically both of your branches (that's what the `\--all`
-means: normally it will just show you your current `HEAD`) and their
-histories. You can also see exactly how they came to be from a common
-source. 
-
-Anyway, let's exit `gitk` (`^Q` or the File menu), and decide that we want
-to merge the work we did on the `mybranch` branch into the `master`
-branch (which is currently our `HEAD` too). To do that, there's a nice
-script called `git merge`, which wants to know which branches you want
-to resolve and what the merge is all about:
-
-------------
-$ git merge "Merge work in mybranch" HEAD mybranch
-------------
-
-where the first argument is going to be used as the commit message if
-the merge can be resolved automatically.
-
-Now, in this case we've intentionally created a situation where the
-merge will need to be fixed up by hand, though, so git will do as much
-of it as it can automatically (which in this case is just merge the `example`
-file, which had no differences in the `mybranch` branch), and say:
-
-----------------
-       Trying really trivial in-index merge...
-       fatal: Merge requires file-level merging
-       Nope.
-       ...
-       Auto-merging hello 
-       CONFLICT (content): Merge conflict in hello 
-       Automatic merge failed/prevented; fix up by hand
-----------------
-
-which is way too verbose, but it basically tells you that it failed the
-really trivial merge ("Simple merge") and did an "Automatic merge"
-instead, but that too failed due to conflicts in `hello`.
-
-Not to worry. It left the (trivial) conflict in `hello` in the same form you
-should already be well used to if you've ever used CVS, so let's just
-open `hello` in our editor (whatever that may be), and fix it up somehow.
-I'd suggest just making it so that `hello` contains all four lines:
-
-------------
-Hello World
-It's a new day for git
-Play, play, play
-Work, work, work
-------------
-
-and once you're happy with your manual merge, just do a
-
-------------
-$ git commit hello
-------------
-
-which will very loudly warn you that you're now committing a merge
-(which is correct, so never mind), and you can write a small merge
-message about your adventures in git-merge-land.
-
-After you're done, start up `gitk \--all` to see graphically what the
-history looks like. Notice that `mybranch` still exists, and you can
-switch to it, and continue to work with it if you want to. The
-`mybranch` branch will not contain the merge, but next time you merge it
-from the `master` branch, git will know how you merged it, so you'll not
-have to do _that_ merge again.
-
-Another useful tool, especially if you do not always work in X-Window
-environment, is `git show-branch`.
+Bob may also notice a branch in his repository that he didn't create:
 
-------------------------------------------------
-$ git show-branch master mybranch
-* [master] Merge work in mybranch
- ! [mybranch] Some work.
---
-+  [master] Merge work in mybranch
-++ [mybranch] Some work.
-------------------------------------------------
+-------------------------------------
+$ git branch
+* master
+  origin
+-------------------------------------
 
-The first two lines indicate that it is showing the two branches
-and the first line of the commit log message from their
-top-of-the-tree commits, you are currently on `master` branch
-(notice the asterisk `*` character), and the first column for
-the later output lines is used to show commits contained in the
-`master` branch, and the second column for the `mybranch`
-branch. Three commits are shown along with their log messages.
-All of them have plus `+` characters in the first column, which
-means they are now part of the `master` branch. Only the "Some
-work" commit has the plus `+` character in the second column,
-because `mybranch` has not been merged to incorporate these
-commits from the master branch.  The string inside brackets
-before the commit log message is a short name you can use to
-name the commit.  In the above example, 'master' and 'mybranch'
-are branch heads.  'master~1' is the first parent of 'master'
-branch head.  Please see 'git-rev-parse' documentation if you
-see more complex cases.
-
-Now, let's pretend you are the one who did all the work in
-`mybranch`, and the fruit of your hard work has finally been merged
-to the `master` branch. Let's go back to `mybranch`, and run
-resolve to get the "upstream changes" back to your branch.
-
-------------
-$ git checkout mybranch
-$ git merge "Merge upstream changes." HEAD master
-------------
-
-This outputs something like this (the actual commit object names
-would be different)
-
-----------------
-Updating from ae3a2da... to a80b4aa....
- example |    1 +
- hello   |    1 +
- 2 files changed, 2 insertions(+), 0 deletions(-)
-----------------
-
-Because your branch did not contain anything more than what are
-already merged into the `master` branch, the resolve operation did
-not actually do a merge. Instead, it just updated the top of
-the tree of your branch to that of the `master` branch. This is
-often called 'fast forward' merge.
-
-You can run `gitk \--all` again to see how the commit ancestry
-looks like, or run `show-branch`, which tells you this.
+The "origin" branch, which was created automatically by "git clone",
+is a pristine copy of Alice's master branch; Bob should never commit
+to it.
 
-------------------------------------------------
-$ git show-branch master mybranch
-! [master] Merge work in mybranch
- * [mybranch] Merge work in mybranch
---
-++ [master] Merge work in mybranch
-------------------------------------------------
+If Bob later decides to work from a different host, he can still
+perform clones and pulls using the ssh protocol:
 
+-------------------------------------
+$ git clone alice.org:/home/alice/project myrepo
+-------------------------------------
 
-Merging external work
----------------------
-
-It's usually much more common that you merge with somebody else than
-merging with your own branches, so it's worth pointing out that git
-makes that very easy too, and in fact, it's not that different from
-doing a `git merge`. In fact, a remote merge ends up being nothing
-more than "fetch the work from a remote repository into a temporary tag"
-followed by a `git merge`.
-
-Fetching from a remote repository is done by, unsurprisingly,
-`git fetch`:
-
-----------------
-$ git fetch <remote-repository>
-----------------
-
-One of the following transports can be used to name the
-repository to download from:
-
-Rsync::
-       `rsync://remote.machine/path/to/repo.git/`
-+
-Rsync transport is usable for both uploading and downloading,
-but is completely unaware of what git does, and can produce
-unexpected results when you download from the public repository
-while the repository owner is uploading into it via `rsync`
-transport.  Most notably, it could update the files under
-`refs/` which holds the object name of the topmost commits
-before uploading the files in `objects/` -- the downloader would
-obtain head commit object name while that object itself is still
-not available in the repository.  For this reason, it is
-considered deprecated.
-
-SSH::
-       `remote.machine:/path/to/repo.git/` or
-+
-`ssh://remote.machine/path/to/repo.git/`
-+
-This transport can be used for both uploading and downloading,
-and requires you to have a log-in privilege over `ssh` to the
-remote machine.  It finds out the set of objects the other side
-lacks by exchanging the head commits both ends have and
-transfers (close to) minimum set of objects.  It is by far the
-most efficient way to exchange git objects between repositories.
-
-Local directory::
-       `/path/to/repo.git/`
-+
-This transport is the same as SSH transport but uses `sh` to run
-both ends on the local machine instead of running other end on
-the remote machine via `ssh`.
-
-git Native::
-       `git://remote.machine/path/to/repo.git/`
-+
-This transport was designed for anonymous downloading.  Like SSH
-transport, it finds out the set of objects the downstream side
-lacks and transfers (close to) minimum set of objects.
-
-HTTP(S)::
-       `http://remote.machine/path/to/repo.git/`
-+
-Downloader from http and https URL
-first obtains the topmost commit object name from the remote site
-by looking at the specified refname under `repo.git/refs/` directory,
-and then tries to obtain the
-commit object by downloading from `repo.git/objects/xx/xxx\...`
-using the object name of that commit object.  Then it reads the
-commit object to find out its parent commits and the associate
-tree object; it repeats this process until it gets all the
-necessary objects.  Because of this behaviour, they are
-sometimes also called 'commit walkers'.
-+
-The 'commit walkers' are sometimes also called 'dumb
-transports', because they do not require any git aware smart
-server like git Native transport does.  Any stock HTTP server
-that does not even support directory index would suffice.  But
-you must prepare your repository with `git-update-server-info`
-to help dumb transport downloaders.
-+
-There are (confusingly enough) `git-ssh-fetch` and `git-ssh-upload`
-programs, which are 'commit walkers'; they outlived their
-usefulness when git Native and SSH transports were introduced,
-and not used by `git pull` or `git push` scripts.
-
-Once you fetch from the remote repository, you `resolve` that
-with your current branch.
-
-However -- it's such a common thing to `fetch` and then
-immediately `resolve`, that it's called `git pull`, and you can
-simply do
-
-----------------
-$ git pull <remote-repository>
-----------------
-
-and optionally give a branch-name for the remote end as a second
-argument.
-
-[NOTE]
-You could do without using any branches at all, by
-keeping as many local repositories as you would like to have
-branches, and merging between them with `git pull`, just like
-you merge between branches. The advantage of this approach is
-that it lets you keep set of files for each `branch` checked
-out and you may find it easier to switch back and forth if you
-juggle multiple lines of development simultaneously. Of
-course, you will pay the price of more disk usage to hold
-multiple working trees, but disk space is cheap these days.
-
-[NOTE]
-You could even pull from your own repository by
-giving '.' as <remote-repository> parameter to `git pull`.  This
-is useful when you want to merge a local branch (or more, if you
-are making an Octopus) into the current branch.
-
-It is likely that you will be pulling from the same remote
-repository from time to time. As a short hand, you can store
-the remote repository URL in a file under .git/remotes/
-directory, like this:
+Alternatively, git has a native protocol, or can use rsync or http;
+see gitlink:git-pull[1] for details.
 
-------------------------------------------------
-$ mkdir -p .git/remotes/
-$ cat >.git/remotes/linus <<\EOF
-URL: http://www.kernel.org/pub/scm/git/git.git/
-EOF
-------------------------------------------------
+Git can also be used in a CVS-like mode, with a central repository
+that various users push changes to; see gitlink:git-push[1] and
+link:cvs-migration.html[git for CVS users].
 
-and use the filename to `git pull` instead of the full URL.
-The URL specified in such file can even be a prefix
-of a full URL, like this:
+Keeping track of history
+------------------------
 
-------------------------------------------------
-$ cat >.git/remotes/jgarzik <<\EOF
-URL: http://www.kernel.org/pub/scm/linux/git/jgarzik/
-EOF
-------------------------------------------------
+Git history is represented as a series of interrelated commits.  The
+most recent commit in the currently checked-out branch can always be
+referred to as HEAD, and the "parent" of any commit can always be
+referred to by appending a caret, "^", to the end of the name of the
+commit.  So, for example,
 
+-------------------------------------
+git diff HEAD^ HEAD
+-------------------------------------
 
-Examples.
+shows the difference between the most-recently checked-in state of
+the tree and the previous state, and
 
-. `git pull linus`
-. `git pull linus tag v0.99.1`
-. `git pull jgarzik/netdev-2.6.git/ e100`
+-------------------------------------
+git diff HEAD^^ HEAD^
+-------------------------------------
 
-the above are equivalent to:
+shows the difference between that previous state and the state two
+commits ago.  Also, HEAD~5 can be used as a shorthand for HEAD^^^^^,
+and more generally HEAD~n can refer to the nth previous commit.
+Commits representing merges have more than one parent, and you can
+specify which parent to follow in that case; see
+gitlink:git-rev-parse[1].
 
-. `git pull http://www.kernel.org/pub/scm/git/git.git/ HEAD`
-. `git pull http://www.kernel.org/pub/scm/git/git.git/ tag v0.99.1`
-. `git pull http://www.kernel.org/pub/.../jgarzik/netdev-2.6.git e100`
+The name of a branch can also be used to refer to the most recent
+commit on that branch; so you can also say things like
 
+-------------------------------------
+git diff HEAD experimental
+-------------------------------------
 
-How does the merge work?
-------------------------
+to see the difference between the most-recently committed tree in
+the current branch and the most-recently committed tree in the
+experimental branch.
 
-We said this tutorial shows what plumbing does to help you cope
-with the porcelain that isn't flushing, but we so far did not
-talk about how the merge really works.  If you are following
-this tutorial the first time, I'd suggest to skip to "Publishing
-your work" section and come back here later.
-
-OK, still with me?  To give us an example to look at, let's go
-back to the earlier repository with "hello" and "example" file,
-and bring ourselves back to the pre-merge state:
-
-------------
-$ git show-branch --more=3 master mybranch
-! [master] Merge work in mybranch
- * [mybranch] Merge work in mybranch
---
-++ [master] Merge work in mybranch
-++ [master^2] Some work.
-++ [master^] Some fun.
-------------
-
-Remember, before running `git merge`, our `master` head was at
-"Some fun." commit, while our `mybranch` head was at "Some
-work." commit.
-
-------------
-$ git checkout mybranch
-$ git reset --hard master^2
-$ git checkout master
-$ git reset --hard master^
-------------
-
-After rewinding, the commit structure should look like this:
-
-------------
-$ git show-branch
-* [master] Some fun.
- ! [mybranch] Some work.
---
- + [mybranch] Some work.
-+  [master] Some fun.
-++ [mybranch^] New day.
-------------
-
-Now we are ready to experiment with the merge by hand.
-
-`git merge` command, when merging two branches, uses 3-way merge
-algorithm.  First, it finds the common ancestor between them.
-The command it uses is `git-merge-base`:
-
-------------
-$ mb=$(git-merge-base HEAD mybranch)
-------------
-
-The command writes the commit object name of the common ancestor
-to the standard output, so we captured its output to a variable,
-because we will be using it in the next step.  BTW, the common
-ancestor commit is the "New day." commit in this case.  You can
-tell it by:
-
-------------
-$ git-name-rev $mb
-my-first-tag
-------------
-
-After finding out a common ancestor commit, the second step is
-this:
-
-------------
-$ git-read-tree -m -u $mb HEAD mybranch
-------------
-
-This is the same `git-read-tree` command we have already seen,
-but it takes three trees, unlike previous examples.  This reads
-the contents of each tree into different 'stage' in the index
-file (the first tree goes to stage 1, the second stage 2,
-etc.).  After reading three trees into three stages, the paths
-that are the same in all three stages are 'collapsed' into stage
-0.  Also paths that are the same in two of three stages are
-collapsed into stage 0, taking the SHA1 from either stage 2 or
-stage 3, whichever is different from stage 1 (i.e. only one side
-changed from the common ancestor).
-
-After 'collapsing' operation, paths that are different in three
-trees are left in non-zero stages.  At this point, you can
-inspect the index file with this command:
-
-------------
-$ git-ls-files --stage
-100644 7f8b141b65fdcee47321e399a2598a235a032422 0      example
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
-100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
-------------
-
-In our example of only two files, we did not have unchanged
-files so only 'example' resulted in collapsing, but in real-life
-large projects, only small number of files change in one commit,
-and this 'collapsing' tends to trivially merge most of the paths
-fairly quickly, leaving only a handful the real changes in non-zero
-stages.
-
-To look at only non-zero stages, use `\--unmerged` flag:
-
-------------
-$ git-ls-files --unmerged
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
-100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
-------------
-
-The next step of merging is to merge these three versions of the
-file, using 3-way merge.  This is done by giving
-`git-merge-one-file` command as one of the arguments to
-`git-merge-index` command:
-
-------------
-$ git-merge-index git-merge-one-file hello
-Auto-merging hello.
-merge: warning: conflicts during merge
-ERROR: Merge conflict in hello.
-fatal: merge program failed
-------------
-
-`git-merge-one-file` script is called with parameters to
-describe those three versions, and is responsible to leave the
-merge results in the working tree.
-It is a fairly straightforward shell script, and
-eventually calls `merge` program from RCS suite to perform a
-file-level 3-way merge.  In this case, `merge` detects
-conflicts, and the merge result with conflict marks is left in
-the working tree..  This can be seen if you run `ls-files
---stage` again at this point:
-
-------------
-$ git-ls-files --stage
-100644 7f8b141b65fdcee47321e399a2598a235a032422 0      example
-100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1      hello
-100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2      hello
-100644 cc44c73eb783565da5831b4d820c962954019b69 3      hello
-------------
-
-This is the state of the index file and the working file after
-`git merge` returns control back to you, leaving the conflicting
-merge for you to resolve.  Notice that the path `hello` is still
-unmerged, and what you see with `git diff` at this point is
-differences since stage 2 (i.e. your version).
-
-
-Publishing your work
---------------------
-
-So we can use somebody else's work from a remote repository; but
-how can *you* prepare a repository to let other people pull from
-it?
-
-Your do your real work in your working tree that has your
-primary repository hanging under it as its `.git` subdirectory.
-You *could* make that repository accessible remotely and ask
-people to pull from it, but in practice that is not the way
-things are usually done. A recommended way is to have a public
-repository, make it reachable by other people, and when the
-changes you made in your primary working tree are in good shape,
-update the public repository from it. This is often called
-'pushing'.
-
-[NOTE]
-This public repository could further be mirrored, and that is
-how git repositories at `kernel.org` are managed.
-
-Publishing the changes from your local (private) repository to
-your remote (public) repository requires a write privilege on
-the remote machine. You need to have an SSH account there to
-run a single command, `git-receive-pack`.
-
-First, you need to create an empty repository on the remote
-machine that will house your public repository. This empty
-repository will be populated and be kept up-to-date by pushing
-into it later. Obviously, this repository creation needs to be
-done only once.
-
-[NOTE]
-`git push` uses a pair of programs,
-`git-send-pack` on your local machine, and `git-receive-pack`
-on the remote machine. The communication between the two over
-the network internally uses an SSH connection.
-
-Your private repository's git directory is usually `.git`, but
-your public repository is often named after the project name,
-i.e. `<project>.git`. Let's create such a public repository for
-project `my-git`. After logging into the remote machine, create
-an empty directory:
-
-------------
-$ mkdir my-git.git
-------------
-
-Then, make that directory into a git repository by running
-`git init-db`, but this time, since its name is not the usual
-`.git`, we do things slightly differently:
-
-------------
-$ GIT_DIR=my-git.git git-init-db
-------------
-
-Make sure this directory is available for others you want your
-changes to be pulled by via the transport of your choice. Also
-you need to make sure that you have the `git-receive-pack`
-program on the `$PATH`.
-
-[NOTE]
-Many installations of sshd do not invoke your shell as the login
-shell when you directly run programs; what this means is that if
-your login shell is `bash`, only `.bashrc` is read and not
-`.bash_profile`. As a workaround, make sure `.bashrc` sets up
-`$PATH` so that you can run `git-receive-pack` program.
-
-[NOTE]
-If you plan to publish this repository to be accessed over http,
-you should do `chmod +x my-git.git/hooks/post-update` at this
-point.  This makes sure that every time you push into this
-repository, `git-update-server-info` is run.
-
-Your "public repository" is now ready to accept your changes.
-Come back to the machine you have your private repository. From
-there, run this command:
-
-------------
-$ git push <public-host>:/path/to/my-git.git master
-------------
-
-This synchronizes your public repository to match the named
-branch head (i.e. `master` in this case) and objects reachable
-from them in your current repository.
-
-As a real example, this is how I update my public git
-repository. Kernel.org mirror network takes care of the
-propagation to other publicly visible machines:
-
-------------
-$ git push master.kernel.org:/pub/scm/git/git.git/ 
-------------
-
-
-Packing your repository
------------------------
+But you may find it more useful to see the list of commits made in
+the experimental branch but not in the current branch, and
 
-Earlier, we saw that one file under `.git/objects/??/` directory
-is stored for each git object you create. This representation
-is efficient to create atomically and safely, but
-not so convenient to transport over the network. Since git objects are
-immutable once they are created, there is a way to optimize the
-storage by "packing them together". The command
-
-------------
-$ git repack
-------------
-
-will do it for you. If you followed the tutorial examples, you
-would have accumulated about 17 objects in `.git/objects/??/`
-directories by now. `git repack` tells you how many objects it
-packed, and stores the packed file in `.git/objects/pack`
-directory.
-
-[NOTE]
-You will see two files, `pack-\*.pack` and `pack-\*.idx`,
-in `.git/objects/pack` directory. They are closely related to
-each other, and if you ever copy them by hand to a different
-repository for whatever reason, you should make sure you copy
-them together. The former holds all the data from the objects
-in the pack, and the latter holds the index for random
-access.
-
-If you are paranoid, running `git-verify-pack` command would
-detect if you have a corrupt pack, but do not worry too much.
-Our programs are always perfect ;-).
-
-Once you have packed objects, you do not need to leave the
-unpacked objects that are contained in the pack file anymore.
-
-------------
-$ git prune-packed
-------------
-
-would remove them for you.
-
-You can try running `find .git/objects -type f` before and after
-you run `git prune-packed` if you are curious.  Also `git
-count-objects` would tell you how many unpacked objects are in
-your repository and how much space they are consuming.
-
-[NOTE]
-`git pull` is slightly cumbersome for HTTP transport, as a
-packed repository may contain relatively few objects in a
-relatively large pack. If you expect many HTTP pulls from your
-public repository you might want to repack & prune often, or
-never.
-
-If you run `git repack` again at this point, it will say
-"Nothing to pack". Once you continue your development and
-accumulate the changes, running `git repack` again will create a
-new pack, that contains objects created since you packed your
-repository the last time. We recommend that you pack your project
-soon after the initial import (unless you are starting your
-project from scratch), and then run `git repack` every once in a
-while, depending on how active your project is.
-
-When a repository is synchronized via `git push` and `git pull`
-objects packed in the source repository are usually stored
-unpacked in the destination, unless rsync transport is used.
-While this allows you to use different packing strategies on
-both ends, it also means you may need to repack both
-repositories every once in a while.
-
-
-Working with Others
--------------------
-
-Although git is a truly distributed system, it is often
-convenient to organize your project with an informal hierarchy
-of developers. Linux kernel development is run this way. There
-is a nice illustration (page 17, "Merges to Mainline") in Randy
-Dunlap's presentation (`http://tinyurl.com/a2jdg`).
-
-It should be stressed that this hierarchy is purely *informal*.
-There is nothing fundamental in git that enforces the "chain of
-patch flow" this hierarchy implies. You do not have to pull
-from only one remote repository.
-
-A recommended workflow for a "project lead" goes like this:
-
-1. Prepare your primary repository on your local machine. Your
-   work is done there.
-
-2. Prepare a public repository accessible to others.
-+
-If other people are pulling from your repository over dumb
-transport protocols (HTTP), you need to keep this repository
-'dumb transport friendly'.  After `git init-db`,
-`$GIT_DIR/hooks/post-update` copied from the standard templates
-would contain a call to `git-update-server-info` but the
-`post-update` hook itself is disabled by default -- enable it
-with `chmod +x post-update`.  This makes sure `git-update-server-info`
-keeps the necessary files up-to-date.
-
-3. Push into the public repository from your primary
-   repository.
-
-4. `git repack` the public repository. This establishes a big
-   pack that contains the initial set of objects as the
-   baseline, and possibly `git prune` if the transport
-   used for pulling from your repository supports packed
-   repositories.
-
-5. Keep working in your primary repository. Your changes
-   include modifications of your own, patches you receive via
-   e-mails, and merges resulting from pulling the "public"
-   repositories of your "subsystem maintainers".
-+
-You can repack this private repository whenever you feel like.
-
-6. Push your changes to the public repository, and announce it
-   to the public.
-
-7. Every once in a while, "git repack" the public repository.
-   Go back to step 5. and continue working.
-
-
-A recommended work cycle for a "subsystem maintainer" who works
-on that project and has an own "public repository" goes like this:
-
-1. Prepare your work repository, by `git clone` the public
-   repository of the "project lead". The URL used for the
-   initial cloning is stored in `.git/remotes/origin`.
-
-2. Prepare a public repository accessible to others, just like
-   the "project lead" person does.
-
-3. Copy over the packed files from "project lead" public
-   repository to your public repository, unless the "project
-   lead" repository lives on the same machine as yours.  In the
-   latter case, you can use `objects/info/alternates` file to
-   point at the repository you are borrowing from.
-
-4. Push into the public repository from your primary
-   repository. Run `git repack`, and possibly `git prune` if the
-   transport used for pulling from your repository supports
-   packed repositories.
-
-5. Keep working in your primary repository. Your changes
-   include modifications of your own, patches you receive via
-   e-mails, and merges resulting from pulling the "public"
-   repositories of your "project lead" and possibly your
-   "sub-subsystem maintainers".
-+
-You can repack this private repository whenever you feel
-like.
-
-6. Push your changes to your public repository, and ask your
-   "project lead" and possibly your "sub-subsystem
-   maintainers" to pull from it.
-
-7. Every once in a while, `git repack` the public repository.
-   Go back to step 5. and continue working.
-
-
-A recommended work cycle for an "individual developer" who does
-not have a "public" repository is somewhat different. It goes
-like this:
-
-1. Prepare your work repository, by `git clone` the public
-   repository of the "project lead" (or a "subsystem
-   maintainer", if you work on a subsystem). The URL used for
-   the initial cloning is stored in `.git/remotes/origin`.
-
-2. Do your work in your repository on 'master' branch.
-
-3. Run `git fetch origin` from the public repository of your
-   upstream every once in a while. This does only the first
-   half of `git pull` but does not merge. The head of the
-   public repository is stored in `.git/refs/heads/origin`.
-
-4. Use `git cherry origin` to see which ones of your patches
-   were accepted, and/or use `git rebase origin` to port your
-   unmerged changes forward to the updated upstream.
-
-5. Use `git format-patch origin` to prepare patches for e-mail
-   submission to your upstream and send it out. Go back to
-   step 2. and continue.
+-------------------------------------
+git whatchanged HEAD..experimental
+-------------------------------------
 
+will do that, just as
 
-Working with Others, Shared Repository Style
---------------------------------------------
-
-If you are coming from CVS background, the style of cooperation
-suggested in the previous section may be new to you. You do not
-have to worry. git supports "shared public repository" style of
-cooperation you are probably more familiar with as well.
+-------------------------------------
+git whatchanged experimental..HEAD
+-------------------------------------
 
-For this, set up a public repository on a machine that is
-reachable via SSH by people with "commit privileges".  Put the
-committers in the same user group and make the repository
-writable by that group.  Make sure their umasks are set up to
-allow group members to write into directories other members
-have created.
+will show the list of commits made on the HEAD but not included in
+experimental.
 
-You, as an individual committer, then:
+You can also give commits convenient names of your own: after running
 
-- First clone the shared repository to a local repository:
-------------------------------------------------
-$ git clone repo.shared.xz:/pub/scm/project.git/ my-project
-$ cd my-project
-$ hack away
-------------------------------------------------
+-------------------------------------
+$ git-tag v2.5 HEAD^^
+-------------------------------------
 
-- Merge the work others might have done while you were hacking
-  away:
-------------------------------------------------
-$ git pull origin
-$ test the merge result
-------------------------------------------------
-[NOTE]
-================================
-The first `git clone` would have placed the following in
-`my-project/.git/remotes/origin` file, and that's why this and
-the next step work.
-------------
-URL: repo.shared.xz:/pub/scm/project.git/ my-project
-Pull: master:origin
-------------
-================================
-
-- push your work as the new head of the shared
-  repository.
-------------------------------------------------
-$ git push origin master
-------------------------------------------------
-If somebody else pushed into the same shared repository while
-you were working locally, `git push` in the last step would
-complain, telling you that the remote `master` head does not
-fast forward.  You need to pull and merge those other changes
-back before you push your work when it happens.
+you can refer to HEAD^^ by the name "v2.5".  If you intend to share
+this name with other people (for example, to identify a release
+version), you should create a "tag" object, and perhaps sign it; see
+gitlink:git-tag[1] for details.
 
+You can revisit the old state of a tree, and make further
+modifications if you wish, using git branch: the command
 
-Advanced Shared Repository Management
+-------------------------------------
+$ git branch stable-release v2.5
 -------------------------------------
 
-Being able to push into a shared repository means being able to
-write into it.  If your developers are coming over the network,
-this means you, as the repository administrator, need to give
-each of them an SSH access to the shared repository machine.
-
-In some cases, though, you may not want to give a normal shell
-account to them, but want to restrict them to be able to only
-do `git push` into the repository and nothing else.
-
-You can achieve this by setting the login shell of your
-developers on the shared repository host to `git-shell` program.
-
-[NOTE]
-Most likely you would also need to list `git-shell` program in
-`/etc/shells` file.
-
-This restricts the set of commands that can be run from incoming
-SSH connection for these users to only `receive-pack` and
-`upload-pack`, so the only thing they can do are `git fetch` and
-`git push`.
-
-You still need to create UNIX user accounts for each developer,
-and put them in the same group.  Make sure that the repository
-shared among these developers is writable by that group.
-
-. Initializing the shared repository with `git-init-db --shared`
-helps somewhat.
-
-. Run the following in the shared repository:
-+
-------------
-$ chgrp -R $group repo.git
-$ find repo.git -type d -print | xargs chmod ug+rwx,g+s
-$ GIT_DIR=repo.git git repo-config core.sharedrepository true
-------------
-
-The above measures make sure that directories lazily created in
-`$GIT_DIR` are writable by group members.  You, as the
-repository administrator, are still responsible to make sure
-your developers belong to that shared repository group and set
-their umask to a value no stricter than 027 (i.e. at least allow
-reading and searching by group members).
-
-You can implement finer grained branch policies using update
-hooks.  There is a document ("control access to branches") in
-Documentation/howto by Carl Baldwin and JC outlining how to (1)
-limit access to branch per user, (2) forbid overwriting existing
-tags.
-
-
-Bundling your work together
----------------------------
+will create a new branch named "stable-release" starting from the
+commit which you tagged with the name v2.5.
+
+You can reset the state of any branch to an earlier commit at any
+time with
+
+-------------------------------------
+$ git reset --hard v2.5
+-------------------------------------
 
-It is likely that you will be working on more than one thing at
-a time.  It is easy to manage those more-or-less independent tasks
-using branches with git.
-
-We have already seen how branches work previously,
-with "fun and work" example using two branches.  The idea is the
-same if there are more than two branches.  Let's say you started
-out from "master" head, and have some new code in the "master"
-branch, and two independent fixes in the "commit-fix" and
-"diff-fix" branches:
-
-------------
-$ git show-branch
-! [commit-fix] Fix commit message normalization.
- ! [diff-fix] Fix rename detection.
-  * [master] Release candidate #1
----
- +  [diff-fix] Fix rename detection.
- +  [diff-fix~1] Better common substring algorithm.
-+   [commit-fix] Fix commit message normalization.
-  + [master] Release candidate #1
-+++ [diff-fix~2] Pretty-print messages.
-------------
-
-Both fixes are tested well, and at this point, you want to merge
-in both of them.  You could merge in 'diff-fix' first and then
-'commit-fix' next, like this:
-
-------------
-$ git merge 'Merge fix in diff-fix' master diff-fix
-$ git merge 'Merge fix in commit-fix' master commit-fix
-------------
-
-Which would result in:
-
-------------
-$ git show-branch
-! [commit-fix] Fix commit message normalization.
- ! [diff-fix] Fix rename detection.
-  * [master] Merge fix in commit-fix
----
-  + [master] Merge fix in commit-fix
-+ + [commit-fix] Fix commit message normalization.
-  + [master~1] Merge fix in diff-fix
- ++ [diff-fix] Fix rename detection.
- ++ [diff-fix~1] Better common substring algorithm.
-  + [master~2] Release candidate #1
-+++ [master~3] Pretty-print messages.
-------------
-
-However, there is no particular reason to merge in one branch
-first and the other next, when what you have are a set of truly
-independent changes (if the order mattered, then they are not
-independent by definition).  You could instead merge those two
-branches into the current branch at once.  First let's undo what
-we just did and start over.  We would want to get the master
-branch before these two merges by resetting it to 'master~2':
-
-------------
-$ git reset --hard master~2
-------------
-
-You can make sure 'git show-branch' matches the state before
-those two 'git merge' you just did.  Then, instead of running
-two 'git merge' commands in a row, you would pull these two
-branch heads (this is known as 'making an Octopus'):
-
-------------
-$ git pull . commit-fix diff-fix
-$ git show-branch
-! [commit-fix] Fix commit message normalization.
- ! [diff-fix] Fix rename detection.
-  * [master] Octopus merge of branches 'diff-fix' and 'commit-fix'
----
-  + [master] Octopus merge of branches 'diff-fix' and 'commit-fix'
-+ + [commit-fix] Fix commit message normalization.
- ++ [diff-fix] Fix rename detection.
- ++ [diff-fix~1] Better common substring algorithm.
-  + [master~1] Release candidate #1
-+++ [master~2] Pretty-print messages.
-------------
-
-Note that you should not do Octopus because you can.  An octopus
-is a valid thing to do and often makes it easier to view the
-commit history if you are pulling more than two independent
-changes at the same time.  However, if you have merge conflicts
-with any of the branches you are merging in and need to hand
-resolve, that is an indication that the development happened in
-those branches were not independent after all, and you should
-merge two at a time, documenting how you resolved the conflicts,
-and the reason why you preferred changes made in one side over
-the other.  Otherwise it would make the project history harder
-to follow, not easier.
-
-[ to be continued.. cvsimports ]
+This will remove all later commits from this branch and reset the
+working tree to the state it had when the given commit was made.  If
+this branch is the only branch containing the later commits, those
+later changes will be lost.  Don't use "git reset" on a
+publicly-visible branch that other developers pull from, as git will
+be confused by history that disappears in this way.
+
+Next Steps
+----------
+
+Some good commands to explore next:
+
+  * gitlink:git-diff[1]: This flexible command does much more than
+    we've seen in the few examples above.
+
+  * gitlink:git-format-patch[1], gitlink:git-am[1]: These convert
+    series of git commits into emailed patches, and vice versa,
+    useful for projects such as the linux kernel which rely heavily
+    on emailed patches.
+
+  * gitlink:git-bisect[1]: When there is a regression in your
+    project, one way to track down the bug is by searching through
+    the history to find the exact commit that's to blame.  Git bisect
+    can help you perform a binary search for that commit.  It is
+    smart enough to perform a close-to-optimal search even in the
+    case of complex non-linear history with lots of merged branches.
+
+Other good starting points include link:everyday.html[Everday GIT
+with 20 Commands Or So] and link:cvs-migration.html[git for CVS
+users].  Also, link:core-tutorial.html[A short git tutorial] gives an
+introduction to lower-level git commands for advanced users and
+developers.
index 73aaff0..72201be 100755 (executable)
@@ -5,9 +5,11 @@ DEF_VER=v1.1.GIT
 
 # First try git-describe, then see if there is a version file
 # (included in release tarballs), then default
-VN=$(git-describe --abbrev=4 HEAD 2>/dev/null) ||
-VN=$(cat version) ||
-VN="$DEF_VER"
+if VN=$(git-describe --abbrev=4 HEAD 2>/dev/null); then
+       VN=$(echo "$VN" | sed -e 's/-/./g');
+else
+       VN=$(cat version) || VN="$DEF_VER"
+fi
 
 VN=$(expr "$VN" : v*'\(.*\)')
 
diff --git a/INSTALL b/INSTALL
index 916ddd4..433449f 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -5,8 +5,8 @@ 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 ;# as yourself
-       # make prefix=/usr install ;# as root
+       $ make prefix=/usr all doc ;# as yourself
+       # make prefix=/usr install install-doc ;# as root
 
 (or prefix=/usr/local, of course).  Just like any program suite
 that uses $prefix, the built results have some paths encoded,
@@ -90,3 +90,27 @@ Issues of note:
    You can place local settings in config.mak and the Makefile
    will include them.  Note that config.mak is not distributed;
    the name is reserved for local settings.
+
+ - To build and install documentation suite, you need to have the
+   asciidoc/xmlto toolchain.  Alternatively, pre-formatted
+   documentation are available in "html" and "man" branches of the git
+   repository itself.  For example, you could:
+
+       $ mkdir manual && cd manual
+       $ git init-db
+       $ git clone-pack git://git.kernel.org/pub/scm/git/git.git man html |
+         while read a b
+         do
+           echo $a >.git/$b
+         done
+       $ cp .git/refs/heads/man .git/refs/heads/master
+       $ git checkout
+
+   to checkout the pre-built man pages.  Also in this repository:
+
+       $ git checkout html
+
+   would instead give you a copy of what you see at:
+
+       http://www.kernel.org/pub/software/scm/git/docs/
+
index fa0cd83..2aa2385 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -19,10 +19,18 @@ all:
 # Define NO_EXPAT if you do not have expat installed.  git-http-push is
 # not built, and you cannot push using http:// and https:// transports.
 #
+# Define NO_D_INO_IN_DIRENT if you don't have d_ino in your struct dirent.
+#
+# Define NO_D_TYPE_IN_DIRENT if your platform defines DT_UNKNOWN but lacks
+# d_type in struct dirent (latest Cygwin -- will be fixed soonish).
+#
 # Define NO_STRCASESTR if you don't have strcasestr.
 #
 # Define NO_SETENV if you don't have setenv in the C library.
 #
+# Define USE_SYMLINK_HEAD if you want .git/HEAD to be a symbolic link.
+# Don't enable it on Windows.
+#
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
@@ -42,6 +50,9 @@ all:
 #
 # Define NO_IPV6 if you lack IPv6 support and getaddrinfo().
 #
+# Define NO_SOCKADDR_STORAGE if your platform does not have struct
+# sockaddr_storage.
+#
 # Define COLLISION_CHECK below if you believe that SHA1's
 # 1461501637330902918203684832716283019655932542976 hashes do not give you
 # sufficient guarantee that no collisions between objects will ever happen.
@@ -56,7 +67,7 @@ all:
 # change being considered an inode change from the update-cache perspective.
 
 GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
-       @sh ./GIT-VERSION-GEN
+       @$(SHELL) ./GIT-VERSION-GEN
 -include GIT-VERSION-FILE
 
 # CFLAGS and LDFLAGS are for the users to override from the command line.
@@ -65,9 +76,11 @@ CFLAGS = -g -O2 -Wall
 LDFLAGS =
 ALL_CFLAGS = $(CFLAGS)
 ALL_LDFLAGS = $(LDFLAGS)
+STRIP ?= strip
 
 prefix = $(HOME)
 bindir = $(prefix)/bin
+gitexecdir = $(prefix)/bin
 template_dir = $(prefix)/share/git-core/templates/
 GIT_PYTHON_DIR = $(prefix)/share/git-core/python
 # DESTDIR=
@@ -112,14 +125,14 @@ SCRIPT_PYTHON = \
 SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH)) \
          $(patsubst %.perl,%,$(SCRIPT_PERL)) \
          $(patsubst %.py,%,$(SCRIPT_PYTHON)) \
-         gitk git-cherry-pick
+         git-cherry-pick
 
 # The ones that do not have to link with lcrypto nor lz.
 SIMPLE_PROGRAMS = \
        git-get-tar-commit-id$X git-mailinfo$X git-mailsplit$X \
        git-stripspace$X git-daemon$X
 
-# ... and all the rest
+# ... and all the rest that could be moved out of bindir to gitexecdir
 PROGRAMS = \
        git-apply$X git-cat-file$X \
        git-checkout-index$X git-clone-pack$X git-commit-tree$X \
@@ -140,8 +153,8 @@ PROGRAMS = \
        git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
        git-describe$X
 
-# what 'all' will build and 'install' will install.
-ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X
+# what 'all' will build and 'install' will install, in gitexecdir
+ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)
 
 # Backward compatibility -- to be removed after 1.0
 PROGRAMS += git-ssh-pull$X git-ssh-push$X
@@ -169,11 +182,11 @@ LIB_H = \
 
 DIFF_OBJS = \
        diff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \
-       diffcore-pickaxe.o diffcore-rename.o tree-diff.o
+       diffcore-pickaxe.o diffcore-rename.o tree-diff.o combine-diff.o
 
 LIB_OBJS = \
        blob.o commit.o connect.o count-delta.o csum-file.o \
-       date.o diff-delta.o entry.o ident.o index.o \
+       date.o diff-delta.o entry.o exec_cmd.o ident.o index.o \
        object.o pack-check.o patch-delta.o path.o pkt-line.o \
        quote.o read-cache.o refs.o run-command.o \
        server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
@@ -219,6 +232,7 @@ ifeq ($(uname_S),SunOS)
        SHELL_PATH = /bin/bash
        NO_STRCASESTR = YesPlease
        ifeq ($(uname_R),5.8)
+               NO_UNSETENV = YesPlease
                NO_SETENV = YesPlease
        endif
        INSTALL = ginstall
@@ -226,6 +240,8 @@ ifeq ($(uname_S),SunOS)
        ALL_CFLAGS += -D__EXTENSIONS__
 endif
 ifeq ($(uname_O),Cygwin)
+       NO_D_TYPE_IN_DIRENT = YesPlease
+       NO_D_INO_IN_DIRENT = YesPlease
        NO_STRCASESTR = YesPlease
        NEEDS_LIBICONV = YesPlease
        # There are conflicting reports about this.
@@ -234,7 +250,11 @@ ifeq ($(uname_O),Cygwin)
        # NO_MMAP = YesPlease
        NO_IPV6 = YesPlease
        X = .exe
-       ALL_CFLAGS += -DUSE_SYMLINK_HEAD=0
+endif
+ifeq ($(uname_S),FreeBSD)
+       NEEDS_LIBICONV = YesPlease
+       ALL_CFLAGS += -I/usr/local/include
+       ALL_LDFLAGS += -L/usr/local/lib
 endif
 ifeq ($(uname_S),OpenBSD)
        NO_STRCASESTR = YesPlease
@@ -327,6 +347,12 @@ ifdef NEEDS_NSL
        LIBS += -lnsl
        SIMPLE_LIB += -lnsl
 endif
+ifdef NO_D_TYPE_IN_DIRENT
+       ALL_CFLAGS += -DNO_D_TYPE_IN_DIRENT
+endif
+ifdef NO_D_INO_IN_DIRENT
+       ALL_CFLAGS += -DNO_D_INO_IN_DIRENT
+endif
 ifdef NO_STRCASESTR
        COMPAT_CFLAGS += -DNO_STRCASESTR
        COMPAT_OBJS += compat/strcasestr.o
@@ -335,12 +361,23 @@ ifdef NO_SETENV
        COMPAT_CFLAGS += -DNO_SETENV
        COMPAT_OBJS += compat/setenv.o
 endif
+ifdef NO_SETENV
+       COMPAT_CFLAGS += -DNO_UNSETENV
+       COMPAT_OBJS += compat/unsetenv.o
+endif
 ifdef NO_MMAP
        COMPAT_CFLAGS += -DNO_MMAP
        COMPAT_OBJS += compat/mmap.o
 endif
 ifdef NO_IPV6
-       ALL_CFLAGS += -DNO_IPV6 -Dsockaddr_storage=sockaddr_in
+       ALL_CFLAGS += -DNO_IPV6
+endif
+ifdef NO_SOCKADDR_STORAGE
+ifdef NO_IPV6
+       ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in
+else
+       ALL_CFLAGS += -Dsockaddr_storage=sockaddr_in6
+endif
 endif
 
 ifdef PPC_SHA1
@@ -366,13 +403,16 @@ LIB_OBJS += $(COMPAT_OBJS)
 export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
 ### Build rules
 
-all: $(ALL_PROGRAMS)
+all: $(ALL_PROGRAMS) git$X gitk
 
 all:
        $(MAKE) -C templates
 
+strip: $(PROGRAMS) git$X
+       $(STRIP) $(STRIP_OPTS) $(PROGRAMS) git$X
+
 git$X: git.c $(LIB_FILE)
-       $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
+       $(CC) -DGIT_VERSION='"$(GIT_VERSION)"' \
                $(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE)
 
 $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
@@ -412,6 +452,8 @@ git$X git.spec \
 %.o: %.S
        $(CC) -o $*.o -c $(ALL_CFLAGS) $<
 
+exec_cmd.o: ALL_CFLAGS += -DGIT_EXEC_PATH=\"$(gitexecdir)\"
+
 git-%$X: %.o $(LIB_FILE)
        $(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
 
@@ -468,7 +510,9 @@ check:
 
 install: all
        $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(bindir))
-       $(INSTALL) $(ALL_PROGRAMS) $(call shellquote,$(DESTDIR)$(bindir))
+       $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(gitexecdir))
+       $(INSTALL) $(ALL_PROGRAMS) $(call shellquote,$(DESTDIR)$(gitexecdir))
+       $(INSTALL) git$X gitk $(call shellquote,$(DESTDIR)$(bindir))
        $(MAKE) -C templates install
        $(INSTALL) -d -m755 $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
        $(INSTALL) $(PYMODULES) $(call shellquote,$(DESTDIR)$(GIT_PYTHON_DIR))
@@ -502,8 +546,7 @@ rpm: dist
 
 clean:
        rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o $(LIB_FILE)
-       rm -f $(PROGRAMS) $(SIMPLE_PROGRAMS) git$X
-       rm -f $(filter-out gitk,$(SCRIPTS))
+       rm -f $(ALL_PROGRAMS) git$X
        rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo
        rm -rf $(GIT_TARNAME)
        rm -f $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
@@ -512,6 +555,6 @@ clean:
        $(MAKE) -C t/ clean
        rm -f GIT-VERSION-FILE
 
-.PHONY: all install clean
+.PHONY: all install clean strip
 .PHONY: .FORCE-GIT-VERSION-FILE
 
diff --git a/cache.h b/cache.h
index 29c9e81..bdbe2d6 100644 (file)
--- a/cache.h
+++ b/cache.h
 #define deflateBound(c,s)  ((s) + (((s) + 7) >> 3) + (((s) + 63) >> 6) + 11)
 #endif
 
-#ifdef DT_UNKNOWN
+#if defined(DT_UNKNOWN) && !NO_D_TYPE_IN_DIRENT
 #define DTYPE(de)      ((de)->d_type)
 #else
+#undef DT_UNKNOWN
+#undef DT_DIR
+#undef DT_REG
+#undef DT_LNK
 #define DT_UNKNOWN     0
 #define DT_DIR         1
 #define DT_REG         2
@@ -205,9 +209,6 @@ extern char *write_sha1_file_prepare(void *buf,
 
 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, const char **paths);
-
 extern int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
                              size_t bufsize, size_t *bufposn);
 extern int write_sha1_to_fd(int fd, const unsigned char *sha1);
@@ -220,6 +221,9 @@ extern int has_pack_file(const unsigned char *sha1);
 extern int has_pack_index(const unsigned char *sha1);
 
 /* Convert to/from hex/sha1 representation */
+#define MINIMUM_ABBREV 4
+#define DEFAULT_ABBREV 7
+
 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! */
diff --git a/combine-diff.c b/combine-diff.c
new file mode 100644 (file)
index 0000000..243f967
--- /dev/null
@@ -0,0 +1,688 @@
+#include "cache.h"
+#include "commit.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "quote.h"
+
+static int uninteresting(struct diff_filepair *p)
+{
+       if (diff_unmodified_pair(p))
+               return 1;
+       if (!S_ISREG(p->one->mode) || !S_ISREG(p->two->mode))
+               return 1;
+       return 0;
+}
+
+static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
+{
+       struct diff_queue_struct *q = &diff_queued_diff;
+       struct combine_diff_path *p;
+       int i;
+
+       if (!n) {
+               struct combine_diff_path *list = NULL, **tail = &list;
+               for (i = 0; i < q->nr; i++) {
+                       int len;
+                       const char *path;
+                       if (uninteresting(q->queue[i]))
+                               continue;
+                       path = q->queue[i]->two->path;
+                       len = strlen(path);
+
+                       p = xmalloc(sizeof(*p) + len + 1 + num_parent * 20);
+                       p->path = (char*) &(p->parent_sha1[num_parent][0]);
+                       memcpy(p->path, path, len);
+                       p->path[len] = 0;
+                       p->len = len;
+                       p->next = NULL;
+                       memcpy(p->sha1, q->queue[i]->two->sha1, 20);
+                       memcpy(p->parent_sha1[n], q->queue[i]->one->sha1, 20);
+                       *tail = p;
+                       tail = &p->next;
+               }
+               return list;
+       }
+
+       for (p = curr; p; p = p->next) {
+               int found = 0;
+               if (!p->len)
+                       continue;
+               for (i = 0; i < q->nr; i++) {
+                       const char *path;
+                       int len;
+
+                       if (uninteresting(q->queue[i]))
+                               continue;
+                       path = q->queue[i]->two->path;
+                       len = strlen(path);
+                       if (len == p->len && !memcmp(path, p->path, len)) {
+                               found = 1;
+                               memcpy(p->parent_sha1[n],
+                                      q->queue[i]->one->sha1, 20);
+                               break;
+                       }
+               }
+               if (!found)
+                       p->len = 0;
+       }
+       return curr;
+}
+
+struct lline {
+       struct lline *next;
+       int len;
+       unsigned long parent_map;
+       char line[FLEX_ARRAY];
+};
+
+struct sline {
+       struct lline *lost_head, **lost_tail;
+       char *bol;
+       int len;
+       unsigned long flag;
+};
+
+static char *grab_blob(const unsigned char *sha1, unsigned long *size)
+{
+       char *blob;
+       char type[20];
+       if (!memcmp(sha1, null_sha1, 20)) {
+               /* deleted blob */
+               *size = 0;
+               return xcalloc(1, 1);
+       }
+       blob = read_sha1_file(sha1, type, size);
+       if (strcmp(type, "blob"))
+               die("object '%s' is not a blob!", sha1_to_hex(sha1));
+       return blob;
+}
+
+#define TMPPATHLEN 50
+#define MAXLINELEN 10240
+
+static void write_to_temp_file(char *tmpfile, void *blob, unsigned long size)
+{
+       int fd = git_mkstemp(tmpfile, TMPPATHLEN, ".diff_XXXXXX");
+       if (fd < 0)
+               die("unable to create temp-file");
+       if (write(fd, blob, size) != size)
+               die("unable to write temp-file");
+       close(fd);
+}
+
+static void write_temp_blob(char *tmpfile, const unsigned char *sha1)
+{
+       unsigned long size;
+       void *blob;
+       blob = grab_blob(sha1, &size);
+       write_to_temp_file(tmpfile, blob, size);
+       free(blob);
+}
+
+static int parse_num(char **cp_p, unsigned int *num_p)
+{
+       char *cp = *cp_p;
+       unsigned int num = 0;
+       int read_some;
+
+       while ('0' <= *cp && *cp <= '9')
+               num = num * 10 + *cp++ - '0';
+       if (!(read_some = cp - *cp_p))
+               return -1;
+       *cp_p = cp;
+       *num_p = num;
+       return 0;
+}
+
+static int parse_hunk_header(char *line, int len,
+                            unsigned int *ob, unsigned int *on,
+                            unsigned int *nb, unsigned int *nn)
+{
+       char *cp;
+       cp = line + 4;
+       if (parse_num(&cp, ob)) {
+       bad_line:
+               return error("malformed diff output: %s", line);
+       }
+       if (*cp == ',') {
+               cp++;
+               if (parse_num(&cp, on))
+                       goto bad_line;
+       }
+       else
+               *on = 1;
+       if (*cp++ != ' ' || *cp++ != '+')
+               goto bad_line;
+       if (parse_num(&cp, nb))
+               goto bad_line;
+       if (*cp == ',') {
+               cp++;
+               if (parse_num(&cp, nn))
+                       goto bad_line;
+       }
+       else
+               *nn = 1;
+       return -!!memcmp(cp, " @@", 3);
+}
+
+static void append_lost(struct sline *sline, int n, const char *line)
+{
+       struct lline *lline;
+       int len = strlen(line);
+       unsigned long this_mask = (1UL<<n);
+       if (line[len-1] == '\n')
+               len--;
+
+       /* Check to see if we can squash things */
+       if (sline->lost_head) {
+               struct lline *last_one = NULL;
+               /* We cannot squash it with earlier one */
+               for (lline = sline->lost_head;
+                    lline;
+                    lline = lline->next)
+                       if (lline->parent_map & this_mask)
+                               last_one = lline;
+               lline = last_one ? last_one->next : sline->lost_head;
+               while (lline) {
+                       if (lline->len == len &&
+                           !memcmp(lline->line, line, len)) {
+                               lline->parent_map |= this_mask;
+                               return;
+                       }
+                       lline = lline->next;
+               }
+       }
+
+       lline = xmalloc(sizeof(*lline) + len + 1);
+       lline->len = len;
+       lline->next = NULL;
+       lline->parent_map = this_mask;
+       memcpy(lline->line, line, len);
+       lline->line[len] = 0;
+       *sline->lost_tail = lline;
+       sline->lost_tail = &lline->next;
+}
+
+static void combine_diff(const unsigned char *parent, const char *ourtmp,
+                        struct sline *sline, int cnt, int n)
+{
+       FILE *in;
+       char parent_tmp[TMPPATHLEN];
+       char cmd[TMPPATHLEN * 2 + 1024];
+       char line[MAXLINELEN];
+       unsigned int lno, ob, on, nb, nn;
+       unsigned long pmask = ~(1UL << n);
+       struct sline *lost_bucket = NULL;
+
+       write_temp_blob(parent_tmp, parent);
+       sprintf(cmd, "diff --unified=0 -La/x -Lb/x '%s' '%s'",
+               parent_tmp, ourtmp);
+       in = popen(cmd, "r");
+       if (!in)
+               return;
+
+       lno = 1;
+       while (fgets(line, sizeof(line), in) != NULL) {
+               int len = strlen(line);
+               if (5 < len && !memcmp("@@ -", line, 4)) {
+                       if (parse_hunk_header(line, len,
+                                             &ob, &on, &nb, &nn))
+                               break;
+                       lno = nb;
+                       if (!nb) {
+                               /* @@ -1,2 +0,0 @@ to remove the
+                                * first two lines...
+                                */
+                               nb = 1;
+                       }
+                       lost_bucket = &sline[nb-1]; /* sline is 0 based */
+                       continue;
+               }
+               if (!lost_bucket)
+                       continue;
+               switch (line[0]) {
+               case '-':
+                       append_lost(lost_bucket, n, line+1);
+                       break;
+               case '+':
+                       sline[lno-1].flag &= pmask;
+                       lno++;
+                       break;
+               }
+       }
+       fclose(in);
+       unlink(parent_tmp);
+}
+
+static unsigned long context = 3;
+static char combine_marker = '@';
+
+static int interesting(struct sline *sline, unsigned long all_mask)
+{
+       return ((sline->flag & all_mask) != all_mask || sline->lost_head);
+}
+
+static unsigned long line_common_diff(struct sline *sline, unsigned long all_mask)
+{
+       /*
+        * Look at the line and see from which parents we have the
+        * same difference.
+        */
+
+       /* Lower bits of sline->flag records if the parent had this
+        * line, so XOR with all_mask gives us on-bits for parents we
+        * have differences with.
+        */
+       unsigned long common_adds = (sline->flag ^ all_mask) & all_mask;
+       unsigned long common_removes = all_mask;
+
+       /* If all the parents have this line, that also counts as
+        * having the same difference.
+        */
+       if (!common_adds)
+               common_adds = all_mask;
+
+       if (sline->lost_head) {
+               /* Lost head list records the lines removed from
+                * the parents, and parent_map records from which
+                * parent the line was removed.
+                */
+               struct lline *ll;
+               for (ll = sline->lost_head; ll; ll = ll->next) {
+                       common_removes &= ll->parent_map;
+               }
+       }
+       return common_adds & common_removes;
+}
+
+static unsigned long line_all_diff(struct sline *sline, unsigned long all_mask)
+{
+       /*
+        * Look at the line and see from which parents we have some difference.
+        */
+       unsigned long different = (sline->flag ^ all_mask) & all_mask;
+       if (sline->lost_head) {
+               /* Lost head list records the lines removed from
+                * the parents, and parent_map records from which
+                * parent the line was removed.
+                */
+               struct lline *ll;
+               for (ll = sline->lost_head; ll; ll = ll->next) {
+                       different |= ll->parent_map;
+               }
+       }
+       return different;
+}
+
+static unsigned long adjust_hunk_tail(struct sline *sline,
+                                     unsigned long all_mask,
+                                     unsigned long hunk_begin,
+                                     unsigned long i)
+{
+       /* i points at the first uninteresting line.
+        * If the last line of the hunk was interesting
+        * only because it has some deletion, then
+        * it is not all that interesting for the
+        * purpose of giving trailing context lines.
+        */
+       if ((hunk_begin + 1 <= i) &&
+           ((sline[i-1].flag & all_mask) == all_mask))
+               i--;
+       return i;
+}
+
+static unsigned long next_interesting(struct sline *sline,
+                                     unsigned long mark,
+                                     unsigned long i,
+                                     unsigned long cnt,
+                                     int uninteresting)
+{
+       while (i < cnt)
+               if (uninteresting ?
+                   !(sline[i].flag & mark) :
+                   (sline[i].flag & mark))
+                       return i;
+               else
+                       i++;
+       return cnt;
+}
+
+static int give_context(struct sline *sline, unsigned long cnt, int num_parent)
+{
+       unsigned long all_mask = (1UL<<num_parent) - 1;
+       unsigned long mark = (1UL<<num_parent);
+       unsigned long i;
+
+       i = next_interesting(sline, mark, 0, cnt, 0);
+       if (cnt <= i)
+               return 0;
+
+       while (i < cnt) {
+               unsigned long j = (context < i) ? (i - context) : 0;
+               unsigned long k;
+               while (j < i)
+                       sline[j++].flag |= mark;
+
+       again:
+               j = next_interesting(sline, mark, i, cnt, 1);
+               if (cnt <= j)
+                       break; /* the rest are all interesting */
+
+               /* lookahead context lines */
+               k = next_interesting(sline, mark, j, cnt, 0);
+               j = adjust_hunk_tail(sline, all_mask, i, j);
+
+               if (k < j + context) {
+                       /* k is interesting and [j,k) are not, but
+                        * paint them interesting because the gap is small.
+                        */
+                       while (j < k)
+                               sline[j++].flag |= mark;
+                       i = k;
+                       goto again;
+               }
+
+               /* j is the first uninteresting line and there is
+                * no overlap beyond it within context lines.
+                */
+               i = k;
+               k = (j + context < cnt) ? j + context : cnt;
+               while (j < k)
+                       sline[j++].flag |= mark;
+       }
+       return 1;
+}
+
+static int make_hunks(struct sline *sline, unsigned long cnt,
+                      int num_parent, int dense)
+{
+       unsigned long all_mask = (1UL<<num_parent) - 1;
+       unsigned long mark = (1UL<<num_parent);
+       unsigned long i;
+       int has_interesting = 0;
+
+       for (i = 0; i < cnt; i++) {
+               if (interesting(&sline[i], all_mask))
+                       sline[i].flag |= mark;
+               else
+                       sline[i].flag &= ~mark;
+       }
+       if (!dense)
+               return give_context(sline, cnt, num_parent);
+
+       /* Look at each hunk, and if we have changes from only one
+        * parent, or the changes are the same from all but one
+        * parent, mark that uninteresting.
+        */
+       i = 0;
+       while (i < cnt) {
+               unsigned long j, hunk_begin, hunk_end;
+               int same, diff;
+               unsigned long same_diff, all_diff;
+               while (i < cnt && !(sline[i].flag & mark))
+                       i++;
+               if (cnt <= i)
+                       break; /* No more interesting hunks */
+               hunk_begin = i;
+               for (j = i + 1; j < cnt; j++) {
+                       if (!(sline[j].flag & mark)) {
+                               /* Look beyond the end to see if there
+                                * is an interesting line after this
+                                * hunk within context span.
+                                */
+                               unsigned long la; /* lookahead */
+                               int contin = 0;
+                               la = adjust_hunk_tail(sline, all_mask,
+                                                    hunk_begin, j);
+                               la = (la + context < cnt) ?
+                                       (la + context) : cnt;
+                               while (j <= --la) {
+                                       if (sline[la].flag & mark) {
+                                               contin = 1;
+                                               break;
+                                       }
+                               }
+                               if (!contin)
+                                       break;
+                               j = la;
+                       }
+               }
+               hunk_end = j;
+
+               /* [i..hunk_end) are interesting.  Now does it have
+                * the same change with all but one parent?
+                */
+               same_diff = all_mask;
+               all_diff = 0;
+               for (j = i; j < hunk_end; j++) {
+                       same_diff &= line_common_diff(sline + j, all_mask);
+                       all_diff |= line_all_diff(sline + j, all_mask);
+               }
+               diff = same = 0;
+               for (j = 0; j < num_parent; j++) {
+                       if (same_diff & (1UL<<j))
+                               same++;
+                       if (all_diff & (1UL<<j))
+                               diff++;
+               }
+               if ((num_parent - 1 <= same) || (diff == 1)) {
+                       /* This hunk is not that interesting after all */
+                       for (j = hunk_begin; j < hunk_end; j++)
+                               sline[j].flag &= ~mark;
+               }
+               i = hunk_end;
+       }
+
+       has_interesting = give_context(sline, cnt, num_parent);
+       return has_interesting;
+}
+
+static void dump_sline(struct sline *sline, int cnt, int num_parent)
+{
+       unsigned long mark = (1UL<<num_parent);
+       int i;
+       int lno = 0;
+
+       while (1) {
+               struct sline *sl = &sline[lno];
+               int hunk_end;
+               while (lno < cnt && !(sline[lno].flag & mark))
+                       lno++;
+               if (cnt <= lno)
+                       break;
+               for (hunk_end = lno + 1; hunk_end < cnt; hunk_end++)
+                       if (!(sline[hunk_end].flag & mark))
+                               break;
+               for (i = 0; i <= num_parent; i++) putchar(combine_marker);
+               printf(" +%d,%d ", lno+1, hunk_end-lno);
+               for (i = 0; i <= num_parent; i++) putchar(combine_marker);
+               putchar('\n');
+               while (lno < hunk_end) {
+                       struct lline *ll;
+                       int j;
+                       sl = &sline[lno++];
+                       ll = sl->lost_head;
+                       while (ll) {
+                               for (j = 0; j < num_parent; j++) {
+                                       if (ll->parent_map & (1UL<<j))
+                                               putchar('-');
+                                       else
+                                               putchar(' ');
+                               }
+                               puts(ll->line);
+                               ll = ll->next;
+                       }
+                       for (j = 0; j < num_parent; j++) {
+                               if ((1UL<<j) & sl->flag)
+                                       putchar(' ');
+                               else
+                                       putchar('+');
+                       }
+                       printf("%.*s\n", sl->len, sl->bol);
+               }
+       }
+}
+
+int show_combined_diff(struct combine_diff_path *elem, int num_parent,
+                      int dense, const char *header, int show_empty)
+{
+       unsigned long size, cnt, lno;
+       char *result, *cp, *ep;
+       struct sline *sline; /* survived lines */
+       int i, show_hunks, shown_header = 0;
+       char ourtmp_buf[TMPPATHLEN];
+       char *ourtmp = ourtmp_buf;
+
+       /* Read the result of merge first */
+       if (memcmp(elem->sha1, null_sha1, 20)) {
+               result = grab_blob(elem->sha1, &size);
+               write_to_temp_file(ourtmp, result, size);
+       }
+       else {
+               struct stat st;
+               int fd;
+               ourtmp = elem->path;
+               if (0 <= (fd = open(ourtmp, O_RDONLY)) &&
+                   !fstat(fd, &st)) {
+                       int len = st.st_size;
+                       int cnt = 0;
+
+                       size = len;
+                       result = xmalloc(len + 1);
+                       while (cnt < len) {
+                               int done = xread(fd, result+cnt, len-cnt);
+                               if (done == 0)
+                                       break;
+                               if (done < 0)
+                                       die("read error '%s'", ourtmp);
+                               cnt += done;
+                       }
+                       result[len] = 0;
+               }
+               else {
+                       /* deleted file */
+                       size = 0;
+                       result = xmalloc(1);
+                       result[0] = 0;
+                       ourtmp = "/dev/null";
+               }
+               if (0 <= fd)
+                       close(fd);
+       }
+
+       for (cnt = 0, cp = result; cp - result < size; cp++) {
+               if (*cp == '\n')
+                       cnt++;
+       }
+       if (result[size-1] != '\n')
+               cnt++; /* incomplete line */
+
+       sline = xcalloc(cnt, sizeof(*sline));
+       ep = result;
+       sline[0].bol = result;
+       for (lno = 0, cp = result; cp - result < size; cp++) {
+               if (*cp == '\n') {
+                       sline[lno].lost_tail = &sline[lno].lost_head;
+                       sline[lno].len = cp - sline[lno].bol;
+                       sline[lno].flag = (1UL<<num_parent) - 1;
+                       lno++;
+                       if (lno < cnt)
+                               sline[lno].bol = cp + 1;
+               }
+       }
+       if (result[size-1] != '\n') {
+               sline[cnt-1].lost_tail = &sline[cnt-1].lost_head;
+               sline[cnt-1].len = size - (sline[cnt-1].bol - result);
+               sline[cnt-1].flag = (1UL<<num_parent) - 1;
+       }
+
+       for (i = 0; i < num_parent; i++)
+               combine_diff(elem->parent_sha1[i], ourtmp, sline, cnt, i);
+
+       show_hunks = make_hunks(sline, cnt, num_parent, dense);
+
+       if (header && (show_hunks || show_empty)) {
+               shown_header++;
+               puts(header);
+       }
+       if (show_hunks) {
+               printf("diff --%s ", dense ? "cc" : "combined");
+               if (quote_c_style(elem->path, NULL, NULL, 0))
+                       quote_c_style(elem->path, NULL, stdout, 0);
+               else
+                       printf("%s", elem->path);
+               putchar('\n');
+               dump_sline(sline, cnt, num_parent);
+       }
+       if (ourtmp == ourtmp_buf)
+               unlink(ourtmp);
+       free(result);
+
+       for (i = 0; i < cnt; i++) {
+               if (sline[i].lost_head) {
+                       struct lline *ll = sline[i].lost_head;
+                       while (ll) {
+                               struct lline *tmp = ll;
+                               ll = ll->next;
+                               free(tmp);
+                       }
+               }
+       }
+       free(sline);
+       return shown_header;
+}
+
+int diff_tree_combined_merge(const unsigned char *sha1,
+                            const char *header,
+                            int show_empty_merge, int dense)
+{
+       struct commit *commit = lookup_commit(sha1);
+       struct diff_options diffopts;
+       struct commit_list *parents;
+       struct combine_diff_path *p, *paths = NULL;
+       int num_parent, i, num_paths;
+
+       diff_setup(&diffopts);
+       diffopts.output_format = DIFF_FORMAT_NO_OUTPUT;
+       diffopts.recursive = 1;
+
+       /* count parents */
+       for (parents = commit->parents, num_parent = 0;
+            parents;
+            parents = parents->next, num_parent++)
+               ; /* nothing */
+
+       /* find set of paths that everybody touches */
+       for (parents = commit->parents, i = 0;
+            parents;
+            parents = parents->next, i++) {
+               struct commit *parent = parents->item;
+               diff_tree_sha1(parent->object.sha1, commit->object.sha1, "",
+                              &diffopts);
+               paths = intersect_paths(paths, i, num_parent);
+               diff_flush(&diffopts);
+       }
+
+       /* find out surviving paths */
+       for (num_paths = 0, p = paths; p; p = p->next) {
+               if (p->len)
+                       num_paths++;
+       }
+       if (num_paths || show_empty_merge) {
+               for (p = paths; p; p = p->next) {
+                       if (!p->len)
+                               continue;
+                       if (show_combined_diff(p, num_parent, dense, header,
+                                              show_empty_merge))
+                               header = NULL;
+               }
+       }
+
+       /* Clean things up */
+       while (paths) {
+               struct combine_diff_path *tmp = paths;
+               paths = paths->next;
+               free(tmp);
+       }
+       return 0;
+}
index b8bf35e..97205bf 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -426,33 +426,37 @@ static int is_empty_line(const char *line, int len)
        return !len;
 }
 
-static int add_parent_info(enum cmit_fmt fmt, char *buf, const char *line, int parents)
+static int add_merge_info(enum cmit_fmt fmt, char *buf, const struct commit *commit, int abbrev)
 {
-       int offset = 0;
+       struct commit_list *parent = commit->parents;
+       int offset;
 
-       if (fmt == CMIT_FMT_ONELINE)
-               return offset;
-       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);
+       if ((fmt == CMIT_FMT_ONELINE) || !parent || !parent->next)
+               return 0;
+
+       offset = sprintf(buf, "Merge:");
+
+       while (parent) {
+               struct commit *p = parent->item;
+               parent = parent->next;
+
+               offset += sprintf(buf + offset,
+                                 abbrev ? " %s..." : " %s",
+                                 abbrev
+                                 ? find_unique_abbrev(p->object.sha1, abbrev)
+                                 : sha1_to_hex(p->object.sha1));
        }
+       buf[offset++] = '\n';
        return offset;
 }
 
-unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space)
+unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *commit, unsigned long len, char *buf, unsigned long space, int abbrev)
 {
        int hdr = 1, body = 0;
        unsigned long offset = 0;
-       int parents = 0;
        int indent = (fmt == CMIT_FMT_ONELINE) ? 0 : 4;
+       int parents_shown = 0;
+       const char *msg = commit->buffer;
 
        for (;;) {
                const char *line = msg;
@@ -488,9 +492,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned l
                        if (!memcmp(line, "parent ", 7)) {
                                if (linelen != 48)
                                        die("bad parent line in commit");
-                               offset += add_parent_info(fmt, buf + offset, line, ++parents);
+                               continue;
                        }
 
+                       if (!parents_shown) {
+                               offset += add_merge_info(fmt, buf + offset,
+                                                        commit, abbrev);
+                               parents_shown = 1;
+                               continue;
+                       }
                        /*
                         * MEDIUM == DEFAULT shows only author with dates.
                         * FULL shows both authors but not dates.
index 9c4a244..986b22d 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -48,7 +48,7 @@ enum cmit_fmt {
 };
 
 extern enum cmit_fmt get_commit_format(const char *arg);
-extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const char *msg, unsigned long len, char *buf, unsigned long space);
+extern unsigned long pretty_print_commit(enum cmit_fmt fmt, const struct commit *, unsigned long len, char *buf, unsigned long space, int abbrev);
 
 /** Removes the first commit from a list sorted by date, and adds all
  * of its parents.
diff --git a/compat/unsetenv.c b/compat/unsetenv.c
new file mode 100644 (file)
index 0000000..3a5e4ec
--- /dev/null
@@ -0,0 +1,26 @@
+#include <stdlib.h>
+#include <string.h>
+
+void gitunsetenv (const char *name)
+{
+     extern char **environ;
+     int src, dst;
+     size_t nmln;
+
+     nmln = strlen(name);
+
+     for (src = dst = 0; environ[src]; ++src) {
+         size_t enln;
+         enln = strlen(environ[src]);
+         if (enln > nmln) {
+               /* might match, and can test for '=' safely */
+              if (0 == strncmp (environ[src], name, nmln)
+                  && '=' == environ[src][nmln])
+                   /* matches, so skip */
+                   continue;
+         }
+         environ[dst] = environ[src];
+         ++dst;
+     }
+     environ[dst] = NULL;
+}
index d6f4e4c..3f2d65c 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -1,3 +1,4 @@
+#include "git-compat-util.h"
 #include "cache.h"
 #include "pkt-line.h"
 #include "quote.h"
@@ -644,10 +645,16 @@ int git_connect(int fd[2], char *url, const char *prog)
                                ssh_basename++;
                        execlp(ssh, ssh_basename, host, command, NULL);
                }
-               else
+               else {
+                       unsetenv(ALTERNATE_DB_ENVIRONMENT);
+                       unsetenv(DB_ENVIRONMENT);
+                       unsetenv(GIT_DIR_ENVIRONMENT);
+                       unsetenv(GRAFT_ENVIRONMENT);
+                       unsetenv(INDEX_ENVIRONMENT);
                        execlp("sh", "sh", "-c", command, NULL);
+               }
                die("exec failed");
-       }               
+       }
        fd[0] = pipefd[0][0];
        fd[1] = pipefd[1][1];
        close(pipefd[0][1]);
index 3bd1426..bb014fa 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -9,6 +9,7 @@
 #include <syslog.h>
 #include "pkt-line.h"
 #include "cache.h"
+#include "exec_cmd.h"
 
 static int log_syslog;
 static int verbose;
@@ -227,7 +228,7 @@ static int upload(char *dir)
        snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
 
        /* git-upload-pack only ever reads stuff, so this is safe */
-       execlp("git-upload-pack", "git-upload-pack", "--strict", timeout_buf, ".", NULL);
+       execl_git_cmd("upload-pack", "--strict", timeout_buf, ".", NULL);
        return -1;
 }
 
index cc95eb0..ff65742 100644 (file)
@@ -11,7 +11,6 @@ static const char describe_usage[] =
 static int all = 0;    /* Default to annotated tags only */
 static int tags = 0;   /* But allow any tags if --tags is specified */
 
-#define DEFAULT_ABBREV 8 /* maybe too many */
 static int abbrev = DEFAULT_ABBREV;
 
 static int names = 0, allocs = 0;
@@ -98,12 +97,20 @@ static int compare_names(const void *_a, const void *_b)
        return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
 }
 
-static void describe(struct commit *cmit, int last_one)
+static void describe(char *arg, int last_one)
 {
+       unsigned char sha1[20];
+       struct commit *cmit;
        struct commit_list *list;
        static int initialized = 0;
        struct commit_name *n;
 
+       if (get_sha1(arg, sha1) < 0)
+               usage(describe_usage);
+       cmit = lookup_commit_reference(sha1);
+       if (!cmit)
+               usage(describe_usage);
+
        if (!initialized) {
                initialized = 1;
                for_each_ref(get_name);
@@ -138,29 +145,29 @@ int main(int argc, char **argv)
 
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
-               unsigned char sha1[20];
-               struct commit *cmit;
 
-               if (!strcmp(arg, "--all")) {
+               if (*arg != '-')
+                       break;
+               else if (!strcmp(arg, "--all"))
                        all = 1;
-                       continue;
-               }
-               if (!strcmp(arg, "--tags")) {
+               else if (!strcmp(arg, "--tags"))
                        tags = 1;
-                       continue;
-               }
-               if (!strncmp(arg, "--abbrev=", 9)) {
+               else if (!strncmp(arg, "--abbrev=", 9)) {
                        abbrev = strtoul(arg + 9, NULL, 10);
-                       if (abbrev < 4 || 40 <= abbrev)
+                       if (abbrev < MINIMUM_ABBREV || 40 <= abbrev)
                                abbrev = DEFAULT_ABBREV;
-                       continue;
                }
-               if (get_sha1(arg, sha1) < 0)
-                       usage(describe_usage);
-               cmit = lookup_commit_reference(sha1);
-               if (!cmit)
+               else
                        usage(describe_usage);
-               describe(cmit, i == argc - 1);
        }
+
+       if (i == argc)
+               describe("HEAD", 1);
+       else
+               while (i < argc) {
+                       describe(argv[i], (i == argc - 1));
+                       i++;
+               }
+
        return 0;
 }
index 6c0696c..4cafd3e 100644 (file)
@@ -7,12 +7,14 @@
 #include "diff.h"
 
 static const char diff_files_usage[] =
-"git-diff-files [-q] [-0/-1/2/3] [<common diff options>] [<path>...]"
+"git-diff-files [-q] [-0/-1/2/3 |-c|--cc] [<common diff options>] [<path>...]"
 COMMON_DIFF_OPTIONS_HELP;
 
 static struct diff_options diff_options;
 static int silent = 0;
 static int diff_unmerged_stage = 2;
+static int combine_merges = 0;
+static int dense_combined_merges = 0;
 
 static void show_unmerge(const char *path)
 {
@@ -66,6 +68,10 @@ int main(int argc, const char **argv)
                        ; /* no-op */
                else if (!strcmp(argv[1], "-s"))
                        ; /* no-op */
+               else if (!strcmp(argv[1], "-c"))
+                       combine_merges = 1;
+               else if (!strcmp(argv[1], "--cc"))
+                       dense_combined_merges = combine_merges = 1;
                else {
                        int diff_opt_cnt;
                        diff_opt_cnt = diff_opt_parse(&diff_options,
@@ -82,6 +88,9 @@ int main(int argc, const char **argv)
                }
                argv++; argc--;
        }
+       if (combine_merges) {
+               diff_options.output_format = DIFF_FORMAT_PATCH;
+       }
 
        /* Find the directory, and set up the pathspec */
        pathspec = get_pathspec(prefix, argv + 1);
@@ -108,14 +117,38 @@ int main(int argc, const char **argv)
                        continue;
 
                if (ce_stage(ce)) {
-                       show_unmerge(ce->name);
+                       struct {
+                               struct combine_diff_path p;
+                               unsigned char fill[4][20];
+                       } combine;
+                       int num_compare_stages = 0;
+
+                       combine.p.next = NULL;
+                       combine.p.len = ce_namelen(ce);
+                       combine.p.path = xmalloc(combine.p.len + 1);
+                       memcpy(combine.p.path, ce->name, combine.p.len);
+                       combine.p.path[combine.p.len] = 0;
+                       memset(combine.p.sha1, 0, 100);
+
                        while (i < entries) {
                                struct cache_entry *nce = active_cache[i];
+                               int stage;
 
                                if (strcmp(ce->name, nce->name))
                                        break;
+
+                               /* Stage #2 (ours) is the first parent,
+                                * stage #3 (theirs) is the second.
+                                */
+                               stage = ce_stage(nce);
+                               if (2 <= stage) {
+                                       num_compare_stages++;
+                                       memcpy(combine.p.parent_sha1[stage-2],
+                                              nce->sha1, 20);
+                               }
+
                                /* diff against the proper unmerged stage */
-                               if (ce_stage(nce) == diff_unmerged_stage)
+                               if (stage == diff_unmerged_stage)
                                        ce = nce;
                                i++;
                        }
@@ -123,10 +156,21 @@ int main(int argc, const char **argv)
                         * Compensate for loop update
                         */
                        i--;
+
+                       if (combine_merges && num_compare_stages == 2) {
+                               show_combined_diff(&combine.p, 2,
+                                                  dense_combined_merges,
+                                                  NULL, 0);
+                               free(combine.p.path);
+                               continue;
+                       }
+                       free(combine.p.path);
+
                        /*
                         * Show the diff for the 'ce' if we found the one
                         * from the desired stage.
                         */
+                       show_unmerge(ce->name);
                        if (ce_stage(ce) != diff_unmerged_stage)
                                continue;
                }
index 87e1061..bbd873b 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "tree.h"
 #include "diff.h"
 
 static int cached_only = 0;
@@ -174,8 +175,7 @@ int main(int argc, const char **argv)
        unsigned char sha1[20];
        const char *prefix = setup_git_directory();
        const char **pathspec = NULL;
-       void *tree;
-       unsigned long size;
+       struct tree *tree;
        int ret;
        int allow_options = 1;
        int i;
@@ -233,10 +233,10 @@ int main(int argc, const char **argv)
 
        mark_merge_entries();
 
-       tree = read_object_with_reference(sha1, "tree", &size, NULL);
+       tree = parse_tree_indirect(sha1);
        if (!tree)
                die("bad tree object %s", tree_name);
-       if (read_tree(tree, size, 1, pathspec))
+       if (read_tree(tree, 1, pathspec))
                die("unable to read tree object %s", tree_name);
 
        ret = diff_cache(active_cache, active_nr, pathspec);
index efa2b94..6593a69 100644 (file)
@@ -6,6 +6,9 @@ static int show_root_diff = 0;
 static int no_commit_id = 0;
 static int verbose_header = 0;
 static int ignore_merges = 1;
+static int show_empty_combined = 0;
+static int combine_merges = 0;
+static int dense_combined_merges = 0;
 static int read_stdin = 0;
 
 static const char *header = NULL;
@@ -64,12 +67,13 @@ static int diff_root_tree(const unsigned char *new, const char *base)
 
 static const char *generate_header(const unsigned char *commit_sha1,
                                   const unsigned char *parent_sha1,
-                                  const char *msg)
+                                  const struct commit *commit)
 {
        static char this_header[16384];
        int offset;
        unsigned long len;
        int abbrev = diff_options.abbrev;
+       const char *msg = commit->buffer;
 
        if (!verbose_header)
                return sha1_to_hex(commit_sha1);
@@ -79,12 +83,16 @@ static const char *generate_header(const unsigned char *commit_sha1,
        offset = sprintf(this_header, "%s%s ",
                         header_prefix,
                         diff_unique_abbrev(commit_sha1, abbrev));
-       offset += sprintf(this_header + offset, "(from %s)\n",
-                        parent_sha1 ?
-                        diff_unique_abbrev(parent_sha1, abbrev) : "root");
-       offset += pretty_print_commit(commit_format, msg, len,
+       if (commit_sha1 != parent_sha1)
+               offset += sprintf(this_header + offset, "(from %s)\n",
+                                 parent_sha1
+                                 ? diff_unique_abbrev(parent_sha1, abbrev)
+                                 : "root");
+       else
+               offset += sprintf(this_header + offset, "(from parents)\n");
+       offset += pretty_print_commit(commit_format, commit, len,
                                      this_header + offset,
-                                     sizeof(this_header) - offset);
+                                     sizeof(this_header) - offset, abbrev);
        return this_header;
 }
 
@@ -103,19 +111,25 @@ static int diff_tree_commit(const unsigned char *commit_sha1)
        
        /* Root commit? */
        if (show_root_diff && !commit->parents) {
-               header = generate_header(sha1, NULL, commit->buffer);
+               header = generate_header(sha1, NULL, commit);
                diff_root_tree(commit_sha1, "");
        }
 
        /* More than one parent? */
-       if (ignore_merges && commit->parents && commit->parents->next)
-               return 0;
+       if (commit->parents && commit->parents->next) {
+               if (ignore_merges)
+                       return 0;
+               else if (combine_merges) {
+                       header = generate_header(sha1, sha1, commit);
+                       return diff_tree_combined_merge(sha1, header,
+                                                       show_empty_combined,
+                                                       dense_combined_merges);
+               }
+       }
 
        for (parents = commit->parents; parents; parents = parents->next) {
                struct commit *parent = parents->item;
-               header = generate_header(sha1,
-                                        parent->object.sha1,
-                                        commit->buffer);
+               header = generate_header(sha1, parent->object.sha1, commit);
                diff_tree_sha1_top(parent->object.sha1, commit_sha1, "");
                if (!header && verbose_header) {
                        header_prefix = "\ndiff-tree ";
@@ -154,7 +168,7 @@ static int diff_tree_stdin(char *line)
 }
 
 static const char diff_tree_usage[] =
-"git-diff-tree [--stdin] [-m] [-s] [-v] [--pretty] [-t] [-r] [--root] "
+"git-diff-tree [--stdin] [-m] [-c] [--cc] [-s] [-v] [--pretty] [-t] [-r] [--root] "
 "[<common diff options>] <tree-ish> [<tree-ish>] [<path>...]\n"
 "  -r            diff recursively\n"
 "  --root        include the initial commit as diff against /dev/null\n"
@@ -217,6 +231,14 @@ int main(int argc, const char **argv)
                        ignore_merges = 0;
                        continue;
                }
+               if (!strcmp(arg, "-c")) {
+                       combine_merges = 1;
+                       continue;
+               }
+               if (!strcmp(arg, "--cc")) {
+                       dense_combined_merges = combine_merges = 1;
+                       continue;
+               }
                if (!strcmp(arg, "-v")) {
                        verbose_header = 1;
                        header_prefix = "diff-tree ";
@@ -245,6 +267,12 @@ int main(int argc, const char **argv)
        if (diff_options.output_format == DIFF_FORMAT_PATCH)
                diff_options.recursive = 1;
 
+       if (combine_merges) {
+               diff_options.output_format = DIFF_FORMAT_PATCH;
+               show_empty_combined = !ignore_merges;
+               ignore_merges = 0;
+       }
+
        diff_tree_setup_paths(get_pathspec(prefix, argv));
        diff_setup_done(&diff_options);
 
diff --git a/diff.c b/diff.c
index 17d68fa..8ae6dbc 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -723,7 +723,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
 
        if (memcmp(one->sha1, two->sha1, 20)) {
                char one_sha1[41];
-               int abbrev = o->full_index ? 40 : DIFF_DEFAULT_INDEX_ABBREV;
+               int abbrev = o->full_index ? 40 : DEFAULT_ABBREV;
                memcpy(one_sha1, sha1_to_hex(one->sha1), 41);
 
                len += snprintf(msg + len, sizeof(msg) - len,
@@ -846,9 +846,14 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
        else if (!strcmp(arg, "--find-copies-harder"))
                options->find_copies_harder = 1;
        else if (!strcmp(arg, "--abbrev"))
-               options->abbrev = DIFF_DEFAULT_ABBREV;
-       else if (!strncmp(arg, "--abbrev=", 9))
+               options->abbrev = DEFAULT_ABBREV;
+       else if (!strncmp(arg, "--abbrev=", 9)) {
                options->abbrev = strtoul(arg + 9, NULL, 10);
+               if (options->abbrev < MINIMUM_ABBREV)
+                       options->abbrev = MINIMUM_ABBREV;
+               else if (40 < options->abbrev)
+                       options->abbrev = 40;
+       }
        else
                return 0;
        return 1;
diff --git a/diff.h b/diff.h
index 5696f2a..9a0169c 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -56,6 +56,19 @@ extern int diff_tree(struct tree_desc *t1, struct tree_desc *t2,
 extern int diff_tree_sha1(const unsigned char *old, const unsigned char *new,
                          const char *base, struct diff_options *opt);
 
+struct combine_diff_path {
+       struct combine_diff_path *next;
+       int len;
+       char *path;
+       unsigned char sha1[20];
+       unsigned char parent_sha1[FLEX_ARRAY][20];
+};
+
+int show_combined_diff(struct combine_diff_path *elem, int num_parent,
+                      int dense, const char *header, int show_empty);
+
+extern int diff_tree_combined_merge(const unsigned char *sha1, const char *, int, int);
+
 extern void diff_addremove(struct diff_options *,
                           int addremove,
                           unsigned mode,
@@ -88,9 +101,6 @@ extern int diff_setup_done(struct diff_options *);
 
 #define DIFF_PICKAXE_ALL       1
 
-#define DIFF_DEFAULT_INDEX_ABBREV      7 /* hex digits */
-#define DIFF_DEFAULT_ABBREV    7 /* hex digits */
-
 extern void diffcore_std(struct diff_options *);
 
 extern void diffcore_std_no_resolve(struct diff_options *);
diff --git a/exec_cmd.c b/exec_cmd.c
new file mode 100644 (file)
index 0000000..55af33b
--- /dev/null
@@ -0,0 +1,117 @@
+#include "cache.h"
+#include "exec_cmd.h"
+#define MAX_ARGS       32
+
+extern char **environ;
+static const char *builtin_exec_path = GIT_EXEC_PATH;
+static const char *current_exec_path = NULL;
+
+void git_set_exec_path(const char *exec_path)
+{
+       current_exec_path = exec_path;
+}
+
+
+/* Returns the highest-priority, location to look for git programs. */
+const char *git_exec_path()
+{
+       const char *env;
+
+       if (current_exec_path)
+               return current_exec_path;
+
+       env = getenv("GIT_EXEC_PATH");
+       if (env) {
+               return env;
+       }
+
+       return builtin_exec_path;
+}
+
+
+int execv_git_cmd(char **argv)
+{
+       char git_command[PATH_MAX + 1];
+       char *tmp;
+       int len, err, i;
+       const char *paths[] = { current_exec_path,
+                               getenv("GIT_EXEC_PATH"),
+                               builtin_exec_path };
+
+       for (i = 0; i < sizeof(paths)/sizeof(paths[0]); ++i) {
+               const char *exec_dir = paths[i];
+               if (!exec_dir) continue;
+
+               if (*exec_dir != '/') {
+                       if (!getcwd(git_command, sizeof(git_command))) {
+                               fprintf(stderr, "git: cannot determine "
+                                       "current directory\n");
+                               exit(1);
+                       }
+                       len = strlen(git_command);
+
+                       /* Trivial cleanup */
+                       while (!strncmp(exec_dir, "./", 2)) {
+                               exec_dir += 2;
+                               while (*exec_dir == '/')
+                                       exec_dir++;
+                       }
+                       snprintf(git_command + len, sizeof(git_command) - len,
+                                "/%s", exec_dir);
+               } else {
+                       strcpy(git_command, exec_dir);
+               }
+
+               len = strlen(git_command);
+               len += snprintf(git_command + len, sizeof(git_command) - len,
+                               "/git-%s", argv[0]);
+
+               if (sizeof(git_command) <= len) {
+                       fprintf(stderr,
+                               "git: command name given is too long.\n");
+                       break;
+               }
+
+               /* argv[0] must be the git command, but the argv array
+                * belongs to the caller, and my be reused in
+                * subsequent loop iterations. Save argv[0] and
+                * restore it on error.
+                */
+
+               tmp = argv[0];
+               argv[0] = git_command;
+
+               /* execve() can only ever return if it fails */
+               execve(git_command, argv, environ);
+
+               err = errno;
+
+               argv[0] = tmp;
+       }
+       return -1;
+
+}
+
+
+int execl_git_cmd(char *cmd,...)
+{
+       int argc;
+       char *argv[MAX_ARGS + 1];
+       char *arg;
+       va_list param;
+
+       va_start(param, cmd);
+       argv[0] = cmd;
+       argc = 1;
+       while (argc < MAX_ARGS) {
+               arg = argv[argc++] = va_arg(param, char *);
+               if (!arg)
+                       break;
+       }
+       va_end(param);
+       if (MAX_ARGS <= argc)
+               return error("too many args to run %s", cmd);
+
+       argv[argc] = NULL;
+       return execv_git_cmd(argv);
+}
diff --git a/exec_cmd.h b/exec_cmd.h
new file mode 100644 (file)
index 0000000..5150ee2
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef __GIT_EXEC_CMD_H_
+#define __GIT_EXEC_CMD_H_
+
+extern void git_set_exec_path(const char *exec_path);
+extern const char* git_exec_path(void);
+extern int execv_git_cmd(char **argv); /* NULL terminated */
+extern int execl_git_cmd(char *cmd, ...);
+
+
+#endif /* __GIT_EXEC_CMD_H_ */
index f46fe6e..859f400 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "exec_cmd.h"
 #include <sys/wait.h>
 
 static int finish_pack(const char *pack_tmp_name, const char *me)
@@ -27,8 +28,7 @@ static int finish_pack(const char *pack_tmp_name, const char *me)
                dup2(pipe_fd[1], 1);
                close(pipe_fd[0]);
                close(pipe_fd[1]);
-               execlp("git-index-pack","git-index-pack",
-                      "-o", idx, pack_tmp_name, NULL);
+               execl_git_cmd("index-pack", "-o", idx, pack_tmp_name, NULL);
                error("cannot exec git-index-pack <%s> <%s>",
                      idx, pack_tmp_name);
                exit(1);
@@ -105,8 +105,7 @@ int receive_unpack_pack(int fd[2], const char *me, int quiet)
                dup2(fd[0], 0);
                close(fd[0]);
                close(fd[1]);
-               execlp("git-unpack-objects", "git-unpack-objects",
-                      quiet ? "-q" : NULL, NULL);
+               execl_git_cmd("unpack-objects", quiet ? "-q" : NULL, NULL);
                die("git-unpack-objects exec failed");
        }
        close(fd[0]);
index 90e638e..9950be2 100644 (file)
@@ -20,6 +20,13 @@ static int check_strict = 0;
 static int keep_cache_objects = 0; 
 static unsigned char head_sha1[20];
 
+#if NO_D_INO_IN_DIRENT
+#define SORT_DIRENT 0
+#define DIRENT_SORT_HINT(de) 0
+#else
+#define SORT_DIRENT 1
+#define DIRENT_SORT_HINT(de) ((de)->d_ino)
+#endif
 
 static void objreport(struct object *obj, const char *severity,
                       const char *err, va_list params)
@@ -307,7 +314,9 @@ static void fsck_sha1_list(void)
 {
        int i, nr = sha1_list.nr;
 
-       qsort(sha1_list.entry, nr, sizeof(struct sha1_entry *), ino_compare);
+       if (SORT_DIRENT)
+               qsort(sha1_list.entry, nr,
+                     sizeof(struct sha1_entry *), ino_compare);
        for (i = 0; i < nr; i++) {
                struct sha1_entry *entry = sha1_list.entry[i];
                unsigned char *sha1 = entry->sha1;
@@ -361,7 +370,7 @@ static int fsck_dir(int i, char *path)
                        memcpy(name+2, de->d_name, len+1);
                        if (get_sha1_hex(name, sha1) < 0)
                                break;
-                       add_sha1_list(sha1, de->d_ino);
+                       add_sha1_list(sha1, DIRENT_SORT_HINT(de));
                        continue;
                }
                fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
index 3bbd111..d99688f 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-USAGE='[-f] [-b <new_branch>] [<branch>] [<paths>...]'
+USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
 SUBDIRECTORY_OK=Sometimes
 . git-sh-setup
 
@@ -9,6 +9,7 @@ new=
 force=
 branch=
 newbranch=
+merge=
 while [ "$#" != "0" ]; do
     arg="$1"
     shift
@@ -26,6 +27,9 @@ while [ "$#" != "0" ]; do
        "-f")
                force=1
                ;;
+       -m)
+               merge=1
+               ;;
        --)
                break
                ;;
@@ -71,7 +75,7 @@ done
 
 if test "$#" -ge 1
 then
-       if test '' != "$newbranch$force"
+       if test '' != "$newbranch$force$merge"
        then
                die "updating paths and switching branches or forcing are incompatible."
        fi
@@ -121,7 +125,48 @@ then
        git-checkout-index -q -f -u -a
 else
     git-update-index --refresh >/dev/null
-    git-read-tree -m -u $old $new
+    merge_error=$(git-read-tree -m -u $old $new 2>&1) || (
+       case "$merge" in
+       '')
+               echo >&2 "$merge_error"
+               exit 1 ;;
+       esac
+
+       # Match the index to the working tree, and do a three-way.
+       git diff-files --name-only | git update-index --remove --stdin &&
+       work=`git write-tree` &&
+       git read-tree --reset $new &&
+       git checkout-index -f -u -q -a &&
+       git read-tree -m -u $old $new $work || exit
+
+       if result=`git write-tree 2>/dev/null`
+       then
+           echo >&2 "Trivially automerged."
+       else
+           git merge-index -o git-merge-one-file -a
+       fi
+
+       # Do not register the cleanly merged paths in the index yet.
+       # this is not a real merge before committing, but just carrying
+       # the working tree changes along.
+       unmerged=`git ls-files -u`
+       git read-tree --reset $new
+       case "$unmerged" in
+       '')     ;;
+       *)
+               (
+                       z40=0000000000000000000000000000000000000000
+                       echo "$unmerged" |
+                       sed -e 's/^[0-7]* [0-9a-f]* /'"0 $z40 /"
+                       echo "$unmerged"
+               ) | git update-index --index-info
+               ;;
+       esac
+       exit 0
+    )
+    saved_err=$?
+    git diff-files --name-status
+    (exit $saved_err)
 fi
 
 # 
index 377d59e..47f3ec9 100755 (executable)
@@ -9,7 +9,7 @@
 unset CDPATH
 
 usage() {
-       echo >&2 "Usage: $0 [-l [-s]] [-q] [-u <upload-pack>] [-o <name>] [-n] <repo> [<dir>]"
+       echo >&2 "Usage: $0 [--bare] [-l [-s]] [-q] [-u <upload-pack>] [-o <name>] [-n] <repo> [<dir>]"
        exit 1
 }
 
@@ -53,11 +53,17 @@ use_local=no
 local_shared=no
 no_checkout=
 upload_pack=
+bare=
 origin=origin
+origin_override=
 while
        case "$#,$1" in
        0,*) break ;;
-       *,-n) no_checkout=yes ;;
+       *,-n|*,--no|*,--no-|*,--no-c|*,--no-ch|*,--no-che|*,--no-chec|\
+       *,--no-check|*,--no-checko|*,--no-checkou|*,--no-checkout)
+         no_checkout=yes ;;
+       *,--na|*,--nak|*,--nake|*,--naked|\
+       *,-b|*,--b|*,--ba|*,--bar|*,--bare) bare=yes ;;
        *,-l|*,--l|*,--lo|*,--loc|*,--loca|*,--local) use_local=yes ;;
         *,-s|*,--s|*,--sh|*,--sha|*,--shar|*,--share|*,--shared) 
           local_shared=yes; use_local=yes ;;
@@ -68,6 +74,11 @@ while
                    echo >&2 "'$2' is not suitable for a branch name"
                    exit 1
                }
+               test -z "$origin_override" || {
+                   echo >&2 "Do not give more than one -o options."
+                   exit 1
+               }
+               origin_override=yes
                origin="$2"; shift
                ;;
        1,-u|1,--upload-pack) usage ;;
@@ -81,6 +92,17 @@ do
        shift
 done
 
+# --bare implies --no-checkout
+if test yes = "$bare"
+then
+       if test yes = "$origin_override"
+       then
+               echo >&2 '--bare and -o $origin options are incompatible.'
+               exit 1
+       fi
+       no_checkout=yes
+fi
+
 # Turn the source into an absolute path if
 # it is local
 repo="$1"
@@ -92,13 +114,20 @@ fi
 
 dir="$2"
 # Try using "humanish" part of source repo if user didn't specify one
-[ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*/||g')
+[ -z "$dir" ] && dir=$(echo "$repo" | sed -e 's|/$||' -e 's|:*/*\.git$||' -e 's|.*[/:]||g')
 [ -e "$dir" ] && echo "$dir already exists." && usage
 mkdir -p "$dir" &&
-D=$(
-       (cd "$dir" && git-init-db && pwd)
-) &&
-test -d "$D" || usage
+D=$(cd "$dir" && pwd) &&
+case "$bare" in
+yes) GIT_DIR="$D" ;;
+*) GIT_DIR="$D/.git" ;;
+esac && export GIT_DIR && git-init-db || usage
+case "$bare" in
+yes)
+       GIT_DIR="$D" ;;
+*)
+       GIT_DIR="$D/.git" ;;
+esac
 
 # We do local magic only when the user tells us to.
 case "$local,$use_local" in
@@ -118,21 +147,21 @@ yes,yes)
            test -f "$repo/$sample_file" || exit
 
            l=
-           if ln "$repo/$sample_file" "$D/.git/objects/sample" 2>/dev/null
+           if ln "$repo/$sample_file" "$GIT_DIR/objects/sample" 2>/dev/null
            then
                    l=l
            fi &&
-           rm -f "$D/.git/objects/sample" &&
+           rm -f "$GIT_DIR/objects/sample" &&
            cd "$repo" &&
-           find objects -depth -print | cpio -puamd$l "$D/.git/" || exit 1
+           find objects -depth -print | cpio -puamd$l "$GIT_DIR/" || exit 1
            ;;
        yes)
-           mkdir -p "$D/.git/objects/info"
+           mkdir -p "$GIT_DIR/objects/info"
            {
                test -f "$repo/objects/info/alternates" &&
                cat "$repo/objects/info/alternates";
                echo "$repo/objects"
-           } >"$D/.git/objects/info/alternates"
+           } >"$GIT_DIR/objects/info/alternates"
            ;;
        esac
 
@@ -143,27 +172,27 @@ yes,yes)
                HEAD=HEAD
        fi
        (cd "$repo" && tar cf - refs $HEAD) |
-       (cd "$D/.git" && tar xf -) || exit 1
+       (cd "$GIT_DIR" && tar xf -) || exit 1
        ;;
 *)
        case "$repo" in
        rsync://*)
                rsync $quiet -av --ignore-existing  \
-                       --exclude info "$repo/objects/" "$D/.git/objects/" &&
+                       --exclude info "$repo/objects/" "$GIT_DIR/objects/" &&
                rsync $quiet -av --ignore-existing  \
-                       --exclude info "$repo/refs/" "$D/.git/refs/" || exit
+                       --exclude info "$repo/refs/" "$GIT_DIR/refs/" || exit
 
                # Look at objects/info/alternates for rsync -- http will
                # support it natively and git native ones will do it on the
                # remote end.  Not having that file is not a crime.
                rsync -q "$repo/objects/info/alternates" \
-                       "$D/.git/TMP_ALT" 2>/dev/null ||
-                       rm -f "$D/.git/TMP_ALT"
-               if test -f "$D/.git/TMP_ALT"
+                       "$GIT_DIR/TMP_ALT" 2>/dev/null ||
+                       rm -f "$GIT_DIR/TMP_ALT"
+               if test -f "$GIT_DIR/TMP_ALT"
                then
                    ( cd "$D" &&
                      . git-parse-remote &&
-                     resolve_alternates "$repo" <"./.git/TMP_ALT" ) |
+                     resolve_alternates "$repo" <"$GIT_DIR/TMP_ALT" ) |
                    while read alt
                    do
                        case "$alt" in 'bad alternate: '*) die "$alt";; esac
@@ -171,9 +200,9 @@ yes,yes)
                        '')     echo >&2 "Getting alternate: $alt" ;;
                        esac
                        rsync $quiet -av --ignore-existing  \
-                           --exclude info "$alt" "$D/.git/objects" || exit
+                           --exclude info "$alt" "$GIT_DIR/objects" || exit
                    done
-                   rm -f "$D/.git/TMP_ALT"
+                   rm -f "$GIT_DIR/TMP_ALT"
                fi
                ;;
        http://*)
@@ -194,25 +223,25 @@ esac
 
 cd "$D" || exit
 
-if test -f ".git/HEAD"
+if test -f "$GIT_DIR/HEAD" && test -z "$bare"
 then
        head_points_at=`git-symbolic-ref HEAD`
        case "$head_points_at" in
        refs/heads/*)
                head_points_at=`expr "$head_points_at" : 'refs/heads/\(.*\)'`
-               mkdir -p .git/remotes &&
-               echo >.git/remotes/origin \
+               mkdir -p "$GIT_DIR/remotes" &&
+               echo >"$GIT_DIR/remotes/origin" \
                "URL: $repo
 Pull: $head_points_at:$origin" &&
                git-update-ref "refs/heads/$origin" $(git-rev-parse HEAD) &&
-               find .git/refs/heads -type f -print |
+               (cd "$GIT_DIR" && find "refs/heads" -type f -print) |
                while read ref
                do
-                       head=`expr "$ref" : '.git/refs/heads/\(.*\)'` &&
+                       head=`expr "$ref" : 'refs/heads/\(.*\)'` &&
                        test "$head_points_at" = "$head" ||
                        test "$origin" = "$head" ||
                        echo "Pull: ${head}:${head}"
-               done >>.git/remotes/origin
+               done >>"$GIT_DIR/remotes/origin"
        esac
 
        case "$no_checkout" in
index 12ce659..f982b8e 100644 (file)
@@ -63,6 +63,11 @@ extern int gitfakemunmap(void *start, size_t length);
 extern int gitsetenv(const char *, const char *, int);
 #endif
 
+#ifdef NO_UNSETENV
+#define unsetenv gitunsetenv
+extern void gitunsetenv(const char *);
+#endif
+
 #ifdef NO_STRCASESTR
 #define strcasestr gitstrcasestr
 extern char *gitstrcasestr(const char *haystack, const char *needle);
index 8619e7d..fc207fc 100755 (executable)
@@ -29,19 +29,63 @@ use IPC::Open2;
 $SIG{'PIPE'}="IGNORE";
 $ENV{'TZ'}="UTC";
 
-our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M);
+our($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A);
+my (%conv_author_name, %conv_author_email);
 
 sub usage() {
        print STDERR <<END;
 Usage: ${\basename $0}     # fetch/update GIT from CVS
-       [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT]
-       [-p opts-for-cvsps] [-C GIT_repository] [-z fuzz]
-       [-i] [-k] [-u] [-s subst] [-m] [-M regex] [CVS_module]
+       [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
+       [-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] [-i] [-k] [-u]
+       [-s subst] [-m] [-M regex] [CVS_module]
 END
        exit(1);
 }
 
-getopts("hivmkuo:d:p:C:z:s:M:P:") or usage();
+sub read_author_info($) {
+       my ($file) = @_;
+       my $user;
+       open my $f, '<', "$file" or die("Failed to open $file: $!\n");
+
+       while (<$f>) {
+               # Expected format is this:
+               #   exon=Andreas Ericsson <ae@op5.se>
+               if (m/^(\S+?)\s*=\s*(.+?)\s*<(.+)>\s*$/) {
+                       $user = $1;
+                       $conv_author_name{$user} = $2;
+                       $conv_author_email{$user} = $3;
+               }
+               # However, we also read from CVSROOT/users format
+               # to ease migration.
+               elsif (/^(\w+):(['"]?)(.+?)\2\s*$/) {
+                       my $mapped;
+                       ($user, $mapped) = ($1, $3);
+                       if ($mapped =~ /^\s*(.*?)\s*<(.*)>\s*$/) {
+                               $conv_author_name{$user} = $1;
+                               $conv_author_email{$user} = $2;
+                       }
+                       elsif ($mapped =~ /^<?(.*)>?$/) {
+                               $conv_author_name{$user} = $user;
+                               $conv_author_email{$user} = $1;
+                       }
+               }
+               # NEEDSWORK: Maybe warn on unrecognized lines?
+       }
+       close ($f);
+}
+
+sub write_author_info($) {
+       my ($file) = @_;
+       open my $f, '>', $file or
+         die("Failed to open $file for writing: $!");
+
+       foreach (keys %conv_author_name) {
+               print $f "$_=$conv_author_name{$_} <$conv_author_email{$_}>\n";
+       }
+       close ($f);
+}
+
+getopts("hivmkuo:d:p:C:z:s:M:P:A:") or usage();
 usage if $opt_h;
 
 @ARGV <= 1 or usage();
@@ -453,7 +497,7 @@ CVS2GIT_HEAD exists.
 Make sure your working directory corresponds to HEAD and remove CVS2GIT_HEAD.
 You may need to run
 
-    git-read-tree -m -u CVS2GIT_HEAD HEAD
+    git read-tree -m -u CVS2GIT_HEAD HEAD
 EOM
        }
        system('cp', "$git_dir/HEAD", "$git_dir/CVS2GIT_HEAD");
@@ -489,6 +533,14 @@ EOM
 -d $git_dir
        or die "Could not create git subdir ($git_dir).\n";
 
+# now we read (and possibly save) author-info as well
+-f "$git_dir/cvs-authors" and
+  read_author_info("$git_dir/cvs-authors");
+if ($opt_A) {
+       read_author_info($opt_A);
+       write_author_info("$git_dir/cvs-authors");
+}
+
 my $pid = open(CVS,"-|");
 die "Cannot fork: $!\n" unless defined $pid;
 unless($pid) {
@@ -702,6 +754,9 @@ while(<CVS>) {
                s/\s+$//;
                if (/^(.*?)\s+<(.*)>/) {
                    ($author_name, $author_email) = ($1, $2);
+               } elsif ($conv_author_name{$_}) {
+                       $author_name = $conv_author_name{$_};
+                       $author_email = $conv_author_email{$_};
                } else {
                    $author_name = $author_email = $_;
                }
index 73e57bd..b4325d9 100755 (executable)
@@ -17,12 +17,20 @@ append=
 force=
 verbose=
 update_head_ok=
+exec=
+upload_pack=
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
        -a|--a|--ap|--app|--appe|--appen|--append)
                append=t
                ;;
+       --upl|--uplo|--uploa|--upload|--upload-|--upload-p|\
+       --upload-pa|--upload-pac|--upload-pack)
+               shift
+               exec="--exec=$1" 
+               upload_pack="-u $1"
+               ;;
        -f|--f|--fo|--for|--forc|--force)
                force=t
                ;;
@@ -40,6 +48,9 @@ do
        -v|--verbose)
                verbose=Yes
                ;;
+       -k|--k|--ke|--kee|--keep)
+               keep=--keep
+               ;;
        -*)
                usage
                ;;
@@ -193,7 +204,7 @@ reflist=$(get_remote_refs_for_fetch "$@")
 if test "$tags"
 then
        taglist=$(IFS=" " &&
-                 git-ls-remote --tags "$remote" |
+                 git-ls-remote $upload_pack --tags "$remote" |
                  while read sha1 name
                  do
                        case "$name" in
@@ -309,7 +320,7 @@ fetch_main () {
     ( : subshell because we muck with IFS
       IFS="    $LF"
       (
-         git-fetch-pack "$remote" $rref || echo failed "$remote"
+         git-fetch-pack $exec $keep "$remote" $rref || echo failed "$remote"
       ) |
       while read sha1 remote_name
       do
@@ -358,7 +369,7 @@ fetch_main "$reflist"
 case "$no_tags$tags" in
 '')
        taglist=$(IFS=" " &&
-       git-ls-remote --tags "$remote" |
+       git-ls-remote $upload_pack --tags "$remote" |
        sed -ne 's|^\([0-9a-f]*\)[      ]\(refs/tags/.*\)^{}$|\1 \2|p' |
        while read sha1 name
        do
index d3979d7..5fb8ce1 100755 (executable)
@@ -34,14 +34,12 @@ outdir=./
 while case "$#" in 0) break;; esac
 do
     case "$1" in
-    -a|--a|--au|--aut|--auth|--autho|--author)
-    author=t ;;
     -c|--c|--ch|--che|--chec|--check)
     check=t ;;
-    -d|--d|--da|--dat|--date)
-    date=t ;;
-    -m|--m|--mb|--mbo|--mbox)
-    date=t author=t mbox=t ;;
+    -a|--a|--au|--aut|--auth|--autho|--author|\
+    -d|--d|--da|--dat|--date|\
+    -m|--m|--mb|--mbo|--mbox) # now noop
+    ;;
     -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
     --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
     keep_subject=t ;;
@@ -173,80 +171,89 @@ titleScript='
        q
 '
 
-whosepatchScript='
-/^author /{
-       s/'\''/'\''\\'\'\''/g
-       s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
-       q
-}'
-
 process_one () {
-       mailScript='
-       /./d
-       /^$/n'
-       case "$keep_subject" in
-       t)  ;;
-       *)
-           mailScript="$mailScript"'
-           s|^\[PATCH[^]]*\] *||
-           s|^|[PATCH'"$num"'] |'
-           ;;
-       esac
-       mailScript="$mailScript"'
-       s|^|Subject: |'
-       case "$mbox" in
-       t)
-           echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
-           ;;
-       esac
+       perl -w -e '
+my ($keep_subject, $num, $signoff, $commsg) = @ARGV;
+my ($signoff_pattern, $done_header, $done_subject, $signoff_seen,
+    $last_was_signoff);
 
-       eval "$(sed -ne "$whosepatchScript" $commsg)"
-       test "$author,$au" = ",$me" || {
-               mailScript="$mailScript"'
-       a\
-From: '"$au"
-       }
-       test "$date,$au" = ",$me" || {
-               mailScript="$mailScript"'
-       a\
-Date: '"$ad"
-       }
+if ($signoff) {
+       $signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`;
+       $signoff =~ s/>.*/>/;
+       $signoff_pattern = quotemeta($signoff);
+}
 
-       mailScript="$mailScript"'
-       a\
+my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat);
+my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
 
-       : body
-       p
-       n
-       b body'
+sub show_date {
+    my ($time, $tz) = @_;
+    my $minutes = abs($tz);
+    $minutes = ($minutes / 100) * 60 + ($minutes % 100);
+    if ($tz < 0) {
+        $minutes = -$minutes;
+    }
+    my $t = $time + $minutes * 60;
+    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t);
+    return sprintf("%s %s %d %02d:%02d:%02d %d %+05d",
+                  $weekday_names[$wday],
+                  $month_names[$mon],
+                  $mday, $hour, $min, $sec,
+                  $year+1900, $tz);
+}
 
-       (cat $commsg ; echo; echo) |
-       sed -ne "$mailScript" |
-       git-stripspace
+print "From nobody Mon Sep 17 00:00:00 2001\n";
+open FH, "git stripspace <$commsg |" or die "open $commsg pipe";
+while (<FH>) {
+    unless ($done_header) {
+       if (/^$/) {
+           $done_header = 1;
+       }
+       elsif (/^author (.*>) (.*)$/) {
+           my ($author_ident, $author_date) = ($1, $2);
+           my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/);
+           $author_date = show_date($utc, $off);
 
-       test "$signoff" = "t" && {
-               offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
-               line="Signed-off-by: $offsigner"
-               grep -q "^$line\$" $commsg || {
-                       echo
-                       echo "$line"
-                       echo
-               }
+           print "From: $author_ident\n";
+           print "Date: $author_date\n";
        }
-       echo
-       echo '---'
-       echo
+       next;
+    }
+    unless ($done_subject) {
+       unless ($keep_subject) {
+           s/^\[PATCH[^]]*\]\s*//;
+           s/^/[PATCH$num] /;
+       }
+        print "Subject: $_";
+       $done_subject = 1;
+       next;
+    }
+
+    $last_was_signoff = 0;
+    if (/Signed-off-by:/i) {
+        if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) {
+           $signoff_seen = 1;
+       }
+    }
+    print $_;
+}
+if (!$signoff_seen && $signoff ne "") {
+    if (!$last_was_signoff) {
+        print "\n";
+    }
+    print "$signoff\n";
+}
+print "\n---\n\n";
+close FH or die "close $commsg pipe";
+' "$keep_subject" "$num" "$signoff" $commsg
+
        git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
        echo
        git-diff-tree -p $diff_opts "$commit"
        echo "-- "
        echo "@@GIT_VERSION@@"
 
-       case "$mbox" in
-       t)
-               echo
-               ;;
-       esac
+       echo
 }
 
 total=`wc -l <$series | tr -dc "[0-9]"`
index 2ed8e95..ad4f2fe 100755 (executable)
@@ -3,22 +3,32 @@
 # Copyright (c) Linus Torvalds, 2005
 #
 
-USAGE='<option>... <pattern> <path>...'
+USAGE='[<option>...] [-e] <pattern> [<path>...]'
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
+got_pattern () {
+       if [ -z "$no_more_patterns" ]
+       then
+               pattern="$1" no_more_patterns=yes
+       else
+               die "git-grep: do not specify more than one pattern"
+       fi
+}
+
+no_more_patterns=
 pattern=
 flags=()
 git_flags=()
 while : ; do
        case "$1" in
-       --cached|--deleted|--others|--killed|\
-       --ignored|--exclude=*|\
+       -o|--cached|--deleted|--others|--killed|\
+       --ignored|--modified|--exclude=*|\
        --exclude-from=*|\--exclude-per-directory=*)
                git_flags=("${git_flags[@]}" "$1")
                ;;
        -e)
-               pattern="$2"
+               got_pattern "$2"
                shift
                ;;
        -A|-B|-C|-D|-d|-f|-m)
@@ -26,7 +36,7 @@ while : ; do
                shift
                ;;
        --)
-               # The rest are git-ls-files paths (or flags)
+               # The rest are git-ls-files paths
                shift
                break
                ;;
@@ -34,10 +44,12 @@ while : ; do
                flags=("${flags[@]}" "$1")
                ;;
        *)
-               if [ -z "$pattern" ]; then
-                       pattern="$1"
+               if [ -z "$no_more_patterns" ]
+               then
+                       got_pattern "$1"
                        shift
                fi
+               [ "$1" = -- ] && shift
                break
                ;;
        esac
@@ -46,5 +58,5 @@ done
 [ "$pattern" ] || {
        usage
 }
-git-ls-files -z "${git_flags[@]}" "$@" |
-       xargs -0 grep "${flags[@]}" -e "$pattern"
+git-ls-files -z "${git_flags[@]}" -- "$@" |
+       xargs -0 grep "${flags[@]}" -e "$pattern" --
index f699268..2c9a588 100755 (executable)
@@ -2,7 +2,8 @@
 #
 
 usage () {
-    echo >&2 "usage: $0 [--heads] [--tags] <repository> <refs>..."
+    echo >&2 "usage: $0 [--heads] [--tags] [-u|--upload-pack <upload-pack>]"
+    echo >&2 "          <repository> <refs>..."
     exit 1;
 }
 
@@ -11,6 +12,7 @@ die () {
     exit 1
 }
 
+exec=
 while case "$#" in 0) break;; esac
 do
   case "$1" in
@@ -18,6 +20,11 @@ do
   heads=heads; shift ;;
   -t|--t|--ta|--tag|--tags)
   tags=tags; shift ;;
+  -u|--u|--up|--upl|--uploa|--upload|--upload-|--upload-p|--upload-pa|\
+  --upload-pac|--upload-pack)
+       shift
+       exec="--exec=$1"
+       shift;;
   --)
   shift; break ;;
   -*)
@@ -66,7 +73,7 @@ rsync://* )
        ;;
 
 * )
-       git-peek-remote "$peek_repo" ||
+       git-peek-remote $exec "$peek_repo" ||
                echo "failed    slurping"
        ;;
 esac |
index 7adffdc..eb74f96 100755 (executable)
@@ -48,16 +48,24 @@ MRC=$head MSG= PARENT="-p $head"
 MRT=$(git-write-tree)
 CNT=1 ;# counting our head
 NON_FF_MERGE=0
+OCTOPUS_FAILURE=0
 for SHA1 in $remotes
 do
+       case "$OCTOPUS_FAILURE" in
+       1)
+               # We allow only last one to have a hand-resolvable
+               # conflicts.  Last round failed and we still had
+               # a head to merge.
+               echo "Automated merge did not work."
+               echo "Should not be doing an Octopus."
+               exit 2
+       esac
+
        common=$(git-merge-base --all $MRC $SHA1) ||
                die "Unable to find common commit with $SHA1"
 
-       case "$common" in
-       ?*"$LF"?*)
-               die "Not trivially mergeable."
-               ;;
-       $SHA1)
+       case "$LF$common$LF" in
+       *"$LF$SHA1$LF"*)
                echo "Already up-to-date with $SHA1"
                continue
                ;;
@@ -88,7 +96,7 @@ do
        then
                echo "Simple merge did not work, trying automatic merge."
                git-merge-index -o git-merge-one-file -a ||
-               exit 2 ; # Automatic merge failed; should not be doing Octopus
+               OCTOPUS_FAILURE=1
                next=$(git-write-tree 2>/dev/null)
        fi
 
@@ -103,4 +111,4 @@ do
        MRT=$next
 done
 
-exit 0
+exit "$OCTOPUS_FAILURE"
index 0a158ef..92e5a65 100755 (executable)
@@ -301,5 +301,13 @@ then
        "Automatic merge went well; stopped before committing as requested"
        exit 0
 else
+       {
+           echo '
+Conflicts:
+'
+               git ls-files --unmerged |
+               sed -e 's/^[^   ]*      /       /' |
+               uniq
+       } >>"$GIT_DIR/MERGE_MSG"
        die "Automatic merge failed; fix up by hand"
 fi
index 1c5cf80..706db99 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-USAGE='[--all] [--force] <repository> [<refspec>...]'
+USAGE='[--all] [--tags] [--force] <repository> [<refspec>...]'
 . git-sh-setup
 
 # Parse out parameters and then stop at remote, so that we can
@@ -9,12 +9,15 @@ has_all=
 has_force=
 has_exec=
 remote=
+do_tags=
 
 while case "$#" in 0) break ;; esac
 do
        case "$1" in
        --all)
                has_all=--all ;;
+       --tags)
+               do_tags=yes ;;
        --force)
                has_force=--force ;;
        --exec=*)
@@ -36,11 +39,27 @@ esac
 
 . git-parse-remote
 remote=$(get_remote_url "$@")
+
 case "$has_all" in
---all) set x ;;
-'')    set x $(get_remote_refs_for_push "$@") ;;
+--all)
+       set x ;;
+'')
+       case "$do_tags,$#" in
+       yes,1)
+               set x $(cd "$GIT_DIR/refs" && find tags -type f -print) ;;
+       yes,*)
+               set x $(cd "$GIT_DIR/refs" && find tags -type f -print) \
+                   $(get_remote_refs_for_push "$@") ;;
+       ,*)
+               set x $(get_remote_refs_for_push "$@") ;;
+       esac
 esac
-shift
+
+shift ;# away the initial 'x'
+
+# $# is now 0 if there was no explicit refspec on the command line
+# and there was no defalt refspec to push from remotes/ file.
+# we will let git-send-pack to do its "matching refs" thing.
 
 case "$remote" in
 git://*)
index 80e2500..d4f985b 100755 (executable)
@@ -4,7 +4,7 @@ USAGE='[-p] [--max-count=<n>] [<since>..<limit>] [--pretty=<format>] [-m] [git-d
 SUBDIRECTORY_OK='Yes'
 . git-sh-setup
 
-diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@")
+diff_tree_flags=$(git-rev-parse --sq --no-revs --flags "$@") || exit
 test -z "$diff_tree_flags" &&
        diff_tree_flags=$(git-repo-config --get whatchanged.difftree)
 test -z "$diff_tree_flags" &&
diff --git a/git.c b/git.c
index 5e7da74..4616df6 100644 (file)
--- a/git.c
+++ b/git.c
@@ -10,6 +10,7 @@
 #include <stdarg.h>
 #include <sys/ioctl.h>
 #include "git-compat-util.h"
+#include "exec_cmd.h"
 
 #ifndef PATH_MAX
 # define PATH_MAX 4096
@@ -233,14 +234,11 @@ int main(int argc, char **argv, char **envp)
 {
        char git_command[PATH_MAX + 1];
        char wd[PATH_MAX + 1];
-       int i, len, show_help = 0;
-       char *exec_path = getenv("GIT_EXEC_PATH");
+       int i, show_help = 0;
+       const char *exec_path;
 
        getcwd(wd, PATH_MAX);
 
-       if (!exec_path)
-               exec_path = GIT_EXEC_PATH;
-
        for (i = 1; i < argc; i++) {
                char *arg = argv[i];
 
@@ -256,10 +254,11 @@ int main(int argc, char **argv, char **envp)
 
                if (!strncmp(arg, "exec-path", 9)) {
                        arg += 9;
-                       if (*arg == '=')
+                       if (*arg == '=') {
                                exec_path = arg + 1;
-                       else {
-                               puts(exec_path);
+                               git_set_exec_path(exec_path);
+                       } else {
+                               puts(git_exec_path());
                                exit(0);
                        }
                }
@@ -275,42 +274,15 @@ int main(int argc, char **argv, char **envp)
 
        if (i >= argc || show_help) {
                if (i >= argc)
-                       cmd_usage(exec_path, NULL);
+                       cmd_usage(git_exec_path(), NULL);
 
                show_man_page(argv[i]);
        }
 
-       if (*exec_path != '/') {
-               if (!getcwd(git_command, sizeof(git_command))) {
-                       fprintf(stderr,
-                               "git: cannot determine current directory\n");
-                       exit(1);
-               }
-               len = strlen(git_command);
-
-               /* Trivial cleanup */
-               while (!strncmp(exec_path, "./", 2)) {
-                       exec_path += 2;
-                       while (*exec_path == '/')
-                               exec_path++;
-               }
-               snprintf(git_command + len, sizeof(git_command) - len,
-                        "/%s", exec_path);
-       }
-       else
-               strcpy(git_command, exec_path);
-       len = strlen(git_command);
-       prepend_to_path(git_command, len);
-
-       len += snprintf(git_command + len, sizeof(git_command) - len,
-                       "/git-%s", argv[i]);
-       if (sizeof(git_command) <= len) {
-               fprintf(stderr, "git: command name given is too long.\n");
-               exit(1);
-       }
+       exec_path = git_exec_path();
+       prepend_to_path(exec_path, strlen(exec_path));
 
-       /* execve() can only ever return if it fails */
-       execve(git_command, &argv[i], envp);
+       execv_git_cmd(argv + i);
 
        if (errno == ENOENT)
                cmd_usage(exec_path, "'%s' is not a git-command", argv[i]);
index d585b6f..d005643 100644 (file)
--- a/ls-tree.c
+++ b/ls-tree.c
@@ -84,8 +84,7 @@ static int show_tree(unsigned char *sha1, const char *base, int baselen,
 int main(int argc, const char **argv)
 {
        unsigned char sha1[20];
-       char *buf;
-       unsigned long size;
+       struct tree *tree;
 
        prefix = setup_git_directory();
        if (prefix && *prefix)
@@ -131,10 +130,10 @@ int main(int argc, const char **argv)
                usage(ls_tree_usage);
 
        pathspec = get_pathspec(prefix, argv + 2);
-       buf = read_object_with_reference(sha1, "tree", &size, NULL);
-       if (!buf)
+       tree = parse_tree_indirect(sha1);
+       if (!tree)
                die("not a tree object");
-       read_tree_recursive(buf, size, "", 0, 0, pathspec, show_tree);
+       read_tree_recursive(tree, "", 0, 0, pathspec, show_tree);
 
        return 0;
 }
index 14c1358..c529e2d 100644 (file)
@@ -11,7 +11,6 @@
 #include <sys/stat.h>
 #include <string.h>
 #include <stdio.h>
-#include <assert.h>
 #include "cache.h"
 
 static const char git_mailsplit_usage[] =
index f847ec2..eae31e3 100644 (file)
@@ -6,7 +6,7 @@
 
 static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
 
-static const char unpacker[] = "git-unpack-objects";
+static char *unpacker[] = { "unpack-objects", NULL };
 
 static int report_status = 0;
 
@@ -257,7 +257,7 @@ static void read_head_info(void)
 
 static const char *unpack(int *error_code)
 {
-       int code = run_command(unpacker, NULL);
+       int code = run_command_v_opt(1, unpacker, RUN_GIT_CMD);
 
        *error_code = 0;
        switch (code) {
diff --git a/refs.c b/refs.c
index c33729c..d01fc39 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -6,10 +6,6 @@
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define MAXDEPTH 5
 
-#ifndef USE_SYMLINK_HEAD
-#define USE_SYMLINK_HEAD 1
-#endif
-
 const char *resolve_ref(const char *path, unsigned char *sha1, int reading)
 {
        int depth = MAXDEPTH, len;
@@ -80,7 +76,7 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master)
        char ref[1000];
        int fd, len, written;
 
-#if USE_SYMLINK_HEAD
+#ifdef USE_SYMLINK_HEAD
        if (!only_use_symrefs) {
                unlink(git_HEAD);
                if (!symlink(refs_heads_master, git_HEAD))
index d060966..0b142c1 100644 (file)
@@ -21,6 +21,7 @@ static const char rev_list_usage[] =
 "    --min-age=epoch\n"
 "    --sparse\n"
 "    --no-merges\n"
+"    --remove-empty\n"
 "    --all\n"
 "  ordering output:\n"
 "    --merge-order [ --show-breaks ]\n"
@@ -54,6 +55,7 @@ static int stop_traversal = 0;
 static int topo_order = 0;
 static int no_merges = 0;
 static const char **paths = NULL;
+static int remove_empty_trees = 0;
 
 static void show_commit(struct commit *commit)
 {
@@ -81,7 +83,7 @@ static void show_commit(struct commit *commit)
 
        if (verbose_header) {
                static char pretty_header[16384];
-               pretty_print_commit(commit_format, commit->buffer, ~0, pretty_header, sizeof(pretty_header));
+               pretty_print_commit(commit_format, commit, ~0, pretty_header, sizeof(pretty_header), 0);
                printf("%s%c", pretty_header, hdr_termination);
        }
        fflush(stdout);
@@ -424,14 +426,33 @@ static void mark_edges_uninteresting(struct commit_list *list)
        }
 }
 
-static int is_different = 0;
+#define TREE_SAME      0
+#define TREE_NEW       1
+#define TREE_DIFFERENT 2
+static int tree_difference = TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
                    const char *base, const char *path)
 {
-       is_different = 1;
+       int diff = TREE_DIFFERENT;
+
+       /*
+        * Is it an add of a new file? It means that
+        * the old tree didn't have it at all, so we
+        * will turn "TREE_SAME" -> "TREE_NEW", but
+        * leave any "TREE_DIFFERENT" alone (and if
+        * it already was "TREE_NEW", we'll keep it
+        * "TREE_NEW" of course).
+        */
+       if (addremove == '+') {
+               diff = tree_difference;
+               if (diff != TREE_SAME)
+                       return;
+               diff = TREE_NEW;
+       }
+       tree_difference = diff;
 }
 
 static void file_change(struct diff_options *options,
@@ -440,7 +461,7 @@ static void file_change(struct diff_options *options,
                 const unsigned char *new_sha1,
                 const char *base, const char *path)
 {
-       is_different = 1;
+       tree_difference = TREE_DIFFERENT;
 }
 
 static struct diff_options diff_opt = {
@@ -449,12 +470,16 @@ static struct diff_options diff_opt = {
        .change = file_change,
 };
 
-static int same_tree(struct tree *t1, struct tree *t2)
+static int compare_tree(struct tree *t1, struct tree *t2)
 {
-       is_different = 0;
+       if (!t1)
+               return TREE_NEW;
+       if (!t2)
+               return TREE_DIFFERENT;
+       tree_difference = TREE_SAME;
        if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "", &diff_opt) < 0)
-               return 0;
-       return !is_different;
+               return TREE_DIFFERENT;
+       return tree_difference;
 }
 
 static int same_tree_as_empty(struct tree *t1)
@@ -474,28 +499,55 @@ static int same_tree_as_empty(struct tree *t1)
        empty.buf = "";
        empty.size = 0;
 
-       is_different = 0;
+       tree_difference = 0;
        retval = diff_tree(&empty, &real, "", &diff_opt);
        free(tree);
 
-       return retval >= 0 && !is_different;
+       return retval >= 0 && !tree_difference;
 }
 
-static struct commit *try_to_simplify_merge(struct commit *commit, struct commit_list *parent)
+static void try_to_simplify_commit(struct commit *commit)
 {
+       struct commit_list **pp, *parent;
+
        if (!commit->tree)
-               return NULL;
+               return;
 
-       while (parent) {
+       if (!commit->parents) {
+               if (!same_tree_as_empty(commit->tree))
+                       commit->object.flags |= TREECHANGE;
+               return;
+       }
+
+       pp = &commit->parents;
+       while ((parent = *pp) != NULL) {
                struct commit *p = parent->item;
-               parent = parent->next;
+
+               if (p->object.flags & UNINTERESTING) {
+                       pp = &parent->next;
+                       continue;
+               }
+
                parse_commit(p);
-               if (!p->tree)
+               switch (compare_tree(p->tree, commit->tree)) {
+               case TREE_SAME:
+                       parent->next = NULL;
+                       commit->parents = parent;
+                       return;
+
+               case TREE_NEW:
+                       if (remove_empty_trees && same_tree_as_empty(p->tree)) {
+                               *pp = parent->next;
+                               continue;
+                       }
+               /* fallthrough */
+               case TREE_DIFFERENT:
+                       pp = &parent->next;
                        continue;
-               if (same_tree(commit->tree, p->tree))
-                       return p;
+               }
+               die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
        }
-       return NULL;
+       commit->object.flags |= TREECHANGE;
 }
 
 static void add_parents_to_list(struct commit *commit, struct commit_list **list)
@@ -531,20 +583,14 @@ static void add_parents_to_list(struct commit *commit, struct commit_list **list
        }
 
        /*
-        * Ok, the commit wasn't uninteresting. If it
-        * is a merge, try to find the parent that has
-        * no differences in the path set if one exists.
+        * Ok, the commit wasn't uninteresting. Try to
+        * simplify the commit history and find the parent
+        * that has no differences in the path set if one exists.
         */
-       if (paths && parent && parent->next) {
-               struct commit *preferred;
-
-               preferred = try_to_simplify_merge(commit, parent);
-               if (preferred) {
-                       parent->item = preferred;
-                       parent->next = NULL;
-               }
-       }
+       if (paths)
+               try_to_simplify_commit(commit);
 
+       parent = commit->parents;
        while (parent) {
                struct commit *p = parent->item;
 
@@ -558,33 +604,6 @@ static void add_parents_to_list(struct commit *commit, struct commit_list **list
        }
 }
 
-static void compress_list(struct commit_list *list)
-{
-       while (list) {
-               struct commit *commit = list->item;
-               struct commit_list *parent = commit->parents;
-               list = list->next;
-
-               if (!parent) {
-                       if (!same_tree_as_empty(commit->tree))
-                               commit->object.flags |= TREECHANGE;
-                       continue;
-               }
-
-               /*
-                * Exactly one parent? Check if it leaves the tree
-                * unchanged
-                */
-               if (!parent->next) {
-                       struct tree *t1 = commit->tree;
-                       struct tree *t2 = parent->item->tree;
-                       if (!t1 || !t2 || same_tree(t1, t2))
-                               continue;
-               }
-               commit->object.flags |= TREECHANGE;
-       }
-}
-
 static struct commit_list *limit_list(struct commit_list *list)
 {
        struct commit_list *newlist = NULL;
@@ -614,8 +633,6 @@ static struct commit_list *limit_list(struct commit_list *list)
        }
        if (tree_objects)
                mark_edges_uninteresting(newlist);
-       if (paths && dense)
-               compress_list(newlist);
        if (bisect_list)
                newlist = find_bisection(newlist);
        return newlist;
@@ -808,6 +825,10 @@ int main(int argc, const char **argv)
                        dense = 0;
                        continue;
                }
+               if (!strcmp(arg, "--remove-empty")) {
+                       remove_empty_trees = 1;
+                       continue;
+               }
                if (!strcmp(arg, "--")) {
                        i++;
                        break;
@@ -844,8 +865,12 @@ int main(int argc, const char **argv)
                        arg++;
                        limited = 1;
                }
-               if (get_sha1(arg, sha1) < 0)
+               if (get_sha1(arg, sha1) < 0) {
+                       struct stat st;
+                       if (lstat(arg, &st) < 0)
+                               die("'%s': %s", arg, strerror(errno));
                        break;
+               }
                commit = get_commit_reference(arg, sha1, flags);
                handle_one_commit(commit, &list);
        }
index 0c951af..d2f0864 100644 (file)
@@ -20,6 +20,7 @@ static char *def = NULL;
 #define REVERSED 1
 static int show_type = NORMAL;
 static int symbolic = 0;
+static int abbrev = 0;
 static int output_sq = 0;
 
 static int revs_count = 0;
@@ -95,6 +96,8 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
                putchar('^');
        if (symbolic && name)
                show(name);
+       else if (abbrev)
+               show(find_unique_abbrev(sha1, abbrev));
        else
                show(sha1_to_hex(sha1));
 }
@@ -154,6 +157,7 @@ int main(int argc, char **argv)
        const char *prefix = setup_git_directory();
        
        for (i = 1; i < argc; i++) {
+               struct stat st;
                char *arg = argv[i];
                char *dotdot;
        
@@ -195,6 +199,19 @@ int main(int argc, char **argv)
                                verify = 1;
                                continue;
                        }
+                       if (!strcmp(arg, "--short") ||
+                           !strncmp(arg, "--short=", 9)) {
+                               filter &= ~(DO_FLAGS|DO_NOREV);
+                               verify = 1;
+                               abbrev = DEFAULT_ABBREV;
+                               if (arg[8] == '=')
+                                       abbrev = strtoul(arg + 9, NULL, 10);
+                               if (abbrev < MINIMUM_ABBREV)
+                                       abbrev = MINIMUM_ABBREV;
+                               else if (40 <= abbrev)
+                                       abbrev = 40;
+                               continue;
+                       }
                        if (!strcmp(arg, "--sq")) {
                                output_sq = 1;
                                continue;
@@ -293,6 +310,10 @@ int main(int argc, char **argv)
                }
                if (verify)
                        die("Needed a single revision");
+               if ((filter & DO_REVS) &&
+                   (filter & DO_NONFLAGS) && /* !def && */
+                   lstat(arg, &st) < 0)
+                       die("'%s': %s", arg, strerror(errno));
                as_is = 1;
                show_file(arg);
        }
index 8bf5922..b3d287e 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "run-command.h"
 #include <sys/wait.h>
+#include "exec_cmd.h"
 
 int run_command_v_opt(int argc, char **argv, int flags)
 {
@@ -13,9 +14,13 @@ int run_command_v_opt(int argc, char **argv, int flags)
                        int fd = open("/dev/null", O_RDWR);
                        dup2(fd, 0);
                        dup2(fd, 1);
-                       close(fd);                      
+                       close(fd);
+               }
+               if (flags & RUN_GIT_CMD) {
+                       execv_git_cmd(argv);
+               } else {
+                       execvp(argv[0], (char *const*) argv);
                }
-               execvp(argv[0], (char *const*) argv);
                die("exec %s failed.", argv[0]);
        }
        for (;;) {
index 2469eea..ef3ee05 100644 (file)
@@ -12,7 +12,7 @@ enum {
 };
 
 #define RUN_COMMAND_NO_STDIO 1
-
+#define RUN_GIT_CMD         2  /*If this is to be git sub-command */
 int run_command_v_opt(int argc, char **argv, int opt);
 int run_command_v(int argc, char **argv);
 int run_command(const char *cmd, ...);
index cd36193..990be3f 100644 (file)
@@ -3,6 +3,7 @@
 #include "tag.h"
 #include "refs.h"
 #include "pkt-line.h"
+#include "exec_cmd.h"
 
 static const char send_pack_usage[] =
 "git-send-pack [--all] [--exec=git-receive-pack] <remote> [<head>...]\n"
@@ -26,11 +27,11 @@ static int is_zero_sha1(const unsigned char *sha1)
 static void exec_pack_objects(void)
 {
        static char *args[] = {
-               "git-pack-objects",
+               "pack-objects",
                "--stdout",
                NULL
        };
-       execvp("git-pack-objects", args);
+       execv_git_cmd(args);
        die("git-pack-objects exec failed (%s)", strerror(errno));
 }
 
@@ -39,7 +40,7 @@ static void exec_rev_list(struct ref *refs)
        static char *args[1000];
        int i = 0;
 
-       args[i++] = "git-rev-list";     /* 0 */
+       args[i++] = "rev-list"; /* 0 */
        args[i++] = "--objects";        /* 1 */
        while (refs) {
                char *buf = malloc(100);
@@ -58,7 +59,7 @@ static void exec_rev_list(struct ref *refs)
                refs = refs->next;
        }
        args[i] = NULL;
-       execvp("git-rev-list", args);
+       execv_git_cmd(args);
        die("git-rev-list exec failed (%s)", strerror(errno));
 }
 
index b13ed78..ba0747c 100644 (file)
@@ -143,7 +143,7 @@ static int find_unique_short_object(int len, char *canonical,
        }
        /* Both have unique ones -- do they match? */
        if (memcmp(packed_sha1, unpacked_sha1, 20))
-               return -2;
+               return SHORT_NAME_AMBIGUOUS;
        memcpy(sha1, packed_sha1, 20);
        return 0;
 }
@@ -155,7 +155,7 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
        char canonical[40];
        unsigned char res[20];
 
-       if (len < 4)
+       if (len < MINIMUM_ABBREV)
                return -1;
        memset(res, 0, 20);
        memset(canonical, 'x', 40);
diff --git a/shell.c b/shell.c
index cd31618..fc0c73c 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "quote.h"
+#include "exec_cmd.h"
 
 static int do_generic_cmd(const char *me, char *arg)
 {
@@ -7,12 +8,14 @@ static int do_generic_cmd(const char *me, char *arg)
 
        if (!arg || !(arg = sq_dequote(arg)))
                die("bad argument");
+       if (strncmp(me, "git-", 4))
+               die("bad command");
 
-       my_argv[0] = me;
+       my_argv[0] = me + 4;
        my_argv[1] = arg;
        my_argv[2] = NULL;
 
-       return execvp(me, (char**) my_argv);
+       return execv_git_cmd((char**) my_argv);
 }
 
 static struct commands {
index 1935c44..ffe7456 100644 (file)
@@ -5,7 +5,11 @@
 #include "refs.h"
 
 static const char show_branch_usage[] =
-"git-show-branch [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [<refs>...]";
+"git-show-branch [--current] [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [<refs>...]";
+
+static int default_num = 0;
+static int default_alloc = 0;
+static char **default_arg = NULL;
 
 #define UNINTERESTING  01
 
@@ -254,8 +258,8 @@ static void show_one_commit(struct commit *commit, int no_name)
        char pretty[256], *cp;
        struct commit_name *name = commit->object.util;
        if (commit->object.parsed)
-               pretty_print_commit(CMIT_FMT_ONELINE, commit->buffer, ~0,
-                                   pretty, sizeof(pretty));
+               pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0,
+                                   pretty, sizeof(pretty), 0);
        else
                strcpy(pretty, "(unavailable)");
        if (!strncmp(pretty, "[PATCH] ", 8))
@@ -431,12 +435,12 @@ static void snarf_refs(int head, int tag)
        }
 }
 
-static int rev_is_head(char *head_path, int headlen,
-                      char *name,
+static int rev_is_head(char *head_path, int headlen, char *name,
                       unsigned char *head_sha1, unsigned char *sha1)
 {
        int namelen;
-       if ((!head_path[0]) || memcmp(head_sha1, sha1, 20))
+       if ((!head_path[0]) ||
+           (head_sha1 && sha1 && memcmp(head_sha1, sha1, 20)))
                return 0;
        namelen = strlen(name);
        if ((headlen < namelen) ||
@@ -508,6 +512,21 @@ static void append_one_rev(const char *av)
        die("bad sha1 reference %s", av);
 }
 
+static int git_show_branch_config(const char *var, const char *value)
+{
+       if (!strcmp(var, "showbranch.default")) {
+               if (default_alloc <= default_num + 1) {
+                       default_alloc = default_alloc * 3 / 2 + 20;
+                       default_arg = xrealloc(default_arg, sizeof *default_arg * default_alloc);
+               }
+               default_arg[default_num++] = strdup(value);
+               default_arg[default_num] = NULL;
+               return 0;
+       }
+
+       return git_default_config(var, value);
+}
+
 int main(int ac, char **av)
 {
        struct commit *rev[MAX_REVS], *commit;
@@ -526,12 +545,25 @@ int main(int ac, char **av)
        int sha1_name = 0;
        int shown_merge_point = 0;
        int topo_order = 0;
+       int with_current_branch = 0;
+       int head_at = -1;
 
+       git_config(git_show_branch_config);
        setup_git_directory();
 
+       /* If nothing is specified, try the default first */
+       if (ac == 1 && default_num) {
+               ac = default_num + 1;
+               av = default_arg - 1; /* ick; we would not address av[0] */
+       }
+
        while (1 < ac && av[1][0] == '-') {
                char *arg = av[1];
-               if (!strcmp(arg, "--all"))
+               if (!strcmp(arg, "--")) {
+                       ac--; av++;
+                       break;
+               }
+               else if (!strcmp(arg, "--all"))
                        all_heads = all_tags = 1;
                else if (!strcmp(arg, "--heads"))
                        all_heads = 1;
@@ -543,6 +575,8 @@ int main(int ac, char **av)
                        extra = -1;
                else if (!strcmp(arg, "--no-name"))
                        no_name = 1;
+               else if (!strcmp(arg, "--current"))
+                       with_current_branch = 1;
                else if (!strcmp(arg, "--sha1-name"))
                        sha1_name = 1;
                else if (!strncmp(arg, "--more=", 7))
@@ -574,6 +608,34 @@ int main(int ac, char **av)
                ac--; av++;
        }
 
+       head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
+       if (head_path_p) {
+               head_path_len = strlen(head_path_p);
+               memcpy(head_path, head_path_p, head_path_len + 1);
+       }
+       else {
+               head_path_len = 0;
+               head_path[0] = 0;
+       }
+
+       if (with_current_branch && head_path_p) {
+               int has_head = 0;
+               for (i = 0; !has_head && i < ref_name_cnt; i++) {
+                       /* We are only interested in adding the branch
+                        * HEAD points at.
+                        */
+                       if (rev_is_head(head_path,
+                                       head_path_len,
+                                       ref_name[i],
+                                       head_sha1, NULL))
+                               has_head++;
+               }
+               if (!has_head) {
+                       int pfxlen = strlen(git_path("refs/heads/"));
+                       append_one_rev(head_path + pfxlen);
+               }
+       }
+
        if (!ref_name_cnt) {
                fprintf(stderr, "No revs to be shown.\n");
                exit(0);
@@ -609,16 +671,6 @@ int main(int ac, char **av)
        if (0 <= extra)
                join_revs(&list, &seen, num_rev, extra);
 
-       head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1);
-       if (head_path_p) {
-               head_path_len = strlen(head_path_p);
-               memcpy(head_path, head_path_p, head_path_len + 1);
-       }
-       else {
-               head_path_len = 0;
-               head_path[0] = 0;
-       }
-
        if (merge_base)
                return show_merge_base(seen, num_rev);
 
@@ -645,6 +697,8 @@ int main(int ac, char **av)
                        }
                        /* header lines never need name */
                        show_one_commit(rev[i], 1);
+                       if (is_head)
+                               head_at = i;
                }
                if (0 <= extra) {
                        for (i = 0; i < num_rev; i++)
@@ -673,9 +727,19 @@ int main(int ac, char **av)
                shown_merge_point |= ((this_flag & all_revs) == all_revs);
 
                if (1 < num_rev) {
-                       for (i = 0; i < num_rev; i++)
-                               putchar((this_flag & (1u << (i + REV_SHIFT)))
-                                       ? '+' : ' ');
+                       int is_merge = !!(commit->parents && commit->parents->next);
+                       for (i = 0; i < num_rev; i++) {
+                               int mark;
+                               if (!(this_flag & (1u << (i + REV_SHIFT))))
+                                       mark = ' ';
+                               else if (is_merge)
+                                       mark = '-';
+                               else if (i == head_at)
+                                       mark = '*';
+                               else
+                                       mark = '+';
+                               putchar(mark);
+                       }
                        putchar(' ');
                }
                show_one_commit(commit, no_name);
index d7562e9..8ff5dd9 100755 (executable)
@@ -118,8 +118,8 @@ cat > show-branch.expect << EOF
 * [master] Merged "mybranch" changes.
  ! [mybranch] Some work.
 --
-+  [master] Merged "mybranch" changes.
-++ [mybranch] Some work.
+-  [master] Merged "mybranch" changes.
+*+ [mybranch] Some work.
 EOF
 
 git show-branch --topo-order master mybranch > show-branch.output
@@ -142,7 +142,7 @@ cat > show-branch2.expect << EOF
 ! [master] Merged "mybranch" changes.
  * [mybranch] Merged "mybranch" changes.
 --
-++ [master] Merged "mybranch" changes.
+-- [master] Merged "mybranch" changes.
 EOF
 
 git show-branch --topo-order master mybranch > show-branch2.output
index e3ebf38..379a831 100755 (executable)
@@ -58,7 +58,7 @@ EOF
 
 test_expect_success \
     'diff identical, but newly created symlink' \
-    'sleep 1 &&
+    'sleep 3 &&
     ln -s xyzzy frotz &&
     git-diff-index -M -p $tree > current &&
     compare_diff_patch current expected'
index 1beab71..7d354a1 100755 (executable)
@@ -19,13 +19,13 @@ save_tag final unique_commit "final" tree -p two_parents
 
 test_expect_success 'start is valid' 'git-rev-parse start | grep "^[0-9a-f]\{40\}$"'
 test_expect_success 'start^0' "test $(cat .git/refs/tags/start) = $(git-rev-parse start^0)"
-test_expect_success 'start^1 not valid' "test $(git-rev-parse start^1) = start^1"
+test_expect_success 'start^1 not valid' "if git-rev-parse --verify start^1; then false; else :; fi"
 test_expect_success 'second^1 = second^' "test $(git-rev-parse second^1) = $(git-rev-parse second^)"
 test_expect_success 'final^1^1^1' "test $(git-rev-parse start) = $(git-rev-parse final^1^1^1)"
 test_expect_success 'final^1^1^1 = final^^^' "test $(git-rev-parse final^1^1^1) = $(git-rev-parse final^^^)"
 test_expect_success 'final^1^2' "test $(git-rev-parse start2) = $(git-rev-parse final^1^2)"
 test_expect_success 'final^1^2 != final^1^1' "test $(git-rev-parse final^1^2) != $(git-rev-parse final^1^1)"
-test_expect_success 'final^1^3 not valid' "test $(git-rev-parse final^1^3) = final^1^3"
+test_expect_success 'final^1^3 not valid' "if git-rev-parse --verify final^1^3; then false; else :; fi"
 test_expect_failure '--verify start2^1' 'git-rev-parse --verify start2^1'
 test_expect_success '--verify start2^0' 'git-rev-parse --verify start2^0'
 
index 6db555f..d7a8f0a 100644 (file)
@@ -1,6 +1,7 @@
 #!/bin/sh
 #
 # An example hook script to mail out commit update information.
+# It also blocks tags that aren't annotated.
 # Called by git-receive-pack with arguments: refname sha1-old sha1-new
 #
 # To enable this hook:
@@ -8,16 +9,74 @@
 # (2) make this file executable by "chmod +x update".
 #
 
-recipient="commit-list@example.com"
+project=$(cat $GIT_DIR/description)
+recipients="commit-list@somewhere.com commit-list@somewhereelse.com"
 
-if expr "$2" : '0*$' >/dev/null
+ref_type=$(git cat-file -t "$3")
+
+# Only allow annotated tags in a shared repo
+# Remove this code to treat dumb tags the same as everything else
+case "$1","$ref_type" in
+refs/tags/*,commit)
+       echo "*** Un-annotated tags are not allowed in this repo" >&2
+       echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate."
+       exit 1;;
+refs/tags/*,tag)
+       echo "### Pushing version '${1##refs/tags/}' to the masses" >&2
+       # recipients="release-announce@somwehere.com announce@somewhereelse.com"
+       ;;
+esac
+
+# set this  to 'cat' to get a very detailed listing.
+# short only kicks in when an annotated tag is added
+short='git shortlog'
+
+# see 'date --help' for info on how to write this
+# The default is a human-readable iso8601-like format with minute
+# precision ('2006-01-25 15:58 +0100' for example)
+date_format="%F %R %z"
+
+(if expr "$2" : '0*$' >/dev/null
 then
-       echo "Created a new ref, with the following commits:"
-       git-rev-list --pretty "$3"
+       # new ref
+       case "$1" in
+       refs/tags/*)
+               # a pushed and annotated tag (usually) means a new version
+               tag="${1##refs/tags/}"
+               if [ "$ref_type" = tag ]; then
+                       eval $(git cat-file tag $3 | \
+                               sed -n '4s/tagger \([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
+                       date=$(date --date="1970-01-01 00:00:00 $ts seconds" +"$date_format")
+                       echo "Tag '$tag' created by $tagger at $date"
+                       git cat-file tag $3 | sed -n '5,$p'
+                       echo
+               fi
+               prev=$(git describe "$3^" | sed 's/-g.*//')
+               # the first tag in a repo will yield no $prev
+               if [ -z "$prev" ]; then
+                       echo "Changes since the dawn of time:"
+                       git rev-list --pretty $3 | $short
+               else
+                       echo "Changes since $prev:"
+                       git rev-list --pretty $prev..$3 | $short
+                       echo ---
+                       git diff $prev..$3 | diffstat -p1
+                       echo ---
+               fi
+               ;;
+
+       refs/heads/*)
+               branch="${1##refs/heads/}"
+               echo "New branch '$branch' available with the following commits:"
+               git-rev-list --pretty "$3" $(git-rev-parse --not --all)
+               ;;
+       esac
 else
        base=$(git-merge-base "$2" "$3")
        case "$base" in
        "$2")
+               git diff "$3" "^$base" | diffstat -p1
+               echo
                echo "New commits:"
                ;;
        *)
@@ -25,6 +84,6 @@ else
                ;;
        esac
        git-rev-list --pretty "$3" "^$base"
-fi |
-mail -s "Changes to ref $1" "$recipient"
+fi) |
+mail -s "$project: Changes to '${1##refs/heads/}'" $recipients
 exit 0
diff --git a/tree.c b/tree.c
index dc1c41e..87e0d74 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -74,27 +74,24 @@ static int match_tree_entry(const char *base, int baselen, const char *path, uns
        return 0;
 }
 
-int read_tree_recursive(void *buffer, unsigned long size,
+int read_tree_recursive(struct tree *tree,
                        const char *base, int baselen,
                        int stage, const char **match,
                        read_tree_fn_t fn)
 {
-       while (size) {
-               int len = strlen(buffer)+1;
-               unsigned char *sha1 = buffer + len;
-               char *path = strchr(buffer, ' ')+1;
-               unsigned int mode;
-
-               if (size < len + 20 || sscanf(buffer, "%o", &mode) != 1)
-                       return -1;
-
-               buffer = sha1 + 20;
-               size -= len + 20;
-
-               if (!match_tree_entry(base, baselen, path, mode, match))
+       struct tree_entry_list *list;
+       if (parse_tree(tree))
+               return -1;
+       list = tree->entries;
+       while (list) {
+               struct tree_entry_list *current = list;
+               list = list->next;
+               if (!match_tree_entry(base, baselen, current->name,
+                                     current->mode, match))
                        continue;
 
-               switch (fn(sha1, base, baselen, path, mode, stage)) {
+               switch (fn(current->item.any->sha1, base, baselen,
+                          current->name, current->mode, stage)) {
                case 0:
                        continue;
                case READ_TREE_RECURSIVE:
@@ -102,28 +99,19 @@ int read_tree_recursive(void *buffer, unsigned long size,
                default:
                        return -1;
                }
-               if (S_ISDIR(mode)) {
+               if (current->directory) {
                        int retval;
-                       int pathlen = strlen(path);
+                       int pathlen = strlen(current->name);
                        char *newbase;
-                       void *eltbuf;
-                       char elttype[20];
-                       unsigned long eltsize;
 
-                       eltbuf = read_sha1_file(sha1, elttype, &eltsize);
-                       if (!eltbuf || strcmp(elttype, "tree")) {
-                               if (eltbuf) free(eltbuf);
-                               return -1;
-                       }
                        newbase = xmalloc(baselen + 1 + pathlen);
                        memcpy(newbase, base, baselen);
-                       memcpy(newbase + baselen, path, pathlen);
+                       memcpy(newbase + baselen, current->name, pathlen);
                        newbase[baselen + pathlen] = '/';
-                       retval = read_tree_recursive(eltbuf, eltsize,
+                       retval = read_tree_recursive(current->item.tree,
                                                     newbase,
                                                     baselen + pathlen + 1,
                                                     stage, match, fn);
-                       free(eltbuf);
                        free(newbase);
                        if (retval)
                                return -1;
@@ -133,9 +121,9 @@ int read_tree_recursive(void *buffer, unsigned long size,
        return 0;
 }
 
-int read_tree(void *buffer, unsigned long size, int stage, const char **match)
+int read_tree(struct tree *tree, int stage, const char **match)
 {
-       return read_tree_recursive(buffer, size, "", 0, stage, match, read_one_entry);
+       return read_tree_recursive(tree, "", 0, stage, match, read_one_entry);
 }
 
 struct tree *lookup_tree(const unsigned char *sha1)
diff --git a/tree.h b/tree.h
index 57a5bf7..330ab64 100644 (file)
--- a/tree.h
+++ b/tree.h
@@ -37,10 +37,11 @@ struct tree *parse_tree_indirect(const unsigned char *sha1);
 #define READ_TREE_RECURSIVE 1
 typedef int (*read_tree_fn_t)(unsigned char *, const char *, int, const char *, unsigned int, int);
 
-extern int read_tree_recursive(void *buffer, unsigned long size,
-                       const char *base, int baselen,
-                       int stage, const char **match,
-                       read_tree_fn_t fn);
+extern int read_tree_recursive(struct tree *tree,
+                              const char *base, int baselen,
+                              int stage, const char **match,
+                              read_tree_fn_t fn);
 
+extern int read_tree(struct tree *tree, int stage, const char **paths);
 
 #endif /* TREE_H */
index 1834b6b..d198055 100644 (file)
@@ -4,6 +4,7 @@
 #include "tag.h"
 #include "object.h"
 #include "commit.h"
+#include "exec_cmd.h"
 
 static const char upload_pack_usage[] = "git-upload-pack [--strict] [--timeout=nn] <dir>";
 
@@ -60,7 +61,7 @@ static void create_pack_file(void)
                close(0);
                close(fd[0]);
                close(fd[1]);
-               *p++ = "git-rev-list";
+               *p++ = "rev-list";
                *p++ = "--objects";
                if (create_full_pack || MAX_NEEDS <= nr_needs)
                        *p++ = "--all";
@@ -79,13 +80,13 @@ static void create_pack_file(void)
                                buf += 41;
                        }
                *p++ = NULL;
-               execvp("git-rev-list", argv);
+               execv_git_cmd(argv);
                die("git-upload-pack: unable to exec git-rev-list");
        }
        dup2(fd[0], 0);
        close(fd[0]);
        close(fd[1]);
-       execlp("git-pack-objects", "git-pack-objects", "--stdout", NULL);
+       execl_git_cmd("pack-objects", "--stdout", NULL);
        die("git-upload-pack: unable to exec git-pack-objects");
 }