Git基本教学

转自 http://gogojimmy.net/2012/01/17/how-to-use-git-1-git-basic/

前言

Git 是一套分散式的版本控制系統,版本控制是一個開發團隊中不可或缺的工具,Git 最強大的一個特點就是可以無窮無盡的開 branch (分支),好處就是今天不論是修 Bug ,開發新功能,或是研究 feature 都非常的方便,學 Git 到現在大概三個月的時間讓我體會到” Git 用的好,產品開發沒煩惱!!” ,搭配 Github (一個以 Git 作為基礎的程式碼社群服務,上面有非常多的資源)使用更是天下無敵,團隊開發怎麼能少的了用 Git 呢!!!!

坦白說 Git 還是需要一些時間去學習, Workflow 對於使用 Git 有非常大的影響,一個好的 Workflow 會更幫助你學會如何去操作 Git 及培養好的團隊開發技巧,因此這篇就是要來幫助剛學習 Git 的人如何快速上手 Git 及一個開發的 Workflow ,以下是給完全沒接觸過的新手的入門文,如果你想要快速的了解基本操作,你可以直接看這裡。

安裝Git

網路上已經有很多教學了,善用 Google 我相信你可以找到非常多的資源,的我建議可以參考 Github 上的教學(請先建立一個 Github 的帳號,因為他會連 Github 的設定一起設定完成):

  • Mac setup git
  • Windows setup git
  • Linux setup git

若你想看中文的推薦你 Git 的權威教材 Pro Git。

Git 也有很多的 GUI 工具,免費的像是 GITX、 SourceTree、SmartGit (我沒用過但是是2011評價最好的 Git GUI),付費的像是 Tower 都是不錯的選擇,但我會建議先從基本的 Git 指令開始學習,當你對基本的指令熟悉後再去使用各種不同的 GUI 就會更加的得心應手了!

設定你的Git

在每一次的 Git commit (提交,我們稍後會提到) 都會記錄作者的訊息像是 name 及 email ,因此我們使用下面的指令來設定:

$ git config --global user.name "Jimmy Kuo"
$ git config --global user.email "[email protected]"

加上 --global 表示是全域的設定。你可以使用 git config --list 這個指令來看你的 Git 設定內容:

$ git config --list
    user.name=Jimmy Kuo
    [email protected]

或是其實 Git 的設定檔是儲存在你的家目錄的.gitconfig 隱藏檔中,你可以使用編輯器將他打開

$ cat ~/.gitconfig
    [user]
        name = Jimmy Kuo
        email = [email protected]

使用終端機來操作 Git 常會讓人覺得一直打指令很繁瑣,因此 Git 也有提供 alias 的功能,例如你可以將 git status 縮寫為 git stgit checkout 縮寫為 git co 等,你只要這樣設定:

$ git config --global alias.st status

這樣一來只要打 git st 就等同於打 git status 了。

空白對有些語言是有影響的(像是Ruby),因此我們會希望 Git 去忽略空白的變化,這時候你需要在你的設定加入:

$ git config --global apply.whitespace nowarn

如此一來 Git 對於空白的變化便會忽略不計。

Git 預設輸出是沒有顏色的,我們可以讓他在輸出時加上顏色讓我們更容易閱讀:

$ git config --global color.ui true

開始使用Git (init, clone)

要開始使用 Git 你必須先建立一個 Git 的 Repository,你可以把它想做是一個資料庫的意思,有兩種方法可以建立一個 Git 的 Repository:

  • 自己建立一個新的 Repository

例如我現在有個叫做 Animal 專案資料夾,我現在想要開始使用 Git 開始管理,因此我先將目錄切換到 Animal 底下後輸入git init

$ git init
Initialized empty Git repository in /Users/Jimmy/Projects/Animal/.git/

這時你就會看到 Git 告訴你說已經在這邊建立好一個新的 Git Repository。

  • Clone(複製)別人的 Repository

    例如我們在 Github 上面看到人家的程式碼想要抓下來自己修改,或是團隊中別人的程式碼,這時候他們通常會有一個 Git 的檔案位置像是在 Github 的話你就會看到像下面的地方可以讓你複製 git 的 clone 位置:

Git基本教学_第1张图片

將他複製起來後到你的目錄下輸入 git clone

$ git clone https://[email protected]/gogojimmy/Animal.git

