一篇長文說 git 基礎

git_thumb

版本管理在產品級開發中是很是重要的一個部分,它涉及到團隊協做,且影響到產品最終的發佈、上線以及測試環節,當前最流行的版本控制系統是 git。git 內容很是多,本文儘可能剋制地來介紹 git 的基礎內容javascript

概述

版本控制系統的做用

版本控制系統(Version Control System)是一種記錄若干文件修訂記錄的系統,它有如下三個做用:html

一、從當前版本回退到任意版本前端

git_base

二、查看歷史版本java

git_base

三、對比兩個版本差別node

git_base

git 優點

一、速度快linux

二、設計簡單git

三、輕量級的分支操做,容許上千個並行開發的分支,對非線性開發模式的強力支持github

四、有能力高效管理相似 linux 內核同樣的超大規模項目算法

五、git 已經成爲事實上的標準,幾乎全部優秀的前端項目都經過 git 來進行版本控制shell

六、社區成熟活躍,git 的流行離不開 github 的貢獻

重要概念

要理解 git,首先要了解 git 中的重要概念

【術語介紹】

repository 倉庫
branch 分支
summary 概要
track 跟蹤
modify 修改
stage 暫存
commit 提交
push 推送
pull 拉取
clone 克隆
amend 修改
merge 合併
conflict 衝突
origin 起源
upstream 上游
downstream 下游
verbose 冗長的
reflog 迴流

.git 目錄】

每一個項目都有一個 git 目錄(若是 git clone 出來的話,就是其中.git 的目錄),它是 git 用來保存元數據和對象數據庫的地方。這個目錄很是重要,每次克隆鏡像倉庫的時候,實際拷貝的就是這個目錄裏面的數據

【三種狀態】

對於任何一個文件,在 git 中都只有三種狀態:已提交(committed),已修改(modified)和已暫存(staged)

已提交:該文件已經被安全地保存在本地數據庫中了
已修改:修改了某個文件,但尚未提交保存
已暫存:把已修改的文件放在下次提交時要保存的清單中

文化的三種狀態正好對應文件流轉的三個工做區域:git 的工做目錄,暫存區域,以及本地倉庫

git_fileStatus

下面來分別解釋下,這三個工做區域

工做目錄是對項目的某個版本獨立提取出來的內容

暫存區域是一個簡單的文件,通常都放在 .git 目錄中。有時候人們會把這個文件叫作索引文件

本地倉庫就是指的 .git 目錄

基本的 git 工做流程以下:

一、在工做目錄中修改某些文件

二、對修改後的文件進行快照,而後保存到暫存區域

三、提交更新,將保存在暫存區域的文件快照永久轉儲到Git目錄中

【commit 哈希值】

在保存到 git 以前,全部數據都要進行內容的校驗和(checksum)計算,並將此結果做爲數據的惟一標識和索引,而不是文件名

git 使用 SHA-1 算法計算數據的校驗和,經過對文件的內容或目錄的結構計算出一個SHA-1哈希值,做爲指紋字符串。該字符串由40個十六進制字符(0-9及a-f)組成,看起來就像是:

23b9da6552252987aa493b52f8696cd6d3b00372

git 配置

配置級別

git 共有三個配置級別

  --local【默認,高優先級】:隻影響本倉庫,文件爲.git/config

  --global【中優先級】:影響到全部當前用戶的git倉庫,文件爲~/.gitconfig

  --system【低優先級】:影響到全系統的git倉庫,文件爲/etc/gitconfig

基礎配置

通常在新的系統上,須要先配置下本身的 git 工做環境。配置工做只需一次,之後升級時還會沿用如今的配置。固然,若是須要隨時能夠用相同的命令修改已有的配置

一、用戶名

git config --global user.name "xiaohuochai"

二、郵箱

git config --global user.email "121631835@qq.com"

三、文本編輯器

git config --global core.editor "code --wait"

四、更改 git 處理行結束條符的方式

Windows 使用回車(CR)和換行(LF)兩個字符來結束一行,而 Mac 和 Linux 只使用換行(LF)一個字符。下面的代碼告訴 git 在提交時把回車和換行轉換成換行,檢出時不轉換。這樣在 Windows 上的檢出文件中會保留回車和換行,而在 Mac 和 Linux 上,以及版本庫中會保留換行

git config --global core.autocrlf input

五、取消對中文的轉義

使用 git 時,常常會碰到有一些中文文件名或者路徑被轉義成\xx\xx\xx的狀況,經過下面的配置能夠改變默認轉義

git config --global core.quotepath false

六、只容許 push 當前分支到遠程同名分支上

git config --global push.default simple

查看配置

git config --list # 查看全部配置
git config --list --global # 查看全局配置
git config user.name # 查看某個配置項

若是要刪除或修改配置,更簡單的辦法是直接打開~/.gitconfig文件,或者.git/config文件修改便可

關於忽略的配置

通常總會有些文件無需歸入 git 的管理,也不但願它們總出如今未跟蹤文件列表

能夠在項目根目錄建立一個名爲 .gitignore 的文件,列出要忽略的文件模式

