Git實用指南第一篇

本篇提要:git的核心理念、結(jié)構(gòu)、代碼庫的創(chuàng)建和Merge

從開始使用git至今看過無數(shù)文章,大多時候是為了解決一個特定問題,尋求一個特定的解決方案,偶爾也會看一些新手向的教程。對于前者,可能需要你具備一定的git能力,這無可厚非。但大多新手向的教程主要鋪陳git的常用命令,看的時候覺得自己懂了,但實際使用時就會問題重重。

磕磕絆絆至今,我對git也算是有了一些了解,當然都是使用向而不是原理向。在此希望能夠以我自己理解的方式,描述我在使用git時的一些心得,希望能夠幫助一些新手或者常常被git困擾的伙伴。

git的核心理念和結(jié)構(gòu)

五花八門的命令是使用git的手段,并不是git的核心。在我看來,與其掌握100條命令,不如先大致了解一下git的思想和大體架構(gòu)。從使用者角度出發(fā),git的目的就是為了方便我們隨時找到需要的代碼并進行處理,而我們常用的大多數(shù)命令,都是為了這一點服務(wù)的,確切的來說這一點就是一條commit。

從結(jié)構(gòu)來說,git主要有工作區(qū)、暫存區(qū)和版本庫三大區(qū)域。工作區(qū)就是我們寫代碼的地方,git會追蹤你干了什么,并告訴你你的操作和版本庫有哪些不同。暫存區(qū)就是對你現(xiàn)在的工作拍照,也就是所謂快照,繼續(xù)在工作區(qū)操作,暫存區(qū)還是會停留在拍照那一刻,就像手機截屏一樣。版本庫就是當你覺得暫存區(qū)的代碼沒問題之后,就可以入庫了,這時候git就真正把你所做的事變成了一條有價值的commit。

使git變得錯綜復(fù)雜的概念是分支,但分支又是必不可少的,很多時候你不得不放下手頭的工作,先去處理其他的問題,處理完后再回來繼續(xù)工作。這種情況下,必須要保證的是被中斷的工作就停留在它剛才的狀態(tài),否則現(xiàn)有的工作就無法繼續(xù)下去了。分支看起來就像一個副本(雖然git肯定不是這么實現(xiàn)的,但我們還是可以簡單的理解它就是復(fù)制了一次),確保了在某個分支上操作,不會影響到其他分支的內(nèi)容。

以上就是我們?nèi)粘J褂胓it的全部了,在工作區(qū)工作,到某個節(jié)點拍照存到暫存區(qū),確認無誤后合并到版本庫。偶爾來個突發(fā)事件,停下手頭的工作到另一個分支處理,結(jié)束后回來繼續(xù)工作。不過這都是概念性的,相當于定義了游戲規(guī)則,接下來就是看git給我們提供了哪些手段來完成此事。

建模,讓git更有趣

在正式操作git命令之前,我覺得對概念建模能對我們理解git提供有效幫助。我們把git看做一片竹林,我是一個園丁,我和其他的園丁一起照料這片林子,只不過這里的每根竹子要生長一節(jié)都是我們園丁說了算,而不是大地(看起來我們都是上帝?)。而我們每個人的工作,就是決定什么時間讓竹子長一節(jié),記錄我們工作的工具是分配給每個人的一臺電腦。

我們的模型就是這么簡單,一堆人一起“造”竹子,接下來跟隨著時間的腳步看下我們的模型如何運作吧。(PS:看起來怪怪的,莫不是我們開了一個竹子蠟像館?問題是能吸引來哪怕一個游客嗎?不管那么多了,老板的想法不是我們普通工人應(yīng)該琢磨的,有錢拿就行了。。。)

git命令和模型結(jié)合,推進時間線

第一天:項目的建立和代碼合并(Merge)

1. 項目啟動儀式

一開始我們很窮,除了錢什么都沒有。要想造竹子,得先有地盤吧,所以我們就拿錢買了塊地,它大概是

好了,不開玩笑,它大概這么大:

打開你的命令行,輸入以下字母,你就得到了一樣大的土地:

mkdir first_git
cd ./first_git/
git init

當你看到這樣的輸出,就成功了:

git init

這里看到了 master,這是git默認給我們創(chuàng)建的分支,也叫主分支,畢竟git需要在某個分支工作。給我們的模型挖一個坑,稍大一點,以后就是我們的明星產(chǎn)品了。

master

