diff --git a/book/07-git-tools/1-git-tools.asc b/book/07-git-tools/1-git-tools.asc index 16f389785..b300b5f5c 100644 --- a/book/07-git-tools/1-git-tools.asc +++ b/book/07-git-tools/1-git-tools.asc @@ -21,39 +21,48 @@ Git is smart enough to figure out what commit you meant to type if you provide t For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: - $ git log - commit 734713bc047d87bf7eac9674765ae793478c50d3 - Author: Scott Chacon - Date: Fri Jan 2 18:32:33 2009 -0800 +[source,shell] +---- +$ git log +commit 734713bc047d87bf7eac9674765ae793478c50d3 +Author: Scott Chacon +Date: Fri Jan 2 18:32:33 2009 -0800 - fixed refs handling, added gc auto, updated tests + fixed refs handling, added gc auto, updated tests - commit d921970aadf03b3cf0e71becdaab3147ba71cdef - Merge: 1c002dd... 35cfb2b... - Author: Scott Chacon - Date: Thu Dec 11 15:08:43 2008 -0800 +commit d921970aadf03b3cf0e71becdaab3147ba71cdef +Merge: 1c002dd... 35cfb2b... +Author: Scott Chacon +Date: Thu Dec 11 15:08:43 2008 -0800 - Merge commit 'phedders/rdocs' + Merge commit 'phedders/rdocs' - commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b - Author: Scott Chacon - Date: Thu Dec 11 14:58:32 2008 -0800 +commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b +Author: Scott Chacon +Date: Thu Dec 11 14:58:32 2008 -0800 - added some blame and merge stuff + added some blame and merge stuff +---- In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): - $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b - $ git show 1c002dd4b536e7479f - $ git show 1c002d +[source,shell] +---- +$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b +$ git show 1c002dd4b536e7479f +$ git show 1c002d +---- Git can figure out a short, unique abbreviation for your SHA-1 values. If you pass `--abbrev-commit` to the `git log` command, the output will use shorter values but keep them unique; it defaults to using seven characters but makes them longer if necessary to keep the SHA-1 unambiguous: - $ git log --abbrev-commit --pretty=oneline - ca82a6d changed the version number - 085bb3b removed unnecessary test code - a11bef0 first commit +[source,shell] +---- +$ git log --abbrev-commit --pretty=oneline +ca82a6d changed the version number +085bb3b removed unnecessary test code +a11bef0 first commit +---- Generally, eight to ten characters are more than enough to be unique within a project. One of the largest Git projects, the Linux kernel, is beginning to need 12 characters out of the possible 40 to stay unique. @@ -66,7 +75,7 @@ What then? If you do happen to commit an object that hashes to the same SHA-1 value as a previous object in your repository, Git will see the previous object already in your Git database and assume it was already written. If you try to check out that object again at some point, you’ll always get the data of the first object. - However, you should be aware of how ridiculously unlikely this scenario is. The SHA-1 digest is 20 bytes or 160 bits. The number of randomly hashed objects needed to ensure a 50% probability of a single collision is about 2^80 (the formula for determining collision probability is `p = (n(n-1)/2) * (1/2^160))`. 2^80 is 1.2 x 10^24 or 1 million billion billion. That’s 1,200 times the number of grains of sand on the earth. +However, you should be aware of how ridiculously unlikely this scenario is. The SHA-1 digest is 20 bytes or 160 bits. The number of randomly hashed objects needed to ensure a 50% probability of a single collision is about 2^80 (the formula for determining collision probability is `p = (n(n-1)/2) * (1/2^160))`. 2^80 is 1.2 x 10^24 or 1 million billion billion. That’s 1,200 times the number of grains of sand on the earth. Here’s an example to give you an idea of what it would take to get a SHA-1 collision. If all 6.5 billion humans on Earth were programming, and every second, each one was producing code that was the equivalent of the entire Linux kernel history (1 million Git objects) and pushing it into one enormous Git repository, it would take 5 years until that repository contained enough objects to have a 50% probability of a single SHA-1 object collision. @@ -78,16 +87,22 @@ The most straightforward way to specify a commit requires that it have a branch Then, you can use a branch name in any Git command that expects a commit object or SHA-1 value. For instance, if you want to show the last commit object on a branch, the following commands are equivalent, assuming that the `topic1` branch points to `ca82a6d`: - $ git show ca82a6dff817ec66f44342007202690a93763949 - $ git show topic1 +[source,shell] +---- +$ git show ca82a6dff817ec66f44342007202690a93763949 +$ git show topic1 +---- If you want to see which specific SHA a branch points to, or if you want to see what any of these examples boils down to in terms of SHAs, you can use a Git plumbing tool called `rev-parse`. You can see <<_git_internals>> for more information about plumbing tools; basically, `rev-parse` exists for lower-level operations and isn’t designed to be used in day-to-day operations. However, it can be helpful sometimes when you need to see what’s really going on. Here you can run `rev-parse` on your branch. - $ git rev-parse topic1 - ca82a6dff817ec66f44342007202690a93763949 +[source,shell] +---- +$ git rev-parse topic1 +ca82a6dff817ec66f44342007202690a93763949 +---- ==== RefLog Shortnames @@ -95,47 +110,59 @@ One of the things Git does in the background while you’re working away is keep You can see your reflog by using `git reflog`: - $ git reflog - 734713b... HEAD@{0}: commit: fixed refs handling, added gc auto, updated - d921970... HEAD@{1}: merge phedders/rdocs: Merge made by recursive. - 1c002dd... HEAD@{2}: commit: added some blame and merge stuff - 1c36188... HEAD@{3}: rebase -i (squash): updating HEAD - 95df984... HEAD@{4}: commit: # This is a combination of two commits. - 1c36188... HEAD@{5}: rebase -i (squash): updating HEAD - 7e05da5... HEAD@{6}: rebase -i (pick): updating HEAD +[source,shell] +---- +$ git reflog +734713b... HEAD@{0}: commit: fixed refs handling, added gc auto, updated +d921970... HEAD@{1}: merge phedders/rdocs: Merge made by recursive. +1c002dd... HEAD@{2}: commit: added some blame and merge stuff +1c36188... HEAD@{3}: rebase -i (squash): updating HEAD +95df984... HEAD@{4}: commit: # This is a combination of two commits. +1c36188... HEAD@{5}: rebase -i (squash): updating HEAD +7e05da5... HEAD@{6}: rebase -i (pick): updating HEAD +---- Every time your branch tip is updated for any reason, Git stores that information for you in this temporary history. And you can specify older commits with this data, as well. If you want to see the fifth prior value of the HEAD of your repository, you can use the `@{n}` reference that you see in the reflog output: - $ git show HEAD@{5} +[source,shell] +---- +$ git show HEAD@{5} +---- You can also use this syntax to see where a branch was some specific amount of time ago. For instance, to see where your `master` branch was yesterday, you can type - $ git show master@{yesterday} +[source,shell] +---- +$ git show master@{yesterday} +---- That shows you where the branch tip was yesterday. This technique only works for data that’s still in your reflog, so you can’t use it to look for commits older than a few months. To see reflog information formatted like the `git log` output, you can run `git log -g`: - $ git log -g master - commit 734713bc047d87bf7eac9674765ae793478c50d3 - Reflog: master@{0} (Scott Chacon ) - Reflog message: commit: fixed refs handling, added gc auto, updated - Author: Scott Chacon - Date: Fri Jan 2 18:32:33 2009 -0800 - - fixed refs handling, added gc auto, updated tests - - commit d921970aadf03b3cf0e71becdaab3147ba71cdef - Reflog: master@{1} (Scott Chacon ) - Reflog message: merge phedders/rdocs: Merge made by recursive. - Author: Scott Chacon - Date: Thu Dec 11 15:08:43 2008 -0800 - - Merge commit 'phedders/rdocs' +[source,shell] +---- +$ git log -g master +commit 734713bc047d87bf7eac9674765ae793478c50d3 +Reflog: master@{0} (Scott Chacon ) +Reflog message: commit: fixed refs handling, added gc auto, updated +Author: Scott Chacon +Date: Fri Jan 2 18:32:33 2009 -0800 + + fixed refs handling, added gc auto, updated tests + +commit d921970aadf03b3cf0e71becdaab3147ba71cdef +Reflog: master@{1} (Scott Chacon ) +Reflog message: merge phedders/rdocs: Merge made by recursive. +Author: Scott Chacon +Date: Thu Dec 11 15:08:43 2008 -0800 + + Merge commit 'phedders/rdocs' +---- It’s important to note that the reflog information is strictly local – it’s a log of what you’ve done in your repository. The references won’t be the same on someone else’s copy of the repository; and right after you initially clone a repository, you'll have an empty reflog, as no activity has occurred yet in your repository. @@ -147,43 +174,52 @@ The other main way to specify a commit is via its ancestry. If you place a `^` at the end of a reference, Git resolves it to mean the parent of that commit. Suppose you look at the history of your project: - $ git log --pretty=format:'%h %s' --graph - * 734713b fixed refs handling, added gc auto, updated tests - * d921970 Merge commit 'phedders/rdocs' - |\ - | * 35cfb2b Some rdoc changes - * | 1c002dd added some blame and merge stuff - |/ - * 1c36188 ignore *.gem - * 9b29157 add open3_detach to gemspec file list +[source,shell] +---- +$ git log --pretty=format:'%h %s' --graph +* 734713b fixed refs handling, added gc auto, updated tests +* d921970 Merge commit 'phedders/rdocs' +|\ +| * 35cfb2b Some rdoc changes +* | 1c002dd added some blame and merge stuff +|/ +* 1c36188 ignore *.gem +* 9b29157 add open3_detach to gemspec file list +---- Then, you can see the previous commit by specifying `HEAD^`, which means ``the parent of HEAD'': - $ git show HEAD^ - commit d921970aadf03b3cf0e71becdaab3147ba71cdef - Merge: 1c002dd... 35cfb2b... - Author: Scott Chacon - Date: Thu Dec 11 15:08:43 2008 -0800 +[source,shell] +---- +$ git show HEAD^ +commit d921970aadf03b3cf0e71becdaab3147ba71cdef +Merge: 1c002dd... 35cfb2b... +Author: Scott Chacon +Date: Thu Dec 11 15:08:43 2008 -0800 - Merge commit 'phedders/rdocs' + Merge commit 'phedders/rdocs' +---- You can also specify a number after the `^` – for example, `d921970^2` means ``the second parent of d921970.'' This syntax is only useful for merge commits, which have more than one parent. The first parent is the branch you were on when you merged, and the second is the commit on the branch that you merged in: - $ git show d921970^ - commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b - Author: Scott Chacon - Date: Thu Dec 11 14:58:32 2008 -0800 +[source,shell] +---- +$ git show d921970^ +commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b +Author: Scott Chacon +Date: Thu Dec 11 14:58:32 2008 -0800 - added some blame and merge stuff + added some blame and merge stuff - $ git show d921970^2 - commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 - Author: Paul Hedderly - Date: Wed Dec 10 22:22:03 2008 +0000 +$ git show d921970^2 +commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 +Author: Paul Hedderly +Date: Wed Dec 10 22:22:03 2008 +0000 - Some rdoc changes + Some rdoc changes +---- The other main ancestry specification is the `~`. This also refers to the first parent, so `HEAD~` and `HEAD^` are equivalent. @@ -191,21 +227,27 @@ The difference becomes apparent when you specify a number. `HEAD~2` means ``the first parent of the first parent,'' or ``the grandparent'' – it traverses the first parents the number of times you specify. For example, in the history listed earlier, `HEAD~3` would be - $ git show HEAD~3 - commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d - Author: Tom Preston-Werner - Date: Fri Nov 7 13:47:59 2008 -0500 +[source,shell] +---- +$ git show HEAD~3 +commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d +Author: Tom Preston-Werner +Date: Fri Nov 7 13:47:59 2008 -0500 - ignore *.gem + ignore *.gem +---- This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: - $ git show HEAD^^^ - commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d - Author: Tom Preston-Werner - Date: Fri Nov 7 13:47:59 2008 -0500 +[source,shell] +---- +$ git show HEAD^^^ +commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d +Author: Tom Preston-Werner +Date: Fri Nov 7 13:47:59 2008 -0500 - ignore *.gem + ignore *.gem +---- You can also combine these syntaxes – you can get the second parent of the previous reference (assuming it was a merge commit) by using `HEAD~3^2`, and so on. @@ -228,21 +270,30 @@ You want to see what is in your experiment branch that hasn’t yet been merged You can ask Git to show you a log of just those commits with `master..experiment` – that means ``all commits reachable by experiment that aren’t reachable by master.'' For the sake of brevity and clarity in these examples, I’ll use the letters of the commit objects from the diagram in place of the actual log output in the order that they would display: - $ git log master..experiment - D - C +[source,shell] +---- +$ git log master..experiment +D +C +---- If, on the other hand, you want to see the opposite – all commits in `master` that aren’t in `experiment` – you can reverse the branch names. `experiment..master` shows you everything in `master` not reachable from `experiment`: - $ git log experiment..master - F - E +[source,shell] +---- +$ git log experiment..master +F +E +---- This is useful if you want to keep the `experiment` branch up to date and preview what you’re about to merge in. Another very frequent use of this syntax is to see what you’re about to push to a remote: - $ git log origin/master..HEAD +[source,shell] +---- +$ git log origin/master..HEAD +---- This command shows you any commits in your current branch that aren’t in the `master` branch on your `origin` remote. If you run a `git push` and your current branch is tracking `origin/master`, the commits listed by `git log origin/master..HEAD` are the commits that will be transferred to the server. @@ -255,15 +306,21 @@ The double-dot syntax is useful as a shorthand; but perhaps you want to specify Git allows you to do this by using either the `^` character or `--not` before any reference from which you don’t want to see reachable commits. Thus these three commands are equivalent: - $ git log refA..refB - $ git log ^refA refB - $ git log refB --not refA +[source,shell] +---- +$ git log refA..refB +$ git log ^refA refB +$ git log refB --not refA +---- This is nice because with this syntax you can specify more than two references in your query, which you cannot do with the double-dot syntax. For instance, if you want to see all commits that are reachable from `refA` or `refB` but not from `refC`, you can type one of these: - $ git log refA refB ^refC - $ git log refA refB --not refC +[source,shell] +---- +$ git log refA refB ^refC +$ git log refA refB --not refC +---- This makes for a very powerful revision query system that should help you figure out what is in your branches. @@ -273,22 +330,28 @@ The last major range-selection syntax is the triple-dot syntax, which specifies Look back at the example commit history in Figure 6-1. If you want to see what is in `master` or `experiment` but not any common references, you can run - $ git log master...experiment - F - E - D - C +[source,shell] +---- +$ git log master...experiment +F +E +D +C +---- Again, this gives you normal `log` output but shows you only the commit information for those four commits, appearing in the traditional commit date ordering. A common switch to use with the `log` command in this case is `--left-right`, which shows you which side of the range each commit is in. This helps make the data more useful: - $ git log --left-right master...experiment - < F - < E - > D - > C +[source,shell] +---- +$ git log --left-right master...experiment +< F +< E +> D +> C +---- With these tools, you can much more easily let Git know what commit or commits you want to inspect. @@ -300,16 +363,19 @@ These tools are very helpful if you modify a bunch of files and then decide that This way, you can make sure your commits are logically separate changesets and can be easily reviewed by the developers working with you. If you run `git add` with the `-i` or `--interactive` option, Git goes into an interactive shell mode, displaying something like this: - $ git add -i - staged unstaged path - 1: unchanged +0/-1 TODO - 2: unchanged +1/-1 index.html - 3: unchanged +5/-1 lib/simplegit.rb - - *** Commands *** - 1: status 2: update 3: revert 4: add untracked - 5: patch 6: diff 7: quit 8: help - What now> +[source,shell] +---- +$ git add -i + staged unstaged path + 1: unchanged +0/-1 TODO + 2: unchanged +1/-1 index.html + 3: unchanged +5/-1 lib/simplegit.rb + +*** Commands *** + 1: status 2: update 3: revert 4: add untracked + 5: patch 6: diff 7: quit 8: help +What now> +---- You can see that this command shows you a much different view of your staging area – basically the same information you get with `git status` but a bit more succinct and informative. It lists the changes you’ve staged on the left and unstaged changes on the right. @@ -321,90 +387,108 @@ Here you can do a number of things, including staging files, unstaging files, st If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: - What now> 2 - staged unstaged path - 1: unchanged +0/-1 TODO - 2: unchanged +1/-1 index.html - 3: unchanged +5/-1 lib/simplegit.rb - Update>> +[source,shell] +---- +What now> 2 + staged unstaged path + 1: unchanged +0/-1 TODO + 2: unchanged +1/-1 index.html + 3: unchanged +5/-1 lib/simplegit.rb +Update>> +---- To stage the TODO and index.html files, you can type the numbers: - Update>> 1,2 - staged unstaged path - * 1: unchanged +0/-1 TODO - * 2: unchanged +1/-1 index.html - 3: unchanged +5/-1 lib/simplegit.rb - Update>> +[source,shell] +---- +Update>> 1,2 + staged unstaged path +* 1: unchanged +0/-1 TODO +* 2: unchanged +1/-1 index.html + 3: unchanged +5/-1 lib/simplegit.rb +Update>> +---- The `*` next to each file means the file is selected to be staged. If you press Enter after typing nothing at the `Update>>` prompt, Git takes anything selected and stages it for you: - Update>> - updated 2 paths - - *** Commands *** - 1: status 2: update 3: revert 4: add untracked - 5: patch 6: diff 7: quit 8: help - What now> 1 - staged unstaged path - 1: +0/-1 nothing TODO - 2: +1/-1 nothing index.html - 3: unchanged +5/-1 lib/simplegit.rb +[source,shell] +---- +Update>> +updated 2 paths + +*** Commands *** + 1: status 2: update 3: revert 4: add untracked + 5: patch 6: diff 7: quit 8: help +What now> 1 + staged unstaged path + 1: +0/-1 nothing TODO + 2: +1/-1 nothing index.html + 3: unchanged +5/-1 lib/simplegit.rb +---- Now you can see that the TODO and index.html files are staged and the simplegit.rb file is still unstaged. If you want to unstage the TODO file at this point, you use the `3` or `r` (for revert) option: - *** Commands *** - 1: status 2: update 3: revert 4: add untracked - 5: patch 6: diff 7: quit 8: help - What now> 3 - staged unstaged path - 1: +0/-1 nothing TODO - 2: +1/-1 nothing index.html - 3: unchanged +5/-1 lib/simplegit.rb - Revert>> 1 - staged unstaged path - * 1: +0/-1 nothing TODO - 2: +1/-1 nothing index.html - 3: unchanged +5/-1 lib/simplegit.rb - Revert>> [enter] - reverted one path +[source,shell] +---- +*** Commands *** + 1: status 2: update 3: revert 4: add untracked + 5: patch 6: diff 7: quit 8: help +What now> 3 + staged unstaged path + 1: +0/-1 nothing TODO + 2: +1/-1 nothing index.html + 3: unchanged +5/-1 lib/simplegit.rb +Revert>> 1 + staged unstaged path +* 1: +0/-1 nothing TODO + 2: +1/-1 nothing index.html + 3: unchanged +5/-1 lib/simplegit.rb +Revert>> [enter] +reverted one path +---- Looking at your Git status again, you can see that you’ve unstaged the TODO file: - *** Commands *** - 1: status 2: update 3: revert 4: add untracked - 5: patch 6: diff 7: quit 8: help - What now> 1 - staged unstaged path - 1: unchanged +0/-1 TODO - 2: +1/-1 nothing index.html - 3: unchanged +5/-1 lib/simplegit.rb +[source,shell] +---- +*** Commands *** + 1: status 2: update 3: revert 4: add untracked + 5: patch 6: diff 7: quit 8: help +What now> 1 + staged unstaged path + 1: unchanged +0/-1 TODO + 2: +1/-1 nothing index.html + 3: unchanged +5/-1 lib/simplegit.rb +---- To see the diff of what you’ve staged, you can use the `6` or `d` (for diff) command. It shows you a list of your staged files, and you can select the ones for which you would like to see the staged diff. This is much like specifying `git diff --cached` on the command line: - *** Commands *** - 1: status 2: update 3: revert 4: add untracked - 5: patch 6: diff 7: quit 8: help - What now> 6 - staged unstaged path - 1: +1/-1 nothing index.html - Review diff>> 1 - diff --git a/index.html b/index.html - index 4d07108..4335f49 100644 - --- a/index.html - +++ b/index.html - @@ -16,7 +16,7 @@ Date Finder - -

...

- - - - + - -