哪些年,我們玩過的Git

作者:玩世不恭的Coder
公眾號:玩世不恭的Coder
時間:2020-06-05
說明:本文為原創文章,未經允許不可轉載,轉載前請聯繫作者

哪些年,我們玩過的Git

前言一、前期工作常用基本概念的理解Git環境的搭建用戶名和郵箱的配置二、Git的理論基礎工作區域工作流程版本庫的初始化文件的四種狀態Git的初步操作三、關於文件的各種操作文件修改之後版本的回退撤銷修改文件的刪除四、本地項目遠程提交到Github倉庫五、Git的分支管理分支的創建與合併單人分支合併時的衝突解決多人協作下的衝突解決六、標籤管理總結參考資料

前言

關於Git,相信每一位該領域的朋友都了解其的強大。Git是一種廣受歡迎的程式碼管理工具,在實際開發中,我們可以通過Git和團隊更好管理我們的項目版本,也大大提高了團隊的開發效率。在實際使用Git的過程中,我們一般只需要掌握其中的十幾條命令就夠用了,Taoye之前對Git也只是停留在會用的狀態,而由於對Git內部的一些細節平時接觸比較少,所以還是會有一點盲區存在。所以,乘著考研結束的這段空閑時間,對之前學習過的Git做一個整理,一方面分享給各位讀者,另一方面也方便自己日後的複習。本文主要介紹了在實際開發過程中所常用的一些Git操作,由於部落客技術水平有限,在內容上的表述難免會有疏忽和遺漏,也懇請各位Coder多多指教。

一、前期工作

常用基本概念的理解

  • 版本控制

所謂的版本控制是指對軟體開發過程中各種程式程式碼、配置文件及說明文檔等文件變更的管理,是軟體配置管理的核心思想之一。簡單的講,就是方便我們對項目程式碼的各種版本進行管理。

我們可以舉個例子來進行說明一下:相信每一位學生,無論是大學畢業或是研究生畢業,都避免不了完成一篇學術性的論文。我們都知道,學術性論文的撰寫都是一個長期的過程,在這個過程也會經歷不斷地反覆修改,這樣就會產生多個論文版本。相信每一位有過此經歷的同學都會感到痛苦,版本多了甚至都不知道每一個版本的論文內容都修改了啥。而我們的Git就能夠很好的管理各種版本的論文,在每一次提交的時候,都可以在comment中記錄我們對論文的修改內容,並且每一個版本的論文都可以進行回滾等操作,也就是隨意的切換各種版本。如此一來,豈不快哉???

此外,我們還可以使用廖大大提到的例子來解釋一下:在許多遊戲中,都會有一個存檔的操作,如果你對接下來的挑戰Boss沒有足夠的信心,就可以進行一次存檔,當我們的角色因為挑戰Boss而喪命,我們就可以讀取其中的存檔而從當前狀態繼續遊戲,而不需要從頭再來,假如遊戲有保存多個存檔的操作,我們還可以讀取不同的存檔來繼續遊戲。同樣地,在我們對文件進行修改到一定程度的時候,就可以保存一個「快照」,一旦我們對自己的操作的不滿意,就可以進行恢復或是回滾操作,這樣就可以「穿梭」到操作之前狀態繼續工作,而無需從頭再來。

  • 工作區(Working Directory)

可以理解成就是電腦本地磁碟的目錄,比如我們在本地創建了一個temp目錄,那這個目錄就叫做工作區。

  • 暫存區(Staging area)

一般存放在”git目錄”下的index文件(.git/index)中,所以我們把暫存區有時也叫作索引(index)。

  • 版本庫(Repository)

我們的工作區有個隱藏目錄.git,它就是Git的本地版本庫。

對於以上部分概念,有些讀者可能不是很了解,大家可以閱讀下面內容之後再回過頭來進行推敲,相信大家一定會有更加透徹的理解。

Git環境的搭建

對於Git的安裝,在前面我們講解Hexo搭建部落格的時候有介紹過,這裡我們再簡單的回顧一下。

你可在git官網中根據自己的需要進行下載://git-scm.com/。打開之後你將看到如下內容,就無腦download for Windows

將其下載到指定的磁碟,然後Windows系統下傻瓜式安裝即可。安裝好後我們打開cmd終端(win+r -> 輸入cmd -> 回車),執行git --version,若出現git version 2.19.2.windows.1之類的版本輸出,那麼恭喜你已經成功安裝Git。

