《GitHub入門與實戰(zhàn)》第四章

第四章 通過實際操作學(xué)習(xí)Git

4.1 基本操作

git init——初始化倉庫

??要使用 Git 進(jìn)行版本管理却桶,必須先初始化倉庫蔗牡。Git 是使用 git init命令進(jìn)行初始化的嗅剖。請實際建立一個目錄并初始化倉庫。

$ mkdir git-tutorial
$ cd git-tutorial
$ git init
Initialized empty Git repository in /Users/hirocaster/github/github-book
/git-tutorial/.git/

??如果初始化成功黔攒,執(zhí)行了 git init命令的目錄下就會生成 .git 目錄强缘。這個 .git 目錄里存儲著管理當(dāng)前目錄內(nèi)容所需的倉庫數(shù)據(jù)。
??在 Git 中姑丑,我們將這個目錄的內(nèi)容稱為“附屬于該倉庫的工作樹”辞友。文件的編輯等操作在工作樹中進(jìn)行,然后記錄到倉庫中留拾,以此管理文件的歷史快照。如果想將文件恢復(fù)到原先的狀態(tài)痴柔,可以從倉庫中調(diào)取之前的快照疫向,在工作樹中打開。開發(fā)者可以通過這種方式獲取以往的文件谈火。

git status——查看倉庫的狀態(tài)

??git status命令用于顯示 Git 倉庫的狀態(tài)舌涨。這是一個十分常用的命令,請務(wù)必牢記温技。
??工作樹和倉庫在被操作的過程中扭粱,狀態(tài)會不斷發(fā)生變化。

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

??結(jié)果顯示了我們當(dāng)前正處于 master 分支下焊刹。接著還顯示了沒有可提交的內(nèi)容恳蹲。所謂提交(Commit)嘉蕾,是指“記錄工作樹中所有文件的當(dāng)前狀態(tài)”霜旧。
??現(xiàn)在尚沒有可提交的內(nèi)容,就是說當(dāng)前我們建立的這個倉庫中還沒有記錄任何文件的任何狀態(tài)以清。這里崎逃,我們建立 README.md 文件作為管理對象,為第一次提交做前期準(zhǔn)備个绍。

$ touch README.md
$ git status
# On branch master
#
# Initial commit
## Untracked files:#   (use "git add <file>..." to include in what will
be committed)#
#       README.md
nothing added to commit but untracked files present (use "git add" to track)

??將 README . md 文件加入暫存區(qū)后,git status命令的顯示結(jié)果發(fā)生了變化凛虽」慊郑可以看到,README . md 文件顯示在 Changes to be committed 中了至非。

git commit——保存?zhèn)}庫的歷史記錄

??git commit命令可以將當(dāng)前暫存區(qū)中的文件實際保存到倉庫的歷史記錄中篷牌。通過這些記錄踏幻,我們就可以在工作樹中復(fù)原文件。

記述一行提交信息

$ git commit -m "First commit"
[master (root-commit) 9f129ba] First commit
  1 file changed, 0 insertions(+), 0 deletions(-)
  create mode 100644 README.md

-m 參數(shù)后的 "First commit"稱作提交信息该面,是對這個提交的概述。

記述詳細(xì)提交信息

??剛才只簡潔地記述了一行提交信息题造,如果想要記述得更加詳細(xì)猾瘸,請不加 -m丢习,直接執(zhí)行 git commit 命令咐低。執(zhí)行后編輯器就會啟動,并顯示如下結(jié)果见擦。

# 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:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   README.md

在編輯器中記述提交信息的格式如下:

  • 第一行:用一行文字簡述提交的更改內(nèi)容
  • 第二行:空行
  • 第三行以后:記述更改的原因和詳細(xì)內(nèi)容

??只要按照上面的格式輸入羹令,今后便可以通過確認(rèn)日志的命令或工具看到這些記錄。
??在以 #(井號)標(biāo)為注釋的 Changes to be committed(要提交的更改)欄中酒来,可以查看本次提交中包含的文件肪凛。將提交信息按格式記述完畢后,請保存并關(guān)閉編輯器衡奥,以 #(井號)標(biāo)為注釋的行不必刪除远荠。隨后,剛才記述的提交信息就會被提交譬淳。

中止提交

??如果在編輯器啟動后想中止提交,請將提交信息留空并直接關(guān)閉編輯器守伸,隨后提交就會被中止浦妄。

查看提交后的狀態(tài)

??執(zhí)行完 git commit命令后再來查看當(dāng)前狀態(tài)。

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

??當(dāng)前工作樹處于剛剛完成提交的最新狀態(tài)蠢涝,所以結(jié)果顯示沒有更改阅懦。

git log——查看提交日志

??git log命令可以查看以往倉庫中提交的日志。包括可以查看什么人在什么時候進(jìn)行了提交或合并耳胎,以及操作前后有怎樣的差別惕它。

$ git log
     
commit 9f129bae19b2c82fb4e98cde5890e52a6c546922
Author: hirocaster <hohtsuka@gmail.com>
Date:   Sun May 5 16:06:49 2013 +0900
     
    First commit

只顯示提交信息的第一行