我(飛機醬)和我的小伙伴(姑且叫他路人丙)深得老板器重,所以這塊地目前全部屬于我們。雖說我們只有兩個人,但也要衡量誰做的多做的好,發(fā)給每個人的電腦就是用來記錄我們每天都做了什么,最后每個人的成果還要匯總一下交給老板。所以還需要一臺公共的電腦用來匯總我們的信息,把我們各自的工作整理后發(fā)到這臺電腦上,最終整合成美觀的結(jié)果報告等待老板查閱。既然這臺電腦已經(jīng)開了頭,就把它充公吧。

事情要由老板牽頭,所以老板在這臺電腦上簽了名,并寫下以下內(nèi)容:“竹林001號項目啟動”。接下來就什么都不管了...

使用git完成老板的操作可不簡單,你得依次做以下幾個事情:

git config --global user.name "boss"
git config --global user.email "boss@git.com"

以上就是老板簽名的步驟,--global 是說這個電腦上的全部項目都是老板的,操作者也是他自己,而且他也沒必要每次開一個項目都再次簽名認證了(這樣一來,路人丙如果想搗亂,老板一定會發(fā)現(xiàn)他)。

接下來老板新建了一個README.txt并將內(nèi)容竹林001號項目啟動寫進去保存了起來

echo 竹林001號項目啟動 > README.txt

寫好了開場白,還得按照規(guī)矩把它存到版本庫里,不然事情就白做了。先用 git status 看看現(xiàn)在版本庫的狀態(tài):

README

那個紅色的 README.txt 就是我們的工作區(qū),git發(fā)現(xiàn)了工作區(qū)改動,但是暫存區(qū)里沒有它,版本庫里也沒有。既然用git工具,就務(wù)必按照規(guī)則來,先把工作區(qū)拍照存到暫存區(qū),再進入版本庫:

// 添加到暫存區(qū)
git add README.txt

再看看狀態(tài),發(fā)現(xiàn)紅色變成綠色了,這時候就進了暫存區(qū):

暫存區(qū)

最后,終于可以入庫了,我們的項目也算正式啟動。

// 并入版本庫
git commit -m "老板寫下了啟動標語"

-m 后邊加的內(nèi)容表示這次做了什么,方便以后查閱。執(zhí)行完成之后,我們就擁有了第一個commit,也就是第一個有價值的節(jié)點。

first commit

使用 git log 可以看看我們做過什么:

log

commit 后邊那個長長的字符串,它是這次commit的唯一標識,后邊我們會明白它的意義。

現(xiàn)在,我們有了第一節(jié)竹子(畫圖不好,勿吐槽):

first commit

2. 開工,各自為戰(zhàn)

老板只在公共的電腦上寫下了標語,我們自己的電腦還沒有呢,總不能讓老板挨個寫吧?(老板:……)所以我們要有個辦法把公共電腦的內(nèi)容搞過來,這就是git clone。我們只要在自己的電腦上,執(zhí)行 git clone + 版本庫的路徑,就能把代碼同步過來,我這里只能在同一個電腦操作,所以用倉庫名稱進行 clone(同時在源版本庫執(zhí)行git config receive.denyCurrentBranch warn):

clone