對於Linux作業系統下,我們可以直接通過命令的形式來進行安裝:

1# Linux下安裝Git
2sudo apt-get install git

用戶名和郵箱的配置

我們在安裝完成Git之後,首先第一步要做的就是配置Git的用戶名和郵箱,這個是提交項目的用戶的唯一標識。

1# 配置用戶名和郵箱,配置好之後會寫入C:\Users\M的.gitfig文件中
2git config --global user.name "username"
3git config --global user.email "[email protected]"

global說明:global代表全局的意思,也就是說我們在之後提交的每一個項目採用的都是該用戶名和郵箱。假如我們需要在不同的項目中使用不同的用戶名和郵箱,我們則不需要添加–global參數。

我們將此資訊配置好之後,就會寫入C:\Users\M中的,gitfig文件中。此外,我們也可以用過以下命令來查詢我們所配置的用戶名和郵箱:

1# 查詢配置
2git config -l

二、Git的理論基礎

該部分的內容來自://www.cnblogs.com/best/p/7474442.html

工作區域

Git本地有三個工作區域:工作目錄(Working Directory)、暫存區(Stage/Index)、資源庫(Repository或Git Directory)。如果在加上遠程的git倉庫(Remote Directory)就可以分為四個工作區域。文件在這四個區域之間的轉換關係如下:

  • Workspace:工作區,就是你平時存放項目程式碼的地方
  • Index / Stage:暫存區,用於臨時存放你的改動,事實上它只是一個文件,保存即將提交到文件列表資訊
  • Repository:倉庫區(或本地倉庫),就是安全存放數據的位置,這裡面有你提交到所有版本的數據。其中HEAD指向最新放入倉庫的版本
  • Remote:遠程倉庫,託管程式碼的伺服器,可以簡單的認為是你項目組中的一台電腦用於遠程數據交換

本地的三個區域確切的說應該是git倉庫中HEAD指向的版本

  • Directory:使用Git管理的一個目錄,也就是一個倉庫,包含我們的工作空間和Git的管理空間。
  • WorkSpace:需要通過Git進行版本控制的目錄和文件,這些目錄和文件組成了工作空間。
  • .git:存放Git管理資訊的目錄,初始化倉庫的時候自動創建。
  • Index/Stage:暫存區,或者叫待提交更新區,在提交進入repo之前,我們可以把所有的更新放在暫存區。
  • Local Repo:本地倉庫,一個存放在本地的版本庫;HEAD會只是當前的開發分支(branch)。
  • Stash:隱藏,是一個工作狀態保存棧,用於保存/恢復WorkSpace中的臨時狀態。

工作流程

git的工作流程一般是這樣的:

1. 在工作目錄中添加、修改文件;

2. 將需要進行版本管理的文件放入暫存區域;

3. 將暫存區域的文件提交到git倉庫。

因此,git管理的文件有三種狀態:已修改(modified),已暫存(staged),已提交(committed)

版本庫的初始化

我們需要創建一個目錄來作為我們的項目根目錄,進入到該目錄之後,右鍵git bash來啟動git的操作窗口

1# 選擇對應的目錄,右鍵點擊git bash,然後創建一個目標項目目錄,並進入該目錄
2mkdir temp_project
3cd temp_project

之後,我們需要將創建的目錄初始化為Git所能識別倉庫,可以通過git init來實現。初始化完成之後,就會在該目錄中自動創建出一個.git的隱藏目錄,關於我們項目的版本資訊都會存儲在該目錄當中。

1# 初始化一個倉庫,之後會多出.git隱藏文件
2git init

以上就是我們自定義一個倉庫的過程。此外,我們還可以基於已經存在Git倉庫來進行操作,在Github中查找自己想要的Git倉庫,複製其鏈接,然後通過git clone來對該倉庫進行克隆,從而將該倉庫下載到我們的本地目錄中:

1# 使用clone克隆Github中已經存在的倉庫
2git clone XXXXXX.git

文件的四種狀態