文件 .gitignore 的格式規範以下:

一、全部空行或者以註釋符號 # 開頭的行都會被 git 忽略

二、可使用標準的glob模式匹配

三、匹配模式以反斜槓(/)開頭防止遞歸

四、匹配模式最後跟反斜槓(/)說明要忽略的是目錄

五、要忽略指定模式之外的文件或目錄,能夠在模式前加上歎號(!)取反

.gitignore 文件常見設置以下

node_modules/
ecosystem.json
.DS_Store
.idea
.vscode

SSH 配置

若是要進行遠程操做,即從 github 遠程服務器 push 和 pull 代碼,須要解決一個問題,就是 github 怎麼知道是我在提交個人代碼?
除了每次輸入用戶名、密碼外,更簡單的方式是配置 SSH

大多數 git 服務器都會選擇使用 SSH 公鑰來進行受權。系統中的每一個用戶都必須提供一個公鑰用於受權

首先先確認一下是否已經有一個公鑰了。SSH公鑰默認儲存在帳戶的主目錄下的 ~/.ssh 目錄,有.pub後綴的文件就是公鑰,另外一個文件則是密鑰

而後,使用以下命令來生成 SSH key,而後一路回車,使用默認值便可

$ ssh-keygen -t rsa -b 4096 -C 121631835@qq.com

若是一切順利的話,能夠在用戶主目錄裏找到 .ssh 目錄,裏面有 id_rsaid_rsa.pub 兩個文件,這兩個就是 SSH Key 的祕鑰對,id_rsa 是私鑰,不能泄露出去,id_rsa.pub 是公鑰,能夠放心地告訴任何人

SSH的公鑰以下所示:

$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3ltgCdqTIrPuN6yMYCeSMg8shtM+TRBIULDmfeh/9lE51e2g2t8ytLxz/QrPu3jvvpBqMimyPxC0NyW38eIHP9dkXTS0V76LlXy1MZvIjP3SnaU3AJs/fke61wc9y9EdPfrpSjIZpG7Z134+huaioLhPRShRmUQjl3plC9a89fnCyzTmtix5fDKKFjU3ZU6uVSDPy8+o+vsTfwAPQ1ylaBbY733Y1shmd6Texwmb8ttkv1Xj31RdhTdSS2eI3pSN/Ld1GC6/d2u3zcLnC6T4+1WLd0KTm/lqdzB2uWSsnMBI11wfKdw3pqEI17oGrPxurmunoMPzyR/dHwkfwotwh 121631835@qq.com

接下來,登錄 gitHub,打開 Settings 中的 SSH Keys 頁面

github

而後,點 New SSH Key,填上任意 Title,在 Key 文本框裏粘貼 id_rsa.pub 文件的內容

點擊 Add SSH key 按鈕後,即算配置完成了

接下來,使用 ssh -T git@github.com 來測試 SSH 是否配置成功

$ ssh -T git@github.com
Hi littlematch0123! You've successfully authenticated, but GitHub does not provide shell access.

回到這部分最開始的問題,在請求代碼時,我怎麼知道對方是 github 呢?也須要 github 提供一個公鑰給我,因此在第一次鏈接 github 時要選擇 yes 來接受對方的公鑰,也就是下面的代碼

$ ssh -T git@github.com
The authenticity of host 'github.com (13.250.177.223)' can't be established.
RSA key fingerprint is 16:27:ac:a5:76:28:2d:36:63:1b:56:4d:eb:df:a6:48.
Are you sure you want to continue connecting (yes/no)? yes
warning: Permanently added 'github.com' (rsa) to the list of known hosts
Hi littlematch0123! You've successfully authenticated, but GitHub does not provide shell access.

git 基礎操做

初始化新倉庫

要對現有的某個項目開始用 git 管理,只需到此項目所在的目錄,執行

$ git init

初始化後,在當前目錄下會出現一個名爲 .git 的目錄,全部 git 須要的數據和資源都存放在這個目錄中。不過目前,僅僅是按照既有的結構框架初始化好了裏邊全部的文件和目錄,但尚未開始跟蹤管理項目中的任何一個文件

檢查文件狀態

要肯定哪些文件當前處於什麼狀態,能夠用 git status 命令

若是在取得倉庫以後當即執行此命令,會看到相似這樣的輸出

$ git status
On branch master
Initial commit
nothing to commit(create/copy files and use "git add" to track)

這說明如今的工做目錄至關乾淨。換句話說,全部已跟蹤文件在上次提交後都未被更改過,或者沒有任何文件

如今建立一個新文件README,保存退出後運行 git status 會看到該文件出如今未跟蹤文件列表中

$ git status
On branch master

No commits yet

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

    README.txt

nothing added to commit but untracked files present (use "git add" to track)

在狀態報告中能夠看到新建的README文件出如今「Untracked files」下面。未跟蹤的文件意味着 git 在以前的快照(提交)中沒有這些文件

跟蹤新文件

使用命令 git add 開始跟蹤一個新文件。因此,要跟蹤README文件,運行

