這篇博文是自己在學習git過程中的思考總結。本文僅僅代表個人的看法,如有不妥地方還請本文文末留言。 ??
原文鏈接git的學習匯總--原創
GIT是什么
GIT
是一個免費并且開源的分布式版本控制系統,能夠高速有效的處理或小或大的項目。(以上的話是自己翻譯github官網)
至今,自己用過了window系統的TortoiseSVN
, mac系統的CornerStone
,最近的大半年也在用GIT
(主要管理自己的github項目)。比較下來,還是GIT優勢比較明顯。
GIT跨平臺
GIT
可以在不同的操作系統中使用。也許你注意到了,我在window上和mac系統上工作的時候是使用兩個不同的svn。如果我在linux上工作會不會又是一個呢。
GIT是分布式版本控制系統,而svn是集中式版本控制系統
集中式版本控制系統
是集中放在中央服務器上面的,而團隊的人需要從中央服務器上面拉取最新的代碼,然后進行開發,最后推送到中央服務器上面,就像串聯的電路。而分布式版本控制系統
沒有中央服務器,團隊的每個人的電腦就是一個完整的版本庫,就好像并聯的電路(自我理解)。
集中式版本控制系統
必須聯網才能工作,如果是在局域網內還好,帶寬足夠大,速度足夠快,但是遇到網速慢的話,那心里就一萬個羊駝??在蹦騰了。
集中式版本控制系統
安全性比較低,如果中央系統崩潰了,那就有點悲催了。當然你不嫌麻煩,可以定期備份的啦。而分布式中央系統
就比較安全,團隊的每個成員的電腦就是一個完整的版本庫。如果其中一個壞掉了,你可以從團隊另外一個的人員電腦那里拷貝一份就行了。對了,GIT
也會有一臺中央的機子,主要是為了方便團隊的交流,它是可以不存在的。
GIT安裝
GIT
支持不同的系統,看者可以在鏈接https://git-scm.com/downloads中,找到和自己電腦系統匹配的GIT
版本,下載安裝包后根據提示進行安裝。當然,GIT
還提供圖形界面管理工具,看者也可以在鏈接中下載GUI Clients
,如下圖所示--
根據提示安裝完成后,要驗證是否安裝成功??凑呖纱蜷_命令行工具,輸入
git --version
命令,如果安裝成功,控制臺輸出安裝的版本號(當然,安裝前就應該輸入git --version查看是否安裝了git),我這里安裝的GIT
版本是2.10.0
。
GIT配置
GIT
在使用前,需要進行相關的配置。每臺計算機上面只需要配置一次,程序升級的時候會保留配置信息。當然,看者可以在任何時候再次通過運行命令行來修改它們。
用戶信息
設置GIT
的用戶名稱和郵件地址,這個很重要,因為每個GIT
的提交都會使用這些信息,并且它會寫入到每一次的提交中。你可以在自己的倉庫中使用git log
,控制臺上面顯示的每次的提交都有Author
字段,它的值就是用戶名稱 <郵件地址>
。方便查看某次的提交的負責人是誰。
$ git config --global user.name "你的用戶名"
$ git config --global user.email 你的郵箱地址
?? GIT
一般和github
配合使用,看者應該設置用戶名稱為你的github
用戶名。當然,還有和gitlab
等配合使用...
?? 如果配置中使用了--global
選項,那么該命令只需要運行一次,因為之后無論你在該系統上做任何事情,GIT
都會使用這些信息。但是,當你想針對特定項目使用不同的用戶名稱與郵件地址的時候,可以在那個倉庫目錄下運行不使用global
選項的命令來配置。
檢查配置信息
通過git config --list
命令可以列出所有GIT
能找到的配置。如下:(我的git版本為2.10.0)
...
user.name=reng99
user.email=1837895991@qq.com
color.ui=true
core.repositoryformatversion=0
core.filemode=true
core.bare=false
...
當然,你可以通過git config <key>
來檢查GIT
的某一項配置。比如$ git config user.name
。
幫助中心
在使用GIT
的時候,遇到問題尋求幫助的時候,可以運行git help
或git --help
或git
命令來查看。在控制臺上會展示相關的幫助啦。
usage:
...
start a working area (see also: git help tutorial)
...
work on the current change (see also: git help everyday)
...
examine the history and state (see alse: git help revisions)
...
grow,mark and tweak your common history
...
collaborate (see also: git help workflows)
...
更加詳細的內容,請點擊傳送門
創建版本庫
版本庫又名倉庫(repository),可以理解成一個目錄,這個目錄里面所有文件都可以被GIT
管理起來,每個文件的修改、刪除,GIT
都能跟蹤,以便任何時刻都能可以追蹤歷史,或者在將來某個時刻可以還原。
創建一個版本庫,首先得選擇一個存放目錄的地方,我這里選擇了桌面,并且創建一個空的目錄。
$ cd desktop
$ mkdir -p learngit
$ cd learngit
$ pwd
/Users/reng/desktop/learngit
mkdir -p dirnanme
是創建一個子目錄,這里的-p
確保目錄的名稱存在,如果目錄不存在的就新建一個,如果你確定目錄不存在,直接使用mkdir dirname
就可以了。pwd(Print Working Directory)
是顯示當前目錄的整個路徑名。
然后,通過命令行git init
,將創建的目錄變成GIT
可以管理的倉庫:
$ git init
Initialized empty Git repository in /Users/reng/Desktop/learngit/.git/
初始化好倉庫后就可以愉快的玩耍了,但是,得先來了解下GIT
整個工作流程先。
GIT工作流程
為了更好的學習,自己用Axure RP 8
粗略的畫了下流程圖,如下--
本地倉庫(repo)包含工作區和版本庫,那么什么是工作區和版本庫呢?基本的流程又是什么呢?
工作區和版本庫
我們新建一個倉庫,就像我們新建的learngit
倉庫,現在在里面添加一個文件README.md
,用sublime打開learngit
目錄。此時會出現如下圖的情況(當然你設置了其他東西例外)--
如上圖,出現的內容就是工作區( 電腦上能看到的此目錄下的內容),這里工作區只有
README.md
一個文件。工作區有一個隱藏的目錄.git
,這個不算工作區,而是GIT
的版本庫。版本庫又包括暫存區和GIT倉庫。暫存區是一個文件,保存了下次將提交的文件列表信息,而GIT倉庫目錄是GIT
用來保存項目的元數據和對象數據庫的地方。這是GIT
中最重要的部分,從其他計算機克隆倉庫的時候,拷貝的就是這里的數據。當執行git add .
或者git add path/to/filename
的時候,文件從工作區轉到暫存區;執行git commit -m"here is the message described the file you add"
的時候,文件從緩存區添加到GIT倉庫。
基本的工作流
基本的GIT
工作流可以簡單總結如下--
- 在工作區目錄中修改文件
- 暫存區中暫存文件,將文件的快照放入暫存區域
- 提交更新,找到暫存區域的文件,將快照永久性存儲到GIT倉庫目錄
時光機穿梭
到目前為止,在自己創建的本地倉庫--learngit
中已經初具形態了。進入learngit
,執行ls
,可看到目前倉庫中已有的文件README.md。
$ cd desktop/learngit
$ ls
README.md
$ cat README.md
## content
上面展示了本地learngit
內的相關的內容。運行下git status
查看現在的狀態。
$ git status
On branch master
nothing to commit, working tree clean
這時候會提示沒有內容可以提交,工作區是干凈的。因為我之前已經提交(git commit)過了。上面還提示了目前是位于主分支上面,GIT
在初始化(git init)的時候會自動創建一個HEAD
指針指向默認master
分支,也只有一個分支,看者可以通過git branch
查看。
現在,在README.md
上添加一些內容。
## content
### first change
此刻再通過git status
查看當前狀態。
$ 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: README.md
no changes added to commit (use "git add" and/or "git commit -a")
這時候顯示出一堆的東西,告訴我們現在是位于主分支上面,然后告訴我們修改的文件啊,可以使用的命令進行下一步的操縱。那么我們來進行下一步的操作了,git add . 或者 git add README.md
將修改的文件添加到暫存區域。
$ git add .
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
對了,有時候需要在添加的之前(執行git add . 或者 git add path/to/filename)的時候,需要看下修改了哪些內容可以執行下git diff
。那么,現在先回退到修改的前一個版本。
$ git reset HEAD README.md
Unstaged changes after reset:
M README.md
$ git checkout -- README.md
$ ls
README.md
$ cat README.md
## content
回退正確,現在像上次那樣添加內容### first change
,然后執行命令git diff
來查看更改的內容。
$ git diff
diff --git a/README.md b/README.md
index 75759ec..0bc52b9 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
-## content
\ No newline at end of file
+## content
+
+### first change
\ No newline at end of file
現在就顯示了修改前的內容---
前為修改前的內容,和修改后的內容--+
前修改后的內容。查看完之后,覺得沒有問題了,就可以進行添加(git add),提交(git commit)。當然,一般不常用git diff的,因為自己修改的東西自己心里總有點數吧,可能合作中團隊的其他人需要查看文件前后的不同點就需要用到git diff
啦。
版本回退
為了方便講解下版本回退,我先將上面添加的### first change
提交以下--git add . && git commit -m "add first change"
。下面通過git log
就可以查看自己提交的記錄了。
$ git log
commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
Author: reng99 <1837895991@qq.com>
Date: Sun Dec 17 17:11:53 2017 +0800
init README.md
$ git add . && git commit -m "add first change"
[master 0ac49ba] add first change
1 file changed, 3 insertions(+), 1 deletion(-)
$ git log
commit 0ac49bae6ab55df9c05d0770de347665a2568f31
Author: reng99 <1837895991@qq.com>
Date: Mon Dec 18 15:26:06 2017 +0800
add first change
commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
Author: reng99 <1837895991@qq.com>
Date: Sun Dec 17 17:11:53 2017 +0800
init README.md
在上面中,自己先執行了git log
來顯示提交的日志,顯示只有一條,然后執行了add和commit的命令,打印的內容是現實主分支、commit的id、commit的信息、多少個文件的更改、多少個插入以及多少個刪除。之后再次執行git log
打印日志,顯示了兩次提交。?? 注意:當提交(commit)的次數較多之后,控制臺會顯示不下(最多現實4條)那么多的條數,可以通過按鍵盤的向上或向下
鍵查看日志的內容,需要退出查看日志命令的話,在英文輸入法的狀態按下q
,意思就是quit(退出)。
版本的回退就是改變HEAD
指針的指向。通過git reset --hard HEAD^
返回上一個版本,通過git reset --hard HEAD^^
返回上上個版本...由此推論,往上100個版本的話就是100個^
,當然,這樣你數到明天也未必數得正確,所以寫成git reset --hard HEAD~100
。另外一種是,你知道提交的id,例如commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
的前7位就是commit的id(5c2639e),執行git reset --hard 5c2639e
就回到此版本啦。
$ reng$ git reset --hard HEAD^
HEAD is now at 5c2639e init README.md
$ git log
commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
Author: reng99 <1837895991@qq.com>
Date: Sun Dec 17 17:11:53 2017 +0800
init README.md
$ ls
README.md
$ cat README.md
## content
現在你已經回到了最初的版本,這里演示的是通過HEAD
,你也可以通過commit id
來實現的。執行上面的代碼后,README.md
文件里面只有一### content
文字內容,但是過了段時間后,你想恢復到原先的版本,通過git log
命令行,控制臺顯示的以前的信息,通過它找不到回退前的commit id
,怎么辦?GIT
提供一個git reflog
顯示提交的歷史記錄,在那里可以查看提交的id、HEAD
的指針歷史和操作的信息記錄。下面演示回退到最新的版本(也就是commit -m "add first change")--
$ git log
commit 5c2639ee54bd7c8ef2cbf186f5dc4798e72a4a67
Author: reng99 <1837895991@qq.com>
Date: Sun Dec 17 17:11:53 2017 +0800
init README.md
$ git reflog
5c2639e HEAD@{0}: reset: moving to HEAD^
0ac49ba HEAD@{1}: commit: add first change
5c2639e HEAD@{2}: commit (initial): init README.md
$ git reset --hard 0ac49ba
HEAD is now at 0ac49ba add first change
$ ls
README.md
$ cat README.md
## content
### first
現在又回到了最新的版本,又能夠愉快的玩耍了。??
管理修改
GIT
比其他版本控制系統設計優秀,其中一點是--GIT
跟蹤并管理的是修改,而非文件。
下面在README.md
內添加信息### second change
。之后看下變化后的文件的狀態和差異等。
$ ls
README.md
$ cat README.md
## content
### first change
#### second change
$ 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: README.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git add README.md
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
此時,對README.md
進行第三次的修改,添加內容### third change
。
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
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: README.md
$ cat README.md
## content
### first change
#### second change
### third change
$ git commit -m "test file modify"
[master 18f86ba] test file modify
1 file changed, 3 insertions(+), 1 deletion(-)
$ 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: README.md
no changes added to commit (use "git add" and/or "git commit -a")
上面的演示流程是這樣的第一次修改(#### second change) -> git add -> 第二次修改(### third change) -> git commit
。但是最后查看狀態的時候(git status),第二次的修改并沒有被提交上去。因為GIT
管理的是修改,當使用git add
命令的時候,在工作區的第一次修改被放入暫存區,準備提交,但是在工作區的第二次修改并沒有放入到暫存區,而git commit
是將暫存區的修改提交到GIT倉庫
,所以第二次修改的內容是不會被提交的。這也是說明為什么可以多次添加(git add),一次提交(git commit)的原因了。
撤銷修改
文件的撤銷修改分成三種情況,一種是修改在工作區的內容,一種是修改在暫存區的內容,另一種是修改在GIT
倉庫的內容。也許會有看者說,不能修改在遠程庫中的內容嗎?有啊,就是git add
->git commit
->git push
將遠程倉庫的內容覆蓋被,不過團隊人在克隆遠程庫下來的時候,還是可以查看到你提交的錯誤內容的。我們現在只針對本地倉庫的三種情況談下自己的看法--
情況一:撤銷工作區的內容
在管理修改中,自己的工作區還是沒有提交,此時想放棄當前工作區的編輯內容執行git checkout -- file
。接著上面的內容,我這里的工作區內有的內容是### third change
,現在我要放棄第三次修改,只要執行git checkout -- README.md
就可以了。
$ 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: README.md
no changes added to commit (use "git add" and/or "git commit -a")
$ ls
README.md
$ cat README.md
## content
### first change
#### second change
### third change
$ git checkout -- README.md
$ cat README.md
## content
### first change
#### second change
$ git status
On branch master
nothing to commit, working tree clean
情況二:撤銷暫存區的內容
當你不但改亂了工作區的某個文件的內容,還添加(git add)到了暫存區時,想丟棄修改,那么得分兩步來撤銷文件。先是通過git reset HEAD file
,將暫存區的文件退回到工作區,然后通過git checkout -- file
放棄修改改文件的內容。為了方便演示,我這里的暫存區沒什么內容,所以添加內容### tentative content
并將它添加到緩存區。之后,演示將緩存區的內容撤回--
$ cat README.md
## content
### first change
#### second change
### tentative content
$ git add .
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: README.md
$ git reset HEAD README.md
Unstaged changes after reset:
M README.md
$ git checkout -- README.md
$ cat README.md
## content
### first change
#### second change
$ git status
On branch master
nothing to commit, working tree clean
情況三:撤銷GIT倉庫的內容
如果你不僅添加(git add)了內容到暫存區并且提交(git commit)了內容到GIT倉庫
中了。你需要撤銷上一次的內容,也就是要回退到上一個版本,執行git reset --hard HEAD^
就可以啦,詳細的內容查看版本回退
。如下--
$ git status
On branch master
nothing to commit, working tree clean
$ cat README.md
## content
### first change
#### second change
$ git reset --hard HEAD^
HEAD is now at 0ac49ba add first change
$ cat READMEmd
## content
### first change
遠程倉庫
遠程倉庫的使用能夠提高你和團隊的工作效率,無論何時何地,團隊的人員都可以在聯網的情況下將代碼進行拉取,修改和更新。因為我是使用github
來管理項目的,所以我的遠程倉庫是放在github里面。這里默認看者已經安裝了github
,當然也可以用碼云、gitlab等。
本地庫添加到遠程庫
這點很容易,登錄自己注冊的github,如果打不開,請開下VPN。進入自己的首頁(https://github.com/username),點擊+
號創建(new repository)一個名為learngit
的倉庫(注意哦?? 名稱是本地倉庫已經初始化過的,我這里本地有個同名初始化的learngit倉庫),其他的字段自選來填寫。點擊Create repository
創建此遠程倉庫。緊接著就是進行本地倉庫和遠程倉庫的關聯啦,github
很友好的提示了你怎么進行一個遠程倉庫的關聯。
現在按照上圖來關聯下遠程倉庫。
$ git remote add origin https://github.com/reng99/learngit.git
$ git push -u origin master
Counting objects: 6, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 456 bytes | 0 bytes/s, done.
Total 6 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit.git
* [new branch] master -> master
Branch master set up to track remote branch master from origin.
注意?? 第一次向遠程倉庫(關聯)push的時候是$ git push -u origin master
,不能忽略-u
,以后的push不用帶-u
。至此,打開你的github的相關的倉庫就可以看到添加了README.md
文件,我這里地址是https://github.com/reng99/learngit
,因為我是使用markdown語法寫的,控制臺顯示的內容和倉庫的顯示內容有所區別啦。<del>(?? 后期我將learngit倉庫刪除啦,所以你訪問鏈接是找不到這個倉庫的,畢竟不想放一個沒什么內容的倉庫在我的github上)</del>。
遠程庫克隆到本地
從遠程倉庫克隆東西到本地同樣很簡單,只需要進入你想克隆的倉庫,將倉庫的url
復制下來(當然你也可以復制window.location.href的內容),運行git clone address
?,F在我將本地桌面的learngit
的倉庫刪除,然后從遠程將learngit
克隆到本地。
$ cd desktop
$ rm -rf learngit
$ find learngit
find: learngit: No such file or directory
$ git clone https://github.com/reng99/learngit
Cloning into 'learngit'...
remote: Counting objects: 6, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 6 (delta 0), reused 6 (delta 0), pack-reused 0
Unpacking objects: 100% (6/6), done.
成功將gitlearn
從遠程克隆下來,接下來又可以愉快的玩耍啦。
分支管理
分支管理允許創建另一條線/方向上開發,能夠讓你在不影響他人工作的情況下,正常的工作。當在自己創建的分支中完成自己的功過后,合并到主分支就行了(git init初始化的時候已經默認創建了master主分支)。一般團隊的合作是不在主分支上進行的,個人項目除外(個人理解)。
創建分支
當前learngit
倉庫上只有一個分支,那就是master
分支,看者可以通過git branch
命令來查看當前的分支,git branch branchName
命令來創建一個新的分支,我這里創建的是dev
分支。
$ cd desktop/learngit
$ git branch
* master
$ git branch dev
$ git branch
dev
* master
現在已經創建了dev
分支,有兩個分支了,分支前面帶有一個星號的分支說明是當前的正在工作的分區。執行上面的分支后,可以簡單的畫下現在的情況了,有個HEAD
指針指向主分支的最新點,剛才新創建的dev
分支我這里默認是一個dev
的指針指向了dev
分支的最新點。
.
. HEAD指針
. │
├────────*master
└────────dev
│
dev指針
切換分支
我們一般是很少在主分支進行工作的,所以在創建出新的分支之后,我們就切換到新的分支進行相關的工作。可以通過git checkout branchName
切換到已經存在的分支工作,通過分支前面的*
可查看目前位于哪個分支內?,F在我切換到創建的dev
分支。
$ git branch
dev
* master
$ git checkout dev
Switched to branch 'dev'
$ git branch
* dev
master
合并分支
在創建好分支后,我們在新的分支上工作完成后,就需要往主分支上進行合并啦。我修改了分支dev
上的README.md
的內容,就是添加文字### new branch content
。合并分支可以分成兩個合并的方式,一種是本地合并到materz主分支之后,推送(push)到遠程庫,一種是直接將分支推送到遠程庫,在遠程庫進行合并。
本地合并推送
在合并分支前,需要切換到要合并到哪個分支(一般是master主分支),通過git merge branchName
將需要的合并的分支合并到當前分支,我是將dev
分支合并到master
分支。
$ git branch
* dev
master
$ git checkout master
M README.md
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git merge dev
$ Already up-to-date.
$ git add .
$ git commit -m "merge dev branch"
[master d705e73] merge dev branch
1 file changed, 3 insertions(+), 1 deletion(-)
$ git push origin master
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 282 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit
0ac49ba..d705e73 master -> master
合并之后,此時,HEAD
指針就指向了dev
指針,也就是兩者同時指向了master
主分支的最新處。具體的內容參考傳送門
.
.
.
├────────*master
└────────dev
│
dev指針 ── HEAD指針
遠程庫推送合并
遠程庫內合并的話,要先將dev
的分支推送到遠程庫,然后在遠程庫進行合并。我這里在dev
分支上添加了### add new branch content into again
然后demo演示推送(git push origin dev)以及合并。
$ git branch
dev
* master
$ git checkout dev
Switched to branch 'dev'
$ git add .
$ git commit -m "add dev branch commit again"
[dev dc817c4] add dev branch commit again
1 file changed, 3 insertions(+), 1 deletion(-)
$ git push origin dev
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 300 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit
* [new branch] dev -> dev
接下來就是進入我的遠程learngit倉庫進行合并,你會看到下面圖示的提示。點擊Compare && pull request
,然后寫點相關的comment
(選填),點擊Create pull request
。之后在綠色勾的提示下Merge pull request
,緊接著點擊Confirm merge
按鈕確定合并此分支,這時候返回主分支就可以看到dev
內合并的內容了(后期我改動了dev的內容)??凑呷绻吹貌幻靼祝约荷鲜謬L試一下唄!
完成后,你會看到learngit
倉庫的Pull requests
量為1,branches
量為2。你可以點擊進入分支,在ALL branches
里面查看分支的具體內容。
刪除分支
在創建了分支,然后將分支的內容合并到主分支后,分支的使命就完成了,你就可以將分支刪除了,這里的刪除個人認為可以是兩種,一種是本地倉庫的分支刪除,一種是遠程倉庫的分支的刪除。當然啦,留著分支也沒啥,可以留著唄<del>,自己認為有點礙眼</del>。
本地分支的刪除
在本地的learngit
的目錄下,執行命令行git branch -D branchName
就可以刪除了。我這里刪除的是dev
分支。注意?? ,刪除的分支不應該是當前工作的分支,需要切換到其他分支,我這里切換的是master
分支,畢竟我只有兩個分支呢。
$ git branch
* dev
master
$ git branch -D dev
error: Cannot delete branch 'dev' checked out at '/Users/reng/desktop/learngit'
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git branch
dev
* master
$ git branch -D dev
Deleted branch dev (was dc817c4).
$ git branch
* master
遠程庫分支的刪除
刪除遠程庫的分支,只要執行git push origin :branchName
命令就行了?,F在我要刪除我遠程庫中的dev
分支,執行git push origin :dev
。
$ git push origin :dev
To https://github.com/reng99/learngit
- [deleted] dev
此時,打開我的遠程庫learngit,發現之前的Pull requests
量為0,branch
量為1。
重命名分支
通過git branch -m oldBranchName newBranchName
來重命名分支。我這里沒有分支了,現在創建一個reng
分支,然后將它重命名為dev
分支。
$ git branch
* master
$ git branch reng
$ git branch
* master
reng
$ git branch -m reng dev
$ git branch
dev
* master
解決沖突
在我們開發的時候,不知道分支和分支之間的進度情況是什么,難免會產生沖突。當產生沖突的時候,就得將沖突的內容更正,然后提交。為了方便演示,我將本地的learngit
刪除,重新拉取遠程的gitlearn
倉庫(因為我不知道我之前在本地倉庫做的修改是啥,對了,我將遠程的分支刪除了,只剩下master主分支)??寺∠聛砗螅绻€存在本地分支,也將它刪除,之后我將在master
和dev
分支中重新填充里面的README.md
的內容。
$ cd desktop
$ git clone https://github.com/reng99/learngit.git
Cloning into 'learngit'...
remote: Counting objects: 43, done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 43 (delta 4), reused 38 (delta 1), pack-reused 0
Unpacking objects: 100% (43/43), done.
$ cd learngit
$ git branch
* master
$ ls
README.md
$ cat README.md
## master branch content
$ git add .
$ git commit -m "add master branch content"
[master 1cfa0aa] add master branch content
1 file changed, 1 insertion(+), 1 deletion(-)
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 271 bytes | 0 bytes/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit.git
d2f936f..1cfa0aa master -> master
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
$ cat README.md
## master branch content
### dev branch content
$ git add .
$ git commit -m "add dev branch content"
[dev 80faf6d] add dev branch content
1 file changed, 2 insertions(+)
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ cat README.md
## master content
### new master branch content
$ git add .
$ git commit -m "change master content"
[master ec18715] change master content
1 file changed, 3 insertions(+), 1 deletion(-)
$ git merge dev
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
README.md
文件中沖突內容--
<<<<<<< HEAD (當前更改)
## master content
### new master branch content
=======
## master branch content
### dev branch content
>>>>>>> dev (傳入更改)
手動修改了README.md
文件中沖突的內容--
## master branch content
### new master branch content
### dev branch content
然后命令行執行--
$ git add .
$ git commit -m "fix confict content"
[master dd848b4] fix confict content
$ git log --graph
* commit 980788b7690d8bcf14610072fc072460bee7e9f1
|\ Merge: c49d09e 2929dca
| | Author: reng99 <1837895991@qq.com>
| | Date: Thu Dec 21 11:14:10 2017 +0800
| |
| | fix confict content
| |
| * commit 2929dca91ef8f493adba7744cdad19656538334f
| | Author: reng99 <1837895991@qq.com>
| | Date: Thu Dec 21 11:11:49 2017 +0800
| |
| | add dev branch content
| |
* | commit c49d09e33e7098d67b59c845d18e9c6f8a8f4fea
|/ Author: reng99 <1837895991@qq.com>
| Date: Thu Dec 21 11:12:50 2017 +0800
|
| change master content
|
* commit b07f0be8280e4e437cccf2a3f8fac6beef03ff41
| Author: reng99 <1837895991@qq.com>
| Date: Thu Dec 21 11:10:51 2017 +0800
|
:
上面操作過程是,我先從遠程庫中克隆learngit
倉庫到本地,目前的本地learngit
的分支只有master
分支,然后我在master
分支的README.md
中添加相關的文字(見代碼),接著把它推送到遠程庫。然后創建并切換dev
分支,在README.md
文件中添加新內容(見代碼),接著將它提交到GIT倉庫
。又切換到master
分支,修改README.md
到內容(見代碼),提交到GIT倉庫后
開始執行merge
命令合并dev
分支的內容。此時,產生了沖突,這就需要手動將沖突的內容解決,重新commit
到GIT倉庫
,最后你就可以提交到遠程庫了(這步我沒有演示,也就是git push origin master一行命令行的事情)。最后我還使用git log ----graph
打印出整個分支合并圖(從下往上看),方便查看。?? 此時退出git log --graph
是書寫英文狀態按鍵盤的q
鍵。
說這么多,目的只有一個 --> 產生沖突后,需要手動調整??
分支管理策略
先放上一張分支管理策略圖,然后再慢慢講解相關的內容...
在分支管理中,我們不斷的新建分支,開發,合并分支,刪除分支的操作。這里需要注意合并分子的操作,之前我們進行分支的時候是直接將dev
開發的分支使用git merge dev
進行合并,這樣有個缺點:我們看不出分支信息。因為在默認情況下,合并分支的時候,GIT
是使用了Fast Foward
的模式,在這種模式下,刪除分支后,會丟掉分支的信息。下面我重新克隆下我遠程learngit
倉庫,然后創建并更改dev
分支的信息,使用默認的模式進行合并。
$ git branch
* master
$ git branch dev
$ git checkout dev
Switched to branch 'dev'
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
$ git add .
$ git commit -m "add new dev contentt"
[dev 750e1f1] add new dev content
1 file changed, 1 insertion(+)
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git merge dev
Updating 980788b..750e1f1
Fast-forward
README.md | 1 +
1 file changed, 1 insertion(+)
$ git log --graph
* commit 750e1f17854872eed4d6cff8315e404079ecb18f
| Author: reng99 <1837895991@qq.com>
| Date: Fri Dec 22 10:05:36 2017 +0800
|
| add new dev content
|
* commit 980788b7690d8bcf14610072fc072460bee7e9f1
...
上面的合并就是將master分支上面的HEAD
指向dev
指針,如下:
# 記錄是從上往下
- before merge
master
* (begin)
|
|
*
\
\
*
|
|
* (end)
dev
- after merge
master
* (begin)
|
|
*
|
|
*
|
|
* (end)
為了保留分支的情況,保證版本演進的清晰,我們就得使用普通模式合并,也就是在Fast Foward
的模式基礎上加上--no-ff
參數,即git merge --no-ff branchName
,不過我們一般加上你合并的相關信息,即git merge --no-ff -m "your msg here" banchName
?,F在更改dev
分支的內容,再進行合并。
$ git checkout dev
Switched to branch 'dev'
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
$ git add .
$ git commit -m "add no-ff mode content"
[dev 80b628c] add no-ff mode content
1 file changed, 2 insertions(+), 1 deletion(-)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
(use "git push" to publish your local commits)
$ git merge dev --no-ff -m "merge with no-ff" dev
Merge made by the 'recursive' strategy.
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
$ git log --graph
* commit 98746d93a9b64ea02b8ff1c7f0fa5e915405c0e6
|\ Merge: 750e1f1 80b628c
| | Author: reng99 <1837895991@qq.com>
| | Date: Fri Dec 22 14:39:32 2017 +0800
| |
| | merge with no-ff
| |
| * commit 80b628c334618711b77da81fa805ffc246a2cf7d
|/ Author: reng99 <1837895991@qq.com>
| Date: Fri Dec 22 14:38:17 2017 +0800
|
| add no-ff mode content
|
* commit 750e1f17854872eed4d6cff8315e404079ecb18f
...
使用--no-ff
參數的普通模式合并,會執行正常合并,在master
主分支上面會生成一個新的節點,如下(我上面的分支管理策略圖里面的合并就是使用了普通的模式):
# 記錄是從上往下
- --no-ff合并
master
* (before)
|
|
*
|\
| \
| *dev
| |
| |
| *
| /
|/
* (after)
我們在開發中,分支管理可以分成master主分支、dev開發分支、feature功能分支、release預發布分支、hotfixes修補bug分支。其中功能分支、預發布分支和修補bug分支可以歸為臨時分支
。臨時分支
在進行分支的合并之后就可以被刪除了。下面就一一講解自己眼中的各種分支。
主分支master
主分支是在你初始化倉庫的時候(git init),自動生成的一個master分支,刪除不了的哦(演示待會給)。主分支是有且僅有一個,也是發布上線的分支,團隊合作的最終代碼都會在master主分支上面體現出來。也許你也注意到了分支管理策略圖里面的主分支會被打上TAG
的標簽,這是為了方便到某個時間段對版本的查找,標簽tag的學習總結后面給出。
# 記錄是從上往下
master
|
|
*(tag 1.0)
|
|
*(tag 1.1)
|
|
*(tag 1.2)
下面代碼演示下不能放刪除master的情況:
$ cd learngit
$ git branch
dev
* master
$ git branch -D master
error: Cannot delete branch 'master' checked out at '/Users/reng/desktop/learngit'
開發分支develop
在開發的過程中,項目合作者應該保持自己本地有一個開發環境的分支,在進行分支開發之前,需要進行git pull
拉取master
主分支的最新內容,或者通過其他的方法。在獲取到最新的內容之后才可以進行本地的新功能的開發。在開發完成后將內容merge
到主分支之后,不用將dev
分支刪除,因為你開發的就是在這里進行,何必刪除后再新建一個開發環境的分支呢。
接著上面的情況,我目前已經擁有了dev
開發分支:
$ cd learngit
$ git branch
dev
* master
功能(特性)分支feature
一個軟件就是一個個功能疊加起來的,在軟件的開發中,我們總不能在主分支開發,將主分支搞亂吧。當然,你可以在dev分支中開發,一般新建功能分支來開發,然后功能開發完再合并到dev分支,之后刪除功能分支。需要的時候就可以將dev
開發分支合并到master
主分支,這樣就隨時保證dev
分支功能的完整性。
下面演示功能分支user
開發(隨便寫點內容)的合并(這里也演示了合并到master主分支,跳過了release分支的測試),刪除。
$ git checkout dev
Switched to branch 'dev'
$ git branch user
$ git branch
* dev
master
user
$ git checkout user
Switched to branch 'user'
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
### function user
$ git add .
$ git commit -m "function user was acheive"
[user 26beda3] function user was acheive
1 file changed, 2 insertions(+), 1 deletion(-)
$ git checkout dev
Switched to branch 'dev'
$ git merge --no-ff -m "merge user feature" user
Merge made by the 'recursive' strategy.
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 3 commits.
(use "git push" to publish your local commits)
$ git merge --no-ff -m "merge dev branch" dev
Merge made by the 'recursive' strategy.
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
$ git log --graph
* commit f15a1e9012635fc21e944ab76c4cd4bbd539f82f
|\ Merge: 98746d9 0ca83c6
| | Author: reng99 <1837895991@qq.com>
| | Date: Fri Dec 22 16:35:43 2017 +0800
| |
| | merge dev branch
| |
| * commit 0ca83c654df64724743a966f5f0989477e504cbc
| |\ Merge: 80b628c 26beda3
| | | Author: reng99 <1837895991@qq.com>
| | | Date: Fri Dec 22 16:33:27 2017 +0800
| | |
| | | merge user feature
| | |
| | * commit 26beda3b8246e047f10ac0461ca11d1a6f132819
| |/ Author: reng99 <1837895991@qq.com>
| | Date: Fri Dec 22 16:31:41 2017 +0800
| |
| | function user was acheive
| |
* | commit 98746d93a9b64ea02b8ff1c7f0fa5e915405c0e6
|\ \ Merge: 750e1f1 80b628c
| |/ Author: reng99 <1837895991@qq.com>
:
$ git branch -D user
Deleted branch user (was 26beda3).
$ git branch
dev
* master
預發布分支release
在進行一系列的功能的開發和合并后,在滿足迭代目標的時候,就可以打包送測了。這里就需要一個預發布分支release。預發布分支是指在發布正式版本之前( 即合并到master分支之前,可查看上面分支管理策略圖),需要一個有預發布的版本(可以理解為灰度環境)進行測試。
預發布環境是從dev
分支上面分出來的,預發布結束之后,必須合并到dev
和master
分支上面。這里我就不演示了,跟功能分支差不多,就是合并的時候要合并到dev
和master
上,這時候dev
分支和master
的同步的代碼,就不需要將dev
分支合并到master
了。最后將預發布分支刪除掉。
修復bug分支 bug/hotfixes
在寫代碼的過程中,由于種種原因 -> 比如功能考慮不周全,版本上線時間有限,產品突然改需求等,我們寫的代碼就出現一些或大或小的bug或者需要緊急修復。那么我們就可以使用bug分支(其實就是新建一個分支處理bug而已啦,命名隨意起的),然后在這個分支上處理編碼出現的問題。我在分支管理策略圖
上面已經展示了一種出現bug的情況 -> 就是在測試發布版本看似沒問題的情況下,將release
版本整合到master
和dev
中,這時候火眼精金發現了遺留的一個bug,然后新建一個bug分支
處理,再合并到master
和dev
中,之后將bug分支
移除啦。
在開發的過程中,無論咋樣都是這樣 : 新建bug分支 -> 把分支合并 -> 刪除分支,這里的demo就不演示了,可以參考上面的功能(特性)分支feature
。
這里需要注意??的一點,當在開發的過程中,開發到一定的程度,需要停下來需改緊急的bug,那么需要停下手頭的工作需改bug啦。這時候需要將工作現場儲藏(stash功能)起來,等以后回復現場了后接著工作。現在我在原先的gitlearn
倉庫中README.md
文件文末添加### modify content
內容來進行演示。
$ cd desktop
$ cd learngit
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
$ git status
On branch dev
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: README.md
no changes added to commit (use "git add" and/or "git commit -a")
$ git stash
Saved working directory and index state WIP on dev: 80b628c add no-ff mode content
HEAD is now at 80b628c add no-ff mode content
$ git status
On branch dev
nothing to commit, working tree clean
然后過段時間(這里省略修改的演示),代碼已經修改好合并后,需要回到最新的內容區域進行工作,這就需要還原最新的內容了,demo如下:
$ cd learngit
$ git stash list
stash@{0}: WIP on dev: 80b628c add no-ff mode content
$ git stash pop
On branch dev
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: README.md
no changes added to commit (use "git add" and/or "git commit -a")
Dropped refs/stash@{0} (9e85bcc8435ae38c17db59ddc3cd8401af404827)
$ git stash list
?? git stash
不僅可以隱藏工作區的內容,也可以隱藏暫存區的內容。git stash list
是查看隱藏的列表。git stash pop
是將隱藏的內容恢復并刪除,git stash pop
相當于git stash apply && git stash drop
,這里的git stash apply
是恢復隱藏內容,git stash drop
是刪除隱藏內容。
多人協作
簡單談下自己git協作的過程吧。在負責人將搭建好的倉庫上傳到遠程的倉庫后(一般是包含了master默認的分支和dev分支),自己將遠程倉庫克隆到本地,然后在本地的倉庫上新建一個dev分支
,將遠程的dev分支重新拉取下git pull origin dev
,開發完成后就可以提交自己的代碼到遠程的dev分支了
,如果提交之前或者之后需要修改bug或者添加新的需求的話,需要新建一個相關的分支并完成開發,將他們合并到本地dev
分支后上傳到遠程dev
分支。如果新建的遠程倉庫中只有master分支
,我是這樣處理的:依然要在本地新建一個dev
分支,然后在完成特定版本的開發后,將分支合并到本地master分支
然后再推送到遠程master
分支,本地的dev分支
保留哦。我自己比較偏向于第一種情況。
標簽管理
發布一個版本前,為了唯一確定時刻的版本,我們通常在版本庫中打一個標簽(tag),方便在發布版本以后,可以在某個時刻將某個歷史的版本提取出來(因為標簽tag也是版本庫的一個快照)。
創建標簽
創建標簽是默認在你切換的分支最新提交處創建的。我這里在本地桌面的learngit倉庫
的master分支
上打一個v1.0標簽
。
$ cd desktop/learngit
$ git branch
* dev
master
$ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
$ git tag
$ git tag v1.0
$ git tag
v1.0
當然,你可以在非新commit的地方進行標簽。這就需要你查找到需要打標簽處的commit的id,然后執行git tag tagName commitId
。這里我隨意找master分支中的commit id進行標簽v0.9
的標簽創建。
$ git log --pretty=oneline --abbrev-commit
f15a1e9 merge dev branch
0ca83c6 merge user feature
26beda3 function user was acheive
98746d9 merge with no-ff
...
現在在commit id為 98746d9
處打標簽。
$ git tag v0.9 98746d9
$ git tag
v0.9
v1.0
操作標簽
在上面創建標簽,我們已經有了標簽v0.9 v1.0
。有時候我們標簽打錯了,需要進行刪除,那么就得更改啦,運用git tag -d tagName
$ git tag -d v0.9
Deleted tag 'v0.9' (was 98746d9)
$ git tag
v1.0
$ git tag v0.8 80b628c -m "version 0.8"
$ git tag
v0.8
v1.0
$ git show v0.8
$ git show v0.8
tag v0.8
Tagger: reng99 <1837895991@qq.com>
tag v0.8
Tagger: reng99 <1837895991@qq.com>
Date: Wed Dec 27 16:07:46 2017 +0800
version 0.8
在上面的演示中,我刪除了v0.9,然后在創建v0.8的時候追加了打標簽的信息,之后使用git show tagName
查看簽名信息。
我們還可以進行分支切換標簽,類似于分支的切換,我這里打的兩個標簽的內容是不同的,我可以通過觀察內容的改表來得知時候成功切換標簽了。
$ git tag
v0.8
v1.0
$ git checkout v1.0
HEAD is now at f15a1e9... merge dev branch
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
### function user
$ git checkout v0.8
Previous HEAD position was f15a1e9... merge dev branch
HEAD is now at 80b628c... add no-ff mode content
$ cat README.md
## master branch content
### new master branch content
### dev branch content
### new dev branch content
### merge with no-ff
在確認好標簽后,就可以像遠程推送標簽了,我這里推送v1.0
。
$ git push origin v1.0
Total 0 (delta 0), reused 0 (delta 0)
To https://github.com/reng99/learngit.git
* [new tag] v1.0 -> v1.0
上面是使用git push origin tagName
推送特定的tag
到遠程庫,但是我們能不能推送全部的tag呢?答案是肯定的,看者可以通過git push origin --tags
進行推送。有時候,我們推送了tag標簽到遠程庫中了
,現在想刪除掉怎么辦?這個就略微麻煩點,我們不能像上面提到的刪除本地庫的標簽那樣,通過git tag -d tagName
那樣,而是通過git push origin :refs/tags/tagName
,這里不演示,如果看者感興趣可以自己來把弄一下哦。