diff --git a/book/07-git-tools/sections/diff-debugging.htm b/book/07-git-tools/sections/diff-debugging.htm deleted file mode 100644 index 794433c5..00000000 --- a/book/07-git-tools/sections/diff-debugging.htm +++ /dev/null @@ -1,4815 +0,0 @@ - - -
- -C:\Users\15625\Documents\Git\progit\en\06-git-tools\01-chapter6.markdown | -C:\Users\15625\Documents\Git\progit2-ja\book\07-git-tools\sections\debugging.asc | -||
---|---|---|---|
.1 | # Git Tools # |
- |
-|
2 | |
- |
-|
3 | 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. |
- |
-|
4 | |
- |
-|
5 | 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. |
- |
-|
6 | |
- |
-|
7 | ## Revision Selection ## |
- |
-|
8 | |
- |
-|
9 | 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. |
- |
-|
10 | |
- |
-|
11 | ### Single Revisions ### |
- |
-|
12 | |
- |
-|
13 | 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. |
- |
-|
14 | |
- |
-|
15 | ### Short SHA ### |
- |
-|
16 | |
- |
-|
17 | 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. |
- |
-|
18 | |
- |
-|
19 | For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: |
- |
-|
20 | |
- |
-|
21 | $ git log |
- |
-|
22 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
23 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
24 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
25 | |
- |
-|
26 | fixed refs handling, added gc auto, updated tests |
- |
-|
27 | |
- |
-|
28 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
29 | Merge: 1c002dd... 35cfb2b... |
- |
-|
30 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
31 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
32 | |
- |
-|
33 | Merge commit 'phedders/rdocs' |
- |
-|
34 | |
- |
-|
35 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
36 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
37 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
38 | |
- |
-|
39 | added some blame and merge stuff |
- |
-|
40 | |
- |
-|
41 | In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): |
- |
-|
42 | |
- |
-|
43 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
44 | $ git show 1c002dd4b536e7479f |
- |
-|
45 | $ git show 1c002d |
- |
-|
46 | |
- |
-|
47 | 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: |
- |
-|
48 | |
- |
-|
49 | $ git log --abbrev-commit --pretty=oneline |
- |
-|
50 | ca82a6d changed the version number |
- |
-|
51 | 085bb3b removed unnecessary test code |
- |
-|
52 | a11bef0 first commit |
- |
-|
53 | |
- |
-|
54 | 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. |
- |
-|
55 | |
- |
-|
56 | ### A SHORT NOTE ABOUT SHA-1 ### |
- |
-|
57 | |
- |
-|
58 | 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? |
- |
-|
59 | |
- |
-|
60 | 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. |
- |
-|
61 | |
- |
-|
62 | 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. |
- |
-|
63 | |
- |
-|
64 | 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. 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. |
- |
-|
65 | |
- |
-|
66 | ### Branch References ### |
- |
-|
67 | |
- |
-|
68 | The most straightforward way to specify a commit requires that it have 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`: |
- |
-|
69 | |
- |
-|
70 | $ git show ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
71 | $ git show topic1 |
- |
-|
72 | |
- |
-|
73 | 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 Chapter 9 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. |
- |
-|
74 | |
- |
-|
75 | $ git rev-parse topic1 |
- |
-|
76 | ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
77 | |
- |
-|
78 | ### RefLog Shortnames ### |
- |
-|
79 | |
- |
-|
80 | 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. |
- |
-|
81 | |
- |
-|
82 | You can see your reflog by using `git reflog`: |
- |
-|
83 | |
- |
-|
84 | $ git reflog |
- |
-|
85 | 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated |
- |
-|
86 | d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. |
- |
-|
87 | 1c002dd HEAD@{2}: commit: added some blame and merge stuff |
- |
-|
88 | 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD |
- |
-|
89 | 95df984 HEAD@{4}: commit: # This is a combination of two commits. |
- |
-|
90 | 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD |
- |
-|
91 | 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD |
- |
-|
92 | |
- |
-|
93 | 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: |
- |
-|
94 | |
- |
-|
95 | $ git show HEAD@{5} |
- |
-|
96 | |
- |
-|
97 | 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 |
- |
-|
98 | |
- |
-|
99 | $ git show master@{yesterday} |
- |
-|
100 | |
- |
-|
101 | 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. |
- |
-|
102 | |
- |
-|
103 | To see reflog information formatted like the `git log` output, you can run `git log -g`: |
- |
-|
104 | |
- |
-|
105 | $ git log -g master |
- |
-|
106 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
107 | Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) |
- |
-|
108 | Reflog message: commit: fixed refs handling, added gc auto, updated |
- |
-|
109 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
110 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
111 | |
- |
-|
112 | fixed refs handling, added gc auto, updated tests |
- |
-|
113 | |
- |
-|
114 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
115 | Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) |
- |
-|
116 | Reflog message: merge phedders/rdocs: Merge made by recursive. |
- |
-|
117 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
118 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
119 | |
- |
-|
120 | Merge commit 'phedders/rdocs' |
- |
-|
121 | |
- |
-|
122 | 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. |
- |
-|
123 | |
- |
-|
124 | ### Ancestry References ### |
- |
-|
125 | |
- |
-|
126 | 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. |
- |
-|
127 | Suppose you look at the history of your project: |
- |
-|
128 | |
- |
-|
129 | $ git log --pretty=format:'%h %s' --graph |
- |
-|
130 | * 734713b fixed refs handling, added gc auto, updated tests |
- |
-|
131 | * d921970 Merge commit 'phedders/rdocs' |
- |
-|
132 | |\ |
- |
-|
133 | | * 35cfb2b Some rdoc changes |
- |
-|
134 | * | 1c002dd added some blame and merge stuff |
- |
-|
135 | |/ |
- |
-|
136 | * 1c36188 ignore *.gem |
- |
-|
137 | * 9b29157 add open3_detach to gemspec file list |
- |
-|
138 | |
- |
-|
139 | Then, you can see the previous commit by specifying `HEAD^`, which means "the parent of HEAD": |
- |
-|
140 | |
- |
-|
141 | $ git show HEAD^ |
- |
-|
142 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
143 | Merge: 1c002dd... 35cfb2b... |
- |
-|
144 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
145 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
146 | |
- |
-|
147 | Merge commit 'phedders/rdocs' |
- |
-|
148 | |
- |
-|
149 | 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: |
- |
-|
150 | |
- |
-|
151 | $ git show d921970^ |
- |
-|
152 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
153 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
154 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
155 | |
- |
-|
156 | added some blame and merge stuff |
- |
-|
157 | |
- |
-|
158 | $ git show d921970^2 |
- |
-|
159 | commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 |
- |
-|
160 | Author: Paul Hedderly <paul+git@mjr.org> |
- |
-|
161 | Date: Wed Dec 10 22:22:03 2008 +0000 |
- |
-|
162 | |
- |
-|
163 | Some rdoc changes |
- |
-|
164 | |
- |
-|
165 | 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 |
- |
-|
166 | |
- |
-|
167 | $ git show HEAD~3 |
- |
-|
168 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
169 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
170 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
171 | |
- |
-|
172 | ignore *.gem |
- |
-|
173 | |
- |
-|
174 | This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: |
- |
-|
175 | |
- |
-|
176 | $ git show HEAD^^^ |
- |
-|
177 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
178 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
179 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
180 | |
- |
-|
181 | ignore *.gem |
- |
-|
182 | |
- |
-|
183 | 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. |
- |
-|
184 | |
- |
-|
185 | ### Commit Ranges ### |
- |
-|
186 | |
- |
-|
187 | 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?" |
- |
-|
188 | |
- |
-|
189 | #### Double Dot #### |
- |
-|
190 | |
- |
-|
191 | 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 Figure 6-1. |
- |
-|
192 | |
- |
-|
193 | Insert 18333fig0601.png |
- |
-|
194 | Figure 6-1. Example history for range selection. |
- |
-|
195 | |
- |
-|
196 | 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: |
- |
-|
197 | |
- |
-|
198 | $ git log master..experiment |
- |
-|
199 | D |
- |
-|
200 | C |
- |
-|
201 | |
- |
-|
202 | 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`: |
- |
-|
203 | |
- |
-|
204 | $ git log experiment..master |
- |
-|
205 | F |
- |
-|
206 | E |
- |
-|
207 | |
- |
-|
208 | 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: |
- |
-|
209 | |
- |
-|
210 | $ git log origin/master..HEAD |
- |
-|
211 | |
- |
-|
212 | 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. |
- |
-|
213 | 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. |
- |
-|
214 | |
- |
-|
215 | #### Multiple Points #### |
- |
-|
216 | |
- |
-|
217 | 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: |
- |
-|
218 | |
- |
-|
219 | $ git log refA..refB |
- |
-|
220 | $ git log ^refA refB |
- |
-|
221 | $ git log refB --not refA |
- |
-|
222 | |
- |
-|
223 | 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: |
- |
-|
224 | |
- |
-|
225 | $ git log refA refB ^refC |
- |
-|
226 | $ git log refA refB --not refC |
- |
-|
227 | |
- |
-|
228 | This makes for a very powerful revision query system that should help you figure out what is in your branches. |
- |
-|
229 | |
- |
-|
230 | #### Triple Dot #### |
- |
-|
231 | |
- |
-|
232 | 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 Figure 6-1. |
- |
-|
233 | If you want to see what is in `master` or `experiment` but not any common references, you can run |
- |
-|
234 | |
- |
-|
235 | $ git log master...experiment |
- |
-|
236 | F |
- |
-|
237 | E |
- |
-|
238 | D |
- |
-|
239 | C |
- |
-|
240 | |
- |
-|
241 | 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. |
- |
-|
242 | |
- |
-|
243 | 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: |
- |
-|
244 | |
- |
-|
245 | $ git log --left-right master...experiment |
- |
-|
246 | < F |
- |
-|
247 | < E |
- |
-|
248 | > D |
- |
-|
249 | > C |
- |
-|
250 | |
- |
-|
251 | With these tools, you can much more easily let Git know what commit or commits you want to inspect. |
- |
-|
252 | |
- |
-|
253 | ## Interactive Staging ## |
- |
-|
254 | |
- |
-|
255 | Git comes with a couple of scripts that make some command-line tasks easier. Here, you’ll look at a few interactive commands that can help you easily craft your commits to include only certain combinations and parts of files. These tools are very helpful if you modify a bunch of files and then decide that you want those changes to be in several focused commits rather than one big messy commit. This way, you can make sure your commits are logically separate changesets and can be easily reviewed by the developers working with you. |
- |
-|
256 | If you run `git add` with the `-i` or `--interactive` option, Git goes into an interactive shell mode, displaying something like this: |
- |
-|
257 | |
- |
-|
258 | $ git add -i |
- |
-|
259 | staged unstaged path |
- |
-|
260 | 1: unchanged +0/-1 TODO |
- |
-|
261 | 2: unchanged +1/-1 index.html |
- |
-|
262 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
263 | |
- |
-|
264 | *** Commands *** |
- |
-|
265 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
266 | 5: patch 6: diff 7: quit 8: help |
- |
-|
267 | What now> |
- |
-|
268 | |
- |
-|
269 | 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. |
- |
-|
270 | |
- |
-|
271 | After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. |
- |
-|
272 | |
- |
-|
273 | ### Staging and Unstaging Files ### |
- |
-|
274 | |
- |
-|
275 | If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: |
- |
-|
276 | |
- |
-|
277 | What now> 2 |
- |
-|
278 | staged unstaged path |
- |
-|
279 | 1: unchanged +0/-1 TODO |
- |
-|
280 | 2: unchanged +1/-1 index.html |
- |
-|
281 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
282 | Update>> |
- |
-|
283 | |
- |
-|
284 | To stage the TODO and index.html files, you can type the numbers: |
- |
-|
285 | |
- |
-|
286 | Update>> 1,2 |
- |
-|
287 | staged unstaged path |
- |
-|
288 | * 1: unchanged +0/-1 TODO |
- |
-|
289 | * 2: unchanged +1/-1 index.html |
- |
-|
290 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
291 | Update>> |
- |
-|
292 | |
- |
-|
293 | 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: |
- |
-|
294 | |
- |
-|
295 | Update>> |
- |
-|
296 | updated 2 paths |
- |
-|
297 | |
- |
-|
298 | *** Commands *** |
- |
-|
299 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
300 | 5: patch 6: diff 7: quit 8: help |
- |
-|
301 | What now> 1 |
- |
-|
302 | staged unstaged path |
- |
-|
303 | 1: +0/-1 nothing TODO |
- |
-|
304 | 2: +1/-1 nothing index.html |
- |
-|
305 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
306 | |
- |
-|
307 | 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: |
- |
-|
308 | |
- |
-|
309 | *** Commands *** |
- |
-|
310 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
311 | 5: patch 6: diff 7: quit 8: help |
- |
-|
312 | What now> 3 |
- |
-|
313 | staged unstaged path |
- |
-|
314 | 1: +0/-1 nothing TODO |
- |
-|
315 | 2: +1/-1 nothing index.html |
- |
-|
316 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
317 | Revert>> 1 |
- |
-|
318 | staged unstaged path |
- |
-|
319 | * 1: +0/-1 nothing TODO |
- |
-|
320 | 2: +1/-1 nothing index.html |
- |
-|
321 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
322 | Revert>> [enter] |
- |
-|
323 | reverted one path |
- |
-|
324 | |
- |
-|
325 | Looking at your Git status again, you can see that you’ve unstaged the TODO file: |
- |
-|
326 | |
- |
-|
327 | *** Commands *** |
- |
-|
328 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
329 | 5: patch 6: diff 7: quit 8: help |
- |
-|
330 | What now> 1 |
- |
-|
331 | staged unstaged path |
- |
-|
332 | 1: unchanged +0/-1 TODO |
- |
-|
333 | 2: +1/-1 nothing index.html |
- |
-|
334 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
335 | |
- |
-|
336 | 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: |
- |
-|
337 | |
- |
-|
338 | *** Commands *** |
- |
-|
339 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
340 | 5: patch 6: diff 7: quit 8: help |
- |
-|
341 | What now> 6 |
- |
-|
342 | staged unstaged path |
- |
-|
343 | 1: +1/-1 nothing index.html |
- |
-|
344 | Review diff>> 1 |
- |
-|
345 | diff --git a/index.html b/index.html |
- |
-|
346 | index 4d07108..4335f49 100644 |
- |
-|
347 | --- a/index.html |
- |
-|
348 | +++ b/index.html |
- |
-|
349 | @@ -16,7 +16,7 @@ Date Finder |
- |
-|
350 | |
- |
-|
351 | <p id="out">...</p> |
- |
-|
352 | |
- |
-|
353 | -<div id="footer">contact : support@github.com</div> |
- |
-|
354 | +<div id="footer">contact : email.support@github.com</div> |
- |
-|
355 | |
- |
-|
356 | <script type="text/javascript"> |
- |
-|
357 | |
- |
-|
358 | With these basic commands, you can use the interactive add mode to deal with your staging area a little more easily. |
- |
-|
359 | |
- |
-|
360 | ### Staging Patches ### |
- |
-|
361 | |
- |
-|
362 | It’s also possible for Git to stage certain parts of files and not the rest. For example, if you make two changes to your simplegit.rb file and want to stage one of them and not the other, doing so is very easy in Git. From the interactive prompt, type `5` or `p` (for patch). Git will ask you which files you would like to partially stage; then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one: |
- |
-|
363 | |
- |
-|
364 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
- |
-|
365 | index dd5ecc4..57399e0 100644 |
- |
-|
366 | --- a/lib/simplegit.rb |
- |
-|
367 | +++ b/lib/simplegit.rb |
- |
-|
368 | @@ -22,7 +22,7 @@ class SimpleGit |
- |
-|
369 | end |
- |
-|
370 | |
- |
-|
371 | def log(treeish = 'master') |
- |
-|
372 | - command("git log -n 25 #{treeish}") |
- |
-|
373 | + command("git log -n 30 #{treeish}") |
- |
-|
374 | end |
- |
-|
375 | |
- |
-|
376 | def blame(path) |
- |
-|
377 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? |
- |
-|
378 | |
- |
-|
379 | You have a lot of options at this point. Typing `?` shows a list of what you can do: |
- |
-|
380 | |
- |
-|
381 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? |
- |
-|
382 | y - stage this hunk |
- |
-|
383 | n - do not stage this hunk |
- |
-|
384 | a - stage this and all the remaining hunks in the file |
- |
-|
385 | d - do not stage this hunk nor any of the remaining hunks in the file |
- |
-|
386 | g - select a hunk to go to |
- |
-|
387 | / - search for a hunk matching the given regex |
- |
-|
388 | j - leave this hunk undecided, see next undecided hunk |
- |
-|
389 | J - leave this hunk undecided, see next hunk |
- |
-|
390 | k - leave this hunk undecided, see previous undecided hunk |
- |
-|
391 | K - leave this hunk undecided, see previous hunk |
- |
-|
392 | s - split the current hunk into smaller hunks |
- |
-|
393 | e - manually edit the current hunk |
- |
-|
394 | ? - print help |
- |
-|
395 | |
- |
-|
396 | Generally, you’ll type `y` or `n` if you want to stage each hunk, but staging all of them in certain files or skipping a hunk decision until later can be helpful too. If you stage one part of the file and leave another part unstaged, your status output will look like this: |
- |
-|
397 | |
- |
-|
398 | What now> 1 |
- |
-|
399 | staged unstaged path |
- |
-|
400 | 1: unchanged +0/-1 TODO |
- |
-|
401 | 2: +1/-1 nothing index.html |
- |
-|
402 | 3: +1/-1 +4/-0 lib/simplegit.rb |
- |
-|
403 | |
- |
-|
404 | The status of the simplegit.rb file is interesting. It shows you that a couple of lines are staged and a couple are unstaged. You’ve partially staged this file. At this point, you can exit the interactive adding script and run `git commit` to commit the partially staged files. |
- |
-|
405 | |
- |
-|
406 | Finally, you don’t need to be in interactive add mode to do the partial-file staging — you can start the same script by using `git add -p` or `git add --patch` on the command line. |
- |
-|
407 | |
- |
-|
408 | ## Stashing ## |
- |
-|
409 | |
- |
-|
410 | Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the `git stash` command. |
- |
-|
411 | |
- |
-|
412 | Stashing takes the dirty state of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time. |
- |
-|
413 | |
- |
-|
414 | ### Stashing Your Work ### |
- |
-|
415 | |
- |
-|
416 | To demonstrate, you’ll go into your project and start working on a couple of files and possibly stage one of the changes. If you run `git status`, you can see your dirty state: |
- |
-|
417 | |
- |
-|
418 | $ git status |
- |
-|
419 | # On branch master |
- |
-|
420 | # Changes to be committed: |
- |
-|
421 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
422 | # |
- |
-|
423 | # modified: index.html |
- |
-|
424 | # |
- |
-|
425 | # Changes not staged for commit: |
- |
-|
426 | # (use "git add <file>..." to update what will be committed) |
- |
-|
427 | # |
- |
-|
428 | # modified: lib/simplegit.rb |
- |
-|
429 | # |
- |
-|
430 | |
- |
-|
431 | Now you want to switch branches, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run `git stash`: |
- |
-|
432 | |
- |
-|
433 | $ git stash |
- |
-|
434 | Saved working directory and index state \ |
- |
-|
435 | "WIP on master: 049d078 added the index file" |
- |
-|
436 | HEAD is now at 049d078 added the index file |
- |
-|
437 | (To restore them type "git stash apply") |
- |
-|
438 | |
- |
-|
439 | Your working directory is clean: |
- |
-|
440 | |
- |
-|
441 | $ git status |
- |
-|
442 | # On branch master |
- |
-|
443 | nothing to commit, working directory clean |
- |
-|
444 | |
- |
-|
445 | At this point, you can easily switch branches and do work elsewhere; your changes are stored on your stack. To see which stashes you’ve stored, you can use `git stash list`: |
- |
-|
446 | |
- |
-|
447 | $ git stash list |
- |
-|
448 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
449 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
450 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
451 | |
- |
-|
452 | In this case, two stashes were done previously, so you have access to three different stashed works. You can reapply the one you just stashed by using the command shown in the help output of the original stash command: `git stash apply`. If you want to apply one of the older stashes, you can specify it by naming it, like this: `git stash apply stash@{2}`. If you don’t specify a stash, Git assumes the most recent stash and tries to apply it: |
- |
-|
453 | |
- |
-|
454 | $ git stash apply |
- |
-|
455 | # On branch master |
- |
-|
456 | # Changes not staged for commit: |
- |
-|
457 | # (use "git add <file>..." to update what will be committed) |
- |
-|
458 | # |
- |
-|
459 | # modified: index.html |
- |
-|
460 | # modified: lib/simplegit.rb |
- |
-|
461 | # |
- |
-|
462 | |
- |
-|
463 | You can see that Git re-modifies the files you uncommitted when you saved the stash. In this case, you had a clean working directory when you tried to apply the stash, and you tried to apply it on the same branch you saved it from; but having a clean working directory and applying it on the same branch aren’t necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash — Git gives you merge conflicts if anything no longer applies cleanly. |
- |
-|
464 | |
- |
-|
465 | The changes to your files were reapplied, but the file you staged before wasn’t restaged. To do that, you must run the `git stash apply` command with a `--index` option to tell the command to try to reapply the staged changes. If you had run that instead, you’d have gotten back to your original position: |
- |
-|
466 | |
- |
-|
467 | $ git stash apply --index |
- |
-|
468 | # On branch master |
- |
-|
469 | # Changes to be committed: |
- |
-|
470 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
471 | # |
- |
-|
472 | # modified: index.html |
- |
-|
473 | # |
- |
-|
474 | # Changes not staged for commit: |
- |
-|
475 | # (use "git add <file>..." to update what will be committed) |
- |
-|
476 | # |
- |
-|
477 | # modified: lib/simplegit.rb |
- |
-|
478 | # |
- |
-|
479 | |
- |
-|
480 | The apply option only tries to apply the stashed work — you continue to have it on your stack. To remove it, you can run `git stash drop` with the name of the stash to remove: |
- |
-|
481 | |
- |
-|
482 | $ git stash list |
- |
-|
483 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
484 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
485 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
486 | $ git stash drop stash@{0} |
- |
-|
487 | Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) |
- |
-|
488 | |
- |
-|
489 | You can also run `git stash pop` to apply the stash and then immediately drop it from your stack. |
- |
-|
490 | |
- |
-|
491 | ### Un-applying a Stash ### |
- |
-|
492 | |
- |
-|
493 | In some use case scenarios you might want to apply stashed changes, do some work, but then un-apply those changes that originally came from the stash. Git does not provide such a `stash unapply` command, but it is possible to achieve the effect by simply retrieving the patch associated with a stash and applying it in reverse: |
- |
-|
494 | |
- |
-|
495 | $ git stash show -p stash@{0} | git apply -R |
- |
-|
496 | |
- |
-|
497 | Again, if you don’t specify a stash, Git assumes the most recent stash: |
- |
-|
498 | |
- |
-|
499 | $ git stash show -p | git apply -R |
- |
-|
500 | |
- |
-|
501 | You may want to create an alias and effectively add a `stash-unapply` command to your Git. For example: |
- |
-|
502 | |
- |
-|
503 | $ git config --global alias.stash-unapply '!git stash show -p | git apply -R' |
- |
-|
504 | $ git stash apply |
- |
-|
505 | $ #... work work work |
- |
-|
506 | $ git stash-unapply |
- |
-|
507 | |
- |
-|
508 | ### Creating a Branch from a Stash ### |
- |
-|
509 | |
- |
-|
510 | If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you’ve since modified, you’ll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run `git stash branch`, which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully: |
- |
-|
511 | |
- |
-|
512 | $ git stash branch testchanges |
- |
-|
513 | Switched to a new branch "testchanges" |
- |
-|
514 | # On branch testchanges |
- |
-|
515 | # Changes to be committed: |
- |
-|
516 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
517 | # |
- |
-|
518 | # modified: index.html |
- |
-|
519 | # |
- |
-|
520 | # Changes not staged for commit: |
- |
-|
521 | # (use "git add <file>..." to update what will be committed) |
- |
-|
522 | # |
- |
-|
523 | # modified: lib/simplegit.rb |
- |
-|
524 | # |
- |
-|
525 | Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) |
- |
-|
526 | |
- |
-|
527 | This is a nice shortcut to recover stashed work easily and work on it in a new branch. |
- |
-|
528 | |
- |
-|
529 | ## Rewriting History ## |
- |
-|
530 | |
- |
-|
531 | Many times, when working with Git, you may want to revise your commit history for some reason. One of the great things about Git is that it allows you to make decisions at the last possible moment. You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with the stash command, and you can rewrite commits that already happened so they look like they happened in a different way. This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others. |
- |
-|
532 | |
- |
-|
533 | In this section, you’ll cover how to accomplish these very useful tasks so that you can make your commit history look the way you want before you share it with others. |
- |
-|
534 | |
- |
-|
535 | ### Changing the Last Commit ### |
- |
-|
536 | |
- |
-|
537 | Changing your last commit is probably the most common rewriting of history that you’ll do. You’ll often want to do two basic things to your last commit: change the commit message, or change the snapshot you just recorded by adding, changing and removing files. |
- |
-|
538 | |
- |
-|
539 | If you only want to modify your last commit message, it’s very simple: |
- |
-|
540 | |
- |
-|
541 | $ git commit --amend |
- |
-|
542 | |
- |
-|
543 | That drops you into your text editor, which has your last commit message in it, ready for you to modify the message. When you save and close the editor, the editor writes a new commit containing that message and makes it your new last commit. |
- |
-|
544 | |
- |
-|
545 | If you’ve committed and then you want to change the snapshot you committed by adding or changing files, possibly because you forgot to add a newly created file when you originally committed, the process works basically the same way. You stage the changes you want by editing a file and running `git add` on it or `git rm` to a tracked file, and the subsequent `git commit --amend` takes your current staging area and makes it the snapshot for the new commit. |
- |
-|
546 | |
- |
-|
547 | You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it. |
- |
-|
548 | |
- |
-|
549 | ### Changing Multiple Commit Messages ### |
- |
-|
550 | |
- |
-|
551 | To modify a commit that is farther back in your history, you must move to more complex tools. Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD they were originally based on instead of moving them to another one. With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the `-i` option to `git rebase`. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto. |
- |
-|
552 | |
- |
-|
553 | For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to `git rebase -i` the parent of the last commit you want to edit, which is `HEAD~2^` or `HEAD~3`. It may be easier to remember the `~3` because you’re trying to edit the last three commits; but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit: |
- |
-|
554 | |
- |
-|
555 | $ git rebase -i HEAD~3 |
- |
-|
556 | |
- |
-|
557 | Remember again that this is a rebasing command — every commit included in the range `HEAD~3..HEAD` will be rewritten, whether you change the message or not. Don’t include any commit you’ve already pushed to a central server — doing so will confuse other developers by providing an alternate version of the same change. |
- |
-|
558 | |
- |
-|
559 | Running this command gives you a list of commits in your text editor that looks something like this: |
- |
-|
560 | |
- |
-|
561 | pick f7f3f6d changed my name a bit |
- |
-|
562 | pick 310154e updated README formatting and added blame |
- |
-|
563 | pick a5f4a0d added cat-file |
- |
-|
564 | |
- |
-|
565 | # Rebase 710f0f8..a5f4a0d onto 710f0f8 |
- |
-|
566 | # |
- |
-|
567 | # Commands: |
- |
-|
568 | # p, pick = use commit |
- |
-|
569 | # r, reword = use commit, but edit the commit message |
- |
-|
570 | # e, edit = use commit, but stop for amending |
- |
-|
571 | # s, squash = use commit, but meld into previous commit |
- |
-|
572 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
573 | # x, exec = run command (the rest of the line) using shell |
- |
-|
574 | # |
- |
-|
575 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
576 | # |
- |
-|
577 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
578 | # |
- |
-|
579 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
580 | # |
- |
-|
581 | # Note that empty commits are commented out |
- |
-|
582 | |
- |
-|
583 | It’s important to note that these commits are listed in the opposite order than you normally see them using the `log` command. If you run a `log`, you see something like this: |
- |
-|
584 | |
- |
-|
585 | $ git log --pretty=format:"%h %s" HEAD~3..HEAD |
- |
-|
586 | a5f4a0d added cat-file |
- |
-|
587 | 310154e updated README formatting and added blame |
- |
-|
588 | f7f3f6d changed my name a bit |
- |
-|
589 | |
- |
-|
590 | Notice the reverse order. The interactive rebase gives you a script that it’s going to run. It will start at the commit you specify on the command line (`HEAD~3`) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that’s the first one it will replay. |
- |
-|
591 | |
- |
-|
592 | You need to edit the script so that it stops at the commit you want to edit. To do so, change the word pick to the word edit for each of the commits you want the script to stop after. For example, to modify only the third commit message, you change the file to look like this: |
- |
-|
593 | |
- |
-|
594 | edit f7f3f6d changed my name a bit |
- |
-|
595 | pick 310154e updated README formatting and added blame |
- |
-|
596 | pick a5f4a0d added cat-file |
- |
-|
597 | |
- |
-|
598 | When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message: |
- |
-|
599 | |
- |
-|
600 | <!-- This is actually weird, as the SHA-1 of 7482e0d is not present in the list, |
- |
-|
601 | nor is the commit message. Please review |
- |
-|
602 | --> |
- |
-|
603 | |
- |
-|
604 | $ git rebase -i HEAD~3 |
- |
-|
605 | Stopped at 7482e0d... updated the gemspec to hopefully work better |
- |
-|
606 | You can amend the commit now, with |
- |
-|
607 | |
- |
-|
608 | git commit --amend |
- |
-|
609 | |
- |
-|
610 | Once you’re satisfied with your changes, run |
- |
-|
611 | |
- |
-|
612 | git rebase --continue |
- |
-|
613 | |
- |
-|
614 | These instructions tell you exactly what to do. Type |
- |
-|
615 | |
- |
-|
616 | $ git commit --amend |
- |
-|
617 | |
- |
-|
618 | Change the commit message, and exit the editor. Then, run |
- |
-|
619 | |
- |
-|
620 | $ git rebase --continue |
- |
-|
621 | |
- |
-|
622 | This command will apply the other two commits automatically, and then you’re done. If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit. Each time, Git will stop, let you amend the commit, and continue when you’re finished. |
- |
-|
623 | |
- |
-|
624 | ### Reordering Commits ### |
- |
-|
625 | |
- |
-|
626 | You can also use interactive rebases to reorder or remove commits entirely. If you want to remove the "added cat-file" commit and change the order in which the other two commits are introduced, you can change the rebase script from this |
- |
-|
627 | |
- |
-|
628 | pick f7f3f6d changed my name a bit |
- |
-|
629 | pick 310154e updated README formatting and added blame |
- |
-|
630 | pick a5f4a0d added cat-file |
- |
-|
631 | |
- |
-|
632 | to this: |
- |
-|
633 | |
- |
-|
634 | pick 310154e updated README formatting and added blame |
- |
-|
635 | pick f7f3f6d changed my name a bit |
- |
-|
636 | |
- |
-|
637 | When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies `310154e` and then `f7f3f6d`, and then stops. You effectively change the order of those commits and remove the "added cat-file" commit completely. |
- |
-|
638 | |
- |
-|
639 | ### Squashing Commits ### |
- |
-|
640 | |
- |
-|
641 | It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool. The script puts helpful instructions in the rebase message: |
- |
-|
642 | |
- |
-|
643 | # |
- |
-|
644 | # Commands: |
- |
-|
645 | # p, pick = use commit |
- |
-|
646 | # r, reword = use commit, but edit the commit message |
- |
-|
647 | # e, edit = use commit, but stop for amending |
- |
-|
648 | # s, squash = use commit, but meld into previous commit |
- |
-|
649 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
650 | # x, exec = run command (the rest of the line) using shell |
- |
-|
651 | # |
- |
-|
652 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
653 | # |
- |
-|
654 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
655 | # |
- |
-|
656 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
657 | # |
- |
-|
658 | # Note that empty commits are commented out |
- |
-|
659 | |
- |
-|
660 | If, instead of "pick" or "edit", you specify "squash", Git applies both that change and the change directly before it and makes you merge the commit messages together. So, if you want to make a single commit from these three commits, you make the script look like this: |
- |
-|
661 | |
- |
-|
662 | pick f7f3f6d changed my name a bit |
- |
-|
663 | squash 310154e updated README formatting and added blame |
- |
-|
664 | squash a5f4a0d added cat-file |
- |
-|
665 | |
- |
-|
666 | When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages: |
- |
-|
667 | |
- |
-|
668 | # This is a combination of 3 commits. |
- |
-|
669 | # The first commit's message is: |
- |
-|
670 | changed my name a bit |
- |
-|
671 | |
- |
-|
672 | # This is the 2nd commit message: |
- |
-|
673 | |
- |
-|
674 | updated README formatting and added blame |
- |
-|
675 | |
- |
-|
676 | # This is the 3rd commit message: |
- |
-|
677 | |
- |
-|
678 | added cat-file |
- |
-|
679 | |
- |
-|
680 | When you save that, you have a single commit that introduces the changes of all three previous commits. |
- |
-|
681 | |
- |
-|
682 | ### Splitting a Commit ### |
- |
-|
683 | |
- |
-|
684 | Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with. For example, suppose you want to split the middle commit of your three commits. Instead of "updated README formatting and added blame", you want to split it into two commits: "updated README formatting" for the first, and "added blame" for the second. You can do that in the `rebase -i` script by changing the instruction on the commit you want to split to "edit": |
- |
-|
685 | |
- |
-|
686 | pick f7f3f6d changed my name a bit |
- |
-|
687 | edit 310154e updated README formatting and added blame |
- |
-|
688 | pick a5f4a0d added cat-file |
- |
-|
689 | |
- |
-|
690 | When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (`f7f3f6d`), applies the second (`310154e`), and drops you to the console. There, you can do a mixed reset of that commit with `git reset HEAD^`, which effectively undoes that commit and leaves the modified files unstaged. Now you can take the changes that have been reset, and create multiple commits out of them. Simply stage and commit files until you have several commits, and run `git rebase --continue` when you’re done: |
- |
-|
691 | |
- |
-|
692 | $ git reset HEAD^ |
- |
-|
693 | $ git add README |
- |
-|
694 | $ git commit -m 'updated README formatting' |
- |
-|
695 | $ git add lib/simplegit.rb |
- |
-|
696 | $ git commit -m 'added blame' |
- |
-|
697 | $ git rebase --continue |
- |
-|
698 | |
- |
-|
699 | Git applies the last commit (`a5f4a0d`) in the script, and your history looks like this: |
- |
-|
700 | |
- |
-|
701 | $ git log -4 --pretty=format:"%h %s" |
- |
-|
702 | 1c002dd added cat-file |
- |
-|
703 | 9b29157 added blame |
- |
-|
704 | 35cfb2b updated README formatting |
- |
-|
705 | f3cc40e changed my name a bit |
- |
-|
706 | |
- |
-|
707 | Once again, this changes the SHAs of all the commits in your list, so make sure no commit shows up in that list that you’ve already pushed to a shared repository. |
- |
-|
708 | |
- |
-|
709 | ### The Nuclear Option: filter-branch ### |
- |
-|
710 | |
- |
-|
711 | There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way — for instance, changing your e-mail address globally or removing a file from every commit. The command is `filter-branch`, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite. However, it can be very useful. You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of. |
- |
-|
712 | |
- |
-|
713 | #### Removing a File from Every Commit #### |
- |
-|
714 | |
- |
-|
715 | This occurs fairly commonly. Someone accidentally commits a huge binary file with a thoughtless `git add .`, and you want to remove it everywhere. Perhaps you accidentally committed a file that contained a password, and you want to make your project open source. `filter-branch` is the tool you probably want to use to scrub your entire history. To remove a file named passwords.txt from your entire history, you can use the `--tree-filter` option to `filter-branch`: |
- |
-|
716 | |
- |
-|
717 | $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD |
- |
-|
718 | Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) |
- |
-|
719 | Ref 'refs/heads/master' was rewritten |
- |
-|
720 | |
- |
-|
721 | The `--tree-filter` option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt from every snapshot, whether it exists or not. If you want to remove all accidentally committed editor backup files, you can run something like `git filter-branch --tree-filter "rm -f *~" HEAD`. |
- |
-|
722 | |
- |
-|
723 | You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end. It’s generally a good idea to do this in a testing branch and then hard-reset your master branch after you’ve determined the outcome is what you really want. To run `filter-branch` on all your branches, you can pass `--all` to the command. |
- |
-|
724 | |
- |
-|
725 | #### Making a Subdirectory the New Root #### |
- |
-|
726 | |
- |
-|
727 | Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk, tags, and so on). If you want to make the `trunk` subdirectory be the new project root for every commit, `filter-branch` can help you do that, too: |
- |
-|
728 | |
- |
-|
729 | $ git filter-branch --subdirectory-filter trunk HEAD |
- |
-|
730 | Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) |
- |
-|
731 | Ref 'refs/heads/master' was rewritten |
- |
-|
732 | |
- |
-|
733 | Now your new project root is what was in the `trunk` subdirectory each time. Git will also automatically remove commits that did not affect the subdirectory. |
- |
-|
734 | |
- |
-|
735 | #### Changing E-Mail Addresses Globally #### |
- |
-|
736 | |
- |
-|
737 | Another common case is that you forgot to run `git config` to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with `filter-branch` as well. You need to be careful to change only the e-mail addresses that are yours, so you use `--commit-filter`: |
- |
-|
738 | |
- |
-|
739 | $ git filter-branch --commit-filter ' |
- |
-|
740 | if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; |
- |
-|
741 | then |
- |
-|
742 | GIT_AUTHOR_NAME="Scott Chacon"; |
- |
-|
743 | GIT_AUTHOR_EMAIL="schacon@example.com"; |
- |
-|
744 | git commit-tree "$@"; |
- |
-|
745 | else |
- |
-|
746 | git commit-tree "$@"; |
- |
-|
747 | fi' HEAD |
- |
-|
748 | |
- |
-|
749 | This goes through and rewrites every commit to have your new address. Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address. |
- |
-|
750 | |
- |
-|
751 | ### The Very Fast Nuclear Option: Big Friendly Giant Repo Cleaner (BFG) ### |
- |
-|
752 | |
- |
-|
753 | [Roberto Tyley](https://github.com/rtyley) has written a similar tool to `filter-branch` called the BFG. BFG cannot do as much as `filter-branch`, but it is _very_ fast and on a large repository this can make a big difference. If the change you want to make is in the scope of BFG capability, and you have performance issues, then you should consider using it. |
- |
-|
754 | |
- |
-|
755 | See the [BFG](http://rtyley.github.io/bfg-repo-cleaner/) website for details. |
- |
-|
756 | |
- |
-|
757 | ## Debugging with Git ## |
-1 | === Debugging with Git |
-
758 | |
-2 | |
-
759 | Git also provides a couple of tools to help you debug issues in your projects. Because Git is designed to work with nearly any type of project, these tools are pretty generic, but they can often help you hunt for a bug or culprit when things go wrong. |
-3 | Git also provides a couple of tools to help you debug issues in your projects. Because Git is designed to work with nearly any type of project, these tools are pretty generic, but they can often help you hunt for a bug or culprit when things go wrong. |
-
760 | |
-4 | |
-
. | |
-5 | [[_file_annotation]] |
-
761 | ### File Annotation ### |
-6 | ==== File Annotation |
-
762 | |
-7 | |
-
763 | If you track down a bug in your code and want to know when it was introduced and why, file annotation is often your best tool. It shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with `git blame` to see when each line of the method was last edited and by whom. This example uses the `-L` option to limit the output to lines 12 through 22: |
-8 | If you track down a bug in your code and want to know when it was introduced and why, file annotation is often your best tool. It shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with `git blame` to see when each line of the method was last edited and by whom. This example uses the `-L` option to limit the output to lines 12 through 22: |
-
764 | |
-9 | |
-
. | |
-10 | [source,console] |
-
|
-11 | ---- |
-|
765 | $ git blame -L 12,22 simplegit.rb |
-12 | $ git blame -L 12,22 simplegit.rb |
-
766 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master') |
-13 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master') |
-
767 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}") |
-14 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}") |
-
768 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end |
-15 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end |
-
769 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15) |
-16 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15) |
-
770 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master') |
-17 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master') |
-
771 | 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}") |
-18 | 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}") |
-
772 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end |
-19 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end |
-
773 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19) |
-20 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19) |
-
774 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path) |
-21 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path) |
-
775 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}") |
-22 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}") |
-
776 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end |
-23 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end |
-
|
-24 | ---- |
-|
777 | |
-25 | |
-
.778 | Notice that the first field is the partial SHA-1 of the commit that last modified that line. The next two fields are values extracted from that commit—the author name and the authored date of that commit — so you can easily see who modified that line and when. After that come the line number and the content of the file. Also note the `^4832fe2` commit lines, which designate that those lines were in this file’s original commit. That commit is when this file was first added to this project, and those lines have been unchanged since. This is a tad confusing, because now you’ve seen at least three different ways that Git uses the `^` to modify a commit SHA, but that is what it means here. |
-26 | Notice that the first field is the partial SHA-1 of the commit that last modified that line. The next two fields are values extracted from that commit–the author name and the authored date of that commit – so you can easily see who modified that line and when. After that come the line number and the content of the file. Also note the `^4832fe2` commit lines, which designate that those lines were in this file’s original commit. That commit is when this file was first added to this project, and those lines have been unchanged since. This is a tad confusing, because now you’ve seen at least three different ways that Git uses the `^` to modify a commit SHA, but that is what it means here. |
-
779 | |
-27 | |
-
.780 | Another cool thing about Git is that it doesn’t track file renames explicitly. It records the snapshots and then tries to figure out what was renamed implicitly, after the fact. One of the interesting features of this is that you can ask it to figure out all sorts of code movement as well. If you pass `-C` to `git blame`, Git analyzes the file you’re annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere. Recently, I was refactoring a file named `GITServerHandler.m` into multiple files, one of which was `GITPackUpload.m`. By blaming `GITPackUpload.m` with the `-C` option, I could see where sections of the code originally came from: |
-28 | Another cool thing about Git is that it doesn’t track file renames explicitly. It records the snapshots and then tries to figure out what was renamed implicitly, after the fact. One of the interesting features of this is that you can ask it to figure out all sorts of code movement as well. If you pass `-C` to `git blame`, Git analyzes the file you’re annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere. For example, say you are refactoring a file named `GITServerHandler.m` into multiple files, one of which is `GITPackUpload.m`. By blaming `GITPackUpload.m` with the `-C` option, you can see where sections of the code originally came from: |
-
781 | |
-29 | |
-
.782 | $ git blame -C -L 141,153 GITPackUpload.m |
-30 | [source,console] |
-
783 | f344f58d GITServerHandler.m (Scott 2009-01-04 141) |
-31 | ---- |
-
784 | f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC |
-32 | $ git blame -C -L 141,153 GITPackUpload.m |
-
785 | f344f58d GITServerHandler.m (Scott 2009-01-04 143) { |
-33 | f344f58d GITServerHandler.m (Scott 2009-01-04 141) |
-
786 | 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI |
-34 | f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC |
-
787 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 145) |
-35 | f344f58d GITServerHandler.m (Scott 2009-01-04 143) { |
-
788 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha; |
-36 | 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI |
-
789 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g |
-37 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 145) |
-
790 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 148) |
-38 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha; |
-
791 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI |
-39 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g |
-
792 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 150) |
-40 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 148) |
-
793 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) { |
-41 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI |
-
794 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb |
-42 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 150) |
-
795 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 153) |
-43 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) { |
-
|
-44 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb |
-|
|
-45 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 153) |
-|
|
-46 | ---- |
-|
796 | |
-47 | |
-
797 | This is really useful. Normally, you get as the original commit the commit where you copied the code over, because that is the first time you touched those lines in this file. Git tells you the original commit where you wrote those lines, even if it was in another file. |
-48 | This is really useful. Normally, you get as the original commit the commit where you copied the code over, because that is the first time you touched those lines in this file. Git tells you the original commit where you wrote those lines, even if it was in another file. |
-
798 | |
-49 | |
-
. | |
-50 | [[_binary_search]] |
-
799 | ### Binary Search ### |
-51 | ==== Binary Search |
-
800 | |
-52 | |
-
801 | Annotating a file helps if you know where the issue is to begin with. If you don’t know what is breaking, and there have been dozens or hundreds of commits since the last state where you know the code worked, you’ll likely turn to `git bisect` for help. The `bisect` command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. |
-53 | Annotating a file helps if you know where the issue is to begin with. If you don’t know what is breaking, and there have been dozens or hundreds of commits since the last state where you know the code worked, you’ll likely turn to `git bisect` for help. The `bisect` command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. |
-
802 | |
-54 | |
-
803 | Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that. You go back to your code, and it turns out you can reproduce the issue, but you can’t figure out what is going wrong. You can bisect the code to find out. First you run `git bisect start` to get things going, and then you use `git bisect bad` to tell the system that the current commit you’re on is broken. Then, you must tell bisect when the last known good state was, using `git bisect good [good_commit]`: |
-55 | Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that. You go back to your code, and it turns out you can reproduce the issue, but you can’t figure out what is going wrong. You can bisect the code to find out. First you run `git bisect start` to get things going, and then you use `git bisect bad` to tell the system that the current commit you’re on is broken. Then, you must tell bisect when the last known good state was, using `git bisect good [good_commit]`: |
-
804 | |
-56 | |
-
. | |
-57 | [source,console] |
-
|
-58 | ---- |
-|
805 | $ git bisect start |
-59 | $ git bisect start |
-
806 | $ git bisect bad |
-60 | $ git bisect bad |
-
807 | $ git bisect good v1.0 |
-61 | $ git bisect good v1.0 |
-
808 | Bisecting: 6 revisions left to test after this |
-62 | Bisecting: 6 revisions left to test after this |
-
809 | [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo |
-63 | [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo |
-
|
-64 | ---- |
-|
810 | |
-65 | |
-
811 | Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. If it does, then it was introduced sometime before this middle commit; if it doesn’t, then the problem was introduced sometime after the middle commit. It turns out there is no issue here, and you tell Git that by typing `git bisect good` and continue your journey: |
-66 | Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. If it does, then it was introduced sometime before this middle commit; if it doesn’t, then the problem was introduced sometime after the middle commit. It turns out there is no issue here, and you tell Git that by typing `git bisect good` and continue your journey: |
-
812 | |
-67 | |
-
. | |
-68 | [source,console] |
-
|
-69 | ---- |
-|
813 | $ git bisect good |
-70 | $ git bisect good |
-
814 | Bisecting: 3 revisions left to test after this |
-71 | Bisecting: 3 revisions left to test after this |
-
815 | [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing |
-72 | [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing |
-
|
-73 | ---- |
-|
816 | |
-74 | |
-
817 | Now you’re on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with `git bisect bad`: |
-75 | Now you’re on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with `git bisect bad`: |
-
818 | |
-76 | |
-
. | |
-77 | [source,console] |
-
|
-78 | ---- |
-|
819 | $ git bisect bad |
-79 | $ git bisect bad |
-
820 | Bisecting: 1 revisions left to test after this |
-80 | Bisecting: 1 revisions left to test after this |
-
821 | [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table |
-81 | [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table |
-
|
-82 | ---- |
-|
822 | |
-83 | |
-
823 | This commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and show some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug: |
-84 | This commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and show some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug: |
-
824 | |
-85 | |
-
. | |
-86 | [source,console] |
-
|
-87 | ---- |
-|
825 | $ git bisect good |
-88 | $ git bisect good |
-
826 | b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit |
-89 | b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit |
-
827 | commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 |
-90 | commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 |
-
828 | Author: PJ Hyett <pjhyett@example.com> |
-91 | Author: PJ Hyett <pjhyett@example.com> |
-
829 | Date: Tue Jan 27 14:48:32 2009 -0800 |
-92 | Date: Tue Jan 27 14:48:32 2009 -0800 |
-
830 | |
-93 | |
-
831 | secure this thing |
-94 | secure this thing |
-
832 | |
-95 | |
-
833 | :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730 |
-96 | :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730 |
-
834 | f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config |
-97 | f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config |
-
|
-98 | ---- |
-|
835 | |
-99 | |
-
836 | When you’re finished, you should run `git bisect reset` to reset your HEAD to where you were before you started, or you’ll end up in a weird state: |
-100 | When you’re finished, you should run `git bisect reset` to reset your HEAD to where you were before you started, or you’ll end up in a weird state: |
-
837 | |
-101 | |
-
. | |
-102 | [source,console] |
-
|
-103 | ---- |
-|
838 | $ git bisect reset |
-104 | $ git bisect reset |
-
|
-105 | ---- |
-|
839 | |
-106 | |
-
840 | This is a powerful tool that can help you check hundreds of commits for an introduced bug in minutes. In fact, if you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate `git bisect`. First, you again tell it the scope of the bisect by providing the known bad and good commits. You can do this by listing them with the `bisect start` command if you want, listing the known bad commit first and the known good commit second: |
-107 | This is a powerful tool that can help you check hundreds of commits for an introduced bug in minutes. In fact, if you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate `git bisect`. First, you again tell it the scope of the bisect by providing the known bad and good commits. You can do this by listing them with the `bisect start` command if you want, listing the known bad commit first and the known good commit second: |
-
841 | |
-108 | |
-
. | |
-109 | [source,console] |
-
|
-110 | ---- |
-|
842 | $ git bisect start HEAD v1.0 |
-111 | $ git bisect start HEAD v1.0 |
-
843 | $ git bisect run test-error.sh |
-112 | $ git bisect run test-error.sh |
-
|
-113 | ---- |
-|
844 | |
-114 | |
-
845 | Doing so automatically runs `test-error.sh` on each checked-out commit until Git finds the first broken commit. You can also run something like `make` or `make tests` or whatever you have that runs automated tests for you. |
-115 | Doing so automatically runs `test-error.sh` on each checked-out commit until Git finds the first broken commit. You can also run something like `make` or `make tests` or whatever you have that runs automated tests for you. |
-
.846 | |
- |
-|
847 | ## Submodules ## |
- |
-|
848 | |
- |
-|
849 | It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other. |
- |
-|
850 | |
- |
-|
851 | Here’s an example. Suppose you’re developing a web site and creating Atom feeds. Instead of writing your own Atom-generating code, you decide to use a library. You’re likely to have to either include this code from a shared library like a CPAN install or Ruby gem, or copy the source code into your own project tree. The issue with including the library is that it’s difficult to customize the library in any way and often more difficult to deploy it, because you need to make sure every client has that library available. The issue with vendoring the code into your own project is that any custom changes you make are difficult to merge when upstream changes become available. |
- |
-|
852 | |
- |
-|
853 | Git addresses this issue using submodules. Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. |
- |
-|
854 | |
- |
-|
855 | ### Starting with Submodules ### |
- |
-|
856 | |
- |
-|
857 | Suppose you want to add the Rack library (a Ruby web server gateway interface) to your project, possibly maintain your own changes to it, but continue to merge in upstream changes. The first thing you should do is clone the external repository into your subdirectory. You add external projects as submodules with the `git submodule add` command: |
- |
-|
858 | |
- |
-|
859 | $ git submodule add git://github.com/chneukirchen/rack.git rack |
- |
-|
860 | Initialized empty Git repository in /opt/subtest/rack/.git/ |
- |
-|
861 | remote: Counting objects: 3181, done. |
- |
-|
862 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
863 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
864 | Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done. |
- |
-|
865 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
866 | |
- |
-|
867 | Now you have the Rack project under a subdirectory named `rack` within your project. You can go into that subdirectory, make changes, add your own writable remote repository to push your changes into, fetch and merge from the original repository, and more. If you run `git status` right after you add the submodule, you see two things: |
- |
-|
868 | |
- |
-|
869 | $ git status |
- |
-|
870 | # On branch master |
- |
-|
871 | # Changes to be committed: |
- |
-|
872 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
873 | # |
- |
-|
874 | # new file: .gitmodules |
- |
-|
875 | # new file: rack |
- |
-|
876 | # |
- |
-|
877 | |
- |
-|
878 | First you notice the `.gitmodules` file. This is a configuration file that stores the mapping between the project’s URL and the local subdirectory you’ve pulled it into: |
- |
-|
879 | |
- |
-|
880 | $ cat .gitmodules |
- |
-|
881 | [submodule "rack"] |
- |
-|
882 | path = rack |
- |
-|
883 | url = git://github.com/chneukirchen/rack.git |
- |
-|
884 | |
- |
-|
885 | If you have multiple submodules, you’ll have multiple entries in this file. It’s important to note that this file is version-controlled with your other files, like your `.gitignore` file. It’s pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from. |
- |
-|
886 | |
- |
-|
887 | The other listing in the `git status` output is the rack entry. If you run `git diff` on that, you see something interesting: |
- |
-|
888 | |
- |
-|
889 | $ git diff --cached rack |
- |
-|
890 | diff --git a/rack b/rack |
- |
-|
891 | new file mode 160000 |
- |
-|
892 | index 0000000..08d709f |
- |
-|
893 | --- /dev/null |
- |
-|
894 | +++ b/rack |
- |
-|
895 | @@ -0,0 +1 @@ |
- |
-|
896 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
897 | |
- |
-|
898 | Although `rack` is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git records it as a particular commit from that repository. When you make changes and commit in that subdirectory, the superproject notices that the HEAD there has changed and records the exact commit you’re currently working off of; that way, when others clone this project, they can re-create the environment exactly. |
- |
-|
899 | |
- |
-|
900 | This is an important point with submodules: you record them as the exact commit they’re at. You can’t record a submodule at `master` or some other symbolic reference. |
- |
-|
901 | |
- |
-|
902 | When you commit, you see something like this: |
- |
-|
903 | |
- |
-|
904 | $ git commit -m 'first commit with submodule rack' |
- |
-|
905 | [master 0550271] first commit with submodule rack |
- |
-|
906 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
907 | create mode 100644 .gitmodules |
- |
-|
908 | create mode 160000 rack |
- |
-|
909 | |
- |
-|
910 | Notice the 160000 mode for the rack entry. That is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file. |
- |
-|
911 | |
- |
-|
912 | You can treat the `rack` directory as a separate project and then update your superproject from time to time with a pointer to the latest commit in that subproject. All the Git commands work independently in the two directories: |
- |
-|
913 | |
- |
-|
914 | $ git log -1 |
- |
-|
915 | commit 0550271328a0038865aad6331e620cd7238601bb |
- |
-|
916 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
917 | Date: Thu Apr 9 09:03:56 2009 -0700 |
- |
-|
918 | |
- |
-|
919 | first commit with submodule rack |
- |
-|
920 | $ cd rack/ |
- |
-|
921 | $ git log -1 |
- |
-|
922 | commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
923 | Author: Christian Neukirchen <chneukirchen@gmail.com> |
- |
-|
924 | Date: Wed Mar 25 14:49:04 2009 +0100 |
- |
-|
925 | |
- |
-|
926 | Document version change |
- |
-|
927 | |
- |
-|
928 | ### Cloning a Project with Submodules ### |
- |
-|
929 | |
- |
-|
930 | Here you’ll clone a project with a submodule in it. When you receive such a project, you get the directories that contain submodules, but none of the files yet: |
- |
-|
931 | |
- |
-|
932 | $ git clone git://github.com/schacon/myproject.git |
- |
-|
933 | Initialized empty Git repository in /opt/myproject/.git/ |
- |
-|
934 | remote: Counting objects: 6, done. |
- |
-|
935 | remote: Compressing objects: 100% (4/4), done. |
- |
-|
936 | remote: Total 6 (delta 0), reused 0 (delta 0) |
- |
-|
937 | Receiving objects: 100% (6/6), done. |
- |
-|
938 | $ cd myproject |
- |
-|
939 | $ ls -l |
- |
-|
940 | total 8 |
- |
-|
941 | -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README |
- |
-|
942 | drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack |
- |
-|
943 | $ ls rack/ |
- |
-|
944 | $ |
- |
-|
945 | |
- |
-|
946 | The `rack` directory is there, but empty. You must run two commands: `git submodule init` to initialize your local configuration file, and `git submodule update` to fetch all the data from that project and check out the appropriate commit listed in your superproject: |
- |
-|
947 | |
- |
-|
948 | $ git submodule init |
- |
-|
949 | Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack' |
- |
-|
950 | $ git submodule update |
- |
-|
951 | Initialized empty Git repository in /opt/myproject/rack/.git/ |
- |
-|
952 | remote: Counting objects: 3181, done. |
- |
-|
953 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
954 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
955 | Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done. |
- |
-|
956 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
957 | Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433' |
- |
-|
958 | |
- |
-|
959 | Now your `rack` subdirectory is at the exact state it was in when you committed earlier. If another developer makes changes to the rack code and commits, and you pull that reference down and merge it in, you get something a bit odd: |
- |
-|
960 | |
- |
-|
961 | $ git merge origin/master |
- |
-|
962 | Updating 0550271..85a3eee |
- |
-|
963 | Fast forward |
- |
-|
964 | rack | 2 +- |
- |
-|
965 | 1 files changed, 1 insertions(+), 1 deletions(-) |
- |
-|
966 | [master*]$ git status |
- |
-|
967 | # On branch master |
- |
-|
968 | # Changes not staged for commit: |
- |
-|
969 | # (use "git add <file>..." to update what will be committed) |
- |
-|
970 | # (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
971 | # |
- |
-|
972 | # modified: rack |
- |
-|
973 | # |
- |
-|
974 | |
- |
-|
975 | You merged in what is basically a change to the pointer for your submodule; but it doesn’t update the code in the submodule directory, so it looks like you have a dirty state in your working directory: |
- |
-|
976 | |
- |
-|
977 | $ git diff |
- |
-|
978 | diff --git a/rack b/rack |
- |
-|
979 | index 6c5e70b..08d709f 160000 |
- |
-|
980 | --- a/rack |
- |
-|
981 | +++ b/rack |
- |
-|
982 | @@ -1 +1 @@ |
- |
-|
983 | -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
984 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
985 | |
- |
-|
986 | This is the case because the pointer you have for the submodule isn’t what is actually in the submodule directory. To fix this, you must run `git submodule update` again: |
- |
-|
987 | |
- |
-|
988 | $ git submodule update |
- |
-|
989 | remote: Counting objects: 5, done. |
- |
-|
990 | remote: Compressing objects: 100% (3/3), done. |
- |
-|
991 | remote: Total 3 (delta 1), reused 2 (delta 0) |
- |
-|
992 | Unpacking objects: 100% (3/3), done. |
- |
-|
993 | From git@github.com:schacon/rack |
- |
-|
994 | 08d709f..6c5e70b master -> origin/master |
- |
-|
995 | Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0' |
- |
-|
996 | |
- |
-|
997 | You have to do this every time you pull down a submodule change in the main project. It’s strange, but it works. |
- |
-|
998 | |
- |
-|
999 | One common problem happens when a developer makes a change locally in a submodule but doesn’t push it to a public server. Then, they commit a pointer to that non-public state and push up the superproject. When other developers try to run `git submodule update`, the submodule system can’t find the commit that is referenced, because it exists only on the first developer’s system. If that happens, you see an error like this: |
- |
-|
1000 | |
- |
-|
1001 | $ git submodule update |
- |
-|
1002 | fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
1003 | Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack' |
- |
-|
1004 | |
- |
-|
1005 | You have to see who last changed the submodule: |
- |
-|
1006 | |
- |
-|
1007 | $ git log -1 rack |
- |
-|
1008 | commit 85a3eee996800fcfa91e2119372dd4172bf76678 |
- |
-|
1009 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
1010 | Date: Thu Apr 9 09:19:14 2009 -0700 |
- |
-|
1011 | |
- |
-|
1012 | added a submodule reference I will never make public. hahahahaha! |
- |
-|
1013 | |
- |
-|
1014 | Then, you e-mail that guy and yell at him. |
- |
-|
1015 | |
- |
-|
1016 | ### Superprojects ### |
- |
-|
1017 | |
- |
-|
1018 | Sometimes, developers want to get a combination of a large project’s subdirectories, depending on what team they’re on. This is common if you’re coming from CVS or Subversion, where you’ve defined a module or collection of subdirectories, and you want to keep this type of workflow. |
- |
-|
1019 | |
- |
-|
1020 | A good way to do this in Git is to make each of the subdirectories a separate Git repository and then create superproject Git repositories that contain multiple submodules. A benefit of this approach is that you can more specifically define the relationships between the projects with tags and branches in the superprojects. |
- |
-|
1021 | |
- |
-|
1022 | ### Issues with Submodules ### |
- |
-|
1023 | |
- |
-|
1024 | Using submodules isn’t without hiccups, however. First, you must be relatively careful when working in the submodule directory. When you run `git submodule update`, it checks out the specific version of the project, but not within a branch. This is called having a detached HEAD — it means the HEAD file points directly to a commit, not to a symbolic reference. The issue is that you generally don’t want to work in a detached HEAD environment, because it’s easy to lose changes. If you do an initial `submodule update`, commit in that submodule directory without creating a branch to work in, and then run `git submodule update` again from the superproject without committing in the meantime, Git will overwrite your changes without telling you. Technically you won’t lose the work, but you won’t have a branch pointing to it, so it will be somewhat difficult to retrieve. |
- |
-|
1025 | |
- |
-|
1026 | To avoid this issue, create a branch when you work in a submodule directory with `git checkout -b work` or something equivalent. When you do the submodule update a second time, it will still revert your work, but at least you have a pointer to get back to. |
- |
-|
1027 | |
- |
-|
1028 | Switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory: |
- |
-|
1029 | |
- |
-|
1030 | $ git checkout -b rack |
- |
-|
1031 | Switched to a new branch "rack" |
- |
-|
1032 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1033 | Initialized empty Git repository in /opt/myproj/rack/.git/ |
- |
-|
1034 | ... |
- |
-|
1035 | Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done. |
- |
-|
1036 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1037 | $ git commit -am 'added rack submodule' |
- |
-|
1038 | [rack cc49a69] added rack submodule |
- |
-|
1039 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
1040 | create mode 100644 .gitmodules |
- |
-|
1041 | create mode 160000 rack |
- |
-|
1042 | $ git checkout master |
- |
-|
1043 | Switched to branch "master" |
- |
-|
1044 | $ git status |
- |
-|
1045 | # On branch master |
- |
-|
1046 | # Untracked files: |
- |
-|
1047 | # (use "git add <file>..." to include in what will be committed) |
- |
-|
1048 | # |
- |
-|
1049 | # rack/ |
- |
-|
1050 | |
- |
-|
1051 | You have to either move it out of the way or remove it, in which case you have to clone it again when you switch back—and you may lose local changes or branches that you didn’t push up. |
- |
-|
1052 | |
- |
-|
1053 | The last main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have the rack files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run `submodule add`, Git yells at you: |
- |
-|
1054 | |
- |
-|
1055 | $ rm -Rf rack/ |
- |
-|
1056 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1057 | 'rack' already exists in the index |
- |
-|
1058 | |
- |
-|
1059 | You have to unstage the `rack` directory first. Then you can add the submodule: |
- |
-|
1060 | |
- |
-|
1061 | $ git rm -r rack |
- |
-|
1062 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1063 | Initialized empty Git repository in /opt/testsub/rack/.git/ |
- |
-|
1064 | remote: Counting objects: 3184, done. |
- |
-|
1065 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1066 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1067 | Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done. |
- |
-|
1068 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1069 | |
- |
-|
1070 | Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule — you get this error: |
- |
-|
1071 | |
- |
-|
1072 | $ git checkout master |
- |
-|
1073 | error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge. |
- |
-|
1074 | |
- |
-|
1075 | You have to move the `rack` submodule directory out of the way before you can switch to a branch that doesn’t have it: |
- |
-|
1076 | |
- |
-|
1077 | $ mv rack /tmp/ |
- |
-|
1078 | $ git checkout master |
- |
-|
1079 | Switched to branch "master" |
- |
-|
1080 | $ ls |
- |
-|
1081 | README rack |
- |
-|
1082 | |
- |
-|
1083 | Then, when you switch back, you get an empty `rack` directory. You can either run `git submodule update` to reclone, or you can move your `/tmp/rack` directory back into the empty directory. |
- |
-|
1084 | |
- |
-|
1085 | ## Subtree Merging ## |
- |
-|
1086 | |
- |
-|
1087 | Now that you’ve seen the difficulties of the submodule system, let’s look at an alternate way to solve the same problem. When Git merges, it looks at what it has to merge together and then chooses an appropriate merging strategy to use. If you’re merging two branches, Git uses a _recursive_ strategy. If you’re merging more than two branches, Git picks the _octopus_ strategy. These strategies are automatically chosen for you because the recursive strategy can handle complex three-way merge situations — for example, more than one common ancestor — but it can only handle merging two branches. The octopus merge can handle multiple branches but is more cautious to avoid difficult conflicts, so it’s chosen as the default strategy if you’re trying to merge more than two branches. |
- |
-|
1088 | |
- |
-|
1089 | However, there are other strategies you can choose as well. One of them is the _subtree_ merge, and you can use it to deal with the subproject issue. Here you’ll see how to do the same rack embedding as in the last section, but using subtree merges instead. |
- |
-|
1090 | |
- |
-|
1091 | The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one and vice versa. When you specify a subtree merge, Git is smart enough to figure out that one is a subtree of the other and merge appropriately — it’s pretty amazing. |
- |
-|
1092 | |
- |
-|
1093 | You first add the Rack application to your project. You add the Rack project as a remote reference in your own project and then check it out into its own branch: |
- |
-|
1094 | |
- |
-|
1095 | $ git remote add rack_remote git@github.com:schacon/rack.git |
- |
-|
1096 | $ git fetch rack_remote |
- |
-|
1097 | warning: no common commits |
- |
-|
1098 | remote: Counting objects: 3184, done. |
- |
-|
1099 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1100 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1101 | Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. |
- |
-|
1102 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1103 | From git@github.com:schacon/rack |
- |
-|
1104 | * [new branch] build -> rack_remote/build |
- |
-|
1105 | * [new branch] master -> rack_remote/master |
- |
-|
1106 | * [new branch] rack-0.4 -> rack_remote/rack-0.4 |
- |
-|
1107 | * [new branch] rack-0.9 -> rack_remote/rack-0.9 |
- |
-|
1108 | $ git checkout -b rack_branch rack_remote/master |
- |
-|
1109 | Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. |
- |
-|
1110 | Switched to a new branch "rack_branch" |
- |
-|
1111 | |
- |
-|
1112 | Now you have the root of the Rack project in your `rack_branch` branch and your own project in the `master` branch. If you check out one and then the other, you can see that they have different project roots: |
- |
-|
1113 | |
- |
-|
1114 | $ ls |
- |
-|
1115 | AUTHORS KNOWN-ISSUES Rakefile contrib lib |
- |
-|
1116 | COPYING README bin example test |
- |
-|
1117 | $ git checkout master |
- |
-|
1118 | Switched to branch "master" |
- |
-|
1119 | $ ls |
- |
-|
1120 | README |
- |
-|
1121 | |
- |
-|
1122 | You want to pull the Rack project into your `master` project as a subdirectory. You can do that in Git with `git read-tree`. You’ll learn more about `read-tree` and its friends in Chapter 9, but for now know that it reads the root tree of one branch into your current staging area and working directory. You just switched back to your `master` branch, and you pull the `rack` branch into the `rack` subdirectory of your `master` branch of your main project: |
- |
-|
1123 | |
- |
-|
1124 | $ git read-tree --prefix=rack/ -u rack_branch |
- |
-|
1125 | |
- |
-|
1126 | When you commit, it looks like you have all the Rack files under that subdirectory — as though you copied them in from a tarball. What gets interesting is that you can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, you can pull in upstream changes by switching to that branch and pulling: |
- |
-|
1127 | |
- |
-|
1128 | $ git checkout rack_branch |
- |
-|
1129 | $ git pull |
- |
-|
1130 | |
- |
-|
1131 | Then, you can merge those changes back into your master branch. You can use `git merge -s subtree` and it will work fine; but Git will also merge the histories together, which you probably don’t want. To pull in the changes and prepopulate the commit message, use the `--squash` and `--no-commit` options as well as the `-s subtree` strategy option: |
- |
-|
1132 | |
- |
-|
1133 | $ git checkout master |
- |
-|
1134 | $ git merge --squash -s subtree --no-commit rack_branch |
- |
-|
1135 | Squash commit -- not updating HEAD |
- |
-|
1136 | Automatic merge went well; stopped before committing as requested |
- |
-|
1137 | |
- |
-|
1138 | All the changes from your Rack project are merged in and ready to be committed locally. You can also do the opposite — make changes in the `rack` subdirectory of your master branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. |
- |
-|
1139 | |
- |
-|
1140 | To get a diff between what you have in your `rack` subdirectory and the code in your `rack_branch` branch — to see if you need to merge them — you can’t use the normal `diff` command. Instead, you must run `git diff-tree` with the branch you want to compare to: |
- |
-|
1141 | |
- |
-|
1142 | $ git diff-tree -p rack_branch |
- |
-|
1143 | |
- |
-|
1144 | Or, to compare what is in your `rack` subdirectory with what the `master` branch on the server was the last time you fetched, you can run |
- |
-|
1145 | |
- |
-|
1146 | $ git diff-tree -p rack_remote/master |
- |
-|
1147 | |
- |
-|
1148 | ## Summary ## |
- |
-|
1149 | |
- |
-|
1150 | You’ve seen a number of advanced tools that allow you to manipulate your commits and staging area more precisely. When you notice issues, you should be able to easily figure out what commit introduced them, when, and by whom. If you want to use subprojects in your project, you’ve learned a few ways to accommodate those needs. At this point, you should be able to do most of the things in Git that you’ll need on the command line day to day and feel comfortable doing so. |
- |
-|
1151 | |
-116 | |
-
C:\Users\15625\Documents\Git\progit\en\06-git-tools\01-chapter6.markdown | -C:\Users\15625\Documents\Git\progit2-ja\book\07-git-tools\sections\interactive-staging.asc | -||
---|---|---|---|
.1 | # Git Tools # |
-1 | [[_interactive_staging]] |
-
2 | |
- |
-|
3 | 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. |
- |
-|
4 | |
- |
-|
5 | 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. |
- |
-|
6 | |
- |
-|
7 | ## Revision Selection ## |
- |
-|
8 | |
- |
-|
9 | 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. |
- |
-|
10 | |
- |
-|
11 | ### Single Revisions ### |
- |
-|
12 | |
- |
-|
13 | 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. |
- |
-|
14 | |
- |
-|
15 | ### Short SHA ### |
- |
-|
16 | |
- |
-|
17 | 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. |
- |
-|
18 | |
- |
-|
19 | For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: |
- |
-|
20 | |
- |
-|
21 | $ git log |
- |
-|
22 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
23 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
24 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
25 | |
- |
-|
26 | fixed refs handling, added gc auto, updated tests |
- |
-|
27 | |
- |
-|
28 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
29 | Merge: 1c002dd... 35cfb2b... |
- |
-|
30 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
31 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
32 | |
- |
-|
33 | Merge commit 'phedders/rdocs' |
- |
-|
34 | |
- |
-|
35 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
36 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
37 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
38 | |
- |
-|
39 | added some blame and merge stuff |
- |
-|
40 | |
- |
-|
41 | In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): |
- |
-|
42 | |
- |
-|
43 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
44 | $ git show 1c002dd4b536e7479f |
- |
-|
45 | $ git show 1c002d |
- |
-|
46 | |
- |
-|
47 | 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: |
- |
-|
48 | |
- |
-|
49 | $ git log --abbrev-commit --pretty=oneline |
- |
-|
50 | ca82a6d changed the version number |
- |
-|
51 | 085bb3b removed unnecessary test code |
- |
-|
52 | a11bef0 first commit |
- |
-|
53 | |
- |
-|
54 | 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. |
- |
-|
55 | |
- |
-|
56 | ### A SHORT NOTE ABOUT SHA-1 ### |
- |
-|
57 | |
- |
-|
58 | 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? |
- |
-|
59 | |
- |
-|
60 | 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. |
- |
-|
61 | |
- |
-|
62 | 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. |
- |
-|
63 | |
- |
-|
64 | 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. 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. |
- |
-|
65 | |
- |
-|
66 | ### Branch References ### |
- |
-|
67 | |
- |
-|
68 | The most straightforward way to specify a commit requires that it have 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`: |
- |
-|
69 | |
- |
-|
70 | $ git show ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
71 | $ git show topic1 |
- |
-|
72 | |
- |
-|
73 | 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 Chapter 9 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. |
- |
-|
74 | |
- |
-|
75 | $ git rev-parse topic1 |
- |
-|
76 | ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
77 | |
- |
-|
78 | ### RefLog Shortnames ### |
- |
-|
79 | |
- |
-|
80 | 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. |
- |
-|
81 | |
- |
-|
82 | You can see your reflog by using `git reflog`: |
- |
-|
83 | |
- |
-|
84 | $ git reflog |
- |
-|
85 | 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated |
- |
-|
86 | d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. |
- |
-|
87 | 1c002dd HEAD@{2}: commit: added some blame and merge stuff |
- |
-|
88 | 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD |
- |
-|
89 | 95df984 HEAD@{4}: commit: # This is a combination of two commits. |
- |
-|
90 | 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD |
- |
-|
91 | 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD |
- |
-|
92 | |
- |
-|
93 | 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: |
- |
-|
94 | |
- |
-|
95 | $ git show HEAD@{5} |
- |
-|
96 | |
- |
-|
97 | 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 |
- |
-|
98 | |
- |
-|
99 | $ git show master@{yesterday} |
- |
-|
100 | |
- |
-|
101 | 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. |
- |
-|
102 | |
- |
-|
103 | To see reflog information formatted like the `git log` output, you can run `git log -g`: |
- |
-|
104 | |
- |
-|
105 | $ git log -g master |
- |
-|
106 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
107 | Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) |
- |
-|
108 | Reflog message: commit: fixed refs handling, added gc auto, updated |
- |
-|
109 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
110 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
111 | |
- |
-|
112 | fixed refs handling, added gc auto, updated tests |
- |
-|
113 | |
- |
-|
114 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
115 | Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) |
- |
-|
116 | Reflog message: merge phedders/rdocs: Merge made by recursive. |
- |
-|
117 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
118 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
119 | |
- |
-|
120 | Merge commit 'phedders/rdocs' |
- |
-|
121 | |
- |
-|
122 | 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. |
- |
-|
123 | |
- |
-|
124 | ### Ancestry References ### |
- |
-|
125 | |
- |
-|
126 | 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. |
- |
-|
127 | Suppose you look at the history of your project: |
- |
-|
128 | |
- |
-|
129 | $ git log --pretty=format:'%h %s' --graph |
- |
-|
130 | * 734713b fixed refs handling, added gc auto, updated tests |
- |
-|
131 | * d921970 Merge commit 'phedders/rdocs' |
- |
-|
132 | |\ |
- |
-|
133 | | * 35cfb2b Some rdoc changes |
- |
-|
134 | * | 1c002dd added some blame and merge stuff |
- |
-|
135 | |/ |
- |
-|
136 | * 1c36188 ignore *.gem |
- |
-|
137 | * 9b29157 add open3_detach to gemspec file list |
- |
-|
138 | |
- |
-|
139 | Then, you can see the previous commit by specifying `HEAD^`, which means "the parent of HEAD": |
- |
-|
140 | |
- |
-|
141 | $ git show HEAD^ |
- |
-|
142 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
143 | Merge: 1c002dd... 35cfb2b... |
- |
-|
144 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
145 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
146 | |
- |
-|
147 | Merge commit 'phedders/rdocs' |
- |
-|
148 | |
- |
-|
149 | 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: |
- |
-|
150 | |
- |
-|
151 | $ git show d921970^ |
- |
-|
152 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
153 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
154 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
155 | |
- |
-|
156 | added some blame and merge stuff |
- |
-|
157 | |
- |
-|
158 | $ git show d921970^2 |
- |
-|
159 | commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 |
- |
-|
160 | Author: Paul Hedderly <paul+git@mjr.org> |
- |
-|
161 | Date: Wed Dec 10 22:22:03 2008 +0000 |
- |
-|
162 | |
- |
-|
163 | Some rdoc changes |
- |
-|
164 | |
- |
-|
165 | 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 |
- |
-|
166 | |
- |
-|
167 | $ git show HEAD~3 |
- |
-|
168 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
169 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
170 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
171 | |
- |
-|
172 | ignore *.gem |
- |
-|
173 | |
- |
-|
174 | This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: |
- |
-|
175 | |
- |
-|
176 | $ git show HEAD^^^ |
- |
-|
177 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
178 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
179 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
180 | |
- |
-|
181 | ignore *.gem |
- |
-|
182 | |
- |
-|
183 | 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. |
- |
-|
184 | |
- |
-|
185 | ### Commit Ranges ### |
- |
-|
186 | |
- |
-|
187 | 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?" |
- |
-|
188 | |
- |
-|
189 | #### Double Dot #### |
- |
-|
190 | |
- |
-|
191 | 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 Figure 6-1. |
- |
-|
192 | |
- |
-|
193 | Insert 18333fig0601.png |
- |
-|
194 | Figure 6-1. Example history for range selection. |
- |
-|
195 | |
- |
-|
196 | 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: |
- |
-|
197 | |
- |
-|
198 | $ git log master..experiment |
- |
-|
199 | D |
- |
-|
200 | C |
- |
-|
201 | |
- |
-|
202 | 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`: |
- |
-|
203 | |
- |
-|
204 | $ git log experiment..master |
- |
-|
205 | F |
- |
-|
206 | E |
- |
-|
207 | |
- |
-|
208 | 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: |
- |
-|
209 | |
- |
-|
210 | $ git log origin/master..HEAD |
- |
-|
211 | |
- |
-|
212 | 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. |
- |
-|
213 | 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. |
- |
-|
214 | |
- |
-|
215 | #### Multiple Points #### |
- |
-|
216 | |
- |
-|
217 | 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: |
- |
-|
218 | |
- |
-|
219 | $ git log refA..refB |
- |
-|
220 | $ git log ^refA refB |
- |
-|
221 | $ git log refB --not refA |
- |
-|
222 | |
- |
-|
223 | 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: |
- |
-|
224 | |
- |
-|
225 | $ git log refA refB ^refC |
- |
-|
226 | $ git log refA refB --not refC |
- |
-|
227 | |
- |
-|
228 | This makes for a very powerful revision query system that should help you figure out what is in your branches. |
- |
-|
229 | |
- |
-|
230 | #### Triple Dot #### |
- |
-|
231 | |
- |
-|
232 | 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 Figure 6-1. |
- |
-|
233 | If you want to see what is in `master` or `experiment` but not any common references, you can run |
- |
-|
234 | |
- |
-|
235 | $ git log master...experiment |
- |
-|
236 | F |
- |
-|
237 | E |
- |
-|
238 | D |
- |
-|
239 | C |
- |
-|
240 | |
- |
-|
241 | 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. |
- |
-|
242 | |
- |
-|
243 | 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: |
- |
-|
244 | |
- |
-|
245 | $ git log --left-right master...experiment |
- |
-|
246 | < F |
- |
-|
247 | < E |
- |
-|
248 | > D |
- |
-|
249 | > C |
- |
-|
250 | |
- |
-|
251 | With these tools, you can much more easily let Git know what commit or commits you want to inspect. |
- |
-|
252 | |
- |
-|
253 | ## Interactive Staging ## |
-2 | === Interactive Staging |
-
254 | |
-3 | |
-
.255 | Git comes with a couple of scripts that make some command-line tasks easier. Here, you’ll look at a few interactive commands that can help you easily craft your commits to include only certain combinations and parts of files. These tools are very helpful if you modify a bunch of files and then decide that you want those changes to be in several focused commits rather than one big messy commit. This way, you can make sure your commits are logically separate changesets and can be easily reviewed by the developers working with you. |
-4 | Git comes with a couple of scripts that make some command-line tasks easier. Here, you’ll look at a few interactive commands that can help you easily craft your commits to include only certain combinations and parts of files. These tools are very helpful if you modify a bunch of files and then decide that you want those changes to be in several focused commits rather than one big messy commit. 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: |
-
256 | If you run `git add` with the `-i` or `--interactive` option, Git goes into an interactive shell mode, displaying something like this: |
- |
-|
257 | |
-5 | |
-
. | |
-6 | [source,console] |
-
|
-7 | ---- |
-|
258 | $ git add -i |
-8 | $ git add -i |
-
259 | staged unstaged path |
-9 | staged unstaged path |
-
260 | 1: unchanged +0/-1 TODO |
-10 | 1: unchanged +0/-1 TODO |
-
261 | 2: unchanged +1/-1 index.html |
-11 | 2: unchanged +1/-1 index.html |
-
262 | 3: unchanged +5/-1 lib/simplegit.rb |
-12 | 3: unchanged +5/-1 lib/simplegit.rb |
-
263 | |
-13 | |
-
264 | *** Commands *** |
-14 | *** Commands *** |
-
265 | 1: status 2: update 3: revert 4: add untracked |
-15 | 1: status 2: update 3: revert 4: add untracked |
-
266 | 5: patch 6: diff 7: quit 8: help |
-16 | 5: patch 6: diff 7: quit 8: help |
-
267 | What now> |
-17 | What now> |
-
|
-18 | ---- |
-|
268 | |
-19 | |
-
.269 | 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. |
-20 | 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. |
-
270 | |
-21 | |
-
271 | After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. |
-22 | After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. |
-
272 | |
-23 | |
-
.273 | ### Staging and Unstaging Files ### |
-24 | ==== Staging and Unstaging Files |
-
274 | |
-25 | |
-
275 | If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: |
-26 | If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: |
-
276 | |
-27 | |
-
. | |
-28 | [source,console] |
-
|
-29 | ---- |
-|
277 | What now> 2 |
-30 | What now> 2 |
-
278 | staged unstaged path |
-31 | staged unstaged path |
-
279 | 1: unchanged +0/-1 TODO |
-32 | 1: unchanged +0/-1 TODO |
-
280 | 2: unchanged +1/-1 index.html |
-33 | 2: unchanged +1/-1 index.html |
-
281 | 3: unchanged +5/-1 lib/simplegit.rb |
-34 | 3: unchanged +5/-1 lib/simplegit.rb |
-
282 | Update>> |
-35 | Update>> |
-
|
-36 | ---- |
-|
283 | |
-37 | |
-
284 | To stage the TODO and index.html files, you can type the numbers: |
-38 | To stage the TODO and index.html files, you can type the numbers: |
-
285 | |
-39 | |
-
. | |
-40 | [source,console] |
-
|
-41 | ---- |
-|
286 | Update>> 1,2 |
-42 | Update>> 1,2 |
-
287 | staged unstaged path |
-43 | staged unstaged path |
-
288 | * 1: unchanged +0/-1 TODO |
-44 | * 1: unchanged +0/-1 TODO |
-
289 | * 2: unchanged +1/-1 index.html |
-45 | * 2: unchanged +1/-1 index.html |
-
290 | 3: unchanged +5/-1 lib/simplegit.rb |
-46 | 3: unchanged +5/-1 lib/simplegit.rb |
-
291 | Update>> |
-47 | Update>> |
-
|
-48 | ---- |
-|
292 | |
-49 | |
-
293 | 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: |
-50 | 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: |
-
294 | |
-51 | |
-
. | |
-52 | [source,console] |
-
|
-53 | ---- |
-|
295 | Update>> |
-54 | Update>> |
-
296 | updated 2 paths |
-55 | updated 2 paths |
-
297 | |
-56 | |
-
298 | *** Commands *** |
-57 | *** Commands *** |
-
299 | 1: status 2: update 3: revert 4: add untracked |
-58 | 1: status 2: update 3: revert 4: add untracked |
-
300 | 5: patch 6: diff 7: quit 8: help |
-59 | 5: patch 6: diff 7: quit 8: help |
-
301 | What now> 1 |
-60 | What now> 1 |
-
302 | staged unstaged path |
-61 | staged unstaged path |
-
303 | 1: +0/-1 nothing TODO |
-62 | 1: +0/-1 nothing TODO |
-
304 | 2: +1/-1 nothing index.html |
-63 | 2: +1/-1 nothing index.html |
-
305 | 3: unchanged +5/-1 lib/simplegit.rb |
-64 | 3: unchanged +5/-1 lib/simplegit.rb |
-
|
-65 | ---- |
-|
306 | |
-66 | |
-
307 | 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: |
-67 | 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: |
-
308 | |
-68 | |
-
.309 | *** Commands *** |
-69 | [source,console] |
-
310 | 1: status 2: update 3: revert 4: add untracked |
-70 | ---- |
-
311 | 5: patch 6: diff 7: quit 8: help |
-71 | *** Commands *** |
-
312 | What now> 3 |
-72 | 1: status 2: update 3: revert 4: add untracked |
-
313 | staged unstaged path |
-73 | 5: patch 6: diff 7: quit 8: help |
-
314 | 1: +0/-1 nothing TODO |
-74 | What now> 3 |
-
315 | 2: +1/-1 nothing index.html |
-75 | staged unstaged path |
-
316 | 3: unchanged +5/-1 lib/simplegit.rb |
-76 | 1: +0/-1 nothing TODO |
-
317 | Revert>> 1 |
-77 | 2: +1/-1 nothing index.html |
-
318 | staged unstaged path |
-78 | 3: unchanged +5/-1 lib/simplegit.rb |
-
319 | * 1: +0/-1 nothing TODO |
-79 | Revert>> 1 |
-
320 | 2: +1/-1 nothing index.html |
-80 | staged unstaged path |
-
321 | 3: unchanged +5/-1 lib/simplegit.rb |
-81 | * 1: +0/-1 nothing TODO |
-
322 | Revert>> [enter] |
-82 | 2: +1/-1 nothing index.html |
-
323 | reverted one path |
-83 | 3: unchanged +5/-1 lib/simplegit.rb |
-
|
-84 | Revert>> [enter] |
-|
|
-85 | reverted one path |
-|
|
-86 | ---- |
-|
324 | |
-87 | |
-
325 | Looking at your Git status again, you can see that you’ve unstaged the TODO file: |
-88 | Looking at your Git status again, you can see that you’ve unstaged the TODO file: |
-
326 | |
-89 | |
-
. | |
-90 | [source,console] |
-
|
-91 | ---- |
-|
327 | *** Commands *** |
-92 | *** Commands *** |
-
328 | 1: status 2: update 3: revert 4: add untracked |
-93 | 1: status 2: update 3: revert 4: add untracked |
-
329 | 5: patch 6: diff 7: quit 8: help |
-94 | 5: patch 6: diff 7: quit 8: help |
-
330 | What now> 1 |
-95 | What now> 1 |
-
331 | staged unstaged path |
-96 | staged unstaged path |
-
332 | 1: unchanged +0/-1 TODO |
-97 | 1: unchanged +0/-1 TODO |
-
333 | 2: +1/-1 nothing index.html |
-98 | 2: +1/-1 nothing index.html |
-
334 | 3: unchanged +5/-1 lib/simplegit.rb |
-99 | 3: unchanged +5/-1 lib/simplegit.rb |
-
|
-100 | ---- |
-|
335 | |
-101 | |
-
336 | 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: |
-102 | 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: |
-
337 | |
-103 | |
-
. | |
-104 | [source,console] |
-
|
-105 | ---- |
-|
338 | *** Commands *** |
-106 | *** Commands *** |
-
339 | 1: status 2: update 3: revert 4: add untracked |
-107 | 1: status 2: update 3: revert 4: add untracked |
-
340 | 5: patch 6: diff 7: quit 8: help |
-108 | 5: patch 6: diff 7: quit 8: help |
-
341 | What now> 6 |
-109 | What now> 6 |
-
342 | staged unstaged path |
-110 | staged unstaged path |
-
343 | 1: +1/-1 nothing index.html |
-111 | 1: +1/-1 nothing index.html |
-
344 | Review diff>> 1 |
-112 | Review diff>> 1 |
-
345 | diff --git a/index.html b/index.html |
-113 | diff --git a/index.html b/index.html |
-
346 | index 4d07108..4335f49 100644 |
-114 | index 4d07108..4335f49 100644 |
-
347 | --- a/index.html |
-115 | --- a/index.html |
-
348 | +++ b/index.html |
-116 | +++ b/index.html |
-
349 | @@ -16,7 +16,7 @@ Date Finder |
-117 | @@ -16,7 +16,7 @@ Date Finder |
-
350 | |
-118 | |
-
.351 | <p id="out">...</p> |
-119 | <p id="out">...</p> |
-
352 | |
-120 | |
-
.353 | -<div id="footer">contact : support@github.com</div> |
-121 | -<div id="footer">contact : support@github.com</div> |
-
354 | +<div id="footer">contact : email.support@github.com</div> |
-122 | +<div id="footer">contact : email.support@github.com</div> |
-
355 | |
-123 | |
-
.356 | <script type="text/javascript"> |
-124 | <script type="text/javascript"> |
-
|
-125 | ---- |
-|
357 | |
-126 | |
-
358 | With these basic commands, you can use the interactive add mode to deal with your staging area a little more easily. |
-127 | With these basic commands, you can use the interactive add mode to deal with your staging area a little more easily. |
-
359 | |
-128 | |
-
.360 | ### Staging Patches ### |
-129 | ==== Staging Patches |
-
361 | |
-130 | |
-
362 | It’s also possible for Git to stage certain parts of files and not the rest. For example, if you make two changes to your simplegit.rb file and want to stage one of them and not the other, doing so is very easy in Git. From the interactive prompt, type `5` or `p` (for patch). Git will ask you which files you would like to partially stage; then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one: |
-131 | It’s also possible for Git to stage certain parts of files and not the rest. For example, if you make two changes to your simplegit.rb file and want to stage one of them and not the other, doing so is very easy in Git. From the interactive prompt, type `5` or `p` (for patch). Git will ask you which files you would like to partially stage; then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one: |
-
363 | |
-132 | |
-
.364 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
-133 | [source,console] |
-
365 | index dd5ecc4..57399e0 100644 |
-134 | ---- |
-
366 | --- a/lib/simplegit.rb |
-135 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
-
367 | +++ b/lib/simplegit.rb |
-136 | index dd5ecc4..57399e0 100644 |
-
368 | @@ -22,7 +22,7 @@ class SimpleGit |
-137 | --- a/lib/simplegit.rb |
-
369 | end |
-138 | +++ b/lib/simplegit.rb |
-
370 | |
-139 | @@ -22,7 +22,7 @@ class SimpleGit |
-
371 | def log(treeish = 'master') |
-140 | end |
-
372 | - command("git log -n 25 #{treeish}") |
-141 | |
-
373 | + command("git log -n 30 #{treeish}") |
-142 | def log(treeish = 'master') |
-
374 | end |
-143 | - command("git log -n 25 #{treeish}") |
-
375 | |
-144 | + command("git log -n 30 #{treeish}") |
-
376 | def blame(path) |
-145 | end |
-
377 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? |
-146 | |
-
|
-147 | def blame(path) |
-|
|
-148 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? |
-|
|
-149 | ---- |
-|
378 | |
-150 | |
-
379 | You have a lot of options at this point. Typing `?` shows a list of what you can do: |
-151 | You have a lot of options at this point. Typing `?` shows a list of what you can do: |
-
380 | |
-152 | |
-
.381 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? |
-153 | [source,console] |
-
382 | y - stage this hunk |
-154 | ---- |
-
383 | n - do not stage this hunk |
-155 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? |
-
384 | a - stage this and all the remaining hunks in the file |
-156 | y - stage this hunk |
-
385 | d - do not stage this hunk nor any of the remaining hunks in the file |
-157 | n - do not stage this hunk |
-
386 | g - select a hunk to go to |
-158 | a - stage this and all the remaining hunks in the file |
-
387 | / - search for a hunk matching the given regex |
-159 | d - do not stage this hunk nor any of the remaining hunks in the file |
-
388 | j - leave this hunk undecided, see next undecided hunk |
-160 | g - select a hunk to go to |
-
389 | J - leave this hunk undecided, see next hunk |
-161 | / - search for a hunk matching the given regex |
-
390 | k - leave this hunk undecided, see previous undecided hunk |
-162 | j - leave this hunk undecided, see next undecided hunk |
-
391 | K - leave this hunk undecided, see previous hunk |
-163 | J - leave this hunk undecided, see next hunk |
-
392 | s - split the current hunk into smaller hunks |
-164 | k - leave this hunk undecided, see previous undecided hunk |
-
393 | e - manually edit the current hunk |
-165 | K - leave this hunk undecided, see previous hunk |
-
394 | ? - print help |
-166 | s - split the current hunk into smaller hunks |
-
|
-167 | e - manually edit the current hunk |
-|
|
-168 | ? - print help |
-|
|
-169 | ---- |
-|
395 | |
-170 | |
-
396 | Generally, you’ll type `y` or `n` if you want to stage each hunk, but staging all of them in certain files or skipping a hunk decision until later can be helpful too. If you stage one part of the file and leave another part unstaged, your status output will look like this: |
-171 | Generally, you’ll type `y` or `n` if you want to stage each hunk, but staging all of them in certain files or skipping a hunk decision until later can be helpful too. If you stage one part of the file and leave another part unstaged, your status output will look like this: |
-
397 | |
-172 | |
-
. | |
-173 | [source,console] |
-
|
-174 | ---- |
-|
398 | What now> 1 |
-175 | What now> 1 |
-
399 | staged unstaged path |
-176 | staged unstaged path |
-
400 | 1: unchanged +0/-1 TODO |
-177 | 1: unchanged +0/-1 TODO |
-
401 | 2: +1/-1 nothing index.html |
-178 | 2: +1/-1 nothing index.html |
-
402 | 3: +1/-1 +4/-0 lib/simplegit.rb |
-179 | 3: +1/-1 +4/-0 lib/simplegit.rb |
-
|
-180 | ---- |
-|
403 | |
-181 | |
-
404 | The status of the simplegit.rb file is interesting. It shows you that a couple of lines are staged and a couple are unstaged. You’ve partially staged this file. At this point, you can exit the interactive adding script and run `git commit` to commit the partially staged files. |
-182 | The status of the simplegit.rb file is interesting. It shows you that a couple of lines are staged and a couple are unstaged. You’ve partially staged this file. At this point, you can exit the interactive adding script and run `git commit` to commit the partially staged files. |
-
405 | |
-183 | |
-
.406 | Finally, you don’t need to be in interactive add mode to do the partial-file staging — you can start the same script by using `git add -p` or `git add --patch` on the command line. |
-184 | You also don’t need to be in interactive add mode to do the partial-file staging – you can start the same script by using `git add -p` or `git add --patch` on the command line. |
-
407 | |
-185 | |
-
.408 | |
-186 | Furthermore, you can use patch mode for partially resetting files with the `reset --patch` command, for checking out parts of files with the `checkout --patch` command and for stashing parts of files with the `stash save --patch` command. We'll go into more details on each of these as we get to more advanced usages of these commands. |
-
409 | ## Stashing ## |
- |
-|
410 | |
- |
-|
411 | Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the `git stash` command. |
- |
-|
412 | |
- |
-|
413 | Stashing takes the dirty state of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time. |
- |
-|
414 | |
- |
-|
415 | ### Stashing Your Work ### |
- |
-|
416 | |
- |
-|
417 | To demonstrate, you’ll go into your project and start working on a couple of files and possibly stage one of the changes. If you run `git status`, you can see your dirty state: |
- |
-|
418 | |
- |
-|
419 | $ git status |
- |
-|
420 | # On branch master |
- |
-|
421 | # Changes to be committed: |
- |
-|
422 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
423 | # |
- |
-|
424 | # modified: index.html |
- |
-|
425 | # |
- |
-|
426 | # Changes not staged for commit: |
- |
-|
427 | # (use "git add <file>..." to update what will be committed) |
- |
-|
428 | # |
- |
-|
429 | # modified: lib/simplegit.rb |
- |
-|
430 | # |
- |
-|
431 | |
- |
-|
432 | Now you want to switch branches, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run `git stash`: |
- |
-|
433 | |
- |
-|
434 | $ git stash |
- |
-|
435 | Saved working directory and index state \ |
- |
-|
436 | "WIP on master: 049d078 added the index file" |
- |
-|
437 | HEAD is now at 049d078 added the index file |
- |
-|
438 | (To restore them type "git stash apply") |
- |
-|
439 | |
- |
-|
440 | Your working directory is clean: |
- |
-|
441 | |
- |
-|
442 | $ git status |
- |
-|
443 | # On branch master |
- |
-|
444 | nothing to commit, working directory clean |
- |
-|
445 | |
- |
-|
446 | At this point, you can easily switch branches and do work elsewhere; your changes are stored on your stack. To see which stashes you’ve stored, you can use `git stash list`: |
- |
-|
447 | |
- |
-|
448 | $ git stash list |
- |
-|
449 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
450 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
451 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
452 | |
- |
-|
453 | In this case, two stashes were done previously, so you have access to three different stashed works. You can reapply the one you just stashed by using the command shown in the help output of the original stash command: `git stash apply`. If you want to apply one of the older stashes, you can specify it by naming it, like this: `git stash apply stash@{2}`. If you don’t specify a stash, Git assumes the most recent stash and tries to apply it: |
- |
-|
454 | |
- |
-|
455 | $ git stash apply |
- |
-|
456 | # On branch master |
- |
-|
457 | # Changes not staged for commit: |
- |
-|
458 | # (use "git add <file>..." to update what will be committed) |
- |
-|
459 | # |
- |
-|
460 | # modified: index.html |
- |
-|
461 | # modified: lib/simplegit.rb |
- |
-|
462 | # |
- |
-|
463 | |
- |
-|
464 | You can see that Git re-modifies the files you uncommitted when you saved the stash. In this case, you had a clean working directory when you tried to apply the stash, and you tried to apply it on the same branch you saved it from; but having a clean working directory and applying it on the same branch aren’t necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash — Git gives you merge conflicts if anything no longer applies cleanly. |
- |
-|
465 | |
- |
-|
466 | The changes to your files were reapplied, but the file you staged before wasn’t restaged. To do that, you must run the `git stash apply` command with a `--index` option to tell the command to try to reapply the staged changes. If you had run that instead, you’d have gotten back to your original position: |
- |
-|
467 | |
- |
-|
468 | $ git stash apply --index |
- |
-|
469 | # On branch master |
- |
-|
470 | # Changes to be committed: |
- |
-|
471 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
472 | # |
- |
-|
473 | # modified: index.html |
- |
-|
474 | # |
- |
-|
475 | # Changes not staged for commit: |
- |
-|
476 | # (use "git add <file>..." to update what will be committed) |
- |
-|
477 | # |
- |
-|
478 | # modified: lib/simplegit.rb |
- |
-|
479 | # |
- |
-|
480 | |
- |
-|
481 | The apply option only tries to apply the stashed work — you continue to have it on your stack. To remove it, you can run `git stash drop` with the name of the stash to remove: |
- |
-|
482 | |
- |
-|
483 | $ git stash list |
- |
-|
484 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
485 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
486 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
487 | $ git stash drop stash@{0} |
- |
-|
488 | Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) |
- |
-|
489 | |
- |
-|
490 | You can also run `git stash pop` to apply the stash and then immediately drop it from your stack. |
- |
-|
491 | |
- |
-|
492 | ### Un-applying a Stash ### |
- |
-|
493 | |
- |
-|
494 | In some use case scenarios you might want to apply stashed changes, do some work, but then un-apply those changes that originally came from the stash. Git does not provide such a `stash unapply` command, but it is possible to achieve the effect by simply retrieving the patch associated with a stash and applying it in reverse: |
- |
-|
495 | |
- |
-|
496 | $ git stash show -p stash@{0} | git apply -R |
- |
-|
497 | |
- |
-|
498 | Again, if you don’t specify a stash, Git assumes the most recent stash: |
- |
-|
499 | |
- |
-|
500 | $ git stash show -p | git apply -R |
- |
-|
501 | |
- |
-|
502 | You may want to create an alias and effectively add a `stash-unapply` command to your Git. For example: |
- |
-|
503 | |
- |
-|
504 | $ git config --global alias.stash-unapply '!git stash show -p | git apply -R' |
- |
-|
505 | $ git stash apply |
- |
-|
506 | $ #... work work work |
- |
-|
507 | $ git stash-unapply |
- |
-|
508 | |
- |
-|
509 | ### Creating a Branch from a Stash ### |
- |
-|
510 | |
- |
-|
511 | If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you’ve since modified, you’ll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run `git stash branch`, which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully: |
- |
-|
512 | |
- |
-|
513 | $ git stash branch testchanges |
- |
-|
514 | Switched to a new branch "testchanges" |
- |
-|
515 | # On branch testchanges |
- |
-|
516 | # Changes to be committed: |
- |
-|
517 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
518 | # |
- |
-|
519 | # modified: index.html |
- |
-|
520 | # |
- |
-|
521 | # Changes not staged for commit: |
- |
-|
522 | # (use "git add <file>..." to update what will be committed) |
- |
-|
523 | # |
- |
-|
524 | # modified: lib/simplegit.rb |
- |
-|
525 | # |
- |
-|
526 | Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) |
- |
-|
527 | |
- |
-|
528 | This is a nice shortcut to recover stashed work easily and work on it in a new branch. |
- |
-|
529 | |
- |
-|
530 | ## Rewriting History ## |
- |
-|
531 | |
- |
-|
532 | Many times, when working with Git, you may want to revise your commit history for some reason. One of the great things about Git is that it allows you to make decisions at the last possible moment. You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with the stash command, and you can rewrite commits that already happened so they look like they happened in a different way. This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others. |
- |
-|
533 | |
- |
-|
534 | In this section, you’ll cover how to accomplish these very useful tasks so that you can make your commit history look the way you want before you share it with others. |
- |
-|
535 | |
- |
-|
536 | ### Changing the Last Commit ### |
- |
-|
537 | |
- |
-|
538 | Changing your last commit is probably the most common rewriting of history that you’ll do. You’ll often want to do two basic things to your last commit: change the commit message, or change the snapshot you just recorded by adding, changing and removing files. |
- |
-|
539 | |
- |
-|
540 | If you only want to modify your last commit message, it’s very simple: |
- |
-|
541 | |
- |
-|
542 | $ git commit --amend |
- |
-|
543 | |
- |
-|
544 | That drops you into your text editor, which has your last commit message in it, ready for you to modify the message. When you save and close the editor, the editor writes a new commit containing that message and makes it your new last commit. |
- |
-|
545 | |
- |
-|
546 | If you’ve committed and then you want to change the snapshot you committed by adding or changing files, possibly because you forgot to add a newly created file when you originally committed, the process works basically the same way. You stage the changes you want by editing a file and running `git add` on it or `git rm` to a tracked file, and the subsequent `git commit --amend` takes your current staging area and makes it the snapshot for the new commit. |
- |
-|
547 | |
- |
-|
548 | You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it. |
- |
-|
549 | |
- |
-|
550 | ### Changing Multiple Commit Messages ### |
- |
-|
551 | |
- |
-|
552 | To modify a commit that is farther back in your history, you must move to more complex tools. Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD they were originally based on instead of moving them to another one. With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the `-i` option to `git rebase`. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto. |
- |
-|
553 | |
- |
-|
554 | For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to `git rebase -i` the parent of the last commit you want to edit, which is `HEAD~2^` or `HEAD~3`. It may be easier to remember the `~3` because you’re trying to edit the last three commits; but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit: |
- |
-|
555 | |
- |
-|
556 | $ git rebase -i HEAD~3 |
- |
-|
557 | |
- |
-|
558 | Remember again that this is a rebasing command — every commit included in the range `HEAD~3..HEAD` will be rewritten, whether you change the message or not. Don’t include any commit you’ve already pushed to a central server — doing so will confuse other developers by providing an alternate version of the same change. |
- |
-|
559 | |
- |
-|
560 | Running this command gives you a list of commits in your text editor that looks something like this: |
- |
-|
561 | |
- |
-|
562 | pick f7f3f6d changed my name a bit |
- |
-|
563 | pick 310154e updated README formatting and added blame |
- |
-|
564 | pick a5f4a0d added cat-file |
- |
-|
565 | |
- |
-|
566 | # Rebase 710f0f8..a5f4a0d onto 710f0f8 |
- |
-|
567 | # |
- |
-|
568 | # Commands: |
- |
-|
569 | # p, pick = use commit |
- |
-|
570 | # r, reword = use commit, but edit the commit message |
- |
-|
571 | # e, edit = use commit, but stop for amending |
- |
-|
572 | # s, squash = use commit, but meld into previous commit |
- |
-|
573 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
574 | # x, exec = run command (the rest of the line) using shell |
- |
-|
575 | # |
- |
-|
576 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
577 | # |
- |
-|
578 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
579 | # |
- |
-|
580 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
581 | # |
- |
-|
582 | # Note that empty commits are commented out |
- |
-|
583 | |
- |
-|
584 | It’s important to note that these commits are listed in the opposite order than you normally see them using the `log` command. If you run a `log`, you see something like this: |
- |
-|
585 | |
- |
-|
586 | $ git log --pretty=format:"%h %s" HEAD~3..HEAD |
- |
-|
587 | a5f4a0d added cat-file |
- |
-|
588 | 310154e updated README formatting and added blame |
- |
-|
589 | f7f3f6d changed my name a bit |
- |
-|
590 | |
- |
-|
591 | Notice the reverse order. The interactive rebase gives you a script that it’s going to run. It will start at the commit you specify on the command line (`HEAD~3`) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that’s the first one it will replay. |
- |
-|
592 | |
- |
-|
593 | You need to edit the script so that it stops at the commit you want to edit. To do so, change the word pick to the word edit for each of the commits you want the script to stop after. For example, to modify only the third commit message, you change the file to look like this: |
- |
-|
594 | |
- |
-|
595 | edit f7f3f6d changed my name a bit |
- |
-|
596 | pick 310154e updated README formatting and added blame |
- |
-|
597 | pick a5f4a0d added cat-file |
- |
-|
598 | |
- |
-|
599 | When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message: |
- |
-|
600 | |
- |
-|
601 | <!-- This is actually weird, as the SHA-1 of 7482e0d is not present in the list, |
- |
-|
602 | nor is the commit message. Please review |
- |
-|
603 | --> |
- |
-|
604 | |
- |
-|
605 | $ git rebase -i HEAD~3 |
- |
-|
606 | Stopped at 7482e0d... updated the gemspec to hopefully work better |
- |
-|
607 | You can amend the commit now, with |
- |
-|
608 | |
- |
-|
609 | git commit --amend |
- |
-|
610 | |
- |
-|
611 | Once you’re satisfied with your changes, run |
- |
-|
612 | |
- |
-|
613 | git rebase --continue |
- |
-|
614 | |
- |
-|
615 | These instructions tell you exactly what to do. Type |
- |
-|
616 | |
- |
-|
617 | $ git commit --amend |
- |
-|
618 | |
- |
-|
619 | Change the commit message, and exit the editor. Then, run |
- |
-|
620 | |
- |
-|
621 | $ git rebase --continue |
- |
-|
622 | |
- |
-|
623 | This command will apply the other two commits automatically, and then you’re done. If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit. Each time, Git will stop, let you amend the commit, and continue when you’re finished. |
- |
-|
624 | |
- |
-|
625 | ### Reordering Commits ### |
- |
-|
626 | |
- |
-|
627 | You can also use interactive rebases to reorder or remove commits entirely. If you want to remove the "added cat-file" commit and change the order in which the other two commits are introduced, you can change the rebase script from this |
- |
-|
628 | |
- |
-|
629 | pick f7f3f6d changed my name a bit |
- |
-|
630 | pick 310154e updated README formatting and added blame |
- |
-|
631 | pick a5f4a0d added cat-file |
- |
-|
632 | |
- |
-|
633 | to this: |
- |
-|
634 | |
- |
-|
635 | pick 310154e updated README formatting and added blame |
- |
-|
636 | pick f7f3f6d changed my name a bit |
- |
-|
637 | |
- |
-|
638 | When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies `310154e` and then `f7f3f6d`, and then stops. You effectively change the order of those commits and remove the "added cat-file" commit completely. |
- |
-|
639 | |
- |
-|
640 | ### Squashing Commits ### |
- |
-|
641 | |
- |
-|
642 | It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool. The script puts helpful instructions in the rebase message: |
- |
-|
643 | |
- |
-|
644 | # |
- |
-|
645 | # Commands: |
- |
-|
646 | # p, pick = use commit |
- |
-|
647 | # r, reword = use commit, but edit the commit message |
- |
-|
648 | # e, edit = use commit, but stop for amending |
- |
-|
649 | # s, squash = use commit, but meld into previous commit |
- |
-|
650 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
651 | # x, exec = run command (the rest of the line) using shell |
- |
-|
652 | # |
- |
-|
653 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
654 | # |
- |
-|
655 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
656 | # |
- |
-|
657 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
658 | # |
- |
-|
659 | # Note that empty commits are commented out |
- |
-|
660 | |
- |
-|
661 | If, instead of "pick" or "edit", you specify "squash", Git applies both that change and the change directly before it and makes you merge the commit messages together. So, if you want to make a single commit from these three commits, you make the script look like this: |
- |
-|
662 | |
- |
-|
663 | pick f7f3f6d changed my name a bit |
- |
-|
664 | squash 310154e updated README formatting and added blame |
- |
-|
665 | squash a5f4a0d added cat-file |
- |
-|
666 | |
- |
-|
667 | When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages: |
- |
-|
668 | |
- |
-|
669 | # This is a combination of 3 commits. |
- |
-|
670 | # The first commit's message is: |
- |
-|
671 | changed my name a bit |
- |
-|
672 | |
- |
-|
673 | # This is the 2nd commit message: |
- |
-|
674 | |
- |
-|
675 | updated README formatting and added blame |
- |
-|
676 | |
- |
-|
677 | # This is the 3rd commit message: |
- |
-|
678 | |
- |
-|
679 | added cat-file |
- |
-|
680 | |
- |
-|
681 | When you save that, you have a single commit that introduces the changes of all three previous commits. |
- |
-|
682 | |
- |
-|
683 | ### Splitting a Commit ### |
- |
-|
684 | |
- |
-|
685 | Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with. For example, suppose you want to split the middle commit of your three commits. Instead of "updated README formatting and added blame", you want to split it into two commits: "updated README formatting" for the first, and "added blame" for the second. You can do that in the `rebase -i` script by changing the instruction on the commit you want to split to "edit": |
- |
-|
686 | |
- |
-|
687 | pick f7f3f6d changed my name a bit |
- |
-|
688 | edit 310154e updated README formatting and added blame |
- |
-|
689 | pick a5f4a0d added cat-file |
- |
-|
690 | |
- |
-|
691 | When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (`f7f3f6d`), applies the second (`310154e`), and drops you to the console. There, you can do a mixed reset of that commit with `git reset HEAD^`, which effectively undoes that commit and leaves the modified files unstaged. Now you can take the changes that have been reset, and create multiple commits out of them. Simply stage and commit files until you have several commits, and run `git rebase --continue` when you’re done: |
- |
-|
692 | |
- |
-|
693 | $ git reset HEAD^ |
- |
-|
694 | $ git add README |
- |
-|
695 | $ git commit -m 'updated README formatting' |
- |
-|
696 | $ git add lib/simplegit.rb |
- |
-|
697 | $ git commit -m 'added blame' |
- |
-|
698 | $ git rebase --continue |
- |
-|
699 | |
- |
-|
700 | Git applies the last commit (`a5f4a0d`) in the script, and your history looks like this: |
- |
-|
701 | |
- |
-|
702 | $ git log -4 --pretty=format:"%h %s" |
- |
-|
703 | 1c002dd added cat-file |
- |
-|
704 | 9b29157 added blame |
- |
-|
705 | 35cfb2b updated README formatting |
- |
-|
706 | f3cc40e changed my name a bit |
- |
-|
707 | |
- |
-|
708 | Once again, this changes the SHAs of all the commits in your list, so make sure no commit shows up in that list that you’ve already pushed to a shared repository. |
- |
-|
709 | |
- |
-|
710 | ### The Nuclear Option: filter-branch ### |
- |
-|
711 | |
- |
-|
712 | There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way — for instance, changing your e-mail address globally or removing a file from every commit. The command is `filter-branch`, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite. However, it can be very useful. You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of. |
- |
-|
713 | |
- |
-|
714 | #### Removing a File from Every Commit #### |
- |
-|
715 | |
- |
-|
716 | This occurs fairly commonly. Someone accidentally commits a huge binary file with a thoughtless `git add .`, and you want to remove it everywhere. Perhaps you accidentally committed a file that contained a password, and you want to make your project open source. `filter-branch` is the tool you probably want to use to scrub your entire history. To remove a file named passwords.txt from your entire history, you can use the `--tree-filter` option to `filter-branch`: |
- |
-|
717 | |
- |
-|
718 | $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD |
- |
-|
719 | Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) |
- |
-|
720 | Ref 'refs/heads/master' was rewritten |
- |
-|
721 | |
- |
-|
722 | The `--tree-filter` option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt from every snapshot, whether it exists or not. If you want to remove all accidentally committed editor backup files, you can run something like `git filter-branch --tree-filter "rm -f *~" HEAD`. |
- |
-|
723 | |
- |
-|
724 | You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end. It’s generally a good idea to do this in a testing branch and then hard-reset your master branch after you’ve determined the outcome is what you really want. To run `filter-branch` on all your branches, you can pass `--all` to the command. |
- |
-|
725 | |
- |
-|
726 | #### Making a Subdirectory the New Root #### |
- |
-|
727 | |
- |
-|
728 | Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk, tags, and so on). If you want to make the `trunk` subdirectory be the new project root for every commit, `filter-branch` can help you do that, too: |
- |
-|
729 | |
- |
-|
730 | $ git filter-branch --subdirectory-filter trunk HEAD |
- |
-|
731 | Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) |
- |
-|
732 | Ref 'refs/heads/master' was rewritten |
- |
-|
733 | |
- |
-|
734 | Now your new project root is what was in the `trunk` subdirectory each time. Git will also automatically remove commits that did not affect the subdirectory. |
- |
-|
735 | |
- |
-|
736 | #### Changing E-Mail Addresses Globally #### |
- |
-|
737 | |
- |
-|
738 | Another common case is that you forgot to run `git config` to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with `filter-branch` as well. You need to be careful to change only the e-mail addresses that are yours, so you use `--commit-filter`: |
- |
-|
739 | |
- |
-|
740 | $ git filter-branch --commit-filter ' |
- |
-|
741 | if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; |
- |
-|
742 | then |
- |
-|
743 | GIT_AUTHOR_NAME="Scott Chacon"; |
- |
-|
744 | GIT_AUTHOR_EMAIL="schacon@example.com"; |
- |
-|
745 | git commit-tree "$@"; |
- |
-|
746 | else |
- |
-|
747 | git commit-tree "$@"; |
- |
-|
748 | fi' HEAD |
- |
-|
749 | |
- |
-|
750 | This goes through and rewrites every commit to have your new address. Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address. |
- |
-|
751 | |
- |
-|
752 | ### The Very Fast Nuclear Option: Big Friendly Giant Repo Cleaner (BFG) ### |
- |
-|
753 | |
- |
-|
754 | [Roberto Tyley](https://github.com/rtyley) has written a similar tool to `filter-branch` called the BFG. BFG cannot do as much as `filter-branch`, but it is _very_ fast and on a large repository this can make a big difference. If the change you want to make is in the scope of BFG capability, and you have performance issues, then you should consider using it. |
- |
-|
755 | |
- |
-|
756 | See the [BFG](http://rtyley.github.io/bfg-repo-cleaner/) website for details. |
- |
-|
757 | |
- |
-|
758 | ## Debugging with Git ## |
- |
-|
759 | |
- |
-|
760 | Git also provides a couple of tools to help you debug issues in your projects. Because Git is designed to work with nearly any type of project, these tools are pretty generic, but they can often help you hunt for a bug or culprit when things go wrong. |
- |
-|
761 | |
- |
-|
762 | ### File Annotation ### |
- |
-|
763 | |
- |
-|
764 | If you track down a bug in your code and want to know when it was introduced and why, file annotation is often your best tool. It shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with `git blame` to see when each line of the method was last edited and by whom. This example uses the `-L` option to limit the output to lines 12 through 22: |
- |
-|
765 | |
- |
-|
766 | $ git blame -L 12,22 simplegit.rb |
- |
-|
767 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master') |
- |
-|
768 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}") |
- |
-|
769 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end |
- |
-|
770 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15) |
- |
-|
771 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master') |
- |
-|
772 | 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}") |
- |
-|
773 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end |
- |
-|
774 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19) |
- |
-|
775 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path) |
- |
-|
776 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}") |
- |
-|
777 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end |
- |
-|
778 | |
- |
-|
779 | Notice that the first field is the partial SHA-1 of the commit that last modified that line. The next two fields are values extracted from that commit—the author name and the authored date of that commit — so you can easily see who modified that line and when. After that come the line number and the content of the file. Also note the `^4832fe2` commit lines, which designate that those lines were in this file’s original commit. That commit is when this file was first added to this project, and those lines have been unchanged since. This is a tad confusing, because now you’ve seen at least three different ways that Git uses the `^` to modify a commit SHA, but that is what it means here. |
- |
-|
780 | |
- |
-|
781 | Another cool thing about Git is that it doesn’t track file renames explicitly. It records the snapshots and then tries to figure out what was renamed implicitly, after the fact. One of the interesting features of this is that you can ask it to figure out all sorts of code movement as well. If you pass `-C` to `git blame`, Git analyzes the file you’re annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere. Recently, I was refactoring a file named `GITServerHandler.m` into multiple files, one of which was `GITPackUpload.m`. By blaming `GITPackUpload.m` with the `-C` option, I could see where sections of the code originally came from: |
- |
-|
782 | |
- |
-|
783 | $ git blame -C -L 141,153 GITPackUpload.m |
- |
-|
784 | f344f58d GITServerHandler.m (Scott 2009-01-04 141) |
- |
-|
785 | f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC |
- |
-|
786 | f344f58d GITServerHandler.m (Scott 2009-01-04 143) { |
- |
-|
787 | 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI |
- |
-|
788 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 145) |
- |
-|
789 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha; |
- |
-|
790 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g |
- |
-|
791 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 148) |
- |
-|
792 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI |
- |
-|
793 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 150) |
- |
-|
794 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) { |
- |
-|
795 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb |
- |
-|
796 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 153) |
- |
-|
797 | |
- |
-|
798 | This is really useful. Normally, you get as the original commit the commit where you copied the code over, because that is the first time you touched those lines in this file. Git tells you the original commit where you wrote those lines, even if it was in another file. |
- |
-|
799 | |
- |
-|
800 | ### Binary Search ### |
- |
-|
801 | |
- |
-|
802 | Annotating a file helps if you know where the issue is to begin with. If you don’t know what is breaking, and there have been dozens or hundreds of commits since the last state where you know the code worked, you’ll likely turn to `git bisect` for help. The `bisect` command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. |
- |
-|
803 | |
- |
-|
804 | Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that. You go back to your code, and it turns out you can reproduce the issue, but you can’t figure out what is going wrong. You can bisect the code to find out. First you run `git bisect start` to get things going, and then you use `git bisect bad` to tell the system that the current commit you’re on is broken. Then, you must tell bisect when the last known good state was, using `git bisect good [good_commit]`: |
- |
-|
805 | |
- |
-|
806 | $ git bisect start |
- |
-|
807 | $ git bisect bad |
- |
-|
808 | $ git bisect good v1.0 |
- |
-|
809 | Bisecting: 6 revisions left to test after this |
- |
-|
810 | [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo |
- |
-|
811 | |
- |
-|
812 | Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. If it does, then it was introduced sometime before this middle commit; if it doesn’t, then the problem was introduced sometime after the middle commit. It turns out there is no issue here, and you tell Git that by typing `git bisect good` and continue your journey: |
- |
-|
813 | |
- |
-|
814 | $ git bisect good |
- |
-|
815 | Bisecting: 3 revisions left to test after this |
- |
-|
816 | [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing |
- |
-|
817 | |
- |
-|
818 | Now you’re on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with `git bisect bad`: |
- |
-|
819 | |
- |
-|
820 | $ git bisect bad |
- |
-|
821 | Bisecting: 1 revisions left to test after this |
- |
-|
822 | [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table |
- |
-|
823 | |
- |
-|
824 | This commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and show some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug: |
- |
-|
825 | |
- |
-|
826 | $ git bisect good |
- |
-|
827 | b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit |
- |
-|
828 | commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 |
- |
-|
829 | Author: PJ Hyett <pjhyett@example.com> |
- |
-|
830 | Date: Tue Jan 27 14:48:32 2009 -0800 |
- |
-|
831 | |
- |
-|
832 | secure this thing |
- |
-|
833 | |
- |
-|
834 | :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730 |
- |
-|
835 | f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config |
- |
-|
836 | |
- |
-|
837 | When you’re finished, you should run `git bisect reset` to reset your HEAD to where you were before you started, or you’ll end up in a weird state: |
- |
-|
838 | |
- |
-|
839 | $ git bisect reset |
- |
-|
840 | |
- |
-|
841 | This is a powerful tool that can help you check hundreds of commits for an introduced bug in minutes. In fact, if you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate `git bisect`. First, you again tell it the scope of the bisect by providing the known bad and good commits. You can do this by listing them with the `bisect start` command if you want, listing the known bad commit first and the known good commit second: |
- |
-|
842 | |
- |
-|
843 | $ git bisect start HEAD v1.0 |
- |
-|
844 | $ git bisect run test-error.sh |
- |
-|
845 | |
- |
-|
846 | Doing so automatically runs `test-error.sh` on each checked-out commit until Git finds the first broken commit. You can also run something like `make` or `make tests` or whatever you have that runs automated tests for you. |
- |
-|
847 | |
- |
-|
848 | ## Submodules ## |
- |
-|
849 | |
- |
-|
850 | It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other. |
- |
-|
851 | |
- |
-|
852 | Here’s an example. Suppose you’re developing a web site and creating Atom feeds. Instead of writing your own Atom-generating code, you decide to use a library. You’re likely to have to either include this code from a shared library like a CPAN install or Ruby gem, or copy the source code into your own project tree. The issue with including the library is that it’s difficult to customize the library in any way and often more difficult to deploy it, because you need to make sure every client has that library available. The issue with vendoring the code into your own project is that any custom changes you make are difficult to merge when upstream changes become available. |
- |
-|
853 | |
- |
-|
854 | Git addresses this issue using submodules. Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. |
- |
-|
855 | |
- |
-|
856 | ### Starting with Submodules ### |
- |
-|
857 | |
- |
-|
858 | Suppose you want to add the Rack library (a Ruby web server gateway interface) to your project, possibly maintain your own changes to it, but continue to merge in upstream changes. The first thing you should do is clone the external repository into your subdirectory. You add external projects as submodules with the `git submodule add` command: |
- |
-|
859 | |
- |
-|
860 | $ git submodule add git://github.com/chneukirchen/rack.git rack |
- |
-|
861 | Initialized empty Git repository in /opt/subtest/rack/.git/ |
- |
-|
862 | remote: Counting objects: 3181, done. |
- |
-|
863 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
864 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
865 | Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done. |
- |
-|
866 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
867 | |
- |
-|
868 | Now you have the Rack project under a subdirectory named `rack` within your project. You can go into that subdirectory, make changes, add your own writable remote repository to push your changes into, fetch and merge from the original repository, and more. If you run `git status` right after you add the submodule, you see two things: |
- |
-|
869 | |
- |
-|
870 | $ git status |
- |
-|
871 | # On branch master |
- |
-|
872 | # Changes to be committed: |
- |
-|
873 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
874 | # |
- |
-|
875 | # new file: .gitmodules |
- |
-|
876 | # new file: rack |
- |
-|
877 | # |
- |
-|
878 | |
- |
-|
879 | First you notice the `.gitmodules` file. This is a configuration file that stores the mapping between the project’s URL and the local subdirectory you’ve pulled it into: |
- |
-|
880 | |
- |
-|
881 | $ cat .gitmodules |
- |
-|
882 | [submodule "rack"] |
- |
-|
883 | path = rack |
- |
-|
884 | url = git://github.com/chneukirchen/rack.git |
- |
-|
885 | |
- |
-|
886 | If you have multiple submodules, you’ll have multiple entries in this file. It’s important to note that this file is version-controlled with your other files, like your `.gitignore` file. It’s pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from. |
- |
-|
887 | |
- |
-|
888 | The other listing in the `git status` output is the rack entry. If you run `git diff` on that, you see something interesting: |
- |
-|
889 | |
- |
-|
890 | $ git diff --cached rack |
- |
-|
891 | diff --git a/rack b/rack |
- |
-|
892 | new file mode 160000 |
- |
-|
893 | index 0000000..08d709f |
- |
-|
894 | --- /dev/null |
- |
-|
895 | +++ b/rack |
- |
-|
896 | @@ -0,0 +1 @@ |
- |
-|
897 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
898 | |
- |
-|
899 | Although `rack` is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git records it as a particular commit from that repository. When you make changes and commit in that subdirectory, the superproject notices that the HEAD there has changed and records the exact commit you’re currently working off of; that way, when others clone this project, they can re-create the environment exactly. |
- |
-|
900 | |
- |
-|
901 | This is an important point with submodules: you record them as the exact commit they’re at. You can’t record a submodule at `master` or some other symbolic reference. |
- |
-|
902 | |
- |
-|
903 | When you commit, you see something like this: |
- |
-|
904 | |
- |
-|
905 | $ git commit -m 'first commit with submodule rack' |
- |
-|
906 | [master 0550271] first commit with submodule rack |
- |
-|
907 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
908 | create mode 100644 .gitmodules |
- |
-|
909 | create mode 160000 rack |
- |
-|
910 | |
- |
-|
911 | Notice the 160000 mode for the rack entry. That is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file. |
- |
-|
912 | |
- |
-|
913 | You can treat the `rack` directory as a separate project and then update your superproject from time to time with a pointer to the latest commit in that subproject. All the Git commands work independently in the two directories: |
- |
-|
914 | |
- |
-|
915 | $ git log -1 |
- |
-|
916 | commit 0550271328a0038865aad6331e620cd7238601bb |
- |
-|
917 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
918 | Date: Thu Apr 9 09:03:56 2009 -0700 |
- |
-|
919 | |
- |
-|
920 | first commit with submodule rack |
- |
-|
921 | $ cd rack/ |
- |
-|
922 | $ git log -1 |
- |
-|
923 | commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
924 | Author: Christian Neukirchen <chneukirchen@gmail.com> |
- |
-|
925 | Date: Wed Mar 25 14:49:04 2009 +0100 |
- |
-|
926 | |
- |
-|
927 | Document version change |
- |
-|
928 | |
- |
-|
929 | ### Cloning a Project with Submodules ### |
- |
-|
930 | |
- |
-|
931 | Here you’ll clone a project with a submodule in it. When you receive such a project, you get the directories that contain submodules, but none of the files yet: |
- |
-|
932 | |
- |
-|
933 | $ git clone git://github.com/schacon/myproject.git |
- |
-|
934 | Initialized empty Git repository in /opt/myproject/.git/ |
- |
-|
935 | remote: Counting objects: 6, done. |
- |
-|
936 | remote: Compressing objects: 100% (4/4), done. |
- |
-|
937 | remote: Total 6 (delta 0), reused 0 (delta 0) |
- |
-|
938 | Receiving objects: 100% (6/6), done. |
- |
-|
939 | $ cd myproject |
- |
-|
940 | $ ls -l |
- |
-|
941 | total 8 |
- |
-|
942 | -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README |
- |
-|
943 | drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack |
- |
-|
944 | $ ls rack/ |
- |
-|
945 | $ |
- |
-|
946 | |
- |
-|
947 | The `rack` directory is there, but empty. You must run two commands: `git submodule init` to initialize your local configuration file, and `git submodule update` to fetch all the data from that project and check out the appropriate commit listed in your superproject: |
- |
-|
948 | |
- |
-|
949 | $ git submodule init |
- |
-|
950 | Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack' |
- |
-|
951 | $ git submodule update |
- |
-|
952 | Initialized empty Git repository in /opt/myproject/rack/.git/ |
- |
-|
953 | remote: Counting objects: 3181, done. |
- |
-|
954 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
955 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
956 | Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done. |
- |
-|
957 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
958 | Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433' |
- |
-|
959 | |
- |
-|
960 | Now your `rack` subdirectory is at the exact state it was in when you committed earlier. If another developer makes changes to the rack code and commits, and you pull that reference down and merge it in, you get something a bit odd: |
- |
-|
961 | |
- |
-|
962 | $ git merge origin/master |
- |
-|
963 | Updating 0550271..85a3eee |
- |
-|
964 | Fast forward |
- |
-|
965 | rack | 2 +- |
- |
-|
966 | 1 files changed, 1 insertions(+), 1 deletions(-) |
- |
-|
967 | [master*]$ git status |
- |
-|
968 | # On branch master |
- |
-|
969 | # Changes not staged for commit: |
- |
-|
970 | # (use "git add <file>..." to update what will be committed) |
- |
-|
971 | # (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
972 | # |
- |
-|
973 | # modified: rack |
- |
-|
974 | # |
- |
-|
975 | |
- |
-|
976 | You merged in what is basically a change to the pointer for your submodule; but it doesn’t update the code in the submodule directory, so it looks like you have a dirty state in your working directory: |
- |
-|
977 | |
- |
-|
978 | $ git diff |
- |
-|
979 | diff --git a/rack b/rack |
- |
-|
980 | index 6c5e70b..08d709f 160000 |
- |
-|
981 | --- a/rack |
- |
-|
982 | +++ b/rack |
- |
-|
983 | @@ -1 +1 @@ |
- |
-|
984 | -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
985 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
986 | |
- |
-|
987 | This is the case because the pointer you have for the submodule isn’t what is actually in the submodule directory. To fix this, you must run `git submodule update` again: |
- |
-|
988 | |
- |
-|
989 | $ git submodule update |
- |
-|
990 | remote: Counting objects: 5, done. |
- |
-|
991 | remote: Compressing objects: 100% (3/3), done. |
- |
-|
992 | remote: Total 3 (delta 1), reused 2 (delta 0) |
- |
-|
993 | Unpacking objects: 100% (3/3), done. |
- |
-|
994 | From git@github.com:schacon/rack |
- |
-|
995 | 08d709f..6c5e70b master -> origin/master |
- |
-|
996 | Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0' |
- |
-|
997 | |
- |
-|
998 | You have to do this every time you pull down a submodule change in the main project. It’s strange, but it works. |
- |
-|
999 | |
- |
-|
1000 | One common problem happens when a developer makes a change locally in a submodule but doesn’t push it to a public server. Then, they commit a pointer to that non-public state and push up the superproject. When other developers try to run `git submodule update`, the submodule system can’t find the commit that is referenced, because it exists only on the first developer’s system. If that happens, you see an error like this: |
- |
-|
1001 | |
- |
-|
1002 | $ git submodule update |
- |
-|
1003 | fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
1004 | Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack' |
- |
-|
1005 | |
- |
-|
1006 | You have to see who last changed the submodule: |
- |
-|
1007 | |
- |
-|
1008 | $ git log -1 rack |
- |
-|
1009 | commit 85a3eee996800fcfa91e2119372dd4172bf76678 |
- |
-|
1010 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
1011 | Date: Thu Apr 9 09:19:14 2009 -0700 |
- |
-|
1012 | |
- |
-|
1013 | added a submodule reference I will never make public. hahahahaha! |
- |
-|
1014 | |
- |
-|
1015 | Then, you e-mail that guy and yell at him. |
- |
-|
1016 | |
- |
-|
1017 | ### Superprojects ### |
- |
-|
1018 | |
- |
-|
1019 | Sometimes, developers want to get a combination of a large project’s subdirectories, depending on what team they’re on. This is common if you’re coming from CVS or Subversion, where you’ve defined a module or collection of subdirectories, and you want to keep this type of workflow. |
- |
-|
1020 | |
- |
-|
1021 | A good way to do this in Git is to make each of the subdirectories a separate Git repository and then create superproject Git repositories that contain multiple submodules. A benefit of this approach is that you can more specifically define the relationships between the projects with tags and branches in the superprojects. |
- |
-|
1022 | |
- |
-|
1023 | ### Issues with Submodules ### |
- |
-|
1024 | |
- |
-|
1025 | Using submodules isn’t without hiccups, however. First, you must be relatively careful when working in the submodule directory. When you run `git submodule update`, it checks out the specific version of the project, but not within a branch. This is called having a detached HEAD — it means the HEAD file points directly to a commit, not to a symbolic reference. The issue is that you generally don’t want to work in a detached HEAD environment, because it’s easy to lose changes. If you do an initial `submodule update`, commit in that submodule directory without creating a branch to work in, and then run `git submodule update` again from the superproject without committing in the meantime, Git will overwrite your changes without telling you. Technically you won’t lose the work, but you won’t have a branch pointing to it, so it will be somewhat difficult to retrieve. |
- |
-|
1026 | |
- |
-|
1027 | To avoid this issue, create a branch when you work in a submodule directory with `git checkout -b work` or something equivalent. When you do the submodule update a second time, it will still revert your work, but at least you have a pointer to get back to. |
- |
-|
1028 | |
- |
-|
1029 | Switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory: |
- |
-|
1030 | |
- |
-|
1031 | $ git checkout -b rack |
- |
-|
1032 | Switched to a new branch "rack" |
- |
-|
1033 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1034 | Initialized empty Git repository in /opt/myproj/rack/.git/ |
- |
-|
1035 | ... |
- |
-|
1036 | Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done. |
- |
-|
1037 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1038 | $ git commit -am 'added rack submodule' |
- |
-|
1039 | [rack cc49a69] added rack submodule |
- |
-|
1040 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
1041 | create mode 100644 .gitmodules |
- |
-|
1042 | create mode 160000 rack |
- |
-|
1043 | $ git checkout master |
- |
-|
1044 | Switched to branch "master" |
- |
-|
1045 | $ git status |
- |
-|
1046 | # On branch master |
- |
-|
1047 | # Untracked files: |
- |
-|
1048 | # (use "git add <file>..." to include in what will be committed) |
- |
-|
1049 | # |
- |
-|
1050 | # rack/ |
- |
-|
1051 | |
- |
-|
1052 | You have to either move it out of the way or remove it, in which case you have to clone it again when you switch back—and you may lose local changes or branches that you didn’t push up. |
- |
-|
1053 | |
- |
-|
1054 | The last main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have the rack files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run `submodule add`, Git yells at you: |
- |
-|
1055 | |
- |
-|
1056 | $ rm -Rf rack/ |
- |
-|
1057 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1058 | 'rack' already exists in the index |
- |
-|
1059 | |
- |
-|
1060 | You have to unstage the `rack` directory first. Then you can add the submodule: |
- |
-|
1061 | |
- |
-|
1062 | $ git rm -r rack |
- |
-|
1063 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1064 | Initialized empty Git repository in /opt/testsub/rack/.git/ |
- |
-|
1065 | remote: Counting objects: 3184, done. |
- |
-|
1066 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1067 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1068 | Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done. |
- |
-|
1069 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1070 | |
- |
-|
1071 | Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule — you get this error: |
- |
-|
1072 | |
- |
-|
1073 | $ git checkout master |
- |
-|
1074 | error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge. |
- |
-|
1075 | |
- |
-|
1076 | You have to move the `rack` submodule directory out of the way before you can switch to a branch that doesn’t have it: |
- |
-|
1077 | |
- |
-|
1078 | $ mv rack /tmp/ |
- |
-|
1079 | $ git checkout master |
- |
-|
1080 | Switched to branch "master" |
- |
-|
1081 | $ ls |
- |
-|
1082 | README rack |
- |
-|
1083 | |
- |
-|
1084 | Then, when you switch back, you get an empty `rack` directory. You can either run `git submodule update` to reclone, or you can move your `/tmp/rack` directory back into the empty directory. |
- |
-|
1085 | |
- |
-|
1086 | ## Subtree Merging ## |
- |
-|
1087 | |
- |
-|
1088 | Now that you’ve seen the difficulties of the submodule system, let’s look at an alternate way to solve the same problem. When Git merges, it looks at what it has to merge together and then chooses an appropriate merging strategy to use. If you’re merging two branches, Git uses a _recursive_ strategy. If you’re merging more than two branches, Git picks the _octopus_ strategy. These strategies are automatically chosen for you because the recursive strategy can handle complex three-way merge situations — for example, more than one common ancestor — but it can only handle merging two branches. The octopus merge can handle multiple branches but is more cautious to avoid difficult conflicts, so it’s chosen as the default strategy if you’re trying to merge more than two branches. |
- |
-|
1089 | |
- |
-|
1090 | However, there are other strategies you can choose as well. One of them is the _subtree_ merge, and you can use it to deal with the subproject issue. Here you’ll see how to do the same rack embedding as in the last section, but using subtree merges instead. |
- |
-|
1091 | |
- |
-|
1092 | The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one and vice versa. When you specify a subtree merge, Git is smart enough to figure out that one is a subtree of the other and merge appropriately — it’s pretty amazing. |
- |
-|
1093 | |
- |
-|
1094 | You first add the Rack application to your project. You add the Rack project as a remote reference in your own project and then check it out into its own branch: |
- |
-|
1095 | |
- |
-|
1096 | $ git remote add rack_remote git@github.com:schacon/rack.git |
- |
-|
1097 | $ git fetch rack_remote |
- |
-|
1098 | warning: no common commits |
- |
-|
1099 | remote: Counting objects: 3184, done. |
- |
-|
1100 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1101 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1102 | Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. |
- |
-|
1103 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1104 | From git@github.com:schacon/rack |
- |
-|
1105 | * [new branch] build -> rack_remote/build |
- |
-|
1106 | * [new branch] master -> rack_remote/master |
- |
-|
1107 | * [new branch] rack-0.4 -> rack_remote/rack-0.4 |
- |
-|
1108 | * [new branch] rack-0.9 -> rack_remote/rack-0.9 |
- |
-|
1109 | $ git checkout -b rack_branch rack_remote/master |
- |
-|
1110 | Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. |
- |
-|
1111 | Switched to a new branch "rack_branch" |
- |
-|
1112 | |
- |
-|
1113 | Now you have the root of the Rack project in your `rack_branch` branch and your own project in the `master` branch. If you check out one and then the other, you can see that they have different project roots: |
- |
-|
1114 | |
- |
-|
1115 | $ ls |
- |
-|
1116 | AUTHORS KNOWN-ISSUES Rakefile contrib lib |
- |
-|
1117 | COPYING README bin example test |
- |
-|
1118 | $ git checkout master |
- |
-|
1119 | Switched to branch "master" |
- |
-|
1120 | $ ls |
- |
-|
1121 | README |
- |
-|
1122 | |
- |
-|
1123 | You want to pull the Rack project into your `master` project as a subdirectory. You can do that in Git with `git read-tree`. You’ll learn more about `read-tree` and its friends in Chapter 9, but for now know that it reads the root tree of one branch into your current staging area and working directory. You just switched back to your `master` branch, and you pull the `rack` branch into the `rack` subdirectory of your `master` branch of your main project: |
- |
-|
1124 | |
- |
-|
1125 | $ git read-tree --prefix=rack/ -u rack_branch |
- |
-|
1126 | |
- |
-|
1127 | When you commit, it looks like you have all the Rack files under that subdirectory — as though you copied them in from a tarball. What gets interesting is that you can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, you can pull in upstream changes by switching to that branch and pulling: |
- |
-|
1128 | |
- |
-|
1129 | $ git checkout rack_branch |
- |
-|
1130 | $ git pull |
- |
-|
1131 | |
- |
-|
1132 | Then, you can merge those changes back into your master branch. You can use `git merge -s subtree` and it will work fine; but Git will also merge the histories together, which you probably don’t want. To pull in the changes and prepopulate the commit message, use the `--squash` and `--no-commit` options as well as the `-s subtree` strategy option: |
- |
-|
1133 | |
- |
-|
1134 | $ git checkout master |
- |
-|
1135 | $ git merge --squash -s subtree --no-commit rack_branch |
- |
-|
1136 | Squash commit -- not updating HEAD |
- |
-|
1137 | Automatic merge went well; stopped before committing as requested |
- |
-|
1138 | |
- |
-|
1139 | All the changes from your Rack project are merged in and ready to be committed locally. You can also do the opposite — make changes in the `rack` subdirectory of your master branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. |
- |
-|
1140 | |
- |
-|
1141 | To get a diff between what you have in your `rack` subdirectory and the code in your `rack_branch` branch — to see if you need to merge them — you can’t use the normal `diff` command. Instead, you must run `git diff-tree` with the branch you want to compare to: |
- |
-|
1142 | |
- |
-|
1143 | $ git diff-tree -p rack_branch |
- |
-|
1144 | |
- |
-|
1145 | Or, to compare what is in your `rack` subdirectory with what the `master` branch on the server was the last time you fetched, you can run |
- |
-|
1146 | |
- |
-|
1147 | $ git diff-tree -p rack_remote/master |
- |
-|
1148 | |
- |
-|
1149 | ## Summary ## |
- |
-|
1150 | |
- |
-|
1151 | You’ve seen a number of advanced tools that allow you to manipulate your commits and staging area more precisely. When you notice issues, you should be able to easily figure out what commit introduced them, when, and by whom. If you want to use subprojects in your project, you’ve learned a few ways to accommodate those needs. At this point, you should be able to do most of the things in Git that you’ll need on the command line day to day and feel comfortable doing so. |
- |
-|
1152 | |
-187 | |
-
C:\Users\15625\Documents\Git\progit\en\06-git-tools\01-chapter6.markdown | -C:\Users\15625\Documents\Git\progit2-ja\book\07-git-tools\sections\revision-selection.asc | -||
---|---|---|---|
.1 | # Git Tools # |
-1 | [[_revision_selection]] |
-
2 | |
- |
-|
3 | 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. |
- |
-|
4 | |
- |
-|
5 | 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. |
- |
-|
6 | |
- |
-|
7 | ## Revision Selection ## |
-2 | === Revision Selection |
-
8 | |
-3 | |
-
9 | 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. |
-4 | 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. |
-
10 | |
-5 | |
-
.11 | ### Single Revisions ### |
-6 | ==== Single Revisions |
-
12 | |
-7 | |
-
13 | 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. |
-8 | 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. |
-
14 | |
-9 | |
-
.15 | ### Short SHA ### |
-10 | ==== Short SHA |
-
16 | |
-11 | |
-
.17 | 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. |
-12 | 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. |
-
18 | |
-13 | |
-
19 | For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: |
-14 | For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: |
-
20 | |
-15 | |
-
. | |
-16 | [source,console] |
-
|
-17 | ---- |
-|
21 | $ git log |
-18 | $ git log |
-
22 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
-19 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
-
23 | Author: Scott Chacon <schacon@gmail.com> |
-20 | Author: Scott Chacon <schacon@gmail.com> |
-
24 | Date: Fri Jan 2 18:32:33 2009 -0800 |
-21 | Date: Fri Jan 2 18:32:33 2009 -0800 |
-
25 | |
-22 | |
-
.26 | fixed refs handling, added gc auto, updated tests |
-23 | fixed refs handling, added gc auto, updated tests |
-
27 | |
-24 | |
-
.28 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
-25 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
-
29 | Merge: 1c002dd... 35cfb2b... |
-26 | Merge: 1c002dd... 35cfb2b... |
-
30 | Author: Scott Chacon <schacon@gmail.com> |
-27 | Author: Scott Chacon <schacon@gmail.com> |
-
31 | Date: Thu Dec 11 15:08:43 2008 -0800 |
-28 | Date: Thu Dec 11 15:08:43 2008 -0800 |
-
32 | |
-29 | |
-
.33 | Merge commit 'phedders/rdocs' |
-30 | Merge commit 'phedders/rdocs' |
-
34 | |
-31 | |
-
.35 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
-32 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
-
36 | Author: Scott Chacon <schacon@gmail.com> |
-33 | Author: Scott Chacon <schacon@gmail.com> |
-
37 | Date: Thu Dec 11 14:58:32 2008 -0800 |
-34 | Date: Thu Dec 11 14:58:32 2008 -0800 |
-
38 | |
-35 | |
-
.39 | added some blame and merge stuff |
-36 | added some blame and merge stuff |
-
|
-37 | ---- |
-|
40 | |
-38 | |
-
41 | In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): |
-39 | In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): |
-
42 | |
-40 | |
-
. | |
-41 | [source,console] |
-
|
-42 | ---- |
-|
43 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
-43 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
-
44 | $ git show 1c002dd4b536e7479f |
-44 | $ git show 1c002dd4b536e7479f |
-
45 | $ git show 1c002d |
-45 | $ git show 1c002d |
-
|
-46 | ---- |
-|
46 | |
-47 | |
-
47 | 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: |
-48 | 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: |
-
48 | |
-49 | |
-
. | |
-50 | [source,console] |
-
|
-51 | ---- |
-|
49 | $ git log --abbrev-commit --pretty=oneline |
-52 | $ git log --abbrev-commit --pretty=oneline |
-
50 | ca82a6d changed the version number |
-53 | ca82a6d changed the version number |
-
51 | 085bb3b removed unnecessary test code |
-54 | 085bb3b removed unnecessary test code |
-
52 | a11bef0 first commit |
-55 | a11bef0 first commit |
-
|
-56 | ---- |
-|
53 | |
-57 | |
-
.54 | 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. |
-58 | Generally, eight to ten characters are more than enough to be unique within a project. |
-
55 | |
-59 | |
-
. | |
-60 | 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 SHAs overlap more than the first 11 characters. |
-
|
-61 | |
-|
|
-62 | [NOTE] |
-|
56 | ### A SHORT NOTE ABOUT SHA-1 ### |
-63 | .A SHORT NOTE ABOUT SHA-1 |
-
|
-64 | ==== |
-|
57 | |
-65 | |
-
58 | 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? |
-66 | 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? |
-
59 | |
-67 | |
-
60 | 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. |
-68 | 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. |
-
61 | |
-69 | |
-
.62 | 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. |
-70 | 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. |
-
63 | |
-71 | |
-
.64 | 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. 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. |
-72 | 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. |
-
|
-73 | ==== |
-|
65 | |
-74 | |
-
. | |
-75 | [[_branch_references]] |
-
66 | ### Branch References ### |
-76 | ==== Branch References |
-
67 | |
-77 | |
-
68 | The most straightforward way to specify a commit requires that it have 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`: |
-78 | The most straightforward way to specify a commit requires that it have 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`: |
-
69 | |
-79 | |
-
. | |
-80 | [source,console] |
-
|
-81 | ---- |
-|
70 | $ git show ca82a6dff817ec66f44342007202690a93763949 |
-82 | $ git show ca82a6dff817ec66f44342007202690a93763949 |
-
71 | $ git show topic1 |
-83 | $ git show topic1 |
-
|
-84 | ---- |
-|
72 | |
-85 | |
-
.73 | 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 Chapter 9 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. |
-86 | 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. |
-
74 | |
-87 | |
-
. | |
-88 | [source,console] |
-
|
-89 | ---- |
-|
75 | $ git rev-parse topic1 |
-90 | $ git rev-parse topic1 |
-
76 | ca82a6dff817ec66f44342007202690a93763949 |
-91 | ca82a6dff817ec66f44342007202690a93763949 |
-
|
-92 | ---- |
-|
77 | |
-93 | |
-
. | |
-94 | [[_git_reflog]] |
-
78 | ### RefLog Shortnames ### |
-95 | ==== RefLog Shortnames |
-
79 | |
-96 | |
-
.80 | 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. |
-97 | 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. |
-
81 | |
-98 | |
-
82 | You can see your reflog by using `git reflog`: |
-99 | You can see your reflog by using `git reflog`: |
-
83 | |
-100 | |
-
. | |
-101 | [source,console] |
-
|
-102 | ---- |
-|
84 | $ git reflog |
-103 | $ git reflog |
-
85 | 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated |
-104 | 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated |
-
86 | d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. |
-105 | d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. |
-
87 | 1c002dd HEAD@{2}: commit: added some blame and merge stuff |
-106 | 1c002dd HEAD@{2}: commit: added some blame and merge stuff |
-
88 | 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD |
-107 | 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD |
-
89 | 95df984 HEAD@{4}: commit: # This is a combination of two commits. |
-108 | 95df984 HEAD@{4}: commit: # This is a combination of two commits. |
-
90 | 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD |
-109 | 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD |
-
91 | 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD |
-110 | 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD |
-
|
-111 | ---- |
-|
92 | |
-112 | |
-
93 | 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: |
-113 | 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: |
-
94 | |
-114 | |
-
. | |
-115 | [source,console] |
-
|
-116 | ---- |
-|
95 | $ git show HEAD@{5} |
-117 | $ git show HEAD@{5} |
-
|
-118 | ---- |
-|
96 | |
-119 | |
-
97 | 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 |
-120 | 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 |
-
98 | |
-121 | |
-
. | |
-122 | [source,console] |
-
|
-123 | ---- |
-|
99 | $ git show master@{yesterday} |
-124 | $ git show master@{yesterday} |
-
|
-125 | ---- |
-|
100 | |
-126 | |
-
101 | 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. |
-127 | 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. |
-
102 | |
-128 | |
-
103 | To see reflog information formatted like the `git log` output, you can run `git log -g`: |
-129 | To see reflog information formatted like the `git log` output, you can run `git log -g`: |
-
104 | |
-130 | |
-
. | |
-131 | [source,console] |
-
|
-132 | ---- |
-|
105 | $ git log -g master |
-133 | $ git log -g master |
-
106 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
-134 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
-
107 | Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) |
-135 | Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) |
-
108 | Reflog message: commit: fixed refs handling, added gc auto, updated |
-136 | Reflog message: commit: fixed refs handling, added gc auto, updated |
-
109 | Author: Scott Chacon <schacon@gmail.com> |
-137 | Author: Scott Chacon <schacon@gmail.com> |
-
110 | Date: Fri Jan 2 18:32:33 2009 -0800 |
-138 | Date: Fri Jan 2 18:32:33 2009 -0800 |
-
111 | |
-139 | |
-
.112 | fixed refs handling, added gc auto, updated tests |
-140 | fixed refs handling, added gc auto, updated tests |
-
113 | |
-141 | |
-
.114 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
-142 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
-
115 | Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) |
-143 | Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) |
-
116 | Reflog message: merge phedders/rdocs: Merge made by recursive. |
-144 | Reflog message: merge phedders/rdocs: Merge made by recursive. |
-
117 | Author: Scott Chacon <schacon@gmail.com> |
-145 | Author: Scott Chacon <schacon@gmail.com> |
-
118 | Date: Thu Dec 11 15:08:43 2008 -0800 |
-146 | Date: Thu Dec 11 15:08:43 2008 -0800 |
-
119 | |
-147 | |
-
.120 | Merge commit 'phedders/rdocs' |
-148 | Merge commit 'phedders/rdocs' |
-
|
-149 | ---- |
-|
121 | |
-150 | |
-
.122 | 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. |
-151 | 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. |
-
123 | |
-152 | |
-
.124 | ### Ancestry References ### |
-153 | ==== Ancestry References |
-
125 | |
-154 | |
-
.126 | 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. |
-155 | 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. |
-
127 | Suppose you look at the history of your project: |
-156 | Suppose you look at the history of your project: |
-
128 | |
-157 | |
-
. | |
-158 | [source,console] |
-
|
-159 | ---- |
-|
129 | $ git log --pretty=format:'%h %s' --graph |
-160 | $ git log --pretty=format:'%h %s' --graph |
-
130 | * 734713b fixed refs handling, added gc auto, updated tests |
-161 | * 734713b fixed refs handling, added gc auto, updated tests |
-
131 | * d921970 Merge commit 'phedders/rdocs' |
-162 | * d921970 Merge commit 'phedders/rdocs' |
-
132 | |\ |
-163 | |\ |
-
133 | | * 35cfb2b Some rdoc changes |
-164 | | * 35cfb2b Some rdoc changes |
-
134 | * | 1c002dd added some blame and merge stuff |
-165 | * | 1c002dd added some blame and merge stuff |
-
135 | |/ |
-166 | |/ |
-
136 | * 1c36188 ignore *.gem |
-167 | * 1c36188 ignore *.gem |
-
137 | * 9b29157 add open3_detach to gemspec file list |
-168 | * 9b29157 add open3_detach to gemspec file list |
-
|
-169 | ---- |
-|
138 | |
-170 | |
-
.139 | Then, you can see the previous commit by specifying `HEAD^`, which means "the parent of HEAD": |
-171 | Then, you can see the previous commit by specifying `HEAD^`, which means ``the parent of HEAD'': |
-
140 | |
-172 | |
-
. | |
-173 | [source,console] |
-
|
-174 | ---- |
-|
141 | $ git show HEAD^ |
-175 | $ git show HEAD^ |
-
142 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
-176 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
-
143 | Merge: 1c002dd... 35cfb2b... |
-177 | Merge: 1c002dd... 35cfb2b... |
-
144 | Author: Scott Chacon <schacon@gmail.com> |
-178 | Author: Scott Chacon <schacon@gmail.com> |
-
145 | Date: Thu Dec 11 15:08:43 2008 -0800 |
-179 | Date: Thu Dec 11 15:08:43 2008 -0800 |
-
146 | |
-180 | |
-
.147 | Merge commit 'phedders/rdocs' |
-181 | Merge commit 'phedders/rdocs' |
-
|
-182 | ---- |
-|
148 | |
-183 | |
-
.149 | 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: |
-184 | 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: |
-
150 | |
-185 | |
-
. | |
-186 | [source,console] |
-
|
-187 | ---- |
-|
151 | $ git show d921970^ |
-188 | $ git show d921970^ |
-
152 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
-189 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
-
153 | Author: Scott Chacon <schacon@gmail.com> |
-190 | Author: Scott Chacon <schacon@gmail.com> |
-
154 | Date: Thu Dec 11 14:58:32 2008 -0800 |
-191 | Date: Thu Dec 11 14:58:32 2008 -0800 |
-
155 | |
-192 | |
-
.156 | added some blame and merge stuff |
-193 | added some blame and merge stuff |
-
157 | |
-194 | |
-
.158 | $ git show d921970^2 |
-195 | $ git show d921970^2 |
-
159 | commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 |
-196 | commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 |
-
160 | Author: Paul Hedderly <paul+git@mjr.org> |
-197 | Author: Paul Hedderly <paul+git@mjr.org> |
-
161 | Date: Wed Dec 10 22:22:03 2008 +0000 |
-198 | Date: Wed Dec 10 22:22:03 2008 +0000 |
-
162 | |
-199 | |
-
.163 | Some rdoc changes |
-200 | Some rdoc changes |
-
|
-201 | ---- |
-|
164 | |
-202 | |
-
.165 | 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 |
-203 | 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 |
-
166 | |
-204 | |
-
. | |
-205 | [source,console] |
-
|
-206 | ---- |
-|
167 | $ git show HEAD~3 |
-207 | $ git show HEAD~3 |
-
168 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
-208 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
-
169 | Author: Tom Preston-Werner <tom@mojombo.com> |
-209 | Author: Tom Preston-Werner <tom@mojombo.com> |
-
170 | Date: Fri Nov 7 13:47:59 2008 -0500 |
-210 | Date: Fri Nov 7 13:47:59 2008 -0500 |
-
171 | |
-211 | |
-
.172 | ignore *.gem |
-212 | ignore *.gem |
-
|
-213 | ---- |
-|
173 | |
-214 | |
-
174 | This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: |
-215 | This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: |
-
175 | |
-216 | |
-
. | |
-217 | [source,console] |
-
|
-218 | ---- |
-|
176 | $ git show HEAD^^^ |
-219 | $ git show HEAD^^^ |
-
177 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
-220 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
-
178 | Author: Tom Preston-Werner <tom@mojombo.com> |
-221 | Author: Tom Preston-Werner <tom@mojombo.com> |
-
179 | Date: Fri Nov 7 13:47:59 2008 -0500 |
-222 | Date: Fri Nov 7 13:47:59 2008 -0500 |
-
180 | |
-223 | |
-
.181 | ignore *.gem |
-224 | ignore *.gem |
-
|
-225 | ---- |
-|
182 | |
-226 | |
-
.183 | 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. |
-227 | 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. |
-
184 | |
-228 | |
-
. | |
-229 | [[_commit_ranges]] |
-
185 | ### Commit Ranges ### |
-230 | ==== Commit Ranges |
-
186 | |
-231 | |
-
.187 | 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?" |
-232 | 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?'' |
-
188 | |
-233 | |
-
.189 | #### Double Dot #### |
-234 | ===== Double Dot |
-
190 | |
-235 | |
-
.191 | 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 Figure 6-1. |
-236 | 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 <<double_dot>>. |
-
192 | |
-237 | |
-
.193 | Insert 18333fig0601.png |
- |
-|
194 | Figure 6-1. Example history for range selection. |
- |
-|
|
-238 | [[double_dot]] |
-|
|
-239 | .Example history for range selection. |
-|
|
-240 | image::images/double-dot.png[Example history for range selection.] |
-|
195 | |
-241 | |
-
.196 | 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: |
-242 | 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: |
-
197 | |
-243 | |
-
. | |
-244 | [source,console] |
-
|
-245 | ---- |
-|
198 | $ git log master..experiment |
-246 | $ git log master..experiment |
-
199 | D |
-247 | D |
-
200 | C |
-248 | C |
-
|
-249 | ---- |
-|
201 | |
-250 | |
-
.202 | 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`: |
-251 | 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`: |
-
203 | |
-252 | |
-
. | |
-253 | [source,console] |
-
|
-254 | ---- |
-|
204 | $ git log experiment..master |
-255 | $ git log experiment..master |
-
205 | F |
-256 | F |
-
206 | E |
-257 | E |
-
|
-258 | ---- |
-|
207 | |
-259 | |
-
208 | 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: |
-260 | 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: |
-
209 | |
-261 | |
-
. | |
-262 | [source,console] |
-
|
-263 | ---- |
-|
210 | $ git log origin/master..HEAD |
-264 | $ git log origin/master..HEAD |
-
|
-265 | ---- |
-|
211 | |
-266 | |
-
212 | 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. |
-267 | 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. |
-
.213 | 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. |
-268 | 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. |
-
214 | |
-269 | |
-
.215 | #### Multiple Points #### |
-270 | ===== Multiple Points |
-
216 | |
-271 | |
-
217 | 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: |
-272 | 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: |
-
218 | |
-273 | |
-
. | |
-274 | [source,console] |
-
|
-275 | ---- |
-|
219 | $ git log refA..refB |
-276 | $ git log refA..refB |
-
220 | $ git log ^refA refB |
-277 | $ git log ^refA refB |
-
221 | $ git log refB --not refA |
-278 | $ git log refB --not refA |
-
|
-279 | ---- |
-|
222 | |
-280 | |
-
223 | 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: |
-281 | 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: |
-
224 | |
-282 | |
-
. | |
-283 | [source,console] |
-
|
-284 | ---- |
-|
225 | $ git log refA refB ^refC |
-285 | $ git log refA refB ^refC |
-
226 | $ git log refA refB --not refC |
-286 | $ git log refA refB --not refC |
-
|
-287 | ---- |
-|
227 | |
-288 | |
-
228 | This makes for a very powerful revision query system that should help you figure out what is in your branches. |
-289 | This makes for a very powerful revision query system that should help you figure out what is in your branches. |
-
229 | |
-290 | |
-
. | |
-291 | [[_triple_dot]] |
-
230 | #### Triple Dot #### |
-292 | ===== Triple Dot |
-
231 | |
-293 | |
-
.232 | 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 Figure 6-1. |
-294 | 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 <<double_dot>>. |
-
233 | If you want to see what is in `master` or `experiment` but not any common references, you can run |
-295 | If you want to see what is in `master` or `experiment` but not any common references, you can run |
-
234 | |
-296 | |
-
. | |
-297 | [source,console] |
-
|
-298 | ---- |
-|
235 | $ git log master...experiment |
-299 | $ git log master...experiment |
-
236 | F |
-300 | F |
-
237 | E |
-301 | E |
-
238 | D |
-302 | D |
-
239 | C |
-303 | C |
-
|
-304 | ---- |
-|
240 | |
-305 | |
-
241 | 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. |
-306 | 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. |
-
242 | |
-307 | |
-
243 | 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: |
-308 | 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: |
-
244 | |
-309 | |
-
. | |
-310 | [source,console] |
-
|
-311 | ---- |
-|
245 | $ git log --left-right master...experiment |
-312 | $ git log --left-right master...experiment |
-
246 | < F |
-313 | < F |
-
247 | < E |
-314 | < E |
-
248 | > D |
-315 | > D |
-
249 | > C |
-316 | > C |
-
|
-317 | ---- |
-|
250 | |
-318 | |
-
251 | With these tools, you can much more easily let Git know what commit or commits you want to inspect. |
-319 | With these tools, you can much more easily let Git know what commit or commits you want to inspect. |
-
.252 | |
- |
-|
253 | ## Interactive Staging ## |
- |
-|
254 | |
- |
-|
255 | Git comes with a couple of scripts that make some command-line tasks easier. Here, you’ll look at a few interactive commands that can help you easily craft your commits to include only certain combinations and parts of files. These tools are very helpful if you modify a bunch of files and then decide that you want those changes to be in several focused commits rather than one big messy commit. This way, you can make sure your commits are logically separate changesets and can be easily reviewed by the developers working with you. |
- |
-|
256 | If you run `git add` with the `-i` or `--interactive` option, Git goes into an interactive shell mode, displaying something like this: |
- |
-|
257 | |
- |
-|
258 | $ git add -i |
- |
-|
259 | staged unstaged path |
- |
-|
260 | 1: unchanged +0/-1 TODO |
- |
-|
261 | 2: unchanged +1/-1 index.html |
- |
-|
262 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
263 | |
- |
-|
264 | *** Commands *** |
- |
-|
265 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
266 | 5: patch 6: diff 7: quit 8: help |
- |
-|
267 | What now> |
- |
-|
268 | |
- |
-|
269 | 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. |
- |
-|
270 | |
- |
-|
271 | After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. |
- |
-|
272 | |
- |
-|
273 | ### Staging and Unstaging Files ### |
- |
-|
274 | |
- |
-|
275 | If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: |
- |
-|
276 | |
- |
-|
277 | What now> 2 |
- |
-|
278 | staged unstaged path |
- |
-|
279 | 1: unchanged +0/-1 TODO |
- |
-|
280 | 2: unchanged +1/-1 index.html |
- |
-|
281 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
282 | Update>> |
- |
-|
283 | |
- |
-|
284 | To stage the TODO and index.html files, you can type the numbers: |
- |
-|
285 | |
- |
-|
286 | Update>> 1,2 |
- |
-|
287 | staged unstaged path |
- |
-|
288 | * 1: unchanged +0/-1 TODO |
- |
-|
289 | * 2: unchanged +1/-1 index.html |
- |
-|
290 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
291 | Update>> |
- |
-|
292 | |
- |
-|
293 | 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: |
- |
-|
294 | |
- |
-|
295 | Update>> |
- |
-|
296 | updated 2 paths |
- |
-|
297 | |
- |
-|
298 | *** Commands *** |
- |
-|
299 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
300 | 5: patch 6: diff 7: quit 8: help |
- |
-|
301 | What now> 1 |
- |
-|
302 | staged unstaged path |
- |
-|
303 | 1: +0/-1 nothing TODO |
- |
-|
304 | 2: +1/-1 nothing index.html |
- |
-|
305 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
306 | |
- |
-|
307 | 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: |
- |
-|
308 | |
- |
-|
309 | *** Commands *** |
- |
-|
310 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
311 | 5: patch 6: diff 7: quit 8: help |
- |
-|
312 | What now> 3 |
- |
-|
313 | staged unstaged path |
- |
-|
314 | 1: +0/-1 nothing TODO |
- |
-|
315 | 2: +1/-1 nothing index.html |
- |
-|
316 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
317 | Revert>> 1 |
- |
-|
318 | staged unstaged path |
- |
-|
319 | * 1: +0/-1 nothing TODO |
- |
-|
320 | 2: +1/-1 nothing index.html |
- |
-|
321 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
322 | Revert>> [enter] |
- |
-|
323 | reverted one path |
- |
-|
324 | |
- |
-|
325 | Looking at your Git status again, you can see that you’ve unstaged the TODO file: |
- |
-|
326 | |
- |
-|
327 | *** Commands *** |
- |
-|
328 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
329 | 5: patch 6: diff 7: quit 8: help |
- |
-|
330 | What now> 1 |
- |
-|
331 | staged unstaged path |
- |
-|
332 | 1: unchanged +0/-1 TODO |
- |
-|
333 | 2: +1/-1 nothing index.html |
- |
-|
334 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
335 | |
- |
-|
336 | 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: |
- |
-|
337 | |
- |
-|
338 | *** Commands *** |
- |
-|
339 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
340 | 5: patch 6: diff 7: quit 8: help |
- |
-|
341 | What now> 6 |
- |
-|
342 | staged unstaged path |
- |
-|
343 | 1: +1/-1 nothing index.html |
- |
-|
344 | Review diff>> 1 |
- |
-|
345 | diff --git a/index.html b/index.html |
- |
-|
346 | index 4d07108..4335f49 100644 |
- |
-|
347 | --- a/index.html |
- |
-|
348 | +++ b/index.html |
- |
-|
349 | @@ -16,7 +16,7 @@ Date Finder |
- |
-|
350 | |
- |
-|
351 | <p id="out">...</p> |
- |
-|
352 | |
- |
-|
353 | -<div id="footer">contact : support@github.com</div> |
- |
-|
354 | +<div id="footer">contact : email.support@github.com</div> |
- |
-|
355 | |
- |
-|
356 | <script type="text/javascript"> |
- |
-|
357 | |
- |
-|
358 | With these basic commands, you can use the interactive add mode to deal with your staging area a little more easily. |
- |
-|
359 | |
- |
-|
360 | ### Staging Patches ### |
- |
-|
361 | |
- |
-|
362 | It’s also possible for Git to stage certain parts of files and not the rest. For example, if you make two changes to your simplegit.rb file and want to stage one of them and not the other, doing so is very easy in Git. From the interactive prompt, type `5` or `p` (for patch). Git will ask you which files you would like to partially stage; then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one: |
- |
-|
363 | |
- |
-|
364 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
- |
-|
365 | index dd5ecc4..57399e0 100644 |
- |
-|
366 | --- a/lib/simplegit.rb |
- |
-|
367 | +++ b/lib/simplegit.rb |
- |
-|
368 | @@ -22,7 +22,7 @@ class SimpleGit |
- |
-|
369 | end |
- |
-|
370 | |
- |
-|
371 | def log(treeish = 'master') |
- |
-|
372 | - command("git log -n 25 #{treeish}") |
- |
-|
373 | + command("git log -n 30 #{treeish}") |
- |
-|
374 | end |
- |
-|
375 | |
- |
-|
376 | def blame(path) |
- |
-|
377 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? |
- |
-|
378 | |
- |
-|
379 | You have a lot of options at this point. Typing `?` shows a list of what you can do: |
- |
-|
380 | |
- |
-|
381 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? |
- |
-|
382 | y - stage this hunk |
- |
-|
383 | n - do not stage this hunk |
- |
-|
384 | a - stage this and all the remaining hunks in the file |
- |
-|
385 | d - do not stage this hunk nor any of the remaining hunks in the file |
- |
-|
386 | g - select a hunk to go to |
- |
-|
387 | / - search for a hunk matching the given regex |
- |
-|
388 | j - leave this hunk undecided, see next undecided hunk |
- |
-|
389 | J - leave this hunk undecided, see next hunk |
- |
-|
390 | k - leave this hunk undecided, see previous undecided hunk |
- |
-|
391 | K - leave this hunk undecided, see previous hunk |
- |
-|
392 | s - split the current hunk into smaller hunks |
- |
-|
393 | e - manually edit the current hunk |
- |
-|
394 | ? - print help |
- |
-|
395 | |
- |
-|
396 | Generally, you’ll type `y` or `n` if you want to stage each hunk, but staging all of them in certain files or skipping a hunk decision until later can be helpful too. If you stage one part of the file and leave another part unstaged, your status output will look like this: |
- |
-|
397 | |
- |
-|
398 | What now> 1 |
- |
-|
399 | staged unstaged path |
- |
-|
400 | 1: unchanged +0/-1 TODO |
- |
-|
401 | 2: +1/-1 nothing index.html |
- |
-|
402 | 3: +1/-1 +4/-0 lib/simplegit.rb |
- |
-|
403 | |
- |
-|
404 | The status of the simplegit.rb file is interesting. It shows you that a couple of lines are staged and a couple are unstaged. You’ve partially staged this file. At this point, you can exit the interactive adding script and run `git commit` to commit the partially staged files. |
- |
-|
405 | |
- |
-|
406 | Finally, you don’t need to be in interactive add mode to do the partial-file staging — you can start the same script by using `git add -p` or `git add --patch` on the command line. |
- |
-|
407 | |
- |
-|
408 | ## Stashing ## |
- |
-|
409 | |
- |
-|
410 | Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the `git stash` command. |
- |
-|
411 | |
- |
-|
412 | Stashing takes the dirty state of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time. |
- |
-|
413 | |
- |
-|
414 | ### Stashing Your Work ### |
- |
-|
415 | |
- |
-|
416 | To demonstrate, you’ll go into your project and start working on a couple of files and possibly stage one of the changes. If you run `git status`, you can see your dirty state: |
- |
-|
417 | |
- |
-|
418 | $ git status |
- |
-|
419 | # On branch master |
- |
-|
420 | # Changes to be committed: |
- |
-|
421 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
422 | # |
- |
-|
423 | # modified: index.html |
- |
-|
424 | # |
- |
-|
425 | # Changes not staged for commit: |
- |
-|
426 | # (use "git add <file>..." to update what will be committed) |
- |
-|
427 | # |
- |
-|
428 | # modified: lib/simplegit.rb |
- |
-|
429 | # |
- |
-|
430 | |
- |
-|
431 | Now you want to switch branches, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run `git stash`: |
- |
-|
432 | |
- |
-|
433 | $ git stash |
- |
-|
434 | Saved working directory and index state \ |
- |
-|
435 | "WIP on master: 049d078 added the index file" |
- |
-|
436 | HEAD is now at 049d078 added the index file |
- |
-|
437 | (To restore them type "git stash apply") |
- |
-|
438 | |
- |
-|
439 | Your working directory is clean: |
- |
-|
440 | |
- |
-|
441 | $ git status |
- |
-|
442 | # On branch master |
- |
-|
443 | nothing to commit, working directory clean |
- |
-|
444 | |
- |
-|
445 | At this point, you can easily switch branches and do work elsewhere; your changes are stored on your stack. To see which stashes you’ve stored, you can use `git stash list`: |
- |
-|
446 | |
- |
-|
447 | $ git stash list |
- |
-|
448 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
449 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
450 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
451 | |
- |
-|
452 | In this case, two stashes were done previously, so you have access to three different stashed works. You can reapply the one you just stashed by using the command shown in the help output of the original stash command: `git stash apply`. If you want to apply one of the older stashes, you can specify it by naming it, like this: `git stash apply stash@{2}`. If you don’t specify a stash, Git assumes the most recent stash and tries to apply it: |
- |
-|
453 | |
- |
-|
454 | $ git stash apply |
- |
-|
455 | # On branch master |
- |
-|
456 | # Changes not staged for commit: |
- |
-|
457 | # (use "git add <file>..." to update what will be committed) |
- |
-|
458 | # |
- |
-|
459 | # modified: index.html |
- |
-|
460 | # modified: lib/simplegit.rb |
- |
-|
461 | # |
- |
-|
462 | |
- |
-|
463 | You can see that Git re-modifies the files you uncommitted when you saved the stash. In this case, you had a clean working directory when you tried to apply the stash, and you tried to apply it on the same branch you saved it from; but having a clean working directory and applying it on the same branch aren’t necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash — Git gives you merge conflicts if anything no longer applies cleanly. |
- |
-|
464 | |
- |
-|
465 | The changes to your files were reapplied, but the file you staged before wasn’t restaged. To do that, you must run the `git stash apply` command with a `--index` option to tell the command to try to reapply the staged changes. If you had run that instead, you’d have gotten back to your original position: |
- |
-|
466 | |
- |
-|
467 | $ git stash apply --index |
- |
-|
468 | # On branch master |
- |
-|
469 | # Changes to be committed: |
- |
-|
470 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
471 | # |
- |
-|
472 | # modified: index.html |
- |
-|
473 | # |
- |
-|
474 | # Changes not staged for commit: |
- |
-|
475 | # (use "git add <file>..." to update what will be committed) |
- |
-|
476 | # |
- |
-|
477 | # modified: lib/simplegit.rb |
- |
-|
478 | # |
- |
-|
479 | |
- |
-|
480 | The apply option only tries to apply the stashed work — you continue to have it on your stack. To remove it, you can run `git stash drop` with the name of the stash to remove: |
- |
-|
481 | |
- |
-|
482 | $ git stash list |
- |
-|
483 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
484 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
485 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
486 | $ git stash drop stash@{0} |
- |
-|
487 | Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) |
- |
-|
488 | |
- |
-|
489 | You can also run `git stash pop` to apply the stash and then immediately drop it from your stack. |
- |
-|
490 | |
- |
-|
491 | ### Un-applying a Stash ### |
- |
-|
492 | |
- |
-|
493 | In some use case scenarios you might want to apply stashed changes, do some work, but then un-apply those changes that originally came from the stash. Git does not provide such a `stash unapply` command, but it is possible to achieve the effect by simply retrieving the patch associated with a stash and applying it in reverse: |
- |
-|
494 | |
- |
-|
495 | $ git stash show -p stash@{0} | git apply -R |
- |
-|
496 | |
- |
-|
497 | Again, if you don’t specify a stash, Git assumes the most recent stash: |
- |
-|
498 | |
- |
-|
499 | $ git stash show -p | git apply -R |
- |
-|
500 | |
- |
-|
501 | You may want to create an alias and effectively add a `stash-unapply` command to your Git. For example: |
- |
-|
502 | |
- |
-|
503 | $ git config --global alias.stash-unapply '!git stash show -p | git apply -R' |
- |
-|
504 | $ git stash apply |
- |
-|
505 | $ #... work work work |
- |
-|
506 | $ git stash-unapply |
- |
-|
507 | |
- |
-|
508 | ### Creating a Branch from a Stash ### |
- |
-|
509 | |
- |
-|
510 | If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you’ve since modified, you’ll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run `git stash branch`, which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully: |
- |
-|
511 | |
- |
-|
512 | $ git stash branch testchanges |
- |
-|
513 | Switched to a new branch "testchanges" |
- |
-|
514 | # On branch testchanges |
- |
-|
515 | # Changes to be committed: |
- |
-|
516 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
517 | # |
- |
-|
518 | # modified: index.html |
- |
-|
519 | # |
- |
-|
520 | # Changes not staged for commit: |
- |
-|
521 | # (use "git add <file>..." to update what will be committed) |
- |
-|
522 | # |
- |
-|
523 | # modified: lib/simplegit.rb |
- |
-|
524 | # |
- |
-|
525 | Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) |
- |
-|
526 | |
- |
-|
527 | This is a nice shortcut to recover stashed work easily and work on it in a new branch. |
- |
-|
528 | |
- |
-|
529 | ## Rewriting History ## |
- |
-|
530 | |
- |
-|
531 | Many times, when working with Git, you may want to revise your commit history for some reason. One of the great things about Git is that it allows you to make decisions at the last possible moment. You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with the stash command, and you can rewrite commits that already happened so they look like they happened in a different way. This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others. |
- |
-|
532 | |
- |
-|
533 | In this section, you’ll cover how to accomplish these very useful tasks so that you can make your commit history look the way you want before you share it with others. |
- |
-|
534 | |
- |
-|
535 | ### Changing the Last Commit ### |
- |
-|
536 | |
- |
-|
537 | Changing your last commit is probably the most common rewriting of history that you’ll do. You’ll often want to do two basic things to your last commit: change the commit message, or change the snapshot you just recorded by adding, changing and removing files. |
- |
-|
538 | |
- |
-|
539 | If you only want to modify your last commit message, it’s very simple: |
- |
-|
540 | |
- |
-|
541 | $ git commit --amend |
- |
-|
542 | |
- |
-|
543 | That drops you into your text editor, which has your last commit message in it, ready for you to modify the message. When you save and close the editor, the editor writes a new commit containing that message and makes it your new last commit. |
- |
-|
544 | |
- |
-|
545 | If you’ve committed and then you want to change the snapshot you committed by adding or changing files, possibly because you forgot to add a newly created file when you originally committed, the process works basically the same way. You stage the changes you want by editing a file and running `git add` on it or `git rm` to a tracked file, and the subsequent `git commit --amend` takes your current staging area and makes it the snapshot for the new commit. |
- |
-|
546 | |
- |
-|
547 | You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it. |
- |
-|
548 | |
- |
-|
549 | ### Changing Multiple Commit Messages ### |
- |
-|
550 | |
- |
-|
551 | To modify a commit that is farther back in your history, you must move to more complex tools. Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD they were originally based on instead of moving them to another one. With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the `-i` option to `git rebase`. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto. |
- |
-|
552 | |
- |
-|
553 | For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to `git rebase -i` the parent of the last commit you want to edit, which is `HEAD~2^` or `HEAD~3`. It may be easier to remember the `~3` because you’re trying to edit the last three commits; but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit: |
- |
-|
554 | |
- |
-|
555 | $ git rebase -i HEAD~3 |
- |
-|
556 | |
- |
-|
557 | Remember again that this is a rebasing command — every commit included in the range `HEAD~3..HEAD` will be rewritten, whether you change the message or not. Don’t include any commit you’ve already pushed to a central server — doing so will confuse other developers by providing an alternate version of the same change. |
- |
-|
558 | |
- |
-|
559 | Running this command gives you a list of commits in your text editor that looks something like this: |
- |
-|
560 | |
- |
-|
561 | pick f7f3f6d changed my name a bit |
- |
-|
562 | pick 310154e updated README formatting and added blame |
- |
-|
563 | pick a5f4a0d added cat-file |
- |
-|
564 | |
- |
-|
565 | # Rebase 710f0f8..a5f4a0d onto 710f0f8 |
- |
-|
566 | # |
- |
-|
567 | # Commands: |
- |
-|
568 | # p, pick = use commit |
- |
-|
569 | # r, reword = use commit, but edit the commit message |
- |
-|
570 | # e, edit = use commit, but stop for amending |
- |
-|
571 | # s, squash = use commit, but meld into previous commit |
- |
-|
572 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
573 | # x, exec = run command (the rest of the line) using shell |
- |
-|
574 | # |
- |
-|
575 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
576 | # |
- |
-|
577 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
578 | # |
- |
-|
579 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
580 | # |
- |
-|
581 | # Note that empty commits are commented out |
- |
-|
582 | |
- |
-|
583 | It’s important to note that these commits are listed in the opposite order than you normally see them using the `log` command. If you run a `log`, you see something like this: |
- |
-|
584 | |
- |
-|
585 | $ git log --pretty=format:"%h %s" HEAD~3..HEAD |
- |
-|
586 | a5f4a0d added cat-file |
- |
-|
587 | 310154e updated README formatting and added blame |
- |
-|
588 | f7f3f6d changed my name a bit |
- |
-|
589 | |
- |
-|
590 | Notice the reverse order. The interactive rebase gives you a script that it’s going to run. It will start at the commit you specify on the command line (`HEAD~3`) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that’s the first one it will replay. |
- |
-|
591 | |
- |
-|
592 | You need to edit the script so that it stops at the commit you want to edit. To do so, change the word pick to the word edit for each of the commits you want the script to stop after. For example, to modify only the third commit message, you change the file to look like this: |
- |
-|
593 | |
- |
-|
594 | edit f7f3f6d changed my name a bit |
- |
-|
595 | pick 310154e updated README formatting and added blame |
- |
-|
596 | pick a5f4a0d added cat-file |
- |
-|
597 | |
- |
-|
598 | When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message: |
- |
-|
599 | |
- |
-|
600 | <!-- This is actually weird, as the SHA-1 of 7482e0d is not present in the list, |
- |
-|
601 | nor is the commit message. Please review |
- |
-|
602 | --> |
- |
-|
603 | |
- |
-|
604 | $ git rebase -i HEAD~3 |
- |
-|
605 | Stopped at 7482e0d... updated the gemspec to hopefully work better |
- |
-|
606 | You can amend the commit now, with |
- |
-|
607 | |
- |
-|
608 | git commit --amend |
- |
-|
609 | |
- |
-|
610 | Once you’re satisfied with your changes, run |
- |
-|
611 | |
- |
-|
612 | git rebase --continue |
- |
-|
613 | |
- |
-|
614 | These instructions tell you exactly what to do. Type |
- |
-|
615 | |
- |
-|
616 | $ git commit --amend |
- |
-|
617 | |
- |
-|
618 | Change the commit message, and exit the editor. Then, run |
- |
-|
619 | |
- |
-|
620 | $ git rebase --continue |
- |
-|
621 | |
- |
-|
622 | This command will apply the other two commits automatically, and then you’re done. If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit. Each time, Git will stop, let you amend the commit, and continue when you’re finished. |
- |
-|
623 | |
- |
-|
624 | ### Reordering Commits ### |
- |
-|
625 | |
- |
-|
626 | You can also use interactive rebases to reorder or remove commits entirely. If you want to remove the "added cat-file" commit and change the order in which the other two commits are introduced, you can change the rebase script from this |
- |
-|
627 | |
- |
-|
628 | pick f7f3f6d changed my name a bit |
- |
-|
629 | pick 310154e updated README formatting and added blame |
- |
-|
630 | pick a5f4a0d added cat-file |
- |
-|
631 | |
- |
-|
632 | to this: |
- |
-|
633 | |
- |
-|
634 | pick 310154e updated README formatting and added blame |
- |
-|
635 | pick f7f3f6d changed my name a bit |
- |
-|
636 | |
- |
-|
637 | When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies `310154e` and then `f7f3f6d`, and then stops. You effectively change the order of those commits and remove the "added cat-file" commit completely. |
- |
-|
638 | |
- |
-|
639 | ### Squashing Commits ### |
- |
-|
640 | |
- |
-|
641 | It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool. The script puts helpful instructions in the rebase message: |
- |
-|
642 | |
- |
-|
643 | # |
- |
-|
644 | # Commands: |
- |
-|
645 | # p, pick = use commit |
- |
-|
646 | # r, reword = use commit, but edit the commit message |
- |
-|
647 | # e, edit = use commit, but stop for amending |
- |
-|
648 | # s, squash = use commit, but meld into previous commit |
- |
-|
649 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
650 | # x, exec = run command (the rest of the line) using shell |
- |
-|
651 | # |
- |
-|
652 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
653 | # |
- |
-|
654 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
655 | # |
- |
-|
656 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
657 | # |
- |
-|
658 | # Note that empty commits are commented out |
- |
-|
659 | |
- |
-|
660 | If, instead of "pick" or "edit", you specify "squash", Git applies both that change and the change directly before it and makes you merge the commit messages together. So, if you want to make a single commit from these three commits, you make the script look like this: |
- |
-|
661 | |
- |
-|
662 | pick f7f3f6d changed my name a bit |
- |
-|
663 | squash 310154e updated README formatting and added blame |
- |
-|
664 | squash a5f4a0d added cat-file |
- |
-|
665 | |
- |
-|
666 | When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages: |
- |
-|
667 | |
- |
-|
668 | # This is a combination of 3 commits. |
- |
-|
669 | # The first commit's message is: |
- |
-|
670 | changed my name a bit |
- |
-|
671 | |
- |
-|
672 | # This is the 2nd commit message: |
- |
-|
673 | |
- |
-|
674 | updated README formatting and added blame |
- |
-|
675 | |
- |
-|
676 | # This is the 3rd commit message: |
- |
-|
677 | |
- |
-|
678 | added cat-file |
- |
-|
679 | |
- |
-|
680 | When you save that, you have a single commit that introduces the changes of all three previous commits. |
- |
-|
681 | |
- |
-|
682 | ### Splitting a Commit ### |
- |
-|
683 | |
- |
-|
684 | Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with. For example, suppose you want to split the middle commit of your three commits. Instead of "updated README formatting and added blame", you want to split it into two commits: "updated README formatting" for the first, and "added blame" for the second. You can do that in the `rebase -i` script by changing the instruction on the commit you want to split to "edit": |
- |
-|
685 | |
- |
-|
686 | pick f7f3f6d changed my name a bit |
- |
-|
687 | edit 310154e updated README formatting and added blame |
- |
-|
688 | pick a5f4a0d added cat-file |
- |
-|
689 | |
- |
-|
690 | When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (`f7f3f6d`), applies the second (`310154e`), and drops you to the console. There, you can do a mixed reset of that commit with `git reset HEAD^`, which effectively undoes that commit and leaves the modified files unstaged. Now you can take the changes that have been reset, and create multiple commits out of them. Simply stage and commit files until you have several commits, and run `git rebase --continue` when you’re done: |
- |
-|
691 | |
- |
-|
692 | $ git reset HEAD^ |
- |
-|
693 | $ git add README |
- |
-|
694 | $ git commit -m 'updated README formatting' |
- |
-|
695 | $ git add lib/simplegit.rb |
- |
-|
696 | $ git commit -m 'added blame' |
- |
-|
697 | $ git rebase --continue |
- |
-|
698 | |
- |
-|
699 | Git applies the last commit (`a5f4a0d`) in the script, and your history looks like this: |
- |
-|
700 | |
- |
-|
701 | $ git log -4 --pretty=format:"%h %s" |
- |
-|
702 | 1c002dd added cat-file |
- |
-|
703 | 9b29157 added blame |
- |
-|
704 | 35cfb2b updated README formatting |
- |
-|
705 | f3cc40e changed my name a bit |
- |
-|
706 | |
- |
-|
707 | Once again, this changes the SHAs of all the commits in your list, so make sure no commit shows up in that list that you’ve already pushed to a shared repository. |
- |
-|
708 | |
- |
-|
709 | ### The Nuclear Option: filter-branch ### |
- |
-|
710 | |
- |
-|
711 | There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way — for instance, changing your e-mail address globally or removing a file from every commit. The command is `filter-branch`, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite. However, it can be very useful. You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of. |
- |
-|
712 | |
- |
-|
713 | #### Removing a File from Every Commit #### |
- |
-|
714 | |
- |
-|
715 | This occurs fairly commonly. Someone accidentally commits a huge binary file with a thoughtless `git add .`, and you want to remove it everywhere. Perhaps you accidentally committed a file that contained a password, and you want to make your project open source. `filter-branch` is the tool you probably want to use to scrub your entire history. To remove a file named passwords.txt from your entire history, you can use the `--tree-filter` option to `filter-branch`: |
- |
-|
716 | |
- |
-|
717 | $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD |
- |
-|
718 | Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) |
- |
-|
719 | Ref 'refs/heads/master' was rewritten |
- |
-|
720 | |
- |
-|
721 | The `--tree-filter` option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt from every snapshot, whether it exists or not. If you want to remove all accidentally committed editor backup files, you can run something like `git filter-branch --tree-filter "rm -f *~" HEAD`. |
- |
-|
722 | |
- |
-|
723 | You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end. It’s generally a good idea to do this in a testing branch and then hard-reset your master branch after you’ve determined the outcome is what you really want. To run `filter-branch` on all your branches, you can pass `--all` to the command. |
- |
-|
724 | |
- |
-|
725 | #### Making a Subdirectory the New Root #### |
- |
-|
726 | |
- |
-|
727 | Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk, tags, and so on). If you want to make the `trunk` subdirectory be the new project root for every commit, `filter-branch` can help you do that, too: |
- |
-|
728 | |
- |
-|
729 | $ git filter-branch --subdirectory-filter trunk HEAD |
- |
-|
730 | Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) |
- |
-|
731 | Ref 'refs/heads/master' was rewritten |
- |
-|
732 | |
- |
-|
733 | Now your new project root is what was in the `trunk` subdirectory each time. Git will also automatically remove commits that did not affect the subdirectory. |
- |
-|
734 | |
- |
-|
735 | #### Changing E-Mail Addresses Globally #### |
- |
-|
736 | |
- |
-|
737 | Another common case is that you forgot to run `git config` to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with `filter-branch` as well. You need to be careful to change only the e-mail addresses that are yours, so you use `--commit-filter`: |
- |
-|
738 | |
- |
-|
739 | $ git filter-branch --commit-filter ' |
- |
-|
740 | if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; |
- |
-|
741 | then |
- |
-|
742 | GIT_AUTHOR_NAME="Scott Chacon"; |
- |
-|
743 | GIT_AUTHOR_EMAIL="schacon@example.com"; |
- |
-|
744 | git commit-tree "$@"; |
- |
-|
745 | else |
- |
-|
746 | git commit-tree "$@"; |
- |
-|
747 | fi' HEAD |
- |
-|
748 | |
- |
-|
749 | This goes through and rewrites every commit to have your new address. Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address. |
- |
-|
750 | |
- |
-|
751 | ### The Very Fast Nuclear Option: Big Friendly Giant Repo Cleaner (BFG) ### |
- |
-|
752 | |
- |
-|
753 | [Roberto Tyley](https://github.com/rtyley) has written a similar tool to `filter-branch` called the BFG. BFG cannot do as much as `filter-branch`, but it is _very_ fast and on a large repository this can make a big difference. If the change you want to make is in the scope of BFG capability, and you have performance issues, then you should consider using it. |
- |
-|
754 | |
- |
-|
755 | See the [BFG](http://rtyley.github.io/bfg-repo-cleaner/) website for details. |
- |
-|
756 | |
- |
-|
757 | ## Debugging with Git ## |
- |
-|
758 | |
- |
-|
759 | Git also provides a couple of tools to help you debug issues in your projects. Because Git is designed to work with nearly any type of project, these tools are pretty generic, but they can often help you hunt for a bug or culprit when things go wrong. |
- |
-|
760 | |
- |
-|
761 | ### File Annotation ### |
- |
-|
762 | |
- |
-|
763 | If you track down a bug in your code and want to know when it was introduced and why, file annotation is often your best tool. It shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with `git blame` to see when each line of the method was last edited and by whom. This example uses the `-L` option to limit the output to lines 12 through 22: |
- |
-|
764 | |
- |
-|
765 | $ git blame -L 12,22 simplegit.rb |
- |
-|
766 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master') |
- |
-|
767 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}") |
- |
-|
768 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end |
- |
-|
769 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15) |
- |
-|
770 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master') |
- |
-|
771 | 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}") |
- |
-|
772 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end |
- |
-|
773 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19) |
- |
-|
774 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path) |
- |
-|
775 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}") |
- |
-|
776 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end |
- |
-|
777 | |
- |
-|
778 | Notice that the first field is the partial SHA-1 of the commit that last modified that line. The next two fields are values extracted from that commit—the author name and the authored date of that commit — so you can easily see who modified that line and when. After that come the line number and the content of the file. Also note the `^4832fe2` commit lines, which designate that those lines were in this file’s original commit. That commit is when this file was first added to this project, and those lines have been unchanged since. This is a tad confusing, because now you’ve seen at least three different ways that Git uses the `^` to modify a commit SHA, but that is what it means here. |
- |
-|
779 | |
- |
-|
780 | Another cool thing about Git is that it doesn’t track file renames explicitly. It records the snapshots and then tries to figure out what was renamed implicitly, after the fact. One of the interesting features of this is that you can ask it to figure out all sorts of code movement as well. If you pass `-C` to `git blame`, Git analyzes the file you’re annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere. Recently, I was refactoring a file named `GITServerHandler.m` into multiple files, one of which was `GITPackUpload.m`. By blaming `GITPackUpload.m` with the `-C` option, I could see where sections of the code originally came from: |
- |
-|
781 | |
- |
-|
782 | $ git blame -C -L 141,153 GITPackUpload.m |
- |
-|
783 | f344f58d GITServerHandler.m (Scott 2009-01-04 141) |
- |
-|
784 | f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC |
- |
-|
785 | f344f58d GITServerHandler.m (Scott 2009-01-04 143) { |
- |
-|
786 | 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI |
- |
-|
787 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 145) |
- |
-|
788 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha; |
- |
-|
789 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g |
- |
-|
790 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 148) |
- |
-|
791 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI |
- |
-|
792 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 150) |
- |
-|
793 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) { |
- |
-|
794 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb |
- |
-|
795 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 153) |
- |
-|
796 | |
- |
-|
797 | This is really useful. Normally, you get as the original commit the commit where you copied the code over, because that is the first time you touched those lines in this file. Git tells you the original commit where you wrote those lines, even if it was in another file. |
- |
-|
798 | |
- |
-|
799 | ### Binary Search ### |
- |
-|
800 | |
- |
-|
801 | Annotating a file helps if you know where the issue is to begin with. If you don’t know what is breaking, and there have been dozens or hundreds of commits since the last state where you know the code worked, you’ll likely turn to `git bisect` for help. The `bisect` command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. |
- |
-|
802 | |
- |
-|
803 | Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that. You go back to your code, and it turns out you can reproduce the issue, but you can’t figure out what is going wrong. You can bisect the code to find out. First you run `git bisect start` to get things going, and then you use `git bisect bad` to tell the system that the current commit you’re on is broken. Then, you must tell bisect when the last known good state was, using `git bisect good [good_commit]`: |
- |
-|
804 | |
- |
-|
805 | $ git bisect start |
- |
-|
806 | $ git bisect bad |
- |
-|
807 | $ git bisect good v1.0 |
- |
-|
808 | Bisecting: 6 revisions left to test after this |
- |
-|
809 | [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo |
- |
-|
810 | |
- |
-|
811 | Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. If it does, then it was introduced sometime before this middle commit; if it doesn’t, then the problem was introduced sometime after the middle commit. It turns out there is no issue here, and you tell Git that by typing `git bisect good` and continue your journey: |
- |
-|
812 | |
- |
-|
813 | $ git bisect good |
- |
-|
814 | Bisecting: 3 revisions left to test after this |
- |
-|
815 | [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing |
- |
-|
816 | |
- |
-|
817 | Now you’re on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with `git bisect bad`: |
- |
-|
818 | |
- |
-|
819 | $ git bisect bad |
- |
-|
820 | Bisecting: 1 revisions left to test after this |
- |
-|
821 | [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table |
- |
-|
822 | |
- |
-|
823 | This commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and show some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug: |
- |
-|
824 | |
- |
-|
825 | $ git bisect good |
- |
-|
826 | b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit |
- |
-|
827 | commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 |
- |
-|
828 | Author: PJ Hyett <pjhyett@example.com> |
- |
-|
829 | Date: Tue Jan 27 14:48:32 2009 -0800 |
- |
-|
830 | |
- |
-|
831 | secure this thing |
- |
-|
832 | |
- |
-|
833 | :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730 |
- |
-|
834 | f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config |
- |
-|
835 | |
- |
-|
836 | When you’re finished, you should run `git bisect reset` to reset your HEAD to where you were before you started, or you’ll end up in a weird state: |
- |
-|
837 | |
- |
-|
838 | $ git bisect reset |
- |
-|
839 | |
- |
-|
840 | This is a powerful tool that can help you check hundreds of commits for an introduced bug in minutes. In fact, if you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate `git bisect`. First, you again tell it the scope of the bisect by providing the known bad and good commits. You can do this by listing them with the `bisect start` command if you want, listing the known bad commit first and the known good commit second: |
- |
-|
841 | |
- |
-|
842 | $ git bisect start HEAD v1.0 |
- |
-|
843 | $ git bisect run test-error.sh |
- |
-|
844 | |
- |
-|
845 | Doing so automatically runs `test-error.sh` on each checked-out commit until Git finds the first broken commit. You can also run something like `make` or `make tests` or whatever you have that runs automated tests for you. |
- |
-|
846 | |
- |
-|
847 | ## Submodules ## |
- |
-|
848 | |
- |
-|
849 | It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other. |
- |
-|
850 | |
- |
-|
851 | Here’s an example. Suppose you’re developing a web site and creating Atom feeds. Instead of writing your own Atom-generating code, you decide to use a library. You’re likely to have to either include this code from a shared library like a CPAN install or Ruby gem, or copy the source code into your own project tree. The issue with including the library is that it’s difficult to customize the library in any way and often more difficult to deploy it, because you need to make sure every client has that library available. The issue with vendoring the code into your own project is that any custom changes you make are difficult to merge when upstream changes become available. |
- |
-|
852 | |
- |
-|
853 | Git addresses this issue using submodules. Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. |
- |
-|
854 | |
- |
-|
855 | ### Starting with Submodules ### |
- |
-|
856 | |
- |
-|
857 | Suppose you want to add the Rack library (a Ruby web server gateway interface) to your project, possibly maintain your own changes to it, but continue to merge in upstream changes. The first thing you should do is clone the external repository into your subdirectory. You add external projects as submodules with the `git submodule add` command: |
- |
-|
858 | |
- |
-|
859 | $ git submodule add git://github.com/chneukirchen/rack.git rack |
- |
-|
860 | Initialized empty Git repository in /opt/subtest/rack/.git/ |
- |
-|
861 | remote: Counting objects: 3181, done. |
- |
-|
862 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
863 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
864 | Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done. |
- |
-|
865 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
866 | |
- |
-|
867 | Now you have the Rack project under a subdirectory named `rack` within your project. You can go into that subdirectory, make changes, add your own writable remote repository to push your changes into, fetch and merge from the original repository, and more. If you run `git status` right after you add the submodule, you see two things: |
- |
-|
868 | |
- |
-|
869 | $ git status |
- |
-|
870 | # On branch master |
- |
-|
871 | # Changes to be committed: |
- |
-|
872 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
873 | # |
- |
-|
874 | # new file: .gitmodules |
- |
-|
875 | # new file: rack |
- |
-|
876 | # |
- |
-|
877 | |
- |
-|
878 | First you notice the `.gitmodules` file. This is a configuration file that stores the mapping between the project’s URL and the local subdirectory you’ve pulled it into: |
- |
-|
879 | |
- |
-|
880 | $ cat .gitmodules |
- |
-|
881 | [submodule "rack"] |
- |
-|
882 | path = rack |
- |
-|
883 | url = git://github.com/chneukirchen/rack.git |
- |
-|
884 | |
- |
-|
885 | If you have multiple submodules, you’ll have multiple entries in this file. It’s important to note that this file is version-controlled with your other files, like your `.gitignore` file. It’s pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from. |
- |
-|
886 | |
- |
-|
887 | The other listing in the `git status` output is the rack entry. If you run `git diff` on that, you see something interesting: |
- |
-|
888 | |
- |
-|
889 | $ git diff --cached rack |
- |
-|
890 | diff --git a/rack b/rack |
- |
-|
891 | new file mode 160000 |
- |
-|
892 | index 0000000..08d709f |
- |
-|
893 | --- /dev/null |
- |
-|
894 | +++ b/rack |
- |
-|
895 | @@ -0,0 +1 @@ |
- |
-|
896 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
897 | |
- |
-|
898 | Although `rack` is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git records it as a particular commit from that repository. When you make changes and commit in that subdirectory, the superproject notices that the HEAD there has changed and records the exact commit you’re currently working off of; that way, when others clone this project, they can re-create the environment exactly. |
- |
-|
899 | |
- |
-|
900 | This is an important point with submodules: you record them as the exact commit they’re at. You can’t record a submodule at `master` or some other symbolic reference. |
- |
-|
901 | |
- |
-|
902 | When you commit, you see something like this: |
- |
-|
903 | |
- |
-|
904 | $ git commit -m 'first commit with submodule rack' |
- |
-|
905 | [master 0550271] first commit with submodule rack |
- |
-|
906 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
907 | create mode 100644 .gitmodules |
- |
-|
908 | create mode 160000 rack |
- |
-|
909 | |
- |
-|
910 | Notice the 160000 mode for the rack entry. That is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file. |
- |
-|
911 | |
- |
-|
912 | You can treat the `rack` directory as a separate project and then update your superproject from time to time with a pointer to the latest commit in that subproject. All the Git commands work independently in the two directories: |
- |
-|
913 | |
- |
-|
914 | $ git log -1 |
- |
-|
915 | commit 0550271328a0038865aad6331e620cd7238601bb |
- |
-|
916 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
917 | Date: Thu Apr 9 09:03:56 2009 -0700 |
- |
-|
918 | |
- |
-|
919 | first commit with submodule rack |
- |
-|
920 | $ cd rack/ |
- |
-|
921 | $ git log -1 |
- |
-|
922 | commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
923 | Author: Christian Neukirchen <chneukirchen@gmail.com> |
- |
-|
924 | Date: Wed Mar 25 14:49:04 2009 +0100 |
- |
-|
925 | |
- |
-|
926 | Document version change |
- |
-|
927 | |
- |
-|
928 | ### Cloning a Project with Submodules ### |
- |
-|
929 | |
- |
-|
930 | Here you’ll clone a project with a submodule in it. When you receive such a project, you get the directories that contain submodules, but none of the files yet: |
- |
-|
931 | |
- |
-|
932 | $ git clone git://github.com/schacon/myproject.git |
- |
-|
933 | Initialized empty Git repository in /opt/myproject/.git/ |
- |
-|
934 | remote: Counting objects: 6, done. |
- |
-|
935 | remote: Compressing objects: 100% (4/4), done. |
- |
-|
936 | remote: Total 6 (delta 0), reused 0 (delta 0) |
- |
-|
937 | Receiving objects: 100% (6/6), done. |
- |
-|
938 | $ cd myproject |
- |
-|
939 | $ ls -l |
- |
-|
940 | total 8 |
- |
-|
941 | -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README |
- |
-|
942 | drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack |
- |
-|
943 | $ ls rack/ |
- |
-|
944 | $ |
- |
-|
945 | |
- |
-|
946 | The `rack` directory is there, but empty. You must run two commands: `git submodule init` to initialize your local configuration file, and `git submodule update` to fetch all the data from that project and check out the appropriate commit listed in your superproject: |
- |
-|
947 | |
- |
-|
948 | $ git submodule init |
- |
-|
949 | Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack' |
- |
-|
950 | $ git submodule update |
- |
-|
951 | Initialized empty Git repository in /opt/myproject/rack/.git/ |
- |
-|
952 | remote: Counting objects: 3181, done. |
- |
-|
953 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
954 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
955 | Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done. |
- |
-|
956 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
957 | Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433' |
- |
-|
958 | |
- |
-|
959 | Now your `rack` subdirectory is at the exact state it was in when you committed earlier. If another developer makes changes to the rack code and commits, and you pull that reference down and merge it in, you get something a bit odd: |
- |
-|
960 | |
- |
-|
961 | $ git merge origin/master |
- |
-|
962 | Updating 0550271..85a3eee |
- |
-|
963 | Fast forward |
- |
-|
964 | rack | 2 +- |
- |
-|
965 | 1 files changed, 1 insertions(+), 1 deletions(-) |
- |
-|
966 | [master*]$ git status |
- |
-|
967 | # On branch master |
- |
-|
968 | # Changes not staged for commit: |
- |
-|
969 | # (use "git add <file>..." to update what will be committed) |
- |
-|
970 | # (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
971 | # |
- |
-|
972 | # modified: rack |
- |
-|
973 | # |
- |
-|
974 | |
- |
-|
975 | You merged in what is basically a change to the pointer for your submodule; but it doesn’t update the code in the submodule directory, so it looks like you have a dirty state in your working directory: |
- |
-|
976 | |
- |
-|
977 | $ git diff |
- |
-|
978 | diff --git a/rack b/rack |
- |
-|
979 | index 6c5e70b..08d709f 160000 |
- |
-|
980 | --- a/rack |
- |
-|
981 | +++ b/rack |
- |
-|
982 | @@ -1 +1 @@ |
- |
-|
983 | -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
984 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
985 | |
- |
-|
986 | This is the case because the pointer you have for the submodule isn’t what is actually in the submodule directory. To fix this, you must run `git submodule update` again: |
- |
-|
987 | |
- |
-|
988 | $ git submodule update |
- |
-|
989 | remote: Counting objects: 5, done. |
- |
-|
990 | remote: Compressing objects: 100% (3/3), done. |
- |
-|
991 | remote: Total 3 (delta 1), reused 2 (delta 0) |
- |
-|
992 | Unpacking objects: 100% (3/3), done. |
- |
-|
993 | From git@github.com:schacon/rack |
- |
-|
994 | 08d709f..6c5e70b master -> origin/master |
- |
-|
995 | Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0' |
- |
-|
996 | |
- |
-|
997 | You have to do this every time you pull down a submodule change in the main project. It’s strange, but it works. |
- |
-|
998 | |
- |
-|
999 | One common problem happens when a developer makes a change locally in a submodule but doesn’t push it to a public server. Then, they commit a pointer to that non-public state and push up the superproject. When other developers try to run `git submodule update`, the submodule system can’t find the commit that is referenced, because it exists only on the first developer’s system. If that happens, you see an error like this: |
- |
-|
1000 | |
- |
-|
1001 | $ git submodule update |
- |
-|
1002 | fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
1003 | Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack' |
- |
-|
1004 | |
- |
-|
1005 | You have to see who last changed the submodule: |
- |
-|
1006 | |
- |
-|
1007 | $ git log -1 rack |
- |
-|
1008 | commit 85a3eee996800fcfa91e2119372dd4172bf76678 |
- |
-|
1009 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
1010 | Date: Thu Apr 9 09:19:14 2009 -0700 |
- |
-|
1011 | |
- |
-|
1012 | added a submodule reference I will never make public. hahahahaha! |
- |
-|
1013 | |
- |
-|
1014 | Then, you e-mail that guy and yell at him. |
- |
-|
1015 | |
- |
-|
1016 | ### Superprojects ### |
- |
-|
1017 | |
- |
-|
1018 | Sometimes, developers want to get a combination of a large project’s subdirectories, depending on what team they’re on. This is common if you’re coming from CVS or Subversion, where you’ve defined a module or collection of subdirectories, and you want to keep this type of workflow. |
- |
-|
1019 | |
- |
-|
1020 | A good way to do this in Git is to make each of the subdirectories a separate Git repository and then create superproject Git repositories that contain multiple submodules. A benefit of this approach is that you can more specifically define the relationships between the projects with tags and branches in the superprojects. |
- |
-|
1021 | |
- |
-|
1022 | ### Issues with Submodules ### |
- |
-|
1023 | |
- |
-|
1024 | Using submodules isn’t without hiccups, however. First, you must be relatively careful when working in the submodule directory. When you run `git submodule update`, it checks out the specific version of the project, but not within a branch. This is called having a detached HEAD — it means the HEAD file points directly to a commit, not to a symbolic reference. The issue is that you generally don’t want to work in a detached HEAD environment, because it’s easy to lose changes. If you do an initial `submodule update`, commit in that submodule directory without creating a branch to work in, and then run `git submodule update` again from the superproject without committing in the meantime, Git will overwrite your changes without telling you. Technically you won’t lose the work, but you won’t have a branch pointing to it, so it will be somewhat difficult to retrieve. |
- |
-|
1025 | |
- |
-|
1026 | To avoid this issue, create a branch when you work in a submodule directory with `git checkout -b work` or something equivalent. When you do the submodule update a second time, it will still revert your work, but at least you have a pointer to get back to. |
- |
-|
1027 | |
- |
-|
1028 | Switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory: |
- |
-|
1029 | |
- |
-|
1030 | $ git checkout -b rack |
- |
-|
1031 | Switched to a new branch "rack" |
- |
-|
1032 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1033 | Initialized empty Git repository in /opt/myproj/rack/.git/ |
- |
-|
1034 | ... |
- |
-|
1035 | Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done. |
- |
-|
1036 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1037 | $ git commit -am 'added rack submodule' |
- |
-|
1038 | [rack cc49a69] added rack submodule |
- |
-|
1039 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
1040 | create mode 100644 .gitmodules |
- |
-|
1041 | create mode 160000 rack |
- |
-|
1042 | $ git checkout master |
- |
-|
1043 | Switched to branch "master" |
- |
-|
1044 | $ git status |
- |
-|
1045 | # On branch master |
- |
-|
1046 | # Untracked files: |
- |
-|
1047 | # (use "git add <file>..." to include in what will be committed) |
- |
-|
1048 | # |
- |
-|
1049 | # rack/ |
- |
-|
1050 | |
- |
-|
1051 | You have to either move it out of the way or remove it, in which case you have to clone it again when you switch back—and you may lose local changes or branches that you didn’t push up. |
- |
-|
1052 | |
- |
-|
1053 | The last main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have the rack files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run `submodule add`, Git yells at you: |
- |
-|
1054 | |
- |
-|
1055 | $ rm -Rf rack/ |
- |
-|
1056 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1057 | 'rack' already exists in the index |
- |
-|
1058 | |
- |
-|
1059 | You have to unstage the `rack` directory first. Then you can add the submodule: |
- |
-|
1060 | |
- |
-|
1061 | $ git rm -r rack |
- |
-|
1062 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1063 | Initialized empty Git repository in /opt/testsub/rack/.git/ |
- |
-|
1064 | remote: Counting objects: 3184, done. |
- |
-|
1065 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1066 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1067 | Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done. |
- |
-|
1068 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1069 | |
- |
-|
1070 | Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule — you get this error: |
- |
-|
1071 | |
- |
-|
1072 | $ git checkout master |
- |
-|
1073 | error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge. |
- |
-|
1074 | |
- |
-|
1075 | You have to move the `rack` submodule directory out of the way before you can switch to a branch that doesn’t have it: |
- |
-|
1076 | |
- |
-|
1077 | $ mv rack /tmp/ |
- |
-|
1078 | $ git checkout master |
- |
-|
1079 | Switched to branch "master" |
- |
-|
1080 | $ ls |
- |
-|
1081 | README rack |
- |
-|
1082 | |
- |
-|
1083 | Then, when you switch back, you get an empty `rack` directory. You can either run `git submodule update` to reclone, or you can move your `/tmp/rack` directory back into the empty directory. |
- |
-|
1084 | |
- |
-|
1085 | ## Subtree Merging ## |
- |
-|
1086 | |
- |
-|
1087 | Now that you’ve seen the difficulties of the submodule system, let’s look at an alternate way to solve the same problem. When Git merges, it looks at what it has to merge together and then chooses an appropriate merging strategy to use. If you’re merging two branches, Git uses a _recursive_ strategy. If you’re merging more than two branches, Git picks the _octopus_ strategy. These strategies are automatically chosen for you because the recursive strategy can handle complex three-way merge situations — for example, more than one common ancestor — but it can only handle merging two branches. The octopus merge can handle multiple branches but is more cautious to avoid difficult conflicts, so it’s chosen as the default strategy if you’re trying to merge more than two branches. |
- |
-|
1088 | |
- |
-|
1089 | However, there are other strategies you can choose as well. One of them is the _subtree_ merge, and you can use it to deal with the subproject issue. Here you’ll see how to do the same rack embedding as in the last section, but using subtree merges instead. |
- |
-|
1090 | |
- |
-|
1091 | The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one and vice versa. When you specify a subtree merge, Git is smart enough to figure out that one is a subtree of the other and merge appropriately — it’s pretty amazing. |
- |
-|
1092 | |
- |
-|
1093 | You first add the Rack application to your project. You add the Rack project as a remote reference in your own project and then check it out into its own branch: |
- |
-|
1094 | |
- |
-|
1095 | $ git remote add rack_remote git@github.com:schacon/rack.git |
- |
-|
1096 | $ git fetch rack_remote |
- |
-|
1097 | warning: no common commits |
- |
-|
1098 | remote: Counting objects: 3184, done. |
- |
-|
1099 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1100 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1101 | Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. |
- |
-|
1102 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1103 | From git@github.com:schacon/rack |
- |
-|
1104 | * [new branch] build -> rack_remote/build |
- |
-|
1105 | * [new branch] master -> rack_remote/master |
- |
-|
1106 | * [new branch] rack-0.4 -> rack_remote/rack-0.4 |
- |
-|
1107 | * [new branch] rack-0.9 -> rack_remote/rack-0.9 |
- |
-|
1108 | $ git checkout -b rack_branch rack_remote/master |
- |
-|
1109 | Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. |
- |
-|
1110 | Switched to a new branch "rack_branch" |
- |
-|
1111 | |
- |
-|
1112 | Now you have the root of the Rack project in your `rack_branch` branch and your own project in the `master` branch. If you check out one and then the other, you can see that they have different project roots: |
- |
-|
1113 | |
- |
-|
1114 | $ ls |
- |
-|
1115 | AUTHORS KNOWN-ISSUES Rakefile contrib lib |
- |
-|
1116 | COPYING README bin example test |
- |
-|
1117 | $ git checkout master |
- |
-|
1118 | Switched to branch "master" |
- |
-|
1119 | $ ls |
- |
-|
1120 | README |
- |
-|
1121 | |
- |
-|
1122 | You want to pull the Rack project into your `master` project as a subdirectory. You can do that in Git with `git read-tree`. You’ll learn more about `read-tree` and its friends in Chapter 9, but for now know that it reads the root tree of one branch into your current staging area and working directory. You just switched back to your `master` branch, and you pull the `rack` branch into the `rack` subdirectory of your `master` branch of your main project: |
- |
-|
1123 | |
- |
-|
1124 | $ git read-tree --prefix=rack/ -u rack_branch |
- |
-|
1125 | |
- |
-|
1126 | When you commit, it looks like you have all the Rack files under that subdirectory — as though you copied them in from a tarball. What gets interesting is that you can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, you can pull in upstream changes by switching to that branch and pulling: |
- |
-|
1127 | |
- |
-|
1128 | $ git checkout rack_branch |
- |
-|
1129 | $ git pull |
- |
-|
1130 | |
- |
-|
1131 | Then, you can merge those changes back into your master branch. You can use `git merge -s subtree` and it will work fine; but Git will also merge the histories together, which you probably don’t want. To pull in the changes and prepopulate the commit message, use the `--squash` and `--no-commit` options as well as the `-s subtree` strategy option: |
- |
-|
1132 | |
- |
-|
1133 | $ git checkout master |
- |
-|
1134 | $ git merge --squash -s subtree --no-commit rack_branch |
- |
-|
1135 | Squash commit -- not updating HEAD |
- |
-|
1136 | Automatic merge went well; stopped before committing as requested |
- |
-|
1137 | |
- |
-|
1138 | All the changes from your Rack project are merged in and ready to be committed locally. You can also do the opposite — make changes in the `rack` subdirectory of your master branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. |
- |
-|
1139 | |
- |
-|
1140 | To get a diff between what you have in your `rack` subdirectory and the code in your `rack_branch` branch — to see if you need to merge them — you can’t use the normal `diff` command. Instead, you must run `git diff-tree` with the branch you want to compare to: |
- |
-|
1141 | |
- |
-|
1142 | $ git diff-tree -p rack_branch |
- |
-|
1143 | |
- |
-|
1144 | Or, to compare what is in your `rack` subdirectory with what the `master` branch on the server was the last time you fetched, you can run |
- |
-|
1145 | |
- |
-|
1146 | $ git diff-tree -p rack_remote/master |
- |
-|
1147 | |
- |
-|
1148 | ## Summary ## |
- |
-|
1149 | |
- |
-|
1150 | You’ve seen a number of advanced tools that allow you to manipulate your commits and staging area more precisely. When you notice issues, you should be able to easily figure out what commit introduced them, when, and by whom. If you want to use subprojects in your project, you’ve learned a few ways to accommodate those needs. At this point, you should be able to do most of the things in Git that you’ll need on the command line day to day and feel comfortable doing so. |
- |
-|
1151 | |
-320 | |
-
C:\Users\15625\Documents\Git\progit\en\06-git-tools\01-chapter6.markdown | -C:\Users\15625\Documents\Git\progit2-ja\book\07-git-tools\sections\rewriting-history.asc | -||
---|---|---|---|
.1 | # Git Tools # |
-1 | [[_rewriting_history]] |
-
2 | |
- |
-|
3 | 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. |
- |
-|
4 | |
- |
-|
5 | 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. |
- |
-|
6 | |
- |
-|
7 | ## Revision Selection ## |
- |
-|
8 | |
- |
-|
9 | 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. |
- |
-|
10 | |
- |
-|
11 | ### Single Revisions ### |
- |
-|
12 | |
- |
-|
13 | 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. |
- |
-|
14 | |
- |
-|
15 | ### Short SHA ### |
- |
-|
16 | |
- |
-|
17 | 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. |
- |
-|
18 | |
- |
-|
19 | For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: |
- |
-|
20 | |
- |
-|
21 | $ git log |
- |
-|
22 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
23 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
24 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
25 | |
- |
-|
26 | fixed refs handling, added gc auto, updated tests |
- |
-|
27 | |
- |
-|
28 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
29 | Merge: 1c002dd... 35cfb2b... |
- |
-|
30 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
31 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
32 | |
- |
-|
33 | Merge commit 'phedders/rdocs' |
- |
-|
34 | |
- |
-|
35 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
36 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
37 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
38 | |
- |
-|
39 | added some blame and merge stuff |
- |
-|
40 | |
- |
-|
41 | In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): |
- |
-|
42 | |
- |
-|
43 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
44 | $ git show 1c002dd4b536e7479f |
- |
-|
45 | $ git show 1c002d |
- |
-|
46 | |
- |
-|
47 | 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: |
- |
-|
48 | |
- |
-|
49 | $ git log --abbrev-commit --pretty=oneline |
- |
-|
50 | ca82a6d changed the version number |
- |
-|
51 | 085bb3b removed unnecessary test code |
- |
-|
52 | a11bef0 first commit |
- |
-|
53 | |
- |
-|
54 | 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. |
- |
-|
55 | |
- |
-|
56 | ### A SHORT NOTE ABOUT SHA-1 ### |
- |
-|
57 | |
- |
-|
58 | 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? |
- |
-|
59 | |
- |
-|
60 | 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. |
- |
-|
61 | |
- |
-|
62 | 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. |
- |
-|
63 | |
- |
-|
64 | 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. 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. |
- |
-|
65 | |
- |
-|
66 | ### Branch References ### |
- |
-|
67 | |
- |
-|
68 | The most straightforward way to specify a commit requires that it have 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`: |
- |
-|
69 | |
- |
-|
70 | $ git show ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
71 | $ git show topic1 |
- |
-|
72 | |
- |
-|
73 | 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 Chapter 9 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. |
- |
-|
74 | |
- |
-|
75 | $ git rev-parse topic1 |
- |
-|
76 | ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
77 | |
- |
-|
78 | ### RefLog Shortnames ### |
- |
-|
79 | |
- |
-|
80 | 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. |
- |
-|
81 | |
- |
-|
82 | You can see your reflog by using `git reflog`: |
- |
-|
83 | |
- |
-|
84 | $ git reflog |
- |
-|
85 | 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated |
- |
-|
86 | d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. |
- |
-|
87 | 1c002dd HEAD@{2}: commit: added some blame and merge stuff |
- |
-|
88 | 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD |
- |
-|
89 | 95df984 HEAD@{4}: commit: # This is a combination of two commits. |
- |
-|
90 | 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD |
- |
-|
91 | 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD |
- |
-|
92 | |
- |
-|
93 | 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: |
- |
-|
94 | |
- |
-|
95 | $ git show HEAD@{5} |
- |
-|
96 | |
- |
-|
97 | 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 |
- |
-|
98 | |
- |
-|
99 | $ git show master@{yesterday} |
- |
-|
100 | |
- |
-|
101 | 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. |
- |
-|
102 | |
- |
-|
103 | To see reflog information formatted like the `git log` output, you can run `git log -g`: |
- |
-|
104 | |
- |
-|
105 | $ git log -g master |
- |
-|
106 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
107 | Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) |
- |
-|
108 | Reflog message: commit: fixed refs handling, added gc auto, updated |
- |
-|
109 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
110 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
111 | |
- |
-|
112 | fixed refs handling, added gc auto, updated tests |
- |
-|
113 | |
- |
-|
114 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
115 | Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) |
- |
-|
116 | Reflog message: merge phedders/rdocs: Merge made by recursive. |
- |
-|
117 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
118 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
119 | |
- |
-|
120 | Merge commit 'phedders/rdocs' |
- |
-|
121 | |
- |
-|
122 | 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. |
- |
-|
123 | |
- |
-|
124 | ### Ancestry References ### |
- |
-|
125 | |
- |
-|
126 | 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. |
- |
-|
127 | Suppose you look at the history of your project: |
- |
-|
128 | |
- |
-|
129 | $ git log --pretty=format:'%h %s' --graph |
- |
-|
130 | * 734713b fixed refs handling, added gc auto, updated tests |
- |
-|
131 | * d921970 Merge commit 'phedders/rdocs' |
- |
-|
132 | |\ |
- |
-|
133 | | * 35cfb2b Some rdoc changes |
- |
-|
134 | * | 1c002dd added some blame and merge stuff |
- |
-|
135 | |/ |
- |
-|
136 | * 1c36188 ignore *.gem |
- |
-|
137 | * 9b29157 add open3_detach to gemspec file list |
- |
-|
138 | |
- |
-|
139 | Then, you can see the previous commit by specifying `HEAD^`, which means "the parent of HEAD": |
- |
-|
140 | |
- |
-|
141 | $ git show HEAD^ |
- |
-|
142 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
143 | Merge: 1c002dd... 35cfb2b... |
- |
-|
144 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
145 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
146 | |
- |
-|
147 | Merge commit 'phedders/rdocs' |
- |
-|
148 | |
- |
-|
149 | 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: |
- |
-|
150 | |
- |
-|
151 | $ git show d921970^ |
- |
-|
152 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
153 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
154 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
155 | |
- |
-|
156 | added some blame and merge stuff |
- |
-|
157 | |
- |
-|
158 | $ git show d921970^2 |
- |
-|
159 | commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 |
- |
-|
160 | Author: Paul Hedderly <paul+git@mjr.org> |
- |
-|
161 | Date: Wed Dec 10 22:22:03 2008 +0000 |
- |
-|
162 | |
- |
-|
163 | Some rdoc changes |
- |
-|
164 | |
- |
-|
165 | 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 |
- |
-|
166 | |
- |
-|
167 | $ git show HEAD~3 |
- |
-|
168 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
169 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
170 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
171 | |
- |
-|
172 | ignore *.gem |
- |
-|
173 | |
- |
-|
174 | This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: |
- |
-|
175 | |
- |
-|
176 | $ git show HEAD^^^ |
- |
-|
177 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
178 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
179 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
180 | |
- |
-|
181 | ignore *.gem |
- |
-|
182 | |
- |
-|
183 | 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. |
- |
-|
184 | |
- |
-|
185 | ### Commit Ranges ### |
- |
-|
186 | |
- |
-|
187 | 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?" |
- |
-|
188 | |
- |
-|
189 | #### Double Dot #### |
- |
-|
190 | |
- |
-|
191 | 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 Figure 6-1. |
- |
-|
192 | |
- |
-|
193 | Insert 18333fig0601.png |
- |
-|
194 | Figure 6-1. Example history for range selection. |
- |
-|
195 | |
- |
-|
196 | 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: |
- |
-|
197 | |
- |
-|
198 | $ git log master..experiment |
- |
-|
199 | D |
- |
-|
200 | C |
- |
-|
201 | |
- |
-|
202 | 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`: |
- |
-|
203 | |
- |
-|
204 | $ git log experiment..master |
- |
-|
205 | F |
- |
-|
206 | E |
- |
-|
207 | |
- |
-|
208 | 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: |
- |
-|
209 | |
- |
-|
210 | $ git log origin/master..HEAD |
- |
-|
211 | |
- |
-|
212 | 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. |
- |
-|
213 | 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. |
- |
-|
214 | |
- |
-|
215 | #### Multiple Points #### |
- |
-|
216 | |
- |
-|
217 | 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: |
- |
-|
218 | |
- |
-|
219 | $ git log refA..refB |
- |
-|
220 | $ git log ^refA refB |
- |
-|
221 | $ git log refB --not refA |
- |
-|
222 | |
- |
-|
223 | 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: |
- |
-|
224 | |
- |
-|
225 | $ git log refA refB ^refC |
- |
-|
226 | $ git log refA refB --not refC |
- |
-|
227 | |
- |
-|
228 | This makes for a very powerful revision query system that should help you figure out what is in your branches. |
- |
-|
229 | |
- |
-|
230 | #### Triple Dot #### |
- |
-|
231 | |
- |
-|
232 | 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 Figure 6-1. |
- |
-|
233 | If you want to see what is in `master` or `experiment` but not any common references, you can run |
- |
-|
234 | |
- |
-|
235 | $ git log master...experiment |
- |
-|
236 | F |
- |
-|
237 | E |
- |
-|
238 | D |
- |
-|
239 | C |
- |
-|
240 | |
- |
-|
241 | 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. |
- |
-|
242 | |
- |
-|
243 | 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: |
- |
-|
244 | |
- |
-|
245 | $ git log --left-right master...experiment |
- |
-|
246 | < F |
- |
-|
247 | < E |
- |
-|
248 | > D |
- |
-|
249 | > C |
- |
-|
250 | |
- |
-|
251 | With these tools, you can much more easily let Git know what commit or commits you want to inspect. |
- |
-|
252 | |
- |
-|
253 | ## Interactive Staging ## |
- |
-|
254 | |
- |
-|
255 | Git comes with a couple of scripts that make some command-line tasks easier. Here, you’ll look at a few interactive commands that can help you easily craft your commits to include only certain combinations and parts of files. These tools are very helpful if you modify a bunch of files and then decide that you want those changes to be in several focused commits rather than one big messy commit. This way, you can make sure your commits are logically separate changesets and can be easily reviewed by the developers working with you. |
- |
-|
256 | If you run `git add` with the `-i` or `--interactive` option, Git goes into an interactive shell mode, displaying something like this: |
- |
-|
257 | |
- |
-|
258 | $ git add -i |
- |
-|
259 | staged unstaged path |
- |
-|
260 | 1: unchanged +0/-1 TODO |
- |
-|
261 | 2: unchanged +1/-1 index.html |
- |
-|
262 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
263 | |
- |
-|
264 | *** Commands *** |
- |
-|
265 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
266 | 5: patch 6: diff 7: quit 8: help |
- |
-|
267 | What now> |
- |
-|
268 | |
- |
-|
269 | 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. |
- |
-|
270 | |
- |
-|
271 | After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. |
- |
-|
272 | |
- |
-|
273 | ### Staging and Unstaging Files ### |
- |
-|
274 | |
- |
-|
275 | If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: |
- |
-|
276 | |
- |
-|
277 | What now> 2 |
- |
-|
278 | staged unstaged path |
- |
-|
279 | 1: unchanged +0/-1 TODO |
- |
-|
280 | 2: unchanged +1/-1 index.html |
- |
-|
281 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
282 | Update>> |
- |
-|
283 | |
- |
-|
284 | To stage the TODO and index.html files, you can type the numbers: |
- |
-|
285 | |
- |
-|
286 | Update>> 1,2 |
- |
-|
287 | staged unstaged path |
- |
-|
288 | * 1: unchanged +0/-1 TODO |
- |
-|
289 | * 2: unchanged +1/-1 index.html |
- |
-|
290 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
291 | Update>> |
- |
-|
292 | |
- |
-|
293 | 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: |
- |
-|
294 | |
- |
-|
295 | Update>> |
- |
-|
296 | updated 2 paths |
- |
-|
297 | |
- |
-|
298 | *** Commands *** |
- |
-|
299 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
300 | 5: patch 6: diff 7: quit 8: help |
- |
-|
301 | What now> 1 |
- |
-|
302 | staged unstaged path |
- |
-|
303 | 1: +0/-1 nothing TODO |
- |
-|
304 | 2: +1/-1 nothing index.html |
- |
-|
305 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
306 | |
- |
-|
307 | 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: |
- |
-|
308 | |
- |
-|
309 | *** Commands *** |
- |
-|
310 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
311 | 5: patch 6: diff 7: quit 8: help |
- |
-|
312 | What now> 3 |
- |
-|
313 | staged unstaged path |
- |
-|
314 | 1: +0/-1 nothing TODO |
- |
-|
315 | 2: +1/-1 nothing index.html |
- |
-|
316 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
317 | Revert>> 1 |
- |
-|
318 | staged unstaged path |
- |
-|
319 | * 1: +0/-1 nothing TODO |
- |
-|
320 | 2: +1/-1 nothing index.html |
- |
-|
321 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
322 | Revert>> [enter] |
- |
-|
323 | reverted one path |
- |
-|
324 | |
- |
-|
325 | Looking at your Git status again, you can see that you’ve unstaged the TODO file: |
- |
-|
326 | |
- |
-|
327 | *** Commands *** |
- |
-|
328 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
329 | 5: patch 6: diff 7: quit 8: help |
- |
-|
330 | What now> 1 |
- |
-|
331 | staged unstaged path |
- |
-|
332 | 1: unchanged +0/-1 TODO |
- |
-|
333 | 2: +1/-1 nothing index.html |
- |
-|
334 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
335 | |
- |
-|
336 | 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: |
- |
-|
337 | |
- |
-|
338 | *** Commands *** |
- |
-|
339 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
340 | 5: patch 6: diff 7: quit 8: help |
- |
-|
341 | What now> 6 |
- |
-|
342 | staged unstaged path |
- |
-|
343 | 1: +1/-1 nothing index.html |
- |
-|
344 | Review diff>> 1 |
- |
-|
345 | diff --git a/index.html b/index.html |
- |
-|
346 | index 4d07108..4335f49 100644 |
- |
-|
347 | --- a/index.html |
- |
-|
348 | +++ b/index.html |
- |
-|
349 | @@ -16,7 +16,7 @@ Date Finder |
- |
-|
350 | |
- |
-|
351 | <p id="out">...</p> |
- |
-|
352 | |
- |
-|
353 | -<div id="footer">contact : support@github.com</div> |
- |
-|
354 | +<div id="footer">contact : email.support@github.com</div> |
- |
-|
355 | |
- |
-|
356 | <script type="text/javascript"> |
- |
-|
357 | |
- |
-|
358 | With these basic commands, you can use the interactive add mode to deal with your staging area a little more easily. |
- |
-|
359 | |
- |
-|
360 | ### Staging Patches ### |
- |
-|
361 | |
- |
-|
362 | It’s also possible for Git to stage certain parts of files and not the rest. For example, if you make two changes to your simplegit.rb file and want to stage one of them and not the other, doing so is very easy in Git. From the interactive prompt, type `5` or `p` (for patch). Git will ask you which files you would like to partially stage; then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one: |
- |
-|
363 | |
- |
-|
364 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
- |
-|
365 | index dd5ecc4..57399e0 100644 |
- |
-|
366 | --- a/lib/simplegit.rb |
- |
-|
367 | +++ b/lib/simplegit.rb |
- |
-|
368 | @@ -22,7 +22,7 @@ class SimpleGit |
- |
-|
369 | end |
- |
-|
370 | |
- |
-|
371 | def log(treeish = 'master') |
- |
-|
372 | - command("git log -n 25 #{treeish}") |
- |
-|
373 | + command("git log -n 30 #{treeish}") |
- |
-|
374 | end |
- |
-|
375 | |
- |
-|
376 | def blame(path) |
- |
-|
377 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? |
- |
-|
378 | |
- |
-|
379 | You have a lot of options at this point. Typing `?` shows a list of what you can do: |
- |
-|
380 | |
- |
-|
381 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? |
- |
-|
382 | y - stage this hunk |
- |
-|
383 | n - do not stage this hunk |
- |
-|
384 | a - stage this and all the remaining hunks in the file |
- |
-|
385 | d - do not stage this hunk nor any of the remaining hunks in the file |
- |
-|
386 | g - select a hunk to go to |
- |
-|
387 | / - search for a hunk matching the given regex |
- |
-|
388 | j - leave this hunk undecided, see next undecided hunk |
- |
-|
389 | J - leave this hunk undecided, see next hunk |
- |
-|
390 | k - leave this hunk undecided, see previous undecided hunk |
- |
-|
391 | K - leave this hunk undecided, see previous hunk |
- |
-|
392 | s - split the current hunk into smaller hunks |
- |
-|
393 | e - manually edit the current hunk |
- |
-|
394 | ? - print help |
- |
-|
395 | |
- |
-|
396 | Generally, you’ll type `y` or `n` if you want to stage each hunk, but staging all of them in certain files or skipping a hunk decision until later can be helpful too. If you stage one part of the file and leave another part unstaged, your status output will look like this: |
- |
-|
397 | |
- |
-|
398 | What now> 1 |
- |
-|
399 | staged unstaged path |
- |
-|
400 | 1: unchanged +0/-1 TODO |
- |
-|
401 | 2: +1/-1 nothing index.html |
- |
-|
402 | 3: +1/-1 +4/-0 lib/simplegit.rb |
- |
-|
403 | |
- |
-|
404 | The status of the simplegit.rb file is interesting. It shows you that a couple of lines are staged and a couple are unstaged. You’ve partially staged this file. At this point, you can exit the interactive adding script and run `git commit` to commit the partially staged files. |
- |
-|
405 | |
- |
-|
406 | Finally, you don’t need to be in interactive add mode to do the partial-file staging — you can start the same script by using `git add -p` or `git add --patch` on the command line. |
- |
-|
407 | |
- |
-|
408 | ## Stashing ## |
- |
-|
409 | |
- |
-|
410 | Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the `git stash` command. |
- |
-|
411 | |
- |
-|
412 | Stashing takes the dirty state of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time. |
- |
-|
413 | |
- |
-|
414 | ### Stashing Your Work ### |
- |
-|
415 | |
- |
-|
416 | To demonstrate, you’ll go into your project and start working on a couple of files and possibly stage one of the changes. If you run `git status`, you can see your dirty state: |
- |
-|
417 | |
- |
-|
418 | $ git status |
- |
-|
419 | # On branch master |
- |
-|
420 | # Changes to be committed: |
- |
-|
421 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
422 | # |
- |
-|
423 | # modified: index.html |
- |
-|
424 | # |
- |
-|
425 | # Changes not staged for commit: |
- |
-|
426 | # (use "git add <file>..." to update what will be committed) |
- |
-|
427 | # |
- |
-|
428 | # modified: lib/simplegit.rb |
- |
-|
429 | # |
- |
-|
430 | |
- |
-|
431 | Now you want to switch branches, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run `git stash`: |
- |
-|
432 | |
- |
-|
433 | $ git stash |
- |
-|
434 | Saved working directory and index state \ |
- |
-|
435 | "WIP on master: 049d078 added the index file" |
- |
-|
436 | HEAD is now at 049d078 added the index file |
- |
-|
437 | (To restore them type "git stash apply") |
- |
-|
438 | |
- |
-|
439 | Your working directory is clean: |
- |
-|
440 | |
- |
-|
441 | $ git status |
- |
-|
442 | # On branch master |
- |
-|
443 | nothing to commit, working directory clean |
- |
-|
444 | |
- |
-|
445 | At this point, you can easily switch branches and do work elsewhere; your changes are stored on your stack. To see which stashes you’ve stored, you can use `git stash list`: |
- |
-|
446 | |
- |
-|
447 | $ git stash list |
- |
-|
448 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
449 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
450 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
451 | |
- |
-|
452 | In this case, two stashes were done previously, so you have access to three different stashed works. You can reapply the one you just stashed by using the command shown in the help output of the original stash command: `git stash apply`. If you want to apply one of the older stashes, you can specify it by naming it, like this: `git stash apply stash@{2}`. If you don’t specify a stash, Git assumes the most recent stash and tries to apply it: |
- |
-|
453 | |
- |
-|
454 | $ git stash apply |
- |
-|
455 | # On branch master |
- |
-|
456 | # Changes not staged for commit: |
- |
-|
457 | # (use "git add <file>..." to update what will be committed) |
- |
-|
458 | # |
- |
-|
459 | # modified: index.html |
- |
-|
460 | # modified: lib/simplegit.rb |
- |
-|
461 | # |
- |
-|
462 | |
- |
-|
463 | You can see that Git re-modifies the files you uncommitted when you saved the stash. In this case, you had a clean working directory when you tried to apply the stash, and you tried to apply it on the same branch you saved it from; but having a clean working directory and applying it on the same branch aren’t necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash — Git gives you merge conflicts if anything no longer applies cleanly. |
- |
-|
464 | |
- |
-|
465 | The changes to your files were reapplied, but the file you staged before wasn’t restaged. To do that, you must run the `git stash apply` command with a `--index` option to tell the command to try to reapply the staged changes. If you had run that instead, you’d have gotten back to your original position: |
- |
-|
466 | |
- |
-|
467 | $ git stash apply --index |
- |
-|
468 | # On branch master |
- |
-|
469 | # Changes to be committed: |
- |
-|
470 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
471 | # |
- |
-|
472 | # modified: index.html |
- |
-|
473 | # |
- |
-|
474 | # Changes not staged for commit: |
- |
-|
475 | # (use "git add <file>..." to update what will be committed) |
- |
-|
476 | # |
- |
-|
477 | # modified: lib/simplegit.rb |
- |
-|
478 | # |
- |
-|
479 | |
- |
-|
480 | The apply option only tries to apply the stashed work — you continue to have it on your stack. To remove it, you can run `git stash drop` with the name of the stash to remove: |
- |
-|
481 | |
- |
-|
482 | $ git stash list |
- |
-|
483 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
484 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
485 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
486 | $ git stash drop stash@{0} |
- |
-|
487 | Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) |
- |
-|
488 | |
- |
-|
489 | You can also run `git stash pop` to apply the stash and then immediately drop it from your stack. |
- |
-|
490 | |
- |
-|
491 | ### Un-applying a Stash ### |
- |
-|
492 | |
- |
-|
493 | In some use case scenarios you might want to apply stashed changes, do some work, but then un-apply those changes that originally came from the stash. Git does not provide such a `stash unapply` command, but it is possible to achieve the effect by simply retrieving the patch associated with a stash and applying it in reverse: |
- |
-|
494 | |
- |
-|
495 | $ git stash show -p stash@{0} | git apply -R |
- |
-|
496 | |
- |
-|
497 | Again, if you don’t specify a stash, Git assumes the most recent stash: |
- |
-|
498 | |
- |
-|
499 | $ git stash show -p | git apply -R |
- |
-|
500 | |
- |
-|
501 | You may want to create an alias and effectively add a `stash-unapply` command to your Git. For example: |
- |
-|
502 | |
- |
-|
503 | $ git config --global alias.stash-unapply '!git stash show -p | git apply -R' |
- |
-|
504 | $ git stash apply |
- |
-|
505 | $ #... work work work |
- |
-|
506 | $ git stash-unapply |
- |
-|
507 | |
- |
-|
508 | ### Creating a Branch from a Stash ### |
- |
-|
509 | |
- |
-|
510 | If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you’ve since modified, you’ll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run `git stash branch`, which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully: |
- |
-|
511 | |
- |
-|
512 | $ git stash branch testchanges |
- |
-|
513 | Switched to a new branch "testchanges" |
- |
-|
514 | # On branch testchanges |
- |
-|
515 | # Changes to be committed: |
- |
-|
516 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
517 | # |
- |
-|
518 | # modified: index.html |
- |
-|
519 | # |
- |
-|
520 | # Changes not staged for commit: |
- |
-|
521 | # (use "git add <file>..." to update what will be committed) |
- |
-|
522 | # |
- |
-|
523 | # modified: lib/simplegit.rb |
- |
-|
524 | # |
- |
-|
525 | Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) |
- |
-|
526 | |
- |
-|
527 | This is a nice shortcut to recover stashed work easily and work on it in a new branch. |
- |
-|
528 | |
- |
-|
529 | ## Rewriting History ## |
-2 | === Rewriting History |
-
530 | |
-3 | |
-
.531 | Many times, when working with Git, you may want to revise your commit history for some reason. One of the great things about Git is that it allows you to make decisions at the last possible moment. You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with the stash command, and you can rewrite commits that already happened so they look like they happened in a different way. This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others. |
-4 | Many times, when working with Git, you may want to revise your commit history for some reason. One of the great things about Git is that it allows you to make decisions at the last possible moment. You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with the stash command, and you can rewrite commits that already happened so they look like they happened in a different way. This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely – all before you share your work with others. |
-
532 | |
-5 | |
-
533 | In this section, you’ll cover how to accomplish these very useful tasks so that you can make your commit history look the way you want before you share it with others. |
-6 | In this section, you’ll cover how to accomplish these very useful tasks so that you can make your commit history look the way you want before you share it with others. |
-
534 | |
-7 | |
-
. | |
-8 | [[_git_amend]] |
-
535 | ### Changing the Last Commit ### |
-9 | ==== Changing the Last Commit |
-
536 | |
-10 | |
-
.537 | Changing your last commit is probably the most common rewriting of history that you’ll do. You’ll often want to do two basic things to your last commit: change the commit message, or change the snapshot you just recorded by adding, changing and removing files. |
-11 | Changing your last commit is probably the most common rewriting of history that you’ll do. You’ll often want to do two basic things to your last commit: change the commit message, or change the snapshot you just recorded by adding, changing and removing files. |
-
538 | |
-12 | |
-
539 | If you only want to modify your last commit message, it’s very simple: |
-13 | If you only want to modify your last commit message, it’s very simple: |
-
540 | |
-14 | |
-
. | |
-15 | [source,console] |
-
|
-16 | ---- |
-|
541 | $ git commit --amend |
-17 | $ git commit --amend |
-
|
-18 | ---- |
-|
542 | |
-19 | |
-
543 | That drops you into your text editor, which has your last commit message in it, ready for you to modify the message. When you save and close the editor, the editor writes a new commit containing that message and makes it your new last commit. |
-20 | That drops you into your text editor, which has your last commit message in it, ready for you to modify the message. When you save and close the editor, the editor writes a new commit containing that message and makes it your new last commit. |
-
544 | |
-21 | |
-
545 | If you’ve committed and then you want to change the snapshot you committed by adding or changing files, possibly because you forgot to add a newly created file when you originally committed, the process works basically the same way. You stage the changes you want by editing a file and running `git add` on it or `git rm` to a tracked file, and the subsequent `git commit --amend` takes your current staging area and makes it the snapshot for the new commit. |
-22 | If you’ve committed and then you want to change the snapshot you committed by adding or changing files, possibly because you forgot to add a newly created file when you originally committed, the process works basically the same way. You stage the changes you want by editing a file and running `git add` on it or `git rm` to a tracked file, and the subsequent `git commit --amend` takes your current staging area and makes it the snapshot for the new commit. |
-
546 | |
-23 | |
-
.547 | You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it. |
-24 | You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase – don’t amend your last commit if you’ve already pushed it. |
-
548 | |
-25 | |
-
. | |
-26 | [[_changing_multiple]] |
-
549 | ### Changing Multiple Commit Messages ### |
-27 | ==== Changing Multiple Commit Messages |
-
550 | |
-28 | |
-
551 | To modify a commit that is farther back in your history, you must move to more complex tools. Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD they were originally based on instead of moving them to another one. With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the `-i` option to `git rebase`. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto. |
-29 | To modify a commit that is farther back in your history, you must move to more complex tools. Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD they were originally based on instead of moving them to another one. With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the `-i` option to `git rebase`. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto. |
-
552 | |
-30 | |
-
553 | For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to `git rebase -i` the parent of the last commit you want to edit, which is `HEAD~2^` or `HEAD~3`. It may be easier to remember the `~3` because you’re trying to edit the last three commits; but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit: |
-31 | For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to `git rebase -i` the parent of the last commit you want to edit, which is `HEAD~2^` or `HEAD~3`. It may be easier to remember the `~3` because you’re trying to edit the last three commits; but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit: |
-
554 | |
-32 | |
-
. | |
-33 | [source,console] |
-
|
-34 | ---- |
-|
555 | $ git rebase -i HEAD~3 |
-35 | $ git rebase -i HEAD~3 |
-
|
-36 | ---- |
-|
556 | |
-37 | |
-
.557 | Remember again that this is a rebasing command — every commit included in the range `HEAD~3..HEAD` will be rewritten, whether you change the message or not. Don’t include any commit you’ve already pushed to a central server — doing so will confuse other developers by providing an alternate version of the same change. |
-38 | Remember again that this is a rebasing command – every commit included in the range `HEAD~3..HEAD` will be rewritten, whether you change the message or not. Don’t include any commit you’ve already pushed to a central server – doing so will confuse other developers by providing an alternate version of the same change. |
-
558 | |
-39 | |
-
559 | Running this command gives you a list of commits in your text editor that looks something like this: |
-40 | Running this command gives you a list of commits in your text editor that looks something like this: |
-
560 | |
-41 | |
-
. | |
-42 | [source,console] |
-
|
-43 | ---- |
-|
561 | pick f7f3f6d changed my name a bit |
-44 | pick f7f3f6d changed my name a bit |
-
562 | pick 310154e updated README formatting and added blame |
-45 | pick 310154e updated README formatting and added blame |
-
563 | pick a5f4a0d added cat-file |
-46 | pick a5f4a0d added cat-file |
-
564 | |
-47 | |
-
565 | # Rebase 710f0f8..a5f4a0d onto 710f0f8 |
-48 | # Rebase 710f0f8..a5f4a0d onto 710f0f8 |
-
566 | # |
-49 | # |
-
567 | # Commands: |
-50 | # Commands: |
-
568 | # p, pick = use commit |
-51 | # p, pick = use commit |
-
569 | # r, reword = use commit, but edit the commit message |
-52 | # r, reword = use commit, but edit the commit message |
-
570 | # e, edit = use commit, but stop for amending |
-53 | # e, edit = use commit, but stop for amending |
-
571 | # s, squash = use commit, but meld into previous commit |
-54 | # s, squash = use commit, but meld into previous commit |
-
572 | # f, fixup = like "squash", but discard this commit's log message |
-55 | # f, fixup = like "squash", but discard this commit's log message |
-
573 | # x, exec = run command (the rest of the line) using shell |
-56 | # x, exec = run command (the rest of the line) using shell |
-
574 | # |
-57 | # |
-
575 | # These lines can be re-ordered; they are executed from top to bottom. |
-58 | # These lines can be re-ordered; they are executed from top to bottom. |
-
576 | # |
-59 | # |
-
577 | # If you remove a line here THAT COMMIT WILL BE LOST. |
-60 | # If you remove a line here THAT COMMIT WILL BE LOST. |
-
578 | # |
-61 | # |
-
579 | # However, if you remove everything, the rebase will be aborted. |
-62 | # However, if you remove everything, the rebase will be aborted. |
-
580 | # |
-63 | # |
-
581 | # Note that empty commits are commented out |
-64 | # Note that empty commits are commented out |
-
|
-65 | ---- |
-|
582 | |
-66 | |
-
583 | It’s important to note that these commits are listed in the opposite order than you normally see them using the `log` command. If you run a `log`, you see something like this: |
-67 | It’s important to note that these commits are listed in the opposite order than you normally see them using the `log` command. If you run a `log`, you see something like this: |
-
584 | |
-68 | |
-
. | |
-69 | [source,console] |
-
|
-70 | ---- |
-|
585 | $ git log --pretty=format:"%h %s" HEAD~3..HEAD |
-71 | $ git log --pretty=format:"%h %s" HEAD~3..HEAD |
-
586 | a5f4a0d added cat-file |
-72 | a5f4a0d added cat-file |
-
587 | 310154e updated README formatting and added blame |
-73 | 310154e updated README formatting and added blame |
-
588 | f7f3f6d changed my name a bit |
-74 | f7f3f6d changed my name a bit |
-
|
-75 | ---- |
-|
589 | |
-76 | |
-
590 | Notice the reverse order. The interactive rebase gives you a script that it’s going to run. It will start at the commit you specify on the command line (`HEAD~3`) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that’s the first one it will replay. |
-77 | Notice the reverse order. The interactive rebase gives you a script that it’s going to run. It will start at the commit you specify on the command line (`HEAD~3`) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that’s the first one it will replay. |
-
591 | |
-78 | |
-
.592 | You need to edit the script so that it stops at the commit you want to edit. To do so, change the word pick to the word edit for each of the commits you want the script to stop after. For example, to modify only the third commit message, you change the file to look like this: |
-79 | You need to edit the script so that it stops at the commit you want to edit. To do so, change the word `pick' to the word `edit' for each of the commits you want the script to stop after. For example, to modify only the third commit message, you change the file to look like this: |
-
593 | |
-80 | |
-
. | |
-81 | [source,console] |
-
|
-82 | ---- |
-|
594 | edit f7f3f6d changed my name a bit |
-83 | edit f7f3f6d changed my name a bit |
-
595 | pick 310154e updated README formatting and added blame |
-84 | pick 310154e updated README formatting and added blame |
-
596 | pick a5f4a0d added cat-file |
-85 | pick a5f4a0d added cat-file |
-
|
-86 | ---- |
-|
597 | |
-87 | |
-
598 | When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message: |
-88 | When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message: |
-
599 | |
-89 | |
-
.600 | <!-- This is actually weird, as the SHA-1 of 7482e0d is not present in the list, |
-90 | [source,console] |
-
601 | nor is the commit message. Please review |
- |
-|
602 | --> |
- |
-|
603 | |
- |
-|
|
-91 | ---- |
-|
604 | $ git rebase -i HEAD~3 |
-92 | $ git rebase -i HEAD~3 |
-
605 | Stopped at 7482e0d... updated the gemspec to hopefully work better |
-93 | Stopped at f7f3f6d... changed my name a bit |
-
606 | You can amend the commit now, with |
-94 | You can amend the commit now, with |
-
607 | |
-95 | |
-
.608 | git commit --amend |
-96 | git commit --amend |
-
609 | |
-97 | |
-
.610 | Once you’re satisfied with your changes, run |
-98 | Once you’re satisfied with your changes, run |
-
611 | |
-99 | |
-
.612 | git rebase --continue |
-100 | git rebase --continue |
-
|
-101 | ---- |
-|
613 | |
-102 | |
-
614 | These instructions tell you exactly what to do. Type |
-103 | These instructions tell you exactly what to do. Type |
-
615 | |
-104 | |
-
. | |
-105 | [source,console] |
-
|
-106 | ---- |
-|
616 | $ git commit --amend |
-107 | $ git commit --amend |
-
|
-108 | ---- |
-|
617 | |
-109 | |
-
618 | Change the commit message, and exit the editor. Then, run |
-110 | Change the commit message, and exit the editor. Then, run |
-
619 | |
-111 | |
-
. | |
-112 | [source,console] |
-
|
-113 | ---- |
-|
620 | $ git rebase --continue |
-114 | $ git rebase --continue |
-
|
-115 | ---- |
-|
621 | |
-116 | |
-
622 | This command will apply the other two commits automatically, and then you’re done. If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit. Each time, Git will stop, let you amend the commit, and continue when you’re finished. |
-117 | This command will apply the other two commits automatically, and then you’re done. If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit. Each time, Git will stop, let you amend the commit, and continue when you’re finished. |
-
623 | |
-118 | |
-
.624 | ### Reordering Commits ### |
-119 | ==== Reordering Commits |
-
625 | |
-120 | |
-
.626 | You can also use interactive rebases to reorder or remove commits entirely. If you want to remove the "added cat-file" commit and change the order in which the other two commits are introduced, you can change the rebase script from this |
-121 | You can also use interactive rebases to reorder or remove commits entirely. If you want to remove the ``added cat-file'' commit and change the order in which the other two commits are introduced, you can change the rebase script from this |
-
627 | |
-122 | |
-
. | |
-123 | [source,console] |
-
|
-124 | ---- |
-|
628 | pick f7f3f6d changed my name a bit |
-125 | pick f7f3f6d changed my name a bit |
-
629 | pick 310154e updated README formatting and added blame |
-126 | pick 310154e updated README formatting and added blame |
-
630 | pick a5f4a0d added cat-file |
-127 | pick a5f4a0d added cat-file |
-
|
-128 | ---- |
-|
631 | |
-129 | |
-
632 | to this: |
-130 | to this: |
-
633 | |
-131 | |
-
. | |
-132 | |
-
|
-133 | [source,console] |
-|
|
-134 | ---- |
-|
634 | pick 310154e updated README formatting and added blame |
-135 | pick 310154e updated README formatting and added blame |
-
635 | pick f7f3f6d changed my name a bit |
-136 | pick f7f3f6d changed my name a bit |
-
|
-137 | ---- |
-|
636 | |
-138 | |
-
.637 | When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies `310154e` and then `f7f3f6d`, and then stops. You effectively change the order of those commits and remove the "added cat-file" commit completely. |
-139 | When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies `310154e` and then `f7f3f6d`, and then stops. You effectively change the order of those commits and remove the ``added cat-file'' commit completely. |
-
638 | |
-140 | |
-
. | |
-141 | [[_squashing]] |
-
639 | ### Squashing Commits ### |
-142 | ==== Squashing Commits |
-
640 | |
-143 | |
-
641 | It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool. The script puts helpful instructions in the rebase message: |
-144 | It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool. The script puts helpful instructions in the rebase message: |
-
642 | |
-145 | |
-
. | |
-146 | [source,console] |
-
|
-147 | ---- |
-|
643 | # |
-148 | # |
-
644 | # Commands: |
-149 | # Commands: |
-
645 | # p, pick = use commit |
-150 | # p, pick = use commit |
-
646 | # r, reword = use commit, but edit the commit message |
-151 | # r, reword = use commit, but edit the commit message |
-
647 | # e, edit = use commit, but stop for amending |
-152 | # e, edit = use commit, but stop for amending |
-
648 | # s, squash = use commit, but meld into previous commit |
-153 | # s, squash = use commit, but meld into previous commit |
-
649 | # f, fixup = like "squash", but discard this commit's log message |
-154 | # f, fixup = like "squash", but discard this commit's log message |
-
650 | # x, exec = run command (the rest of the line) using shell |
-155 | # x, exec = run command (the rest of the line) using shell |
-
651 | # |
-156 | # |
-
652 | # These lines can be re-ordered; they are executed from top to bottom. |
-157 | # These lines can be re-ordered; they are executed from top to bottom. |
-
653 | # |
-158 | # |
-
654 | # If you remove a line here THAT COMMIT WILL BE LOST. |
-159 | # If you remove a line here THAT COMMIT WILL BE LOST. |
-
655 | # |
-160 | # |
-
656 | # However, if you remove everything, the rebase will be aborted. |
-161 | # However, if you remove everything, the rebase will be aborted. |
-
657 | # |
-162 | # |
-
658 | # Note that empty commits are commented out |
-163 | # Note that empty commits are commented out |
-
|
-164 | ---- |
-|
659 | |
-165 | |
-
.660 | If, instead of "pick" or "edit", you specify "squash", Git applies both that change and the change directly before it and makes you merge the commit messages together. So, if you want to make a single commit from these three commits, you make the script look like this: |
-166 | If, instead of ``pick'' or ``edit'', you specify ``squash'', Git applies both that change and the change directly before it and makes you merge the commit messages together. So, if you want to make a single commit from these three commits, you make the script look like this: |
-
661 | |
-167 | |
-
. | |
-168 | [source,console] |
-
|
-169 | ---- |
-|
662 | pick f7f3f6d changed my name a bit |
-170 | pick f7f3f6d changed my name a bit |
-
663 | squash 310154e updated README formatting and added blame |
-171 | squash 310154e updated README formatting and added blame |
-
664 | squash a5f4a0d added cat-file |
-172 | squash a5f4a0d added cat-file |
-
|
-173 | ---- |
-|
665 | |
-174 | |
-
666 | When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages: |
-175 | When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages: |
-
667 | |
-176 | |
-
. | |
-177 | [source,console] |
-
|
-178 | ---- |
-|
668 | # This is a combination of 3 commits. |
-179 | # This is a combination of 3 commits. |
-
669 | # The first commit's message is: |
-180 | # The first commit's message is: |
-
670 | changed my name a bit |
-181 | changed my name a bit |
-
671 | |
-182 | |
-
.672 | # This is the 2nd commit message: |
-183 | # This is the 2nd commit message: |
-
673 | |
-184 | |
-
.674 | updated README formatting and added blame |
-185 | updated README formatting and added blame |
-
675 | |
-186 | |
-
.676 | # This is the 3rd commit message: |
-187 | # This is the 3rd commit message: |
-
677 | |
-188 | |
-
.678 | added cat-file |
-189 | added cat-file |
-
|
-190 | ---- |
-|
679 | |
-191 | |
-
680 | When you save that, you have a single commit that introduces the changes of all three previous commits. |
-192 | When you save that, you have a single commit that introduces the changes of all three previous commits. |
-
681 | |
-193 | |
-
.682 | ### Splitting a Commit ### |
-194 | ==== Splitting a Commit |
-
683 | |
-195 | |
-
.684 | Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with. For example, suppose you want to split the middle commit of your three commits. Instead of "updated README formatting and added blame", you want to split it into two commits: "updated README formatting" for the first, and "added blame" for the second. You can do that in the `rebase -i` script by changing the instruction on the commit you want to split to "edit": |
-196 | Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with. For example, suppose you want to split the middle commit of your three commits. Instead of ``updated README formatting and added blame'', you want to split it into two commits: ``updated README formatting'' for the first, and ``added blame'' for the second. You can do that in the `rebase -i` script by changing the instruction on the commit you want to split to ``edit'': |
-
685 | |
-197 | |
-
. | |
-198 | [source,console] |
-
|
-199 | ---- |
-|
686 | pick f7f3f6d changed my name a bit |
-200 | pick f7f3f6d changed my name a bit |
-
687 | edit 310154e updated README formatting and added blame |
-201 | edit 310154e updated README formatting and added blame |
-
688 | pick a5f4a0d added cat-file |
-202 | pick a5f4a0d added cat-file |
-
|
-203 | ---- |
-|
689 | |
-204 | |
-
.690 | When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (`f7f3f6d`), applies the second (`310154e`), and drops you to the console. There, you can do a mixed reset of that commit with `git reset HEAD^`, which effectively undoes that commit and leaves the modified files unstaged. Now you can take the changes that have been reset, and create multiple commits out of them. Simply stage and commit files until you have several commits, and run `git rebase --continue` when you’re done: |
-205 | Then, when the script drops you to the command line, you reset that commit, take the changes that have been reset, and create multiple commits out of them. When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (`f7f3f6d`), applies the second (`310154e`), and drops you to the console. There, you can do a mixed reset of that commit with `git reset HEAD^`, which effectively undoes that commit and leaves the modified files unstaged. Now you can stage and commit files until you have several commits, and run `git rebase --continue` when you’re done: |
-
691 | |
-206 | |
-
. | |
-207 | [source,console] |
-
|
-208 | ---- |
-|
692 | $ git reset HEAD^ |
-209 | $ git reset HEAD^ |
-
693 | $ git add README |
-210 | $ git add README |
-
694 | $ git commit -m 'updated README formatting' |
-211 | $ git commit -m 'updated README formatting' |
-
695 | $ git add lib/simplegit.rb |
-212 | $ git add lib/simplegit.rb |
-
696 | $ git commit -m 'added blame' |
-213 | $ git commit -m 'added blame' |
-
697 | $ git rebase --continue |
-214 | $ git rebase --continue |
-
|
-215 | ---- |
-|
698 | |
-216 | |
-
699 | Git applies the last commit (`a5f4a0d`) in the script, and your history looks like this: |
-217 | Git applies the last commit (`a5f4a0d`) in the script, and your history looks like this: |
-
700 | |
-218 | |
-
. | |
-219 | [source,console] |
-
|
-220 | ---- |
-|
701 | $ git log -4 --pretty=format:"%h %s" |
-221 | $ git log -4 --pretty=format:"%h %s" |
-
702 | 1c002dd added cat-file |
-222 | 1c002dd added cat-file |
-
703 | 9b29157 added blame |
-223 | 9b29157 added blame |
-
704 | 35cfb2b updated README formatting |
-224 | 35cfb2b updated README formatting |
-
705 | f3cc40e changed my name a bit |
-225 | f3cc40e changed my name a bit |
-
|
-226 | ---- |
-|
706 | |
-227 | |
-
707 | Once again, this changes the SHAs of all the commits in your list, so make sure no commit shows up in that list that you’ve already pushed to a shared repository. |
-228 | Once again, this changes the SHAs of all the commits in your list, so make sure no commit shows up in that list that you’ve already pushed to a shared repository. |
-
708 | |
-229 | |
-
.709 | ### The Nuclear Option: filter-branch ### |
-230 | ==== The Nuclear Option: filter-branch |
-
710 | |
-231 | |
-
.711 | There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way — for instance, changing your e-mail address globally or removing a file from every commit. The command is `filter-branch`, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite. However, it can be very useful. You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of. |
-232 | There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way – for instance, changing your e-mail address globally or removing a file from every commit. The command is `filter-branch`, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite. However, it can be very useful. You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of. |
-
712 | |
-233 | |
-
. | |
-234 | [[_removing_file_every_commit]] |
-
713 | #### Removing a File from Every Commit #### |
-235 | ===== Removing a File from Every Commit |
-
714 | |
-236 | |
-
715 | This occurs fairly commonly. Someone accidentally commits a huge binary file with a thoughtless `git add .`, and you want to remove it everywhere. Perhaps you accidentally committed a file that contained a password, and you want to make your project open source. `filter-branch` is the tool you probably want to use to scrub your entire history. To remove a file named passwords.txt from your entire history, you can use the `--tree-filter` option to `filter-branch`: |
-237 | This occurs fairly commonly. Someone accidentally commits a huge binary file with a thoughtless `git add .`, and you want to remove it everywhere. Perhaps you accidentally committed a file that contained a password, and you want to make your project open source. `filter-branch` is the tool you probably want to use to scrub your entire history. To remove a file named passwords.txt from your entire history, you can use the `--tree-filter` option to `filter-branch`: |
-
716 | |
-238 | |
-
. | |
-239 | [source,console] |
-
|
-240 | ---- |
-|
717 | $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD |
-241 | $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD |
-
718 | Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) |
-242 | Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) |
-
719 | Ref 'refs/heads/master' was rewritten |
-243 | Ref 'refs/heads/master' was rewritten |
-
|
-244 | ---- |
-|
720 | |
-245 | |
-
.721 | The `--tree-filter` option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt from every snapshot, whether it exists or not. If you want to remove all accidentally committed editor backup files, you can run something like `git filter-branch --tree-filter "rm -f *~" HEAD`. |
-246 | The `--tree-filter` option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt from every snapshot, whether it exists or not. If you want to remove all accidentally committed editor backup files, you can run something like `git filter-branch --tree-filter 'rm -f *~' HEAD`. |
-
722 | |
-247 | |
-
723 | You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end. It’s generally a good idea to do this in a testing branch and then hard-reset your master branch after you’ve determined the outcome is what you really want. To run `filter-branch` on all your branches, you can pass `--all` to the command. |
-248 | You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end. It’s generally a good idea to do this in a testing branch and then hard-reset your master branch after you’ve determined the outcome is what you really want. To run `filter-branch` on all your branches, you can pass `--all` to the command. |
-
724 | |
-249 | |
-
.725 | #### Making a Subdirectory the New Root #### |
-250 | ===== Making a Subdirectory the New Root |
-
726 | |
-251 | |
-
727 | Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk, tags, and so on). If you want to make the `trunk` subdirectory be the new project root for every commit, `filter-branch` can help you do that, too: |
-252 | Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk, tags, and so on). If you want to make the `trunk` subdirectory be the new project root for every commit, `filter-branch` can help you do that, too: |
-
728 | |
-253 | |
-
. | |
-254 | [source,console] |
-
|
-255 | ---- |
-|
729 | $ git filter-branch --subdirectory-filter trunk HEAD |
-256 | $ git filter-branch --subdirectory-filter trunk HEAD |
-
730 | Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) |
-257 | Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) |
-
731 | Ref 'refs/heads/master' was rewritten |
-258 | Ref 'refs/heads/master' was rewritten |
-
|
-259 | ---- |
-|
732 | |
-260 | |
-
733 | Now your new project root is what was in the `trunk` subdirectory each time. Git will also automatically remove commits that did not affect the subdirectory. |
-261 | Now your new project root is what was in the `trunk` subdirectory each time. Git will also automatically remove commits that did not affect the subdirectory. |
-
734 | |
-262 | |
-
.735 | #### Changing E-Mail Addresses Globally #### |
-263 | ===== Changing E-Mail Addresses Globally |
-
736 | |
-264 | |
-
737 | Another common case is that you forgot to run `git config` to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with `filter-branch` as well. You need to be careful to change only the e-mail addresses that are yours, so you use `--commit-filter`: |
-265 | Another common case is that you forgot to run `git config` to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with `filter-branch` as well. You need to be careful to change only the e-mail addresses that are yours, so you use `--commit-filter`: |
-
738 | |
-266 | |
-
. | |
-267 | [source,console] |
-
|
-268 | ---- |
-|
739 | $ git filter-branch --commit-filter ' |
-269 | $ git filter-branch --commit-filter ' |
-
740 | if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; |
-270 | if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; |
-
741 | then |
-271 | then |
-
742 | GIT_AUTHOR_NAME="Scott Chacon"; |
-272 | GIT_AUTHOR_NAME="Scott Chacon"; |
-
743 | GIT_AUTHOR_EMAIL="schacon@example.com"; |
-273 | GIT_AUTHOR_EMAIL="schacon@example.com"; |
-
744 | git commit-tree "$@"; |
-274 | git commit-tree "$@"; |
-
745 | else |
-275 | else |
-
746 | git commit-tree "$@"; |
-276 | git commit-tree "$@"; |
-
747 | fi' HEAD |
-277 | fi' HEAD |
-
|
-278 | ---- |
-|
748 | |
-279 | |
-
749 | This goes through and rewrites every commit to have your new address. Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address. |
-280 | This goes through and rewrites every commit to have your new address. Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address. |
-
.750 | |
- |
-|
751 | ### The Very Fast Nuclear Option: Big Friendly Giant Repo Cleaner (BFG) ### |
- |
-|
752 | |
- |
-|
753 | [Roberto Tyley](https://github.com/rtyley) has written a similar tool to `filter-branch` called the BFG. BFG cannot do as much as `filter-branch`, but it is _very_ fast and on a large repository this can make a big difference. If the change you want to make is in the scope of BFG capability, and you have performance issues, then you should consider using it. |
- |
-|
754 | |
- |
-|
755 | See the [BFG](http://rtyley.github.io/bfg-repo-cleaner/) website for details. |
- |
-|
756 | |
- |
-|
757 | ## Debugging with Git ## |
- |
-|
758 | |
- |
-|
759 | Git also provides a couple of tools to help you debug issues in your projects. Because Git is designed to work with nearly any type of project, these tools are pretty generic, but they can often help you hunt for a bug or culprit when things go wrong. |
- |
-|
760 | |
- |
-|
761 | ### File Annotation ### |
- |
-|
762 | |
- |
-|
763 | If you track down a bug in your code and want to know when it was introduced and why, file annotation is often your best tool. It shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with `git blame` to see when each line of the method was last edited and by whom. This example uses the `-L` option to limit the output to lines 12 through 22: |
- |
-|
764 | |
- |
-|
765 | $ git blame -L 12,22 simplegit.rb |
- |
-|
766 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master') |
- |
-|
767 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}") |
- |
-|
768 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end |
- |
-|
769 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15) |
- |
-|
770 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master') |
- |
-|
771 | 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}") |
- |
-|
772 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end |
- |
-|
773 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19) |
- |
-|
774 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path) |
- |
-|
775 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}") |
- |
-|
776 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end |
- |
-|
777 | |
- |
-|
778 | Notice that the first field is the partial SHA-1 of the commit that last modified that line. The next two fields are values extracted from that commit—the author name and the authored date of that commit — so you can easily see who modified that line and when. After that come the line number and the content of the file. Also note the `^4832fe2` commit lines, which designate that those lines were in this file’s original commit. That commit is when this file was first added to this project, and those lines have been unchanged since. This is a tad confusing, because now you’ve seen at least three different ways that Git uses the `^` to modify a commit SHA, but that is what it means here. |
- |
-|
779 | |
- |
-|
780 | Another cool thing about Git is that it doesn’t track file renames explicitly. It records the snapshots and then tries to figure out what was renamed implicitly, after the fact. One of the interesting features of this is that you can ask it to figure out all sorts of code movement as well. If you pass `-C` to `git blame`, Git analyzes the file you’re annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere. Recently, I was refactoring a file named `GITServerHandler.m` into multiple files, one of which was `GITPackUpload.m`. By blaming `GITPackUpload.m` with the `-C` option, I could see where sections of the code originally came from: |
- |
-|
781 | |
- |
-|
782 | $ git blame -C -L 141,153 GITPackUpload.m |
- |
-|
783 | f344f58d GITServerHandler.m (Scott 2009-01-04 141) |
- |
-|
784 | f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC |
- |
-|
785 | f344f58d GITServerHandler.m (Scott 2009-01-04 143) { |
- |
-|
786 | 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI |
- |
-|
787 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 145) |
- |
-|
788 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha; |
- |
-|
789 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g |
- |
-|
790 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 148) |
- |
-|
791 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI |
- |
-|
792 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 150) |
- |
-|
793 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) { |
- |
-|
794 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb |
- |
-|
795 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 153) |
- |
-|
796 | |
- |
-|
797 | This is really useful. Normally, you get as the original commit the commit where you copied the code over, because that is the first time you touched those lines in this file. Git tells you the original commit where you wrote those lines, even if it was in another file. |
- |
-|
798 | |
- |
-|
799 | ### Binary Search ### |
- |
-|
800 | |
- |
-|
801 | Annotating a file helps if you know where the issue is to begin with. If you don’t know what is breaking, and there have been dozens or hundreds of commits since the last state where you know the code worked, you’ll likely turn to `git bisect` for help. The `bisect` command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. |
- |
-|
802 | |
- |
-|
803 | Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that. You go back to your code, and it turns out you can reproduce the issue, but you can’t figure out what is going wrong. You can bisect the code to find out. First you run `git bisect start` to get things going, and then you use `git bisect bad` to tell the system that the current commit you’re on is broken. Then, you must tell bisect when the last known good state was, using `git bisect good [good_commit]`: |
- |
-|
804 | |
- |
-|
805 | $ git bisect start |
- |
-|
806 | $ git bisect bad |
- |
-|
807 | $ git bisect good v1.0 |
- |
-|
808 | Bisecting: 6 revisions left to test after this |
- |
-|
809 | [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo |
- |
-|
810 | |
- |
-|
811 | Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. If it does, then it was introduced sometime before this middle commit; if it doesn’t, then the problem was introduced sometime after the middle commit. It turns out there is no issue here, and you tell Git that by typing `git bisect good` and continue your journey: |
- |
-|
812 | |
- |
-|
813 | $ git bisect good |
- |
-|
814 | Bisecting: 3 revisions left to test after this |
- |
-|
815 | [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing |
- |
-|
816 | |
- |
-|
817 | Now you’re on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with `git bisect bad`: |
- |
-|
818 | |
- |
-|
819 | $ git bisect bad |
- |
-|
820 | Bisecting: 1 revisions left to test after this |
- |
-|
821 | [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table |
- |
-|
822 | |
- |
-|
823 | This commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and show some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug: |
- |
-|
824 | |
- |
-|
825 | $ git bisect good |
- |
-|
826 | b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit |
- |
-|
827 | commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 |
- |
-|
828 | Author: PJ Hyett <pjhyett@example.com> |
- |
-|
829 | Date: Tue Jan 27 14:48:32 2009 -0800 |
- |
-|
830 | |
- |
-|
831 | secure this thing |
- |
-|
832 | |
- |
-|
833 | :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730 |
- |
-|
834 | f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config |
- |
-|
835 | |
- |
-|
836 | When you’re finished, you should run `git bisect reset` to reset your HEAD to where you were before you started, or you’ll end up in a weird state: |
- |
-|
837 | |
- |
-|
838 | $ git bisect reset |
- |
-|
839 | |
- |
-|
840 | This is a powerful tool that can help you check hundreds of commits for an introduced bug in minutes. In fact, if you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate `git bisect`. First, you again tell it the scope of the bisect by providing the known bad and good commits. You can do this by listing them with the `bisect start` command if you want, listing the known bad commit first and the known good commit second: |
- |
-|
841 | |
- |
-|
842 | $ git bisect start HEAD v1.0 |
- |
-|
843 | $ git bisect run test-error.sh |
- |
-|
844 | |
- |
-|
845 | Doing so automatically runs `test-error.sh` on each checked-out commit until Git finds the first broken commit. You can also run something like `make` or `make tests` or whatever you have that runs automated tests for you. |
- |
-|
846 | |
- |
-|
847 | ## Submodules ## |
- |
-|
848 | |
- |
-|
849 | It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other. |
- |
-|
850 | |
- |
-|
851 | Here’s an example. Suppose you’re developing a web site and creating Atom feeds. Instead of writing your own Atom-generating code, you decide to use a library. You’re likely to have to either include this code from a shared library like a CPAN install or Ruby gem, or copy the source code into your own project tree. The issue with including the library is that it’s difficult to customize the library in any way and often more difficult to deploy it, because you need to make sure every client has that library available. The issue with vendoring the code into your own project is that any custom changes you make are difficult to merge when upstream changes become available. |
- |
-|
852 | |
- |
-|
853 | Git addresses this issue using submodules. Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. |
- |
-|
854 | |
- |
-|
855 | ### Starting with Submodules ### |
- |
-|
856 | |
- |
-|
857 | Suppose you want to add the Rack library (a Ruby web server gateway interface) to your project, possibly maintain your own changes to it, but continue to merge in upstream changes. The first thing you should do is clone the external repository into your subdirectory. You add external projects as submodules with the `git submodule add` command: |
- |
-|
858 | |
- |
-|
859 | $ git submodule add git://github.com/chneukirchen/rack.git rack |
- |
-|
860 | Initialized empty Git repository in /opt/subtest/rack/.git/ |
- |
-|
861 | remote: Counting objects: 3181, done. |
- |
-|
862 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
863 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
864 | Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done. |
- |
-|
865 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
866 | |
- |
-|
867 | Now you have the Rack project under a subdirectory named `rack` within your project. You can go into that subdirectory, make changes, add your own writable remote repository to push your changes into, fetch and merge from the original repository, and more. If you run `git status` right after you add the submodule, you see two things: |
- |
-|
868 | |
- |
-|
869 | $ git status |
- |
-|
870 | # On branch master |
- |
-|
871 | # Changes to be committed: |
- |
-|
872 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
873 | # |
- |
-|
874 | # new file: .gitmodules |
- |
-|
875 | # new file: rack |
- |
-|
876 | # |
- |
-|
877 | |
- |
-|
878 | First you notice the `.gitmodules` file. This is a configuration file that stores the mapping between the project’s URL and the local subdirectory you’ve pulled it into: |
- |
-|
879 | |
- |
-|
880 | $ cat .gitmodules |
- |
-|
881 | [submodule "rack"] |
- |
-|
882 | path = rack |
- |
-|
883 | url = git://github.com/chneukirchen/rack.git |
- |
-|
884 | |
- |
-|
885 | If you have multiple submodules, you’ll have multiple entries in this file. It’s important to note that this file is version-controlled with your other files, like your `.gitignore` file. It’s pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from. |
- |
-|
886 | |
- |
-|
887 | The other listing in the `git status` output is the rack entry. If you run `git diff` on that, you see something interesting: |
- |
-|
888 | |
- |
-|
889 | $ git diff --cached rack |
- |
-|
890 | diff --git a/rack b/rack |
- |
-|
891 | new file mode 160000 |
- |
-|
892 | index 0000000..08d709f |
- |
-|
893 | --- /dev/null |
- |
-|
894 | +++ b/rack |
- |
-|
895 | @@ -0,0 +1 @@ |
- |
-|
896 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
897 | |
- |
-|
898 | Although `rack` is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git records it as a particular commit from that repository. When you make changes and commit in that subdirectory, the superproject notices that the HEAD there has changed and records the exact commit you’re currently working off of; that way, when others clone this project, they can re-create the environment exactly. |
- |
-|
899 | |
- |
-|
900 | This is an important point with submodules: you record them as the exact commit they’re at. You can’t record a submodule at `master` or some other symbolic reference. |
- |
-|
901 | |
- |
-|
902 | When you commit, you see something like this: |
- |
-|
903 | |
- |
-|
904 | $ git commit -m 'first commit with submodule rack' |
- |
-|
905 | [master 0550271] first commit with submodule rack |
- |
-|
906 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
907 | create mode 100644 .gitmodules |
- |
-|
908 | create mode 160000 rack |
- |
-|
909 | |
- |
-|
910 | Notice the 160000 mode for the rack entry. That is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file. |
- |
-|
911 | |
- |
-|
912 | You can treat the `rack` directory as a separate project and then update your superproject from time to time with a pointer to the latest commit in that subproject. All the Git commands work independently in the two directories: |
- |
-|
913 | |
- |
-|
914 | $ git log -1 |
- |
-|
915 | commit 0550271328a0038865aad6331e620cd7238601bb |
- |
-|
916 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
917 | Date: Thu Apr 9 09:03:56 2009 -0700 |
- |
-|
918 | |
- |
-|
919 | first commit with submodule rack |
- |
-|
920 | $ cd rack/ |
- |
-|
921 | $ git log -1 |
- |
-|
922 | commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
923 | Author: Christian Neukirchen <chneukirchen@gmail.com> |
- |
-|
924 | Date: Wed Mar 25 14:49:04 2009 +0100 |
- |
-|
925 | |
- |
-|
926 | Document version change |
- |
-|
927 | |
- |
-|
928 | ### Cloning a Project with Submodules ### |
- |
-|
929 | |
- |
-|
930 | Here you’ll clone a project with a submodule in it. When you receive such a project, you get the directories that contain submodules, but none of the files yet: |
- |
-|
931 | |
- |
-|
932 | $ git clone git://github.com/schacon/myproject.git |
- |
-|
933 | Initialized empty Git repository in /opt/myproject/.git/ |
- |
-|
934 | remote: Counting objects: 6, done. |
- |
-|
935 | remote: Compressing objects: 100% (4/4), done. |
- |
-|
936 | remote: Total 6 (delta 0), reused 0 (delta 0) |
- |
-|
937 | Receiving objects: 100% (6/6), done. |
- |
-|
938 | $ cd myproject |
- |
-|
939 | $ ls -l |
- |
-|
940 | total 8 |
- |
-|
941 | -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README |
- |
-|
942 | drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack |
- |
-|
943 | $ ls rack/ |
- |
-|
944 | $ |
- |
-|
945 | |
- |
-|
946 | The `rack` directory is there, but empty. You must run two commands: `git submodule init` to initialize your local configuration file, and `git submodule update` to fetch all the data from that project and check out the appropriate commit listed in your superproject: |
- |
-|
947 | |
- |
-|
948 | $ git submodule init |
- |
-|
949 | Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack' |
- |
-|
950 | $ git submodule update |
- |
-|
951 | Initialized empty Git repository in /opt/myproject/rack/.git/ |
- |
-|
952 | remote: Counting objects: 3181, done. |
- |
-|
953 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
954 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
955 | Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done. |
- |
-|
956 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
957 | Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433' |
- |
-|
958 | |
- |
-|
959 | Now your `rack` subdirectory is at the exact state it was in when you committed earlier. If another developer makes changes to the rack code and commits, and you pull that reference down and merge it in, you get something a bit odd: |
- |
-|
960 | |
- |
-|
961 | $ git merge origin/master |
- |
-|
962 | Updating 0550271..85a3eee |
- |
-|
963 | Fast forward |
- |
-|
964 | rack | 2 +- |
- |
-|
965 | 1 files changed, 1 insertions(+), 1 deletions(-) |
- |
-|
966 | [master*]$ git status |
- |
-|
967 | # On branch master |
- |
-|
968 | # Changes not staged for commit: |
- |
-|
969 | # (use "git add <file>..." to update what will be committed) |
- |
-|
970 | # (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
971 | # |
- |
-|
972 | # modified: rack |
- |
-|
973 | # |
- |
-|
974 | |
- |
-|
975 | You merged in what is basically a change to the pointer for your submodule; but it doesn’t update the code in the submodule directory, so it looks like you have a dirty state in your working directory: |
- |
-|
976 | |
- |
-|
977 | $ git diff |
- |
-|
978 | diff --git a/rack b/rack |
- |
-|
979 | index 6c5e70b..08d709f 160000 |
- |
-|
980 | --- a/rack |
- |
-|
981 | +++ b/rack |
- |
-|
982 | @@ -1 +1 @@ |
- |
-|
983 | -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
984 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
985 | |
- |
-|
986 | This is the case because the pointer you have for the submodule isn’t what is actually in the submodule directory. To fix this, you must run `git submodule update` again: |
- |
-|
987 | |
- |
-|
988 | $ git submodule update |
- |
-|
989 | remote: Counting objects: 5, done. |
- |
-|
990 | remote: Compressing objects: 100% (3/3), done. |
- |
-|
991 | remote: Total 3 (delta 1), reused 2 (delta 0) |
- |
-|
992 | Unpacking objects: 100% (3/3), done. |
- |
-|
993 | From git@github.com:schacon/rack |
- |
-|
994 | 08d709f..6c5e70b master -> origin/master |
- |
-|
995 | Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0' |
- |
-|
996 | |
- |
-|
997 | You have to do this every time you pull down a submodule change in the main project. It’s strange, but it works. |
- |
-|
998 | |
- |
-|
999 | One common problem happens when a developer makes a change locally in a submodule but doesn’t push it to a public server. Then, they commit a pointer to that non-public state and push up the superproject. When other developers try to run `git submodule update`, the submodule system can’t find the commit that is referenced, because it exists only on the first developer’s system. If that happens, you see an error like this: |
- |
-|
1000 | |
- |
-|
1001 | $ git submodule update |
- |
-|
1002 | fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
1003 | Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack' |
- |
-|
1004 | |
- |
-|
1005 | You have to see who last changed the submodule: |
- |
-|
1006 | |
- |
-|
1007 | $ git log -1 rack |
- |
-|
1008 | commit 85a3eee996800fcfa91e2119372dd4172bf76678 |
- |
-|
1009 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
1010 | Date: Thu Apr 9 09:19:14 2009 -0700 |
- |
-|
1011 | |
- |
-|
1012 | added a submodule reference I will never make public. hahahahaha! |
- |
-|
1013 | |
- |
-|
1014 | Then, you e-mail that guy and yell at him. |
- |
-|
1015 | |
- |
-|
1016 | ### Superprojects ### |
- |
-|
1017 | |
- |
-|
1018 | Sometimes, developers want to get a combination of a large project’s subdirectories, depending on what team they’re on. This is common if you’re coming from CVS or Subversion, where you’ve defined a module or collection of subdirectories, and you want to keep this type of workflow. |
- |
-|
1019 | |
- |
-|
1020 | A good way to do this in Git is to make each of the subdirectories a separate Git repository and then create superproject Git repositories that contain multiple submodules. A benefit of this approach is that you can more specifically define the relationships between the projects with tags and branches in the superprojects. |
- |
-|
1021 | |
- |
-|
1022 | ### Issues with Submodules ### |
- |
-|
1023 | |
- |
-|
1024 | Using submodules isn’t without hiccups, however. First, you must be relatively careful when working in the submodule directory. When you run `git submodule update`, it checks out the specific version of the project, but not within a branch. This is called having a detached HEAD — it means the HEAD file points directly to a commit, not to a symbolic reference. The issue is that you generally don’t want to work in a detached HEAD environment, because it’s easy to lose changes. If you do an initial `submodule update`, commit in that submodule directory without creating a branch to work in, and then run `git submodule update` again from the superproject without committing in the meantime, Git will overwrite your changes without telling you. Technically you won’t lose the work, but you won’t have a branch pointing to it, so it will be somewhat difficult to retrieve. |
- |
-|
1025 | |
- |
-|
1026 | To avoid this issue, create a branch when you work in a submodule directory with `git checkout -b work` or something equivalent. When you do the submodule update a second time, it will still revert your work, but at least you have a pointer to get back to. |
- |
-|
1027 | |
- |
-|
1028 | Switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory: |
- |
-|
1029 | |
- |
-|
1030 | $ git checkout -b rack |
- |
-|
1031 | Switched to a new branch "rack" |
- |
-|
1032 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1033 | Initialized empty Git repository in /opt/myproj/rack/.git/ |
- |
-|
1034 | ... |
- |
-|
1035 | Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done. |
- |
-|
1036 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1037 | $ git commit -am 'added rack submodule' |
- |
-|
1038 | [rack cc49a69] added rack submodule |
- |
-|
1039 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
1040 | create mode 100644 .gitmodules |
- |
-|
1041 | create mode 160000 rack |
- |
-|
1042 | $ git checkout master |
- |
-|
1043 | Switched to branch "master" |
- |
-|
1044 | $ git status |
- |
-|
1045 | # On branch master |
- |
-|
1046 | # Untracked files: |
- |
-|
1047 | # (use "git add <file>..." to include in what will be committed) |
- |
-|
1048 | # |
- |
-|
1049 | # rack/ |
- |
-|
1050 | |
- |
-|
1051 | You have to either move it out of the way or remove it, in which case you have to clone it again when you switch back—and you may lose local changes or branches that you didn’t push up. |
- |
-|
1052 | |
- |
-|
1053 | The last main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have the rack files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run `submodule add`, Git yells at you: |
- |
-|
1054 | |
- |
-|
1055 | $ rm -Rf rack/ |
- |
-|
1056 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1057 | 'rack' already exists in the index |
- |
-|
1058 | |
- |
-|
1059 | You have to unstage the `rack` directory first. Then you can add the submodule: |
- |
-|
1060 | |
- |
-|
1061 | $ git rm -r rack |
- |
-|
1062 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1063 | Initialized empty Git repository in /opt/testsub/rack/.git/ |
- |
-|
1064 | remote: Counting objects: 3184, done. |
- |
-|
1065 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1066 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1067 | Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done. |
- |
-|
1068 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1069 | |
- |
-|
1070 | Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule — you get this error: |
- |
-|
1071 | |
- |
-|
1072 | $ git checkout master |
- |
-|
1073 | error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge. |
- |
-|
1074 | |
- |
-|
1075 | You have to move the `rack` submodule directory out of the way before you can switch to a branch that doesn’t have it: |
- |
-|
1076 | |
- |
-|
1077 | $ mv rack /tmp/ |
- |
-|
1078 | $ git checkout master |
- |
-|
1079 | Switched to branch "master" |
- |
-|
1080 | $ ls |
- |
-|
1081 | README rack |
- |
-|
1082 | |
- |
-|
1083 | Then, when you switch back, you get an empty `rack` directory. You can either run `git submodule update` to reclone, or you can move your `/tmp/rack` directory back into the empty directory. |
- |
-|
1084 | |
- |
-|
1085 | ## Subtree Merging ## |
- |
-|
1086 | |
- |
-|
1087 | Now that you’ve seen the difficulties of the submodule system, let’s look at an alternate way to solve the same problem. When Git merges, it looks at what it has to merge together and then chooses an appropriate merging strategy to use. If you’re merging two branches, Git uses a _recursive_ strategy. If you’re merging more than two branches, Git picks the _octopus_ strategy. These strategies are automatically chosen for you because the recursive strategy can handle complex three-way merge situations — for example, more than one common ancestor — but it can only handle merging two branches. The octopus merge can handle multiple branches but is more cautious to avoid difficult conflicts, so it’s chosen as the default strategy if you’re trying to merge more than two branches. |
- |
-|
1088 | |
- |
-|
1089 | However, there are other strategies you can choose as well. One of them is the _subtree_ merge, and you can use it to deal with the subproject issue. Here you’ll see how to do the same rack embedding as in the last section, but using subtree merges instead. |
- |
-|
1090 | |
- |
-|
1091 | The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one and vice versa. When you specify a subtree merge, Git is smart enough to figure out that one is a subtree of the other and merge appropriately — it’s pretty amazing. |
- |
-|
1092 | |
- |
-|
1093 | You first add the Rack application to your project. You add the Rack project as a remote reference in your own project and then check it out into its own branch: |
- |
-|
1094 | |
- |
-|
1095 | $ git remote add rack_remote git@github.com:schacon/rack.git |
- |
-|
1096 | $ git fetch rack_remote |
- |
-|
1097 | warning: no common commits |
- |
-|
1098 | remote: Counting objects: 3184, done. |
- |
-|
1099 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1100 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1101 | Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. |
- |
-|
1102 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1103 | From git@github.com:schacon/rack |
- |
-|
1104 | * [new branch] build -> rack_remote/build |
- |
-|
1105 | * [new branch] master -> rack_remote/master |
- |
-|
1106 | * [new branch] rack-0.4 -> rack_remote/rack-0.4 |
- |
-|
1107 | * [new branch] rack-0.9 -> rack_remote/rack-0.9 |
- |
-|
1108 | $ git checkout -b rack_branch rack_remote/master |
- |
-|
1109 | Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. |
- |
-|
1110 | Switched to a new branch "rack_branch" |
- |
-|
1111 | |
- |
-|
1112 | Now you have the root of the Rack project in your `rack_branch` branch and your own project in the `master` branch. If you check out one and then the other, you can see that they have different project roots: |
- |
-|
1113 | |
- |
-|
1114 | $ ls |
- |
-|
1115 | AUTHORS KNOWN-ISSUES Rakefile contrib lib |
- |
-|
1116 | COPYING README bin example test |
- |
-|
1117 | $ git checkout master |
- |
-|
1118 | Switched to branch "master" |
- |
-|
1119 | $ ls |
- |
-|
1120 | README |
- |
-|
1121 | |
- |
-|
1122 | You want to pull the Rack project into your `master` project as a subdirectory. You can do that in Git with `git read-tree`. You’ll learn more about `read-tree` and its friends in Chapter 9, but for now know that it reads the root tree of one branch into your current staging area and working directory. You just switched back to your `master` branch, and you pull the `rack` branch into the `rack` subdirectory of your `master` branch of your main project: |
- |
-|
1123 | |
- |
-|
1124 | $ git read-tree --prefix=rack/ -u rack_branch |
- |
-|
1125 | |
- |
-|
1126 | When you commit, it looks like you have all the Rack files under that subdirectory — as though you copied them in from a tarball. What gets interesting is that you can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, you can pull in upstream changes by switching to that branch and pulling: |
- |
-|
1127 | |
- |
-|
1128 | $ git checkout rack_branch |
- |
-|
1129 | $ git pull |
- |
-|
1130 | |
- |
-|
1131 | Then, you can merge those changes back into your master branch. You can use `git merge -s subtree` and it will work fine; but Git will also merge the histories together, which you probably don’t want. To pull in the changes and prepopulate the commit message, use the `--squash` and `--no-commit` options as well as the `-s subtree` strategy option: |
- |
-|
1132 | |
- |
-|
1133 | $ git checkout master |
- |
-|
1134 | $ git merge --squash -s subtree --no-commit rack_branch |
- |
-|
1135 | Squash commit -- not updating HEAD |
- |
-|
1136 | Automatic merge went well; stopped before committing as requested |
- |
-|
1137 | |
- |
-|
1138 | All the changes from your Rack project are merged in and ready to be committed locally. You can also do the opposite — make changes in the `rack` subdirectory of your master branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. |
- |
-|
1139 | |
- |
-|
1140 | To get a diff between what you have in your `rack` subdirectory and the code in your `rack_branch` branch — to see if you need to merge them — you can’t use the normal `diff` command. Instead, you must run `git diff-tree` with the branch you want to compare to: |
- |
-|
1141 | |
- |
-|
1142 | $ git diff-tree -p rack_branch |
- |
-|
1143 | |
- |
-|
1144 | Or, to compare what is in your `rack` subdirectory with what the `master` branch on the server was the last time you fetched, you can run |
- |
-|
1145 | |
- |
-|
1146 | $ git diff-tree -p rack_remote/master |
- |
-|
1147 | |
- |
-|
1148 | ## Summary ## |
- |
-|
1149 | |
- |
-|
1150 | You’ve seen a number of advanced tools that allow you to manipulate your commits and staging area more precisely. When you notice issues, you should be able to easily figure out what commit introduced them, when, and by whom. If you want to use subprojects in your project, you’ve learned a few ways to accommodate those needs. At this point, you should be able to do most of the things in Git that you’ll need on the command line day to day and feel comfortable doing so. |
- |
-|
1151 | |
-281 | |
-
C:\Users\15625\Documents\Git\progit\en\02-git-basics\01-chapter2.markdown | -C:\Users\15625\Documents\Git\progit2-ja\book\07-git-tools\sections\signing.asc | -||
---|---|---|---|
. | |
-1 | [[_signing]] |
-
|
-2 | === Signing Your Work |
-|
|
-3 | |
-|
|
-4 | Git is cryptographically secure, but it's not foolproof. If you're taking work from others on the internet and want to verify that commits are actually from a trusted source, Git has a few ways to sign and verify work using GPG. |
-|
|
-5 | |
-|
|
-6 | ==== GPG Introduction |
-|
|
-7 | |
-|
|
-8 | First of all, if you want to sign anything you need to get GPG configured and your personal key installed. |
-|
|
-9 | |
-|
|
-10 | [source,console] |
-|
|
-11 | ---- |
-|
|
-12 | $ gpg --list-keys |
-|
|
-13 | /Users/schacon/.gnupg/pubring.gpg |
-|
|
-14 | --------------------------------- |
-|
|
-15 | pub 2048R/0A46826A 2014-06-04 |
-|
|
-16 | uid Scott Chacon (Git signing key) <schacon@gmail.com> |
-|
|
-17 | sub 2048R/874529A9 2014-06-04 |
-|
|
-18 | ---- |
-|
|
-19 | |
-|
|
-20 | If you don't have a key installed, you can generate one with `gpg --gen-key`. |
-|
|
-21 | |
-|
|
-22 | [source,console] |
-|
|
-23 | ---- |
-|
|
-24 | gpg --gen-key |
-|
|
-25 | ---- |
-|
|
-26 | |
-|
|
-27 | Once you have a private key to sign with, you can configure Git to use it for signing things by setting the `user.signingkey` config setting. |
-|
|
-28 | |
-|
|
-29 | [source,console] |
-|
|
-30 | ---- |
-|
|
-31 | git config --global user.signingkey 0A46826A |
-|
|
-32 | ---- |
-|
|
-33 | |
-|
1 | # Git Basics # |
-34 | Now Git will use your key by default to sign tags and commits if you want. |
-
2 | |
- |
-|
3 | If you can read only one chapter to get going with Git, this is it. This chapter covers every basic command you need to do the vast majority of the things you’ll eventually spend your time doing with Git. By the end of the chapter, you should be able to configure and initialize a repository, begin and stop tracking files, and stage and commit changes. We’ll also show you how to set up Git to ignore certain files and file patterns, how to undo mistakes quickly and easily, how to browse the history of your project and view changes between commits, and how to push and pull from remote repositories. |
- |
-|
4 | |
- |
-|
5 | ## Getting a Git Repository ## |
- |
-|
6 | |
- |
-|
7 | You can get a Git project using two main approaches. The first takes an existing project or directory and imports it into Git. The second clones an existing Git repository from another server. |
- |
-|
8 | |
- |
-|
9 | ### Initializing a Repository in an Existing Directory ### |
- |
-|
10 | |
- |
-|
11 | If you’re starting to track an existing project in Git, you need to go to the project’s directory and type |
- |
-|
12 | |
- |
-|
13 | $ git init |
- |
-|
14 | |
- |
-|
15 | This creates a new subdirectory named `.git` that contains all of your necessary repository files — a Git repository skeleton. At this point, nothing in your project is tracked yet. (See *Chapter 9* for more information about exactly what files are contained in the `.git` directory you just created.) |
- |
-|
16 | |
- |
-|
17 | If you want to start version-controlling existing files (as opposed to an empty directory), you should probably begin tracking those files and do an initial commit. You can accomplish that with a few `git add` commands that specify the files you want to track, followed by a commit: |
- |
-|
18 | |
- |
-|
19 | $ git add *.c |
- |
-|
20 | $ git add README |
- |
-|
21 | $ git commit -m 'initial project version' |
- |
-|
22 | |
- |
-|
23 | We’ll go over what these commands do in just a minute. At this point, you have a Git repository with tracked files and an initial commit. |
- |
-|
24 | |
- |
-|
25 | ### Cloning an Existing Repository ### |
- |
-|
26 | |
- |
-|
27 | If you want to get a copy of an existing Git repository — for example, a project you’d like to contribute to — the command you need is `git clone`. If you’re familiar with other VCS systems such as Subversion, you’ll notice that the command is `clone` and not `checkout`. This is an important distinction — Git receives a copy of nearly all data that the server has. Every version of every file for the history of the project is pulled down when you run `git clone`. In fact, if your server disk gets corrupted, you can use any of the clones on any client to set the server back to the state it was in when it was cloned (you may lose some server-side hooks and such, but all the versioned data would be there — see *Chapter 4* for more details). |
- |
-|
28 | |
- |
-|
29 | You clone a repository with `git clone [url]`. For example, if you want to clone the Ruby Git library called Grit, you can do so like this: |
- |
-|
30 | |
- |
-|
31 | $ git clone git://github.com/schacon/grit.git |
- |
-|
32 | |
- |
-|
33 | That creates a directory named `grit`, initializes a `.git` directory inside it, pulls down all the data for that repository, and checks out a working copy of the latest version. If you go into the new `grit` directory, you’ll see the project files in there, ready to be worked on or used. If you want to clone the repository into a directory named something other than grit, you can specify that as the next command-line option: |
- |
-|
34 | |
- |
-|
35 | $ git clone git://github.com/schacon/grit.git mygrit |
- |
-|
36 | |
- |
-|
37 | That command does the same thing as the previous one, but the target directory is called `mygrit`. |
- |
-|
38 | |
- |
-|
39 | Git has a number of different transfer protocols you can use. The previous example uses the `git://` protocol, but you may also see `http(s)://` or `user@server:/path.git`, which uses the SSH transfer protocol. *Chapter 4* will introduce all of the available options the server can set up to access your Git repository and the pros and cons of each. |
- |
-|
40 | |
- |
-|
41 | ## Recording Changes to the Repository ## |
- |
-|
42 | |
- |
-|
43 | You have a bona fide Git repository and a checkout or working copy of the files for that project. You need to make some changes and commit snapshots of those changes into your repository each time the project reaches a state you want to record. |
- |
-|
44 | |
- |
-|
45 | Remember that each file in your working directory can be in one of two states: *tracked* or *untracked*. *Tracked* files are files that were in the last snapshot; they can be *unmodified*, *modified*, or *staged*. *Untracked* files are everything else — any files in your working directory that were not in your last snapshot and are not in your staging area. When you first clone a repository, all of your files will be tracked and unmodified because you just checked them out and haven’t edited anything. |
- |
-|
46 | |
- |
-|
47 | As you edit files, Git sees them as modified, because you’ve changed them since your last commit. You *stage* these modified files and then commit all your staged changes, and the cycle repeats. This lifecycle is illustrated in Figure 2-1. |
- |
-|
48 | |
- |
-|
49 | Insert 18333fig0201.png |
- |
-|
50 | Figure 2-1. The lifecycle of the status of your files. |
- |
-|
51 | |
- |
-|
52 | ### Checking the Status of Your Files ### |
- |
-|
53 | |
- |
-|
54 | The main tool you use to determine which files are in which state is the `git status` command. If you run this command directly after a clone, you should see something like this: |
- |
-|
55 | |
- |
-|
56 | $ git status |
- |
-|
57 | On branch master |
- |
-|
58 | nothing to commit, working directory clean |
- |
-|
59 | |
- |
-|
60 | This means you have a clean working directory — in other words, no tracked files are modified. Git also doesn’t see any untracked files, or they would be listed here. Finally, the command tells you which branch you’re on. For now, that is always `master`, which is the default; you won’t worry about it here. The next chapter will go over branches and references in detail. |
- |
-|
61 | |
- |
-|
62 | Let’s say you add a new file to your project, a simple `README` file. If the file didn’t exist before, and you run `git status`, you see your untracked file like so: |
- |
-|
63 | |
- |
-|
64 | $ vim README |
- |
-|
65 | $ git status |
- |
-|
66 | On branch master |
- |
-|
67 | Untracked files: |
- |
-|
68 | (use "git add <file>..." to include in what will be committed) |
- |
-|
69 | |
- |
-|
70 | README |
- |
-|
71 | |
- |
-|
72 | nothing added to commit but untracked files present (use "git add" to track) |
- |
-|
73 | |
- |
-|
74 | You can see that your new `README` file is untracked, because it’s under the “Untracked files” heading in your status output. Untracked basically means that Git sees a file you didn’t have in the previous snapshot (commit); Git won’t start including it in your commit snapshots until you explicitly tell it to do so. It does this so you don’t accidentally begin including generated binary files or other files that you did not mean to include. You do want to start including README, so let’s start tracking the file. |
- |
-|
75 | |
- |
-|
76 | ### Tracking New Files ### |
- |
-|
77 | |
- |
-|
78 | In order to begin tracking a new file, you use the command `git add`. To begin tracking the `README` file, you can run this: |
- |
-|
79 | |
- |
-|
80 | $ git add README |
- |
-|
81 | |
- |
-|
82 | If you run your status command again, you can see that your `README` file is now tracked and staged: |
- |
-|
83 | |
- |
-|
84 | $ git status |
- |
-|
85 | On branch master |
- |
-|
86 | Changes to be committed: |
- |
-|
87 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
88 | |
- |
-|
89 | new file: README |
- |
-|
90 | |
- |
-|
91 | |
- |
-|
92 | You can tell that it’s staged because it’s under the “Changes to be committed” heading. If you commit at this point, the version of the file at the time you ran `git add` is what will be in the historical snapshot. You may recall that when you ran `git init` earlier, you then ran `git add (files)` — that was to begin tracking files in your directory. The `git add` command takes a path name for either a file or a directory; if it’s a directory, the command adds all the files in that directory recursively. |
- |
-|
93 | |
- |
-|
94 | ### Staging Modified Files ### |
- |
-|
95 | |
- |
-|
96 | Let’s change a file that was already tracked. If you change a previously tracked file called `benchmarks.rb` and then run your `status` command again, you get something that looks like this: |
- |
-|
97 | |
- |
-|
98 | $ git status |
- |
-|
99 | On branch master |
- |
-|
100 | Changes to be committed: |
- |
-|
101 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
102 | |
- |
-|
103 | new file: README |
- |
-|
104 | |
- |
-|
105 | Changes not staged for commit: |
- |
-|
106 | (use "git add <file>..." to update what will be committed) |
- |
-|
107 | (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
108 | |
- |
-|
109 | modified: benchmarks.rb |
- |
-|
110 | |
- |
-|
111 | |
- |
-|
112 | The `benchmarks.rb` file appears under a section named “Changes not staged for commit” — which means that a file that is tracked has been modified in the working directory but not yet staged. To stage it, you run the `git add` command (it’s a multipurpose command — you use it to begin tracking new files, to stage files, and to do other things like marking merge-conflicted files as resolved). Let’s run `git add` now to stage the `benchmarks.rb` file, and then run `git status` again: |
- |
-|
113 | |
- |
-|
114 | $ git add benchmarks.rb |
- |
-|
115 | $ git status |
- |
-|
116 | On branch master |
- |
-|
117 | Changes to be committed: |
- |
-|
118 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
119 | |
- |
-|
120 | new file: README |
- |
-|
121 | modified: benchmarks.rb |
- |
-|
122 | |
- |
-|
123 | |
- |
-|
124 | Both files are staged and will go into your next commit. At this point, suppose you remember one little change that you want to make in `benchmarks.rb` before you commit it. You open it again and make that change, and you’re ready to commit. However, let’s run `git status` one more time: |
- |
-|
125 | |
- |
-|
126 | $ vim benchmarks.rb |
- |
-|
127 | $ git status |
- |
-|
128 | On branch master |
- |
-|
129 | Changes to be committed: |
- |
-|
130 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
131 | |
- |
-|
132 | new file: README |
- |
-|
133 | modified: benchmarks.rb |
- |
-|
134 | |
- |
-|
135 | Changes not staged for commit: |
- |
-|
136 | (use "git add <file>..." to update what will be committed) |
- |
-|
137 | (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
138 | |
- |
-|
139 | modified: benchmarks.rb |
- |
-|
140 | |
- |
-|
141 | |
- |
-|
142 | What the heck? Now `benchmarks.rb` is listed as both staged and unstaged. How is that possible? It turns out that Git stages a file exactly as it is when you run the `git add` command. If you commit now, the version of `benchmarks.rb` as it was when you last ran the `git add` command is how it will go into the commit, not the version of the file as it looks in your working directory when you run `git commit`. If you modify a file after you run `git add`, you have to run `git add` again to stage the latest version of the file: |
- |
-|
143 | |
- |
-|
144 | $ git add benchmarks.rb |
- |
-|
145 | $ git status |
- |
-|
146 | On branch master |
- |
-|
147 | Changes to be committed: |
- |
-|
148 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
149 | |
- |
-|
150 | new file: README |
- |
-|
151 | modified: benchmarks.rb |
- |
-|
152 | |
- |
-|
153 | |
- |
-|
154 | ### Ignoring Files ### |
- |
-|
155 | |
- |
-|
156 | Often, you’ll have a class of files that you don’t want Git to automatically add or even show you as being untracked. These are generally automatically generated files such as log files or files produced by your build system. In such cases, you can create a file listing patterns to match them named `.gitignore`. Here is an example `.gitignore` file: |
- |
-|
157 | |
- |
-|
158 | $ cat .gitignore |
- |
-|
159 | *.[oa] |
- |
-|
160 | *~ |
- |
-|
161 | |
- |
-|
162 | The first line tells Git to ignore any files ending in `.o` or `.a` — *object* and *archive* files that may be the product of building your code. The second line tells Git to ignore all files that end with a tilde (`~`), which is used by many text editors such as Emacs to mark temporary files. You may also include a `log`, `tmp`, or `pid` directory; automatically generated documentation; and so on. Setting up a `.gitignore` file before you get going is generally a good idea so you don’t accidentally commit files that you really don’t want in your Git repository. |
- |
-|
163 | |
- |
-|
164 | The rules for the patterns you can put in the `.gitignore` file are as follows: |
- |
-|
165 | |
- |
-|
166 | * Blank lines or lines starting with `#` are ignored. |
- |
-|
167 | * Standard glob patterns work. |
- |
-|
168 | * You can end patterns with a forward slash (`/`) to specify a directory. |
- |
-|
169 | * You can negate a pattern by starting it with an exclamation point (`!`). |
- |
-|
170 | |
- |
-|
171 | Glob patterns are like simplified regular expressions that shells use. An asterisk (`*`) matches zero or more characters; `[abc]` matches any character inside the brackets (in this case `a`, `b`, or `c`); a question mark (`?`) matches a single character; and brackets enclosing characters separated by a hyphen(`[0-9]`) matches any character in the range (in this case 0 through 9) . |
- |
-|
172 | |
- |
-|
173 | Here is another example `.gitignore` file: |
- |
-|
174 | |
- |
-|
175 | # a comment - this is ignored |
- |
-|
176 | # no .a files |
- |
-|
177 | *.a |
- |
-|
178 | # but do track lib.a, even though you're ignoring .a files above |
- |
-|
179 | !lib.a |
- |
-|
180 | # only ignore the root TODO file, not subdir/TODO |
- |
-|
181 | /TODO |
- |
-|
182 | # ignore all files in the build/ directory |
- |
-|
183 | build/ |
- |
-|
184 | # ignore doc/notes.txt, but not doc/server/arch.txt |
- |
-|
185 | doc/*.txt |
- |
-|
186 | # ignore all .txt files in the doc/ directory |
- |
-|
187 | doc/**/*.txt |
- |
-|
188 | |
- |
-|
189 | A `**/` pattern is available in Git since version 1.8.2. |
- |
-|
190 | |
- |
-|
191 | ### Viewing Your Staged and Unstaged Changes ### |
- |
-|
192 | |
- |
-|
193 | If the `git status` command is too vague for you — you want to know exactly what you changed, not just which files were changed — you can use the `git diff` command. We’ll cover `git diff` in more detail later; but you’ll probably use it most often to answer these two questions: What have you changed but not yet staged? And what have you staged that you are about to commit? Although `git status` answers those questions very generally, `git diff` shows you the exact lines added and removed — the patch, as it were. |
- |
-|
194 | |
- |
-|
195 | Let’s say you edit and stage the `README` file again and then edit the `benchmarks.rb` file without staging it. If you run your `status` command, you once again see something like this: |
- |
-|
196 | |
- |
-|
197 | $ git status |
- |
-|
198 | On branch master |
- |
-|
199 | Changes to be committed: |
- |
-|
200 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
201 | |
- |
-|
202 | new file: README |
- |
-|
203 | |
- |
-|
204 | Changes not staged for commit: |
- |
-|
205 | (use "git add <file>..." to update what will be committed) |
- |
-|
206 | (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
207 | |
- |
-|
208 | modified: benchmarks.rb |
- |
-|
209 | |
- |
-|
210 | |
- |
-|
211 | To see what you’ve changed but not yet staged, type `git diff` with no other arguments: |
- |
-|
212 | |
- |
-|
213 | $ git diff |
- |
-|
214 | diff --git a/benchmarks.rb b/benchmarks.rb |
- |
-|
215 | index 3cb747f..da65585 100644 |
- |
-|
216 | --- a/benchmarks.rb |
- |
-|
217 | +++ b/benchmarks.rb |
- |
-|
218 | @@ -36,6 +36,10 @@ def main |
- |
-|
219 | @commit.parents[0].parents[0].parents[0] |
- |
-|
220 | end |
- |
-|
221 | |
- |
-|
222 | + run_code(x, 'commits 1') do |
- |
-|
223 | + git.commits.size |
- |
-|
224 | + end |
- |
-|
225 | + |
- |
-|
226 | run_code(x, 'commits 2') do |
- |
-|
227 | log = git.commits('master', 15) |
- |
-|
228 | log.size |
- |
-|
229 | |
- |
-|
230 | That command compares what is in your working directory with what is in your staging area. The result tells you the changes you’ve made that you haven’t yet staged. |
- |
-|
231 | |
- |
-|
232 | If you want to see what you’ve staged that will go into your next commit, you can use `git diff --cached`. (In Git versions 1.6.1 and later, you can also use `git diff --staged`, which may be easier to remember.) This command compares your staged changes to your last commit: |
- |
-|
233 | |
- |
-|
234 | $ git diff --cached |
- |
-|
235 | diff --git a/README b/README |
- |
-|
236 | new file mode 100644 |
- |
-|
237 | index 0000000..03902a1 |
- |
-|
238 | --- /dev/null |
- |
-|
239 | +++ b/README2 |
- |
-|
240 | @@ -0,0 +1,5 @@ |
- |
-|
241 | +grit |
- |
-|
242 | + by Tom Preston-Werner, Chris Wanstrath |
- |
-|
243 | + http://github.com/mojombo/grit |
- |
-|
244 | + |
- |
-|
245 | +Grit is a Ruby library for extracting information from a Git repository |
- |
-|
246 | |
- |
-|
247 | It’s important to note that `git diff` by itself doesn’t show all changes made since your last commit — only changes that are still unstaged. This can be confusing, because if you’ve staged all of your changes, `git diff` will give you no output. |
- |
-|
248 | |
- |
-|
249 | For another example, if you stage the `benchmarks.rb` file and then edit it, you can use `git diff` to see the changes in the file that are staged and the changes that are unstaged: |
- |
-|
250 | |
- |
-|
251 | $ git add benchmarks.rb |
- |
-|
252 | $ echo '# test line' >> benchmarks.rb |
- |
-|
253 | $ git status |
- |
-|
254 | On branch master |
- |
-|
255 | Changes to be committed: |
- |
-|
256 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
257 | |
- |
-|
258 | modified: benchmarks.rb |
- |
-|
259 | |
- |
-|
260 | Changes not staged for commit: |
- |
-|
261 | (use "git add <file>..." to update what will be committed) |
- |
-|
262 | (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
263 | |
- |
-|
264 | modified: benchmarks.rb |
- |
-|
265 | |
- |
-|
266 | |
- |
-|
267 | Now you can use `git diff` to see what is still unstaged |
- |
-|
268 | |
- |
-|
269 | $ git diff |
- |
-|
270 | diff --git a/benchmarks.rb b/benchmarks.rb |
- |
-|
271 | index e445e28..86b2f7c 100644 |
- |
-|
272 | --- a/benchmarks.rb |
- |
-|
273 | +++ b/benchmarks.rb |
- |
-|
274 | @@ -127,3 +127,4 @@ end |
- |
-|
275 | main() |
- |
-|
276 | |
- |
-|
277 | ##pp Grit::GitRuby.cache_client.stats |
- |
-|
278 | +# test line |
- |
-|
279 | |
- |
-|
280 | and `git diff --cached` to see what you’ve staged so far: |
- |
-|
281 | |
- |
-|
282 | $ git diff --cached |
- |
-|
283 | diff --git a/benchmarks.rb b/benchmarks.rb |
- |
-|
284 | index 3cb747f..e445e28 100644 |
- |
-|
285 | --- a/benchmarks.rb |
- |
-|
286 | +++ b/benchmarks.rb |
- |
-|
287 | @@ -36,6 +36,10 @@ def main |
- |
-|
288 | @commit.parents[0].parents[0].parents[0] |
- |
-|
289 | end |
- |
-|
290 | |
- |
-|
291 | + run_code(x, 'commits 1') do |
- |
-|
292 | + git.commits.size |
- |
-|
293 | + end |
- |
-|
294 | + |
- |
-|
295 | run_code(x, 'commits 2') do |
- |
-|
296 | log = git.commits('master', 15) |
- |
-|
297 | log.size |
- |
-|
298 | |
- |
-|
299 | ### Committing Your Changes ### |
- |
-|
300 | |
- |
-|
301 | Now that your staging area is set up the way you want it, you can commit your changes. Remember that anything that is still unstaged — any files you have created or modified that you haven’t run `git add` on since you edited them — won’t go into this commit. They will stay as modified files on your disk. |
- |
-|
302 | In this case, the last time you ran `git status`, you saw that everything was staged, so you’re ready to commit your changes. The simplest way to commit is to type `git commit`: |
- |
-|
303 | |
- |
-|
304 | $ git commit |
- |
-|
305 | |
- |
-|
306 | Doing so launches your editor of choice. (This is set by your shell’s `$EDITOR` environment variable — usually vim or emacs, although you can configure it with whatever you want using the `git config --global core.editor` command as you saw in *Chapter 1*). |
- |
-|
307 | |
- |
-|
308 | The editor displays the following text (this example is a Vim screen): |
- |
-|
309 | |
- |
-|
310 | # Please enter the commit message for your changes. Lines starting |
- |
-|
311 | # with '#' will be ignored, and an empty message aborts the commit. |
- |
-|
312 | # On branch master |
- |
-|
313 | # Changes to be committed: |
- |
-|
314 | # new file: README |
- |
-|
315 | # modified: benchmarks.rb |
- |
-|
316 | # |
- |
-|
317 | ~ |
- |
-|
318 | ~ |
- |
-|
319 | ~ |
- |
-|
320 | ".git/COMMIT_EDITMSG" 10L, 283C |
- |
-|
321 | |
- |
-|
322 | You can see that the default commit message contains the latest output of the `git status` command commented out and one empty line on top. You can remove these comments and type your commit message, or you can leave them there to help you remember what you’re committing. (For an even more explicit reminder of what you’ve modified, you can pass the `-v` option to `git commit`. Doing so also puts the diff of your change in the editor so you can see exactly what you did.) When you exit the editor, Git creates your commit with that commit message (with the comments and diff stripped out). |
- |
-|
323 | |
- |
-|
324 | Alternatively, you can type your commit message inline with the `commit` command by specifying it after a `-m` flag, like this: |
- |
-|
325 | |
- |
-|
326 | $ git commit -m "Story 182: Fix benchmarks for speed" |
- |
-|
327 | [master 463dc4f] Story 182: Fix benchmarks for speed |
- |
-|
328 | 2 files changed, 3 insertions(+) |
- |
-|
329 | create mode 100644 README |
- |
-|
330 | |
- |
-|
331 | Now you’ve created your first commit! You can see that the commit has given you some output about itself: which branch you committed to (`master`), what SHA-1 checksum the commit has (`463dc4f`), how many files were changed, and statistics about lines added and removed in the commit. |
- |
-|
332 | |
- |
-|
333 | Remember that the commit records the snapshot you set up in your staging area. Anything you didn’t stage is still sitting there modified; you can do another commit to add it to your history. Every time you perform a commit, you’re recording a snapshot of your project that you can revert to or compare to later. |
- |
-|
334 | |
- |
-|
335 | ### Skipping the Staging Area ### |
- |
-|
336 | |
- |
-|
337 | Although it can be amazingly useful for crafting commits exactly how you want them, the staging area is sometimes a bit more complex than you need in your workflow. If you want to skip the staging area, Git provides a simple shortcut. Providing the `-a` option to the `git commit` command makes Git automatically stage every file that is already tracked before doing the commit, letting you skip the `git add` part: |
- |
-|
338 | |
- |
-|
339 | $ git status |
- |
-|
340 | On branch master |
- |
-|
341 | Changes not staged for commit: |
- |
-|
342 | (use "git add <file>..." to update what will be committed) |
- |
-|
343 | (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
344 | |
- |
-|
345 | modified: benchmarks.rb |
- |
-|
346 | |
- |
-|
347 | no changes added to commit (use "git add" and/or "git commit -a") |
- |
-|
348 | $ git commit -a -m 'added new benchmarks' |
- |
-|
349 | [master 83e38c7] added new benchmarks |
- |
-|
350 | 1 files changed, 5 insertions(+) |
- |
-|
351 | |
- |
-|
352 | Notice how you don’t have to run `git add` on the `benchmarks.rb` file in this case before you commit. |
- |
-|
353 | |
- |
-|
354 | ### Removing Files ### |
- |
-|
355 | |
- |
-|
356 | To remove a file from Git, you have to remove it from your tracked files (more accurately, remove it from your staging area) and then commit. The `git rm` command does that and also removes the file from your working directory so you don’t see it as an untracked file next time around. |
- |
-|
357 | |
- |
-|
358 | If you simply remove the file from your working directory, it shows up under the “Changes not staged for commit” (that is, _unstaged_) area of your `git status` output: |
- |
-|
359 | |
- |
-|
360 | $ rm grit.gemspec |
- |
-|
361 | $ git status |
- |
-|
362 | On branch master |
- |
-|
363 | Changes not staged for commit: |
- |
-|
364 | (use "git add/rm <file>..." to update what will be committed) |
- |
-|
365 | (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
366 | |
- |
-|
367 | deleted: grit.gemspec |
- |
-|
368 | |
- |
-|
369 | no changes added to commit (use "git add" and/or "git commit -a") |
- |
-|
370 | |
- |
-|
371 | Then, if you run `git rm`, it stages the file’s removal: |
- |
-|
372 | |
- |
-|
373 | $ git rm grit.gemspec |
- |
-|
374 | rm 'grit.gemspec' |
- |
-|
375 | $ git status |
- |
-|
376 | On branch master |
- |
-|
377 | Changes to be committed: |
- |
-|
378 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
379 | |
- |
-|
380 | deleted: grit.gemspec |
- |
-|
381 | |
- |
-|
382 | |
- |
-|
383 | The next time you commit, the file will be gone and no longer tracked. If you modified the file and added it to the index already, you must force the removal with the `-f` option. This is a safety feature to prevent accidental removal of data that hasn’t yet been recorded in a snapshot and that can’t be recovered from Git. |
- |
-|
384 | |
- |
-|
385 | Another useful thing you may want to do is to keep the file in your working tree but remove it from your staging area. In other words, you may want to keep the file on your hard drive but not have Git track it anymore. This is particularly useful if you forgot to add something to your `.gitignore` file and accidentally staged it, like a large log file or a bunch of `.a` compiled files. To do this, use the `--cached` option: |
- |
-|
386 | |
- |
-|
387 | $ git rm --cached readme.txt |
- |
-|
388 | |
- |
-|
389 | You can pass files, directories, and file-glob patterns to the `git rm` command. That means you can do things such as |
- |
-|
390 | |
- |
-|
391 | $ git rm log/\*.log |
- |
-|
392 | |
- |
-|
393 | Note the backslash (`\`) in front of the `*`. This is necessary because Git does its own filename expansion in addition to your shell’s filename expansion. On Windows with the system console, the backslash must be omitted. This command removes all files that have the `.log` extension in the `log/` directory. Or, you can do something like this: |
- |
-|
394 | |
- |
-|
395 | $ git rm \*~ |
- |
-|
396 | |
- |
-|
397 | This command removes all files that end with `~`. |
- |
-|
398 | |
- |
-|
399 | ### Moving Files ### |
- |
-|
400 | |
- |
-|
401 | Unlike many other VCS systems, Git doesn’t explicitly track file movement. If you rename a file in Git, no metadata is stored in Git that tells it you renamed the file. However, Git is pretty smart about figuring that out after the fact — we’ll deal with detecting file movement a bit later. |
- |
-|
402 | |
- |
-|
403 | Thus it’s a bit confusing that Git has a `mv` command. If you want to rename a file in Git, you can run something like |
- |
-|
404 | |
- |
-|
405 | $ git mv file_from file_to |
- |
-|
406 | |
- |
-|
407 | and it works fine. In fact, if you run something like this and look at the status, you’ll see that Git considers it a renamed file: |
- |
-|
408 | |
- |
-|
409 | $ git mv README README.txt |
- |
-|
410 | $ git status |
- |
-|
411 | On branch master |
- |
-|
412 | Changes to be committed: |
- |
-|
413 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
414 | |
- |
-|
415 | renamed: README -> README.txt |
- |
-|
416 | |
- |
-|
417 | |
- |
-|
418 | However, this is equivalent to running something like this: |
- |
-|
419 | |
- |
-|
420 | $ mv README README.txt |
- |
-|
421 | $ git rm README |
- |
-|
422 | $ git add README.txt |
- |
-|
423 | |
- |
-|
424 | Git figures out that it’s a rename implicitly, so it doesn’t matter if you rename a file that way or with the `mv` command. The only real difference is that `mv` is one command instead of three — it’s a convenience function. More important, you can use any tool you like to rename a file, and address the add/rm later, before you commit. |
- |
-|
425 | |
- |
-|
426 | ## Viewing the Commit History ## |
- |
-|
427 | |
- |
-|
428 | After you have created several commits, or if you have cloned a repository with an existing commit history, you’ll probably want to look back to see what has happened. The most basic and powerful tool to do this is the `git log` command. |
- |
-|
429 | |
- |
-|
430 | These examples use a very simple project called `simplegit` that I often use for demonstrations. To get the project, run |
- |
-|
431 | |
- |
-|
432 | git clone git://github.com/schacon/simplegit-progit.git |
- |
-|
433 | |
- |
-|
434 | When you run `git log` in this project, you should get output that looks something like this: |
- |
-|
435 | |
- |
-|
436 | $ git log |
- |
-|
437 | commit ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
438 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
439 | Date: Mon Mar 17 21:52:11 2008 -0700 |
- |
-|
440 | |
- |
-|
441 | changed the version number |
- |
-|
442 | |
- |
-|
443 | commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 |
- |
-|
444 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
445 | Date: Sat Mar 15 16:40:33 2008 -0700 |
- |
-|
446 | |
- |
-|
447 | removed unnecessary test code |
- |
-|
448 | |
- |
-|
449 | commit a11bef06a3f659402fe7563abf99ad00de2209e6 |
- |
-|
450 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
451 | Date: Sat Mar 15 10:31:28 2008 -0700 |
- |
-|
452 | |
- |
-|
453 | first commit |
- |
-|
454 | |
- |
-|
455 | By default, with no arguments, `git log` lists the commits made in that repository in reverse chronological order. That is, the most recent commits show up first. As you can see, this command lists each commit with its SHA-1 checksum, the author’s name and e-mail, the date written, and the commit message. |
- |
-|
456 | |
- |
-|
457 | A huge number and variety of options to the `git log` command are available to show you exactly what you’re looking for. Here, we’ll show you some of the most-used options. |
- |
-|
458 | |
- |
-|
459 | One of the more helpful options is `-p`, which shows the diff introduced in each commit. You can also use `-2`, which limits the output to only the last two entries: |
- |
-|
460 | |
- |
-|
461 | $ git log -p -2 |
- |
-|
462 | commit ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
463 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
464 | Date: Mon Mar 17 21:52:11 2008 -0700 |
- |
-|
465 | |
- |
-|
466 | changed the version number |
- |
-|
467 | |
- |
-|
468 | diff --git a/Rakefile b/Rakefile |
- |
-|
469 | index a874b73..8f94139 100644 |
- |
-|
470 | --- a/Rakefile |
- |
-|
471 | +++ b/Rakefile |
- |
-|
472 | @@ -5,5 +5,5 @@ require 'rake/gempackagetask' |
- |
-|
473 | spec = Gem::Specification.new do |s| |
- |
-|
474 | s.name = "simplegit" |
- |
-|
475 | - s.version = "0.1.0" |
- |
-|
476 | + s.version = "0.1.1" |
- |
-|
477 | s.author = "Scott Chacon" |
- |
-|
478 | s.email = "schacon@gee-mail.com |
- |
-|
479 | |
- |
-|
480 | commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 |
- |
-|
481 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
482 | Date: Sat Mar 15 16:40:33 2008 -0700 |
- |
-|
483 | |
- |
-|
484 | removed unnecessary test code |
- |
-|
485 | |
- |
-|
486 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
- |
-|
487 | index a0a60ae..47c6340 100644 |
- |
-|
488 | --- a/lib/simplegit.rb |
- |
-|
489 | +++ b/lib/simplegit.rb |
- |
-|
490 | @@ -18,8 +18,3 @@ class SimpleGit |
- |
-|
491 | end |
- |
-|
492 | |
- |
-|
493 | end |
- |
-|
494 | - |
- |
-|
495 | -if $0 == __FILE__ |
- |
-|
496 | - git = SimpleGit.new |
- |
-|
497 | - puts git.show |
- |
-|
498 | -end |
- |
-|
499 | \ No newline at end of file |
- |
-|
500 | |
- |
-|
501 | This option displays the same information but with a diff directly following each entry. This is very helpful for code review or to quickly browse what happened during a series of commits that a collaborator has added. |
- |
-|
502 | |
- |
-|
503 | Sometimes it's easier to review changes on the word level rather than on the line level. There is a `--word-diff` option available in Git, that you can append to the `git log -p` command to get word diff instead of normal line by line diff. Word diff format is quite useless when applied to source code, but it comes in handy when applied to large text files, like books or your dissertation. Here is an example: |
- |
-|
504 | |
- |
-|
505 | $ git log -U1 --word-diff |
- |
-|
506 | commit ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
507 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
508 | Date: Mon Mar 17 21:52:11 2008 -0700 |
- |
-|
509 | |
- |
-|
510 | changed the version number |
- |
-|
511 | |
- |
-|
512 | diff --git a/Rakefile b/Rakefile |
- |
-|
513 | index a874b73..8f94139 100644 |
- |
-|
514 | --- a/Rakefile |
- |
-|
515 | +++ b/Rakefile |
- |
-|
516 | @@ -7,3 +7,3 @@ spec = Gem::Specification.new do |s| |
- |
-|
517 | s.name = "simplegit" |
- |
-|
518 | s.version = [-"0.1.0"-]{+"0.1.1"+} |
- |
-|
519 | s.author = "Scott Chacon" |
- |
-|
520 | |
- |
-|
521 | As you can see, there is no added and removed lines in this output as in a normal diff. Changes are shown inline instead. You can see the added word enclosed in `{+ +}` and removed one enclosed in `[- -]`. You may also want to reduce the usual three lines context in diff output to only one line, as the context is now words, not lines. You can do this with `-U1` as we did in the example above. |
- |
-|
522 | |
- |
-|
523 | You can also use a series of summarizing options with `git log`. For example, if you want to see some abbreviated stats for each commit, you can use the `--stat` option: |
- |
-|
524 | |
- |
-|
525 | $ git log --stat |
- |
-|
526 | commit ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
527 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
528 | Date: Mon Mar 17 21:52:11 2008 -0700 |
- |
-|
529 | |
- |
-|
530 | changed the version number |
- |
-|
531 | |
- |
-|
532 | Rakefile | 2 +- |
- |
-|
533 | 1 file changed, 1 insertion(+), 1 deletion(-) |
- |
-|
534 | |
- |
-|
535 | commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 |
- |
-|
536 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
537 | Date: Sat Mar 15 16:40:33 2008 -0700 |
- |
-|
538 | |
- |
-|
539 | removed unnecessary test code |
- |
-|
540 | |
- |
-|
541 | lib/simplegit.rb | 5 ----- |
- |
-|
542 | 1 file changed, 5 deletions(-) |
- |
-|
543 | |
- |
-|
544 | commit a11bef06a3f659402fe7563abf99ad00de2209e6 |
- |
-|
545 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
546 | Date: Sat Mar 15 10:31:28 2008 -0700 |
- |
-|
547 | |
- |
-|
548 | first commit |
- |
-|
549 | |
- |
-|
550 | README | 6 ++++++ |
- |
-|
551 | Rakefile | 23 +++++++++++++++++++++++ |
- |
-|
552 | lib/simplegit.rb | 25 +++++++++++++++++++++++++ |
- |
-|
553 | 3 files changed, 54 insertions(+) |
- |
-|
554 | |
- |
-|
555 | As you can see, the `--stat` option prints below each commit entry a list of modified files, how many files were changed, and how many lines in those files were added and removed. It also puts a summary of the information at the end. |
- |
-|
556 | Another really useful option is `--pretty`. This option changes the log output to formats other than the default. A few prebuilt options are available for you to use. The `oneline` option prints each commit on a single line, which is useful if you’re looking at a lot of commits. In addition, the `short`, `full`, and `fuller` options show the output in roughly the same format but with less or more information, respectively: |
- |
-|
557 | |
- |
-|
558 | $ git log --pretty=oneline |
- |
-|
559 | ca82a6dff817ec66f44342007202690a93763949 changed the version number |
- |
-|
560 | 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test code |
- |
-|
561 | a11bef06a3f659402fe7563abf99ad00de2209e6 first commit |
- |
-|
562 | |
- |
-|
563 | The most interesting option is `format`, which allows you to specify your own log output format. This is especially useful when you’re generating output for machine parsing — because you specify the format explicitly, you know it won’t change with updates to Git: |
- |
-|
564 | |
- |
-|
565 | $ git log --pretty=format:"%h - %an, %ar : %s" |
- |
-|
566 | ca82a6d - Scott Chacon, 11 months ago : changed the version number |
- |
-|
567 | 085bb3b - Scott Chacon, 11 months ago : removed unnecessary test code |
- |
-|
568 | a11bef0 - Scott Chacon, 11 months ago : first commit |
- |
-|
569 | |
- |
-|
570 | Table 2-1 lists some of the more useful options that format takes. |
- |
-|
571 | |
- |
-|
572 | <!-- Attention to translators: this is a table declaration. |
- |
-|
573 | The lines must be formatted as follows |
- |
-|
574 | <TAB><First column text><TAB><Second column text> |
- |
-|
575 | --> |
- |
-|
576 | |
- |
-|
577 | Option Description of Output |
- |
-|
578 | %H Commit hash |
- |
-|
579 | %h Abbreviated commit hash |
- |
-|
580 | %T Tree hash |
- |
-|
581 | %t Abbreviated tree hash |
- |
-|
582 | %P Parent hashes |
- |
-|
583 | %p Abbreviated parent hashes |
- |
-|
584 | %an Author name |
- |
-|
585 | %ae Author e-mail |
- |
-|
586 | %ad Author date (format respects the --date= option) |
- |
-|
587 | %ar Author date, relative |
- |
-|
588 | %cn Committer name |
- |
-|
589 | %ce Committer email |
- |
-|
590 | %cd Committer date |
- |
-|
591 | %cr Committer date, relative |
- |
-|
592 | %s Subject |
- |
-|
593 | |
- |
-|
594 | You may be wondering what the difference is between _author_ and _committer_. The _author_ is the person who originally wrote the patch, whereas the _committer_ is the person who last applied the patch. So, if you send in a patch to a project and one of the core members applies the patch, both of you get credit — you as the author and the core member as the committer. We’ll cover this distinction a bit more in *Chapter 5*. |
- |
-|
595 | |
- |
-|
596 | The `oneline` and `format` options are particularly useful with another `log` option called `--graph`. This option adds a nice little ASCII graph showing your branch and merge history, which we can see in our copy of the Grit project repository: |
- |
-|
597 | |
- |
-|
598 | $ git log --pretty=format:"%h %s" --graph |
- |
-|
599 | * 2d3acf9 ignore errors from SIGCHLD on trap |
- |
-|
600 | * 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit |
- |
-|
601 | |\ |
- |
-|
602 | | * 420eac9 Added a method for getting the current branch. |
- |
-|
603 | * | 30e367c timeout code and tests |
- |
-|
604 | * | 5a09431 add timeout protection to grit |
- |
-|
605 | * | e1193f8 support for heads with slashes in them |
- |
-|
606 | |/ |
- |
-|
607 | * d6016bc require time for xmlschema |
- |
-|
608 | * 11d191e Merge branch 'defunkt' into local |
- |
-|
609 | |
- |
-|
610 | Those are only some simple output-formatting options to `git log` — there are many more. Table 2-2 lists the options we’ve covered so far and some other common formatting options that may be useful, along with how they change the output of the `log` command. |
- |
-|
611 | |
- |
-|
612 | <!-- Attention to translators: this is a table declaration. |
- |
-|
613 | The lines must be formatted as follows |
- |
-|
614 | <TAB><First column text><TAB><Second column text> |
- |
-|
615 | --> |
- |
-|
616 | |
- |
-|
617 | Option Description |
- |
-|
618 | -p Show the patch introduced with each commit. |
- |
-|
619 | --word-diff Show the patch in a word diff format. |
- |
-|
620 | --stat Show statistics for files modified in each commit. |
- |
-|
621 | --shortstat Display only the changed/insertions/deletions line from the --stat command. |
- |
-|
622 | --name-only Show the list of files modified after the commit information. |
- |
-|
623 | --name-status Show the list of files affected with added/modified/deleted information as well. |
- |
-|
624 | --abbrev-commit Show only the first few characters of the SHA-1 checksum instead of all 40. |
- |
-|
625 | --relative-date Display the date in a relative format (for example, “2 weeks ago”) instead of using the full date format. |
- |
-|
626 | --graph Display an ASCII graph of the branch and merge history beside the log output. |
- |
-|
627 | --pretty Show commits in an alternate format. Options include oneline, short, full, fuller, and format (where you specify your own format). |
- |
-|
628 | --oneline A convenience option short for `--pretty=oneline --abbrev-commit`. |
- |
-|
629 | |
- |
-|
630 | ### Limiting Log Output ### |
- |
-|
631 | |
- |
-|
632 | In addition to output-formatting options, `git log` takes a number of useful limiting options — that is, options that let you show only a subset of commits. You’ve seen one such option already — the `-2` option, which shows only the last two commits. In fact, you can do `-<n>`, where `n` is any integer to show the last `n` commits. In reality, you’re unlikely to use that often, because Git by default pipes all output through a pager so you see only one page of log output at a time. |
- |
-|
633 | |
- |
-|
634 | However, the time-limiting options such as `--since` and `--until` are very useful. For example, this command gets the list of commits made in the last two weeks: |
- |
-|
635 | |
- |
-|
636 | $ git log --since=2.weeks |
- |
-|
637 | |
- |
-|
638 | This command works with lots of formats — you can specify a specific date (“2008-01-15”) or a relative date such as “2 years 1 day 3 minutes ago”. |
- |
-|
639 | |
- |
-|
640 | You can also filter the list to commits that match some search criteria. The `--author` option allows you to filter on a specific author, and the `--grep` option lets you search for keywords in the commit messages. (Note that if you specify both author and grep options, the command will match commits with both.) |
- |
-|
641 | |
- |
-|
642 | If you want to specify multiple grep options, you have to add `--all-match` or the command will match commits with either. |
- |
-|
643 | |
- |
-|
644 | The last really useful option to pass to `git log` as a filter is a path. If you specify a directory or file name, you can limit the log output to commits that introduced a change to those files. This is always the last option and is generally preceded by double dashes (`--`) to separate the paths from the options. |
- |
-|
645 | |
- |
-|
646 | In Table 2-3 we’ll list these and a few other common options for your reference. |
- |
-|
647 | |
- |
-|
648 | <!-- Attention to translators: this is a table declaration. |
- |
-|
649 | The lines must be formatted as follows |
- |
-|
650 | <TAB><First column text><TAB><Second column text> |
- |
-|
651 | --> |
- |
-|
652 | |
- |
-|
653 | Option Description |
- |
-|
654 | -(n) Show only the last n commits |
- |
-|
655 | --since, --after Limit the commits to those whose CommitDate was made on-or-after the specified date/time. |
- |
-|
656 | --until, --before Limit the commits to those whose CommitDate was made on-or-before the specified date/time. |
- |
-|
657 | --author Only show commits in which the author entry matches the specified string. |
- |
-|
658 | --committer Only show commits in which the committer entry matches the specified string. |
- |
-|
659 | |
- |
-|
660 | |
- |
-|
661 | ### Limiting Log Output according to Date/Time ### |
- |
-|
662 | |
- |
-|
663 | To determine which commits in the Git source code repository (git://git.kernel.org/pub/scm/git/git.git) have CommitDate on 2014-04-29 relative to your local timezone (as set on your computer), use |
- |
-|
664 | |
- |
-|
665 | $ git log --after="2014-04-29 00:00:00" --before="2014-04-29 23:59:59" \ |
- |
-|
666 | --pretty=fuller |
- |
-|
667 | |
- |
-|
668 | As the output will be different according to the timezone where it will be run, it's recommended to always use an absolute time such as ISO 8601 format (which includes timezone information) as argument to `--after` and `--before`, so that everone running the command will get the same repeatable results. |
- |
-|
669 | |
- |
-|
670 | To obtain commits made at a specific instant in time (e.g. 29 April 2013 at 17:07:22 CET), we can use |
- |
-|
671 | |
- |
-|
672 | $ git log --after="2013-04-29T17:07:22+0200" \ |
- |
-|
673 | --before="2013-04-29T17:07:22+0200" --pretty=fuller |
- |
-|
674 | |
- |
-|
675 | commit de7c201a10857e5d424dbd8db880a6f24ba250f9 |
- |
-|
676 | Author: Ramkumar Ramachandra <artagnon@gmail.com> |
- |
-|
677 | AuthorDate: Mon Apr 29 18:19:37 2013 +0530 |
- |
-|
678 | Commit: Junio C Hamano <gitster@pobox.com> |
- |
-|
679 | CommitDate: Mon Apr 29 08:07:22 2013 -0700 |
- |
-|
680 | |
- |
-|
681 | git-completion.bash: lexical sorting for diff.statGraphWidth |
- |
-|
682 | |
- |
-|
683 | df44483a (diff --stat: add config option to limit graph width, |
- |
-|
684 | 2012-03-01) added the option diff.startGraphWidth to the list of |
- |
-|
685 | configuration variables in git-completion.bash, but failed to notice |
- |
-|
686 | that the list is sorted alphabetically. Move it to its rightful place |
- |
-|
687 | in the list. |
- |
-|
688 | |
- |
-|
689 | Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com> |
- |
-|
690 | Signed-off-by: Junio C Hamano <gitster@pobox.com> |
- |
-|
691 | |
- |
-|
692 | The above times (`AuthorDate`, `CommitDate`) are displayed in default format (`--date=default`), which shows timezone information of respective author and commiter. |
- |
-|
693 | |
- |
-|
694 | Other useful formats include `--date=iso` (ISO 8601), `--date=rfc` (RFC 2822), `--date=raw` (seconds since the epoch (1970-01-01 UTC)) `--date=local` (times according to your local timezone) as well as `--date=relative` (e.g. "2 hours ago"). |
- |
-|
695 | |
- |
-|
696 | When using `git log` without specifying time, the time defaults to the time at which the command is run on your computer (keeping the identical offset from UTC). |
- |
-|
697 | |
- |
-|
698 | For example, running a `git log` at 09:00 on your computer with your timezone currently 3 hours ahead of UTC, makes the following two commands equivalent: |
- |
-|
699 | |
- |
-|
700 | $ git log --after=2008-06-01 --before=2008-07-01 |
- |
-|
701 | $ git log --after="2008-06-01T09:00:00+0300" \ |
- |
-|
702 | --before="2008-07-01T09:00:00+0300" |
- |
-|
703 | |
- |
-|
704 | As a final example, if you want to see which commits modifying test files in the Git source code history were committed by Junio Hamano with CommitDate being in the month of October 2008 (relative to the timezone of New York) and were not merges, you can run something like this: |
- |
-|
705 | |
- |
-|
706 | $ git log --pretty="%h - %s" --author=gitster \ |
- |
-|
707 | --after="2008-10-01T00:00:00-0400" \ |
- |
-|
708 | --before="2008-10-31T23:59:59-0400" --no-merges -- t/ |
- |
-|
709 | 5610e3b - Fix testcase failure when extended attribute |
- |
-|
710 | acd3b9e - Enhance hold_lock_file_for_{update,append}() |
- |
-|
711 | f563754 - demonstrate breakage of detached checkout wi |
- |
-|
712 | d1a43f2 - reset --hard/read-tree --reset -u: remove un |
- |
-|
713 | 51a94af - Fix "checkout --track -b newbranch" on detac |
- |
-|
714 | b0ad11e - pull: allow "git pull origin $something:$cur |
- |
-|
715 | |
- |
-|
716 | Of the more than 36,000 commits in the Git source code history, this command shows the 6 that match those criteria. |
- |
-|
717 | |
- |
-|
718 | ### Using a GUI to Visualize History ### |
- |
-|
719 | |
- |
-|
720 | If you like to use a more graphical tool to visualize your commit history, you may want to take a look at a Tcl/Tk program called `gitk` that is distributed with Git. Gitk is basically a visual `git log` tool, and it accepts nearly all the filtering options that `git log` does. If you type `gitk` on the command line in your project, you should see something like Figure 2-2. |
- |
-|
721 | |
- |
-|
722 | Insert 18333fig0202.png |
- |
-|
723 | Figure 2-2. The gitk history visualizer. |
- |
-|
724 | |
- |
-|
725 | You can see the commit history in the top half of the window along with a nice ancestry graph. The diff viewer in the bottom half of the window shows you the changes introduced at any commit you click. |
- |
-|
726 | |
- |
-|
727 | ## Undoing Things ## |
- |
-|
728 | |
- |
-|
729 | At any stage, you may want to undo something. Here, we’ll review a few basic tools for undoing changes that you’ve made. Be careful, because you can’t always revert some of these undos. This is one of the few areas in Git where you may lose some work if you do it wrong. |
- |
-|
730 | |
- |
-|
731 | ### Changing Your Last Commit ### |
- |
-|
732 | |
- |
-|
733 | One of the common undos takes place when you commit too early and possibly forget to add some files, or you mess up your commit message. If you want to try that commit again, you can run commit with the `--amend` option: |
- |
-|
734 | |
- |
-|
735 | $ git commit --amend |
- |
-|
736 | |
- |
-|
737 | This command takes your staging area and uses it for the commit. If you’ve made no changes since your last commit (for instance, you run this command immediately after your previous commit), then your snapshot will look exactly the same and all you’ll change is your commit message. |
- |
-|
738 | |
- |
-|
739 | The same commit-message editor fires up, but it already contains the message of your previous commit. You can edit the message the same as always, but it overwrites your previous commit. |
- |
-|
740 | |
- |
-|
741 | As an example, if you commit and then realize you forgot to stage the changes in a file you wanted to add to this commit, you can do something like this: |
- |
-|
742 | |
- |
-|
743 | $ git commit -m 'initial commit' |
- |
-|
744 | $ git add forgotten_file |
- |
-|
745 | $ git commit --amend |
- |
-|
746 | |
- |
-|
747 | After these three commands, you end up with a single commit — the second commit replaces the results of the first. |
- |
-|
748 | |
- |
-|
749 | ### Unstaging a Staged File ### |
- |
-|
750 | |
- |
-|
751 | The next two sections demonstrate how to wrangle your staging area and working directory changes. The nice part is that the command you use to determine the state of those two areas also reminds you how to undo changes to them. For example, let’s say you’ve changed two files and want to commit them as two separate changes, but you accidentally type `git add *` and stage them both. How can you unstage one of the two? The `git status` command reminds you: |
- |
-|
752 | |
- |
-|
753 | $ git add . |
- |
-|
754 | $ git status |
- |
-|
755 | On branch master |
- |
-|
756 | Changes to be committed: |
- |
-|
757 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
758 | |
- |
-|
759 | modified: README.txt |
- |
-|
760 | modified: benchmarks.rb |
- |
-|
761 | |
- |
-|
762 | |
- |
-|
763 | Right below the “Changes to be committed” text, it says "use `git reset HEAD <file>...` to unstage". So, let’s use that advice to unstage the `benchmarks.rb` file: |
- |
-|
764 | |
- |
-|
765 | $ git reset HEAD benchmarks.rb |
- |
-|
766 | Unstaged changes after reset: |
- |
-|
767 | M benchmarks.rb |
- |
-|
768 | $ git status |
- |
-|
769 | On branch master |
- |
-|
770 | Changes to be committed: |
- |
-|
771 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
772 | |
- |
-|
773 | modified: README.txt |
- |
-|
774 | |
- |
-|
775 | Changes not staged for commit: |
- |
-|
776 | (use "git add <file>..." to update what will be committed) |
- |
-|
777 | (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
778 | |
- |
-|
779 | modified: benchmarks.rb |
- |
-|
780 | |
- |
-|
781 | |
- |
-|
782 | The command is a bit strange, but it works. The `benchmarks.rb` file is modified but once again unstaged. |
- |
-|
783 | |
- |
-|
784 | ### Unmodifying a Modified File ### |
- |
-|
785 | |
- |
-|
786 | What if you realize that you don’t want to keep your changes to the `benchmarks.rb` file? How can you easily unmodify it — revert it back to what it looked like when you last committed (or initially cloned, or however you got it into your working directory)? Luckily, `git status` tells you how to do that, too. In the last example output, the unstaged area looks like this: |
- |
-|
787 | |
- |
-|
788 | Changes not staged for commit: |
- |
-|
789 | (use "git add <file>..." to update what will be committed) |
- |
-|
790 | (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
791 | |
- |
-|
792 | modified: benchmarks.rb |
- |
-|
793 | |
- |
-|
794 | |
- |
-|
795 | It tells you pretty explicitly how to discard the changes you’ve made (at least, the newer versions of Git, 1.6.1 and later, do this — if you have an older version, we highly recommend upgrading it to get some of these nicer usability features). Let’s do what it says: |
- |
-|
796 | |
- |
-|
797 | $ git checkout -- benchmarks.rb |
- |
-|
798 | $ git status |
- |
-|
799 | On branch master |
- |
-|
800 | Changes to be committed: |
- |
-|
801 | (use "git reset HEAD <file>..." to unstage) |
- |
-|
802 | |
- |
-|
803 | modified: README.txt |
- |
-|
804 | |
- |
-|
805 | |
- |
-|
806 | You can see that the changes have been reverted. You should also realize that this is a dangerous command: any changes you made to that file are gone — you just copied another file over it. Don’t ever use this command unless you absolutely know that you don’t want the file. If you just need to get it out of the way, we’ll go over stashing and branching in the next chapter; these are generally better ways to go. |
- |
-|
807 | |
- |
-|
808 | Remember, anything that is committed in Git can almost always be recovered. Even commits that were on branches that were deleted or commits that were overwritten with an `--amend` commit can be recovered (see *Chapter 9* for data recovery). However, anything you lose that was never committed is likely never to be seen again. |
- |
-|
809 | |
- |
-|
810 | ## Working with Remotes ## |
- |
-|
811 | |
- |
-|
812 | To be able to collaborate on any Git project, you need to know how to manage your remote repositories. Remote repositories are versions of your project that are hosted on the Internet or network somewhere. You can have several of them, each of which generally is either read-only or read/write for you. Collaborating with others involves managing these remote repositories and pushing and pulling data to and from them when you need to share work. |
- |
-|
813 | Managing remote repositories includes knowing how to add remote repositories, remove remotes that are no longer valid, manage various remote branches and define them as being tracked or not, and more. In this section, we’ll cover these remote-management skills. |
- |
-|
814 | |
- |
-|
815 | ### Showing Your Remotes ### |
- |
-|
816 | |
- |
-|
817 | To see which remote servers you have configured, you can run the `git remote` command. It lists the shortnames of each remote handle you’ve specified. If you’ve cloned your repository, you should at least see *origin* — that is the default name Git gives to the server you cloned from: |
- |
-|
818 | |
- |
-|
819 | $ git clone git://github.com/schacon/ticgit.git |
- |
-|
820 | Cloning into 'ticgit'... |
- |
-|
821 | remote: Reusing existing pack: 1857, done. |
- |
-|
822 | remote: Total 1857 (delta 0), reused 0 (delta 0) |
- |
-|
823 | Receiving objects: 100% (1857/1857), 374.35 KiB | 193.00 KiB/s, done. |
- |
-|
824 | Resolving deltas: 100% (772/772), done. |
- |
-|
825 | Checking connectivity... done. |
- |
-|
826 | $ cd ticgit |
- |
-|
827 | $ git remote |
- |
-|
828 | origin |
- |
-|
829 | |
- |
-|
830 | You can also specify `-v`, which shows you the URL that Git has stored for the shortname to be expanded to: |
- |
-|
831 | |
- |
-|
832 | $ git remote -v |
- |
-|
833 | origin git://github.com/schacon/ticgit.git (fetch) |
- |
-|
834 | origin git://github.com/schacon/ticgit.git (push) |
- |
-|
835 | |
- |
-|
836 | If you have more than one remote, the command lists them all. For example, my Grit repository looks something like this. |
- |
-|
837 | |
- |
-|
838 | $ cd grit |
- |
-|
839 | $ git remote -v |
- |
-|
840 | bakkdoor git://github.com/bakkdoor/grit.git |
- |
-|
841 | cho45 git://github.com/cho45/grit.git |
- |
-|
842 | defunkt git://github.com/defunkt/grit.git |
- |
-|
843 | koke git://github.com/koke/grit.git |
- |
-|
844 | origin git@github.com:mojombo/grit.git |
- |
-|
845 | |
- |
-|
846 | This means I can pull contributions from any of these users pretty easily. But notice that only the origin remote is an SSH URL, so it’s the only one I can push to (we’ll cover why this is in *Chapter 4*). |
- |
-|
847 | |
- |
-|
848 | ### Adding Remote Repositories ### |
- |
-|
849 | |
- |
-|
850 | I’ve mentioned and given some demonstrations of adding remote repositories in previous sections, but here is how to do it explicitly. To add a new remote Git repository as a shortname you can reference easily, run `git remote add [shortname] [url]`: |
- |
-|
851 | |
- |
-|
852 | $ git remote |
- |
-|
853 | origin |
- |
-|
854 | $ git remote add pb git://github.com/paulboone/ticgit.git |
- |
-|
855 | $ git remote -v |
- |
-|
856 | origin git://github.com/schacon/ticgit.git |
- |
-|
857 | pb git://github.com/paulboone/ticgit.git |
- |
-|
858 | |
- |
-|
859 | Now you can use the string `pb` on the command line in lieu of the whole URL. For example, if you want to fetch all the information that Paul has but that you don’t yet have in your repository, you can run `git fetch pb`: |
- |
-|
860 | |
- |
-|
861 | $ git fetch pb |
- |
-|
862 | remote: Counting objects: 58, done. |
- |
-|
863 | remote: Compressing objects: 100% (41/41), done. |
- |
-|
864 | remote: Total 44 (delta 24), reused 1 (delta 0) |
- |
-|
865 | Unpacking objects: 100% (44/44), done. |
- |
-|
866 | From git://github.com/paulboone/ticgit |
- |
-|
867 | * [new branch] master -> pb/master |
- |
-|
868 | * [new branch] ticgit -> pb/ticgit |
- |
-|
869 | |
- |
-|
870 | Paul’s master branch is accessible locally as `pb/master` — you can merge it into one of your branches, or you can check out a local branch at that point if you want to inspect it. |
- |
-|
871 | |
- |
-|
872 | ### Fetching and Pulling from Your Remotes ### |
- |
-|
873 | |
- |
-|
874 | As you just saw, to get data from your remote projects, you can run: |
- |
-|
875 | |
- |
-|
876 | $ git fetch [remote-name] |
- |
-|
877 | |
- |
-|
878 | The command goes out to that remote project and pulls down all the data from that remote project that you don’t have yet. After you do this, you should have references to all the branches from that remote, which you can merge in or inspect at any time. (We’ll go over what branches are and how to use them in much more detail in *Chapter 3*.) |
- |
-|
879 | |
- |
-|
880 | If you clone a repository, the command automatically adds that remote repository under the name *origin*. So, `git fetch origin` fetches any new work that has been pushed to that server since you cloned (or last fetched from) it. It’s important to note that the `fetch` command pulls the data to your local repository — it doesn’t automatically merge it with any of your work or modify what you’re currently working on. You have to merge it manually into your work when you’re ready. |
- |
-|
881 | |
- |
-|
882 | If you have a branch set up to track a remote branch (see the next section and *Chapter 3* for more information), you can use the `git pull` command to automatically fetch and then merge a remote branch into your current branch. This may be an easier or more comfortable workflow for you; and by default, the `git clone` command automatically sets up your local master branch to track the remote master branch on the server you cloned from (assuming the remote has a master branch). Running `git pull` generally fetches data from the server you originally cloned from and automatically tries to merge it into the code you’re currently working on. |
- |
-|
883 | |
- |
-|
884 | ### Pushing to Your Remotes ### |
- |
-|
885 | |
- |
-|
886 | When you have your project at a point that you want to share, you have to push it upstream. The command for this is simple: `git push [remote-name] [branch-name]`. If you want to push your master branch to your `origin` server (again, cloning generally sets up both of those names for you automatically), then you can run this to push your work back up to the server: |
- |
-|
887 | |
- |
-|
888 | $ git push origin master |
- |
-|
889 | |
- |
-|
890 | This command works only if you cloned from a server to which you have write access and if nobody has pushed in the meantime. If you and someone else clone at the same time and they push upstream and then you push upstream, your push will rightly be rejected. You’ll have to pull down their work first and incorporate it into yours before you’ll be allowed to push. See *Chapter 3* for more detailed information on how to push to remote servers. |
- |
-|
891 | |
- |
-|
892 | ### Inspecting a Remote ### |
- |
-|
893 | |
- |
-|
894 | If you want to see more information about a particular remote, you can use the `git remote show [remote-name]` command. If you run this command with a particular shortname, such as `origin`, you get something like this: |
- |
-|
895 | |
- |
-|
896 | $ git remote show origin |
- |
-|
897 | * remote origin |
- |
-|
898 | URL: git://github.com/schacon/ticgit.git |
- |
-|
899 | Remote branch merged with 'git pull' while on branch master |
- |
-|
900 | master |
- |
-|
901 | Tracked remote branches |
- |
-|
902 | master |
- |
-|
903 | ticgit |
- |
-|
904 | |
- |
-|
905 | It lists the URL for the remote repository as well as the tracking branch information. The command helpfully tells you that if you’re on the master branch and you run `git pull`, it will automatically merge in the master branch on the remote after it fetches all the remote references. It also lists all the remote references it has pulled down. |
- |
-|
906 | |
- |
-|
907 | That is a simple example you’re likely to encounter. When you’re using Git more heavily, however, you may see much more information from `git remote show`: |
- |
-|
908 | |
- |
-|
909 | $ git remote show origin |
- |
-|
910 | * remote origin |
- |
-|
911 | URL: git@github.com:defunkt/github.git |
- |
-|
912 | Remote branch merged with 'git pull' while on branch issues |
- |
-|
913 | issues |
- |
-|
914 | Remote branch merged with 'git pull' while on branch master |
- |
-|
915 | master |
- |
-|
916 | New remote branches (next fetch will store in remotes/origin) |
- |
-|
917 | caching |
- |
-|
918 | Stale tracking branches (use 'git remote prune') |
- |
-|
919 | libwalker |
- |
-|
920 | walker2 |
- |
-|
921 | Tracked remote branches |
- |
-|
922 | acl |
- |
-|
923 | apiv2 |
- |
-|
924 | dashboard2 |
- |
-|
925 | issues |
- |
-|
926 | master |
- |
-|
927 | postgres |
- |
-|
928 | Local branch pushed with 'git push' |
- |
-|
929 | master:master |
- |
-|
930 | |
- |
-|
931 | This command shows which branch is automatically pushed when you run `git push` on certain branches. It also shows you which remote branches on the server you don’t yet have, which remote branches you have that have been removed from the server, and multiple branches that are automatically merged when you run `git pull`. |
- |
-|
932 | |
- |
-|
933 | ### Removing and Renaming Remotes ### |
- |
-|
934 | |
- |
-|
935 | If you want to rename a reference, in newer versions of Git you can run `git remote rename` to change a remote’s shortname. For instance, if you want to rename `pb` to `paul`, you can do so with `git remote rename`: |
- |
-|
936 | |
- |
-|
937 | $ git remote rename pb paul |
- |
-|
938 | $ git remote |
- |
-|
939 | origin |
- |
-|
940 | paul |
- |
-|
941 | |
- |
-|
942 | It’s worth mentioning that this changes your remote branch names, too. What used to be referenced at `pb/master` is now at `paul/master`. |
- |
-|
943 | |
- |
-|
944 | If you want to remove a reference for some reason — you’ve moved the server or are no longer using a particular mirror, or perhaps a contributor isn’t contributing anymore — you can use `git remote rm`: |
- |
-|
945 | |
- |
-|
946 | $ git remote rm paul |
- |
-|
947 | $ git remote |
- |
-|
948 | origin |
- |
-|
949 | |
- |
-|
950 | ## Tagging ## |
- |
-|
951 | |
- |
-|
952 | Like most VCSs, Git has the ability to tag specific points in history as being important. Generally, people use this functionality to mark release points (`v1.0`, and so on). In this section, you’ll learn how to list the available tags, how to create new tags, and what the different types of tags are. |
- |
-|
953 | |
- |
-|
954 | ### Listing Your Tags ### |
- |
-|
955 | |
- |
-|
956 | Listing the available tags in Git is straightforward. Just type `git tag`: |
- |
-|
957 | |
- |
-|
958 | $ git tag |
- |
-|
959 | v0.1 |
- |
-|
960 | v1.3 |
- |
-|
961 | |
- |
-|
962 | This command lists the tags in alphabetical order; the order in which they appear has no real importance. |
- |
-|
963 | |
- |
-|
964 | You can also search for tags with a particular pattern. The Git source repo, for instance, contains more than 240 tags. If you’re only interested in looking at the 1.4.2 series, you can run this: |
- |
-|
965 | |
- |
-|
966 | $ git tag -l 'v1.4.2.*' |
- |
-|
967 | v1.4.2.1 |
- |
-|
968 | v1.4.2.2 |
- |
-|
969 | v1.4.2.3 |
- |
-|
970 | v1.4.2.4 |
- |
-|
971 | |
- |
-|
972 | ### Creating Tags ### |
- |
-|
973 | |
- |
-|
974 | Git uses two main types of tags: lightweight and annotated. A lightweight tag is very much like a branch that doesn’t change — it’s just a pointer to a specific commit. Annotated tags, however, are stored as full objects in the Git database. They’re checksummed; contain the tagger name, e-mail, and date; have a tagging message; and can be signed and verified with GNU Privacy Guard (GPG). It’s generally recommended that you create annotated tags so you can have all this information; but if you want a temporary tag or for some reason don’t want to keep the other information, lightweight tags are available too. |
- |
-|
975 | |
- |
-|
976 | ### Annotated Tags ### |
- |
-|
977 | |
- |
-|
978 | Creating an annotated tag in Git is simple. The easiest way is to specify `-a` when you run the `tag` command: |
- |
-|
979 | |
- |
-|
980 | $ git tag -a v1.4 -m 'my version 1.4' |
- |
-|
981 | $ git tag |
- |
-|
982 | v0.1 |
- |
-|
983 | v1.3 |
- |
-|
984 | v1.4 |
- |
-|
985 | |
- |
-|
986 | The `-m` specifies a tagging message, which is stored with the tag. If you don’t specify a message for an annotated tag, Git launches your editor so you can type it in. |
- |
-|
987 | |
- |
-|
988 | You can see the tag data along with the commit that was tagged by using the `git show` command: |
- |
-|
989 | |
- |
-|
990 | $ git show v1.4 |
- |
-|
991 | tag v1.4 |
- |
-|
992 | Tagger: Scott Chacon <schacon@gee-mail.com> |
- |
-|
993 | Date: Mon Feb 9 14:45:11 2009 -0800 |
- |
-|
994 | |
- |
-|
995 | my version 1.4 |
- |
-|
996 | |
- |
-|
997 | commit 15027957951b64cf874c3557a0f3547bd83b3ff6 |
- |
-|
998 | Merge: 4a447f7... a6b4c97... |
- |
-|
999 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
1000 | Date: Sun Feb 8 19:02:46 2009 -0800 |
- |
-|
1001 | |
- |
-|
1002 | Merge branch 'experiment' |
- |
-|
1003 | |
- |
-|
1004 | That shows the tagger information, the date the commit was tagged, and the annotation message before showing the commit information. |
- |
-|
1005 | |
-35 | |
-
.1006 | ### Signed Tags ### |
-36 | ==== Signing Tags |
-
1007 | |
-37 | |
-
.1008 | You can also sign your tags with GPG, assuming you have a private key. All you have to do is use `-s` instead of `-a`: |
-38 | If you have a GPG private key setup, you can now use it to sign new tags. All you have to do is use `-s` instead of `-a`: |
-
1009 | |
-39 | |
-
. | |
-40 | [source,console] |
-
|
-41 | ---- |
-|
1010 | $ git tag -s v1.5 -m 'my signed 1.5 tag' |
-42 | $ git tag -s v1.5 -m 'my signed 1.5 tag' |
-
|
-43 | |
-|
1011 | You need a passphrase to unlock the secret key for |
-44 | You need a passphrase to unlock the secret key for |
-
1012 | user: "Scott Chacon <schacon@gee-mail.com>" |
-45 | user: "Ben Straub <ben@straub.cc>" |
-
1013 | 1024-bit DSA key, ID F721C45A, created 2009-02-09 |
-46 | 2048-bit RSA key, ID 800430EB, created 2014-05-04 |
-
|
-47 | ---- |
-|
1014 | |
-48 | |
-
1015 | If you run `git show` on that tag, you can see your GPG signature attached to it: |
-49 | If you run `git show` on that tag, you can see your GPG signature attached to it: |
-
1016 | |
-50 | |
-
. | |
-51 | [source,console] |
-
|
-52 | -------- |
-|
1017 | $ git show v1.5 |
-53 | $ git show v1.5 |
-
1018 | tag v1.5 |
-54 | tag v1.5 |
-
1019 | Tagger: Scott Chacon <schacon@gee-mail.com> |
-55 | Tagger: Ben Straub <ben@straub.cc> |
-
1020 | Date: Mon Feb 9 15:22:20 2009 -0800 |
-56 | Date: Sat May 3 20:29:41 2014 -0700 |
-
1021 | |
-57 | |
-
.1022 | my signed 1.5 tag |
-58 | my signed 1.5 tag |
-
1023 | -----BEGIN PGP SIGNATURE----- |
-59 | -----BEGIN PGP SIGNATURE----- |
-
1024 | Version: GnuPG v1.4.8 (Darwin) |
-60 | Version: GnuPG v1 |
-
1025 | |
-61 | |
-
.1026 | iEYEABECAAYFAkmQurIACgkQON3DxfchxFr5cACeIMN+ZxLKggJQf0QYi |
-62 | iQEcBAABAgAGBQJTZbQlAAoJEF0+sviABDDrZbQH/09PfE51KPVPlanr6 |
-
1027 | Ki0An2JeAVUCAiJ7Ox6ZEtK+NvZAj82/ |
-63 | LQxfojUWiLQdg2ESJItkcuweYg+kc3HCyFejeDIBw9dpXt00rY26p05qr |
-
|
-64 | hM1/PswpPLuBSr+oCIDj5GMC2r2iEKsfv2fJbNW8iWAXVLoWZRF8B0Mfq |
-|
|
-65 | ecorc4iXzQu7tupRihslbNkfvfciMnSDeSvzCpWAHl7h8Wj6hhqePmLm9 |
-|
|
-66 | 8S5B/1SSQuEAjRZgI4IexpZoeKGVDptPHxLLS38fozsyi0QyDyzEgJxcJ |
-|
|
-67 | RUysgqjcpT8+iQM1PblGfHR4XAhuOqN5Fx06PSaFZhqvWFezJ28/CLyX5 |
-|
1028 | =WryJ |
-68 | =EFTF |
-
1029 | -----END PGP SIGNATURE----- |
-69 | -----END PGP SIGNATURE----- |
-
|
-70 | |
-|
1030 | commit 15027957951b64cf874c3557a0f3547bd83b3ff6 |
-71 | commit ca82a6dff817ec66f44342007202690a93763949 |
-
1031 | Merge: 4a447f7... a6b4c97... |
- |
-|
1032 | Author: Scott Chacon <schacon@gee-mail.com> |
-72 | Author: Scott Chacon <schacon@gee-mail.com> |
-
1033 | Date: Sun Feb 8 19:02:46 2009 -0800 |
-73 | Date: Mon Mar 17 21:52:11 2008 -0700 |
-
1034 | |
-74 | |
-
.1035 | Merge branch 'experiment' |
-75 | changed the version number |
-
1036 | |
-76 | -------- |
-
1037 | A bit later, you’ll learn how to verify signed tags. |
- |
-|
1038 | |
- |
-|
1039 | ### Lightweight Tags ### |
- |
-|
1040 | |
- |
-|
1041 | Another way to tag commits is with a lightweight tag. This is basically the commit checksum stored in a file — no other information is kept. To create a lightweight tag, don’t supply the `-a`, `-s`, or `-m` option: |
- |
-|
1042 | |
- |
-|
1043 | $ git tag v1.4-lw |
- |
-|
1044 | $ git tag |
- |
-|
1045 | v0.1 |
- |
-|
1046 | v1.3 |
- |
-|
1047 | v1.4 |
- |
-|
1048 | v1.4-lw |
- |
-|
1049 | v1.5 |
- |
-|
1050 | |
- |
-|
1051 | This time, if you run `git show` on the tag, you don’t see the extra tag information. The command just shows the commit: |
- |
-|
1052 | |
- |
-|
1053 | $ git show v1.4-lw |
- |
-|
1054 | commit 15027957951b64cf874c3557a0f3547bd83b3ff6 |
- |
-|
1055 | Merge: 4a447f7... a6b4c97... |
- |
-|
1056 | Author: Scott Chacon <schacon@gee-mail.com> |
- |
-|
1057 | Date: Sun Feb 8 19:02:46 2009 -0800 |
- |
-|
1058 | |
- |
-|
1059 | Merge branch 'experiment' |
- |
-|
1060 | |
-77 | |
-
.1061 | ### Verifying Tags ### |
-78 | ==== Verifying Tags |
-
1062 | |
-79 | |
-
1063 | To verify a signed tag, you use `git tag -v [tag-name]`. This command uses GPG to verify the signature. You need the signer’s public key in your keyring for this to work properly: |
-80 | To verify a signed tag, you use `git tag -v [tag-name]`. This command uses GPG to verify the signature. You need the signer’s public key in your keyring for this to work properly: |
-
1064 | |
-81 | |
-
. | |
-82 | [source,console] |
-
|
-83 | ---- |
-|
1065 | $ git tag -v v1.4.2.1 |
-84 | $ git tag -v v1.4.2.1 |
-
1066 | object 883653babd8ee7ea23e6a5c392bb739348b1eb61 |
-85 | object 883653babd8ee7ea23e6a5c392bb739348b1eb61 |
-
1067 | type commit |
-86 | type commit |
-
1068 | tag v1.4.2.1 |
-87 | tag v1.4.2.1 |
-
1069 | tagger Junio C Hamano <junkio@cox.net> 1158138501 -0700 |
-88 | tagger Junio C Hamano <junkio@cox.net> 1158138501 -0700 |
-
1070 | |
-89 | |
-
.1071 | GIT 1.4.2.1 |
-90 | GIT 1.4.2.1 |
-
1072 | |
-91 | |
-
.1073 | Minor fixes since 1.4.2, including git-mv and git-http with alternates. |
-92 | Minor fixes since 1.4.2, including git-mv and git-http with alternates. |
-
1074 | gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A |
-93 | gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A |
-
1075 | gpg: Good signature from "Junio C Hamano <junkio@cox.net>" |
-94 | gpg: Good signature from "Junio C Hamano <junkio@cox.net>" |
-
1076 | gpg: aka "[jpeg image of size 1513]" |
-95 | gpg: aka "[jpeg image of size 1513]" |
-
1077 | Primary key fingerprint: 3565 2A26 2040 E066 C9A7 4A7D C0C6 D9A4 F311 9B9A |
-96 | Primary key fingerprint: 3565 2A26 2040 E066 C9A7 4A7D C0C6 D9A4 F311 9B9A |
-
|
-97 | ---- |
-|
1078 | |
-98 | |
-
1079 | If you don’t have the signer’s public key, you get something like this instead: |
-99 | If you don’t have the signer’s public key, you get something like this instead: |
-
1080 | |
-100 | |
-
. | |
-101 | [source,console] |
-
|
-102 | ---- |
-|
1081 | gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A |
-103 | gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A |
-
1082 | gpg: Can't check signature: public key not found |
-104 | gpg: Can't check signature: public key not found |
-
1083 | error: could not verify the tag 'v1.4.2.1' |
-105 | error: could not verify the tag 'v1.4.2.1' |
-
|
-106 | ---- |
-|
|
-107 | |
-|
|
-108 | [[_signing_commits]] |
-|
|
-109 | ==== Signing Commits |
-|
|
-110 | |
-|
|
-111 | In more recent versions of Git (v1.7.9 and above), you can now also sign individual commits. |
-|
|
-112 | If you're interested in signing commits directly instead of just the tags, all you need to do is add a `-S` to your `git commit` command. |
-|
|
-113 | |
-|
|
-114 | [source,console] |
-|
|
-115 | ---- |
-|
|
-116 | $ git commit -a -S -m 'signed commit' |
-|
|
-117 | |
-|
|
-118 | You need a passphrase to unlock the secret key for |
-|
|
-119 | user: "Scott Chacon (Git signing key) <schacon@gmail.com>" |
-|
|
-120 | 2048-bit RSA key, ID 0A46826A, created 2014-06-04 |
-|
|
-121 | |
-|
|
-122 | [master 5c3386c] signed commit |
-|
|
-123 | 4 files changed, 4 insertions(+), 24 deletions(-) |
-|
|
-124 | rewrite Rakefile (100%) |
-|
|
-125 | create mode 100644 lib/git.rb |
-|
|
-126 | ---- |
-|
|
-127 | |
-|
|
-128 | To see and verify these signatures, there is also a `--show-signature` option to `git log`. |
-|
|
-129 | |
-|
|
-130 | [source,console] |
-|
|
-131 | ---- |
-|
|
-132 | $ git log --show-signature -1 |
-|
|
-133 | commit 5c3386cf54bba0a33a32da706aa52bc0155503c2 |
-|
|
-134 | gpg: Signature made Wed Jun 4 19:49:17 2014 PDT using RSA key ID 0A46826A |
-|
|
-135 | gpg: Good signature from "Scott Chacon (Git signing key) <schacon@gmail.com>" |
-|
|
-136 | Author: Scott Chacon <schacon@gmail.com> |
-|
|
-137 | Date: Wed Jun 4 19:49:17 2014 -0700 |
-|
|
-138 | |
-|
|
-139 | signed commit |
-|
|
-140 | ---- |
-|
|
-141 | |
-|
|
-142 | Additionally, you can configure `git log` to check any signatures it finds and list them in it's output with the `%G?` format. |
-|
|
-143 | |
-|
|
-144 | [source,console] |
-|
|
-145 | ---- |
-|
|
-146 | $ git log --pretty="format:%h %G? %aN %s" |
-|
|
-147 | |
-|
|
-148 | 5c3386c G Scott Chacon signed commit |
-|
|
-149 | ca82a6d N Scott Chacon changed the version number |
-|
|
-150 | 085bb3b N Scott Chacon removed unnecessary test code |
-|
|
-151 | a11bef0 N Scott Chacon first commit |
-|
|
-152 | ---- |
-|
|
-153 | |
-|
|
-154 | Here we can see that only the latest commit is signed and valid and the previous commits are not. |
-|
|
-155 | |
-|
|
-156 | In Git 1.8.3 and later, "git merge" and "git pull" can be told to inspect and reject when merging a commit that does not carry a trusted GPG signature with the `--verify-signatures` command. |
-|
|
-157 | |
-|
|
-158 | If you use this option when merging a branch and it contains commits that are not signed and valid, the merge will not work. |
-|
|
-159 | |
-|
|
-160 | [source,console] |
-|
|
-161 | ---- |
-|
|
-162 | $ git merge --verify-signatures non-verify |
-|
|
-163 | fatal: Commit ab06180 does not have a GPG signature. |
-|
|
-164 | ---- |
-|
|
-165 | |
-|
|
-166 | If the merge contains only valid signed commits, the merge command will show you all the signatures it has checked and then move forward with the merge. |
-|
|
-167 | |
-|
|
-168 | [source,console] |
-|
|
-169 | ---- |
-|
|
-170 | $ git merge --verify-signatures signed-branch |
-|
|
-171 | Commit 13ad65e has a good GPG signature by Scott Chacon (Git signing key) <schacon@gmail.com> |
-|
|
-172 | Updating 5c3386c..13ad65e |
-|
|
-173 | Fast-forward |
-|
|
-174 | README | 2 ++ |
-|
|
-175 | 1 file changed, 2 insertions(+) |
-|
|
-176 | ---- |
-|
|
-177 | |
-|
|
-178 | You can also use the `-S` option with the `git merge` command itself to sign the resulting merge commit itself. The following example both verifies that every commit in the branch to be merged is signed and furthermore signs the resulting merge commit. |
-|
|
-179 | |
-|
|
-180 | [source,console] |
-|
|
-181 | ---- |
-|
|
-182 | $ git merge --verify-signatures -S signed-branch |
-|
|
-183 | Commit 13ad65e has a good GPG signature by Scott Chacon (Git signing key) <schacon@gmail.com> |
-|
|
-184 | |
-|
|
-185 | You need a passphrase to unlock the secret key for |
-|
|
-186 | user: "Scott Chacon (Git signing key) <schacon@gmail.com>" |
-|
|
-187 | 2048-bit RSA key, ID 0A46826A, created 2014-06-04 |
-|
|
-188 | |
-|
|
-189 | Merge made by the 'recursive' strategy. |
-|
|
-190 | README | 2 ++ |
-|
|
-191 | 1 file changed, 2 insertions(+) |
-|
|
-192 | ---- |
-|
1084 | |
-193 | |
-
.1085 | ### Tagging Later ### |
-194 | ==== Everyone Must Sign |
-
1086 | |
-195 | |
-
.1087 | You can also tag commits after you’ve moved past them. Suppose your commit history looks like this: |
-196 | Signing tags and commits is great, but if you decide to use this in your normal workflow, you'll have to make sure that everyone on your team understands how to do so. If you don't, you'll end up spending a lot of time helping people figure out how to rewrite their commits with signed versions. Make sure you understand GPG and the benefits of signing things before adopting this as part of your standard workflow. |
-
1088 | |
- |
-|
1089 | $ git log --pretty=oneline |
- |
-|
1090 | 15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment' |
- |
-|
1091 | a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support |
- |
-|
1092 | 0d52aaab4479697da7686c15f77a3d64d9165190 one more thing |
- |
-|
1093 | 6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment' |
- |
-|
1094 | 0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function |
- |
-|
1095 | 4682c3261057305bdd616e23b64b0857d832627b added a todo file |
- |
-|
1096 | 166ae0c4d3f420721acbb115cc33848dfcc2121a started write support |
- |
-|
1097 | 9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile |
- |
-|
1098 | 964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo |
- |
-|
1099 | 8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme |
- |
-|
1100 | |
- |
-|
1101 | Now, suppose you forgot to tag the project at `v1.2`, which was at the "updated rakefile" commit. You can add it after the fact. To tag that commit, you specify the commit checksum (or part of it) at the end of the command: |
- |
-|
1102 | |
- |
-|
1103 | $ git tag -a v1.2 -m 'version 1.2' 9fceb02 |
- |
-|
1104 | |
- |
-|
1105 | You can see that you’ve tagged the commit: |
- |
-|
1106 | |
- |
-|
1107 | $ git tag |
- |
-|
1108 | v0.1 |
- |
-|
1109 | v1.2 |
- |
-|
1110 | v1.3 |
- |
-|
1111 | v1.4 |
- |
-|
1112 | v1.4-lw |
- |
-|
1113 | v1.5 |
- |
-|
1114 | |
- |
-|
1115 | $ git show v1.2 |
- |
-|
1116 | tag v1.2 |
- |
-|
1117 | Tagger: Scott Chacon <schacon@gee-mail.com> |
- |
-|
1118 | Date: Mon Feb 9 15:32:16 2009 -0800 |
- |
-|
1119 | |
- |
-|
1120 | version 1.2 |
- |
-|
1121 | commit 9fceb02d0ae598e95dc970b74767f19372d61af8 |
- |
-|
1122 | Author: Magnus Chacon <mchacon@gee-mail.com> |
- |
-|
1123 | Date: Sun Apr 27 20:43:35 2008 -0700 |
- |
-|
1124 | |
- |
-|
1125 | updated rakefile |
- |
-|
1126 | ... |
- |
-|
1127 | |
- |
-|
1128 | ### Sharing Tags ### |
- |
-|
1129 | |
- |
-|
1130 | By default, the `git push` command doesn’t transfer tags to remote servers. You will have to explicitly push tags to a shared server after you have created them. This process is just like sharing remote branches — you can run `git push origin [tagname]`. |
- |
-|
1131 | |
- |
-|
1132 | $ git push origin v1.5 |
- |
-|
1133 | Counting objects: 50, done. |
- |
-|
1134 | Compressing objects: 100% (38/38), done. |
- |
-|
1135 | Writing objects: 100% (44/44), 4.56 KiB, done. |
- |
-|
1136 | Total 44 (delta 18), reused 8 (delta 1) |
- |
-|
1137 | To git@github.com:schacon/simplegit.git |
- |
-|
1138 | * [new tag] v1.5 -> v1.5 |
- |
-|
1139 | |
- |
-|
1140 | If you have a lot of tags that you want to push up at once, you can also use the `--tags` option to the `git push` command. This will transfer all of your tags to the remote server that are not already there. |
- |
-|
1141 | |
- |
-|
1142 | $ git push origin --tags |
- |
-|
1143 | Counting objects: 50, done. |
- |
-|
1144 | Compressing objects: 100% (38/38), done. |
- |
-|
1145 | Writing objects: 100% (44/44), 4.56 KiB, done. |
- |
-|
1146 | Total 44 (delta 18), reused 8 (delta 1) |
- |
-|
1147 | To git@github.com:schacon/simplegit.git |
- |
-|
1148 | * [new tag] v0.1 -> v0.1 |
- |
-|
1149 | * [new tag] v1.2 -> v1.2 |
- |
-|
1150 | * [new tag] v1.4 -> v1.4 |
- |
-|
1151 | * [new tag] v1.4-lw -> v1.4-lw |
- |
-|
1152 | * [new tag] v1.5 -> v1.5 |
- |
-|
1153 | |
- |
-|
1154 | Now, when someone else clones or pulls from your repository, they will get all your tags as well. |
- |
-|
1155 | |
- |
-|
1156 | ## Tips and Tricks ## |
- |
-|
1157 | |
- |
-|
1158 | Before we finish this chapter on basic Git, a few little tips and tricks may make your Git experience a bit simpler, easier, or more familiar. Many people use Git without using any of these tips, and we won’t refer to them or assume you’ve used them later in the book; but you should probably know how to do them. |
- |
-|
1159 | |
- |
-|
1160 | ### Auto-Completion ### |
- |
-|
1161 | |
- |
-|
1162 | If you use the Bash shell, Git comes with a nice auto-completion script you can enable. Download it directly from the Git source code at https://github.com/git/git/blob/master/contrib/completion |
- |
-|
1163 | |
- |
-|
1164 | source ~/git-completion.bash |
- |
-|
1165 | |
- |
-|
1166 | If you want to set up Git to automatically have Bash shell completion for all users, copy this script to the `/opt/local/etc/bash_completion.d` directory on Mac systems or to the `/etc/bash_completion.d/` directory on Linux systems. This is a directory of scripts that Bash will automatically load to provide shell completions. |
- |
-|
1167 | |
- |
-|
1168 | If you’re using Windows with Git Bash, which is the default when installing Git on Windows with msysGit, auto-completion should be preconfigured. |
- |
-|
1169 | |
- |
-|
1170 | Press the Tab key when you’re writing a Git command, and it should return a set of suggestions for you to pick from: |
- |
-|
1171 | |
- |
-|
1172 | $ git co<tab><tab> |
- |
-|
1173 | commit config |
- |
-|
1174 | |
- |
-|
1175 | In this case, typing `git co` and then pressing the Tab key twice suggests commit and config. Adding `m<tab>` completes `git commit` automatically. |
- |
-|
1176 | |
- |
-|
1177 | This also works with options, which is probably more useful. For instance, if you’re running a `git log` command and can’t remember one of the options, you can start typing it and press Tab to see what matches: |
- |
-|
1178 | |
- |
-|
1179 | $ git log --s<tab><tab> |
- |
-|
1180 | --shortstat --sparse |
- |
-|
1181 | --simplify-by-decoration --src-prefix= |
- |
-|
1182 | --simplify-merges --stat |
- |
-|
1183 | --since= --summary |
- |
-|
1184 | |
- |
-|
1185 | That’s a pretty nice trick and may save you some time and documentation reading. |
- |
-|
1186 | |
- |
-|
1187 | ### Git Aliases ### |
- |
-|
1188 | |
- |
-|
1189 | Git doesn’t infer your command if you type it in partially. If you don’t want to type the entire text of each of the Git commands, you can easily set up an alias for each command using `git config`. Here are a couple of examples you may want to set up: |
- |
-|
1190 | |
- |
-|
1191 | $ git config --global alias.co checkout |
- |
-|
1192 | $ git config --global alias.br branch |
- |
-|
1193 | $ git config --global alias.ci commit |
- |
-|
1194 | $ git config --global alias.st status |
- |
-|
1195 | |
- |
-|
1196 | This means that, for example, instead of typing `git commit`, you just need to type `git ci`. As you go on using Git, you’ll probably use other commands frequently as well; in this case, don’t hesitate to create new aliases. |
- |
-|
1197 | |
- |
-|
1198 | This technique can also be very useful in creating commands that you think should exist. For example, to correct the usability problem you encountered with unstaging a file, you can add your own unstage alias to Git: |
- |
-|
1199 | |
- |
-|
1200 | $ git config --global alias.unstage 'reset HEAD --' |
- |
-|
1201 | |
- |
-|
1202 | This makes the following two commands equivalent: |
- |
-|
1203 | |
- |
-|
1204 | $ git unstage fileA |
- |
-|
1205 | $ git reset HEAD fileA |
- |
-|
1206 | |
- |
-|
1207 | This seems a bit clearer. It’s also common to add a `last` command, like this: |
- |
-|
1208 | |
- |
-|
1209 | $ git config --global alias.last 'log -1 HEAD' |
- |
-|
1210 | |
- |
-|
1211 | This way, you can see the last commit easily: |
- |
-|
1212 | |
- |
-|
1213 | $ git last |
- |
-|
1214 | commit 66938dae3329c7aebe598c2246a8e6af90d04646 |
- |
-|
1215 | Author: Josh Goebel <dreamer3@example.com> |
- |
-|
1216 | Date: Tue Aug 26 19:48:51 2008 +0800 |
- |
-|
1217 | |
- |
-|
1218 | test for current head |
- |
-|
1219 | |
- |
-|
1220 | Signed-off-by: Scott Chacon <schacon@example.com> |
- |
-|
1221 | |
- |
-|
1222 | As you can tell, Git simply replaces the new command with whatever you alias it to. However, maybe you want to run an external command, rather than a Git subcommand. In that case, you start the command with a `!` character. This is useful if you write your own tools that work with a Git repository. We can demonstrate by aliasing `git visual` to run `gitk`: |
- |
-|
1223 | |
- |
-|
1224 | $ git config --global alias.visual '!gitk' |
- |
-|
1225 | |
- |
-|
1226 | ## Summary ## |
- |
-|
1227 | |
- |
-|
1228 | At this point, you can do all the basic local Git operations — creating or cloning a repository, making changes, staging and committing those changes, and viewing the history of all the changes the repository has been through. Next, we’ll cover Git’s killer feature: its branching model. |
- |
-|
1229 | |
-197 | |
-
C:\Users\15625\Documents\Git\progit\en\06-git-tools\01-chapter6.markdown | -C:\Users\15625\Documents\Git\progit2-ja\book\07-git-tools\sections\stashing-cleaning.asc | -||
---|---|---|---|
.1 | # Git Tools # |
-1 | [[_git_stashing]] |
-
2 | |
- |
-|
3 | 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. |
- |
-|
4 | |
- |
-|
5 | 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. |
- |
-|
6 | |
- |
-|
7 | ## Revision Selection ## |
- |
-|
8 | |
- |
-|
9 | 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. |
- |
-|
10 | |
- |
-|
11 | ### Single Revisions ### |
- |
-|
12 | |
- |
-|
13 | 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. |
- |
-|
14 | |
- |
-|
15 | ### Short SHA ### |
- |
-|
16 | |
- |
-|
17 | 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. |
- |
-|
18 | |
- |
-|
19 | For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: |
- |
-|
20 | |
- |
-|
21 | $ git log |
- |
-|
22 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
23 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
24 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
25 | |
- |
-|
26 | fixed refs handling, added gc auto, updated tests |
- |
-|
27 | |
- |
-|
28 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
29 | Merge: 1c002dd... 35cfb2b... |
- |
-|
30 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
31 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
32 | |
- |
-|
33 | Merge commit 'phedders/rdocs' |
- |
-|
34 | |
- |
-|
35 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
36 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
37 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
38 | |
- |
-|
39 | added some blame and merge stuff |
- |
-|
40 | |
- |
-|
41 | In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): |
- |
-|
42 | |
- |
-|
43 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
44 | $ git show 1c002dd4b536e7479f |
- |
-|
45 | $ git show 1c002d |
- |
-|
46 | |
- |
-|
47 | 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: |
- |
-|
48 | |
- |
-|
49 | $ git log --abbrev-commit --pretty=oneline |
- |
-|
50 | ca82a6d changed the version number |
- |
-|
51 | 085bb3b removed unnecessary test code |
- |
-|
52 | a11bef0 first commit |
- |
-|
53 | |
- |
-|
54 | 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. |
- |
-|
55 | |
- |
-|
56 | ### A SHORT NOTE ABOUT SHA-1 ### |
- |
-|
57 | |
- |
-|
58 | 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? |
- |
-|
59 | |
- |
-|
60 | 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. |
- |
-|
61 | |
- |
-|
62 | 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. |
- |
-|
63 | |
- |
-|
64 | 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. 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. |
- |
-|
65 | |
- |
-|
66 | ### Branch References ### |
- |
-|
67 | |
- |
-|
68 | The most straightforward way to specify a commit requires that it have 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`: |
- |
-|
69 | |
- |
-|
70 | $ git show ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
71 | $ git show topic1 |
- |
-|
72 | |
- |
-|
73 | 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 Chapter 9 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. |
- |
-|
74 | |
- |
-|
75 | $ git rev-parse topic1 |
- |
-|
76 | ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
77 | |
- |
-|
78 | ### RefLog Shortnames ### |
- |
-|
79 | |
- |
-|
80 | 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. |
- |
-|
81 | |
- |
-|
82 | You can see your reflog by using `git reflog`: |
- |
-|
83 | |
- |
-|
84 | $ git reflog |
- |
-|
85 | 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated |
- |
-|
86 | d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. |
- |
-|
87 | 1c002dd HEAD@{2}: commit: added some blame and merge stuff |
- |
-|
88 | 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD |
- |
-|
89 | 95df984 HEAD@{4}: commit: # This is a combination of two commits. |
- |
-|
90 | 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD |
- |
-|
91 | 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD |
- |
-|
92 | |
- |
-|
93 | 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: |
- |
-|
94 | |
- |
-|
95 | $ git show HEAD@{5} |
- |
-|
96 | |
- |
-|
97 | 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 |
- |
-|
98 | |
- |
-|
99 | $ git show master@{yesterday} |
- |
-|
100 | |
- |
-|
101 | 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. |
- |
-|
102 | |
- |
-|
103 | To see reflog information formatted like the `git log` output, you can run `git log -g`: |
- |
-|
104 | |
- |
-|
105 | $ git log -g master |
- |
-|
106 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
107 | Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) |
- |
-|
108 | Reflog message: commit: fixed refs handling, added gc auto, updated |
- |
-|
109 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
110 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
111 | |
- |
-|
112 | fixed refs handling, added gc auto, updated tests |
- |
-|
113 | |
- |
-|
114 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
115 | Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) |
- |
-|
116 | Reflog message: merge phedders/rdocs: Merge made by recursive. |
- |
-|
117 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
118 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
119 | |
- |
-|
120 | Merge commit 'phedders/rdocs' |
- |
-|
121 | |
- |
-|
122 | 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. |
- |
-|
123 | |
- |
-|
124 | ### Ancestry References ### |
- |
-|
125 | |
- |
-|
126 | 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. |
- |
-|
127 | Suppose you look at the history of your project: |
- |
-|
128 | |
- |
-|
129 | $ git log --pretty=format:'%h %s' --graph |
- |
-|
130 | * 734713b fixed refs handling, added gc auto, updated tests |
- |
-|
131 | * d921970 Merge commit 'phedders/rdocs' |
- |
-|
132 | |\ |
- |
-|
133 | | * 35cfb2b Some rdoc changes |
- |
-|
134 | * | 1c002dd added some blame and merge stuff |
- |
-|
135 | |/ |
- |
-|
136 | * 1c36188 ignore *.gem |
- |
-|
137 | * 9b29157 add open3_detach to gemspec file list |
- |
-|
138 | |
- |
-|
139 | Then, you can see the previous commit by specifying `HEAD^`, which means "the parent of HEAD": |
- |
-|
140 | |
- |
-|
141 | $ git show HEAD^ |
- |
-|
142 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
143 | Merge: 1c002dd... 35cfb2b... |
- |
-|
144 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
145 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
146 | |
- |
-|
147 | Merge commit 'phedders/rdocs' |
- |
-|
148 | |
- |
-|
149 | 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: |
- |
-|
150 | |
- |
-|
151 | $ git show d921970^ |
- |
-|
152 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
153 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
154 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
155 | |
- |
-|
156 | added some blame and merge stuff |
- |
-|
157 | |
- |
-|
158 | $ git show d921970^2 |
- |
-|
159 | commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 |
- |
-|
160 | Author: Paul Hedderly <paul+git@mjr.org> |
- |
-|
161 | Date: Wed Dec 10 22:22:03 2008 +0000 |
- |
-|
162 | |
- |
-|
163 | Some rdoc changes |
- |
-|
164 | |
- |
-|
165 | 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 |
- |
-|
166 | |
- |
-|
167 | $ git show HEAD~3 |
- |
-|
168 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
169 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
170 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
171 | |
- |
-|
172 | ignore *.gem |
- |
-|
173 | |
- |
-|
174 | This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: |
- |
-|
175 | |
- |
-|
176 | $ git show HEAD^^^ |
- |
-|
177 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
178 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
179 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
180 | |
- |
-|
181 | ignore *.gem |
- |
-|
182 | |
- |
-|
183 | 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. |
- |
-|
184 | |
- |
-|
185 | ### Commit Ranges ### |
- |
-|
186 | |
- |
-|
187 | 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?" |
- |
-|
188 | |
- |
-|
189 | #### Double Dot #### |
- |
-|
190 | |
- |
-|
191 | 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 Figure 6-1. |
- |
-|
192 | |
- |
-|
193 | Insert 18333fig0601.png |
- |
-|
194 | Figure 6-1. Example history for range selection. |
- |
-|
195 | |
- |
-|
196 | 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: |
- |
-|
197 | |
- |
-|
198 | $ git log master..experiment |
- |
-|
199 | D |
- |
-|
200 | C |
- |
-|
201 | |
- |
-|
202 | 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`: |
- |
-|
203 | |
- |
-|
204 | $ git log experiment..master |
- |
-|
205 | F |
- |
-|
206 | E |
- |
-|
207 | |
- |
-|
208 | 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: |
- |
-|
209 | |
- |
-|
210 | $ git log origin/master..HEAD |
- |
-|
211 | |
- |
-|
212 | 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. |
- |
-|
213 | 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. |
- |
-|
214 | |
- |
-|
215 | #### Multiple Points #### |
- |
-|
216 | |
- |
-|
217 | 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: |
- |
-|
218 | |
- |
-|
219 | $ git log refA..refB |
- |
-|
220 | $ git log ^refA refB |
- |
-|
221 | $ git log refB --not refA |
- |
-|
222 | |
- |
-|
223 | 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: |
- |
-|
224 | |
- |
-|
225 | $ git log refA refB ^refC |
- |
-|
226 | $ git log refA refB --not refC |
- |
-|
227 | |
- |
-|
228 | This makes for a very powerful revision query system that should help you figure out what is in your branches. |
- |
-|
229 | |
- |
-|
230 | #### Triple Dot #### |
- |
-|
231 | |
- |
-|
232 | 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 Figure 6-1. |
- |
-|
233 | If you want to see what is in `master` or `experiment` but not any common references, you can run |
- |
-|
234 | |
- |
-|
235 | $ git log master...experiment |
- |
-|
236 | F |
- |
-|
237 | E |
- |
-|
238 | D |
- |
-|
239 | C |
- |
-|
240 | |
- |
-|
241 | 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. |
- |
-|
242 | |
- |
-|
243 | 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: |
- |
-|
244 | |
- |
-|
245 | $ git log --left-right master...experiment |
- |
-|
246 | < F |
- |
-|
247 | < E |
- |
-|
248 | > D |
- |
-|
249 | > C |
- |
-|
250 | |
- |
-|
251 | With these tools, you can much more easily let Git know what commit or commits you want to inspect. |
- |
-|
252 | |
- |
-|
253 | ## Interactive Staging ## |
- |
-|
254 | |
- |
-|
255 | Git comes with a couple of scripts that make some command-line tasks easier. Here, you’ll look at a few interactive commands that can help you easily craft your commits to include only certain combinations and parts of files. These tools are very helpful if you modify a bunch of files and then decide that you want those changes to be in several focused commits rather than one big messy commit. This way, you can make sure your commits are logically separate changesets and can be easily reviewed by the developers working with you. |
- |
-|
256 | If you run `git add` with the `-i` or `--interactive` option, Git goes into an interactive shell mode, displaying something like this: |
- |
-|
257 | |
- |
-|
258 | $ git add -i |
- |
-|
259 | staged unstaged path |
- |
-|
260 | 1: unchanged +0/-1 TODO |
- |
-|
261 | 2: unchanged +1/-1 index.html |
- |
-|
262 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
263 | |
- |
-|
264 | *** Commands *** |
- |
-|
265 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
266 | 5: patch 6: diff 7: quit 8: help |
- |
-|
267 | What now> |
- |
-|
268 | |
- |
-|
269 | 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. |
- |
-|
270 | |
- |
-|
271 | After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. |
- |
-|
272 | |
- |
-|
273 | ### Staging and Unstaging Files ### |
- |
-|
274 | |
- |
-|
275 | If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: |
- |
-|
276 | |
- |
-|
277 | What now> 2 |
- |
-|
278 | staged unstaged path |
- |
-|
279 | 1: unchanged +0/-1 TODO |
- |
-|
280 | 2: unchanged +1/-1 index.html |
- |
-|
281 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
282 | Update>> |
- |
-|
283 | |
- |
-|
284 | To stage the TODO and index.html files, you can type the numbers: |
- |
-|
285 | |
- |
-|
286 | Update>> 1,2 |
- |
-|
287 | staged unstaged path |
- |
-|
288 | * 1: unchanged +0/-1 TODO |
- |
-|
289 | * 2: unchanged +1/-1 index.html |
- |
-|
290 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
291 | Update>> |
- |
-|
292 | |
- |
-|
293 | 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: |
- |
-|
294 | |
- |
-|
295 | Update>> |
- |
-|
296 | updated 2 paths |
- |
-|
297 | |
- |
-|
298 | *** Commands *** |
- |
-|
299 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
300 | 5: patch 6: diff 7: quit 8: help |
- |
-|
301 | What now> 1 |
- |
-|
302 | staged unstaged path |
- |
-|
303 | 1: +0/-1 nothing TODO |
- |
-|
304 | 2: +1/-1 nothing index.html |
- |
-|
305 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
306 | |
- |
-|
307 | 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: |
- |
-|
308 | |
- |
-|
309 | *** Commands *** |
- |
-|
310 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
311 | 5: patch 6: diff 7: quit 8: help |
- |
-|
312 | What now> 3 |
- |
-|
313 | staged unstaged path |
- |
-|
314 | 1: +0/-1 nothing TODO |
- |
-|
315 | 2: +1/-1 nothing index.html |
- |
-|
316 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
317 | Revert>> 1 |
- |
-|
318 | staged unstaged path |
- |
-|
319 | * 1: +0/-1 nothing TODO |
- |
-|
320 | 2: +1/-1 nothing index.html |
- |
-|
321 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
322 | Revert>> [enter] |
- |
-|
323 | reverted one path |
- |
-|
324 | |
- |
-|
325 | Looking at your Git status again, you can see that you’ve unstaged the TODO file: |
- |
-|
326 | |
- |
-|
327 | *** Commands *** |
- |
-|
328 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
329 | 5: patch 6: diff 7: quit 8: help |
- |
-|
330 | What now> 1 |
- |
-|
331 | staged unstaged path |
- |
-|
332 | 1: unchanged +0/-1 TODO |
- |
-|
333 | 2: +1/-1 nothing index.html |
- |
-|
334 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
335 | |
- |
-|
336 | 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: |
- |
-|
337 | |
- |
-|
338 | *** Commands *** |
- |
-|
339 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
340 | 5: patch 6: diff 7: quit 8: help |
- |
-|
341 | What now> 6 |
- |
-|
342 | staged unstaged path |
- |
-|
343 | 1: +1/-1 nothing index.html |
- |
-|
344 | Review diff>> 1 |
- |
-|
345 | diff --git a/index.html b/index.html |
- |
-|
346 | index 4d07108..4335f49 100644 |
- |
-|
347 | --- a/index.html |
- |
-|
348 | +++ b/index.html |
- |
-|
349 | @@ -16,7 +16,7 @@ Date Finder |
- |
-|
350 | |
- |
-|
351 | <p id="out">...</p> |
- |
-|
352 | |
- |
-|
353 | -<div id="footer">contact : support@github.com</div> |
- |
-|
354 | +<div id="footer">contact : email.support@github.com</div> |
- |
-|
355 | |
- |
-|
356 | <script type="text/javascript"> |
- |
-|
357 | |
- |
-|
358 | With these basic commands, you can use the interactive add mode to deal with your staging area a little more easily. |
- |
-|
359 | |
- |
-|
360 | ### Staging Patches ### |
- |
-|
361 | |
- |
-|
362 | It’s also possible for Git to stage certain parts of files and not the rest. For example, if you make two changes to your simplegit.rb file and want to stage one of them and not the other, doing so is very easy in Git. From the interactive prompt, type `5` or `p` (for patch). Git will ask you which files you would like to partially stage; then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one: |
- |
-|
363 | |
- |
-|
364 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
- |
-|
365 | index dd5ecc4..57399e0 100644 |
- |
-|
366 | --- a/lib/simplegit.rb |
- |
-|
367 | +++ b/lib/simplegit.rb |
- |
-|
368 | @@ -22,7 +22,7 @@ class SimpleGit |
- |
-|
369 | end |
- |
-|
370 | |
- |
-|
371 | def log(treeish = 'master') |
- |
-|
372 | - command("git log -n 25 #{treeish}") |
- |
-|
373 | + command("git log -n 30 #{treeish}") |
- |
-|
374 | end |
- |
-|
375 | |
- |
-|
376 | def blame(path) |
- |
-|
377 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? |
- |
-|
378 | |
- |
-|
379 | You have a lot of options at this point. Typing `?` shows a list of what you can do: |
- |
-|
380 | |
- |
-|
381 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? |
- |
-|
382 | y - stage this hunk |
- |
-|
383 | n - do not stage this hunk |
- |
-|
384 | a - stage this and all the remaining hunks in the file |
- |
-|
385 | d - do not stage this hunk nor any of the remaining hunks in the file |
- |
-|
386 | g - select a hunk to go to |
- |
-|
387 | / - search for a hunk matching the given regex |
- |
-|
388 | j - leave this hunk undecided, see next undecided hunk |
- |
-|
389 | J - leave this hunk undecided, see next hunk |
- |
-|
390 | k - leave this hunk undecided, see previous undecided hunk |
- |
-|
391 | K - leave this hunk undecided, see previous hunk |
- |
-|
392 | s - split the current hunk into smaller hunks |
- |
-|
393 | e - manually edit the current hunk |
- |
-|
394 | ? - print help |
- |
-|
395 | |
- |
-|
396 | Generally, you’ll type `y` or `n` if you want to stage each hunk, but staging all of them in certain files or skipping a hunk decision until later can be helpful too. If you stage one part of the file and leave another part unstaged, your status output will look like this: |
- |
-|
397 | |
- |
-|
398 | What now> 1 |
- |
-|
399 | staged unstaged path |
- |
-|
400 | 1: unchanged +0/-1 TODO |
- |
-|
401 | 2: +1/-1 nothing index.html |
- |
-|
402 | 3: +1/-1 +4/-0 lib/simplegit.rb |
- |
-|
403 | |
- |
-|
404 | The status of the simplegit.rb file is interesting. It shows you that a couple of lines are staged and a couple are unstaged. You’ve partially staged this file. At this point, you can exit the interactive adding script and run `git commit` to commit the partially staged files. |
- |
-|
405 | |
- |
-|
406 | Finally, you don’t need to be in interactive add mode to do the partial-file staging — you can start the same script by using `git add -p` or `git add --patch` on the command line. |
- |
-|
407 | |
- |
-|
408 | ## Stashing ## |
-2 | === Stashing and Cleaning |
-
409 | |
-3 | |
-
410 | Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the `git stash` command. |
-4 | Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the `git stash` command. |
-
411 | |
-5 | |
-
.412 | Stashing takes the dirty state of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time. |
-6 | Stashing takes the dirty state of your working directory – that is, your modified tracked files and staged changes – and saves it on a stack of unfinished changes that you can reapply at any time. |
-
413 | |
-7 | |
-
.414 | ### Stashing Your Work ### |
-8 | ==== Stashing Your Work |
-
415 | |
-9 | |
-
416 | To demonstrate, you’ll go into your project and start working on a couple of files and possibly stage one of the changes. If you run `git status`, you can see your dirty state: |
-10 | To demonstrate, you’ll go into your project and start working on a couple of files and possibly stage one of the changes. If you run `git status`, you can see your dirty state: |
-
417 | |
-11 | |
-
. | |
-12 | [source,console] |
-
|
-13 | ---- |
-|
418 | $ git status |
-14 | $ git status |
-
419 | # On branch master |
- |
-|
420 | # Changes to be committed: |
-15 | Changes to be committed: |
-
421 | # (use "git reset HEAD <file>..." to unstage) |
-16 | (use "git reset HEAD <file>..." to unstage) |
-
422 | # |
-17 | |
-
423 | # modified: index.html |
-18 | modified: index.html |
-
424 | # |
-19 | |
-
425 | # Changes not staged for commit: |
-20 | Changes not staged for commit: |
-
426 | # (use "git add <file>..." to update what will be committed) |
-21 | (use "git add <file>..." to update what will be committed) |
-
427 | # |
-22 | (use "git checkout -- <file>..." to discard changes in working directory) |
-
|
-23 | |
-|
428 | # modified: lib/simplegit.rb |
-24 | modified: lib/simplegit.rb |
-
429 | # |
-25 | ---- |
-
430 | |
-26 | |
-
.431 | Now you want to switch branches, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run `git stash`: |
-27 | Now you want to switch branches, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run `git stash` or `git stash save`: |
-
432 | |
-28 | |
-
. | |
-29 | [source,console] |
-
|
-30 | ---- |
-|
433 | $ git stash |
-31 | $ git stash |
-
434 | Saved working directory and index state \ |
-32 | Saved working directory and index state \ |
-
435 | "WIP on master: 049d078 added the index file" |
-33 | "WIP on master: 049d078 added the index file" |
-
436 | HEAD is now at 049d078 added the index file |
-34 | HEAD is now at 049d078 added the index file |
-
437 | (To restore them type "git stash apply") |
-35 | (To restore them type "git stash apply") |
-
|
-36 | ---- |
-|
438 | |
-37 | |
-
439 | Your working directory is clean: |
-38 | Your working directory is clean: |
-
440 | |
-39 | |
-
. | |
-40 | [source,console] |
-
|
-41 | ---- |
-|
441 | $ git status |
-42 | $ git status |
-
442 | # On branch master |
-43 | # On branch master |
-
443 | nothing to commit, working directory clean |
-44 | nothing to commit, working directory clean |
-
|
-45 | ---- |
-|
444 | |
-46 | |
-
445 | At this point, you can easily switch branches and do work elsewhere; your changes are stored on your stack. To see which stashes you’ve stored, you can use `git stash list`: |
-47 | At this point, you can easily switch branches and do work elsewhere; your changes are stored on your stack. To see which stashes you’ve stored, you can use `git stash list`: |
-
446 | |
-48 | |
-
. | |
-49 | [source,console] |
-
|
-50 | ---- |
-|
447 | $ git stash list |
-51 | $ git stash list |
-
448 | stash@{0}: WIP on master: 049d078 added the index file |
-52 | stash@{0}: WIP on master: 049d078 added the index file |
-
449 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
-53 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
-
450 | stash@{2}: WIP on master: 21d80a5 added number to log |
-54 | stash@{2}: WIP on master: 21d80a5 added number to log |
-
|
-55 | ---- |
-|
451 | |
-56 | |
-
452 | In this case, two stashes were done previously, so you have access to three different stashed works. You can reapply the one you just stashed by using the command shown in the help output of the original stash command: `git stash apply`. If you want to apply one of the older stashes, you can specify it by naming it, like this: `git stash apply stash@{2}`. If you don’t specify a stash, Git assumes the most recent stash and tries to apply it: |
-57 | In this case, two stashes were done previously, so you have access to three different stashed works. You can reapply the one you just stashed by using the command shown in the help output of the original stash command: `git stash apply`. If you want to apply one of the older stashes, you can specify it by naming it, like this: `git stash apply stash@{2}`. If you don’t specify a stash, Git assumes the most recent stash and tries to apply it: |
-
453 | |
-58 | |
-
. | |
-59 | [source,console] |
-
|
-60 | ---- |
-|
454 | $ git stash apply |
-61 | $ git stash apply |
-
455 | # On branch master |
-62 | # On branch master |
-
456 | # Changes not staged for commit: |
-63 | # Changed but not updated: |
-
457 | # (use "git add <file>..." to update what will be committed) |
-64 | # (use "git add <file>..." to update what will be committed) |
-
458 | # |
-65 | # |
-
459 | # modified: index.html |
-66 | # modified: index.html |
-
460 | # modified: lib/simplegit.rb |
-67 | # modified: lib/simplegit.rb |
-
461 | # |
-68 | # |
-
|
-69 | ---- |
-|
462 | |
-70 | |
-
.463 | You can see that Git re-modifies the files you uncommitted when you saved the stash. In this case, you had a clean working directory when you tried to apply the stash, and you tried to apply it on the same branch you saved it from; but having a clean working directory and applying it on the same branch aren’t necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash — Git gives you merge conflicts if anything no longer applies cleanly. |
-71 | You can see that Git re-modifies the files you reverted when you saved the stash. In this case, you had a clean working directory when you tried to apply the stash, and you tried to apply it on the same branch you saved it from; but having a clean working directory and applying it on the same branch aren’t necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash – Git gives you merge conflicts if anything no longer applies cleanly. |
-
464 | |
-72 | |
-
465 | The changes to your files were reapplied, but the file you staged before wasn’t restaged. To do that, you must run the `git stash apply` command with a `--index` option to tell the command to try to reapply the staged changes. If you had run that instead, you’d have gotten back to your original position: |
-73 | The changes to your files were reapplied, but the file you staged before wasn’t restaged. To do that, you must run the `git stash apply` command with a `--index` option to tell the command to try to reapply the staged changes. If you had run that instead, you’d have gotten back to your original position: |
-
466 | |
-74 | |
-
. | |
-75 | [source,console] |
-
|
-76 | ---- |
-|
467 | $ git stash apply --index |
-77 | $ git stash apply --index |
-
468 | # On branch master |
-78 | # On branch master |
-
469 | # Changes to be committed: |
-79 | # Changes to be committed: |
-
470 | # (use "git reset HEAD <file>..." to unstage) |
-80 | # (use "git reset HEAD <file>..." to unstage) |
-
471 | # |
-81 | # |
-
472 | # modified: index.html |
-82 | # modified: index.html |
-
473 | # |
-83 | # |
-
474 | # Changes not staged for commit: |
-84 | # Changed but not updated: |
-
475 | # (use "git add <file>..." to update what will be committed) |
-85 | # (use "git add <file>..." to update what will be committed) |
-
476 | # |
-86 | # |
-
477 | # modified: lib/simplegit.rb |
-87 | # modified: lib/simplegit.rb |
-
478 | # |
-88 | # |
-
|
-89 | ---- |
-|
479 | |
-90 | |
-
.480 | The apply option only tries to apply the stashed work — you continue to have it on your stack. To remove it, you can run `git stash drop` with the name of the stash to remove: |
-91 | The apply option only tries to apply the stashed work – you continue to have it on your stack. To remove it, you can run `git stash drop` with the name of the stash to remove: |
-
481 | |
-92 | |
-
. | |
-93 | [source,console] |
-
|
-94 | ---- |
-|
482 | $ git stash list |
-95 | $ git stash list |
-
483 | stash@{0}: WIP on master: 049d078 added the index file |
-96 | stash@{0}: WIP on master: 049d078 added the index file |
-
484 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
-97 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
-
485 | stash@{2}: WIP on master: 21d80a5 added number to log |
-98 | stash@{2}: WIP on master: 21d80a5 added number to log |
-
486 | $ git stash drop stash@{0} |
-99 | $ git stash drop stash@{0} |
-
487 | Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) |
-100 | Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) |
-
|
-101 | ---- |
-|
488 | |
-102 | |
-
489 | You can also run `git stash pop` to apply the stash and then immediately drop it from your stack. |
-103 | You can also run `git stash pop` to apply the stash and then immediately drop it from your stack. |
-
.490 | |
- |
-|
491 | ### Un-applying a Stash ### |
- |
-|
492 | |
- |
-|
493 | In some use case scenarios you might want to apply stashed changes, do some work, but then un-apply those changes that originally came from the stash. Git does not provide such a `stash unapply` command, but it is possible to achieve the effect by simply retrieving the patch associated with a stash and applying it in reverse: |
- |
-|
494 | |
- |
-|
495 | $ git stash show -p stash@{0} | git apply -R |
- |
-|
496 | |
- |
-|
497 | Again, if you don’t specify a stash, Git assumes the most recent stash: |
- |
-|
498 | |
- |
-|
499 | $ git stash show -p | git apply -R |
- |
-|
500 | |
- |
-|
501 | You may want to create an alias and effectively add a `stash-unapply` command to your Git. For example: |
- |
-|
502 | |
- |
-|
503 | $ git config --global alias.stash-unapply '!git stash show -p | git apply -R' |
- |
-|
504 | $ git stash apply |
- |
-|
505 | $ #... work work work |
- |
-|
506 | $ git stash-unapply |
- |
-|
507 | |
-104 | |
-
. | |
-105 | ==== Creative Stashing |
-
|
-106 | |
-|
|
-107 | There are a few stash variants that may also be helpful. The first option that is quite popular is the `--keep-index` option to the `stash save` command. This tells Git to not stash anything that you've already staged with the `git add` command. |
-|
|
-108 | |
-|
|
-109 | This can be really helpful if you've made a number of changes but want to only commit some of them and then come back to the rest of the changes at a later time. |
-|
|
-110 | |
-|
|
-111 | [source,console] |
-|
|
-112 | ---- |
-|
|
-113 | $ git status -s |
-|
|
-114 | M index.html |
-|
|
-115 | M lib/simplegit.rb |
-|
|
-116 | |
-|
|
-117 | $ git stash --keep-index |
-|
|
-118 | Saved working directory and index state WIP on master: 1b65b17 added the index file |
-|
|
-119 | HEAD is now at 1b65b17 added the index file |
-|
|
-120 | |
-|
|
-121 | $ git status -s |
-|
|
-122 | M index.html |
-|
|
-123 | ---- |
-|
|
-124 | |
-|
|
-125 | Another common thing you may want to do with stash is to stash the untracked files as well as the tracked ones. By default, `git stash` will only store files that are already in the index. If you specify `--include-untracked` or `-u`, Git will also stash any untracked files you have created. |
-|
|
-126 | |
-|
|
-127 | [source,console] |
-|
|
-128 | ---- |
-|
|
-129 | $ git status -s |
-|
|
-130 | M index.html |
-|
|
-131 | M lib/simplegit.rb |
-|
|
-132 | ?? new-file.txt |
-|
|
-133 | |
-|
|
-134 | $ git stash -u |
-|
|
-135 | Saved working directory and index state WIP on master: 1b65b17 added the index file |
-|
|
-136 | HEAD is now at 1b65b17 added the index file |
-|
|
-137 | |
-|
|
-138 | $ git status -s |
-|
|
-139 | $ |
-|
|
-140 | ---- |
-|
|
-141 | |
-|
|
-142 | Finally, if you specify the `--patch` flag, Git will not stash everything that is modified but will instead prompt you interactively which of the changes you would like to stash and which you would like to keep in your working directly. |
-|
|
-143 | |
-|
|
-144 | [source,console] |
-|
|
-145 | ---- |
-|
|
-146 | $ git stash --patch |
-|
|
-147 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
-|
|
-148 | index 66d332e..8bb5674 100644 |
-|
|
-149 | --- a/lib/simplegit.rb |
-|
|
-150 | +++ b/lib/simplegit.rb |
-|
|
-151 | @@ -16,6 +16,10 @@ class SimpleGit |
-|
|
-152 | return `#{git_cmd} 2>&1`.chomp |
-|
|
-153 | end |
-|
|
-154 | end |
-|
|
-155 | + |
-|
|
-156 | + def show(treeish = 'master') |
-|
|
-157 | + command("git show #{treeish}") |
-|
|
-158 | + end |
-|
|
-159 | |
-|
|
-160 | end |
-|
|
-161 | test |
-|
|
-162 | Stash this hunk [y,n,q,a,d,/,e,?]? y |
-|
|
-163 | |
-|
|
-164 | Saved working directory and index state WIP on master: 1b65b17 added the index file |
-|
|
-165 | ---- |
-|
|
-166 | |
-|
|
-167 | |
-|
508 | ### Creating a Branch from a Stash ### |
-168 | ==== Creating a Branch from a Stash |
-
509 | |
-169 | |
-
510 | If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you’ve since modified, you’ll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run `git stash branch`, which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully: |
-170 | If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you’ve since modified, you’ll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run `git stash branch`, which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully: |
-
511 | |
-171 | |
-
. | |
-172 | [source,console] |
-
|
-173 | ---- |
-|
512 | $ git stash branch testchanges |
-174 | $ git stash branch testchanges |
-
513 | Switched to a new branch "testchanges" |
-175 | Switched to a new branch "testchanges" |
-
514 | # On branch testchanges |
-176 | # On branch testchanges |
-
515 | # Changes to be committed: |
-177 | # Changes to be committed: |
-
516 | # (use "git reset HEAD <file>..." to unstage) |
-178 | # (use "git reset HEAD <file>..." to unstage) |
-
517 | # |
-179 | # |
-
518 | # modified: index.html |
-180 | # modified: index.html |
-
519 | # |
-181 | # |
-
520 | # Changes not staged for commit: |
-182 | # Changed but not updated: |
-
521 | # (use "git add <file>..." to update what will be committed) |
-183 | # (use "git add <file>..." to update what will be committed) |
-
522 | # |
-184 | # |
-
523 | # modified: lib/simplegit.rb |
-185 | # modified: lib/simplegit.rb |
-
524 | # |
-186 | # |
-
525 | Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) |
-187 | Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) |
-
|
-188 | ---- |
-|
526 | |
-189 | |
-
527 | This is a nice shortcut to recover stashed work easily and work on it in a new branch. |
-190 | This is a nice shortcut to recover stashed work easily and work on it in a new branch. |
-
. | |
-191 | |
-
|
-192 | [[_git_clean]] |
-|
|
-193 | ==== Cleaning your Working Directory |
-|
|
-194 | |
-|
|
-195 | Finally, you may not want to stash some work or files in your working directory, but simply get rid of them. The `git clean` command will do this for you. |
-|
|
-196 | |
-|
|
-197 | Some common reasons for this might be to remove cruft that has been generated by merges or external tools or to remove build artifacts in order to run a clean build. |
-|
|
-198 | |
-|
|
-199 | You'll want to be pretty careful with this command, since it's designed to remove files from your working directory that are not tracked. If you change your mind, there is often no retrieving the content of those files. A safer option is to run `git stash --all` to remove everything but save it in a stash. |
-|
|
-200 | |
-|
|
-201 | Assuming you do want to remove cruft files or clean your working directory, you can do so with `git clean`. To remove all the untracked files in your working directory, you can run `git clean -f -d`, which removes any files and also any subdirectories that become empty as a result. The `-f` means 'force' or "really do this". |
-|
|
-202 | |
-|
|
-203 | If you ever want to see what it would do, you can run the command with the `-n` option, which means ``do a dry run and tell me what you _would_ have removed''. |
-|
|
-204 | |
-|
|
-205 | [source,console] |
-|
|
-206 | ---- |
-|
|
-207 | $ git clean -d -n |
-|
|
-208 | Would remove test.o |
-|
|
-209 | Would remove tmp/ |
-|
|
-210 | ---- |
-|
|
-211 | |
-|
|
-212 | By default, the `git clean` command will only remove untracked files that are not ignored. Any file that matches a pattern in your `.gitignore` or other ignore files will not be removed. If you want to remove those files too, such as to remove all `.o` files generated from a build so you can do a fully clean build, you can add a `-x` to the clean command. |
-|
|
-213 | |
-|
|
-214 | [source,console] |
-|
|
-215 | ---- |
-|
|
-216 | $ git status -s |
-|
|
-217 | M lib/simplegit.rb |
-|
|
-218 | ?? build.TMP |
-|
|
-219 | ?? tmp/ |
-|
|
-220 | |
-|
|
-221 | $ git clean -n -d |
-|
|
-222 | Would remove build.TMP |
-|
|
-223 | Would remove tmp/ |
-|
|
-224 | |
-|
|
-225 | $ git clean -n -d -x |
-|
|
-226 | Would remove build.TMP |
-|
|
-227 | Would remove test.o |
-|
|
-228 | Would remove tmp/ |
-|
|
-229 | ---- |
-|
|
-230 | |
-|
|
-231 | If you don't know what the `git clean` command is going to do, always run it with a `-n` first to double check before changing the `-n` to a `-f` and doing it for real. The other way you can be careful about the process is to run it with the `-i` or ``interactive'' flag. |
-|
|
-232 | |
-|
|
-233 | This will run the clean command in an interactive mode. |
-|
|
-234 | |
-|
|
-235 | [source,console] |
-|
|
-236 | ---- |
-|
|
-237 | $ git clean -x -i |
-|
|
-238 | Would remove the following items: |
-|
|
-239 | build.TMP test.o |
-|
|
-240 | *** Commands *** |
-|
|
-241 | 1: clean 2: filter by pattern 3: select by numbers 4: ask each 5: quit |
-|
|
-242 | 6: help |
-|
|
-243 | What now> |
-|
|
-244 | ---- |
-|
528 | |
-245 | |
-
.529 | ## Rewriting History ## |
-246 | This way you can step through each file individually or specify patterns for deletion interactively. |
-
530 | |
- |
-|
531 | Many times, when working with Git, you may want to revise your commit history for some reason. One of the great things about Git is that it allows you to make decisions at the last possible moment. You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with the stash command, and you can rewrite commits that already happened so they look like they happened in a different way. This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others. |
- |
-|
532 | |
- |
-|
533 | In this section, you’ll cover how to accomplish these very useful tasks so that you can make your commit history look the way you want before you share it with others. |
- |
-|
534 | |
- |
-|
535 | ### Changing the Last Commit ### |
- |
-|
536 | |
- |
-|
537 | Changing your last commit is probably the most common rewriting of history that you’ll do. You’ll often want to do two basic things to your last commit: change the commit message, or change the snapshot you just recorded by adding, changing and removing files. |
- |
-|
538 | |
- |
-|
539 | If you only want to modify your last commit message, it’s very simple: |
- |
-|
540 | |
- |
-|
541 | $ git commit --amend |
- |
-|
542 | |
- |
-|
543 | That drops you into your text editor, which has your last commit message in it, ready for you to modify the message. When you save and close the editor, the editor writes a new commit containing that message and makes it your new last commit. |
- |
-|
544 | |
- |
-|
545 | If you’ve committed and then you want to change the snapshot you committed by adding or changing files, possibly because you forgot to add a newly created file when you originally committed, the process works basically the same way. You stage the changes you want by editing a file and running `git add` on it or `git rm` to a tracked file, and the subsequent `git commit --amend` takes your current staging area and makes it the snapshot for the new commit. |
- |
-|
546 | |
- |
-|
547 | You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it. |
- |
-|
548 | |
- |
-|
549 | ### Changing Multiple Commit Messages ### |
- |
-|
550 | |
- |
-|
551 | To modify a commit that is farther back in your history, you must move to more complex tools. Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD they were originally based on instead of moving them to another one. With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the `-i` option to `git rebase`. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto. |
- |
-|
552 | |
- |
-|
553 | For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to `git rebase -i` the parent of the last commit you want to edit, which is `HEAD~2^` or `HEAD~3`. It may be easier to remember the `~3` because you’re trying to edit the last three commits; but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit: |
- |
-|
554 | |
- |
-|
555 | $ git rebase -i HEAD~3 |
- |
-|
556 | |
- |
-|
557 | Remember again that this is a rebasing command — every commit included in the range `HEAD~3..HEAD` will be rewritten, whether you change the message or not. Don’t include any commit you’ve already pushed to a central server — doing so will confuse other developers by providing an alternate version of the same change. |
- |
-|
558 | |
- |
-|
559 | Running this command gives you a list of commits in your text editor that looks something like this: |
- |
-|
560 | |
- |
-|
561 | pick f7f3f6d changed my name a bit |
- |
-|
562 | pick 310154e updated README formatting and added blame |
- |
-|
563 | pick a5f4a0d added cat-file |
- |
-|
564 | |
- |
-|
565 | # Rebase 710f0f8..a5f4a0d onto 710f0f8 |
- |
-|
566 | # |
- |
-|
567 | # Commands: |
- |
-|
568 | # p, pick = use commit |
- |
-|
569 | # r, reword = use commit, but edit the commit message |
- |
-|
570 | # e, edit = use commit, but stop for amending |
- |
-|
571 | # s, squash = use commit, but meld into previous commit |
- |
-|
572 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
573 | # x, exec = run command (the rest of the line) using shell |
- |
-|
574 | # |
- |
-|
575 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
576 | # |
- |
-|
577 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
578 | # |
- |
-|
579 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
580 | # |
- |
-|
581 | # Note that empty commits are commented out |
- |
-|
582 | |
- |
-|
583 | It’s important to note that these commits are listed in the opposite order than you normally see them using the `log` command. If you run a `log`, you see something like this: |
- |
-|
584 | |
- |
-|
585 | $ git log --pretty=format:"%h %s" HEAD~3..HEAD |
- |
-|
586 | a5f4a0d added cat-file |
- |
-|
587 | 310154e updated README formatting and added blame |
- |
-|
588 | f7f3f6d changed my name a bit |
- |
-|
589 | |
- |
-|
590 | Notice the reverse order. The interactive rebase gives you a script that it’s going to run. It will start at the commit you specify on the command line (`HEAD~3`) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that’s the first one it will replay. |
- |
-|
591 | |
- |
-|
592 | You need to edit the script so that it stops at the commit you want to edit. To do so, change the word pick to the word edit for each of the commits you want the script to stop after. For example, to modify only the third commit message, you change the file to look like this: |
- |
-|
593 | |
- |
-|
594 | edit f7f3f6d changed my name a bit |
- |
-|
595 | pick 310154e updated README formatting and added blame |
- |
-|
596 | pick a5f4a0d added cat-file |
- |
-|
597 | |
- |
-|
598 | When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message: |
- |
-|
599 | |
- |
-|
600 | <!-- This is actually weird, as the SHA-1 of 7482e0d is not present in the list, |
- |
-|
601 | nor is the commit message. Please review |
- |
-|
602 | --> |
- |
-|
603 | |
- |
-|
604 | $ git rebase -i HEAD~3 |
- |
-|
605 | Stopped at 7482e0d... updated the gemspec to hopefully work better |
- |
-|
606 | You can amend the commit now, with |
- |
-|
607 | |
- |
-|
608 | git commit --amend |
- |
-|
609 | |
- |
-|
610 | Once you’re satisfied with your changes, run |
- |
-|
611 | |
- |
-|
612 | git rebase --continue |
- |
-|
613 | |
- |
-|
614 | These instructions tell you exactly what to do. Type |
- |
-|
615 | |
- |
-|
616 | $ git commit --amend |
- |
-|
617 | |
- |
-|
618 | Change the commit message, and exit the editor. Then, run |
- |
-|
619 | |
- |
-|
620 | $ git rebase --continue |
- |
-|
621 | |
- |
-|
622 | This command will apply the other two commits automatically, and then you’re done. If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit. Each time, Git will stop, let you amend the commit, and continue when you’re finished. |
- |
-|
623 | |
- |
-|
624 | ### Reordering Commits ### |
- |
-|
625 | |
- |
-|
626 | You can also use interactive rebases to reorder or remove commits entirely. If you want to remove the "added cat-file" commit and change the order in which the other two commits are introduced, you can change the rebase script from this |
- |
-|
627 | |
- |
-|
628 | pick f7f3f6d changed my name a bit |
- |
-|
629 | pick 310154e updated README formatting and added blame |
- |
-|
630 | pick a5f4a0d added cat-file |
- |
-|
631 | |
- |
-|
632 | to this: |
- |
-|
633 | |
- |
-|
634 | pick 310154e updated README formatting and added blame |
- |
-|
635 | pick f7f3f6d changed my name a bit |
- |
-|
636 | |
- |
-|
637 | When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies `310154e` and then `f7f3f6d`, and then stops. You effectively change the order of those commits and remove the "added cat-file" commit completely. |
- |
-|
638 | |
- |
-|
639 | ### Squashing Commits ### |
- |
-|
640 | |
- |
-|
641 | It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool. The script puts helpful instructions in the rebase message: |
- |
-|
642 | |
- |
-|
643 | # |
- |
-|
644 | # Commands: |
- |
-|
645 | # p, pick = use commit |
- |
-|
646 | # r, reword = use commit, but edit the commit message |
- |
-|
647 | # e, edit = use commit, but stop for amending |
- |
-|
648 | # s, squash = use commit, but meld into previous commit |
- |
-|
649 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
650 | # x, exec = run command (the rest of the line) using shell |
- |
-|
651 | # |
- |
-|
652 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
653 | # |
- |
-|
654 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
655 | # |
- |
-|
656 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
657 | # |
- |
-|
658 | # Note that empty commits are commented out |
- |
-|
659 | |
- |
-|
660 | If, instead of "pick" or "edit", you specify "squash", Git applies both that change and the change directly before it and makes you merge the commit messages together. So, if you want to make a single commit from these three commits, you make the script look like this: |
- |
-|
661 | |
- |
-|
662 | pick f7f3f6d changed my name a bit |
- |
-|
663 | squash 310154e updated README formatting and added blame |
- |
-|
664 | squash a5f4a0d added cat-file |
- |
-|
665 | |
- |
-|
666 | When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages: |
- |
-|
667 | |
- |
-|
668 | # This is a combination of 3 commits. |
- |
-|
669 | # The first commit's message is: |
- |
-|
670 | changed my name a bit |
- |
-|
671 | |
- |
-|
672 | # This is the 2nd commit message: |
- |
-|
673 | |
- |
-|
674 | updated README formatting and added blame |
- |
-|
675 | |
- |
-|
676 | # This is the 3rd commit message: |
- |
-|
677 | |
- |
-|
678 | added cat-file |
- |
-|
679 | |
- |
-|
680 | When you save that, you have a single commit that introduces the changes of all three previous commits. |
- |
-|
681 | |
- |
-|
682 | ### Splitting a Commit ### |
- |
-|
683 | |
- |
-|
684 | Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with. For example, suppose you want to split the middle commit of your three commits. Instead of "updated README formatting and added blame", you want to split it into two commits: "updated README formatting" for the first, and "added blame" for the second. You can do that in the `rebase -i` script by changing the instruction on the commit you want to split to "edit": |
- |
-|
685 | |
- |
-|
686 | pick f7f3f6d changed my name a bit |
- |
-|
687 | edit 310154e updated README formatting and added blame |
- |
-|
688 | pick a5f4a0d added cat-file |
- |
-|
689 | |
- |
-|
690 | When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (`f7f3f6d`), applies the second (`310154e`), and drops you to the console. There, you can do a mixed reset of that commit with `git reset HEAD^`, which effectively undoes that commit and leaves the modified files unstaged. Now you can take the changes that have been reset, and create multiple commits out of them. Simply stage and commit files until you have several commits, and run `git rebase --continue` when you’re done: |
- |
-|
691 | |
- |
-|
692 | $ git reset HEAD^ |
- |
-|
693 | $ git add README |
- |
-|
694 | $ git commit -m 'updated README formatting' |
- |
-|
695 | $ git add lib/simplegit.rb |
- |
-|
696 | $ git commit -m 'added blame' |
- |
-|
697 | $ git rebase --continue |
- |
-|
698 | |
- |
-|
699 | Git applies the last commit (`a5f4a0d`) in the script, and your history looks like this: |
- |
-|
700 | |
- |
-|
701 | $ git log -4 --pretty=format:"%h %s" |
- |
-|
702 | 1c002dd added cat-file |
- |
-|
703 | 9b29157 added blame |
- |
-|
704 | 35cfb2b updated README formatting |
- |
-|
705 | f3cc40e changed my name a bit |
- |
-|
706 | |
- |
-|
707 | Once again, this changes the SHAs of all the commits in your list, so make sure no commit shows up in that list that you’ve already pushed to a shared repository. |
- |
-|
708 | |
- |
-|
709 | ### The Nuclear Option: filter-branch ### |
- |
-|
710 | |
- |
-|
711 | There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way — for instance, changing your e-mail address globally or removing a file from every commit. The command is `filter-branch`, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite. However, it can be very useful. You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of. |
- |
-|
712 | |
- |
-|
713 | #### Removing a File from Every Commit #### |
- |
-|
714 | |
- |
-|
715 | This occurs fairly commonly. Someone accidentally commits a huge binary file with a thoughtless `git add .`, and you want to remove it everywhere. Perhaps you accidentally committed a file that contained a password, and you want to make your project open source. `filter-branch` is the tool you probably want to use to scrub your entire history. To remove a file named passwords.txt from your entire history, you can use the `--tree-filter` option to `filter-branch`: |
- |
-|
716 | |
- |
-|
717 | $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD |
- |
-|
718 | Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) |
- |
-|
719 | Ref 'refs/heads/master' was rewritten |
- |
-|
720 | |
- |
-|
721 | The `--tree-filter` option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt from every snapshot, whether it exists or not. If you want to remove all accidentally committed editor backup files, you can run something like `git filter-branch --tree-filter "rm -f *~" HEAD`. |
- |
-|
722 | |
- |
-|
723 | You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end. It’s generally a good idea to do this in a testing branch and then hard-reset your master branch after you’ve determined the outcome is what you really want. To run `filter-branch` on all your branches, you can pass `--all` to the command. |
- |
-|
724 | |
- |
-|
725 | #### Making a Subdirectory the New Root #### |
- |
-|
726 | |
- |
-|
727 | Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk, tags, and so on). If you want to make the `trunk` subdirectory be the new project root for every commit, `filter-branch` can help you do that, too: |
- |
-|
728 | |
- |
-|
729 | $ git filter-branch --subdirectory-filter trunk HEAD |
- |
-|
730 | Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) |
- |
-|
731 | Ref 'refs/heads/master' was rewritten |
- |
-|
732 | |
- |
-|
733 | Now your new project root is what was in the `trunk` subdirectory each time. Git will also automatically remove commits that did not affect the subdirectory. |
- |
-|
734 | |
- |
-|
735 | #### Changing E-Mail Addresses Globally #### |
- |
-|
736 | |
- |
-|
737 | Another common case is that you forgot to run `git config` to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with `filter-branch` as well. You need to be careful to change only the e-mail addresses that are yours, so you use `--commit-filter`: |
- |
-|
738 | |
- |
-|
739 | $ git filter-branch --commit-filter ' |
- |
-|
740 | if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; |
- |
-|
741 | then |
- |
-|
742 | GIT_AUTHOR_NAME="Scott Chacon"; |
- |
-|
743 | GIT_AUTHOR_EMAIL="schacon@example.com"; |
- |
-|
744 | git commit-tree "$@"; |
- |
-|
745 | else |
- |
-|
746 | git commit-tree "$@"; |
- |
-|
747 | fi' HEAD |
- |
-|
748 | |
- |
-|
749 | This goes through and rewrites every commit to have your new address. Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address. |
- |
-|
750 | |
- |
-|
751 | ### The Very Fast Nuclear Option: Big Friendly Giant Repo Cleaner (BFG) ### |
- |
-|
752 | |
- |
-|
753 | [Roberto Tyley](https://github.com/rtyley) has written a similar tool to `filter-branch` called the BFG. BFG cannot do as much as `filter-branch`, but it is _very_ fast and on a large repository this can make a big difference. If the change you want to make is in the scope of BFG capability, and you have performance issues, then you should consider using it. |
- |
-|
754 | |
- |
-|
755 | See the [BFG](http://rtyley.github.io/bfg-repo-cleaner/) website for details. |
- |
-|
756 | |
- |
-|
757 | ## Debugging with Git ## |
- |
-|
758 | |
- |
-|
759 | Git also provides a couple of tools to help you debug issues in your projects. Because Git is designed to work with nearly any type of project, these tools are pretty generic, but they can often help you hunt for a bug or culprit when things go wrong. |
- |
-|
760 | |
- |
-|
761 | ### File Annotation ### |
- |
-|
762 | |
- |
-|
763 | If you track down a bug in your code and want to know when it was introduced and why, file annotation is often your best tool. It shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with `git blame` to see when each line of the method was last edited and by whom. This example uses the `-L` option to limit the output to lines 12 through 22: |
- |
-|
764 | |
- |
-|
765 | $ git blame -L 12,22 simplegit.rb |
- |
-|
766 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master') |
- |
-|
767 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}") |
- |
-|
768 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end |
- |
-|
769 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15) |
- |
-|
770 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master') |
- |
-|
771 | 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}") |
- |
-|
772 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end |
- |
-|
773 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19) |
- |
-|
774 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path) |
- |
-|
775 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}") |
- |
-|
776 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end |
- |
-|
777 | |
- |
-|
778 | Notice that the first field is the partial SHA-1 of the commit that last modified that line. The next two fields are values extracted from that commit—the author name and the authored date of that commit — so you can easily see who modified that line and when. After that come the line number and the content of the file. Also note the `^4832fe2` commit lines, which designate that those lines were in this file’s original commit. That commit is when this file was first added to this project, and those lines have been unchanged since. This is a tad confusing, because now you’ve seen at least three different ways that Git uses the `^` to modify a commit SHA, but that is what it means here. |
- |
-|
779 | |
- |
-|
780 | Another cool thing about Git is that it doesn’t track file renames explicitly. It records the snapshots and then tries to figure out what was renamed implicitly, after the fact. One of the interesting features of this is that you can ask it to figure out all sorts of code movement as well. If you pass `-C` to `git blame`, Git analyzes the file you’re annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere. Recently, I was refactoring a file named `GITServerHandler.m` into multiple files, one of which was `GITPackUpload.m`. By blaming `GITPackUpload.m` with the `-C` option, I could see where sections of the code originally came from: |
- |
-|
781 | |
- |
-|
782 | $ git blame -C -L 141,153 GITPackUpload.m |
- |
-|
783 | f344f58d GITServerHandler.m (Scott 2009-01-04 141) |
- |
-|
784 | f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC |
- |
-|
785 | f344f58d GITServerHandler.m (Scott 2009-01-04 143) { |
- |
-|
786 | 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI |
- |
-|
787 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 145) |
- |
-|
788 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha; |
- |
-|
789 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g |
- |
-|
790 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 148) |
- |
-|
791 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI |
- |
-|
792 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 150) |
- |
-|
793 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) { |
- |
-|
794 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb |
- |
-|
795 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 153) |
- |
-|
796 | |
- |
-|
797 | This is really useful. Normally, you get as the original commit the commit where you copied the code over, because that is the first time you touched those lines in this file. Git tells you the original commit where you wrote those lines, even if it was in another file. |
- |
-|
798 | |
- |
-|
799 | ### Binary Search ### |
- |
-|
800 | |
- |
-|
801 | Annotating a file helps if you know where the issue is to begin with. If you don’t know what is breaking, and there have been dozens or hundreds of commits since the last state where you know the code worked, you’ll likely turn to `git bisect` for help. The `bisect` command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. |
- |
-|
802 | |
- |
-|
803 | Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that. You go back to your code, and it turns out you can reproduce the issue, but you can’t figure out what is going wrong. You can bisect the code to find out. First you run `git bisect start` to get things going, and then you use `git bisect bad` to tell the system that the current commit you’re on is broken. Then, you must tell bisect when the last known good state was, using `git bisect good [good_commit]`: |
- |
-|
804 | |
- |
-|
805 | $ git bisect start |
- |
-|
806 | $ git bisect bad |
- |
-|
807 | $ git bisect good v1.0 |
- |
-|
808 | Bisecting: 6 revisions left to test after this |
- |
-|
809 | [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo |
- |
-|
810 | |
- |
-|
811 | Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. If it does, then it was introduced sometime before this middle commit; if it doesn’t, then the problem was introduced sometime after the middle commit. It turns out there is no issue here, and you tell Git that by typing `git bisect good` and continue your journey: |
- |
-|
812 | |
- |
-|
813 | $ git bisect good |
- |
-|
814 | Bisecting: 3 revisions left to test after this |
- |
-|
815 | [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing |
- |
-|
816 | |
- |
-|
817 | Now you’re on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with `git bisect bad`: |
- |
-|
818 | |
- |
-|
819 | $ git bisect bad |
- |
-|
820 | Bisecting: 1 revisions left to test after this |
- |
-|
821 | [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table |
- |
-|
822 | |
- |
-|
823 | This commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and show some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug: |
- |
-|
824 | |
- |
-|
825 | $ git bisect good |
- |
-|
826 | b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit |
- |
-|
827 | commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 |
- |
-|
828 | Author: PJ Hyett <pjhyett@example.com> |
- |
-|
829 | Date: Tue Jan 27 14:48:32 2009 -0800 |
- |
-|
830 | |
- |
-|
831 | secure this thing |
- |
-|
832 | |
- |
-|
833 | :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730 |
- |
-|
834 | f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config |
- |
-|
835 | |
- |
-|
836 | When you’re finished, you should run `git bisect reset` to reset your HEAD to where you were before you started, or you’ll end up in a weird state: |
- |
-|
837 | |
- |
-|
838 | $ git bisect reset |
- |
-|
839 | |
- |
-|
840 | This is a powerful tool that can help you check hundreds of commits for an introduced bug in minutes. In fact, if you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate `git bisect`. First, you again tell it the scope of the bisect by providing the known bad and good commits. You can do this by listing them with the `bisect start` command if you want, listing the known bad commit first and the known good commit second: |
- |
-|
841 | |
- |
-|
842 | $ git bisect start HEAD v1.0 |
- |
-|
843 | $ git bisect run test-error.sh |
- |
-|
844 | |
- |
-|
845 | Doing so automatically runs `test-error.sh` on each checked-out commit until Git finds the first broken commit. You can also run something like `make` or `make tests` or whatever you have that runs automated tests for you. |
- |
-|
846 | |
- |
-|
847 | ## Submodules ## |
- |
-|
848 | |
- |
-|
849 | It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other. |
- |
-|
850 | |
- |
-|
851 | Here’s an example. Suppose you’re developing a web site and creating Atom feeds. Instead of writing your own Atom-generating code, you decide to use a library. You’re likely to have to either include this code from a shared library like a CPAN install or Ruby gem, or copy the source code into your own project tree. The issue with including the library is that it’s difficult to customize the library in any way and often more difficult to deploy it, because you need to make sure every client has that library available. The issue with vendoring the code into your own project is that any custom changes you make are difficult to merge when upstream changes become available. |
- |
-|
852 | |
- |
-|
853 | Git addresses this issue using submodules. Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. |
- |
-|
854 | |
- |
-|
855 | ### Starting with Submodules ### |
- |
-|
856 | |
- |
-|
857 | Suppose you want to add the Rack library (a Ruby web server gateway interface) to your project, possibly maintain your own changes to it, but continue to merge in upstream changes. The first thing you should do is clone the external repository into your subdirectory. You add external projects as submodules with the `git submodule add` command: |
- |
-|
858 | |
- |
-|
859 | $ git submodule add git://github.com/chneukirchen/rack.git rack |
- |
-|
860 | Initialized empty Git repository in /opt/subtest/rack/.git/ |
- |
-|
861 | remote: Counting objects: 3181, done. |
- |
-|
862 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
863 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
864 | Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done. |
- |
-|
865 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
866 | |
- |
-|
867 | Now you have the Rack project under a subdirectory named `rack` within your project. You can go into that subdirectory, make changes, add your own writable remote repository to push your changes into, fetch and merge from the original repository, and more. If you run `git status` right after you add the submodule, you see two things: |
- |
-|
868 | |
- |
-|
869 | $ git status |
- |
-|
870 | # On branch master |
- |
-|
871 | # Changes to be committed: |
- |
-|
872 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
873 | # |
- |
-|
874 | # new file: .gitmodules |
- |
-|
875 | # new file: rack |
- |
-|
876 | # |
- |
-|
877 | |
- |
-|
878 | First you notice the `.gitmodules` file. This is a configuration file that stores the mapping between the project’s URL and the local subdirectory you’ve pulled it into: |
- |
-|
879 | |
- |
-|
880 | $ cat .gitmodules |
- |
-|
881 | [submodule "rack"] |
- |
-|
882 | path = rack |
- |
-|
883 | url = git://github.com/chneukirchen/rack.git |
- |
-|
884 | |
- |
-|
885 | If you have multiple submodules, you’ll have multiple entries in this file. It’s important to note that this file is version-controlled with your other files, like your `.gitignore` file. It’s pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from. |
- |
-|
886 | |
- |
-|
887 | The other listing in the `git status` output is the rack entry. If you run `git diff` on that, you see something interesting: |
- |
-|
888 | |
- |
-|
889 | $ git diff --cached rack |
- |
-|
890 | diff --git a/rack b/rack |
- |
-|
891 | new file mode 160000 |
- |
-|
892 | index 0000000..08d709f |
- |
-|
893 | --- /dev/null |
- |
-|
894 | +++ b/rack |
- |
-|
895 | @@ -0,0 +1 @@ |
- |
-|
896 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
897 | |
- |
-|
898 | Although `rack` is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git records it as a particular commit from that repository. When you make changes and commit in that subdirectory, the superproject notices that the HEAD there has changed and records the exact commit you’re currently working off of; that way, when others clone this project, they can re-create the environment exactly. |
- |
-|
899 | |
- |
-|
900 | This is an important point with submodules: you record them as the exact commit they’re at. You can’t record a submodule at `master` or some other symbolic reference. |
- |
-|
901 | |
- |
-|
902 | When you commit, you see something like this: |
- |
-|
903 | |
- |
-|
904 | $ git commit -m 'first commit with submodule rack' |
- |
-|
905 | [master 0550271] first commit with submodule rack |
- |
-|
906 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
907 | create mode 100644 .gitmodules |
- |
-|
908 | create mode 160000 rack |
- |
-|
909 | |
- |
-|
910 | Notice the 160000 mode for the rack entry. That is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file. |
- |
-|
911 | |
- |
-|
912 | You can treat the `rack` directory as a separate project and then update your superproject from time to time with a pointer to the latest commit in that subproject. All the Git commands work independently in the two directories: |
- |
-|
913 | |
- |
-|
914 | $ git log -1 |
- |
-|
915 | commit 0550271328a0038865aad6331e620cd7238601bb |
- |
-|
916 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
917 | Date: Thu Apr 9 09:03:56 2009 -0700 |
- |
-|
918 | |
- |
-|
919 | first commit with submodule rack |
- |
-|
920 | $ cd rack/ |
- |
-|
921 | $ git log -1 |
- |
-|
922 | commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
923 | Author: Christian Neukirchen <chneukirchen@gmail.com> |
- |
-|
924 | Date: Wed Mar 25 14:49:04 2009 +0100 |
- |
-|
925 | |
- |
-|
926 | Document version change |
- |
-|
927 | |
- |
-|
928 | ### Cloning a Project with Submodules ### |
- |
-|
929 | |
- |
-|
930 | Here you’ll clone a project with a submodule in it. When you receive such a project, you get the directories that contain submodules, but none of the files yet: |
- |
-|
931 | |
- |
-|
932 | $ git clone git://github.com/schacon/myproject.git |
- |
-|
933 | Initialized empty Git repository in /opt/myproject/.git/ |
- |
-|
934 | remote: Counting objects: 6, done. |
- |
-|
935 | remote: Compressing objects: 100% (4/4), done. |
- |
-|
936 | remote: Total 6 (delta 0), reused 0 (delta 0) |
- |
-|
937 | Receiving objects: 100% (6/6), done. |
- |
-|
938 | $ cd myproject |
- |
-|
939 | $ ls -l |
- |
-|
940 | total 8 |
- |
-|
941 | -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README |
- |
-|
942 | drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack |
- |
-|
943 | $ ls rack/ |
- |
-|
944 | $ |
- |
-|
945 | |
- |
-|
946 | The `rack` directory is there, but empty. You must run two commands: `git submodule init` to initialize your local configuration file, and `git submodule update` to fetch all the data from that project and check out the appropriate commit listed in your superproject: |
- |
-|
947 | |
- |
-|
948 | $ git submodule init |
- |
-|
949 | Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack' |
- |
-|
950 | $ git submodule update |
- |
-|
951 | Initialized empty Git repository in /opt/myproject/rack/.git/ |
- |
-|
952 | remote: Counting objects: 3181, done. |
- |
-|
953 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
954 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
955 | Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done. |
- |
-|
956 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
957 | Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433' |
- |
-|
958 | |
- |
-|
959 | Now your `rack` subdirectory is at the exact state it was in when you committed earlier. If another developer makes changes to the rack code and commits, and you pull that reference down and merge it in, you get something a bit odd: |
- |
-|
960 | |
- |
-|
961 | $ git merge origin/master |
- |
-|
962 | Updating 0550271..85a3eee |
- |
-|
963 | Fast forward |
- |
-|
964 | rack | 2 +- |
- |
-|
965 | 1 files changed, 1 insertions(+), 1 deletions(-) |
- |
-|
966 | [master*]$ git status |
- |
-|
967 | # On branch master |
- |
-|
968 | # Changes not staged for commit: |
- |
-|
969 | # (use "git add <file>..." to update what will be committed) |
- |
-|
970 | # (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
971 | # |
- |
-|
972 | # modified: rack |
- |
-|
973 | # |
- |
-|
974 | |
- |
-|
975 | You merged in what is basically a change to the pointer for your submodule; but it doesn’t update the code in the submodule directory, so it looks like you have a dirty state in your working directory: |
- |
-|
976 | |
- |
-|
977 | $ git diff |
- |
-|
978 | diff --git a/rack b/rack |
- |
-|
979 | index 6c5e70b..08d709f 160000 |
- |
-|
980 | --- a/rack |
- |
-|
981 | +++ b/rack |
- |
-|
982 | @@ -1 +1 @@ |
- |
-|
983 | -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
984 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
985 | |
- |
-|
986 | This is the case because the pointer you have for the submodule isn’t what is actually in the submodule directory. To fix this, you must run `git submodule update` again: |
- |
-|
987 | |
- |
-|
988 | $ git submodule update |
- |
-|
989 | remote: Counting objects: 5, done. |
- |
-|
990 | remote: Compressing objects: 100% (3/3), done. |
- |
-|
991 | remote: Total 3 (delta 1), reused 2 (delta 0) |
- |
-|
992 | Unpacking objects: 100% (3/3), done. |
- |
-|
993 | From git@github.com:schacon/rack |
- |
-|
994 | 08d709f..6c5e70b master -> origin/master |
- |
-|
995 | Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0' |
- |
-|
996 | |
- |
-|
997 | You have to do this every time you pull down a submodule change in the main project. It’s strange, but it works. |
- |
-|
998 | |
- |
-|
999 | One common problem happens when a developer makes a change locally in a submodule but doesn’t push it to a public server. Then, they commit a pointer to that non-public state and push up the superproject. When other developers try to run `git submodule update`, the submodule system can’t find the commit that is referenced, because it exists only on the first developer’s system. If that happens, you see an error like this: |
- |
-|
1000 | |
- |
-|
1001 | $ git submodule update |
- |
-|
1002 | fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
1003 | Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack' |
- |
-|
1004 | |
- |
-|
1005 | You have to see who last changed the submodule: |
- |
-|
1006 | |
- |
-|
1007 | $ git log -1 rack |
- |
-|
1008 | commit 85a3eee996800fcfa91e2119372dd4172bf76678 |
- |
-|
1009 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
1010 | Date: Thu Apr 9 09:19:14 2009 -0700 |
- |
-|
1011 | |
- |
-|
1012 | added a submodule reference I will never make public. hahahahaha! |
- |
-|
1013 | |
- |
-|
1014 | Then, you e-mail that guy and yell at him. |
- |
-|
1015 | |
- |
-|
1016 | ### Superprojects ### |
- |
-|
1017 | |
- |
-|
1018 | Sometimes, developers want to get a combination of a large project’s subdirectories, depending on what team they’re on. This is common if you’re coming from CVS or Subversion, where you’ve defined a module or collection of subdirectories, and you want to keep this type of workflow. |
- |
-|
1019 | |
- |
-|
1020 | A good way to do this in Git is to make each of the subdirectories a separate Git repository and then create superproject Git repositories that contain multiple submodules. A benefit of this approach is that you can more specifically define the relationships between the projects with tags and branches in the superprojects. |
- |
-|
1021 | |
- |
-|
1022 | ### Issues with Submodules ### |
- |
-|
1023 | |
- |
-|
1024 | Using submodules isn’t without hiccups, however. First, you must be relatively careful when working in the submodule directory. When you run `git submodule update`, it checks out the specific version of the project, but not within a branch. This is called having a detached HEAD — it means the HEAD file points directly to a commit, not to a symbolic reference. The issue is that you generally don’t want to work in a detached HEAD environment, because it’s easy to lose changes. If you do an initial `submodule update`, commit in that submodule directory without creating a branch to work in, and then run `git submodule update` again from the superproject without committing in the meantime, Git will overwrite your changes without telling you. Technically you won’t lose the work, but you won’t have a branch pointing to it, so it will be somewhat difficult to retrieve. |
- |
-|
1025 | |
- |
-|
1026 | To avoid this issue, create a branch when you work in a submodule directory with `git checkout -b work` or something equivalent. When you do the submodule update a second time, it will still revert your work, but at least you have a pointer to get back to. |
- |
-|
1027 | |
- |
-|
1028 | Switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory: |
- |
-|
1029 | |
- |
-|
1030 | $ git checkout -b rack |
- |
-|
1031 | Switched to a new branch "rack" |
- |
-|
1032 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1033 | Initialized empty Git repository in /opt/myproj/rack/.git/ |
- |
-|
1034 | ... |
- |
-|
1035 | Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done. |
- |
-|
1036 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1037 | $ git commit -am 'added rack submodule' |
- |
-|
1038 | [rack cc49a69] added rack submodule |
- |
-|
1039 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
1040 | create mode 100644 .gitmodules |
- |
-|
1041 | create mode 160000 rack |
- |
-|
1042 | $ git checkout master |
- |
-|
1043 | Switched to branch "master" |
- |
-|
1044 | $ git status |
- |
-|
1045 | # On branch master |
- |
-|
1046 | # Untracked files: |
- |
-|
1047 | # (use "git add <file>..." to include in what will be committed) |
- |
-|
1048 | # |
- |
-|
1049 | # rack/ |
- |
-|
1050 | |
- |
-|
1051 | You have to either move it out of the way or remove it, in which case you have to clone it again when you switch back—and you may lose local changes or branches that you didn’t push up. |
- |
-|
1052 | |
- |
-|
1053 | The last main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have the rack files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run `submodule add`, Git yells at you: |
- |
-|
1054 | |
- |
-|
1055 | $ rm -Rf rack/ |
- |
-|
1056 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1057 | 'rack' already exists in the index |
- |
-|
1058 | |
- |
-|
1059 | You have to unstage the `rack` directory first. Then you can add the submodule: |
- |
-|
1060 | |
- |
-|
1061 | $ git rm -r rack |
- |
-|
1062 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1063 | Initialized empty Git repository in /opt/testsub/rack/.git/ |
- |
-|
1064 | remote: Counting objects: 3184, done. |
- |
-|
1065 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1066 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1067 | Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done. |
- |
-|
1068 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1069 | |
- |
-|
1070 | Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule — you get this error: |
- |
-|
1071 | |
- |
-|
1072 | $ git checkout master |
- |
-|
1073 | error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge. |
- |
-|
1074 | |
- |
-|
1075 | You have to move the `rack` submodule directory out of the way before you can switch to a branch that doesn’t have it: |
- |
-|
1076 | |
- |
-|
1077 | $ mv rack /tmp/ |
- |
-|
1078 | $ git checkout master |
- |
-|
1079 | Switched to branch "master" |
- |
-|
1080 | $ ls |
- |
-|
1081 | README rack |
- |
-|
1082 | |
- |
-|
1083 | Then, when you switch back, you get an empty `rack` directory. You can either run `git submodule update` to reclone, or you can move your `/tmp/rack` directory back into the empty directory. |
- |
-|
1084 | |
- |
-|
1085 | ## Subtree Merging ## |
- |
-|
1086 | |
- |
-|
1087 | Now that you’ve seen the difficulties of the submodule system, let’s look at an alternate way to solve the same problem. When Git merges, it looks at what it has to merge together and then chooses an appropriate merging strategy to use. If you’re merging two branches, Git uses a _recursive_ strategy. If you’re merging more than two branches, Git picks the _octopus_ strategy. These strategies are automatically chosen for you because the recursive strategy can handle complex three-way merge situations — for example, more than one common ancestor — but it can only handle merging two branches. The octopus merge can handle multiple branches but is more cautious to avoid difficult conflicts, so it’s chosen as the default strategy if you’re trying to merge more than two branches. |
- |
-|
1088 | |
- |
-|
1089 | However, there are other strategies you can choose as well. One of them is the _subtree_ merge, and you can use it to deal with the subproject issue. Here you’ll see how to do the same rack embedding as in the last section, but using subtree merges instead. |
- |
-|
1090 | |
- |
-|
1091 | The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one and vice versa. When you specify a subtree merge, Git is smart enough to figure out that one is a subtree of the other and merge appropriately — it’s pretty amazing. |
- |
-|
1092 | |
- |
-|
1093 | You first add the Rack application to your project. You add the Rack project as a remote reference in your own project and then check it out into its own branch: |
- |
-|
1094 | |
- |
-|
1095 | $ git remote add rack_remote git@github.com:schacon/rack.git |
- |
-|
1096 | $ git fetch rack_remote |
- |
-|
1097 | warning: no common commits |
- |
-|
1098 | remote: Counting objects: 3184, done. |
- |
-|
1099 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1100 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1101 | Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. |
- |
-|
1102 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1103 | From git@github.com:schacon/rack |
- |
-|
1104 | * [new branch] build -> rack_remote/build |
- |
-|
1105 | * [new branch] master -> rack_remote/master |
- |
-|
1106 | * [new branch] rack-0.4 -> rack_remote/rack-0.4 |
- |
-|
1107 | * [new branch] rack-0.9 -> rack_remote/rack-0.9 |
- |
-|
1108 | $ git checkout -b rack_branch rack_remote/master |
- |
-|
1109 | Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. |
- |
-|
1110 | Switched to a new branch "rack_branch" |
- |
-|
1111 | |
- |
-|
1112 | Now you have the root of the Rack project in your `rack_branch` branch and your own project in the `master` branch. If you check out one and then the other, you can see that they have different project roots: |
- |
-|
1113 | |
- |
-|
1114 | $ ls |
- |
-|
1115 | AUTHORS KNOWN-ISSUES Rakefile contrib lib |
- |
-|
1116 | COPYING README bin example test |
- |
-|
1117 | $ git checkout master |
- |
-|
1118 | Switched to branch "master" |
- |
-|
1119 | $ ls |
- |
-|
1120 | README |
- |
-|
1121 | |
- |
-|
1122 | You want to pull the Rack project into your `master` project as a subdirectory. You can do that in Git with `git read-tree`. You’ll learn more about `read-tree` and its friends in Chapter 9, but for now know that it reads the root tree of one branch into your current staging area and working directory. You just switched back to your `master` branch, and you pull the `rack` branch into the `rack` subdirectory of your `master` branch of your main project: |
- |
-|
1123 | |
- |
-|
1124 | $ git read-tree --prefix=rack/ -u rack_branch |
- |
-|
1125 | |
- |
-|
1126 | When you commit, it looks like you have all the Rack files under that subdirectory — as though you copied them in from a tarball. What gets interesting is that you can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, you can pull in upstream changes by switching to that branch and pulling: |
- |
-|
1127 | |
- |
-|
1128 | $ git checkout rack_branch |
- |
-|
1129 | $ git pull |
- |
-|
1130 | |
- |
-|
1131 | Then, you can merge those changes back into your master branch. You can use `git merge -s subtree` and it will work fine; but Git will also merge the histories together, which you probably don’t want. To pull in the changes and prepopulate the commit message, use the `--squash` and `--no-commit` options as well as the `-s subtree` strategy option: |
- |
-|
1132 | |
- |
-|
1133 | $ git checkout master |
- |
-|
1134 | $ git merge --squash -s subtree --no-commit rack_branch |
- |
-|
1135 | Squash commit -- not updating HEAD |
- |
-|
1136 | Automatic merge went well; stopped before committing as requested |
- |
-|
1137 | |
- |
-|
1138 | All the changes from your Rack project are merged in and ready to be committed locally. You can also do the opposite — make changes in the `rack` subdirectory of your master branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. |
- |
-|
1139 | |
- |
-|
1140 | To get a diff between what you have in your `rack` subdirectory and the code in your `rack_branch` branch — to see if you need to merge them — you can’t use the normal `diff` command. Instead, you must run `git diff-tree` with the branch you want to compare to: |
- |
-|
1141 | |
- |
-|
1142 | $ git diff-tree -p rack_branch |
- |
-|
1143 | |
- |
-|
1144 | Or, to compare what is in your `rack` subdirectory with what the `master` branch on the server was the last time you fetched, you can run |
- |
-|
1145 | |
- |
-|
1146 | $ git diff-tree -p rack_remote/master |
- |
-|
1147 | |
- |
-|
1148 | ## Summary ## |
- |
-|
1149 | |
- |
-|
1150 | You’ve seen a number of advanced tools that allow you to manipulate your commits and staging area more precisely. When you notice issues, you should be able to easily figure out what commit introduced them, when, and by whom. If you want to use subprojects in your project, you’ve learned a few ways to accommodate those needs. At this point, you should be able to do most of the things in Git that you’ll need on the command line day to day and feel comfortable doing so. |
- |
-|
1151 | |
-247 | |
-
C:\Users\15625\Documents\Git\progit\en\06-git-tools\01-chapter6.markdown | -C:\Users\15625\Documents\Git\progit2-ja\book\07-git-tools\sections\submodules.asc | -||
---|---|---|---|
.1 | # Git Tools # |
-1 | [[_git_submodules]] |
-
2 | |
- |
-|
3 | 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. |
- |
-|
4 | |
- |
-|
5 | 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. |
- |
-|
6 | |
- |
-|
7 | ## Revision Selection ## |
- |
-|
8 | |
- |
-|
9 | 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. |
- |
-|
10 | |
- |
-|
11 | ### Single Revisions ### |
- |
-|
12 | |
- |
-|
13 | 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. |
- |
-|
14 | |
- |
-|
15 | ### Short SHA ### |
- |
-|
16 | |
- |
-|
17 | 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. |
- |
-|
18 | |
- |
-|
19 | For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: |
- |
-|
20 | |
- |
-|
21 | $ git log |
- |
-|
22 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
23 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
24 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
25 | |
- |
-|
26 | fixed refs handling, added gc auto, updated tests |
- |
-|
27 | |
- |
-|
28 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
29 | Merge: 1c002dd... 35cfb2b... |
- |
-|
30 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
31 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
32 | |
- |
-|
33 | Merge commit 'phedders/rdocs' |
- |
-|
34 | |
- |
-|
35 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
36 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
37 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
38 | |
- |
-|
39 | added some blame and merge stuff |
- |
-|
40 | |
- |
-|
41 | In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): |
- |
-|
42 | |
- |
-|
43 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
44 | $ git show 1c002dd4b536e7479f |
- |
-|
45 | $ git show 1c002d |
- |
-|
46 | |
- |
-|
47 | 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: |
- |
-|
48 | |
- |
-|
49 | $ git log --abbrev-commit --pretty=oneline |
- |
-|
50 | ca82a6d changed the version number |
- |
-|
51 | 085bb3b removed unnecessary test code |
- |
-|
52 | a11bef0 first commit |
- |
-|
53 | |
- |
-|
54 | 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. |
- |
-|
55 | |
- |
-|
56 | ### A SHORT NOTE ABOUT SHA-1 ### |
- |
-|
57 | |
- |
-|
58 | 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? |
- |
-|
59 | |
- |
-|
60 | 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. |
- |
-|
61 | |
- |
-|
62 | 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. |
- |
-|
63 | |
- |
-|
64 | 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. 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. |
- |
-|
65 | |
- |
-|
66 | ### Branch References ### |
- |
-|
67 | |
- |
-|
68 | The most straightforward way to specify a commit requires that it have 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`: |
- |
-|
69 | |
- |
-|
70 | $ git show ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
71 | $ git show topic1 |
- |
-|
72 | |
- |
-|
73 | 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 Chapter 9 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. |
- |
-|
74 | |
- |
-|
75 | $ git rev-parse topic1 |
- |
-|
76 | ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
77 | |
- |
-|
78 | ### RefLog Shortnames ### |
- |
-|
79 | |
- |
-|
80 | 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. |
- |
-|
81 | |
- |
-|
82 | You can see your reflog by using `git reflog`: |
- |
-|
83 | |
- |
-|
84 | $ git reflog |
- |
-|
85 | 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated |
- |
-|
86 | d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. |
- |
-|
87 | 1c002dd HEAD@{2}: commit: added some blame and merge stuff |
- |
-|
88 | 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD |
- |
-|
89 | 95df984 HEAD@{4}: commit: # This is a combination of two commits. |
- |
-|
90 | 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD |
- |
-|
91 | 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD |
- |
-|
92 | |
- |
-|
93 | 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: |
- |
-|
94 | |
- |
-|
95 | $ git show HEAD@{5} |
- |
-|
96 | |
- |
-|
97 | 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 |
- |
-|
98 | |
- |
-|
99 | $ git show master@{yesterday} |
- |
-|
100 | |
- |
-|
101 | 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. |
- |
-|
102 | |
- |
-|
103 | To see reflog information formatted like the `git log` output, you can run `git log -g`: |
- |
-|
104 | |
- |
-|
105 | $ git log -g master |
- |
-|
106 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
107 | Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) |
- |
-|
108 | Reflog message: commit: fixed refs handling, added gc auto, updated |
- |
-|
109 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
110 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
111 | |
- |
-|
112 | fixed refs handling, added gc auto, updated tests |
- |
-|
113 | |
- |
-|
114 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
115 | Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) |
- |
-|
116 | Reflog message: merge phedders/rdocs: Merge made by recursive. |
- |
-|
117 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
118 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
119 | |
- |
-|
120 | Merge commit 'phedders/rdocs' |
- |
-|
121 | |
- |
-|
122 | 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. |
- |
-|
123 | |
- |
-|
124 | ### Ancestry References ### |
- |
-|
125 | |
- |
-|
126 | 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. |
- |
-|
127 | Suppose you look at the history of your project: |
- |
-|
128 | |
- |
-|
129 | $ git log --pretty=format:'%h %s' --graph |
- |
-|
130 | * 734713b fixed refs handling, added gc auto, updated tests |
- |
-|
131 | * d921970 Merge commit 'phedders/rdocs' |
- |
-|
132 | |\ |
- |
-|
133 | | * 35cfb2b Some rdoc changes |
- |
-|
134 | * | 1c002dd added some blame and merge stuff |
- |
-|
135 | |/ |
- |
-|
136 | * 1c36188 ignore *.gem |
- |
-|
137 | * 9b29157 add open3_detach to gemspec file list |
- |
-|
138 | |
- |
-|
139 | Then, you can see the previous commit by specifying `HEAD^`, which means "the parent of HEAD": |
- |
-|
140 | |
- |
-|
141 | $ git show HEAD^ |
- |
-|
142 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
143 | Merge: 1c002dd... 35cfb2b... |
- |
-|
144 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
145 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
146 | |
- |
-|
147 | Merge commit 'phedders/rdocs' |
- |
-|
148 | |
- |
-|
149 | 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: |
- |
-|
150 | |
- |
-|
151 | $ git show d921970^ |
- |
-|
152 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
153 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
154 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
155 | |
- |
-|
156 | added some blame and merge stuff |
- |
-|
157 | |
- |
-|
158 | $ git show d921970^2 |
- |
-|
159 | commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 |
- |
-|
160 | Author: Paul Hedderly <paul+git@mjr.org> |
- |
-|
161 | Date: Wed Dec 10 22:22:03 2008 +0000 |
- |
-|
162 | |
- |
-|
163 | Some rdoc changes |
- |
-|
164 | |
- |
-|
165 | 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 |
- |
-|
166 | |
- |
-|
167 | $ git show HEAD~3 |
- |
-|
168 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
169 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
170 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
171 | |
- |
-|
172 | ignore *.gem |
- |
-|
173 | |
- |
-|
174 | This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: |
- |
-|
175 | |
- |
-|
176 | $ git show HEAD^^^ |
- |
-|
177 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
178 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
179 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
180 | |
- |
-|
181 | ignore *.gem |
- |
-|
182 | |
- |
-|
183 | 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. |
- |
-|
184 | |
- |
-|
185 | ### Commit Ranges ### |
- |
-|
186 | |
- |
-|
187 | 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?" |
- |
-|
188 | |
- |
-|
189 | #### Double Dot #### |
- |
-|
190 | |
- |
-|
191 | 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 Figure 6-1. |
- |
-|
192 | |
- |
-|
193 | Insert 18333fig0601.png |
- |
-|
194 | Figure 6-1. Example history for range selection. |
- |
-|
195 | |
- |
-|
196 | 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: |
- |
-|
197 | |
- |
-|
198 | $ git log master..experiment |
- |
-|
199 | D |
- |
-|
200 | C |
- |
-|
201 | |
- |
-|
202 | 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`: |
- |
-|
203 | |
- |
-|
204 | $ git log experiment..master |
- |
-|
205 | F |
- |
-|
206 | E |
- |
-|
207 | |
- |
-|
208 | 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: |
- |
-|
209 | |
- |
-|
210 | $ git log origin/master..HEAD |
- |
-|
211 | |
- |
-|
212 | 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. |
- |
-|
213 | 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. |
- |
-|
214 | |
- |
-|
215 | #### Multiple Points #### |
- |
-|
216 | |
- |
-|
217 | 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: |
- |
-|
218 | |
- |
-|
219 | $ git log refA..refB |
- |
-|
220 | $ git log ^refA refB |
- |
-|
221 | $ git log refB --not refA |
- |
-|
222 | |
- |
-|
223 | 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: |
- |
-|
224 | |
- |
-|
225 | $ git log refA refB ^refC |
- |
-|
226 | $ git log refA refB --not refC |
- |
-|
227 | |
- |
-|
228 | This makes for a very powerful revision query system that should help you figure out what is in your branches. |
- |
-|
229 | |
- |
-|
230 | #### Triple Dot #### |
- |
-|
231 | |
- |
-|
232 | 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 Figure 6-1. |
- |
-|
233 | If you want to see what is in `master` or `experiment` but not any common references, you can run |
- |
-|
234 | |
- |
-|
235 | $ git log master...experiment |
- |
-|
236 | F |
- |
-|
237 | E |
- |
-|
238 | D |
- |
-|
239 | C |
- |
-|
240 | |
- |
-|
241 | 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. |
- |
-|
242 | |
- |
-|
243 | 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: |
- |
-|
244 | |
- |
-|
245 | $ git log --left-right master...experiment |
- |
-|
246 | < F |
- |
-|
247 | < E |
- |
-|
248 | > D |
- |
-|
249 | > C |
- |
-|
250 | |
- |
-|
251 | With these tools, you can much more easily let Git know what commit or commits you want to inspect. |
- |
-|
252 | |
- |
-|
253 | ## Interactive Staging ## |
- |
-|
254 | |
- |
-|
255 | Git comes with a couple of scripts that make some command-line tasks easier. Here, you’ll look at a few interactive commands that can help you easily craft your commits to include only certain combinations and parts of files. These tools are very helpful if you modify a bunch of files and then decide that you want those changes to be in several focused commits rather than one big messy commit. This way, you can make sure your commits are logically separate changesets and can be easily reviewed by the developers working with you. |
- |
-|
256 | If you run `git add` with the `-i` or `--interactive` option, Git goes into an interactive shell mode, displaying something like this: |
- |
-|
257 | |
- |
-|
258 | $ git add -i |
- |
-|
259 | staged unstaged path |
- |
-|
260 | 1: unchanged +0/-1 TODO |
- |
-|
261 | 2: unchanged +1/-1 index.html |
- |
-|
262 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
263 | |
- |
-|
264 | *** Commands *** |
- |
-|
265 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
266 | 5: patch 6: diff 7: quit 8: help |
- |
-|
267 | What now> |
- |
-|
268 | |
- |
-|
269 | 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. |
- |
-|
270 | |
- |
-|
271 | After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. |
- |
-|
272 | |
- |
-|
273 | ### Staging and Unstaging Files ### |
- |
-|
274 | |
- |
-|
275 | If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: |
- |
-|
276 | |
- |
-|
277 | What now> 2 |
- |
-|
278 | staged unstaged path |
- |
-|
279 | 1: unchanged +0/-1 TODO |
- |
-|
280 | 2: unchanged +1/-1 index.html |
- |
-|
281 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
282 | Update>> |
- |
-|
283 | |
- |
-|
284 | To stage the TODO and index.html files, you can type the numbers: |
- |
-|
285 | |
- |
-|
286 | Update>> 1,2 |
- |
-|
287 | staged unstaged path |
- |
-|
288 | * 1: unchanged +0/-1 TODO |
- |
-|
289 | * 2: unchanged +1/-1 index.html |
- |
-|
290 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
291 | Update>> |
- |
-|
292 | |
- |
-|
293 | 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: |
- |
-|
294 | |
- |
-|
295 | Update>> |
- |
-|
296 | updated 2 paths |
- |
-|
297 | |
- |
-|
298 | *** Commands *** |
- |
-|
299 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
300 | 5: patch 6: diff 7: quit 8: help |
- |
-|
301 | What now> 1 |
- |
-|
302 | staged unstaged path |
- |
-|
303 | 1: +0/-1 nothing TODO |
- |
-|
304 | 2: +1/-1 nothing index.html |
- |
-|
305 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
306 | |
- |
-|
307 | 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: |
- |
-|
308 | |
- |
-|
309 | *** Commands *** |
- |
-|
310 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
311 | 5: patch 6: diff 7: quit 8: help |
- |
-|
312 | What now> 3 |
- |
-|
313 | staged unstaged path |
- |
-|
314 | 1: +0/-1 nothing TODO |
- |
-|
315 | 2: +1/-1 nothing index.html |
- |
-|
316 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
317 | Revert>> 1 |
- |
-|
318 | staged unstaged path |
- |
-|
319 | * 1: +0/-1 nothing TODO |
- |
-|
320 | 2: +1/-1 nothing index.html |
- |
-|
321 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
322 | Revert>> [enter] |
- |
-|
323 | reverted one path |
- |
-|
324 | |
- |
-|
325 | Looking at your Git status again, you can see that you’ve unstaged the TODO file: |
- |
-|
326 | |
- |
-|
327 | *** Commands *** |
- |
-|
328 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
329 | 5: patch 6: diff 7: quit 8: help |
- |
-|
330 | What now> 1 |
- |
-|
331 | staged unstaged path |
- |
-|
332 | 1: unchanged +0/-1 TODO |
- |
-|
333 | 2: +1/-1 nothing index.html |
- |
-|
334 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
335 | |
- |
-|
336 | 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: |
- |
-|
337 | |
- |
-|
338 | *** Commands *** |
- |
-|
339 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
340 | 5: patch 6: diff 7: quit 8: help |
- |
-|
341 | What now> 6 |
- |
-|
342 | staged unstaged path |
- |
-|
343 | 1: +1/-1 nothing index.html |
- |
-|
344 | Review diff>> 1 |
- |
-|
345 | diff --git a/index.html b/index.html |
- |
-|
346 | index 4d07108..4335f49 100644 |
- |
-|
347 | --- a/index.html |
- |
-|
348 | +++ b/index.html |
- |
-|
349 | @@ -16,7 +16,7 @@ Date Finder |
- |
-|
350 | |
- |
-|
351 | <p id="out">...</p> |
- |
-|
352 | |
- |
-|
353 | -<div id="footer">contact : support@github.com</div> |
- |
-|
354 | +<div id="footer">contact : email.support@github.com</div> |
- |
-|
355 | |
- |
-|
356 | <script type="text/javascript"> |
- |
-|
357 | |
- |
-|
358 | With these basic commands, you can use the interactive add mode to deal with your staging area a little more easily. |
- |
-|
359 | |
- |
-|
360 | ### Staging Patches ### |
- |
-|
361 | |
- |
-|
362 | It’s also possible for Git to stage certain parts of files and not the rest. For example, if you make two changes to your simplegit.rb file and want to stage one of them and not the other, doing so is very easy in Git. From the interactive prompt, type `5` or `p` (for patch). Git will ask you which files you would like to partially stage; then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one: |
- |
-|
363 | |
- |
-|
364 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
- |
-|
365 | index dd5ecc4..57399e0 100644 |
- |
-|
366 | --- a/lib/simplegit.rb |
- |
-|
367 | +++ b/lib/simplegit.rb |
- |
-|
368 | @@ -22,7 +22,7 @@ class SimpleGit |
- |
-|
369 | end |
- |
-|
370 | |
- |
-|
371 | def log(treeish = 'master') |
- |
-|
372 | - command("git log -n 25 #{treeish}") |
- |
-|
373 | + command("git log -n 30 #{treeish}") |
- |
-|
374 | end |
- |
-|
375 | |
- |
-|
376 | def blame(path) |
- |
-|
377 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? |
- |
-|
378 | |
- |
-|
379 | You have a lot of options at this point. Typing `?` shows a list of what you can do: |
- |
-|
380 | |
- |
-|
381 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? |
- |
-|
382 | y - stage this hunk |
- |
-|
383 | n - do not stage this hunk |
- |
-|
384 | a - stage this and all the remaining hunks in the file |
- |
-|
385 | d - do not stage this hunk nor any of the remaining hunks in the file |
- |
-|
386 | g - select a hunk to go to |
- |
-|
387 | / - search for a hunk matching the given regex |
- |
-|
388 | j - leave this hunk undecided, see next undecided hunk |
- |
-|
389 | J - leave this hunk undecided, see next hunk |
- |
-|
390 | k - leave this hunk undecided, see previous undecided hunk |
- |
-|
391 | K - leave this hunk undecided, see previous hunk |
- |
-|
392 | s - split the current hunk into smaller hunks |
- |
-|
393 | e - manually edit the current hunk |
- |
-|
394 | ? - print help |
- |
-|
395 | |
- |
-|
396 | Generally, you’ll type `y` or `n` if you want to stage each hunk, but staging all of them in certain files or skipping a hunk decision until later can be helpful too. If you stage one part of the file and leave another part unstaged, your status output will look like this: |
- |
-|
397 | |
- |
-|
398 | What now> 1 |
- |
-|
399 | staged unstaged path |
- |
-|
400 | 1: unchanged +0/-1 TODO |
- |
-|
401 | 2: +1/-1 nothing index.html |
- |
-|
402 | 3: +1/-1 +4/-0 lib/simplegit.rb |
- |
-|
403 | |
- |
-|
404 | The status of the simplegit.rb file is interesting. It shows you that a couple of lines are staged and a couple are unstaged. You’ve partially staged this file. At this point, you can exit the interactive adding script and run `git commit` to commit the partially staged files. |
- |
-|
405 | |
- |
-|
406 | Finally, you don’t need to be in interactive add mode to do the partial-file staging — you can start the same script by using `git add -p` or `git add --patch` on the command line. |
- |
-|
407 | |
- |
-|
408 | ## Stashing ## |
- |
-|
409 | |
- |
-|
410 | Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the `git stash` command. |
- |
-|
411 | |
- |
-|
412 | Stashing takes the dirty state of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time. |
- |
-|
413 | |
- |
-|
414 | ### Stashing Your Work ### |
- |
-|
415 | |
- |
-|
416 | To demonstrate, you’ll go into your project and start working on a couple of files and possibly stage one of the changes. If you run `git status`, you can see your dirty state: |
- |
-|
417 | |
- |
-|
418 | $ git status |
- |
-|
419 | # On branch master |
- |
-|
420 | # Changes to be committed: |
- |
-|
421 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
422 | # |
- |
-|
423 | # modified: index.html |
- |
-|
424 | # |
- |
-|
425 | # Changes not staged for commit: |
- |
-|
426 | # (use "git add <file>..." to update what will be committed) |
- |
-|
427 | # |
- |
-|
428 | # modified: lib/simplegit.rb |
- |
-|
429 | # |
- |
-|
430 | |
- |
-|
431 | Now you want to switch branches, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run `git stash`: |
- |
-|
432 | |
- |
-|
433 | $ git stash |
- |
-|
434 | Saved working directory and index state \ |
- |
-|
435 | "WIP on master: 049d078 added the index file" |
- |
-|
436 | HEAD is now at 049d078 added the index file |
- |
-|
437 | (To restore them type "git stash apply") |
- |
-|
438 | |
- |
-|
439 | Your working directory is clean: |
- |
-|
440 | |
- |
-|
441 | $ git status |
- |
-|
442 | # On branch master |
- |
-|
443 | nothing to commit, working directory clean |
- |
-|
444 | |
- |
-|
445 | At this point, you can easily switch branches and do work elsewhere; your changes are stored on your stack. To see which stashes you’ve stored, you can use `git stash list`: |
- |
-|
446 | |
- |
-|
447 | $ git stash list |
- |
-|
448 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
449 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
450 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
451 | |
- |
-|
452 | In this case, two stashes were done previously, so you have access to three different stashed works. You can reapply the one you just stashed by using the command shown in the help output of the original stash command: `git stash apply`. If you want to apply one of the older stashes, you can specify it by naming it, like this: `git stash apply stash@{2}`. If you don’t specify a stash, Git assumes the most recent stash and tries to apply it: |
- |
-|
453 | |
- |
-|
454 | $ git stash apply |
- |
-|
455 | # On branch master |
- |
-|
456 | # Changes not staged for commit: |
- |
-|
457 | # (use "git add <file>..." to update what will be committed) |
- |
-|
458 | # |
- |
-|
459 | # modified: index.html |
- |
-|
460 | # modified: lib/simplegit.rb |
- |
-|
461 | # |
- |
-|
462 | |
- |
-|
463 | You can see that Git re-modifies the files you uncommitted when you saved the stash. In this case, you had a clean working directory when you tried to apply the stash, and you tried to apply it on the same branch you saved it from; but having a clean working directory and applying it on the same branch aren’t necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash — Git gives you merge conflicts if anything no longer applies cleanly. |
- |
-|
464 | |
- |
-|
465 | The changes to your files were reapplied, but the file you staged before wasn’t restaged. To do that, you must run the `git stash apply` command with a `--index` option to tell the command to try to reapply the staged changes. If you had run that instead, you’d have gotten back to your original position: |
- |
-|
466 | |
- |
-|
467 | $ git stash apply --index |
- |
-|
468 | # On branch master |
- |
-|
469 | # Changes to be committed: |
- |
-|
470 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
471 | # |
- |
-|
472 | # modified: index.html |
- |
-|
473 | # |
- |
-|
474 | # Changes not staged for commit: |
- |
-|
475 | # (use "git add <file>..." to update what will be committed) |
- |
-|
476 | # |
- |
-|
477 | # modified: lib/simplegit.rb |
- |
-|
478 | # |
- |
-|
479 | |
- |
-|
480 | The apply option only tries to apply the stashed work — you continue to have it on your stack. To remove it, you can run `git stash drop` with the name of the stash to remove: |
- |
-|
481 | |
- |
-|
482 | $ git stash list |
- |
-|
483 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
484 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
485 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
486 | $ git stash drop stash@{0} |
- |
-|
487 | Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) |
- |
-|
488 | |
- |
-|
489 | You can also run `git stash pop` to apply the stash and then immediately drop it from your stack. |
- |
-|
490 | |
- |
-|
491 | ### Un-applying a Stash ### |
- |
-|
492 | |
- |
-|
493 | In some use case scenarios you might want to apply stashed changes, do some work, but then un-apply those changes that originally came from the stash. Git does not provide such a `stash unapply` command, but it is possible to achieve the effect by simply retrieving the patch associated with a stash and applying it in reverse: |
- |
-|
494 | |
- |
-|
495 | $ git stash show -p stash@{0} | git apply -R |
- |
-|
496 | |
- |
-|
497 | Again, if you don’t specify a stash, Git assumes the most recent stash: |
- |
-|
498 | |
- |
-|
499 | $ git stash show -p | git apply -R |
- |
-|
500 | |
- |
-|
501 | You may want to create an alias and effectively add a `stash-unapply` command to your Git. For example: |
- |
-|
502 | |
- |
-|
503 | $ git config --global alias.stash-unapply '!git stash show -p | git apply -R' |
- |
-|
504 | $ git stash apply |
- |
-|
505 | $ #... work work work |
- |
-|
506 | $ git stash-unapply |
- |
-|
507 | |
- |
-|
508 | ### Creating a Branch from a Stash ### |
- |
-|
509 | |
- |
-|
510 | If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you’ve since modified, you’ll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run `git stash branch`, which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully: |
- |
-|
511 | |
- |
-|
512 | $ git stash branch testchanges |
- |
-|
513 | Switched to a new branch "testchanges" |
- |
-|
514 | # On branch testchanges |
- |
-|
515 | # Changes to be committed: |
- |
-|
516 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
517 | # |
- |
-|
518 | # modified: index.html |
- |
-|
519 | # |
- |
-|
520 | # Changes not staged for commit: |
- |
-|
521 | # (use "git add <file>..." to update what will be committed) |
- |
-|
522 | # |
- |
-|
523 | # modified: lib/simplegit.rb |
- |
-|
524 | # |
- |
-|
525 | Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) |
- |
-|
526 | |
- |
-|
527 | This is a nice shortcut to recover stashed work easily and work on it in a new branch. |
- |
-|
528 | |
- |
-|
529 | ## Rewriting History ## |
- |
-|
530 | |
- |
-|
531 | Many times, when working with Git, you may want to revise your commit history for some reason. One of the great things about Git is that it allows you to make decisions at the last possible moment. You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with the stash command, and you can rewrite commits that already happened so they look like they happened in a different way. This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others. |
- |
-|
532 | |
- |
-|
533 | In this section, you’ll cover how to accomplish these very useful tasks so that you can make your commit history look the way you want before you share it with others. |
- |
-|
534 | |
- |
-|
535 | ### Changing the Last Commit ### |
- |
-|
536 | |
- |
-|
537 | Changing your last commit is probably the most common rewriting of history that you’ll do. You’ll often want to do two basic things to your last commit: change the commit message, or change the snapshot you just recorded by adding, changing and removing files. |
- |
-|
538 | |
- |
-|
539 | If you only want to modify your last commit message, it’s very simple: |
- |
-|
540 | |
- |
-|
541 | $ git commit --amend |
- |
-|
542 | |
- |
-|
543 | That drops you into your text editor, which has your last commit message in it, ready for you to modify the message. When you save and close the editor, the editor writes a new commit containing that message and makes it your new last commit. |
- |
-|
544 | |
- |
-|
545 | If you’ve committed and then you want to change the snapshot you committed by adding or changing files, possibly because you forgot to add a newly created file when you originally committed, the process works basically the same way. You stage the changes you want by editing a file and running `git add` on it or `git rm` to a tracked file, and the subsequent `git commit --amend` takes your current staging area and makes it the snapshot for the new commit. |
- |
-|
546 | |
- |
-|
547 | You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it. |
- |
-|
548 | |
- |
-|
549 | ### Changing Multiple Commit Messages ### |
- |
-|
550 | |
- |
-|
551 | To modify a commit that is farther back in your history, you must move to more complex tools. Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD they were originally based on instead of moving them to another one. With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the `-i` option to `git rebase`. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto. |
- |
-|
552 | |
- |
-|
553 | For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to `git rebase -i` the parent of the last commit you want to edit, which is `HEAD~2^` or `HEAD~3`. It may be easier to remember the `~3` because you’re trying to edit the last three commits; but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit: |
- |
-|
554 | |
- |
-|
555 | $ git rebase -i HEAD~3 |
- |
-|
556 | |
- |
-|
557 | Remember again that this is a rebasing command — every commit included in the range `HEAD~3..HEAD` will be rewritten, whether you change the message or not. Don’t include any commit you’ve already pushed to a central server — doing so will confuse other developers by providing an alternate version of the same change. |
- |
-|
558 | |
- |
-|
559 | Running this command gives you a list of commits in your text editor that looks something like this: |
- |
-|
560 | |
- |
-|
561 | pick f7f3f6d changed my name a bit |
- |
-|
562 | pick 310154e updated README formatting and added blame |
- |
-|
563 | pick a5f4a0d added cat-file |
- |
-|
564 | |
- |
-|
565 | # Rebase 710f0f8..a5f4a0d onto 710f0f8 |
- |
-|
566 | # |
- |
-|
567 | # Commands: |
- |
-|
568 | # p, pick = use commit |
- |
-|
569 | # r, reword = use commit, but edit the commit message |
- |
-|
570 | # e, edit = use commit, but stop for amending |
- |
-|
571 | # s, squash = use commit, but meld into previous commit |
- |
-|
572 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
573 | # x, exec = run command (the rest of the line) using shell |
- |
-|
574 | # |
- |
-|
575 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
576 | # |
- |
-|
577 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
578 | # |
- |
-|
579 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
580 | # |
- |
-|
581 | # Note that empty commits are commented out |
- |
-|
582 | |
- |
-|
583 | It’s important to note that these commits are listed in the opposite order than you normally see them using the `log` command. If you run a `log`, you see something like this: |
- |
-|
584 | |
- |
-|
585 | $ git log --pretty=format:"%h %s" HEAD~3..HEAD |
- |
-|
586 | a5f4a0d added cat-file |
- |
-|
587 | 310154e updated README formatting and added blame |
- |
-|
588 | f7f3f6d changed my name a bit |
- |
-|
589 | |
- |
-|
590 | Notice the reverse order. The interactive rebase gives you a script that it’s going to run. It will start at the commit you specify on the command line (`HEAD~3`) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that’s the first one it will replay. |
- |
-|
591 | |
- |
-|
592 | You need to edit the script so that it stops at the commit you want to edit. To do so, change the word pick to the word edit for each of the commits you want the script to stop after. For example, to modify only the third commit message, you change the file to look like this: |
- |
-|
593 | |
- |
-|
594 | edit f7f3f6d changed my name a bit |
- |
-|
595 | pick 310154e updated README formatting and added blame |
- |
-|
596 | pick a5f4a0d added cat-file |
- |
-|
597 | |
- |
-|
598 | When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message: |
- |
-|
599 | |
- |
-|
600 | <!-- This is actually weird, as the SHA-1 of 7482e0d is not present in the list, |
- |
-|
601 | nor is the commit message. Please review |
- |
-|
602 | --> |
- |
-|
603 | |
- |
-|
604 | $ git rebase -i HEAD~3 |
- |
-|
605 | Stopped at 7482e0d... updated the gemspec to hopefully work better |
- |
-|
606 | You can amend the commit now, with |
- |
-|
607 | |
- |
-|
608 | git commit --amend |
- |
-|
609 | |
- |
-|
610 | Once you’re satisfied with your changes, run |
- |
-|
611 | |
- |
-|
612 | git rebase --continue |
- |
-|
613 | |
- |
-|
614 | These instructions tell you exactly what to do. Type |
- |
-|
615 | |
- |
-|
616 | $ git commit --amend |
- |
-|
617 | |
- |
-|
618 | Change the commit message, and exit the editor. Then, run |
- |
-|
619 | |
- |
-|
620 | $ git rebase --continue |
- |
-|
621 | |
- |
-|
622 | This command will apply the other two commits automatically, and then you’re done. If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit. Each time, Git will stop, let you amend the commit, and continue when you’re finished. |
- |
-|
623 | |
- |
-|
624 | ### Reordering Commits ### |
- |
-|
625 | |
- |
-|
626 | You can also use interactive rebases to reorder or remove commits entirely. If you want to remove the "added cat-file" commit and change the order in which the other two commits are introduced, you can change the rebase script from this |
- |
-|
627 | |
- |
-|
628 | pick f7f3f6d changed my name a bit |
- |
-|
629 | pick 310154e updated README formatting and added blame |
- |
-|
630 | pick a5f4a0d added cat-file |
- |
-|
631 | |
- |
-|
632 | to this: |
- |
-|
633 | |
- |
-|
634 | pick 310154e updated README formatting and added blame |
- |
-|
635 | pick f7f3f6d changed my name a bit |
- |
-|
636 | |
- |
-|
637 | When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies `310154e` and then `f7f3f6d`, and then stops. You effectively change the order of those commits and remove the "added cat-file" commit completely. |
- |
-|
638 | |
- |
-|
639 | ### Squashing Commits ### |
- |
-|
640 | |
- |
-|
641 | It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool. The script puts helpful instructions in the rebase message: |
- |
-|
642 | |
- |
-|
643 | # |
- |
-|
644 | # Commands: |
- |
-|
645 | # p, pick = use commit |
- |
-|
646 | # r, reword = use commit, but edit the commit message |
- |
-|
647 | # e, edit = use commit, but stop for amending |
- |
-|
648 | # s, squash = use commit, but meld into previous commit |
- |
-|
649 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
650 | # x, exec = run command (the rest of the line) using shell |
- |
-|
651 | # |
- |
-|
652 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
653 | # |
- |
-|
654 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
655 | # |
- |
-|
656 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
657 | # |
- |
-|
658 | # Note that empty commits are commented out |
- |
-|
659 | |
- |
-|
660 | If, instead of "pick" or "edit", you specify "squash", Git applies both that change and the change directly before it and makes you merge the commit messages together. So, if you want to make a single commit from these three commits, you make the script look like this: |
- |
-|
661 | |
- |
-|
662 | pick f7f3f6d changed my name a bit |
- |
-|
663 | squash 310154e updated README formatting and added blame |
- |
-|
664 | squash a5f4a0d added cat-file |
- |
-|
665 | |
- |
-|
666 | When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages: |
- |
-|
667 | |
- |
-|
668 | # This is a combination of 3 commits. |
- |
-|
669 | # The first commit's message is: |
- |
-|
670 | changed my name a bit |
- |
-|
671 | |
- |
-|
672 | # This is the 2nd commit message: |
- |
-|
673 | |
- |
-|
674 | updated README formatting and added blame |
- |
-|
675 | |
- |
-|
676 | # This is the 3rd commit message: |
- |
-|
677 | |
- |
-|
678 | added cat-file |
- |
-|
679 | |
- |
-|
680 | When you save that, you have a single commit that introduces the changes of all three previous commits. |
- |
-|
681 | |
- |
-|
682 | ### Splitting a Commit ### |
- |
-|
683 | |
- |
-|
684 | Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with. For example, suppose you want to split the middle commit of your three commits. Instead of "updated README formatting and added blame", you want to split it into two commits: "updated README formatting" for the first, and "added blame" for the second. You can do that in the `rebase -i` script by changing the instruction on the commit you want to split to "edit": |
- |
-|
685 | |
- |
-|
686 | pick f7f3f6d changed my name a bit |
- |
-|
687 | edit 310154e updated README formatting and added blame |
- |
-|
688 | pick a5f4a0d added cat-file |
- |
-|
689 | |
- |
-|
690 | When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (`f7f3f6d`), applies the second (`310154e`), and drops you to the console. There, you can do a mixed reset of that commit with `git reset HEAD^`, which effectively undoes that commit and leaves the modified files unstaged. Now you can take the changes that have been reset, and create multiple commits out of them. Simply stage and commit files until you have several commits, and run `git rebase --continue` when you’re done: |
- |
-|
691 | |
- |
-|
692 | $ git reset HEAD^ |
- |
-|
693 | $ git add README |
- |
-|
694 | $ git commit -m 'updated README formatting' |
- |
-|
695 | $ git add lib/simplegit.rb |
- |
-|
696 | $ git commit -m 'added blame' |
- |
-|
697 | $ git rebase --continue |
- |
-|
698 | |
- |
-|
699 | Git applies the last commit (`a5f4a0d`) in the script, and your history looks like this: |
- |
-|
700 | |
- |
-|
701 | $ git log -4 --pretty=format:"%h %s" |
- |
-|
702 | 1c002dd added cat-file |
- |
-|
703 | 9b29157 added blame |
- |
-|
704 | 35cfb2b updated README formatting |
- |
-|
705 | f3cc40e changed my name a bit |
- |
-|
706 | |
- |
-|
707 | Once again, this changes the SHAs of all the commits in your list, so make sure no commit shows up in that list that you’ve already pushed to a shared repository. |
- |
-|
708 | |
- |
-|
709 | ### The Nuclear Option: filter-branch ### |
- |
-|
710 | |
- |
-|
711 | There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way — for instance, changing your e-mail address globally or removing a file from every commit. The command is `filter-branch`, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite. However, it can be very useful. You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of. |
- |
-|
712 | |
- |
-|
713 | #### Removing a File from Every Commit #### |
- |
-|
714 | |
- |
-|
715 | This occurs fairly commonly. Someone accidentally commits a huge binary file with a thoughtless `git add .`, and you want to remove it everywhere. Perhaps you accidentally committed a file that contained a password, and you want to make your project open source. `filter-branch` is the tool you probably want to use to scrub your entire history. To remove a file named passwords.txt from your entire history, you can use the `--tree-filter` option to `filter-branch`: |
- |
-|
716 | |
- |
-|
717 | $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD |
- |
-|
718 | Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) |
- |
-|
719 | Ref 'refs/heads/master' was rewritten |
- |
-|
720 | |
- |
-|
721 | The `--tree-filter` option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt from every snapshot, whether it exists or not. If you want to remove all accidentally committed editor backup files, you can run something like `git filter-branch --tree-filter "rm -f *~" HEAD`. |
- |
-|
722 | |
- |
-|
723 | You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end. It’s generally a good idea to do this in a testing branch and then hard-reset your master branch after you’ve determined the outcome is what you really want. To run `filter-branch` on all your branches, you can pass `--all` to the command. |
- |
-|
724 | |
- |
-|
725 | #### Making a Subdirectory the New Root #### |
- |
-|
726 | |
- |
-|
727 | Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk, tags, and so on). If you want to make the `trunk` subdirectory be the new project root for every commit, `filter-branch` can help you do that, too: |
- |
-|
728 | |
- |
-|
729 | $ git filter-branch --subdirectory-filter trunk HEAD |
- |
-|
730 | Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) |
- |
-|
731 | Ref 'refs/heads/master' was rewritten |
- |
-|
732 | |
- |
-|
733 | Now your new project root is what was in the `trunk` subdirectory each time. Git will also automatically remove commits that did not affect the subdirectory. |
- |
-|
734 | |
- |
-|
735 | #### Changing E-Mail Addresses Globally #### |
- |
-|
736 | |
- |
-|
737 | Another common case is that you forgot to run `git config` to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with `filter-branch` as well. You need to be careful to change only the e-mail addresses that are yours, so you use `--commit-filter`: |
- |
-|
738 | |
- |
-|
739 | $ git filter-branch --commit-filter ' |
- |
-|
740 | if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; |
- |
-|
741 | then |
- |
-|
742 | GIT_AUTHOR_NAME="Scott Chacon"; |
- |
-|
743 | GIT_AUTHOR_EMAIL="schacon@example.com"; |
- |
-|
744 | git commit-tree "$@"; |
- |
-|
745 | else |
- |
-|
746 | git commit-tree "$@"; |
- |
-|
747 | fi' HEAD |
- |
-|
748 | |
- |
-|
749 | This goes through and rewrites every commit to have your new address. Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address. |
- |
-|
750 | |
- |
-|
751 | ### The Very Fast Nuclear Option: Big Friendly Giant Repo Cleaner (BFG) ### |
- |
-|
752 | |
- |
-|
753 | [Roberto Tyley](https://github.com/rtyley) has written a similar tool to `filter-branch` called the BFG. BFG cannot do as much as `filter-branch`, but it is _very_ fast and on a large repository this can make a big difference. If the change you want to make is in the scope of BFG capability, and you have performance issues, then you should consider using it. |
- |
-|
754 | |
- |
-|
755 | See the [BFG](http://rtyley.github.io/bfg-repo-cleaner/) website for details. |
- |
-|
756 | |
- |
-|
757 | ## Debugging with Git ## |
- |
-|
758 | |
- |
-|
759 | Git also provides a couple of tools to help you debug issues in your projects. Because Git is designed to work with nearly any type of project, these tools are pretty generic, but they can often help you hunt for a bug or culprit when things go wrong. |
- |
-|
760 | |
- |
-|
761 | ### File Annotation ### |
- |
-|
762 | |
- |
-|
763 | If you track down a bug in your code and want to know when it was introduced and why, file annotation is often your best tool. It shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with `git blame` to see when each line of the method was last edited and by whom. This example uses the `-L` option to limit the output to lines 12 through 22: |
- |
-|
764 | |
- |
-|
765 | $ git blame -L 12,22 simplegit.rb |
- |
-|
766 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master') |
- |
-|
767 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}") |
- |
-|
768 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end |
- |
-|
769 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15) |
- |
-|
770 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master') |
- |
-|
771 | 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}") |
- |
-|
772 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end |
- |
-|
773 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19) |
- |
-|
774 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path) |
- |
-|
775 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}") |
- |
-|
776 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end |
- |
-|
777 | |
- |
-|
778 | Notice that the first field is the partial SHA-1 of the commit that last modified that line. The next two fields are values extracted from that commit—the author name and the authored date of that commit — so you can easily see who modified that line and when. After that come the line number and the content of the file. Also note the `^4832fe2` commit lines, which designate that those lines were in this file’s original commit. That commit is when this file was first added to this project, and those lines have been unchanged since. This is a tad confusing, because now you’ve seen at least three different ways that Git uses the `^` to modify a commit SHA, but that is what it means here. |
- |
-|
779 | |
- |
-|
780 | Another cool thing about Git is that it doesn’t track file renames explicitly. It records the snapshots and then tries to figure out what was renamed implicitly, after the fact. One of the interesting features of this is that you can ask it to figure out all sorts of code movement as well. If you pass `-C` to `git blame`, Git analyzes the file you’re annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere. Recently, I was refactoring a file named `GITServerHandler.m` into multiple files, one of which was `GITPackUpload.m`. By blaming `GITPackUpload.m` with the `-C` option, I could see where sections of the code originally came from: |
- |
-|
781 | |
- |
-|
782 | $ git blame -C -L 141,153 GITPackUpload.m |
- |
-|
783 | f344f58d GITServerHandler.m (Scott 2009-01-04 141) |
- |
-|
784 | f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC |
- |
-|
785 | f344f58d GITServerHandler.m (Scott 2009-01-04 143) { |
- |
-|
786 | 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI |
- |
-|
787 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 145) |
- |
-|
788 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha; |
- |
-|
789 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g |
- |
-|
790 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 148) |
- |
-|
791 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI |
- |
-|
792 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 150) |
- |
-|
793 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) { |
- |
-|
794 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb |
- |
-|
795 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 153) |
- |
-|
796 | |
- |
-|
797 | This is really useful. Normally, you get as the original commit the commit where you copied the code over, because that is the first time you touched those lines in this file. Git tells you the original commit where you wrote those lines, even if it was in another file. |
- |
-|
798 | |
- |
-|
799 | ### Binary Search ### |
- |
-|
800 | |
- |
-|
801 | Annotating a file helps if you know where the issue is to begin with. If you don’t know what is breaking, and there have been dozens or hundreds of commits since the last state where you know the code worked, you’ll likely turn to `git bisect` for help. The `bisect` command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. |
- |
-|
802 | |
- |
-|
803 | Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that. You go back to your code, and it turns out you can reproduce the issue, but you can’t figure out what is going wrong. You can bisect the code to find out. First you run `git bisect start` to get things going, and then you use `git bisect bad` to tell the system that the current commit you’re on is broken. Then, you must tell bisect when the last known good state was, using `git bisect good [good_commit]`: |
- |
-|
804 | |
- |
-|
805 | $ git bisect start |
- |
-|
806 | $ git bisect bad |
- |
-|
807 | $ git bisect good v1.0 |
- |
-|
808 | Bisecting: 6 revisions left to test after this |
- |
-|
809 | [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo |
- |
-|
810 | |
- |
-|
811 | Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. If it does, then it was introduced sometime before this middle commit; if it doesn’t, then the problem was introduced sometime after the middle commit. It turns out there is no issue here, and you tell Git that by typing `git bisect good` and continue your journey: |
- |
-|
812 | |
- |
-|
813 | $ git bisect good |
- |
-|
814 | Bisecting: 3 revisions left to test after this |
- |
-|
815 | [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing |
- |
-|
816 | |
- |
-|
817 | Now you’re on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with `git bisect bad`: |
- |
-|
818 | |
- |
-|
819 | $ git bisect bad |
- |
-|
820 | Bisecting: 1 revisions left to test after this |
- |
-|
821 | [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table |
- |
-|
822 | |
- |
-|
823 | This commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and show some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug: |
- |
-|
824 | |
- |
-|
825 | $ git bisect good |
- |
-|
826 | b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit |
- |
-|
827 | commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 |
- |
-|
828 | Author: PJ Hyett <pjhyett@example.com> |
- |
-|
829 | Date: Tue Jan 27 14:48:32 2009 -0800 |
- |
-|
830 | |
- |
-|
831 | secure this thing |
- |
-|
832 | |
- |
-|
833 | :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730 |
- |
-|
834 | f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config |
- |
-|
835 | |
- |
-|
836 | When you’re finished, you should run `git bisect reset` to reset your HEAD to where you were before you started, or you’ll end up in a weird state: |
- |
-|
837 | |
- |
-|
838 | $ git bisect reset |
- |
-|
839 | |
- |
-|
840 | This is a powerful tool that can help you check hundreds of commits for an introduced bug in minutes. In fact, if you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate `git bisect`. First, you again tell it the scope of the bisect by providing the known bad and good commits. You can do this by listing them with the `bisect start` command if you want, listing the known bad commit first and the known good commit second: |
- |
-|
841 | |
- |
-|
842 | $ git bisect start HEAD v1.0 |
- |
-|
843 | $ git bisect run test-error.sh |
- |
-|
844 | |
- |
-|
845 | Doing so automatically runs `test-error.sh` on each checked-out commit until Git finds the first broken commit. You can also run something like `make` or `make tests` or whatever you have that runs automated tests for you. |
- |
-|
846 | |
- |
-|
847 | ## Submodules ## |
-2 | === Submodules |
-
848 | |
-3 | |
-
849 | It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other. |
-4 | It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other. |
-
850 | |
-5 | |
-
851 | Here’s an example. Suppose you’re developing a web site and creating Atom feeds. Instead of writing your own Atom-generating code, you decide to use a library. You’re likely to have to either include this code from a shared library like a CPAN install or Ruby gem, or copy the source code into your own project tree. The issue with including the library is that it’s difficult to customize the library in any way and often more difficult to deploy it, because you need to make sure every client has that library available. The issue with vendoring the code into your own project is that any custom changes you make are difficult to merge when upstream changes become available. |
-6 | Here’s an example. Suppose you’re developing a web site and creating Atom feeds. Instead of writing your own Atom-generating code, you decide to use a library. You’re likely to have to either include this code from a shared library like a CPAN install or Ruby gem, or copy the source code into your own project tree. The issue with including the library is that it’s difficult to customize the library in any way and often more difficult to deploy it, because you need to make sure every client has that library available. The issue with vendoring the code into your own project is that any custom changes you make are difficult to merge when upstream changes become available. |
-
852 | |
-7 | |
-
853 | Git addresses this issue using submodules. Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. |
-8 | Git addresses this issue using submodules. Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. |
-
854 | |
-9 | |
-
. | |
-10 | [[_starting_submodules]] |
-
855 | ### Starting with Submodules ### |
-11 | ==== Starting with Submodules |
-
856 | |
-12 | |
-
.857 | Suppose you want to add the Rack library (a Ruby web server gateway interface) to your project, possibly maintain your own changes to it, but continue to merge in upstream changes. The first thing you should do is clone the external repository into your subdirectory. You add external projects as submodules with the `git submodule add` command: |
- |
-|
|
-13 | We'll walk through developing a simple project that has been split up into a main project and a few sub-projects. |
-|
858 | |
-14 | |
-
. | |
-15 | Let's start by adding an existing Git repository as a submodule of the repository that we're working on. To add a new submodule you use the `git submodule add` command with the URL of the project you would like to start tracking. In this example, we'll add a library called ``DbConnector''. |
-
|
-16 | |
-|
|
-17 | [source,console] |
-|
|
-18 | ---- |
-|
859 | $ git submodule add git://github.com/chneukirchen/rack.git rack |
-19 | $ git submodule add https://github.com/chaconinc/DbConnector |
-
860 | Initialized empty Git repository in /opt/subtest/rack/.git/ |
-20 | Cloning into 'DbConnector'... |
-
861 | remote: Counting objects: 3181, done. |
-21 | remote: Counting objects: 11, done. |
-
862 | remote: Compressing objects: 100% (1534/1534), done. |
-22 | remote: Compressing objects: 100% (10/10), done. |
-
863 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
-23 | remote: Total 11 (delta 0), reused 11 (delta 0) |
-
864 | Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done. |
-24 | Unpacking objects: 100% (11/11), done. |
-
865 | Resolving deltas: 100% (1951/1951), done. |
-25 | Checking connectivity... done. |
-
|
-26 | ---- |
-|
866 | |
-27 | |
-
.867 | Now you have the Rack project under a subdirectory named `rack` within your project. You can go into that subdirectory, make changes, add your own writable remote repository to push your changes into, fetch and merge from the original repository, and more. If you run `git status` right after you add the submodule, you see two things: |
- |
-|
|
-28 | By default, submodules will add the subproject into a directory named the same as the repository, in this case ``DbConnector''. You can add a different path at the end of the command if you want it to go elsewhere. |
-|
868 | |
-29 | |
-
. | |
-30 | If you run `git status` at this point, you'll notice a few things. |
-
|
-31 | |
-|
|
-32 | [source,console] |
-|
|
-33 | ---- |
-|
869 | $ git status |
-34 | $ git status |
-
870 | # On branch master |
-35 | On branch master |
-
|
-36 | Your branch is up-to-date with 'origin/master'. |
-|
|
-37 | |
-|
871 | # Changes to be committed: |
-38 | Changes to be committed: |
-
872 | # (use "git reset HEAD <file>..." to unstage) |
-39 | (use "git reset HEAD <file>..." to unstage) |
-
873 | # |
-40 | |
-
874 | # new file: .gitmodules |
-41 | new file: .gitmodules |
-
875 | # new file: rack |
-42 | new file: DbConnector |
-
876 | # |
-43 | ---- |
-
877 | |
-44 | |
-
.878 | First you notice the `.gitmodules` file. This is a configuration file that stores the mapping between the project’s URL and the local subdirectory you’ve pulled it into: |
-45 | First you should notice the new `.gitmodules` file. This is a configuration file that stores the mapping between the project’s URL and the local subdirectory you’ve pulled it into: |
-
879 | |
-46 | |
-
. | |
-47 | [source,console] |
-
|
-48 | ---- |
-|
880 | $ cat .gitmodules |
-49 | $ cat .gitmodules |
-
881 | [submodule "rack"] |
-50 | [submodule "DbConnector"] |
-
882 | path = rack |
-51 | path = DbConnector |
-
883 | url = git://github.com/chneukirchen/rack.git |
-52 | url = https://github.com/chaconinc/DbConnector |
-
|
-53 | ---- |
-|
884 | |
-54 | |
-
885 | If you have multiple submodules, you’ll have multiple entries in this file. It’s important to note that this file is version-controlled with your other files, like your `.gitignore` file. It’s pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from. |
-55 | If you have multiple submodules, you’ll have multiple entries in this file. It’s important to note that this file is version-controlled with your other files, like your `.gitignore` file. It’s pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from. |
-
886 | |
-56 | |
-
. | |
-57 | [NOTE] |
-
|
-58 | ===== |
-|
|
-59 | Since the URL in the .gitmodules file is what other people will first try to clone/fetch from, make sure to use a URL that they can access if possible. For example, if you use a different URL to push to than others would to pull from, use the one that others have access to. You can overwrite this value locally with `git config submodule.DbConnector.url PRIVATE_URL` for your own use. |
-|
|
-60 | ===== |
-|
|
-61 | |
-|
887 | The other listing in the `git status` output is the rack entry. If you run `git diff` on that, you see something interesting: |
-62 | The other listing in the `git status` output is the project folder entry. If you run `git diff` on that, you see something interesting: |
-
888 | |
-63 | |
-
. | |
-64 | [source,console] |
-
|
-65 | ---- |
-|
889 | $ git diff --cached rack |
-66 | $ git diff --cached DbConnector |
-
890 | diff --git a/rack b/rack |
-67 | diff --git a/DbConnector b/DbConnector |
-
891 | new file mode 160000 |
-68 | new file mode 160000 |
-
892 | index 0000000..08d709f |
-69 | index 0000000..c3f01dc |
-
893 | --- /dev/null |
-70 | --- /dev/null |
-
894 | +++ b/rack |
-71 | +++ b/DbConnector |
-
895 | @@ -0,0 +1 @@ |
-72 | @@ -0,0 +1 @@ |
-
896 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
-73 | +Subproject commit c3f01dc8862123d317dd46284b05b6892c7b29bc |
-
|
-74 | ---- |
-|
897 | |
-75 | |
-
.898 | Although `rack` is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git records it as a particular commit from that repository. When you make changes and commit in that subdirectory, the superproject notices that the HEAD there has changed and records the exact commit you’re currently working off of; that way, when others clone this project, they can re-create the environment exactly. |
-76 | Although `DbConnector` is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git sees it as a particular commit from that repository. |
-
899 | |
-77 | |
-
.900 | This is an important point with submodules: you record them as the exact commit they’re at. You can’t record a submodule at `master` or some other symbolic reference. |
- |
-|
|
-78 | If you want a little nicer diff output, you can pass the `--submodule` option to `git diff`. |
-|
|
-79 | |
-|
|
-80 | [source,console] |
-|
|
-81 | ---- |
-|
|
-82 | $ git diff --cached --submodule |
-|
|
-83 | diff --git a/.gitmodules b/.gitmodules |
-|
|
-84 | new file mode 100644 |
-|
|
-85 | index 0000000..71fc376 |
-|
|
-86 | --- /dev/null |
-|
|
-87 | +++ b/.gitmodules |
-|
|
-88 | @@ -0,0 +1,3 @@ |
-|
|
-89 | +[submodule "DbConnector"] |
-|
|
-90 | + path = DbConnector |
-|
|
-91 | + url = https://github.com/chaconinc/DbConnector |
-|
|
-92 | Submodule DbConnector 0000000...c3f01dc (new submodule) |
-|
|
-93 | ---- |
-|
901 | |
-94 | |
-
902 | When you commit, you see something like this: |
-95 | When you commit, you see something like this: |
-
903 | |
-96 | |
-
. | |
-97 | [source,console] |
-
|
-98 | ---- |
-|
904 | $ git commit -m 'first commit with submodule rack' |
-99 | $ git commit -am 'added DbConnector module' |
-
905 | [master 0550271] first commit with submodule rack |
-100 | [master fb9093c] added DbConnector module |
-
906 | 2 files changed, 4 insertions(+), 0 deletions(-) |
-101 | 2 files changed, 4 insertions(+) |
-
907 | create mode 100644 .gitmodules |
-102 | create mode 100644 .gitmodules |
-
908 | create mode 160000 rack |
-103 | create mode 160000 DbConnector |
-
|
-104 | ---- |
-|
909 | |
-105 | |
-
.910 | Notice the 160000 mode for the rack entry. That is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file. |
-106 | Notice the `160000` mode for the `DbConnector` entry. That is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file. |
-
911 | |
- |
-|
912 | You can treat the `rack` directory as a separate project and then update your superproject from time to time with a pointer to the latest commit in that subproject. All the Git commands work independently in the two directories: |
- |
-|
913 | |
- |
-|
914 | $ git log -1 |
- |
-|
915 | commit 0550271328a0038865aad6331e620cd7238601bb |
- |
-|
916 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
917 | Date: Thu Apr 9 09:03:56 2009 -0700 |
- |
-|
918 | |
- |
-|
919 | first commit with submodule rack |
- |
-|
920 | $ cd rack/ |
- |
-|
921 | $ git log -1 |
- |
-|
922 | commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
923 | Author: Christian Neukirchen <chneukirchen@gmail.com> |
- |
-|
924 | Date: Wed Mar 25 14:49:04 2009 +0100 |
- |
-|
925 | |
- |
-|
926 | Document version change |
- |
-|
927 | |
-107 | |
-
. | |
-108 | [[_cloning_submodules]] |
-
928 | ### Cloning a Project with Submodules ### |
-109 | ==== Cloning a Project with Submodules |
-
929 | |
-110 | |
-
.930 | Here you’ll clone a project with a submodule in it. When you receive such a project, you get the directories that contain submodules, but none of the files yet: |
-111 | Here we’ll clone a project with a submodule in it. When you clone such a project, by default you get the directories that contain submodules, but none of the files within them yet: |
-
931 | |
-112 | |
-
. | |
-113 | [source,console] |
-
|
-114 | ---- |
-|
932 | $ git clone git://github.com/schacon/myproject.git |
-115 | $ git clone https://github.com/chaconinc/MainProject |
-
933 | Initialized empty Git repository in /opt/myproject/.git/ |
-116 | Cloning into 'MainProject'... |
-
934 | remote: Counting objects: 6, done. |
-117 | remote: Counting objects: 14, done. |
-
935 | remote: Compressing objects: 100% (4/4), done. |
-118 | remote: Compressing objects: 100% (13/13), done. |
-
936 | remote: Total 6 (delta 0), reused 0 (delta 0) |
-119 | remote: Total 14 (delta 1), reused 13 (delta 0) |
-
937 | Receiving objects: 100% (6/6), done. |
-120 | Unpacking objects: 100% (14/14), done. |
-
938 | $ cd myproject |
-121 | Checking connectivity... done. |
-
939 | $ ls -l |
-122 | $ cd MainProject |
-
940 | total 8 |
-123 | $ ls -la |
-
941 | -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README |
-124 | total 16 |
-
942 | drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack |
-125 | drwxr-xr-x 9 schacon staff 306 Sep 17 15:21 . |
-
943 | $ ls rack/ |
-126 | drwxr-xr-x 7 schacon staff 238 Sep 17 15:21 .. |
-
944 | $ |
-127 | drwxr-xr-x 13 schacon staff 442 Sep 17 15:21 .git |
-
|
-128 | -rw-r--r-- 1 schacon staff 92 Sep 17 15:21 .gitmodules |
-|
|
-129 | drwxr-xr-x 2 schacon staff 68 Sep 17 15:21 DbConnector |
-|
|
-130 | -rw-r--r-- 1 schacon staff 756 Sep 17 15:21 Makefile |
-|
|
-131 | drwxr-xr-x 3 schacon staff 102 Sep 17 15:21 includes |
-|
|
-132 | drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 scripts |
-|
|
-133 | drwxr-xr-x 4 schacon staff 136 Sep 17 15:21 src |
-|
|
-134 | $ cd DbConnector/ |
-|
|
-135 | $ ls |
-|
|
-136 | $ |
-|
|
-137 | ---- |
-|
945 | |
-138 | |
-
.946 | The `rack` directory is there, but empty. You must run two commands: `git submodule init` to initialize your local configuration file, and `git submodule update` to fetch all the data from that project and check out the appropriate commit listed in your superproject: |
-139 | The `DbConnector` directory is there, but empty. You must run two commands: `git submodule init` to initialize your local configuration file, and `git submodule update` to fetch all the data from that project and check out the appropriate commit listed in your superproject: |
-
947 | |
-140 | |
-
. | |
-141 | [source,console] |
-
|
-142 | ---- |
-|
948 | $ git submodule init |
-143 | $ git submodule init |
-
949 | Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack' |
-144 | Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector' |
-
950 | $ git submodule update |
-145 | $ git submodule update |
-
951 | Initialized empty Git repository in /opt/myproject/rack/.git/ |
-146 | Cloning into 'DbConnector'... |
-
952 | remote: Counting objects: 3181, done. |
-147 | remote: Counting objects: 11, done. |
-
953 | remote: Compressing objects: 100% (1534/1534), done. |
-148 | remote: Compressing objects: 100% (10/10), done. |
-
954 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
-149 | remote: Total 11 (delta 0), reused 11 (delta 0) |
-
955 | Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done. |
-150 | Unpacking objects: 100% (11/11), done. |
-
956 | Resolving deltas: 100% (1951/1951), done. |
-151 | Checking connectivity... done. |
-
957 | Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433' |
-152 | Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc' |
-
|
-153 | ---- |
-|
958 | |
-154 | |
-
.959 | Now your `rack` subdirectory is at the exact state it was in when you committed earlier. If another developer makes changes to the rack code and commits, and you pull that reference down and merge it in, you get something a bit odd: |
-155 | Now your `DbConnector` subdirectory is at the exact state it was in when you committed earlier. |
-
|
-156 | |
-|
|
-157 | There is another way to do this which is a little simpler, however. If you pass `--recursive` to the `git clone` command, it will automatically initialize and update each submodule in the repository. |
-|
|
-158 | |
-|
|
-159 | [source,console] |
-|
|
-160 | ---- |
-|
|
-161 | $ git clone --recursive https://github.com/chaconinc/MainProject |
-|
|
-162 | Cloning into 'MainProject'... |
-|
|
-163 | remote: Counting objects: 14, done. |
-|
|
-164 | remote: Compressing objects: 100% (13/13), done. |
-|
|
-165 | remote: Total 14 (delta 1), reused 13 (delta 0) |
-|
|
-166 | Unpacking objects: 100% (14/14), done. |
-|
|
-167 | Checking connectivity... done. |
-|
|
-168 | Submodule 'DbConnector' (https://github.com/chaconinc/DbConnector) registered for path 'DbConnector' |
-|
|
-169 | Cloning into 'DbConnector'... |
-|
|
-170 | remote: Counting objects: 11, done. |
-|
|
-171 | remote: Compressing objects: 100% (10/10), done. |
-|
|
-172 | remote: Total 11 (delta 0), reused 11 (delta 0) |
-|
|
-173 | Unpacking objects: 100% (11/11), done. |
-|
|
-174 | Checking connectivity... done. |
-|
|
-175 | Submodule path 'DbConnector': checked out 'c3f01dc8862123d317dd46284b05b6892c7b29bc' |
-|
|
-176 | ---- |
-|
960 | |
-177 | |
-
.961 | $ git merge origin/master |
- |
-|
962 | Updating 0550271..85a3eee |
- |
-|
963 | Fast forward |
- |
-|
964 | rack | 2 +- |
- |
-|
965 | 1 files changed, 1 insertions(+), 1 deletions(-) |
- |
-|
966 | [master*]$ git status |
- |
-|
967 | # On branch master |
- |
-|
968 | # Changes not staged for commit: |
- |
-|
969 | # (use "git add <file>..." to update what will be committed) |
- |
-|
970 | # (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
971 | # |
- |
-|
972 | # modified: rack |
- |
-|
973 | # |
- |
-|
974 | |
- |
-|
975 | You merged in what is basically a change to the pointer for your submodule; but it doesn’t update the code in the submodule directory, so it looks like you have a dirty state in your working directory: |
- |
-|
976 | |
- |
-|
977 | $ git diff |
- |
-|
978 | diff --git a/rack b/rack |
- |
-|
979 | index 6c5e70b..08d709f 160000 |
- |
-|
980 | --- a/rack |
- |
-|
981 | +++ b/rack |
- |
-|
982 | @@ -1 +1 @@ |
- |
-|
983 | -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
984 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
985 | |
- |
-|
986 | This is the case because the pointer you have for the submodule isn’t what is actually in the submodule directory. To fix this, you must run `git submodule update` again: |
- |
-|
987 | |
- |
-|
988 | $ git submodule update |
- |
-|
989 | remote: Counting objects: 5, done. |
- |
-|
990 | remote: Compressing objects: 100% (3/3), done. |
- |
-|
991 | remote: Total 3 (delta 1), reused 2 (delta 0) |
- |
-|
992 | Unpacking objects: 100% (3/3), done. |
- |
-|
993 | From git@github.com:schacon/rack |
- |
-|
994 | 08d709f..6c5e70b master -> origin/master |
- |
-|
995 | Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0' |
- |
-|
996 | |
- |
-|
997 | You have to do this every time you pull down a submodule change in the main project. It’s strange, but it works. |
- |
-|
998 | |
- |
-|
999 | One common problem happens when a developer makes a change locally in a submodule but doesn’t push it to a public server. Then, they commit a pointer to that non-public state and push up the superproject. When other developers try to run `git submodule update`, the submodule system can’t find the commit that is referenced, because it exists only on the first developer’s system. If that happens, you see an error like this: |
- |
-|
1000 | |
- |
-|
1001 | $ git submodule update |
- |
-|
1002 | fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
1003 | Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack' |
- |
-|
1004 | |
- |
-|
1005 | You have to see who last changed the submodule: |
- |
-|
1006 | |
- |
-|
1007 | $ git log -1 rack |
- |
-|
1008 | commit 85a3eee996800fcfa91e2119372dd4172bf76678 |
- |
-|
1009 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
1010 | Date: Thu Apr 9 09:19:14 2009 -0700 |
- |
-|
1011 | |
- |
-|
1012 | added a submodule reference I will never make public. hahahahaha! |
- |
-|
1013 | |
- |
-|
1014 | Then, you e-mail that guy and yell at him. |
- |
-|
|
-178 | ==== Working on a Project with Submodules |
-|
|
-179 | |
-|
|
-180 | Now we have a copy of a project with submodules in it and will collaborate with our teammates on both the main project and the submodule project. |
-|
|
-181 | |
-|
|
-182 | ===== Pulling in Upstream Changes |
-|
|
-183 | |
-|
|
-184 | The simplest model of using submodules in a project would be if you were simply consuming a subproject and wanted to get updates from it from time to time but were not actually modifying anything in your checkout. Let's walk though a simple example there. |
-|
|
-185 | |
-|
|
-186 | If you want to check for new work in a submodule, you can go into the directory and run `git fetch` and `git merge` the upstream branch to update the local code. |
-|
|
-187 | |
-|
|
-188 | [source,console] |
-|
|
-189 | ---- |
-|
|
-190 | $ git fetch |
-|
|
-191 | From https://github.com/chaconinc/DbConnector |
-|
|
-192 | c3f01dc..d0354fc master -> origin/master |
-|
|
-193 | $ git merge origin/master |
-|
|
-194 | Updating c3f01dc..d0354fc |
-|
|
-195 | Fast-forward |
-|
|
-196 | scripts/connect.sh | 1 + |
-|
|
-197 | src/db.c | 1 + |
-|
|
-198 | 2 files changed, 2 insertions(+) |
-|
|
-199 | ---- |
-|
|
-200 | |
-|
|
-201 | Now if you go back into the main project and run `git diff --submodule` you can see that the submodule was updated and get a list of commits that were added to it. If you don't want to type `--submodule` every time you run `git diff`, you can set it as the default format by setting the `diff.submodule` config value to ``log''. |
-|
|
-202 | |
-|
|
-203 | [source,console] |
-|
|
-204 | ---- |
-|
|
-205 | $ git config --global diff.submodule log |
-|
|
-206 | $ git diff |
-|
|
-207 | Submodule DbConnector c3f01dc..d0354fc: |
-|
|
-208 | > more efficient db routine |
-|
|
-209 | > better connection routine |
-|
|
-210 | ---- |
-|
|
-211 | |
-|
|
-212 | If you commit at this point then you will lock the submodule into having the new code when other people update. |
-|
|
-213 | |
-|
|
-214 | There is an easier way to do this as well, if you prefer to not manually fetch and merge in the subdirectory. If you run `git submodule update --remote`, Git will go into your submodules and fetch and update for you. |
-|
|
-215 | |
-|
|
-216 | [source,console] |
-|
|
-217 | ---- |
-|
|
-218 | $ git submodule update --remote DbConnector |
-|
|
-219 | remote: Counting objects: 4, done. |
-|
|
-220 | remote: Compressing objects: 100% (2/2), done. |
-|
|
-221 | remote: Total 4 (delta 2), reused 4 (delta 2) |
-|
|
-222 | Unpacking objects: 100% (4/4), done. |
-|
|
-223 | From https://github.com/chaconinc/DbConnector |
-|
|
-224 | 3f19983..d0354fc master -> origin/master |
-|
|
-225 | Submodule path 'DbConnector': checked out 'd0354fc054692d3906c85c3af05ddce39a1c0644' |
-|
|
-226 | ---- |
-|
|
-227 | |
-|
|
-228 | This command will by default assume that you want to update the checkout to the `master` branch of the submodule repository. You can, however, set this to something different if you want. For example, if you want to have the DbConnector submodule track that repository's ``stable'' branch, you can set it in either your `.gitmodules` file (so everyone else also tracks it), or just in your local `.git/config` file. Let's set it in the `.gitmodules` file: |
-|
|
-229 | |
-|
|
-230 | [source,console] |
-|
|
-231 | ---- |
-|
|
-232 | $ git config -f .gitmodules submodule.DbConnector.branch stable |
-|
|
-233 | |
-|
|
-234 | $ git submodule update --remote |
-|
|
-235 | remote: Counting objects: 4, done. |
-|
|
-236 | remote: Compressing objects: 100% (2/2), done. |
-|
|
-237 | remote: Total 4 (delta 2), reused 4 (delta 2) |
-|
|
-238 | Unpacking objects: 100% (4/4), done. |
-|
|
-239 | From https://github.com/chaconinc/DbConnector |
-|
|
-240 | 27cf5d3..c87d55d stable -> origin/stable |
-|
|
-241 | Submodule path 'DbConnector': checked out 'c87d55d4c6d4b05ee34fbc8cb6f7bf4585ae6687' |
-|
|
-242 | ---- |
-|
|
-243 | |
-|
|
-244 | If you leave off the `-f .gitmodules` it will only make the change for you, but it probably makes more sense to track that information with the repository so everyone else does as well. |
-|
|
-245 | |
-|
|
-246 | When we run `git status` at this point, Git will show us that we have ``new commits'' on the submodule. |
-|
|
-247 | |
-|
|
-248 | [source,console] |
-|
|
-249 | ---- |
-|
|
-250 | $ git status |
-|
|
-251 | On branch master |
-|
|
-252 | Your branch is up-to-date with 'origin/master'. |
-|
|
-253 | |
-|
|
-254 | Changes not staged for commit: |
-|
|
-255 | (use "git add <file>..." to update what will be committed) |
-|
|
-256 | (use "git checkout -- <file>..." to discard changes in working directory) |
-|
|
-257 | |
-|
|
-258 | modified: .gitmodules |
-|
|
-259 | modified: DbConnector (new commits) |
-|
|
-260 | |
-|
|
-261 | no changes added to commit (use "git add" and/or "git commit -a") |
-|
|
-262 | ---- |
-|
|
-263 | |
-|
|
-264 | If you set the configuration setting `status.submodulesummary`, Git will also show you a short summary of changes to your submodules: |
-|
|
-265 | |
-|
|
-266 | [source,console] |
-|
|
-267 | ---- |
-|
|
-268 | $ git config status.submodulesummary 1 |
-|
|
-269 | |
-|
|
-270 | $ git status |
-|
|
-271 | On branch master |
-|
|
-272 | Your branch is up-to-date with 'origin/master'. |
-|
|
-273 | |
-|
|
-274 | Changes not staged for commit: |
-|
|
-275 | (use "git add <file>..." to update what will be committed) |
-|
|
-276 | (use "git checkout -- <file>..." to discard changes in working directory) |
-|
|
-277 | |
-|
|
-278 | modified: .gitmodules |
-|
|
-279 | modified: DbConnector (new commits) |
-|
|
-280 | |
-|
|
-281 | Submodules changed but not updated: |
-|
|
-282 | |
-|
|
-283 | * DbConnector c3f01dc...c87d55d (4): |
-|
|
-284 | > catch non-null terminated lines |
-|
|
-285 | ---- |
-|
|
-286 | |
-|
|
-287 | At this point if you run `git diff` we can see both that we have modified our `.gitmodules` file and also that there are a number of commits that we've pulled down and are ready to commit to our submodule project. |
-|
|
-288 | |
-|
|
-289 | [source,console] |
-|
|
-290 | ---- |
-|
|
-291 | $ git diff |
-|
|
-292 | diff --git a/.gitmodules b/.gitmodules |
-|
|
-293 | index 6fc0b3d..fd1cc29 100644 |
-|
|
-294 | --- a/.gitmodules |
-|
|
-295 | +++ b/.gitmodules |
-|
|
-296 | @@ -1,3 +1,4 @@ |
-|
|
-297 | [submodule "DbConnector"] |
-|
|
-298 | path = DbConnector |
-|
|
-299 | url = https://github.com/chaconinc/DbConnector |
-|
|
-300 | + branch = stable |
-|
|
-301 | Submodule DbConnector c3f01dc..c87d55d: |
-|
|
-302 | > catch non-null terminated lines |
-|
|
-303 | > more robust error handling |
-|
|
-304 | > more efficient db routine |
-|
|
-305 | > better connection routine |
-|
|
-306 | ---- |
-|
|
-307 | |
-|
|
-308 | This is pretty cool as we can actually see the log of commits that we're about to commit to in our submodule. Once committed, you can see this information after the fact as well when you run `git log -p`. |
-|
|
-309 | |
-|
|
-310 | [source,console] |
-|
|
-311 | ---- |
-|
|
-312 | $ git log -p --submodule |
-|
|
-313 | commit 0a24cfc121a8a3c118e0105ae4ae4c00281cf7ae |
-|
|
-314 | Author: Scott Chacon <schacon@gmail.com> |
-|
|
-315 | Date: Wed Sep 17 16:37:02 2014 +0200 |
-|
|
-316 | |
-|
|
-317 | updating DbConnector for bug fixes |
-|
|
-318 | |
-|
|
-319 | diff --git a/.gitmodules b/.gitmodules |
-|
|
-320 | index 6fc0b3d..fd1cc29 100644 |
-|
|
-321 | --- a/.gitmodules |
-|
|
-322 | +++ b/.gitmodules |
-|
|
-323 | @@ -1,3 +1,4 @@ |
-|
|
-324 | [submodule "DbConnector"] |
-|
|
-325 | path = DbConnector |
-|
|
-326 | url = https://github.com/chaconinc/DbConnector |
-|
|
-327 | + branch = stable |
-|
|
-328 | Submodule DbConnector c3f01dc..c87d55d: |
-|
|
-329 | > catch non-null terminated lines |
-|
|
-330 | > more robust error handling |
-|
|
-331 | > more efficient db routine |
-|
|
-332 | > better connection routine |
-|
|
-333 | ---- |
-|
|
-334 | |
-|
|
-335 | Git will by default try to update **all** of your submodules when you run `git submodule update --remote` so if you have a lot of them, you may want to pass the name of just the submodule you want to try to update. |
-|
|
-336 | |
-|
|
-337 | ===== Working on a Submodule |
-|
|
-338 | |
-|
|
-339 | It's quite likely that if you're using submodules, you're doing so because you really want to work on the code in the submodule at the same time as you're working on the code in the main project (or across several submodules). Otherwise you would probably instead be using a simpler dependency management system (such as Maven or Rubygems). |
-|
|
-340 | |
-|
|
-341 | So now let's go through an example of making changes to the submodule at the same time as the main project and committing and publishing those changes at the same time. |
-|
|
-342 | |
-|
|
-343 | So far, when we've run the `git submodule update` command to fetch changes from the submodule repositories, Git would get the changes and update the files in the subdirectory but will leave the sub-repository in what's called a ``detached HEAD'' state. This means that there is no local working branch (like ``master'', for example) tracking changes. So any changes you make aren't being tracked well. |
-|
|
-344 | |
-|
|
-345 | In order to set up your submodule to be easier to go in and hack on, you need do two things. You need to go into each submodule and check out a branch to work on. Then you need to tell Git what to do if you have made changes and then `git submodule update --remote` pulls in new work from upstream. The options are that you can merge them into your local work, or you can try to rebase your local work on top of the new changes. |
-|
|
-346 | |
-|
|
-347 | First of all, let's go into our submodule directory and check out a branch. |
-|
|
-348 | |
-|
|
-349 | [source,console] |
-|
|
-350 | ---- |
-|
|
-351 | $ git checkout stable |
-|
|
-352 | Switched to branch 'stable' |
-|
|
-353 | ---- |
-|
|
-354 | |
-|
|
-355 | Let's try it with the ``merge'' option. To specify it manually, we can just add the `--merge` option to our `update` call. Here we'll see that there was a change on the server for this submodule and it gets merged in. |
-|
|
-356 | |
-|
|
-357 | [source,console] |
-|
|
-358 | ---- |
-|
|
-359 | $ git submodule update --remote --merge |
-|
|
-360 | remote: Counting objects: 4, done. |
-|
|
-361 | remote: Compressing objects: 100% (2/2), done. |
-|
|
-362 | remote: Total 4 (delta 2), reused 4 (delta 2) |
-|
|
-363 | Unpacking objects: 100% (4/4), done. |
-|
|
-364 | From https://github.com/chaconinc/DbConnector |
-|
|
-365 | c87d55d..92c7337 stable -> origin/stable |
-|
|
-366 | Updating c87d55d..92c7337 |
-|
|
-367 | Fast-forward |
-|
|
-368 | src/main.c | 1 + |
-|
|
-369 | 1 file changed, 1 insertion(+) |
-|
|
-370 | Submodule path 'DbConnector': merged in '92c7337b30ef9e0893e758dac2459d07362ab5ea' |
-|
|
-371 | ---- |
-|
|
-372 | |
-|
|
-373 | If we go into the DbConnector directory, we have the new changes already merged into our local `stable` branch. Now let's see what happens when we make our own local change to the library and someone else pushes another change upstream at the same time. |
-|
|
-374 | |
-|
|
-375 | [source,console] |
-|
|
-376 | ---- |
-|
|
-377 | $ cd DbConnector/ |
-|
|
-378 | $ vim src/db.c |
-|
|
-379 | $ git commit -am 'unicode support' |
-|
|
-380 | [stable f906e16] unicode support |
-|
|
-381 | 1 file changed, 1 insertion(+) |
-|
|
-382 | ---- |
-|
|
-383 | |
-|
|
-384 | Now if we update our submodule we can see what happens when we have made a local change and upstream also has a change we need to incorporate. |
-|
|
-385 | |
-|
|
-386 | [source,console] |
-|
|
-387 | ---- |
-|
|
-388 | $ git submodule update --remote --rebase |
-|
|
-389 | First, rewinding head to replay your work on top of it... |
-|
|
-390 | Applying: unicode support |
-|
|
-391 | Submodule path 'DbConnector': rebased into '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94' |
-|
|
-392 | ---- |
-|
|
-393 | |
-|
|
-394 | If you forget the `--rebase` or `--merge`, Git will just update the submodule to whatever is on the server and reset your project to a detached HEAD state. |
-|
|
-395 | |
-|
|
-396 | [source,console] |
-|
|
-397 | ---- |
-|
|
-398 | $ git submodule update --remote |
-|
|
-399 | Submodule path 'DbConnector': checked out '5d60ef9bbebf5a0c1c1050f242ceeb54ad58da94' |
-|
|
-400 | ---- |
-|
|
-401 | |
-|
|
-402 | If this happens, don't worry, you can simply go back into the directory and check out your branch again (which will still contain your work) and merge or rebase `origin/stable` (or whatever remote branch you want) manually. |
-|
|
-403 | |
-|
|
-404 | If you haven't committed your changes in your submodule and you run a submodule update that would cause issues, Git will fetch the changes but not overwrite unsaved work in your submodule directory. |
-|
|
-405 | |
-|
|
-406 | [source,console] |
-|
|
-407 | ---- |
-|
|
-408 | $ git submodule update --remote |
-|
|
-409 | remote: Counting objects: 4, done. |
-|
|
-410 | remote: Compressing objects: 100% (3/3), done. |
-|
|
-411 | remote: Total 4 (delta 0), reused 4 (delta 0) |
-|
|
-412 | Unpacking objects: 100% (4/4), done. |
-|
|
-413 | From https://github.com/chaconinc/DbConnector |
-|
|
-414 | 5d60ef9..c75e92a stable -> origin/stable |
-|
|
-415 | error: Your local changes to the following files would be overwritten by checkout: |
-|
|
-416 | scripts/setup.sh |
-|
|
-417 | Please, commit your changes or stash them before you can switch branches. |
-|
|
-418 | Aborting |
-|
|
-419 | Unable to checkout 'c75e92a2b3855c9e5b66f915308390d9db204aca' in submodule path 'DbConnector' |
-|
|
-420 | ---- |
-|
|
-421 | |
-|
|
-422 | If you made changes that conflict with something changed upstream, Git will let you know when you run the update. |
-|
|
-423 | |
-|
|
-424 | [source,console] |
-|
|
-425 | ---- |
-|
|
-426 | $ git submodule update --remote --merge |
-|
|
-427 | Auto-merging scripts/setup.sh |
-|
|
-428 | CONFLICT (content): Merge conflict in scripts/setup.sh |
-|
|
-429 | Recorded preimage for 'scripts/setup.sh' |
-|
|
-430 | Automatic merge failed; fix conflicts and then commit the result. |
-|
|
-431 | Unable to merge 'c75e92a2b3855c9e5b66f915308390d9db204aca' in submodule path 'DbConnector' |
-|
|
-432 | ---- |
-|
|
-433 | |
-|
|
-434 | You can go into the submodule directory and fix the conflict just as you normally would. |
-|
|
-435 | |
-|
|
-436 | [[_publishing_submodules]] |
-|
|
-437 | ===== Publishing Submodule Changes |
-|
|
-438 | |
-|
|
-439 | Now we have some changes in our submodule directory. Some of these were brought in from upstream by our updates and others were made locally and aren't available to anyone else yet as we haven't pushed them yet. |
-|
|
-440 | |
-|
|
-441 | [source,console] |
-|
|
-442 | ---- |
-|
|
-443 | $ git diff |
-|
|
-444 | Submodule DbConnector c87d55d..82d2ad3: |
-|
|
-445 | > Merge from origin/stable |
-|
|
-446 | > updated setup script |
-|
|
-447 | > unicode support |
-|
|
-448 | > remove unnecessary method |
-|
|
-449 | > add new option for conn pooling |
-|
|
-450 | ---- |
-|
|
-451 | |
-|
|
-452 | If we commit in the main project and push it up without pushing the submodule changes up as well, other people who try to check out our changes are going to be in trouble since they will have no way to get the submodule changes that are depended on. Those changes will only exist on our local copy. |
-|
|
-453 | |
-|
|
-454 | In order to make sure this doesn't happen, you can ask Git to check that all your submodules have been pushed properly before pushing the main project. The `git push` command takes the `--recurse-submodules` argument which can be set to either ``check'' or ``on-demand''. The ``check'' option will make `push` simply fail if any of the committed submodule changes haven't been pushed. |
-|
|
-455 | |
-|
|
-456 | [source,console] |
-|
|
-457 | ---- |
-|
|
-458 | $ git push --recurse-submodules=check |
-|
|
-459 | The following submodule paths contain changes that can |
-|
|
-460 | not be found on any remote: |
-|
|
-461 | DbConnector |
-|
|
-462 | |
-|
|
-463 | Please try |
-|
|
-464 | |
-|
|
-465 | git push --recurse-submodules=on-demand |
-|
|
-466 | |
-|
|
-467 | or cd to the path and use |
-|
|
-468 | |
-|
|
-469 | git push |
-|
|
-470 | |
-|
|
-471 | to push them to a remote. |
-|
|
-472 | ---- |
-|
|
-473 | |
-|
|
-474 | As you can see, it also gives us some helpful advice on what we might want to do next. The simple option is to go into each submodule and manually push to the remotes to make sure they're externally available and then try this push again. |
-|
|
-475 | |
-|
|
-476 | The other option is to use the ``on-demand'' value, which will try to do this for you. |
-|
|
-477 | |
-|
|
-478 | [source,console] |
-|
|
-479 | ---- |
-|
|
-480 | $ git push --recurse-submodules=on-demand |
-|
|
-481 | Pushing submodule 'DbConnector' |
-|
|
-482 | Counting objects: 9, done. |
-|
|
-483 | Delta compression using up to 8 threads. |
-|
|
-484 | Compressing objects: 100% (8/8), done. |
-|
|
-485 | Writing objects: 100% (9/9), 917 bytes | 0 bytes/s, done. |
-|
|
-486 | Total 9 (delta 3), reused 0 (delta 0) |
-|
|
-487 | To https://github.com/chaconinc/DbConnector |
-|
|
-488 | c75e92a..82d2ad3 stable -> stable |
-|
|
-489 | Counting objects: 2, done. |
-|
|
-490 | Delta compression using up to 8 threads. |
-|
|
-491 | Compressing objects: 100% (2/2), done. |
-|
|
-492 | Writing objects: 100% (2/2), 266 bytes | 0 bytes/s, done. |
-|
|
-493 | Total 2 (delta 1), reused 0 (delta 0) |
-|
|
-494 | To https://github.com/chaconinc/MainProject |
-|
|
-495 | 3d6d338..9a377d1 master -> master |
-|
|
-496 | ---- |
-|
|
-497 | |
-|
|
-498 | As you can see there, Git went into the DbConnector module and pushed it before pushing the main project. If that submodule push fails for some reason, the main project push will also fail. |
-|
|
-499 | |
-|
|
-500 | ===== Merging Submodule Changes |
-|
|
-501 | |
-|
|
-502 | If you change a submodule reference at the same time as someone else, you may run into some problems. That is, if the submodule histories have diverged and are committed to diverging branches in a superproject, it may take a bit of work for you to fix. |
-|
|
-503 | |
-|
|
-504 | If one of the commits is a direct ancestor of the other (a fast-forward merge), then Git will simply choose the latter for the merge, so that works fine. |
-|
|
-505 | |
-|
|
-506 | Git will not attempt even a trivial merge for you, however. If the submodule commits diverge and need to be merged, you will get something that looks like this: |
-|
|
-507 | |
-|
|
-508 | [source,console] |
-|
|
-509 | ---- |
-|
|
-510 | $ git pull |
-|
|
-511 | remote: Counting objects: 2, done. |
-|
|
-512 | remote: Compressing objects: 100% (1/1), done. |
-|
|
-513 | remote: Total 2 (delta 1), reused 2 (delta 1) |
-|
|
-514 | Unpacking objects: 100% (2/2), done. |
-|
|
-515 | From https://github.com/chaconinc/MainProject |
-|
|
-516 | 9a377d1..eb974f8 master -> origin/master |
-|
|
-517 | Fetching submodule DbConnector |
-|
|
-518 | warning: Failed to merge submodule DbConnector (merge following commits not found) |
-|
|
-519 | Auto-merging DbConnector |
-|
|
-520 | CONFLICT (submodule): Merge conflict in DbConnector |
-|
|
-521 | Automatic merge failed; fix conflicts and then commit the result. |
-|
|
-522 | ---- |
-|
|
-523 | |
-|
|
-524 | So basically what has happened here is that Git has figured out that the two branches record points in the submodule's history that are divergent and need to be merged. It explains it as ``merge following commits not found'', which is confusing but we'll explain why that is in a bit. |
-|
|
-525 | |
-|
|
-526 | To solve the problem, you need to figure out what state the submodule should be in. Strangely, Git doesn't really give you much information to help out here, not even the SHAs of the commits of both sides of the history. Fortunately, it's simple to figure out. If you run `git diff` you can get the SHAs of the commits recorded in both branches you were trying to merge. |
-|
|
-527 | |
-|
|
-528 | [source,console] |
-|
|
-529 | ---- |
-|
|
-530 | $ git diff |
-|
|
-531 | diff --cc DbConnector |
-|
|
-532 | index eb41d76,c771610..0000000 |
-|
|
-533 | --- a/DbConnector |
-|
|
-534 | +++ b/DbConnector |
-|
|
-535 | ---- |
-|
|
-536 | |
-|
|
-537 | So, in this case, `eb41d76` is the commit in our submodule that *we* had and `c771610` is the commit that upstream had. If we go into our submodule directory, it should already be on `eb41d76` as the merge would not have touched it. If for whatever reason it's not, you can simply create and checkout a branch pointing to it. |
-|
|
-538 | |
-|
|
-539 | What is important is the SHA of the commit from the other side. This is what you'll have to merge in and resolve. You can either just try the merge with the SHA directly, or you can create a branch for it and then try to merge that in. We would suggest the latter, even if only to make a nicer merge commit message. |
-|
|
-540 | |
-|
|
-541 | So, we will go into our submodule directory, create a branch based on that second SHA from `git diff` and manually merge. |
-|
|
-542 | |
-|
|
-543 | [source,console] |
-|
|
-544 | ---- |
-|
|
-545 | $ cd DbConnector |
-|
|
-546 | |
-|
|
-547 | $ git rev-parse HEAD |
-|
|
-548 | eb41d764bccf88be77aced643c13a7fa86714135 |
-|
|
-549 | |
-|
|
-550 | $ git branch try-merge c771610 |
-|
|
-551 | (DbConnector) $ git merge try-merge |
-|
|
-552 | Auto-merging src/main.c |
-|
|
-553 | CONFLICT (content): Merge conflict in src/main.c |
-|
|
-554 | Recorded preimage for 'src/main.c' |
-|
|
-555 | Automatic merge failed; fix conflicts and then commit the result. |
-|
|
-556 | ---- |
-|
|
-557 | |
-|
|
-558 | We got an actual merge conflict here, so if we resolve that and commit it, then we can simply update the main project with the result. |
-|
|
-559 | |
-|
|
-560 | [source,console] |
-|
|
-561 | ---- |
-|
|
-562 | $ vim src/main.c <1> |
-|
|
-563 | $ git add src/main.c |
-|
|
-564 | $ git commit -am 'merged our changes' |
-|
|
-565 | Recorded resolution for 'src/main.c'. |
-|
|
-566 | [master 9fd905e] merged our changes |
-|
|
-567 | |
-|
|
-568 | $ cd .. <2> |
-|
|
-569 | $ git diff <3> |
-|
|
-570 | diff --cc DbConnector |
-|
|
-571 | index eb41d76,c771610..0000000 |
-|
|
-572 | --- a/DbConnector |
-|
|
-573 | +++ b/DbConnector |
-|
|
-574 | @@@ -1,1 -1,1 +1,1 @@@ |
-|
|
-575 | - Subproject commit eb41d764bccf88be77aced643c13a7fa86714135 |
-|
|
-576 | -Subproject commit c77161012afbbe1f58b5053316ead08f4b7e6d1d |
-|
|
-577 | ++Subproject commit 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a |
-|
|
-578 | $ git add DbConnector <4> |
-|
|
-579 | |
-|
|
-580 | $ git commit -m "Merge Tom's Changes" <5> |
-|
|
-581 | [master 10d2c60] Merge Tom's Changes |
-|
|
-582 | ---- |
-|
|
-583 | |
-|
|
-584 | <1> First we resolve the conflict |
-|
|
-585 | <2> Then we go back to the main project directory |
-|
|
-586 | <3> We can check the SHAs again |
-|
|
-587 | <4> Resolve the conflicted submodule entry |
-|
|
-588 | <5> Commit our merge |
-|
|
-589 | |
-|
|
-590 | It can be a bit confusing, but it's really not very hard. |
-|
|
-591 | |
-|
|
-592 | Interestingly, there is another case that Git handles. |
-|
|
-593 | If a merge commit exists in the submodule directory that contains **both** commits in it's history, Git will suggest it to you as a possible solution. It sees that at some point in the submodule project, someone merged branches containing these two commits, so maybe you'll want that one. |
-|
|
-594 | |
-|
|
-595 | This is why the error message from before was ``merge following commits not found'', because it could not do *this*. It's confusing because who would expect it to **try** to do this? |
-|
|
-596 | |
-|
|
-597 | If it does find a single acceptable merge commit, you'll see something like this: |
-|
|
-598 | |
-|
|
-599 | [source,console] |
-|
|
-600 | ---- |
-|
|
-601 | $ git merge origin/master |
-|
|
-602 | warning: Failed to merge submodule DbConnector (not fast-forward) |
-|
|
-603 | Found a possible merge resolution for the submodule: |
-|
|
-604 | 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a: > merged our changes |
-|
|
-605 | If this is correct simply add it to the index for example |
-|
|
-606 | by using: |
-|
|
-607 | |
-|
|
-608 | git update-index --cacheinfo 160000 9fd905e5d7f45a0d4cbc43d1ee550f16a30e825a "DbConnector" |
-|
|
-609 | |
-|
|
-610 | which will accept this suggestion. |
-|
|
-611 | Auto-merging DbConnector |
-|
|
-612 | CONFLICT (submodule): Merge conflict in DbConnector |
-|
|
-613 | Automatic merge failed; fix conflicts and then commit the result. |
-|
|
-614 | ---- |
-|
|
-615 | |
-|
|
-616 | What it's suggesting that you do is to update the index like you had run `git add`, which clears the conflict, then commit. You probably shouldn't do this though. You can just as easily go into the submodule directory, see what the difference is, fast-forward to this commit, test it properly, and then commit it. |
-|
|
-617 | |
-|
|
-618 | [source,console] |
-|
|
-619 | ---- |
-|
|
-620 | $ cd DbConnector/ |
-|
|
-621 | $ git merge 9fd905e |
-|
|
-622 | Updating eb41d76..9fd905e |
-|
|
-623 | Fast-forward |
-|
|
-624 | |
-|
|
-625 | $ cd .. |
-|
|
-626 | $ git add DbConnector |
-|
|
-627 | $ git commit -am 'Fast forwarded to a common submodule child' |
-|
|
-628 | ---- |
-|
|
-629 | |
-|
|
-630 | This accomplishes the same thing, but at least this way you can verify that it works and you have the code in your submodule directory when you're done. |
-|
|
-631 | |
-|
|
-632 | |
-|
|
-633 | ==== Submodule Tips |
-|
|
-634 | |
-|
|
-635 | There are a few things you can do to make working with submodules a little easier. |
-|
|
-636 | |
-|
|
-637 | ===== Submodule Foreach |
-|
|
-638 | |
-|
|
-639 | There is a `foreach` submodule command to run some arbitrary command in each submodule. This can be really helpful if you have a number of submodules in the same project. |
-|
|
-640 | |
-|
|
-641 | For example, let's say we want to start a new feature or do a bugfix and we have work going on in several submodules. We can easily stash all the work in all our submodules. |
-|
|
-642 | |
-|
|
-643 | [source,console] |
-|
|
-644 | ---- |
-|
|
-645 | $ git submodule foreach 'git stash' |
-|
|
-646 | Entering 'CryptoLibrary' |
-|
|
-647 | No local changes to save |
-|
|
-648 | Entering 'DbConnector' |
-|
|
-649 | Saved working directory and index state WIP on stable: 82d2ad3 Merge from origin/stable |
-|
|
-650 | HEAD is now at 82d2ad3 Merge from origin/stable |
-|
|
-651 | ---- |
-|
|
-652 | |
-|
|
-653 | Then we can create a new branch and switch to it in all our submodules. |
-|
|
-654 | |
-|
|
-655 | [source,console] |
-|
|
-656 | ---- |
-|
|
-657 | $ git submodule foreach 'git checkout -b featureA' |
-|
|
-658 | Entering 'CryptoLibrary' |
-|
|
-659 | Switched to a new branch 'featureA' |
-|
|
-660 | Entering 'DbConnector' |
-|
|
-661 | Switched to a new branch 'featureA' |
-|
|
-662 | ---- |
-|
|
-663 | |
-|
|
-664 | You get the idea. One really useful thing you can do is produce a nice unified diff of what is changed in your main project and all your subprojects as well. |
-|
|
-665 | |
-|
|
-666 | [source,console] |
-|
|
-667 | ---- |
-|
|
-668 | $ git diff; git submodule foreach 'git diff' |
-|
|
-669 | Submodule DbConnector contains modified content |
-|
|
-670 | diff --git a/src/main.c b/src/main.c |
-|
|
-671 | index 210f1ae..1f0acdc 100644 |
-|
|
-672 | --- a/src/main.c |
-|
|
-673 | +++ b/src/main.c |
-|
|
-674 | @@ -245,6 +245,8 @@ static int handle_alias(int *argcp, const char ***argv) |
-|
|
-675 | |
-|
|
-676 | commit_pager_choice(); |
-|
|
-677 | |
-|
|
-678 | + url = url_decode(url_orig); |
-|
|
-679 | + |
-|
|
-680 | /* build alias_argv */ |
-|
|
-681 | alias_argv = xmalloc(sizeof(*alias_argv) * (argc + 1)); |
-|
|
-682 | alias_argv[0] = alias_string + 1; |
-|
|
-683 | Entering 'DbConnector' |
-|
|
-684 | diff --git a/src/db.c b/src/db.c |
-|
|
-685 | index 1aaefb6..5297645 100644 |
-|
|
-686 | --- a/src/db.c |
-|
|
-687 | +++ b/src/db.c |
-|
|
-688 | @@ -93,6 +93,11 @@ char *url_decode_mem(const char *url, int len) |
-|
|
-689 | return url_decode_internal(&url, len, NULL, &out, 0); |
-|
|
-690 | } |
-|
|
-691 | |
-|
|
-692 | +char *url_decode(const char *url) |
-|
|
-693 | +{ |
-|
|
-694 | + return url_decode_mem(url, strlen(url)); |
-|
|
-695 | +} |
-|
|
-696 | + |
-|
|
-697 | char *url_decode_parameter_name(const char **query) |
-|
|
-698 | { |
-|
|
-699 | struct strbuf out = STRBUF_INIT; |
-|
|
-700 | ---- |
-|
|
-701 | |
-|
|
-702 | Here we can see that we're defining a function in a submodule and calling it in the main project. This is obviously a simplified example, but hopefully it gives you an idea of how this may be useful. |
-|
|
-703 | |
-|
|
-704 | ===== Useful Aliases |
-|
|
-705 | |
-|
|
-706 | You may want to set up some aliases for some of these commands as they can be quite long and you can't set configuration options for most of them to make them defaults. We covered setting up Git aliases in <<_git_aliases>>, but here is an example of what you may want to set up if you plan on working with submodules in Git a lot. |
-|
|
-707 | |
-|
|
-708 | [source,console] |
-|
|
-709 | ---- |
-|
|
-710 | $ git config alias.sdiff '!'"git diff && git submodule foreach 'git diff'" |
-|
|
-711 | $ git config alias.spush 'push --recurse-submodules=on-demand' |
-|
|
-712 | $ git config alias.supdate 'submodule update --remote --merge' |
-|
|
-713 | ---- |
-|
1015 | |
-714 | |
-
.1016 | |
-715 | This way you can simply run `git supdate` when you want to update your submodules, or `git spush` to push with submodule dependency checking. |
-
1017 | |
- |
-|
1018 | ### Superprojects ### |
- |
-|
1019 | |
- |
-|
1020 | Sometimes, developers want to get a combination of a large project’s subdirectories, depending on what team they’re on. This is common if you’re coming from CVS or Subversion, where you’ve defined a module or collection of subdirectories, and you want to keep this type of workflow. |
- |
-|
1021 | |
- |
-|
1022 | A good way to do this in Git is to make each of the subdirectories a separate Git repository and then create superproject Git repositories that contain multiple submodules. A benefit of this approach is that you can more specifically define the relationships between the projects with tags and branches in the superprojects. |
- |
-|
1023 | |
-716 | |
-
.1024 | ### Issues with Submodules ### |
-717 | ==== Issues with Submodules |
-
1025 | |
-718 | |
-
.1026 | Using submodules isn’t without hiccups, however. First, you must be relatively careful when working in the submodule directory. When you run `git submodule update`, it checks out the specific version of the project, but not within a branch. This is called having a detached HEAD — it means the HEAD file points directly to a commit, not to a symbolic reference. The issue is that you generally don’t want to work in a detached HEAD environment, because it’s easy to lose changes. If you do an initial `submodule update`, commit in that submodule directory without creating a branch to work in, and then run `git submodule update` again from the superproject without committing in the meantime, Git will overwrite your changes without telling you. Technically you won’t lose the work, but you won’t have a branch pointing to it, so it will be somewhat difficult to retrieve. |
-719 | Using submodules isn’t without hiccups, however. |
-
1027 | |
- |
-|
1028 | To avoid this issue, create a branch when you work in a submodule directory with `git checkout -b work` or something equivalent. When you do the submodule update a second time, it will still revert your work, but at least you have a pointer to get back to. |
- |
-|
1029 | |
-720 | |
-
.1030 | Switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory: |
-721 | For instance switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory: |
-
1031 | |
-722 | |
-
. | |
-723 | [source,console] |
-
|
-724 | ---- |
-|
1032 | $ git checkout -b rack |
-725 | $ git checkout -b add-crypto |
-
1033 | Switched to a new branch "rack" |
-726 | Switched to a new branch 'add-crypto' |
-
|
-727 | |
-|
1034 | $ git submodule add git@github.com:schacon/rack.git rack |
-728 | $ git submodule add https://github.com/chaconinc/CryptoLibrary |
-
1035 | Initialized empty Git repository in /opt/myproj/rack/.git/ |
- |
-|
1036 | ... |
- |
-|
1037 | Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done. |
- |
-|
1038 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
|
-729 | Cloning into 'CryptoLibrary'... |
-|
|
-730 | ... |
-|
|
-731 | |
-|
1039 | $ git commit -am 'added rack submodule' |
-732 | $ git commit -am 'adding crypto library' |
-
1040 | [rack cc49a69] added rack submodule |
-733 | [add-crypto 4445836] adding crypto library |
-
1041 | 2 files changed, 4 insertions(+), 0 deletions(-) |
-734 | 2 files changed, 4 insertions(+) |
-
1042 | create mode 100644 .gitmodules |
- |
-|
1043 | create mode 160000 rack |
- |
-|
|
-735 | create mode 160000 CryptoLibrary |
-|
|
-736 | |
-|
1044 | $ git checkout master |
-737 | $ git checkout master |
-
|
-738 | warning: unable to rmdir CryptoLibrary: Directory not empty |
-|
1045 | Switched to branch "master" |
-739 | Switched to branch 'master' |
-
|
-740 | Your branch is up-to-date with 'origin/master'. |
-|
|
-741 | |
-|
1046 | $ git status |
-742 | $ git status |
-
1047 | # On branch master |
-743 | On branch master |
-
|
-744 | Your branch is up-to-date with 'origin/master'. |
-|
|
-745 | |
-|
1048 | # Untracked files: |
-746 | Untracked files: |
-
1049 | # (use "git add <file>..." to include in what will be committed) |
-747 | (use "git add <file>..." to include in what will be committed) |
-
1050 | # |
-748 | |
-
1051 | # rack/ |
-749 | CryptoLibrary/ |
-
1052 | |
-750 | |
-
. | |
-751 | nothing added to commit but untracked files present (use "git add" to track) |
-
|
-752 | ---- |
-|
1053 | You have to either move it out of the way or remove it, in which case you have to clone it again when you switch back—and you may lose local changes or branches that you didn’t push up. |
-753 | |
-
|
-754 | Removing the directory isn't difficult, but it can be a bit confusing to have that in there. If you do remove it and then switch back to the branch that has that submodule, you will need to run `submodule update --init` to repopulate it. |
-|
|
-755 | |
-|
|
-756 | [source,console] |
-|
|
-757 | ---- |
-|
|
-758 | $ git clean -fdx |
-|
|
-759 | Removing CryptoLibrary/ |
-|
|
-760 | |
-|
|
-761 | $ git checkout add-crypto |
-|
|
-762 | Switched to branch 'add-crypto' |
-|
|
-763 | |
-|
|
-764 | $ ls CryptoLibrary/ |
-|
|
-765 | |
-|
|
-766 | $ git submodule update --init |
-|
|
-767 | Submodule path 'CryptoLibrary': checked out 'b8dda6aa182ea4464f3f3264b11e0268545172af' |
-|
|
-768 | |
-|
|
-769 | $ ls CryptoLibrary/ |
-|
|
-770 | Makefile includes scripts src |
-|
|
-771 | ---- |
-|
|
-772 | |
-|
|
-773 | Again, not really very difficult, but it can be a little confusing. |
-|
1054 | |
-774 | |
-
.1055 | The last main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have the rack files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run `submodule add`, Git yells at you: |
-775 | The other main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run `submodule add`, Git yells at you: |
-
1056 | |
-776 | |
-
. | |
-777 | [source,console] |
-
|
-778 | ---- |
-|
1057 | $ rm -Rf rack/ |
-779 | $ rm -Rf CryptoLibrary/ |
-
1058 | $ git submodule add git@github.com:schacon/rack.git rack |
-780 | $ git submodule add https://github.com/chaconinc/CryptoLibrary |
-
1059 | 'rack' already exists in the index |
-781 | 'CryptoLibrary' already exists in the index |
-
|
-782 | ---- |
-|
1060 | |
-783 | |
-
.1061 | You have to unstage the `rack` directory first. Then you can add the submodule: |
-784 | You have to unstage the `CryptoLibrary` directory first. Then you can add the submodule: |
-
1062 | |
-785 | |
-
. | |
-786 | [source,console] |
-
|
-787 | ---- |
-|
1063 | $ git rm -r rack |
-788 | $ git rm -r CryptoLibrary |
-
1064 | $ git submodule add git@github.com:schacon/rack.git rack |
-789 | $ git submodule add https://github.com/chaconinc/CryptoLibrary |
-
1065 | Initialized empty Git repository in /opt/testsub/rack/.git/ |
-790 | Cloning into 'CryptoLibrary'... |
-
1066 | remote: Counting objects: 3184, done. |
-791 | remote: Counting objects: 11, done. |
-
1067 | remote: Compressing objects: 100% (1465/1465), done. |
-792 | remote: Compressing objects: 100% (10/10), done. |
-
1068 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
-793 | remote: Total 11 (delta 0), reused 11 (delta 0) |
-
1069 | Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done. |
-794 | Unpacking objects: 100% (11/11), done. |
-
1070 | Resolving deltas: 100% (1952/1952), done. |
-795 | Checking connectivity... done. |
-
|
-796 | ---- |
-|
1071 | |
-797 | |
-
.1072 | Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule — you get this error: |
-798 | Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule – you get this error: |
-
1073 | |
-799 | |
-
. | |
-800 | [source,console] |
-
|
-801 | ---- |
-|
1074 | $ git checkout master |
-802 | $ git checkout master |
-
1075 | error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge. |
-803 | error: The following untracked working tree files would be overwritten by checkout: |
-
|
-804 | CryptoLibrary/Makefile |
-|
|
-805 | CryptoLibrary/includes/crypto.h |
-|
|
-806 | ... |
-|
|
-807 | Please move or remove them before you can switch branches. |
-|
|
-808 | Aborting |
-|
|
-809 | ---- |
-|
1076 | |
-810 | |
-
.1077 | You have to move the `rack` submodule directory out of the way before you can switch to a branch that doesn’t have it: |
- |
-|
1078 | |
- |
-|
1079 | $ mv rack /tmp/ |
- |
-|
1080 | $ git checkout master |
- |
-|
1081 | Switched to branch "master" |
- |
-|
1082 | $ ls |
- |
-|
1083 | README rack |
- |
-|
|
-811 | You can force it to switch with `checkout -f`, but be careful that you don't have unsaved changes in there as they could be overwritten with that command. |
-|
|
-812 | |
-|
|
-813 | [source,console] |
-|
|
-814 | ---- |
-|
|
-815 | $ git checkout -f master |
-|
|
-816 | warning: unable to rmdir CryptoLibrary: Directory not empty |
-|
|
-817 | Switched to branch 'master' |
-|
|
-818 | ---- |
-|
1084 | |
-819 | |
-
.1085 | Then, when you switch back, you get an empty `rack` directory. You can either run `git submodule update` to reclone, or you can move your `/tmp/rack` directory back into the empty directory. |
-820 | Then, when you switch back, you get an empty `CryptoLibrary` directory for some reason and `git submodule update` may not fix it either. You may need to go into your submodule directory and run a `git checkout .` to get all your files back. You could run this in a `submodule foreach` script to run it for multiple submodules. |
-
|
-821 | |
-|
|
-822 | It's important to note that submodules these days keep all their Git data in the top project's `.git` directory, so unlike much older versions of Git, destroying a submodule directory won't lose any commits or branches that you had. |
-|
|
-823 | |
-|
1086 | |
-824 | With these tools, submodules can be a fairly simple and effective method for developing on several related but still separate projects simultaneously. |
-
1087 | ## Subtree Merging ## |
- |
-|
1088 | |
- |
-|
1089 | Now that you’ve seen the difficulties of the submodule system, let’s look at an alternate way to solve the same problem. When Git merges, it looks at what it has to merge together and then chooses an appropriate merging strategy to use. If you’re merging two branches, Git uses a _recursive_ strategy. If you’re merging more than two branches, Git picks the _octopus_ strategy. These strategies are automatically chosen for you because the recursive strategy can handle complex three-way merge situations — for example, more than one common ancestor — but it can only handle merging two branches. The octopus merge can handle multiple branches but is more cautious to avoid difficult conflicts, so it’s chosen as the default strategy if you’re trying to merge more than two branches. |
- |
-|
1090 | |
- |
-|
1091 | However, there are other strategies you can choose as well. One of them is the _subtree_ merge, and you can use it to deal with the subproject issue. Here you’ll see how to do the same rack embedding as in the last section, but using subtree merges instead. |
- |
-|
1092 | |
- |
-|
1093 | The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one and vice versa. When you specify a subtree merge, Git is smart enough to figure out that one is a subtree of the other and merge appropriately — it’s pretty amazing. |
- |
-|
1094 | |
- |
-|
1095 | You first add the Rack application to your project. You add the Rack project as a remote reference in your own project and then check it out into its own branch: |
- |
-|
1096 | |
- |
-|
1097 | $ git remote add rack_remote git@github.com:schacon/rack.git |
- |
-|
1098 | $ git fetch rack_remote |
- |
-|
1099 | warning: no common commits |
- |
-|
1100 | remote: Counting objects: 3184, done. |
- |
-|
1101 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1102 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1103 | Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. |
- |
-|
1104 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1105 | From git@github.com:schacon/rack |
- |
-|
1106 | * [new branch] build -> rack_remote/build |
- |
-|
1107 | * [new branch] master -> rack_remote/master |
- |
-|
1108 | * [new branch] rack-0.4 -> rack_remote/rack-0.4 |
- |
-|
1109 | * [new branch] rack-0.9 -> rack_remote/rack-0.9 |
- |
-|
1110 | $ git checkout -b rack_branch rack_remote/master |
- |
-|
1111 | Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. |
- |
-|
1112 | Switched to a new branch "rack_branch" |
- |
-|
1113 | |
- |
-|
1114 | Now you have the root of the Rack project in your `rack_branch` branch and your own project in the `master` branch. If you check out one and then the other, you can see that they have different project roots: |
- |
-|
1115 | |
- |
-|
1116 | $ ls |
- |
-|
1117 | AUTHORS KNOWN-ISSUES Rakefile contrib lib |
- |
-|
1118 | COPYING README bin example test |
- |
-|
1119 | $ git checkout master |
- |
-|
1120 | Switched to branch "master" |
- |
-|
1121 | $ ls |
- |
-|
1122 | README |
- |
-|
1123 | |
- |
-|
1124 | You want to pull the Rack project into your `master` project as a subdirectory. You can do that in Git with `git read-tree`. You’ll learn more about `read-tree` and its friends in Chapter 9, but for now know that it reads the root tree of one branch into your current staging area and working directory. You just switched back to your `master` branch, and you pull the `rack` branch into the `rack` subdirectory of your `master` branch of your main project: |
- |
-|
1125 | |
- |
-|
1126 | $ git read-tree --prefix=rack/ -u rack_branch |
- |
-|
1127 | |
- |
-|
1128 | When you commit, it looks like you have all the Rack files under that subdirectory — as though you copied them in from a tarball. What gets interesting is that you can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, you can pull in upstream changes by switching to that branch and pulling: |
- |
-|
1129 | |
- |
-|
1130 | $ git checkout rack_branch |
- |
-|
1131 | $ git pull |
- |
-|
1132 | |
- |
-|
1133 | Then, you can merge those changes back into your master branch. You can use `git merge -s subtree` and it will work fine; but Git will also merge the histories together, which you probably don’t want. To pull in the changes and prepopulate the commit message, use the `--squash` and `--no-commit` options as well as the `-s subtree` strategy option: |
- |
-|
1134 | |
- |
-|
1135 | $ git checkout master |
- |
-|
1136 | $ git merge --squash -s subtree --no-commit rack_branch |
- |
-|
1137 | Squash commit -- not updating HEAD |
- |
-|
1138 | Automatic merge went well; stopped before committing as requested |
- |
-|
1139 | |
- |
-|
1140 | All the changes from your Rack project are merged in and ready to be committed locally. You can also do the opposite — make changes in the `rack` subdirectory of your master branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. |
- |
-|
1141 | |
- |
-|
1142 | To get a diff between what you have in your `rack` subdirectory and the code in your `rack_branch` branch — to see if you need to merge them — you can’t use the normal `diff` command. Instead, you must run `git diff-tree` with the branch you want to compare to: |
- |
-|
1143 | |
- |
-|
1144 | $ git diff-tree -p rack_branch |
- |
-|
1145 | |
- |
-|
1146 | Or, to compare what is in your `rack` subdirectory with what the `master` branch on the server was the last time you fetched, you can run |
- |
-|
1147 | |
- |
-|
1148 | $ git diff-tree -p rack_remote/master |
- |
-|
1149 | |
- |
-|
1150 | ## Summary ## |
- |
-|
1151 | |
- |
-|
1152 | You’ve seen a number of advanced tools that allow you to manipulate your commits and staging area more precisely. When you notice issues, you should be able to easily figure out what commit introduced them, when, and by whom. If you want to use subprojects in your project, you’ve learned a few ways to accommodate those needs. At this point, you should be able to do most of the things in Git that you’ll need on the command line day to day and feel comfortable doing so. |
- |
-|
1153 | |
-825 | |
-
C:\Users\15625\Documents\Git\progit\en\06-git-tools\01-chapter6.markdown | -C:\Users\15625\Documents\Git\progit2-ja\book\07-git-tools\sections\subtree-merges.asc | -||
---|---|---|---|
.1 | # Git Tools # |
-1 | [[_subtree_merge]] |
-
2 | |
- |
-|
3 | 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. |
- |
-|
4 | |
- |
-|
5 | 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. |
- |
-|
6 | |
- |
-|
7 | ## Revision Selection ## |
- |
-|
8 | |
- |
-|
9 | 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. |
- |
-|
10 | |
- |
-|
11 | ### Single Revisions ### |
- |
-|
12 | |
- |
-|
13 | 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. |
- |
-|
14 | |
- |
-|
15 | ### Short SHA ### |
- |
-|
16 | |
- |
-|
17 | 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. |
- |
-|
18 | |
- |
-|
19 | For example, to see a specific commit, suppose you run a `git log` command and identify the commit where you added certain functionality: |
- |
-|
20 | |
- |
-|
21 | $ git log |
- |
-|
22 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
23 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
24 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
25 | |
- |
-|
26 | fixed refs handling, added gc auto, updated tests |
- |
-|
27 | |
- |
-|
28 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
29 | Merge: 1c002dd... 35cfb2b... |
- |
-|
30 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
31 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
32 | |
- |
-|
33 | Merge commit 'phedders/rdocs' |
- |
-|
34 | |
- |
-|
35 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
36 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
37 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
38 | |
- |
-|
39 | added some blame and merge stuff |
- |
-|
40 | |
- |
-|
41 | In this case, choose `1c002dd....` If you `git show` that commit, the following commands are equivalent (assuming the shorter versions are unambiguous): |
- |
-|
42 | |
- |
-|
43 | $ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
44 | $ git show 1c002dd4b536e7479f |
- |
-|
45 | $ git show 1c002d |
- |
-|
46 | |
- |
-|
47 | 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: |
- |
-|
48 | |
- |
-|
49 | $ git log --abbrev-commit --pretty=oneline |
- |
-|
50 | ca82a6d changed the version number |
- |
-|
51 | 085bb3b removed unnecessary test code |
- |
-|
52 | a11bef0 first commit |
- |
-|
53 | |
- |
-|
54 | 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. |
- |
-|
55 | |
- |
-|
56 | ### A SHORT NOTE ABOUT SHA-1 ### |
- |
-|
57 | |
- |
-|
58 | 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? |
- |
-|
59 | |
- |
-|
60 | 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. |
- |
-|
61 | |
- |
-|
62 | 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. |
- |
-|
63 | |
- |
-|
64 | 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. 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. |
- |
-|
65 | |
- |
-|
66 | ### Branch References ### |
- |
-|
67 | |
- |
-|
68 | The most straightforward way to specify a commit requires that it have 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`: |
- |
-|
69 | |
- |
-|
70 | $ git show ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
71 | $ git show topic1 |
- |
-|
72 | |
- |
-|
73 | 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 Chapter 9 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. |
- |
-|
74 | |
- |
-|
75 | $ git rev-parse topic1 |
- |
-|
76 | ca82a6dff817ec66f44342007202690a93763949 |
- |
-|
77 | |
- |
-|
78 | ### RefLog Shortnames ### |
- |
-|
79 | |
- |
-|
80 | 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. |
- |
-|
81 | |
- |
-|
82 | You can see your reflog by using `git reflog`: |
- |
-|
83 | |
- |
-|
84 | $ git reflog |
- |
-|
85 | 734713b HEAD@{0}: commit: fixed refs handling, added gc auto, updated |
- |
-|
86 | d921970 HEAD@{1}: merge phedders/rdocs: Merge made by recursive. |
- |
-|
87 | 1c002dd HEAD@{2}: commit: added some blame and merge stuff |
- |
-|
88 | 1c36188 HEAD@{3}: rebase -i (squash): updating HEAD |
- |
-|
89 | 95df984 HEAD@{4}: commit: # This is a combination of two commits. |
- |
-|
90 | 1c36188 HEAD@{5}: rebase -i (squash): updating HEAD |
- |
-|
91 | 7e05da5 HEAD@{6}: rebase -i (pick): updating HEAD |
- |
-|
92 | |
- |
-|
93 | 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: |
- |
-|
94 | |
- |
-|
95 | $ git show HEAD@{5} |
- |
-|
96 | |
- |
-|
97 | 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 |
- |
-|
98 | |
- |
-|
99 | $ git show master@{yesterday} |
- |
-|
100 | |
- |
-|
101 | 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. |
- |
-|
102 | |
- |
-|
103 | To see reflog information formatted like the `git log` output, you can run `git log -g`: |
- |
-|
104 | |
- |
-|
105 | $ git log -g master |
- |
-|
106 | commit 734713bc047d87bf7eac9674765ae793478c50d3 |
- |
-|
107 | Reflog: master@{0} (Scott Chacon <schacon@gmail.com>) |
- |
-|
108 | Reflog message: commit: fixed refs handling, added gc auto, updated |
- |
-|
109 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
110 | Date: Fri Jan 2 18:32:33 2009 -0800 |
- |
-|
111 | |
- |
-|
112 | fixed refs handling, added gc auto, updated tests |
- |
-|
113 | |
- |
-|
114 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
115 | Reflog: master@{1} (Scott Chacon <schacon@gmail.com>) |
- |
-|
116 | Reflog message: merge phedders/rdocs: Merge made by recursive. |
- |
-|
117 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
118 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
119 | |
- |
-|
120 | Merge commit 'phedders/rdocs' |
- |
-|
121 | |
- |
-|
122 | 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. |
- |
-|
123 | |
- |
-|
124 | ### Ancestry References ### |
- |
-|
125 | |
- |
-|
126 | 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. |
- |
-|
127 | Suppose you look at the history of your project: |
- |
-|
128 | |
- |
-|
129 | $ git log --pretty=format:'%h %s' --graph |
- |
-|
130 | * 734713b fixed refs handling, added gc auto, updated tests |
- |
-|
131 | * d921970 Merge commit 'phedders/rdocs' |
- |
-|
132 | |\ |
- |
-|
133 | | * 35cfb2b Some rdoc changes |
- |
-|
134 | * | 1c002dd added some blame and merge stuff |
- |
-|
135 | |/ |
- |
-|
136 | * 1c36188 ignore *.gem |
- |
-|
137 | * 9b29157 add open3_detach to gemspec file list |
- |
-|
138 | |
- |
-|
139 | Then, you can see the previous commit by specifying `HEAD^`, which means "the parent of HEAD": |
- |
-|
140 | |
- |
-|
141 | $ git show HEAD^ |
- |
-|
142 | commit d921970aadf03b3cf0e71becdaab3147ba71cdef |
- |
-|
143 | Merge: 1c002dd... 35cfb2b... |
- |
-|
144 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
145 | Date: Thu Dec 11 15:08:43 2008 -0800 |
- |
-|
146 | |
- |
-|
147 | Merge commit 'phedders/rdocs' |
- |
-|
148 | |
- |
-|
149 | 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: |
- |
-|
150 | |
- |
-|
151 | $ git show d921970^ |
- |
-|
152 | commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b |
- |
-|
153 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
154 | Date: Thu Dec 11 14:58:32 2008 -0800 |
- |
-|
155 | |
- |
-|
156 | added some blame and merge stuff |
- |
-|
157 | |
- |
-|
158 | $ git show d921970^2 |
- |
-|
159 | commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548 |
- |
-|
160 | Author: Paul Hedderly <paul+git@mjr.org> |
- |
-|
161 | Date: Wed Dec 10 22:22:03 2008 +0000 |
- |
-|
162 | |
- |
-|
163 | Some rdoc changes |
- |
-|
164 | |
- |
-|
165 | 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 |
- |
-|
166 | |
- |
-|
167 | $ git show HEAD~3 |
- |
-|
168 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
169 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
170 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
171 | |
- |
-|
172 | ignore *.gem |
- |
-|
173 | |
- |
-|
174 | This can also be written `HEAD^^^`, which again is the first parent of the first parent of the first parent: |
- |
-|
175 | |
- |
-|
176 | $ git show HEAD^^^ |
- |
-|
177 | commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d |
- |
-|
178 | Author: Tom Preston-Werner <tom@mojombo.com> |
- |
-|
179 | Date: Fri Nov 7 13:47:59 2008 -0500 |
- |
-|
180 | |
- |
-|
181 | ignore *.gem |
- |
-|
182 | |
- |
-|
183 | 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. |
- |
-|
184 | |
- |
-|
185 | ### Commit Ranges ### |
- |
-|
186 | |
- |
-|
187 | 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?" |
- |
-|
188 | |
- |
-|
189 | #### Double Dot #### |
- |
-|
190 | |
- |
-|
191 | 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 Figure 6-1. |
- |
-|
192 | |
- |
-|
193 | Insert 18333fig0601.png |
- |
-|
194 | Figure 6-1. Example history for range selection. |
- |
-|
195 | |
- |
-|
196 | 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: |
- |
-|
197 | |
- |
-|
198 | $ git log master..experiment |
- |
-|
199 | D |
- |
-|
200 | C |
- |
-|
201 | |
- |
-|
202 | 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`: |
- |
-|
203 | |
- |
-|
204 | $ git log experiment..master |
- |
-|
205 | F |
- |
-|
206 | E |
- |
-|
207 | |
- |
-|
208 | 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: |
- |
-|
209 | |
- |
-|
210 | $ git log origin/master..HEAD |
- |
-|
211 | |
- |
-|
212 | 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. |
- |
-|
213 | 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. |
- |
-|
214 | |
- |
-|
215 | #### Multiple Points #### |
- |
-|
216 | |
- |
-|
217 | 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: |
- |
-|
218 | |
- |
-|
219 | $ git log refA..refB |
- |
-|
220 | $ git log ^refA refB |
- |
-|
221 | $ git log refB --not refA |
- |
-|
222 | |
- |
-|
223 | 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: |
- |
-|
224 | |
- |
-|
225 | $ git log refA refB ^refC |
- |
-|
226 | $ git log refA refB --not refC |
- |
-|
227 | |
- |
-|
228 | This makes for a very powerful revision query system that should help you figure out what is in your branches. |
- |
-|
229 | |
- |
-|
230 | #### Triple Dot #### |
- |
-|
231 | |
- |
-|
232 | 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 Figure 6-1. |
- |
-|
233 | If you want to see what is in `master` or `experiment` but not any common references, you can run |
- |
-|
234 | |
- |
-|
235 | $ git log master...experiment |
- |
-|
236 | F |
- |
-|
237 | E |
- |
-|
238 | D |
- |
-|
239 | C |
- |
-|
240 | |
- |
-|
241 | 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. |
- |
-|
242 | |
- |
-|
243 | 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: |
- |
-|
244 | |
- |
-|
245 | $ git log --left-right master...experiment |
- |
-|
246 | < F |
- |
-|
247 | < E |
- |
-|
248 | > D |
- |
-|
249 | > C |
- |
-|
250 | |
- |
-|
251 | With these tools, you can much more easily let Git know what commit or commits you want to inspect. |
- |
-|
252 | |
- |
-|
253 | ## Interactive Staging ## |
- |
-|
254 | |
- |
-|
255 | Git comes with a couple of scripts that make some command-line tasks easier. Here, you’ll look at a few interactive commands that can help you easily craft your commits to include only certain combinations and parts of files. These tools are very helpful if you modify a bunch of files and then decide that you want those changes to be in several focused commits rather than one big messy commit. This way, you can make sure your commits are logically separate changesets and can be easily reviewed by the developers working with you. |
- |
-|
256 | If you run `git add` with the `-i` or `--interactive` option, Git goes into an interactive shell mode, displaying something like this: |
- |
-|
257 | |
- |
-|
258 | $ git add -i |
- |
-|
259 | staged unstaged path |
- |
-|
260 | 1: unchanged +0/-1 TODO |
- |
-|
261 | 2: unchanged +1/-1 index.html |
- |
-|
262 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
263 | |
- |
-|
264 | *** Commands *** |
- |
-|
265 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
266 | 5: patch 6: diff 7: quit 8: help |
- |
-|
267 | What now> |
- |
-|
268 | |
- |
-|
269 | 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. |
- |
-|
270 | |
- |
-|
271 | After this comes a Commands section. Here you can do a number of things, including staging files, unstaging files, staging parts of files, adding untracked files, and seeing diffs of what has been staged. |
- |
-|
272 | |
- |
-|
273 | ### Staging and Unstaging Files ### |
- |
-|
274 | |
- |
-|
275 | If you type `2` or `u` at the `What now>` prompt, the script prompts you for which files you want to stage: |
- |
-|
276 | |
- |
-|
277 | What now> 2 |
- |
-|
278 | staged unstaged path |
- |
-|
279 | 1: unchanged +0/-1 TODO |
- |
-|
280 | 2: unchanged +1/-1 index.html |
- |
-|
281 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
282 | Update>> |
- |
-|
283 | |
- |
-|
284 | To stage the TODO and index.html files, you can type the numbers: |
- |
-|
285 | |
- |
-|
286 | Update>> 1,2 |
- |
-|
287 | staged unstaged path |
- |
-|
288 | * 1: unchanged +0/-1 TODO |
- |
-|
289 | * 2: unchanged +1/-1 index.html |
- |
-|
290 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
291 | Update>> |
- |
-|
292 | |
- |
-|
293 | 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: |
- |
-|
294 | |
- |
-|
295 | Update>> |
- |
-|
296 | updated 2 paths |
- |
-|
297 | |
- |
-|
298 | *** Commands *** |
- |
-|
299 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
300 | 5: patch 6: diff 7: quit 8: help |
- |
-|
301 | What now> 1 |
- |
-|
302 | staged unstaged path |
- |
-|
303 | 1: +0/-1 nothing TODO |
- |
-|
304 | 2: +1/-1 nothing index.html |
- |
-|
305 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
306 | |
- |
-|
307 | 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: |
- |
-|
308 | |
- |
-|
309 | *** Commands *** |
- |
-|
310 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
311 | 5: patch 6: diff 7: quit 8: help |
- |
-|
312 | What now> 3 |
- |
-|
313 | staged unstaged path |
- |
-|
314 | 1: +0/-1 nothing TODO |
- |
-|
315 | 2: +1/-1 nothing index.html |
- |
-|
316 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
317 | Revert>> 1 |
- |
-|
318 | staged unstaged path |
- |
-|
319 | * 1: +0/-1 nothing TODO |
- |
-|
320 | 2: +1/-1 nothing index.html |
- |
-|
321 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
322 | Revert>> [enter] |
- |
-|
323 | reverted one path |
- |
-|
324 | |
- |
-|
325 | Looking at your Git status again, you can see that you’ve unstaged the TODO file: |
- |
-|
326 | |
- |
-|
327 | *** Commands *** |
- |
-|
328 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
329 | 5: patch 6: diff 7: quit 8: help |
- |
-|
330 | What now> 1 |
- |
-|
331 | staged unstaged path |
- |
-|
332 | 1: unchanged +0/-1 TODO |
- |
-|
333 | 2: +1/-1 nothing index.html |
- |
-|
334 | 3: unchanged +5/-1 lib/simplegit.rb |
- |
-|
335 | |
- |
-|
336 | 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: |
- |
-|
337 | |
- |
-|
338 | *** Commands *** |
- |
-|
339 | 1: status 2: update 3: revert 4: add untracked |
- |
-|
340 | 5: patch 6: diff 7: quit 8: help |
- |
-|
341 | What now> 6 |
- |
-|
342 | staged unstaged path |
- |
-|
343 | 1: +1/-1 nothing index.html |
- |
-|
344 | Review diff>> 1 |
- |
-|
345 | diff --git a/index.html b/index.html |
- |
-|
346 | index 4d07108..4335f49 100644 |
- |
-|
347 | --- a/index.html |
- |
-|
348 | +++ b/index.html |
- |
-|
349 | @@ -16,7 +16,7 @@ Date Finder |
- |
-|
350 | |
- |
-|
351 | <p id="out">...</p> |
- |
-|
352 | |
- |
-|
353 | -<div id="footer">contact : support@github.com</div> |
- |
-|
354 | +<div id="footer">contact : email.support@github.com</div> |
- |
-|
355 | |
- |
-|
356 | <script type="text/javascript"> |
- |
-|
357 | |
- |
-|
358 | With these basic commands, you can use the interactive add mode to deal with your staging area a little more easily. |
- |
-|
359 | |
- |
-|
360 | ### Staging Patches ### |
- |
-|
361 | |
- |
-|
362 | It’s also possible for Git to stage certain parts of files and not the rest. For example, if you make two changes to your simplegit.rb file and want to stage one of them and not the other, doing so is very easy in Git. From the interactive prompt, type `5` or `p` (for patch). Git will ask you which files you would like to partially stage; then, for each section of the selected files, it will display hunks of the file diff and ask if you would like to stage them, one by one: |
- |
-|
363 | |
- |
-|
364 | diff --git a/lib/simplegit.rb b/lib/simplegit.rb |
- |
-|
365 | index dd5ecc4..57399e0 100644 |
- |
-|
366 | --- a/lib/simplegit.rb |
- |
-|
367 | +++ b/lib/simplegit.rb |
- |
-|
368 | @@ -22,7 +22,7 @@ class SimpleGit |
- |
-|
369 | end |
- |
-|
370 | |
- |
-|
371 | def log(treeish = 'master') |
- |
-|
372 | - command("git log -n 25 #{treeish}") |
- |
-|
373 | + command("git log -n 30 #{treeish}") |
- |
-|
374 | end |
- |
-|
375 | |
- |
-|
376 | def blame(path) |
- |
-|
377 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? |
- |
-|
378 | |
- |
-|
379 | You have a lot of options at this point. Typing `?` shows a list of what you can do: |
- |
-|
380 | |
- |
-|
381 | Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ? |
- |
-|
382 | y - stage this hunk |
- |
-|
383 | n - do not stage this hunk |
- |
-|
384 | a - stage this and all the remaining hunks in the file |
- |
-|
385 | d - do not stage this hunk nor any of the remaining hunks in the file |
- |
-|
386 | g - select a hunk to go to |
- |
-|
387 | / - search for a hunk matching the given regex |
- |
-|
388 | j - leave this hunk undecided, see next undecided hunk |
- |
-|
389 | J - leave this hunk undecided, see next hunk |
- |
-|
390 | k - leave this hunk undecided, see previous undecided hunk |
- |
-|
391 | K - leave this hunk undecided, see previous hunk |
- |
-|
392 | s - split the current hunk into smaller hunks |
- |
-|
393 | e - manually edit the current hunk |
- |
-|
394 | ? - print help |
- |
-|
395 | |
- |
-|
396 | Generally, you’ll type `y` or `n` if you want to stage each hunk, but staging all of them in certain files or skipping a hunk decision until later can be helpful too. If you stage one part of the file and leave another part unstaged, your status output will look like this: |
- |
-|
397 | |
- |
-|
398 | What now> 1 |
- |
-|
399 | staged unstaged path |
- |
-|
400 | 1: unchanged +0/-1 TODO |
- |
-|
401 | 2: +1/-1 nothing index.html |
- |
-|
402 | 3: +1/-1 +4/-0 lib/simplegit.rb |
- |
-|
403 | |
- |
-|
404 | The status of the simplegit.rb file is interesting. It shows you that a couple of lines are staged and a couple are unstaged. You’ve partially staged this file. At this point, you can exit the interactive adding script and run `git commit` to commit the partially staged files. |
- |
-|
405 | |
- |
-|
406 | Finally, you don’t need to be in interactive add mode to do the partial-file staging — you can start the same script by using `git add -p` or `git add --patch` on the command line. |
- |
-|
407 | |
- |
-|
408 | ## Stashing ## |
- |
-|
409 | |
- |
-|
410 | Often, when you’ve been working on part of your project, things are in a messy state and you want to switch branches for a bit to work on something else. The problem is, you don’t want to do a commit of half-done work just so you can get back to this point later. The answer to this issue is the `git stash` command. |
- |
-|
411 | |
- |
-|
412 | Stashing takes the dirty state of your working directory — that is, your modified tracked files and staged changes — and saves it on a stack of unfinished changes that you can reapply at any time. |
- |
-|
413 | |
- |
-|
414 | ### Stashing Your Work ### |
- |
-|
415 | |
- |
-|
416 | To demonstrate, you’ll go into your project and start working on a couple of files and possibly stage one of the changes. If you run `git status`, you can see your dirty state: |
- |
-|
417 | |
- |
-|
418 | $ git status |
- |
-|
419 | # On branch master |
- |
-|
420 | # Changes to be committed: |
- |
-|
421 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
422 | # |
- |
-|
423 | # modified: index.html |
- |
-|
424 | # |
- |
-|
425 | # Changes not staged for commit: |
- |
-|
426 | # (use "git add <file>..." to update what will be committed) |
- |
-|
427 | # |
- |
-|
428 | # modified: lib/simplegit.rb |
- |
-|
429 | # |
- |
-|
430 | |
- |
-|
431 | Now you want to switch branches, but you don’t want to commit what you’ve been working on yet; so you’ll stash the changes. To push a new stash onto your stack, run `git stash`: |
- |
-|
432 | |
- |
-|
433 | $ git stash |
- |
-|
434 | Saved working directory and index state \ |
- |
-|
435 | "WIP on master: 049d078 added the index file" |
- |
-|
436 | HEAD is now at 049d078 added the index file |
- |
-|
437 | (To restore them type "git stash apply") |
- |
-|
438 | |
- |
-|
439 | Your working directory is clean: |
- |
-|
440 | |
- |
-|
441 | $ git status |
- |
-|
442 | # On branch master |
- |
-|
443 | nothing to commit, working directory clean |
- |
-|
444 | |
- |
-|
445 | At this point, you can easily switch branches and do work elsewhere; your changes are stored on your stack. To see which stashes you’ve stored, you can use `git stash list`: |
- |
-|
446 | |
- |
-|
447 | $ git stash list |
- |
-|
448 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
449 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
450 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
451 | |
- |
-|
452 | In this case, two stashes were done previously, so you have access to three different stashed works. You can reapply the one you just stashed by using the command shown in the help output of the original stash command: `git stash apply`. If you want to apply one of the older stashes, you can specify it by naming it, like this: `git stash apply stash@{2}`. If you don’t specify a stash, Git assumes the most recent stash and tries to apply it: |
- |
-|
453 | |
- |
-|
454 | $ git stash apply |
- |
-|
455 | # On branch master |
- |
-|
456 | # Changes not staged for commit: |
- |
-|
457 | # (use "git add <file>..." to update what will be committed) |
- |
-|
458 | # |
- |
-|
459 | # modified: index.html |
- |
-|
460 | # modified: lib/simplegit.rb |
- |
-|
461 | # |
- |
-|
462 | |
- |
-|
463 | You can see that Git re-modifies the files you uncommitted when you saved the stash. In this case, you had a clean working directory when you tried to apply the stash, and you tried to apply it on the same branch you saved it from; but having a clean working directory and applying it on the same branch aren’t necessary to successfully apply a stash. You can save a stash on one branch, switch to another branch later, and try to reapply the changes. You can also have modified and uncommitted files in your working directory when you apply a stash — Git gives you merge conflicts if anything no longer applies cleanly. |
- |
-|
464 | |
- |
-|
465 | The changes to your files were reapplied, but the file you staged before wasn’t restaged. To do that, you must run the `git stash apply` command with a `--index` option to tell the command to try to reapply the staged changes. If you had run that instead, you’d have gotten back to your original position: |
- |
-|
466 | |
- |
-|
467 | $ git stash apply --index |
- |
-|
468 | # On branch master |
- |
-|
469 | # Changes to be committed: |
- |
-|
470 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
471 | # |
- |
-|
472 | # modified: index.html |
- |
-|
473 | # |
- |
-|
474 | # Changes not staged for commit: |
- |
-|
475 | # (use "git add <file>..." to update what will be committed) |
- |
-|
476 | # |
- |
-|
477 | # modified: lib/simplegit.rb |
- |
-|
478 | # |
- |
-|
479 | |
- |
-|
480 | The apply option only tries to apply the stashed work — you continue to have it on your stack. To remove it, you can run `git stash drop` with the name of the stash to remove: |
- |
-|
481 | |
- |
-|
482 | $ git stash list |
- |
-|
483 | stash@{0}: WIP on master: 049d078 added the index file |
- |
-|
484 | stash@{1}: WIP on master: c264051 Revert "added file_size" |
- |
-|
485 | stash@{2}: WIP on master: 21d80a5 added number to log |
- |
-|
486 | $ git stash drop stash@{0} |
- |
-|
487 | Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43) |
- |
-|
488 | |
- |
-|
489 | You can also run `git stash pop` to apply the stash and then immediately drop it from your stack. |
- |
-|
490 | |
- |
-|
491 | ### Un-applying a Stash ### |
- |
-|
492 | |
- |
-|
493 | In some use case scenarios you might want to apply stashed changes, do some work, but then un-apply those changes that originally came from the stash. Git does not provide such a `stash unapply` command, but it is possible to achieve the effect by simply retrieving the patch associated with a stash and applying it in reverse: |
- |
-|
494 | |
- |
-|
495 | $ git stash show -p stash@{0} | git apply -R |
- |
-|
496 | |
- |
-|
497 | Again, if you don’t specify a stash, Git assumes the most recent stash: |
- |
-|
498 | |
- |
-|
499 | $ git stash show -p | git apply -R |
- |
-|
500 | |
- |
-|
501 | You may want to create an alias and effectively add a `stash-unapply` command to your Git. For example: |
- |
-|
502 | |
- |
-|
503 | $ git config --global alias.stash-unapply '!git stash show -p | git apply -R' |
- |
-|
504 | $ git stash apply |
- |
-|
505 | $ #... work work work |
- |
-|
506 | $ git stash-unapply |
- |
-|
507 | |
- |
-|
508 | ### Creating a Branch from a Stash ### |
- |
-|
509 | |
- |
-|
510 | If you stash some work, leave it there for a while, and continue on the branch from which you stashed the work, you may have a problem reapplying the work. If the apply tries to modify a file that you’ve since modified, you’ll get a merge conflict and will have to try to resolve it. If you want an easier way to test the stashed changes again, you can run `git stash branch`, which creates a new branch for you, checks out the commit you were on when you stashed your work, reapplies your work there, and then drops the stash if it applies successfully: |
- |
-|
511 | |
- |
-|
512 | $ git stash branch testchanges |
- |
-|
513 | Switched to a new branch "testchanges" |
- |
-|
514 | # On branch testchanges |
- |
-|
515 | # Changes to be committed: |
- |
-|
516 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
517 | # |
- |
-|
518 | # modified: index.html |
- |
-|
519 | # |
- |
-|
520 | # Changes not staged for commit: |
- |
-|
521 | # (use "git add <file>..." to update what will be committed) |
- |
-|
522 | # |
- |
-|
523 | # modified: lib/simplegit.rb |
- |
-|
524 | # |
- |
-|
525 | Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359) |
- |
-|
526 | |
- |
-|
527 | This is a nice shortcut to recover stashed work easily and work on it in a new branch. |
- |
-|
528 | |
- |
-|
529 | ## Rewriting History ## |
- |
-|
530 | |
- |
-|
531 | Many times, when working with Git, you may want to revise your commit history for some reason. One of the great things about Git is that it allows you to make decisions at the last possible moment. You can decide what files go into which commits right before you commit with the staging area, you can decide that you didn’t mean to be working on something yet with the stash command, and you can rewrite commits that already happened so they look like they happened in a different way. This can involve changing the order of the commits, changing messages or modifying files in a commit, squashing together or splitting apart commits, or removing commits entirely — all before you share your work with others. |
- |
-|
532 | |
- |
-|
533 | In this section, you’ll cover how to accomplish these very useful tasks so that you can make your commit history look the way you want before you share it with others. |
- |
-|
534 | |
- |
-|
535 | ### Changing the Last Commit ### |
- |
-|
536 | |
- |
-|
537 | Changing your last commit is probably the most common rewriting of history that you’ll do. You’ll often want to do two basic things to your last commit: change the commit message, or change the snapshot you just recorded by adding, changing and removing files. |
- |
-|
538 | |
- |
-|
539 | If you only want to modify your last commit message, it’s very simple: |
- |
-|
540 | |
- |
-|
541 | $ git commit --amend |
- |
-|
542 | |
- |
-|
543 | That drops you into your text editor, which has your last commit message in it, ready for you to modify the message. When you save and close the editor, the editor writes a new commit containing that message and makes it your new last commit. |
- |
-|
544 | |
- |
-|
545 | If you’ve committed and then you want to change the snapshot you committed by adding or changing files, possibly because you forgot to add a newly created file when you originally committed, the process works basically the same way. You stage the changes you want by editing a file and running `git add` on it or `git rm` to a tracked file, and the subsequent `git commit --amend` takes your current staging area and makes it the snapshot for the new commit. |
- |
-|
546 | |
- |
-|
547 | You need to be careful with this technique because amending changes the SHA-1 of the commit. It’s like a very small rebase — don’t amend your last commit if you’ve already pushed it. |
- |
-|
548 | |
- |
-|
549 | ### Changing Multiple Commit Messages ### |
- |
-|
550 | |
- |
-|
551 | To modify a commit that is farther back in your history, you must move to more complex tools. Git doesn’t have a modify-history tool, but you can use the rebase tool to rebase a series of commits onto the HEAD they were originally based on instead of moving them to another one. With the interactive rebase tool, you can then stop after each commit you want to modify and change the message, add files, or do whatever you wish. You can run rebase interactively by adding the `-i` option to `git rebase`. You must indicate how far back you want to rewrite commits by telling the command which commit to rebase onto. |
- |
-|
552 | |
- |
-|
553 | For example, if you want to change the last three commit messages, or any of the commit messages in that group, you supply as an argument to `git rebase -i` the parent of the last commit you want to edit, which is `HEAD~2^` or `HEAD~3`. It may be easier to remember the `~3` because you’re trying to edit the last three commits; but keep in mind that you’re actually designating four commits ago, the parent of the last commit you want to edit: |
- |
-|
554 | |
- |
-|
555 | $ git rebase -i HEAD~3 |
- |
-|
556 | |
- |
-|
557 | Remember again that this is a rebasing command — every commit included in the range `HEAD~3..HEAD` will be rewritten, whether you change the message or not. Don’t include any commit you’ve already pushed to a central server — doing so will confuse other developers by providing an alternate version of the same change. |
- |
-|
558 | |
- |
-|
559 | Running this command gives you a list of commits in your text editor that looks something like this: |
- |
-|
560 | |
- |
-|
561 | pick f7f3f6d changed my name a bit |
- |
-|
562 | pick 310154e updated README formatting and added blame |
- |
-|
563 | pick a5f4a0d added cat-file |
- |
-|
564 | |
- |
-|
565 | # Rebase 710f0f8..a5f4a0d onto 710f0f8 |
- |
-|
566 | # |
- |
-|
567 | # Commands: |
- |
-|
568 | # p, pick = use commit |
- |
-|
569 | # r, reword = use commit, but edit the commit message |
- |
-|
570 | # e, edit = use commit, but stop for amending |
- |
-|
571 | # s, squash = use commit, but meld into previous commit |
- |
-|
572 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
573 | # x, exec = run command (the rest of the line) using shell |
- |
-|
574 | # |
- |
-|
575 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
576 | # |
- |
-|
577 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
578 | # |
- |
-|
579 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
580 | # |
- |
-|
581 | # Note that empty commits are commented out |
- |
-|
582 | |
- |
-|
583 | It’s important to note that these commits are listed in the opposite order than you normally see them using the `log` command. If you run a `log`, you see something like this: |
- |
-|
584 | |
- |
-|
585 | $ git log --pretty=format:"%h %s" HEAD~3..HEAD |
- |
-|
586 | a5f4a0d added cat-file |
- |
-|
587 | 310154e updated README formatting and added blame |
- |
-|
588 | f7f3f6d changed my name a bit |
- |
-|
589 | |
- |
-|
590 | Notice the reverse order. The interactive rebase gives you a script that it’s going to run. It will start at the commit you specify on the command line (`HEAD~3`) and replay the changes introduced in each of these commits from top to bottom. It lists the oldest at the top, rather than the newest, because that’s the first one it will replay. |
- |
-|
591 | |
- |
-|
592 | You need to edit the script so that it stops at the commit you want to edit. To do so, change the word pick to the word edit for each of the commits you want the script to stop after. For example, to modify only the third commit message, you change the file to look like this: |
- |
-|
593 | |
- |
-|
594 | edit f7f3f6d changed my name a bit |
- |
-|
595 | pick 310154e updated README formatting and added blame |
- |
-|
596 | pick a5f4a0d added cat-file |
- |
-|
597 | |
- |
-|
598 | When you save and exit the editor, Git rewinds you back to the last commit in that list and drops you on the command line with the following message: |
- |
-|
599 | |
- |
-|
600 | <!-- This is actually weird, as the SHA-1 of 7482e0d is not present in the list, |
- |
-|
601 | nor is the commit message. Please review |
- |
-|
602 | --> |
- |
-|
603 | |
- |
-|
604 | $ git rebase -i HEAD~3 |
- |
-|
605 | Stopped at 7482e0d... updated the gemspec to hopefully work better |
- |
-|
606 | You can amend the commit now, with |
- |
-|
607 | |
- |
-|
608 | git commit --amend |
- |
-|
609 | |
- |
-|
610 | Once you’re satisfied with your changes, run |
- |
-|
611 | |
- |
-|
612 | git rebase --continue |
- |
-|
613 | |
- |
-|
614 | These instructions tell you exactly what to do. Type |
- |
-|
615 | |
- |
-|
616 | $ git commit --amend |
- |
-|
617 | |
- |
-|
618 | Change the commit message, and exit the editor. Then, run |
- |
-|
619 | |
- |
-|
620 | $ git rebase --continue |
- |
-|
621 | |
- |
-|
622 | This command will apply the other two commits automatically, and then you’re done. If you change pick to edit on more lines, you can repeat these steps for each commit you change to edit. Each time, Git will stop, let you amend the commit, and continue when you’re finished. |
- |
-|
623 | |
- |
-|
624 | ### Reordering Commits ### |
- |
-|
625 | |
- |
-|
626 | You can also use interactive rebases to reorder or remove commits entirely. If you want to remove the "added cat-file" commit and change the order in which the other two commits are introduced, you can change the rebase script from this |
- |
-|
627 | |
- |
-|
628 | pick f7f3f6d changed my name a bit |
- |
-|
629 | pick 310154e updated README formatting and added blame |
- |
-|
630 | pick a5f4a0d added cat-file |
- |
-|
631 | |
- |
-|
632 | to this: |
- |
-|
633 | |
- |
-|
634 | pick 310154e updated README formatting and added blame |
- |
-|
635 | pick f7f3f6d changed my name a bit |
- |
-|
636 | |
- |
-|
637 | When you save and exit the editor, Git rewinds your branch to the parent of these commits, applies `310154e` and then `f7f3f6d`, and then stops. You effectively change the order of those commits and remove the "added cat-file" commit completely. |
- |
-|
638 | |
- |
-|
639 | ### Squashing Commits ### |
- |
-|
640 | |
- |
-|
641 | It’s also possible to take a series of commits and squash them down into a single commit with the interactive rebasing tool. The script puts helpful instructions in the rebase message: |
- |
-|
642 | |
- |
-|
643 | # |
- |
-|
644 | # Commands: |
- |
-|
645 | # p, pick = use commit |
- |
-|
646 | # r, reword = use commit, but edit the commit message |
- |
-|
647 | # e, edit = use commit, but stop for amending |
- |
-|
648 | # s, squash = use commit, but meld into previous commit |
- |
-|
649 | # f, fixup = like "squash", but discard this commit's log message |
- |
-|
650 | # x, exec = run command (the rest of the line) using shell |
- |
-|
651 | # |
- |
-|
652 | # These lines can be re-ordered; they are executed from top to bottom. |
- |
-|
653 | # |
- |
-|
654 | # If you remove a line here THAT COMMIT WILL BE LOST. |
- |
-|
655 | # |
- |
-|
656 | # However, if you remove everything, the rebase will be aborted. |
- |
-|
657 | # |
- |
-|
658 | # Note that empty commits are commented out |
- |
-|
659 | |
- |
-|
660 | If, instead of "pick" or "edit", you specify "squash", Git applies both that change and the change directly before it and makes you merge the commit messages together. So, if you want to make a single commit from these three commits, you make the script look like this: |
- |
-|
661 | |
- |
-|
662 | pick f7f3f6d changed my name a bit |
- |
-|
663 | squash 310154e updated README formatting and added blame |
- |
-|
664 | squash a5f4a0d added cat-file |
- |
-|
665 | |
- |
-|
666 | When you save and exit the editor, Git applies all three changes and then puts you back into the editor to merge the three commit messages: |
- |
-|
667 | |
- |
-|
668 | # This is a combination of 3 commits. |
- |
-|
669 | # The first commit's message is: |
- |
-|
670 | changed my name a bit |
- |
-|
671 | |
- |
-|
672 | # This is the 2nd commit message: |
- |
-|
673 | |
- |
-|
674 | updated README formatting and added blame |
- |
-|
675 | |
- |
-|
676 | # This is the 3rd commit message: |
- |
-|
677 | |
- |
-|
678 | added cat-file |
- |
-|
679 | |
- |
-|
680 | When you save that, you have a single commit that introduces the changes of all three previous commits. |
- |
-|
681 | |
- |
-|
682 | ### Splitting a Commit ### |
- |
-|
683 | |
- |
-|
684 | Splitting a commit undoes a commit and then partially stages and commits as many times as commits you want to end up with. For example, suppose you want to split the middle commit of your three commits. Instead of "updated README formatting and added blame", you want to split it into two commits: "updated README formatting" for the first, and "added blame" for the second. You can do that in the `rebase -i` script by changing the instruction on the commit you want to split to "edit": |
- |
-|
685 | |
- |
-|
686 | pick f7f3f6d changed my name a bit |
- |
-|
687 | edit 310154e updated README formatting and added blame |
- |
-|
688 | pick a5f4a0d added cat-file |
- |
-|
689 | |
- |
-|
690 | When you save and exit the editor, Git rewinds to the parent of the first commit in your list, applies the first commit (`f7f3f6d`), applies the second (`310154e`), and drops you to the console. There, you can do a mixed reset of that commit with `git reset HEAD^`, which effectively undoes that commit and leaves the modified files unstaged. Now you can take the changes that have been reset, and create multiple commits out of them. Simply stage and commit files until you have several commits, and run `git rebase --continue` when you’re done: |
- |
-|
691 | |
- |
-|
692 | $ git reset HEAD^ |
- |
-|
693 | $ git add README |
- |
-|
694 | $ git commit -m 'updated README formatting' |
- |
-|
695 | $ git add lib/simplegit.rb |
- |
-|
696 | $ git commit -m 'added blame' |
- |
-|
697 | $ git rebase --continue |
- |
-|
698 | |
- |
-|
699 | Git applies the last commit (`a5f4a0d`) in the script, and your history looks like this: |
- |
-|
700 | |
- |
-|
701 | $ git log -4 --pretty=format:"%h %s" |
- |
-|
702 | 1c002dd added cat-file |
- |
-|
703 | 9b29157 added blame |
- |
-|
704 | 35cfb2b updated README formatting |
- |
-|
705 | f3cc40e changed my name a bit |
- |
-|
706 | |
- |
-|
707 | Once again, this changes the SHAs of all the commits in your list, so make sure no commit shows up in that list that you’ve already pushed to a shared repository. |
- |
-|
708 | |
- |
-|
709 | ### The Nuclear Option: filter-branch ### |
- |
-|
710 | |
- |
-|
711 | There is another history-rewriting option that you can use if you need to rewrite a larger number of commits in some scriptable way — for instance, changing your e-mail address globally or removing a file from every commit. The command is `filter-branch`, and it can rewrite huge swaths of your history, so you probably shouldn’t use it unless your project isn’t yet public and other people haven’t based work off the commits you’re about to rewrite. However, it can be very useful. You’ll learn a few of the common uses so you can get an idea of some of the things it’s capable of. |
- |
-|
712 | |
- |
-|
713 | #### Removing a File from Every Commit #### |
- |
-|
714 | |
- |
-|
715 | This occurs fairly commonly. Someone accidentally commits a huge binary file with a thoughtless `git add .`, and you want to remove it everywhere. Perhaps you accidentally committed a file that contained a password, and you want to make your project open source. `filter-branch` is the tool you probably want to use to scrub your entire history. To remove a file named passwords.txt from your entire history, you can use the `--tree-filter` option to `filter-branch`: |
- |
-|
716 | |
- |
-|
717 | $ git filter-branch --tree-filter 'rm -f passwords.txt' HEAD |
- |
-|
718 | Rewrite 6b9b3cf04e7c5686a9cb838c3f36a8cb6a0fc2bd (21/21) |
- |
-|
719 | Ref 'refs/heads/master' was rewritten |
- |
-|
720 | |
- |
-|
721 | The `--tree-filter` option runs the specified command after each checkout of the project and then recommits the results. In this case, you remove a file called passwords.txt from every snapshot, whether it exists or not. If you want to remove all accidentally committed editor backup files, you can run something like `git filter-branch --tree-filter "rm -f *~" HEAD`. |
- |
-|
722 | |
- |
-|
723 | You’ll be able to watch Git rewriting trees and commits and then move the branch pointer at the end. It’s generally a good idea to do this in a testing branch and then hard-reset your master branch after you’ve determined the outcome is what you really want. To run `filter-branch` on all your branches, you can pass `--all` to the command. |
- |
-|
724 | |
- |
-|
725 | #### Making a Subdirectory the New Root #### |
- |
-|
726 | |
- |
-|
727 | Suppose you’ve done an import from another source control system and have subdirectories that make no sense (trunk, tags, and so on). If you want to make the `trunk` subdirectory be the new project root for every commit, `filter-branch` can help you do that, too: |
- |
-|
728 | |
- |
-|
729 | $ git filter-branch --subdirectory-filter trunk HEAD |
- |
-|
730 | Rewrite 856f0bf61e41a27326cdae8f09fe708d679f596f (12/12) |
- |
-|
731 | Ref 'refs/heads/master' was rewritten |
- |
-|
732 | |
- |
-|
733 | Now your new project root is what was in the `trunk` subdirectory each time. Git will also automatically remove commits that did not affect the subdirectory. |
- |
-|
734 | |
- |
-|
735 | #### Changing E-Mail Addresses Globally #### |
- |
-|
736 | |
- |
-|
737 | Another common case is that you forgot to run `git config` to set your name and e-mail address before you started working, or perhaps you want to open-source a project at work and change all your work e-mail addresses to your personal address. In any case, you can change e-mail addresses in multiple commits in a batch with `filter-branch` as well. You need to be careful to change only the e-mail addresses that are yours, so you use `--commit-filter`: |
- |
-|
738 | |
- |
-|
739 | $ git filter-branch --commit-filter ' |
- |
-|
740 | if [ "$GIT_AUTHOR_EMAIL" = "schacon@localhost" ]; |
- |
-|
741 | then |
- |
-|
742 | GIT_AUTHOR_NAME="Scott Chacon"; |
- |
-|
743 | GIT_AUTHOR_EMAIL="schacon@example.com"; |
- |
-|
744 | git commit-tree "$@"; |
- |
-|
745 | else |
- |
-|
746 | git commit-tree "$@"; |
- |
-|
747 | fi' HEAD |
- |
-|
748 | |
- |
-|
749 | This goes through and rewrites every commit to have your new address. Because commits contain the SHA-1 values of their parents, this command changes every commit SHA in your history, not just those that have the matching e-mail address. |
- |
-|
750 | |
- |
-|
751 | ### The Very Fast Nuclear Option: Big Friendly Giant Repo Cleaner (BFG) ### |
- |
-|
752 | |
- |
-|
753 | [Roberto Tyley](https://github.com/rtyley) has written a similar tool to `filter-branch` called the BFG. BFG cannot do as much as `filter-branch`, but it is _very_ fast and on a large repository this can make a big difference. If the change you want to make is in the scope of BFG capability, and you have performance issues, then you should consider using it. |
- |
-|
754 | |
- |
-|
755 | See the [BFG](http://rtyley.github.io/bfg-repo-cleaner/) website for details. |
- |
-|
756 | |
- |
-|
757 | ## Debugging with Git ## |
- |
-|
758 | |
- |
-|
759 | Git also provides a couple of tools to help you debug issues in your projects. Because Git is designed to work with nearly any type of project, these tools are pretty generic, but they can often help you hunt for a bug or culprit when things go wrong. |
- |
-|
760 | |
- |
-|
761 | ### File Annotation ### |
- |
-|
762 | |
- |
-|
763 | If you track down a bug in your code and want to know when it was introduced and why, file annotation is often your best tool. It shows you what commit was the last to modify each line of any file. So, if you see that a method in your code is buggy, you can annotate the file with `git blame` to see when each line of the method was last edited and by whom. This example uses the `-L` option to limit the output to lines 12 through 22: |
- |
-|
764 | |
- |
-|
765 | $ git blame -L 12,22 simplegit.rb |
- |
-|
766 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 12) def show(tree = 'master') |
- |
-|
767 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 13) command("git show #{tree}") |
- |
-|
768 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 14) end |
- |
-|
769 | ^4832fe2 (Scott Chacon 2008-03-15 10:31:28 -0700 15) |
- |
-|
770 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 16) def log(tree = 'master') |
- |
-|
771 | 79eaf55d (Scott Chacon 2008-04-06 10:15:08 -0700 17) command("git log #{tree}") |
- |
-|
772 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 18) end |
- |
-|
773 | 9f6560e4 (Scott Chacon 2008-03-17 21:52:20 -0700 19) |
- |
-|
774 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 20) def blame(path) |
- |
-|
775 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 21) command("git blame #{path}") |
- |
-|
776 | 42cf2861 (Magnus Chacon 2008-04-13 10:45:01 -0700 22) end |
- |
-|
777 | |
- |
-|
778 | Notice that the first field is the partial SHA-1 of the commit that last modified that line. The next two fields are values extracted from that commit—the author name and the authored date of that commit — so you can easily see who modified that line and when. After that come the line number and the content of the file. Also note the `^4832fe2` commit lines, which designate that those lines were in this file’s original commit. That commit is when this file was first added to this project, and those lines have been unchanged since. This is a tad confusing, because now you’ve seen at least three different ways that Git uses the `^` to modify a commit SHA, but that is what it means here. |
- |
-|
779 | |
- |
-|
780 | Another cool thing about Git is that it doesn’t track file renames explicitly. It records the snapshots and then tries to figure out what was renamed implicitly, after the fact. One of the interesting features of this is that you can ask it to figure out all sorts of code movement as well. If you pass `-C` to `git blame`, Git analyzes the file you’re annotating and tries to figure out where snippets of code within it originally came from if they were copied from elsewhere. Recently, I was refactoring a file named `GITServerHandler.m` into multiple files, one of which was `GITPackUpload.m`. By blaming `GITPackUpload.m` with the `-C` option, I could see where sections of the code originally came from: |
- |
-|
781 | |
- |
-|
782 | $ git blame -C -L 141,153 GITPackUpload.m |
- |
-|
783 | f344f58d GITServerHandler.m (Scott 2009-01-04 141) |
- |
-|
784 | f344f58d GITServerHandler.m (Scott 2009-01-04 142) - (void) gatherObjectShasFromC |
- |
-|
785 | f344f58d GITServerHandler.m (Scott 2009-01-04 143) { |
- |
-|
786 | 70befddd GITServerHandler.m (Scott 2009-03-22 144) //NSLog(@"GATHER COMMI |
- |
-|
787 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 145) |
- |
-|
788 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 146) NSString *parentSha; |
- |
-|
789 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 147) GITCommit *commit = [g |
- |
-|
790 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 148) |
- |
-|
791 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 149) //NSLog(@"GATHER COMMI |
- |
-|
792 | ad11ac80 GITPackUpload.m (Scott 2009-03-24 150) |
- |
-|
793 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 151) if(commit) { |
- |
-|
794 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 152) [refDict setOb |
- |
-|
795 | 56ef2caf GITServerHandler.m (Scott 2009-01-05 153) |
- |
-|
796 | |
- |
-|
797 | This is really useful. Normally, you get as the original commit the commit where you copied the code over, because that is the first time you touched those lines in this file. Git tells you the original commit where you wrote those lines, even if it was in another file. |
- |
-|
798 | |
- |
-|
799 | ### Binary Search ### |
- |
-|
800 | |
- |
-|
801 | Annotating a file helps if you know where the issue is to begin with. If you don’t know what is breaking, and there have been dozens or hundreds of commits since the last state where you know the code worked, you’ll likely turn to `git bisect` for help. The `bisect` command does a binary search through your commit history to help you identify as quickly as possible which commit introduced an issue. |
- |
-|
802 | |
- |
-|
803 | Let’s say you just pushed out a release of your code to a production environment, you’re getting bug reports about something that wasn’t happening in your development environment, and you can’t imagine why the code is doing that. You go back to your code, and it turns out you can reproduce the issue, but you can’t figure out what is going wrong. You can bisect the code to find out. First you run `git bisect start` to get things going, and then you use `git bisect bad` to tell the system that the current commit you’re on is broken. Then, you must tell bisect when the last known good state was, using `git bisect good [good_commit]`: |
- |
-|
804 | |
- |
-|
805 | $ git bisect start |
- |
-|
806 | $ git bisect bad |
- |
-|
807 | $ git bisect good v1.0 |
- |
-|
808 | Bisecting: 6 revisions left to test after this |
- |
-|
809 | [ecb6e1bc347ccecc5f9350d878ce677feb13d3b2] error handling on repo |
- |
-|
810 | |
- |
-|
811 | Git figured out that about 12 commits came between the commit you marked as the last good commit (v1.0) and the current bad version, and it checked out the middle one for you. At this point, you can run your test to see if the issue exists as of this commit. If it does, then it was introduced sometime before this middle commit; if it doesn’t, then the problem was introduced sometime after the middle commit. It turns out there is no issue here, and you tell Git that by typing `git bisect good` and continue your journey: |
- |
-|
812 | |
- |
-|
813 | $ git bisect good |
- |
-|
814 | Bisecting: 3 revisions left to test after this |
- |
-|
815 | [b047b02ea83310a70fd603dc8cd7a6cd13d15c04] secure this thing |
- |
-|
816 | |
- |
-|
817 | Now you’re on another commit, halfway between the one you just tested and your bad commit. You run your test again and find that this commit is broken, so you tell Git that with `git bisect bad`: |
- |
-|
818 | |
- |
-|
819 | $ git bisect bad |
- |
-|
820 | Bisecting: 1 revisions left to test after this |
- |
-|
821 | [f71ce38690acf49c1f3c9bea38e09d82a5ce6014] drop exceptions table |
- |
-|
822 | |
- |
-|
823 | This commit is fine, and now Git has all the information it needs to determine where the issue was introduced. It tells you the SHA-1 of the first bad commit and show some of the commit information and which files were modified in that commit so you can figure out what happened that may have introduced this bug: |
- |
-|
824 | |
- |
-|
825 | $ git bisect good |
- |
-|
826 | b047b02ea83310a70fd603dc8cd7a6cd13d15c04 is first bad commit |
- |
-|
827 | commit b047b02ea83310a70fd603dc8cd7a6cd13d15c04 |
- |
-|
828 | Author: PJ Hyett <pjhyett@example.com> |
- |
-|
829 | Date: Tue Jan 27 14:48:32 2009 -0800 |
- |
-|
830 | |
- |
-|
831 | secure this thing |
- |
-|
832 | |
- |
-|
833 | :040000 040000 40ee3e7821b895e52c1695092db9bdc4c61d1730 |
- |
-|
834 | f24d3c6ebcfc639b1a3814550e62d60b8e68a8e4 M config |
- |
-|
835 | |
- |
-|
836 | When you’re finished, you should run `git bisect reset` to reset your HEAD to where you were before you started, or you’ll end up in a weird state: |
- |
-|
837 | |
- |
-|
838 | $ git bisect reset |
- |
-|
839 | |
- |
-|
840 | This is a powerful tool that can help you check hundreds of commits for an introduced bug in minutes. In fact, if you have a script that will exit 0 if the project is good or non-0 if the project is bad, you can fully automate `git bisect`. First, you again tell it the scope of the bisect by providing the known bad and good commits. You can do this by listing them with the `bisect start` command if you want, listing the known bad commit first and the known good commit second: |
- |
-|
841 | |
- |
-|
842 | $ git bisect start HEAD v1.0 |
- |
-|
843 | $ git bisect run test-error.sh |
- |
-|
844 | |
- |
-|
845 | Doing so automatically runs `test-error.sh` on each checked-out commit until Git finds the first broken commit. You can also run something like `make` or `make tests` or whatever you have that runs automated tests for you. |
- |
-|
846 | |
- |
-|
847 | ## Submodules ## |
- |
-|
848 | |
- |
-|
849 | It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other. |
- |
-|
850 | |
- |
-|
851 | Here’s an example. Suppose you’re developing a web site and creating Atom feeds. Instead of writing your own Atom-generating code, you decide to use a library. You’re likely to have to either include this code from a shared library like a CPAN install or Ruby gem, or copy the source code into your own project tree. The issue with including the library is that it’s difficult to customize the library in any way and often more difficult to deploy it, because you need to make sure every client has that library available. The issue with vendoring the code into your own project is that any custom changes you make are difficult to merge when upstream changes become available. |
- |
-|
852 | |
- |
-|
853 | Git addresses this issue using submodules. Submodules allow you to keep a Git repository as a subdirectory of another Git repository. This lets you clone another repository into your project and keep your commits separate. |
- |
-|
854 | |
- |
-|
855 | ### Starting with Submodules ### |
- |
-|
856 | |
- |
-|
857 | Suppose you want to add the Rack library (a Ruby web server gateway interface) to your project, possibly maintain your own changes to it, but continue to merge in upstream changes. The first thing you should do is clone the external repository into your subdirectory. You add external projects as submodules with the `git submodule add` command: |
- |
-|
858 | |
- |
-|
859 | $ git submodule add git://github.com/chneukirchen/rack.git rack |
- |
-|
860 | Initialized empty Git repository in /opt/subtest/rack/.git/ |
- |
-|
861 | remote: Counting objects: 3181, done. |
- |
-|
862 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
863 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
864 | Receiving objects: 100% (3181/3181), 675.42 KiB | 422 KiB/s, done. |
- |
-|
865 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
866 | |
- |
-|
867 | Now you have the Rack project under a subdirectory named `rack` within your project. You can go into that subdirectory, make changes, add your own writable remote repository to push your changes into, fetch and merge from the original repository, and more. If you run `git status` right after you add the submodule, you see two things: |
- |
-|
868 | |
- |
-|
869 | $ git status |
- |
-|
870 | # On branch master |
- |
-|
871 | # Changes to be committed: |
- |
-|
872 | # (use "git reset HEAD <file>..." to unstage) |
- |
-|
873 | # |
- |
-|
874 | # new file: .gitmodules |
- |
-|
875 | # new file: rack |
- |
-|
876 | # |
- |
-|
877 | |
- |
-|
878 | First you notice the `.gitmodules` file. This is a configuration file that stores the mapping between the project’s URL and the local subdirectory you’ve pulled it into: |
- |
-|
879 | |
- |
-|
880 | $ cat .gitmodules |
- |
-|
881 | [submodule "rack"] |
- |
-|
882 | path = rack |
- |
-|
883 | url = git://github.com/chneukirchen/rack.git |
- |
-|
884 | |
- |
-|
885 | If you have multiple submodules, you’ll have multiple entries in this file. It’s important to note that this file is version-controlled with your other files, like your `.gitignore` file. It’s pushed and pulled with the rest of your project. This is how other people who clone this project know where to get the submodule projects from. |
- |
-|
886 | |
- |
-|
887 | The other listing in the `git status` output is the rack entry. If you run `git diff` on that, you see something interesting: |
- |
-|
888 | |
- |
-|
889 | $ git diff --cached rack |
- |
-|
890 | diff --git a/rack b/rack |
- |
-|
891 | new file mode 160000 |
- |
-|
892 | index 0000000..08d709f |
- |
-|
893 | --- /dev/null |
- |
-|
894 | +++ b/rack |
- |
-|
895 | @@ -0,0 +1 @@ |
- |
-|
896 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
897 | |
- |
-|
898 | Although `rack` is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git records it as a particular commit from that repository. When you make changes and commit in that subdirectory, the superproject notices that the HEAD there has changed and records the exact commit you’re currently working off of; that way, when others clone this project, they can re-create the environment exactly. |
- |
-|
899 | |
- |
-|
900 | This is an important point with submodules: you record them as the exact commit they’re at. You can’t record a submodule at `master` or some other symbolic reference. |
- |
-|
901 | |
- |
-|
902 | When you commit, you see something like this: |
- |
-|
903 | |
- |
-|
904 | $ git commit -m 'first commit with submodule rack' |
- |
-|
905 | [master 0550271] first commit with submodule rack |
- |
-|
906 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
907 | create mode 100644 .gitmodules |
- |
-|
908 | create mode 160000 rack |
- |
-|
909 | |
- |
-|
910 | Notice the 160000 mode for the rack entry. That is a special mode in Git that basically means you’re recording a commit as a directory entry rather than a subdirectory or a file. |
- |
-|
911 | |
- |
-|
912 | You can treat the `rack` directory as a separate project and then update your superproject from time to time with a pointer to the latest commit in that subproject. All the Git commands work independently in the two directories: |
- |
-|
913 | |
- |
-|
914 | $ git log -1 |
- |
-|
915 | commit 0550271328a0038865aad6331e620cd7238601bb |
- |
-|
916 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
917 | Date: Thu Apr 9 09:03:56 2009 -0700 |
- |
-|
918 | |
- |
-|
919 | first commit with submodule rack |
- |
-|
920 | $ cd rack/ |
- |
-|
921 | $ git log -1 |
- |
-|
922 | commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
923 | Author: Christian Neukirchen <chneukirchen@gmail.com> |
- |
-|
924 | Date: Wed Mar 25 14:49:04 2009 +0100 |
- |
-|
925 | |
- |
-|
926 | Document version change |
- |
-|
927 | |
- |
-|
928 | ### Cloning a Project with Submodules ### |
- |
-|
929 | |
- |
-|
930 | Here you’ll clone a project with a submodule in it. When you receive such a project, you get the directories that contain submodules, but none of the files yet: |
- |
-|
931 | |
- |
-|
932 | $ git clone git://github.com/schacon/myproject.git |
- |
-|
933 | Initialized empty Git repository in /opt/myproject/.git/ |
- |
-|
934 | remote: Counting objects: 6, done. |
- |
-|
935 | remote: Compressing objects: 100% (4/4), done. |
- |
-|
936 | remote: Total 6 (delta 0), reused 0 (delta 0) |
- |
-|
937 | Receiving objects: 100% (6/6), done. |
- |
-|
938 | $ cd myproject |
- |
-|
939 | $ ls -l |
- |
-|
940 | total 8 |
- |
-|
941 | -rw-r--r-- 1 schacon admin 3 Apr 9 09:11 README |
- |
-|
942 | drwxr-xr-x 2 schacon admin 68 Apr 9 09:11 rack |
- |
-|
943 | $ ls rack/ |
- |
-|
944 | $ |
- |
-|
945 | |
- |
-|
946 | The `rack` directory is there, but empty. You must run two commands: `git submodule init` to initialize your local configuration file, and `git submodule update` to fetch all the data from that project and check out the appropriate commit listed in your superproject: |
- |
-|
947 | |
- |
-|
948 | $ git submodule init |
- |
-|
949 | Submodule 'rack' (git://github.com/chneukirchen/rack.git) registered for path 'rack' |
- |
-|
950 | $ git submodule update |
- |
-|
951 | Initialized empty Git repository in /opt/myproject/rack/.git/ |
- |
-|
952 | remote: Counting objects: 3181, done. |
- |
-|
953 | remote: Compressing objects: 100% (1534/1534), done. |
- |
-|
954 | remote: Total 3181 (delta 1951), reused 2623 (delta 1603) |
- |
-|
955 | Receiving objects: 100% (3181/3181), 675.42 KiB | 173 KiB/s, done. |
- |
-|
956 | Resolving deltas: 100% (1951/1951), done. |
- |
-|
957 | Submodule path 'rack': checked out '08d709f78b8c5b0fbeb7821e37fa53e69afcf433' |
- |
-|
958 | |
- |
-|
959 | Now your `rack` subdirectory is at the exact state it was in when you committed earlier. If another developer makes changes to the rack code and commits, and you pull that reference down and merge it in, you get something a bit odd: |
- |
-|
960 | |
- |
-|
961 | $ git merge origin/master |
- |
-|
962 | Updating 0550271..85a3eee |
- |
-|
963 | Fast forward |
- |
-|
964 | rack | 2 +- |
- |
-|
965 | 1 files changed, 1 insertions(+), 1 deletions(-) |
- |
-|
966 | [master*]$ git status |
- |
-|
967 | # On branch master |
- |
-|
968 | # Changes not staged for commit: |
- |
-|
969 | # (use "git add <file>..." to update what will be committed) |
- |
-|
970 | # (use "git checkout -- <file>..." to discard changes in working directory) |
- |
-|
971 | # |
- |
-|
972 | # modified: rack |
- |
-|
973 | # |
- |
-|
974 | |
- |
-|
975 | You merged in what is basically a change to the pointer for your submodule; but it doesn’t update the code in the submodule directory, so it looks like you have a dirty state in your working directory: |
- |
-|
976 | |
- |
-|
977 | $ git diff |
- |
-|
978 | diff --git a/rack b/rack |
- |
-|
979 | index 6c5e70b..08d709f 160000 |
- |
-|
980 | --- a/rack |
- |
-|
981 | +++ b/rack |
- |
-|
982 | @@ -1 +1 @@ |
- |
-|
983 | -Subproject commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
984 | +Subproject commit 08d709f78b8c5b0fbeb7821e37fa53e69afcf433 |
- |
-|
985 | |
- |
-|
986 | This is the case because the pointer you have for the submodule isn’t what is actually in the submodule directory. To fix this, you must run `git submodule update` again: |
- |
-|
987 | |
- |
-|
988 | $ git submodule update |
- |
-|
989 | remote: Counting objects: 5, done. |
- |
-|
990 | remote: Compressing objects: 100% (3/3), done. |
- |
-|
991 | remote: Total 3 (delta 1), reused 2 (delta 0) |
- |
-|
992 | Unpacking objects: 100% (3/3), done. |
- |
-|
993 | From git@github.com:schacon/rack |
- |
-|
994 | 08d709f..6c5e70b master -> origin/master |
- |
-|
995 | Submodule path 'rack': checked out '6c5e70b984a60b3cecd395edd5b48a7575bf58e0' |
- |
-|
996 | |
- |
-|
997 | You have to do this every time you pull down a submodule change in the main project. It’s strange, but it works. |
- |
-|
998 | |
- |
-|
999 | One common problem happens when a developer makes a change locally in a submodule but doesn’t push it to a public server. Then, they commit a pointer to that non-public state and push up the superproject. When other developers try to run `git submodule update`, the submodule system can’t find the commit that is referenced, because it exists only on the first developer’s system. If that happens, you see an error like this: |
- |
-|
1000 | |
- |
-|
1001 | $ git submodule update |
- |
-|
1002 | fatal: reference isn’t a tree: 6c5e70b984a60b3cecd395edd5b48a7575bf58e0 |
- |
-|
1003 | Unable to checkout '6c5e70b984a60b3cecd395edd5ba7575bf58e0' in submodule path 'rack' |
- |
-|
1004 | |
- |
-|
1005 | You have to see who last changed the submodule: |
- |
-|
1006 | |
- |
-|
1007 | $ git log -1 rack |
- |
-|
1008 | commit 85a3eee996800fcfa91e2119372dd4172bf76678 |
- |
-|
1009 | Author: Scott Chacon <schacon@gmail.com> |
- |
-|
1010 | Date: Thu Apr 9 09:19:14 2009 -0700 |
- |
-|
1011 | |
- |
-|
1012 | added a submodule reference I will never make public. hahahahaha! |
- |
-|
1013 | |
- |
-|
1014 | Then, you e-mail that guy and yell at him. |
- |
-|
1015 | |
- |
-|
1016 | ### Superprojects ### |
- |
-|
1017 | |
- |
-|
1018 | Sometimes, developers want to get a combination of a large project’s subdirectories, depending on what team they’re on. This is common if you’re coming from CVS or Subversion, where you’ve defined a module or collection of subdirectories, and you want to keep this type of workflow. |
- |
-|
1019 | |
- |
-|
1020 | A good way to do this in Git is to make each of the subdirectories a separate Git repository and then create superproject Git repositories that contain multiple submodules. A benefit of this approach is that you can more specifically define the relationships between the projects with tags and branches in the superprojects. |
- |
-|
1021 | |
- |
-|
1022 | ### Issues with Submodules ### |
- |
-|
1023 | |
- |
-|
1024 | Using submodules isn’t without hiccups, however. First, you must be relatively careful when working in the submodule directory. When you run `git submodule update`, it checks out the specific version of the project, but not within a branch. This is called having a detached HEAD — it means the HEAD file points directly to a commit, not to a symbolic reference. The issue is that you generally don’t want to work in a detached HEAD environment, because it’s easy to lose changes. If you do an initial `submodule update`, commit in that submodule directory without creating a branch to work in, and then run `git submodule update` again from the superproject without committing in the meantime, Git will overwrite your changes without telling you. Technically you won’t lose the work, but you won’t have a branch pointing to it, so it will be somewhat difficult to retrieve. |
- |
-|
1025 | |
- |
-|
1026 | To avoid this issue, create a branch when you work in a submodule directory with `git checkout -b work` or something equivalent. When you do the submodule update a second time, it will still revert your work, but at least you have a pointer to get back to. |
- |
-|
1027 | |
- |
-|
1028 | Switching branches with submodules in them can also be tricky. If you create a new branch, add a submodule there, and then switch back to a branch without that submodule, you still have the submodule directory as an untracked directory: |
- |
-|
1029 | |
- |
-|
1030 | $ git checkout -b rack |
- |
-|
1031 | Switched to a new branch "rack" |
- |
-|
1032 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1033 | Initialized empty Git repository in /opt/myproj/rack/.git/ |
- |
-|
1034 | ... |
- |
-|
1035 | Receiving objects: 100% (3184/3184), 677.42 KiB | 34 KiB/s, done. |
- |
-|
1036 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1037 | $ git commit -am 'added rack submodule' |
- |
-|
1038 | [rack cc49a69] added rack submodule |
- |
-|
1039 | 2 files changed, 4 insertions(+), 0 deletions(-) |
- |
-|
1040 | create mode 100644 .gitmodules |
- |
-|
1041 | create mode 160000 rack |
- |
-|
1042 | $ git checkout master |
- |
-|
1043 | Switched to branch "master" |
- |
-|
1044 | $ git status |
- |
-|
1045 | # On branch master |
- |
-|
1046 | # Untracked files: |
- |
-|
1047 | # (use "git add <file>..." to include in what will be committed) |
- |
-|
1048 | # |
- |
-|
1049 | # rack/ |
- |
-|
1050 | |
- |
-|
1051 | You have to either move it out of the way or remove it, in which case you have to clone it again when you switch back—and you may lose local changes or branches that you didn’t push up. |
- |
-|
1052 | |
- |
-|
1053 | The last main caveat that many people run into involves switching from subdirectories to submodules. If you’ve been tracking files in your project and you want to move them out into a submodule, you must be careful or Git will get angry at you. Assume that you have the rack files in a subdirectory of your project, and you want to switch it to a submodule. If you delete the subdirectory and then run `submodule add`, Git yells at you: |
- |
-|
1054 | |
- |
-|
1055 | $ rm -Rf rack/ |
- |
-|
1056 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1057 | 'rack' already exists in the index |
- |
-|
1058 | |
- |
-|
1059 | You have to unstage the `rack` directory first. Then you can add the submodule: |
- |
-|
1060 | |
- |
-|
1061 | $ git rm -r rack |
- |
-|
1062 | $ git submodule add git@github.com:schacon/rack.git rack |
- |
-|
1063 | Initialized empty Git repository in /opt/testsub/rack/.git/ |
- |
-|
1064 | remote: Counting objects: 3184, done. |
- |
-|
1065 | remote: Compressing objects: 100% (1465/1465), done. |
- |
-|
1066 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
- |
-|
1067 | Receiving objects: 100% (3184/3184), 677.42 KiB | 88 KiB/s, done. |
- |
-|
1068 | Resolving deltas: 100% (1952/1952), done. |
- |
-|
1069 | |
- |
-|
1070 | Now suppose you did that in a branch. If you try to switch back to a branch where those files are still in the actual tree rather than a submodule — you get this error: |
- |
-|
1071 | |
- |
-|
1072 | $ git checkout master |
- |
-|
1073 | error: Untracked working tree file 'rack/AUTHORS' would be overwritten by merge. |
- |
-|
1074 | |
- |
-|
1075 | You have to move the `rack` submodule directory out of the way before you can switch to a branch that doesn’t have it: |
- |
-|
1076 | |
- |
-|
1077 | $ mv rack /tmp/ |
- |
-|
1078 | $ git checkout master |
- |
-|
1079 | Switched to branch "master" |
- |
-|
1080 | $ ls |
- |
-|
1081 | README rack |
- |
-|
1082 | |
- |
-|
1083 | Then, when you switch back, you get an empty `rack` directory. You can either run `git submodule update` to reclone, or you can move your `/tmp/rack` directory back into the empty directory. |
- |
-|
1084 | |
- |
-|
1085 | ## Subtree Merging ## |
-2 | ===== Subtree Merging |
-
1086 | |
- |
-|
1087 | Now that you’ve seen the difficulties of the submodule system, let’s look at an alternate way to solve the same problem. When Git merges, it looks at what it has to merge together and then chooses an appropriate merging strategy to use. If you’re merging two branches, Git uses a _recursive_ strategy. If you’re merging more than two branches, Git picks the _octopus_ strategy. These strategies are automatically chosen for you because the recursive strategy can handle complex three-way merge situations — for example, more than one common ancestor — but it can only handle merging two branches. The octopus merge can handle multiple branches but is more cautious to avoid difficult conflicts, so it’s chosen as the default strategy if you’re trying to merge more than two branches. |
- |
-|
1088 | |
- |
-|
1089 | However, there are other strategies you can choose as well. One of them is the _subtree_ merge, and you can use it to deal with the subproject issue. Here you’ll see how to do the same rack embedding as in the last section, but using subtree merges instead. |
- |
-|
1090 | |
-3 | |
-
.1091 | The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one and vice versa. When you specify a subtree merge, Git is smart enough to figure out that one is a subtree of the other and merge appropriately — it’s pretty amazing. |
-4 | The idea of the subtree merge is that you have two projects, and one of the projects maps to a subdirectory of the other one and vice versa. When you specify a subtree merge, Git is often smart enough to figure out that one is a subtree of the other and merge appropriately. |
-
|
-5 | |
-|
|
-6 | We'll go through an example of adding a separate project into an existing project and then merging the code of the second into a subdirectory of the first. |
-|
1092 | |
-7 | |
-
.1093 | You first add the Rack application to your project. You add the Rack project as a remote reference in your own project and then check it out into its own branch: |
-8 | First, we'll add the Rack application to our project. We'll add the Rack project as a remote reference in our own project and then check it out into its own branch: |
-
1094 | |
-9 | |
-
. | |
-10 | [source,console] |
-
|
-11 | ---- |
-|
1095 | $ git remote add rack_remote git@github.com:schacon/rack.git |
-12 | $ git remote add rack_remote https://github.com/rack/rack |
-
1096 | $ git fetch rack_remote |
-13 | $ git fetch rack_remote |
-
1097 | warning: no common commits |
-14 | warning: no common commits |
-
1098 | remote: Counting objects: 3184, done. |
-15 | remote: Counting objects: 3184, done. |
-
1099 | remote: Compressing objects: 100% (1465/1465), done. |
-16 | remote: Compressing objects: 100% (1465/1465), done. |
-
1100 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
-17 | remote: Total 3184 (delta 1952), reused 2770 (delta 1675) |
-
1101 | Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. |
-18 | Receiving objects: 100% (3184/3184), 677.42 KiB | 4 KiB/s, done. |
-
1102 | Resolving deltas: 100% (1952/1952), done. |
-19 | Resolving deltas: 100% (1952/1952), done. |
-
1103 | From git@github.com:schacon/rack |
-20 | From https://github.com/rack/rack |
-
1104 | * [new branch] build -> rack_remote/build |
-21 | * [new branch] build -> rack_remote/build |
-
1105 | * [new branch] master -> rack_remote/master |
-22 | * [new branch] master -> rack_remote/master |
-
1106 | * [new branch] rack-0.4 -> rack_remote/rack-0.4 |
-23 | * [new branch] rack-0.4 -> rack_remote/rack-0.4 |
-
1107 | * [new branch] rack-0.9 -> rack_remote/rack-0.9 |
-24 | * [new branch] rack-0.9 -> rack_remote/rack-0.9 |
-
1108 | $ git checkout -b rack_branch rack_remote/master |
-25 | $ git checkout -b rack_branch rack_remote/master |
-
1109 | Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. |
-26 | Branch rack_branch set up to track remote branch refs/remotes/rack_remote/master. |
-
1110 | Switched to a new branch "rack_branch" |
-27 | Switched to a new branch "rack_branch" |
-
|
-28 | ---- |
-|
1111 | |
-29 | |
-
.1112 | Now you have the root of the Rack project in your `rack_branch` branch and your own project in the `master` branch. If you check out one and then the other, you can see that they have different project roots: |
-30 | Now we have the root of the Rack project in our `rack_branch` branch and our own project in the `master` branch. If you check out one and then the other, you can see that they have different project roots: |
-
1113 | |
-31 | |
-
. | |
-32 | [source,console] |
-
|
-33 | ---- |
-|
1114 | $ ls |
-34 | $ ls |
-
1115 | AUTHORS KNOWN-ISSUES Rakefile contrib lib |
-35 | AUTHORS KNOWN-ISSUES Rakefile contrib lib |
-
1116 | COPYING README bin example test |
-36 | COPYING README bin example test |
-
1117 | $ git checkout master |
-37 | $ git checkout master |
-
1118 | Switched to branch "master" |
-38 | Switched to branch "master" |
-
1119 | $ ls |
-39 | $ ls |
-
1120 | README |
-40 | README |
-
|
-41 | ---- |
-|
|
-42 | |
-|
|
-43 | This is sort of a strange concept. Not all the branches in your repository actually have to be branches of the same project. It's not common, because it's rarely helpful, but it's fairly easy to have branches contain completely different histories. |
-|
1121 | |
-44 | |
-
.1122 | You want to pull the Rack project into your `master` project as a subdirectory. You can do that in Git with `git read-tree`. You’ll learn more about `read-tree` and its friends in Chapter 9, but for now know that it reads the root tree of one branch into your current staging area and working directory. You just switched back to your `master` branch, and you pull the `rack` branch into the `rack` subdirectory of your `master` branch of your main project: |
-45 | In this case, we want to pull the Rack project into our `master` project as a subdirectory. We can do that in Git with `git read-tree`. You’ll learn more about `read-tree` and its friends in <<_git_internals>>, but for now know that it reads the root tree of one branch into your current staging area and working directory. We just switched back to your `master` branch, and we pull the `rack` branch into the `rack` subdirectory of our `master` branch of our main project: |
-
1123 | |
-46 | |
-
. | |
-47 | [source,console] |
-
|
-48 | ---- |
-|
1124 | $ git read-tree --prefix=rack/ -u rack_branch |
-49 | $ git read-tree --prefix=rack/ -u rack_branch |
-
|
-50 | ---- |
-|
1125 | |
-51 | |
-
.1126 | When you commit, it looks like you have all the Rack files under that subdirectory — as though you copied them in from a tarball. What gets interesting is that you can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, you can pull in upstream changes by switching to that branch and pulling: |
-52 | When we commit, it looks like we have all the Rack files under that subdirectory – as though we copied them in from a tarball. What gets interesting is that we can fairly easily merge changes from one of the branches to the other. So, if the Rack project updates, we can pull in upstream changes by switching to that branch and pulling: |
-
1127 | |
-53 | |
-
. | |
-54 | [source,console] |
-
|
-55 | ---- |
-|
1128 | $ git checkout rack_branch |
-56 | $ git checkout rack_branch |
-
1129 | $ git pull |
-57 | $ git pull |
-
|
-58 | ---- |
-|
1130 | |
-59 | |
-
.1131 | Then, you can merge those changes back into your master branch. You can use `git merge -s subtree` and it will work fine; but Git will also merge the histories together, which you probably don’t want. To pull in the changes and prepopulate the commit message, use the `--squash` and `--no-commit` options as well as the `-s subtree` strategy option: |
-60 | Then, we can merge those changes back into our `master` branch. We can use `git merge -s subtree` and it will work fine; but Git will also merge the histories together, which we probably don’t want. To pull in the changes and prepopulate the commit message, use the `--squash` and `--no-commit` options as well as the `-s subtree` strategy option: |
-
1132 | |
-61 | |
-
. | |
-62 | [source,console] |
-
|
-63 | ---- |
-|
1133 | $ git checkout master |
-64 | $ git checkout master |
-
1134 | $ git merge --squash -s subtree --no-commit rack_branch |
-65 | $ git merge --squash -s subtree --no-commit rack_branch |
-
1135 | Squash commit -- not updating HEAD |
-66 | Squash commit -- not updating HEAD |
-
1136 | Automatic merge went well; stopped before committing as requested |
-67 | Automatic merge went well; stopped before committing as requested |
-
|
-68 | ---- |
-|
1137 | |
-69 | |
-
.1138 | All the changes from your Rack project are merged in and ready to be committed locally. You can also do the opposite — make changes in the `rack` subdirectory of your master branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. |
-70 | All the changes from the Rack project are merged in and ready to be committed locally. You can also do the opposite – make changes in the `rack` subdirectory of your master branch and then merge them into your `rack_branch` branch later to submit them to the maintainers or push them upstream. |
-
|
-71 | |
-|
|
-72 | This gives us a way to have a workflow somewhat similar to the submodule workflow without using submodules (which we will cover in <<_git_submodules>>). We can keep branches with other related projects in our repository and subtree merge them into our project occasionally. It is nice in some ways, for example all the code is committed to a single place. However, it has other drawbacks in that it's a bit more complex and easier to make mistakes in reintegrating changes or accidentally pushing a branch into an unrelated repository. |
-|
1139 | |
-73 | |
-
.1140 | To get a diff between what you have in your `rack` subdirectory and the code in your `rack_branch` branch — to see if you need to merge them — you can’t use the normal `diff` command. Instead, you must run `git diff-tree` with the branch you want to compare to: |
-74 | Another slightly weird thing is that to get a diff between what you have in your `rack` subdirectory and the code in your `rack_branch` branch – to see if you need to merge them – you can’t use the normal `diff` command. Instead, you must run `git diff-tree` with the branch you want to compare to: |
-
1141 | |
-75 | |
-
. | |
-76 | [source,console] |
-
|
-77 | ---- |
-|
1142 | $ git diff-tree -p rack_branch |
-78 | $ git diff-tree -p rack_branch |
-
|
-79 | ---- |
-|
1143 | |
-80 | |
-
1144 | Or, to compare what is in your `rack` subdirectory with what the `master` branch on the server was the last time you fetched, you can run |
-81 | Or, to compare what is in your `rack` subdirectory with what the `master` branch on the server was the last time you fetched, you can run |
-
1145 | |
-82 | |
-
. | |
-83 | [source,console] |
-
|
-84 | ---- |
-|
1146 | $ git diff-tree -p rack_remote/master |
-85 | $ git diff-tree -p rack_remote/master |
-
1147 | |
-86 | ---- |
-
1148 | ## Summary ## |
- |
-|
1149 | |
- |
-|
1150 | You’ve seen a number of advanced tools that allow you to manipulate your commits and staging area more precisely. When you notice issues, you should be able to easily figure out what commit introduced them, when, and by whom. If you want to use subprojects in your project, you’ve learned a few ways to accommodate those needs. At this point, you should be able to do most of the things in Git that you’ll need on the command line day to day and feel comfortable doing so. |
- |
-|
1151 | |
-87 | |
-