1. 程式人生 > >Git | 一篇文章搞定Git、GitHub的理解和使用(學習筆記)

Git | 一篇文章搞定Git、GitHub的理解和使用(學習筆記)

Git learning note

本筆記整理自廖雪峰老師的Git教程,加上了自己的實踐結果和一些理解,旨在使科研工作者(基本上是獨立開發的那種)看完就能理解和使用Git。廖老師的教程生動活潑,條理清晰,推薦閱讀。還可以贊助哦。

目錄

Git 簡介

Git是分散式版本控制系統。 啥是分散式,和與之相對的集中式咧?

舉個栗子

小a,小b,小c共同開發專案,有兩種版本控制方法:

集中式:

伺服器維護一個最新版本專案,當小a/b/c要修改專案的時候,就從伺服器上下載專案,改完了上傳。為了保證伺服器上永遠是最新的,就需要一直聯網;而且一旦中央伺服器崩了,一切都over。

CVS是最早的版本控制系統,SVN是目前最流行的集中式版本控制系統。上述兩個都是開源、免費的。

分散式

一開始,小a本地電腦有一個倉庫,用於儲存“小a對專案的修改”(小b、小c也一樣)。需要更新整個專案的時候,小a、b、c之間就互相推送“修改”,那麼本地倉庫就儲存了“所有人對該專案的修改”。因此,每個人都有最新版的專案,斷網斷電也不用擔心。

時下最火熱的當然就是Git啦~

Git 與 GitHub

專案伊始,只有一個原始版本庫(版本庫也叫倉庫),別的機器可以clone這個原始版本庫,那麼所有clone的機器,它們的版本庫其實都是一樣的,並沒有主次之分。

所以團隊協作的時候,這樣做到版本控制解:找一臺電腦充當伺服器的角色,oncall 24 hour,其他每個人都從這個“伺服器”倉庫clone一份到自己的電腦上,並且各自把各自的提交推送到伺服器倉庫裡,也從伺服器倉庫中拉取別人的提交。(具體操作請看廖老師教程)

沒有搭建伺服器怎麼辦?用GitHub。這個網站就是提供Git倉庫託管服務的,所以,只要註冊一個GitHub賬號,就可以免費獲得Git遠端倉庫。

GitHub還是一個開源協作社群,通過GitHub,既可以讓別人參與你的開源專案,也可以參與別人的開源專案。

悄咪咪說一句,國內的Git託管服務是碼雲

Git使用

命令簡要解釋

命令 解釋
git init 初始化/新建倉庫
git config [–global] 設定Git配置
git add < filename > 新增檔案修改到暫存區
git commit 從暫存區提交本次修改到當前分支
git status 檢視倉庫狀態(哪些檔案修改、是否提交)
git diff 檢視和上一次commit版本的具體差別
git log 檢視歷史commit
git log –graph 以圖的形式檢視當前分支歷史commit
git reset 將當前版本退回設定版本
git checkout – < filename > 撤銷工作區檔案的修改
git checkout < branchname > 切換到指定分支
git checkout -b < branchname > 建立並切換到分支
git rm < filename > 從當前版本刪除檔案(相當於在檔案系統刪除檔案後add)
git remote add < 遠端倉庫名 > < 遠端倉庫地址 > 新增遠端倉庫
git remote -v 檢視所有遠端倉庫和地址
git push < 遠端倉庫名 > < 本地分支名 > 將本地分支推送到遠端倉庫
git clone < 遠端倉庫地址 > 將遠端倉庫克隆到本地工作路徑
git fetch < 遠端倉庫名 > 抓取遠端倉庫所有更新到本地倉庫
git pull < 遠端倉庫名 > 抓取併合並遠端倉庫所有更新到本地倉庫
git branch 檢視所有分支
git branch < branchname > 建立分支
git branch -d < branchname > 刪除分支,將“-d”改為“-D”是強制刪除
git branch -dr < remote/branch > 刪除遠端分支
git merge < branchname > 將指定分支合併到當前分支上

安裝

在cmd輸入git,看看系統有沒有安裝Git:

比如在linux下:

$ git
The program 'git' is currently not installed. You can install it by typing:
sudo apt-get install git

建立倉庫

版本庫又名倉庫,英文repository,可以簡單理解成一個目錄,這個目錄裡面的所有檔案都可以被Git管理起來,每個檔案的修改、刪除,Git都能跟蹤,以便任何時刻都可以追蹤歷史,或者在將來某個時刻可以“還原”。

