]> git.cworth.org Git - hgbook-git/commitdiff
Rewrite push and pull examples i a more "git-friendly" fashion
authorCarl Worth <cworth@cworth.org>
Fri, 28 Sep 2007 22:02:54 +0000 (15:02 -0700)
committerCarl Worth <cworth@cworth.org>
Fri, 28 Sep 2007 22:02:54 +0000 (15:02 -0700)
We now take advantage of remotes and use master..origin rather
than the magic FETCH_HEAD.

tour.mdwn

index 53f8f2ca8cc7e7fcec82c06c5a05a9eb1508c00c..d5b25f2cab4e022eb12bdd9d2afef3facf326179 100644 (file)
--- a/tour.mdwn
+++ b/tour.mdwn
@@ -146,14 +146,11 @@ a directory tree in your filesystem that git treats as
 special. You can rename or delete a repository any time you like,
 using either the command line or your file browser.
 
-#### 2.3.1 Creating a local copy of a remote repository
+#### 2.3.1 Creating a copy of a remote repository
 
-As suggested, a repository can be copied through normal file-copying
-commands. But git also provides a "git clone" tool for copying a
-repository. This provides a means of copying a repository over the
-network, and is also useful with a local repository since it is much
-more efficient than creating a normal copy, (creating a local clones
-is blazingly fast).
+The "git clone" command is used to create a local copy of a remote
+repository. This is generally the first git operation you will use
+when beginning to work with an existing project.
 
 We've assembled a simple repository that will be used in the examples
 throughout this chapter. Go ahead and clone this repository now so
@@ -210,6 +207,33 @@ What this means for now is that we’re free to experiment with our
 repository, safe in the knowledge that it’s a private “sandbox” that
 won’t affect anyone else.
 
+#### 2.3.2 Creating copy of a local repository
+
+As mentioned above, a repository can be copied through normal
+file-copying commands. But it's useful to use "git clone" even when
+just making a local copy of a repository. Using "git clone" will be
+much faster and will use much less space than a normal copy. In fact,
+local clones are impressively fast. Go ahead and make a local clone
+now as follows:
+
+       $ git clone hello hello-clone
+       Initialized empty Git repository in /tmp/hello-clone/.git/
+       0 blocks
+
+       [XXX Git says "empty" here, (presumably from the git-init
+       part), but shouldn't the command also report the succesful
+       clone which makes it non-empty? And what the heck does "0
+       blocks" mean? This is not friendly output.]
+
+In fact, let's make a couple more clones at the same time. This isn't
+just to drive home the speed and storage benefits of local clones, but
+we'll use each of these cloned repositories in Section 2.8 when
+discussing how to move commits between repositories:
+
+          $ git clone hello hello-pull
+          $ git clone hello hello-fetch
+          $ git clone hello hello-remote
+
 #### 2.3.2 What’s in a repository?
 
 When we take a more detailed look inside a repository, we can see that
@@ -522,57 +546,21 @@ passing the -q or --quiet options.
 
 ### 2.6 Making and reviewing changes
 
-Now that we have a grasp of viewing history in git, let’s take a
-look at making some changes and examining them.
+Now that we have a grasp of viewing history in git, let’s take a look
+at making some changes and examining them. You should be working
+within the "hello" directory that we originally cloned.
 
-The first thing we’ll do is isolate our experiment in a repository of
-its own. We use the “git clone” command, but we don’t need to clone a
-copy of the remote repository. Since we already have a copy of it
-locally, we can just clone that instead. This is much faster than
-cloning over the network, and cloning a local repository uses less
-disk space in most cases, too.
+In the repository, we have a file hello.c that contains the classic
+“hello, world” program. But this program currently has a syntax error
+and won't compile, (there's a missing 'n' between the '\' and the '"'
+on the line containing printf). Let's fix that now. You can use a text
+editor to correct the file, or you can copy-and-paste the sed command
+below:
 
-       $ cd ..
-       $ git clone hello my-hello
-       Initialized empty Git repository in /tmp/my-hello/.git/
-       0 blocks
+       $ sed -i '/printf/s/\\"/\\n"/' hello.c
 
