一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

git教程

 望穿墻 2015-03-19

Git版本控制軟件結(jié)合GitHub從入門到精通常用命令學(xué)習(xí)手冊 – 愛分享

git版本控制軟件

注意:

學(xué)習(xí)前請先配置好Git客戶端



GIT 學(xué)習(xí)手冊簡介

本站為 Git 學(xué)習(xí)參考手冊。目的是為學(xué)習(xí)與記憶 Git 使用中最重要、最普遍的命令提供快速翻閱。 這些命令以你可能需要的操作類型劃分,并且將提供日常使用中需要的一些常用的命令以及參數(shù)。

本手冊將從入門到精通指導(dǎo)大家。 首先,我們要從如何以 Git 的思維方式管理源代碼開始。

如何以 GIT 的方式思考(這節(jié)可以不用看懂,接著看下面的內(nèi)容,看完就全懂了。

懂得 Git,第一件重要的事情就是要知道它與 Subversion、Perforce 或者任何你用過的版本控制工具都有著很大的差別。 通常,忘掉你預(yù)想的版本控制方式,改以 Git 的方式思考,能夠幫助你更好地學(xué)習(xí) Git。

讓我們從頭開始。假設(shè)你正在設(shè)計一個新的源代碼管理系統(tǒng)。在你使用某個工具之前,是如何完成基本的源碼版本控制工作的呢? 十有八九,你只是在項目到達(dá)某些階段的時候,對項目做一份拷貝。

 $ cp -R project project.bak

這樣,你就可以在事情變得一團(tuán)糟的時候很方便的返回到之前的狀態(tài),或者通過對比當(dāng)前的項目與之前的拷貝,看看自己在之后的工作中,都做了哪些修改。

如果你有點偏執(zhí),你可能會經(jīng)常作上面說的事情,或許還會給項目拷貝加個日期:

 $ cp -R project project.2010-06-01.bak

如此,你就有了一堆項目在各個階段的快照,來作比較、查看。使用這種模式,你還可以有效地與人分享項目變更。 如果你會在項目到達(dá)一定階段的時候給它打個包,丟到自己的網(wǎng)站上,那其他的開發(fā)者們,就能很方便地下載它,做點改動,并給你補丁回饋。

 $ wget http:///project.2010-06-01.zip
 $ unzip project.2010-06-01.zip
 $ cp -R project.2010-06-01 project-my-copy
 $ cd project-my-copy
 $ (做了某些修改)
 $ diff project-my-copy project.2010-06-01 > change.patch
 $ (通過E-mail發(fā)送修改補丁)

以此方式,原先的開發(fā)者就能將其他人的改動應(yīng)用到他的項目中去,其他開發(fā)者也能了解你做的變更。其實這便是許多開源項目采用過多年的協(xié)作方式。

這辦法其實很好使,所以假設(shè)我們現(xiàn)在想要寫個工具,讓這個辦法更快、更簡單。 我們與其實現(xiàn)一個工具以記錄每個文件的版本,可能不如去實現(xiàn)個工具以使創(chuàng)建、儲存項目的快照更加方便,不用每次都去人肉作整個項目的拷貝。

這就是 Git 的精要所在。你通過 git commit告訴 Git 你想保存一份項目快照, Git 就會為你的項目中的各個文件的當(dāng)前狀態(tài)存一份記錄。之后,絕大部分的 Git 命令都圍繞這些記錄展開。 比如查看它們的區(qū)別(diff),提取它們的內(nèi)容,等等。

snapshots git
如果你將 Git 看作一個排序、對比以及合并項目更新的工具,那就容易理解狀況和正確做事了。

目錄

獲取與創(chuàng)建項目

基本的快照

分支與合并

分享與更新項目

檢查與比較

一、獲取與創(chuàng)建項目

你得先有一個 Git 倉庫,才能用它進(jìn)行操作。倉庫是 Git 存放你要保存的快照的數(shù)據(jù)的地方。

擁有一個 Git 倉庫的途徑有兩種。在已有的目錄中,初始化一個新的,其一。 比如一個新的項目,或者一個已存在的項目,但該項目尚未有版本控制。如果你想要復(fù)制一份別人的項目, 或者與別人合作某個項目,也可以從一個公開的 Git 倉庫克隆,其二。本章將對兩者都做介紹。

Git使用前配置

如果設(shè)置了,在輸入命令示界面可以很方便的使用復(fù)制和粘貼(用左鍵選取要復(fù)制的,點右鍵直接就可以復(fù)制,粘貼時只需點一下右鍵。)設(shè)置方法:Git Bash快捷圖標(biāo)(桌面圖標(biāo))右鍵屬性-選項,把快速編輯模式勾上就可以,如下圖:

git快速編輯模式配置

設(shè)置Git本地項目開發(fā)庫默認(rèn)路徑

如果設(shè)置了,就不用每次打開Git再cd打開目錄了。方法:右鍵Git Bash快捷圖標(biāo)(桌面圖標(biāo))屬性,找到快捷方式-起始位置,把你的項目地址放在這里就可以了。如下圖:

git默認(rèn)項目開發(fā)庫路徑設(shè)置

配置本地用戶和郵箱

用戶名郵箱作用 : 我們需要設(shè)置一個用戶名 和 郵箱, 這是用來上傳本地倉庫到GitHub中, 在GitHub中顯示代碼上傳者;
使用命令 :

git config --global user.name "HanShuliang" //設(shè)置用戶名 
git config --global user.email "13241153187@163.com" //設(shè)置郵箱

git客戶端版本控制軟件

到此Git客戶端已安裝及GitHub配置完成,現(xiàn)在可以從GitHub傳輸代碼了。

git init 將一個目錄初始化為 Git 倉庫

在目錄中執(zhí)行 git init,就可以創(chuàng)建一個 Git 倉庫了。比如,我們恰好有個目錄,里頭有些許文件,如下:

$ cd konnichiwa$ lsREADME   hello.rb

在這個項目里頭,我們會用各種編程語言寫 “Hello World” 實例。 到目前為止,我們只有 Ruby 的,不過,這才剛上路嘛。為了開始用 Git 對這個項目作版本控制,我們執(zhí)行一下 git init。

$ git initInitialized empty Git repository in /opt/konnichiwa/.git/
# 在 /opt/konnichiwa/.git 目錄初始化空 Git 倉庫完畢。

現(xiàn)在你可以看到在你的項目目錄中有個 .git 的子目錄。 這就是你的 Git 倉庫了,所有有關(guān)你的此項目的快照數(shù)據(jù)都存放在這里。

$ ls -a.        ..       .git     README   hello.rb

恭喜,現(xiàn)在你就有了一個 Git 倉庫的架子,可以開始快照你的項目了。

簡而言之,用 git init 來在目錄中創(chuàng)建新的 Git 倉庫。 你可以在任何時候、任何目錄中這么做,完全是本地化的。

git clone 復(fù)制一個 Git 倉庫,以上下其手

如果你需要與他人合作一個項目,或者想要復(fù)制一個項目,看看代碼,你就可以克隆那個項目。 執(zhí)行 git clone [url],[url] 為你想要復(fù)制的項目,就可以了。

$ git clone git://github.com/schacon/simplegit.gitInitialized empty Git repository in /private/tmp/simplegit/.git/
remote: Counting objects: 100, done.
remote: Compressing objects: 100% (86/86), done.
remote: Total 100 (delta 35), reused 0 (delta 0)
Receiving objects: 100% (100/100), 9.51 KiB, done.
Resolving deltas: 100% (35/35), done.$ cd simplegit/$ lsREADME   Rakefile lib

上述操作將復(fù)制該項目的全部記錄,讓你本地?fù)碛羞@些。并且該操作將拷貝該項目的主分支, 使你能夠查看代碼,或編輯、修改。進(jìn)到該目錄中,你會看到 .git 子目錄。 所有的項目數(shù)據(jù)都存在那里。

$ ls -a.        ..       .git     README   Rakefile lib$ cd .git$ lsHEAD        description info        packed-refsbranches    hooks       logs        refsconfig      index       objects

默認(rèn)情況下,Git 會按照你提供的 URL 所指示的項目的名稱創(chuàng)建你的本地項目目錄。 通常就是該 URL 最后一個 / 之后的任何東西。如果你想要一個不一樣的名字, 你可以在該命令后加上它,就在那個 URL 后面。

簡而言之,使用 git clone 拷貝一個 Git 倉庫到本地,讓自己能夠查看該項目,或者進(jìn)行修改。

二、基本快照

Git 的工作就是創(chuàng)建和保存你的項目的快照及與之后的快照進(jìn)行對比。本章將對有關(guān)創(chuàng)建與提交你的項目的快照的命令作介紹。

這里有個重要的概念,Git 有一個叫做“索引”的東東,有點像是你的快照的緩存區(qū)。這就使你能夠從更改的文件中創(chuàng)建出一系列組織良好的快照,而不是一次提交所有的更改。

簡而言之,使用 git add 添加需要追蹤的新文件和待提交的更改, 然后使用 git status 和 git diff 查看有何改動, 最后用 git commit 將你的快照記錄。這就是你要用的基本流程,絕大部分時候都是這樣的。

git add 添加文件到緩存

在 Git 中,在提交你修改的文件之前,你需要把它們添加到緩存。如果該文件是新創(chuàng)建的,你可以執(zhí)行 git add 將該文件添加到緩存,但是,即使該文件已經(jīng)被追蹤了 —— 也就是說,曾經(jīng)提交過了 —— 你仍然需要執(zhí)行 git add 將新更改的文件添加到緩存去。讓我們看幾個例子:

回到我們的 Hello World 示例,初始化該項目之后,我們就要用 git add 將我們的文件添加進(jìn)去了。 我們可以用 git status 看看我們的項目的當(dāng)前狀態(tài)。

$ git status -s?? README?? hello.rb

我們有倆尚未被追蹤的文件,得添加一下。

$ git add README hello.rb

現(xiàn)在我們再執(zhí)行 git status,就可以看到這倆文件已經(jīng)加上去了。

$ git status -sA  READMEA  hello.rb

新項目中,添加所有文件很普遍,可以在當(dāng)前工作目錄執(zhí)行命令:git add .。 因為 Git 會遞歸地將你執(zhí)行命令時所在的目錄中的所有文件添加上去,所以如果你將當(dāng)前的工作目錄作為參數(shù), 它就會追蹤那兒的所有文件了。如此,git add . 就和 git add README hello.rb 有一樣的效果。 此外,效果一致的還有 git add *,不過那只是因為我們這還木有子目錄,不需要遞歸地添加新文件。

好了,現(xiàn)在我們改個文件,再跑一下 git status,有點古怪。

$ vim README$ git status -sAM READMEA  hello.rb

“AM” 狀態(tài)的意思是,這個文件在我們將它添加到緩存之后又有改動。這意味著如果我們現(xiàn)在提交快照, 我們記錄的將是上次跑 git add 的時候的文件版本,而不是現(xiàn)在在磁盤中的這個。 Git 并不認(rèn)為磁盤中的文件與你想快照的文件必須是一致的 —— (如果你需要它們一致,)得用 git add 命令告訴它。

一言以蔽之, 當(dāng)你要將你的修改包含在即將提交的快照里的時候,執(zhí)行 git add。 任何你沒有添加的改動都不會被包含在內(nèi) —— 這意味著你可以比絕大多數(shù)其他源代碼版本控制系統(tǒng)更精確地歸置你的快照。

請查看《Pro Git》中 git add 的 “-p” 參數(shù),以了解更多關(guān)于提交文件的靈活性的例子。

git status 查看你的文件在工作目錄與緩存的狀態(tài)

正如你在 git add 小節(jié)中所看到的,你可以執(zhí)行 git status 命令查看你的代碼在緩存與當(dāng)前工作目錄的狀態(tài)。我演示該命令的時候加了 -s 參數(shù),以獲得簡短的結(jié)果輸出。 若沒有這個標(biāo)記,命令 git status 將告訴你更多的提示與上下文欣喜。 以下便是同樣狀態(tài)下,有跟沒有 -s 參數(shù)的輸出對比。簡短的輸出如下:

$ git status -sAM READMEA  hello.rb

而同樣的狀態(tài),詳細(xì)的輸出看起來是這樣的:

$ git status# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
# new file:   README# new file:   hello.rb#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified:   README#

你很容易發(fā)現(xiàn)簡短的輸出看起來很緊湊。而詳細(xì)輸出則很有幫助,提示你可以用何種命令完成你接下來可能要做的事情。

Git 還會告訴你在你上次提交之后,有哪些文件被刪除、修改或者存入緩存了。

$ git status -sM  README D hello.rb

你可以看到,在簡短輸出中,有兩欄。第一欄是緩存的,第二欄則是工作目錄的。 所以假設(shè)你臨時提交了 README 文件,然后又改了些,并且沒有執(zhí)行 git add,你會看到這個:

$ git status -sMM README D hello.rb

一言以蔽之,執(zhí)行 git status 以查看在你上次提交之后有啥被修改或者臨時提交了, 從而決定自己是否需要提交一次快照,同時也能知道有什么改變被記錄進(jìn)去了。

git diff 顯示已寫入緩存與已修改但尚未寫入緩存的改動的區(qū)別

git diff 有兩個主要的應(yīng)用場景。我們將在此介紹其一, 在 檢閱與對照 一章中,我們將介紹其二。 我們這里介紹的方式是用此命令描述已臨時提交的或者已修改但尚未提交的改動。

git diff #尚未緩存的改動

如果沒有其他參數(shù),git diff 會以規(guī)范化的 diff 格式(一個補?。╋@示自從你上次提交快照之后尚未緩存的所有更改。

$ vim hello.rb$ git status -s
 M hello.rb$ git diffdiff --git a/hello.rb b/hello.rb
index d62ac43..8d15d50 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,7 @@
 class HelloWorld

   def self.hello-    puts "hello world"+    puts "hola mundo"
   end

 end

所以,git status顯示你上次提交更新至后所更改或者寫入緩存的改動, 而 git diff 一行一行地顯示這些改動具體是啥。 通常執(zhí)行完 git status 之后接著跑一下 git diff 是個好習(xí)慣。

git diff –cached #查看已緩存的改動

git diff --cached 命令會告訴你有哪些內(nèi)容已經(jīng)寫入緩存了。 也就是說,此命令顯示的是接下來要寫入快照的內(nèi)容。所以,如果你將上述示例中的 hello.rb 寫入緩存,因為 git diff顯示的是尚未緩存的改動,所以在此執(zhí)行它不會顯示任何信息。

$ git status -s
 M hello.rb$ git add hello.rb $ git status -sM  hello.rb$ git diff$ 

如果你想看看已緩存的改動,你需要執(zhí)行的是 git diff --cached。

$ git status -sM  hello.rb$ git diff$ $ git diff --cacheddiff --git a/hello.rb b/hello.rb
index d62ac43..8d15d50 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,7 @@
 class HelloWorld

   def self.hello-    puts "hello world"+    puts "hola mundo"
   end

 end

git diff HEAD 查看已緩存的與未緩存的所有改動

如果你想一并查看已緩存的與未緩存的改動,可以執(zhí)行 git diff HEAD —— 也就是說你要看到的是工作目錄與上一次提交的更新的區(qū)別,無視緩存。 假設(shè)我們又改了些 ruby.rb 的內(nèi)容,那緩存的與未緩存的改動我們就都有了。 以上三個 diff 命令的結(jié)果如下:

$ vim hello.rb $ git diffdiff --git a/hello.rb b/hello.rb
index 4f40006..2ae9ba4 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,7 @@
 class HelloWorld+  # says hello
   def self.hello
     puts "hola mundo"
   end

 end$ git diff --cacheddiff --git a/hello.rb b/hello.rb
index 2aabb6e..4f40006 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,7 @@
 class HelloWorld

   def self.hello-    puts "hello world"+    puts "hola mundo"
   end

 end$ git diff HEADdiff --git a/hello.rb b/hello.rb
index 2aabb6e..2ae9ba4 100644
--- a/hello.rb
+++ b/hello.rb@@ -1,7 +1,8 @@
 class HelloWorld+  # says hello
   def self.hello-    puts "hello world"+    puts "hola mundo"
   end

 end

git diff –stat 顯示摘要而非整個 diff

如果我們不想要看整個 diff 輸出,但是又想比 git status 詳細(xì)點, 就可以用 --stat 選項。該選項使它顯示摘要而非全文。上文示例在使用 --stat 選項時,輸出如下:

$ git status -sMM hello.rb$ git diff --stat
 hello.rb |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)$ git diff --cached --stat
 hello.rb |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)$ git diff HEAD --stat
 hello.rb |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

你還可以在上述命令后面制定一個目錄,從而只查看特定文件或子目錄的 diff 輸出。

簡而言之, 執(zhí)行 git diff 來查看執(zhí)行 git status 的結(jié)果的詳細(xì)信息 —— 一行一行地顯示這些文件是如何被修改或?qū)懭刖彺娴摹?/p>

git commit 記錄緩存內(nèi)容的快照

現(xiàn)在你使用 git add 命令將想要快照的內(nèi)容寫入了緩存, 執(zhí)行 git commit 就將它實際存儲快照了。 Git 為你的每一個提交都記錄你的名字與電子郵箱地址,所以第一步是告訴 Git 這些都是啥。

$ git config --global user.name 'Your Name'$ git config --global user.email you@somedomain.com

讓我們寫入緩存,并提交對 hello.rb 的所有改動。在首個例子中,我們使用 -m 選項以在命令行中提供提交注釋。

$ git add hello.rb $ git status -sM  hello.rb$ git commit -m 'my hola mundo changes'[master 68aa034] my hola mundo changes
 1 files changed, 2 insertions(+), 1 deletions(-)

現(xiàn)在我們已經(jīng)記錄了快照。如果我們再執(zhí)行 git status,會看到我們有一個“干凈的工作目錄”。 這意味著我們在最近一次提交之后,沒有做任何改動 —— 在我們的項目中沒有未快照的工作。

$ git status# On branch master
nothing to commit (working directory clean)

如果你漏掉了 -m 選項,Git 會嘗試為你打開一個編輯器以填寫提交信息。 如果 Git 在你對它的配置中找不到相關(guān)信息,默認(rèn)會打開 vim。屏幕會像這樣:

# 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:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   hello.rb
#
~
~
".git/COMMIT_EDITMSG" 9L, 257C

在此,你在文件頭部添加實際的提交信息。以“#”開頭的行都會被無視 ——Git 將 git status 的輸出結(jié)果放在那兒以提示你都改了、緩存了啥。

通常,撰寫良好的提交信息是很重要的。以開放源代碼項目為例,多多少少以以下格式寫你的提示消息是個不成文的規(guī)定:

簡短的關(guān)于改動的總結(jié)(25個字或者更少)