在你的工作目錄下,輸入

$ git init
Initialized empty Git repository in /Users/michael/learngit/.git/

生成的 .git 目錄就是用來跟蹤管理倉庫的。

配置倉庫

git config命令用於一系列配置。配置Git的時候,加上--global是針對當前使用者起作用的(比如給命令起別名),如果不加,那隻針對當前的倉庫起作用。

配置檔案:
每個倉庫的Git配置檔案都放在.git/config檔案中,當前使用者的Git配置檔案放在使用者主目錄下的一個隱藏檔案.gitconfig中。

新增、提交:

用命令git add <filename> 將檔案更改新增到暫存區,git add .新增所有更改到暫存區

$ git add readme.txt
$ git add .

新增成功則沒有返回訊息

$ git commit -m "wrote a readme file"
[master (root-commit) cb926e7] wrote a readme file
 1 file changed, 2 insertions(+)
 create mode 100644 readme.txt

引數-m 後面是這次提交的說明

忽略某種檔案

工作目錄下建立一個名為.gitignore的檔案,把要忽略的檔名或資料夾或檔案型別填進去,Git就會在commit的時候自動忽略這些檔案。

不需要從頭寫.gitignore檔案,GitHub已經為我們準備了各種配置檔案,只需要組合一下就可以使用了。所有配置檔案可以直接線上瀏覽:github/gitignore

忽略檔案的原則是:

  • 忽略作業系統自動生成的檔案,比如縮圖等;
  • 忽略資料集;
  • 忽略編譯生成的中間檔案、可執行檔案等,也就是如果一個檔案是通過另一個檔案自動生成的,那自動生成的檔案就沒必要放進版本庫,比如Java編譯產生的.class檔案;
  • 忽略你自己的帶有敏感資訊的配置檔案,比如存放口令的配置檔案。

舉個栗子

一個.gitignore檔案

# Windows:
Thumbs.db
ehthumbs.db
Desktop.ini

# Python:
*.py[cod]
*.so
*.egg
*.egg-info
dist
build

# My configurations:
db.ini
deploy_key_rsa

更多實操請看廖老師教程。

撤銷修改

一般步驟是 commit_last->)add_1->(add_2->…add_n)->commit,你可能會在這些時間點希望撤銷:

  1. 修改還沒add到暫存區,希望撤銷工作區的修改,則撤銷結果是撤銷回上一次commit或上一次add,取決於add_1否是已經發生過

    命令git checkout -- <filename>可以丟棄工作區的修改:

    $ git checkout -- readme.txt

    --這個引數說明是針對當前分支的檔案,如果只用 git checkout <branch_name>表示的是切換分支

  2. 修改已經add_i,想撤銷到add_j

    不行,已經add過之後,工作區沒東西可以撤銷。暫存區裡面沒有“版本回退”。

總之,只能使用git checkout撤銷工作區的修改。版本回退只能用git reset在commit過的版本庫裡面回退。

“刪除檔案”也是一種修改操作,commit過的檔案一直存在版本庫裡,所以在工作區刪除檔案(如testdel.txt)也可以撤銷。此時用git checkout -- <deletedfilename>可撤銷還沒有add的“刪除操作”

舉個栗子 :(|)表示當前

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   git node.md
        new file:   testdel.txt

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:    testdel.txt

(檔案沒有commit過) add->刪除檔案->add(|)->commit 的status:工作區修改為空不可撤銷

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        modified:   git node.md

commit->刪除檔案->add(|)->commit 的status:工作區修改為空不可撤銷,需要版本回退

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        deleted:    testdel.txt

$ git checkout -- testdel.txt
error: pathspec 'testdel.txt' did not match any file(s) known to git.

前面說到,commit過的檔案一直存在版本庫裡,如果確定已經不需要該檔案了,可以直接git rm <filename> (經實踐,親測相當於刪除後add)

檢視狀態

在工作目錄下使用命令git status檢視當前倉庫狀態,包括更新了哪些檔案,有沒有add,有沒有commit:

 $ git status
On branch master
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:   test.py

Untracked files:
  (use "git add <file>..." to include in what will be committed)

        git node.md

no changes added to commit (use "git add" and/or "git commit -a")

