文章範圍
- 安裝 和 設定 Git
- Git 基本操作
- 長樹枝了(分叉成不同的版本)
- Ruby on Rails 上使用
- Git 維護
本文章的來源大部分是由 Git 官方網站裡的基本和更
先進一點的教學翻譯的, 然後加入了自己寫的範例。 Railscasts裡的資料也有參考到。 此文章的內容會談到 Git 基本管理, 維護, 還有資料分枝。 此文章不會談到架設自己的 Git 伺服器 或 push 跟 pull 等跟別人合作的方法。
Git 指令快速查照
git init # 開始用 Git 來管理 git add . # 把所有檔案加入 Git git commit -a # 做成一個版本 git commit -a -m "your message here" # commit 時直接寫訊息, 不用到下個螢幕上寫 git add mine.txt # 把 mine.txt 加入管理 git tag v0.02 # 本版別名取為 v0.02 git tag v0.03 40e3e # 把版本 40e3e 取名為 v0.03; git status # 查詢從上個版本到現在哪些檔案被修改 git diff # 查詢從上個版本到現在哪幾行被修改 git diff v0.01 v0.03 # 查詢兩個版本間的差異 git diff v0.01:story.txt v0.03:story.txt # 查詢兩個版本間某個檔案的差異 git log # 查詢有史以來哪幾個檔案被修改 git log -p # 查詢有史以來哪幾行被修改 git log --stat --summary # 查詢每個版本間變動的檔案跟行數 git show v1.01 # 查詢 v1.01 版裡的修改內容 git show v1.01:story.txt # 叫出在 v1.01 版時的 story.txt 檔案 git show v1.01:story.txt > test.txt # 把 v1.01 版的 story.txt 抓出來,放入一個叫 test.txt 的檔案 git show HEAD # 看此版本修改的資料 git show HEAD^ # 看此版本前一版的修改的資料 git show HEAD^^ # 看此版本前前一版的修改的資料 git show HEAD~4 # 看此版本前前前前一版的修改的資料 >_< git grep "貓" v0.01 # 查詢 0.01 版裡頭有沒有貓 git grep "熊" # 查詢現在的版本裡有沒有熊 git branch 加英文版 # 建立新的分枝叫 "加英文版" git branch # 查看現有的分枝 git branch beta v0.5 # 依照 "v0.5" 版裡的內容來建立一個叫 "beta" 的分枝 git checkout 加英文版 # 換到 "加英文版" 的分枝上 git checkout master # 換到主幹上 git branch -d 加英文版 # 刪除 "加英文版" 分枝 gitk # 版本瀏覽軟體 git gui # 瀏覽從上一版本到現在變動的地方; 沒辦法顯示中文 git gc # 維護 Git 的資料庫 git fsck --full # 同上, 三不五時下次指令
想當初 - Git 的背景
想當初 Subversion 的教學文章才開始寫沒幾天,RoR 的人馬又瘋去用 Git 了。 RoR 用的技術常常換真的有點受不了 o.O 但 Git 用了幾天之後, 覺得它比 Subversion 好用多了。 Git 不僅可以來管程式碼的版本, 寫書時也是蠻有用的。 凡是要保留不同版本的文字檔, Git 都可以用的到。 文字檔如果改好後, 下個
git commit -a
指令, 就可以把目前的版本保留下來。 以後寫到第 x 版時突然發覺一個有用的程式碼被刪掉了, 就可以回到以前的版本來復原。 Migration 的用途是在資料庫的版本控制,而 Git 的用途是在文字檔的版本控制。
Git 是 Linus Torvalds 寫的, 也就是寫企鵝系統 Linux 的那個。 你可以看看他 介紹 Git 的影片。 裡面一直說用 Subversion 的人很笨,所以他聽起來有點沒水準。 世上的人不可能每個人都像他那麼聰明吧? Linux 的版本管理就是用 Git 的喔, 所以 Git 也很適合超大型的檔案版本管理。 Git 很快也很省空間。 它利用分散式的管理方法, 也就是說每個人下載的版本裡有包含有史以來更改過的紀錄。 沒有一個人的版本是主要的版本, 你喜歡把自己的版本怎麼改就怎麼改, 那你喜歡接受任何人的更改就接受人和人的更改。 這樣來說,不就天下大亂? Linus 說不會, 因為他只接收他信任的人的修改, 不會隨便去跟阿貓阿狗的程式碼合併。 而他信任的人也同樣的只會接受他們信任的人的修改, 依此類推。
Subversion 用起來是蠻麻煩的, 討厭的地方就是它會放個 .svn 檔在每一個檔案夾裡, 所以沒有用 Subversion 時還要把哪些檔案清掉。 另外, 單機用 Subversion 時還要架伺服器, commit 來 commit 去後才能開始用, 而且 log 一亂掉後就不能再 commit 了, 又得重來, 一肚子火。 相信以後 git 也可能會一肚子火, 但目前用的還蠻愉快的
安裝 和 設定 Git
Git 可以來這裡下載。 如果你是用 Linux 或蘋果的話,下這些指令來安裝 (版本號碼要用最新的喔, 這是寫文章時最新版):
curl http://kernel.org/pub/software/scm/git/git-1.5.5.1.tar.gz -O gunzip git-1.5.5.1.tar.gz ; tar -xvf git-1.5.5.1.tar; cd git-1.5.5.1 make configure ./configure --prefix=/usr/local make all sudo make install
安裝好後設定自己的名字跟信箱, 以後跟別人合作案子時名字就會顯示:
git config --global user.name "myNameHere" git config --global user.email myEmail@gmail.com
來看看 config 的內容:
git config -l
Git 基本操作
建立一個檔案夾叫 myGitProject, 我們會用這個來模擬版本控制。 檔案夾裡放一個 story.txt, story.txt 的內容是:
很久很久以前, 在個遙遠的森林裡, 一隻兔子遇到了一隻貓。
然後到 myGitProject 的目錄下個指令:
git init
Git 就會回覆
Initialized empty Git repository in .git/
這時候一個隱藏的檔案夾叫 .git 就會出現。 檔案夾要用
ls -l -a
才會顯現。