??如果只想讓程序顯示第一行簡述信息怠缸,可以在 git log命令后加上 --pretty=short钳宪。這樣一來開發(fā)人員就能夠更輕松地把握多個提交。

$ git log --pretty=short
         
commit 9f129bae19b2c82fb4e98cde5890e52a6c546922
Author: hirocaster <hohtsuka@gmail.com>
         
    First commit

只顯示指定目錄搔体、文件的日志

??只要在 git log命令后加上目錄名半醉,便會只顯示該目錄下的日志。如果加的是文件名缩多,就會只顯示與該文件相關(guān)的日志。

git log README.md

顯示文件的改動

如果想查看提交所帶來的改動梁钾,可以加上 -p參數(shù)逊抡,文件的前后差別就會顯示在提交信息之后。

git log -p

??執(zhí)行下面的命令冒嫡,就可以只查看 README.md 文件的提交日志以及提交前后的差別。

git log -p README.md

git diff——查看更改前后的差別

??git diff命令可以查看工作樹方咆、暫存區(qū)蟀架、最新提交之間的差別。

查看工作樹和暫存區(qū)的差別

??執(zhí)行 git diff 命令钩述,查看當(dāng)前工作樹與暫存區(qū)的差別。

$ git diff
         
diff --git a/README.md b/README.md
index e69de29..cb5dc9f 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# Git教程

??由于我們尚未用 git add 命令向暫存區(qū)添加任何東西,所以程序只會顯示工作樹與最新提交狀態(tài)之間的差別职恳。
??“+”號標(biāo)出的是新添加的行方面,被刪除的行則用“-”號標(biāo)出恭金。我們可以看到褂策,這次只添加了一行。

查看工作樹和最新提交的差別

??如果現(xiàn)在執(zhí)行 git diff 命令耿焊,由于工作樹和暫存區(qū)的狀態(tài)并無差別,結(jié)果什么都不會顯示罗侯。要查看與最新提交的差別溪猿,請執(zhí)行以下命令。

$ git diff HEAD
diff --git a/README.md b/README.md
index e69de29..cb5dc9f 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1 @@
+# Git教程

??不妨養(yǎng)成這樣一個好習(xí)慣:在執(zhí)行 git commit 命令之前先執(zhí)行 git diff HEAD 命令讲弄,查看本次提交與上次提交之間有什么差別依痊,等確認(rèn)完畢后再進(jìn)行提交。這里的 HEAD 是指向當(dāng)前分支中最新一次提交的指針抗悍。

4.2 分支的操作

??在進(jìn)行多個并行作業(yè)時,我們會用到分支赏壹。在這類并行開發(fā)的過程中衔沼,往往同時存在多個最新代碼狀態(tài)。從 master 分支創(chuàng)建 feature-A 分支和 fix-B 分支后菩佑,每個分支中都擁有自己的最新代碼。master 分支是 Git 默認(rèn)創(chuàng)建的分支稍坯,因此基本上所有開發(fā)都是以這個分支為中心進(jìn)行的。

從 master 分支創(chuàng)建 feature-A 分支和 fix-B 分支

??不同分支中混巧,可以同時進(jìn)行完全不同的作業(yè)勤揩。等該分支的作業(yè)完成之后再與 master 分支合并。通過靈活運(yùn)用分支傍衡,可以讓多人同時高效地進(jìn)行并行開發(fā)负蠕。


分支作業(yè)結(jié)束后的狀態(tài)

git branch——顯示分支一覽表

??git branch命令可以將分支名列表顯示,同時可以確認(rèn)當(dāng)前所在分支箱残。

$ git branch
* master

??可以看到 master 分支左側(cè)標(biāo)有“*”(星號)止吁,表示這是我們當(dāng)前所在的分支。也就是說敬惦,我們正在 master 分支下進(jìn)行開發(fā)。結(jié)果中沒有顯示其他分支名宏怔,表示本地倉庫中只存在 master 一個分支畴椰。

git checkout -b——創(chuàng)建、切換分支

??如果想以當(dāng)前的 master 分支為基礎(chǔ)創(chuàng)建新的分支斜脂,我們需要用到 git checkout -b命令。

切換到 feature-A 分支并進(jìn)行提交

??執(zhí)行下面的命令玷或,創(chuàng)建名為 feature-A 的分支片任。

$ git checkout -b feature-A
Switched to a new branch 'feature-A'

??連續(xù)執(zhí)行下面兩條命令也能收到同樣效果。

git branch feature-A
git checkout feature-A

??創(chuàng)建 feature-A 分支位他,并將當(dāng)前分支切換為 feature-A 分支。這時再來查看分支列表棱诱,會顯示我們處于 feature-A 分支下涝动。

$ git branch
* feature-A
  master

??feature-A 分支左側(cè)標(biāo)有 “ * ” ,表示當(dāng)前分支為 feature-A靡菇。在這個狀態(tài)下像正常開發(fā)那樣修改代碼、執(zhí)行 git add 命令并進(jìn)行提交的話厦凤,代碼就會提交至 feature-A 分支育苟。像這樣不斷對一個分支(例如 feature-A)進(jìn)行提交的操作,我們稱為“培育分支”博烂。

??我們在README.md中添加了 feature-A 這樣一行字母漱竖,然后進(jìn)行提交。