當前對比上一次commit具體做了什麼更改,可用git diff命令檢視,該命令顯示格式是Unix通用的diff格式。

$ git diff

檢視當前工作區的檔案與當前分支最新版本的區別:

$ git diff HEAD -- <filename>

檢視提交歷史

檢視歷史commit,用git log,顯示順序是由最近到最久遠

$ git log
commit e950b67202ee0060faca06f963bfa97550ca90de
Author: unknown <411037167@qq.com>
Date:   Wed Aug 9 23:59:14 2017 +0800

    add git node

commit f8d90c0d112489303e919915d15519da9fdeb5bc
Author: unknown <411037167@qq.com>
Date:   Wed Aug 9 22:27:39 2017 +0800

    update readme.txt

commit ed4ce2f7ef016e99182dcaf75f9335d70f53ec1b

git log --pretty=oneline得到一個更加簡潔的一行顯示方式:

$ git log --pretty=oneline
e950b67202ee0060faca06f963bfa97550ca90de add git node
f8d90c0d112489303e919915d15519da9fdeb5bc update readme.txt
ed4ce2f7ef016e99182dcaf75f9335d70f53ec1b add readme.txt

這一長串 commit id,是一個大數字經過SHA1編碼的十六進位制表示。在引用的時候可以省略地只打前幾個字元,只要系統能區別就行。

版本回退

把當前版本回退到上一個版本,就可以使用git reset命令:

$ git reset --hard HEAD^
HEAD is now at ea34578 add distributed

返回指定commit id(可簡寫)的版本:

$ git reset --hard 3628164
HEAD is now at 3628164 append GPL
  • HEAD: 實際是一個指標,作用是指向版本庫中當前分支的當前版本
    • HEAD^ :上一個版本
    • HEAD^^ :上上一個版本
    • HEAD~100 :往上100個版本

當回退版本的時候,Git僅僅是把HEAD改為指向你指定回退的版本,然後順便把工作區的檔案更新了。(所以回退ok,前進也ok,畢竟只是把指標換個地方指,就意味著“當前版本是這個啦”)

git reflog命令檢視每一次git 操作,從而可以檢視commit id:

$ git reflog
ea34578 HEAD@{0}: reset: moving to HEAD^
3628164 HEAD@{1}: commit: add source code
ea34578 HEAD@{2}: commit: update readme
cb926e7 HEAD@{3}: commit (initial): add readme

這個例子表示即使從“add source”回退了上一個版本“update readme”(第一行),還是可以檢視“update readme”的commit id(第二行顯示內容)

工作區、暫存區、版本庫

關於工作區(working directory)、暫存區(storage)、版本庫(repository)的概念,請看廖雪峰教程配圖和講解

一般步驟是 commit_last->)add_1->(add_2->…add_n)->commit,每次add都更新暫存區,每次commit都更新版本庫。

  • 版本庫可以回退版本、切換分支
  • 還沒暫存的工作區操作可以被撤銷
  • 暫存區的add_i,add_(i+1),…,add_n之間不可以回退。

GitHub使用

Github上的東西是公開的,如果不想給人看,要交“保護費”的。想要私有的另一個方法是:自己搭建一臺伺服器作為Git倉庫——適用於企業內部開發。 接下來主要還是GitHub的相關使用。

通訊設定

Git使用SSH連線,使用GitHub,你需要做如下設定:

  1. 註冊GitHub賬號:用郵箱就能註冊。

  2. 在GitHub新增公鑰:
    登陸GitHub網站。點選個人頭像處下拉選單view profile and more。左邊personnal settings一欄點選“SSH and GPG keys”。點選新增公鑰。

第2-3步對你要用到的每臺機器都可以設定
SSH連線在第一次驗證GitHub伺服器的Key時(如第一次clone或push),會發出SSH警告,需要你確認GitHub的Key的指紋資訊是否真的來自GitHub的伺服器,敲yes就好。

從本地到遠端:push

