從只會git add .的菜鳥到掌握git基本功能

bibi94發表於2018-03-31

前言

最近想把程式碼傳到GitHub上,結果我發現的demo的npm全是本地安裝,上穿到GitHub要死要死,幾百M,然後我就搜了下怎麼不上傳node_modules弄了半天也沒成功,於是準備靜下心學一下git,也當一個筆記日後好翻閱.
研究前我的只會5個命令

$ git init 
$ git add .
$ git commit -m "提交的xxxxx"
$ git pull
$ git push
複製程式碼

Git是什麼

Git是一個開源的分散式版本控制系統,用於敏捷高效地處理任何或小或大的專案。 Git 是 Linus Torvalds 為了幫助管理 Linux 核心開發而開發的一個開放原始碼的版本控制軟體。 Git 與常用的版本控制工具 CVS, Subversion 等不同,它採用了分散式版本庫的方式,不必伺服器端軟體支援。

Git的工作流程

從只會git add .的菜鳥到掌握git基本功能

  1. 克隆 Git 資源作為工作目錄。
  2. 在克隆的資源上新增或修改檔案。
  3. 如果其他人修改了,你可以更新資源。
  4. 在提交前檢視修改。
  5. 提交修改。
  6. 在修改完成後,如果發現錯誤,可以撤回提交併再次修改並提交。

基本概念

從只會git add .的菜鳥到掌握git基本功能

本地倉庫(版本庫)

版本庫:工作區有一個隱藏目錄.git,這個不算工作區,而是Git的版本庫。

本地倉庫(版本庫)是什麼

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

暫存區

暫存區:英文叫stage, 或index。一般存放在 ".git目錄下" 下的index檔案(.git/index)中,所以我們把暫存區有時也叫作索引(index)。

工作區

工作區:就是你在電腦裡能看到的目錄。

Git的基本使用

我就不詳細介紹了,估計大家都會,就是上面辣個圖,圖上幾個命令

$ git init,              //初始化本地倉庫 .git
$ git status -sb,        //顯示當前所有檔案的狀態
$ git diff                //檢視更改,檢視difference,顯示的格式正是Unix通用的diff格式,
$ git add 檔案路徑        //用來將變動加到暫存區
$ git commit -m "資訊"    //用來正式提交變動,提交至 .git 倉庫如果有新的變動,我們只需要依次執行 git add xxx 和 git commit -m 'xxx' 兩個命令即可
$ git log                 //檢視變更歷史
複製程式碼

版本回退

當我提交了幾個commit,假設我們現在有3個版本(1,2,3),現在是版本3,發現剛剛的提交錯誤了,想撤回回到版本2

$ git reset --hard               //重置暫存區與工作區,與上一次commit保持一致
複製程式碼

然後你由發現剛剛的提交是正確的,又想回到版本3,再輸入下面這個命令,相當於你那個回退沒有做

$ git reset --hard [commitid]     //重置當前分支的HEAD為指定commit,同時重置暫存區和工作區,與指定commit一致
//commitid 使用git log --stat檢視
複製程式碼

平行世界B
在平時世界B的你,剛剛把版本回退到了版本2,於是睡覺去了,第二天,發現版本3才是對的,可是使用git log已經檢視不到commit資訊了,怎麼辦?

$ git reflog                      //用來記錄你的每一次命令,顯示當前分支的最近幾次提交
複製程式碼

場景1:當你改亂了工作區某個檔案的內容,想直接丟棄工作區的修改時,用命令
$ git checkout -- file

場景2:當你不但改亂了工作區某個檔案的內容,還新增到了暫存區時,想丟棄修改,分兩步,第一步用命令
$ git reset HEAD file
,就回到了場景1,第二步按場景1操作。

場景3:已經提交了不合適的修改到版本庫時,想要撤銷本次提交,git reset --hard,不過前提是沒有推送到遠端倉庫。

刪除檔案

