原文:《Pro Git》
2.1 Git 基礎 - 獲取 Git 倉庫
假如你只能閱讀一章來學習 Git,本章就是你的不二選擇。 本章內容涵蓋你在使用 Git 完成各種工作中將要使用的各種基本命令。 在學習完本章之后,你應該能夠配置並初始化一個倉庫(repository)、開始或停止跟蹤(track)文件、暫存(stage)或提交(commit)更改。 本章也將向你演示如何配置 Git 來忽略指定的文件和文件模式、如何迅速而簡單地撤銷錯誤操作、如何瀏覽你的項目的歷史版本以及不同提交(commits)間的差異、如何向你的遠程倉庫推送(push)以及如何從你的遠程倉庫拉取(pull)文件。
獲取 Git 倉庫
有兩種取得 Git 項目倉庫的方法。 第一種是在現有項目或目錄下導入所有文件到 Git 中; 第二種是從一個服務器克隆一個現有的 Git 倉庫。
在現有目錄中初始化倉庫
如果你打算使用 Git 來對現有的項目進行管理,你只需要進入該項目目錄並輸入:
1 $ git init
該命令將創建一個名為 .git
的子目錄,這個子目錄含有你初始化的 Git 倉庫中所有的必須文件,這些文件是 Git 倉庫的骨干。 但是,在這個時候,我們僅僅是做了一個初始化的操作,你的項目里的文件還沒有被跟蹤。 (參見 Git 內部原理 來了解更多關於到底 .git
文件夾中包含了哪些文件的信息。)
如果你是在一個已經存在文件的文件夾(而不是空文件夾)中初始化 Git 倉庫來進行版本控制的話,你應該開始跟蹤這些文件並提交。 你可通過 git add
命令來實現對指定文件的跟蹤,然后執行 git commit
提交:
1 $ git add *.c 2 $ git add LICENSE 3 $ git commit -m 'initial project version'
稍后我們再逐一解釋每一條指令的意思。 現在,你已經得到了一個實際維護(或者說是跟蹤)着若干個文件的 Git 倉庫。
克隆現有的倉庫
如果你想獲得一份已經存在了的 Git 倉庫的拷貝,比如說,你想為某個開源項目貢獻自己的一份力,這時就要用到 git clone
命令。 如果你對其它的 VCS 系統(比如說Subversion)很熟悉,請留心一下你所使用的命令是"clone"而不是"checkout"。 這是 Git 區別於其它版本控制系統的一個重要特性,Git 克隆的是該 Git 倉庫服務器上的幾乎所有數據,而不是僅僅復制完成你的工作所需要文件。 當你執行 git clone
命令的時候,默認配置下遠程 Git 倉庫中的每一個文件的每一個版本都將被拉取下來。 事實上,如果你的服務器的磁盤壞掉了,你通常可以使用任何一個克隆下來的用戶端來重建服務器上的倉庫(雖然可能會丟失某些服務器端的掛鈎設置,但是所有版本的數據仍在,詳見 在服務器上搭建 Git )。
克隆倉庫的命令格式是 git clone [url]
。 比如,要克隆 Git 的可鏈接庫 libgit2,可以用下面的命令:
1 $ git clone https://github.com/libgit2/libgit2
這會在當前目錄下創建一個名為 “libgit2” 的目錄,並在這個目錄下初始化一個 .git
文件夾,從遠程倉庫拉取下所有數據放入 .git
文件夾,然后從中讀取最新版本的文件的拷貝。 如果你進入到這個新建的 libgit2
文件夾,你會發現所有的項目文件已經在里面了,准備就緒等待后續的開發和使用。 如果你想在克隆遠程倉庫的時候,自定義本地倉庫的名字,你可以使用如下命令:
1 $ git clone https://github.com/libgit2/libgit2 mylibgit
這將執行與上一個命令相同的操作,不過在本地創建的倉庫名字變為 mylibgit
。
Git 支持多種數據傳輸協議。 上面的例子使用的是 https://
協議,不過你也可以使用 git://
協議或者使用 SSH 傳輸協議,比如 user@server:path/to/repo.git
。 在服務器上搭建 Git 將會介紹所有這些協議在服務器端如何配置使用,以及各種方式之間的利弊。
2.2 Git 基礎 - 記錄每次更新到倉庫
記錄每次更新到倉庫
現在我們手上有了一個真實項目的 Git 倉庫,並從這個倉庫中取出了所有文件的工作拷貝。 接下來,對這些文件做些修改,在完成了一個階段的目標之后,提交本次更新到倉庫。
請記住,你工作目錄下的每一個文件都不外乎這兩種狀態:已跟蹤或未跟蹤。 已跟蹤的文件是指那些被納入了版本控制的文件,在上一次快照中有它們的記錄,在工作一段時間后,它們的狀態可能處於未修改,已修改或已放入暫存區。 工作目錄中除已跟蹤文件以外的所有其它文件都屬於未跟蹤文件,它們既不存在於上次快照的記錄中,也沒有放入暫存區。 初次克隆某個倉庫的時候,工作目錄中的所有文件都屬於已跟蹤文件,並處於未修改狀態。
編輯過某些文件之后,由於自上次提交后你對它們做了修改,Git 將它們標記為已修改文件。 我們逐步將這些修改過的文件放入暫存區,然后提交所有暫存了的修改,如此反復。所以使用 Git 時文件的生命周期如下:
檢查當前文件狀態
要查看哪些文件處於什么狀態,可以用 git status
命令。 如果在克隆倉庫后立即使用此命令,會看到類似這樣的輸出:
1 $ git status 2 On branch master 3 nothing to commit, working directory clean
這說明你現在的工作目錄相當干凈。換句話說,所有已跟蹤文件在上次提交后都未被更改過。 此外,上面的信息還表明,當前目錄下沒有出現任何處於未跟蹤狀態的新文件,否則 Git 會在這里列出來。 最后,該命令還顯示了當前所在分支,並告訴你這個分支同遠程服務器上對應的分支沒有偏離。 現在,分支名是 “master”,這是默認的分支名。 我們在 Git 分支 會詳細討論分支和引用。
現在,讓我們在項目下創建一個新的 README 文件。 如果之前並不存在這個文件,使用 git status
命令,你將看到一個新的未跟蹤文件:
$ echo 'My Project' > 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
下面。 未跟蹤的文件意味着 Git 在之前的快照(提交)中沒有這些文件;Git 不會自動將之納入跟蹤范圍,除非你明明白白地告訴它“我需要跟蹤該文件”, 這樣的處理讓你不必擔心將生成的二進制文件或其它不想被跟蹤的文件包含進來。 不過現在的例子中,我們確實想要跟蹤管理 README 這個文件。
跟蹤新文件
使用命令 git add
開始跟蹤一個文件。 所以,要跟蹤 README 文件,運行:
$ 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
只要在 Changes to be committed
這行下面的,就說明是已暫存狀態。 如果此時提交,那么該文件此時此刻的版本將被留存在歷史記錄中。 你可能會想起之前我們使用 git init
后就運行了 git add (files)
命令,開始跟蹤當前目錄下的文件。 git add
命令使用文件或目錄的路徑作為參數;如果參數是目錄的路徑,該命令將遞歸地跟蹤該目錄下的所有文件。
暫存已修改文件
現在我們來修改一個已被跟蹤的文件。 如果你修改了一個名為 CONTRIBUTING.md
的已被跟蹤的文件,然后運行 git 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) (use "git checkout -- <file>..." to discard changes in working directory) modified: CONTRIBUTING.md
文件 CONTRIBUTING.md
出現在 Changes not staged for commit
這行下面,說明已跟蹤文件的內容發生了變化,但還沒有放到暫存區。 要暫存這次更新,需要運行 git add
命令。 這是個多功能命令:可以用它開始跟蹤新文件,或者把已跟蹤的文件放到暫存區,還能用於合並時把有沖突的文件標記為已解決狀態等。 將這個命令理解為“添加內容到下一次提交中”而不是“將一個文件添加到項目中”要更加合適。 現在讓我們運行 git add
將"CONTRIBUTING.md"放到暫存區,然后再看看 git status
的輸出:
$ git add CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README modified: CONTRIBUTING.md
現在兩個文件都已暫存,下次提交時就會一並記錄到倉庫。 假設此時,你想要在 CONTRIBUTING.md
里再加條注釋, 重新編輯存盤后,准備好提交。 不過且慢,再運行 git status
看看:
$ vim CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README modified: CONTRIBUTING.md 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: CONTRIBUTING.md
怎么回事? 現在 CONTRIBUTING.md
文件同時出現在暫存區和非暫存區。 這怎么可能呢? 好吧,實際上 Git 只不過暫存了你運行 git add
命令時的版本, 如果你現在提交,CONTRIBUTING.md
的版本是你最后一次運行 git add
命令時的那個版本,而不是你運行 git commit
時,在工作目錄中的當前版本。 所以,運行了 git add
之后又作了修訂的文件,需要重新運行 git add
把最新版本重新暫存起來:
$ git add CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) new file: README modified: CONTRIBUTING.md
狀態簡覽
git status
命令的輸出十分詳細,但其用語有些繁瑣。 如果你使用 git status -s
命令或 git status --short
命令,你將得到一種更為緊湊的格式輸出。 運行 git status -s
,狀態報告輸出如下:
$ git status -s M README MM Rakefile A lib/git.rb M lib/simplegit.rb ?? LICENSE.txt
新添加的未跟蹤文件前面有 ??
標記,新添加到暫存區中的文件前面有 A
標記,修改過的文件前面有 M
標記。 你可能注意到了 M
有兩個可以出現的位置,出現在右邊的 M
表示該文件被修改了但是還沒放入暫存區,出現在靠左邊的 M
表示該文件被修改了並放入了暫存區。 例如,上面的狀態報告顯示: README
文件在工作區被修改了但是還沒有將修改后的文件放入暫存區,lib/simplegit.rb
文件被修改了並將修改后的文件放入了暫存區。 而 Rakefile
在工作區被修改並提交到暫存區后又在工作區中被修改了,所以在暫存區和工作區都有該文件被修改了的記錄。
忽略文件
一般我們總會有些文件無需納入 Git 的管理,也不希望它們總出現在未跟蹤文件列表。 通常都是些自動生成的文件,比如日志文件,或者編譯過程中創建的臨時文件等。 在這種情況下,我們可以創建一個名為 .gitignore
的文件,列出要忽略的文件模式。 來看一個實際的例子:
$ cat .gitignore *.[oa] *~
第一行告訴 Git 忽略所有以 .o
或 .a
結尾的文件。一般這類對象文件和存檔文件都是編譯過程中出現的。 第二行告訴 Git 忽略所有以波浪符(~)結尾的文件,許多文本編輯軟件(比如 Emacs)都用這樣的文件名保存副本。 此外,你可能還需要忽略 log,tmp 或者 pid 目錄,以及自動生成的文檔等等。 要養成一開始就設置好 .gitignore 文件的習慣,以免將來誤提交這類無用的文件。
文件 .gitignore
的格式規范如下:
-
所有空行或者以
#
開頭的行都會被 Git 忽略。 -
可以使用標准的 glob 模式匹配。
-
匹配模式可以以(
/
)開頭防止遞歸。 -
匹配模式可以以(
/
)結尾指定目錄。 -
要忽略指定模式以外的文件或目錄,可以在模式前加上驚嘆號(
!
)取反。
所謂的 glob 模式是指 shell 所使用的簡化了的正則表達式。 星號(*
)匹配零個或多個任意字符;[abc]
匹配任何一個列在方括號中的字符(這個例子要么匹配一個 a,要么匹配一個 b,要么匹配一個 c);問號(?
)只匹配一個任意字符;如果在方括號中使用短划線分隔兩個字符,表示所有在這兩個字符范圍內的都可以匹配(比如 [0-9]
表示匹配所有 0 到 9 的數字)。 使用兩個星號(*
) 表示匹配任意中間目錄,比如`a/**/z` 可以匹配 a/z
, a/b/z
或 `a/b/c/z`等。
我們再看一個 .gitignore 文件的例子:
# no .a files *.a # but do track lib.a, even though you're ignoring .a files above !lib.a # only ignore the TODO file in the current directory, not subdir/TODO /TODO # ignore all files in the build/ directory build/ # ignore doc/notes.txt, but not doc/server/arch.txt doc/*.txt # ignore all .pdf files in the doc/ directory doc/**/*.pdf
Tip
|
GitHub 有一個十分詳細的針對數十種項目及語言的 |
查看已暫存和未暫存的修改
如果 git status
命令的輸出對於你來說過於模糊,你想知道具體修改了什么地方,可以用 git diff
命令。 稍后我們會詳細介紹 git diff
,你可能通常會用它來回答這兩個問題:當前做的哪些更新還沒有暫存? 有哪些更新已經暫存起來准備好了下次提交? 盡管 git status
已經通過在相應欄下列出文件名的方式回答了這個問題,git diff
將通過文件補丁的格式顯示具體哪些行發生了改變。
假如再次修改 README 文件后暫存,然后編輯 CONTRIBUTING.md
文件后先不暫存, 運行 status
命令將會看到:
$ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: README 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: CONTRIBUTING.md
要查看尚未暫存的文件更新了哪些部分,不加參數直接輸入 git diff
:
$ git diff diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ebb991..643e24f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,8 @@ branch directly, things can get messy. Please include a nice description of your changes when you submit your PR; if we have to read the whole diff to figure out why you're contributing in the first place, you're less likely to get feedback and have your change -merged in. +merged in. Also, split your changes into comprehensive chunks if your patch is +longer than a dozen lines. If you are starting to work on a particular area, feel free to submit a PR that highlights your work in progress (and note in the PR title that it's
此命令比較的是工作目錄中當前文件和暫存區域快照之間的差異, 也就是修改之后還沒有暫存起來的變化內容。
若要查看已暫存的將要添加到下次提交里的內容,可以用 git diff --cached
命令。(Git 1.6.1 及更高版本還允許使用 git diff --staged
,效果是相同的,但更好記些。)
$ git diff --staged diff --git a/README b/README new file mode 100644 index 0000000..03902a1 --- /dev/null +++ b/README @@ -0,0 +1 @@ +My Project
請注意,git diff 本身只顯示尚未暫存的改動,而不是自上次提交以來所做的所有改動。 所以有時候你一下子暫存了所有更新過的文件后,運行 git diff
后卻什么也沒有,就是這個原因。
像之前說的,暫存 CONTRIBUTING.md
后再編輯,運行 git status
會看到暫存前后的兩個版本。 如果我們的環境(終端輸出)看起來如下:
$ git add CONTRIBUTING.md $ echo '# test line' >> CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) modified: CONTRIBUTING.md 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: CONTRIBUTING.md
現在運行 git diff
看暫存前后的變化:
$ git diff diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 643e24f..87f08c8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -119,3 +119,4 @@ at the ## Starter Projects See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md). +# test line
然后用 git diff --cached
查看已經暫存起來的變化:(--staged 和 --cached 是同義詞)
$ git diff --cached diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8ebb991..643e24f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -65,7 +65,8 @@ branch directly, things can get messy. Please include a nice description of your changes when you submit your PR; if we have to read the whole diff to figure out why you're contributing in the first place, you're less likely to get feedback and have your change -merged in. +merged in. Also, split your changes into comprehensive chunks if your patch is +longer than a dozen lines. If you are starting to work on a particular area, feel free to submit a PR that highlights your work in progress (and note in the PR title that it's
Note
|
Git Diff 的插件版本
在本書中,我們使用 |
提交更新
現在的暫存區域已經准備妥當可以提交了。 在此之前,請一定要確認還有什么修改過的或新建的文件還沒有 git add
過,否則提交的時候不會記錄這些還沒暫存起來的變化。 這些修改過的文件只保留在本地磁盤。 所以,每次准備提交前,先用 git status
看下,是不是都已暫存起來了, 然后再運行提交命令 git commit
:
$ git commit
這種方式會啟動文本編輯器以便輸入本次提交的說明。 (默認會啟用 shell 的環境變量 $EDITOR
所指定的軟件,一般都是 vim 或 emacs。當然也可以按照 起步 介紹的方式,使用 git config --global core.editor
命令設定你喜歡的編輯軟件。)
編輯器會顯示類似下面的文本信息(本例選用 Vim 的屏顯方式展示):
# Please enter the commit message for your changes. Lines starting # with '#' will be ignored, and an empty message aborts the commit. # On branch master # Changes to be committed: # new file: README # modified: CONTRIBUTING.md # ~ ~ ~ ".git/COMMIT_EDITMSG" 9L, 283C
可以看到,默認的提交消息包含最后一次運行 git status
的輸出,放在注釋行里,另外開頭還有一空行,供你輸入提交說明。 你完全可以去掉這些注釋行,不過留着也沒關系,多少能幫你回想起這次更新的內容有哪些。 (如果想要更詳細的對修改了哪些內容的提示,可以用 -v
選項,這會將你所做的改變的 diff 輸出放到編輯器中從而使你知道本次提交具體做了哪些修改。) 退出編輯器時,Git 會丟掉注釋行,用你輸入提交附帶信息生成一次提交。
另外,你也可以在 commit
命令后添加 -m
選項,將提交信息與命令放在同一行,如下所示:
$ git commit -m "Story 182: Fix benchmarks for speed" [master 463dc4f] Story 182: Fix benchmarks for speed 2 files changed, 2 insertions(+) create mode 100644 README
好,現在你已經創建了第一個提交! 可以看到,提交后它會告訴你,當前是在哪個分支(master
)提交的,本次提交的完整 SHA-1 校驗和是什么(463dc4f
),以及在本次提交中,有多少文件修訂過,多少行添加和刪改過。
請記住,提交時記錄的是放在暫存區域的快照。 任何還未暫存的仍然保持已修改狀態,可以在下次提交時納入版本管理。 每一次運行提交操作,都是對你項目作一次快照,以后可以回到這個狀態,或者進行比較。
跳過使用暫存區域
盡管使用暫存區域的方式可以精心准備要提交的細節,但有時候這么做略顯繁瑣。 Git 提供了一個跳過使用暫存區域的方式, 只要在提交的時候,給 git commit
加上 -a
選項,Git 就會自動把所有已經跟蹤過的文件暫存起來一並提交,從而跳過 git add
步驟:
$ 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: CONTRIBUTING.md no changes added to commit (use "git add" and/or "git commit -a") $ git commit -a -m 'added new benchmarks' [master 83e38c7] added new benchmarks 1 file changed, 5 insertions(+), 0 deletions(-)
看到了嗎?提交之前不再需要 git add
文件“CONTRIBUTING.md”了。
移除文件
要從 Git 中移除某個文件,就必須要從已跟蹤文件清單中移除(確切地說,是從暫存區域移除),然后提交。 可以用 git rm
命令完成此項工作,並連帶從工作目錄中刪除指定的文件,這樣以后就不會出現在未跟蹤文件清單中了。
如果只是簡單地從工作目錄中手工刪除文件,運行 git status
時就會在 “Changes not staged for commit” 部分(也就是 未暫存清單)看到:
$ rm PROJECTS.md $ git status On branch master Your branch is up-to-date with 'origin/master'. 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: PROJECTS.md no changes added to commit (use "git add" and/or "git commit -a")
然后再運行 git rm
記錄此次移除文件的操作:
$ git rm PROJECTS.md rm 'PROJECTS.md' $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) deleted: PROJECTS.md
下一次提交時,該文件就不再納入版本管理了。 如果刪除之前修改過並且已經放到暫存區域的話,則必須要用強制刪除選項 -f
(譯注:即 force 的首字母)。 這是一種安全特性,用於防止誤刪還沒有添加到快照的數據,這樣的數據不能被 Git 恢復。
另外一種情況是,我們想把文件從 Git 倉庫中刪除(亦即從暫存區域移除),但仍然希望保留在當前工作目錄中。 換句話說,你想讓文件保留在磁盤,但是並不想讓 Git 繼續跟蹤。 當你忘記添加 .gitignore
文件,不小心把一個很大的日志文件或一堆 .a
這樣的編譯生成文件添加到暫存區時,這一做法尤其有用。 為達到這一目的,使用 --cached
選項:
$ git rm --cached README
git rm
命令后面可以列出文件或者目錄的名字,也可以使用 glob
模式。 比方說:
$ git rm log/\*.log
注意到星號 *
之前的反斜杠 \
, 因為 Git 有它自己的文件模式擴展匹配方式,所以我們不用 shell 來幫忙展開。 此命令刪除 log/
目錄下擴展名為 .log
的所有文件。 類似的比如
$ git rm \*~
該命令為刪除以 ~
結尾的所有文件。
移動文件
不像其它的 VCS 系統,Git 並不顯式跟蹤文件移動操作。 如果在 Git 中重命名了某個文件,倉庫中存儲的元數據並不會體現出這是一次改名操作。 不過 Git 非常聰明,它會推斷出究竟發生了什么,至於具體是如何做到的,我們稍后再談。
既然如此,當你看到 Git 的 mv
命令時一定會困惑不已。 要在 Git 中對文件改名,可以這么做:
$ git mv file_from file_to
它會恰如預期般正常工作。 實際上,即便此時查看狀態信息,也會明白無誤地看到關於重命名操作的說明:
$ git mv README.md README $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) renamed: README.md -> README
其實,運行 git mv
就相當於運行了下面三條命令:
$ mv README.md README $ git rm README.md $ git add README
如此分開操作,Git 也會意識到這是一次改名,所以不管何種方式結果都一樣。 兩者唯一的區別是,mv
是一條命令而另一種方式需要三條命令,直接用 git mv
輕便得多。 不過有時候用其他工具批處理改名的話,要記得在提交前刪除老的文件名,再添加新的文件名。
2.3 Git 基礎 - 查看提交歷史
查看提交歷史
在提交了若干更新,又或者克隆了某個項目之后,你也許想回顧下提交歷史。 完成這個任務最簡單而又有效的工具是 git log
命令。
接下來的例子會用我專門用於演示的 simplegit 項目, 運行下面的命令獲取該項目源代碼:
git clone https://github.com/schacon/simplegit-progit
然后在此項目中運行 git log,應該會看到下面的輸出:
$ git log commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700 changed the version number commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 16:40:33 2008 -0700 removed unnecessary test commit a11bef06a3f659402fe7563abf99ad00de2209e6 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 10:31:28 2008 -0700 first commit
默認不用任何參數的話,git log
會按提交時間列出所有的更新,最近的更新排在最上面。 正如你所看到的,這個命令會列出每個提交的 SHA-1 校驗和、作者的名字和電子郵件地址、提交時間以及提交說明。
git log
有許多選項可以幫助你搜尋你所要找的提交, 接下來我們介紹些最常用的。
一個常用的選項是 -p
,用來顯示每次提交的內容差異。 你也可以加上 -2
來僅顯示最近兩次提交:
$ git log -p -2 commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700 changed the version number diff --git a/Rakefile b/Rakefile index a874b73..8f94139 100644 --- a/Rakefile +++ b/Rakefile @@ -5,7 +5,7 @@ require 'rake/gempackagetask' spec = Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.name = "simplegit" - s.version = "0.1.0" + s.version = "0.1.1" s.author = "Scott Chacon" s.email = "schacon@gee-mail.com" s.summary = "A simple gem for using Git in Ruby code." commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 16:40:33 2008 -0700 removed unnecessary test diff --git a/lib/simplegit.rb b/lib/simplegit.rb index a0a60ae..47c6340 100644 --- a/lib/simplegit.rb +++ b/lib/simplegit.rb @@ -18,8 +18,3 @@ class SimpleGit end end - -if $0 == __FILE__ - git = SimpleGit.new - puts git.show -end \ No newline at end of file
該選項除了顯示基本信息之外,還附帶了每次 commit 的變化。 當進行代碼審查,或者快速瀏覽某個搭檔提交的 commit 所帶來的變化的時候,這個參數就非常有用了。 你也可以為 git log
附帶一系列的總結性選項。 比如說,如果你想看到每次提交的簡略的統計信息,你可以使用 --stat
選項:
$ git log --stat commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700 changed the version number Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 16:40:33 2008 -0700 removed unnecessary test lib/simplegit.rb | 5 ----- 1 file changed, 5 deletions(-) commit a11bef06a3f659402fe7563abf99ad00de2209e6 Author: Scott Chacon <schacon@gee-mail.com> Date: Sat Mar 15 10:31:28 2008 -0700 first commit README | 6 ++++++ Rakefile | 23 +++++++++++++++++++++++ lib/simplegit.rb | 25 +++++++++++++++++++++++++ 3 files changed, 54 insertions(+)
正如你所看到的,--stat
選項在每次提交的下面列出額所有被修改過的文件、有多少文件被修改了以及被修改過的文件的哪些行被移除或是添加了。 在每次提交的最后還有一個總結。
另外一個常用的選項是 --pretty
。 這個選項可以指定使用不同於默認格式的方式展示提交歷史。 這個選項有一些內建的子選項供你使用。 比如用 oneline
將每個提交放在一行顯示,查看的提交數很大時非常有用。 另外還有 short
,full
和 fuller
可以用,展示的信息或多或少有些不同,請自己動手實踐一下看看效果如何。
$ git log --pretty=oneline ca82a6dff817ec66f44342007202690a93763949 changed the version number 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test a11bef06a3f659402fe7563abf99ad00de2209e6 first commit
但最有意思的是 format,可以定制要顯示的記錄格式。 這樣的輸出對后期提取分析格外有用 — 因為你知道輸出的格式不會隨着 Git 的更新而發生改變:
$ git log --pretty=format:"%h - %an, %ar : %s" ca82a6d - Scott Chacon, 6 years ago : changed the version number 085bb3b - Scott Chacon, 6 years ago : removed unnecessary test a11bef0 - Scott Chacon, 6 years ago : first commit
git log --pretty=format
常用的選項 列出了常用的格式占位符寫法及其代表的意義。
選項 | 說明 |
---|---|
|
提交對象(commit)的完整哈希字串 |
|
提交對象的簡短哈希字串 |
|
樹對象(tree)的完整哈希字串 |
|
樹對象的簡短哈希字串 |
|
父對象(parent)的完整哈希字串 |
|
父對象的簡短哈希字串 |
|
作者(author)的名字 |
|
作者的電子郵件地址 |
|
作者修訂日期(可以用 --date= 選項定制格式) |
|
作者修訂日期,按多久以前的方式顯示 |
|
提交者(committer)的名字 |
|
提交者的電子郵件地址 |
|
提交日期 |
|
提交日期,按多久以前的方式顯示 |
|
提交說明 |
你一定奇怪 作者 和 提交者 之間究竟有何差別, 其實作者指的是實際作出修改的人,提交者指的是最后將此工作成果提交到倉庫的人。 所以,當你為某個項目發布補丁,然后某個核心成員將你的補丁並入項目時,你就是作者,而那個核心成員就是提交者。 我們會在 分布式 Git 再詳細介紹兩者之間的細微差別。
當 oneline 或 format 與另一個 log
選項 --graph
結合使用時尤其有用。 這個選項添加了一些ASCII字符串來形象地展示你的分支、合並歷史:
$ git log --pretty=format:"%h %s" --graph * 2d3acf9 ignore errors from SIGCHLD on trap * 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit |\ | * 420eac9 Added a method for getting the current branch. * | 30e367c timeout code and tests * | 5a09431 add timeout protection to grit * | e1193f8 support for heads with slashes in them |/ * d6016bc require time for xmlschema * 11d191e Merge branch 'defunkt' into local
這種輸出類型會在我們下一章學完分支與合並以后變得更加有趣。
以上只是簡單介紹了一些 git log
命令支持的選項。 git log
的常用選項 列出了我們目前涉及到的和沒涉及到的選項,以及它們是如何影響 log 命令的輸出的:
選項 | 說明 |
---|---|
|
按補丁格式顯示每個更新之間的差異。 |
|
顯示每次更新的文件修改統計信息。 |
|
只顯示 --stat 中最后的行數修改添加移除統計。 |
|
僅在提交信息后顯示已修改的文件清單。 |
|
顯示新增、修改、刪除的文件清單。 |
|
僅顯示 SHA-1 的前幾個字符,而非所有的 40 個字符。 |
|
使用較短的相對時間顯示(比如,“2 weeks ago”)。 |
|
顯示 ASCII 圖形表示的分支合並歷史。 |
|
使用其他格式顯示歷史提交信息。可用的選項包括 oneline,short,full,fuller 和 format(后跟指定格式)。 |
限制輸出長度
除了定制輸出格式的選項之外,git log
還有許多非常實用的限制輸出長度的選項,也就是只輸出部分提交信息。 之前你已經看到過 -2
了,它只顯示最近的兩條提交, 實際上,這是 -<n>
選項的寫法,其中的 n
可以是任何整數,表示僅顯示最近的若干條提交。 不過實踐中我們是不太用這個選項的,Git 在輸出所有提交時會自動調用分頁程序,所以你一次只會看到一頁的內容。
另外還有按照時間作限制的選項,比如 --since
和 --until
也很有用。 例如,下面的命令列出所有最近兩周內的提交:
$ git log --since=2.weeks
這個命令可以在多種格式下工作,比如說具體的某一天 "2008-01-15"
,或者是相對地多久以前 "2 years 1 day 3 minutes ago"
。
還可以給出若干搜索條件,列出符合的提交。 用 --author
選項顯示指定作者的提交,用 --grep
選項搜索提交說明中的關鍵字。 (請注意,如果要得到同時滿足這兩個選項搜索條件的提交,就必須用 --all-match
選項。否則,滿足任意一個條件的提交都會被匹配出來)
另一個非常有用的篩選選項是 -S
,可以列出那些添加或移除了某些字符串的提交。 比如說,你想找出添加或移除了某一個特定函數的引用的提交,你可以這樣使用:
$ git log -Sfunction_name
最后一個很實用的 git log
選項是路徑(path), 如果只關心某些文件或者目錄的歷史提交,可以在 git log 選項的最后指定它們的路徑。 因為是放在最后位置上的選項,所以用兩個短划線(--)隔開之前的選項和后面限定的路徑名。
在 限制 git log
輸出的選項 中列出了常用的選項
選項 | 說明 |
---|---|
|
僅顯示最近的 n 條提交 |
|
僅顯示指定時間之后的提交。 |
|
僅顯示指定時間之前的提交。 |
|
僅顯示指定作者相關的提交。 |
|
僅顯示指定提交者相關的提交。 |
|
僅顯示含指定關鍵字的提交 |
|
僅顯示添加或移除了某個關鍵字的提交 |
$ git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \ --before="2008-11-01" --no-merges -- t/ 5610e3b - Fix testcase failure when extended attributes are in use acd3b9e - Enhance hold_lock_file_for_{update,append}() API f563754 - demonstrate breakage of detached checkout with symbolic link HEAD d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged new paths 51a94af - Fix "checkout --track -b newbranch" on detached HEAD b0ad11e - pull: allow "git pull origin $something:$current_branch" into an unborn branch
在近 40000 條提交中,上面的輸出僅列出了符合條件的 6 條記錄。
2.4 Git 基礎 - 撤消操作
撤消操作
在任何一個階段,你都有可能想要撤消某些操作。 這里,我們將會學習幾個撤消你所做修改的基本工具。 注意,有些撤消操作是不可逆的。 這是在使用 Git 的過程中,會因為操作失誤而導致之前的工作丟失的少有的幾個地方之一。
有時候我們提交完了才發現漏掉了幾個文件沒有添加,或者提交信息寫錯了。 此時,可以運行帶有 --amend
選項的提交命令嘗試重新提交:
$ git commit --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) renamed: README.md -> README modified: CONTRIBUTING.md
在 “Changes to be committed” 文字正下方,提示使用 git reset HEAD <file>...
來取消暫存。 所以,我們可以這樣來取消暫存 CONTRIBUTING.md
文件:
$ git reset HEAD CONTRIBUTING.md Unstaged changes after reset: M CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) renamed: README.md -> README 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: CONTRIBUTING.md
這個命令有點兒奇怪,但是起作用了。 CONTRIBUTING.md
文件已經是修改未暫存的狀態了。
Note
|
雖然在調用時加上 |
Note
|
雖然在調用時加上 |
到目前為止這個神奇的調用就是你需要對 git reset
命令了解的全部。我們將會在 重置揭密 中了解 reset
的更多細節以及如何掌握它做一些真正有趣的事。
撤消對文件的修改
如果你並不想保留對 CONTRIBUTING.md
文件的修改怎么辦? 你該如何方便地撤消修改 - 將它還原成上次提交時的樣子(或者剛克隆完的樣子,或者剛把它放入工作目錄時的樣子)? 幸運的是,git status
也告訴了你應該如何做。 在最后一個例子中,未暫存區域是這樣:
$ git checkout -- CONTRIBUTING.md $ git status On branch master Changes to be committed: (use "git reset HEAD <file>..." to unstage) renamed: README.md -> README
可以看到那些修改已經被撤消了。
Important
|
你需要知道 |
如果你仍然想保留對那個文件做出的修改,但是現在仍然需要撤消,我們將會在 Git 分支 介紹保存進度與分支;這些通常是更好的做法。
記住,在 Git 中任何 已提交的 東西幾乎總是可以恢復的。 甚至那些被刪除的分支中的提交或使用 --amend
選項覆蓋的提交也可以恢復(閱讀 數據恢復 了解數據恢復)。 然而,任何你未提交的東西丟失后很可能再也找不到了。
2.5 Git 基礎 - 遠程倉庫的使用
遠程倉庫的使用
為了能在任意 Git 項目上協作,你需要知道如何管理自己的遠程倉庫。 遠程倉庫是指托管在因特網或其他網絡中的你的項目的版本庫。 你可以有好幾個遠程倉庫,通常有些倉庫對你只讀,有些則可以讀寫。 與他人協作涉及管理遠程倉庫以及根據需要推送或拉取數據。 管理遠程倉庫包括了解如何添加遠程倉庫、移除無效的遠程倉庫、管理不同的遠程分支並定義它們是否被跟蹤等等。 在本節中,我們將介紹一部分遠程管理的技能。
查看遠程倉庫
如果想查看你已經配置的遠程倉庫服務器,可以運行 git remote
命令。 它會列出你指定的每一個遠程服務器的簡寫。 如果你已經克隆了自己的倉庫,那么至少應該能看到 origin - 這是 Git 給你克隆的倉庫服務器的默認名字:
$ git clone https://github.com/schacon/ticgit Cloning into 'ticgit'... remote: Reusing existing pack: 1857, done. remote: Total 1857 (delta 0), reused 0 (delta 0) Receiving objects: 100% (1857/1857), 374.35 KiB | 268.00 KiB/s, done. Resolving deltas: 100% (772/772), done. Checking connectivity... done. $ cd ticgit $ git remote origin
你也可以指定選項 -v
,會顯示需要讀寫遠程倉庫使用的 Git 保存的簡寫與其對應的 URL。
$ git remote -v origin https://github.com/schacon/ticgit (fetch) origin https://github.com/schacon/ticgit (push)
如果你的遠程倉庫不止一個,該命令會將它們全部列出。 例如,與幾個協作者合作的,擁有多個遠程倉庫的倉庫看起來像下面這樣:
$ cd grit $ git remote -v bakkdoor https://github.com/bakkdoor/grit (fetch) bakkdoor https://github.com/bakkdoor/grit (push) cho45 https://github.com/cho45/grit (fetch) cho45 https://github.com/cho45/grit (push) defunkt https://github.com/defunkt/grit (fetch) defunkt https://github.com/defunkt/grit (push) koke git://github.com/koke/grit.git (fetch) koke git://github.com/koke/grit.git (push) origin git@github.com:mojombo/grit.git (fetch) origin git@github.com:mojombo/grit.git (push)
這樣我們可以輕松拉取其中任何一個用戶的貢獻。 此外,我們大概還會有某些遠程倉庫的推送權限,雖然我們目前還不會在此介紹。
注意這些遠程倉庫使用了不同的協議;我們將會在 在服務器上搭建 Git 中了解關於它們的更多信息。
添加遠程倉庫
我在之前的章節中已經提到並展示了如何添加遠程倉庫的示例,不過這里將告訴你如何明確地做到這一點。 運行 git remote add <shortname> <url>
添加一個新的遠程 Git 倉庫,同時指定一個你可以輕松引用的簡寫:
$ git remote origin $ git remote add pb https://github.com/paulboone/ticgit $ git remote -v origin https://github.com/schacon/ticgit (fetch) origin https://github.com/schacon/ticgit (push) pb https://github.com/paulboone/ticgit (fetch) pb https://github.com/paulboone/ticgit (push)
現在你可以在命令行中使用字符串 pb
來代替整個 URL。 例如,如果你想拉取 Paul 的倉庫中有但你沒有的信息,可以運行 git fetch pb
:
$ git fetch pb remote: Counting objects: 43, done. remote: Compressing objects: 100% (36/36), done. remote: Total 43 (delta 10), reused 31 (delta 5) Unpacking objects: 100% (43/43), done. From https://github.com/paulboone/ticgit * [new branch] master -> pb/master * [new branch] ticgit -> pb/ticgit
現在 Paul 的 master 分支可以在本地通過 pb/master
訪問到 - 你可以將它合並到自己的某個分支中,或者如果你想要查看它的話,可以檢出一個指向該點的本地分支。 (我們將會在 Git 分支 中詳細介紹什么是分支以及如何使用分支。)
從遠程倉庫中抓取與拉取
就如剛才所見,從遠程倉庫中獲得數據,可以執行:
$ git fetch [remote-name]
這個命令會訪問遠程倉庫,從中拉取所有你還沒有的數據。 執行完成后,你將會擁有那個遠程倉庫中所有分支的引用,可以隨時合並或查看。
如果你使用 clone
命令克隆了一個倉庫,命令會自動將其添加為遠程倉庫並默認以 “origin” 為簡寫。 所以,git fetch origin
會抓取克隆(或上一次抓取)后新推送的所有工作。 必須注意 git fetch
命令會將數據拉取到你的本地倉庫 - 它並不會自動合並或修改你當前的工作。 當准備好時你必須手動將其合並入你的工作。
如果你有一個分支設置為跟蹤一個遠程分支(閱讀下一節與 Git 分支 了解更多信息),可以使用 git pull
命令來自動的抓取然后合並遠程分支到當前分支。 這對你來說可能是一個更簡單或更舒服的工作流程;默認情況下,git clone
命令會自動設置本地 master 分支跟蹤克隆的遠程倉庫的 master 分支(或不管是什么名字的默認分支)。 運行 git pull
通常會從最初克隆的服務器上抓取數據並自動嘗試合並到當前所在的分支。
推送到遠程倉庫
當你想分享你的項目時,必須將其推送到上游。 這個命令很簡單:git push [remote-name] [branch-name]
。 當你想要將 master 分支推送到 origin
服務器時(再次說明,克隆時通常會自動幫你設置好那兩個名字),那么運行這個命令就可以將你所做的備份到服務器:
$ git push origin master
只有當你有所克隆服務器的寫入權限,並且之前沒有人推送過時,這條命令才能生效。 當你和其他人在同一時間克隆,他們先推送到上游然后你再推送到上游,你的推送就會毫無疑問地被拒絕。 你必須先將他們的工作拉取下來並將其合並進你的工作后才能推送。 閱讀 Git 分支 了解如何推送到遠程倉庫服務器的詳細信息。
查看遠程倉庫
如果想要查看某一個遠程倉庫的更多信息,可以使用 git remote show [remote-name]
命令。 如果想以一個特定的縮寫名運行這個命令,例如 origin
,會得到像下面類似的信息:
$ git remote show origin * remote origin Fetch URL: https://github.com/schacon/ticgit Push URL: https://github.com/schacon/ticgit HEAD branch: master Remote branches: master tracked dev-branch tracked Local branch configured for 'git pull': master merges with remote master Local ref configured for 'git push': master pushes to master (up to date)
它同樣會列出遠程倉庫的 URL 與跟蹤分支的信息。 這些信息非常有用,它告訴你正處於 master 分支,並且如果運行 git pull,就會抓取所有的遠程引用,然后將遠程 master 分支合並到本地 master 分支。 它也會列出拉取到的所有遠程引用。
這是一個經常遇到的簡單例子。 如果你是 Git 的重度使用者,那么還可以通過 git remote show
看到更多的信息。
$ git remote show origin * remote origin URL: https://github.com/my-org/complex-project Fetch URL: https://github.com/my-org/complex-project Push URL: https://github.com/my-org/complex-project HEAD branch: master Remote branches: master tracked dev-branch tracked markdown-strip tracked issue-43 new (next fetch will store in remotes/origin) issue-45 new (next fetch will store in remotes/origin) refs/remotes/origin/issue-11 stale (use 'git remote prune' to remove) Local branches configured for 'git pull': dev-branch merges with remote dev-branch master merges with remote master Local refs configured for 'git push': dev-branch pushes to dev-branch (up to date) markdown-strip pushes to markdown-strip (up to date) master pushes to master (up to date)
這個命令列出了當你在特定的分支上執行 git push
會自動地推送到哪一個遠程分支。 它也同樣地列出了哪些遠程分支不在你的本地,哪些遠程分支已經從服務器上移除了,還有當你執行 git pull
時哪些分支會自動合並。
遠程倉庫的移除與重命名
如果想要重命名引用的名字可以運行 git remote rename
去修改一個遠程倉庫的簡寫名。 例如,想要將 pb
重命名為 paul
,可以用 git remote rename
這樣做:
$ git remote rename pb paul $ git remote origin paul
值得注意的是這同樣也會修改你的遠程分支名字。 那些過去引用 pb/master
的現在會引用 paul/master
。
如果因為一些原因想要移除一個遠程倉庫 - 你已經從服務器上搬走了或不再想使用某一個特定的鏡像了,又或者某一個貢獻者不再貢獻了 - 可以使用 git remote rm
:
$ git remote rm paul $ git remote origin
2.6 Git 基礎 - 打標簽
打標簽
像其他版本控制系統(VCS)一樣,Git 可以給歷史中的某一個提交打上標簽,以示重要。 比較有代表性的是人們會使用這個功能來標記發布結點(v1.0 等等)。 在本節中,你將會學習如何列出已有的標簽、如何創建新標簽、以及不同類型的標簽分別是什么。
列出標簽
在 Git 中列出已有的標簽是非常簡單直觀的。 只需要輸入 git tag
:
$ git tag v0.1 v1.3
這個命令以字母順序列出標簽;但是它們出現的順序並不重要。
你也可以使用特定的模式查找標簽。 例如,Git 自身的源代碼倉庫包含標簽的數量超過 500 個。 如果只對 1.8.5 系列感興趣,可以運行:
$ git tag -l 'v1.8.5*' v1.8.5 v1.8.5-rc0 v1.8.5-rc1 v1.8.5-rc2 v1.8.5-rc3 v1.8.5.1 v1.8.5.2 v1.8.5.3 v1.8.5.4 v1.8.5.5
創建標簽
Git 使用兩種主要類型的標簽:輕量標簽(lightweight)與附注標簽(annotated)。
一個輕量標簽很像一個不會改變的分支 - 它只是一個特定提交的引用。
然而,附注標簽是存儲在 Git 數據庫中的一個完整對象。 它們是可以被校驗的;其中包含打標簽者的名字、電子郵件地址、日期時間;還有一個標簽信息;並且可以使用 GNU Privacy Guard (GPG)簽名與驗證。 通常建議創建附注標簽,這樣你可以擁有以上所有信息;但是如果你只是想用一個臨時的標簽,或者因為某些原因不想要保存那些信息,輕量標簽也是可用的。
附注標簽
在 Git 中創建一個附注標簽是很簡單的。 最簡單的方式是當你在運行 tag
命令時指定 -a
選項:
$ git tag -a v1.4 -m 'my version 1.4' $ git tag v0.1 v1.3 v1.4
-m
選項指定了一條將會存儲在標簽中的信息。 如果沒有為附注標簽指定一條信息,Git 會運行編輯器要求你輸入信息。
通過使用 git show
命令可以看到標簽信息與對應的提交信息:
$ git show v1.4 tag v1.4 Tagger: Ben Straub <ben@straub.cc> Date: Sat May 3 20:19:12 2014 -0700 my version 1.4 commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700 changed the version number
輸出顯示了打標簽者的信息、打標簽的日期時間、附注信息,然后顯示具體的提交信息。
輕量標簽
另一種給提交打標簽的方式是使用輕量標簽。 輕量標簽本質上是將提交校驗和存儲到一個文件中 - 沒有保存任何其他信息。 創建輕量標簽,不需要使用 -a
、-s
或 -m
選項,只需要提供標簽名字:
$ git tag v1.4-lw $ git tag v0.1 v1.3 v1.4 v1.4-lw v1.5
這時,如果在標簽上運行 git show
,你不會看到額外的標簽信息。 命令只會顯示出提交信息:
$ git show v1.4-lw commit ca82a6dff817ec66f44342007202690a93763949 Author: Scott Chacon <schacon@gee-mail.com> Date: Mon Mar 17 21:52:11 2008 -0700 changed the version number
后期打標簽
你也可以對過去的提交打標簽。 假設提交歷史是這樣的:
$ git log --pretty=oneline 15027957951b64cf874c3557a0f3547bd83b3ff6 Merge branch 'experiment' a6b4c97498bd301d84096da251c98a07c7723e65 beginning write support 0d52aaab4479697da7686c15f77a3d64d9165190 one more thing 6d52a271eda8725415634dd79daabbc4d9b6008e Merge branch 'experiment' 0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc added a commit function 4682c3261057305bdd616e23b64b0857d832627b added a todo file 166ae0c4d3f420721acbb115cc33848dfcc2121a started write support 9fceb02d0ae598e95dc970b74767f19372d61af8 updated rakefile 964f16d36dfccde844893cac5b347e7b3d44abbc commit the todo 8a5cbc430f1a9c3d00faaeffd07798508422908a updated readme
現在,假設在 v1.2 時你忘記給項目打標簽,也就是在 “updated rakefile” 提交。 你可以在之后補上標簽。 要在那個提交上打標簽,你需要在命令的末尾指定提交的校驗和(或部分校驗和):
$ git tag -a v1.2 9fceb02
可以看到你已經在那次提交上打上標簽了:
$ git tag v0.1 v1.2 v1.3 v1.4 v1.4-lw v1.5 $ git show v1.2 tag v1.2 Tagger: Scott Chacon <schacon@gee-mail.com> Date: Mon Feb 9 15:32:16 2009 -0800 version 1.2 commit 9fceb02d0ae598e95dc970b74767f19372d61af8 Author: Magnus Chacon <mchacon@gee-mail.com> Date: Sun Apr 27 20:43:35 2008 -0700 updated rakefile ...
共享標簽
默認情況下,git push
命令並不會傳送標簽到遠程倉庫服務器上。 在創建完標簽后你必須顯式地推送標簽到共享服務器上。 這個過程就像共享遠程分支一樣 - 你可以運行 git push origin [tagname]
。
$ git push origin v1.5 Counting objects: 14, done. Delta compression using up to 8 threads. Compressing objects: 100% (12/12), done. Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done. Total 14 (delta 3), reused 0 (delta 0) To git@github.com:schacon/simplegit.git * [new tag] v1.5 -> v1.5
如果想要一次性推送很多標簽,也可以使用帶有 --tags
選項的 git push
命令。 這將會把所有不在遠程倉庫服務器上的標簽全部傳送到那里。
$ git push origin --tags Counting objects: 1, done. Writing objects: 100% (1/1), 160 bytes | 0 bytes/s, done. Total 1 (delta 0), reused 0 (delta 0) To git@github.com:schacon/simplegit.git * [new tag] v1.4 -> v1.4 * [new tag] v1.4-lw -> v1.4-lw
現在,當其他人從倉庫中克隆或拉取,他們也能得到你的那些標簽。
檢出標簽
在 Git 中你並不能真的檢出一個標簽,因為它們並不能像分支一樣來回移動。 如果你想要工作目錄與倉庫中特定的標簽版本完全一樣,可以使用 git checkout -b [branchname] [tagname]
在特定的標簽上創建一個新分支:
$ git checkout -b version2 v2.0.0 Switched to a new branch 'version2'
當然,如果在這之后又進行了一次提交,version2
分支會因為改動向前移動了,那么 version2
分支就會和 v2.0.0
標簽稍微有些不同,這時就應該當心了。
2.7 Git 基礎 - Git 別名
Git 別名
在我們結束本章 Git 基礎之前,正好有一個小技巧可以使你的 Git 體驗更簡單、容易、熟悉:別名。 我們不會在之后的章節中引用到或假定你使用過它們,但是你大概應該知道如何使用它們。
Git 並不會在你輸入部分命令時自動推斷出你想要的命令。 如果不想每次都輸入完整的 Git 命令,可以通過 git config
文件來輕松地為每一個命令設置一個別名。 這里有一些例子你可以試試:
$ git config --global alias.co checkout $ git config --global alias.br branch $ git config --global alias.ci commit $ git config --global alias.st status
這意味着,當要輸入 git commit
時,只需要輸入 git ci
。 隨着你繼續不斷地使用 Git,可能也會經常使用其他命令,所以創建別名時不要猶豫。
在創建你認為應該存在的命令時這個技術會很有用。 例如,為了解決取消暫存文件的易用性問題,可以向 Git 中添加你自己的取消暫存別名:
$ git config --global alias.unstage 'reset HEAD --'
這會使下面的兩個命令等價:
$ git unstage fileA $ git reset HEAD -- fileA
這樣看起來更清楚一些。 通常也會添加一個 last
命令,像這樣:
$ git config --global alias.last 'log -1 HEAD'
這樣,可以輕松地看到最后一次提交:
$ git last commit 66938dae3329c7aebe598c2246a8e6af90d04646 Author: Josh Goebel <dreamer3@example.com> Date: Tue Aug 26 19:48:51 2008 +0800 test for current head Signed-off-by: Scott Chacon <schacon@example.com>
可以看出,Git 只是簡單地將別名替換為對應的命令。 然而,你可能想要執行外部命令,而不是一個 Git 子命令。 如果是那樣的話,可以在命令前面加入 !
符號。 如果你自己要寫一些與 Git 倉庫協作的工具的話,那會很有用。 我們現在演示將 git visual
定義為 gitk
的別名:
$ git config --global alias.visual '!gitk'