我們要對文件的版本進行控制,首先需要明白當前文件處於什麼樣的狀態,對於不同狀態下的文件,我們可以進行不同的操作。而在Git中,文件主要是有四種狀態:

  • Untracked: 未跟蹤, 此文件在文件夾中, 但並沒有加入到git庫, 不參與版本控制. 通過git add 狀態變為Staged.
  • Unmodify: 文件已經入庫, 未修改, 即版本庫中的文件快照內容與文件夾中完全一致. 這種類型的文件有兩種去處, 如果它被修改, 而變為Modified. 如果使用git rm移出版本庫, 則成為Untracked文件
  • Modified: 文件已修改, 僅僅是修改, 並沒有進行其他的操作. 這個文件也有兩個去處, 通過git add可進入暫存staged狀態, 使用git checkout 則丟棄修改過, 返回到unmodify狀態, 這個git checkout即從庫中取出文件, 覆蓋當前修改
  • Staged: 暫存狀態. 執行git commit則將修改同步到庫中, 這時庫中的文件和本地文件又變為一致, 文件為Unmodify狀態. 執行git reset HEAD filename取消暫存, 文件狀態為Modified

Git的初步操作

了解了文件的狀態之後,我們不妨模擬一個關於論文的例子來初步了解下版本庫以及文件的簡單操作:

  • 創建一個工作區,並進行初始化
1# 創建一個工作區
2mkdir my_dir
3# 進入工作區
4cd my_dir
5# 通過init命令將工作區轉化成Git可以管理的倉庫
6git init
7# 在該倉庫中創建一個paper.txt,用於編寫我們的論文
8touch paper.txt
  • 我們在paper.txt之中編輯如下內容:
1# 我們在`paper.txt`之中編輯如下內容:
2Oh, my god, I will write my graduate paper.
3Come on, Taoye.
  • 通過add命令將paper.txt文件添加到暫存區
1# 通過add命令將paper.txt文件添加到暫存區
2$ git add paper.txt
  • 通過commit命令將暫存區中的內容提交到倉庫
1# 通過commit命令將暫存區中的內容提交到倉庫,指定提交paper.txt文件
2$ git commit -m "start writing my paper" paper.txt
3
4# 對工作區中的文件統一提交至暫存區
5$ git commit -m "XXXXXXXXXXX"

使用commit命令之後,就能將我們暫存區中的內容提交至倉庫中,其中的-m參數表示的是提交說明,用於解釋說明本次提價的內容。提交完成之後,我們可以在Git中通過git status來查看文件的狀態:

1# 查看指定文件的狀態
2git status paper.txt
3# 查看所有文件的狀態
4git status

以上操作過程如下圖所示:

三、關於文件的各種操作

文件修改之後

在上節中,我們已經介紹了文件的四種狀態,並且以關於論文的例子來初步了解了版本庫以及文件庫的簡單操作。

假設現在論文指導老師對Taoye提出了要求:「Taoye同學,時間來不及了,你今天必須給我完成論文的摘要部分!!!否則,後果自負!!!」

「Excuse me?今天?我遊戲打得正嗨呢。面對老師如此強硬的要求,沒辦法了,只能暫停上王者的上分階段了,開始剛論文。」對此,Taoye進行了一個騷氣的三下五除二操作,廣泛的涉獵各種優秀的學者文獻並進行構思,迅速開寫paper.txt中的摘要部分,完成之後的內容如下所示:

1Oh, my god, I will write my graduate paper.
2Come on, Taoye!
3
4I have finished the summary today!

之後,我們再通過git status命令來查看一下該論文文件的狀態:

1$ git status paper.txt
2On branch master
3Changes not staged for commit:
4  (use "git add <file>..." to update what will be committed)
5  (use "git checkout -- <file>..." to discard changes in working directory)
6
7        modified:   paper.txt
8
9no changes added to commit (use "git add" and/or "git commit -a")

此時,我們可以發現工作區中的內容發生了修改,並且與我們上一次提交後版本庫中的內容不一致,這個時候,Git就會提醒我們需要再次進行add、commit操作來將修改後的文件添加並提交至版本庫。另外,我們也可以使用diff命令來查看據上次提交所修改的內容:

1# 查看據上次提交所修改的內容
2git diff paper.txt

當確認文件修改的內容無誤之後,我們就可以使用add、commit操作來提交我們的文件到版本庫中:

1# 添加至暫存區
2git add paper.txt
3# 提交至版本庫
4git commit -m "finished the summary" paper.txt
5# 查看狀態
6git status paper.txt

OK,Taoye從白天剛到深夜。如次一來,總算是完成了老師給的要求,興奮的將內容提交給老師審閱,然後又繼續打王者了。

版本的回退