如此便會將這個 Git Repository下載到我們的資料夾, git clone 預設會將下載的 git 存成一樣檔名的資料夾,如果你要更改成別的名稱的話只需要在網址後面加上你想要更改的名稱即可,像是:

$ git clone https://[email protected]/gogojimmy/Animal.git monkey

這樣子下載下來的 Repository 的名稱就會從原本的 Animal 變成 monkey 了。

Git的基本功(status, add, commit, log, .gitignore)

在一個 Git 的 Repository 中你可以輸入git status來檢查目前 Git 的狀態

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

這代表目前是一個乾淨的目錄狀態,沒有未被追蹤或是被修改的檔案, On branch master 表示你目前正在名為 master 的 branch 上,等等會再說明 branch 的功用。我們現在在這個目錄新增一個 test 檔案後,再使用git status 來查看:

$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#   test.rb
nothing added to commit but untracked files present (use "git add" to track)

這時候你會看到我剛剛新增的 test.rb 檔案變成在 Untracked files (未被追蹤的檔案),表示過去在這個 Git Repository 中從未有這支檔案,是一支未被追蹤的檔案,我們要把這個檔案讓 Git 能夠追蹤的話,我們使用 git add 這個指令來把它加入追蹤:

$ git add test.rb
$ git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached ..." to unstage)
#
#   new file:   test.rb
#

你可以看到原本 test.rb 還在 Untracked files 中,經過我們使用 git add test.rb 後他就變成了 Changes to commit,通常我們稱這個狀態叫做 stage ,修改過但還沒使用 git add 的檔案稱為 unstage 。

Tips: 一次加入全部的檔案

如果你一次修改了很多檔案,懶得一個一個輸入 git add 的話,你可以輸入 git add . 這會幫你將所有剛剛修改過或新增加的檔案一次 Add 進 stage 狀態,但強烈不推薦這樣的作法,這樣的方法雖然方便但很容易會不小心加入一些其他不必要的檔案,一個正確的觀念是你必須隨時都很清楚你的檔案狀態,因此最好是手動將你確定要加入的檔案使用 git add來加入,有一個更好的作法是使用互動模式 git add -i ,在互動模式下你可以方便的選擇你要加入的檔案,或是移除剛剛不小心加入的檔案 (revert)。

已經在 stage 狀態的檔案的下一步就是準備提交( commit ),commit 是寫程式時一個很重要的動作,一個 commit 在 Git 中就是一個節點,這些 commit 的節點就是未來你可以回朔及追蹤的參考,你可以想像就像是電玩遊戲時的存檔,每一個 commit 就是一次存檔,讓我們未來在需要的時候都可以回到這些存檔時的狀態。因此我們將剛剛做的修改提交:

$ git commit

此時你會看到畫面跳到你在 git 中設定的編輯器畫面:

#
# 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 …" to unstage)
#
#       new file: test.rb
#

這個視窗中最上面的空行是要給你寫下你這次 commit 的訊息,例如在這裡寫下 “Add test.rb file to test git function”來表示這一次提交的目的,強烈建議在 commit 的時候要盡量清楚表達這次 commit 的內容為何,因為這會讓你未來要回頭看 code 的時候能讓你快速的找到你想要找的內容,也能對團隊中其他成員了解你在做什麼。

在 commit 完後會顯出出你這次 commit 的更動:

[master 5f76371] add ignore files
 2 files changed, 8 insertions(+), 0 deletions(-)
 create mode 100644 .gitignore
 create mode 100644 test.rb

如果你覺得每次這樣跳出編輯器很麻煩,你也可以在 commit 時加上 -m 的參數來快速提交:

$ git commit -m "Add test.rb to test git function"

若使用 -am 的話還能將所有未被 add 的檔案一併 add 進來( 更新:如果是第一次新增還沒有被 add 的檔案是不會一起加入的,只有之前已經被 add 過 commit 的檔案才會被加入 ),但就像前面所說這樣的作法並不推薦,你應該清楚的加入你應該加入的檔案就好:

$ git commit -am "Add test.rb to test git function"

若使用 -v 的話會列出更動的紀錄:

# 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 ..." to unstage)
#
#   new file:   .gitignore
#   new file:   test.rb
#
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..61521a9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+*.swp
+log/*.log
diff --git a/test.rb b/test.rb
new file mode 100644
index 0000000..23df1c1
--- /dev/null
+++ b/test.rb
@@ -0,0 +1,5 @@
+class Test
+  def test
+    puts "This is a test file"
+  end
+end

加號(+)代表增加的部份,減號(-)代表刪除的部份。

我們可以使用 git log 的指令查看過去 commit 的紀錄:

$ git log
5f76371 - (HEAD, master) add ignore files (3 minutes ago) 
4ca268d - (origin/master) First commit (33 minutes ago) 

前面的亂碼代表的是當次 commit 的版號,後面的是 commit 的訊息、時間以及 commit的作者,你可以使用 --stat 參數看到更詳盡的訊息:

$ git log --stat
5f76371 - (HEAD, master) add ignore files (7 minutes ago) 
 .gitignore |    3 +++
 test.rb    |    5 +++++
 2 files changed, 8 insertions(+), 0 deletions(-)

4ca268d - (origin/master) First commit (37 minutes ago) 
 lib/animal.rb       |    5 +++++
 spec/animal_spec.rb |    5 +++++
 2 files changed, 10 insertions(+), 0 deletions(-)

如果你想看到檔案更詳細的變更內容,你可以加上 -p 的參數:

$ git log -p
5f76371 - (HEAD, master) add ignore files (8 minutes ago) 
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..61521a9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+.DS_Store
+*.swp
+log/*.log
diff --git a/test.rb b/test.rb
new file mode 100644
index 0000000..23df1c1
--- /dev/null
+++ b/test.rb
@@ -0,0 +1,5 @@
+class Test
+  def test
+    puts "This is a test file"
+  end
+end

因此我們稍微做一下流程的整理:

修改檔案 => 加入 stage (git add) => 提交( git commit )=> 繼續修改其他檔案

Tips: commit 的最佳時機? 什麼時候該 commit ?

什麼時候才是 commit 的最好時機並沒有一個定論,大部分人會告訴你通常你完成了一個階段性的小工作就做一次的 commit ,我的感覺是當你想要記錄目前的狀態的時候就是你 commit 的最佳時機,例如說剛完成某個page,某個任務需求而你想要做個記錄的時候。

有些檔案我們不希望加入版本控制的追蹤,例如說Database的schema或是一些log檔,這時候你可以將他們加入 .gitignore 中來讓 Git 忽略他們,使用編輯器來打開你的 .gitignore 檔案。

$ vim .gitignore

在這邊假設我想將 Mac 自動產生的.DS_Store, vim 的 swp 暫存檔及 log 檔忽略,我便分行加入:

.DS_Store
*.swp
log/*.log

如此一來便會將這些檔案從git追蹤忽略了。

Tips: 被加入 gitignore 的檔案一樣出現在 status 中?

有時候你會發現即時你將檔案加入了 .gitignore 卻一樣會出現在 Git 的追蹤狀態中,這是由於你想要忽略的檔案之前已經被 Git 追蹤了,因此你現在想要讓 Git 忽略他的話只能先將他刪除然後再 commit 一次,再來這支檔案再出現就不會再追蹤這支檔案了。

Git branch

branch (分支)應該是 Git 最重要的技能了,在一個多人專案的開發過程中我們有時候要開發新功能,有時候是要修正某個Bug,有時候想要測試某個特異功能能不能 work ,這時候我們通常都會從主 branch 再開出一條新的 branch 來做,這支新開的 branch 會帶著你的主 branch 目前的最新狀態,當你完成你所要開發的新功能/ Bug 修正後確認沒問題就再把它 merge(合併)回主 Branch ,如此便完成了新功能的開發或是 Bug 的修正,因此每個人都可以從主 branch 拉一條新的 branch 來做自己想做的事,再來我們好好了解一下 branch 的使用。

了解 branch 最好的方法就是有圖像可以看,你可以用任何你已安裝的GUI來查看,但在你安裝 Git 的時候其實同時也安裝了最基本的 Git GUI 叫做 gitk,你可以使用 gitk --all 這個指令來呼叫他,此時你應該能看到以下的畫面:

Git基本教学_第2张图片

點擊左上方區塊的 commit 節點你可以看到當次 commit 的詳細資料,例如作者以及他的更新記錄,但你會發現這時候終端機是無法輸入的,因此請你先關掉 gitk ,改打指令 gitk --all & 來讓 gitk 在背景執行。

git branch 這個指令可以列出所有的 branch 並告訴你目前正在哪個 branch:

$ git br
* master
  develop
  feature/test

上面的訊息告訴我們在這個 Git repository裡有3支 branch ,而你目前正在 master branch 上。假設我們現在要開一支新的 branch 叫做 cat ,使用 git branch 來幫助你開一支新的 branch

$ git branch cat
$ git branch
  cat
* master

上面我們開了一支新的 branch 叫做 cat ,使用 git branch 再查看一次發現已經多了這支新的 branch了,這時候你去查看你的 gitk 的圖像狀態會發現像下圖一樣,新的 branch cat 與 master 在同一條水平線上,表示目前他們的狀態是一模一樣的。

Git基本教学_第3张图片

你應該也有發現,雖然我們建立了一個 cat 的 branch ,但其實我們所在的 branch 還是在 master branch,因此我們現在還需要切換過去,因此我們使用 git checkout 來切換:

$ git checkout cat
Switched to branch 'cat'

這樣就會從原本的 mater branch 切換到 cat branch 了。

接下來假設我正在 cat 這支 branch 做開發,因此新增一個檔案,加上一些內容,將它 add 到 stage 後再 commit 它。

$ vim lib/cat.rb
$ git status
# On branch cat
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#   lib/cat.rb
nothing added to commit but untracked files present (use "git add" to track)
$ git add lib/cat.rb
$ git commit -m "Add Cat.rb"
[cat ea7d309] Add Cat.rb
 1 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 lib/cat.rb

Git基本教学_第4张图片

上面的流程你已經很熟悉了,接下來我們再切換到原本的 master branch ,這時候你會發現剛剛在 cat branch 新增的 cat.rb 檔案已經不見了。

$ git checkout master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.

Git 在我們切換 branch 的同時就會很聰明的會把我們的工作目錄更動成那個 branch 該有的狀態,如果你這時候切換到 GUI 去看,你會發現到與剛剛 cat 和 master branch 在同一條水平線上不同,cat branch現在已經比 master branch 再多出了一個 commit 的內容。

Git基本教学_第5张图片

現在我在切換到 cat branch 去增加更多的內容,一樣再將它 add 到 stage 後,再 commit 它。

$ git checkout cat
Switched to branch 'cat'
$ mvim lib/cat.rb
$ git add lib/cat.rb
$ git commit -m "Add initializer"
[cat a3bce42] Add initializer
 1 files changed, 3 insertions(+), 1 deletions(-)

Git基本教学_第6张图片

切到 GUI 來看的話你會發現現在 cat 這支 branch 比 master branch 要在多上兩個 commit 的內容。

Git基本教学_第7张图片

如果這時候我們在 master 上繼續開發會發生什麼事呢? 我們現在切換到 master branch 並增加一個檔案及內容,照慣例 add 後 commit。

$ git co master
Switched to branch 'master'
Your branch is ahead of 'origin/master' by 1 commit.
$ git add spec/animal_spec.rb
$ git commit -m "Another spec"
[master 7d72927] Another spec
 1 files changed, 2 insertions(+), 0 deletions(-)

Git基本教学_第8张图片

我在 master branch 的 animal_spec.rb 增加了一些內容,把它 commit 之後我們切換到 GUI 來看。

Git基本教学_第9张图片

我們發現現在 master branch 與 cat branch 已經產生分歧了,因為兩支 branch 都有了各自往後開發的 commit ,而且由於 master branch 最後一次的 commit 時間較新因此排列在最前面。

Git rebase 整理現在的 branch

假設我們現在在 cat branch 的開發動作已經完畢,通常我們現在要做的事情會是將 cat branch 合併回 master branch,在開發流程上, master branch 就像是一個主要的 branch ,每個開發人員都是從 master branch checkout 出去一支新的 branch 做開發,在開發完畢後就再將開發完的 branch 合併回 master branch,因此 master branch 都會保有最新的開發好的狀態,一般在 Git 教學中會教你現在使用 git merge 這個指令來將兩個 branch 合併,但這邊我要先教你的是 git rebase 這個指令。

與 git merge 不同的是, git rebase 不單單只是將兩個不同的 branch 合併起來,而是將某一支 branch 基於另一支 branch 的內容合併起來,這是什麼意思? 以我們的例子來說,我們在 cat branch 開發完了以後,這時候我們的 master branch 也有了其他開發者所合併回去的內容,換句話說現在的 master branch 與我們當初 checkout 出去的時候的狀態已經不同了,但我們會希望我們現在這支 cat branch 的內容就像是剛剛從 master branch checkout出來一樣乾淨,也就是說讓 cat branch 中保有 master branch 最新的狀態, git rebase 會基於 master branch 目前最後一次的 commit 內容再往後把你在 cat branch 上commit 的內容加上去,我們現在在 cat branch 輸入 git rebase master 來將 cat branch 基於 master branch 做 rebase。

$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: Add Cat.rb
Applying: Add initializer

過程中沒有發生衝突,這時候我們到 GUI 看看現在的結果。

Git基本教学_第10张图片

原先 cat branch 上的兩個 commit (Add Cat.rb 和 Add initializer) 已經合併到 master branch 最新的 commit (Another spec),換句話說目前 cat branch 的內容就像是剛從 master branch 所 checkout 出來然後再加上自己的 commit,因此不同於 git merge 的線圖會把 cat branch 合併到 master branch , 而是把原本的 cat branch 接到 master branch 因此只有一條線,當一個專案有很多的 branch 再做開發的時候會避免很多 branch 的線接來接去難以辨認。

開發過程中,若你在開發的 branch 功能比較多, commit 的量也比較多時,建議使用rebase將你現在的 branch 整理過再合併回主幹,這樣會產生較漂亮的線圖

若你想要看看目前的 branch 與其他 branch 有哪些差異,你可以使用 git diff 的指令去觀察,例如我現在想要看 master 跟 cat 這兩個 branch 的差異,我只要下:

$ git diff cat master
diff --git a/lib/cat.rb b/lib/cat.rb
deleted file mode 100644
index 1227d26..0000000
--- a/lib/cat.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-class Cat < Animal
-  def initialize
-    super
-  end
-end

你就可以看到現在的 cat branch 跟 master branch 的差異在哪了。

如果我們開發完畢時,我們會把開發好的東西合併回 master 很自然的我們通常都會使用 git merge 這個指令來合併兩個branch

$ git merge cat
Merge made by recursive.
 lib/cat.rb |    3 +++
 1 files changed, 3 insertions(+), 0 deletions(-)
 create mode 100644 lib/cat.rb

這時候我們看圖的話會是這樣:

Git基本教学_第11张图片

可以看到我剛剛在 master branch 下了 git merge cat 這個指令來告訴 git 要 merge cat 到現在所在的 branch ,因此在圖上就看到了 cat branch 拉一條線回來合併到了 master 這個 branch 了,解釋這張圖的意思就是, cat branch 從 master branch 的 Another spec 這一次的 commit 分支出來後,自己產生了三次的 commit (Add Cat.rb、Add initializer、Rever “Add initializer”) 然後合併到 master。

Confict: 處理 Git 合併時的衝突:

很常發生的情況是再 merge 或是 rebase 的圖中產生了 convict (衝突),這時候 Git 會停下來請你去處理,例如我們在 cat 和 master 的 branch 都對 lib/cat.rb 這支檔案做編輯,然後我們將他們 merge:

$ git merge cat
Auto-merging lib/cat.rb
CONFLICT (content): Merge conflict in lib/cat.rb
Automatic merge failed; fix conflicts and then commit the result.

你會看到 Git 告訴你在你合併的過程中在 lib/cat.rb 這支檔案發生了衝突,Git 不知道該怎麼處理因此要你去處理它,這時候我們打開這支檔案會看到這樣的情況:

Git基本教学_第12张图片

<<<<<<<<<< HEAD 到 ========== 的中間區域是目前你所在 branch 的 commit 內容,而從 =========== 到 >>>>>>>>>>> cat 則是你要合併的 cat branch 的內容,你必須做出決定看是要兩個都留下,或是選一個,或是改成你想要的內容,改好後記得要將 Git 自動產生的 <<< 、 =====、 <<< 的內容都刪除,修改完畢後存檔,將剛剛修改過的檔案再使用 git add 加入 stage ,將所有的衝突都修正完畢後就使用 git commit 提交一次 commit,由於這次的 commit 是在處理 merge 時的衝突,因此 Git 很聰明的已經幫我們加上了一些預設的訊息 “Merge branch ‘cat’”, commit 提交後就會看到合併成功的訊息了。

$ git add lib/cat.rb
$ git commit
[master c37c9e3] Merge branch 'cat'

發生 confict 時的處理步驟

  1. 將發生 confict 的檔案打開,處理內容( 別忘了刪除<<<、===、>>> )。
  2. 使用 git add 將處理好的檔案加入 stage。
  3. 反覆步驟 1~2 直到所有 confict 處理完畢。
  4. git commit 提交合併訊息。
  5. 完成

講到這裡我們再來整理一下工作流程,讓大家再複習一下 git 的使用:

  1. 在專案中會有一條主 branch 是大家將開發好或是修好的東西合併回去的對象,所有要開發的新功能或是修 bug 都是從主 branch 拉出一條新的 branch 去工作。
  2. 當你完成一個階段性的任務時,將你剛剛 所新增的內容使用 git add 加入到 stage 的狀態,並且使用 git commit 加上 commit 的訊息來提交一次的 commit。
  3. 反覆動作 2 直到你完成這支 branch 的主要目的(新功能/修 bug ),若這時你離主 branch 已經有一段時間,或是確定主 branch 上已經有新的 commit ,使用 git rebase 將自己的分支整理然後使用 git merge 合併回主 branch,反之則是直接使用 git merge 將自己 branch 的內容合併回去。

Git reset 取消上一次的操作

取消 merge

版本控制最大的好處之一就是讓你永遠可以後悔,因此我們常會希望把已暫存的檔案、已提交的 commit 或是已合併的 branch 取消修改,這時候我們可以使用 git reset 這個指令來幫助我們,像現在我若是想要取消剛剛的 merge 動作,我只要下:

$ git reset --hard ORIG_HEAD
HEAD is now at c126ff9 Config initialze

這時候再回去看圖你會發現已經回到合併前的樣子了:

Git基本教学_第13张图片

取消已暫存的檔案

有時候手殘不小心將還沒修改完的檔案使用 git add 加入了 stage ,這時候可以使用 git reset HEAD  來將這支檔案取消 stage:

Git基本教学_第14张图片

你可以看到我使用 git add 將檔案加入 stage 後,在我的 status 狀態顯示 lib/cat.rb 這支檔案現在已經準備好被 commit ,但這時我使用了 git reset HEAD 將這支檔案取消 stage,再使用 status 查看時它就變回一支還沒加入 stage 的檔案了。

取消修改過的檔案

連續剛剛的情況,若是我想完全放棄這次修改 (將檔案狀態回復到最新的一次 commit 時的狀態),我可以使用 git checkout --  來回復這支檔案:

$ git checkout -- lib/cat.rb

取消變更不會有任何訊息,但這時你去看檔案會發現他已經回復成沒修改過時的模樣了。

修改上一次的commit

手誤打太快, commit 訊息打錯時,我們可以使用 git commit --amend 來幫助我們重新修改:

git reset git reset

在上面我想要修改打錯字的 commit 訊息 “Cat initiae”,因此我使用 git commit --amend 來修改成正確的訊息。

強制回復到上一次 commit 的版本

有時候我們想要放棄所有修改回到 commit 時的狀態,這時候我們可以下 git reset --hard HEAD 來回復,HEAD 參數可以加上一些變化,例如 HEAD^ 表示目前版本的上一個版本 HEAD~2 則是再上一個,因此你可以自由的跳回去之前的狀態。

git reset, hard 與 soft 的差異

你可能會在這邊感到疑惑,在使用 git reset 的時候都會看到一個 soft 或是 hard 的參數,這代表什麼樣的意義?基本上在使用 git reset 的時候,都會把目前狀態回復到你想回復的版本,但若是不加參數的情況,會把你做過的修改仍然保留,但是,若是加上 –soft 參數,則會把做過的修改加入 stage ,若是加上 hard 參數的話則是把做過的修改完全刪除,回到那個版本原本的樣子。

Git情景剧

  • 如何安裝 Git
    • Mac : 安裝 Homebrew

        brew install git
      
    • Linux(Debian) : apt-get install git-core
    • Linux(Fedora) : yum install git-core
    • Windows : 下載安裝 msysGit
  • 如何設定 Git

    • Mac : Set Up Git on Mac
    • Linux : Set Up Git on Linux
    • Windows : Set up Git on Windows
  • 如何開始一個 Git Respository

    • 在專案底下使用 git init 開始一個新的 Git repo.
    • 使用 git clone 複製一個專案
  • 如何將檔案加入 Stage

    • 使用 git add 將想要的檔案加入 Stage.
    • git add . 會將所有編修過的檔案加入 Stage (新增但還沒 Commit 過的檔案並不會加入)
  • 如何將檔案從 Stage 中移除(取消add)

    • git reset HEAD 檔案名稱
  • 如何將檔案提交(commit)

    • 使用 git commit會將 Stage 狀態的檔案做 Commit 動作
    • git commit -m "commit訊息" 可以略過編輯器直接輸入 commit 訊息完成提交。
    • git commit -am "commit訊息" 等同於先git add .後略過編輯器提交 commit。
  • 如何修改/取消上一次的 commit

    • git commit --amend 修改上一次的 commit 訊息。
    • git commit --amend 檔案1 檔案2... 將檔案1、檔案2加入上一次的 commit。
    • git reset HEAD^ --soft 取消剛剛的 commit,但保留修改過的檔案。
    • git reset HEAD^ --hard 取消剛剛的 commit,回到再上一次 commit的 乾淨狀態。
  • 分支基本操作(branch)

    • git branch 列出所有本地端的 branch。
    • git branch -r 列出所有遠端的 branch。
    • git branch -a 列出所有本地及遠端的 branch。
    • git branch "branch名稱" 建立一個新的 branch。
    • git checkout -b "branch名稱" 建立一個新的 branch 並切換到該 branch。
    • git branch branch名稱 起始點 以起始點作為基準建立一個新的 branch,起始點可以是一個 tag,branch 或是 commit。
    • git branch --track branch名稱 遠端branch 建立一個 tracking 遠端 branch 的 branch,這樣以後 push/pull都會直接對應到該遠端的branch。
    • git branch --set-upstream branch 遠端branch 將一個已存在的 branch 設定成 tracking 遠端的branch。
    • git branch -d "branch 名稱" 刪除 branch。
    • git -r -d 遠端branch 刪除一個 tracking 的遠端 branch,例如git branch -r -d wycats/master
    • git push repository名稱 :遠端branch 刪除一個 repository 的 branch,通常用在刪除遠端的 branch,例如git push origin :old_branch_to_be_deleted
    • git checkout branch名稱 切換到另一個 branch(所有修改過程會被保留)。
  • 遠端操作(remote)

    • git remote add remote名稱 remote網址 加入一個 remote repository,例如 git remote add github git://github.com/gogojimmy/test.git
    • git push remote名稱 :branch名稱 刪除遠端 branch,例如 git push origin :somebranch
    • git pull remote名稱 branch名稱 下載一個遠端的 branch 並合併(注意是下載遠端的 branch 合併到目前本地端所在的 branch)。
    • git push 類似於 pull 操作,將本地端的 branch 上傳到遠端。
  • 合併操作(merge)

    • git merge branch名稱 合併指定的 branch 到目前的 branch。
    • git merge branch名稱 --no-commit 合併指定的 branch 到目前的 branch 但是不會產生合併的 commit。
    • git cherry-pick SHA 將某一個 commit 的內容合併到目前 branch,指定 commit 是使用該 commit 的 SHA 值,例如 git cherry-pick 7300a6130d9447e18a931e898b64eefedea19544
  • 暫存操作(stash)

    • git stash 將目前所做的修改都暫存起來。
    • git stash apply 取出最新一次的暫存。
    • git stash pop 取出最新一次的暫存並將他從暫存清單中移除。
    • git stash list 顯示出所有的暫存清單。
    • git stash clear 清除所有暫存。
  • 常見問題:

    • 我的 code 改爛了我想全部重來,我要如何快速回到乾淨的目錄?
      • git reset --hard 這指令會清除所有與最近一次 commit 不同的修改。
    • merge 過程中發生 confict 我想放棄 merge,要如何取消 merge?
      • 一樣使用 git reset --hard 可以取消這次的 merge。
    • 如何取消這次的 merge 回到 merge 前的狀態?
      • git reset --hard ORIG_HEAD 這指令會取消最近一次成功的 merge 以及所有你在這次 merge 後所做的修改。
    • 如何回復單獨檔案到原本 commit 的狀態?
      • git checkout 檔案名稱 這指令會將已經被修改過的檔案回復到最近一次 commit 的樣子。

你可能感兴趣的:(Git基本教学)