$ git add README.md
$ git commit -m "Add feature-A"
[feature-A 8a6c8b9] Add feature-A
 1 file changed, 2 insertions(+)

??于是馍惹,這一行就添加到 feature-A 分支中了。

切換到 master 分支

??現(xiàn)在我們再來看一看 master 分支有沒有受到影響悼吱。首先切換至 master 分支良狈。

$ git checkout master
Switched to branch 'master'

??然后查看 README . md 文件,會發(fā)現(xiàn) README . md 文件仍然保持原先的狀態(tài)们颜,并沒有被添加文字。feature-A 分支的更改不會影響到 master 分支努溃,這正是在開發(fā)中創(chuàng)建分支的優(yōu)點阻问。只要創(chuàng)建多個分支,就可以在不互相影響的情況下同時進(jìn)行多個功能的開發(fā)第队。

切換回上一個分支

??現(xiàn)在,我們再切換回 feature-A 分支凳谦。

$ git checkout -
Switched to branch 'feature-A'

??像上面這樣用“-”(連字符)代替分支名,就可以切換至上一個分支家凯。當(dāng)然如失,將“-”替換成 feature-A 同樣可以切換到 feature-A 分支。

特性分支

??Git 與 Subversion(SVN)等集中型版本管理系統(tǒng)不同褪贵,創(chuàng)建分支時不需要連接中央倉庫,所以能夠相對輕松地創(chuàng)建分支世舰。因此偎快,當(dāng)今大部分工作流程中都用到了特性(Topic)分支。

??特性分支顧名思義晒夹,是集中實現(xiàn)單一特性(主題),除此之外不進(jìn)行任何作業(yè)的分支喷好。在日常開發(fā)中读跷,往往會創(chuàng)建數(shù)個特性分支,同時在此之外再保留一個隨時可以發(fā)布軟件的穩(wěn)定分支无切。穩(wěn)定分支的角色通常由 master 分支擔(dān)當(dāng)。

特性分支

??之前我們創(chuàng)建了 feature-A 分支哆键,這一分支主要實現(xiàn) feature-A瘦锹,除 feature-A 的實現(xiàn)之外不進(jìn)行任何作業(yè)闪盔。即便在開發(fā)過程中發(fā)現(xiàn)了 BUG泪掀,也需要再創(chuàng)建新的分支颂碘,在新分支中進(jìn)行修正。
??基于特定主題的作業(yè)在特性分支中進(jìn)行凭涂,主題完成后再與 master 分支合并贴妻。只要保持這樣一個開發(fā)流程,就能保證 master 分支可以隨時供人查看澎胡。這樣一來,其他開發(fā)者也可以放心大膽地從 master 分支創(chuàng)建新的特性分支攻谁。

主干分支

??主干分支是剛才講解的特性分支的原點弯予,同時也是合并的終點。通常人們會用 master 分支作為主干分支受楼。主干分支中并沒有開發(fā)到一半的代碼,可以隨時供他人查看艳汽。
??有時我們需要讓這個主干分支總是配置在正式環(huán)境中对雪,有時又需要用標(biāo)簽 Tag 等創(chuàng)建版本信息,同時管理多個版本發(fā)布馋艺。擁有多個版本發(fā)布時,主干分支也有多個捐祠。

git merge——合并分支

??接下來交汤,假設(shè) feature-A 已經(jīng)實現(xiàn)完畢劫笙,想要將它合并到主干分支 master 中填大。首先切換到 master 分支。

$ git checkout master
Switched to branch 'master'

??然后合并 feature-A 分支允华。為了在歷史記錄中明確記錄下本次分支合并寥掐,我們需要創(chuàng)建合并提交。因此百炬,在合并時加上 --no-ff參數(shù)。

git merge --no-ff feature-A

??隨后編輯器會啟動剖踊,用于錄入合并提交的信息衫贬。

Merge branch 'feature-A'
     
# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message aborts
# the commit.

??默認(rèn)信息中已經(jīng)包含了是從 feature-A 分支合并過來的相關(guān)內(nèi)容,所以可不必做任何更改梆造。將編輯器中顯示的內(nèi)容保存葬毫,關(guān)閉編輯器,然后就會看到下面的結(jié)果供常。

Merge made by the 'recursive' strategy.
 README.md | 2 ++
 1 file changed, 2 insertions(+)

??這樣一來,feature-A 分支的內(nèi)容就合并到 master 分支中了麻裁。

git log --graph——以圖表形式查看分支

??用 git log --graph 命令進(jìn)行查看的話源祈,能很清楚地看到特性分支(feature-A)提交的內(nèi)容已被合并。除此以外香缺,特性分支的創(chuàng)建以及合并也都清楚明了。git log --graph 命令可以用圖表形式輸出提交日志锋拖,非常直觀。

4.3 更改提交的操作

git reset——回溯歷史版本

??Git 的另一特征便是可以靈活操作歷史版本兽埃。借助分散倉庫的優(yōu)勢,可以在不影響其他倉庫的前提下對歷史版本進(jìn)行操作柄错。我們先回溯歷史版本,創(chuàng)建一個名為 fix-B 的特性分支给猾。