Taoye連續打了12h的遊戲,有點累了。重新打開自己完美的論文欣賞一下,卻突然發現有幾個錯別字,這種低級錯誤實屬不應該出現啊。思慮半刻,Taoye修改之後,再次add commit就完事了:

paper.txt修改之後的內容:

1Oh, my god, I will write my graduate paper.
2Come on, Taoye!
3
4I have finished the summary today!
5Some typos have been fixed.

再次add、commit操作:

1git add paper.txt
2git commit -m "fixed the typos" paper.txt

也就是說,現在我們論文總共是有三個版本,分別是:初始化的論文、完成摘要的論文、修改錯別字後的論文。我們可以通過log命令來查看各種版本的論文文件:

1# 顯示各種版本文件的詳細資訊
2$ git log paper.txt
3# 通過一行簡單顯示文件的資訊
4$ git log --pretty=oneline paper.txt
5d7938315aa0f4a4d40c6a94a10ab8db25b50e23b (HEAD -> master) fixed the typos
6454cc579f0fe51fdfd97132384a9c5fcaa1993c2 finished the summary
7dc1dcd9501dec52e6160ce98bb5c118abb805289 start writing my paper

從以上log的輸出資訊,我們可以知道所提交文件的所有歷史記錄,其中記錄了提交時間、提交作者(郵箱)、提交說明(-m)等資訊。並且如果我們仔細一看,會發現每一個版本的文件都會對應一個commit id,這個id其實就相當於每一個版本的唯一標識。比如,從上面的輸出結果,我們可以得到第一個版本的commit id = dc1dcd9501dec52e6160ce98bb5c118abb805289,而這個commit id的作用就是方便我們日後自由「穿梭」到各個版本(就是一種穿越時空的意思)。

Taoye的論文指導老師審閱了摘要內容之後,正言厲色的說道:「你這寫的啥玩意兒?牛頭不對馬嘴,而且居然還有錯別字?」

沒辦法了,Taoye只能虛心接受老師的批評,再次苦逼的修改論文了。

我們現在論文是版本三,有沒有一種快速有效的方法回退到版本一呢???在Git中,我們可以通過git reset --hard來實現這個需求,也就是版本的回退。版本的回退可以用兩種形式,一種是通過HEAD來基於當前版本進行回退,另一種是通過commit id來回退到指定的版本,其用法如下所示:

1# HEAD表示的是當前版本,可以通過HEAD^回退到上一個版本
2git reset --hard HEAD^
3git reset --hard HEAD^^     # 回退到上上版本,回退到多少個版本之前就使用多少個^
4git reset --hard HEAD~50    # 回退到50個版本之前
5
6# 指定commit id來進行版本回退
7git reset --hard dc1dcd

值得注意的是,我們通過指定id來進行版本回退的時候,由於id過長,我們沒必要全寫,只需要傳入前幾個字元保證id的唯一性即可。有使用過Docker的朋友,應該會熟悉,我們在指定容器的時候,也是類似的操作。

下面Taoye版本三的論文迅速回退到版本一,騷操作如下:

1$ git reset --hard dc1dcd
2$ cat paper.txt
3Oh, my god, I will write my graduate paper.
4Come on, Taoye.

關於版本穿梭的簡單解釋:其實,在Git中,有個HEAD指針用於指向當前版本,當我們進行版本回退的時候,其實就是改變HEAD的指向,指向對應的版本也就實現了版本的回退。這有點類似於數據結構中鏈表的操作。

此外,我們還可以通過git reflog來查看當前版本文件的變換:

1$ git reflog paper.txt
2dc1dcd9 HEAD@{1}: reset: moving to dc1dcd                       # 版本的回退(回退到版本一)
3d793831 (HEAD -> master) HEAD@{2}: commit: fixed the typos      # 第三次提交(版本三)
4454cc57 HEAD@{3}: commit: finished the summary                  # 第二次提交(版本二)
5dc1dcd9 HEAD@{4}: commit (initial): start writing my paper      # 第一次提交(版本一)

撤銷修改

假設Taoye在寫論文的時候,每天都在不斷地修修改改,心裏面非常的煩躁,很不是滋味。於是乎,在paper.txt中添加如下一句話:

1I don't like my tutor, he often criticizes me.

然而,在打算提交的時候,想了想還是有點不太妥,要是因為這麼一句話,最終導致無法畢業那就完蛋了。對此,我們在對文件commit之前使用’git status paper.txt’命令發現,可以通過checkout --來進行修改撤銷,撤銷至修之前的狀態,操作如下:

1$ git checkout -- paper.txt
2$ cat paper.txt     # 執行之後,可以發現已經撤銷到無最後一句的狀態

以上是發生在我們對工作區中的文件進行修改,但是還沒有執行git add操作,將工作區中的paper.txt添加至暫存區中的場景,已達到撤銷至修改之前的狀態。假設我們在對paper.txt修改之後,再執行git add paper.txt命令將文件添加至暫存區,那麼我們該怎樣撤銷呢?按照思路,我們可以先通過git reset HEAD將暫存區中的內容撤銷掉放回工作區,然後撤銷工作區即可實現需求,對此,有如下操作:

1# 將暫存區中的內容撤銷掉放回工作區
2git reset HEAD paper.txt
3# 撤銷工作區修改的內容
4git checkout -- paper.txt

如此一來,就完美的將I don't like my tutor, he often criticizes me.撤銷掉,Taoye也就能順利畢業了。關於撤銷,要記得與版本回退區分開來,撤銷是我們在對文件進行修改但是還沒有進行commit的時候發生的,而版本回退是在執行了commit提交操作之後發生的。

文件的刪除

在上面的內容中,我們已經詳細的介紹了關於文件的修改、版本回退、撤銷等操作,下面我們來講講文件在刪除之後應該會出現哪些操作。

假設現在出現了這麼一種情況:Taoye有個頑皮的妹妹,她在用我電腦的時候,不小心將我的paper.txt論文文件從本地磁碟刪除了。一氣之下,Taoye將妹妹關進了小黑屋自我反省七天。悲劇啊,Taoye忙活了將近一個月的論文就此煙消雲散,Git該如何挽回這樣的結局呢?

paper.txt文件刪除之後,我們使用git status來查看一下文件的狀態:

1$ git status
2On branch master
3Changes not staged for commit:
4  (use "git add/rm <file>..." to update what will be committed)
5  (use "git checkout -- <file>..." to discard changes in working directory)
6
7        deleted:    paper.txt
8
9no changes added to commit (use "git add" and/or "git commit -a")

在Git看來,你將文件從工作區中刪除,其實也是對文件的一種修改。也就是說,現在我們的工作區中的內容與版本庫中的內容不一致(工作區沒有了paper.txt文件,而版本庫依然存在paper.txt文件)。為了將兩者空間中的內容達到一致,我們現在有兩種選擇,一是將版本庫中的paper.txt文件刪除掉,二是將工作區中的paper.txt文件恢復。

有了以上的思路,我們可以有如下操作:

1# 將版本庫中的paper.txt文件刪除掉,需要執行commit才會生效
2git rm -f paper.txt
3git commit -m "delete the paper.txt"
4
5# 將工作區中的paper.txt文件恢復
6git checkout -- paper.txt

對於文件的刪除,需要注意的是,只有提交到版本庫的文件,才能進行恢復,對於為提交到版本庫的文件是無法進行恢復的。

四、本地項目遠程提交到Github倉庫

Taoye在之前參加2019年騰訊微信小程式比賽的時候,開發了一個關於偵探推理的項目。Taoye現在想要將該項目從本地提交到Github,該如何實現呢?

我們在實現該需求之前,首先需要將本地與Github進行聯通。對此我們應該通過ssh在本地生成一個公鑰,然後在Github中配置該公鑰。操作如下:

1# 1、生成公鑰,執行之後會在.ssh目錄中生成秘鑰文件,其中id_rsa.pub表示的是公鑰
2cd ~/.ssh
3ssh-keygen -t rsa 26647879@qq.com
4# 2、進入github,settings -> SSH keys -> add key,然後將id_rsa.put中的公鑰複製進去
5# 3、配置好公鑰之後,需要驗證本地與github的連通性
6ssh -T git@github.com

在確認本地與Github聯通之後,我們就能正常地將項目從本地遠程提交到Github中了。

  • 登錄Github,創建一個目標倉庫,取名為detective,用來存儲我們的項目,並複製其中的.git鏈接地址
  • 在Github創建倉庫之後,需要將該遠程倉庫與本地關聯起來
1# 在本地關聯目標倉庫,方便之後將項目推送至該遠程倉庫
2git remote add origin https://XXXXXX.git
  • 進入我們的本地項目,然後初始化為git可管理的倉庫
1cd detective
2git init
  • 將工作區中項目的所有文件添加至暫存區