把本地的倉庫推送到GitHub的倉庫:

  1. 在GitHub網站上建立倉庫:
    點選頭像旁邊的+號,new repository是建立新倉庫,import reposi是從其他版本控制系統匯入檔案和歷史記錄。這裡我建立了一個名叫“nonblindGAN”的專案。

  2. 從本地倉庫關聯遠端庫GitHub,使用命令:
    git remote add <remote_name> [email protected]:path/repo-name.git

  3. 推送內容到GitHub的倉庫:
    如果用git push <remote_name> <branch_name>命令,把當前分支master推送到名為origin的遠端倉庫,則具體步驟為:

    • 第一次推送master分支的所有內容,使用命令:

      $ git push -u origin master
    • 之後每次本地commit後,如需推送master最新修改,使用命令:

      $ git push origin master

    如需推送當前分支最新修改,使用命令:

    $ git push

    引數-u:如果當前分支與多個主機存在追蹤關係,則可以使用-u選項指定一個預設主機,這樣後面就可以不加任何引數使用git push。

舉個栗子

我在賬號chudanwu下建立了一個倉庫“nonblindGAN”,建立完後打算按照提示:‘push an existing repository from the command line 幹活。

第一行是通過https協議連線遠端倉庫,第二行是通過SSH協議。使用https除了速度慢以外,還有個最大的麻煩是每次推送都必須輸入口令,但是在某些只開放http埠的公司內部就無法使用ssh協議而只能用https。

$ git remote add origin https://github.com/chudanwu/nonblindGAN.git
$ git remote add origin [email protected].com:chudanwu/nonblindGAN.git
$ git push -u origin master

遠端庫的名字就是origin,這是Git預設的叫法(比如clone的時候會自動命名,後面會提到),可以用別的,但是origin這個名字一看就知道是遠端庫。

從遠端到本地:clone & pull & fetch

clone

假設本地沒有倉庫GitHub遠端端有個倉庫,那麼我們要開發,就把GitHub上的倉庫clone下來(將會建立一個資料夾)。此時使用命令:
git clone <remote_address>
clone的同時會自動命名remote為origin,想要自己設定的話,給clone命令加上引數-o:
git clone -o <remote_name> <remote_address>

同樣remote_address可以是https的,或者ssh,GitHub還支援其他協議,但通過ssh支援的原生git協議速度最快。

  • 通過SSH clone:

    $ git clone [email protected]:chudanwu/nonblindGAN.git
    
  • 通過https clone:

    $ git clone https://github.com/chudanwu/nonblindGAN.git
    

【本小節後面的內容涉及“分支”的概念,建議先行跳過,看完第三部分“分支”再倒回來】

關係圖

fetch

如果本地已經建立了倉庫,比如已經initremote add ,現在需要把倉庫裡存在的內容抓取過來。此時可以在工作區(工作目錄)使用命令
git fetch <remote_name>
或者
git fetch <remote_name> <remote_branch>

git fetch作用是將某個遠端庫的所有分支或指定分支,全部取回本地。命令通常用來檢視其他人的程序,因為它取回的程式碼對你本地的開發程式碼沒有影響。

抓取不會改變工作區,而是暫時儲存了 remote倉庫所有分支到本地,命名為remote_name/remote_branch,如果要更新工作區,還需要merge一下,即:
git merge remote_name/remote_branch

舉個栗子

在本地的新倉庫處(只做了initremote add),把叫做github的遠端庫的分支全部抓取下來:

PS C:\Users\**\blindGAN> git fetch github
remote: Counting objects: 29, done.
remote: Compressing objects: 100% (23/23), done.
remote: Total 29 (delta 4), reused 29 (delta 4), pack-reused 0
Unpacking objects: 100% (29/29), done.
From github.com:chudanwu/blindGAN
 * [new branch]      dev        -> github/dev
 * [new branch]      master     -> github/master
PS C:\Users\**\blindGAN> git branch
#無顯示,沒有branch
PS C:\Users\**\blindGAN> git checkout dev
Branch dev set up to track remote branch dev from github.#建立了追蹤關係
Switched to a new branch 'dev'
PS C:\Users\**\blindGAN> git checkout master
Branch master set up to track remote branch master from github.#建立了追蹤關係
Switched to a new branch 'master'  #checkout後,都自動在本地庫建上相應的branch
PS C:\Users\**\blindGAN> git checkout dev
Switched to branch 'dev'
Your branch is up-to-date with 'github/dev'.  #再次切換告訴你是最新的
PS C:\Users\**\blindGAN> git branch  #現在檢視branch有好幾個了
* dev
  master
PS C:\Users\**\blindGAN> git branch -a 
#顯示所有branch,後面兩個是fetch來的
* dev
  master
  remotes/github/dev
  remotes/github/master