-       [XXX We say "empty" here, (presumably from the git-init part),
-       but shouldn't the command also report the succesful clone
-       which makes it non-empty? And what the heck does "0 blocks"
-       mean?]
-
-As an aside, it’s often good practice to keep a “pristine” copy of a
-remote repository around, which you can then make temporary clones of
-to create sandboxes for each task you want to work on. This lets you
-work on multiple tasks in parallel, each isolated from the others
-until it’s complete and you’re ready to integrate it back. Because
-local clones are so cheap, there’s almost no overhead to cloning and
-destroying repositories whenever you want.
-
-Alternatively, you can achieve much the same effect by creating
-multiple branches in a single repository, (but we won't go into detail
-on how to do that in this chapter). Some people greatly appreciate
-having multiple branches in a single repository rather than having
-many repositories cluttering up their filesystem. Other people prefer
-the ability to have working-tree changes, and intermediate build
-files, etc. each isolated in a separate repository per branch. Both
-modes are very well-supported by git, so it's really a matter of which
-you find most appropriate at any time given your tastes and project
-workflows.
-
-In our my-hello repository, we have a file hello.c that contains the
-classic “hello, world” program. Let’s use the ancient and venerable
-sed command to edit this file so that it prints a second line of
-output. (I’m only using sed to do this because it’s easy to write a
-scripted example this way. Since you’re not under the same constraint,
-you probably won’t want to use sed; simply use your preferred text
-editor to do the same thing.)
-
-       $ sed -i '/printf/a\\tprintf("hello again!\\n");' hello.c     
-
-The “git status” command will tell us what git knows about the files
-in the repository.
+After you've made that change, the “git status” command will tell you
+what git knows about the files in the repository.
 
        $ ls 
        hello.c  Makefile
@@ -585,9 +573,21 @@ in the repository.
        #
        no changes added to commit (use "git add" and/or "git commit -a")
 
-We see that “git status” command prints a line with "modified" for
-hello.c. The “git status” command will not print any output for files
-that have not been modified.
+First "git status" tells us that the current branch is "master". This
+means that the master branch is what will be updatea when we create a
+new commit.
+
+Note: In git a branch is a very simple notion---it's simply a name
+that points to a particular commit, (literally nothing more than a
+pointer---look at the contents of .git/refs/heads/master if you're
+curious). The fact that a branch is so light is what makes the
+creation of new branches an instantaneous operation in git. Together
+with the ease of merging, git makes branches a joy to work with. But
+we'll delay talk of branching and merging to a future chapter.
+
+Next “git status” prints a line with "modified" for each modified
+file---in this case just hello.c. The “git status” command will not
+print any output for files that have not been modified.
 
 Notice that we didn’t need to inform git that we were going to modify
 the file before we started, or that we had modified the file after we
@@ -599,14 +599,15 @@ this, we use the “git diff” command.
 
        $ git diff
        diff --git a/hello.c b/hello.c
-       index 9a3ff79..6d28887 100644
+       index 9a3ff79..ea364d3 100644
        --- a/hello.c
        +++ b/hello.c