如果有必要,更詳細(xì)的解釋文字。約 36 字時換行。在某些情況下,
第一行會被作為電子郵件的開頭,而剩余的則會作為郵件內(nèi)容。
將小結(jié)從內(nèi)容隔開的空行是至關(guān)重要的(除非你沒有內(nèi)容);
如果這兩個待在一起,有些 git 工具會犯迷糊。

空行之后是更多的段落。

 - 列表也可以

 - 通常使用連字符(-)或者星號(*)來標(biāo)記列表,前面有個空格,
   在列表項之間有空行,不過這些約定也會有些變化。

# 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:
#   (use "git reset HEAD <file>..." to unstage)
#
# modified:   hello.rb
#
~
~
~
".git/COMMIT_EDITMSG" 25L, 884C written

提交注解是很重要的。因為 Git 很大一部分能耐就是它在組織本地提交和與他人分享的彈性, 它很給力地能夠讓你為邏輯獨立的改變寫三到四條提交注解,以便你的工作被同仁審閱。因為提交與推送改動是有區(qū)別的, 請務(wù)必花時間將各個邏輯獨立的改動放到另外一個提交,并附上一份良好的提交注解, 以使與你合作的人能夠方便地了解你所做的,以及你為何要這么做。

git commit -a 自動將在提交前將已記錄、修改的文件放入緩存區(qū)

如果你覺得 git add 提交緩存的流程太過繁瑣,Git 也允許你用 -a 選項跳過這一步。 基本上這句話的意思就是,為任何已有記錄的文件執(zhí)行 git add —— 也就是說,任何在你最近的提交中已經(jīng)存在,并且之后被修改的文件。 這讓你能夠用更 Subversion 方式的流程,修改些文件,然后想要快照所有所做的改動的時候執(zhí)行 git commit -a。 不過你仍然需要執(zhí)行 git add 來添加新文件,就像 Subversion 一樣。

$ vim hello.rb$ git status -s
 M  hello.rb$ git commit -m 'changes to hello file'# On branch master
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
# modified:   hello.rb
#no changes added to commit (use "git add" and/or "git commit -a")$ git commit -am 'changes to hello file'[master 78b2670] changes to hello file
 1 files changed, 2 insertions(+), 1 deletions(-)

注意,如果你不緩存改動,直接執(zhí)行 git commit,Git 會直接給出 git status 命令的輸出,提醒你啥也沒緩存。我已將該消息中的重要部分高亮,它說沒有添加需要提交的緩存。 如果你使用 -a,它會緩存并提交每個改動(不含新文件)。

現(xiàn)在你就完成了整個快照的流程 ——改些文件,然后用 git add 將要提交的改動提交到緩存, 用 git status 和 git diff 看看你都改了啥,最后 git commit 永久地保存快照。

簡而言之,執(zhí)行 git commit 記錄緩存區(qū)的快照。如果需要的話,這個快照可以用來做比較、共享以及恢復(fù)。

git reset HEAD 取消緩存已緩存的內(nèi)容

git reset 可能是人類寫的最費解的命令了。 我用 Git 有些年頭了,甚至還寫了本書,但有的時候還是會搞不清它會做什么。 所以,我只說三個明確的,通常有用的調(diào)用。請你跟我一樣盡管用它 —— 因為它可以很有用。

在此例中,我們可以用它來將不小心緩存的東東取消緩存。假設(shè)你修改了兩個文件,想要將它們記錄到兩個不同的提交中去。 你應(yīng)該緩存并提交一個,再緩存并提交另外一個。如果你不小心兩個都緩存了,那要如何才能取消緩存呢? 你可以用 git reset HEAD -- file。 技術(shù)上說,在這里你不需要使用 -- —— 它用來告訴 Git 這時你已經(jīng)不再列選項,剩下的是文件路徑了。 不過養(yǎng)成使用它分隔選項與路徑的習(xí)慣很重要,即使在你可能并不需要的時候。

好,讓我們看看取消緩存是什么樣子的。這里我們有兩個最近提交之后又有所改動的文件。我們將兩個都緩存,并取消緩存其中一個。

$ git status -s
 M README M hello.rb$ git add .$ git status -sM  READMEM  hello.rb$ git reset HEAD -- hello.rb Unstaged changes after reset:
M hello.rb$ git status -sM  README M hello.rb

現(xiàn)在你執(zhí)行 git commit 將只記錄 README 文件的改動,并不含現(xiàn)在并不在緩存中的 hello.rb。

如果你好奇,它實際的操作是將該文件在“索引”中的校驗和重置為最近一次提交中的值。 git add 會計算一個文件的校驗和,將它添加到“索引”中, 而 git reset HEAD 將它改寫回原先的,從而取消緩存操作。

如果你想直接執(zhí)行 git unstage,你可以在 Git 中配置個別名。 執(zhí)行 git config --global alias.unstage "reset HEAD" 即可。 一旦執(zhí)行完它,你就可以直接用 git unstage [file]作為代替了。

如果你忘了取消緩存的命令,Git 的常規(guī) git status 輸出的提示會很有幫助。 例如,在你有已緩存的文件時,如果你不帶 -s 執(zhí)行 git status,它將告訴你怎樣取消緩存:

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

簡而言之,執(zhí)行 git reset HEAD 以取消之前 git add 添加,但不希望包含在下一提交快照中的緩存。

git rm 將文件從緩存區(qū)移除

git rm 會將條目從緩存區(qū)中移除。這與 git reset HEAD 將條目取消緩存是有區(qū)別的。 “取消緩存”的意思就是將緩存區(qū)恢復(fù)為我們做出修改之前的樣子。 在另一方面,git rm 則將該文件徹底從緩存區(qū)踢出,因此它不再下一個提交快照之內(nèi),進(jìn)而有效地刪除它。

默認(rèn)情況下,git rm file 會將文件從緩存區(qū)和你的硬盤中(工作目錄)刪除。 如果要在工作目錄中留著該文件,可以使用 git rm --cached

git mv git rm –cached orig; mv orig new; git add new

不像絕大多數(shù)其他版本控制系統(tǒng),Git 并不記錄記錄文件重命名。它反而只記錄快照,并對比快照以找到有啥文件可能被重命名了。 如果一個文件從更新中刪除了,而在下次快照中新添加的另一個文件的內(nèi)容與它很相似,Git 就知道這極有可能是個重命名。 因此,雖然有 git mv 命令,但它有點多余 —— 它做得所有事情就是 git rm --cached, 重命名磁盤上的文件,然后再執(zhí)行 git add 把新文件添加到緩存區(qū)。 你并不需要用它,不過如果覺得這樣容易些,盡管用吧。

我自己并不使用此命令的普通形式 —— 刪除文件。通常直接從硬盤刪除文件,然后執(zhí)行 git commit -a 會簡單些。 它會自動將刪除的文件從索引中移除。

簡而言之, 執(zhí)行 git rm 來刪除 Git 追蹤的文件。它還會刪除你的工作目錄中的相應(yīng)文件。

三、分支與合并

分支是我最喜歡的 Git 特性之一。如果你用過其他版本控制系統(tǒng),把你所知的分支給忘記,倒可能更有幫助些 —— 事實上,以我們使用分支的方式,把 Git 的分支看作 上下文 反而更合適。 當(dāng)你檢出分支時,你可以在兩三個不同的分支之間來回切換。

簡而言之,你可以執(zhí)行 git branch (branchname) 來創(chuàng)建分支, 使用 git checkout (branchname) 命令切換到該分支,在該分支的上下文環(huán)境中, 提交快照等,之后可以很容易地來回切換。當(dāng)你切換分支的時候,Git 會用該分支的最后提交的快照替換你的工作目錄的內(nèi)容, 所以多個分支不需要多個目錄。使用 git merge 來合并分支。你可以多次合并到統(tǒng)一分支, 也可以選擇在合并之后直接刪除被并入的分支。

git branch 列出、創(chuàng)建與管理工作上下文    git checkout 切換到新的分支上下文

git branch 命令是 Git 中的通用分支管理工具,可以通過它完成多項任務(wù)。 我們先說你會用到的最多的命令 —— 列出分支、創(chuàng)建分支和刪除分支。 我們還會介紹用來切換分支的 git checkout 命令。

git branch 列出可用的分支

沒有參數(shù)時,git branch 會列出你在本地的分支。你所在的分支的行首會有個星號作標(biāo)記。 如果你開啟了彩色模式,當(dāng)前分支會用綠色顯示。

$ git branch
* master

此例的意思就是,我們有一個叫做“master”的分支,并且該分支是當(dāng)前分支。 當(dāng)你執(zhí)行 git init 的時候,缺省情況下 Git 就會為你創(chuàng)建“master”分支。 但是這名字一點特殊意味都沒有 —— 事實上你并不非得要一個叫做“master”的分支。 不過由于它是缺省分支名的緣故,絕大部分項目都有這個分支。

git branch (branchname) 創(chuàng)建新分支

我們動手創(chuàng)建一個分支,并切換過去。執(zhí)行 git branch (branchname) 即可。

$ git branch testing
$ git branch
* master
  testing

現(xiàn)在我們可以看到,有了一個新分支。當(dāng)你以此方式在上次提交更新之后創(chuàng)建了新分支,如果后來又有更新提交, 然后又切換到了“testing”分支,Git 將還原你的工作目錄到你創(chuàng)建分支時候的樣子 —— 你可以把它看作一個記錄你當(dāng)前進(jìn)度的書簽。讓我們實際運用看看 —— 我們用 git checkout (branch) 切換到我們要修改的分支。