回溯歷史颂跨,創(chuàng)建 fix-B 分支

回溯到創(chuàng)建 feature-A 分支前

要讓倉庫的 HEAD、暫存區(qū)毫捣、當(dāng)前工作樹回溯到指定狀態(tài),需要用到 git reset --hard命令。只要提供目標(biāo)時間點的哈希值 蹲诀,就可以完全恢復(fù)至該時間點的狀態(tài)。

哈希值在每個環(huán)境中各不相同则北,讀者請查看自身當(dāng)前環(huán)境中 Add index 的哈希值,進(jìn)行替換尚揣。

$ git reset --hard fd0cbf0d4a25f747230694d95cac1be72d33441d
HEAD is now at fd0cbf0 Add index

??我們已經(jīng)成功回溯到特性分支(feature-A)創(chuàng)建之前的狀態(tài)掖举。由于所有文件都回溯到了指定哈希值對應(yīng)的時間點上,README . md 文件的內(nèi)容也恢復(fù)到了當(dāng)時的狀態(tài)方篮。

創(chuàng)建 fix-B 分支

  • 創(chuàng)建特性分支(fix-B)
$ git checkout -b fix-B
Switched to a new branch 'fix-B'
  • 作為這個主題的作業(yè)內(nèi)容
# Git教程
         
  - fix-B
  • 然后直接提交 README . md 文件
$ git add README.md
         
$ git commit -m "Fix B"
[fix-B 4096d9e] Fix B
 1 file changed, 2 insertions(+)
  • 現(xiàn)在的狀態(tài)如圖所示


    當(dāng)前 fix-B 分支的狀態(tài)

推進(jìn)至 feature-A 分支合并后的狀態(tài)

??首先恢復(fù)到 feature-A 分支合并后的狀態(tài)励负。這一操作為 “ 推進(jìn)歷史 ” 。git log 命令只能查看以當(dāng)前狀態(tài)為終點的歷史日志巾表。所以這里要使用 git reflog 命令汁掠,查看當(dāng)前倉庫的操作日志调塌。在日志中找出回溯歷史之前的哈希值惠猿,通過 git reset --hard 命令恢復(fù)到回溯歷史前的狀態(tài)。

  • 首先執(zhí)行 git reflog 命令偶妖,查看當(dāng)前倉庫執(zhí)行過的操作的日志
$ git reflog
4096d9e HEAD@{0}: commit: Fix B
fd0cbf0 HEAD@{1}: checkout: moving from master to fix-B
fd0cbf0 HEAD@{2}: reset: moving to fd0cbf0d4a25f747230694d95cac1be72d33441d
83b0b94 HEAD@{3}: merge feature-A: Merge made by the 'recursive' strategy.
fd0cbf0 HEAD@{4}: checkout: moving from feature-A to master
8a6c8b9 HEAD@{5}: checkout: moving from master to feature-A
fd0cbf0 HEAD@{6}: checkout: moving from feature-A to master
8a6c8b9 HEAD@{7}: commit: Add feature-A
fd0cbf0 HEAD@{8}: checkout: moving from master to feature-A
fd0cbf0 HEAD@{9}: commit: Add index
9f129ba HEAD@{10}: commit (initial): First commit

??在日志中,我們可以看到 commit态秧、checkout扼鞋、resetmerge 等 Git 命令的執(zhí)行記錄捐友。只要不進(jìn)行 Git 的 GC(Garbage Collection,垃圾回收)匣砖,就可以通過日志隨意調(diào)取近期的歷史狀態(tài)昏滴,就像給時間機(jī)器指定一個時間點,在過去未來中自由穿梭一般谣殊。即便開發(fā)者錯誤執(zhí)行了 Git 操作,基本也都可以利用 git reflog 命令恢復(fù)到原先的狀態(tài)宜狐,所以請務(wù)必牢記本部分。
??從上面數(shù)第四行表示 feature-A 特性分支合并后的狀態(tài)肌厨,對應(yīng)哈希值為 83b0b94豁陆。我們將 HEAD、暫存區(qū)盒音、工作樹恢復(fù)到這個時間點的狀態(tài)馅而。

哈希值只要輸入 4 位以上就可以執(zhí)行瓮恭。

$ git checkout master
         
$ git reset --hard 83b0b94
HEAD is now at 83b0b94 Merge branch 'feature-A'

??之前我們使用 git reset --hard 命令回溯了歷史厘熟,這里又再次通過它恢復(fù)到了回溯前的歷史狀態(tài)。

恢復(fù)歷史后的狀態(tài)

消除沖突

??現(xiàn)在只要合并 fix-B 分支绳姨,就可以得到我們想要的狀態(tài)。

$ git merge --no-ff fix-B
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Recorded preimage for 'README.md'
Automatic merge failed; fix conflicts and then commit the result.

??這時脑蠕,系統(tǒng)告訴我們 README . md 文件發(fā)生了沖突(Conflict)跪削。系統(tǒng)在合并 README . md 文件時,feature-A 分支更改的部分與本次想要合并的 fix-B 分支更改的部分發(fā)生了沖突碾盐。不解決沖突就無法完成合并,所以我們打開 README . md 文件哼审,解決這個沖突孕豹。