如果抓取過來的分支/所有分支,不需要merge,(比如抓取dev分支,在dev上再做分支自己開發新功能用),可使用命令
git checkout -b <newbranch> <remote_name/remote_branch>,即

$ git checkout -b newfun1 origin/dev

pull

  • git pull <remote_name> <remote_branch>:<local_branch>:抓取遠端庫某個分支的更新,再與本地的指定分支合併。
  • git pull <remote_name> <remote_branch>:抓取遠端庫某個分支的更新,再與本地當前分支合併。
  • git pull <remote_name>:本地的當前分支自動與存在追蹤關係的遠端庫分支”進行合併。

下面兩個操作是一個意思:

$ git pull remote_name remote_branch
$ git fetch remote_name
$ git merge remote_name/remote_branch

都是取回remote_name/remote_branch分支,再與當前分支合併。

在一些場合,Git會自動建立追蹤(tracking)關係,比如上面fetch的例子,再比如clone的時候。建立追蹤關係的原則是“分支名相同的自動建立”。
Git也允許手動建立追蹤關係:
git branch --set-upstream <local_branch> <remote_name/remote_branch>

如果當前分支與遠端分支存在追蹤關係,pull就可以省略遠端分支名,寫為
git pull <remote_name>
作用是:本地的當前分支自動與對應的遠端庫的”追蹤分支”(remote-tracking branch)進行合併。

push

和pull:git pull <remote_name> <remote_branch>:<local_branch>類似,有push。
前面提到過的push,實際上寫法應該是
git push <remote_name> <local_branch>:<remote_branch>
表示將本地指定分支推送到遠端庫指定分支。
如果省略:<remote_branch>,則自動在遠端庫建立新分支;
如果省略<local_branch>(寫為git push <remote_name> :<remote_branch>,則自動刪除遠端庫的分支(因為push了空分支))

但通常,實際上大家都會將本地分支和遠端分支命名成一樣的,所以由於追蹤關係的存在,pull 和 push可以分別簡寫成

  • pull:git pull <remote_name>
  • push:git push <remote_name>

參與開源專案

在GitHub上,可以任意Fork開源倉庫;自己擁有Fork後的倉庫的讀寫許可權;可以推送pull request給官方倉庫來貢獻程式碼。

具體來說步驟如下:

  1. 對開源專案,從官方fork到自己的倉庫

  2. 對fork到倉庫的專案,clone 到本地(一定要從自己的賬號下clone倉庫,這樣你才能推送修改。如果從官方倉庫地址clone,因為沒有許可權,你將不能推送修改。)

  3. 修改完專案後,往自己的倉庫push。

  4. 如果你希望官方倉庫能接受你的修改,可以在GitHub上發起一個pull request。當然,對方是否接受你的pull request就不一定了。

分支

無論建立、切換和刪除分支,Git在1秒鐘之內就能完成!無論你的版本庫是1個檔案還是1萬個檔案。

為什麼需要分支

用分支的目的是什麼?或者說,使用的情景是怎樣的?

舉個栗子

你準備開發一個新功能,需要兩週才能完成,第一週你寫了50%的程式碼,如果立刻提交,由於程式碼還沒寫完,不完整的程式碼庫會導致別人不能幹活了。如果等程式碼全部寫完再一次提交,又存在丟失每天進度的巨大風險。

現在有了分支,就不用怕了。你建立了一個屬於你自己的分支,別人看不到,還繼續在原來的分支上正常工作,而你在自己的分支上幹活,想提交就提交,直到開發完畢後,再一次性合併到原來的分支上,這樣,既安全,又不影響別人工作。

分支的原則

在實際開發中,我們應該按照幾個基本原則進行分支管理:

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

在企業級開發中,工作流程圖可參考以下文章

都有很多插圖,棒棒噠。

建立、合併分支

第一條分支是主分支master。你可以把它想象成一個時間串兒,每commit一次,就給這條串兒新增一個節點——串就變長一點。
HEAD指向你最新commit的那個結點。實際上是HEAD指向當前分支名,當前分支名指向最新commit,所以HEAD指向當前分支的commit。
我們按照例子中的步驟來看(其實是很實際的例子),有以下幾步:

  1. 現有主分支master

  2. 建立分支dev

  3. 在分支dev上做開發,正常的commit

  4. 開發完成,合併分支dev和master

  5. 刪除分支dev

一開始的時候,master分支是一條線,Git用master指向最新的提交,再用HEAD指向master,就能確定當前分支,以及當前分支的提交點。建立新的分支dev時,Git新建了一個指標叫dev,指向master相同的commit,再把HEAD指向dev,就表示當前分支在dev上。

1=>2
                                          dev ← HEAD
                  建立分支dev              ↓
--o--o--o--o--o       ==>    --o--o--o--o--o
              ↑                           ↑
           master ← HEAD                master         

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

3
               dev ← HEAD               HEAD → dev
                ↓                              ↓
                o                            o--o
               /   再次commit               /
--o--o--o--o--o       ==>    --o--o--o--o--o
              ↑                           ↑
           master                       master         

在dev上的工作完成了,就可以把dev合併到master上。在我們的例子中,直接把master指向dev的當前提交,就完成了合併。刪除dev分支就是把dev指標給刪掉,刪掉後,我們就剩下了一條master分支

4=>5
                  dev  
                   ↓    刪除dev                   
                o--o      ==>            HEAD → master
               /   ↑                             ↓
--o--o--o--o--o   master      --o--o--o--o--o--o--o   
                   ↑
                 HEAD

接下來看看實際上命令怎麼敲:

建立分支並切換到分支

$ git checkout -b dev
Switched to a new branch 'dev'

git checkout命令加上-b引數表示建立並切換,相當於

$ git branch dev
$ git checkout dev
Switched to branch 'dev'

平時切換分支的時候,注意在切換前把add到暫存區的修改commit掉,因為分支是共享暫存區的,一不小心就會有產生”誤會”

檢視現有分支

命令git branch <branch_name>可檢視現有分支,有“*”號的分支是當前所在分支

$ git branch
* dev
master

合併分支

前情提要:在分支上dev修改readme.txt,然後commit。

$ git merge dev
Updating d17efd8..fec145a
Fast-forward
 readme.txt |    1 +
 1 file changed, 1 insertion(+)

輸出Fast-forward:說明這次合併是“快進模式”,也就是直接把master指向dev的當前提交,所以合併速度非常快。當然,也不是每次合併都能Fast-forward。

合併分支時,如果可能,Git會用Fast forward模式(如例子裡的4=>5),但這種模式下,刪除分支後,會丟掉分支資訊。

如果要強制禁用Fast forward模式,Git就會在merge時生成一個新的commit,這樣,從分支歷史上就可以看出分支資訊。禁用的情況下merge命令是:git merge --no-ff -m "commit description" branchname
因為本次合併要建立一個新的commit,所以加上-m引數,把commit描述寫進去。

$ git merge --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
 readme.txt |    1 +
 1 file changed, 1 insertion(+)
fast-forward
               dev                      
                ↓                               
                o                            dev
               /   merge dev                 ↓ 
--o--o--o--o--o       ==>    --o--o--o--o--o--o   
              ↑                              ↑  
           master ← HEAD                   master ← HEAD 


no-ff
               dev                          dev
                ↓                           ↓
                o                            o
               /   merge dev                / \
--o--o--o--o--o       ==>    --o--o--o--o--o---o
              ↑                               ↑
           master ← HEAD             HEAD → master                  

刪除分支

合併完成後,就可以放心地刪除dev分支了。當然,當前分支是不能刪除的,請先切換。

$ git branch -d dev
Deleted branch dev (was fec145a).

分支衝突

不是所有的merge都可以成功,當兩個分支都commit了,如果互相有衝突(同一個地方有改動,不管內容是否一樣,都有衝突),是merge不了的。需要檢視衝突部分,手動修改後,才能merge。

舉個栗子

我們在分支master和分支test中分別修改 readme.txt並commit,那麼這時如果merge,就會在readme.txt上產生衝突。

               test  
                ↓    
                o  
               /      
--o--o--o--o--o--o             
                 ↑
               master ← HEAD
$ git merge test
Auto-merging readme.txt
CONFLICT (content): Merge conflict in readme.txt
Automatic merge failed; fix conflicts and then commit the result.

上面這段輸出說明自動merge失敗了。要你解決衝突。
而在readme.txt中,Git用<<<<<<<,=======,>>>>>>>標記出不同分支的衝突部分內容。

git status命令檢視衝突的檔案:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)

        both modified:   readme.txt

no changes added to commit (use "git add" and/or "git commit -a")

