日前因為 GitHub 的火紅帶領了 Git 這個工具的流行,而大部分的開發 IDE 也都支援 Git 的功能,這篇文章透過筆者的觀點來解釋 git 的運作方式,進而利用 Xcode 來說明圖形介面的操作和其相對應底層的機制是如何。
首先是
安裝 Git
- 可以從最新的 Xcode 安裝或
- 到這裡下載 http://code.google.com/p/git-osx-installer/ (雖然檔案寫的是 snow leopard 但是 lion, mountain lion 都也可以用。)
1. 用 Xcode 安裝非常簡單,打開 Xcode 的 Preferences。如下
看到視窗後選 Downloads 的 Tab 然後安裝 Command Line Tools 如下。
這樣就好了在終端機輸入
git --version會看到如下,那就是安裝成功了
git version 1.7.10.2 (Apple Git-33)可以直接跳過下面,到 Git - 基本操作。
用 2. 的方法安裝的話 Mountain Lion 的朋友要注意一下,不是從 Apple Mac App 下載的安裝程式會被預設的安全機制給拒絕安裝,這時候就要打開,系統偏好設定。從左上方的蘋果圖示按下。
接著選擇安全性與隱私
看到如下畫面如果左下的鎖是鎖起來,點選,然後輸入密碼打開它
然後允許任何來源
這樣就可以安裝 Git。安裝好之後,把終端機(Terminal),打開輸入
git --version一樣要看到如下
git version 1.7.10.2 (Apple Git-33)安裝完成後,就來操作一下 git。
在終端機輸入
mkdir gitpractice
Git - 基本操作
來產生一個資料夾,這只是一個資料夾沒有什麼特別的,
再輸入
cd gitpractice進入剛剛產生的 gitpractice 之內
這個裡面就是空的,沒有任何東西輸入
ls -al來確認一下,得到
total 0這樣的結果。現在要把這個資料夾轉成 git 可以處理的資料夾,輸入
drwxr-xr-x 2 chronoer staff 68 12 29 16:30 .
drwxr-xr-x+ 52 chronoer staff 1768 12 29 16:30 ..
git init會得到如下
Initialized empty Git repository in /Users/chronoer/gitpractice/.git/再用
ls -al來看看這個資料夾有什麼變化,得到如下
total 0多了一個 .git 這個資料夾,這個 .git 就是存放和 git 的操作相關所有的在這個 gitpractice 底下所有檔案改成的歷史資料都存在這。而這個資料夾的存在,就代表,gitpractice 這個資料夾是受到 git 控管的,任何的檔案結構的改變都可以記錄下來,存放在 .git 這個資料夾裡面。有兩點要注意
drwxr-xr-x 3 chronoer staff 102 12 29 16:31 .
drwxr-xr-x+ 52 chronoer staff 1768 12 29 16:30 ..
drwxr-xr-x 10 chronoer staff 340 12 29 16:31 .git
Git 控管的資料夾只有根目錄會有 .git 不是每個子目錄都有。這個和 svn 做法不同比如說 gitpractice 底下還有個 src 和 doc,架構如下
gitpractice/只有 gitpractice 裡面會有 .git/ 這個資料夾,src 和 doc 裡不會有,雖然 src 和 doc 這兩個也都受 git 控管,裡面檔案的變化也會記錄在其 git 根目錄 gitparctice 的 .git 之下。換言之如果把 .git 整個移除,那麼就無法得知 gitpractice 底下所有檔案變化,gitpractice 就回到一般目錄的身份。
src/
doc/
另一個要注意的是
Git 不會自動控管檔案,而要手動加入。用 git add <fileName> 來把 <fileName> 加入 git 管理的隊伍中。那我們就來動手加入一個檔案試。首先新增一個文字檔在 gitpractice/ 裡。輸入
echo "first line" > sample.txt然後用
ls -al看是不是多了一個文字檔如下
total 8
drwxr-xr-x 4 chronoer staff 136 12 29 16:57 .
drwxr-xr-x+ 52 chronoer staff 1768 12 29 16:30 ..
drwxr-xr-x 10 chronoer staff 340 12 29 16:31 .git
-rw-r--r-- 1 chronoer staff 11 12 29 16:57 sample.txt
再用
cat sample.txt把這個文字檔的內容呈現在螢幕上
first line這樣子我們新增一個檔案在 gitpractice/ 裡面,那這個檔案有被 git 所管理了嗎?NO 當然沒有,我們可以下
git status來看 git 管理的檔案目前的狀態。如下
# On branch master
#
# Initial commit
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# sample.txt
出現 untracked files: 然後有一個檔案就是剛剛新增的叫 sample.txt。
先插開一下話題,設定一下 git,如果要讓 git 的一些常用的輸出有顏色出現比較好看的話,可以做如下設定。可以參考這個網頁。
git config --global color.diff auto # git diff 要顯示顏色
git config --global color.status auto # git status 要顯示顏色
git config --global color.branch auto
git config --global color.log auto
OK 回到正題,我們新增一個檔案但是不被 git 所管理,那這種檔案叫 untracked file 。那怎麼變成 tracked 呢?就要如下輸入
git add sample.txt
然後再輸入
git status
就會得到如下
# On branch master
#
# Initial commit
#
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
#
# new file: sample.txt
#
沒有出現 untracked 字樣,這樣就是表示這個檔案 sample.txt 已為 git 所管理。
接下來我們要把這個檔案目前的樣子,記錄在 git 的歷史裡面,做為一個歷史的截圖,要輸入以下指令
git commit sample.txt -m "add first line"
其中用了 -m 這個 option 指的是對這個歷史節點,一定要留下註解,就是寫在 -m 之後的字串。然後會得到如下回應
[master (root-commit) 56f3cf3] add first line
1 file changed, 1 insertion(+)
create mode 100644 sample.txt
然後再用
git log
把歷史記錄呈現出來,如下
commit 56f3cf35c041a80f71393649cab87bbcab5d40bb
Author: chronoer <scentsome@gmail.com>
Date: Sat Dec 29 21:56:25 2012 +0800
add first line
記得 git log 只會呈現 git commit 之後的狀態,沒有 commit 的檔案是不會出現在 git log 列表裡的。
稍微看一下歷史記錄的內容,第一個 row 看到一堆奇怪的編碼如下
commit 56f3cf35c041a80f71393649cab87bbcab5d40bb這個代表 git 把 sample.txt 的整份檔案做一個 HASH 的動作,然後存成一個 40 character 的字串,就代表了那個時候的檔案狀態 (讀者產生的 40 字元的字串會因內容不同而產生不同的字串)。接下來看到的是
Author: chronoer <scentsome@gmail.com>這個就是作者和 e-mail ,可以從這兩個指令設定
git config --global user.name "chronoer"接下來就是記錄的日期
git config --global user.email "scentsome@gmail.com"
Date: Sat Dec 29 21:56:25 2012 +0800和當初 commit 時候的註解
add first line接著我們再用
git status來看一下git 的狀態
會得到
# On branch master表明目前工作的目錄沒什麼改變是一個常態。
nothing to commit (working directory clean)
那我們就來改變一下 sample.txt 的內容
輸入
echo "second line" >> sample.txt注意是兩個">" 意思是把 "second line" 接到 sample.txt 的最後一行後面一行。用
cat sample.txt就會看到如下 sample.txt 的內容
first line此時我們改變了 sample.txt 然後用
second line
git status來查看目前 git 所管理的檔案的狀態,得到如下反應
# On branch master任何被 git add 加入的檔案有改變,而還沒 commit 之前,都會如上表示,為 modified 代表有被修改。
# 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: sample.txt#
這邊還有一個 Changes not staged for commit 的敘述,是什麼意思呢,之後再解釋,現在我們要把 sample.txt 變成 staged 或是 index 如下輸入
git add sample.txt再一次把修改中或是 unstaged 的檔案加入到 git 系統
然後我再用
git status來看檔案的狀態會得到如下的回應
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: sample.txt
#
就不是 unstaged 狀態了。
為了把目前修改的 sample.txt 內容當成一個歷史的節點存下來,要用
git commit sample.txt -m "second line"就會得到如下的回應
[master c1c905f] second line再次用
1 file changed, 1 insertion(+)
git status看一下目前的狀態,會得到
# On branch master把剛剛修改的 sample.txt 再一次加入到了歷史的節點,工作目錄底下也就回到常態。到目前為止我們 commit 了兩次,用
nothing to commit (working directory clean)
git log
就可以看到兩個 commit 的記錄,就有兩個 HASH 之後的 40 個字元的字串。如下
commit c1c905f0d0f19a3ba750c3571d845bcc967c9ae6比較新的放在上方。就有兩筆 commit log。
Author: chronoer <scentsome@gmail.com>
Date: Sat Dec 29 23:33:35 2012 +0800
second line
commit 56f3cf35c041a80f71393649cab87bbcab5d40bb
Author: chronoer <scentsome@gmail.com>
Date: Sat Dec 29 21:56:25 2012 +0800
add first line
到目前為止的動作整理一下
- 新增資料夾 - mkdir gitpractice
- 進入資料夾 - cd gitpractice
- 把資料夾轉成 git 可以管理的 - git init
- 新增檔案 - echo "first line" > sample.txt
- 加入 git 管理 - git add sample.txt
- 記錄歷史節點 - git commit sample.txt -m "first line"
- 修改檔案 - echo "second line" >> sample.txt
- 變成 staged 檔案 - git add sample.txt
- 記錄歷史節點 - git commit sample.txt -m "second line" (若沒做 8. 會一起執行)
以上就是基本用 git 來做版本控管的方式,每一個 commit log 都可以視為一個版本。
如何檢視各個版本就是用
git log會得到如下的結果
commit c1c905f0d0f19a3ba750c3571d845bcc967c9ae6Author: chronoer <scentsome@gmail.com>把各個 commit 版本呈現出來。每個版本的代號就是 40 個字的字串。如果想要比較兩個不同版本的差異可以用
Date: Sat Dec 29 23:33:35 2012 +0800
second line
commit 56f3cf35c041a80f71393649cab87bbcab5d40bbAuthor: chronoer <scentsome@gmail.com>
Date: Sat Dec 29 21:56:25 2012 +0800
add first line
git diff <commit id> <commit id>
其中<commit id> 可以用原本的 40 字的前 7 個來代表比如上面的例子,要比較
c1c905f0d0f19a3ba750c3571d845bcc967c9ae656f3cf35c041a80f71393649cab87bbcab5d40bb可以用 c1c905f 和 56f3cf3 寫成
git diff c1c905f 56f3cf3得到如下結果
diff --git a/sample.txt b/sample.txt也就是 a/sample.txt, c1c905f 比 b/sample.txt 56f3cf3 少了一行 second line
index 06fcdd7..08fe272 100644
--- a/sample.txt
+++ b/sample.txt
@@ -1,2 +1 @@
first line
-second line
如果把比較的檔案位子交換成
git diff 56f3cf3 c1c905f
就會得到
diff --git a/sample.txt b/sample.txt也就是 56f3cf3 比 c1c905f 多了一行 second line
index 08fe272..06fcdd7 100644
--- a/sample.txt
+++ b/sample.txt
@@ -1 +1,2 @@
first line
+second line
有了版本,就會想到要之前的版本,在 git 可以這麼做
git checkout <commit id> <file name><file name> 就會回到 <commit id> 的狀態,比如在這個例子,我們這麼做
git checkout 56f3cf3 sample.txt然後再用
cat sample.txt看看 sample.txt 的內容得到
first line回到了 56f3cf3 的狀態了
很方便的版本管理可以任意回到之前的版本。
最後筆者來整理一下檔案在 git 資料夾裡的狀態。
剛剛我們用 git checkout <commit id> <file name> 之後會回到之前 commit 狀態,此時如果用
git status來看狀態會是如下
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: sample.txt
#
是回到一個 staged 的 sample.txt,如果再對這個檔案修改。比如用 vi 存完檔
就會發生一個很有趣的狀況
用
git status來看會看到
# On branch master同一個檔案有兩個狀態,一個是 staged 另一個是 unstaged 可以直接 commit 或是先把 unstaged 用 git add 變成 staged
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: sample.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: sample.txt#
之後如果 commit 就會再多出一個 commit log。
以上是 git 的簡介,下一篇會來探討Xcode 的 GUI 和 git 運作的相關功能。