-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlearngit.txt
493 lines (330 loc) · 14.1 KB
/
learngit.txt
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
1、创建版本库(repository),其实就是一个目录,这个目录里面的
所有文件都可以被Git管理起来,每个文件的修改、删除、Git都能跟踪
,以便任何时刻都可以跟踪历史,或者在将来某个时刻可以“还原”
首先创建一个空目录
$ mkdir git-learn
$ cd git-learn
进入这个空目录,然后通过git init命令把这个变成Git可以管理的仓库
然后Git就创建好了,并且是一个空的仓库(empty Git repository),
这时该目录下会多一个隐藏的目录.git/,这个目录就是Git跟踪管理
版本库的,没事不要动这个目录里面的文件,不然就把Git仓库破坏了
也不一定需要在空目录下创建,选择一个已经有东西的目录也是可以的。
首先这里明确一下,所有的版本控制系统,只能跟踪文本文件的改动,
比如:txt文件,网页,程序代码等。版本控制系统可以告诉你每次的
改动,比如第五行加了单词。而图片、视频这种二进制文件也能由版本
控制系统管理,但没法追踪文件的变化,也就是说只知道该了,不知道
改了哪里。
好了归入正题,进行文件的添加(把文件添加到版本库中)。
可以先创建一个文本文件readme.txt(里面随便由什么内容)
第一步使用git add命令告诉git,把文件添加到仓库:
$ git add readme.txt
第二步,用命令git commit告诉Git,把文件提交到仓库。
$ git commit -m "this is my first commit txt file"
-m 选项是本次提交的声明
git commit 命令执行成功会告诉你, 1 file changed, 3insertions(+)
即1个文件改动,插入两行内容。
为什么Git添加文件需要 add 与 commit 一共两部呢?因为commit可以
一次提交很多文件,所以可以多次add不同的文件
$ git add file1.txt
$ git add file2.txt file3.txt
$ git commit -m "add 3 files."
-------------------------------------------------------------
已经成功并添加一个readme.txt文件。
接下来继续修改readme.txt ,改成如下内容
Git is a distributed version control system.
Git is free software.
然后运行git status命令查看结果
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: readme.txt
no changes added to commit (use "git add" and/or "git commit -a")
$git status命令 可以让我们时刻掌握仓库当前的状态。
结果告诉我们readme.txt 被修改过了,而且还没有准备提交修改。
虽然Git告诉我们readme.txt被修改了,但如果能看看具体修改了什么内
容,自然是很好的。比如你休假两周从国外回来,第一天上班时,已经
记不清上次怎么修改的。
所以可以使用git diff 这个命令查看
$git diff readme.txt
知道对readme.txt 作了什么修改后,再把他提交到仓库就放心多了,
提交修改和提交新文件步骤一样是两步
$git add readme.txt
在在执行第二步git commit之前,我们再运行git status看看当前
仓库的状态:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.txt
git status告诉我们,将要被提交的修改包括readme.txt
$git commit -m "add distributed in line1"
提交后,我们再用git status命令看看仓库的当前状态:
$ git status
On branch master
nothing to commit, working tree clean
提示我们没有文件需要提交,干净的工作区。
----------------------------------------------------------------
版本回退:
现在,再练习一次,修改readme.txt文件如下:
Git is a distributed version control system.
Git is free software distributed under the GPL.
$ git add readme.txt
$ git commit -m "append GPL"
每当你觉得文件修改到一定程度的时候,就可以“保存一个快照”,这个
快照在Git中被称为commit。一旦你把文件改乱了,或者误删了文件,还
可以从最近的一个commit恢复,然后继续工作,而不是把几个月的工作
成果全部丢失。
现在,我们回顾一下readme.txt文件一共有几个版本被提交到Git仓库
里了:
当然了,在实际工作中,我们脑子里怎么可能记得一个几千行的文件
每次都改了什么内容,不然要版本控制系统干什么。版本控制系统肯
定有某个命令可以告诉我们历史记录,在Git中,我们用git log命令
查看:
$ git log
//git log命令显示从最近到最远的提交日志
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上
--pretty=oneline参数:
$ git log --pretty=oneline
其中提交说明前面的一大串十六进制数字就是版本号commit id(版本号)
好了,现在我们启动时光穿梭机,准备把readme.txt回退到上一个版本
也就是add distributed的那个版本,怎么做呢?
首先,Git必须知道当前版本是哪个版本?
在Git中,用HEAD表示当前版本,也就是最新的提交的commit id(版本号)
上一个版本就是HEAD^
上上一个版本就是HEAD^^
当然往上100个版本写100个^比较容易数不过来,所以写成HEAD~100
现在,我们要把当前版本回退到上一个版本,就可以使用git reset命令:
$ git reset --hard HEAD^
用git log再看看现在版本库的状态:
最新的那个版本已经看不见了
好比你从21世纪坐时光穿梭机来到了19世纪,想再回去已经回不去了,
肿么办?
办法其实还是有的,只要上面的命令行窗口还没有被关掉,你就可以顺
着往上找啊找啊,找到那个“最新的那个版本”的commit id。
于是就可以指定回到未来的某个版本:
版本号没必要写全,前几位就可以了,Git会自动去找。当然也不能只
写前一两位,因为Git可能会找到多个版本号,就无法确定是哪一个了。
$git reset --hard d59de913 //....
// 经过尝试果然能回退
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD指针
,当你回退版本的时候,Git仅仅是把HEAD从指向版本号:
画个关系说明下:
HEAD----> 最新版本
次新版本
.
.
.
$ git reset --hard HEAD^
最新版本
HEAD----> 次新版本
.
.
.
所以你让HEAD指向哪个版本,你就把当前版本定位在哪。
现在,你回退到了某个版本,关掉了电脑,第二天早上就后悔了,想恢
复到新版本怎么办?找不到新版本的commit id怎么办?
在Git中,总是有后悔药可以吃的。当你用$ git reset --hard HEAD^
回退到add distributed版本时,再想恢复到最新版本,就必须找到
最新版本的commit id。Git提供了一个命令git reflog用来记录你的
每一次命令:
$ git reflog
然后就可以从中找到你想回到的“最新版本”的commit id。
总结:HEAD 指向的是当前版本。
穿梭前,用git log可以查看提交历史,以便确定要回退到哪个版本。
要重返未来,用git reflog查看命令历史,以便确定要回到未来的哪
个版本。
---------------------------------------------------------------
工作区和暂存区:
工作区(Working Directory)
就是电脑里能够看到的目录,比如自己的git-learn 就是一个工作区
版本库(Repository)
工作区有一个隐藏目录 .git/ 这个不算工作区,而是Git的版本库
Git的版本库里存了很多东西,其中最重要的就是称为stage(或者
叫index)的暂存区。
还有Git为我们自动创建的第一个分支master,以及指向master的一个
指针叫HEAD。
前面讲了我们把文件往Git版本库里添加的时候,是分两步执行的:
第一步是用git add把文件添加进去,实际上就是把文件修改添加到
暂存区;
第二步是用git commit提交更改,实际上就是把暂存区的所有内容
提交到当前分支。
因为我们创建Git版本库时,Git自动为我们创建了唯一一个master分支
,所以,现在,git commit就是往master分支上提交更改。
你可以简单理解为,需要提交的文件修改通通放到暂存区,然后,
一次性提交暂存区的所有修改。
第一步先将修改加入到暂存区,然后将暂存区中的修改提交到分支
---------------------------------------------------------------
为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是
修改,而非文件。
每次修改,如果不用git add到暂存区,那就不会加入到commit中
---------------------------------------------------------------
撤销修改:
git reset HEAD <file>可以把暂存区的修改撤销掉(unstage),重新
放回工作区:
命令git checkout -- readme.txt意思就是,把readme.txt文件在工作
区的修改全部撤销,这里有两种情况:
一种是readme.txt自修改后还没有被放到暂存区,现在,撤销修改就回
到和版本库一模一样的状态(即丢失工作区中的修改);
一种是readme.txt已经添加到暂存区后,又作了修改,现在,撤销修改
就回到添加到暂存区后的状态。
总之,就是让这个文件回到最近一次git commit或git add时的状态。
现在假定是凌晨3点,你不但写了一些胡话,还git add到暂存区了:
庆幸的是,在commit之前,你发现了这个问题。用git status查看一下,
修改只是添加到了暂存区,还没有提交:
Git同样告诉我们,用命令git reset HEAD <file>可以把暂存区的修改
撤销掉(unstage),重新放回工作区:
git reset 命令既可以回退版本,也可以把暂存区中的修改回退到工作区
当我们用HEAD时,表示最新的版本。
将暂存区中的修改回退到工作区,然后如果可以根据需要是否使用git
checkout 命令撤销工作区的修改。
-------------------------------------------------------------
删除文件:
在Git中,删除也是一个修改操作,我们实战一下,先添加一个新文件
test.txt到Git并且提交:
一般情况下,你通常直接在文件管理器中把没用的文件删了,或者
用rm命令删了。
然后使用git status命令可以看到Git 告诉你删除了哪些文件。
现在你有两个选择:
一是确实要从版本库中删除该文件,那就用命令git rm删掉,并
且git commit
然后,文件就从版本库中被删除了。
另一种情况是删错了,因为版本库里还有呢,所以可以很轻松地
把误删的文件恢复到最新版本。
使用git checkout -- <file>
git checkout 其实使用版本库里的版本替换工作区的版本,无论
工作区是修改还是删除。都可以一键还原。
作用就等于从工作区撤销修改。
#注意从来没有添加到版本库就被删除的文件是无法恢复的。
-----------------------------------------------------------
远程仓库:
$git remote add origin [email protected]:haochen233/learngit
远程库的名字就叫origin,这是git默认的叫法
$git push -u origin master 把本地库的素有内容推送到远程库上
要关联一个远程库,使用命令git remote add origin git@server-name:path/repo-name.git;
关联后,使用命令git push -u origin master第一次推送master分支的所有内容;
此后,每次本地提交后,只要有必要,就可以使用命令git push origin master推送最新修改;
--------------------------------------------------------------
从远程库克隆
用命令git clone克隆一个本地库:
$ git clone [email protected]:michaelliao/gitskills.git
---------------------------------------------------------------
创建与合并分支:
首先,我们创建dev分支,然后切换到dev分支:
$ git checkout -b dev
git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
$ git branch dev
$ git checkout dev
然后,用$git branch命令查看当前分支:
git branch命令会列出所有分支,当前分支前面会标一个*号。
HEAD严格来说不是指向提交,而是指向master,master才是指向提交,
所以HEAD指向的就是当前分支。
当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向
master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:
从现在开始,对工作的修改和提交就是针对dev分支了,比如新提交
一次后,dev指针往前移动一步,而master指针不变。
然后,我们就可以在dev分支上正常提交,比如对readme.txt做个修改,加上一行:
Creating a new branch is quick.
现在,dev分支的工作完成,我们就可以切换回master分支:
切换回master分支后,再查看一个readme.txt文件,刚才添加的内容不
见了!因为那个提交是在dev分支上,而master分支此刻的提交点并没有
变:
现在,我们把dev分支的工作成果合并到master分支上:
$ git merge dev
git merge命令用于合并指定分支到当前分支。合并后,再查看readme
.txt的内容,就可以看到,和dev分支的最新提交是完全一样的。
注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”
,也就是直接把master指向dev的当前提交,所以合并速度非常快。
合并完成后,就可以放心的删除dev分支了:
$git branch -d dev
删除后,查看branch,就只剩下master分支了:
$ git branch
因为创建、合并和删除分支非常的快,所以Git鼓励你使用分支完成
某个人物,合并后再删掉分支,这和直接在master分支上工作效果是
一样的,但过程更安全。
小结
Git鼓励大量使用分支:
查看分支:git branch
创建分支:git branch <name>
切换分支:git checkout <name>或者git switch <name>
创建+切换分支:git checkout -b <name>或者git switch -c <name>
合并某分支到当前分支:git merge <name>
删除分支:git branch -d <name>
--------------------------------------------------------------
解决冲突:
当git 无法自动合并分支时。就必须首先解决冲突,在提交,合并完成
解决冲突就是把Git合并失败的文件手动编辑为我们希望的内容,再提
交。
用git log --graph 命令可以看到分支合并图。
---------------------------------------------------------------
分支管理策略:
通常,合并分支时,如果可能,Git会用Fast forward模式,但这种模
式下,删除分支后,会丢掉分支信息。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的
commit,这样,从分支历史上就可以看出分支信息。
Git分支十分强大,在团队开发中应该充分应用。
合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史
有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经
做过合并。
例如: git merge --no-ff -m "merge with --no-ff" dev
分支策略
在实际开发中,我们应该按照几个基本原则进行分支管理:
首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时
不能在上面干活;
那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,
到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在
master分支发布1.0版本;
你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,
时不时地往dev分支上合并就可以了。
---------------------------------------------------------------
Bug分支 :
软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,
由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支
来修复,修复后,合并分支,然后将临时分支删除。
当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一
个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还
没有提交:
并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成
还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
幸好,Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,
等以后恢复现场后继续工作:
$git stash
现在,用git status查看工作区,就是干净的(除非有没有被Git管理
的文件),因此可以放心地创建分支来修复bug。
首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就
从master创建临时分支:
$git switch master
$git switch -c issue-101 or $git checkout -b issue-101
然后修复bug,需要把“Git is free software ...”改为“Git is a free
software ...”,然后提交:
修复完后,切换分支,并完成合并,最后删除分支issue-101
然后修复完bug后,继续回到dev分支干活了。
工作区是干净的,刚才的工作现场存到哪去了?用$git stash list
命令看看:
工作现场还在,Git把stash内容存在某个地方了,但是需要恢复一下,
有两个办法:
(1)git stash apply 恢复,但是在恢复后,stash内容并不删除,
你需要用git stash drop 来删除;
(2)git stash pop ,恢复的同时也把stash内容删除了:
再用git stash list查看,就看不到任何stash内容了:
$git stash list
在master分支上修复了bug后,我们要想一想,dev分支是早期从
master分支分出来的,所以,这个bug其实在当前dev分支上也存在。
那怎么在dev分支上修复同样的bug?重复操作一次,提交不就行了?
同样的bug,要在dev上修复,我们只需要把caec38c fix bug101 这个
提交所做的修改“复制”到dev分支上,注意只想复制caec38c fix bug101
这个提交所做的修改,并不是把整个master分支merge过来。
为了方便操作,Git专门提供了一个cherry-pick命令,让我们能复制一
个特定的提交到当前分支:
$git cherry-pick caec38c
有些聪明的童鞋会想了,既然可以在master分支上修复bug后,在dev分
支上可以“重放”这个修复过程,那么直接在dev分支上修复bug,然后在
master分支上“重放”行不行?当然可以,不过你仍然需要git stash命
令保存现场,才能从dev分支切换到master分支。
小结
修复bug时,我们会通过创建新的bug分支进行修复,然后合并,最后删除;
当手头工作没有完成时,先把工作现场git stash一下,然后去修复bug,修复后,再git stash pop,回到工作现场;
在master分支上修复的bug,想要合并到当前dev分支,可以用
git cherry-pick <commit>命令,把bug提交的修改“复制”到当前分支,
避免重复劳动。