可以看到老板修改的內(nèi)容已經(jīng)過來了。路人丙也執(zhí)行了同樣的操作,這樣就可以各做各的。為了知道是誰做的工作,我們還分別設(shè)置了自己的名字,這樣以后的每個commit都會攜帶作者(路人丙干了壞事,我們看看 git log 就能抓到他?。H绻挥幸粋€項目,可以不加--global。現(xiàn)在我們的土地上有了三根一樣的竹子:

clone

一切準備就緒,接下來就可以開始干活了,飛機醬和路人丙都在全力造竹子,并記錄到自己的電腦里,此時公共電腦并不知道他們干了什么。第一天因為沒有經(jīng)驗,工作中出現(xiàn)了多次失誤,飛機醬只造好了一竹節(jié),路人丙好一些,造好了兩節(jié)。所以他們的竹子是這樣的:

第一天

在他們自己的版本庫里,都在 work_01.txt文件里記錄了自己的工作,并保存到了版本庫:

飛機醬的日志
路人丙的日志

下班后,飛機醬把記錄同步到了公共電腦上,然后開心的回家了。他是這么操作的:

git push origin master:master
push

origin是遠程版本庫名稱,雖然我們沒有配置它,但它默認就是origin,第一個master是飛機醬電腦上使用的分支,第二個master是遠程版本庫的分支。(飛機醬可能提前補課了,命令寫的很全)

接下來到路人丙同步了,他也使用了類似的操作,但是卻發(fā)生了意外:

push

一樣的操作,結(jié)果卻迥異,路人丙的內(nèi)心一定是凌亂的,但他還認識幾個單詞,大致明白了提示的意思。原來飛機醬在他前面進行了push操作,導致他這里文件不夠新,git就拒絕了他。所以他按照提示中 (e.g., 'git pull ...') before pushing again. 又試了一次。結(jié)果在git pull時再次發(fā)生意外:

pull

conflict的意思是沖突,conflict出現(xiàn)在對同一個文件的同一行進行了不同修改的時候。我們記得飛機醬和路人丙都創(chuàng)建了 work_01.txt文件并寫入了內(nèi)容,所以git合并時發(fā)現(xiàn)第一行都寫入了內(nèi)容,就沒法決定讓誰的修改在前,就會要求我們手動解決?,F(xiàn)在我們打開路人丙的 work_01.txt,看看它的內(nèi)容:

conflict

可以看到,兩人輸入的內(nèi)容用 <<<<HEAD...====...>>>>{commitId}這樣的格式包裹起來了。前面是路人丙的內(nèi)容,后邊是飛機醬的內(nèi)容。想要合并到遠程版本庫,就一定要先解決掉這個問題,否則老板看到就要發(fā)火了~所以路人丙改了它,并把飛機醬的內(nèi)容放在了下邊(老板肯定會先看到前面的內(nèi)容):

修改內(nèi)容

因為又發(fā)生了修改,所以內(nèi)容現(xiàn)在還在工作區(qū),就需要再次把它加到暫存區(qū),再入庫。一通操作后,總算合并成功了:

push

現(xiàn)在讓我們更新一下當前模型的狀態(tài),路人丙為了解決沖突增加了一個commit,這時候公共電腦的log看起來是這樣的:

遠端版本庫

是不是理所當然的認為竹子長這樣呢:

想象中

看起來沒有問題,而且git log顯示也是如此。但是這里有個問題,大家都是今天干完的活,都要把自己的竹子安到明星產(chǎn)品上,憑什么你先放?路人丙的工作又快又好,難道不是更有資格先放嗎?路人丙認為把兩個人的竹子擰在一起,最后再用灰色的那節(jié)整合在一起最公平了,老板一眼就明白我們是同時完成的工作:

整合

看起來很奇怪吧?好好的竹子因為互相爭搶鼓了一個包,看起來十分不美觀(話說這樣老板不會生氣嗎?還是可能會吸引來奇怪的旅客?)。

實際上git pull操作是由兩個命令組成的,第一個是git fetch,表示把遠端版本庫的內(nèi)容拉取到本地,第二步才是執(zhí)行git merge操作把拉取下來的新內(nèi)容和我們自己的內(nèi)容合并在一起。既然都想占第一個位置,干脆git幫你整合一下,然后用一個新的commit代替就好了。要想證明事實上的確如此,使用上面的 git log 是不行的,你要在后邊加上 --pretty=raw

git log --pretty=raw
pretty

commit較多,但最后一條顯示不全的是老板的提交,并不影響我們分析它。我們主要看每條commit的parent字段,可以發(fā)現(xiàn)最上方的那一條有兩個parent,分別指向了飛機醬的第一條和路人丙的第二條,而被指向的這兩條數(shù)據(jù)的parent都是老板的那次提交,這和我們預(yù)期的完全一致。

3. 老板的怒火,推倒重來

解決了問題,路人丙很開心。關(guān)掉電腦,背上背包,正準備回去美餐一頓,老板卻過來視察工作了,先是夸了一頓路人丙工作努力,轉(zhuǎn)眼看到造好的竹子,怒發(fā)沖冠,責令路人丙馬上把問題解決掉!

這問題難不倒他,既然能把竹子合起來,再拆開豈不是相當容易,于是他拿起那臺公共電腦,輸入了以下命令:

// reset --merge 重置merge,后邊跟上之前的commitId,就可以回到過去
git reset --merge 2b0218d74edbea391c8b7853580c99164af8d32a

再看看日志,完蛋,紙飛機的記錄不見了,被路人丙的記錄取而代之!

重置merge

這下可怎么好,明天飛機醬來了不好交代呀,而且把別人的工作整丟也不符合道義,有后悔藥吃就好了。這時候一邊的老板看不下去了,拿起電腦飛快的敲了一條指令:

git reset --hard HEAD@{1}

然后把電腦交回路人丙,丟下一句:好了,后悔藥吃下去了。(路人丙:我@#$*&^^,發(fā)生了什么事?)再一看,果然又回到剛才的狀態(tài)了,原來老板是大神呀,可惜太冷漠,苦事還得我來做。

檢查一下剛才的 git reset xxx ,后邊跟了路人丙的commitId,結(jié)果就剩下了他的工作,這里一共兩條路,指到飛機醬那邊是不是就好了呢,再試一次,反正有老板在隨時能吃后悔藥(話說這么大膽的么)。

git reset --merge ba65055dab9700d0e814719bbf23713c163ade76
再次重置merge

果然是,我路人丙就是個天才,哈哈哈!

等等,這不是飛機醬離開時的狀態(tài)嗎,剛剛的路行不通,我該怎么辦好呢?回想一下剛剛好像是因為沖突才多了一個commit,那如果我不讓它沖突豈不就好了?這工作記錄都寫一個文件里看著也有些亂,我再換個文件也許就好了呢。路人丙用求助的眼神看著老板,意思是希望老板能把他自己的工作記錄刪一下,他已經(jīng)有了好的idea想再試一下。接下來又是老板炫技的時間,不過這次是在路人丙的電腦上:

git log
// 重置到老板自己的commitId
git reset --hard 420e8fe77141a4f6c9cfed2518c65b04d21bcec0

于是路人丙的工作全不見了,一切仿佛回到了剛開始。

重新來過

這次路人丙給文件起了一個不一樣的名字 lurenbing_01.txt,又把工作記錄寫了進去,再次進行提交:

push

果然還是需要先pull,再pull一下看看結(jié)果:

這次確實沒有了沖突,但是彈出了一個框,內(nèi)容說明這是一次merge,先不管它,直接下一步,再看git log --pretty=raw

log

為什么,都不在一起了還是這樣,路人丙都要崩潰了。一定是剛剛pull的問題,我要是先pull,再寫我的工作記錄是不是就好了。再次讓老板幫忙重置后,路人丙先pull了一次:

先pull

這下飛機醬的工作記錄都到我這了,我再寫應(yīng)該沒問題了吧,一頓操作下來,這一次順利的合并了進去,既沒有沖突,也沒有彈出框。到公共電腦上一看,很好很順利:

完成工作

時間太晚,工作也馬馬虎虎算搞定了,老板打算放過路人丙,但臨走前問了一個令路人丙瞬間凌亂的問題:如果每次都要先pull再開始寫你自己的commit,那你每天都要等飛機醬下班后才開始工作?(路人丙:那我每天豈不是比飛機醬多工作一倍的時間?明天我要找他研究研究,這實在不是人干的事啊~)

最后更新一下我們的模型,可以看到路人丙的竹節(jié)最后還是放在了上邊,所以干的又快又好有什么用呢,快要快在關(guān)鍵時刻。

模型的最終狀態(tài)

第一天的工作就到此結(jié)束了,可以看到,只要路人丙希望兩個人的工作能夠平等的占據(jù)第一的位置,就一定會形成鼓包,通過一個新竹節(jié)把這兩條路歸一。而如果他自愿把工作放在后邊,一切就很順暢,有時候真的是退一步海闊天空啊。


我是飛機醬,如果您喜歡我的文章,可以關(guān)注我~

編程之路,道阻且長。唯,路漫漫其修遠兮,吾將上下而求索。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 因為原文太長超出字數(shù),Lesson 3 就放在另一篇文章里 How to Use Git and GitHub 標...
    赤樂君閱讀 5,507評論 1 5
  • Git教程 一、Git簡介 1.1. Git的誕生1.2.集中式的vs分布式 二、安裝Git 三、創(chuàng)建版本庫 四、...
    曹淵說創(chuàng)業(yè)閱讀 1,035評論 0 2
  • 昨天在同事電腦上操作了一把cherry-pick代碼,發(fā)現(xiàn)很多功能不用,就慢慢忘記了,梳理了下流程圖: git c...
    gogoingmonkey閱讀 753評論 0 0
  • 轉(zhuǎn)載 :作者 [zhangwang] @(前端開發(fā))[Git|工具]以前也看過一些關(guān)于git的視頻,可是覺得自己一...
    堅持編程_lyz閱讀 737評論 0 2
  • 你要做一個不動聲色的大人了。不準情緒化,不準偷偷想念,不準回頭看。去過自己另外的生活。你要聽話,不是所有的魚都會生...
    夏末1閱讀 335評論 0 0

友情鏈接更多精彩內(nèi)容