-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
2422 lines (1193 loc) · 223 KB
/
index.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>
<html class="theme-next pisces use-motion" lang="zh-Hans">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
<link href="/css/main.css?v=5.1.2" rel="stylesheet" type="text/css" />
<meta name="keywords" content="Hexo, NexT" />
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.2" />
<meta name="description" content="平凡的人生千篇一律,而不凡的人生万里挑一。">
<meta property="og:type" content="website">
<meta property="og:title" content="Louis - 东篱之下">
<meta property="og:url" content="https://louismelo.github.io/index.html">
<meta property="og:site_name" content="Louis - 东篱之下">
<meta property="og:description" content="平凡的人生千篇一律,而不凡的人生万里挑一。">
<meta property="og:locale" content="zh-Hans">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Louis - 东篱之下">
<meta name="twitter:description" content="平凡的人生千篇一律,而不凡的人生万里挑一。">
<script type="text/javascript" id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Pisces',
version: '5.1.2',
sidebar: {"position":"left","display":"post","offset":12,"offset_float":12,"b2t":false,"scrollpercent":false,"onmobile":false},
fancybox: true,
tabs: true,
motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn"}},
duoshuo: {
userId: '0',
author: '博主'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
</script>
<link rel="canonical" href="https://louismelo.github.io/"/>
<title>Louis - 东篱之下</title>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div class="container sidebar-position-left
page-home">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-wrapper">
<div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">Louis - 东篱之下</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<p class="site-subtitle"></p>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section">
<i class="menu-item-icon fa fa-fw fa-home"></i> <br />
首页
</a>
</li>
<li class="menu-item menu-item-categories">
<a href="/categories/" rel="section">
<i class="menu-item-icon fa fa-fw fa-th"></i> <br />
分类
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section">
<i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
归档
</a>
</li>
</ul>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<section id="posts" class="posts-expand">
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://louismelo.github.io/2018/02/09/React笔记-State-and-Lifecycle/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Louis Melo">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.png">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Louis - 东篱之下">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/02/09/React笔记-State-and-Lifecycle/" itemprop="url">React笔记 - State and Lifecycle</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-02-09T22:30:42+08:00">
2018-02-09
</time>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="State-and-Lifecycle"><a href="#State-and-Lifecycle" class="headerlink" title="State and Lifecycle"></a>State and Lifecycle</h1><p>直到现在,我们只学会了一种更新 UI 的方法,调用<code>ReactDOM.render()</code>来改变渲染出的组件。</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">tick</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">const</span> element = (</div><div class="line"> <div></div><div class="line"> <h1>Hello, world!<span class="xml"><span class="tag"></<span class="name">h1</span>></span></span></div><div class="line"> <h2>It is {<span class="keyword">new</span> <span class="built_in">Date</span>().toLocaleTimeString()}.<<span class="regexp">/h2></span></div><div class="line"><span class="regexp"> </</span>div></div><div class="line"> );</div><div class="line"> ReactDOM.render(</div><div class="line"> element,</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'root'</span>)</div><div class="line"> );</div><div class="line">}</div><div class="line"></div><div class="line">setInterval(tick, <span class="number">1000</span>);</div></pre></td></tr></table></figure>
<p>在这一节当中,我们将学习如何真正的封装<code>Clock</code>组件,并且让它变得可以重复使用。</p>
<p>我们可以先把 clock 的样子封装一下,先将它的 DOM 结构构造出来:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">Clock</span>(<span class="params">props</span>) </span>{</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <div></div><div class="line"> <h1>Hello, world!<span class="xml"><span class="tag"></<span class="name">h1</span>></span></span></div><div class="line"> <h2>It is {props.date.toLocaleTimeString()}.<<span class="regexp">/h2></span></div><div class="line"><span class="regexp"> </</span>div></div><div class="line"> );</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">tick</span>(<span class="params"></span>) </span>{</div><div class="line"> ReactDOM.render(</div><div class="line"> <Clock date={<span class="keyword">new</span> <span class="built_in">Date</span>()} />,</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'root'</span>)</div><div class="line"> );</div><div class="line">}</div><div class="line"></div><div class="line">setInterval(tick, <span class="number">1000</span>);</div></pre></td></tr></table></figure></p>
<p>然而,这样的做法遗漏了一个最重要的要求:就是我们需要将<code>Clock</code>设置定时器并更新UI的这些功能都在<code>Clock</code>内部实现,理想状态下,我们只需要写一次<code>Clock</code>组件,它就应该可以在其他地方使用,并且自己更新自己。<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">ReactDOM.render(</div><div class="line"> <Clock />,</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'root'</span>)</div><div class="line">);</div></pre></td></tr></table></figure></p>
<p>为了实现它,我们需要给<code>Clock</code>组件添加”state”。</p>
<blockquote>
<p>State is similar to props, but it is private and fully controlled by the component.<br>state 与 props 相似,但是它是组件私有的,并且完全由组件控制。</p>
</blockquote>
<h2 id="之前提到过,class-component比functional-component要多一些特别的功能,state-就是其中之一。"><a href="#之前提到过,class-component比functional-component要多一些特别的功能,state-就是其中之一。" class="headerlink" title="之前提到过,class component比functional component要多一些特别的功能,state 就是其中之一。"></a>之前提到过,<code>class component</code>比<code>functional component</code>要多一些特别的功能,state 就是其中之一。</h2><h2 id="Converting-a-Function-to-Class"><a href="#Converting-a-Function-to-Class" class="headerlink" title="Converting a Function to Class"></a>Converting a Function to Class</h2><p>通过以下5个步骤,你可以将<code>Clock</code>这样的 function 改写成 class:</p>
<ol>
<li>用同样的名字,创建一个<code>ES6 class</code>,使其继承自<code>React.Component</code></li>
<li>添加一个<code>render()</code>方法</li>
<li>将 function 的主体全部复制到<code>render()</code>方法中</li>
<li>将<code>render()</code>方法中的<code>this.props</code>全部替换成<code>props</code></li>
<li>将之前声明的 function 删掉</li>
</ol>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Clock</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <div></div><div class="line"> <h1>Hello, world!<span class="xml"><span class="tag"></<span class="name">h1</span>></span></span></div><div class="line"> <h2>It is {<span class="keyword">this</span>.props.date.toLocaleTimeString()}.<<span class="regexp">/h2></span></div><div class="line"><span class="regexp"> </</span>div></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="现在,Clock就从一个-function-变成了一个-class,它也具备了local-state和lifecycle-hooks的额外功能。"><a href="#现在,Clock就从一个-function-变成了一个-class,它也具备了local-state和lifecycle-hooks的额外功能。" class="headerlink" title="现在,Clock就从一个 function 变成了一个 class,它也具备了local state和lifecycle hooks的额外功能。"></a>现在,<code>Clock</code>就从一个 function 变成了一个 class,它也具备了<code>local state</code>和<code>lifecycle hooks</code>的额外功能。</h2><h2 id="Adding-Local-State-to-a-Class"><a href="#Adding-Local-State-to-a-Class" class="headerlink" title="Adding Local State to a Class"></a>Adding Local State to a Class</h2><p>通过以下的三个步骤,我们可以将<code>date</code>从 props 中移动到 state 里:</p>
<ol>
<li><p>将<code>render()</code>方法中的<code>this.props.date</code>替换为<code>this.state.date</code></p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Clock</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <div></div><div class="line"> <h1>Hello, world!<span class="xml"><span class="tag"></<span class="name">h1</span>></span></span></div><div class="line"> <h2>It is {<span class="keyword">this</span>.state.date.toLocaleTimeString()}.<<span class="regexp">/h2></span></div><div class="line"><span class="regexp"> </</span>div></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
<li><p>添加一个<code>class constructor</code>指定初始时的<code>this.state</code>:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Clock</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(props) {</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> <span class="keyword">this</span>.state = {<span class="attr">date</span>: <span class="keyword">new</span> <span class="built_in">Date</span>()};</div><div class="line"> }</div><div class="line"></div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <div></div><div class="line"> <h1>Hello, world!<span class="xml"><span class="tag"></<span class="name">h1</span>></span></span></div><div class="line"> <h2>It is {<span class="keyword">this</span>.state.date.toLocaleTimeString()}.<<span class="regexp">/h2></span></div><div class="line"><span class="regexp"> </</span>div></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
</li>
</ol>
<p>这里要注意我们是如何把<code>props</code>传递给父级构造函数的:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">constructor</span>(props) {</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> <span class="keyword">this</span>.state = {<span class="attr">date</span>: <span class="keyword">new</span> <span class="built_in">Date</span>()};</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>class components 必须调用父级构造函数,并且将<code>props</code>作为参数传递给它。</p>
<ol>
<li>去掉<code>Clock</code>元素的<code>date</code>属性<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">ReactDOM.render(</div><div class="line"> <Clock />,</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'root'</span>)</div><div class="line">);</div></pre></td></tr></table></figure>
</li>
</ol>
<p>我们等会再加上定时器的相关代码,现在,代码应该像是这样的:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Clock</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(props) {</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> <span class="keyword">this</span>.state = {<span class="attr">date</span>: <span class="keyword">new</span> <span class="built_in">Date</span>()};</div><div class="line"> }</div><div class="line"></div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <div></div><div class="line"> <h1>Hello, world!<span class="xml"><span class="tag"></<span class="name">h1</span>></span></span></div><div class="line"> <h2>It is {<span class="keyword">this</span>.state.date.toLocaleTimeString()}.<<span class="regexp">/h2></span></div><div class="line"><span class="regexp"> </</span>div></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">ReactDOM.render(</div><div class="line"> <Clock />,</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'root'</span>)</div><div class="line">);</div></pre></td></tr></table></figure></p>
<h2 id="下面,我们将让Clock元素自己设定一个定时器,并且每秒钟更新一次。"><a href="#下面,我们将让Clock元素自己设定一个定时器,并且每秒钟更新一次。" class="headerlink" title="下面,我们将让Clock元素自己设定一个定时器,并且每秒钟更新一次。"></a>下面,我们将让<code>Clock</code>元素自己设定一个定时器,并且每秒钟更新一次。</h2><h2 id="Adding-Lifecycle-Methods-to-a-Class"><a href="#Adding-Lifecycle-Methods-to-a-Class" class="headerlink" title="Adding Lifecycle Methods to a Class"></a>Adding Lifecycle Methods to a Class</h2><p>在有许多的组件构成的应用中,有一点是很重要的,就是能在组件被销毁时释放它们所占用的资源。我们想要在每次<code>Clock</code>被渲染到DOM的时候,启动一个定时器,这在 React 中被称为”mounting”。同样,在由<code>Clock</code>渲染的DOM被移除时,我们也需要清除掉这个定时器,这在 React 中被称为”unmounting”。<br>在组件的类中,我们可以声明特殊的方法,这些代码会在组件”mount”和”unmount”时被调用。<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Clock</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(props) {</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> <span class="keyword">this</span>.state = {<span class="attr">date</span>: <span class="keyword">new</span> <span class="built_in">Date</span>()};</div><div class="line"> }</div><div class="line"></div><div class="line"> componentDidMount() {</div><div class="line"> <span class="comment">// runs after the component output has been rendered to the DOM</span></div><div class="line"> }</div><div class="line"></div><div class="line"> componentWillUnmount() {</div><div class="line"> <span class="comment">// runs after the component has been removed from DOM</span></div><div class="line"> }</div><div class="line"></div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <div></div><div class="line"> <h1>Hello, world!<span class="xml"><span class="tag"></<span class="name">h1</span>></span></span></div><div class="line"> <h2>It is {<span class="keyword">this</span>.state.date.toLocaleTimeString()}.<<span class="regexp">/h2></span></div><div class="line"><span class="regexp"> </</span>div></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这些方法称为”lifecycle hooks”,<code>componentDidMount()</code>方法会在组件被渲染到DOM后执行,在这个方法内设置定时器是很棒的:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">componentDidMount() {</div><div class="line"> <span class="keyword">this</span>.timerID = setInterval(</div><div class="line"> () => <span class="keyword">this</span>.tick(),</div><div class="line"> <span class="number">1000</span></div><div class="line"> );</div><div class="line"> }</div></pre></td></tr></table></figure></p>
<p>这里将<code>setInterval()</code>方法返回的 int 值存储在<code>this.timerID</code>中,方便在后面 unmount 的时候,对其使用<code>clearInterval()</code>方法。</p>
<blockquote>
<p>While this.props is set up by React itself and this.state has a special meaning, you are free to add additional fields to the class manually if you need to store something that is not used for the visual output.</p>
<p>If you don’t use something in render(), it shouldn’t be in the state.</p>
</blockquote>
<p>在<code>componentWillUnmount()</code>方法中,销毁掉定时器:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">componentWillUnmount() {</div><div class="line"> clearInterval(<span class="keyword">this</span>.timerID);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>最后,我们要实现<code>tick()</code>方法:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">tick() {</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> date: <span class="keyword">new</span> <span class="built_in">Date</span>()</div><div class="line"> });</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>这个方法会调用<code>this.setState()</code>方法,来安排更新组件的state。<br>好,我们现在再来回顾一下刚才发生了什么,以及方法被调用的顺序:</p>
<ol>
<li>当<code><Clock /></code>元素被传递给<code>React.render()</code>方法时,React 会调用<code>Clock</code>组件的构造函数,由于<code>Clock</code>需要展示当前的时间,所以在<code>Clock</code>的构造函数中,<code>this.state</code>初始化了一个包含当前时间的对象,然后我们对它进行更新;</li>
<li>React 接着调用<code>Clock</code>组件的<code>render()</code>方法,这使得 React 知道要在屏幕上展示什么东西。接下来 React 会根据<code>Clock</code>的<code>render()</code>方法来更新 DOM;</li>
<li>当<code>Clock</code>的输出被插入到 DOM 中时,React 会调用<code>componentDidMount()</code>方法,在这个方法内部,<code>Clock</code>向浏览器请求设置一个定时器,每秒调用一次组件中的<code>tick()</code>方法;</li>
<li>每隔一秒钟,浏览器就会调用<code>tick()</code>方法,在这个方法内部,<code>Clock</code>组件通过调用包含当前时间对象的<code>setState()</code>方法来对 UI 进行更新。多亏了<code>setState()</code>方法,React 才能知道组件的状态已经改变了,这时会再次调用<code>render()</code>方法,来确定需要在屏幕上展示出什么。这次,<code>render()</code> 方法中的<code>this.state.date</code>已经不同了,所以渲染出的结果将会包含更新过的时间,React 会对 DOM 进行相对应的更新;</li>
<li>如果<code>Clock</code>组件从 DOM 中被移除了,React 会调用<code>componentWillUnmount()</code>方法来将定时器停止。</li>
</ol>
<hr>
<h2 id="正确使用-State"><a href="#正确使用-State" class="headerlink" title="正确使用 State"></a>正确使用 State</h2><p>对于<code>setState()</code>方法,你需要知道以下三件事情:</p>
<h3 id="不要直接修改-State"><a href="#不要直接修改-State" class="headerlink" title="不要直接修改 State"></a>不要直接修改 State</h3><p>举个例子,下面的代码不会对组件进行重新渲染:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Wrong</span></div><div class="line"><span class="keyword">this</span>.state.comment = <span class="string">'Hello'</span>;</div></pre></td></tr></table></figure>
<p>正确的方法应该是,使用<code>setState()</code>方法对其进行修改:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Correct</span></div><div class="line"><span class="keyword">this</span>.setState({<span class="attr">comment</span>: <span class="string">'Hello'</span>});</div></pre></td></tr></table></figure>
<p>唯一能够对<code>this.state</code>进行赋值的地方是构造函数,除此之外,都不可以。</p>
<h3 id="State-的更新有可能是异步的"><a href="#State-的更新有可能是异步的" class="headerlink" title="State 的更新有可能是异步的"></a>State 的更新有可能是异步的</h3><p>出于性能方面的考虑,React 有可能将多个<code>setState()</code>请求整合成单独一次的更新。由于<code>this.props</code>和<code>this.state</code>都可能被异步更新,所以不要依赖它们的值来计算下一个 state。举例来说,下面的代码可能会导致计数器更新失败:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Wrong</span></div><div class="line"><span class="keyword">this</span>.setState({</div><div class="line"> counter: <span class="keyword">this</span>.state.counter + <span class="keyword">this</span>.props.increment</div><div class="line">});</div></pre></td></tr></table></figure>
<p>要改正的话,需要用到第二种形式的<code>setState()</code>方法, 这个方法会接收函数作为参数,而不是以对象作为参数。这个<strong>匿名方法</strong>会接收上一个 state 作为第一个参数,将更新进行时的 props 作为第二个参数:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Correct</span></div><div class="line"><span class="keyword">this</span>.setState(<span class="function">(<span class="params">prevState, props</span>) =></span> ({</div><div class="line"> counter: prevState.counter + props.increment</div><div class="line">}));</div></pre></td></tr></table></figure>
<p>上面使用了<code>arrow function</code>,其实使用常规的函数也是一样的:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="comment">// Correct</span></div><div class="line"><span class="keyword">this</span>.setState(<span class="function"><span class="keyword">function</span>(<span class="params">prevState, props</span>)</span>{</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> counter: prevState.counter + props.increment</div><div class="line"> };</div><div class="line">});</div></pre></td></tr></table></figure>
<h3 id="State-Updates-are-Merged"><a href="#State-Updates-are-Merged" class="headerlink" title="State Updates are Merged"></a>State Updates are Merged</h3><p>当你调用<code>setState()</code>方法的时候,React 会将你提供的对象并入(merge into)当前的状态中。举个例子,你的 state 可能会包含多个独立的变量:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">constructor</span>(props) {</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> <span class="keyword">this</span>.state = {</div><div class="line"> posts: [],</div><div class="line"> comments: []</div><div class="line"> };</div><div class="line">}</div></pre></td></tr></table></figure>
<p>然后,你可以分开调用<code>setState()</code>方法对它们单独进行更新:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">componentDidMount() {</div><div class="line"> fetchPosts().then(<span class="function"><span class="params">response</span> =></span> {</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> posts: response.posts</div><div class="line"> });</div><div class="line"> });</div><div class="line"> </div><div class="line"> fetchComments().then(<span class="function"><span class="params">response</span> =></span> {</div><div class="line"> <span class="keyword">this</span>.setState({</div><div class="line"> comments: response.comments</div><div class="line"> });</div><div class="line"> });</div><div class="line">}</div></pre></td></tr></table></figure>
<p>The merging is shallow,所以<code>this.setState({comments})</code>会保持<code>this.state.posts</code>的完整,并且整个替换掉<code>this.state.comments</code>。</p>
<hr>
<h2 id="The-Data-Flows-Down"><a href="#The-Data-Flows-Down" class="headerlink" title="The Data Flows Down"></a>The Data Flows Down</h2><p>不管是父级组件还是子级组件,都不能知道一个特定的组件到底是有状态的还是无状态的,它们也不应该关心它是定义成一个函数还是一个类。这就是为什么 state 经常被称作是<code>本地的</code>或者是<code>被封装的</code>,除了自己,别的组件是不能对其进行访问的。组件可以选择将自己的 state 作为 props 传递给它的子级组件:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><h2>It is {<span class="keyword">this</span>.state.date.toLocaleTimeString()}.<<span class="regexp">/h2></span></div></pre></td></tr></table></figure>
<p>对于用户定义的组件,也是可以的:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><FormattedDate date={<span class="keyword">this</span>.state.date} /></div></pre></td></tr></table></figure>
<p><code>FormattedDate</code>组件将会接收到一个包含在其 props 中的<code>date</code>对象,它不知道这个对象是来自<code>Clock</code>的 state,还是<code>Clock</code>的 props,或者是硬编码进去的:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">FormattedDate</span>(<span class="params">props</span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag"><<span class="name">h2</span>></span>It is {props.date.toLocaleTimeString}.<span class="tag"></<span class="name">h2</span>></span></span>;</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这通常被称作“自顶向下的”或“单向的”数据流,任何的 state 都永远是为某些特定的 component 所有的,并且任何的从那个 state 处得到的数据或者 UI 只能影响它们“下面的”组件。</p>
<blockquote>
<p>如果你把一个组件树想象成由 props 构成的瀑布的花,每个组件的 state 就像一个额外的水源,它在某个任意的点上汇入,但是也将“顺流而下”。</p>
</blockquote>
<p>为了展示所有的组件都是真正独立的,我们可以创建一个<code>App</code>组件来渲染3个<code><Clock /></code>:</p>
<figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">App</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <div></div><div class="line"> <Clock /></div><div class="line"> <Clock /></div><div class="line"> <Clock /></div><div class="line"> <<span class="regexp">/div></span></div><div class="line"><span class="regexp"> );</span></div><div class="line"><span class="regexp">}</span></div><div class="line"><span class="regexp"></span></div><div class="line"><span class="regexp">ReactDOM.render(</span></div><div class="line"><span class="regexp"> <App /</span>>,</div><div class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'root'</span>)</div><div class="line">);</div></pre></td></tr></table></figure>
<p>这样,每个<code>Clock</code>都会单独地设置自己的定时器并且更新。</p>
<p>在 React apps 中,考虑一个组件是有状态的还是无状态的,涉及到一个实现的细节,即这个组件是否会<code>随着时间而发生变化</code>。你既可以在有状态的组件中使用无状态的组件,也可以在无状态的组件中,使用有状态的组件,这都是可以的。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://louismelo.github.io/2018/01/30/Git-分支管理/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Louis Melo">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.png">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Louis - 东篱之下">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/01/30/Git-分支管理/" itemprop="url">Git-分支管理</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-01-30T20:36:18+08:00">
2018-01-30
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/Git/" itemprop="url" rel="index">
<span itemprop="name">Git</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="Git-分支管理"><a href="#Git-分支管理" class="headerlink" title="Git 分支管理"></a>Git 分支管理</h1><p>分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN。</p>
<p>如果两个平行宇宙互不干扰,那对现在的你也没啥影响。不过,在某个时间点,两个平行宇宙合并了,结果,你既学会了Git又学会了SVN!<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384908633976bb65b57548e64bf9be7253aebebd49af000/0" alt="分支管理"><br>分支在实际中有什么用呢?假设你准备开发一个新功能,但是需要两周才能完成,第一周你写了50%的代码,如果立刻提交,由于代码还没写完,不完整的代码库会导致别人不能干活了。如果等代码全部写完再一次提交,又存在丢失每天进度的巨大风险。</p>
<p>现在有了分支,就不用怕了。你创建了一个属于你自己的分支,别人看不到,还继续在原来的分支上正常工作,而你在自己的分支上干活,想提交就提交,直到开发完毕后,再一次性合并到原来的分支上,这样,既<code>安全</code>,又<code>不影响别人工作</code>。</p>
<p>其他版本控制系统如SVN等都有分支管理,但是用过之后你会发现,这些版本控制系统创建和切换分支比蜗牛还慢,简直让人无法忍受,结果分支功能成了摆设,大家都不去用。</p>
<p>但Git的分支是与众不同的,无论创建、切换和删除分支,Git在1秒钟之内就能完成!无论你的版本库是1个文件还是1万个文件。</p>
<h2 id="创建与合并分支"><a href="#创建与合并分支" class="headerlink" title="创建与合并分支"></a>创建与合并分支</h2><hr>
<p>在每次提交后,Git都把各个版本串成一条时间线,这条时间线就是一个分支。截止到目前,只有一条时间线,在Git里,这个分支叫主分支,即<code>master</code>分支。<code>HEAD</code>严格来说不是指向提交,而是指向<code>master</code>,<code>master</code>才是指向提交的,所以,<code>HEAD</code>指向的就是当前分支。</p>
<p>一开始的时候,<code>master</code>分支是一条线,Git用<code>master</code>指向最新的提交,再用<code>HEAD</code>指向<code>master</code>,就能确定当前分支,以及当前分支的提交点:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/0013849087937492135fbf4bbd24dfcbc18349a8a59d36d000/0" alt="master"><br>每次提交,<code>master</code>分支都会向前移动一步,这样,随着你不断提交,<code>master</code>分支的线也越来越长。</p>
<p>当我们创建新的分支,例如<code>dev</code>时,Git新建了一个指针叫<code>dev</code>,指向m<code>aster</code>相同的提交,再把<code>HEAD</code>指向<code>dev</code>,就表示当前分支在<code>dev</code>上:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384908811773187a597e2d844eefb11f5cf5d56135ca000/0" alt="dev"><br>你看,Git创建一个分支很快,因为除了增加一个dev指针,改改HEAD的指向,工作区的文件都没有任何变化!</p>
<p>不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/0013849088235627813efe7649b4f008900e5365bb72323000/0" alt="new dev"><br>假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/00138490883510324231a837e5d4aee844d3e4692ba50f5000/0" alt="合并"><br>所以Git合并分支也很快!就改改指针,工作区内容也不变!</p>
<p>合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384908867187c83ca970bf0f46efa19badad99c40235000/0" alt="删除"></p>
<p>好了,看懂了这些基本的流程,我们就开始实战吧:</p>
<p>首先,我们创建一个名为<code>dev</code>的分支,并切换到<code>dev</code>分支上去:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git checkout -b dev</div><div class="line">Switched to a new branch <span class="string">'dev'</span></div></pre></td></tr></table></figure></p>
<p>这里的<code>git checkout -b dev</code>命令表示的是“创建dev分支并切换到dev分支”,这其实相当于两条命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git branch dev</div><div class="line">$ git checkout dev</div><div class="line">Switched to branch <span class="string">'dev'</span></div></pre></td></tr></table></figure></p>
<p>然后,使用<code>git branch</code>查看分支情况:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git branch</div><div class="line">* dev</div><div class="line"> master</div></pre></td></tr></table></figure></p>
<p>这条命令会返回所有的分支,并且在当前的分支上,会有一颗<code>*</code>号。<br>现在,我们就可以在<code>dev</code>分支上进行正常的提交了,比如我们再在<code>readme.txt</code>文件里加一行内容:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Creating a new branch is quick.</div></pre></td></tr></table></figure></p>
<p>将这个改动提交到版本库中:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git add readme.txt</div><div class="line">$ git commit -m <span class="string">'add new branch'</span></div><div class="line">[dev d19f6e8] add new branch</div><div class="line"> 1 file changed, 2 insertions(+)</div></pre></td></tr></table></figure></p>
<p>OK,现在假设我们在<code>dev</code>分支上的工作已经完成了, 现在我们回到<code>master</code>分支上来:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git checkout master</div><div class="line">Switched to branch <span class="string">'master'</span></div><div class="line">Your branch is up-to-date with <span class="string">'origin/master'</span>.</div></pre></td></tr></table></figure></p>
<p>切换回<code>master</code>分支后,我们可以看到,原先在<code>dev</code>分支上修改过的<code>readme.txt</code>文件并没有产生变化,还是那四句话:<br><img src="http://res.cloudinary.com/louismelo/image/upload/v1517318302/Screen_Shot_2018-01-30_at_9.17.54_PM_ib0lf8.png" alt="切换回master"><br>此时的指针状态应该是这样的:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384908892295909f96758654469cad60dc50edfa9abd000/0" alt="当前指针"><br>现在,我们需要把<code>dev</code>的工作成果合并到<code>master</code>分支上,我们需要<code>git merge</code>命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ git merge dev</div><div class="line">Updating a81ac4f..d19f6e8</div><div class="line">Fast-forward</div><div class="line"> readme.txt | 2 ++</div><div class="line"> 1 file changed, 2 insertions(+)</div></pre></td></tr></table></figure></p>
<p><code>git merge</code>命令用于将指定的分支(dev)合并到当前分支(master)上来,现在我们再看一下<code>readme.txt</code>文件的内容,发现多了一行内容了,已经和<code>dev</code>分支上一模一样了。<br>注意上面返回的信息中的<code>Fast-forward</code>消息,这意味着这次的合并模式是<code>快进模式</code>,就是将<code>master</code>的指针向前移动到了<code>dev</code>的指针上,这样的合并速度就非常快,当然不是每次都是<code>快进模式</code>的合并,还有其他的合并方式。<br>合并完成后,假设我们也不再需要<code>dev</code>分支了,我们可以执行命令,将其删除:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git branch -d dev</div><div class="line">Deleted branch dev (was d19f6e8).</div></pre></td></tr></table></figure></p>
<p>删除后,我们再查看一下版本库中分支的情况:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git branch</div><div class="line">* master</div></pre></td></tr></table></figure></p>
<p>现在,我们就只剩<code>master</code>这一个分支了。</p>
<h2 id="解决冲突"><a href="#解决冲突" class="headerlink" title="解决冲突"></a>解决冲突</h2><hr>
<p>人生不如意之事十之八九,合并分支往往也不是一帆风顺的。</p>
<p>准备新的<code>feature1</code>分支,继续我们的新分支开发:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git checkout -b feature1</div><div class="line">Switched to a new branch <span class="string">'feature1'</span></div></pre></td></tr></table></figure></p>
<p>修改<code>readme.txt</code>文件的最后一行内容:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Creating a new branch is quick AND simple.</div></pre></td></tr></table></figure></p>
<p>现在,提交这个分支的修改:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git add readme.txt</div><div class="line">$ git commit -m <span class="string">'AND simple'</span></div><div class="line">[feature1 e412abc] AND simple</div><div class="line"> 1 file changed, 1 insertion(+), 1 deletion(-)</div></pre></td></tr></table></figure></p>
<p>好的,完成了<code>feature1</code>上的修改,我们现在回到<code>master</code>分支上来:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git checkout master</div><div class="line">Switched to branch <span class="string">'master'</span></div><div class="line">Your branch is ahead of <span class="string">'origin/master'</span> by 1 commit.</div><div class="line"> (use <span class="string">"git push"</span> to publish your <span class="built_in">local</span> commits)</div></pre></td></tr></table></figure></p>
<p>我们再次修改一下<code>readme.txt</code>文件,将最后一行改为:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Creating a new branch is quick & simple.</div></pre></td></tr></table></figure></p>
<p>提交修改:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git add readme.txt</div><div class="line">$ git commit -m <span class="string">'& simple'</span></div><div class="line">[master 54d63e2] & simple</div><div class="line"> 1 file changed, 1 insertion(+), 1 deletion(-)</div></pre></td></tr></table></figure></p>
<p>现在,我们可以思考一下版本的指针状态,现在<code>mater</code>和<code>feature1</code>分支都有了新的提交,所以,应该是这样的:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384909115478645b93e2b5ae4dc78da049a0d1704a41000/0" alt="分支状态"><br>这种情况下,Git无法执行“快速合并”,只能试图把各自的修改合并起来,但这种合并就可能会有冲突,我们试试看:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git merge feature1</div><div class="line">Auto-merging readme.txt</div><div class="line">CONFLICT (content): Merge conflict <span class="keyword">in</span> readme.txt</div><div class="line">Automatic merge failed; fix conflicts and <span class="keyword">then</span> commit the result.</div></pre></td></tr></table></figure></p>
<p>从返回的信息可以看出来,Git告诉我们在合并时,<code>readme.txt</code>文件存在冲突,需要我们解决了冲突之后再提交结果。我们现在用<code>git status</code>看看:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Your branch is ahead of <span class="string">'origin/master'</span> by 2 commits.</div><div class="line"> (use <span class="string">"git push"</span> to publish your <span class="built_in">local</span> commits)</div><div class="line">You have unmerged paths.</div><div class="line"> (fix conflicts and run <span class="string">"git commit"</span>)</div><div class="line"> (use <span class="string">"git merge --abort"</span> to abort the merge)</div><div class="line"></div><div class="line">Unmerged paths:</div><div class="line"> (use <span class="string">"git add <file>..."</span> to mark resolution)</div><div class="line"></div><div class="line"> both modified: readme.txt</div><div class="line"></div><div class="line">no changes added to commit (use <span class="string">"git add"</span> and/or <span class="string">"git commit -a"</span>)</div></pre></td></tr></table></figure></p>
<p>我们现在再看看<code>readme.txt</code>里面的内容:<br><img src="http://res.cloudinary.com/louismelo/image/upload/v1517321020/Screen_Shot_2018-01-30_at_10.03.16_PM_xv7szx.png" alt="readme.txt"><br>卧槽,居然变成这样了,要怎么办呢?<br>可以看到,Git使用了<code><<<<<<<</code>,<code>=======</code>,<code>>>>>>>></code>标记了不同分支的内容,我们现在将其改成如下内容:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">Creating a new branch is quick and simple.</div></pre></td></tr></table></figure></p>
<p>再执行提交操作:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git add readme.txt</div><div class="line">$ git commit -m <span class="string">'and simple'</span></div><div class="line">[master f31c8e4] and simple</div></pre></td></tr></table></figure></p>
<p>现在,<code>master</code>分支和<code>feature1</code>分支变成了下面的样子了:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/00138490913052149c4b2cd9702422aa387ac024943921b000/0" alt="分支状态"><br>用带参数的<code>git log</code>命令,也可以看到合并的情况:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">$ git <span class="built_in">log</span> --graph --pretty=oneline --abbrev-commit</div><div class="line">* f31c8e4 (HEAD -> master) and simple</div><div class="line">|\</div><div class="line">| * e412abc (feature1) AND simple</div><div class="line">* | 54d63e2 & simple</div><div class="line">|/</div><div class="line">* d19f6e8 add new branch</div></pre></td></tr></table></figure></p>
<p>最后,删除<code>feature1</code>分支:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git branch -d feature1</div><div class="line">Deleted branch feature1 (was e412abc).</div></pre></td></tr></table></figure></p>
<h2 id="分支管理策略"><a href="#分支管理策略" class="headerlink" title="分支管理策略"></a>分支管理策略</h2><hr>
<p>通常,合并分支时,如果可能,Git会用<code>Fast forward</code>模式,但这种模式下,删除分支后,会丢掉分支信息。</p>
<p>如果要强制禁用<code>Fast forward</code>模式,Git就会在merge时生成一个新的commit,这样,从分支历史上就可以看出分支信息。</p>
<p>下面我们实战一下<code>--no-ff</code>方式的<code>git merge</code>:</p>
<p>首先,仍然创建并切换<code>dev</code>分支:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git checkout -b dev</div><div class="line">Switched to a new branch 'dev'</div></pre></td></tr></table></figure></p>
<p>对<code>readme.txt</code>修改后,进行提交:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git add readme.txt</div><div class="line">$ git commit -m <span class="string">'add merge'</span></div><div class="line">[dev 5b54fb4] add merge</div><div class="line"> 1 file changed, 1 insertion(+)</div></pre></td></tr></table></figure></p>
<p>然后,我们切回<code>master</code>并且准备合并<code>dev</code>分支,这里要使用<code>--no-ff</code>参数:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">$ git checkout master</div><div class="line">Switched to branch <span class="string">'master'</span></div><div class="line">Your branch is ahead of <span class="string">'origin/master'</span> by 4 commits.</div><div class="line"> (use <span class="string">"git push"</span> to publish your <span class="built_in">local</span> commits)</div><div class="line">$ git merge --no-ff -m <span class="string">'merge with no-ff'</span> dev</div><div class="line">Merge made by the <span class="string">'recursive'</span> strategy.</div><div class="line"> readme.txt | 1 +</div><div class="line"> 1 file changed, 1 insertion(+)</div></pre></td></tr></table></figure></p>
<p>再次使用<code>git log</code>命令,查看一下分支的情况:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">$ git <span class="built_in">log</span> --graph --pretty=oneline --abbrev-commit</div><div class="line">* 097a662 (HEAD -> master) merge with no-ff</div><div class="line">|\</div><div class="line">| * 5b54fb4 (dev) add merge</div><div class="line">|/</div><div class="line">* f31c8e4 and simple</div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>可以看到,不使用<code>Fast forward</code>模式的情况下,分支情况是这样的:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384909222841acf964ec9e6a4629a35a7a30588281bb000/0" alt="分支情况"></p>
<h3 id="分支策略"><a href="#分支策略" class="headerlink" title="分支策略"></a>分支策略</h3><p>分支的功能很强大,所以我们要遵循以下的几个原则来进行分支管理:</p>
<ol>
<li><code>master</code>分支应该只用来进行版本的发布,平时不能在上面工作</li>
<li>干活都应该在<code>dev</code>上面,当需要发布稳定版本的时候,将<code>dev</code>合并到<code>master</code>上去</li>
<li>每个开发的小伙伴都应该从<code>dev</code>上分支出自己的版本,然后时不时地与<code>dev</code>进行合并就行了</li>
</ol>
<p>## </p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://louismelo.github.io/2018/01/29/Git-远程仓库/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Louis Melo">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.png">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Louis - 东篱之下">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/01/29/Git-远程仓库/" itemprop="url">Git-远程仓库</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-01-29T22:11:58+08:00">
2018-01-29
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/Git/" itemprop="url" rel="index">
<span itemprop="name">Git</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="Git-远程仓库"><a href="#Git-远程仓库" class="headerlink" title="Git 远程仓库"></a>Git 远程仓库</h1><p>Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上。怎么分布呢?最早,肯定只有一台机器有一个原始版本库,此后,别的机器可以“克隆”这个原始版本库,而且每台机器的版本库其实都是一样的,并没有主次之分。</p>
<p>实际情况往往是这样,找一台电脑充当服务器的角色,每天24小时开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。</p>
<p>完全可以自己搭建一台运行Git的服务器,不过现阶段,为了学Git先搭个服务器绝对是小题大作。好在这个世界上有个叫GitHub的神奇的网站,从名字就可以看出,这个网站就是提供Git仓库托管服务的,所以,只要注册一个GitHub账号,就可以免费获得Git远程仓库。</p>
<p>在继续阅读后续内容前,请自行注册GitHub账号。由于你的本地Git仓库和GitHub仓库之间的传输是通过SSH加密的,所以,需要一点设置:</p>
<p>第一步:创建SSH Key。打开terminal,创建SSH Key:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ ssh-keygen -t -rsa -C <span class="string">"[email protected]"</span></div></pre></td></tr></table></figure></p>
<p>这里我会将默认的<code>/Users/louis/.ssh/id_rsa</code>改为<code>/Users/louis/.ssh/github_rsa</code>,这里是不需要设置passphrase的,如果不小心设置了的话,可以运行下面的命令进行更改:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ ssh-keygen -p</div></pre></td></tr></table></figure></p>
<p>这里会一步一步要求你修改你的密码的。如果一切顺利的话,可以在用户主目录里找到.ssh目录,里面有<code>github_rsa</code>和<code>github_rsa.pub</code>两个文件,这两个就是SSH Key的秘钥对,<code>github_rsa</code>是私钥,不能泄露出去,<code>github_rsa.pub</code>是公钥,可以放心地告诉任何人。使用下面的命令复制你的公钥:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ pbcopy < ~/.ssh/github_rsa.pub</div></pre></td></tr></table></figure></p>
<p>打开Github,点击头像进入Settings,在左侧选择SSH and GPG Keys:<br><img src="http://res.cloudinary.com/louismelo/image/upload/v1517236361/Screen_Shot_2018-01-29_at_10.28.11_PM_nk5nbs.png" alt="Github"></p>
<p>为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。</p>
<p>当然,GitHub允许你添加多个Key。假定你有若干电脑,你一会儿在公司提交,一会儿在家里提交,只要把每台电脑的Key都添加到GitHub,就可以在每台电脑上往GitHub推送了。</p>
<p>最后友情提示,在GitHub上免费托管的Git仓库,任何人都可以看到喔(但只有你自己才能改)。所以,不要把敏感信息放进去。</p>
<p>如果你不想让别人看到Git库,有两个办法,一个是交点保护费,让GitHub把公开的仓库变成私有的,这样别人就看不见了(不可读更不可写)。另一个办法是自己动手,搭一个Git服务器,因为是你自己的Git服务器,所以别人也是看不见的。这个方法我们后面会讲到的,相当简单,公司内部开发必备。</p>
<p>确保你拥有一个GitHub账号后,我们就即将开始远程仓库的学习。</p>
<h2 id="添加远程库"><a href="#添加远程库" class="headerlink" title="添加远程库"></a>添加远程库</h2><hr>
<p>现在的情景是,你已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作,真是一举多得。</p>
<p>首先,登陆GitHub,然后,在右上角找到“Create a new repo”按钮,创建一个新的仓库:<br><img src="http://res.cloudinary.com/louismelo/image/upload/v1517312422/Screen_Shot_2018-01-30_at_7.39.10_PM_li1bdg.png" alt="创建新仓库"><br>在Repository name填入<code>Learngit</code>,其他保持默认设置,点击“Create repository”按钮,就成功地创建了一个新的Git仓库:<br><img src="http://res.cloudinary.com/louismelo/image/upload/v1517312560/Screen_Shot_2018-01-30_at_7.42.07_PM_pbg5mf.png" alt="新仓库"><br>目前,在GitHub上的这个<code>Learngit</code>仓库还是空的,GitHub告诉我们,可以从这个仓库克隆出新的仓库,也可以把一个已有的本地仓库与之关联,然后,把本地仓库的内容推送到GitHub仓库。</p>
<p>现在,我们根据GitHub的提示,在本地的<code>Learngit</code>仓库下运行命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git remote add origin [email protected]:LouisMelo/Learngit.git</div></pre></td></tr></table></figure></p>
<p>添加后,远程库的名字就是<code>origin</code>,这是Git默认的叫法,也可以改成别的,但是<code>origin</code>这个名字一看就知道是远程库。<br>下面,就可以把本地库的所有内容推送到远程库上:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">$ git push -u origin master</div><div class="line">Counting objects: 27, <span class="keyword">done</span>.</div><div class="line">Delta compression using up to 8 threads.</div><div class="line">Compressing objects: 100% (22/22), <span class="keyword">done</span>.</div><div class="line">Writing objects: 100% (27/27), 2.26 KiB | 0 bytes/s, <span class="keyword">done</span>.</div><div class="line">Total 27 (delta 8), reused 0 (delta 0)</div><div class="line">remote: Resolving deltas: 100% (8/8), <span class="keyword">done</span>.</div><div class="line">To github.com:LouisMelo/Learngit.git</div><div class="line"> * [new branch] master -> master</div><div class="line">Branch master <span class="built_in">set</span> up to track remote branch master from origin.</div></pre></td></tr></table></figure></p>
<p>把本地库的内容推送到远程,用<code>git push</code>命令,实际上是把当前分支master推送到远程。</p>
<p>由于远程库是空的,我们第一次推送<code>master</code>分支时,加上了<code>-u</code>参数,Git不但会把本地的<code>master</code>分支内容推送的远程新的<code>master</code>分支,还会把本地的<code>master</code>分支和远程的<code>master</code>分支关联起来,在以后的推送或者拉取时就可以简化命令。</p>
<p>推送成功后,可以立刻在GitHub页面中看到远程库的内容已经和本地一模一样:<br><img src="http://res.cloudinary.com/louismelo/image/upload/v1517315199/Screen_Shot_2018-01-30_at_8.26.11_PM_u1rcxt.png" alt="远程仓库"><br>好了,从现在开始,如果本地有了新的提交,你就可以通过下面的命令将它推送到Github了:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git push origin master</div></pre></td></tr></table></figure></p>
<p>分布式版本系统的最大好处之一是在本地工作完全不需要考虑远程库的存在,也就是有没有联网都可以正常工作,而SVN在没有联网的时候是拒绝干活的!当有网络的时候,再把本地提交推送一下就完成了同步,真是太方便了!</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>
</article>
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://louismelo.github.io/2018/01/29/Git-时光机穿梭/">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Louis Melo">
<meta itemprop="description" content="">
<meta itemprop="image" content="/images/avatar.png">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Louis - 东篱之下">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
<a class="post-title-link" href="/2018/01/29/Git-时光机穿梭/" itemprop="url">Git-时光机穿梭</a></h1>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建于" itemprop="dateCreated datePublished" datetime="2018-01-29T14:50:36+08:00">
2018-01-29
</time>
</span>
<span class="post-category" >
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-folder-o"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/Git/" itemprop="url" rel="index">
<span itemprop="name">Git</span>
</a>
</span>
</span>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="Git-时光机穿梭"><a href="#Git-时光机穿梭" class="headerlink" title="Git 时光机穿梭"></a>Git 时光机穿梭</h1><p>我们已经可以将文件添加到git仓库了,下面我们试着修改这个文本文件,看看会有什么结果:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software.</div></pre></td></tr></table></figure></p>
<p>修改完成后,保存,然后运行 <code>git status</code> 看看有什么结果:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes not staged <span class="keyword">for</span> commit:</div><div class="line"> (use <span class="string">"git add <file>..."</span> to update what will be committed)</div><div class="line"> (use <span class="string">"git checkout -- <file>..."</span> to discard changes <span class="keyword">in</span> working directory)</div><div class="line"></div><div class="line"> modified: readme.txt</div><div class="line"></div><div class="line">no changes added to commit (use <span class="string">"git add"</span> and/or <span class="string">"git commit -a"</span>)</div></pre></td></tr></table></figure></p>
<p>这样,返回的信息就会告诉我们,现在有文件被修改了,但是还没有被提交(changes not staged for commit)。<br>虽然<code>git status</code>可以告诉我们当前仓库的状况,但是并不能清楚的告诉我们修改了哪些地方。这时候,我们就需要用到<code>git diff</code>命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">$ git diff readme.txt</div><div class="line">diff --git a/readme.txt b/readme.txt</div><div class="line">index 23bc09e..9247db6 100644</div><div class="line">--- a/readme.txt</div><div class="line">+++ b/readme.txt</div><div class="line">@@ -1,2 +1,2 @@</div><div class="line">-Git is version control system.</div><div class="line">+Git is a distributed version control system.</div><div class="line"> Git is free software.</div></pre></td></tr></table></figure></p>
<p>现在,我们知道了readme.txt中只修改了一行内容。所以我们就可以安心的提交更改了:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git add readme.txt</div></pre></td></tr></table></figure></p>
<p>这时候,我们再来看一下git仓库的状态,使用<code>git status</code>命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes to be committed:</div><div class="line"> (use <span class="string">"git reset HEAD <file>..."</span> to unstage)</div><div class="line"></div><div class="line"> modified: readme.txt</div></pre></td></tr></table></figure></p>
<p>这时,系统告诉我们,将要被提交的修改包含了readme.txt文件。<br>现在我们就提交这次修改,使用<code>git commit</code>命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git commit -m <span class="string">'add distributed'</span></div><div class="line">[master 3b15474] add distributed</div><div class="line"> 1 file changed, 1 insertion(+), 1 deletion(-)</div></pre></td></tr></table></figure></p>
<p>提交完成后,我们再使用<code>git status</code>来查看一下仓库的状态:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">nothing to commit, working tree clean</div></pre></td></tr></table></figure></p>
<p>这时,返回的信息告诉我们,没有需要提交的修改,而且工作目录是干净的。</p>
<h2 id="版本回退"><a href="#版本回退" class="headerlink" title="版本回退"></a>版本回退</h2><hr>
<p>现在我们已经学会了如何修改文件并且将修改提交到git仓库了,下面再联系一次:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div></pre></td></tr></table></figure></p>
<p>然后提交修改:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git add readme.txt</div><div class="line">$ git commit -m <span class="string">'append GPL'</span></div><div class="line">[master 9b51472] append GPL</div><div class="line"> 1 file changed, 1 insertion(+), 1 deletion(-)</div></pre></td></tr></table></figure></p>
<p>像这样,你不断对文件进行修改,然后不断提交修改到版本库里,就好比玩RPG游戏时,每通过一关就会自动把游戏状态存盘,如果某一关没过去,你还可以选择读取前一关的状态。有些时候,在打Boss之前,你会手动存盘,以便万一打Boss失败了,可以从最近的地方重新开始。Git也是一样,每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个快照在Git中被称为<code>commit</code>。一旦你把文件改乱了,或者误删了文件,还可以从最近的一个<code>commit</code>恢复,然后继续工作,而不是把几个月的工作成果全部丢失。</p>
<p>好,现在我们回顾一下我们一共有多少个版本了:<br>版本1: add readme file<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Git is a version control system.</div><div class="line">Git is free software.</div></pre></td></tr></table></figure></p>
<p>版本2: add distributed<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software.</div></pre></td></tr></table></figure></p>
<p>版本3: append GPL<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div></pre></td></tr></table></figure></p>
<p>在git中,我们可以使用<code>git log</code>命令来查看历史记录,它可以告诉我们每次都修改了些什么:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">$ git <span class="built_in">log</span></div><div class="line">commit 9b5147211afee2c3b233d2f74de5f4cec710977b (HEAD -> master)</div><div class="line">Author: louis <[email protected]></div><div class="line">Date: Mon Jan 29 16:26:38 2018 +0800</div><div class="line"></div><div class="line"> append GPL</div><div class="line"></div><div class="line">commit 3b1547438f09df37716d770a9919fce34405994f</div><div class="line">Author: louis <[email protected]></div><div class="line">Date: Mon Jan 29 16:21:32 2018 +0800</div><div class="line"></div><div class="line"> add distributed</div><div class="line"></div><div class="line">commit 7b235af1f06c6952dd4cd41eea8c77bb02be5dce</div><div class="line">Author: louis <[email protected]></div><div class="line">Date: Mon Jan 29 14:55:59 2018 +0800</div><div class="line"></div><div class="line"> add readme file</div></pre></td></tr></table></figure></p>
<p>如果觉得输出的信息太多了,可以试着使用<code>git log --pretty=online</code>,这样,就能看到最精简的信息了。</p>
<p>这里的<code>commit 7b235af1f06c6952dd4cd41eea8c77bb02be5dce</code>就是版本号了,和SVN不一样,git的版本号不是1,2,3,4这样递增的数字,而是一个SHA1计算出来的非常大的数字,用16进制表示。为什么<code>commit id</code>需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3,4作为版本号,那肯定就冲突了。</p>
<p>好了,现在我们知道了这些版本的历史记录之后,就可以启动时光穿梭机了。我们准备把readme.txt回退到上一个版本,即”add distributed”的那个版本,需要怎么做呢?<br>首先,必须知道当前版本是哪个版本,在git中,使用<code>HEAD</code>表示当前的版本,上一个版本就是<code>HEAD^</code>,上上个版本就是<code>HEAD^^</code>,但是如果是上100个版本呢?我们可以写成<code>HEAD~100</code>。<br>现在我们要把当前版本”append GPL”回退到上一个版本”add distributed”,我们需要使用到<code>git reset</code>命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git reset --hard HEAD^</div><div class="line">HEAD is now at 3b15474 add distributed</div></pre></td></tr></table></figure></p>
<p><code>--hard</code>的含义,后续会讲到,先来看看<code>readme.txt</code>是否回到了<code>add distributed</code>版本:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ cat readme.txt</div><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software.</div></pre></td></tr></table></figure></p>
<p>可以发现,现在我们确实回到了<code>add distributed</code>那个版本,我们还可以继续回到上一个版本,不过我们先停一下,使用<code>git log</code>查看一下版本记录:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">$ git <span class="built_in">log</span></div><div class="line">commit 3b1547438f09df37716d770a9919fce34405994f (HEAD -> master)</div><div class="line">Author: louis <[email protected]></div><div class="line">Date: Mon Jan 29 16:21:32 2018 +0800</div><div class="line"></div><div class="line"> add distributed</div><div class="line"></div><div class="line">commit 7b235af1f06c6952dd4cd41eea8c77bb02be5dce</div><div class="line">Author: louis <[email protected]></div><div class="line">Date: Mon Jan 29 14:55:59 2018 +0800</div><div class="line"></div><div class="line"> add readme file</div></pre></td></tr></table></figure></p>
<p>什么?!我们的<code>append GPL</code>的版本怎么不见了呢?好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,肿么办?<br>办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺着往上找啊找啊,找到那个<code>append GPL</code>的<code>commit id</code>是9b51472…,于是就可以指定回到未来的某个版本:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ git reset --hard 9b51472</div><div class="line">HEAD is now at 9b51472 append GPL</div></pre></td></tr></table></figure></p>
<p>版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。</p>
<p>再小心翼翼地看看readme.txt的内容:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ cat readme.txt</div><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div></pre></td></tr></table></figure></p>
<p>果然,我胡汉三又回来了。</p>
<p>Git的版本回退速度非常快,因为Git在内部有个指向当前版本的<code>HEAD</code>指针,当你回退版本的时候,Git仅仅是把<code>HEAD从</code>指向<code>append GPL</code>:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384907584977fc9d4b96c99f4b5f8e448fbd8589d0b2000/0" alt="append GPL"><br>改为指向<code>add distributed</code>:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384907594057a873c79f14184b45a1a66b1509f90b7a000/0" alt="add distributed"><br>然后顺便把工作区的文件更新了。所以你让<code>HEAD</code>指向哪个版本号,你就把当前版本定位在哪。</p>
<p>现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢复到新版本怎么办?找不到新版本的<code>commit id</code>怎么办?</p>
<p>在Git中,总是有后悔药可以吃的。当你用<code>$ git reset --hard HEAD^</code>回退到<code>add distributed</code>版本时,再想恢复到<code>append GPL</code>,就必须找到<code>append GPL</code>的<code>commit id</code>。Git提供了一个命令<code>git reflog</code>用来记录你的每一次命令:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ git reflog</div><div class="line">9b51472 (HEAD -> master) HEAD@{0}: reset: moving to 9b51472</div><div class="line">3b15474 HEAD@{1}: reset: moving to HEAD^</div><div class="line">9b51472 (HEAD -> master) HEAD@{2}: commit: append GPL</div><div class="line">3b15474 HEAD@{3}: commit: add distributed</div><div class="line">7b235af HEAD@{4}: commit (initial): add readme file</div></pre></td></tr></table></figure></p>
<p>从返回的信息可以看出来,<code>append GPL</code>的<code>commit id</code>为<code>9b51472</code>。这样,我们就又可以回到未来啦!</p>
<h2 id="工作区和暂存区"><a href="#工作区和暂存区" class="headerlink" title="工作区和暂存区"></a>工作区和暂存区</h2><hr>
<p>Git和其他版本控制系统如SVN的一个不同之处就是有暂存区的概念。</p>
<p>先来看名词解释。</p>
<p>工作区(Working Directory)<br>就是你在电脑里能看到的目录,比如我的<code>learngit</code>文件夹就是一个工作区:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/0013849082162373cc083b22a2049c4a47408722a61a770000/0" alt="工作区"></p>
<p>版本库(Git Repository)<br>工作区有一个隐藏目录<code>.git</code>,这个不算工作区,而是Git的版本库。</p>
<p>Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支<code>master</code>,以及指向<code>master</code>的一个指针叫<code>HEAD</code>。<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384907702917346729e9afbf4127b6dfbae9207af016000/0" alt="版本库"><br>分支和<code>HEAD</code>的概念我们以后再讲。</p>
<p>前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:</p>
<p>第一步是用<code>git add</code>把文件添加进去,实际上就是把文件修改添加到暂存区;</p>
<p>第二步是用<code>git commit</code>提交更改,实际上就是把暂存区的所有内容提交到当前分支。</p>
<p>因为我们创建Git版本库时,Git自动为我们创建了唯一一个<code>master</code>分支,所以,现在,<code>git commit</code>就是往<code>master</code>分支上提交更改。</p>
<p>你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,一次性提交暂存区的所有修改。</p>
<p>俗话说,实践出真知。现在,我们再练习一遍,先对<code>readme.txt</code>做个修改,比如加上一行内容:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div><div class="line">Git has a mutable index called stage.</div></pre></td></tr></table></figure></p>
<p>然后,在工作区新增一个<code>LICENSE</code>文件:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ touch LICENSE</div><div class="line">$ nano LICENSE</div></pre></td></tr></table></figure></p>
<p>现在,我们用<code>git status</code>查看一下仓库的状态:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes not staged <span class="keyword">for</span> commit:</div><div class="line"> (use <span class="string">"git add <file>..."</span> to update what will be committed)</div><div class="line"> (use <span class="string">"git checkout -- <file>..."</span> to discard changes <span class="keyword">in</span> working directory)</div><div class="line"></div><div class="line"> modified: readme.txt</div><div class="line"></div><div class="line">Untracked files:</div><div class="line"> (use <span class="string">"git add <file>..."</span> to include <span class="keyword">in</span> what will be committed)</div><div class="line"></div><div class="line"> LICENSE</div><div class="line"></div><div class="line">no changes added to commit (use <span class="string">"git add"</span> and/or <span class="string">"git commit -a"</span>)</div></pre></td></tr></table></figure></p>
<p>返回的信息清楚的告诉了我们,<code>readme.txt</code>被修改了,而<code>LICENSE</code>是第一次出现,所以状态是<code>Untracked</code>。</p>
<p>现在,使用两次<code>git add</code>,把两个文件都添加后,再使用<code>git status</code>查看一下:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes to be committed:</div><div class="line"> (use <span class="string">"git reset HEAD <file>..."</span> to unstage)</div><div class="line"></div><div class="line"> new file: LICENSE</div><div class="line"> modified: readme.txt</div></pre></td></tr></table></figure></p>
<p>现在,暂存区的状态就变成这样了:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/001384907720458e56751df1c474485b697575073c40ae9000/0" alt="暂存区"><br>所以,<code>git add</code>命令实际上就是把要提交的所有修改放到暂存区(Stage),然后,执行<code>git commit</code>就可以一次性把暂存区的所有修改提交到分支。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">$ git commit -m <span class="string">"understand how stage works"</span></div><div class="line">[master 2e3b6fb] understand how stage works</div><div class="line"> 2 files changed, 2 insertions(+)</div><div class="line"> create mode 100644 LICENSE</div></pre></td></tr></table></figure></p>
<p>一旦提交成功后,这时你的工作区就是干净的了:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">nothing to commit, working tree clean</div></pre></td></tr></table></figure></p>
<p>现在,版本库就变成了这样:<br><img src="https://cdn.liaoxuefeng.com/cdn/files/attachments/0013849077337835a877df2d26742b88dd7f56a6ace3ecf000/0" alt="版本库"></p>
<h2 id="管理修改"><a href="#管理修改" class="headerlink" title="管理修改"></a>管理修改</h2><hr>
<p>现在,我们已经掌握了暂存区的概念。接下来,我们讨论一下,为什么Git比其他版本控制系统设计的优秀,因为Git跟踪并管理的是修改,而非文件。<br>那什么是修改呢?比如你新增了一行,这就是一个修改;删除了一行,也是一个修改;更改了某些字符,也是一个修改;删了一些又加了一些,也是一个修改;甚至创建一个新文件,也算一个修改。<br>为什么说Git管理的是修改,而不是文件呢?我们还是做实验。第一步,对readme.txt做一个修改,比如加一行内容:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ cat readme.text</div><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div><div class="line">Git has a mutable index called stage.</div><div class="line">Git tracks changes.</div></pre></td></tr></table></figure></p>
<p>然后,添加到暂存区:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">$ git add readme.txt</div><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes to be committed:</div><div class="line"> (use <span class="string">"git reset HEAD <file>..."</span> to unstage)</div><div class="line"></div><div class="line"> modified: readme.txt</div></pre></td></tr></table></figure></p>
<p>然后,再次修改<code>readme.txt</code>:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ cat readme.txt </div><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div><div class="line">Git has a mutable index called stage.</div><div class="line">Git tracks changes of files.</div></pre></td></tr></table></figure></p>
<p>提交:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git commit -m <span class="string">'git tracks changes'</span></div><div class="line">[master 43daa3e] git tracks changes</div><div class="line"> 1 file changed, 1 insertion(+)</div></pre></td></tr></table></figure></p>
<p>提交后,再看看状态:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes not staged <span class="keyword">for</span> commit:</div><div class="line"> (use <span class="string">"git add <file>..."</span> to update what will be committed)</div><div class="line"> (use <span class="string">"git checkout -- <file>..."</span> to discard changes <span class="keyword">in</span> working directory)</div><div class="line"></div><div class="line"> modified: readme.txt</div><div class="line"></div><div class="line">no changes added to commit (use <span class="string">"git add"</span> and/or <span class="string">"git commit -a"</span>)</div></pre></td></tr></table></figure></p>
<p>为什么这里第二次的修改没有被提交呢?<br>我们来回顾一下整个操作过程:<br>第一次修改 -> <code>git add</code> -> 第二次修改 -> <code>git commit</code><br>我们前面讲了,Git管理的是修改,当你使用<code>git add</code>命令后,在工作区的第一次修改被放入暂存区,准备提交,但是,在工作区的第二次修改并没有放入暂存区,所以,<code>git commit</code>只负责把暂存区的修改提交了,也就是第一次的修改被提交了,第二次的修改不会被提交。<br>提交后,用<code>git diff HEAD -- readme.txt</code>命令可以查看工作区和版本库里面最新版本的区别:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">$ git diff HEAD -- readme.txt</div><div class="line">diff --git a/readme.txt b/readme.txt</div><div class="line">index 76d770f..a9c5755 100644</div><div class="line">--- a/readme.txt</div><div class="line">+++ b/readme.txt</div><div class="line">@@ -1,4 +1,4 @@</div><div class="line"> Git is a distributed version control system.</div><div class="line"> Git is free software distributed under the GPL.</div><div class="line"> Git has a mutable index called stage.</div><div class="line">-Git tracks changes.</div><div class="line">+Git tracks changes of files.</div></pre></td></tr></table></figure></p>
<p>可见,第二次修改确实没有被提交。那怎么才能提交第二次的修改呢?只有继续使用<code>git add</code>将修改提交到暂存区,然后再使用<code>git commit</code>将暂存区的修改提交。现在可以了解到,每一次修改,如果不<code>add</code>到暂存区,那就不会加入到<code>commit</code>中。</p>
<h2 id="撤销修改"><a href="#撤销修改" class="headerlink" title="撤销修改"></a>撤销修改</h2><hr>
<p>自然,你是不会犯错的。不过现在是凌晨两点,你正在赶一份工作报告,你在<code>readme.txt</code>中添加了一行:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ cat readme.txt</div><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div><div class="line">Git has a mutable index called stage.</div><div class="line">Git tracks changes of files.</div><div class="line">My stupid boss still prefers SVN.</div></pre></td></tr></table></figure></p>
<p>在你准备提交前,一杯咖啡起了作用,你猛然发现了“stupid boss”可能会让你丢掉这个月的奖金!</p>
<p>既然错误发现得很及时,就可以很容易地纠正它。你可以删掉最后一行,手动把文件恢复到上一个版本的状态。如果用<code>git status</code>查看一下:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line"> On branch master</div><div class="line"> Changes not staged <span class="keyword">for</span> commit:</div><div class="line"> (use <span class="string">"git add <file>..."</span> to update what will be committed)</div><div class="line"> (use <span class="string">"git checkout -- <file>..."</span> to discard changes <span class="keyword">in</span> working directory)</div><div class="line"></div><div class="line"> modified: readme.txt</div><div class="line"></div><div class="line">no changes added to commit (use <span class="string">"git add"</span> and/or <span class="string">"git commit -a"</span>)</div></pre></td></tr></table></figure></p>
<p>从返回的信息可以发现,你可以使用<code>git checkout -- readme.txt</code>来丢弃工作区的修改:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git checkout -- readme.txt</div></pre></td></tr></table></figure></p>
<p>命令<code>git checkout -- readme.txt</code>意思就是,把<code>readme.txt</code>文件在工作区的修改全部撤销,这里有两种情况:</p>
<p>一种是<code>readme.txt</code>自修改后还没有被放到暂存区,现在,撤销修改就回到和版本库一模一样的状态;</p>
<p>一种是<code>readme.txt</code>已经添加到暂存区后,又作了修改,现在,撤销修改就回到添加到暂存区后的状态。</p>
<p>总之,就是让这个文件回到最近一次<code>git commit</code>或<code>git add</code>时的状态。</p>
<p>现在,看看<code>readme.txt</code>的文件内容:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ cat readme.txt</div><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div><div class="line">Git has a mutable index called stage.</div><div class="line">Git tracks changes of files.</div></pre></td></tr></table></figure></p>
<p>文件内容果然复原了。</p>
<p><code>git checkout -- file</code>命令中的<code>--</code>很重要,没有<code>--</code>,就变成了“切换到另一个分支”的命令,我们在后面的分支管理中会再次遇到<code>git checkout</code>命令。</p>
<p>现在假定是凌晨3点,你不但写了一些胡话,还<code>git add</code>到暂存区了:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">$ cat readme.txt</div><div class="line">Git is a distributed version control system.</div><div class="line">Git is free software distributed under the GPL.</div><div class="line">Git has a mutable index called stage.</div><div class="line">Git tracks changes of files.</div><div class="line">My stupid boss still prefers SVN.</div><div class="line"></div><div class="line">$ git add readme.txt</div></pre></td></tr></table></figure></p>
<p>这时,使用<code>git status</code>查看一下当前的状态:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes to be committed:</div><div class="line"> (use <span class="string">"git reset HEAD <file>..."</span> to unstage)</div><div class="line"></div><div class="line"> modified: readme.txt</div></pre></td></tr></table></figure></p>
<p>Git同样告诉我们,用命令<code>git reset HEAD file</code>可以把暂存区的修改撤销掉(unstage),重新放回工作区:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">$ git reset HEAD readme.txt</div><div class="line">Unstaged changes after reset:</div><div class="line">M readme.txt</div></pre></td></tr></table></figure></p>
<p><code>git reset</code>命令既可以回退版本,也可以把暂存区的修改回退到工作区。当我们用<code>HEAD</code>时,表示最新的版本。</p>
<p>再用<code>git status</code>查看一下,现在暂存区是干净的,工作区有修改:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes not staged <span class="keyword">for</span> commit:</div><div class="line"> (use <span class="string">"git add <file>..."</span> to update what will be committed)</div><div class="line"> (use <span class="string">"git checkout -- <file>..."</span> to discard changes <span class="keyword">in</span> working directory)</div><div class="line"></div><div class="line"> modified: readme.txt</div><div class="line"></div><div class="line">no changes added to commit (use <span class="string">"git add"</span> and/or <span class="string">"git commit -a"</span>)</div></pre></td></tr></table></figure></p>
<p>还记得如何撤销工作区的修改吗?<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ git checkout -- readme.txt</div><div class="line"></div><div class="line">$ git status</div><div class="line"> On branch master</div><div class="line">nothing to commit (working directory clean)</div></pre></td></tr></table></figure></p>
<p>现在,假设你不但改错了东西,还从暂存区提交到了版本库,怎么办呢?还记得版本回退一节吗?可以回退到上一个版本。不过,这是有条件的,就是你还没有把自己的本地版本库推送到远程。还记得Git是分布式版本控制系统吗?我们后面会讲到远程版本库,一旦你把“stupid boss”提交推送到远程版本库,你就真的惨了……</p>
<p>场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令<code>git checkout -- file</code>。</p>
<p>场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步,第一步用命令<code>git reset HEAD file</code>,就回到了场景1,第二步按场景1操作。</p>
<p>场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考版本回退一节,不过前提是没有推送到远程库。</p>
<h2 id="删除文件"><a href="#删除文件" class="headerlink" title="删除文件"></a>删除文件</h2><hr>
<p>在Git中,删除也是一个修改操作,我们实战一下,先添加一个新文件test.txt到Git并且提交:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">$ git add test.txt</div><div class="line">$ git commit -m <span class="string">'add test.txt'</span></div><div class="line">[master e432478] add test.txt</div><div class="line"> 1 file changed, 0 insertions(+), 0 deletions(-)</div><div class="line"> create mode 100644 test.txt</div></pre></td></tr></table></figure></p>
<p>一般情况下,你通常直接在文件管理器中把没用的文件删了,或者用<code>rm</code>命令删了:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ rm test.txt</div></pre></td></tr></table></figure></p>
<p>这个时候,Git知道你删除了文件,因此,工作区和版本库就不一致了,<code>git status</code>命令会立刻告诉你哪些文件被删除了:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">$ git status</div><div class="line">On branch master</div><div class="line">Changes not staged <span class="keyword">for</span> commit:</div><div class="line"> (use <span class="string">"git add/rm <file>..."</span> to update what will be committed)</div><div class="line"> (use <span class="string">"git checkout -- <file>..."</span> to discard changes <span class="keyword">in</span> working directory)</div><div class="line"></div><div class="line"> deleted: test.txt</div><div class="line"></div><div class="line">no changes added to commit (use <span class="string">"git add"</span> and/or <span class="string">"git commit -a"</span>)</div></pre></td></tr></table></figure></p>
<p>现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令<code>git rm</code>删掉,并且<code>git commit</code>:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">$ git rm test.txt</div><div class="line">rm <span class="string">'test.txt'</span></div><div class="line">$ git commit -m <span class="string">'remove test.txt'</span></div><div class="line">[master cdaffb3] remove test.txt</div><div class="line"> 1 file changed, 0 insertions(+), 0 deletions(-)</div><div class="line"> delete mode 100644 test.txt</div></pre></td></tr></table></figure></p>
<p>现在,文件就从版本库中被删除了。</p>
<p>另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ git checkout -- test.txt</div></pre></td></tr></table></figure></p>
<p><code>git checkout</code>其实是用版本库里的版本替换工作区的版本,无论工作区是修改还是删除,都可以“一键还原”。</p>
</div>
<footer class="post-footer">
<div class="post-eof"></div>
</footer>
</div>