2013-02-13

Git 常用指令

Hello Git 紀錄了最基本的指令。
git init
git clone
git add
git rm --cached
git commit
git push
接下來就來看看常用的指令。
git checkout -- [file]
git reset HEAD [file]
git commit -a
git rm
git rm -f
git rm --cached
git mv

在開始之前,先複習一下 Hello Git 提到的「Git 檔案的幾種可能性」。
  • 未追蹤的檔案:未經過 git add 的檔案
  • 已追蹤的檔案:經過 git add 的檔案
    • 未修改的檔案:commit 之後未修改的檔案,或者 clone 之後未修改的檔案
    • 已修改的檔案:已修改但未標記的檔案
    • 已標記的檔案:修改且標記的檔案,commit 之後回到「未修改的檔案」
  • 忽略的檔案

忽略的檔案

每個專案總是有一些不需要版本控管的檔案,例如暫存檔、日誌檔或衍生檔。

只要在 .git 同一層目錄下新增 .gitignore 檔,並紀錄要忽略的檔案就可以了。
  • .git
  • .gitignore
  • ...
*.class
/target
logs/
以上的 .gitignore 忽略所有 class 檔、第一層 target 目錄與所有 logs 子目錄。

已修改的檔案

修改(已追蹤的)未修改的檔案(以 Hello Git 裡的 a.txt 為例),為求對照,再新增一個檔案 b.txt。
> git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   a.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b.txt
no changes added to commit (use "git add" and/or "git commit -a")
出現一個新的狀態(Changes not staged for commit),這就是 Hello Git 裡提到的檔案狀態之一,已修改的檔案。

除了已經看過的未追蹤的檔案狀態(Untracked files),最後一行還提示了 no changes added to commit,表示目前沒有任何已標記的檔案。

不管是未追蹤的檔案或者修改的檔案,加入已標記檔案的指令是一樣的,都是 git add

復原已修改的檔案

在將已修改的檔案加入已標記之前,git 提示了另一個指令,復原已修改的檔案,也就是放棄修改,將檔案還原到上次 commit 的狀態。
> git checkout -- a.txt
> git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b.txt
nothing added to commit but untracked files present (use "git add" to track)
可以看到 a.txt 還原成未修改的檔案了。

複習 Hello Git 提過的 git rm --cached 指令,git checkout -- 是將已追蹤的檔案還原,而 git rm --cached 是將未追蹤的檔案還原。

git rm --cached
  • 新增檔案(未追蹤的檔案)
  • git add
  • 已標記的檔案
  • git rm --cached
  • 未追蹤的檔案

git checkout --
  • 修改已追蹤的檔案
  • git add
  • 已標記的檔案
  • git checkout --
  • 已追蹤的未修改檔案

再修改 a.txt 一次,然後將 a.txt 與 b.txt 都加入已標記的檔案。
> git add *.txt

> git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   a.txt
#       new file:   b.txt
# 
git add 不僅接受精確的檔案名稱,也可以使用 * 或目錄名稱。

產生兩個已標記檔案(Changes to be committed),git 並附註兩個檔案新舊的不同狀態(modified 與 new file)。

修改已標記的檔案

接下來有一個有趣但重要的觀念,修改已標記的檔案會發生什麼事?馬上來試試,修改 b.txt 後執行 git status。
> git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   a.txt
#       new file:   b.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:   b.txt
#
有趣的事情發生了,b.txt 出現在兩種狀態裡,分別是已標記的檔案(Changes to be committed)與已修改的檔案(Changes not staged for commit)。

一個檔案為什麼會有兩種狀態?答案就是「複本」。

git 在標記檔案時,已經為該檔案產生複本,所以標記之後再修改檔案的話,並不會影響標記的內容,也就是說,此時執行 git commit 的話,存到本機檔案庫的 b.txt 並不是最新的內容,而是上次執行 git add 時的內容,這是相當重要的一點,也是容易忘記而出錯的地方,修改已標記的檔案後,務必再執行 git add 一次