所有的版本資料都會存在這個檔案夾裡, 不會像 Subversion 也存在每個子檔案夾裡頭。
現在把所有檔案加進去管理系統 :
git add .
然後 commit。 Commit 會把改過的資料用成一個版本。
git commit
Commit 時, Git 會叫你輸入標題和更改筆記。 第一行是標題, 第二行要空白,第三行之後填寫這次更改了什麼。 Git 會自動幫你寫一些內容, 像說哪些檔案是新增的等等。 如果要用它寫的內容, 就把每一行前的 “#” 刪掉就好了。 我把標題寫成 “新的故事”, 內容寫 “第一次的草稿”:

在 蘋果/Linux 上, 要修改時要先按 i 鍵, 改好後要按 esc 鍵,然後打
:wq
:wq 就是儲存然後離開的意思。
Commit 完後, 進去 story.txt 把內容改成如下:
很久很久以前, 在個遙遠的森林裡, 一隻兔子遇到了一隻熊。 熊問兔子說: 你上大號時, 大便會沾到毛上嗎?
改完之後,我們再新增一個檔案叫 joke.txt。 裡面的內容是 “我自己發明的笑話”。 這個檔案也是要加入 Git 的檔案管理:
git add joke.txt
diff - 查看改了什麼
加入之後, 來看看這次更改了什麼:
git diff

紅色的就是刪掉的, 綠色的是新增的。 每一行如有動到任和東西就算是整行有更改。
git status

看看哪個檔案新增了,哪個被修改,還有哪些 Git 沒有在管理。 .DS_Store 是蘋果電腦裡的紀錄,不用管它。
commit 建立版本
看完之後下指令:
git commit -a
然後填內容:

我們再來用最後一版。 打開 story.txt, 加入這行:
兔子開口說: 不會阿。。。 還沒說完時, 熊抓起兔子, 把牠當做衛生紙了。
然後打開 joke.txt, 加入這行:
如果我會寫笑話, 就不用在這裡寫程式了。
然後 commit 並填內容:

gitk
Git 有內建一個版本瀏覽軟體叫 gitk, 下個 “gitk” 指令那個軟體就會跑出來:

中文字有些地方怪怪的。 右手邊在 Comments joke.txt story.txt 上方有個 “Patch”, 要選那個它才會顯示每個版本修改了哪行。 Gitk 裡的自型如果不夠大的話, 可以按 Ctrl 跟 + 鍵 或是 蘋果鍵跟 + 鍵。
版本命名
下指令:
git log

它就顯示一串很長的字: commit 40e3e4c9ef7bf86e7a82973eebc4b538156f28a3
40e3e4c9… 等等這串字就是這版本的名稱。 你如果邊讀這文章和邊自己做的話, 你的版本名稱會不一樣, 因為名稱是即時產生的。 你會想說, 為什麼名字要這麼長, 這麼麻煩呢? 其實那個名字是利用每版裡的資料來 hash 的, 這樣的話它就可以保證內容不會被竄改。 而且 hash 時也有用到作者的名稱還有 commit 時的時間。 如果內容有被改過, 它就不可能會 hash 出同一個字串了。 Git 把每個版本當作一個 object, 那個 object 的名稱就是這串 40 碼的名稱。
要看版本的內容不用整個名字都輸入, 只要輸入到和別的版本名字有差別就好:
git show 40e3
用 40e3e 也可以, 或 40e3e4, 依此類推。 40e3 不是好記的名字, 所以我們可以給它別名:
git-tag v0.03 40e3e
好了後用
git show v0.03
就可以看到同樣的內容了。 我也順便把第一跟第二版命名了。 版本的名字不用一定是 v0.xx, 用任何的文字都可以。
揪出某某版的檔案
如果要看看 story.txt 在第 v0.02 版時是長得怎樣, 可以下此指令:
git show v0.02:story.txt

也可把第 v0.02 版的 story.txt 抓出來,放入一個叫 test.txt 的檔案:
git show v0.02:story.txt > test.txt
這樣的話就可以隨時拿出在某某版本的某某資料。 看完 text.txt 要記得刪掉。
比較版本
版本都命名後, 我可以隨便指定兩個版本來看他們的差別。 例如, 我想看看 v0.01 跟 v0.03 有甚差別:
git diff v0.01 v0.03

反方向查也可以喔
git diff v0.03 v0.01

比較版本時也可以比較版本裡的某個檔案:
git diff v0.01:story.txt v0.03:story.txt

git diff 的功能跟 git log 很接近, 只是 git log 多了個版本的名稱和 commit 時寫的筆記:

git log # 查詢有史以來哪幾個檔案被修改
git log -p # 查詢有史以來哪幾行被修改
git log –stat –summary #查詢每個版本間變動的檔案跟行數
git log –stat –summary 的內容:

收尋版本裡的資料
git grep "貓" v0.01
這指令會去 v0.01 版裡查看看有沒有 “貓” 這個字。 如果沒有指定版本名稱的話, 它就會在目前的版本裡找。

長樹枝了
寫程式時, 有時候會安裝別人寫好的程式碼。 一邊安裝一邊修改自己的程式, 安裝到最後發現新的程式不能用, 這時要復原時又忘了自己的程式怎麼改回來。 這個問題最好解決的方法就是在加入新的程式時先試試看。 如果我們把整個程式的發展當作一顆樹在長, 加入新碼來測驗就是當作樹枝分叉了 (branching)。 如果測試成功, 就可以把樹枝收回來樹幹使用 (merging)。 如果不成功, 把整個樹枝砍掉就好。
現在來測試一個新的樹枝叫 “加英文版”
git branch 加英文版
跑個
git branch

就會顯示一個樹幹叫 master, 另一個樹枝叫 “加英文版”。 有 * 的就是我們目前在的地方。 要跳到 “加英文版” 上的話, 下令:
git checkout 加英文版
這時候我們不管再如何修改文件,都不會動到 master 樹幹裡的文件。 來試試看。 打開 story.txt, 把這段加到後頭然後存檔:
A rabit meets a bear in the woods. The bear says to the rabbit, "Do you have problem with poop sticking to your fur?" The rabbit replies "no" ...as the bear picks him up and wipes his behind with him
存檔後下此指令來製作新版本:
git commit -a

並換回 master 樹幹:
git checkout master
目前 “加英文版” 的樹枝裡應有我們剛剛加的英文笑話, 而 master 裡應該沒有。 打開 story.txt, 裡面果然只有中文。 這時我們把 master 裡的 story.txt 加了一行
兔子大叫 "#$@&"!
改了之後
git commit -a
這樣兩支線有不同的資料, 來合併看看會怎樣:
git merge 加英文版