$ git rm [file1] [file2] ...      //刪除工作區檔案,並且將這次刪除放入暫存區
複製程式碼

另一種情況是刪錯了,因為版本庫裡還有呢,所以可以很輕鬆地把誤刪的檔案恢復到最新版本:

$ git checkout -- test.txt
複製程式碼

分支(branch)

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

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

  • 你已經知道,每次提交,Git都把它們串成一條時間線,這條時間線就是一個分支。截止到目前,只有一條時間線,在Git裡,這個分支叫主分支,即master分支。HEAD嚴格來說不是指向提交,而是指向master,master才是指向提交的,所以,HEAD指向的就是當前分支。

  1. 當我們建立新的分支,例如dev時,Git新建了一個指標叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
    從只會git add .的菜鳥到掌握git基本功能
  2. 你看,Git建立一個分支很快,因為除了增加一個dev指標,改改HEAD的指向,工作區的檔案都沒有任何變化!

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

從只會git add .的菜鳥到掌握git基本功能
3. 假如我們在dev上的工作完成了,就可以把dev合併到master上。Git怎麼合併呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合併:
從只會git add .的菜鳥到掌握git基本功能
4. 合併完分支後,甚至可以刪除dev分支。刪除dev分支就是把dev指標給刪掉,刪掉後,我們就剩下了一條master分支:
從只會git add .的菜鳥到掌握git基本功能

如何使用分支

$ git checkout -b [branch]                //新建一個分支,並切換到該分支
$ git branch                              //命令會列出所有分支,當前分支前面會標一個*號。
$ git add . 
$ git commit -m "提交分支branch"
$ git checkout master                     //切換回master分支
$ git merge [branch]                      //把branch分支合併到master分支
$ git branch -d branch                     //合併完成後刪除branch分支

複製程式碼
檢視分支:git branch

建立分支:git branch <name>

切換分支:git checkout <name>

建立+切換分支:git checkout -b <name>

合併某分支到當前分支:git merge <name>

刪除分支:git branch -d <name>
複製程式碼

分支衝突

比如現在我們的檔案是這樣的

fuck 'webpack'                      //master分支
複製程式碼

我們建立並且切換到parcel分支

$ git checkout -b parcel     
複製程式碼

修改文字內容

fuck 'webpack ---> parcel no.1
提交到暫存區

$ git add .
$ git commit "嘻嘻"                      //parcel分支
複製程式碼

切回master分支

$ git checkout master
複製程式碼

修改文字內容

fuck 'webpack' --> fuck fuck 'webpack'
提交到暫存區

$ git add .
$ git commit "哈哈"                      //maser分支
複製程式碼

這樣我們的兩個分支內容不一樣,有了衝突,我們提交試一下

從只會git add .的菜鳥到掌握git基本功能

$ git merge parcel                      //把parcel分支合併到當前master分支
複製程式碼

然後就衝突了

$ git status                        //可以告訴我們衝突的檔案:
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:     fuck.txt
#
複製程式碼

我們得手動修改當前master分支的內容與parcel分支內容相同

fuck fuck 'webpack' --> parcel no.1
再次提交

$ git add .
$ git commit "撲撲"                      //maser分支
複製程式碼

最後,刪除parcel分支

 git branch -d parcel
複製程式碼

分支管理策略

通常,合併分支時,如果可能,Git會用Fast forward模式(預設模式),但這種模式下,刪除分支後,會丟掉分支資訊。

如果要強制禁用Fast forward模式,Git就會在合併分支時生成一個新的commit,這樣,從分支歷史上就可以看出分支資訊。

列如

$ git checkout -b dev                                 //首先,仍然建立並切換dev分支:
$ git add readme.txt                                  //修改readme.txt檔案,並提交一個新的commit
$ git checkout master                                 //現在,我們切換回master分支
$ git merge --no-ff -m "merge with no-ff" dev         //準備合併dev分支,請注意--no-ff引數,表示禁用Fast forward
$ git log --graph --pretty=oneline --abbrev-commit    //合併後,我們用git log看看分支歷史:
//合併分支時,加上--no-ff引數就可以用普通模式合併,合併後的歷史有分支,能看出來曾經做過合併,而fast forward合併就看不出來曾經做過合併。
複製程式碼

