-
Notifications
You must be signed in to change notification settings - Fork 0
/
git.html
1863 lines (1773 loc) · 58.5 KB
/
git.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="generator" content="AsciiDoc 7.0.2">
<style type="text/css">
/* Debug borders */
p, li, dt, dd, div, pre, h1, h2, h3, h4, h5, h6 {
/*
border: 1px solid red;
*/
}
body {
margin: 1em 5% 1em 5%;
}
a { color: blue; }
a:visited { color: fuchsia; }
em {
font-style: italic;
}
strong {
font-weight: bold;
}
tt {
color: navy;
}
h1, h2, h3, h4, h5, h6 {
color: #527bbd;
font-family: sans-serif;
margin-top: 1.2em;
margin-bottom: 0.5em;
line-height: 1.3;
}
h1 {
border-bottom: 2px solid silver;
}
h2 {
border-bottom: 2px solid silver;
padding-top: 0.5em;
}
div.sectionbody {
font-family: serif;
margin-left: 0;
}
hr {
border: 1px solid silver;
}
p {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
pre {
padding: 0;
margin: 0;
}
span#author {
color: #527bbd;
font-family: sans-serif;
font-weight: bold;
font-size: 1.2em;
}
span#email {
}
span#revision {
font-family: sans-serif;
}
div#footer {
font-family: sans-serif;
font-size: small;
border-top: 2px solid silver;
padding-top: 0.5em;
margin-top: 4.0em;
}
div#footer-text {
float: left;
padding-bottom: 0.5em;
}
div#footer-badges {
float: right;
padding-bottom: 0.5em;
}
div#preamble,
div.tableblock, div.imageblock, div.exampleblock, div.verseblock,
div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock,
div.admonitionblock {
margin-right: 10%;
margin-top: 1.5em;
margin-bottom: 1.5em;
}
div.admonitionblock {
margin-top: 2.5em;
margin-bottom: 2.5em;
}
div.content { /* Block element content. */
padding: 0;
}
/* Block element titles. */
div.title, caption.title {
font-family: sans-serif;
font-weight: bold;
text-align: left;
margin-top: 1.0em;
margin-bottom: 0.5em;
}
div.title + * {
margin-top: 0;
}
td div.title:first-child {
margin-top: 0.0em;
}
div.content div.title:first-child {
margin-top: 0.0em;
}
div.content + div.title {
margin-top: 0.0em;
}
div.sidebarblock > div.content {
background: #ffffee;
border: 1px solid silver;
padding: 0.5em;
}
div.listingblock > div.content {
border: 1px solid silver;
background: #f4f4f4;
padding: 0.5em;
}
div.quoteblock > div.content {
padding-left: 2.0em;
}
div.quoteblock .attribution {
text-align: right;
}
div.admonitionblock .icon {
vertical-align: top;
font-size: 1.1em;
font-weight: bold;
text-decoration: underline;
color: #527bbd;
padding-right: 0.5em;
}
div.admonitionblock td.content {
padding-left: 0.5em;
border-left: 2px solid silver;
}
div.exampleblock > div.content {
border-left: 2px solid silver;
padding: 0.5em;
}
div.verseblock div.content {
white-space: pre;
}
div.imageblock div.content { padding-left: 0; }
div.imageblock img { border: 1px solid silver; }
span.image img { border-style: none; }
dl {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
dt {
margin-top: 0.5em;
margin-bottom: 0;
font-style: italic;
}
dd > *:first-child {
margin-top: 0;
}
ul, ol {
list-style-position: outside;
}
ol.olist2 {
list-style-type: lower-alpha;
}
div.tableblock > table {
border-color: #527bbd;
border-width: 3px;
}
thead {
font-family: sans-serif;
font-weight: bold;
}
tfoot {
font-weight: bold;
}
div.hlist {
margin-top: 0.8em;
margin-bottom: 0.8em;
}
td.hlist1 {
vertical-align: top;
font-style: italic;
padding-right: 0.8em;
}
td.hlist2 {
vertical-align: top;
}
@media print {
div#footer-badges { display: none; }
}
/* Workarounds for IE6's broken and incomplete CSS2. */
div.sidebar-content {
background: #ffffee;
border: 1px solid silver;
padding: 0.5em;
}
div.sidebar-title, div.image-title {
font-family: sans-serif;
font-weight: bold;
margin-top: 0.0em;
margin-bottom: 0.5em;
}
div.listingblock div.content {
border: 1px solid silver;
background: #f4f4f4;
padding: 0.5em;
}
div.quoteblock-content {
padding-left: 2.0em;
}
div.exampleblock-content {
border-left: 2px solid silver;
padding-left: 0.5em;
}
</style><title>Git 中文教程</title></head>
<body>
<div id="header">
<h1>Git 中文教程</h1>
</div>
<!-- Begin of Introduction session
********************************************************************************************-->
<h2>介绍</h2>
<div class="sectionbody">
<p>
Git --- The stupid content tracker, 傻瓜内容跟踪器。Linus 是这样给我们介绍 Git 的。
</p>
<p>
Git 是用于 Linux 内核开发的版本控制工具。与常用的版本控制工具 CVS, Subversion 等不同,
它采用了分布式版本库的方式,不必服务器端软件支持,使源代码的发布和交流极其方便。
Git 的速度很快,这对于诸如 Linux kernel 这样的大项目来说自然很重要。
Git 最为出色的是它的合并跟踪(merge tracing)能力。
</p>
<p>
实际上内核开发团队决定开始开发和使用 Git 来作为内核开发的版本控制系统的时候,
世界开源社群的反对声音不少,最大的理由是 Git 太艰涩难懂,从 Git 的内部工作机制来说,的确是这样。
但是随着开发的深入,Git 的正常使用都由一些友好的脚本命令来执行,使 Git 变得非常好用,
即使是用来管理我们自己的开发项目,Git 都是一个友好,有力的工具。
现在,越来越多的著名项目采用 Git 来管理项目开发,例如:wine, U-boot 等,详情看
<a href="http://www.kernel.org/git" target="new">http://www.kernel.org/git</a>
</p>
<p>
作为开源自由原教旨主义项目,Git 没有对版本库的浏览和修改做任何的权限限制。
它只适用于 Linux / Unix 平台,没有 Windows 版本,目前也没有这样的开发计划。
</p>
<p>
本文将以 Git 官方文档
<a href="http://www.kernel.org/pub/software/scm/git/docs/tutorial.html" target="new">Tutorial</a>,
<a href="http://www.kernel.org/pub/software/scm/git/docs/core-tutorial.html" target="new">
core-tutorial</a> 和
<a href="http://www.kernel.org/pub/software/scm/git/docs/everyday.html" target="new">Everyday GIT</a>
作为蓝本翻译整理,但是暂时去掉了对 Git 内部工作机制的阐述,
力求简明扼要,并加入了作者使用 Git 的过程中的一些心得体会,注意事项,以及更多的例子。
建议你最好通过你所使用的 Unix / Linux 发行版的安装包来安装 Git,
你可以<a href="http://justjavac.com/git.html">在线浏览本文</a>
,也可以通过下面的命令来得到本文最新的版本库,并且通过后面的学习用 Git 作为工具参加到本文的创作中来。</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-clone http://www.bitsun.com/git/gittutorcn.git</tt><pre></pre></pre></div>
</div>
</div>
<!-- Creating a Repository
****************************************************************************************-->
<h2>创建一个版本库:git-init-db</h2>
<div class="sectionbody">
<p>
创建一个 Git 版本库是很容易的,只要用命令 <tt>git-init-db</tt> 就可以了。
现在我们来为本文的写作创建一个版本库:</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ mkdir gittutorcn
$ cd gittutorcn
$ git-init-db</tt><pre></pre></pre></div>
</div>
</div>
<p>git 将会作出以下的回应</p>
<div class="listingblock">
<div class="content">
<pre>defaulting to local storage area<pre></pre></pre></div>
</div>
<p>
这样,一个空的版本库就创建好了,并在当前目录中创建一个叫 .git 的子目录。
你可以用 <tt>ls -a</tt> 查看一下,并请注意其中的三项内容:
</p>
<ul>
<li>
<p>
一个叫 HEAD 的文件,我们现在来查看一下它的内容:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ cat .git/HEAD</tt></pre>
</div>
</div>
<p>
现在 HEAD 的内容应该是这样:
</p>
<div class="listingblock">
<div class="content">
<pre>ref: refs/heads/master</pre>
</div>
</div>
<p>我们可以看到,HEAD 文件中的内容其实只是包含了一个索引信息,
并且,这个索引将总是指向你的项目中的当前开发分支。</p>
</li>
<li>
<p>一个叫 objects 的子目录,它包含了你的项目中的所有对象,我们不必直接地了解到这些对象内容,
我们应该关心是存放在这些对象中的项目的数据。</p>
<div class="admonitionblock">
<table><tbody><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
关于 git 对象的分类,以及 git 对象数据库的说明,
请参看 <a href="http://www.kernel.org/pub/software/scm/git/docs/#Discussion," target="new">
[Discussion]</a>
</td>
</tr></tbody></table>
</div>
</li>
<li>
<p>一个叫 refs 的子目录,它用来保存指向对象的索引。</p>
</li>
</ul>
<p>
具体地说,子目录 <tt>refs</tt> 包含着两个子目录叫 <tt>heads</tt> 和 <tt>tags</tt>,
就像他们的名字所表达的意味一样:他们存放了不同的开发分支的<em>头</em>的索引,
或者是你用来标定版本的<em>标签</em>的索引。
</p>
<p>
请注意:<tt>master</tt> 是默认的分支,这也是为什么 <tt>.git/HEAD</tt>
创建的时候就指向 <tt>master</tt> 的原因,尽管目前它其实并不存在。
git 将假设你会在 <tt>master</tt> 上开始并展开你以后的工作,除非你自己创建你自己的分支。
</p>
<p>
另外,这只是一个约定俗成的习惯而已,实际上你可以将你的工作分支叫任何名字,
而不必在版本库中一定要有一个叫 <tt>master</tt> 的分支,尽管很多 git 工具都认为 <tt>master</tt>
分支是存在的。
</p>
<p>
现在已经创建好了一个 git 版本库,但是它是空的,还不能做任何事情,下一步就是怎么向版本库植入数据了。
</p>
<!-- Populating the Repository
*******************************************************************************************-->
<h2>植入内容跟踪信息:git-add</h2>
<div class="sectionbody">
<p>
为了简明起见,我们创建两个文件作为练习:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ echo "Hello world" > hello
$ echo "Silly example" > example
</tt></pre>
</div>
</div>
<p>
我们再用 <tt>git-add</tt> 命令将这两个文件加入到版本库文件索引当中:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-add hello example
</tt></pre>
</div>
</div>
<p>
<tt>git-add </tt>实际上是个脚本命令,它是对 git 内核命令 <tt>git-update-index</tt> 的调用。
因此上面的命令和下面的命令其实是等价的:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-update-index --add hello example</tt><pre></pre></pre></div>
</div>
<p>
如果你要将某个文件从 git 的目录跟踪系统中清除出去,同样可以用
<tt>git-update-index</tt> 命令。例如:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-update-index --force-remove foo.c</tt></pre>
</div>
</div>
<div class="admonitionblock">
<table><tbody><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<tt>git-add</tt> 可以将某个目录下的所有内容全都纳入内容跟踪之下,例如:
<tt>git-add ./path/to/your/wanted</tt> 。但是在这样做之前,
应该注意先将一些我们不希望跟踪的文件清理掉,
例如,gcc 编译出来的 *.o 文件,vim 的交换文件 .*.swp 之类。
</td>
</tr></tbody></table>
</div>
<p>
应该建立一个清晰的概念就是,<tt>git-add</tt> 和 <tt>git-update-index</tt>
只是刷新了 git 的跟踪信息,hello 和 example 这两个文件中的内容并没有提交到 git 的内容跟踪范畴之内。
</p>
</div>
<!-- Committing git state
************************************************************************************************-->
<h2>提交内容到版本库:git-commit</h2>
<div class="sectionbody">
<p>
既然我们刷新了 Git 的跟踪信息,现在我们看看版本库的状态:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-status</tt></pre>
</div>
</div>
<p>
我们能看到 git 的状态提示:
</p>
<div class="listingblock">
<div class="content">
<pre>#
# Initial commit
#
#
# Updated but not checked in:
# (will commit)
#
# new file: example
# new file: hello
#
</pre>
</div>
</div>
<p>
提示信息告诉我们版本库中加入了两个新的文件,并且 git 提示我们提交这些文件,
我们可以通过 <tt>git-commit</tt> 命令来提交:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-commit -m "Initial commit of gittutor reposistory"</tt></pre>
</div>
</div>
</div>
<!-- Working with repository
************************************************************************************************-->
<h2>查看当前的工作:git-diff</h2>
<div class="sectionbody">
<p>
<tt>git-diff</tt> 命令将比较当前的工作目录和版本库数据库中的差异。
现在我们编辑一些文件来体验一下 git 的跟踪功能。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ echo "It's a new day for git" >> hello</tt><pre></pre></pre></div>
</div>
<p>
我们再来比较一下,当前的工作目录和版本库中的数据的差别。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-diff</tt></pre>
</div>
</div>
<p>
差异将以典型的 patch 方式表示出来:
</p>
<div class="listingblock">
<div class="content">
<pre>diff --git a/hello b/hello
index a5c1966..bd9212c 100644
--- a/hello
+++ b/hello
@@ -1 +1,2 @@
Hello, world
+It's a new day for git
</pre>
</div>
</div>
<p>
此时,我们可以再次使用组合命令 <tt>git-update-index</tt> 和
<tt>git-commit</tt> 将我们的工作提交到版本库中。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-update-index hello
$ git-commit -m "new day for git"
</tt></pre>
</div>
</div>
<p>
实际上,如果要提交的文件都是已经纳入 git 版本库的文件,那么不必为这些文件都应用
<tt>git-update-index</tt> 命令之后再进行提交,下面的命令更简捷并且和上面的命令是等价的。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-commit -a -m "new day for git"</tt></pre>
</div>
</div>
</div>
<!-- Managing git Branch
************************************************************************************************-->
<h2>管理分支:git-branch</h2>
<div class="sectionbody">
<p>
直至现在为止,我们的项目版本库一直都是只有一个分支 <tt>master</tt>。
在 git 版本库中创建分支的成本几乎为零,所以,不必吝啬多创建几个分支。
下面列举一些常见的分支策略,仅供大家参考:
</p>
<ul>
<li>
<p>
创建一个属于自己的个人工作分支,以避免对主分支 <tt>master</tt> 造成太多的干扰,
也方便与他人交流协作。
</p>
</li>
<li>
<p>
当进行高风险的工作时,创建一个试验性的分支,扔掉一个烂摊子总比收拾一个烂摊子好得多。
</p>
</li>
<li>
<p>
合并别人的工作的时候,最好是创建一个临时的分支,关于如何用临时分支合并别人的工作的技巧,
将会在后面讲述。
</p>
</li>
</ul>
<h3>创建分支</h3>
<p>
下面的命令将创建我自己的工作分支,名叫 robin,并且将以后的工作转移到这个分支上开展。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-branch robin
$ git-checkout robin
</tt></pre>
</div>
</div>
<h3>删除分支</h3>
<p>
要删除版本库中的某个分支,使用 <tt>git-branch -D</tt> 命令就可以了,例如:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-branch -D branch-name</tt></pre>
</div>
</div>
</div>
<!-- Inspectting Changes
************************************************************************************************-->
<h2>查看项目的发展变化和比较差异</h2>
<p>
这一节介绍几个查看项目的版本库的发展变化以及比较差异的很有用的命令:
</p>
<h4>git-show-branch</h4>
<h4>git-diff</h4>
<h4>git-whatchanged</h4>
<br>
<p>
我们现在为 <tt>robin</tt>, <tt>master</tt> 两个分支都增加一些内容。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-checkout robin
$ echo "Work, work, workd" >> hello
$ git-commit -m "Some workd" -i hello
</tt></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-checkout master
$ echo "Play, play, play" >> hello
$ echo "Lots of fun" >> example
$ git-commit -m "Some fun" -i hello example
</tt></pre>
</div>
</div>
<p>
<tt>git-show-branch</tt> 命令可以使我们看到版本库中每个分支的世系发展状态,
并且可以看到每次提交的内容是否已进入每个分支。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-show-branch
</tt></pre>
</div>
</div>
<p>
这个命令让我们看到版本库的发展记录。
</p>
<div class="listingblock">
<div class="content">
<pre>* [master] Some fun
! [robin] some work
--
* [master] Some fun
+ [robin] some work
*+ [master^] a new day for git
</pre>
</div>
</div>
<p>
譬如我们要查看世系标号为 <tt>master^</tt> 和 <tt>robin</tt> 的版本的差异情况,
我们可以使用这样的命令:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-diff master^ robin
</tt></pre>
</div>
</div>
<p>
我们可以看到这两个版本的差异:
</p>
<div class="listingblock">
<div class="content">
<pre>diff --git a/hello b/hello
index 263414f..cc44c73 100644
--- a/hello
+++ b/hello
@@ -1,2 +1,3 @@
Hello World
It's a new day for git
+Work, work, work
</pre>
</div>
</div>
<div class="admonitionblock">
<table><tbody><tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
关于 GIT 版本世系编号的定义,请参看
<a href="http://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html" target="new">
git-rev-parse
</a> 。
</td>
</tr></tbody></table>
</div>
<p>
我们现在再用 <tt>git-whatchanged</tt> 命令来看看 <tt>master</tt> 分支是怎么发展的。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-checkout master
$ git-whatchanged
</tt></pre>
</div>
</div>
<div class="listingblock">
<div class="content">
<pre>diff-tree 1d2fa05... (from 3ecebc0...)
Author: Vortune.Robin <[email protected]>
Date: Tue Mar 21 02:24:31 2006 +0800
Some fun
:100644 100644 f24c74a... 7f8b141... M example
:100644 100644 263414f... 06fa6a2... M hello
diff-tree 3ecebc0... (from 895f09a...)
Author: Vortune.Robin <[email protected]>
Date: Tue Mar 21 02:17:23 2006 +0800
a new day for git
:100644 100644 557db03... 263414f... M hello
</[email protected]></[email protected]></pre>
</div>
</div>
<p>
从上面的内容中我们可以看到,在 <tt>robin</tt> 分支中的日志为 "Some work" 的内容,
并没有在 <tt>master</tt> 分支中出现。
</p>
<!-- Merge to branch
************************************************************************************************-->
<h2>合并两个分支:git-merge</h2>
<div class="sectionbody">
<p>
既然我们为项目创建了不同的分支,
那么我们就要经常地将自己或者是别人在一个分支上的工作合并到其他的分支上去。
现在我们看看怎么将 <tt>robin</tt> 分支上的工作合并到 <tt>master</tt> 分支中。
现在转移我们当前的工作分支到 <tt>master</tt>,并且将 <tt>robin</tt> 分支上的工作合并进来。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-checkout master
$ git-merge "Merge work in robin" HEAD robin</tt><pre></pre></pre></div>
</div>
合并两个分支,还有一个更简便的方式,下面的命令和上面的命令是等价的。
<div class="listingblock">
<div class="content">
<pre><tt>$ git-checkout master
$ git-pull . robin
</tt></pre>
</div>
</div>
<p>
但是,此时 git 会出现合并冲突提示:
</p>
<div class="listingblock">
<div class="content">
<pre>Trying really trivial in-index merge...
fatal: Merge requires file-level merging
Nope.
Merging HEAD with d2659fcf690ec693c04c82b03202fc5530d50960
Merging:
1d2fa05b13b63e39f621d8ee911817df0662d9b7 Some fun
d2659fcf690ec693c04c82b03202fc5530d50960 some work
found 1 common ancestor(s):
3ecebc0cb4894a33208dfa7c7c6fc8b5f9da0eda a new day for git
Auto-merging hello
CONFLICT (content): Merge conflict in hello
Automatic merge failed; fix up by hand
</pre>
</div>
</div>
<p>
git 的提示指出,在合并作用于文件 hello 的 'Some fun' 和 'some work' 这两个对象时有冲突,
具体通俗点说,就是在 <tt>master</tt>, <tt>robin</tt> 这两个分支中的 hello 文件的某些相同的行中的内容不一样。
我们需要手动解决这些冲突,现在先让我们看看现在的 hello 文件中的内容。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ cat hello
</tt></pre>
</div>
</div>
<p>
此时的 hello 文件应是这样的,用过其他的版本控制系统的朋友应该很容易看出这个典型的冲突表示格式:
</p>
<div class="listingblock">
<div class="content">
<pre>Hello World
It's a new day for git
<<<<<<< HEAD/hello
Play, play, play
=======
Work, work, work
>>>>>>> d2659fcf690ec693c04c82b03202fc5530d50960/hello
</pre>
</div>
</div>
<p>
我们用编辑器将 hello 文件改为:
</p>
<div class="listingblock">
<div class="content">
<pre>Hello World
It's a new day for git
Play, play, play
Work, work, work
</pre>
</div>
</div>
<p>
现在可以将手动解决了冲突的文件提交了。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-commit -i hello
</tt></pre>
</div>
</div>
<p>
以上是典型的两路合并(2-way merge)算法,绝大多数情况下已经够用。
但是还有更复杂的三路合并和多内容树合并的情况。详情可参看:
<a href="http://www.kernel.org/pub/software/scm/git/docs/git-read-tree.html" target="new">
git-read-tree</a>,
<a href="http://www.kernel.org/pub/software/scm/git/docs/git-merge.html" target="new">
git-merge</a> 等文档。
</p>
</div>
<!-- Undo and Redo
************************************************************************************************-->
<h2>逆转与恢复:git-reset</h2>
<div class="sectionbody">
项目跟踪工具的一个重要任务之一,就是使我们能够随时逆转(Undo)和恢复(Redo)某一阶段的工作。
<p>
<a href="http://www.kernel.org/pub/software/scm/git/docs/git-reset.html" target="new">git-reset
</a> 命令就是为这样的任务准备的。
它将当前的工作分支的 <em>头</em> 定位到以前提交的任何版本中,它有三个重置的算法选项。
</p>
<h4>命令形式:</h4>
<p>
<strong>
git-reset [--mixed | --soft | --hard] [<commit-ish>]
</strong>
</p>
<h4>命令的选项:</h4>
<dl>
<dt>
--mixed
</dt>
<dd>
仅是重置索引的位置,而不改变你的工作树中的任何东西(即,文件中的所有变化都会被保留,
也不标记他们为待提交状态),并且提示什么内容还没有被更新了。这个是默认的选项。
</dd>
<dt>
--soft
</dt>
<dd>
既不触动索引的位置,也不改变工作树中的任何内容,我们只是要求这些内容成为一份好的内容
(之后才成为真正的提交内容)。
这个选项使你可以将已经提交的东西重新逆转至“已更新但未提交(Updated but not Check in)”的状态。
就像已经执行过 <tt>git-update-index</tt> 命令,但是还没有执行 <tt>git-commit</tt> 命令一样。
</dd>
<dt>
--hard
</dt>
<dd>
将工作树中的内容和头索引都切换至指定的版本位置中,也就是说自 <commit-ish>
之后的所有的跟踪内容和工作树中的内容都会全部丢失。
因此,这个选项要慎用,除非你已经非常确定你的确不想再看到那些东西了。
</dd>
</dl>
</div>
<div class="sectionbody">
<h3>一个重要技巧--逆转提交与恢复</h3>
<p>
可能有人会问,--soft 选项既不重置头索引的位置,也不改变工作树中的内容,
那么它有什么用呢?现在我们介绍一个 --soft 选项的使用技巧。
下面我们用例子来说明:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ git-checkout master
$ git-checkout -b softreset
$ git-show-branch
</tt></pre>
</div>
</div>
<p>
这里我们创建了一个 <tt>master</tt> 的拷贝分支 <tt>softreset</tt>,
现在我们可以看到两个分支是在同一起跑线上的。
</p>
<div class="listingblock">
<div class="content">
<pre>! [master] Merge branch 'robin'
! [robin] some work
* [softreset] Merge branch 'robin'
---
- - [master] Merge branch 'robin'
+ * [master^] Some fun
++* [robin] some work
</pre>
</div>
</div>
<p>
我们为 <tt></tt> 文件增加一些内容并提交。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ echo "Botch, botch, botch" >> hello
$ git-commit -a -m "some botch"
$ git-show-branch
</tt></pre>
</div>
</div>
<p>
我们可以看到此时 <tt>softreset</tt> 比 <tt>master</tt> 推进了一个版本 "some botch" 。
</p>
<div class="listingblock">
<div class="content">
<pre>! [master] Merge branch 'robin'
! [robin] some work
* [softreset] some botch
---
* [softreset] some botch
- - [master] Merge branch 'robin'
+ * [master^] Some fun
++* [robin] some work
</pre>
</div>
</div>
<p>
现在让我们来考虑这样的一种情况,假如我们现在对刚刚提交的内容不满意,
那么我们再编辑项目的内容,再提交的话,那么 "some botch" 的内容就会留在版本库中了。
我们当然不希望将有明显问题的内容留在版本库中,这个时候 <tt>--soft</tt> 选项就很有用了。
为了深入了解 <tt>--soft</tt> 的机制,我们看看现在 softreset 分支的头和 ORIG_HEAD 保存的索引。
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ cat .git/refs/heads/softreset .git/ORIG_HEAD
</tt></pre>
</div>
</div>
<p>
结果如下:
</p>
<div class="listingblock">
<div class="content">
<pre>5e7cf906233e052bdca8c598cad2cb5478f9540a
7bbd1370e2c667d955b6f6652bf8274efdc1fbd3
</pre>
</div>
</div>
<p>
现在用 <tt>--soft</tt> 选项逆转刚才提交的内容:
</p>
<div class="listingblock">
<div class="content">
<pre><tt>git-reset --soft HEAD^
</tt></pre>
</div>
</div>
<p>
现在让我们再看看 .git/ORIG_HEAD 的中保存了什么?
</p>
<div class="listingblock">
<div class="content">
<pre><tt>$ cat .git/ORIG_HEAD
</tt></pre>
</div>
</div>
<p>
结果如下:
</p>
<div class="listingblock">
<div class="content">
<pre>5e7cf906233e052bdca8c598cad2cb5478f9540a
</pre>
</div>
</div>
<p>
看!现在的 .git/ORIG_HEAD 等于逆转前的 .git/refs/heads/softreset 。
也就是说,<tt>git-reset --soft HEAD^</tt> 命令逆转了刚才提交的版本进度,
但是它将那次提交的对象的索引拷贝到了 .git/ORIG_HEAD 中。
</p>
<p>
我们再编辑 hello 文件成为下面的内容:
</p>
<div class="listingblock">