囧! 抱怨了。 電腦沒辦法自動把兩個不同的支線合併,因為同一行被更改了。 下個指令
git diff
就會跟你說衝突在哪:

打開 story.txt 你會發現 Git 把內容裡多加了
<<<<<<< HEAD:story.txt
和
++>>>>>>> 加英文版:story.txt
等字樣, 而且裡面包含了兩個支線的資料。 我們把多餘的文字和 “兔子大叫 “#$@&”!” 刪掉, 然後
git commit -a

這時我們把 gitk 軟體啟動
gitk
就可以看到分支的圖片

因為 “加英文版” 已經跟 master 合併了, 我們就可以把 “加英文版” 刪掉:
git branch -d 加英文版
刪掉後支線圖變這樣:

上面的 -d 指令會確保說分枝刪除前要先回併到主幹上, 如果你要放棄分枝裡修改的內容, 就下大寫的 D:
git branch -D 加英文版
Ruby on Rails 上使用
在 RoR 上的使用方法跟平常的文字檔案版本管理沒什麼兩樣, 只要在專案的根目錄多加一個 .gitignore 檔。 在 Linux/蘋果 系統上, 檔案名稱前加個點 “.” 的話, 那個檔案就是隱藏的。 .gitignore 檔的目地就是不要去管理一些紀錄檔, 臨時檔等等有的沒有的。 放在裡面的檔案名稱通通都會被排除在文件管理之外。 .gitignore 裡的內容放這些:
.DS_Store log/*.log tmp/**/* config/database.yml
.DS_Store 是蘋果在每個檔案夾放的東西, 不用管他。 log 就是紀錄檔, 我們沒必要去做版本管理, 因為它常常變。 tmp 是臨時檔。 database.yml 不要管因為我們不希望記錄裡面資料庫的密碼。 如果你有用 SQL Lite 的話, 就該加個 db/*.sqlite3 如果用 eclipse 或 netbeans 類的, 要看看需不需要加個 .project 或 .settings
每個 rails 的專案下都會有固定的檔案夾, 像 app 和 config 和 doc 等等。 如果某些檔案夾是空的話, Git 不會把那些檔案夾加入版本控管。 在 rails 的專案裡, 一開始 tmp 和 log 和 vendor 都是空的, 但我們要把這些檔案夾加入版本控管, 所以我們在這三個夾裡放空的 .gitignore 檔, Git 才會誤以為檔案夾裡有東西, 並而控管檔案夾:
touch tmp/.gitignore log/.gitignore vendor/.gitignore
這指令下後, 三個空白的 .gitignore 檔案會出現在三個檔案夾裡, 這樣就好。
現在下令:
git add . git commit -a
就可以開始修改專案檔案了。
Git 維護
三不五時下個維護的指令可以讓 Git 佔的空間變小:
git count-objects
Git 會回答類似:
11 objects, 44 kilobytes
這表示 11 個 object 散掉了, 浪費了 44 kilobytes 的空間。 要解決這個問題就下指令:
git gc
下這個指令來維持 Git 的健康, 它怎麼維持的我也不知道:
git fsck --full
July 4th, 2008 at 9:47 am
3Q!
July 13th, 2008 at 4:09 am
很好的文章,谢谢。
用事例做起来比较简单,看到其他的文章都是空讲的很抽象。
July 17th, 2008 at 5:32 pm
應該是.gitignore而不是.ignore吧?
July 17th, 2008 at 5:47 pm
喔!謝謝!真抱歉。 改好了。 >_<
December 4th, 2008 at 2:36 pm
Hi!
寫得真好:)
剛好我需要看這種文件
只是好像沒有伺服器部分的教學=_=?
多謝!
By CFC
December 4th, 2008 at 2:59 pm
對阿,我自己沒有用到伺服器的部份所以沒有經驗也就沒寫了 >_<
December 4th, 2008 at 4:22 pm
jserv的slide:
http://jserv.sayya.org/writing/loving-git.pdf
可以參考看看
December 8th, 2008 at 6:01 pm
git daemon start
可以啟動伺服器:)