前面是說本地版本比遠端版本多commit了兩次,後面是說明衝突的檔案和解決方法:readme.txt檔案存在衝突,必須手動解決衝突後再提交。也就是說mergeing被中斷了,等你改掉readme.txt中的衝突段然後commit的時候,將會完成merge

改完之後(比如直接把衝突段都留下來,各留一行),add,然後看status:

$ git status
On branch master
Your branch is ahead of 'origin/master' by 2 commits.
  (use "git push" to publish your local commits)
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:

        modified:   readme.txt

還剩下commit就完事了!commit後就完成merge了。

用圖檢視分支合併情況

git log --graph命令可以看到分支合併圖。

簡化版:git log --graph --pretty=oneline --abbrev-commit

這個命令好像很長的樣子,沒關係,可以給他設定別名:

$ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"

一個華麗麗的效果,靠git lg就能輸出來。

當前分支的是直線,merge 過來的分支是折線。fast-forward的合併看不到折線(只要直線),禁用fast-forward後可以看到。

舉個栗子

#改動readme,解決衝突後,test合併到master
#當前分支為master
$ git log --graph --pretty=oneline --abbrev-commit
* 3f619af master readme2
*   0e15a96 conflict fixed
|\
| * d99660c test readme
* | 8a5a09a master readme
|/
* 75692c8 branch test
...
#上面完事後,再次修改兩個分支的readme,解決衝突後,master合併到test
#當前分支為test
$ git log --graph --pretty=oneline --abbrev-commit
*   f0d6e01 master merge to test1
|\
| * 3f619af master readme2
| *   0e15a96 conflict fixed
| |\
| * | 8a5a09a master readme
* | | bf852ab test readme2
| |/
|/|
* | d99660c test readme
|/
* 75692c8 branch test
...

高階內容

包括對bug開一個分支如何操作,對實驗性新功能(feature)開一個分支如何操作,多人合作啥時候遠端推送等,暫時用不到的技能,請參看廖老師的教程

相關推薦

Git | 文章GitGitHub理解使用學習筆記

Git learning note 本筆記整理自廖雪峰老師的Git教程,加上了自己的實踐結果和一些理解,旨在使科研工作者(基本上是獨立開發的那種)看完就能理解和使用Git。廖老師的教程生動活潑,條理清晰,推薦閱讀。還可以贊助哦。 目錄 Git 簡

文章懂DataSetDataFrameRDD-《每日五分鐘大數據》

implicit 操作數 frame 大數據 函數 for 臨時 變量 ade 1. 三者共性: 1、RDD、DataFrame、Dataset全都是spark平臺下的分布式彈性數據集,為處理超大型數據提供便利 2、三者都有惰性機制,執行trainform操作時不會立即執

文章懂DataSetDataFrameRDD-《每日五分鐘大資料》

1. 三者共性: 1、RDD、DataFrame、Dataset全都是spark平臺下的分散式彈性資料集,為處理超大型資料提供便利 2、三者都有惰性機制,執行trainform操作時不會立即執行,遇到Action才會執行 3、三者都會根據spark的記憶體情況自動快取運算,這樣即使資料量很大,也不用擔心會

文章Markdown

markdown一篇文章搞定Markdown一篇文章搞定Markdown

文章前端面試

ron miss 就是 節點數 網頁 那是 png html 性能優化 本文旨在用最通俗的語言講述最枯燥的基本知識 面試過前端的老鐵都知道,對於前端,面試官喜歡一開始先問些HTML5新增元素啊特性啊,或者是js閉包啊原型啊,或者是css垂直水平居中怎麽實現啊之類的基礎問題

文章你的spring定時器

Cron表示式是一個字串,字串以5或6個空格隔開,分開工6或7個域,每一個域代表一個含義,Cron有如下兩種語法 格式: Seconds Minutes Hours DayofMonth Month DayofWeek Year 或 Seconds Minutes Hours DayofMo

文章懂DataSetDataFrameRDD

1. 三者共性: 1、RDD、DataFrame、Dataset全都是spark平臺下的分散式彈性資料集,為處理超大型資料提供便利 2、三者都有惰性機制,執行trainform操作時不會立即執行,遇到Action才會執行 3、三者都會根據spark的記憶體情況自動快取運算,這樣即使資

面試專欄|文章ArrayListLinkedList所有面試問題