$ git add README.txt

使用命令 git add . 會批量跟蹤全部工做目錄下未被跟蹤的文件

$ git add .

此時再運行 git status 命令,會看到README文件已被跟蹤,並處於暫存狀態

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   README.txt

只要在「Changes to be committed」這行下面的,就說明是已暫存狀態

暫存已修改文件

如今修改下以前已跟蹤過的文件README.txt,將其內容修改成hello world

而後再次運行status命令,會看到這樣的狀態報告:

$ echo hello world > README.txt
$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   README.txt

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

文件README.txt出如今 「Changes not staged for commit」 這行下面,說明已跟蹤文件的內容發生了變化,但尚未放到暫存區。要暫存此次更新,須要運行git add命令

git add 命令是個多功能命令,根據目標文件的狀態不一樣,此命令的效果也不一樣:能夠用它開始跟蹤新文件,或者把已跟蹤的文件放到暫存區,還能用於合併時把有衝突的文件標記爲已解決狀態等

所以,將這個命令理解爲「添加內容到下一次提交中」而不是「將一個文件添加到項目中」要更加合適

如今運行 git add 將README.txt放到暫存區,而後再看看git status的輸出

$ git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

    new file:   README.txt

提交更新

每次準備提交前,先用 git status 看下,是否是都已暫存起來了,而後再運行提交命令git commit

$ git commit

這種方式會啓動文本編輯器以便輸入本次提交的說明,編輯器會顯示相似下面的文本信息

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   new file:   README.txt
#
# Changes not staged for commit:
#   modified:   README.txt
#

能夠看到,默認的提交消息包含最後一次運行 git status 的輸出,放在註釋行裏,另外開頭還有一空行,須要輸入提交說明

另外也能夠用 -m 參數後跟提交說明的方式,在一行命令中提交更新

$ git commit -m '更新 README 內容'
[master 34c5aa0] 更新 README 內容
 1 file changed, 1 insertion(+), 1 deletion(-)

提交後它會提示,當前是在哪一個分支(master)提交的,本次提交的完整SHA-1校驗和是什麼(34c5aa0),以及在本次提交中,有多少文件修訂過,多少行添改和刪改過

在提交的時候,給 git commit 加上 -a 選項,git 會自動把全部已經跟蹤過的文件暫存起來一併提交

$ git commit -am '更新 README'
[master daa40d0] 更新 README
 1 file changed, 1 insertion(+), 1 deletion(-)

可是,跳過 git add 步驟,不等於徹底不使用 git add。由於 git commit -a 是將全部跟蹤過的文件暫存起來並提交,只是省略了暫存這一步。但一個未跟蹤狀態的文件須要使用 git add 命令來使其變成已跟蹤狀態

還有一種提交方式是使用 -v--verbose選項,翻譯成中文是冗餘的,它不只能回顧剛剛修改的內容,並且會迫使把提交理由寫得更詳細些

將 README 內容中的 12345 去掉
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Changes to be committed:
#   modified:   README.txt
#
# ------------------------ >8 ------------------------
# Do not modify or remove the line above.
# Everything below it will be ignored.
diff --git a/README.txt b/README.txt
index 5c1d8ad..95d09f2 100644
--- a/README.txt
+++ b/README.txt
@@ -1 +1 @@
-hello world12345
\ No newline at end of file
+hello world
\ No newline at end of file

輸出結果以下:

$ git commit --verbose
[master 2494a62] 將 README 內容中的 12345 去掉
 1 file changed, 1 insertion(+), 1 deletion(-)

git 查看

狀態簡覽

git status 命令的輸出十分詳細,但其用語有些繁瑣。 若是使用 git status -s 命令或 git status --short 命令,將獲得一種更爲緊湊的格式輸出

$ git status -s
 M README   # 文件被修改,但尚未放入暫存區
MM Rakefile # 在工做區被修改並提交到暫存區後又在工做區中被修改了
A  lib/git.rb # 新添加到暫存區中的文件
M  lib/simplegit.rb # 文件被修改,且放入了暫存區
?? LICENSE.txt # 新添加的未跟蹤的文件

通常地,-s 選項與-b選項同時使用,s 表明 summary(概要),b 表明 branch(分支)

$ git status -sb
## master...origin/master [ahead 1]
 M "git.md"

狀態詳覽

若是在知道具體哪行發生了改變,要使用 git diff 命令

git diff 命令比較的是工做目錄中當前文件和暫存區域快照之間的差別, 也就是修改以後尚未暫存起來的變化內容,若是暫存了全部更新過的文件後,則運行 git diff 後會什麼都沒有

下面的代碼中,README.txt 文件的內容從 'hello world1' 變化到 'hello world123'

$ git diff
diff --git a/README.txt b/README.txt
index 62b372b..6d7f756 100644
--- a/README.txt
+++ b/README.txt
@@ -1 +1 @@
-hello world1
\ No newline at end of file
+hello world123
\ No newline at end of file

若是要看已經暫存起來的文件和上次提交時的快照之間的差別,能夠用 git diff--cached 命令

