以下筆記主要參考gitgot娱据,大致了解git使用和原理谎柄。
第一部分我們從個(gè)人的視角去研究如何用好Git,并且揭示Git的原理和奧秘俊扭,主要有以下內(nèi)容。
- 創(chuàng)建git本地庫(kù)
- 工作區(qū)和版本庫(kù)
- HEAD和master分支
- Git重置 (reset命令)
- Git檢出 (checkout命令)
- Git stash
- Git 刪除文件
- 文件忽略
- Git amend
- Git cherry-pick(揀選),rebase(變基),revert(反轉(zhuǎn)提交)
一.創(chuàng)建git本地庫(kù)
$ mkdir learngit //創(chuàng)建空目錄
$ cd learngit
$ pwd
/Users/michael/learngit
$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/
$ git init demo //創(chuàng)建目錄并進(jìn)入當(dāng)前目錄
此時(shí)該目錄下會(huì)多一個(gè).git的隱藏目錄坠陈,保存了git庫(kù)信息萨惑,具體內(nèi)容稍后介紹.
二.工作區(qū)和版本庫(kù)
工作區(qū)(Working Directory)就是你在電腦里能看到的目錄。
版本庫(kù)(Repository)工作區(qū)有一個(gè)隱藏目錄.git仇矾,這個(gè)不算工作區(qū)庸蔼,而是Git的版本庫(kù)。
版本庫(kù)里面最主要的內(nèi)容為stage(又叫index)暫存區(qū)和mater分枝,指向master的指針HEAD.(在創(chuàng)建Git版本庫(kù)時(shí)贮匕,Git自動(dòng)為我們創(chuàng)建了唯一一個(gè)master分支)
第一步是用git add --> 把文件添加進(jìn)去姐仅,實(shí)際上就是把文件修改添加到暫存區(qū)
第二步是用git commit -->提交更改,實(shí)際上就是把暫存區(qū)的所有內(nèi)容提交到當(dāng)前分支。
1 .查看狀態(tài)
$ git status
我們先參照上面的實(shí)例,在read.txt文件中新增一行,執(zhí)行該命令.
baihangdeMacBook-Pro:demo baihang$ vi read.txt
baihangdeMacBook-Pro:demo baihang$ 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: welcome.txt
#
no changes added to commit (use "git add" and/or "git commit -a")
也就是說(shuō)要對(duì)修改的:read.txt文件執(zhí)行:command:git add
命令掏膏,將修改的文件添加到“提交任務(wù)”中劳翰,然后才能提交.
或者執(zhí)行git checkout --read.txt
刪除工作區(qū)改動(dòng).
好了,現(xiàn)在就將修改的文件“添加”到提交任務(wù)中,再執(zhí)行stauts
命令馒疹。
baihangdeMacBook-Pro:demo baihang$ git add read.txt
baihangdeMacBook-Pro:demo baihang$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: read.txt
baihangdeMacBook-Pro:demo baihang$
此時(shí)說(shuō)明可以被提交佳簸。你也可以試試用reset HEAD read.txt
撤銷本次暫存區(qū)改動(dòng)。接下來(lái)颖变,我們提交暫存區(qū)內(nèi)容.
baihangdeMacBook-Pro:demo baihang$ git commit -m "add first"
[master 5c80ce0] add first
1 file changed, 1 insertion(+)
baihangdeMacBook-Pro:demo baihang$ git status
On branch master
nothing to commit, working tree clean
git commit -m "add first"
是提交命令生均,-m "add first"是提交說(shuō)明。此時(shí)查看狀態(tài)說(shuō)明沒(méi)東西可以提交悼做,工作區(qū)也是干凈的疯特。
當(dāng)我們使用git status
命令時(shí),顯示內(nèi)容可能過(guò)長(zhǎng)肛走,我們可以使用git status -s
來(lái)簡(jiǎn)化輸出內(nèi)容漓雅。
baihangdeMacBook-Pro:demo baihang$ vi read.txt
baihangdeMacBook-Pro:demo baihang$ git st -s
M read.txt
baihangdeMacBook-Pro:demo baihang$ git add read.txt
baihangdeMacBook-Pro:demo baihang$ git st -s
M read.txt
- 雖然都是 M(Modified)標(biāo)識(shí),但是位置不一樣朽色。在執(zhí)行:command:
git add
命令之前邻吞,這個(gè)M位于第二列(第一列是一個(gè)空格),在執(zhí)行完:command:git add
之后葫男,字符M位于第一列(第二列是空白)抱冷。 - 位于第一列的字符M的含義是:版本庫(kù)中的文件和處于中間狀態(tài)——提交任務(wù)(提交暫存區(qū),即stage)中的文件相比有改動(dòng)梢褐。
- 位于第二列的字符M的含義是:工作區(qū)當(dāng)前的文件和處于中間狀態(tài)——提交任務(wù)(提交暫存區(qū)旺遮,即stage)中的文件相比也有改動(dòng)。
2.diff 比較差異
這時(shí)read.txt有三個(gè)版本,一個(gè)在工作區(qū)盈咳,一個(gè)在等待提交的暫存區(qū)耿眉,還有一個(gè)是版本庫(kù)中最新版本的,我們可以用diff命令查看其區(qū)別.
$ git diff //工作區(qū)和提交任務(wù)(提交暫存區(qū),stage)中相比的差異鱼响。
$ git diff readme.txt //readme.txt中工作區(qū)和暫存區(qū)差異
$ git diff HEAD //工作區(qū)和HEAD(當(dāng)前工作分支)相比
$ git diff --cached //提交暫存區(qū)(提交任務(wù)鸣剪,stage)和版本庫(kù)中文件
我們可以用以下命令查看暫存區(qū)和HEAD(版本庫(kù)中當(dāng)前提交)指向的目錄樹.
baihangdeMacBook-Pro:demo baihang$ git ls-files -s //暫存區(qū)
100644 9c59e24b8393179a5d712de4f990178df5734d99 0 read.txt
baihangdeMacBook-Pro:demo baihang$ git ls-tree HEAD //HEAD指向的目錄樹
100644 blob 9c59e24b8393179a5d712de4f990178df5734d99 read.txt```
#三.HEAD和master分支
#####1.git對(duì)象
首先我們對(duì)git對(duì)象進(jìn)行一下研究。
baihangdeMacBook-Pro:demo baihang$ git log -1 --pretty=raw
commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86
tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9
parent a0c641e92b10d8bcca1ed1bf84ca80340fdefee6
author jkbaihang 909920027@qq.com 1494642732 +0800
committer jkbaihang 909920027@qq.com 1494642732 +0800
add first
git log表示顯示歷史提交版本丈积,-1表示顯示一個(gè)筐骇,--pretty=raw表示顯示詳細(xì)日志輸出。
一個(gè)提交中居然包含了三個(gè)SHA1哈希值(一種表示數(shù)據(jù)的方法,當(dāng)我們使用時(shí)可以只用前幾位)表示的對(duì)象ID江滨。
- commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86
這是本次提交的唯一標(biāo)識(shí)铛纬。
- tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9
這是本次提交所對(duì)應(yīng)的目錄樹。
- parent a0c641e92b10d8bcca1ed1bf84ca80340fdefee6這是本地提交的父提交(上一次提交)唬滑。
這時(shí)候我們需要使用一個(gè)工具如下.
git cat-file -t告唆,查看Git對(duì)象的類型莫秆,主要的git對(duì)象包括tree,commit悔详,parent,和blob等惹挟。
git cat-file -p茄螃,查看Git對(duì)象的內(nèi)容
查看類型
$ git cat-file -t e695606
commit
$ git cat-file -t f58d
tree
$ git cat-file -t a0c6
commit
查看內(nèi)容
$ git cat-file -p e695606
tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9
parent a0c641e92b10d8bcca1ed1bf84ca80340fdefee6
author jkbaihang 909920027@qq.com 1494642732 +0800
committer jkbaihang 909920027@qq.com 1494642732 +0800
add first
$ git cat-file -p f58da9a
100644 blob fd3c069c1de4f4bc9b15940f490aeb48852f3c42 read.txt
$ git cat-file -p a0c641e //此時(shí)沒(méi)parent,因?yàn)槭浅跏继峤?br>
tree 190d840dd3d8fa319bdec6b8112b0957be7ee769
author jkbaihang 909920027@qq.com 1494642732 +0800
committer jkbaihang 909920027@qq.com 1494642732 +0800
add read
上述tree中存在一個(gè)blog對(duì)象连锯,該對(duì)象保存著read.txt的內(nèi)容
$ git cat-file -t fd3c069c1de4f4bc9b15940f490aeb48852f3c42
blob
$ git cat-file -p fd3c069c1de4f4bc9b15940f490aeb48852f3c42
first
second
這些對(duì)象保存在哪里的呢归苍?在objects目錄下。
baihangdeMacBook-Pro:demo baihang$ cd .git
baihangdeMacBook-Pro:.git baihang$ ls
COMMIT_EDITMSG branches hooks logs
HEAD config index objects
ORIG_HEAD description info refs
baihangdeMacBook-Pro:.git baihang$ cd objects
![Screen Shot 2017-05-13 at 11.22.43 AM.png](http://upload-images.jianshu.io/upload_images/1485048-f9531778587f3069.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
#####2.HEAD和mater分支
$ git branch
- master
`git branch`查看所有分支运怖,*代表當(dāng)前分支拼弃。
$ git log -1 HEAD //查看HEAD指針指向的提交
commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86
Author: jkbaihang 909920027@qq.com
Date: Sat May 13 10:32:12 2017 +0800
add first
$ git log -1 master //查看master分支對(duì)應(yīng)的提交
commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86
Date: Sat May 13 10:32:12 2017 +0800
add first
$ git log -1 refs/heads/master /查看master分支對(duì)應(yīng)提交,可以省略前面部分
commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86
Date: Sat May 13 10:32:12 2017 +0800
add first
說(shuō)明head和master對(duì)應(yīng)的提交都是一樣的摇展。接下來(lái)吻氧,我們查看一下他們所對(duì)應(yīng)的文件的內(nèi)容.
$ cat .git/HEAD
ref: refs/heads/master
$ cat .git/refs/heads/master
e695606fc5e31b2ff9038a48a3d363f4c21a3d86
$ git cat-file -t e695606
commit
$ git cat-file -p e695606
tree f58da9a820e3fd9d84ab2ca2f1b467ac265038f9
commit e695606fc5e31b2ff9038a48a3d363f4c21a3d86
Author: jkbaihang 909920027@qq.com
Date: Sat May 13 10:32:12 2017 +0800
add first
根據(jù)結(jié)果可以得出結(jié)論-----master代表分支master中最新的提交,而HEAD指向的是master引用咏连。
![Screen Shot 2017-05-13 at 6.00.16 PM.png](http://upload-images.jianshu.io/upload_images/1485048-8763a33650dcede0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
目錄:file:`.git/refs`是保存引用的命名空間盯孙,其中:file:`.git/refs/heads`目錄下的引用又稱為分支。對(duì)于分支既可以使用正規(guī)的長(zhǎng)格式的表示法祟滴,如:file:`refs/heads/master`振惰,也可以去掉前面的兩級(jí)目錄用master來(lái)表示。Git 有一個(gè)底層命令:command:`git rev-parse`可以用于顯示引用對(duì)應(yīng)的提交ID垄懂。
$ git rev-parse master
e695606fc5e31b2ff9038a48a3d363f4c21a3d86
$ git rev-parse refs/heads/master
e695606fc5e31b2ff9038a48a3d363f4c21a3d86
$ git rev-parse HEAD
e695606fc5e31b2ff9038a48a3d363f4c21a3d86
#四.Git重置 (reset命令)
$ git log --oneline //--oneline代表簡(jiǎn)化一行內(nèi)容
e695606 add first
a0c641e add read
上面是我們的提交記錄骑晶,這個(gè)時(shí)候我們?cè)龠M(jìn)行一次修改和提交。
$ vi read.txt
$ git add read.txt
$ git commit -m "add second"
[master 562b0df] add second
1 file changed, 1 insertion(+)
$ git log --oneline
4902dc3 add second
e695606 add first
a0c641e add read
$ cat .git/refs/heads/master
4902dc375672fbf52a226e0354100b75d4fe31e3
可以看到草慧,master引用變成了最新的提交桶蛔,如果這個(gè)時(shí)候我們想取消這一次提交,我們就可以使用`reset`命令冠蒋。
這里我們學(xué)習(xí)一些方法羽圃,可以方便的訪問(wèn)Git庫(kù)中的對(duì)象。
- 使用master代表分支master中最新的提交抖剿,使用全稱refs/heads/master亦可朽寞。
- 使用HEAD代表版本庫(kù)中最近的一次提交。
- 符號(hào)` ^可以用于指代父提交斩郎。例如:
- HEAD^代表版本庫(kù)中上一次提交脑融,即最近一次提交的父提交。
- HEAD^^則代表HEAD^的父提交缩宜。
- 對(duì)于一個(gè)提交有多個(gè)父提交肘迎,可以在符號(hào)^后面用數(shù)字表示是第幾個(gè)父提交甥温。例如:
- a573106^2含義是提交a573106的多個(gè)父提交中的第二個(gè)父提交。
- HEAD^1相當(dāng)于HEAD^含義是HEAD多個(gè)父提交中的第一個(gè)妓布。
- HEAD^^2含義是HEAD^(HEAD父提交)的多個(gè)父提交中的第二個(gè)姻蚓。
####reset具體使用
$ git reset HEAD^ //或者git reset e695606,可以重置到前一次提交匣沼,也可以直接使用提交ID重置到任何一次提交狰挡。
Unstaged changes after reset:
M read.txt
$ git log --oneline
e695606 add first
a0c641e add read
可以看到這個(gè)時(shí)候提交記錄second取消了,最新提交變成first释涛,然后工作區(qū)和暫存區(qū)內(nèi)容有差異加叁,為啥會(huì)有差異呢?
reset有兩種用法:
- 一種是含有路徑(不會(huì)重置引用唇撬,更不會(huì)改變工作區(qū)它匕,而是用指定提交狀態(tài)(<commit>)下的文件(<paths>)替換掉暫存區(qū)中的文件)
$ git vi read.txt //文本中增加一行
$ git add read.txt //此時(shí)工作區(qū)和暫存區(qū)都增加了一行
$ git reset read.txt //將此時(shí)提交狀態(tài)下文件替換暫存區(qū)
- 一種是不含路徑(重置引用,根據(jù)不同的選項(xiàng)窖认,可以對(duì)暫存區(qū)或者工作區(qū)進(jìn)行重置)豫柬。下面我們主要說(shuō)第二種。
![Screen Shot 2017-05-13 at 8.34.31 PM.png](http://upload-images.jianshu.io/upload_images/1485048-feced4ee1c0dfb4f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
$ git reset //git reset HEAD也是同作用
僅用HEAD指向的目錄樹重置暫存區(qū)扑浸,工作區(qū)不會(huì)受到影響轮傍,相當(dāng)于將之前用`git add`命令更新到暫存區(qū)的內(nèi)容撤出暫存區(qū)。引用也未改變首装,因?yàn)橐弥刂玫紿EAD相當(dāng)于沒(méi)有重置创夜。
$ git reset HEAD^
工作區(qū)不改變,但是暫存區(qū)會(huì)回退到上一次提交之前仙逻,引用也會(huì)回退一次驰吓。
reset命令有三個(gè)參數(shù)類型,--soft,--mixed(一般默認(rèn)類型),--hard類型系奉。
-
- 使用參數(shù)--hard檬贰,如::command:`git reset --hard <commit>`。
會(huì)執(zhí)行上圖中的1缺亮、2翁涤、3全部的三個(gè)動(dòng)作。即:
1.替換引用的指向萌踱。引用指向新的提交ID葵礼。
2.替換暫存區(qū)。替換后并鸵,暫存區(qū)的內(nèi)容和引用指向的目錄樹一致鸳粉。
3.替換工作區(qū)。替換后园担,工作區(qū)的內(nèi)容變得和暫存區(qū)一致,也和HEAD所指向的目錄樹內(nèi)容相同。
- 使用參數(shù)--soft扎筒,如::command:`git reset --soft <commit>`。
會(huì)執(zhí)行上圖中的操作1湖雹。即只更改引用的指向,不改變暫存區(qū)和工作區(qū)曙搬。
- 使用參數(shù)--mixed或者不使用參數(shù)(缺省即為--mixed)劝枣,如:command:`git reset <commit>`。
會(huì)執(zhí)行上圖中的操作1和操作2织鲸。即更改引用的指向以及重置暫存區(qū),但是不改變工作區(qū)溪胶。
命令 | 工作區(qū) | 暫存區(qū) | Master
----|------
當(dāng)前版本 |B|B |B(A為上一提交)
--soft | B| B|A
--mixed | B| A|A
--hard| A | A|A
####用reflog挽救錯(cuò)誤的重置
1.可以查看日志來(lái)查詢master歷史
$ tail -5 .git/logs/refs/heads/master
2.可以使用reflog查詢到歷史操作記錄
$ git reflog //查看HEAD
$ git reflog master //查看master
#五.Git檢出 (checkout命令)
#### 1.Git檢出
上一節(jié)reset作用主要是修改master搂擦,而這節(jié)的checkout則是修改HEAD,HEAD一般是默認(rèn)指向refs/heads/master。接下來(lái)我們checkout命令哗脖。
$ git log --oneline
dd35068 three
b4f6dd1 second
519bbb0 first
b4f87be init
$ git checkout 519bbb0
Note: checking out '519bbb0'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 519bbb0... first
$ git checkout 519bbb0
注意: 正檢出 '519bbb0'.
您現(xiàn)在處于 '分離頭指針' 狀態(tài)瀑踢。您可以檢查、測(cè)試和提交才避,而不影響任何分支橱夭。
通過(guò)執(zhí)行另外的一個(gè) checkout 檢出指令會(huì)丟棄在此狀態(tài)下的修改和提交。
如果想保留在此狀態(tài)下的修改和提交桑逝,使用 -b 參數(shù)調(diào)用 checkout 檢出指令以
創(chuàng)建新的跟蹤分支棘劣。如:
git checkout -b new_branch_name
頭指針現(xiàn)在指向 519bbb0... 提交說(shuō)明為:first
什么叫做“分離頭指針”狀態(tài)?查看一下此時(shí)HEAD的內(nèi)容就明白了楞遏。
$ cat .git/HEAD
519bbb07a0aaef319088ca21ebbe91301b5c583b
$ git bra
- (HEAD detached at 519bbb0)
master
我們?cè)俨榭匆幌伦钚氯罩?
$ git reflog -1 //注意該日志是HEAD,而不是master
519bbb0 HEAD@{0}: checkout: moving from master to 519bbb0
很明顯茬暇,HEAD由指向master變?yōu)橹赶騠irst那個(gè)提交Id。我們?cè)倏聪耺aster現(xiàn)在的id是什么寡喝。
$ git rev-parse HEAD master
519bbb07a0aaef319088ca21ebbe91301b5c583b
dd35068fac7a74780d85194edfbc2adf1d815c54
綜上
- HEAD指向了一個(gè)新的ID糙俗,而master并無(wú)變動(dòng)。
我們?cè)谠摖顟B(tài)下预鬓,新建一個(gè)文件,并提交巧骚。
$ vi head.txt
$ git add head.txt
$ git commit -m "init head.txt"
[detached HEAD f573e31] init head.txt
1 file changed, 2 insertions(+)
接下來(lái)查看一下,日志格二,可以看到HEAD指向了新的提交.
git log --oneline
f573e31 init head.txt
519bbb0 first
b4f87be init
baihangdeMacBook-Pro:baihang baihang$ cat .git/HEAD
f573e314bd5d1640f7853b26010887e4b6892e48
baihangdeMacBook-Pro:baihang baihang$
我們切換回master分支,并查看日志劈彪。
$ git checkout master
Warning: you are leaving 1 commit behind, not connected to
any of your branches:
f573e31 init head.txt
If you want to keep it by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> f573e31
Switched to branch 'master'
baihangdeMacBook-Pro:baihang baihang$ git log --oneline
dd35068 three
b4f6dd1 second
519bbb0 first
b4f87be init
baihangdeMacBook-Pro:baihang baihang$
很明顯,之前HEAD提交記錄不在了顶猜。我們?cè)诳聪律厦孢@段話粉臊,他的大致意思是,該提交沒(méi)連接任何分支驶兜,如果你想保存它扼仲,那你必須通過(guò)命令新建一個(gè)分支远寸,因?yàn)樵撚涗涬S時(shí)會(huì)被刪除 。(在后面新建分支中會(huì)講到)
#####2.挽救分離頭指針屠凶。
在“分離頭指針”模式下進(jìn)行的測(cè)試提交除了使用提交ID(f573e31)訪問(wèn)之外驰后,不能通過(guò)master分支或其他引用訪問(wèn)到。如果這個(gè)提交是master分支所需要的矗愧,那么該如何處理呢灶芝?
- 我們可以通過(guò)**合并**分支來(lái)執(zhí)行。
我們先確保返回master分支唉韭。
git bra -v
- master dd35068 three
然后執(zhí)行合并操作
$ git merge f573e31
Merge made by the 'recursive' strategy.
head.txt | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 head.txt
工作區(qū)此時(shí)多了一個(gè)head.txt文件
$ ls
head.txt read.txt
此時(shí)我們?cè)俨榭匆幌氯罩疽固椋褂?-graph 可以看到鏈接狀態(tài)。
$ git log --graph --oneline
- 1ed3940 Merge commit 'f573e31'
|\
| * f573e31 init head.txt - | dd35068 three
- | b4f6dd1 second
|/ - 519bbb0 first
- b4f87be init
再查看一下最新提交的內(nèi)容
$ git cat-file -p HEAD
tree 749f7a050f7e5dbfe4afc559ed3308172ea96921
parent dd35068fac7a74780d85194edfbc2adf1d815c54
parent f573e314bd5d1640f7853b26010887e4b6892e48
author jkbaihang 909920027@qq.com 1494742070 +0800
committer jkbaihang 909920027@qq.com 1494742070 +0800
Merge commit 'f573e31'
- 該提交中有兩個(gè)父提交属愤,這就是合并的奧秘女器。
#####3.checkout命令
![](http://upload-images.jianshu.io/upload_images/1485048-67d16272a20f32b9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 命令::command:`git checkout branch`
檢出branch分支。要完成如圖的三個(gè)步驟住诸,更新HEAD以指向branch分支驾胆,以branch指向的樹更新暫存區(qū)和工作區(qū)。
- 命令::command:`git checkout -- filename`
用暫存區(qū)中:file:`filename`文件來(lái)覆蓋工作區(qū)中的:file:`filename`文件贱呐。相當(dāng)于取消自上次執(zhí)行:command:`git add filename`以來(lái)(如果執(zhí)行過(guò))本地的修改丧诺。(這個(gè)命令很危險(xiǎn),因?yàn)閷?duì)于本地的修改會(huì)悄無(wú)聲息的覆蓋奄薇,毫不留情驳阎。)
- 命令::command:`git checkout -- . 或?qū)懽?git checkout .`
注意::command:`git checkout`命令后的參數(shù)為一個(gè)點(diǎn)(“.”)。這條命令最危險(xiǎn)馁蒂!會(huì)取消所有本地的修改(相對(duì)于暫存區(qū))搞隐。相當(dāng)于將暫存區(qū)的所有文件直接覆蓋本地文件,不給用戶任何確認(rèn)的機(jī)會(huì)远搪!
- 命令::command:`git checkout branch -- filename`
維持HEAD的指向不變劣纲。將branch所指向的提交中的:file:`filename`替換暫存區(qū)和工作區(qū)中相應(yīng)的文件。注意會(huì)將暫存區(qū)和工作區(qū)中的:file:`filename`文件直接覆蓋谁鳍。
#六.Git stash
有些時(shí)候你手上的工作正做到一半癞季,但是領(lǐng)導(dǎo)需要你馬上做另一項(xiàng)工作,我們就可以把當(dāng)前工作現(xiàn)場(chǎng)“儲(chǔ)藏”起來(lái)倘潜,等以后恢復(fù)現(xiàn)場(chǎng)后繼續(xù)工作绷柒。
我們新建一個(gè)git,然后創(chuàng)建一個(gè)read.txt涮因。
$ echo init > read.txt
$ git add read.txt
git ci -m "init"
[master (root-commit) 9d885c5] init
1 file changed, 1 insertion(+)
create mode 100644 read.txt
接下來(lái)废睦,我們新增一行,如果這個(gè)時(shí)候你需要暫停當(dāng)前工作养泡,你就可以使用stash命令保存嗜湃。
$ echo first >> read.txt
$ git add read.txt
$ git stash save "firstSave"
Saved working directory and index state On master: firstSave
HEAD is now at 9d885c5 init
我們查看一下狀態(tài),發(fā)現(xiàn)工作區(qū)和暫存區(qū)都恢復(fù)正常狀態(tài)了奈应。
$ git status
On branch master
nothing to commit, working tree clean
我們可以用stash list查看一下當(dāng)前stash進(jìn)度列表
$ git stash list
stash@{0}: On master: firstSave
###stash是怎么保存工作區(qū)和暫存區(qū)數(shù)據(jù)的呢?
我們先看看git文件里面refs/stash的數(shù)據(jù)购披。
$ cat .git/refs/stash
0f27315d1a24e01bc701cab00b71d8b6ec5bb887
$ git cat-file -p 0f27315d
tree 154a4f6d4a332c54dcf9ef2867ecd45714222b84
parent f2e10ed446f10ebcc6f7173aa0b4695057f445b7
parent ea4f40fd65186f5f53aa9f35a9af79be1801df90
author jkbaihang 909920027@qq.com 1494827393 +0800
committer jkbaihang 909920027@qq.com 1494827393 +0800
我們?cè)倏纯刺峤粴v史杖挣。
$ git log --graph --pretty=raw 0f27315d
- commit 0f27315d1a24e01bc701cab00b71d8b6ec5bb887
|\ tree 154a4f6d4a332c54dcf9ef2867ecd45714222b84
| | parent 9d885c5c380150fc1cad8c944dbcbdd582f92c9a
| | parent ea4f40fd65186f5f53aa9f35a9af79be1801df90
| | author jkbaihang 909920027@qq.com 1494827393 +0800
| | committer jkbaihang 909920027@qq.com 1494827393 +0800
| |
| | on master: firstSave
| |
| * commit ea4f40fd65186f5f53aa9f35a9af79be1801df90
|/ tree 3c191023105e79c4844cb22d320346772ecc2b65
| parent 9d885c5c380150fc1cad8c944dbcbdd582f92c9a
| author jkbaihang 909920027@qq.com 1494827393 +0800
| committer jkbaihang 909920027@qq.com 1494827393 +0800
|
| index on master: 9d885c5c init
|
- commit 9d885c5c380150fc1cad8c944dbcbdd582f92c9a
| tree 3c191023105e79c4844cb22d320346772ecc2b65
| author jkbaihang 909920027@qq.com 1494827329 +0800
| committer jkbaihang 909920027@qq.com 1494827329 +0800
|
可以看到在提交關(guān)系圖可以看到進(jìn)度保存的最新提交是一個(gè)合并提交。最新的提交代表了工作區(qū)進(jìn)度刚陡。而最新提交的第二個(gè)父提交(上圖中顯示為第二個(gè)提交)有index on master字樣惩妇,說(shuō)明這個(gè)提交代表著暫存區(qū)的進(jìn)度。
#####恢復(fù)操作
如果我們想要恢復(fù)當(dāng)前保存的內(nèi)容筐乳,可以執(zhí)行下面pop操作
$ git stash pop stash@{0} //不寫stash@{0}編號(hào)的話歌殃,默認(rèn)為最新一次
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: read.txt
no changes added to commit (use "git add" and/or "git commit -a")
Dropped stash@{0} (acd7dc99fed73b95b802da0f7e555867ce59a798)
$ git stash list //查詢一下stash列表可以看到無(wú)stash
根據(jù)提示,我們可以看到恢復(fù)了工作區(qū)蝙云,同時(shí)stash@{0}被刪除氓皱。(如果我們想同時(shí)恢復(fù)暫存區(qū),應(yīng)該加--index參數(shù)贮懈,git stash pop --index stash@{0})
如果我們不想刪除stash存儲(chǔ)的進(jìn)度的話,我們可以使用apply命令,之后也可以使用drop命令刪除存儲(chǔ)的進(jìn)度优训。
$ git stash apply
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: read.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ git stash drop stash@{0}
Dropped stash@{0} (9395ebe4c5d60bfc122202416a078a602052da82)
###stash命令列表
- 命令::command:`git stash`
保存當(dāng)前工作進(jìn)度朵你。會(huì)分別對(duì)暫存區(qū)和工作區(qū)的狀態(tài)進(jìn)行保存。
- 命令::command:`git stash list`
顯示進(jìn)度列表揣非。此命令顯然暗示了:command:`git stash`可以多次保存工作進(jìn)度抡医,并且在恢復(fù)的時(shí)候進(jìn)行選擇。
- 命令::command:`git stash pop [--index] [<stash>]`
如果不使用任何參數(shù)早敬,會(huì)恢復(fù)最新保存的工作進(jìn)度忌傻,并將恢復(fù)的工作進(jìn)度從存儲(chǔ)的工作進(jìn)度列表中清除。
如果提供<stash>參數(shù)(來(lái)自于:command:`git stash list`顯示的列表)搞监,則從該<stash>中恢復(fù)水孩。恢復(fù)完畢也將從進(jìn)度列表中刪除<stash>琐驴。
選項(xiàng)--index除了恢復(fù)工作區(qū)的文件外俘种,還嘗試恢復(fù)暫存區(qū)。這也就是為什么在本章一開始恢復(fù)進(jìn)度的時(shí)候顯示的狀態(tài)和保存進(jìn)度前略有不同绝淡。
- 命令::command:`git stash apply [--index] [<stash>]`
除了不刪除恢復(fù)的進(jìn)度之外宙刘,其余和:command:`git stash pop`命令一樣。
- 命令::command:`git stash drop [<stash>]`
刪除一個(gè)存儲(chǔ)的進(jìn)度牢酵。缺省刪除最新的進(jìn)度悬包。
- 命令::command:`git stash clear`
刪除所有存儲(chǔ)的進(jìn)度。
- 命令::command:`git stash branch <branchname> <stash>`
基于進(jìn)度創(chuàng)建分支馍乙。
#七.Git 刪除文件
$ ls
read.txt
$ echo "hello" > hack.txt
$ git add hack.txt
$ git ci -m "add hack.txt"
[master 13dc137] add hack.txt
1 file changed, 1 insertion(+), 1 deletion(-)
$ ls
hack.txt read.txt
我們新建一個(gè)hack.txt并提交布近,如果后面我們不需要hack.txt怎么辦垫释?
首先我們需要在本地,就是工作區(qū)中刪除吊输,然后再在暫存區(qū)中刪除饶号,最后再提交一次。
$ rm hack.txt //本地刪除
$ git status
On branch master
Changes not staged for commit:
(use "git add/rm <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
deleted: hack.txt
no changes added to commit (use "git add" and/or "git commit -a")
$ git rm hack.txt //緩存區(qū)刪除
rm 'hack.txt'
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
deleted: hack.txt
$ git commit -m "delete hack.txt" //提交季蚂,文件在版本庫(kù)最新提交中刪除了
[master ff6c9a4] delete hack.txt
1 file changed, 1 deletion(-)
如果我們需要已經(jīng)刪除的數(shù)據(jù)怎么辦茫船?注意,該文件知識(shí)在版本庫(kù)最新提交中刪除了扭屁,我們可以在歷史版本中查看算谈。
$ git cat-file -p HEAD^:hack.txt
hello
####一些其他命令
- :command:`git add -u`
執(zhí)行:command:`git add -u`命令可以將(被版本庫(kù)追蹤的)本地文件的變更(修改、刪除)全部記錄到暫存區(qū)中料滥。
比如上面例子中然眼,我們可以用`git add-u` 代替`git rm hack.txt
- :command:`git add -A`
執(zhí)行:command:`git add -A`命令會(huì)對(duì)工作區(qū)中所有改動(dòng)以及新增文件添加到暫存區(qū),也是一個(gè)常用的技巧葵腹。如下
git cat-file -p HEAD^:hack.txt >hack.txt
$ git add -A
$ git commit -m "restore hack.txt"
[master 86d4116] restore hack.txt
1 file changed, 1 insertion(+)
create mode 100644 hack.txt
#八.文件忽略
像我們做c語(yǔ)言程序時(shí)高每,經(jīng)常會(huì)出現(xiàn)一些對(duì)象文件o,如果我們執(zhí)行add-A命令后,會(huì)把這些對(duì)象文件也添加進(jìn)暫存區(qū)践宴。(但是并不需要)鲸匿。我們就需要進(jìn)行文件忽略設(shè)置。
$ echo "first" > hello.o //新建一個(gè)o文件
$ git st -s //問(wèn)號(hào)表示未跟蹤
?? hello.o
$ echo "*.o" > .gitignore //新建一個(gè).gitignore文件阻肩,并設(shè)置忽略內(nèi)容
$ cat .gitignore
*.o
$ git status -s
?? .gitignore //可以發(fā)現(xiàn)hello.o文件被忽略了
$ git add .gitignore
$ git ci -m "ignore object files" //如果不希望添加到庫(kù)里带欢,也不希望:file:.gitignore
文件帶來(lái)干擾,可以在忽略文件中忽略自己烤惊。)
[master d067bdb] ignore object files
1 file changed, 1 insertion(+)
create mode 100644 .gitignore
- 忽略文件步驟為新建一個(gè).gitnore文件并設(shè)置忽略內(nèi)容乔煞,再add,commit柒室。
如果我們想查看忽略的文件渡贾,可以使用--ignored參數(shù)。
$ git st --ignored -s
?? .gitignore
!! hello.o
要是我們想增加一個(gè)忽略類型的文件雄右,可以使用-f參數(shù)剥啤,并明確文件名。
git add -f hello.o
baihangdeMacBook-Pro:baihangdemo baihang$ git st -s
A hello.o
?? .gitignore
#九.Git amend
在日常的Git操作中不脯,會(huì)經(jīng)常出現(xiàn)這樣的狀況府怯,輸入:command:`git commit`命令剛剛敲下回車鍵就后悔了:可能是提交說(shuō)明中出現(xiàn)了錯(cuò)別字,或者有文件忘記提交防楷,或者有的修改不應(yīng)該提交牺丙,諸如此類。于是Git提供了一個(gè)簡(jiǎn)潔的操作——修補(bǔ)式提交,命令是::command:`git commit --amend`冲簿。
- 修改最新提交說(shuō)明
$ git log --pretty=oneline
36b133bdd6d3c169bc6ed22479712d650179bc79 mistake commit
92cb42d419326fc4bf22afb2463dac9606596bbe init
$ git commit --amend
[master 9ea37af] correct commit
Date: Mon May 15 22:44:39 2017 +0800
1 file changed, 1 insertion(+), 1 deletion(-)
baihangdeMacBook-Pro:demo baihang$ git log --pretty=oneline
9ea37afeb54ea83835fe2707a68bb900cbf7a1fe correct commit
92cb42d419326fc4bf22afb2463dac9606596bbe init
當(dāng)然上述也可以使用reset重置執(zhí)行粟判。
#十.Git cherry-pick(揀選),rebase(變基),revert(反轉(zhuǎn)提交)
####1.cherry-pick
假設(shè)有這樣一個(gè)提交記錄,我們想刪除中間D提交峦剔,應(yīng)該怎么做呢档礁。
![Screen Shot 2017-05-16 at 11.19.43 AM.png](http://upload-images.jianshu.io/upload_images/1485048-dff92697a2aa76e7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我們可以在C的基礎(chǔ)上新建一個(gè)分支,然后將E和F嫁接在上面吝沫。
![Screen Shot 2017-05-16 at 11.21.00 AM.png](http://upload-images.jianshu.io/upload_images/1485048-16bf433c2403ebf7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我們先自己做一個(gè)提交記錄如下呻澜。
git log --oneline
5934eeb six
99f7448 five
476e3d5 four
76adf28 three
cc624c3 second
20ad652 first
然后每次的提交版本,我們?cè)O(shè)置一個(gè)tag惨险。(大致就是就是每個(gè)提交版本設(shè)置標(biāo)識(shí)羹幸,后面會(huì)詳細(xì)說(shuō)明)
$ git tag F
$ git tag E HEAD^
$ git tag D HEAD^^
$ git tag C HEAD^^^
$ git tag B HEAD~4
$ git tag A HEAD~5
$ git log --oneline --decorate //--decorate 顯示tag
5934eeb (HEAD -> master, tag: F) six
99f7448 (tag: E) five
476e3d5 (tag: D) four
76adf28 (tag: C) three
cc624c3 (tag: B) second
20ad652 (tag: A) first
我們將頭指針切換到 C,切換過(guò)程顯示處于非跟蹤狀態(tài)的警告,沒(méi)有關(guān)系辫愉,因?yàn)橹皇谴笾卵菔尽?
$ git checkout C
Note: checking out 'C'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 76adf28... three
- 執(zhí)行揀選操作將E提交在當(dāng)前HEAD上重放栅受,我的理解就是嫁接在上面。
$ git cherry-pick master^
error: could not apply 99f7448... five
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
這個(gè)時(shí)候說(shuō)明有些沖突恭朗,沒(méi)關(guān)系屏镊,你打開文件,編輯內(nèi)容痰腮,重新提交而芥。
$ git add read.txt
baihangdeMacBook-Pro:cherry baihang$ git ci -m "five "
[detached HEAD a93028e] five
Date: Tue May 16 10:33:30 2017 +0800
1 file changed, 2 insertions(+)
- 執(zhí)行揀選操作將F提交在當(dāng)前HEAD上重放。
$ git cherry-pick master
[detached HEAD 03bc166] six
Date: Tue May 16 10:33:48 2017 +0800
1 file changed, 1 insertion(+)
baihangdeMacBook-Pro:cherry baihang$ git log --pretty=oneline
03bc166 (HEAD) six
a93028e five change
76adf28 (tag: C) three
cc624c3 (tag: B) second
20ad652 (tag: A) first
這個(gè)時(shí)候D已經(jīng)不在了诽嘉,如果我們不想刪除D,希望把CD合并成一次提交又該怎么做呢蔚出?
![Screen Shot 2017-05-16 at 11.38.00 AM.png](http://upload-images.jianshu.io/upload_images/1485048-685462f07ea4415a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我們先切換回初始狀態(tài)弟翘。
$ git checkout master
Warning: you are leaving 2 commits behind, not connected to
any of your branches:
03bc166 six
a93028e five change
If you want to keep them by creating a new branch, this may be a good time
to do so with:
git branch <new-branch-name> 03bc166
Switched to branch 'master'
$ git reset --hard F
HEAD is now at 5934eeb six
頭指針指向提交D
$ git checkout D
Note: checking out 'D'.
You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.
If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:
git checkout -b <new-branch-name>
HEAD is now at 476e3d5... four
融合C,D虫腋。下面soft表示工作區(qū)和暫存區(qū)都沒(méi)變,相當(dāng)于內(nèi)容是D的內(nèi)容稀余,但是當(dāng)前版本是B悦冀。
$ git reset --soft HEAD^^
執(zhí)行提交,提交說(shuō)明重用C提交的提交說(shuō)明睛琳。
baihangdeMacBook-Pro:cherry baihang$ git ci -C C
[detached HEAD 4ce6589] three
Date: Tue May 16 10:33:05 2017 +0800
1 file changed, 2 insertions(+)
接下來(lái)執(zhí)行揀選操作盒蟆。
git cherry-pick E
[detached HEAD d9b0199] five
Date: Tue May 16 10:33:30 2017 +0800
1 file changed, 1 insertion(+)
$ git cherry-pick F
[detached HEAD 01f1bd5] six
Date: Tue May 16 10:33:48 2017 +0800
1 file changed, 1 insertion(+)
我們查看一下日志,發(fā)現(xiàn)C和D已經(jīng)融合师骗。
$ git log --oneline --decorate
01f1bd5 (HEAD) six
d9b0199 five
4ce6589 three
cc624c3 (tag: B) second
20ad652 (tag: A) first
####2.reabase
命令:command:`git rebase`是對(duì)提交執(zhí)行變基操作历等,即可以實(shí)現(xiàn)將指定范圍的提交“嫁接”到另外一個(gè)提交之上。其常用的命令行格式有:
用法1: git rebase --onto <newbase> <since> <till> //<since>..<till>是指包括<till>的所有歷史提交排除<since>以及<since>的歷史提交后形成的版本范圍辟癌。
用法2: git rebase --onto <newbase> <since>
用法3: git rebase <newbase> <till>
用法4: git rebase <newbase>
下面是上述命令的完整版本寒屯。
用法1: git rebase --onto <newbase> <since> <till>
用法2: git rebase --onto <newbase> <since> [HEAD]
用法3: git rebase [--onto] <newbase> [<newbase>] <till>
用法4: git rebase [--onto] <newbase> [<newbase>] [HEAD]
當(dāng)遇到?jīng)_突時(shí)的一些指令
用法6: git rebase --continue //繼續(xù)變基操作
用法7: git rebase --skip //跳過(guò)此提交
用法8: git rebase --abort //就此終止變基操作切換到變基前的分支上。
#######變基操作的過(guò)程:
- 首先會(huì)執(zhí)行:command:`git checkout`切換到<till>。
因?yàn)闀?huì)切換到<till>寡夹,因此如果<till>指向的不是一個(gè)分支(如master)处面,則變基操作是在detached HEAD(分離頭指針)狀態(tài)進(jìn)行的,當(dāng)變基結(jié)束后菩掏,還要像在“時(shí)間旅行一”中那樣魂角,對(duì)master分支執(zhí)行重置以實(shí)現(xiàn)把變基結(jié)果記錄在分支中。
- 將<since>..<till>所標(biāo)識(shí)的提交范圍寫到一個(gè)臨時(shí)文件中智绸。<since>..<till>是指包括<till>的所有歷史提交排除<since>以及<since>的歷史提交后形成的版本范圍野揪。
- 當(dāng)前分支強(qiáng)制重置(git reset --hard)到<newbase>。
相當(dāng)于執(zhí)行::command:`git reset --hard <newbase>`传于。
- 從保存在臨時(shí)文件中的提交列表中囱挑,一個(gè)一個(gè)將提交按照順序重新提交到重置之后的分支上。
- 如果遇到提交已經(jīng)在分支中包含沼溜,跳過(guò)該提交平挑。
- 如果在提交過(guò)程遇到?jīng)_突,變基過(guò)程暫停系草。用戶解決沖突后通熄,執(zhí)行:command:`git rebase --continue`繼續(xù)變基操作≌叶迹或者執(zhí)行:command:`git rebase --skip`跳過(guò)此提交唇辨。或者執(zhí)行:command:`git rebase --abort`就此終止變基操作切換到變基前的分支上能耻。
現(xiàn)在我們了解了大致過(guò)程赏枚,先執(zhí)行上一節(jié)中的刪除D。
$ git rebase --onto C E^ F
First, rewinding head to replay your work on top of it...
Applying: five
Using index info to reconstruct a base tree...
M read.txt
Falling back to patching base and 3-way merge...
Auto-merging read.txt
CONFLICT (content): Merge conflict in read.txt
error: Failed to merge in the changes.
Patch failed at 0001 five
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
發(fā)現(xiàn)出現(xiàn)沖突晓猛,下面有3個(gè)方案饿幅,一是修改沖突后,繼續(xù)戒职,而是跳過(guò)該提交栗恩,三是取消變基操作。我們分別來(lái)試驗(yàn)一下洪燥。
1.修改沖突
$ vi read.txt
$ git add read.txt //注意修改后磕秤,需要加入暫存區(qū)
$ git rebase --continue
Applying: five
Applying: six
Using index info to reconstruct a base tree...
M read.txt
Falling back to patching base and 3-way merge...
Auto-merging read.txt
$ git log --oneline --decorate
1cf52e2 (HEAD) six
f29de0d five
76adf28 (tag: C) three
cc624c3 (tag: B) second
20ad652 (tag: A) first
$ git branch
- (HEAD detached from refs/heads/master) //此時(shí)在分離頭指針狀態(tài)下
master
2.跳過(guò)
$ git rebase --skip
Applying: six
Using index info to reconstruct a base tree...
M read.txt
Falling back to patching base and 3-way merge...
Auto-merging read.txt
CONFLICT (content): Merge conflict in read.txt
error: Failed to merge in the changes.
Patch failed at 0002 six
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
$ vi read.txt
$ git add read.txt
$ git rebase --continue
Applying: six
$ git log --oneline --decorate
ec67d91 (HEAD) six
76adf28 (tag: C) three
cc624c3 (tag: B) second
20ad652 (tag: A) first
此時(shí)發(fā)現(xiàn)跳過(guò)之后,E提交不在了捧韵。
3.取消 rebase
$ git rebase --abort
$ git log --oneline --decorate
5934eeb (HEAD, tag: F, master) six
99f7448 (tag: E) five
476e3d5 (tag: D) four
76adf28 (tag: C) three
cc624c3 (tag: B) second
20ad652 (tag: A) first
試試合并C和D
$ git checkout D
Previous HEAD position was 5934eeb... six
HEAD is now at 476e3d5... four
$ git reset --soft HEAD^^
$ git commit -C C
$ git tag newbase
$ git rebase --onto newbase E^ master //變基操命令行沒(méi)有像之前的操作使用使用了參數(shù)F市咆,而是使用分支master。所以接下來(lái)的變基操作會(huì)直接修改master分支再来,而無(wú)須再進(jìn)行對(duì)master的重置操作蒙兰。
$ git rebase --onto newbase E^ master
First, rewinding head to replay your work on top of it...
Applying: five
Applying: six
$ git log --oneline --decorate
f4b4a50 (HEAD -> master) six
7388125 five
94e64aa (tag: newbase) three
cc624c3 (tag: B) second
20ad652 (tag: A) first
- 看看提交日志,看到提交C和提交D都不見了,代之以融合后的提交newbase癞己。還可以看到最新的提交除了和HEAD的指向一致膀斋,也和master分支的指向一致。
#####3.rebase加強(qiáng)版
在rebase操作上我們可以增加-i參數(shù)痹雅,進(jìn)入一個(gè)交互界面仰担。
首先我們按照上兩節(jié)要求刪除D
$git rebase -i C
pick 476e3d5 four
pick 99f7448 five
pick 5934eeb six
Rebase 76adf28..5934eeb onto 76adf28 (3 commands)
Commands:
p, pick = use commit
r, reword = use commit, but edit the commit message
e, edit = use commit, but stop for amending
s, squash = use commit, but meld into previous commit
f, fixup = like "squash", but discard this commit's log message
x, exec = run command (the rest of the line) using shell
d, drop = remove commit
These lines can be re-ordered; they are executed from top to bottom.
If you remove a line here THAT COMMIT WILL BE LOST.
However, if you remove everything, the rebase will be aborted.
Note that empty commits are commented out
~
"~/cherry/.git/rebase-merge/git-rebase-todo" 22L, 692C
于是我們進(jìn)入一個(gè)編輯界面,我們可以看到下面有很多命令绩社。
- 開頭的四行由上到下依次對(duì)應(yīng)于提交C摔蓝、D、E愉耙、F贮尉。
- 前四行缺省的動(dòng)作都是pick,即應(yīng)用此提交朴沿。
- 參考配置文件中的注釋猜谚,可以通過(guò)修改動(dòng)作名稱,在變基的時(shí)候執(zhí)行特定操作赌渣。
- 動(dòng)作reword或者簡(jiǎn)寫為r魏铅,含義是變基時(shí)應(yīng)用此提交,但是在提交的時(shí)候允許用戶修改提交說(shuō)明坚芜。
- 動(dòng)作edit或者簡(jiǎn)寫為e览芳,也會(huì)應(yīng)用此提交,但是會(huì)在應(yīng)用時(shí)停止鸿竖,提示用戶使用:command:`git commit --amend`執(zhí)行提交沧竟,以便對(duì)提交進(jìn)行修補(bǔ)。
- 當(dāng)用戶執(zhí)行:command:`git commit --amend`完成提交后缚忧,還需要執(zhí)行:command:`git rebase --continue`繼續(xù)變基操作悟泵。Git會(huì)對(duì)用戶進(jìn)行相應(yīng)地提示。
- 實(shí)際上用戶在變基暫停狀態(tài)執(zhí)行修補(bǔ)提交可以執(zhí)行多次搔谴,相當(dāng)于把一個(gè)提交分解為多個(gè)提交魁袜。而且edit動(dòng)作也可以實(shí)現(xiàn)reword的動(dòng)作桩撮,因此對(duì)于老版本的Git沒(méi)有reword可用敦第,則可以使用此動(dòng)作。
- 動(dòng)作squash或者簡(jiǎn)寫為s店量,該提交會(huì)與前面的提交壓縮為一個(gè)芜果。
- 動(dòng)作fixup或者簡(jiǎn)寫為f,類似squash動(dòng)作融师,但是此提交的提交說(shuō)明被丟棄右钾。
- 可以通過(guò)修改配置文件中這四個(gè)提交的先后順序,進(jìn)而改變最終變基后提交的先后順序。
- 可以對(duì)相應(yīng)提交對(duì)應(yīng)的行執(zhí)行刪除操作舀射,這樣該提交就不會(huì)被應(yīng)用窘茁,進(jìn)而在變基后的提交中被刪除。
接下來(lái)我們刪除其中的D的那一行脆烟,保存,于是就成功刪除了山林。
pick 99f7448 five
pick 5934eeb six
Successfully rebased and updated refs/heads/master.
合并又是怎么做的呢?
$ rebase -i C^
pick 76adf28 three
squash 476e3d5 four
pick 99f7448 five
pick 5934eeb six
下面顯示了合并信息邢羔。
This is a combination of 2 commits.
This is the 1st commit message:
three
This is the commit message #2:
four
#####4.revert
前面介紹的操作都涉及到對(duì)歷史的修改驼抹,這對(duì)于一個(gè)人使用Git沒(méi)有問(wèn)題,但是如果多人協(xié)同就會(huì)有問(wèn)題了拜鹤。在這種情況下要想修正一個(gè)錯(cuò)誤歷史提交的正確做法是反轉(zhuǎn)提交框冀,即重新做一次新的提交,相當(dāng)于錯(cuò)誤的歷史提交的反向提交敏簿,修正錯(cuò)誤的歷史提交明也。
當(dāng)前版本庫(kù)最新的提交包含如下改動(dòng):
$ git show HEAD
commit 5934eeb5b5c53308fb4d09fae00e5395231e2353
Author: jkbaihang 909920027@qq.com
Date: Tue May 16 10:33:48 2017 +0800
six
diff --git a/read.txt b/read.txt
index 8fda00d..cead32e 100644
--- a/read.txt
+++ b/read.txt
@@ -3,3 +3,4 @@ B
C
D
E
+F
在不改變這個(gè)提交的前提下對(duì)其修改進(jìn)行撤銷,就需要用到git revert反轉(zhuǎn)提交惯裕。
$ git Revert "six revert"
This reverts commit 5934eeb5b5c53308fb4d09fae00e5395231e2353.
可以在編輯器中修改提交說(shuō)明诡右,提交說(shuō)明編輯完畢保存退出則完成反轉(zhuǎn)提交。查看提交日志可以看到新的提交相當(dāng)于所撤銷提交的反向提交轻猖。
[master de89819] Revert "six revert"
1 file changed, 1 deletion(-)
$ git log --stat -2
commit de89819510c78833caa438c841e23c2c60e467a3
Author: jkbaihang 909920027@qq.com
Date: Tue May 16 23:30:26 2017 +0800
Revert "six revert"
This reverts commit 5934eeb5b5c53308fb4d09fae00e5395231e2353.
read.txt | 1 -
1 file changed, 1 deletion(-)
commit 5934eeb5b5c53308fb4d09fae00e5395231e2353
Author: jkbaihang 909920027@qq.com
Date: Tue May 16 10:33:48 2017 +0800
six
read.txt | 1 +
1 file changed, 1 insertion(+)
#10.Git克隆
Git的版本庫(kù)目錄和工作區(qū)在一起帆吻,因此存在一損俱損的問(wèn)題,即如果刪除一個(gè)項(xiàng)目的工作區(qū)咙边,同時(shí)也會(huì)把這個(gè)項(xiàng)目的版本庫(kù)刪除掉猜煮。一個(gè)項(xiàng)目?jī)H在一個(gè)工作區(qū)中維護(hù)太危險(xiǎn)了,如果有兩個(gè)工作區(qū)就會(huì)好很多败许。
![Screen Shot 2017-05-19 at 3.33.12 PM.png](http://upload-images.jianshu.io/upload_images/1485048-ce416eb5e20ef5c4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
- 版本庫(kù)A通過(guò)克隆操作創(chuàng)建克隆版本庫(kù)B王带。
- 版本庫(kù)A可以通過(guò)推送(PUSH)操作,將新提交傳遞給版本庫(kù)B市殷;
- 版本庫(kù)A可以通過(guò)拉回(PULL)操作愕撰,將版本庫(kù)B中的新提交拉回到自身(A)。
- 版本庫(kù)B可以通過(guò)拉回(PULL)操作醋寝,將版本庫(kù)A中的新提交拉回到自身(B)搞挣。
- 版本庫(kù)B可以通過(guò)推送(PUSH)操作,將新提交傳遞給版本庫(kù)A音羞;
###Clone
Git使用:command:`git clone`命令實(shí)現(xiàn)版本庫(kù)克隆囱桨,主要有如下三種用法:
>用法1: git clone <repository> <directory>
>用法2: git clone --bare <repository> <directory.git>
>用法3: git clone --mirror <repository> <directory.git>
- 用法1將<repository>指向的版本庫(kù)創(chuàng)建一個(gè)克隆到:file:`<directory>`目錄。目錄:file:`<directory>`相當(dāng)于克隆版本庫(kù)的工作區(qū)嗅绰,文件都會(huì)檢出舍肠,版本庫(kù)位于工作區(qū)下的:file:`.git`目錄中搀继。
- 用法2和用法3創(chuàng)建的克隆版本庫(kù)都不含工作區(qū),直接就是版本庫(kù)的內(nèi)容翠语,這樣的版本庫(kù)稱為裸版本庫(kù)叽躯。一般約定俗成裸版本庫(kù)的目錄名以:file:`.git`為后綴,所以上面示例中將克隆出來(lái)的裸版本庫(kù)目錄名寫做:file:`<directory.git>`肌括。
- 用法3區(qū)別于用法2之處在于用法3克隆出來(lái)的裸版本對(duì)上游版本庫(kù)進(jìn)行了注冊(cè)险毁,這樣可以在裸版本庫(kù)中使用:command:`git fetch`命令和上游版本庫(kù)進(jìn)行持續(xù)同步。
用法3只在 1.6.0 或更新版本的Git才提供们童。
###Remote
為了便于管理畔况,Git要求每個(gè)遠(yuǎn)程主機(jī)都必須指定一個(gè)主機(jī)名。`git remote`命令就用于管理主機(jī)名慧库。
不帶選項(xiàng)的時(shí)候跷跪,`git remote`命令列出所有遠(yuǎn)程主機(jī)。
$ git remote
origin
使用`-v`選項(xiàng)齐板,可以參看遠(yuǎn)程主機(jī)的網(wǎng)址吵瞻。
$ git remote -v
origin /users/baihang/user1 (fetch)
origin /users/baihang/user1 (push)
上面命令表示,當(dāng)前只有一臺(tái)遠(yuǎn)程主機(jī)甘磨,叫做`origin`橡羞,以及它的網(wǎng)址。
`git remote add`命令用于添加遠(yuǎn)程主機(jī)济舆。
$ git remote add <主機(jī)名> <網(wǎng)址>
`git remote rm`命令用于刪除遠(yuǎn)程主機(jī)卿泽。
$ git remote rm <主機(jī)名>
`git remote rename`命令用于遠(yuǎn)程主機(jī)的改名。
$ git remote rename <原主機(jī)名> <新主機(jī)名>
###Fetch
一旦遠(yuǎn)程主機(jī)的版本庫(kù)有了更新(Git術(shù)語(yǔ)叫做commit)滋觉,需要將這些更新取回本地签夭,這時(shí)就要用到`git fetch`命令。
$ git fetch <遠(yuǎn)程主機(jī)名>
上面命令將某個(gè)遠(yuǎn)程主機(jī)的更新椎侠,全部取回本地第租。
`git fetch`命令通常用來(lái)查看其他人的進(jìn)程,因?yàn)樗』氐拇a對(duì)你本地的開發(fā)代碼沒(méi)有影響我纪。
默認(rèn)情況下慎宾,`git fetch`取回所有分支(branch)的更新。如果只想取回特定分支的更新浅悉,可以指定分支名趟据。
$ git fetch <遠(yuǎn)程主機(jī)名> <分支名>
比如,取回origin主機(jī)的master分支仇冯。
$ git fetch origin master
所取回的更新之宿,在本地主機(jī)上要用"遠(yuǎn)程主機(jī)名/分支名"的形式讀取族操。比如`origin`主機(jī)的`master`苛坚,就要用`origin/master`讀取比被。
git branch命令的-r選項(xiàng),可以用來(lái)查看遠(yuǎn)程分支泼舱,-a選項(xiàng)查看所有分支等缀。
$ git branch -r
origin/master
$ git branch -a
- master
remotes/origin/master
上面命令表示,本地主機(jī)的當(dāng)前分支是`master`娇昙,遠(yuǎn)程分支是`origin/master`尺迂。
取回遠(yuǎn)程主機(jī)的更新以后,可以在它的基礎(chǔ)上冒掌,使用git checkout命令創(chuàng)建一個(gè)新的分支噪裕。
$ git checkout -b newBrach origin/master
上面命令表示,在`origin/master`的基礎(chǔ)上股毫,創(chuàng)建一個(gè)新分支膳音。
此外,也可以使用`git merge`命令或者`git rebase`命令铃诬,在本地分支上合并遠(yuǎn)程分支祭陷。
$ git merge origin/master
或者
$ git rebase origin/master
上面命令表示在當(dāng)前分支上,合并origin/master趣席。
###Push
`git push`命令用于將本地分支的更新兵志,推送到遠(yuǎn)程主機(jī)。它的格式與`git pull`命令相仿宣肚。
$ git push <遠(yuǎn)程主機(jī)名> <本地分支名>:<遠(yuǎn)程分支名>
注意想罕,分支推送順序的寫法是<來(lái)源地>:<目的地>,所以`git pull`是<遠(yuǎn)程分支>:<本地分支>霉涨,而`git push`是<本地分支>:<遠(yuǎn)程分支>弧呐。
如果省略遠(yuǎn)程分支名,則表示將本地分支推送與之存在"追蹤關(guān)系"的遠(yuǎn)程分支(通常兩者同名)嵌纲,如果該遠(yuǎn)程分支不存在俘枫,則會(huì)被新建。
$ git push origin master
上面命令表示逮走,將本地的`master`分支推送到`origin`主機(jī)的`master`分支鸠蚪。如果后者不存在,則會(huì)被新建师溅。
如果省略本地分支名茅信,則表示刪除指定的遠(yuǎn)程分支,因?yàn)檫@等同于推送一個(gè)空的本地分支到遠(yuǎn)程分支墓臭。
$ git push origin :master
等同于
$ git push origin --delete master
上面命令表示刪除`origin`主機(jī)的`master`分支蘸鲸。
如果當(dāng)前分支與遠(yuǎn)程分支之間存在追蹤關(guān)系,則本地分支和遠(yuǎn)程分支都可以省略窿锉。
$ git push origin
如果當(dāng)前分支只有一個(gè)追蹤分支酌摇,那么主機(jī)名都可以省略膝舅。
$ git push
如果當(dāng)前分支與多個(gè)主機(jī)存在追蹤關(guān)系,則可以使用-u選項(xiàng)指定一個(gè)默認(rèn)主機(jī)窑多,這樣后面就可以不加任何參數(shù)使用`git push`仍稀。
$ git push -u origin master
上面命令將本地的master分支推送到origin主機(jī),同時(shí)指定`origin`為默認(rèn)主機(jī)埂息,后面就可以不加任何參數(shù)使用`git push`了技潘。
###Pull
git pull命令的作用是,取回遠(yuǎn)程主機(jī)某個(gè)分支的更新千康,再與本地的指定分支合并待错。它的完整格式稍稍有點(diǎn)復(fù)雜问畅。
$ git pull <遠(yuǎn)程主機(jī)名> <遠(yuǎn)程分支名>:<本地分支名>
比如,取回`origin`主機(jī)的`next`分支,與本地的`master`分支合并玻熙,需要寫成下面這樣钓试。
$ git pull origin next:master
如果遠(yuǎn)程分支是與當(dāng)前分支合并造垛,則冒號(hào)后面的部分可以省略顺囊。
$ git pull origin next
上面命令表示,取回`origin/next`分支砂碉,再與當(dāng)前分支合并蛀蜜。實(shí)質(zhì)上,這等同于先做`git fetch`增蹭,再做`git merge`滴某。
$ git fetch origin
$ git merge origin/next
在某些場(chǎng)合,Git會(huì)自動(dòng)在本地分支與遠(yuǎn)程分支之間滋迈,建立一種追蹤關(guān)系(tracking)霎奢。比如,在`git clone`的時(shí)候饼灿,所有本地分支默認(rèn)與遠(yuǎn)程主機(jī)的同名分支幕侠,建立追蹤關(guān)系,也就是說(shuō)碍彭,本地的`master`分支自動(dòng)"追蹤"`origin/master`分支晤硕。
Git也允許手動(dòng)建立追蹤關(guān)系。
git branch --set-upstream master origin/next
上面命令指定master分支追蹤`origin/next`分支庇忌。
如果當(dāng)前分支與遠(yuǎn)程分支存在追蹤關(guān)系舞箍,`git pull`就可以省略遠(yuǎn)程分支名。
$ git pull origin
上面命令表示皆疹,本地的當(dāng)前分支自動(dòng)與對(duì)應(yīng)的origin主機(jī)"追蹤分支"(remote-tracking branch)進(jìn)行合并疏橄。
如果當(dāng)前分支只有一個(gè)追蹤分支,連遠(yuǎn)程主機(jī)名都可以省略略就。
$ git pull
上面命令表示捎迫,當(dāng)前分支自動(dòng)與唯一一個(gè)追蹤分支進(jìn)行合并晃酒。
####對(duì)等工作區(qū)
![](http://upload-images.jianshu.io/upload_images/1485048-4fb7cd30ca127ccf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我們先創(chuàng)建一個(gè)版本庫(kù),和user2空目錄立砸。
$ git init user1
Initialized empty Git repository in /Users/baihang/user1/.git/
$ mkdir user2
接下來(lái)我們進(jìn)入user1掖疮,初始一個(gè)空的提交初茶,然后克隆到user2颗祝。
$ cd user1
$git ci --allow-empty -m "init commit"
[master (root-commit) 50fa2fc] init commit
$ git clone /users/baihang/user1 /users/baihang/user2
Cloning into '/users/baihang/user2'...
done.
在user1生成一些測(cè)試提交
git commit --allow-empty -m "test1"
[master 0ed19a0] test1
$ git commit --allow-empty -m "test2"
[master 0efd5b0] test2
這個(gè)時(shí)候我們能執(zhí)行push試試
git push /users/baihang/user2
fatal: The current branch master has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream /users/baihang/user2 master
這我們說(shuō)明一下upstream是什么
>git中存在upstream和downstream,簡(jiǎn)言之恼布,當(dāng)我們把倉(cāng)庫(kù)A中某分支x的代碼push到倉(cāng)庫(kù)B分支y螺戳,此時(shí)倉(cāng)庫(kù)B的這個(gè)分支y就叫做A中x分支的upstream,而x則被稱作y的downstream折汞,這是一個(gè)相對(duì)關(guān)系倔幼,每一個(gè)本地分支都相對(duì)地可以有一個(gè)遠(yuǎn)程的upstream分支(注意這個(gè)upstream分支可以不同名,但通常我們都會(huì)使用同名分支作為upstream)爽待。
************
初次提交本地分支损同,例如git push origin develop操作,并不會(huì)定義當(dāng)前本地分支的upstream分支鸟款,我們可以通過(guò)git push --set-upstream origin develop膏燃,關(guān)聯(lián)本地develop分支的upstream分支。
接下來(lái)我們關(guān)聯(lián)后何什,得出一大推內(nèi)容组哩。
$ git push --set-upstream /users/baihang/user2 master
Counting objects: 2, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 257 bytes | 0 bytes/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: error: refusing to update checked out branch: refs/heads/master
remote: error: By default, updating the current branch in a non-bare repository
remote: is denied, because it will make the index and work tree inconsistent
remote: with what you pushed, and will require 'git reset --hard' to match
remote: the work tree to HEAD.
remote:
remote: You can set 'receive.denyCurrentBranch' configuration variable to
remote: 'ignore' or 'warn' in the remote repository to allow pushing into
remote: its current branch; however, this is not recommended unless you
remote: arranged to update its work tree to match what you pushed in some
remote: other way.
remote:
remote: To squelch this message and still keep the default behaviour, set
remote: 'receive.denyCurrentBranch' configuration variable to 'refuse'.
To /users/baihang/user2
! [remote rejected] master -> master (branch is currently checked out)
error: failed to push some refs to '/users/baihang/user2'
翻譯成中文意思如下
$ git push --set-upstream /users/baihang/user2 master
...
對(duì)方說(shuō): 錯(cuò)了:
拒絕更新已檢出的分支 refs/heads/master 。
缺省更新非裸版本庫(kù)的當(dāng)前分支是不被允許的处渣,因?yàn)檫@將會(huì)導(dǎo)致
暫存區(qū)和工作區(qū)與您推送至版本庫(kù)的新提交不一致伶贰。這太古怪了。
如果您一意孤行罐栈,也不是不允許黍衙,但是您需要為我設(shè)置如下參數(shù):
receive.denyCurrentBranch = ignore|warn
到/users/baihang/user2
! [對(duì)方拒絕] master -> master (分支當(dāng)前已檢出)
錯(cuò)誤: 部分引用的推送失敗了, 至 '/users/baihang/user2'
從錯(cuò)誤輸出可以看出,雖然可以改變Git的缺省行為荠诬,允許向工作區(qū)推送已經(jīng)檢出的分支们豌,但是這么做實(shí)在不高明。
我們?cè)囋嚨絺溆脦?kù)中執(zhí)行pull命令,可以看到同步浅妆。
$ cd user2
$ git pull
From /users/baihang/user1
50fa2fc..0efd5b0 master -> origin/master
Updating 50fa2fc..0efd5b0
Fast-forward
$ git log --oneline -2
0efd5b0 test2
0ed19a0 test1
#####為什么執(zhí)行 git pull 拉回命令沒(méi)有像執(zhí)行 git push 命令那樣提供那么多的參數(shù)呢望迎?
這是因?yàn)樵趫?zhí)行:command:`git clone`操作后,克隆出來(lái)的user2版本庫(kù)中對(duì)源版本庫(kù)進(jìn)行了注冊(cè)凌外,所以當(dāng)在 user2版本庫(kù)執(zhí)行拉回操作辩尊,無(wú)須設(shè)置源版本庫(kù)的地址。
可以使用下面的命令查看對(duì)上游版本庫(kù)的注冊(cè)信息:
git remote -v
origin /users/baihang/user1 (fetch)
origin /users/baihang/user1 (push)
實(shí)際注冊(cè)上游遠(yuǎn)程版本庫(kù)的奧秘都在Git的配置文件中(略去無(wú)關(guān)的行):
$ cat .git/config
[core]
...
[remote "origin"]
url = /users/baihang/user1
fetch = +refs/heads/:refs/remotes/origin/
[branch "master"]
remote = origin
merge = refs/heads/master
####克隆生成裸版本庫(kù)
上一節(jié)在對(duì)等工作區(qū)模式下康辑,工作區(qū)之間執(zhí)行推送摄欲,可能會(huì)引發(fā)大段的錯(cuò)誤輸出轿亮,如果采用裸版本庫(kù)則沒(méi)有相應(yīng)的問(wèn)題。這是因?yàn)槁惆姹編?kù)沒(méi)有工作區(qū)胸墙。沒(méi)有工作區(qū)還有一個(gè)好處就是空間占用會(huì)更小我注。
![Screen Shot 2017-05-19 at 4.25.46 PM.png](http://upload-images.jianshu.io/upload_images/1485048-93bec42a97f8f131.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
我們克隆一個(gè)裸版本庫(kù),以.git后綴命名(裸版本庫(kù)以.git為后綴)迟隅。
$ git clone --bare /users/baihang/user1 /users/baihang/user1.git
Cloning into bare repository '/users/baihang/user1.git'...