進(jìn)階學(xué)習(xí)資料
本文主要介紹Git的一些進(jìn)階技巧命令的使用。
定制專屬的快捷命令
掌握Git小技巧,設(shè)置別名,簡化操作。 通過git config
命令為常用Git指令設(shè)定簡短別名。
# 如設(shè)置co別名用于checkout
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci "commit -s"
git config --global alias.st status
比如,輸入git ci
即可代替冗長的git commit -s
。隨著Git使用深入,不妨為常用命令創(chuàng)建別名。例如,為簡化取消暫存文件的操作,可自定義一個(gè)Git別名。
git config --global alias.unstage 'reset HEAD --'
以下是兩種常用的方法,主要解決將文件從暫存區(qū)移除:
# 方法一: 適用于單個(gè)文件或多文件的情況。
git reset HEAD -- fileA
# 方法二: 雖然Git沒有直接的unstage命令,可通過別名來簡化操作。
git unstage fileA
您可以使用git last
來代替git log -1 HEAD
命令,以查看最近一次的提交記錄。
git config --global alias.last 'log -1 HEAD'
這樣,可以輕松地看到最后一次提交信息:
# 這個(gè)命令是為Git配置一個(gè)全局別名last,使每次輸入git last就能顯示最近一次的提交信息。
git last
commit 66938dae3329c7aebe598c2246a8e6af90d04646 Author: Josh Goebel <dreamer3@example.com> Date: Tue Aug 26 19:48:51 2008 +0800 test for current head Signed-off-by: Scott Chacon <schacon@example.com>
代碼合并與變基
在 Git 中整合來自不同分支的修改主要有兩種方法:合并(merge) 以及變基(rebase)。
合并:對(duì)于兩個(gè)分支,如下圖:
整合分支最容易的方法是 Merge 命令。 它會(huì)把兩個(gè)分支的最新快照(C3和 C4)以及二者最近的共同祖先(C2)進(jìn)行三方合并,合并的結(jié)果是生成一個(gè)新的快照(并提交)
Rebase合并
通過提取在 C4 中引入的補(bǔ)丁和修改,然后在 C3的基礎(chǔ)上應(yīng)用一次。 在 Git 中,這種操作就叫做變基。 您可以使用 rebase命令將提交到某一分支上的所有修改都移至另一分支上。
在上面這個(gè)例子中,運(yùn)行:
git checkout experiment git rebase master First, rewinding head to replay your work on top of it... Applying: added staged command
它的原理是首先找到這兩個(gè)分支(即當(dāng)前分支 Experiment、變基操作的目標(biāo)基底分支 Master)的最近共同祖先 C2,然后對(duì)比當(dāng)前分支相對(duì)于該祖先的歷次提交,提取相應(yīng)的修改并存為臨時(shí)文件,然后將當(dāng)前分支指向目標(biāo)基底 C3, 最后以此將之前另存為臨時(shí)文件的修改依序應(yīng)用。
現(xiàn)在回到master分支,進(jìn)行一次快進(jìn)合并。
# 切換到Git倉庫的master分支。 git checkout master # 合并當(dāng)前分支到已檢出的分支。在執(zhí)行g(shù)it merge之前,確保您已經(jīng)切換到了想要合并到的目標(biāo)分支。 git merge experiment
優(yōu)雅地合并分支代碼并提交
對(duì)比兩種合并方法,rebase能讓提交歷史更整潔,呈現(xiàn)串行趨勢,因此備受青睞。它不僅能優(yōu)雅地合并代碼,還能修改提交歷史。通過rebase,您可以將本地多個(gè)開發(fā)提交合并,再整合到目標(biāo)分支,使歷史記錄更加清晰。
代碼暫存
工作時(shí),當(dāng)您忙碌于項(xiàng)目某部分時(shí),而這時(shí)想要切換到另一個(gè)分支做一點(diǎn)別的事情。 若需臨時(shí)轉(zhuǎn)至另一分支,當(dāng)下半成品提交顯得多余, 這時(shí)執(zhí)行git stash
命令可以將未完成的修改保存到一個(gè)棧上,而您可以隨時(shí)重新應(yīng)用這些改動(dòng)切換分支。
以下為例,進(jìn)入項(xiàng)目查看之前改動(dòng)的文件,顯示結(jié)果:
git status
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: index.html
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: lib/simplegit.rb
現(xiàn)在想要切換分支,但是還不想要進(jìn)行提交,運(yùn)行git stash
或git stash save
:
git stash
Saved working directory and index state \
"WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")
工作目錄變干凈了,并且剛剛的修改都不存在了:
git status
# On branch master nothing to commit, working directory clean
在這時(shí),能夠輕易地切換分支并在其他工作投入,此時(shí)修改將被存儲(chǔ)在棧上, 想要查看棧存的東西,可以執(zhí)行git stash list
:
git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
在本例中,有兩個(gè)之前做的暫存,所以接觸到了三個(gè)不同的暫存工作。 可以通過原來stash命令的幫助提示的命令將剛剛暫存的工作重新應(yīng)用:git stash apply
。
如果想要應(yīng)用其中一個(gè)更舊的暫存,可以通過名字指定它,比如:git stash apply stash@{2}
, 如果不指定一個(gè)暫存,Git認(rèn)為指定的是最近的暫存:
git stash apply
# On branch master
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
#
# modified: index.html
# modified: lib/simplegit.rb #
可以看到Git重新修改了當(dāng)您暫存時(shí)撤消的文件。 也可以運(yùn)行git stash pop
來應(yīng)用暫存并從棧上扔掉它。
暫存的丟棄可以運(yùn)行git stash drop
加上將要移除的暫存的名字來移除它:
git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051 Revert "added file_size"
stash@{2}: WIP on master: 21d80a5 added number to log
git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)
從暫存創(chuàng)建一個(gè)分支
如果暫存了一些工作,在后續(xù)重新應(yīng)用工作時(shí)可能會(huì)有問題。 如果應(yīng)用嘗試修改剛剛修改的文件,您會(huì)得到一個(gè)合并沖突并不得不解決它。 如果想要一個(gè)輕松的方式來再次測試儲(chǔ)藏的改動(dòng),可以運(yùn)行git stash branch
創(chuàng)建一個(gè)新分支,檢出暫存工作時(shí)所在的提交,重新在那應(yīng)用工作,然后在應(yīng)用成功后扔掉:
git stash branch testchanges
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
# modified: index.html
# Changed but not updated:
# (use "git add <file>..." to update what will be committed)
# modified: lib/simplegit.rb # Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)
這是在新分支輕松恢復(fù)暫存工作并繼續(xù)工作的一個(gè)很不錯(cuò)的途徑。
修改代碼歷史
Git的強(qiáng)大之處在于它允許您隨時(shí)調(diào)整提交歷史。無論是調(diào)整順序、修改信息、編輯文件,還是合并、拆分、刪除提交,都可以在工作前輕松完成。
修改最后一次提交
最常見的操作是修改最近一次提交,您可能想更改提交信息或更新文件。
如果,只是想修改最近一次提交的提交信息,那么很簡單:
git commit --amend
想要精簡提交信息或修改已提交的快照,只需進(jìn)入文本編輯器修改最近一條提交信息,然后保存關(guān)閉即可。若已提交卻忘記添加文件,可通過
git add
或git rm
修改文件,再運(yùn)行git commit --amend
更新快照,但請(qǐng)注意,修正會(huì)改變提交的SHA-1校驗(yàn)和,類似小變基,推送后請(qǐng)勿修正。修改多個(gè)提交信息
要修改歷史中較早的提交,需使用更復(fù)雜的工具。Git無直接改變歷史工具,但可用交互式變基。通過
git rebase -i
,可在任意提交后停止,修改信息、添加文件等。要修改最后三次提交,使用HEAD~3
作為參數(shù)傳遞給git rebase -i
命令。記住,這實(shí)際上指定了以前的四次提交中的父提交。git rebase -i HEAD~3
需要重點(diǎn)注意的是相對(duì)于正常使用的 log 命令,這些提交顯示的順序是相反的。 運(yùn)行一次 log 命令,會(huì)看到類似這樣的東西:
git log --pretty=format:"%h %s" HEAD~3..HEAD a5f4a0d added cat-file 310154e updated README formatting and added blame f7f3f6d changed my name a bit
反序變基腳本, 它從指定提交(如HEAD~3)逆序重演每個(gè)變更,要停在某次變更上修改,編輯腳本,將想改的‘pick’換成‘edit’。
例如,只調(diào)第三次提交
edit f7f3f6d changed my name a bit pick 310154e updated README formatting and added blame pick a5f4a0d added cat-file
當(dāng)保存并退出編輯器時(shí),Git將您帶回到列表中的最后一次提交,把送回命令行并提示以下信息:
git rebase -i HEAD~3 Stopped at f7f3f6d... changed my name a bit You can amend the commit now, with git commit --amend Once you’re satisfied with your changes, run git rebase --continue
這些指令準(zhǔn)確地告訴您該做什么。
git commit --amend
修改提交信息,然后退出編輯器。 然后,運(yùn)行
git rebase --continue
這個(gè)命令將會(huì)自動(dòng)地應(yīng)用另外兩個(gè)提交,然后就完成了。 如果需要將不止一處的 pick 改為 edit,需要在每一個(gè)修改為 edit 的提交上重復(fù)這些步驟。 每一次,Git 將會(huì)停止,讓您修正提交,然后繼續(xù)直到完成。
想重塑您的提交歷史,可以通過變基命令操作,但需注意,此操作針對(duì)的是
HEAD~3..HEAD
范圍內(nèi)的提交,且會(huì)徹底重寫它們,關(guān)鍵要點(diǎn)如下:?重寫提交?:變基不僅修改內(nèi)容,還重寫提交信息。
?避免沖突?:別碰已推送的提交,否則會(huì)造成版本混亂。
?編輯器操作?:運(yùn)行命令后,文本編輯器將展示待重寫的提交列表。
pick f7f3f6d changed my name a bit # 使用這個(gè)提交。 pick 310154e updated README formatting and added blame pick a5f4a0d added cat-file # Rebase 710f0f8..a5f4a0d onto 710f0f8 # # 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 # # 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
重新排序提交
也可以使用交互式變基來重新排序或完全移除提交。 如果想要移除 “
added cat-file
” 提交然后修改另外兩個(gè)提交引入的順序,可以將變基腳本從這樣:pick f7f3f6d changed my name a bit pick 310154e updated README formatting and added blame pick a5f4a0d added cat-file
改為這樣:
pick 310154e updated README formatting and added blame pick f7f3f6d changed my name a bit
當(dāng)保存并退出編輯器時(shí),Git 將您的分支帶回這些提交的父提交,應(yīng)用 310154e,然后應(yīng)用 f7f3f6d,最后停止。 事實(shí)修改了那些提交的順序并完全地移除了 “
added cat-file
” 提交。壓縮提交
通過交互式變基工具,也可以將一連串提交壓縮成一個(gè)單獨(dú)的提交。 在變基信息中腳本給出了有用的指令。
# 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 # # 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
如果,指定 “squash” 而不是 “pick” 或 “edit”,Git將應(yīng)用兩者的修改并合并提交信息在一起。 所以,如果想要這三次提交變?yōu)橐粋€(gè)提交,可以這樣修改腳本。
pick f7f3f6d changed my name a bit squash 310154e updated README formatting and added blame squash a5f4a0d added cat-file
當(dāng)保存并退出編輯器時(shí),Git 應(yīng)用所有的三次修改然后將你放到編輯器中來合并三次提交信息。
# This is a combination of 3 commits. # The first commit's message is: changed my name a bit # This is the 2nd commit message: updated README formatting and added blame # This is the 3rd commit message: added cat-file
當(dāng)你保存之后,你就擁有了一個(gè)包含前三次提交的全部變更的提交。
拆分提交
拆分一個(gè)提交會(huì)撤消這個(gè)提交,然后多次地部分地暫存與提交直到完成你所需次數(shù)的提交。 例如,假設(shè)想要拆分三次提交的中間那次提交。 想要將它拆分為兩次提交:第一個(gè) “
updated README formatting
”,第二個(gè) “added blame
” 來代替原來的 “updated README formatting and added blame
”。 可以通過修改rebase -i
的腳本來做到這點(diǎn),將要拆分的提交的指令修改為 “edit”。pick f7f3f6d changed my name a bit edit 310154e updated README formatting and added blame pick a5f4a0d added cat-file
然后,當(dāng)腳本將你進(jìn)入到命令行時(shí),重置那個(gè)提交,拿到被重置的修改,從中創(chuàng)建幾次提交。 當(dāng)保存并退出編輯器時(shí),Git帶您到列表中第一個(gè)提交的父提交,應(yīng)用第一個(gè)提交(f7f3f6d),應(yīng)用第二個(gè)提交(310154e),然后讓你進(jìn)入命令行。 那里,可以通過
git reset HEAD^
做一次針對(duì)那個(gè)提交的混合重置,實(shí)際上將會(huì)撤消那次提交并將修改的文件未暫存。 現(xiàn)在可以暫存并提交文件直到有幾個(gè)提交,然后當(dāng)完成時(shí)運(yùn)行git rebase --continue
:git reset HEAD^ git add README git commit -m 'updated README formatting' git add lib/simplegit.rb git commit -m 'added blame' git rebase --continue
Git在腳本中應(yīng)用最后一次提交(a5f4a0d),歷史記錄看起來像這樣:
git log -4 --pretty=format:"%h %s" 1c002dd added cat-file 9b29157 added blame 35cfb2b updated README formatting f3cc40e changed my name a bit
再次強(qiáng)調(diào),如果您正打算推送提交到共享倉庫,請(qǐng)先暫停。我們有一個(gè)重要更新:所有在列表中的提交的SHA-1校驗(yàn)和已經(jīng)更改。為確保數(shù)據(jù)的完整性和避免沖突,請(qǐng)務(wù)必確認(rèn)這些提交尚未被推送到共享倉庫。