但是,對同一個檔案執行多次 git add 並不會保留多個複本,git 只會保留最新的一個版本。

取消已標記的檔案

git 在已標記的檔案裡提示了取消的指令,先取消新增的檔案 b.txt。
> git reset HEAD b.txt
> git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   a.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b.txt
可以看到 b.txt 回到原本的狀態,也就是未追蹤的檔案。

再取消既有的檔案 a.txt。
> git reset HEAD a.txt
Unstaged changes after reset:
M       a.txt

> git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       modified:   a.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b.txt
no changes added to commit (use "git add" and/or "git commit -a")
嗯,都回到執行 git add 之前的狀態。

未修改的檔案

本機操作的最後一步,交付檔案到本機檔案庫,先將 a.txt 與 b.txt 執行 git add 到已標記的檔案,再執行 git commit 將已標記檔案存入本機檔案庫,並將檔案狀態還原成未修改的檔案。
> git commit -m 'another_test'
[master bf7cea7] 'another_test'
 2 files changed, 2 insertions(+)
 create mode 100644 b.txt

> git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
nothing to commit, working directory clean

直達車

可以在 git 提示的指令中發現 no changes added to commit 後面,除了 git add 外,還有一個指令 git commit -a,這個指令是 git addgit commit 的綜合體,但是不像 git add 可以指定檔案名稱,所以 git commit -a 是將所有修改過的已追蹤檔案執行 git add 與 git commit,略過未追蹤的檔案。
> git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#
# 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:   a.txt
#       modified:   b.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

> git commit -a -m '3_commit'
[master 29853af] '3_commit'
 2 files changed, 2 insertions(+), 2 deletions(-)

刪除檔案

刪除檔案有一些特別的狀況:
  • 完全刪除:包括檔案庫與原檔案
    • 手動刪除
      • 手動刪除檔案
      • 執行 git rm 指令
      • 執行 git commit 指令
    • 自動刪除
      • 執行 git rm 指令
      • 執行 git commit 指令
  • 移除追蹤:只從檔案庫中刪除,保留原檔案
    • 執行 git rm --cached
  • 刪除已標記檔案

完全刪除之手動刪除
> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

> git add a.txt

> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    a.txt
#
no changes added to commit (use "git add" and/or "git commit -a")

> git rm a.txt
rm 'a.txt'

> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       deleted:    a.txt
#
奇怪的是,按照提示使用 git add 並無作用,一定要用 git rm 才會將刪除的檔案標記。

完全刪除之自動刪除
> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
nothing to commit, working directory clean

> git rm a.txt
rm 'a.txt'

> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       deleted:    a.txt
#

移除追蹤
> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
nothing to commit, working directory clean

> git rm --cached a.txt
rm 'a.txt'

> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       deleted:    a.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a.txt

刪除已標記檔案
> git add a.txt

> git rm a.txt
error: 'a.txt' has changes staged in the index
(use --cached to keep the file, or -f to force removal)

> git rm -f a.txt
rm 'a.txt'
不可對已標記檔案執行 git rm,因為這樣會完全遺失標記的內容,若真的要刪除已標記的檔案,可以執行 git rm -f。

移動檔案

使用 git mv 可以簡單的移動或重新命名檔案。
> git mv a.txt c.txt

> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       renamed:    a.txt -> c.txt
#
但有趣的是,git mv 事實上是三個指令的綜合體。
  • 手動移動或重新命名檔案
  • git rm 舊檔案
  • git add 新檔案
> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    a.txt
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       c.txt
no changes added to commit (use "git add" and/or "git commit -a")

> git rm a.txt
rm 'a.txt'

> git add c.txt

> git status
# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.
#
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       renamed:    a.txt -> c.txt
#


回到 Git 學習筆記
---
---
---

沒有留言:

張貼留言