團隊分支管理

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

首先,master分支應該是非常穩定的,也就是僅用來發布新版本,平時不能在上面幹活;

那在哪幹活呢?幹活都在dev分支上,也就是說,dev分支是不穩定的,到某個時候,比如1.0版本釋出時,再把dev分支合併到master上,在master分支釋出1.0版本;

你和你的小夥伴們每個人都在dev分支上幹活,每個人都有自己的分支,時不時地往dev分支上合併就可以了。

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

從只會git add .的菜鳥到掌握git基本功能

團隊分支應該真是這樣

|- master                       //正式版
  |- dev                        //測試版
    |- michael                  //隊員michael-adc
    |- bob                      //隊員bob-肉
    |- bibi                     //隊員bibi-大腿
複製程式碼

Bug分支

軟體開發中,bug就像家常便飯一樣。有了bug就需要修復,在Git中,由於分支是如此的強大,所以,每個bug都可以通過一個新的臨時分支來修復,修復後,合併分支,然後將臨時分支刪除。

當你接到一個修復一個代號101的bug的任務時,很自然地,你想建立一個分支issue-101來修復它,但是,等等,當前正在dev上進行的工作還沒有提交

------------                                              //我們在dev分支上,發現master分支上有代號101號bug
$ git stash                                               //冷凍現在在dev分支上的工作狀態 凍結吧!  
$ git checkout master                                     //這個bug發生在master主分支上,我們切回master分支
$ git checkout -b issue-101                               //建立代號101的修復bug分支
修改你的bug
$ git add readme.txt                                      //提交到暫存區
$ git commit -m "fix bug 101"                             //注意填寫資訊,以免日後查證
$ git checkout master                                     //切換回master分支
$ git merge --no-ff -m "merged bug fix 101" issue-101     //合併分支,注意不使用fast forward模式
$ git branch -d issue-101                                 //刪除issue-101分支
$ git checkout dev                                        //bug 改完了,是時候回到dev繼續寫bug了
$ git stash list                                          //檢視剛剛的凍結現場
$ git stash pop                                           //git stash pop,恢復的同時把stash內容也刪了:
//一是用git stash apply恢復,但是恢復後,stash內容並不刪除,你需要用git stash drop來刪除
複製程式碼

開發一個新測試功能

開發一個新feature,最好新建一個分支;

如果要丟棄一個沒有被合併過的分支,可以通過git branch -D <name>強行刪除。

多人協作

當你從遠端倉庫克隆時,實際上Git自動把本地的master分支和遠端的master分支對應起來了,並且,遠端倉庫的預設名稱是origin。 要檢視遠端庫的資訊,用git remote -v

推送分支

推送分支,就是把該分支上的所有本地提交推送到遠端庫。推送時,要指定本地分支,這樣,Git就會把該分支推送到遠端庫對應的遠端分支上:

$ git push origin master
複製程式碼

如果要推送其他分支,比如dev,就改成:

$ git push origin dev
複製程式碼

但是,並不是一定要把本地分支往遠端推送,那麼,哪些分支需要推送,哪些不需要呢?

  • master分支是主分支,因此要時刻與遠端同步;

  • dev分支是開發分支,團隊所有成員都需要在上面工作,所以也需要與遠端同步;

  • bug分支只用於在本地修復bug,就沒必要推到遠端了,除非老闆要看看你每週到底修復了幾個bug;

  • feature分支是否推到遠端,取決於你是否和你的小夥伴合作在上面開發。