$ lsREADME   hello.rb$ echo 'test content' > test.txt$ echo 'more content' > more.txt$ git add *.txt$ git commit -m 'added two files'[master 8bd6d8b] added two files
 2 files changed, 2 insertions(+), 0 deletions(-)
 create mode 100644 more.txt
 create mode 100644 test.txt$ lsREADME   hello.rb more.txt test.txt$ git checkout testingSwitched to branch 'testing'$ lsREADME   hello.rb

當(dāng)我們切換到“測試”分支的時候,我們添加的新文件被移除了。切換回“master”分支的時候,它們有重新出現(xiàn)了。

$ lsREADME   hello.rb$ git checkout masterSwitched to branch 'master'$ lsREADME   hello.rb more.txt test.txt

git checkout -b (branchname) 創(chuàng)建新分支,并立即切換到它

通常情況下,你會更希望立即切換到新分支,從而在該分支中操作,然后當(dāng)此分支的開發(fā)日趨穩(wěn)定時, 將它合并到穩(wěn)定版本的分支(例如“master”)中去。 執(zhí)行 git branch newbranch; git checkout newbranch 也很簡單, 不過 Git 還為你提供了快捷方式:git checkout -b newbranch。

$ git branch* master$ lsREADME   hello.rb more.txt test.txt$ git checkout -b removalsSwitched to a new branch 'removals'$ git rm more.txt rm 'more.txt'$ git rm test.txt rm 'test.txt'$ lsREADME   hello.rb$ git commit -am 'removed useless files'[removals 8f7c949] removed useless files
 2 files changed, 0 insertions(+), 2 deletions(-)
 delete mode 100644 more.txt
 delete mode 100644 test.txt$ git checkout masterSwitched to branch 'master'$ lsREADME   hello.rb more.txt test.txt

如你所見,我們創(chuàng)建了一個分支,在該分支的上下文中移除了一些文件,然后切換回我們的主分支,那些文件又回來了。 使用分支將工作切分開來,從而讓我們能夠在不同上下文中做事,并來回切換。

創(chuàng)建新分支,在其中完成一部分工作,完成之后將它合并到主分支并刪除。你會覺得這很方便,因為這么做很快很容易。 如此,當(dāng)你覺得這部分工作并不靠譜,舍棄它很容易。并且,如果你必須回到穩(wěn)定分支做些事情, 也可以很方便地這個獨立分支的工作先丟在一邊,完成要事之后再切換回來。

git branch -d (branchname) 刪除分支

假設(shè)我們要刪除一個分支(比如上例中的“testing”分支,該分支沒啥特殊的內(nèi)容了), 可以執(zhí)行 git branch -d (branch) 把它刪掉。

$ git branch* master
  testing$ git branch -d testingDeleted branch testing (was 78b2670).$ git branch* master

簡而言之 使用 git branch 列出現(xiàn)有的分支、創(chuàng)建新分支以及刪除不必要或者已合并的分支。

git merge 將分支合并到你的當(dāng)前分支

一旦某分支有了獨立內(nèi)容,你終究會希望將它合并回到你的主分支。 你可以使用 git merge 命令將任何分支合并到當(dāng)前分支中去。 我們那上例中的“removals”分支為例。假設(shè)我們創(chuàng)建了一個分支,移除了一些文件,并將它提交到該分支, 其實該分支是與我們的主分支(也就是“master”)獨立開來的。 要想將這些移除操作包含在主分支中,你可以將“removals”分支合并回去。

$ git branch* master
  removals$ lsREADME   hello.rb more.txt test.txt$ git merge removalsUpdating 8bd6d8b..8f7c949
Fast-forward
 more.txt |    1 -
 test.txt |    1 -
 2 files changed, 0 insertions(+), 2 deletions(-)
 delete mode 100644 more.txt
 delete mode 100644 test.txt$ lsREADME   hello.rb

更多復(fù)雜合并

當(dāng)然,合并并不僅僅是簡單的文件添加、移除的操作,Git 也會合并修改 —— 事實上,它很會合并修改。 舉例,我們看看在某分支中編輯某個文件,然后在另一個分支中把它的名字改掉再做些修改, 最后將這倆分支合并起來。你覺得會變成一坨 shi?我們試試看。

$ git branch* master$ cat hello.rb class HelloWorld
  def self.hello
    puts "Hello World"
  end
end

HelloWorld.hello

首先,我們創(chuàng)建一個叫做“change_class”的分支,切換過去,從而將重命名類等操作獨立出來。我們將類名從 “HelloWorld” 改為 “HiWorld”。

$ git checkout -b change_classM hello.rb
Switched to a new branch 'change_class'$ vim hello.rb $ head -1 hello.rb class HiWorld$ git commit -am 'changed the class name'[change_class 3467b0a] changed the class name
 1 files changed, 2 insertions(+), 4 deletions(-)

然后,將重命名類操作提交到 “change_class” 分支中。 現(xiàn)在,假如切換回 “master” 分支我們可以看到類名恢復(fù)到了我們切換到 “change_class” 分支之前的樣子。 現(xiàn)在,再做些修改(即代碼中的輸出),同時將文件名從 hello.rb 改為 ruby.rb。

$ git checkout masterSwitched to branch 'master'$ git mv hello.rb ruby.rb$ vim ruby.rb $ git diffdiff --git a/ruby.rb b/ruby.rb
index 2aabb6e..bf64b17 100644
--- a/ruby.rb
+++ b/ruby.rb@@ -1,7 +1,7 @@
 class HelloWorld

   def self.hello-    puts "Hello World"+    puts "Hello World from Ruby"
   end

 end$ git commit -am 'added from ruby'[master b7ae93b] added from ruby
 1 files changed, 1 insertions(+), 1 deletions(-)
 rename hello.rb => ruby.rb (65%)

現(xiàn)在這些改變已經(jīng)記錄到我的 “master” 分支了。請注意,這里類名還是 “HelloWorld”,而不是 “HiWorld”。 然后我想將類名的改變合并過來,我把 “change_class” 分支合并過來就行了。 但是,我已經(jīng)將文件名都改掉了,Git 知道該怎么辦么?

$ git branch
  change_class
* master$ git merge change_classRenaming hello.rb => ruby.rb
Auto-merging ruby.rb
Merge made by recursive.
 ruby.rb |    6 ++----
 1 files changed, 2 insertions(+), 4 deletions(-)$ cat ruby.rbclass HiWorld
  def self.hello
    puts "Hello World from Ruby"
  end
end

HiWorld.hello

不錯,它就是發(fā)現(xiàn)了。請注意,在這部操作,我沒有遇到合并沖突,并且文件已經(jīng)重命名、類名也換掉了。挺酷。

合并沖突

那么,Git 合并很有魔力,我們再也不用處理合并沖突了,對嗎?不太確切。 不同分支中修改了相同區(qū)塊的代碼,電腦自己猜不透神馬的情況下,沖突就擺在我們面前了。 我們看看兩個分支中改了同一行代碼的例子。

$ git branch* master$ git checkout -b fix_readmeSwitched to a new branch 'fix_readme'$ vim README $ git commit -am 'fixed readme title'[fix_readme 3ac015d] fixed readme title
 1 files changed, 1 insertions(+), 1 deletions(-)

我們在某分支中修改了 README 文件中的一行,并提交了。我們再在 “master” 分支中對同個文件的同一行內(nèi)容作不同的修改。

$ git checkout masterSwitched to branch 'master'$ vim README $ git commit -am 'fixed readme title differently'[master 3cbb6aa] fixed readme title differently
 1 files changed, 1 insertions(+), 1 deletions(-)

有意思的來了 —— 我們將前一個分支合并到 “master” 分支,一個合并沖突就出現(xiàn)了。

$ git merge fix_readmeAuto-merging README
CONFLICT (content): Merge conflict in README
Automatic merge failed; fix conflicts and then commit the result.$ cat README <<<<<<< HEAD
Many Hello World Examples
=======
Hello World Lang Examples
>>>>>>> fix_readme

This project has examples of hello world in
nearly every programming language.

你可以看到,Git 在產(chǎn)生合并沖突的地方插入了標(biāo)準(zhǔn)的與 Subversion 很像的合并沖突標(biāo)記。 輪到我們?nèi)ソ鉀Q這些沖突了。在這里我們就手動把它解決。如果你要 Git 打開一個圖形化的合并工具, 可以看看 git 合并工具 (比如 kdiff3、emerge、p4merge 等)。

$ vim README   here I'm fixing the conflict$ git diffdiff --cc README
index 9103e27,69cad1a..0000000
--- a/README
+++ b/README@@@ -1,4 -1,4 +1,4 @@@- Many Hello World Examples
 -Hello World Lang Examples++Many Hello World Lang Examples

  This project has examples of hello world in

在 Git 中,處理合并沖突的時候有個很酷的提示。 如果你執(zhí)行 git diff,就像我演示的這樣,它會告訴你沖突的兩方,和你是如何解決的。 現(xiàn)在是時候把它標(biāo)記為已解決了。在 Git 中,我們可以用 git add —— 要告訴 Git 文件沖突已經(jīng)解決,你必須把它寫入緩存區(qū)。

$ git status -sUU README$ git add README $ git status -sM  README$ git commit [master 8d585ea] Merge branch 'fix_readme'

現(xiàn)在我們成功解決了合并中的沖突,并提交了結(jié)果

簡而言之 使用 git merge 將另一個分支并入當(dāng)前的分支中去。 Git 會自動以最佳方式將兩個不同快照中獨特的工作合并到一個新快照中去。

git log 顯示一個分支中提交的更改記錄

到目前為止,我們已經(jīng)提交快照到項目中,在不同的各自分離的上下文中切換, 但假如我們忘了自己是如何到目前這一步的那該怎么辦?或者假如我們想知道此分支與彼分支到底有啥區(qū)別? Git 提供了一個告訴你使你達(dá)成當(dāng)前快照的所有提交消息的工具,叫做 git log。

要理解日志(log)命令,你需要了解當(dāng)執(zhí)行 git commit 以存儲一個快照的時候,都有啥信息被保存了。 除了文件詳單、提交消息和提交者的信息,Git 還保存了你的此次提交所基于的快照。 也就是,假如你克隆了一個項目,你是在什么快照的基礎(chǔ)上做的修改而得到新保存的快照的? 這有益于為項目進(jìn)程提供上下文,使 Git 能夠弄明白誰做了什么改動。 如果 Git 有你的快照所基于的快照的話,它就能自動判斷你都改變了什么。而新提交所基于的提交,被稱作新提交的“父親”。

某分支的按時間排序的“父親”列表,當(dāng)你在該分支時,可以執(zhí)行 git log 以查看。 例如,如果我們在本章中操作的 Hello World 項目中執(zhí)行 git log,我們可以看到已提交的消息。

$ git logcommit 8d585ea6faf99facd39b55d6f6a3b3f481ad0d3dMerge: 3cbb6aa 3ac015d
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jun 4 12:59:47 2010 +0200

    Merge branch 'fix_readme'

    Conflicts:
        READMEcommit 3cbb6aae5c0cbd711c098e113ae436801371c95eAuthor: Scott Chacon <schacon@gmail.com>
Date:   Fri Jun 4 12:58:53 2010 +0200

    fixed readme title differentlycommit 3ac015da8ade34d4c7ebeffa2053fcac33fb495bAuthor: Scott Chacon <schacon@gmail.com>
Date:   Fri Jun 4 12:58:36 2010 +0200

    fixed readme titlecommit 558151a95567ba4181bab5746bc8f34bd87143d6Merge: b7ae93b 3467b0a
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jun 4 12:37:05 2010 +0200

    Merge branch 'change_class'
...

我們可以用 --oneline 選項來查看歷史記錄的緊湊簡潔的版本。

$ git log --oneline8d585ea Merge branch 'fix_readme'
3cbb6aa fixed readme title differently
3ac015d fixed readme title
558151a Merge branch 'change_class'
b7ae93b added from ruby
3467b0a changed the class name
17f4acf first commit

這告訴我們的是,此項目的開發(fā)歷史。如果提交消息描述性很好,這就能為我們提供關(guān)于有啥改動被應(yīng)用、或者影響了當(dāng)前快照的狀態(tài)、以及這快照里頭都有啥。

我們還可以用它的十分有幫助的 --graph 選項,查看歷史中什么時候出現(xiàn)了分支、合并。以下為相同的命令,開啟了拓?fù)鋱D選項:

$ git log --oneline --graph*   8d585ea Merge branch 'fix_readme'
|| * 3ac015d fixed readme title
* | 3cbb6aa fixed readme title differently
|/
*   558151a Merge branch 'change_class'
|| * 3467b0a changed the class name
* | b7ae93b added from ruby
|/
* 17f4acf first commit

現(xiàn)在我們可以更清楚明了地看到何時工作分叉、又何時歸并。 這對查看發(fā)生了什么、應(yīng)用了什么改變很有幫助,并且極大地幫助你管理你的分支。 讓我們創(chuàng)建一個分支,在里頭做些事情,然后切回到主分支,也做點事情,然后看看 log 命令是如何幫助我們理清這倆分支上都發(fā)生了啥的。

首先我們創(chuàng)建一個分支,來添加 Erlang 編程語言的 Hello World 示例 —— 我們想要在一個分支里頭做這個,以避免讓可能還不能工作的代碼弄亂我們的穩(wěn)定分支。 這樣就可以切來切去,片葉不沾身。

$ git checkout -b erlangSwitched to a new branch 'erlang'$ vim erlang_hw.erl$ git add erlang_hw.erl $ git commit -m 'added erlang'[erlang ab5ab4c] added erlang
 1 files changed, 5 insertions(+), 0 deletions(-)
 create mode 100644 erlang_hw.erl

由于我們玩函數(shù)式編程很開心,以至于沉迷其中,又在“erlang”分支中添加了一個 Haskell 的示例程序。

$ vim haskell.hs$ git add haskell.hs $ git commit -m 'added haskell'[erlang 1834130] added haskell
 1 files changed, 4 insertions(+), 0 deletions(-)
 create mode 100644 haskell.hs

最后,我們決定還是把 Ruby 程序的類名改回原先的樣子。與其創(chuàng)建另一個分支,我們可以返回主分支,改變它,然后直接提交。

$ git checkout masterSwitched to branch 'master'$ lsREADME  ruby.rb$ vim ruby.rb $ git commit -am 'reverted to old class name'[master 594f90b] reverted to old class name
 1 files changed, 2 insertions(+), 2 deletions(-)

現(xiàn)在假設(shè)我們有段時間不做這個項目了,我們做別的去了。 當(dāng)我們回來的時候,我們想知道“erlang”分支都是啥,而主分支的進(jìn)度又是怎樣。 僅僅看分支的名字,我們是無從知道自己還在里面有 Haskell 的改動的,但是用 git log 我們就可以。 如果你在命令行中提供一個分支名字,它就會顯示該分支歷史中“可及”的提交,即從該分支創(chuàng)立起可追溯的影響了最終的快照的提交。

$ git log --oneline erlang1834130 added haskellab5ab4c added erlang
8d585ea Merge branch 'fix_readme'
3cbb6aa fixed readme title differently
3ac015d fixed readme title
558151a Merge branch 'change_class'
b7ae93b added from ruby
3467b0a changed the class name
17f4acf first commit

如此,我們很容易就看到分支里頭還包括了 Haskell 代碼(高亮顯示了)。 更酷的是,我們很容易地告訴 Git,我們只對某個分支中可及的提交感興趣。換句話說,某分支中與其他分支相比唯一的提交。

在此例中,如果我們想要合并“erlang”分支,我們需要看當(dāng)合并的時候,都有啥提交會作用到我們的快照上去。 我們告訴 Git 的方式是,在不想要看到的分支前放一個 ^。 例如,如果我們想要看“erlang”分支中但不在主分支中的提交,我們可以用 erlang ^master,或者反之。

$ git log --oneline erlang ^master1834130 added haskell
ab5ab4c added erlang$ git log --oneline master ^erlang594f90b reverted to old class name

這為我們提供了一個良好的、簡易的分支管理工具。它使我們能夠非常容易地查看對某個分支唯一的提交,從而知道我們?nèi)鄙偈裁?,以及?dāng)我們要合并時,會有什么被合并進(jìn)去。

簡而言之 使用 git log 列出促成當(dāng)前分支目前的快照的提交歷史記錄。這使你能夠看到項目是如何到達(dá)現(xiàn)在的狀況的。

git tag 給歷史記錄中的某個重要的一點打上標(biāo)簽

如果你達(dá)到一個重要的階段,并希望永遠(yuǎn)記住那個特別的提交快照,你可以使用 git tag 給它打上標(biāo)簽。 該 tag 命令基本上會給該特殊提交打上永久的書簽,從而使你在將來能夠用它與其他提交比較。 通常,你會在切取一個發(fā)布版本或者交付一些東西的時候打個標(biāo)簽。

比如說,我們想為我們的 Hello World 項目發(fā)布一個“1.0”版本。 我們可以用 git tag -a v1.0 命令給最新一次提交打上(HEAD)“v1.0”的標(biāo)簽。 -a 選項意為“創(chuàng)建一個帶注解的標(biāo)簽”,從而使你為標(biāo)簽添加注解。絕大部分時候都會這么做的。 不用 -a 選項也可以執(zhí)行的,但它不會記錄這標(biāo)簽是啥時候打的,誰打的,也不會讓你添加個標(biāo)簽的注解。 我推薦一直創(chuàng)建帶注解的標(biāo)簽。

$ git tag -a v1.0 

當(dāng)你執(zhí)行 git tag -a 命令時,Git 會打開你的編輯器,讓你寫一句標(biāo)簽注解,就像你給提交寫注解一樣。

現(xiàn)在,注意當(dāng)我們執(zhí)行 git log --decorate 時,我們可以看到我們的標(biāo)簽了:

$ git log --oneline --decorate --graph* 594f90b (HEAD, tag: v1.0, master) reverted to old class name
*   8d585ea Merge branch 'fix_readme'
|| * 3ac015d (fix_readme) fixed readme title
* | 3cbb6aa fixed readme title differently
|/
*   558151a Merge branch 'change_class'
|| * 3467b0a changed the class name
* | b7ae93b added from ruby
|/
* 17f4acf first commit

如果我們有新提交,該標(biāo)簽依然會待在該提交的邊上,所以我們已經(jīng)給那個特定快照永久打上標(biāo)簽,并且能夠?qū)⑺c未來的快照做比較。

不過我們并不需要給當(dāng)前提交打標(biāo)簽。如果我們忘了給某個提交打標(biāo)簽,又將它發(fā)布了,我們可以給它追加標(biāo)簽。 在相同的命令末尾加上提交的 SHA,執(zhí)行,就可以了。 例如,假設(shè)我們發(fā)布了提交 558151a(幾個提交之前的事情了),但是那時候忘了給它打標(biāo)簽。 我們現(xiàn)在也可以:

$ git tag -a v0.9 558151a$ git log --oneline --decorate --graph* 594f90b (HEAD, tag: v1.0, master) reverted to old class name
*   8d585ea Merge branch 'fix_readme'
|| * 3ac015d (fix_readme) fixed readme title
* | 3cbb6aa fixed readme title differently
|/
*   558151a (tag: v0.9) Merge branch 'change_class'
|| * 3467b0a changed the class name
* | b7ae93b added from ruby
|/
* 17f4acf first commit

四、分享與更新項目

Git 并不像 Subversion 那樣有個中心服務(wù)器。 目前為止所有的命令都是本地執(zhí)行的,更新的知識本地的數(shù)據(jù)庫。 要通過 Git 與其他開發(fā)者合作,你需要將數(shù)據(jù)放到一臺其他開發(fā)者能夠連接的服務(wù)器上。 Git 實現(xiàn)此流程的方式是將你的數(shù)據(jù)與另一個倉庫同步。在服務(wù)器與客戶端之間并沒有實質(zhì)的區(qū)別 —— Git 倉庫就是 Git 倉庫,你可以很容易地在兩者之間同步。

一旦你有了個 Git 倉庫,不管它是在你自己的服務(wù)器上,或者是由 GitHub 之類的地方提供, 你都可以告訴 Git 推送你擁有的遠(yuǎn)端倉庫還沒有的數(shù)據(jù),或者叫 Git 從別的倉庫把差別取過來。

聯(lián)網(wǎng)的時候你可以隨時做這個,它并不需要對應(yīng)一個 commit 或者別的什么。 一般你會本地提交幾次,然后從你的項目克隆自的線上的共享倉庫提取數(shù)據(jù)以保持最新,將新完成的合并到你完成的工作中去,然后推送你的改動會服務(wù)器。

簡而言之 使用 git fetch 更新你的項目,使用 git push 分享你的改動。 你可以用 git remote 管理你的遠(yuǎn)程倉庫。

git remote 羅列、添加和刪除遠(yuǎn)端倉庫別名

不像中心化的版本控制系統(tǒng)(客戶端與服務(wù)端很不一樣),Git 倉庫基本上都是一致的,并且并可以同步他們。 這使得擁有多個遠(yuǎn)端倉庫變得容易 —— 你可以擁有一些只讀的倉庫,另外的一些也可寫的倉庫。

當(dāng)你需要與遠(yuǎn)端倉庫同步的時候,不需要使用它詳細(xì)的鏈接。Git 儲存了你感興趣的遠(yuǎn)端倉庫的鏈接的別名或者昵稱。 你可以使用 git remote 命令管理這個遠(yuǎn)端倉庫列表。

git remote 列出遠(yuǎn)端別名

如果沒有任何參數(shù),Git 會列出它存儲的遠(yuǎn)端倉庫別名了事。默認(rèn)情況下,如果你的項目是克隆的(與本地創(chuàng)建一個新的相反), Git 會自動將你的項目克隆自的倉庫添加到列表中,并取名“origin”。 如果你執(zhí)行時加上 -v 參數(shù),你還可以看到每個別名的實際鏈接地址。

$ git remoteorigin$ git remote -vorigin	git@github.com:github/git-reference.git (fetch)
origin	git@github.com:github/git-reference.git (push)

在此你看到了該鏈接兩次,是因為 Git 允許你為每個遠(yuǎn)端倉庫添加不同的推送與獲取的鏈接,以備你讀寫時希望使用不同的協(xié)議。

git remote add 為你的項目添加一個新的遠(yuǎn)端倉庫

如果你希望分享一個本地創(chuàng)建的倉庫,或者你想要獲取別人的倉庫中的貢獻(xiàn) —— 如果你想要以任何方式與一個新倉庫溝通,最簡單的方式通常就是把它添加為一個遠(yuǎn)端倉庫。 執(zhí)行 git remote add [alias] [url] 就可以。 此命令將 [url] 以 [alias] 的別名添加為本地的遠(yuǎn)端倉庫。

例如,假設(shè)我們想要與整個世界分享我們的 Hello World 程序。 我們可以在一臺服務(wù)器上創(chuàng)建一個新倉庫(我以 GitHub 為例子)。 它應(yīng)該會給你一個鏈接,在這里就是“git@github.com:schacon/hw.git”。 要把它添加到我們的項目以便我們推送以及獲取更新,我們可以這樣:

$ git remote$ git remote add github git@github.com:schacon/hw.git$ git remote -vgithub	git@github.com:schacon/hw.git (fetch)
github	git@github.com:schacon/hw.git (push)

像分支的命名一樣,遠(yuǎn)端倉庫的別名是強(qiáng)制的 —— 就像“master”,沒有特別意義,但它廣為使用, 因為 git init 默認(rèn)用它;“origin”經(jīng)常被用作遠(yuǎn)端倉庫別名,就因為 git clone 默認(rèn)用它作為克隆自的鏈接的別名。此例中,我決定給我的遠(yuǎn)端倉庫取名“github”,但我叫它隨便什么都可以。

git remote rm 刪除現(xiàn)存的某個別名

Git addeth and Git taketh away. 如果你需要刪除一個遠(yuǎn)端 —— 不再需要它了、項目已經(jīng)沒了,等等 —— 你可以使用 git remote rm [alias] 把它刪掉。

$ git remote -vgithub	git@github.com:schacon/hw.git (fetch)
github	git@github.com:schacon/hw.git (push)$ git remote add origin git://github.com/pjhyett/hw.git$ git remote -vgithub	git@github.com:schacon/hw.git (fetch)
github	git@github.com:schacon/hw.git (push)
origin	git://github.com/pjhyett/hw.git (fetch)
origin	git://github.com/pjhyett/hw.git (push)$ git remote rm origin$ git remote -vgithub	git@github.com:schacon/hw.git (fetch)
github	git@github.com:schacon/hw.git (push)

簡而言之 你可以用 git remote 列出你的遠(yuǎn)端倉庫和那些倉庫的鏈接。 你可以使用 git remote add 添加新的遠(yuǎn)端倉庫,用 git remote rm 刪掉已存在的那些。

git fetch 從遠(yuǎn)端倉庫下載新分支與數(shù)據(jù)    git pull 從遠(yuǎn)端倉庫提取數(shù)據(jù)并嘗試合并到當(dāng)前分支

Git 有兩個命令用來從某一遠(yuǎn)端倉庫更新。 git fetch 會使你與另一倉庫同步,提取你本地所沒有的數(shù)據(jù),為你在同步時的該遠(yuǎn)端的每一分支提供書簽。 這些分支被叫做“遠(yuǎn)端分支”,除了 Git 不允許你檢出(切換到該分支)之外,跟本地分支沒區(qū)別 —— 你可以將它們合并到當(dāng)前分支,與其他分支作比較差異,查看那些分支的歷史日志,等等。同步之后你就可以在本地操作這些。

第二個會從遠(yuǎn)端服務(wù)器提取新數(shù)據(jù)的命令是 git pull。 基本上,該命令就是在 git fetch 之后緊接著 git merge 遠(yuǎn)端分支到你所在的任意分支。 我個人不太喜歡這命令 —— 我更喜歡fetch 和 merge 分開來做。少點魔法,少點問題。 不過,如果你喜歡這主意,你可以看一下 git pull 的 官方文檔。

假設(shè)你配置好了一個遠(yuǎn)端,并且你想要提取更新,你可以首先執(zhí)行 git fetch [alias] 告訴 Git 去獲取它有你沒有的數(shù)據(jù),然后你可以執(zhí)行 git merge [alias]/[branch] 以將服務(wù)器上的任何更新(假設(shè)有人這時候推送到服務(wù)器了)合并到你的當(dāng)前分支。 那么,如果我是與兩三個其他人合作 Hello World 項目,并且想要將我最近連接之后的所有改動拿過來,我可以這么做:

$ git fetch githubremote: Counting objects: 4006, done.
remote: Compressing objects: 100% (1322/1322), done.
remote: Total 2783 (delta 1526), reused 2587 (delta 1387)
Receiving objects: 100% (2783/2783), 1.23 MiB | 10 KiB/s, done.
Resolving deltas: 100% (1526/1526), completed with 387 local objects.
From github.com:schacon/hw
   8e29b09..c7c5a10  master     -> github/master
   0709fdc..d4ccf73  c-langs    -> github/c-langs
   6684f82..ae06d2b  java       -> github/java
 * [new branch]      ada        -> github/ada
 * [new branch]      lisp       -> github/lisp

可以看到自從上一次與遠(yuǎn)端倉庫同步以后,又新贈或更新了五個分支。 “ada”與“l(fā)isp”分支是新的,而“master”、“clang”與“java”分支則被更新了。 在此例中,我的團(tuán)隊在合并入主分支之前,將提議的更新推送到遠(yuǎn)端分支以審核。

你可以看到 Git 做的映射。遠(yuǎn)端倉庫的主分支成為了本地的一個叫做“github/master”的分支。 這樣我就可以執(zhí)行 git merge github/master 將遠(yuǎn)端的主分支和并入我的本地主分支。 或者,我可以 git log github/master ^master 看看該分支上的新提交。 如果你的遠(yuǎn)端倉庫叫做“origin”,那遠(yuǎn)端主分支就會叫做 origin/master。幾乎所有能在本地分支上執(zhí)行的命令都可以在遠(yuǎn)端分支上用。

如果你有多個遠(yuǎn)端倉庫,你可以執(zhí)行 git fetch [alias] 提取特定的遠(yuǎn)端倉庫, 或者執(zhí)行 git fetch --all 告訴 Git 同步所有的遠(yuǎn)端倉庫。

簡而言之 執(zhí)行 git fetch [alias] 來將你的倉庫與遠(yuǎn)端倉庫同步,提取所有它獨有的數(shù)據(jù)到本地分支以合并或者怎樣。

git push 推送你的新分支與數(shù)據(jù)到某個遠(yuǎn)端倉庫

想要與他人分享你牛鼻的提交,你需要將改動推送到遠(yuǎn)端倉庫。 執(zhí)行 git push [alias] [branch],就會將你的 [branch] 分支推送成為 [alias] 遠(yuǎn)端上的 [branch] 分支。 讓我們試試推送我們的主分支到先前添加的“github”遠(yuǎn)端倉庫上去。

$ git push github masterCounting objects: 25, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (25/25), done.
Writing objects: 100% (25/25), 2.43 KiB, done.
Total 25 (delta 4), reused 0 (delta 0)
To git@github.com:schacon/hw.git
 * [new branch]      master -> master

挺簡單。現(xiàn)在如果有人從該倉庫克隆,他會得到我提交的完完全全的一份歷史記錄了。

如果有個像之前創(chuàng)建的“erlang”分支那樣的主題分支,想只分享這個,該怎么辦呢?你可以相應(yīng)的只推送該分支。

$ git push github erlangCounting objects: 7, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 652 bytes, done.
Total 6 (delta 1), reused 0 (delta 0)
To git@github.com:schacon/hw.git
 * [new branch]      erlang -> erlang

現(xiàn)在當(dāng)人們從該倉庫克隆時,他們就會得到一個“erlang”分支以查閱、合并。 用這種方式,你可以推送任何分支到任何你有寫權(quán)限的倉庫。 如果你的分支已經(jīng)在該倉庫中了,它會試著去更新,如果它不再,Git 會把它加上。

最后一個當(dāng)你推送到遠(yuǎn)端分支時會碰到的主要問題是,其他人在此期間也推送了的情況。 如果你和另一個開發(fā)者同時克隆了,又都有提交,那么當(dāng)她推送后你也想推送時,默認(rèn)情況下 Git 不會讓你覆蓋她的改動。 相反的,它會在你試圖推送的分支上執(zhí)行 git log,確定它能夠在你的推送分支的歷史記錄中看到服務(wù)器分支的當(dāng)前進(jìn)度。 如果它在在你的歷史記錄中看不到,它就會下結(jié)論說你過時了,并打回你的推送。 你需要正式提取、合并,然后再次推送 —— 以確定你把她的改動也考慮在內(nèi)了。

當(dāng)你試圖推送到某個以被更新的遠(yuǎn)端分支時,會出現(xiàn)下面這種情況:

$ git push github masterTo git@github.com:schacon/hw.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'git@github.com:schacon/hw.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes before pushing again.  See the 'Note about
fast-forwards' section of 'git push --help' for details.

你可以修正這個問題。執(zhí)行 git fetch github; git merge github/master,然后再推送

簡而言之 執(zhí)行 git push [alias] [branch] 將你的本地改動推送到遠(yuǎn)端倉庫。 如果可以的話,它會依據(jù)你的 [branch] 的樣子,推送到遠(yuǎn)端的 [branch] 去。 如果在你上次提取、合并之后,另有人推送了,Git 服務(wù)器會拒絕你的推送,知道你是最新的為止。

五、檢查與比較

現(xiàn)在你有了一堆分支,短期的主題、長期的特性或者其它。怎樣追蹤他們呢? Git 有一組工具,可以幫助你弄明白工作是在哪兒完成的,兩個分支間的區(qū)別是啥,等等。

簡而言之 執(zhí)行 git log 找到你的項目歷史中的特定提交 —— 按作者、日期、內(nèi)容或者歷史記錄。執(zhí)行 git diff 比較歷史記錄中的兩個不同的點 —— 通常是為了看看兩個分支有啥區(qū)別,或者從某個版本到另一個版本,你的軟件都有啥變化。

git log 過濾你的提交歷史記錄

通過查看分支中另一分支看不到的提交記錄,我們已經(jīng)看到如何用 git log 來比較分支。 (如果你不記得了,它看起來是這樣的:git log branchA ^branchB)。 而且,你也可以用git log 去尋找特定的提交。 在此,我們會看到一些更廣為使用的 git log 選項,不過哪有很多。 完整的清單可以看看官方文檔。

git log –author 只尋找某個特定作者的提交

要過濾你的提交歷史,只尋找某個特定作者的提交,你可以使用 --author 選項。 例如,比方說我們要找 Git 源碼中 Linus 提交的部分。 我們可以執(zhí)行類似 git log --author=Linus 的命令。 這個查找是大小寫敏感的,并且也會檢索電子郵箱地址。 我在此例中使用 -[number] 選項,以限制結(jié)果為最近 [number] 次的提交。

$ git log --author=Linus --oneline -581b50f3 Move 'builtin-*' into a 'builtin/' subdirectory
3bb7256 make "index-pack" a built-in
377d027 make "git pack-redundant" a built-in
b532581 make "git unpack-file" a built-in
112dd51 make "mktag" a built-in

git log –since –before 根據(jù)日期過濾提交記錄

如果你要指定一個你感興趣的日期范圍以過濾你的提交,可以執(zhí)行幾個選項 —— 我用 --since 和 --before,但是你也可以用 --until 和 --after。 例如,如果我要看 Git 項目中三周前且在四月十八日之后的所有提交,我可以執(zhí)行這個(我還用了 --no-merges 選項以隱藏合并提交):

$ git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges5469e2d Git 1.7.1-rc2
d43427d Documentation/remote-helpers: Fix typos and improve language
272a36b Fixup: Second argument may be any arbitrary string
b6c8d2d Documentation/remote-helpers: Add invocation section
5ce4f4e Documentation/urls: Rewrite to accomodate transport::address
00b84e9 Documentation/remote-helpers: Rewrite description
03aa87e Documentation: Describe other situations where -z affects git diff
77bc694 rebase-interactive: silence warning when no commits rewritten
636db2c t3301: add tests to use --format="%N"

git log –grep 根據(jù)提交注釋過濾提交記錄

你或許還想根據(jù)提交注釋中的某個特定短語查找提交記錄。可以用 --grep 選項。 比如說我知道有個提交是有關(guān)使用 P4EDITOR 環(huán)境變量,又想回憶起那個改動是啥樣子的 —— 我可以用--grep 選項找到該提交。

$ git log --grep=P4EDITOR --no-mergescommit 82cea9ffb1c4677155e3e2996d76542502611370Author: Shawn Bohrer
Date:   Wed Mar 12 19:03:24 2008 -0500

    git-p4: Use P4EDITOR environment variable when set

    Perforce allows you to set the P4EDITOR environment variable to your
    preferred editor for use in perforce.  Since we are displaying a
    perforce changelog to the user we should use it when it is defined.

    Signed-off-by: Shawn Bohrer <shawn.bohrer@gmail.com>
    Signed-off-by: Simon Hausmann <simon@lst.de>

Git 會對所有的 --grep 和 --author 參數(shù)作邏輯或。 如果你用 --grep 和 --author 時,想看的是某人寫作的并且有某個特殊的注釋內(nèi)容的提交記錄, 你需要加上 --all-match 選項。 在這些例子中,我會用上 --format 選項,這樣我們就可以看到每個提交的作者是誰了。

如果我查找注釋內(nèi)容含有 “p4 depo”的提交,我得到了三個提交:

$ git log --grep="p4 depo" --format="%h %an %s"ee4fd1a Junio C Hamano Merge branch 'master' of git://repo.or.cz/git/fastimport
da4a660 Benjamin Sergeant git-p4 fails when cloning a p4 depo.
1cd5738 Simon Hausmann Make incremental imports easier to use by storing the p4 d

如果我加上 --author=Hausmann 參數(shù),與進(jìn)一步過濾上述結(jié)果到 Simon 的唯一提交相反, 它會告訴我所有 Simon 的提交,或者注釋中有“p4 demo”的提交:

$ git log --grep="p4 depo" --format="%h %an %s" --author="Hausmann"cdc7e38 Simon Hausmann Make it possible to abort the submission of a change to Pe
f5f7e4a Simon Hausmann Clean up the git-p4 documentation
30b5940 Simon Hausmann git-p4: Fix import of changesets with file deletions
4c750c0 Simon Hausmann git-p4: git-p4 submit cleanups.
0e36f2d Simon Hausmann git-p4: Removed git-p4 submit --direct.
edae1e2 Simon Hausmann git-p4: Clean up git-p4 submit's log message handling.
4b61b5c Simon Hausmann git-p4: Remove --log-substitutions feature.
36ee4ee Simon Hausmann git-p4: Ensure the working directory and the index are cle
e96e400 Simon Hausmann git-p4: Fix submit user-interface.
38f9f5e Simon Hausmann git-p4: Fix direct import from perforce after fetching cha
2094714 Simon Hausmann git-p4: When skipping a patch as part of "git-p4 submit" m
1ca3d71 Simon Hausmann git-p4: Added support for automatically importing newly ap
...

不過,如果加上 --all-match,結(jié)果就是我想要的了:

$ git log --grep="p4 depo" --format="%h %an %s" --author="Hausmann" --all-match1cd5738 Simon Hausmann Make incremental imports easier to use by storing the p4 d

git log -S 依據(jù)所引入的差值過濾

如果你寫的提交注釋都極度糟糕怎么辦?或者,如果你要找某個函數(shù)是何時引入的,某些變量是在哪里開始被使用的? 你可以告訴 Git 在每個提交之間的差值中查找特定字符串。 例如,如果我們想要找出哪個提交修改出了類似函數(shù)名“userformat_find_requirements”, 我們可以執(zhí)行(注意在“-S”與你要找的東東之間沒有“=”):

$ git log -Suserformat_find_requirementscommit 5b16360330822527eac1fa84131d185ff784c9fbAuthor: Johannes Gilger
Date:   Tue Apr 13 22:31:12 2010 +0200

    pretty: Initialize notes if %N is used

    When using git log --pretty='%N' without an explicit --show-notes, git
    would segfault. This patches fixes this behaviour by loading the needed
    notes datastructures if --pretty is used and the format contains %N.
    When --pretty='%N' is used together with --no-notes, %N won't be
    expanded.

    This is an extension to a proposed patch by Jeff King.

    Signed-off-by: Johannes Gilger
    Signed-off-by: Junio C Hamano

git log -p 顯示每個提交引入的補丁

每個提交都是項目的一個快照。由于每個提交都記錄它所基于的快照,Git 能夠經(jīng)常對它們求差值,并以補丁形式向你展示。 這意味著,對任意提交,你都可以獲取該提交給項目引入補丁。 你可以用 git show [SHA] 加上某個特定的提交 SHA 獲取,或者執(zhí)行 git log -p, 它會告訴 Git 輸出每個提交之后的補丁。這是個總結(jié)某一分支或者兩個提交之間都發(fā)生了神馬的好途徑。

$ git log -p --no-merges -2commit 594f90bdee4faf063ad07a4a6f503fdead3ef606Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jun 4 15:46:55 2010 +0200

    reverted to old class namediff --git a/ruby.rb b/ruby.rb
index bb86f00..192151c 100644
--- a/ruby.rb
+++ b/ruby.rb@@ -1,7 +1,7 @@-class HiWorld+class HelloWorld
   def self.hello
     puts "Hello World from Ruby"
   end
 end-HiWorld.hello+HelloWorld.hellocommit 3cbb6aae5c0cbd711c098e113ae436801371c95eAuthor: Scott Chacon <schacon@gmail.com>
Date:   Fri Jun 4 12:58:53 2010 +0200

    fixed readme title differentlydiff --git a/README b/README
index d053cc8..9103e27 100644
--- a/README
+++ b/README@@ -1,4 +1,4 @@-Hello World Examples+Many Hello World Examples
 ======================

 This project has examples of hello world in

這是個總結(jié)改動,以及合并或發(fā)布之前重審一系列提交的好方式。

git log –stat 顯示每個提交引入的改動的差值統(tǒng)計

如果 -p 選項對你來說太詳細(xì)了,你可以用 --stat 總結(jié)這些改動。 這是不用 -p,而用 --stat 選項時,同一份日志的輸出。

$ git log --stat --no-merges -2commit 594f90bdee4faf063ad07a4a6f503fdead3ef606Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Jun 4 15:46:55 2010 +0200

    reverted to old class name

 ruby.rb |    4 ++--
 1 files changed, 2 insertions(+), 2 deletions(-)commit 3cbb6aae5c0cbd711c098e113ae436801371c95eAuthor: Scott Chacon <schacon@gmail.com>
Date:   Fri Jun 4 12:58:53 2010 +0200

    fixed readme title differently

 README |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

同樣的基本信息,但更緊湊 —— 它仍然讓你看到相對改動,和改動了哪些文件。

git diff

最后,要查看兩個提交快照的絕對改動,你可以用 git diff 命令。 這在兩個主要情況中廣為使用 —— 查看兩個分支彼此之間的差值,和查看自發(fā)布或者某個舊歷史點之后都有啥變了。讓我們看看這倆情況。

你僅需執(zhí)行 git diff [version](或者你給該發(fā)布打的任何標(biāo)簽)就可以查看自最近發(fā)布之后的改動。 例如,如果我們想要看看自 v0.9 發(fā)布之后我們的項目改變了啥,我們可以執(zhí)行 git diff v0.9

$ git diff v0.9diff --git a/README b/README
index d053cc8..d4173d5 100644
--- a/README
+++ b/README@@ -1,4 +1,4 @@-Hello World Examples+Many Hello World Lang Examples
 ======================

 This project has examples of hello world indiff --git a/ruby.rb b/ruby.rb
index bb86f00..192151c 100644
--- a/ruby.rb
+++ b/ruby.rb@@ -1,7 +1,7 @@-class HiWorld+class HelloWorld
   def self.hello
     puts "Hello World from Ruby"
   end
 end-HiWorld.hello+HelloWorld.hello

正如 git log,你可以給它加上 --stat 參數(shù)。

$ git diff v0.9 --stat
 README  |    2 +-
 ruby.rb |    4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

要比較兩個不同的分支,你可以執(zhí)行類似 git diff branchA branchB 的命令。 不過它的問題在于它會完完全全按你說的作 —— 它會直接給你個補丁文件,該補丁能夠?qū)⒓追种У淖钚驴煺兆兂梢曳种У淖钚驴煺盏臉幼印?這意味著如果兩個分支已經(jīng)產(chǎn)生分歧 —— 奔往兩個不同方向了 —— 它會移除甲分支中引入的所有工作,然后累加乙分支中的所有工作。 這大概不是你要的吧 —— 你想要不在甲分支中的乙分支的改動。所以你真的需要的是兩個分支叉開去時,和最新的乙分支的差別。 所以,如果我們的歷史記錄看起來像這樣:

$ git log --graph --oneline --decorate --all* 594f90b (HEAD, tag: v1.0, master) reverted to old class name
| * 1834130 (erlang) added haskell
| * ab5ab4c added erlang
|/
*   8d585ea Merge branch 'fix_readme'
...

并且,我們想要看“erlang”分支與主分支相比的查別。執(zhí)行 git diff master erlang 會給我們錯誤的結(jié)果。

$ git diff --stat master erlang
 erlang_hw.erl |    5 +++++
 haskell.hs    |    4 ++++
 ruby.rb       |    4 ++--
 3 files changed, 11 insertions(+), 2 deletions(-)

你可以看到,它加上了 erlang 和 haskell 文件,這確實是我們在該分支中做的, 但是它同時恢復(fù)了我們在主分支中改動的 ruby 文件。我們真心想要的只是“erlang”分支中的改動(添加兩個文件)。 我們可以通過求兩個分支分歧時的共同提交與該分支的差值得到想要的結(jié)果:

$ git diff --stat 8d585ea erlang
 erlang_hw.erl |    5 +++++
 haskell.hs    |    4 ++++
 2 files changed, 9 insertions(+), 0 deletions(-)

這才是我們在找的,但是我們可不想要每次都要找出兩個分支分歧時的那次提交。 幸運的是,Git 為此提供了一個快捷方式。 如果你執(zhí)行 git diff master...erlang(在分支名之間有三個半角的點), Git 就會自動找出兩個分支的共同提交(也被成為合并基礎(chǔ)),并求差值。

$ git diff --stat master erlang
 erlang_hw.erl |    5 +++++
 haskell.hs    |    4 ++++
 ruby.rb       |    4 ++--
 3 files changed, 11 insertions(+), 2 deletions(-)$ git diff --stat master...erlang
 erlang_hw.erl |    5 +++++
 haskell.hs    |    4 ++++
 2 files changed, 9 insertions(+), 0 deletions(-)

幾乎每一次你要對比兩個分支的時候,你都會想用三個點的語法,因為它通常會給你你想要的。

順帶提一句,你還可以讓 Git 手工計算兩次提交的合并基礎(chǔ)(第一個共同的祖提交),即 git merge-base 命令:

$ git merge-base master erlang8d585ea6faf99facd39b55d6f6a3b3f481ad0d3d

所以你執(zhí)行下面這個也跟 git diff master...erlang 一樣:

$ git diff --stat $(git merge-base master erlang) erlang
 erlang_hw.erl |    5 +++++
 haskell.hs    |    4 ++++
 2 files changed, 9 insertions(+), 0 deletions(-)

當(dāng)然,我會推薦簡單點的那個。

簡而言之 使用 git diff 查看某一分支自它偏離出來起與過去某一點之間項目的改動。 總是使用 git diff branchA...branchB 來查看 branchB 與 branchA 的相對差值,這會讓事情簡單點。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    蜜桃av人妻精品一区二区三区| 精品人妻一区二区三区免费| 欧美丰满大屁股一区二区三区| 国产女高清在线看免费观看| 中文久久乱码一区二区| 黄色国产一区二区三区| 国产一区二区三区丝袜不卡| 日韩黄片大全免费在线看| 久久碰国产一区二区三区| 色婷婷丁香激情五月天| 日本欧美三级中文字幕| 高清一区二区三区不卡免费| 国产欧美一区二区久久 | 欧美日韩国产免费看黄片| 99久久精品午夜一区二| 高清一区二区三区不卡免费| 亚洲精品美女三级完整版视频 | 亚洲欧美日韩网友自拍| 午夜激情视频一区二区| 欧美大粗爽一区二区三区| 日本女优一区二区三区免费| 国产一区二区三区草莓av| 午夜免费精品视频在线看| 日韩高清毛片免费观看| 亚洲视频一区二区久久久| 成人亚洲国产精品一区不卡 | 99国产一区在线播放| 国产成人亚洲综合色就色| 91亚洲精品国产一区| 精品欧美日韩一区二区三区| 好吊视频有精品永久免费| 亚洲av成人一区二区三区在线| 国产精品一区二区三区欧美 | 丰满人妻少妇精品一区二区三区| 中国美女偷拍福利视频| 国产一区麻豆水好多高潮| 亚洲一区二区三区免费的视频| 日本精品中文字幕在线视频| 91蜜臀精品一区二区三区| 国产精品视频一区二区秋霞| 国产成人精品国产成人亚洲|