下面的代碼中,README.txt 文件的內容從空內容變化到 'hello world1'

$ git diff --cached
diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..62b372b
--- /dev/null
+++ b/README.txt
@@ -0,0 +1 @@
+hello world1
\ No newline at end of file

查看提交歷史

使用 git log 命令能夠查看提交歷史

$ git log
commit 3f7b9ed403e6d624651014a5d15c481463572c15 (HEAD -> master)
Author: xiaohuochai <121631835@qq.com>
Date:   Sun Dec 29 23:19:44 2019 +0800

    add b

commit ee5ae6f1dd5f620f4d2ac4a3702eb4814a062fce
Author: xiaohuochai <121631835@qq.com>
Date:   Sun Dec 29 23:15:10 2019 +0800

    delete c

默認不用任何參數的話,git log 會按提交時間列出全部的更新,最近的更新排在最上面,每次更新都有一個SHA-1校驗和、做者的名字和電子郵件地址、提交時間,最後縮進一個段落顯示提交說明

咱們經常使用 -p 選項展開顯示每次提交的內容差別,用 -2 則僅顯示最近的兩次更新

$ git log -p -2
commit 3f7b9ed403e6d624651014a5d15c481463572c15 (HEAD -> master)
Author: xiaohuochai <121631835@qq.com>
Date:   Sun Dec 29 23:19:44 2019 +0800

    add b

diff --git a/b1 b/b1
new file mode 100644
index 0000000..e69de29

commit ee5ae6f1dd5f620f4d2ac4a3702eb4814a062fce
Author: xiaohuochai <121631835@qq.com>
Date:   Sun Dec 29 23:15:10 2019 +0800

    delete c

diff --git a/c b/c
deleted file mode 100644
index e69de29..0000000

該選項除了顯示基本信息以外,還在附帶了每次 commit 的變化。當進行代碼審查,或者快速瀏覽某個搭檔提交的 commit 的變化時,這個參數就很是有用了

能夠用 --oneline 選項將每一個提交放在一行顯示,這在提交數很大時很是有用

$ git log --oneline
3f7b9ed (HEAD -> master) add b
ee5ae6f delete c

git 命令歷史

git reflog 命令按照以前通過的全部的 commit 路徑按序來排列,用來記錄每一次命令,經常使用於版本切換的輔助操做中

$ git reflog

git 版本切換

git 要進行版本切換,就必須知道當前版本是哪一個版本。在 git 中,用 HEAD 來表示當前版本,也就是最新的提交,上一個版本就是 HEAD^,上上一個版本就是 HEAD^^,固然往上100個版本寫100個^比較容易數不過來,因此寫成 HEAD~100

先使用 git log --online 來簡覽當前的提交歷史

$ git log --oneline
e7422c8 (HEAD -> master) add b
ee5ae6f delete c
8760a0f add c

使用命令 git reset --hard commit_id 在版本的歷史之間切換

git reset --hard HEAD^
HEAD is now at ee5ae6f delete c

再使用 git log --online 來簡覽提交歷史,發現最新的那個版本已經看不到了

$ git log --oneline
ee5ae6f (HEAD -> master) delete c
8760a0f add c

若是找到最新版本的提交對象呢? git提供了一個命令 git reflog,該命令按照以前通過的全部的 commit 路徑按序來排列,用來記錄每一次命令

