Git版本控制管理@[TOC]
git
Git is a distributed version control system.
知識(shí)點(diǎn)
版本回退
1.工作區(qū)和暫存區(qū)
工作區(qū): 電腦里的目錄
版本庫(kù): .git
版本庫(kù)中含有暫存區(qū)(stage)
git add 將文件添加到暫存區(qū)
git commit 將文件提交到本地分支
劃重點(diǎn)
git checkout -- <file> (注意 這里有一個(gè)空格) 用于丟棄工作區(qū)的修改
情況1. readme.md 還沒(méi)有被放到暫存區(qū),撤銷修改后和版本庫(kù)一模一樣
情況2. readme.md 已經(jīng)添加到暫存區(qū)后,又作了修改,現(xiàn)在,撤銷修改就回到添加到暫存區(qū)后的狀態(tài)
git reset HEAD <file> 將暫存區(qū)的修改撤銷,放回工作區(qū)
具體而言: git reset調(diào)整HEAD引用指向給定的提交,默認(rèn)情況下還會(huì)更新索引以匹配該提交
如果我們希望工作區(qū) 暫存區(qū)都沒(méi)有進(jìn)行修改
先進(jìn)行 git reset 再進(jìn)行 git checkout
2.刪除文件 git rm
在文件管理器中刪除沒(méi)用的文件 rm test.txt
此時(shí)工作區(qū)和版本庫(kù)就不一致了,git status命令會(huì)立刻告訴你哪些文件被刪除了:
$ 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: test.txt
no changes added to commit (use "git add" and/or "git commit -a")
git rm: 將在版本庫(kù)和工作目錄中同時(shí)刪除文件
注意: git rm 是一條對(duì)索引進(jìn)行操作的命令
git rm --cached 可以將誤添加的文件恢復(fù)為未添加狀態(tài)
刪除索引中的文件并把它保留在工作目錄中,git rm則會(huì)將文件從索引目錄和工作目錄中都刪除
$ git status
On branch master
You are currently rebasing branch 'dev' on 'b79e97b'.
(all conflicts fixed: run "git rebase --continue")
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: readme.md
Untracked files:
(use "git add <file>..." to include in what will be committed)
opps.md
3. 移動(dòng)文件 git mv
如果你需要移動(dòng)或者重命名文件,可以執(zhí)行
$ mv stuff newstuff
$ git rm stuff
$ git add newstuff
天吶,移動(dòng)一個(gè)文件居然三部才能完成????
使用git mv 命令 ,將文件stuff 重新命名為 newstuff
git mv stuff newstuff
4.查看提交的歷史記錄 git log
git log newstuff
如果你剛好重新命名了一個(gè)文件,你會(huì)發(fā)現(xiàn)除了命名之后的記錄之外之前文件的記錄都丟了
Git其實(shí)是記的全部的歷史記錄的,但是顯示要限制于在命令中指定的文件名; --follow選項(xiàng)會(huì)讓git在日志中回溯并找到內(nèi)容相關(guān)聯(lián)的整個(gè)歷史記錄;
$ git log --follow newstuff
git log 提交范圍
git log ^ XY 等同于 git log X...Y 可以認(rèn)為是集合減法
用Y之前的所有提交減去X之前的所有提交且包括X
范圍: (X,Y]
topic...master 表示排除從topic分支可達(dá)的提交記錄
情境一

此時(shí)包含的提交記錄是 V W X Y Z
情境二

此時(shí)包含的提交記錄是 W X Y Z
版本庫(kù)搜索(強(qiáng)大的命令)
1. 使用git bisect命令 (啟動(dòng)二分搜索)
步驟:
// 1. 啟動(dòng)二分搜索,Git進(jìn)入二分模式,并為自己設(shè)置一些狀態(tài)信息, 一旦啟動(dòng),需要告訴git哪一個(gè)是壞的
$ git bisect start
// 2. 可以默認(rèn)使用當(dāng)前分支提交作為壞提交
$ git bisect bad
// 3. 同樣需要告訴git 哪一個(gè)是好的
$ git bisect good v1.0.4
2. 識(shí)別特定提交 git blame
$ git blame -L 35 , init/version.c
分支管理
1.創(chuàng)建& 合并分支 git checkout / git merge
創(chuàng)建分支實(shí)際上是改變指針HEAD的指向,工作區(qū)文件沒(méi)有任何變化