在面試中經常碰到:ArrayList和LinkedList的特點和區別? 個人認為這個問題的回答應該分成這幾部分: 介紹ArrayList底層實現 介紹LinkedList底層實現 兩者個適用於哪些場合 本文也是按照上面這幾部分組織的。 ArrayList的原始碼解析

文章C語言所有的基本語法.

C 作為一門工程實用性極強的語言,提供了對作業系統和記憶體的精準控制,高效能的執行時環境,原始碼級的跨平臺編譯等優點,才是我們必須學習和使用 C 的理由。 C語言標記/令牌 C語言程式包括各種令牌和令牌可以是一個關鍵字,識別符號,常量,字串文字或符號。 例如,下面的C語句包括五個

文章Maven安裝到建立maven版Spring MVC專案及配置

配置maven 本地安裝 新建變數名為MAVEN_HOME,值為maven安裝目錄的系統變數 在系統變數名為Path的值中新增“%MAVEN_HOME%\bin;” cmd命令列輸入mvn -v 檢視是否安裝成功 修改maven配置檔案 apac

文章2017最新標題優化方法

很多人都說小2哥你為什麼一直都在寫直通車的文章?為什麼不寫寫自然搜尋方面的內容?你不知道我們新手更需要的就是這些免費流量的內容嗎? 我從13年開始寫文章,13年和14年我是寫自然搜尋寫的最多的,但是從15年開始的時候,我基本上寫的文章都是圍繞直通車。 其實這也是和我的

文章懂人工智慧機器學習深度學習之間的區別

概述2015年11月9日,Google釋出人工智慧系統TensorFlow並宣佈開源。這兩年在不管在國內還是在國外,人工智慧、機器學習彷彿一夜之前傳遍大街小巷。機器學習作為人工智慧的一種型別,可以讓軟體根據大量的資料來對未來的情況進行闡述或預判。如今,領先的科技巨頭無不在機器

資料庫的原理,文章

https://blog.csdn.net/zhangcanyan/article/details/51439012 一提到關係型資料庫,我禁不住想:有些東西被忽視了。關係型資料庫無處不在,而且種類繁多,從小巧實用的 SQLite 到強大的 Teradata 。但很少有文章講解資料庫是如何工作的。你可以自己

文章矩陣相關概念及意義--通俗解釋彙總

一篇文章理解矩陣在講什麼。 最近在學習矩陣相關知識,但是其抽象的解釋讓人摸不著頭腦,通過瀏覽一些部落格的內容和自己的理解,本文通過通俗的語言將矩陣的內涵做了總結。其中除了書本和個人觀點,部分引用部落格:。本文主要幫助大家理解矩陣,但不一定都是正確的,但願能起

資料庫的原理,文章

合併聯接 合併聯接是唯一產生排序的聯接演算法。 注:這個簡化的合併聯接不區分內表或外表;兩個表扮演同樣的角色。但是真實的實現方式是不同的,比如當處理重複值時。 1.(可選)排序聯接運算:兩個輸入源都按照聯接關鍵字排序。 2.合併聯接運算:排序後的輸入源合併到一起。

文章springMVC中的請求對映

實驗的專案是採用預設配置的spring boot專案,使用的工具為IDEA和POSTMAN。 希望這些案例能夠幫助你理解和思考。 talk is cheap,show me the code! 1、從最簡單的hello world開始 @

文章面試中的連結串列題目(java實現)

連結串列的資料結構 class ListNode { ListNode next; int val; ListNode(int x){ val = x; next = null;

文章SpringMVC參數綁

進行 pwd dad 技術 默認 int acra servlet key SpringMVC參數綁定,簡單來說就是將客戶端請求的key/value數據綁定到controller方法的形參上,然後就可以在controller中使用該參數了 下面通過5個常用的註解演

Python正則表達式很難?文章他,不是我吹!

編譯 返回 特殊字符 但是 參數 查找字符串 cas 行處理 產生 1. 正則表達式語法 1.1 字符與字符類 1 特殊字符:.^$?+*{}| 以上特殊字符要想使用字面值,必須使用進行轉義 2 字符類 1. 包含在[]中的一個或者多個字符被稱為字符類,字符類在匹

文章面試中的二叉樹題目(java實現)

結構 cer dea mat lastcomm ++ mir let balanced 最近總結了一些數據結構和算法相關的題目,這是第一篇文章,關於二叉樹的。 先上二叉樹的數據結構: class TreeNode{ int val; //左孩子 Tr