$ git reflog
ee5ae6f (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
e7422c8 HEAD@{1}: commit (amend): add b
3f7b9ed HEAD@{2}: commit: add b
ee5ae6f (HEAD -> master) HEAD@{3}: commit: delete c
8760a0f HEAD@{4}: commit: add c

git reflog 命令返回的結果中發現,e7422c8 是最新版本的 commit id

  下面使用 git reset --hard 命令,將文件恢復到最新版本

$ git reset --hard e7422c8
HEAD is now at e7422c8 add b

git 分支管理

幾乎每一種版本控制系統都以某種形式支持分支。使用分支意味着能夠從開發主線上分離開來,而後在不影響主線的同時繼續工做。

git 中的分支,其實本質上僅僅是個指向 commit 對象的可變指針,即一個包含所指對象校驗和(40個字符長度SHA-1字串)的文件,因此建立和銷燬一個分支就變得很是廉價。說白了,新建一個分支就是向一個文件寫入41個字節(外加一個換行符)那麼簡單,固然也就很快了

git 會使用 master 做爲分支的默認名字。在若干次提交後,其實已經有了一個指向最後一次提交對象的 master 分支,它在每次提交的時候都會自動向前移動

git_branch

建立分支

建立一個新的分支指針。好比新建一個 testing 分支,可使用 git branch 命令

$ git branch testing

這會在當前 commit 對象上新建一個分支指針

git_branch

git 保存着一個名爲 HEAD 的特別指針,它是一個指向正在工做中的本地分支的指針(能夠將 HEAD 想象爲當前分支的別名)。運行 git branch 命令,僅僅是創建了一個新的分支,但不會自動切換到這個分支中去,因此在這個例子中,依然還在 master 分支裏工做

切換分支

要切換到其餘分支,能夠執行 git checkout 命令。如今轉換到新建的 testing 分支,這樣 HEAD 就指向了 testing 分支

$ git checkout testing

git_branch

git checkout 使用 -b選項,能夠新建並切換到該分支,好比 iss53 分支

$ git checkout -b iss53

合併分支

可使用 git merge 命令來實現分支合併。通常地,合併分支有如下三種狀況

一、快進(Fast forward)合併

若是當前 master 分支所在的提交對象是要併入的 hotfix 分支的直接上游,git 只需把 master 分支指針直接右移。換句話說,若是順着一個分支走下去能夠到達另外一個分支的話,那麼 git 在合併二者時,只會簡單地把指針右移,由於這種單線的歷史分支不存在任何須要解決的分歧,因此這種合併過程能夠稱爲快進(Fast forward)

$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
 README | 1 -
 1 file changed, 1 deletion(-)

二、無衝突合併

以下圖所示,issue 53分支要合併回 master 分支中,git 會用兩個分支的末端(C4和C5)以及它們的共同祖先(C2)進行一次簡單的三方合併計

git_branch

$ git checkout master
$ git merge iss53
Auto-merging README
Merge made by the 'recursive' strategy.
 README | 1 +
 1 file changed, 1 insertion(+)

git 對三方合併後的結果從新作一個新的快照,並自動建立一個指向它的提交對象(C6)。這個提交對象比較特殊,它有兩個祖先(C4和C5)

git_branch

三、有衝突合併

若是在不一樣的分支中都修改了同一個文件的同一部分,git 就沒法乾淨地把二者合到一塊兒(邏輯上說,這種問題只能由人來裁決)。若是在解決問題 #53 的過程當中修改了 hotfix 中修改的部分,將獲得相似下面的結果

$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

git 做了合併,但沒有提交,它會停下來等待解決衝突。要看看哪些文件在合併時發生衝突,能夠用 git status 查閱

$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")

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

        both modified:      index.html

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

任何包含未解決衝突的文件都會以未合併(unmerged)的狀態列出。git會在有衝突的文件里加入標準的衝突解決標記,能夠經過它們來手工定位並解決這些衝突。能夠看到此文件包含相似下面這樣的部分:

<<<<<<< HEAD
<div id="footer">contact : email.support@github.com</div>
=======
<div id="footer">
  please contact us at support@github.com
</div>
>>>>>>> iss53

在解決了全部文件裏的全部衝突後,要運行 git add 命令將把它們標記爲已解決狀態(實際上就是將一次快照保存到暫存區域)。由於一旦暫存,就表示衝突已經解決

再運行一次 git status 來確認全部衝突都已解決

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

        modified:   index.html

若是確認全部衝突都已解決,能夠用 git commit 來完成此次合併提交。提交說明會自動生成

Merge branch 'iss53'

Conflicts:
  index.html
#
# It looks like you may be committing a merge.
# If this is not correct, please remove the file
#       .git/MERGE_HEAD
# and try again.
#

查看分支

git branch 命令若是不加任何參數,它會給出當前全部分支的清單。master 分支前的 * 字符表示當前所在的分支。也就是說,若是如今提交更新,master 分支將隨着開發進度前移

$ git branch
  iss53
* master
  testing

若要查看各個分支最後一個提交對象的信息,運行 git branch -v

$ git branch -v
  iss53   93b412c fix javascript issue
* master  7a98805 Merge branch 'iss53'
  testing 782fd34 add scott to the author list in the readmes

刪除分支

以前的工做成果已經合併到 master 了,那麼 iss53 分支也就沒用了。可使用 git branch -d 命令來刪除它

$ git branch -d iss53

若是一個分支沒有被合併過,則該分支須要使用 -D 選項來刪除

$ git branch -D iss53

git 遠程倉庫與分支

要參與任何一個 git 項目的協做,必需要了解該如何管理遠程倉庫。遠程倉庫是指託管在網絡上的項目倉庫,同他人協做開發某個項目時,須要管理這些遠程倉庫,以便推送或拉取數據,分享各自的工做進展。管理遠程倉庫的工做,包括添加遠程庫,移除遠程庫,管理遠程庫分支,定義是否跟蹤這些分支等

遠程分支(remote branch)是對遠程倉庫中的分支的索引。它們是一些沒法移動的本地分支;只有在 git 進行網絡交互時纔會更新。遠程分支就像是書籤,提醒着上次鏈接遠程倉庫時上面各分支的位置

一般用(遠程倉庫名)/(分支名)這樣的形式表示遠程分支,好比 origin/master 分支

克隆遠程倉庫

克隆倉庫的命令格式爲 git clone [url]。好比,要克隆代碼倉庫 git_learn,能夠用下面的命令:

$ git clone git@github.com:littlematch0123/git_learn.git

這會在當前目錄下建立一個名爲 git_learn 的目錄,其中包含一個.git的目錄,用於保存下載下來的全部版本記錄,而後從中取出最新版本的文件拷貝。若是進入這個新建的 git_learn 目錄,會看到項目中的全部文件已經在裏邊了,準備好後續開發和使用。若是但願在克隆的時候,本身定義要新建的項目目錄名稱,能夠在上面的命令末尾指定新的名字

$ git clone git@github.com:littlematch0123/git_learn.git learnGit

若是最後一個字符是點,表示會在當前目錄存放項目的全部文件,但當前目錄一開始最好是個空目錄

$ git clone git@github.com:littlematch0123/git_learn.git .

查看遠程倉庫

要查看當前配置有哪些遠程倉庫,能夠用 git remote 命令,它會列出每一個遠程庫的簡短名字。在克隆完某個項目後,至少能夠看到一個名爲 origin 的遠程庫,git 默認使用這個名字來標識所克隆的原始倉庫

$ git remote
origin

也能夠加上 -v 選項(v爲--verbose的簡寫,中文意思是冗長的),顯示對應的克隆地址。若是沒有推送權限,將看不到 push 的地址

$ git remote -v
origin  git@github.com:littlematch0123/git_learn.git (fetch)
origin  git@github.com:littlematch0123/git_learn.git (push)

添加遠程倉庫

一般狀況下,一個本地 git 倉庫對應一個遠程倉庫;然而,在一些狀況下,一個本地倉庫須要同時關聯多個遠程倉庫,好比同時將一個項目發佈在 github 和 coding上

添加一個新的遠程倉庫,能夠指定一個名字,以便未來引用,運行 git remote add [shortname] [url]

$ git remote add coding git@git.coding.net:ehuo0123/git_learn.git
$ git remote -v
coding  git@git.coding.net:ehuo0123/git_learn.git (fetch)
coding  git@git.coding.net:ehuo0123/git_learn.git (push)
origin  git@github.com:littlematch0123/git_learn.git (fetch)
origin  git@github.com:littlematch0123/git_learn.git (push)

推送本地分支

git push 命令用於將本地分支的更新,推送到遠程主機

$ git push <遠程主機名> <本地分支名>:<遠程分支名>

下面命令的意思是取出我在本地的 serverfix 分支,推送到遠程倉庫的 serverfix 分支中去

$ git push origin serverfix:serverfix

固然,分支名字能夠不一樣,但不建議這樣作

git push origin serverfix:awesomebranch

由於本地和遠程分支的名字相同,有下面簡要寫法

$ git push origin serverfix

若是要把本地的 master 分支推送到 origin 服務器上,能夠運行下面的命令

$ git push origin master

下面命令表示將當前分支推送到 origin 主機的對應分支,若是當前分支是 master 分支則推送 master 分支,若是是 x 分支則推送 x 分支

$ git push origin

通常地,當前分支只有一個追蹤分支,那麼主機名均可以省略

$ git push

若是當前分支與多個主機存在追蹤關係,則可使用 -u 選項指定一個默認主機,這樣後面就能夠不加任何參數使用 git push

$ git push -u origin master

無論是否存在對應的遠程分支,將本地的全部分支都推送到遠程主機,這時須要使用 –all 選項

$ git push --all origin

從服務器抓取數據

使用 git fetch 命令從服務器抓取全部分支的數據

$ git fetch origin
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/littlematch0123/git_learn
 * [new branch]      y          -> origin/y

若是加上分支名,則只更新該分支的數據

$ git fetch origin master

可是要注意的是,fetch 命令只抓取數據,本地不會自動生成一份可編輯的副本(拷貝)。 換句話說,這種狀況下,不會有一個新的 y 分支——只有一個不能夠修改的 origin/y 指針

能夠運行 git merge origin/y 將這些工做合併到當前所在的 master 分支

$ git merge origin/y

若是想要在本身的 y 分支上工做,能夠將其創建在遠程跟蹤分支之上:

$ git checkout -b y origin/y

從一個遠程跟蹤分支檢出一個本地分支會自動建立所謂的「跟蹤分支」(它跟蹤的分支叫作「上游分支」)。 跟蹤分支是與遠程分支有直接關係的本地分支,本地分支與遠程分支之間創建了一種追蹤關係(tracking)

當克隆一個倉庫時,它一般會自動地建立一個跟蹤 origin/master 的 master 分支

若是在一個跟蹤分支上輸入 git pull,git 能自動地識別去哪一個服務器上抓取、合併到哪一個分支。因此,實際上,git pullgit fetch 後跟git merge FETCH_HEAD 的縮寫。

$ git pull <遠程主機名> <遠程分支名>:<本地分支名>

好比,要取回 origin 主機的 next 分支,與本地的 master 分支合併,須要寫成下面這樣

$ git pull origin next:master

若是遠程分支(next)要與當前分支合併,以下

$ git pull origin next

若是當前分支與遠程分支存在追蹤關係,git pull就能夠省略遠程分支名

$ git pull origin

若是當前分支只有一個追蹤分支,連遠程主機名均可以省略

$ git pull

若是 git pull 時,提示 no tracking information,則說明本地分支和遠程分支的追蹤關係沒有建立,用命令 git branch --set-upstream branch-name origin/branch-name 來創建追蹤

刪除遠程分支

若是省略本地分支名,則表示刪除指定的遠程分支,由於這等同於推送一個空的本地分支到遠程分支

下面命令表示刪除 origin 主機的 x 分支

$ git push origin :x
# 等同於
$ git push origin --delete x

遠程倉庫刪除和重命名

$ git remote rename coding cd # 重命名
$ git remote rm coding # 刪除

不區分遠程倉庫

因爲添加了多個遠程倉庫,在 push 和 pull 時便面臨了倉庫的選擇問題。誠然如此較爲嚴謹,可是在許多狀況下,只須要保持遠程倉庫徹底一致,而不須要進行區分,於是這樣的區分便顯得有些「多餘」

先查看當前的 git remote 狀況

$ git remote -v
origin  git@github.com:littlematch0123/git_learn.git (fetch)
origin  git@github.com:littlematch0123/git_learn.git (push)

接下來,不額外添加遠程倉庫,而是給現有的遠程倉庫添加額外的URL

使用 git remote set-url --add <name> <url>,給已有的遠程倉庫添加一個遠程地址

$ git remote set-url --add origin git@git.coding.net:ehuo0123/git_learn.git

再次查看所關聯的遠程倉庫:

$ git remote -v
origin  git@github.com:littlematch0123/git_learn.git (fetch)
origin  git@github.com:littlematch0123/git_learn.git (push)
origin  git@git.coding.net:ehuo0123/git_learn.git (push)

這樣設置後的 push 和pull 操做與最初的操做徹底一致,不須要進行調整

若是再也不須要多個倉庫,可使用git remote set-url --delete <name> <url>,將其刪除

$ git remote set-url --delete origin git@git.coding.net:ehuo0123/git_learn.git

git 其餘操做

刪除文件

一、從工做目錄中刪除文件,直接使用 rm 命令刪除便可,由於其沒有歸入 git 版本庫中,git 並不知道

touch a # 新建 a
rm a # 刪除 a

若是多此一舉地使用 git rm a,反而會提示錯誤

$ git rm a
fatal: pathspec 'a' did not match any files

二、從暫存區中刪除文件,須要使用 git rm -f 命令來強制刪除

touch b # 新建 b
git add b # 將 b 添加到暫存區
git rm -f b # 刪除 b

若是使用 git rm b,會提示以下錯誤

$ git rm b
error: the following file has changes staged in the index:
    b
(use --cached to keep the file, or -f to force removal)

三、從本地倉庫中刪除文件,使用git rm命令便可

touch c # 新建 c
git add c # 將 c 添加到暫存區
git commit -m 'add c' # 提交到本地倉庫
git rm c # 刪除 c

四、若是僅僅是想把文件從 git 倉庫中刪除(亦即從暫存區域移除),但仍然但願保留在當前工做目錄中。換句話說,僅是從跟蹤清單中刪除。好比一些文件不當心歸入倉庫後,要移除跟蹤但不刪除文件,以便稍後在 .gitignore 文件中補上,用--cached選項便可

$ git rm d --cached

文件重命名

一、從工做目錄中文件重命名,直接使用 mv 命令刪除便可,由於其沒有歸入 git 版本庫中,git 並不知道

touch a # 新建 a
mv a a1 # 重命名 a 爲 a1

若是多此一舉地使用 git mv a a1,反而會提示錯誤

$ git mv a a1
fatal: not under version control, source=a, destination=a1

二、從暫存區,或者本地倉庫中重命名文件,直接使用 git mv 命令就能夠了

$ git mv b1 b2
localhost:t bailiang$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    b1 -> b2

撤消操做

任什麼時候候,都有可能須要撤消剛纔所作的某些操做。但要注意的是,有些撤銷操做是不可逆的,因此要謹慎當心,一旦失誤,就有可能丟失部分工做成果

一、修改最後一次提交

有時候提交完了才發現漏掉了幾個文件沒有加,或者提交信息寫錯了。想要撤消剛纔的提交操做,可使用 --amend 選項從新提交:

$ git commit --amend

若是剛纔提交時忘了暫存某些修改,能夠先補上暫存操做,而後再運行 --amend 提交

$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend

上面的三條命令最終只是產生一個提交,第二個提交命令修正了第一個的提交內容

二、取消已暫存的文件
使用 git reset HEAD <file>... 命令能夠取消暫存,將暫存區的文件恢復到工做目錄中

$ git reset HEAD a.txt

三、取消對文件的修改

使用 git checkout -- <file>... 命令能夠將文件恢復到上一個版本的狀態。要注意的是這個命令很是危險,對文件作的任何修改都會消失,由於只是拷貝了另外一個文件來覆蓋它。除非確實不想要那個文件了,不然不要使用這個命令

$ git checkout -- a.txt

注意事項

一、版本控制系統只能跟蹤文本文件的改動,好比TXT文件,網頁,全部的程序代碼等等。圖片、視頻這些二進制文件,雖然也能由版本控制系統管理,但無法跟蹤文件的變化,只能把二進制文件每次改動串起來,也就是隻知道圖片從100KB改爲了120KB,但到底改了啥,版本控制系統不知道,也無法知道

微軟的 Word 格式是二進制格式,所以,版本控制系統是無法跟蹤 Word

固然,辦法也是有的,須要安裝 docx2txt 程序,將 word 文檔轉換爲可讀的文本文件

把下面這行文本加到 .gitattributes 文件中:

*.docx diff=word

寫一個腳本把輸出結果包裝成 git 支持的格式。 在可執行路徑下建立一個叫 docx2txt 文件,添加這些內容:

#!/bin/bash
docx2txt.pl $1 -

chmod a+x 給這個文件加上可執行權限。 最後,須要配置 git 來使用這個腳本

$ git config diff.word.textconv docx2txt

如今若是在兩個快照之間進行比較,git 就會對那些以 .docx 結尾的文件應用「word」過濾器,即 docx2txt。這樣,Word 文件就能被高效地轉換成文本文件並進行比較了

二、不要使用 Windows 自帶的記事本編輯任何文本文件。緣由是 Microsoft 開發記事本的團隊使用了一個很是弱智的行爲來保存UTF-8編碼的文件,他們自做聰明地在每一個文件開頭添加了0xefbbbf(十六進制)的字符,會遇到不少難以想象的問題

三、git commit -am能夠寫成git commit -a -m,但不能寫成git commit -m -a

四、在 git 中任何已提交的東西幾乎老是能夠恢復的,但任何未提交的東西丟失後極可能再也找不到了

git 經常使用命令

基礎操做

$ git init  #初始化倉庫
$ git add <file> #跟蹤新文件,或者把已跟蹤的文件放到暫存區
$ git add .  #批量跟蹤全部工做目錄下未被跟蹤的文件
$ git rm <file> #從本地倉庫中刪除文件
$ git rm -f <file> #從暫存區中刪除文件
$ git rm --cached <file> #從git倉庫中刪除,但保留在當前工做目錄中
$ git commit  #把文件提交到本地倉庫
$ git commit -m 'wrote a file'  #-m參數後跟提交說明的方式,在一行命令中提交更新
$ git commit -am 'wrote a file'  #把全部已經跟蹤過的文件暫存起來一併提交
$ git commit -v #啓動文本編輯器以便輸入本次提交的說明,編輯器會顯示與上次提交相比的變動之處
$ git commit --amend  #修改最後一次提交
$ git reset HEAD <file>  #取消暫存
$ git checkout -- <file>  #恢復文件內容

查看操做

$ git status  #檢查當前文件狀態
$ git status -s #更爲緊湊的格式的狀態輸出
$ git diff  #查看工做目錄與暫存區的差別
$ git diff --cached  #查看暫存區與某次提交的差別,默認爲HEAD
$ git diff id1 id2  #查看兩次提交之間的差別
$ git log  #查看提交歷史
$ git log -p #展開顯示每次提交的內容差別
$ git log -2 #僅顯示最近的兩次更新
$ git log --oneline #每一個提交放在一行顯示
$ git log --all #顯示全部分支的提交記錄
$ git log --graph  #顯示 ASCII 圖形表示的分支合併歷史
$ git reflog  #按照以前通過的全部的commit路徑按序來排列,用來記錄每一次命令

分支操做

$ git branch  #列出全部分支,當前分支前面會標一個*號
$ git branch -v  #查看各分支最後一個提交對象的信息
$ git branch <branchName>  #新建分支
$ git branch -d <branchName>  #刪除分支
$ git branch -D <branchName>  #強制刪除分支,用於刪除沒有合併過的分支
$ git checkout <branchName>   #分支切換
$ git checkout -b <branchName>  #建立新分支並切換到該分支
$ git checkout -  #將HEAD移動到上一分支
$ git merge <branchName>  #將目標分支合併到當前分支
$ git reset --hard <commit>  #將當前分支回退到歷史某個版本,提交的內容會複製到暫存區和工做目錄

遠程操做

$ git remote   #查看全部的遠程倉庫
$ git remote -v  #顯示遠程倉庫對應的克隆地址
$ git remote add [shortname] [url]  #添加一個新的遠程倉庫
$ git remote rename pb paul   #將遠程庫的名稱從pb改成paul
$ git remote rm [shortname]   #取消對該遠程庫的關聯
$ git remote set-url --add <name> <url>  #給現有的遠程倉庫添加額外的URL
$ git remote set-url --delete <name> <url>  #給現有的遠程倉庫刪除額外的URL
$ git clone <address>  #克隆遠程倉庫的全部分支
$ git push origin <branchName>  #取出在本地的<branchName>分支,推送到遠程倉庫的<branchName>分支
$ git fetch origin  #從遠程服務器抓取全部分支的數據
$ git pull origin <branchName>  #至關於fetch和merge命令的合體
$ git push origin :serverfix  #在服務器上刪除serverfix分支
$ git push origin --delete serverfix  #刪除服務器分支的另外寫法
相關文章
相關標籤/搜索