合并分支
$ git checkout master
Switched to branch 'master'
$ git merge dev //將dev合并到master
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
2.解決沖突
出現(xiàn)在合并分支的時(shí)候
我們到底應(yīng)該保留哪一部分呢? 到底哪一部分才是最新的修改? 天啊 我分不清了
此時(shí)處于master分支 ,合并了feature1的內(nèi)容
注意現(xiàn)在
<<<<<< HEAD
=======
這部分的內(nèi)容 , 是master分支上的, 下面的這個(gè)>>>>>>> feature1 才是合并進(jìn)來(lái)的分支上的內(nèi)容
<<<<<< HEAD
我不是亂碼
=======
djadjsjd
>>>>>> feature1
圖形化查看提交記錄
git log --graph
使用 --no-ff 采取普通模式合并,合并后的歷史有分支,能看出曾經(jīng)做過(guò)合并
3 刪除分支
- 刪除特定未合并分支
git branch -d <branch-name>
git可能提示銷毀失敗,因?yàn)榉种н€沒(méi)有被合并,如果刪除,將丟失更改 - 強(qiáng)行刪除分支
git branch -d <branch-name>
告訴我們一件事情: 開(kāi)發(fā)一個(gè)新功能,就新建一個(gè)分支 - 恢復(fù)刪除的分支
意外刪除分支或其它引用后,使用 git reflog 命令來(lái)恢復(fù)
git reflog -g // 找到所有commitID
根據(jù)操作的提示信息,回到某一次提交 將修改應(yīng)用在一個(gè)新分支上
git branch [newbranch] [commitID]
4. 處理本地和遠(yuǎn)程分支的關(guān)系
例子: 在dev分支上進(jìn)行開(kāi)發(fā),必須創(chuàng)建遠(yuǎn)程 origin的dev分支到本地
創(chuàng)建遠(yuǎn)程dev分支到本地
$ git checkout -b dev origin/dev
5. rebase 命令
變基操作一次只遷移一個(gè)提交,從各自原始提交為止遷移到新的提交基礎(chǔ);
圖解


變基與合并
變基的一系列提交會(huì)導(dǎo)致Git生成一系列全新的提交,擁有新的SHA1提交ID,基于新的初始狀態(tài),代表不同的差異;
??注意:如果有額外的分支基于你想變基的分支,可能會(huì)產(chǎn)生問(wèn)題;
例子:
# 將dev分支移動(dòng)到master分支的頭
$ git rebase master dev
實(shí)際得到的結(jié)果
如果你想要的是 將dev_2變基到dev最新的提交Z,意想得到的結(jié)果是這樣的
這種情況下,如果你更希望的是移動(dòng)整個(gè)分支(包括該分支的子分支),需要反過(guò)來(lái)把dev2分支變基到dev分支的新提交Y’上面
$ git rebase dev^ dev2
重要的幾個(gè)概念
- 變基將提交重寫(xiě)成新提交
- 不可達(dá)的舊提交會(huì)消失;
- 任何舊的、變基前的提交的用戶可能被困?。?/li>
- 如果有分支用變基前的提交,可能需要反過(guò)來(lái)對(duì)它變基;
- 如果有用戶有不同版本庫(kù)中變基前的提交,即使它已經(jīng)移動(dòng)到你的版本庫(kù)中,它仍然擁有該提交的副本,該用戶現(xiàn)在也必須修復(fù)他的提交歷史記錄。
用書(shū)中這個(gè)復(fù)雜的例子解釋一下這幾句話:
$ git rebase master dev
First, rewinding head to replay your work on top of it...
Applying: X
Applying: Y
Applying: Z
Applying: P
Applying: N
$ git show-branch
* [dev] N
![master] D
--
* [dev] N
* [dev^] P
* [dev~2] Z
* [dev~3] Y
* [dev~4] X
* [dev~5] D
# 現(xiàn)在所有的提交連成了一串
git 應(yīng)用了所有的(非合并)提交變更;發(fā)生的過(guò)程:
在master ...dev 范圍內(nèi)找提交。為了列出所有提交,Git對(duì)圖中的那部分執(zhí)行拓?fù)渑判颍a(chǎn)生該范圍內(nèi)所有提交的一個(gè)線性序列;
【其實(shí)我也沒(méi)有明白 - - 】
diff命令
一個(gè)根級(jí)別的diiff可以有效的將兩個(gè)版本庫(kù)進(jìn)行同步
顯示工作目錄和給定提交之間的差異
1. git diff commit
比較SVN 和 Git 如何產(chǎn)生 diff
git: 每個(gè)修訂版本都有自己的一棵樹(shù),但git不需要它們來(lái)生成diff , Git可以直接操作兩個(gè)版本的完整狀態(tài)快照
svn:跟蹤修訂一系列版本,只儲(chǔ)存文件內(nèi)的差異 查看r1008 與r1908間的diff,svn會(huì)查看兩個(gè)版本之間所有單獨(dú)的diff,然后合成一個(gè)大的diff,把結(jié)果發(fā)送給用戶
合并
1. 合并策略
解決(resolve): 解決策略只操作兩個(gè)分支。定位共同的祖先作為合并基礎(chǔ),然后執(zhí)行一個(gè)直接的三方合并;
遞歸(Recursive): 遞歸策略和解決策略相似,每次處理兩個(gè)分支; 但它可以處理在兩個(gè)分支之間有多個(gè)合并基礎(chǔ)的情況; Git生成一個(gè)臨時(shí)合并來(lái)包含所有相同的合并基礎(chǔ),以此為基礎(chǔ)通過(guò)一個(gè)普通的三方合并算法導(dǎo)出兩個(gè)給定分支的最終合并;
章魚(yú)(Octopus): 專為合并兩個(gè)以上分支而設(shè)計(jì); 在內(nèi)部它需要多次調(diào)用遞歸合并策略,要合并的每個(gè)分支調(diào)一次;
2. 應(yīng)用合并策略
Git會(huì)盡可能嘗試使用簡(jiǎn)單廉價(jià)的算法,如果可能,首先嘗試使用“已經(jīng)是最新的”和“快進(jìn)”策略來(lái)消除不重要的、簡(jiǎn)單的情況;
終止或重新啟動(dòng)合并
a. 如果你開(kāi)始合并,但是因?yàn)槟撤N原因你不想完成它,git提供 如下命令來(lái)終止合并
$ git reset --hard HEAD
這條命令可以立即把工作目錄和索引都還原到 git merge 之前的狀態(tài)
b. 如果要中止或在它已經(jīng)結(jié)束(即 引入一個(gè)新的合并提交)后放棄, 使用:
$ git reset --hard ORIG_HEAD
在開(kāi)始合并操作之前,Git將原始分支的HEAD 保存在 ORIG_HEAD ,就是為了這種目的
從Git 1.6.1 開(kāi)始,有另一種選擇。
如果你把沖突解決方案搞砸了,并且想再返回到嘗試解決前的原始沖突狀態(tài),可以使用 git checkout -m
更改提交
1. 關(guān)于修改歷史記錄的注意事項(xiàng)
如果一個(gè)分支已經(jīng)公開(kāi)了,并且可能已經(jīng)存在于其他版本庫(kù)中,就不應(yīng)該重寫(xiě)、修改或者更改該分支的任何部分;
2.git reset命令
git reset命令是“破壞性”的,可以覆蓋并銷毀工作目錄中的修改;
3. 使用 git cherry-pick
-
在當(dāng)前分支上應(yīng)用給定提交引入的變更
在正常開(kāi)發(fā)過(guò)程中,開(kāi)發(fā)線的提交F修復(fù)了一個(gè)bug;此時(shí)F提交也存在于版本2.3發(fā)布版中,可以對(duì)rel 2.3分支使用 git cherry-pick 來(lái)應(yīng)用bug修復(fù)
cherry-pick.png
$ git checkout rel_2.3
git cherry-pick dev~2 #commit F , above
-
重建一系列提交
通過(guò)從一個(gè)分支選一批提交,然后把它們引入一個(gè)新分支
以Y , W , X , Z 的順序應(yīng)用它們,可以使用如下命令:
執(zhí)行cherry-pick 進(jìn)行亂序之前
$ git checkout master
$ git cherry-pick my_dev^ #Y
$ git cherry-pick my_dev~3 #W
$ git cherry-pick my_dev~2 #X
$ git cherry-pick my_dev #Z

4. 使用 git revert
與git cherry-pick提交命令是大致相同的,但又一個(gè)重要區(qū)別: 應(yīng)用于給定提交的逆過(guò)程
常見(jiàn)用途是:“撤銷”可能深埋在歷史記錄中的某個(gè)提交的影響
$ git revert master~3 master向前數(shù)三個(gè)版本撤銷

儲(chǔ)藏和引用日志
git stash 命令
用于緊急修復(fù)bug 但不提交手頭的工作
git stash (save) 暫存剛才的改動(dòng)記錄
git stash list用于查看之前的工作現(xiàn)場(chǎng),按照時(shí)間由近及遠(yuǎn)的順序舉出儲(chǔ)藏棧
git stash apply 重新創(chuàng)建保存在儲(chǔ)藏棧中的上下文,但記錄歷史不會(huì)從棧中刪除;
git stash drop 丟棄儲(chǔ)藏的狀態(tài)
一旦應(yīng)用儲(chǔ)藏,合并處理了沖突,并希望工作繼續(xù),應(yīng)該使用 git stash grop 來(lái)將狀態(tài)從儲(chǔ)藏棧中刪除,否則git 將會(huì)維護(hù)一個(gè)內(nèi)容不斷增加的棧;
git stash pop = git stash apply + git stash drop
git stash apply stash@{n} n= 0,1,2.....1000... 多次stash之后恢復(fù)指定的stash
2.git stash的其他經(jīng)典應(yīng)用場(chǎng)景: 在臟的目錄中進(jìn)行拉取
場(chǎng)景復(fù)現(xiàn):
- 本地版本庫(kù)進(jìn)行開(kāi)發(fā)過(guò)程中,已經(jīng)做過(guò)多次提交,但仍然有些尚未提交的修改,這時(shí)候上游分支有了你想要的更新,但某些修改與上游分支沖突,導(dǎo)致 pul失敗,因?yàn)?code>git pull 不想覆蓋本地的新版本文件
使用git stash --include--untracked參數(shù)以便它也能儲(chǔ)藏新的未被追蹤的文件和余下的修改。確保在拉取工作目錄時(shí)是完全干凈的;
2.需要暫時(shí)移除已經(jīng)修改的工作來(lái)保證一個(gè)干凈的pull --rebase時(shí), 可以使用git stash
引用日志
確保操作會(huì)如預(yù)期般發(fā)生在計(jì)劃的分支上
git reflog 命令
查看操作歷史記錄(每一步分支切換,commit,merge都可以查看)
搞砸了一次 merge, 想再來(lái)一遍怎么辦? git reset HEAD {@1} ,根據(jù)需要可以添加 --hard 選項(xiàng)
版本庫(kù)
裸版本庫(kù) & 開(kāi)發(fā)版本庫(kù)
默認(rèn)創(chuàng)建的是 開(kāi)發(fā)版本庫(kù),用于日常常規(guī)開(kāi)發(fā);
裸版本庫(kù)角色:作為協(xié)作開(kāi)發(fā)的權(quán)威焦點(diǎn),其他開(kāi)發(fā)人員只能從裸版本庫(kù)clone,fetch,并push更新;
裸版本庫(kù)不能創(chuàng)建遠(yuǎn)程版本庫(kù);
[12章未總結(jié)完全,下次再找實(shí)踐看]
補(bǔ)丁
應(yīng)用場(chǎng)景: 一個(gè)單獨(dú)bug的修復(fù)或一個(gè)特定功能實(shí)現(xiàn)
為最近n次提交生成補(bǔ)丁 最簡(jiǎn)方式是使用 -n選項(xiàng)
$ git for
QA
1.為什么不使用 commit -a 或者 commit -m ./ ?
2. 我應(yīng)該對(duì)一系列操作進(jìn)行合并還是變基?
做你想做的。
通過(guò)合并,兩個(gè)原本獨(dú)立發(fā)展的分支合并到一起,會(huì)產(chǎn)生額外的合并提交歷史記錄更新同時(shí)存在與每一個(gè)分支的變更;
在合并期間必須解決沖突,每個(gè)分支的提交都是基于原來(lái)的提交,當(dāng)推送到上游時(shí),任何合并的歷史記錄將繼續(xù)存在;
a. 認(rèn)為是多余的合并,并不愿意看到它們弄亂歷史記錄;
b. 這些合并是開(kāi)發(fā)歷史記錄更準(zhǔn)確的寫(xiě)照,希望看到它們被保留;
變基操作: 改變了一系列提交是在何時(shí)何地開(kāi)發(fā)的概念;開(kāi)發(fā)歷史記錄的某些方面會(huì)丟失;