查看沖突部分并將其解決

  • 用編輯器打開 README.md 文件十气,查看
# Git教程
         
< < < < < < <  HEAD
  - feature-A
=======
  - fix-B
> > > > > > >  fix-B

??======= 以上的部分是當(dāng)前 HEAD 的內(nèi)容,以下的部分是要合并的 fix-B 分支中的內(nèi)容叶眉。我們在編輯器中將其改成想要的樣子芹枷。

# Git教程
         
  - feature-A
  - fix-B

??本次修正讓 feature-A 與 fix-B 的內(nèi)容并存于文件之中。但是在實際的軟件開發(fā)中鸳慈,往往需要刪除其中之一,所以在處理沖突時绩郎,務(wù)必要仔細(xì)分析沖突部分的內(nèi)容后再行修改潘鲫。

提交解決后的結(jié)果

  • 沖突解決后溉仑,執(zhí)行 git add 命令與 git commit 命令
$ git add README.md
         
$ git commit -m "Fix conflict"
Recorded resolution for 'README.md'.
[master 6a97e48] Fix conflict

??由于本次更改解決了沖突,所以提交信息記為 "Fix conflict"浊竟。

git commit --amend——修改提交信息

??要修改上一條提交信息津畸,可以使用 git commit --amend命令。將上一條提交信息記為了 " Fix conflict "洼畅,但它其實是 fix-B 分支的合并,解決合并時發(fā)生的沖突只是過程之一徘郭,這樣標(biāo)記實在不妥丧肴。于是,要修改這條提交信息芋浮。

git commit --amend

執(zhí)行命令后,編輯器就會啟動镇草。

Fix conflict
     
# 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^1 <file>..." to unstage)
#
#       modified:   README.md
#

??編輯器中顯示的內(nèi)容:包含之前的提交信息。請將提交信息的部分修改為 Merge branch 'fix-B'梯啤,然后保存文件存哲,關(guān)閉編輯器。

[master 2e7db6f] Merge branch 'fix-B'

??現(xiàn)在執(zhí)行 git log --graph 命令察滑,可以看到提交日志中的相應(yīng)內(nèi)容也已經(jīng)被修改。

git rebase -i——壓縮歷史

??在合并特性分支之前贺辰,如果發(fā)現(xiàn)已提交的內(nèi)容中有些許拼寫錯誤等,不妨提交一個修改先舷,然后將這個修改包含到前一個提交之中,壓縮成一個歷史記錄蒋川。這是個會經(jīng)常用到的技巧撩笆。

創(chuàng)建 feature-C 分支

  • 新建一個 feature-C 特性分支
$ git checkout -b feature-C
Switched to a new branch 'feature-C'

??作為 feature-C 的功能實現(xiàn),在 README . md 文件中添加一行文字夕冲,并且故意留下拼寫錯誤,以便之后修正泣栈。

# Git教程

  - feature-A
  - fix-B
  - faeture-C

??提交這部分內(nèi)容弥姻。這個小小的變更就沒必要先執(zhí)行 git add 命令再執(zhí)行 git commit 命令了,我們用 git commit -am 命令來一次完成這兩步操作庭敦。

$ git commit -am "Add feature-C"
[feature-C 7a34294] Add feature-C
 1 file changed, 1 insertion(+)

修正拼寫錯誤

  • 修正剛才預(yù)留的拼寫錯誤
$ git diff
diff --git a/README.md b/README.md
index ad19aba..af647fd 100644
--- a/README.md
+++ b/README.md
@@ -2,4 +2,4 @@
         
   - feature-A
   - fix-B
-  - faeture-C
+  - feature-C
  • 然后進(jìn)行提交
$ git commit -am "Fix typo"
[feature-C 6fba227] Fix typo
 1 file changed, 1 insertion(+), 1 deletion(-)

??錯字漏字等失誤稱作 typo,所以我們將提交信息記為 " Fix typo"伞广。實際上疼电,我們不希望在歷史記錄中看到這類提交,因為健全的歷史蔽豺。如果能在最初提交之前就發(fā)現(xiàn)并修正這些錯誤,也就不會出現(xiàn)這類提交了茫虽。

更改歷史

??將 " Fix typo " 修正的內(nèi)容與之前一次的提交合并濒析,在歷史記錄中合并為一次完美的提交啥纸。為此,我們要用到 git rebase 命令。

git rebase -i HEAD~2

??用上述方式執(zhí)行 git rebase 命令主经,可以選定當(dāng)前分支中包含 HEAD(最新提交)在內(nèi)的兩個最新歷史記錄為對象庭惜,并在編輯器中打開。

pick 7a34294 Add feature-C
pick 6fba227 Fix typo

# Rebase 2e7db6f..6fba227 onto 2e7db6f
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit
#  f, fixup = like "squash", but discard this commit's log message
#  x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out

??將 6fba227 的 Fix typo 的歷史記錄壓縮到 7a34294 的 Add feature-C 里惠遏。按照下圖所示,將 6fba227 左側(cè)的 pick 部分刪除节吮,改寫為 fixup判耕。

pick 7a34294 Add feature-C
fixup 6fba227 Fix typo

