前言
最近想把程式碼傳到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 資源作為工作目錄。
- 在克隆的資源上新增或修改檔案。
- 如果其他人修改了,你可以更新資源。
- 在提交前檢視修改。
- 提交修改。
- 在修改完成後,如果發現錯誤,可以撤回提交併再次修改並提交。
基本概念
本地倉庫(版本庫)
版本庫:工作區有一個隱藏目錄.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指向的就是當前分支。
- 當我們建立新的分支,例如dev時,Git新建了一個指標叫dev,指向master相同的提交,再把HEAD指向dev,就表示當前分支在dev上:
- 你看,Git建立一個分支很快,因為除了增加一個dev指標,改改HEAD的指向,工作區的檔案都沒有任何變化!
不過,從現在開始,對工作區的修改和提交就是針對dev分支了,比如新提交一次後,dev指標往前移動一步,而master指標不變:
3. 假如我們在dev上的工作完成了,就可以把dev合併到master上。Git怎麼合併呢?最簡單的方法,就是直接把master指向dev的當前提交,就完成了合併: 4. 合併完分支後,甚至可以刪除dev分支。刪除dev分支就是把dev指標給刪掉,刪掉後,我們就剩下了一條master分支:如何使用分支
$ 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 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分支上合併就可以了。
所以,團隊合作的分支看起來就像這樣:
團隊分支應該真是這樣
|- 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