1git add ./*
2

  • 將暫存區中的內容提交到版本庫當中
1git commit -m "commit the detective project" ./*
  • 將項目添加到版本庫之後,我們就可以將該項目推送至遠程倉庫了
1# 第一次推送
2git push -u origin master
3# 推送之後,如果我們的項目發生了修改,我們可以不用在使用-u參數進行推送
4git push origin master
5
6# 另外,如果有需要的話,我們還可以使用clone命令將遠程倉庫克隆到本地
7git clone //XXXXXX.git

五、Git的分支管理

分支是Git當中一個非常重要的概念,分支有點類似於樹枝,也就意味著為了避免影響主線的正常開發,我們可以將任務從主線中分離開來,從而形成一個分支,之後的任務都是在分支中來完成的,當任務完成之後,就可以將完整的任務從分支提交到主線。

在前面版本回退一節當中,我們知道,每一次的提交都會產生一個版本,多次提交自然也就會產生多個版本。我們可以將每一個版本看做是一個珠子,而多個版本就會通過一條線串聯起來,從而形成一條版本鏈。這個版本鏈其實就是一個master分支,也就是我們上面所說的主線,我們每一次的提交都是基於master分支來完成的,而HEAD指針則是用來指向當前分支(在沒有其他分支的前提下,就是指向master)。

下面的圖文講解分支的內容,來自廖大大的教程://www.liaoxuefeng.com/wiki/896043488029600/900003767775424

一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點:

每次提交,master分支都會向前移動一步,這樣,隨著你不斷提交,master分支的線也越來越長。

當我們創建新的分支,例如dev時,Git新建了一個指針叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:

你看,Git創建一個分支很快,因為除了增加一個dev指針,改改HEAD的指向,工作區的文件都沒有任何變化!

不過,從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次後,dev指針往前移動一步,而master指針不變:

假如我們在dev上的工作完成了,就可以把dev合併到master上。Git怎麼合併呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合併:

所以Git合併分支也很快!就改改指針,工作區內容也不變!

合併完分支後,甚至可以刪除dev分支。刪除dev分支就是把dev指針給刪掉,刪掉後,我們就剩下了一條master分支:

分支的創建與合併

在上面,已經介紹了分支的概念及其斷鏈、成鏈的原理過程,下面我們通過Git命令來完成分支的創建與合併。

在Git中,我們可以通過git branch來查看有哪些分支、git branch xxx來創建一個分支,其中帶有*號的表示當前分支、git checkout xxx來切換分支:

1# 創建一個名為dev的分支
2$ git branch dev
3# 從master分支切換到dev分支中
4$ git checkout dev
5# 查看當前有多少分支
6$ git branch
7
8# 此外,我們還實現分支的創建、切換這兩個操作合併執行
9$ git checkout -b dev

現在,我們不妨在剛剛創建的dev分支中對paper.txt的內容進行編輯,在最下方添加這麼一句話:I'm writing the content of my paper.。編輯完成並保存之後,我們提交到版本 庫,並再次切換到master分支,使用cat命令來查看paper.txt的內容。

1$ git checkout dev      # 切換到dev分支
2$ vim paper.txt         # 編輯paper.txt,並增添一句話
3$ git add paper.txt     # 添加到暫存區
4$ git commit -m "writing the content of this paper" paper.txt   # 提交到版本庫
5$ cat paper.txt         # 查看paper.txt內容
6$ git checkout master   # 再次切換到master分支
7$ cat paper.txt         # 查看paper.txt內容

採用cat命令兩次查看paper.txt內容時,我們會發現在執行後一次命令時,paper.txt中的內容並沒有添加新增的一句話,這主要是因為我們剛剛採用vim編輯paper.txt的時候是基於dev分支進行的,提交也是提交到dev分支,而非master分支,所以當我們切換到master分支的時候並不能夠看見paper.txt編輯後的內容。如下圖所示:

而我們要想在master分支中查看到paper.txt的新內容,則需要將dev分支合併到master主分支中才行,採用的是git merge命令,操作如下:

1$ git checkout master
2$ git merge dev

合併完成之後,dev的任務已經完成了,也就沒有必要存在了,可以通過git branch -d xxx來刪除分支:

1$ git branch -d env
2Deleted branch env (was e9c7421).

單人分支合併時的衝突解決

在編程的世界裡,多進程佔據了一個舉足輕重的地位。在高並發、高流量的場景下,我們一般通過多進程來提高項目的服務效率,以便提高用戶體驗。話雖如此,但是在使用多進程的時候,許多問題同樣會慢慢浮出水面。同樣地,Git分支雖然能夠方便多個的用戶協同開發,但是將多個不同內容的分支進行合併的時候卻會產生衝突,作為一個對技術有追求的Coder,我們應該要理解為什麼會產生衝突,以及產生衝突後我們應該怎樣解決。

出現衝突的場景:

  1. 切換到dev分支後,對paper.txt進行編輯,然後將保存後的文件提交到版本庫中。
  2. 切換到master分支,對paper.txt進行編輯,將保存後的文件提交到版本庫。
  3. master分支下,將dev分支進行合併。
 1# 1、在dev分支中對paper.txt文件進行編輯,並提交到版本庫
2git checkout dev
3git vim paper.txt
4git add paper.txt
5git commit -m "the first operation" paper.txt
6
7# 2、在master分支中對paper.txt文件進行編輯,並提交到版本庫
8git checkout master
9git vim paper.txt
10git add paper.txt
11git commit -m "the second operation" paper.txt
12
13# 3、在master分支中,將dev分支合併
14git merge dev

此時,在我們執行merge命令進行分支合併的時候,會出現如下內容:

1$ git merge dev
2Auto-merging paper.txt
3CONFLICT (content): Merge conflict in paper.txt
4Automatic merge failed; fix conflicts and then commit the result.

從如上Git給我們提供的資訊可以知道,此時已經產生衝突,我們必須手動解決衝突才能再次提交,使用git status paper.txt也能查看產生衝突的資訊。我們通過vim打開paper.txt文件可以看見如下內容:

 1Oh, my god, I will write my graduate paper.
2Come on, Taoye!
3
4I have finished the summary today!
5Some typos have been fixed.
6I'm writing the content of my paper.
7<<<<<<< HEAD
8
9the second operation.
10=======
11
12the first operation.
13>>>>>>> dev
14

對此,我們要想解決衝突,則需要在master主分支中手動編輯該文件,編輯並保存完成之後,在將文件提交到版本庫即可:

1vim paper.txt
2git add paper.txt
3git commit -m "solve the conflict" paper.txt
4
5# 查看分支的合併情況
6git log --graph --pretty=oneline --abbrev-commit

下面原則來自廖大大的Git教程://www.liaoxuefeng.com/wiki/896043488029600/900005860592480
在實際開發中,我們應該按照幾個基本原則進行分支管理:

  1. 首先,master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面幹活;
  2. 那在哪幹活呢?幹活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本發布時,再把dev分支合併到master上,在master分支發布1.0版本;
  3. 你和你的小夥伴們每個人都在dev分支上幹活,每個人都有自己的分支,時不時地往dev分支上合併就可以了。

所以,團隊合作的分支看起來就像這樣:

多人協作下的衝突解決

現在導師給Taoye提出的新需求是:兩天內完成論文的初稿並提交。

Taoye大量的時間都花費在開黑中了,兩天內完成這個任務對Taoye來說有點困難。於是乎,Taoye想要找妹妹幫忙完成論文的part1部分,而自己完成part2部分。如此一來,兩個進程協同進行,也就能完美的實現了導師給的需求。妙哉,妙哉!Taoye心想到這,立馬提交自己論文的半成品到遠程倉庫,然後給妹妹提供一個git鏈接供其clone。

1# Taoye將本地論文提交到遠程倉庫
2$ git push origin master
1# 妹妹將遠程倉庫克隆到本地
2$ git clone XXXXXX.git

OK,既然兩人的本地都有了論文文件,那麼接下來就要開始並行完成各自的任務了,為了避免自己的操作給主分支帶來不好的影響,於是在master分支中創建一個dev分支用來編輯文件。由於妹妹的效率比Taoye要快,所以率先完成了論文part1部分的內容,對此,妹妹有如下操作:

1# 妹妹的操作
2$ git branch dev
3$ git checkout dev
4$ vim paper.txt

並且在paper.txt添加內容:sister have finished the part1.,part1部分的內容完成之後,將dev分支迅速推送至遠程倉庫:

1$ git add paper.txt
2$ git commit -m "sister have finished the part1" paper.txt
3$ git push origin dev

OK,Taoye交給妹妹的任務已經完成了,於是就興奮的出去玩耍了。畢竟這篇論文是屬於Taoye的,所以還是需要認真的完成,自然花費的時間也就更多了。經歷了一晚上通宵的時間,終於是順利完成了part2部分的內容,於是屁顛屁顛的將論文提交至遠程倉庫:

1# Taoye的操作
2$ git checkout dev
3$ vim paper.txt
4# 在paper.txt添加一句:taoye have finished the part2
5$ git add paper.txt
6$ git commit -m "taoye have finished the part2" paper.txt
7$ git push origin dev

而就在Taoye推送論文到遠程倉庫的時候,由於自己推送內容與妹妹推送的內容不一致,所以導致推送失敗。我們通過Git給出的提示資訊可以知道,要想解決這個衝突,首先需要通過pull命令將最新的提交拉取下來,然後與本地合併,解決衝突之後再推送到遠程倉庫。為此,Taoye立馬執行了pull命令:

1$ git pull

而在執行pull命令的時候,糟糕的問題又接踵而至了,Git提示說:There is no tracking information for the current branch.,也就是說本地的分支與遠程沒有建立鏈接。對此,我們可以建立鏈接後再執行pull命令:

1$ git branch --set-upstream-to=origin/dev dev
2$ git pull

雖然可以執行pull命令,但是會出現衝突提示,所以我們需要首先手動解決衝突,解決的方式和上節中一樣:對paper.txt文件進行編輯,然後提交並推送至遠程倉庫。所以,Taoye對paper.txt文件進行編輯之後,內容如下:

1taoye have finished the part1
2taoye have finished the part2

編輯好後,將文件保存並推送至遠程倉庫:

1$ git add paper.txt
2$ git commit -m "finished the part1 and part2" paper.txt
3$ git push origin dev

所以,在多人協作工作時我們一般准守如下過程:

  1. 完成任務後首先使用git push origin xxx推送至遠程倉庫
  2. 如果推送失敗,則需要執行git pull命令將最新的提交拉取下來
  3. 如果拉取失敗,則可能需要建立連接,所以執行git branch --set-upstream-to=origin/xxx xxx命令
  4. 解決之後,再次執行git pull命令嘗試拉取最新提交
  5. 此時,我們需要對衝突文件進行修改,等到修改完成之後,將文件推送至遠程倉庫

六、標籤管理

在上面的內容中,我們有介紹過,可以根據版本號(很長的字元串)來實現任意版本之間的穿越。但是通過這種較長字元串版本號的形式對用戶並不是特別友好,看的腦闊有點疼。所以我們一般可以給每一個版本貼上一個標籤,並且可以通過這個標籤來定位任意的版本,從而更加容易的實現版本穿越等需求。

在這,我們也可以將標籤看做是版本號的一個別名,使用標籤就相當於使用版本號,兩者的作用是等價的。如果有使用過Docker的話,應該會知道,在Docker中,我們每創建一個容器,Docker都會分配一個容器id以便於我們定位對應的容器,而我們自己也可以為每一個容器定義一個別名。我們在使用一系列的Docker命令對容器進行操作的時候,既可以通過容器id,也可以通過別名的形式來進行操作。

Git中,我們主要是採用tag命令來管理標籤,由於標籤比較的簡單,這裡就不一一講解了,與標籤相關的命令主要有以下一些:

 1$ git tag                       # 查看所有標籤
2$ git tag v1.0                  # 沒有指定版本,則默認給當前版本打標籤
3# 查看各個版本資訊(版本號)
4$ git reflog paper.txt | git log --pretty=oneline paper.txt
5$ git tag v0.9 a35d6b           # 根據版本號來打標籤
6# 打標籤的同時,給標籤添上相應的說明
7$ git tag -a v0.1 -m "version 0.1 released" a35d6b
8$ git show v1.0                 # 查看對應的標籤資訊
9$ git tag -d v0.9               # 刪除標籤
10
11$
 git reset --hard v0.9         # 根據標籤實現版本穿越

總結

總的來講,Git上手還是很快的,關鍵在於平時需要加強Git操作命令的訓練。以上關於Git的命令只是一部分,但如果掌握上述內容,還是可以輕鬆滿足Git的實際需求的。如果有興趣想要了解更多關於Git的知識,可以自行查閱相關的文檔或是書籍來進一步的學習。

參考資料

一個小時學會Git://www.cnblogs.com/best/p/7474442.html#!comments
廖雪峰Git教程://www.liaoxuefeng.com/wiki/896043488029600

Tags: