-
1. Começando
- 1.1 Sobre Controle de VersĂŁo
- 1.2 Uma Breve HistĂłria do Git
- 1.3 O BĂĄsico do Git
- 1.4 A Linha de Comando
- 1.5 Instalando o Git
- 1.6 Configuração Inicial do Git
- 1.7 Pedindo Ajuda
- 1.8 SumĂĄrio
-
2. Fundamentos de Git
-
3. Branches no Git
-
4. Git no servidor
- 4.1 Os Protocolos
- 4.2 Getting Git on a Server
- 4.3 Gerando Sua Chave PĂșblica SSH
- 4.4 Setting Up the Server
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Third Party Hosted Options
- 4.10 SumĂĄrio
-
5. Distributed Git
-
6. GitHub
- 6.1 Configurando uma conta
- 6.2 Contribuindo em um projeto
- 6.3 Maintaining a Project
- 6.4 Managing an organization
- 6.5 Scripting GitHub
- 6.6 Summary
-
7. Git Tools
- 7.1 Revision Selection
- 7.2 Interactive Staging
- 7.3 Stashing and Cleaning
- 7.4 Signing Your Work
- 7.5 Searching
- 7.6 Rewriting History
- 7.7 Reset Demystified
- 7.8 Advanced Merging
- 7.9 Rerere
- 7.10 Debugging with Git
- 7.11 Submodules
- 7.12 Bundling
- 7.13 Replace
- 7.14 Credential Storage
- 7.15 Summary
-
8. Customizing Git
- 8.1 Git Configuration
- 8.2 Git Attributes
- 8.3 Git Hooks
- 8.4 An Example Git-Enforced Policy
- 8.5 Summary
-
9. Git and Other Systems
- 9.1 Git as a Client
- 9.2 Migrating to Git
- 9.3 Summary
-
10. Funcionamento Interno do Git
- 10.1 Encanamento e Porcelana
- 10.2 Objetos do Git
- 10.3 ReferĂȘncias do Git
- 10.4 Packfiles
- 10.5 The Refspec
- 10.6 Transfer Protocols
- 10.7 Maintenance and Data Recovery
- 10.8 VariĂĄveis de ambiente
- 10.9 SumĂĄrio
-
A1. Appendix A: Git em Outros Ambientes
- A1.1 Graphical Interfaces
- A1.2 Git in Visual Studio
- A1.3 Git in Eclipse
- A1.4 Git in Bash
- A1.5 Git in Zsh
- A1.6 Git in Powershell
- A1.7 Resumo
-
A2. Appendix B: Embedding Git in your Applications
- A2.1 Command-line Git
- A2.2 Libgit2
- A2.3 JGit
-
A3. Appendix C: Git Commands
- A3.1 Setup and Config
- A3.2 Getting and Creating Projects
- A3.3 Basic Snapshotting
- A3.4 Branching and Merging
- A3.5 Sharing and Updating Projects
- A3.6 Inspection and Comparison
- A3.7 Debugging
- A3.8 Patching
- A3.9 Email
- A3.10 External Systems
- A3.11 Administration
- A3.12 Plumbing Commands
7.1 Git Tools - Revision Selection
By now, youâve learned most of the day-to-day commands and workflows that you need to manage or maintain a Git repository for your source code control. Youâve accomplished the basic tasks of tracking and committing files, and youâve harnessed the power of the staging area and lightweight topic branching and merging.
Now youâll explore a number of very powerful things that Git can do that you may not necessarily use on a day-to-day basis but that you may need at some point.
Revision Selection
Git allows you to specify specific commits or a range of commits in several ways. They arenât necessarily obvious but are helpful to know.
Single Revisions
You can obviously refer to a commit by the SHA-1 hash that itâs given, but there are more human-friendly ways to refer to commits as well. This section outlines the various ways you can refer to a single commit.
Short SHA-1
Git is smart enough to figure out what commit you meant to type if you provide the first few characters, as long as your partial SHA-1 is at least four characters long and unambiguous â that is, only one object in the current repository begins with that partial SHA-1.
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 <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
fixed refs handling, added gc auto, updated tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
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
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
Generally, eight to ten characters are more than enough to be unique within a project.
As an example, the Linux kernel, which is a pretty large project with over 450k commits and 3.6 million objects, has no two objects whose SHA-1s overlap more than the first 11 characters.
Note
|
A SHORT NOTE ABOUT SHA-1
A lot of people become concerned at some point that they will, by random happenstance, have two objects in their repository that hash to the same SHA-1 value. 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 280
(the formula for determining collision probability is 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 (3.6 million Git objects) and pushing it into one enormous Git repository, it would take roughly 2 years until that repository contained enough objects to have a 50% probability of a single SHA-1 object collision. A higher probability exists that every member of your programming team will be attacked and killed by wolves in unrelated incidents on the same night. |
Branch References
The most straightforward way to specify a commit requires that it has a branch reference pointed at it.
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
If you want to see which specific SHA-1 a branch points to, or if you want to see what any of these examples boils down to in terms of SHA-1s, you can use a Git plumbing tool called rev-parse
.
You can see [ch10-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
RefLog Shortnames
One of the things Git does in the background while youâre working away is keep a âreflogâ â a log of where your HEAD and branch references have been for the last few months.
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
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}
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}
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 <schacon@gmail.com>)
Reflog message: commit: fixed refs handling, added gc auto, updated
Author: Scott Chacon <schacon@gmail.com>
Date: Fri Jan 2 18:32:33 2009 -0800
fixed refs handling, added gc auto, updated tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon <schacon@gmail.com>)
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon <schacon@gmail.com>
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.
Running git show HEAD@{2.months.ago}
will work only if you cloned the project at least two months ago â if you cloned it five minutes ago, youâll get no results.
Ancestry References
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
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 <schacon@gmail.com>
Date: Thu Dec 11 15:08:43 2008 -0800
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 <schacon@gmail.com>
Date: Thu Dec 11 14:58:32 2008 -0800
added some blame and merge stuff
$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly <paul+git@mjr.org>
Date: Wed Dec 10 22:22:03 2008 +0000
Some rdoc changes
The other main ancestry specification is the ~
.
This also refers to the first parent, so HEAD~
and HEAD^
are equivalent.
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 <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
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 <tom@mojombo.com>
Date: Fri Nov 7 13:47:59 2008 -0500
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.
Commit Ranges
Now that you can specify individual commits, letâs see how to specify ranges of commits. This is particularly useful for managing your branches â if you have a lot of branches, you can use range specifications to answer questions such as, âWhat work is on this branch that I havenât yet merged into my main branch?â
Double Dot
The most common range specification is the double-dot syntax. This basically asks Git to resolve a range of commits that are reachable from one commit but arenât reachable from another. For example, say you have a commit history that looks like Example history for range selection..
You want to see what is in your experiment branch that hasnât yet been merged into your master branch.
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
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
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
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.
You can also leave off one side of the syntax to have Git assume HEAD.
For example, you can get the same results as in the previous example by typing git log origin/master..
â Git substitutes HEAD if one side is missing.
Multiple Points
The double-dot syntax is useful as a shorthand; but perhaps you want to specify more than two branches to indicate your revision, such as seeing what commits are in any of several branches that arenât in the branch youâre currently on.
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
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
This makes for a very powerful revision query system that should help you figure out what is in your branches.
Triple Dot
The last major range-selection syntax is the triple-dot syntax, which specifies all the commits that are reachable by either of two references but not by both of them.
Look back at the example commit history in Example history for range selection..
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
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
With these tools, you can much more easily let Git know what commit or commits you want to inspect.