Git 的使用感受
從開始工作到現在,在公司里面一直用 svn 來做版本管理。大約半年前聽說了 Git,因為 Git 的光輝相當耀眼,作者是 Linus Torvalds,被大量的開源軟件采用,如 jQuery, Perl, Qt, ROR, YUI, GNOME 等,所以決定學一學。
比較慶幸的是,國內有一本較好的介紹 Git 的書:《Git 權威指南》。
我大概花了一個月的周末時間來學習它。在這里總結一下使用 Git 的感受,主要是和 SVN 來做一些比較,以便突出 Git 的特點。
學習成本
首先我感覺 Git 的學習成本還是比較高的。svn 基本上不到 20 個命令就可以應付日常的工作了,而 Git 有上百個命令。我在學習 SVN 的時候,基本上沒有看什么書,最多就是在網上隨便看了一些貼子,就基本會使用 SVN 了。而我花在 Git 的學習時間算下來,至少有 1 周。
因為 Git 的學習成本較高,所以當一個會 svn 的同學剛剛接觸 Git 的時候,如果簡單地把 Git 當 SVN 用,就會感覺 Git 相當難用。我在公司就時常聽到同事抱怨它。所以我認為,要想真正用好 Git,還是需要投入時間來學習它,否則是很難使用的。
Git 的內部結構
Git 真正是一個面向程序員的工具,它的內部數據結構是一個有向無環圖,並且,你必須理解它的內部數據結構后,才能掌握它。因為你的很多操作,都其實對應的是這個有向無環圖的操作。比如:
- git commit 就是增加一個結點。
- git commit –amend 就是改發一個結點。
- git reset 就是修改 HEAD 指向的結點。
另外,Git 內部包括三個區域:工作區,暫存區和版本庫。
- git add 是將工作區的內容保存到暫存區
- git checkout 是將暫存區的內容覆蓋工作區
- git commit 是將暫存區的內容保存到版本庫
- git reset 默認情況下是將版本庫的內容覆蓋工作區
- git diff 也有三種情況,分別是比較工作區與暫存區,工作區與版本庫,暫存區與版本庫之間的差別
了解了 Git 的內部結構,對於這些 Git 的命令就更加理解了。
svn 的坑
svn 在平常使用上基本沒什么坑,平時通過svn pe svn:ignore .
設置好忽略的文件,以免誤把不應該加入版本管理的文件加進來。
我唯一遇到的一次問題是這樣的:我有一個目錄要加入 svn 的版本庫,但是目錄里面的一些文件不想加入。如果直接輸入 svn add 目錄名,就會把目錄下所有文件都加入到版本管理中。如果 cd 到那個目錄里面配置 svn:ignore,又會因為當前目錄還不在版本管理中,設置不了。最后找到的解決辦法是在 svn add 的時候增加 –non-recursive 參數:
svn add dirname --non-recursive
或者是:
$ svn add dirname --depth empty |
還有就是對於一些不小心用 svn add 加入了版本管理,但實際上不應該加的目錄。可以這么做:
svn export spool spool-tmp (這里 export 可以將原目錄中的 .svn 目錄給清除掉)
svn rm spool
svn ci -m 'Removing inadvertently added directory "spool".'
mv spool-tmp spool
svn propset svn:ignore 'spool' .
svn ci -m 'Ignoring a directory called "spool".' |
Git 的坑
- 在 windows 下的文件的權限因為無法和 linux 上完全一致,所以用 Git 檢出的文件權限可能顯示為被更改。
另外因為 windows 下的換行和 linux 上也不一樣,協作開發時也容易出問題。所以在 windows 上使用 Git 的同學需要加上以下 2 行配置參數:
git config --global core.filemode false
git config --global core.autocrlf true
第一句是忽略文件權限的改動。
第二句是將文件 checkout 時自動把 LF 轉成 CRLF,check in 時自動把 CRLF 轉成 LF |
- svn 的
svn revert filename
對應的其實是git checkout -- filename
, 而git revert xxx
是基於 xxx 提交所做的改動,做一次反向提交,和 svn revert 完全不一樣。
Git 的一些小技巧
節省clone體積
有些時候,我們只想簡單學習一下項目代碼,這個時候,用 git clone rep_address --depth 1
可以只 clone 每個文件最新的一個提交,這樣速度會快很多。
強制推送
一旦推送到遠程倉庫后,就不要用類似 git reset
, git ci --amend
, git rebase
等破壞性提交了,否則遠程倉庫會因為你的新推送不是 Fast Forward 而拒絕提交 (關於什么是 Fast Forward 要講的太多了,自已看書吧)。如果實在不小心做了。在確定別人沒有檢出前,用 git push -f
可以強制推送到遠程倉庫中。如下圖:

使用 git svn
在公司沒有應用 git 前,你可以用 git svn 來做管理。 git svn 相關命令:
git svn clone -r REV1:HEAD svn_addr local_addr
git svn dcommit 提交到 SVN
git svn fetch 從 svn up 信息
git svn rebase 將從 svn up 過來的信息,rebase 成 git 提交
git svn rebase --continue 沖突后繼續 rebase 信息 |
用 git svn clone 的時候,帶上 -r rev1:HEAD 參數,可以省去將 SVN 整個提交歷史抓取下來的時間。
設置常用命令的別名
在用戶的 home 目錄下,有一個 .gitconfig 文件,里面可以配置一些別名,方便平時的 git 操作。
特別是那些平日使用 SVN 的短命令習慣了的同學,配置一下別名后,使用 git 就會相當順手了。我配置的別名如下。這里特別多說一句,有些人喜歡將 ci 設置成 commit -a,這樣就不用 git add 來把需要提交的文件加入到暫存區了。在《Git 權威指南》中,作者極力反對這樣做。因為 Git 本身在提交前有 add 這步,就是為了讓提交者能夠審視自己的提交文件,以防止錯誤的提交發生。
[alias] st = status -s ci = commit l = log --oneline --decorate -13 ll = log --oneline --decorate co = checkout br = branch rb = rebase dci = dcommit
刪除不在 git 管理下的文件
如果你需要刪除 Git 下沒有加入到版本庫中的文件,可以使用:
git clean -nd 測試刪除
git clean -fd 真實刪除 |
搭建自己的遠程倉庫
搭建一個 Git 遠程倉庫相當簡單,直接在一台帶 SSH 的服務器上用 git init –bare dirname 即可。本地可以用 git remote 命令來設置多個遠程分支。另外,第一次提交的時候,因為遠程倉庫中沒有任何分支,需要用如下指令建立 master 分支:
git remote add origin yourname@yourhost.com:~/path/repository_name
git remote add add2 yourname@yourhost.com:~/path/repository_name
git push origin master
git push add2 master
// 如果 git remote add 設置地址寫錯了,可以用 git remote set-url 更改:
git remote set-url origin yourname@yourhost.com:~/path/repository_name |
如何用 Git 將一個文件的歷史提交恢復?
上次遇到一個問題,我某次提交改動了很多文件,但是其中有一個是不應該改的。所以我需要把這次提交中關於那個文件的改動撤銷。直接用 git checkout 命令可以檢出某一個文件的歷史版本,然后就可以將對這個文件的改動取消了。如下:
git checkout CommitId fileName
git ci -m "revert a file modification" |
本地工作區還有未提交的內容時,不能 pull?
可以先用 git stash 將內容暫存,然后再 pull,成功后再 git stash pop 將修改恢復。
提交的郵箱錯了?
有些時候,因為同時在 github 和公司內部做提交,所以用 2 個不同的郵箱。如果一個新工程 clone 下來,忘了用 git config 來設置提交用戶名和郵箱,就有可能用錯誤的郵箱作為賬號名提交。這個時候,如果你只是錯了最近的一次提交而已,可以用如下命令來將最近的一次提交作者名和郵箱修改:
git config user.email your-email@163.com |
如果等你發現的時候,已經錯了很多提交了。可以用如下命令來一次性修改多個提交的用戶名和郵箱:
git filter-branch -f --env-filter " |
提交的時候自動去掉源碼末尾的空格
源碼末尾的空格幾乎都是無意義的,應該去掉的。大多數 review 系統,都會將源碼末尾的空格標紅。所以,我們何不在提交時讓 git 自動幫我們去掉這些空格呢?這個可以通過設置 git 的 hook 來實現,具體方法如下:
-
用 vim 編輯一個名為 pre-commit 的文件:
vim .git/hooks/pre-commit
-
輸入如下代碼,保存退出 vim
#!/bin/sh
if git-rev-parse --verify HEAD >/dev/null 2>&1 ; then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
# Find files with trailing whitespace
for FILE in `exec git diff-index --check --cached $against -- | sed '/^[+-]/d' | sed -E 's/:[0-9]+:.*//' | uniq` ; do
# Fix them!
sed -i '' -E 's/[[:space:]]*$//' "$FILE"
git add "$FILE"
done |
- 增加 pre-commit 的運行權根:
chmod +x .git/hooks/pre-commit
讓常用操作自動帶顏色
默認的 git diff, status, log 什么的都是不帶顏色的,可以用如下命令讓它們都帶上顏色。另外還有一些有趣的命令,一並列在下面。
git config --global --add user.email "email@163.com"
git config --global --add user.name "your name"
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status -s
git config --global alias.l log --oneline --decorate -12
git config --global color.diff auto
git config --global color.status auto
git config --global color.branch auto
git config --global merge.tool kdiff3
git config --global meregtool.kdiff3.path "/usr/bin/kdiff3"
git config --global alias.visual "!gitk" |
自動補全 git 命令
-
安裝 bash-completion: brew install bash-completion
-
按要求把以下代碼增加到 .bash_profile 文件中:
if [ -f `brew --prefix`/etc/bash_completion ]; then
. `brew --prefix`/etc/bash_completion
fi |
- 下載 bash-completion 對於 Git 的支持
cd /usr/local/etc/bash_completion.d/ |
一些 Git 的資料
- Git Magic 很通俗的一本介紹 Git 的書,比較短小精煉。
- Pro Git 全面介紹 Git 的書,非常詳細。
- 《Git 權威指南》 中國人寫的一本介紹 Git 的書,也非常通俗。我個人主要就是通過這本書來學習 Git 的。
- Github 基於 Git 的開源網站。在 Github 的托管的項目相當多,著名的有:rails, jquery, node, homebrew, three20, jekyll, jquery-ui, backbone, coffee-script, tornado, redis, underscore, asi-http-request, django。