記錄一些Git的工作原理和最基本的使用
文件的三種狀態
在 Git 內部中,任何一個文件可分爲三種狀態:已提交(committed),已修改(modified)和已暫存(staged)。已提交表示該文件已經被安全地保存在本地數據庫中了;已修改表示修改了某個文件,但還沒有提交保存;已暫存表示把已修改的文件放在下次提交時要保存的清單中。
因此,Git 管理項目時,文件流轉在三個工作區域:Git 的工作目錄,暫存區域,本地倉庫。
每個項目都有一個 Git 目錄,如果 git clone
出來的話,就是其中 .git
的目錄。它是 Git 用來保存元數據和對象數據庫的地方。每次克隆鏡像倉庫的時候,實際拷貝的就是這個目錄裏面的數據。
從項目中取出某個版本的所有文件和目錄,用以開始後續工作的叫做工作目錄。這些文件實際上都是從 Git 目錄中的壓縮對象數據庫中提取出來的,接下來就可以在工作目錄中對這些文件進行編輯。
所謂的暫存區域只不過是個簡單的文件,一般都放在 Git 目錄中。有時候人們會把這個文件叫做索引文件,不過標準說法還是叫暫存區域。
基本的 Git 工作流程如下:
- 在工作目錄中修改某些文件。
- 對修改後的文件進行快照,然後保存到暫存區域。
- 提交更新,將保存在暫存區域的文件快照永久轉儲到 Git 目錄中。
取得項目的 Git 倉庫
有兩種取得 Git 項目倉庫的方法。第一種是在現存的目錄下,通過導入所有文件來創建新的 Git 倉庫。第二種是從已有的 Git 倉庫克隆出一個新的鏡像倉庫來。
在工作目錄中初始化新倉庫
要對現有的某個項目開始用 Git 管理,只需到此項目所在的目錄,執行:
$ git init
初始化後,在當前目錄下會出現一個名爲 .git 的目錄,所有 Git 需要的數據和資源都存放在這個目錄中。
不過此時,我們只是在該目錄下初始化好了Git的相關結構,但是此時Git還沒有開始跟蹤管理項目中的任何一個文件。
如果當前目錄下有幾個文件想要納入版本控制,需要先用 git add
命令告訴 Git 開始對這些文件進行跟蹤,然後提交:
$ git add *.c //將所有的.c後綴文件加入跟蹤
$ git add README
$ git commit -m 'initial project version'
從現有倉庫克隆
如果想克隆別人已有的倉庫,可以使用git clone
命令。
克隆倉庫的命令格式爲 git clone [url]
例如:$ git clone git://github.com/schacon/grit.git
這會在當前目錄下創建一個名爲grit
的目錄,其中包含一個 .git
的目錄,用於保存下載下來的所有版本記錄,然後從中取出最新版本的文件拷貝。
如果希望在克隆的時候,自己定義要新建的項目目錄名稱,可以在上面的命令末尾指定新的名字:
$ git clone git://github.com/schacon/grit.git mygrit
Git 支持許多數據傳輸協議。
git協議:git://
,http協議:http(s)://
,ssh協議:user@server:/path.git
記錄每次更新到倉庫
工作目錄下面的所有文件都不外乎這兩種狀態:已跟蹤或未跟蹤。已跟蹤的文件是指本來就被納入版本控制管理的文件,在上次快照中有它們的記錄,工作一段時間後,它們的狀態可能是未更新,已修改或者已放入暫存區。而所有其他文件都屬於未跟蹤文件。它們既沒有上次更新時的快照,也不在當前的暫存區域。初次克隆某個倉庫時,工作目錄中的所有文件都屬於已跟蹤文件,且狀態爲未修改。
檢查當前文件狀態
可以用 git status
命令確定哪些文件當前處於什麼狀態。
如果在克隆倉庫之後立即執行此命令,會看到類似這樣的輸出:
$ git status
# On branch master
nothing to commit (working directory clean)
這說明所有已跟蹤文件在上次提交後都未被更改過。
創建一個新文件 README
,保存退出後運行git status
會看到該文件出現在未跟蹤文件列表中:
$ vim README
$ git status
# On branch master
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# README
nothing added to commit but untracked files present (use "git add" to track)
在狀態報告中可以看到新建的README文件出現在“Untracked files”
下面,這說明此時README
並未被Git跟蹤。
跟蹤新文件
使用命令 git add
開始跟蹤一個新文件。
$ git add README
此時再運行 git status
命令,會看到 README 文件已被跟蹤,並處於暫存狀態:
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: README
#
在 git add
後面可以指明要跟蹤的文件或目錄路徑。如果是目錄的話,就說明要遞歸跟蹤該目錄下的所有文件。
暫存已修改文件
現在我們修改下之前已跟蹤過的文件benchmarks.rb
,然後再次運行 status
命令:
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: README
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
#
# modified: benchmarks.rb
#
文件 benchmarks.rb
出現在 “Changes not staged for commit” 這行下面,說明已跟蹤文件的內容發生了變化,但還沒有放到暫存區。要暫存這次更新,需要運行 git add
命令。
git add
是個多功能命令,根據目標文件的狀態不同,此命令的效果也不同:可以用它開始跟蹤新文件,或者把已跟蹤的文件放到暫存區,還能用於合併時把有衝突的文件標記爲已解決狀態等。
git status
的輸出:
$ git add benchmarks.rb
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: README
# modified: benchmarks.rb
#
現在兩個文件都已暫存,下次提交時就會一併記錄到倉庫。假設此時,在 benchmarks.rb
裏再加條註釋,重新編輯存盤後,準備好提交。此時運行git status
,會看到這樣的情況:
$ vim benchmarks.rb
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# new file: README
# modified: benchmarks.rb
#
# Changes not staged for commit:
# (use "git add <file>..." to update what will be committed)
#
# modified: benchmarks.rb
#
benchmarks.rb
文件出現了兩次,一次算未暫存,一次算已暫存。Git 只不過暫存了你運行 git add
命令時的版本,如果現在提交,那麼提交的是添加註釋前的版本,而非當前工作目錄中的版本。所以,運行了 git add
之後又作了修訂的文件,需要重新運行 git add
把最新版本重新暫存起來。
忽略某些文件
一般我們總會有些文件無需納入 Git 的管理,也不希望它們總出現在未跟蹤文件列表。通常都是些自動生成的文件,比如日誌文件,或者編譯過程中創建的臨時文件等。我們可以創建一個名爲 .gitignore
的文件,列出要忽略的文件模式。
例如:
$ cat .gitignore
*.[oa]
*~
第一行告訴 Git 忽略所有以 .o
或 .a
結尾的文件。第二行告訴 Git 忽略所有以波浪符(~)結尾的文件。
查看已暫存和未暫存的更新
如果要查看具體修改了什麼地方,可以用 git diff
命令。
要查看尚未暫存的文件更新了哪些部分,不加參數直接輸入 git diff:
$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..da65585 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
@commit.parents[0].parents[0].parents[0]
end
+ run_code(x, 'commits 1') do
+ git.commits.size
+ end
+
run_code(x, 'commits 2') do
log = git.commits('master', 15)
log.size
此命令比較的是工作目錄中當前文件和暫存區域快照之間的差異,也就是修改之後還沒有暫存起來的變化內容。
若要看已經暫存起來的文件和上次提交時的快照之間的差異,可以用 git diff --staged
命令。
$ git diff --cached
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README2
@@ -0,0 +1,5 @@
+grit
+ by Tom Preston-Werner, Chris Wanstrath
+ http://github.com/mojombo/grit
+
+Grit is a Ruby library for extracting information from a Git repository
提交更新
使用git commit
命令,將暫存區域的文件提交到git倉庫中。
這種方式會啓動文本編輯器以便輸入本次提交的說明。
通常我們在後面加上 -m參數,參數後直接跟這次提交的註解,可以直接在一行命令中提交。
例如:
$ git commit -m "Story 182: Fix benchmarks for speed"
[master]: created 463dc4f: "Fix benchmarks for speed"
2 files changed, 3 insertions(+), 0 deletions(-)
create mode 100644 README
跳過使用暫存區域
每次提交前,我們都要先使用git add
將修改後的文件,放入暫存區,纔可以使用git commit
命令提交。
如果想省事,我們可以直接跳過使用暫存區域,直接提交更新後的文件。
Git 提供了一個跳過使用暫存區域的方式,只要在提交的時候,給 git commit
加上 -a
選項,Git 就會自動把所有已經跟蹤過的文件暫存起來一併提交,從而跳過 git add
步驟。
移除文件
要從 Git 中移除某個文件,就必須要從已跟蹤文件清單中移除(確切地說,是從暫存區域移除),然後提交。可以用 git rm
命令完成此項工作,並連帶從工作目錄中刪除指定的文件,這樣以後就不會出現在未跟蹤文件清單中了。
另外一種情況是,我們想把文件從 Git 倉庫中刪除(亦即從暫存區域移除),但仍然希望保留在當前工作目錄中。換句話說,僅是從跟蹤清單中刪除。用 --cached
選項即可:
$ git rm --cached readme.txt
重命名文件
要在 Git 中對文件改名,可以使用 git mv
命令。
例如:$ git mv file_from file_to
其實,運行 git mv
就相當於運行了下面三條命令:
$ mv README.txt README
$ git rm README.txt
$ git add README
查看提交歷史
可以使用 git log
命令查看提交歷史。
默認不用任何參數的話,git log
會按提交時間列出所有的更新,最近的更新排在最上面。每次更新都有一個 SHA-1 校驗和、作者的名字和電子郵件地址、提交時間,最後縮進一個段落顯示提交說明。
我們常用 -p
選項展開顯示每次提交的內容差異,用 -2
則僅顯示最近的兩次更新:
$ git log -p -2
撤消操作
修改最後一次提交
有時候我們提交完了才發現漏掉了幾個文件沒有加,或者提交信息寫錯了。想要撤消剛纔的提交操作,可以使用 --amend
選項重新提交:
$ git commit --amend
此命令將使用當前的暫存區域快照提交。如果剛纔提交完沒有作任何改動,直接運行此命令的話,相當於有機會重新編輯提交說明,但將要提交的文件快照和之前的一樣。
啓動文本編輯器後,會看到上次提交時的說明,編輯它確認沒問題後保存退出,就會使用新的提交說明覆蓋剛纔失誤的提交。
如果剛纔提交時忘了暫存某些修改,可以先補上暫存操作,然後再運行 --amend
提交:
$ git commit -m 'initial commit'
$ git add forgotten_file
$ git commit --amend
上面的三條命令最終只是產生一個提交,第二個提交命令修正了第一個的提交內容。
取消已經暫存的文件
下面的例子,有兩個修改過的文件,我們想要分開提交,但不小心用 git add .
全加到了暫存區域。該如何撤消暫存其中的一個文件呢?其實,git status
的命令輸出已經告訴了我們該怎麼做:
$ git add .
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: README.txt
# modified: benchmarks.rb
#
就在 “Changes to be committed” 下面,括號中有提示,可以使用 git reset HEAD <file>...
的方式取消暫存。好吧,我們來試試取消暫存 benchmarks.rb
文件:
$ git reset HEAD benchmarks.rb
benchmarks.rb: locally modified
$ git status
# On branch master
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: README.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: benchmarks.rb
#
取消對文件的修改
如果覺得剛纔對 benchmarks.rb
的修改完全沒有必要,該如何取消修改,回到之前的狀態(也就是修改之前的版本)呢?git status
同樣提示了具體的撤消方法:
# 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: benchmarks.rb
#
即,使用命令:
git checkout -- <file>...
遠程倉庫的使用
遠程倉庫是指託管在網絡上的項目倉庫,可能會有好多個,其中有些你只能讀,另外有些可以寫。同他人協作開發某個項目時,需要管理這些遠程倉庫,以便推送或拉取數據,分享各自的工作進展。
查看當前的遠程庫
要查看當前配置有哪些遠程倉庫,可以用 git remote
命令,它會列出每個遠程庫的簡短名字。在克隆完某個項目後,至少可以看到一個名爲 origin
的遠程庫,Git 默認使用這個名字來標識你所克隆的原始倉庫:
$ git clone git://github.com/schacon/ticgit.git
Initialized empty Git repository in /private/tmp/ticgit/.git/
remote: Counting objects: 595, done.
remote: Compressing objects: 100% (269/269), done.
remote: Total 595 (delta 255), reused 589 (delta 253)
Receiving objects: 100% (595/595), 73.31 KiB | 1 KiB/s, done.
Resolving deltas: 100% (255/255), done.
$ cd ticgit
$ git remote
origin
也可以加上 -v
選項,顯示對應的克隆地址:
$ git remote -v
origin git://github.com/schacon/ticgit.git
添加遠程倉庫
要添加一個新的遠程倉庫,可以指定一個簡單的名字,以便將來引用,運行 git remote add [shortname] [url]
:
$ git remote add pb git://github.com/paulboone/ticgit.git
$ git remote -v
pb git://github.com/paulboone/ticgit.git
從遠程倉庫抓取數據
如果設置了某個分支用於跟蹤某個遠端倉庫的分支,可以使用 git pull 命令自動抓取數據下來,然後將遠端分支自動合併到本地倉庫中當前分支。
推送數據到遠程倉庫
項目進行到一個階段,要同別人分享目前的成果,可以將本地倉庫中的數據推送到遠程倉庫:
git push [remote-name] [branch-name]
如果要把本地的 master
分支推送到 origin
服務器上(再次說明下,克隆操作會自動使用默認的 master
和 origin
名字),可以運行下面的命令:
$ git push origin master
只有在所克隆的服務器上有寫權限,或者同一時刻沒有其他人在推數據,這條命令纔會如期完成任務。如果在你推數據前,已經有其他人推送了若干更新,那你的推送操作就會被駁回。你必須先把他們的更新抓取到本地,合併到自己的項目中,然後纔可以再次推送。
查看遠程倉庫信息
我們可以通過命令 git remote show [remote-name]
查看某個遠程倉庫的詳細信息,比如要看所克隆的 origin
倉庫,可以運行:
$ git remote show origin
* remote origin
URL: git://github.com/schacon/ticgit.git
Remote branch merged with 'git pull' while on branch master
master
Tracked remote branches
master
ticgit
遠程倉庫的刪除和重命名
可以用 git remote rename
命令修改某個遠程倉庫在本地的簡稱,比如想把 pb 改成 paul:
$ git remote rename pb paul
$ git remote
origin
paul
碰到遠端倉庫服務器遷移,或者原來的克隆鏡像不再使用,又或者某個參與者不再貢獻代碼,那麼需要移除對應的遠端倉庫,可以運行 git remote rm
命令:
$ git remote rm paul
$ git remote
origin