-       @@ -8,5 +8,6 @@
+       @@ -7,6 +7,6 @@
+        
         int main(int argc, char **argv)
         {
-               printf("hello, world!\");
-       +       printf("hello again!\n");
+       -       printf("hello, world!\");
+       +       printf("hello, world!\n");
                return 0;
         }
 
@@ -617,18 +618,28 @@ and “git diff” to review our changes, until we’re satisfied with what
 we’ve done and arrive at a natural stopping point where we want to
 record our work in a new commit.
 
-The “git commit” command lets us create a new changeset; we’ll usually
-refer to this as “making a commit” or “committing”.
+If you'd like, go ahead and build the program now with "make" and run
+it to ensure it works. Everything should look good so we're just about
+ready to make a commit. Unsurprisingly, we'll be using "git commit" to
+do this.
+
+#### 2.7.1 Introducing yourself to git
 
-#### 2.7.1 Setting up a username
+Before we run "git commit" though, we should introduce ourself to git.
+Git records your name and address with each change that you commit,
+(as both author and committer unless you tell it otherwise), so that
+you and others will later be able to tell who made each change.
 
-When you try to run “git commit” for the first time, it might not do
-exactly what you want. Git records your name and address with each
-change that you commit, (as both author and committer unless you tell
-it otherwise), so that you and others will later be able to tell who
-made each change. Git tries to automatically figure out a sensible
-name and address to attribute to both author and committer. It will
-attempt each of the following methods, in order, (stopping for each field as soon as a value is found):
+Git tries to automatically figure out a sensible name and address to
+attribute to both author and committer if you haven't explicitly told
+it a name and address. And it tries a lot, (detailed below). If you're
+not interested in these details, you might want to skip to the next
+section which explains how to avoid all this guesswork and tell git
+what your name and email address are.
+
+Here is a list of all the guessing that git will attempt. It will
+attempt each of the following methods, in order, (stopping for each of
+the author and committer name and email as soon as a value is found):
 
   1. If you specify a --author option to the “git commit” command on
      the command line, followed by a "Real Name <email@example.com>"
@@ -706,17 +717,19 @@ continues until a new section begins, or the end of the file. Git
 ignores empty lines and treats any text from “#” to the end of a line
 as a comment.
 
-##### Choosing a user name
+##### Choosing your name and email
 
 You can use any text you like as the value of the name and email
 configuration items, since this information is for reading by other
-people, not for interpreting by git. It is conventional to use a valid
-email address, but some, (notably Linus Torvalds, the original author
-of git), actually like the default user@hostname convention that git
-falls back on without any additional information. There's no
-requirement that the email address actually be valid, and perhaps it's
-useful to be reminded which machine was used to create particular
-commits.
+people, not for interpreting by git. It is conventional to use your
+actual name as well as a valid email address. But some poepl, (notably
+Linus Torvalds, the original author of git), actually like the default
+username@hostname convention that git falls back on without any
+additional information about an email address. There's no requirement
+that the email address actually be valid, and perhaps it's useful to
+be reminded which machine was used to create particular commits. So
+choose the name and email you wish, or follow a particular project's
+conventions.
 
 #### 2.7.2 Writing a commit message
 
@@ -742,7 +755,8 @@ notice that---it does not need to be explicitly told about the
 removal.
 
 The editor that the “git commit” command drops us into will contain an
-empty line, followed by a number of lines starting with “#”.
+empty line, followed by a number of lines starting with “#”. These
+lines contain the same information as seen in "git status" before:
 
        empty line
        # Please enter the commit message for your changes.
@@ -752,10 +766,22 @@ empty line, followed by a number of lines starting with “#”.
        #   (use "git reset HEAD <file>..." to unstage)
        #
        #       modified:   hello.c
-       #       
-
-git ignores the lines that start with “#”; it uses them only
-to tell us which files it’s recording changes to. Modifying or
+       #
+       # Untracked files:
+       #   (use "git add <file>..." to include in what will be committed)
+       #
+       #       hello
+       #       hello.o
+
+Notice that two untracked files (hello and hello.o) have now appeared
+from the build process. Git is reminding us of these in case we
+intended to commit them as well, (in which case we would need to "git
+add" them). We don't actually want to commit these files so we will
+ignore them for now, (and we could tell git to ignore them as well by
+listing them in a .gitignore file).
+
+Git will ignore all lines in the commit message that start with “#”;
+it uses them only to give us information on the commit. Modifying or
 deleting these lines has no effect.
 
 #### 2.7.3 Writing a good commit message
@@ -787,7 +813,8 @@ formatting.
 
 My personal preference is for short, but informative, commit messages
 that tell me something that I can’t figure out with a quick glance at
-the output of “git log -p".
+the output of "git log --stat" or “git log -p", (so repeating the list
+of all modified files is not useful, for example).
 
 #### 2.7.4 Aborting a commit
 
@@ -804,24 +831,22 @@ command produces output that is identical to “git log -p”, but for
 only a single revision, (and the most recent revision by default):
 
        $ git show
-       commit 018cfb742be6176443ffddac454e593e802ddf3e
+       commit fd21e5d6c5eedee70137229ebf348c25181812ab
        Author: Carl Worth <cworth@cworth.org>
-       Date:   Thu Sep 27 23:55:00 2007 -0700
+       Date:   Fri Sep 28 12:50:16 2007 -0700
        
-           Added an extra line of output.
-           
-           If I would have been clever I would have fixed that old typo
-           while I was at it...
+           Fixed the typo so the program actuall complies now.
        
        diff --git a/hello.c b/hello.c
-       index 9a3ff79..6d28887 100644
+       index 9a3ff79..ea364d3 100644
        --- a/hello.c
        +++ b/hello.c
-       @@ -8,5 +8,6 @@
+       @@ -7,6 +7,6 @@
+        
         int main(int argc, char **argv)
         {
-               printf("hello, world!\");
-       +       printf("hello again!\n");
+       -       printf("hello, world!\");
+       +       printf("hello, world!\n");
                return 0;
         }
 
@@ -836,117 +861,102 @@ identifier shown above. Can you do it?)
 #### 2.7.6 Fixing up a broken commit (before anyone else sees it)
 
 So now that we've cloned a local repository, made a change to the
-code, setup our name and email address, and made a commit with a
-careful message, we're just about ready to share our change with the
-world. But wait, we forgot to try to compile it didn't we?
-
-       $ make
-       cc    -c -o hello.o hello.c
-       hello.c:10:9: warning: missing terminating " character
-       hello.c:10:9: warning: missing terminating " character
-       hello.c: In function ‘main’:
-       hello.c:10: error: missing terminating " character
-       hello.c:11: error: expected ‘)’ before ‘;’ token
-       hello.c:13: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast
-       hello.c:13: error: expected ‘;’ before ‘}’ token
-       make: *** [hello.o] Error 1
-
-Oh look. The code's broken and doesn't compile. We don't want to share
-code in this state. For situations where you notice one tiny detail
-that got left out of the last commit, (a silly syntax error, a
-misspelling in a comment or commit messsage), git provides a very
-handy tool for just changing the last commit.
+code, setup our name and email address, and made a careful commit,
+we're just about ready to share our change with the world. But wait,
+that commit message has some really embarrassing misspellings in
+it. Wouldn't it be nice to touch those up before I post this commit
+with a never-to-be-changed again commit identifier?
 
-So fix that typo, (a missing 'n' between the '\' and the '"'), with
-your editor or with something like this:
+This is the exact situation for which "git commit --amend" was
 
-       sed -i 's/\\"/\\n"/' hello.c
+So I can just run that now and fix the broken commit message:
 
-And then you can just amend the previous commit rather than creating a
-new one with the --amend option to "git commit":
+       $ git commit --amend
 
-       $ git commit -a --amend
-
-Note that we use -a to include the code change here. And that helps
-point out a situation where "git commit" is useful without the -a
-option, "git commit --amend" is a useful command for amend just the
-last commit message, without committing any new code changes, even if
-some files have been modified in the working tree.
-
-And here's the final result:
+Here's the final result:
 
        $ git show
-       commit 839b58d021c618bd0e1d336d4d5878a0082672e6
+       commit 3c54ac672ec1130b36837f1b708054a7a1d402de
        Author: Carl Worth <cworth@cworth.org>
-       Date:   Thu Sep 27 23:55:00 2007 -0700
+       Date:   Fri Sep 28 12:50:16 2007 -0700
        
-           Added an extra line of output and fixed the typo bug.
+           Fixed the typo so the program actually compiles now.
        
        diff --git a/hello.c b/hello.c
-       index 9a3ff79..ca750e0 100644
+       index 9a3ff79..ea364d3 100644
        --- a/hello.c
        +++ b/hello.c
-       @@ -7,6 +7,7 @@
+       @@ -7,6 +7,6 @@
         
         int main(int argc, char **argv)
         {
        -       printf("hello, world!\");
        +       printf("hello, world!\n");
-       +       printf("hello again!\n");
                return 0;
         }
 
-I can't help but point out that this really was a poor example for
---amend. The end result is a single commit that does two independent
-things, (fixes one bug and adds one new feature). It's much better to
-create a code history where each commit makes an independent change,
-(and as small as possible). This is important for several reasons:
-
-  * Small changes are easier to review
-
-  * Independent changes are easier to split up if only part of the
-    series gets accepted "upstream" for one reason or another.
-
-  * The smaller the changes are the more useful the history will be
-    when actually using the history, not just viewing it. This is
-    particularly important when doing "git bisect"---that's a powerful
-    tool for isolating the single commit that introduces a bug. And
-    it's much more powerful if the commit it isolates is as small as
-    possible.
-
-So it's a good thing this document is available under a license that
-allows for distribution of modified versions. Someone should clean up
-the --amend example to not teach bad habits like I did above. [Note:
-All this bad-habit stuff was introduced by me, and was not present in
-Bryan's original chapter. -Carl]
+Note that we didn't use "commit -a" this time. This means that "git
+commit --amend" will amend only the commit message and not any of the
+actual files being tracked, (even if some of them had been modified
+between the commits).
+
+It's also possible to use "git commit -a --amend" to similarly fix up
+mistakes noticed in code. That will replace the most recent commit
+with a different commit based on any new changes to files.
+
+I do feel a little hesitant to mention "git commit -a --amend". It's a
+handy command for fixing up something like a misspelling in a comment
+in the code. But if there is anything more significant than that, then
+it would generally be better to create an additional commit rather
+than amending an existing commit. This is important for several
+reasons:
+
+  * The amend operation will destroy a state that was previously saved
+    in a commit. If it's just the commit message being changed, then
+    that's no big deal. But if the contents are being amended, then a
+    mistake could eliminate something valuable.
+
+  * All commits should be logically independent and as small as
+    possible. Abusing "git commit -a --amend" can cause a small commit
+    to grow and acquire unrelated changes.
+
+It's worth emphasizing the value of minimal, independent commits. The
+smaller the changes are the more useful the history will be when
+actually using the history, not just viewing it. This is particularly
+important when doing "git bisect"---that's a powerful tool for
+isolating a single commit that introduces a bug. And it's much more
+helpful when the commit it isolates is as small as possible.
+
+One advantage of using git over some other systems is that the commit
+speed is blazingly fast. The tool doesn't punish you at all for
+committing as often as you get our project into a state that is worth
+saving. "Commit early, commit often" is a well-supported mode of
+operation with git.
 
 ### 2.8 Sharing changes
 
 We mentioned earlier that repositories in git are
 self-contained. This means that the commit we just created exists
-only in our my-hello repository. Let’s look at a few ways that we can
+only in our hello repository. Let’s look at a few ways that we can
 propagate this change into other repositories.
 
-#### 2.8.1 Pulling changes from another repository
+#### 2.8.1 Pulling changes from the original repository
 
-To get started, let’s clone our original hello repository, which does
-not contain the change we just committed. We’ll call our temporary
-repository hello-pull.
+Recall that in Section 2.3.2 we made several local clones of the hello
+repository before we made any commits. This allows us to simulate what
+happens when upstream changes have been committed after you originally
+cloned.
 
-       $ cd ..
-       $ git clone hello hello-pull
-       Initialized empty Git repository in /tmp/hello-pull/.git/
-       0 blocks
+The simplest, (and quite common), scenario is that you inherently
+trust any changes in the original repository and you want to pull
+these directly into your clone. This might be the case if you are
+using git simply to track the progress of a project without making any
+changes.
 
-We could use the “git pull” command to apply changes from my-hello to
-our master branch in hello-pull. However, blindly pulling unknown
-changes into a repository is a somewhat scary prospect. The "git pull"
-command is coneptually the combination of two commands, "git fetch"
-and "git merge"; we can run those separately to examine the changes
-before applying them locally. First we do the fetch:
+In this case, the operation is as simple as just calling "git pull":
 
-       $ cd hello-pull
-       $ git fetch ../my-hello
+       $ cd ../hello-pull
+       $ git pull
        remote: Generating pack...
        Unpacking 3 objects...
         100% (3/3) done
@@ -955,98 +965,209 @@ before applying them locally. First we do the fetch:
        Deltifying 3 objects...
         100% remote: (3/3) done
        Total 3 (delta 1), reused 0 (delta 0)
+       * refs/remotes/origin/master: fast forward to branch 'master' of /tmp/hello
+         old..new: a1a0e8b..3c54ac6
+       Updating a1a0e8b..3c54ac6
+       Fast forward
+        hello.c |    2 +-
+        1 files changed, 1 insertions(+), 1 deletions(-)
+
+XXX: Git is fairly noisy here, but a user has little need to care
+about the several stages of operation involved here. As a tutorial
+writer I'd like to say "ignore all that progress stuff, and look at
+where the stat information starts" but it's hard for a new user to
+even be able to understand that. I think it would be ideal if all of
+the progress-tracking spew were reduced to a single line. Something
+like "Computing (100%) Transferring (100%)" or whatever.
+
+After (lots!) of progresss indication, git gives a report of which
+files were modified, (which is very useful for getting a quick feel
+for what happened). If you would like more details on what changes
+came in, git provides a range that is perfect for examining. Let's
+take a look (again, the commit identifiers will be different for you
+--- just copy-and-paste the range that git prints):
+
+       $ git log a1a0e8b..3c54ac6
+       commit 3c54ac672ec1130b36837f1b708054a7a1d402de
+       Author: Carl Worth <cworth@cworth.org>
+       Date:   Fri Sep 28 12:50:16 2007 -0700
+       
+           Fixed the typo so the program actually compiles now.
 
-The fetched commits (or commit in this case) are available as the name
-FETCH_HEAD. [XXX: Shouldn't git-fetch print that name out to the user
-if the user didn't provide a specific branch name to fetch into.] And
-the difference between what we had before and what exists on
-FETCH_HEAD can easily be examined with the ..FETCH_HEAD range
-notation:
+As expected, we received just the one commit.
 
-       $ git log ..FETCH_HEAD
-       commit 839b58d021c618bd0e1d336d4d5878a0082672e6
+So that's all that's needed in the common case. Just run "git pull"
+everytime you want to pull in new changes that have landed in the
+upstream repository.
+
+Note: Mercurial users who are reading this might wonder if there's a
+need for the equivalent of "hg update" after doing a "git pull". And
+the answer is no. Unlike mercurial, "git pull", (and "git merge") will
+automatically update the working-directory files as necessary.
+
+#### 2.8.2 Using fetch and merge separately to pull
+
+Sometimes you may not know if you want to pull in the changes from the
+remote repository or not. It's useful to be able to examine them
+before accepting them into our branch. The "git pull" command shown in
+the previous section is conceptually the combination of two command,
+"git fetch" and "git merge". We can use these commands separately to
+examine the change before accepting it.
+
+So let's do that within the hello-fetch clone we made earlier. First
+we will do the fetch:
+
+       $ cd ../hello-fetch
+       $ git fetch
+       remote: Generating pack...
+       Unpacking 3 objects...
+       remote: Done counting 5 objects.
+       Result has 3 objects.
+       Deltifying 3 objects...
+        100% remote: (3/3) done
+       Total 3 (delta 1), reused 0 (delta 0)
+        100% (3/3) done
+       * refs/remotes/origin/master: fast forward to branch 'master' of /tmp/hello/
+         old..new: a1a0e8b..3c54ac6
+
+You may notice that the output here looks very much like the first
+portion of the output from "git pull". This is no coincidence.  The
+new changes have been "fetched" into the current repository and are
+stored into "origin/master" and have not been into the current
+"master" branch. Remember that "master" is our current branch. So now,
+"origin/master" is the state of the master branch that exists in the
+"origin" repository, (the one we cloned from).
+
+The most convenient way to examine the fetched changes is with the
+"master..origin" range notation:
+
+       $ git log master..origin
+       commit 3c54ac672ec1130b36837f1b708054a7a1d402de
        Author: Carl Worth <cworth@cworth.org>
-       Date:   Thu Sep 27 23:55:00 2007 -0700
+       Date:   Fri Sep 28 12:50:16 2007 -0700
        
-           Added an extra line of output and fixed the typo bug.
+           Fixed the typo so the program actually compiles now.
 
-Since these commits actually exist in the local repository now, we
-don't need to fetch or pull them from the remote repository again---we
-can now use "git merge" to apply the previously fetched commits. (A
-mercurial user might notice here that git does not have the race
-condition between "hg incoming" and "hg pull" that mercurial has since
-the commits are fetched only once.)
+Another helpful way of visualizing what happened with "git fetch" here
+is to run "gitk --all", which gives a graphical representation of all
+branches. Here is what it would look like:
 
-       $ git merge FETCH_HEAD
-       Updating a1a0e8b..839b58d
+[[img gitk-fetch.png]]
+
+Notice that origin/master points to a single commit that was committed
+on top of the state pointed to by the "master" branch.
+
+Let's assume we are happy with the changes and we want to include them
+into our master branch. To do this we simply run "git merge origin":
+
+       $ git merge origin
+       Updating a1a0e8b..3c54ac6
        Fast forward
-        hello.c |    3 ++-
-        1 files changed, 2 insertions(+), 1 deletions(-)
-
-Notice that "git merge" reports that our branch pointer has been
-updated from a1a0e8b to 839b58d. Also, this is a "fast forward"
-meaning that the new commits are a linear sequence on top of the
-commit we already hand. In other words, there wasn't any divergence
-between these two repositories so no actual "merge" commit was
-created.
-
-This separation of fetch and merge is useful when you need to
-carefully review some changes before applying them. But often you're
-in a situation where you know you trust the remote repository and you
-simply want to pull those changes as conveniently as possible, (no
-extra commands, no typing a magic name like FETCH_HEAD). This is the
-case when the tracking upstream development of a project with git. And
-in that case, the above steps are as simple as just executing "git
-pull". So let's repeat all that the simpler way:
+        hello.c |    2 +-
+        1 files changed, 1 insertions(+), 1 deletions(-)
 
-       $ cd ..
-       $ git clone hello hello-tracking
-       Initialized empty Git repository in /tmp/hello-tracking/.git/
-       0 blocks
-       $ cd hello-tracking
-       $ git pull ../my-hello
+Again, you'll see that this precisely matches the final portion of the
+output from "git pull". Using "git fetch" and "git merge" let us
+achieve exactly what "git pull" did, but we were able to stop in the
+middle to examine the situation, (and we could have decided to reject
+the changes and not merge them---leaving our master branch unchanged).
+
+##### On merges and "fast forward"
+
+You'll notice that we've been seeing the phrase "fast forward" several
+times. This is a special-case operation performed by "git merge" where
+a branch can be advanced along a linear sequence. This happens
+whenever you pull changes that build directly on top of the same
+commit you have as your most recent commit. In other words, there was
+never any divergence or simultaneous commits created in parallel in
+multiple repositories. If there had been parallel commits, then "git
+merge" would actually introduce a new merge commit to tie the two
+commits together.
+
+When a non-fast-forward merge occurs, there is always the possibility
+that a conflict occurs. In this case, "git merge" will leave conflict
+markers in the files and instruct you to resolve the conflicts. When
+you are finished, you would issue a "git commit -a" to create the
+merge commit.
+
+#### 2.8.3 Using "git remote" to pull changes other repositories
+
+We've already described how "git pull" will pull in changes from the
+repository which was the origin of the clone operation. Git also
+provides excellent support for pulling changes from any other
+repository as well, (distributed, rather than centralized
+development).
+
+If you have a situation where you want to pull a single time from some
+repository, then you can simply give the path or URL of the repository
+on the "git pull" command line. However, it's often the case that if
+you want to pull changes from a repository once, you'll want to pull
+changes from that same repository again in the future. This is where
+the "git remote" notion is extremely useful---it allows you to
+associate simple names, (and behaviors), with remote repository URLs
+
+We've already seen one instance of "git remote" which is the creation
+of the "origin" remote which happens automatically during "git
+clone". Let's now create another. Let's assume you are going to be
+working in the hello-remote repository and you'd like to pull changes
+from the hello-pull repository, where your friend "fred" has been
+making changes. Here's how to setup the new remote:
+
+       $ cd ../hello-remote
+       $ git remote add fred ../hello-pull
+
+So that's a "git remote add" command line followed by an arbitrary
+name you'd like for the new remote (fred) and the URL of the remote
+(../hello-pull). Obviously, the URL could be a git:// URL or any other
+git-supported URL in addition to a local path.
+
+The "git remote" command is really just a helper for adding some
+entries to the .git/config file. You might find it more convenient to
+edit that file directly once you get comfortable with things.
+
+At this point the name "fred" will work much like the name "origin"
+has worked in previous examples. For example, we can fetch the changes
+fred has made with "git fetch fred":
+
+       $ git fetch fred
        remote: Generating pack...
+       Unpacking 3 objects...
        remote: Done counting 5 objects.
        Result has 3 objects.
        Deltifying 3 objects...
-       Unpacking 3 objects...
-       remote:  100% (3/3) done
+        100% remote: (3/3) done
        Total 3 (delta 1), reused 0 (delta 0)
         100% (3/3) done
-       Updating a1a0e8b..839b58d
-       Fast forward
-        hello.c |    3 ++-
-        1 files changed, 2 insertions(+), 1 deletions(-)
-
-It should be plain to see that the "git pull" command really did the
-combined sequence of "git fetch" and "git merge". Also, if you want to
-pull from the same repository you cloned from originally, (which is
-the common case for the upstream-tracking scenario), then "git pull"
-with no explicit repository is suffcient, and it will default to
-pulling from the same repository as the original clone.
-
-[XXX: The structure of the preceding section follows that of the
-original hgbook. But an alternate structure that arranged to pull from
-the originally cloned repository (as would be common) would allow for
-more straightforward use of git's features. For example, instead of
-the silly FETCH_HEAD stuff it would allow for "git fetch" and "git log
-master..origin" to be a very nice replacement for "hg
-incoming". Similarly, below, "git log origin..master" would make a
-nice replacement for "hg outgoing" which is something I didn't offer
-at all. One could also use git's remotes with the myriad repositories
-as used here, but it would require doing things like "git remote add
-<some-name> ../hello-pull" and that seems like a bit much to introduce
-for a turorial of this level. If nothing else, if the above section
-seems a little intimidating, understand that it's because things are
-not presented in the most natural "git way", (and I'm a little too
-tired to fix it tonight).]
+       * refs/remotes/fred/master: storing branch 'master' of ../hello-pull
+         commit: 3c54ac6
 
-Note: Mercurial users who are reading this might wonder if there's a
-need for the equivalent of "hg update" after doing a "git pull". And
-the answer is no. Unlike mercurial, "git pull" and "git merge" will
-automatically update the workind-directory files as necessary.
+Notice that this command-line only differs from the "git fetch" we did
+previously by explicitly naming which remote should be fetched. We
+could have explicitly said "git fetch origin" earlier.
+
+We can also list all known remote-tracking branches with "git branch
+-r":
 
-#### 2.8.2 Checking out previous revisions
+       $ git branch -r
+         fred/master
+         origin/HEAD
+         origin/master
+
+These remote-tracking branches make it very easy to collaborate with
+people as they are working on experimental features not yet ready for
+upstream inclusion. For example, if fred's latest code is still
+trashing filesystems then he might not want to push it out the the
+project's primary repository. But he may still want my help with
+it. So he can push it to a branch in his own repository for which I've
+got a remote. Then on my next "git fetch fred" I might notice a new
+branch called fred/trashes-filesystems and I can examine his code with
+a command such as "git log ..fred/trashed-filesystems".
+
+So lots of side collaboration can go on easily, and people working
+only with the primary repository never even have to see this dangerous
+code. It's distributed development at its finest.
+
+#### 2.8.4 Checking out previous revisions
 
 It's often useful to examine the working-tree state of some specific
 revision other than the tip of some branch. For example, maybe you
@@ -1081,61 +1202,65 @@ checking it out again:
        Previous HEAD position was 0a633bf... Create a makefile
        Switched to branch "master"
 
-#### 2.8.3 Pushing changes to another repository
+#### 2.8.5 Pushing changes to another repository
 
-Git lets us push changes to another repository, from the repository
-we’re currently visiting. As with previous examples, above, we’ll
-first create a temporary repository to push our changes into. But
-instead of using "git clone", this time we'll use "git init" to make a
-repository from an empty directory. We do this to create a "bare"
-repository which is simply a repository that has no working-directory
-files associated with it. In general, you should only push to bare
-repositories.
+As an unsurprising parallel to "git pull", git also provides "git
+push" for pushing changes to another repository. Now, generally the
+purpose of pushing to a repository is to have some "collaboration
+point" where potentially multiple people might be pushing or
+pulling. Because there might be multiple people pushing into the
+repository at any point, it wouldn't make sense to have a
+working-directory associated with this repository.
+
+For this, git has the notion of a "bare" repository, which is simply a
+repository with no working directory. Let's create a new bare
+repository and push some changes into it:
 
        $ cd ..
-       $ mkdir hello-push
-       $ cd hello-push
-       $ git --bare init
-       Initialized empty Git repository in /tmp/hello-push/
-
-And then we'll go back to our my-hello repository to perform the
-push. Since this is our very first push into this repository we need
-to tell git which branches to push. The easiest way to do this is to
-use --all to indicate all branches:
-
-       $ cd ../my-hello
-       $ git push ../hello-push --all
+       $ mkdir hello-bare
+       $ cd hello-bare
+       $ git --bare init --shared
+
+The --shared option sets up the necessary group file permissions so
+that other users in my group will be able to push into this repository
+as well.
+
+Now lets return to our hello repository and push some changes to this
+new repository. Since this is our very first push into this repository
+we need to tell git which branches to push. The easiest way to do this
+is to use --all to indicate all branches:
+
+       $ cd ../hello
+       $ git push ../hello-bare --all
        updating 'refs/heads/master'
          from 0000000000000000000000000000000000000000
-         to   839b58d021c618bd0e1d336d4d5878a0082672e6
+         to   3c54ac672ec1130b36837f1b708054a7a1d402de
        Generating pack...
        Done counting 18 objects.
        Deltifying 18 objects...
         100% (18/18) done
        Writing 18 objects...
         100% (18/18) done
-       Total 18 (delta 3), reused 0 (delta 0)
+       Total 18 (delta 3), reused 15 (delta 2)
        Unpacking 18 objects...
         100% (18/18) done
-       refs/heads/master: 0000000000000000000000000000000000000000 -> 839b58d021c618bd0e1d336d4d5878a0082672e6
+       refs/heads/master: 0000000000000000000000000000000000000000 -> 3c54ac672ec1130b36837f1b708054a7a1d402de
 
-For subsequent pushes we don't need to specify --all as "git push"
-will push all branches that exist in both the local and remote
-repositories.
+For subsequent pushes we don't need to specify --all as "git push" by
+default pushes all branches that exist in both the local and remote
+repositories. Also, as with pull, instead of explicitly specifying a
+URL, you may also specify a remote to push to. And by default, after
+cloning a repository, "git push" with no other arguments will attempt
+to push back to the same origin repository. As this is often exactly
+what is wanted, you may find that "git push" alone is often exactly
+what you need.
 
 What happens if we try to pull or push changes and the receiving
 repository already has those changes? Nothing too exciting.
 
-       $ git push ../hello-push
+       $ git push ../hello-bare
        Everything up-to-date
 
-#### 2.8.4 Sharing changes over a network
-
-The commands we have covered in the previous few sections are not
-limited to working with local repositories. Each works in exactly the
-same fashion over a network connection; simply pass in a URL or an ssh
-host:/path/name specification instead of a local path.
-
 ## Appendix D
 Open Publication License