看完这一篇,再也不用担心 Git 的“黑魔法”
簡(jiǎn)介:?相信大部分開(kāi)發(fā)者對(duì) Git 都不陌生,Git 也已成為大部分開(kāi)發(fā)者日常開(kāi)發(fā)必用的工具。本文分享 Git 使用上的一些基礎(chǔ)知識(shí),通俗易懂,非常有用。
更多相關(guān)內(nèi)容:點(diǎn)擊這里?
在 Git Rev News #48 期的 LightReading 中有一篇文章(地址:https://hacker-tools.github.io/version-control/) 寫的不錯(cuò),不僅干貨滿滿而且還附帶了操作視頻。其中的內(nèi)容不僅覆蓋了很多 Git 使用上的基礎(chǔ)知識(shí),也從使用角度上解答了很多剛接觸 Git 的開(kāi)發(fā)者的疑問(wèn)。為了便于讀者理解,我在翻譯的同時(shí)也添加了一些內(nèi)容。以下為正文部分。本文內(nèi)容較長(zhǎng),建議收藏慢慢學(xué)習(xí)。
擔(dān)憂
很多人怕使用 Git,我個(gè)人覺(jué)得主要可能是兩部分的原因:
-
沒(méi)接觸過(guò):平時(shí)接觸的代碼還托管在 SVN 或 CVS 等工具上。
-
不太熟悉:可能對(duì) Git 的使用還不太熟悉和全面,導(dǎo)致了在使用 git 時(shí)步步為營(yíng)。
?
Never Be Afraid To Try Something New.
代碼對(duì)于開(kāi)發(fā)者是勞作成果的結(jié)晶,對(duì)于公司而言是核心資產(chǎn),有一些擔(dān)憂也是正常的。但 Git 也并沒(méi)有我們想象中的那么復(fù)雜,需要讓我們每次使用都心有余悸,其實(shí)我們只需要稍微花一點(diǎn)時(shí)間嘗試多多了解它,在很多時(shí)候你會(huì)發(fā)現(xiàn),非但 Git 不會(huì)讓你產(chǎn)生擔(dān)憂,而且會(huì)讓自己的交付過(guò)程更加高效。
Version Control
談及 Git 就不得不提到版本控制,我們不妨先來(lái)看下版本控制是做什么的,這將有助于后續(xù)對(duì) Git 的理解。
當(dāng)你在工作中面對(duì)的是一些經(jīng)常變化的文檔、代碼等交付物的時(shí)候,考慮如何去追蹤和記錄這些 changes 就變得非常重要,原因可能是:對(duì)于頻繁改動(dòng)和改進(jìn)的交付物,非常有必要去記錄下每次變更的內(nèi)容,每次記錄的內(nèi)容匯成了一段修改的歷史,有了歷史我們才知道我們?cè)?jīng)做了什么。
記錄的歷史中必須要包含一些重要的信息,這樣追溯才變得有意義,比如:
-
Who:是誰(shuí)執(zhí)行的變更?
-
When:什么時(shí)候做出的變更?
-
What:這次變更做了什么事情?
最好可以支持撤銷變更,不讓某一個(gè)提交的嚴(yán)重問(wèn)題,去污染整個(gè)提交歷史。
版本控制系統(tǒng)(VCS: Version Control System),正會(huì)為你提供這種記錄和追溯變更的能力。
?
大多數(shù)的 VCS 支持在多個(gè)使用者之間共享變更的提交歷史,這從實(shí)質(zhì)上讓團(tuán)隊(duì)協(xié)同變?yōu)榱丝赡?#xff0c;簡(jiǎn)單說(shuō)來(lái)就是:
-
你可以看到我的變更提交。
-
我也可以看到你的變更提交。
-
如果雙方都進(jìn)行了變更提交,也可以以某種方式方法進(jìn)行比對(duì)和合并,最終作出統(tǒng)一的變更版本。
VCS 歷經(jīng)多年的發(fā)展,目前業(yè)界中有許多 VCS 工具可供我們選擇。在本文中,我們將會(huì)針對(duì)目前最流行的 Git 來(lái)介紹。
Git 是黑魔法么?
剛接觸 Git 時(shí),Git 確實(shí)有讓人覺(jué)得有點(diǎn)像黑魔法一樣神秘,但是又有哪個(gè)技術(shù)不是這樣呢?當(dāng)我們了解其基本的數(shù)據(jù)結(jié)構(gòu)結(jié)構(gòu)后,會(huì)發(fā)現(xiàn) Git 從使用角度來(lái)講其實(shí)并不復(fù)雜,我們甚至可以更進(jìn)一步的學(xué)習(xí) Git 的一些優(yōu)良的軟件設(shè)計(jì)理論,從中獲益。首先,讓我們先從 commit 說(shuō)起。
git object commit
提交對(duì)象(git commit object):每一個(gè)提交在 Git 中都通過(guò) git commit object 存儲(chǔ),對(duì)象具有一個(gè)全局唯一的名稱,叫做 revision hash。它的名字是由 SHA-1 算法生成,形如"998622294a6c520db718867354bf98348ae3c7e2",我們通常會(huì)取其縮寫方便使用,如"9986222"。
-
對(duì)象構(gòu)成:commit 對(duì)象包含了 author + commit message 的基本信息。
-
對(duì)象存儲(chǔ):git commit object 保存一次變更提交內(nèi)的所有變更內(nèi)容,而不是增量變化的數(shù)據(jù) delta (很多人都理解錯(cuò)了這一點(diǎn)),所以 Git 對(duì)于每次改動(dòng)存儲(chǔ)的都是全部狀態(tài)的數(shù)據(jù)。
-
大對(duì)象存儲(chǔ):因?qū)τ诖笪募男薷暮痛鎯?chǔ),同樣也是存儲(chǔ)全部狀態(tài)的數(shù)據(jù),所以可能會(huì)影響 Git 使用時(shí)的性能(glfs 可以改進(jìn)這一點(diǎn))。
-
提交樹(shù):多個(gè) commit 對(duì)象會(huì)組成一個(gè)提交樹(shù),它讓我們可以輕松的追溯 commit 的歷史,也能對(duì)比樹(shù)上 commit 與 commit 之間的變更差異。
git commit 練習(xí)
讓我們通過(guò)實(shí)戰(zhàn)來(lái)幫助理解,第一步我們來(lái)初始化一個(gè) repository(Git 倉(cāng)庫(kù)),默認(rèn)初始化之后倉(cāng)庫(kù)是空的,其中既沒(méi)有保存任何文本內(nèi)容也沒(méi)有附帶任何提交:
$ git init hackers$ cd hackers$ git status
第二步,讓我們來(lái)看下執(zhí)行過(guò)后 Git 給出的輸出內(nèi)容,它會(huì)指引我們進(jìn)行進(jìn)一步的了解:
? ?hackers git:(master) git statusOn branch masterNo commits yetnothing to commit (create/copy files anduse "git add" to track)
1)output 1: On branch master
對(duì)于剛剛創(chuàng)建空倉(cāng)庫(kù)來(lái)說(shuō),master 是我們的默認(rèn)分支,一個(gè) Git 倉(cāng)庫(kù)下可以有很多分支 (branches),具體某一個(gè)分支的命名可以完全由你自己決定,通常會(huì)起便于理解的名字,如果用 hash 號(hào)的話肯定不是一個(gè)好主意。
branches 是一種引用?(ref),他們指向了一個(gè)確定的 commit hash 號(hào),這樣我們就可以明確我們的分支當(dāng)前的內(nèi)容。
除了 branches 引用以外,還有一種引用叫做 tags,相信大家也不會(huì)陌生。
master 通常被我們更加熟知,因?yàn)榇蠖鄶?shù)的分支開(kāi)發(fā)模式都是用 master 來(lái)指向“最新”的 commit。
On branch master 代表著我們當(dāng)前是在 master 分支下操作,所以每次當(dāng)我們?cè)谔峤恍碌?commit 時(shí),Git 會(huì)自動(dòng)將 master 指向我們新的 commit,當(dāng)工作在其他分支上時(shí),同理。
有一個(gè)很特殊的 ref 名稱叫做 "HEAD",它指向我們當(dāng)前正在操作的 branches 或 tags (正常工作時(shí)),其命名上非常容易理解,表示當(dāng)前的引用狀態(tài)。
通過(guò)?git branch?(或?git tag) 命令你可以靈活的操作和修改 branches 或 tags。
2)output 2:No commits yet
對(duì)于空倉(cāng)庫(kù)來(lái)說(shuō),目前我們還沒(méi)有進(jìn)行任意的提交。
?
nothing to commit (create/copy files anduse "git add" to track)
output 中提示我們需要使用?git add?命令,說(shuō)到這里就必須要提到暫存或索引 (stage),那么如何去理解暫存呢?
暫存
一個(gè)文件從改動(dòng)到提交到 Git 倉(cāng)庫(kù),需要經(jīng)歷三個(gè)狀態(tài):
-
工作區(qū):工作區(qū)指的是我們本地工作的目錄,比如我們可以在剛才創(chuàng)建的 hackers 目錄下新增一個(gè) readme 文件,readme 文件這時(shí)只是本地文件系統(tǒng)上的修改,還未存儲(chǔ)到 Git。
-
暫存(索引)區(qū):暫存實(shí)際上是將我們本地文件系統(tǒng)的改動(dòng)轉(zhuǎn)化為 Git 的對(duì)象存儲(chǔ)的過(guò)程。
-
倉(cāng)庫(kù):git commit 后將提交對(duì)象存儲(chǔ)到 Git 倉(cāng)庫(kù)。
?
git add 的幫助文檔中很詳細(xì)的解釋了暫存這一過(guò)程:
?
This command updates the index using thecurrent content found in the working tree, to prepare the content stagedfor the next commit.
git add 命令將更新暫存區(qū),為接下來(lái)的提交做準(zhǔn)備。
?
It typically adds the current content ofexisting paths as a whole, but with some options it can also be used toadd content with only part of the changes made to the working tree filesapplied, or remove paths that do not exist in the working tree anymore.
The "index" holds a snapshot ofthe content of the working tree, and it is this snapshot that is taken as thecontents of the next commit.
暫存區(qū)的 index 保存的是改動(dòng)的完整文件和目錄的快照 (非 delta)。
?
Thus after making any changes to theworking tree, and before running the commit command, you must use the addcommand to add any new or modified files to the index.
暫存是我們將改動(dòng)提交到 git 倉(cāng)庫(kù)之前必須經(jīng)歷的狀態(tài)。
對(duì) Git 暫存有一定了解后,其相關(guān)操作的使用其實(shí)也非常簡(jiǎn)單,簡(jiǎn)要的說(shuō)明如下:
1)暫存區(qū)操作
-
通過(guò)?git add?命令將改動(dòng)暫存。
-
可以使用?git add -p?來(lái)依次暫存每一個(gè)文件改動(dòng),過(guò)程中我們可以靈活選擇文件。中的變更內(nèi)容,從而決定哪些改動(dòng)暫存。
-
如果?git add?不會(huì)暫存被 ignore 的文件改動(dòng)。
-
通過(guò)?git rm?命令,我們可以刪除文件的同時(shí)將其從暫存區(qū)中剔除。
2)暫存區(qū)修正
-
通過(guò)?git reset?命令進(jìn)行修正,可以先將暫存區(qū)的內(nèi)容清空,在使用?git add -p?命令對(duì)改動(dòng) review 和暫存。
-
這個(gè)過(guò)程不會(huì)對(duì)你的文件進(jìn)行任何修改操作,只是 Git 會(huì)認(rèn)為目前沒(méi)有改動(dòng)需要被提交 。
-
如果我們想分階段(or 分文件)進(jìn)行 reset,可以使用?git reset FILE?或?git reset -p?命令。
3)暫存區(qū)狀態(tài)
-
可以用?git diff --staged?依次檢查暫存區(qū)內(nèi)每一個(gè)文件的修改。
-
用?git diff?查看剩余的還未暫存內(nèi)容的修改。
Just Commit!
當(dāng)你對(duì)需要修改的內(nèi)容和范圍滿意時(shí),你就可以將暫存區(qū)的內(nèi)容進(jìn)行 commit 了,命令為:git commit。
如果你覺(jué)得需要把所有當(dāng)前工作空間的修改全部 commit,可以執(zhí)行?git commit -a?,這相當(dāng)于先執(zhí)行?git add?后執(zhí)行?git commit,將暫存和提交的指令合二為一,這對(duì)于一些開(kāi)發(fā)者來(lái)說(shuō)是很高效的,但是如果提交過(guò)大這樣做通常不合適。
我們建議一個(gè)提交中只做一件事,這在符合單一職責(zé)的同時(shí),也可以讓我們明確的知道每一個(gè) commit 中做了一件什么事情而不是多個(gè)事情。所以通常我們的使用習(xí)慣都是執(zhí)行?git add -p?來(lái) review 我們將要暫存內(nèi)容是否合理?是否需要更細(xì)的拆分提交?這些優(yōu)秀的工程實(shí)踐,將會(huì)讓代碼庫(kù)中的 commits 更加優(yōu)雅。
ok,我們已經(jīng)在不知不覺(jué)中了解了很多內(nèi)容,我們來(lái)回顧下,它們包括了:
-
commit 包含的信息?
-
commit 是如何表示的?
-
暫存區(qū)是什么?如何全部添加、一次添加、刪除、查詢和修正?
-
如何將暫存區(qū)的改動(dòng)內(nèi)容 commit?
-
不要做大提交,一個(gè)提交只做一件事。
附帶的,在了解 commit 過(guò)程中我們知道了從本地改動(dòng)到提交到 Git 倉(cāng)庫(kù),經(jīng)歷的幾個(gè)關(guān)鍵的狀態(tài):
-
工作區(qū) (Working Directory)
-
暫存區(qū) (Index)
-
Git 倉(cāng)庫(kù) (Git Repo)
下圖為上述過(guò)程中各個(gè)狀態(tài)的轉(zhuǎn)換過(guò)程:
-
本地改動(dòng)文件時(shí),此時(shí)還僅僅是工作區(qū)內(nèi)的改動(dòng)
-
當(dāng)執(zhí)行 git add 之后,工作區(qū)內(nèi)的改動(dòng)被索引在暫存區(qū)
-
當(dāng)執(zhí)行?git commit?之后,暫存區(qū)的內(nèi)容對(duì)象將會(huì)存儲(chǔ)在 Git 倉(cāng)庫(kù)中,并執(zhí)行更新 HEAD 指向等后續(xù)操作,這樣就完成了引用與提交、提交與改動(dòng)快照的——對(duì)應(yīng)了。
?
正是因?yàn)?Git 本身對(duì)于這幾個(gè)區(qū)域(狀態(tài))的設(shè)計(jì),為 Git 在本地開(kāi)發(fā)過(guò)程帶來(lái)了靈活的管理空間。我們可以根據(jù)自己的情況,自由的選擇哪些改動(dòng)暫存、哪些暫存的改動(dòng)可以 commit、commit 可以關(guān)聯(lián)到那個(gè)引用,從而進(jìn)一步與其他人進(jìn)行協(xié)同。
提交之后
我們已經(jīng)有了一個(gè) commit,現(xiàn)在我們可以圍繞 commit 做更多有趣的事情:
-
查看 commit 歷史:git log?(或?git log --oneline)。
-
在 commit 中查看改動(dòng)的?diff:git log -p。
-
查看 ref 與提交的關(guān)聯(lián)關(guān)系,如當(dāng)前 master 指向的 commit: git show master 。
-
檢出覆蓋:git checkout NAME(如果 NAME 是一個(gè)具體的提交哈希值時(shí),Git 會(huì)認(rèn)為狀態(tài)是 “detached (分離的)”,因?yàn)?git checkout?過(guò)程中重要的一步是將 HEAD 指向那個(gè)分支的最后一次 commit。所以如果這樣做,將意味著沒(méi)有分支在引用此提交,所以若我們這時(shí)候進(jìn)行提交的話,沒(méi)有人會(huì)知道它們的存在)。
-
使用?git revert NAME?來(lái)對(duì) commit 進(jìn)行反轉(zhuǎn)操作。
-
使用?git diff NAME?將舊版本與當(dāng)前版本進(jìn)行比較,查看 diff。
-
使用?git log NAME?查看指定區(qū)間的提交。
-
使用?git reset NAME?進(jìn)行提交重置操作。
-
使用?git reset --hard NAME:將所有文件的狀態(tài)強(qiáng)制重置為 NAME 的狀態(tài),使用上需要小心。
引用基本操作
引用 (refs) 包含兩種分別是 branches 和 tags, 我們接下來(lái)簡(jiǎn)單介紹下相關(guān)操作:
-
git branch b?命令可以讓我們創(chuàng)建一個(gè)名稱為 b 的分支。
-
當(dāng)我們創(chuàng)建了一個(gè) b 分支后,這也相當(dāng)于意味著 b 的指向就是 HEAD 對(duì)應(yīng)的commit。
-
我們可以先在 b 分支上創(chuàng)建一個(gè)新的 commit A ,然后假如切回 master 分支上,這時(shí)再提交了一個(gè)新的 commit B,那么 master 和 HEAD 將會(huì)指向了新的commit __B,而 b 分支指向的還是原來(lái)的 commit A。
-
git checkout b?可以切換到b分支上,切換后新的提交都會(huì)在b分支上,理所應(yīng)當(dāng)。
-
git checkout master?切換回 master 后,b 分支的提交也不會(huì)帶回 master 上,分支隔離。
分支上提交隔離的設(shè)計(jì),可以讓我們非常輕松的切換我們的修改,非常方便的做各類測(cè)試。
tags 的名稱不會(huì)改變,而且它們有自己的描述信息 (比如可以作為 release note 以及標(biāo)記發(fā)布的版本號(hào)等)。
做好你的提交
可能很多人的提交歷史是長(zhǎng)這個(gè)樣子的:
commit 14: add feature x – maybe even witha commit message about x!commit 13: forgot to add filecommit 12: fix bug?
commit 11: typocommit 10: typo2commit 9: actually fixcommit 8: actually actually fixcommit 7: tests passcommit 6: fix example codecommit 5: typocommit 4: xcommit 3: xcommit 2: xcommit 1: x
單就 Git 而言,這看上去是沒(méi)有問(wèn)題而且合法的,但對(duì)于那些對(duì)你修改感興趣的人(很可能是未來(lái)的你!),這樣的提交在信息在追溯歷史時(shí)可能并沒(méi)有多大幫助。但是如果你的提交已經(jīng)長(zhǎng)成這個(gè)樣子,我們?cè)撛趺崔k?
沒(méi)關(guān)系,Git 有辦法可以彌補(bǔ)這一些:
git commit --amend
我們可以將新的改動(dòng)提交到當(dāng)前最近的提交上,比如你發(fā)現(xiàn)少改了什么,但是又不想多出一個(gè)提交時(shí)會(huì)很有用。
如果我們認(rèn)為我們的提交信息寫的并不好,我要修改修改,這也是一種辦法,但是并不是最好的辦法。
這個(gè)操作會(huì)更改先前的提交,并為其提供新的 hash 值。
git rebase -i HEAD~13
這個(gè)命令非常強(qiáng)大,可以說(shuō)是 Git 提交管理的神器,此命令含義是我們可以針對(duì)之前的 13 次的提交在 VI 環(huán)境中進(jìn)行重新修改設(shè)計(jì):
操作選項(xiàng) p 意味著保持原樣什么都不做,我們可以通過(guò) vim 中編輯提交的順序,使其在提交樹(shù)上生效。
操作選項(xiàng) r:我們可以修改提交信息,這種方式比?commit --amend?要好的多,因?yàn)椴粫?huì)新生成一個(gè) commit。
操作選項(xiàng) e:我們可以修改 commit,比如新增或者刪除某些文件改動(dòng)。
操作選項(xiàng) s:我們可以將這個(gè)提交與其上一次的提交進(jìn)行合并,并重新編輯提交信息。
操作選項(xiàng) f:f代表著 "fixup"。例如我們?nèi)绻脶槍?duì)之前一個(gè)老的提交進(jìn)行 fixup,又不想做一次新的提交破壞提交樹(shù)的歷史的邏輯含義,可以采用這種方式,這種處理方式非常優(yōu)雅。
關(guān)于 Git
版本控制的一個(gè)常見(jiàn)功能是允許多個(gè)人對(duì)一組文件進(jìn)行更改,而不會(huì)互相影響?;蛘吒_切地說(shuō),為了確保如果他們不會(huì)踩到彼此的腳趾,不會(huì)在提交代碼到服務(wù)端時(shí)偷偷的覆蓋彼此的變化。
在 Git 中我們?nèi)绾伪WC這一點(diǎn)呢?
Git 與 SVN 不同,Git 不存在本地文件存在 lock 的情況,這是一種避免出現(xiàn)寫作問(wèn)題的方式,但是并不方便,而 Git 與 SVN 最大的不同在于它是一個(gè)分布式 VCS,這意味著:
-
每個(gè)人都有整個(gè)存儲(chǔ)庫(kù)的本地副本(其中不僅包含了自己的,也包含了其他人的提交到倉(cāng)庫(kù)的所有內(nèi)容)。
-
一些 VCS 是集中式的(例如 SVN):服務(wù)器具有所有提交,而客戶端只有他們“已檢出”的文件。所以基本上在本地我們只有當(dāng)前文件,每次涉及本地不存在的文件操作時(shí),都需要訪問(wèn)服務(wù)端進(jìn)行進(jìn)一步交互。
-
每一個(gè)本地副本都可以當(dāng)作服務(wù)端對(duì)外提供 Git 服務(wù)。
-
我們可以用?git push?推送本地內(nèi)容到任意我們有權(quán)限的 Git 遠(yuǎn)端倉(cāng)庫(kù)。
-
不管是集團(tuán)的 force、Github、Gitlab 等工具,其實(shí)本質(zhì)上都是提供的 Git 倉(cāng)庫(kù)存儲(chǔ)的相關(guān)服務(wù),在這一點(diǎn)上其實(shí)并沒(méi)有特別之處,針對(duì) Git 本身和其協(xié)議上是透明的。
?
SVN,圖片出自 git-scm
?
Git,圖片出自 git-scm
Git 沖突解決
沖突的產(chǎn)生幾乎是不可避免的,當(dāng)沖突產(chǎn)生時(shí)你需要將一個(gè)分支中的更改與另一個(gè)分支中的更改合并,對(duì)應(yīng) Git 的命令為?git merge NAME?,一般過(guò)程如下:
-
找到 HEAD 和 NAME 的一個(gè)共同祖先 (common base)。
-
嘗試將這些 NAME 到共同祖先之間的修改合并到 HEAD 上。
-
新創(chuàng)建一個(gè) merge commit 對(duì)象,包含所有的這些變更內(nèi)容。
-
HEAD 指向這個(gè)新的 merge commit。
Git 將會(huì)保證這個(gè)過(guò)程改動(dòng)不會(huì)丟失,另外一個(gè)命令你可能會(huì)比較熟悉,那就是?git pull?命令,git pull?命令實(shí)際上包含了?git merge?的過(guò)程,具體過(guò)程為:
-
git fetch REMOTE
-
git merge REMOTE/BRANCH
-
和 git push 一樣,有的時(shí)候需要先設(shè)置 "tracking"(-u) ,這樣可以將本地和遠(yuǎn)程的分支一一對(duì)應(yīng)。
如果每次 merge 都如此順利,那肯定是非常完美的,但有時(shí)候你會(huì)發(fā)現(xiàn)在合并時(shí)產(chǎn)生了沖突文件,這時(shí)候也不用擔(dān)心,如何處理沖突的簡(jiǎn)要介紹如下:
-
沖突只是因?yàn)?Git 不清楚你最終要合并后的文本是什么樣子,這是很正常的情況。
-
產(chǎn)生沖突時(shí),Git 會(huì)中斷合并操作,并指導(dǎo)你解決好所有的沖突文件。
-
打開(kāi)你的沖突文件,找到?<<<<<<<?,這是你需要開(kāi)始處理沖突的地方,然后找到?=======,等號(hào)上面的內(nèi)容是 HEAD 到共同祖先之間的改動(dòng),等號(hào)下面是 NAME 到共同祖先之間的改動(dòng)。用 git mergetool 通常是比較好的選擇,當(dāng)然現(xiàn)在大多數(shù) IDE 都集成了不錯(cuò)的沖突解決工具。
-
當(dāng)你把沖突全部解決完畢,請(qǐng)用?git add.?來(lái)暫存這些改動(dòng)吧。
-
最后進(jìn)行?git commit,如果你想放棄當(dāng)前修改重新解決可以使用?git merge --abort?,非常方便。
當(dāng)你完成了以上這些艱巨的任務(wù),最后?git push?吧!
push 失敗?
排除掉遠(yuǎn)端的 Git 服務(wù)存在問(wèn)題以外,我們 push 失敗的大多數(shù)原因都是因?yàn)槲覀冊(cè)诠ぷ鞯膬?nèi)容其他人也在工作的關(guān)系。
Git 是這樣判斷的:
1)會(huì)判斷 REMOTE 的當(dāng)前 commit 是不是你當(dāng)前正在 pushing commit 的祖先。
2)如果是的話,代表你的提交是相對(duì)比較新的,push 是可以成功的 (fast-forwarding)。
3)否則 push 失敗并提示你其他人已經(jīng)在你 push 之前執(zhí)行更新 (push is rejected)。
當(dāng)發(fā)生“push is rejected”后我們的幾個(gè)處理方法如下:
-
使用?git pull?合并遠(yuǎn)程的最新更改(git pull?相當(dāng)于?git fetch?+?git merge)。
-
使用 --force 強(qiáng)制推送本地變化到遠(yuǎn)端引用進(jìn)行覆蓋,需要注意的是 這種覆蓋操作可能會(huì)丟失其他人的提交內(nèi)容。
-
可以使用?--force-with-lease?參數(shù),這樣只有遠(yuǎn)端的 ref 自上次從 fetch 后沒(méi)有改變時(shí)才會(huì)強(qiáng)制進(jìn)行更改,否則“reject the push”,這樣的操作更安全,是一種非常推薦使用的方式。
-
如果 rebase 操作了本地的一些提交,而這些提交之前已經(jīng) push 過(guò)了的話,你可能需要進(jìn)行 force push 了,可以想象看為什么?
本文只是選取部分 Git 基本命令進(jìn)行介紹,目的是拋磚引玉,讓大家對(duì) Git 有一個(gè)基本的認(rèn)識(shí)。當(dāng)我們深入挖掘 Git 時(shí),你會(huì)發(fā)現(xiàn)它本身有著如此多優(yōu)秀的設(shè)計(jì)理念,值得我們學(xué)習(xí)和探究。
不要讓 Git 成為你認(rèn)知領(lǐng)域的黑魔法,而是讓 Git 成為你掌握的魔法。
總結(jié)
以上是生活随笔為你收集整理的看完这一篇,再也不用担心 Git 的“黑魔法”的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 5G改变物联网解决方案的6种方式
- 下一篇: 重磅发布 | 承载亿级流量的开发框架,闲