X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=tour.mdwn;h=1c1333f9878908672b18b7343e0bf447f5623ac8;hb=8ef46e0628c7f12844485b4c292940c0ddbe50a8;hp=8c2fda550e6c297320dd79851863e114d61f7976;hpb=c5638157996f08c45c248758ec8164aff639491a;p=hgbook-git diff --git a/tour.mdwn b/tour.mdwn index 8c2fda5..1c1333f 100644 --- a/tour.mdwn +++ b/tour.mdwn @@ -1,11 +1,11 @@ -## Chapter 2 +## Chapter 2 A tour of git: the basics ### 2.0 Copyright -This document is a modified version originally known as "Distributed -revision control with Mercurial" and originally authored by Bryan -O’Sullivan. The original document was obtained from +This document is a modified version of a document originally titled +"Distributed revision control with Mercurial" and originally authored +by Bryan O’Sullivan. The original document was obtained from . Copyright © 2006, 2007 Bryan O’Sullivan. @@ -29,13 +29,23 @@ Changes made by Carl include the following: * Eliminate line numbers from examples * Modified to describe git instead of mercurial -### 2.1 Installing git on your system +The source of this modified version can be obtained via git: + + git clone git://cworth.org/git/hgbook-git + +or + + git clone http://cworth.org/git/hgbook-git + +and can be [browsed online](http://git.cworth.org/git/hgbook-git) + +### 2.1 Installing git on your system Prebuilt binary packages of git are available for many popular operating systems. These make it easy to start using git on your computer immediately. -#### 2.1.1 Linux +#### 2.1.1 Linux Because each Linux distribution has its own packaging tools, policies, and rate of development, it’s difficult to give a comprehensive set of @@ -50,27 +60,27 @@ install git with a single click. The package name to look for is often git, but is sometimes git-core, (due to an unfortunate name with git, meaning GNU Interactive Tools). - * Debian + * Debian - apt-get install git-core + apt-get install git-core - * Fedora Core + * Fedora Core - yum install git + yum install git - * Gentoo + * Gentoo - emerge git + emerge dev-util/git - * OpenSUSE + * OpenSUSE - yum install git + yum install git - * Ubuntu + * Ubuntu - apt-get install git + apt-get install git-core -#### 2.1.2 Mac OS X +#### 2.1.2 Mac OS X A git-core package is available through [macports](http://macports.org). Once macports is enabled, the command @@ -78,7 +88,7 @@ to install git is: port install git-core -#### 2.1.3 Windows +#### 2.1.3 Windows Git has long been available as part of cygwin, and works reasonably well in that environment. Some people find cygwin a particularly @@ -90,9 +100,9 @@ installers. These include GitMe, a package to install the entire development environment necessary to work on improving the msysgit port of git, and WinGit, a package for installing just git itself without the development environment, (still in Alpha as of September -2008). +2007). -### 2.2 Getting started +### 2.2 Getting started To begin, we’ll use the “git version” command to find out whether git is actually installed properly. Versions 1.5 and newer of git are much @@ -100,10 +110,10 @@ more friendly to new users than versions 1.4 and older. If you aren't yet running version 1.5 or newer, it's highly recommended that you upgrade. - $ git version + $ git version git version 1.5.3.2 -#### 2.2.1 Built-in help +#### 2.2.1 Built-in help Git provides a built-in help system. This is invaluable for those times when you find yourself stuck trying to remember how to run a @@ -111,8 +121,8 @@ command. If you are completely stuck, simply run “git help”; it will print a brief list of commonly-used commands, along with a description of what each does. If you ask for help on a specific command (such as "git help init"), it prints more detailed information. [XXX: Does "git -help " work universally as a built-in or does it expect man to be -present and just call out to "man git-"?] +help \" work universally as a built-in or does it expect man to be +present and just call out to "man git-\"?] [XXX: The original hgbook includes the complete output of "hg help init" at this point. I'm not including the corresponding @@ -125,7 +135,7 @@ present and just call out to "man git-"?] available for git-? And perhaps alos provide a "git -v help" similar to "hg -v help" for more?] -### 2.3 Working with a repository +### 2.3 Working with a repository In git, everything happens inside a repository. The repository for a project contains all of the files that “belong to” that project, @@ -136,13 +146,15 @@ 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 Making a local copy of a repository +#### 2.3.1 Creating a copy of a remote repository -Copying a repository is just a little bit special. While you could use -a normal file copying command to make a copy of a repository, it’s -best to use a built-in command that git provides. This command -is called “git clone”, because it creates an identical copy of an -existing repository. +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 +that you will be able to follow along: $ git clone git://cworth.org/git/hello Initialized empty Git repository in /tmp/hello/.git/ @@ -150,7 +162,7 @@ existing repository. remote: Done counting 15 objects. remote: Deltifying 15 objects... remote: 100% (15/15) done - remote: Total 15 (delta 2), reused 0 (delta 0) + remote: Total 15 (delta 2), reused 15 (delta remote: 2) Indexing 15 objects... 100% (15/15) done Resolving 2 deltas... @@ -162,26 +174,16 @@ efficiently) over http: $ git clone http://cworth.org/git/hello Initialized empty Git repository in /tmp/hello/.git/ - got 8e5536eaf0c9313cfcfd3bb915c7ebb63d6f7a91 - walk 8e5536eaf0c9313cfcfd3bb915c7ebb63d6f7a91 - got e4c1447f272c0f90e0a80e55f495ec377863f6f5 - got d884386a016f03bdd6c2c72ceba5621568cc0329 - got 350a36688de4ee9dfeba52f09bf02385cb967bb2 - walk d884386a016f03bdd6c2c72ceba5621568cc0329 - got 9a3ff79a7c30a4b990d49fe7c9095d6bd2eab6c0 - got ab82c5460482579faae7841e8da3b98fbb34a41c - got e19aeb100a31497481bba401cea42a39fac230ae - walk e19aeb100a31497481bba401cea42a39fac230ae - got 80b260cae9cec3cd52d27c46741ff59c321b852c - got 23f952d3dccd5524c3c1b48b3558a5b7393286c2 - got 9fa6a8464b692252de9a3a20c9e579700d613b17 - walk 9fa6a8464b692252de9a3a20c9e579700d613b17 - got b8937ca165a312157658a67e7d413dd59e6ad377 - got b3f85f210ff86d334575f64cb01c5bf49895b63e - got 556e69f96b04bff82857ddd7f7c08b2f3231d664 - walk 556e69f96b04bff82857ddd7f7c08b2f3231d664 - got ed55ec04ebc1736a91997a6ce7d7091010647c3d - got 43d727f2f3f2f7cb3b098ddad1d7038464a4cee2 + Getting alternates list for http://cworth.org/git/hello + Getting pack list for http://cworth.org/git/hello + Getting index for pack 04ecb061314ecbd60fa0610ecf55a1cbf85ea294 + Getting pack 04ecb061314ecbd60fa0610ecf55a1cbf85ea294 + which contains a1a0e8b392b17caf50325498df54802fe3c03710 + walk a1a0e8b392b17caf50325498df54802fe3c03710 + walk 72d4f10e4a27dbb09ace1503c20dbac1912ee451 + walk 13ed136b983a9c439eddeea8a1c2076cffbb685f + walk 0a633bf58b45fcf1a8299d3c82cd1fd26d3f48f2 + walk db7117a9dd9a6e57e8632ea5848e1101eee0fbde If our clone succeeded, we should now have a local directory called hello. This directory will contain some files. @@ -205,7 +207,34 @@ 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 What’s in a repository? +#### 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 it contains a directory named .git. This is where git keeps all @@ -226,707 +255,1024 @@ distinction is that the repository contains the history of your project, while the working directory contains a snapshot of your project at a particular point in history. -### 2.4 A tour through history +### 2.4 A tour through history One of the first things we might want to do with a new, unfamiliar -repository is understand its history. The “hg log” command gives us a +repository is understand its history. The “git log” command gives us a view of history. - $ hg log - changeset:   4:b57f9a090b62 - tag:         tip - user:        Bryan O'Sullivan  - date:        Tue Sep 06 15:43:07 2005 -0700 - summary:     Trim comments. + $ git log + commit a1a0e8b392b17caf50325498df54802fe3c03710 + Author: Bryan O'Sullivan + Date: Tue Sep 6 15:43:07 2005 -0700 + + Trim comments. - changeset:   3:ff5d7b70a2a9 - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:58 2005 -0700 - summary:     Get make to generate the final binary from a .o file. + commit 72d4f10e4a27dbb09ace1503c20dbac1912ee451 + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:58 2005 -0700 - changeset:   2:057d3c2d823c - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:43 2005 -0700 - summary:     Introduce a typo into hello.c. + Get make to generate the final binary from a .o file. - changeset:   1:82e55d328c8c - user:        mpm@selenic.com - date:        Fri Aug 26 01:21:28 2005 -0700 - summary:     Create a makefile + commit 13ed136b983a9c439eddeea8a1c2076cffbb685f + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:43 2005 -0700 - changeset:   0:0a04b987be5a - user:        mpm@selenic.com - date:        Fri Aug 26 01:20:50 2005 -0700 - summary:     Create a standard "hello, world" program + Introduce a typo into hello.c. + commit 0a633bf58b45fcf1a8299d3c82cd1fd26d3f48f2 + Author: Bryan O'Sullivan + Date: Fri Aug 26 01:21:28 2005 -0700 + + Create a makefile + + commit db7117a9dd9a6e57e8632ea5848e1101eee0fbde + Author: Bryan O'Sullivan + Date: Fri Aug 26 01:20:50 2005 -0700 + + Create a standard "hello, world" program + +This command prints a record of output for each change to the project +that was recorded. In git terminology, we call each of these recorded +events a commit. + +The default fields in a record of output from “git log” are as follows. + + * commit This field consists of a string of 40 hexadecimal characters. + This is a unique identifier for referring to particular commits. + * Author The identity of the person who authored the commit. This + field consist of two sub-fields for the user's name and email + address, (or at least an email-like idenitifer). Note that git + also stores a separate "Committer" field for the person who + commited the change, (since often an author will email a change to + a maintainer that commits it). See below for how to instruct "git + log" to display it as well. + * Date The date and time on which the commit was authored, (again + stored separately from the date the change was committed). + timezone in which it was created. (The date and time are displayed + in the timezone of the person who created the commit.) + * commit message The text message that the creator of the commit + entered to describe the commit, (generally a one-line summary + followed by more supporting text). + +The output of the "git log" command can be made more or less verbose +by means of the --pretty option. For example, with "git log +--pretty=short" the commit identifier will be omitted and only the +first line of each commit message will be show. And with "git log +--pretty=fuller", (the name 'fuller' is in contrast to the default +--pretty=full), the committer name and dates will be printed in +addition to the author name and dates. + +#### 2.4.1 Commits, revisions, and talking to other people -By default, this command prints a brief paragraph of output for each -change to the project that was recorded. In git terminology, we -call each of these recorded events a changeset, because it can contain -a record of changes to several files. +As English is a notoriously sloppy language, and computer science has +a hallowed history of terminological confusion (why use one term when +four will do?), revision control has a variety of words and phrases +that mean the same thing. If you are talking about git history +with other people, you will find that what we have called a “commit” +is often called a "revision". In other systems, a similar notion +is referred to as a "changeset". You might even see abbreviations of +these terms such as "rev", "change", or even "cset". + +While it may not matter much what word you use to refer to the concept +of “a commit”, it's important to know how to name “a specific +commit”. We have already seen one means of referring to a particular +commit, the 40-character hexadecimal string shown by "git log". These +commit identifiers are powerful because they are permanent, unique +identifiers that always identify the same commit in any copy of a +repository. If two users are examining a working directory associated +with the same commit identifier, then those two users have precisely +the same contents in all files, and exactly the same history leading +to that commit. + +So there are places where it is often important to archive the +complete commit identifier, (perhaps in bug-tracking systems to +indicate a specific commit that fixes a bug, for example). But often, +in more casual settings, it's more convenient to use abbreviated +commit identifiers. Git accept any unique prefix of a commit +identifier, (and for reasonably-sized project the first 8 or 10 +characters are almost always unique). + +And unlike the permanent commit identifiers, git also provides +transient means of identifying commits. In fact, in day-to-day use of +git, you will probably use these names more than commit +identifiers. One example is branch names, (such as the default +"master" branch in any git repository), or any project-specific branch +names such as "stable", "experimental", or "crazy-insane-changes". Git +also provides a special name "HEAD" which always refers to the current +branch. + +#### 2.4.2 Naming related commits + +Git offers simple ways to name revisions that are related to +particular revisions in the history. One syntax is the ~ suffix which +refers to the parent of a commit, or if followed by a number, to the +Nth parent. For example, since "HEAD" refers to the most recent commit +in the current branch, "HEAD~", refers to the previous commit, and +"HEAD~2" refers to two commits back in the history. + +Another useful syntax is .. which can be used to specify a range of +commits. So "origin..master" specifies everything that has been +committed to master since it diverged from origin. + +#### 2.4.3 Viewing specific revisions + +You can use "git log" to explore the range syntax just introduced. For +example, to see a list of the most recent 3 revisions you can use +"HEAD~3..", (the destination of the range is implicitly HEAD in this +case): + + $ git log HEAD~3.. + commit a1a0e8b392b17caf50325498df54802fe3c03710 + Author: Bryan O'Sullivan + Date: Tue Sep 6 15:43:07 2005 -0700 + + Trim comments. + + commit 72d4f10e4a27dbb09ace1503c20dbac1912ee451 + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:58 2005 -0700 + + Get make to generate the final binary from a .o file. + + commit 13ed136b983a9c439eddeea8a1c2076cffbb685f + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:43 2005 -0700 + + Introduce a typo into hello.c. -The fields in a record of output from “hg log” are as follows. +#### 2.4.4 Other log filters - * changeset This field has the format of a number, followed by a - colon, followed by a hexadecimal string. These are identifiers for - the changeset. There are two identifiers because the number is - shorter and easier to type than the hex string. - * user The identity of the person who created the changeset. This is - a free-form field, but it most often contains a person’s name and - email address. - * date The date and time on which the changeset was created, and the - timezone in which it was created. (The date and time are local to - that timezone; they display what time and date it was for the - person who created the changeset.) - * summary The first line of the text message that the creator of the - changeset entered to describe the changeset. +Besides filtering by commit identifiers, git allows you to easily +filter the log output according to which files (or directories) are +modified by listing them after "--" which is necessary to distinguish +commit names from file names: -The default output printed by “hg log” is purely a summary; it is -missing a lot of detail. + $ git log -- Makefile + commit 72d4f10e4a27dbb09ace1503c20dbac1912ee451 + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:58 2005 -0700 + + Get make to generate the final binary from a .o file. + + commit 0a633bf58b45fcf1a8299d3c82cd1fd26d3f48f2 + Author: Bryan O'Sullivan + Date: Fri Aug 26 01:21:28 2005 -0700 + + Create a makefile -Figure [2.1][8] provides a graphical representation of the history of -the hello repository, to make it a little easier to see which -direction history is “flowing” in. We’ll be returning to this figure -several times in this chapter and the chapter that follows. +And "git log" can also filter based on the dates at which commits were +created: -* * * + $ git log --since="2 weeks ago" --until="yesterday" -![PIC][9] + [XXX: By default, "git log" displays author dates as "Date" + but then uses commit dates when given a --since option. That + seems like broken defaults to me. Why the inconsistency?] -Figure 2.1: -Graphical history of the hello repository +Another useful option is -n or --max-count which, unsurprisingly, +limits the maximum number of commits to be displayed. -* * * +#### 2.4.5 More detailed information -#### 2.4.1 Changesets, revisions, and talking to other people +While the default information printed by “git log” is useful if you +already know what you’re looking for, you may need to see more details +of the change, such as the "diffstat" information with --stat: -As English is a notoriously sloppy language, and computer science has -a hallowed history of terminological confusion (why use one term when -four will do?), revision control has a variety of words and phrases -that mean the same thing. If you are talking about Mercurial history -with other people, you will find that the word “changeset” is often -compressed to “change” or (when written) “cset”, and sometimes a -changeset is referred to as a “revision” or a “rev”. - -While it doesn’t matter what word you use to refer to the concept of -“a changeset”, the identifier that you use to refer to “a specific -changeset” is of great importance. Recall that the changeset field in -the output from “hg log” identifies a changeset using both a number -and a hexadecimal string. - - * The revision number is only valid in that repository, - * while the hex string is the permanent, unchanging identifier that - will always identify that exact changeset in every copy of the - repository. - -This distinction is important. If you send someone an email talking -about “revision 33”, there’s a high likelihood that their revision 33 -will not be the same as yours. The reason for this is that a revision -number depends on the order in which changes arrived in a repository, -and there is no guarantee that the same changes will happen in the -same order in different repositories. Three changes a,b,c can easily -appear in one repository as 0,1,2, while in another as 1,0,2. - -Mercurial uses revision numbers purely as a convenient shorthand. If -you need to discuss a changeset with someone, or make a record of a -changeset for some other reason (for example, in a bug report), use -the hexadecimal identifier. - -#### 2.4.2 Viewing specific revisions - -To narrow the output of “hg log” down to a single revision, use the -r -(or --rev) option. You can use either a revision number or a long-form -changeset identifier, and you can provide as many revisions as you -want. - - $ hg log -r 3 - changeset:   3:ff5d7b70a2a9 - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:58 2005 -0700 - summary:     Get make to generate the final binary from a .o file. + $ git log --stat --max-count=3 + commit a1a0e8b392b17caf50325498df54802fe3c03710 + Author: Bryan O'Sullivan + Date: Tue Sep 6 15:43:07 2005 -0700 - $ hg log -r ff5d7b70a2a9 - changeset:   3:ff5d7b70a2a9 - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:58 2005 -0700 - summary:     Get make to generate the final binary from a .o file. + Trim comments. - $ hg log -r 1 -r 4 - changeset:   1:82e55d328c8c - user:        mpm@selenic.com - date:        Fri Aug 26 01:21:28 2005 -0700 - summary:     Create a makefile + hello.c | 8 ++------ + 1 files changed, 2 insertions(+), 6 deletions(-) - changeset:   4:b57f9a090b62 - tag:         tip - user:        Bryan O'Sullivan  - date:        Tue Sep 06 15:43:07 2005 -0700 - summary:     Trim comments. + commit 72d4f10e4a27dbb09ace1503c20dbac1912ee451 + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:58 2005 -0700 - -If you want to see the history of several revisions without having to -list each one, you can use range notation; this lets you express the -idea “I want all revisions between a and b, inclusive”. - - $ hg log -r 2:4 - changeset:   2:057d3c2d823c - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:43 2005 -0700 - summary:     Introduce a typo into hello.c. + Get make to generate the final binary from a .o file. - changeset:   3:ff5d7b70a2a9 - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:58 2005 -0700 - summary:     Get make to generate the final binary from a .o file. + Makefile | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) - changeset:   4:b57f9a090b62 - tag:         tip - user:        Bryan O'Sullivan  - date:        Tue Sep 06 15:43:07 2005 -0700 - summary:     Trim comments. + commit 13ed136b983a9c439eddeea8a1c2076cffbb685f + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:43 2005 -0700 - -Mercurial also honours the order in which you specify revisions, so -“hg log -r 2:4” prints 2,3,4 while “hg log -r 4:2” prints 4,3,2. - -#### 2.4.3 More detailed information - -While the summary information printed by “hg log” is useful if you -already know what you’re looking for, you may need to see a complete -description of the change, or a list of the files changed, if you’re -trying to decide whether a changeset is the one you’re looking -for. The “hg log” command’s -v (or --verbose) option gives you this -extra detail. - - $ hg log -v -r 3 - changeset:   3:ff5d7b70a2a9 - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:58 2005 -0700 - files:       Makefile - description: - Get make to generate the final binary from a .o file. + Introduce a typo into hello.c. + hello.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +Or perhaps you'd like to see the actual patch content of each change, +which you can get with -p. That commit with the word typo in its name +looks suspicous, so let's tak a closer look. Remember that we can name +it as master~3, HEAD~3, or any prefix of its commit identifier, (such +as 13ed136b): + + $ git log -p -n 1 13ed136b + commit 13ed136b983a9c439eddeea8a1c2076cffbb685f + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:43 2005 -0700 - -If you want to see both the description and content of a change, add -the -p (or --patch) option. This displays the content of a change as a -unified diff (if you’ve never seen a unified diff before, see -section [12.4][10] for an overview). - - $ hg log -v -p -r 2 - changeset:   2:057d3c2d823c - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:43 2005 -0700 - files:       hello.c - description: - Introduce a typo into hello.c. + Introduce a typo into hello.c. + diff --git a/hello.c b/hello.c + index ed55ec0..80b260c 100644 + --- a/hello.c + +++ b/hello.c + @@ -11,6 +11,6 @@ + + int main(int argc, char **argv) + { + - printf("hello, world!\n"); + + printf("hello, world!\"); + return 0; + } + +Of course, wanting to see all this information for a single commit is +such a common operation that it's given its own name in git, "git +show". So "git show 13ed136b" is a much easier way to get exactly the +same output: + + $ git show 13ed136b + commit 13ed136b983a9c439eddeea8a1c2076cffbb685f + Author: Bryan O'Sullivan + Date: Tue Sep 6 13:15:43 2005 -0700 - diff -r 82e55d328c8c -r 057d3c2d823c hello.c - --- a/hello.c Fri Aug 26 01:21:28 2005 -0700 - +++ b/hello.c Tue Sep 06 13:15:43 2005 -0700 - @@ -11,6 +11,6 @@ + Introduce a typo into hello.c. - int main(int argc, char ⋆⋆argv) - { - - printf("hello, world!∖n"); - + printf("hello, world!∖"); - return 0; - } - - -### 2.5 All about command options - -Let’s take a brief break from exploring Mercurial commands to discuss + diff --git a/hello.c b/hello.c + index ed55ec0..80b260c 100644 + --- a/hello.c + +++ b/hello.c + @@ -11,6 +11,6 @@ + + int main(int argc, char **argv) + { + - printf("hello, world!\n"); + + printf("hello, world!\"); + return 0; + } + +### 2.5 All about command options + +Let’s take a brief break from exploring git commands to discuss a pattern in the way that they work; you may find this useful to keep in mind as we continue our tour. -Mercurial has a consistent and straightforward approach to dealing +Git has a consistent and straightforward approach to dealing with the options that you can pass to commands. It follows the conventions for options that are common to modern Linux and Unix systems. - * Every option has a long name. For example, as we’ve already seen, - the “hg log” command accepts a --rev option. - * Most options have short names, too. Instead of --rev, we can use - -r. (The reason that some options don’t have short names is that - the options in question are rarely used.) - * Long options start with two dashes (e.g. --rev), while short - options start with one (e.g. -r). + * Most options have long names. For example, as we’ve already seen, + the “git log" command accepts a --max-count=\ option. + * Some options have short, single-character names. Often these are + aliases for long commands, (such as "-n \" instead of + --max-count=\), but sometimes the option exists in + short-form with no long-form equivalent, (such as -p). [XXX: It + wouldn't hurt to fix this by adding --patch, etc. right?] + * Long options start with two dashes (e.g. --max-count), while short + options start with one (e.g. -n). + * Option naming and usage is consistent across commands. For - example, every command that lets you specify a changeset ID or - revision number accepts both -r and --rev arguments. - -In the examples throughout this book, I use short options instead of -long. This just reflects my own preference, so don’t read anything -significant into it. - -Most commands that print output of some kind will print more output -when passed a -v (or --verbose) option, and less when passed -q (or ---quiet). - -### 2.6 Making and reviewing changes - -Now that we have a grasp of viewing history in Mercurial, let’s take a -look at making some changes and examining them. - -The first thing we’ll do is isolate our experiment in a repository of -its own. We use the “hg 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. - - $ cd .. - $ hg clone hello my-hello - 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ cd my-hello - -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. - -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 - -Mercurial’s “hg status” command will tell us what Mercurial knows -about the files in the repository. - - $ ls - Makefile  hello.c - $ hg status - M hello.c - -The “hg status” command prints no output for some files, but a line -starting with “M” for hello.c. Unless you tell it to, “hg status” will -not print any output for files that have not been modified. - -The “M” indicates that Mercurial has noticed that we modified -hello.c. We didn’t need to inform Mercurial that we were going to -modify the file before we started, or that we had modified the file -after we were done; it was able to figure this out itself. + example, every command that lets you specify a commit identifier + or range will accept the same expressions, (HEAD~3, + origin..master, 72d4f10e, etc), while any command that can be + limited by paths will accept the same expressions ("-- doc/ + some-file.c"), etc. + +Many commands that print output of some kind can be made more quiet by +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. You should be working +within the "hello" directory that we originally cloned. + +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: + + $ sed -i '/printf/s/\\"/\\n"/' hello.c + +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 + $ git status + # On branch master + # Changed but not updated: + # (use "git add ..." to update what will be committed) + # + # modified: hello.c + # + no changes added to commit (use "git add" and/or "git commit -a") + +First "git status" tells us that the current branch is "master". This +means that the master branch is what will be updated 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 +were done; it was able to figure this out itself. It’s a little bit helpful to know that we’ve modified hello.c, but we might prefer to know exactly what changes we’ve made to it. To do -this, we use the “hg diff” command. - - $ hg diff - diff -r b57f9a090b62 hello.c - --- a/hello.c Tue Sep 06 15:43:07 2005 -0700 - +++ b/hello.c Sun Jun 17 18:05:50 2007 +0000 - @@ -8,5 +8,6 @@ int main(int argc, char ⋆⋆argv) - int main(int argc, char ⋆⋆argv) - { - printf("hello, world!∖"); - + printf("hello again!∖n"); - return 0; - } - -### 2.7 Recording changes in a new changeset - -We can modify files, build and test our changes, and use “hg status” -and “hg diff” to review our changes, until we’re satisfied with what +this, we use the “git diff” command. + + $ git diff + diff --git a/hello.c b/hello.c + index 9a3ff79..ea364d3 100644 + --- a/hello.c + +++ b/hello.c + @@ -7,6 +7,6 @@ + + int main(int argc, char **argv) + { + - printf("hello, world!\"); + + printf("hello, world!\n"); + return 0; + } + +### 2.7 Recording changes in a new commit + +We can modify files, build and test our changes, and use “git status” +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 changeset. - -The “hg commit” command lets us create a new changeset; we’ll usually -refer to this as “making a commit” or “committing”. - -#### 2.7.1 Setting up a username - -When you try to run “hg commit” for the first time, it is not -guaranteed to succeed. Mercurial records your name and address with -each change that you commit, so that you and others will later be able -to tell who made each change. Mercurial tries to automatically figure -out a sensible username to commit the change with. It will attempt -each of the following methods, in order: - - 1. If you specify a -u option to the “hg commit” command on the - command line, followed by a username, this is always given the - highest precedence. - 2. If you have set the HGUSER environment variable, this is checked next. - 3. If you create a file in your home directory called .hgrc, with a - username entry, that will be used next. To see what the contents - of this file should look like, refer to section [2.7.1][11] - below. - 4. If you have set the EMAIL environment variable, this will be used - next. - 5. Mercurial will query your system to find out your local user name - and host name, and construct a username from these - components. Since this often results in a username that is not - very useful, it will print a warning if it has to do this. - -If all of these mechanisms fail, Mercurial will fail, printing an -error message. In this case, it will not let you commit until you set -up a username. - -You should think of the HGUSER environment variable and the -u option -to the “hg commit” command as ways to override Mercurial’s default -selection of username. For normal use, the simplest and most robust -way to set a username for yourself is by creating a .hgrc file; see -below for details. - -##### Creating a Mercurial configuration file - -To set a user name, use your favourite editor to create a file called -.hgrc in your home directory. Mercurial will use this file to look up -your personalised configuration settings. The initial contents of your -.hgrc should look like this. - - # This is a Mercurial configuration file. - [ui] - username = Firstname Lastname  - -The “[ui]” line begins a section of the config file, so you can read -the “username = ...” line as meaning “set the value of the username -item in the ui section”. A section continues until a new section -begins, or the end of the file. Mercurial ignores empty lines and -treats any text from “#” to the end of a line as a comment. - -##### Choosing a user name - -You can use any text you like as the value of the username config -item, since this information is for reading by other people, but for -interpreting by Mercurial. The convention that most people follow is -to use their name and email address, as in the example above. - -Note: Mercurial’s built-in web server obfuscates email addresses, to -make it more difficult for the email harvesting tools that spammers -use. This reduces the likelihood that you’ll start receiving more junk -email if you publish a Mercurial repository on the web. - -#### 2.7.2 Writing a commit message - -When we commit a change, Mercurial drops us into a text editor, to +record our work in a new commit. + +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 + +Before you run "git commit" though, you should introduce yourself to +git. Git records your name and email 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 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 \" + string, then this name and addresss will be used for the author + fields. The committer fields will still be determined as + below. This option is very helpful for when applying a commit + originally authored by someone other than yourself. + 2. If any of the `GIT_AUTHOR_NAME`, `GIT_AUTHOR_EMAIL`, + `GIT_COMMITTER`_NAME, or `GIT_COMMITER_EMAIL` environment variables + are set, then those values will be used for the corresponding + fields. + 3. If you have a file in your home directory called .gitconfig, with + name or email settings in the [user] section, then these values + will be used to set any remaining author and committer + fields. For more details on the contents of this file, refer to + section 2.7.1 below. + 4. If you have a file in the local repository called .git/config, + again with name or email settings in the [user] section, then + these values will be used to set any remaining author and + committer fields. + 5. If you have set the `EMAIL` environment variable, this will be used + to set author and committer email addresses if still unset. + 6. git will query your system to find out your real name from + available GECOS field and your username, hostname, and domain to + construct an email address, (or at least an identifier resembling + an email address). + +If all of these mechanisms fail, "git commit" will fail, printing an +error message instructing you how to use "git config" to tell git your +name and email address. + +You should think of the `GIT_AUTHOR`/`COMMITER_NAME`/`EMAIL` environment +variables and the --author option to the “git commit” command as ways +to override git’s default selection. For normal use, the simplest and +most robust way to set your information is by creating a .gitconfig +file, (either manually or with the "git config" command); see below +for details. + +##### Creating a git configuration file + +To set your name and email address, just use the following commands: + + git config --global user.name "Your Name" + git config --global user.email "you@example.com" + +The --global option means that this command will set global +information, (affecting all repositories on this machine), in the +.gitconfig file in your home directory. Alternately, you could omit +the --global which would make the change take effect only in the local +repository. This is convenient if you want to have different email +addresses associated with different projects, for example. + +Of course, git's configuration file is a simple-to-edit plain-text +file, so instead of using the above commands, you can also just edit +the files directly. Use your favorite editor to create a file called +.gitconfig in your home directory, (or if you ran the above commands +then it will be there already). The initial contents of your +.gitconfig should look like this. + + # This is a git configuration file. + [user] + name = Your Name + email = you@example.com + +Similarly, you can make a repository-specific configuration by editing +.git/config in the local repository. It will already have some +sections present, (created by the "git clone"), just add a [user] +section as above. + +The “[user]” line begins a section of the config file, so you can read +the “name = ...” line as meaning “set the value of the name item in +the user section”. This is the same notion expressed with the +"user.name" syntax on the git-config command line. A section +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 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 your +actual name as well as a valid email address. But some people, (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 + +When we commit a change, git drops us into a text editor to enter a message that will describe the modifications we’ve made in -this changeset. This is called the commit message. It will be a record -for readers of what we did and why, and it will be printed by “hg log” +this commit. This is called the commit message. It will be a record +for readers of what we did and why, and it will be printed by “git log” after we’ve finished committing. - $ hg commit - -The editor that the “hg commit” command drops us into will contain an -empty line, followed by a number of lines starting with “HG:”. + $ git commit -a + +Note: The -a on the command-line instructs git to commit the new +content of *all* tracked files that have been modified. This is a +convenience over explicitly listing filenames to be committed on the +"git commit" command line. It is useful to use "git commit \" +when there is a need to commit only some subset of the files that have +been modified. + +If new files need to be committed for the first time, just use "git +add \" before "git commit -a". If a file needs to be removed, +just remove it as normal before committing and "git commit -a" will +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 “#”. These +lines contain the same information as seen in "git status" before: + + empty line + # Please enter the commit message for your changes. + # (Comment lines starting with '#' will not be included) + # On branch master + # Changes to be committed: + # (use "git reset HEAD ..." to unstage) + # + # modified: hello.c + # + # Untracked files: + # (use "git add ..." 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. - empty line - HG: changed hello.c +#### 2.7.3 Writing a good commit message -Mercurial ignores the lines that start with “HG:”; it uses them only -to tell us which files it’s recording changes to. Modifying or -deleting these lines has no effect. +A good commit message will generally have a single line that +summarizes the commit, a blank line, and then one or more pargraphs +with supporting detail. Since many tools only print the first line of +a commit message by default, it’s important that the first line stands +alone. -#### 2.7.3 Writing a good commit message +One example of a first-line-only viewer is "git log +--pretty=short". Other examples include graphical history viewers such +as gitk and gitview, and web-based viewers such as gitweb and cgit. -Since “hg log” only prints the first line of a commit message by -default, it’s best to write a commit message whose first line stands -alone. Here’s a real example of a commit message that doesn’t follow +Here’s a real example of a commit message that doesn’t follow this guideline, and hence has a summary that is not readable. - changeset:   73:584af0e231be - user:        Censored Person  - date:        Tue Sep 26 21:37:07 2006 -0700 - summary:     include buildmeister/commondefs.   Add an exports and install + $ git log --pretty=short + commit 3ef5535144da88a854f7930503845cd44506c2e2 + Author: Censored Person + + include buildmeister/commondefs. Add an exports and install As far as the remainder of the contents of the commit message are -concerned, there are no hard-and-fast rules. Mercurial itself doesn’t +concerned, there are no hard-and-fast rules. git itself doesn’t interpret or care about the contents of the commit message, though your project may have policies that dictate a certain kind of 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 “hg log --patch”. +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 +#### 2.7.4 Aborting a commit If you decide that you don’t want to commit while in the middle of editing a commit message, simply exit from your editor without saving the file that it’s editing. This will cause nothing to happen to either the repository or the working directory. -If we run the “hg commit” command without any arguments, it records -all of the changes we’ve made, as reported by “hg status” and “hg -diff”. - -#### 2.7.5 Admiring our new handiwork - -Once we’ve finished the commit, we can use the “hg tip” command to -display the changeset we just created. This command produces output -that is identical to “hg log”, but it only displays the newest -revision in the repository. - - $ hg tip -vp - changeset:   5:fa1321bf0c80 - tag:         tip - user:        Bryan O'Sullivan  - date:        Sun Jun 17 18:05:50 2007 +0000 - files:       hello.c - description: - Added an extra line of output +#### 2.7.5 Admiring our new handiwork + +Once we’ve finished the commit, we can use the “git show” command to +display the commit we just created. As discussed previously, this +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 fd21e5d6c5eedee70137229ebf348c25181812ab + Author: Carl Worth + Date: Fri Sep 28 12:50:16 2007 -0700 + + Fixed the typo so the program actuall complies now. + diff --git a/hello.c b/hello.c + index 9a3ff79..ea364d3 100644 + --- a/hello.c + +++ b/hello.c + @@ -7,6 +7,6 @@ + + int main(int argc, char **argv) + { + - printf("hello, world!\"); + + printf("hello, world!\n"); + return 0; + } + +Note that you will not see the same commit identifier for your commit, +even if the change you made is identical to mine. The commit +identifier incorporates not only the contents of the files, but commit +message, the author and committer names and emails, and the author and +commit dates. (OK, so now you probably know enough to be able to guess +the right command to produce a commit with exactly the commit +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 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? + +This is the exact situation for which "git commit --amend" was +invented. So I can just run that now and fix the broken commit +message: + + $ git commit --amend + +Here's the final result: + + $ git show + commit 3c54ac672ec1130b36837f1b708054a7a1d402de + Author: Carl Worth + Date: Fri Sep 28 12:50:16 2007 -0700 - diff -r b57f9a090b62 -r fa1321bf0c80 hello.c - --- a/hello.c Tue Sep 06 15:43:07 2005 -0700 - +++ b/hello.c Sun Jun 17 18:05:50 2007 +0000 - @@ -8,5 +8,6 @@ int main(int argc, char ⋆⋆argv) - int main(int argc, char ⋆⋆argv) - { - printf("hello, world!∖"); - + printf("hello again!∖n"); - return 0; - } + Fixed the typo so the program actually compiles now. + diff --git a/hello.c b/hello.c + index 9a3ff79..ea364d3 100644 + --- a/hello.c + +++ b/hello.c + @@ -7,6 +7,6 @@ + + int main(int argc, char **argv) + { + - printf("hello, world!\"); + + printf("hello, world!\n"); + return 0; + } + +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 hello repository. Let’s look at a few ways that we can +propagate this change into other repositories. -We refer to the newest revision in the repository as the tip revision, -or simply the tip. +#### 2.8.1 Pulling changes from the original repository -### 2.8 Sharing changes +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. -We mentioned earlier that repositories in Mercurial are -self-contained. This means that the changeset we just created exists -only in our my-hello repository. Let’s look at a few ways that we can -propagate this change into other repositories. +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. -#### 2.8.1 Pulling changes from another 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. - - $ cd .. - $ hg clone hello hello-pull - 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - -We’ll use the “hg pull” command to bring changes from my-hello into -hello-pull. However, blindly pulling unknown changes into a repository -is a somewhat scary prospect. Mercurial provides the “hg incoming” -command to tell us what changes the “hg pull” command would pull into -the repository, without actually pulling the changes in. - - $ cd hello-pull - $ hg incoming ../my-hello - comparing with ../my-hello - searching for changes - changeset:   5:fa1321bf0c80 - tag:         tip - user:        Bryan O'Sullivan  - date:        Sun Jun 17 18:05:50 2007 +0000 - summary:     Added an extra line of output - +In this case, the operation is as simple as just calling "git pull": -(Of course, someone could cause more changesets to appear in the -repository that we ran “hg incoming” in, before we get a chance to “hg -pull” the changes, so that we could end up pulling changes that we -didn’t expect.) + $ cd ../hello-pull + $ git pull + remote: Generating pack... + Unpacking 3 objects... + 100% (3/3) done + 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) + * 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 + Date: Fri Sep 28 12:50:16 2007 -0700 + + Fixed the typo so the program actually compiles now. -Bringing changes into a repository is a simple matter of running the -“hg pull” command, and telling it which repository to pull from. +As expected, we received just the one commit. - $ hg tip - changeset:   4:b57f9a090b62 - tag:         tip - user:        Bryan O'Sullivan  - date:        Tue Sep 06 15:43:07 2005 -0700 - summary:     Trim comments. - - $ hg pull ../my-hello - pulling from ../my-hello - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files - (run 'hg update' to get a working copy) - $ hg tip - changeset:   5:fa1321bf0c80 - tag:         tip - user:        Bryan O'Sullivan  - date:        Sun Jun 17 18:05:50 2007 +0000 - summary:     Added an extra line of output - +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. -As you can see from the before-and-after output of “hg tip”, we have -successfully pulled changes into our repository. There remains one -step before we can see these changes in the working directory. - -#### 2.8.2 Updating the working directory - -We have so far glossed over the relationship between a repository and -its working directory. The “hg pull” command that we ran in -section [2.8.1][12] brought changes into the repository, but if we -check, there’s no sign of those changes in the working directory. This -is because “hg pull” does not (by default) touch the working -directory. Instead, we use the “hg update” command to do this. - - $ grep printf hello.c - printf("hello, world!∖"); - $ hg update tip - 1 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ grep printf hello.c - printf("hello, world!∖"); - printf("hello again!∖n"); - -It might seem a bit strange that “hg pull” doesn’t update the working -directory automatically. There’s actually a good reason for this: you -can use “hg update” to update the working directory to the state it -was in at any revision in the history of the repository. If you had -the working directory updated to an old revision—to hunt down the -origin of a bug, say—and ran a “hg pull” which automatically updated -the working directory to a new revision, you might not be terribly -happy. - -However, since pull-then-update is such a common thing to do, -Mercurial lets you combine the two by passing the -u option to “hg -pull”. - - hg pull -u - -If you look back at the output of “hg pull” in section [2.8.1][12] -when we ran it without -u, you can see that it printed a helpful -reminder that we’d have to take an explicit step to update the working -directory: - - (run 'hg update' to get a working copy) - -To find out what revision the working directory is at, use the “hg -parents” command. - - $ hg parents - changeset:   5:fa1321bf0c80 - tag:         tip - user:        Bryan O'Sullivan  - date:        Sun Jun 17 18:05:50 2007 +0000 - summary:     Added an extra line of output - +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. -If you look back at figure [2.1][8], you’ll see arrows connecting each -changeset. The node that the arrow leads from in each case is a -parent, and the node that the arrow leads to is its child. The working -directory has a parent in just the same way; this is the changeset -that the working directory currently contains. - -To update the working directory to a particular revision, give a -revision number or changeset ID to the “hg update” command. - - $ hg update 2 - 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - $ hg parents - changeset:   2:057d3c2d823c - user:        Bryan O'Sullivan  - date:        Tue Sep 06 13:15:43 2005 -0700 - summary:     Introduce a typo into hello.c. - - $ hg update - 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - -If you omit an explicit revision, “hg update” will update to the tip -revision, as shown by the second call to “hg update” in the example -above. - -#### 2.8.3 Pushing changes to another repository - -Mercurial lets us push changes to another repository, from the -repository we’re currently visiting. As with the example of “hg pull” -above, we’ll create a temporary repository to push our changes into. - - $ cd .. - $ hg clone hello hello-push - 2 files updated, 0 files merged, 0 files removed, 0 files unresolved - -The “hg outgoing” command tells us what changes would be pushed into -another repository. - - $ cd my-hello - $ hg outgoing ../hello-push - comparing with ../hello-push - searching for changes - changeset:   5:fa1321bf0c80 - tag:         tip - user:        Bryan O'Sullivan  - date:        Sun Jun 17 18:05:50 2007 +0000 - summary:     Added an extra line of output - +#### 2.8.2 Using fetch and merge separately to pull -And the “hg push” command does the actual push. +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. - $ hg push ../hello-push - pushing to ../hello-push - searching for changes - adding changesets - adding manifests - adding file changes - added 1 changesets with 1 changes to 1 files +So let's do that within the hello-fetch clone we made earlier. First +we will do the fetch: -As with “hg pull”, the “hg push” command does not update the working -directory in the repository that it’s pushing changes into. (Unlike -“hg pull”, “hg push” does not provide a -u option that updates the -other repository’s working directory.) + $ 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 + Date: Fri Sep 28 12:50:16 2007 -0700 + + Fixed the typo so the program actually compiles now. + +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: + +[[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 | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +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 from 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... + 100% remote: (3/3) done + Total 3 (delta 1), reused 0 (delta 0) + 100% (3/3) done + * refs/remotes/fred/master: storing branch 'master' of ../hello-pull + commit: 3c54ac6 + +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": + + $ 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 +would like to build a particular tagged version, or maybe you'd like +to test the behavior of the code before a particular change was +introduced. To do this, use "git checkout" and pass it the name of any +revision, (with a branch name, a tag name, or any other commit +identifier). For example, to examine our project before the original +typo was introduced: + + $ git checkout 0a633bf5 + Note: moving to "0a633bf5" which isn't a local branch + If you want to create a new branch from this checkout, you may do so + (now or later) by using -b with the checkout command again. Example: + git checkout -b + HEAD is now at 0a633bf... Create a makefile + +The note that git gives us is to indicate that we are checking out a +non-branch revision. This is perfectly fine if we are just exploring +history, but if we actually wanted to use this revision as the basis +for new commits, we would first have to create a new branch name as it +describes. + +If we were to use "git checkout" with a branch name, then that would +change the current branch, (meaning that any new commits would advance +that branch pointer). + +For now, let's return back to the tip of the master branch by just +checking it out again: + + $ git checkout master + Previous HEAD position was 0a633bf... Create a makefile + Switched to branch "master" + +#### 2.8.5 Pushing changes to another repository + +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-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 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 15 (delta 2) + Unpacking 18 objects... + 100% (18/18) done + refs/heads/master: 0000000000000000000000000000000000000000 -> 3c54ac672ec1130b36837f1b708054a7a1d402de + +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. - $ hg push ../hello-push - pushing to ../hello-push - searching for changes - no changes found - -#### 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 instead -of a local path. - - $ hg outgoing http://hg.serpentine.com/tutorial/hello - comparing with http://hg.serpentine.com/tutorial/hello - searching for changes - changeset:   5:fa1321bf0c80 - tag:         tip - user:        Bryan O'Sullivan  - date:        Sun Jun 17 18:05:50 2007 +0000 - summary:     Added an extra line of output - + $ git push ../hello-bare + Everything up-to-date -In this example, we can see what changes we could push to the remote -repository, but the repository is understandably not set up to let -anonymous users push to it. - - $ hg push http://hg.serpentine.com/tutorial/hello - pushing to http://hg.serpentine.com/tutorial/hello - searching for changes - ssl required - - [1]: http://hgbook.red-bean.com/hgbookch3.html - [2]: http://hgbook.red-bean.com/hgbookch1.html - [3]: http://hgbook.red-bean.com/hgbookch1.html#tailhgbookch1.html - [4]: #tailhgbookch2.html - [5]: http://hgbook.red-bean.com/hgbook.html#hgbookch2.html - [6]: http://mercurial.berkwood.com/ - [7]: http://hgbook.red-bean.com/hgbookli4.html#Xweb:macpython - [8]: #x6-340581 - [9]: hgbookch2_files/tour-history.png - [10]: http://hgbook.red-bean.com/hgbookch12.html#x16-27100012.4 - [11]: #x6-420002.7.1 - [12]: #x6-490002.8.1 - [13]: http://hgbook.red-bean.com/hgbookch2.html - -## Appendix D +## Appendix D Open Publication License Version 1.0, 8 June 1999 -### D.1 Requirements on both unmodified and modified versions +### D.1 Requirements on both unmodified and modified versions The Open Publication works may be reproduced and distributed in whole or in part, in any medium physical or electronic, provided that the @@ -943,7 +1289,7 @@ presently available at [http://www.opencontent.org/openpub/][http://www.opencontent.org/openpub/]). The reference must be immediately followed with any options elected by -the author(s) and/or publisher of the document (see section D.6). +the author(s) and/or publisher of the document (see section D.6). Commercial redistribution of Open Publication-licensed material is permitted. @@ -955,12 +1301,12 @@ outer surfaces of the book the original publisher’s name shall be as large as the title of the work and cited as possessive with respect to the title. -### D.2 Copyright +### D.2 Copyright The copyright to each Open Publication is owned by its author(s) or designee. -### D.3 Scope of license +### D.3 Scope of license The following license terms apply to all Open Publication works, unless otherwise explicitly stated in the document. @@ -980,7 +1326,7 @@ without warranty of any kind, express or implied, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose or a warranty of non-infringement. -### D.4 Requirements on modified works +### D.4 Requirements on modified works All modified versions of documents covered by this license, including translations, anthologies, compilations and partial documents, must @@ -997,7 +1343,7 @@ meet the following requirements: assert or imply endorsement of the resulting document without the original author’s (or authors’) permission. -### D.5 Good-practice recommendations +### D.5 Good-practice recommendations In addition to the requirements of this license, it is requested from and strongly recommended of redistributors that: @@ -1016,7 +1362,7 @@ and strongly recommended of redistributors that: CD-ROM expression of an Open Publication-licensed work to its author(s). -### D.6 License options +### D.6 License options The author(s) and/or publisher of an Open Publication-licensed document may elect certain options by appending language to the