保存編輯器里的內(nèi)容,關(guān)閉編輯器壁熄。

[detached HEAD 51440c5] Add feature-C
 1 file changed, 1 insertion(+)
Successfully rebased and updated refs/heads/feature-C.

??系統(tǒng)顯示 rebase 成功。也就是以下面這兩個提交作為對象志鞍,將 "Fix typo"的內(nèi)容合并到了上一個提交 "Add feature-C"中,改寫成了一個新的提交」膛铮現(xiàn)在再查看提交日志時會發(fā)現(xiàn) Add feature-C 的哈希值已經(jīng)不是 7a34294 了仙蚜,這證明提交已經(jīng)被更改。
??這樣一來委粉,F(xiàn)ix typo 就從歷史中被抹去,也就相當(dāng)于 Add feature-C 中從來沒有出現(xiàn)過拼寫錯誤汁汗。這算是一種良性的歷史改寫。

合并至 master 分支

$ git checkout master
Switched to branch 'master'
         
$ git merge --no-ff feature-C
Merge made by the 'recursive' strategy.
 README.md | 1 +
 1 file changed, 1 insertion(+)

4.4 推送至遠(yuǎn)程倉庫

??Git 是分散型版本管理系統(tǒng)知牌,但前面所學(xué)習(xí)的斤程,都是針對單一本地倉庫的操作。下面,將開始接觸遠(yuǎn)在網(wǎng)絡(luò)另一頭的遠(yuǎn)程倉庫沮峡。遠(yuǎn)程倉庫顧名思義亿柑,是與本地倉庫相對獨立的另一個倉庫。先在 GitHub 上創(chuàng)建一個倉庫橄杨,并將其設(shè)置為本地倉庫的遠(yuǎn)程倉庫。
??參考第 3 章的 3.2 節(jié)在 GitHub 上新建一個倉庫式矫。為防止與其他倉庫混淆,倉庫名請與本地倉庫保持一致聪廉,即 git-tutorial。創(chuàng)建時請不要勾選 Initialize this repository with a README 選項故慈。因為一旦勾選該選項,GitHub 一側(cè)的倉庫就會自動生成 README 文件干签,從創(chuàng)建之初便與本地倉庫失去了整合性。雖然到時也可以強(qiáng)制覆蓋容劳,但為防止這一情況發(fā)生還是建議不要勾選該選項闸度,直接點擊 Create repository 創(chuàng)建倉庫。

git remote add——添加遠(yuǎn)程倉庫

??在 GitHub 上創(chuàng)建的倉庫路徑為“git@github.com:用戶名/git-tutorial.git”≥航現(xiàn)在用 git remote add命令將它設(shè)置成本地倉庫的遠(yuǎn)程倉庫。

本節(jié)講解中使用的用戶名為 github-book楼熄,讀者請根據(jù)自身環(huán)境予以替換。

git remote add origin git@github.com:github-book/git-tutorial.git

??按照上述格式執(zhí)行 git remote add 命令之后孝赫,Git 會自動將 git@github.com:github-book/git-tutorial.git遠(yuǎn)程倉庫的名稱設(shè)置為 origin(標(biāo)識符)红符。

git push——推送至遠(yuǎn)程倉庫

推送至 master 分支

??如果想將當(dāng)前分支下本地倉庫中的內(nèi)容推送給遠(yuǎn)程倉庫预侯,需要用到 git push 命令。現(xiàn)在假定在 master 分支下進(jìn)行操作萎馅。

$ git push -u origin master
Counting objects: 20, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (10/10), done.
Writing objects: 100% (20/20), 1.60 KiB, done.
Total 20 (delta 3), reused 0 (delta 0)
To git@github.com:github-book/git-tutorial.git
 * [new branch]      master -> master
Branch master set up to track remote branch master from origin.

??像這樣執(zhí)行 git push命令,當(dāng)前分支的內(nèi)容就會被推送給遠(yuǎn)程倉庫 origin 的 master 分支飒货。-u參數(shù)可以在推送的同時,將 origin 倉庫的 master 分支設(shè)置為本地倉庫當(dāng)前分支的 upstream(上游)塘辅。添加了這個參數(shù)皆撩,將來運(yùn)行 git pull命令從遠(yuǎn)程倉庫獲取內(nèi)容時,本地倉庫的這個分支就可以直接從 origin 的 master 分支獲取內(nèi)容扛吞,省去了另外添加參數(shù)的麻煩。
??執(zhí)行該操作后滥比,當(dāng)前本地倉庫 master 分支的內(nèi)容將會被推送到 GitHub 的遠(yuǎn)程倉庫中。在 GitHub 上也可以確認(rèn)遠(yuǎn)程 master 分支的內(nèi)容和本地 master 分支相同濒持。

推送至 master 以外的分支

??除了 master 分支之外,遠(yuǎn)程倉庫也可以創(chuàng)建其他分支弥喉。

$ git checkout -b feature-D
Switched to a new branch 'feature-D'

??在本地倉庫中創(chuàng)建了 feature-D 分支玛迄,現(xiàn)在將它 push 給遠(yuǎn)程倉庫并保持分支名稱不變。

