From 44d0c1f81ac57725e18f56431bf37609a68778b2 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Fri, 28 Sep 2007 00:23:57 -0700 Subject: [PATCH] Port section 2.7 (recording changes) from mercurial to git --- tour.mdwn | 362 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 254 insertions(+), 108 deletions(-) diff --git a/tour.mdwn b/tour.mdwn index 243737b..7ff65f6 100644 --- a/tour.mdwn +++ b/tour.mdwn @@ -605,120 +605,178 @@ this, we use the “git diff” command. return 0; } -### 2.7 Recording changes in a new changeset +### 2.7 Recording changes in a new commit -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 +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. +record our work in a new commit. -The “hg commit” command lets us create a new changeset; we’ll usually +The “git 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. +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): + + 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 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. +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. #### 2.7.2 Writing a commit message -When we commit a change, Mercurial drops us into a text editor, to +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 + $ git commit -a + +Note: The -a on the command-line instructs git to commit all changes +to tracked files. Without this, "git commit" will only commit changes +that have been previously staged for committing with "git add +file". The most common usage is to commit with "git commit -a" and +only use "git add file; git commit" when there is a need to commit +only some subset of changes that have been made. -The editor that the “hg commit” command drops us into will contain an -empty line, followed by a number of lines starting with “HG:”. +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 - HG: changed hello.c + 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 + # -Mercurial ignores the lines that start with “HG:”; it uses them only +git ignores the lines that start with “#”; it uses them only to tell us which files it’s recording changes to. Modifying or deleting these lines has no effect. #### 2.7.3 Writing a good commit message -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 +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. + +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. + +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 -p". #### 2.7.4 Aborting a commit @@ -727,41 +785,129 @@ 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. +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): - $ 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 + $ git show + commit 018cfb742be6176443ffddac454e593e802ddf3e + Author: Carl Worth + Date: Thu Sep 27 23:55:00 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... - 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; - } + diff --git a/hello.c b/hello.c + index 9a3ff79..6d28887 100644 + --- a/hello.c + +++ b/hello.c + @@ -8,5 +8,6 @@ + int main(int argc, char **argv) + { + printf("hello, world!\"); + + printf("hello again!\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 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. + +So fix that typo, (a missing 'n' between the '\' and the '"'), with +your editor or with something like this: + + sed -i 's/\\"/\\n"/' hello.c + +And then you can just amend the previous commit rather than creating a +new one with the --amend option to "git commit": + + $ 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: + + $ git show + commit 839b58d021c618bd0e1d336d4d5878a0082672e6 + Author: Carl Worth + Date: Thu Sep 27 23:55:00 2007 -0700 + + Added an extra line of output and fixed the typo bug. + diff --git a/hello.c b/hello.c + index 9a3ff79..ca750e0 100644 + --- a/hello.c + +++ b/hello.c + @@ -7,6 +7,7 @@ + + int main(int argc, char **argv) + { + - printf("hello, world!\"); + + printf("hello, world!\n"); + + printf("hello again!\n"); + return 0; + } -We refer to the newest revision in the repository as the tip revision, -or simply the tip. +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] ### 2.8 Sharing changes -- 2.43.0