多人協作時,大家都會往master和dev分支上推送各自的修改。  
當你的小夥伴從遠端庫clone時,預設情況下,你的小夥伴只能看到本地的master分支。  
現在,你的小夥伴要在dev分支上開發,就必須建立遠端origin的dev分支到本地,於是他用這個命令建立本地dev分支:
$ git checkout -b dev origin/dev
你的小夥伴已經向origin/dev分支推送了他的提交,而碰巧你也對同樣的檔案作了修改,並試圖推送
推送失敗,因為你的小夥伴的最新提交和你試圖推送的提交有衝突,解決辦法也很簡單,Git已經提示我們,先用git pull把最新的提交從origin/dev抓下來,然後,在本地合併,解決衝突,再推送:
git pull也失敗了,原因是沒有指定本地dev分支與遠端origin/dev分支的連結,根據提示,設定dev和origin/dev的連結:
$ git branch --set-upstream dev origin/dev
再pull
這回git pull成功,但是合併有衝突,需要手動解決,解決的方法和分支管理中的解決衝突完全一樣。解決後,提交,再push:
複製程式碼

因此,多人協作的工作模式通常是這樣:

首先,可以試圖用git push origin branch-name推送自己的修改;

如果推送失敗,則因為遠端分支比你的本地更新,需要先用git pull試圖合併;

如果合併有衝突,則解決衝突,並在本地提交;

沒有衝突或者解決掉衝突後,再用git push origin branch-name推送就能成功!

如果git pull提示“no tracking information”,則說明本地分支和遠端分支的連結關係沒有建立,用命令
git branch --set-upstream branch-name origin/branch-name

這就是多人協作的工作模式,一旦熟悉了,就非常簡單。


檢視遠端庫資訊,使用git remote -v

本地新建的分支如果不推送到遠端,對其他人就是不可見的;

從本地推送分支,使用git push origin branch-name,如果推送失敗,先用git pull抓取遠端的新提交;

在本地建立和遠端分支對應的分支,使用git checkout -b branch-name origin/branch-name,本地和遠端分支的名稱最好一致;

建立本地分支和遠端分支的關聯,使用git branch --set-upstream branch-name origin/branch-name

從遠端抓取分支,使用git pull,如果有衝突,要先處理衝突。

標籤管理

建立標籤

  • 命令git tag <name>用於新建一個標籤,預設為HEAD,也可以指定一個commit id;

  • git tag -a <tagname> -m "blablabla..."可以指定標籤資訊;

  • git tag -s <tagname> -m "blablabla..."可以用PGP簽名標籤;

  • 命令git tag可以檢視所有標籤。

  • 還可以建立帶有說明的標籤,用-a指定標籤名,-m指定說明文字:

$ git tag -a v0.1 -m "version 0.1 released" 3628164
複製程式碼

操作標籤

如果標籤打錯了,也可以刪除:

$ git tag -d v0.1
複製程式碼

如果要推送某個標籤到遠端,使用命令

$ git push origin <tagname>
複製程式碼

或者,一次性推送全部尚未推送到遠端的本地標籤:

$ git push origin --tags
複製程式碼

如果標籤已經推送到遠端,要刪除遠端標籤就麻煩一點,先從本地刪除:

$ git tag -d v0.9
複製程式碼

然後,從遠端刪除。刪除命令也是push,但是格式如下:

git push origin :refs/tags/<tagname>
複製程式碼

忽略特殊檔案

在Git工作區的根目錄下建立一個特殊的.gitignore檔案,然後把要忽略的檔名填進去,Git就會自動忽略這些檔案。 不需要從頭寫.gitignore檔案,GitHub已經為我們準備了各種配置檔案,只需要組合一下就可以使用了。所有配置檔案可以直接線上瀏覽:.gitignore

宣告

本片文章基本參考廖雪峰老師的git教程來,加入了一點我的個人理解,以及配圖等
如果你想深入瞭解git可以檢視下面這幾個連結

常用git命令清單-阮一峰
讀懂diff-阮一峰
git教程-廖雪峰
git教程-菜鳥教程
gitbook
Git Community Book

相關文章