$ git push -u origin feature-D
Total 0 (delta 0), reused 0 (delta 0)
To git@github.com:github-book/git-tutorial.git
 * [new branch]      feature-D -> feature-D
Branch feature-D set up to track remote branch feature-D from origin.

4.5 從遠(yuǎn)程倉庫獲取

??上一節(jié)把在 GitHub 上新建的倉庫設(shè)置成了遠(yuǎn)程倉庫蓖议,并向這個倉庫 push 了 feature-D 分支。現(xiàn)在纺阔,所有能夠訪問這個遠(yuǎn)程倉庫的人都可以獲取 feature-D 分支并加以修改。
??本節(jié)從實際開發(fā)者的角度出發(fā)笛钝,在另一個目錄下新建一個本地倉庫,學(xué)習(xí)從遠(yuǎn)程倉庫獲取內(nèi)容的相關(guān)操作玻靡。這就相當(dāng)于剛剛執(zhí)行過 push 操作的目標(biāo)倉庫又有了另一名新開發(fā)者來共同開發(fā)。

git clone——獲取遠(yuǎn)程倉庫

獲取遠(yuǎn)程倉庫

??首先換到其他目錄下囤捻,將 GitHub 上的倉庫 clone 到本地。注意不要與之前操作的倉庫在同一目錄下蝎土。

$ git clone git@github.com:github-book/git-tutorial.git
Cloning into 'git-tutorial'...
remote: Counting objects: 20, done.
remote: Compressing objects: 100% (7/7), done.
remote: Total 20 (delta 3), reused 20 (delta 3)
Receiving objects: 100% (20/20), done.
Resolving deltas: 100% (3/3), done.
$ cd git-tutorial

??執(zhí)行 git clone 命令后我們會默認(rèn)處于 master 分支下,同時系統(tǒng)會自動將 origin 設(shè)置成該遠(yuǎn)程倉庫的標(biāo)識符挡毅。也就是說醋拧,當(dāng)前本地倉庫的 master 分支與 GitHub 端遠(yuǎn)程倉庫(origin)的 master 分支在內(nèi)容上是完全相同的。

$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/feature-D
  remotes/origin/master

??用 git branch -a命令查看當(dāng)前分支的相關(guān)信息丹壕。添加 -a 參數(shù)可以同時顯示本地倉庫和遠(yuǎn)程倉庫的分支信息。
??結(jié)果中顯示了 remotes/origin/feature-D菌赖,證明遠(yuǎn)程倉庫中已經(jīng)有了 feature-D 分支。

獲取遠(yuǎn)程的 feature-D 分支

  • 將 feature-D 分支獲取至本地倉庫
$ git checkout -b feature-D origin/feature-D
Branch feature-D set up to track remote branch feature-D from origin.
Switched to a new branch 'feature-D'

??-b 參數(shù)的后面是本地倉庫中新建分支的名稱堕绩。為了便于理解邑时,仍將其命名為 feature-D,讓它與遠(yuǎn)程倉庫的對應(yīng)分支保持同名晶丘。新建分支名稱后面是獲取來源的分支名稱。例子中指定了 origin/feature-D浅浮,就是說以名為 origin 的倉庫(這里指 GitHub 端的倉庫)的 feature-D 分支為來源,在本地倉庫中創(chuàng)建 feature-D 分支专执。

向本地的 feature-D 分支提交更改

??現(xiàn)在假定我們是另一名開發(fā)者,要做一個新的提交本股。在 README . md 文件中添加一行文字,查看更改痊末。

$ git diff
diff --git a/README.md b/README.md
index af647fd..30378c9 100644
--- a/README.md
+++ b/README.md
@@ -3,3 +3,4 @@
   - feature-A
   - fix-B
   - feature-C
+  - feature-D

??按照之前學(xué)過的方式提交即可。

$ git commit -am "Add feature-D"
[feature-D ed9721e] Add feature-D
 1 file changed, 1 insertion(+)

推送 feature-D 分支

  • 推送 feature-D 分支
$ git push
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 281 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To git@github.com:github-book/git-tutorial.git
   ca0f98b..ed9721e feature-D -> feature-D

??從遠(yuǎn)程倉庫獲取 feature-D 分支凿叠,在本地倉庫中提交更改盒件,再將 feature-D 分支推送回遠(yuǎn)程倉庫,通過這一系列操作炒刁,就可以與其他開發(fā)者相互合作,共同培育 feature-D 分支翔始,實現(xiàn)某些功能里伯。

git pull——獲取最新的遠(yuǎn)程倉庫分支

??回到原先的那個目錄下。這邊的本地倉庫中只創(chuàng)建了 feature-D 分支疾瓮,并沒有在 feature-D 分支中進(jìn)行任何提交。然而遠(yuǎn)程倉庫的 feature-D 分支中已經(jīng)有了剛剛推送的提交蜒灰。這時我們就可以使用 git pull 命令,將本地的 feature-D 分支更新到最新狀態(tài)强窖。當(dāng)前分支為 feature-D 分支。

$ git pull origin feature-D
remote: Counting objects: 5, done.
remote: Compressing objects: 100% (1/1), done.
remote: Total 3 (delta 1), reused 3 (delta 1)
Unpacking objects: 100% (3/3), done.
From github.com:github-book/git-tutorial
 * branch            feature-D -> FETCH_HEAD
 First, rewinding head to replay your work on top of it...
 Fast-forwarded feature-D to ed9721e686f8c588e55ec6b8071b669f411486b8.

