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,你可能會在這些時間點希望撤銷:
修改還沒add到暫存區,希望撤銷工作區的修改,則撤銷結果是撤銷回上一次commit或上一次add,取決於add_1否是已經發生過
命令
git checkout -- <filename>
可以丟棄工作區的修改:$ git checkout -- readme.txt
--
這個引數說明是針對當前分支的檔案,如果只用git checkout <branch_name>
表示的是切換分支修改已經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,你需要做如下設定:
註冊GitHub賬號:用郵箱就能註冊。
在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的倉庫:
在GitHub網站上建立倉庫:
點選頭像旁邊的+號,new repository是建立新倉庫,import reposi是從其他版本控制系統匯入檔案和歷史記錄。這裡我建立了一個名叫“nonblindGAN”的專案。從本地倉庫關聯遠端庫GitHub,使用命令:
git remote add <remote_name> [email protected]:path/repo-name.git
推送內容到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
如果本地已經建立了倉庫,比如已經init
和remote 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
舉個栗子
在本地的新倉庫處(只做了
init
和remote 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給官方倉庫來貢獻程式碼。
具體來說步驟如下:
對開源專案,從官方fork到自己的倉庫
對fork到倉庫的專案,clone 到本地(一定要從自己的賬號下clone倉庫,這樣你才能推送修改。如果從官方倉庫地址clone,因為沒有許可權,你將不能推送修改。)
修改完專案後,往自己的倉庫push。
如果你希望官方倉庫能接受你的修改,可以在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。
我們按照例子中的步驟來看(其實是很實際的例子),有以下幾步:
現有主分支master
建立分支dev
在分支dev上做開發,正常的commit
開發完成,合併分支dev和master
刪除分支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 | 一篇文章搞定Git、GitHub的理解和使用(學習筆記)
Git learning note 本筆記整理自廖雪峰老師的Git教程,加上了自己的實踐結果和一些理解,旨在使科研工作者(基本上是獨立開發的那種)看完就能理解和使用Git。廖老師的教程生動活潑,條理清晰,推薦閱讀。還可以贊助哦。 目錄 Git 簡
一篇文章搞懂DataSet、DataFrame、RDD-《每日五分鐘搞定大數據》
implicit 操作數 frame 大數據 函數 for 臨時 變量 ade 1. 三者共性: 1、RDD、DataFrame、Dataset全都是spark平臺下的分布式彈性數據集,為處理超大型數據提供便利 2、三者都有惰性機制,執行trainform操作時不會立即執
一篇文章搞懂DataSet、DataFrame、RDD-《每日五分鐘搞定大資料》
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
一篇文章搞懂DataSet、DataFrame、RDD
1. 三者共性: 1、RDD、DataFrame、Dataset全都是spark平臺下的分散式彈性資料集,為處理超大型資料提供便利 2、三者都有惰性機制,執行trainform操作時不會立即執行,遇到Action才會執行 3、三者都會根據spark的記憶體情況自動快取運算,這樣即使資
面試專欄|一篇文章搞定ArrayList和LinkedList所有面試問題
在面試中經常碰到: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