??GitHub 端遠(yuǎn)程倉庫中的 feature-D 分支是最新狀態(tài)翅溺,所以本地倉庫中的 feature-D 分支就得到了更新岩瘦。今后只需要像平常一樣在本地進(jìn)行提交再 push 給遠(yuǎn)程倉庫,就可以與其他開發(fā)者同時在同一個分支中進(jìn)行作業(yè)启昧,不斷給 feature-D 增加新功能。
??如果兩人同時修改了同一部分的源代碼握爷,push 時就很容易發(fā)生沖突跛璧。所以多名開發(fā)者在同一個分支中進(jìn)行作業(yè)時追城,為減少沖突情況的發(fā)生,建議更頻繁地進(jìn)行 push 和 pull 操作座柱。

4.6 幫助大家深入理解 Git 的資料

??至此為止,閱讀并理解所必需的 Git 操作已經(jīng)全部講解完了色洞。但是在實際的開發(fā)現(xiàn)場冠胯,往往要用到更加高級的 Git 操作。這里介紹一些參考資料荠察,能夠幫助各位深入理解 Git 的相關(guān)知識。

Pro Git

??由就職于 GitHub 公司的 Scott Chacon 執(zhí)筆悉盆,是一部零基礎(chǔ)的 Git 學(xué)習(xí)資料⊥⒀牛基于知識共享的 CC BY-NC-SA 3.0 許可協(xié)議,各位可以免費(fèi)閱讀到包括簡體中文在內(nèi)的各國語言版本航缀。

Pro Git:http://git-scm.com/book/zh/v1
Scott Chacon:https://github.com/schacon

LearnGitBranching

??是學(xué)習(xí) Git 基本操作的網(wǎng)站堰怨。注重樹形結(jié)構(gòu)的學(xué)習(xí)方式非常適合初學(xué)者使用,點擊右下角的地球標(biāo)志還可切換各種語言進(jìn)行學(xué)習(xí)备图。

LearnGitBranching:http://pcottle.github.io/learnGitBranching/

tryGit

??可以在 Web 上一邊操作一邊學(xué)習(xí) Git 的基本功能。很可惜該教程只有英文版揽涮。

tryGit:http://try.github.io/

4.7 小結(jié)

??本文就理解本書所必需的 Git 操作進(jìn)行了講解。只要掌握了本文的知識盾似,就足以應(yīng)付日常開發(fā)中的大部分操作了。遇到不常用的特殊操作時零院,還請各位讀者查閱本書介紹的參考資料,確保操作的正確性告抄。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市打洼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌募疮,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奢方,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蟋字,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苛聘,“玉大人,你說我怎么就攤上這事设哗。” “怎么了网梢?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵赂毯,是天一觀的道長。 經(jīng)常有香客問我党涕,道長,這世上最難降的妖魔是什么膛堤? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮回懦,結(jié)果婚禮上气笙,老公的妹妹穿的比我還像新娘怯晕。我一直安慰自己,他們只是感情好谭期,可當(dāng)我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布吧凉。 她就那樣靜靜地躺著,像睡著了一般阀捅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上饲鄙,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天,我揣著相機(jī)與錄音忍级,去河邊找鬼。 笑死轴咱,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的窖剑。 我是一名探鬼主播讯私,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼器瘪!你這毒婦竟也來了翠储?” 一聲冷哼從身側(cè)響起橡疼,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎住拭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滔岳,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年摊求,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片室叉。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡硫惕,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恼除,到底是詐尸還是另有隱情,我是刑警寧澤豁辉,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站秋忙,受9級特大地震影響灰追,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜弹澎,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一努咐、第九天 我趴在偏房一處隱蔽的房頂上張望苦蒿。 院中可真熱鬧佩迟,春花似錦、人聲如沸报强。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至父晶,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間甲喝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工俺猿, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人押袍。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓凯肋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親侮东。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,974評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 啥是git git是個分布式版本控制系統(tǒng)有人問了悄雅,啥是版本控制,啥是分布式這個我們可以從玩一個簡單地RPG游戲開始...
    haleyprince閱讀 321評論 0 0
  • 1.1 GitHub簡介 GitHub是為開發(fā)者提供Git倉庫的托管服務(wù)众眨。這是一個讓開發(fā)者與朋友、同事娩梨、同學(xué)以及陌...
    hainingwyx閱讀 774評論 0 0
  • 聽說好的程序員都在用github。 用github有一陣子了狈定,因為不會用Git,所以一直是通過GUI客戶端程序去同...
    唔空閱讀 3,740評論 3 13
  • 第六章嘗試Pull Request ??Pull Request 是社會化編程的象征纽什。GitHub 創(chuàng)造的這一功能...
    SinkingStone閱讀 237評論 0 0
  • 本文是對《GitHub入門與實踐》一本書的總結(jié)和歸納躲叼,方便日后查看Git各種命令的使用 目錄 git初始設(shè)置 讓輸...
    皮皮大閱讀 733評論 0 4