Git/簡介

在這裡,我們將介紹最簡單的 Git 命令:建立新倉庫,新增和提交檔案,刪除檔案,撤銷錯誤提交,丟棄未提交的更改以及檢視特定提交的檔案。
建立新的 Git 倉庫很簡單。有兩個命令涵蓋了此功能:git-init(1) 和 git-clone(1)。克隆現有的倉庫將在後面介紹。現在,讓我們在新的目錄中建立一個新的倉庫
$ git init myrepo
已在
- /home/username/myrepo/.git/ 上初始化空的 Git 倉庫(Linux)。
- C:/Users/username/myrepo/.git/ 上初始化空的 Git 倉庫(Windows)。
如果你已經有一個你想要變成 Git 倉庫的目錄
$ cd $my_preexisting_repo $ git init
以第一個例子為例,讓我們看看發生了什麼
$ cd myrepo $ ls -A .git
你的整個倉庫都將包含在 .git 目錄中。相反,一些 SCM 會在你的工作目錄中留下檔案(例如 .svn、.cvs、.acodeic 等)。Git 避免了這種情況,並將所有內容都放在倉庫根目錄下的名為 .git 的子目錄中。
說明:要在 Windows 上設定 Git 在每次開啟時指向的預設目錄,請右鍵單擊快捷方式,並更改名為“起始位置”的欄位的路徑。
要檢查倉庫的狀態,請使用 git-status(1) 命令。例如,一個新建立的沒有提交的倉庫應該顯示以下內容
$ git status On branch master Initial commit nothing to commit (create/copy files and use "git add" to track)
養成經常使用 git-status 的習慣,確保你正在做你認為你在做的事情。:)
與大多數其他 VCS 不同,Git 不會假設你想要提交每個修改過的檔案。相反,使用者將他們想要提交的檔案新增到 暫存區(也稱為 索引 或 快取,具體取決於你閱讀的文件部分)。暫存區中的內容就是將要提交的內容。你可以使用 git-status(1) 或 git diff --staged 檢查將要提交的內容。
要將檔案暫存到下一個提交,請使用命令 git-add(1)。
$ nano file.txt hack hack hack... $ git status # On branch master # # Initial commit # # Untracked files: # (use "git add <file>..." to include in what will be committed) # # file.txt nothing added to commit but untracked files present (use "git add" to track)
這表明我們正在使用名為“master”的分支,並且有一個 Git 未跟蹤(沒有提交歷史)的檔案。Git 會提醒你,可以透過執行 git add file.txt 將該檔案包含在下一個提交中。
$ git add file.txt $ git status # On branch master # # Initial commit # # Changes to be committed: # (use "git rm --cached <file>..." to unstage) # # new file: file.txt #
新增檔案後,它將顯示為已準備好提交。現在讓我們提交它。
$ git commit -m 'My first commit' [master (root-commit) be8bf6d] My first commit 1 files changed, 1 insertions(+), 0 deletions(-) create mode 100644 file.txt
在大多數情況下,你不會想要使用 -m 'Commit message' 形式 - 相反,請將其省略,以便開啟 $EDITOR 讓你可以編寫適當的提交資訊。我們將在後面描述這一點,但在示例中,將使用 -m 'Commit message' 形式,以便讀者可以輕鬆地瞭解發生了什麼。
你可以使用 git add -A 自動暫存所有已更改和未跟蹤的檔案,以包含在下一個提交中。一旦檔案被跟蹤,git add -u 將在檔案發生更改時暫存它。
如果你改變主意不想暫存檔案,並且還沒有提交,你可以使用 git-reset(1) 命令的最簡單形式來 取消 暫存。
$ git reset file.txt
取消暫存單個檔案,或者
$ git reset
移除暫存區中的所有內容。
git-reset 有更多功能,例如
- 取消最近的兩個提交,但不影響檔案:
git reset 'HEAD~2'。 - 取消最近的兩個提交,並將它們修改後的內容還原到檔案中:
git reset HEAD~2 --hard。 - 取消分支上的最近兩個操作:
git reset 'HEAD@{2}'(使用git reflog)。這可以用來取消意外的重置操作。
要從 git add -A 的檢視範圍中排除某些未跟蹤的檔案,請繼續閱讀...
git restore 會恢復到引數中指定的版本的[1]檔案。
通常,你的工作區中會有一些你不希望新增到倉庫中的檔案。例如,emacs會為每個你用它編輯的檔案建立一個帶有波浪號字尾的備份副本,例如filename~。即使你可以手動避免將它們新增到提交中(這意味著永遠不要使用 git add -A),它們也會使狀態列表變得混亂。
為了告訴 Git 忽略某些檔案,你可以建立一個忽略檔案,該檔案的每一行都表示要忽略的檔案的規範(可以使用萬用字元)。可以使用空格或#字元作為註釋開始符。
例如
# Ignore emacs backup files: *~ # Ignore everything in the cache directory: app/cache
Git 會在兩個名稱下查詢忽略檔案
- .git/info/exclude— 這特定於你自己的倉庫副本,而不是倉庫的公共部分。
- .gitignore— 由於這在.git目錄之外,所以它通常會像倉庫中的任何其他檔案一樣被 Git 跟蹤。
你在其中任何一個(或兩個)檔案中放入的內容取決於你的需求。.gitignore是一個很好的地方,可以列出所有使用此倉庫副本的人可能希望忽略的內容,例如構建產品。如果你正在進行自己的個人實驗,這些實驗不太可能涉及其他程式碼貢獻者,那麼你可以將相關的忽略行放入.git/info/exclude.
注意,忽略檔案條目僅與 git status 和 git add -A(新增所有新檔案和更改過的檔案)命令相關。你使用 git add filename 明確新增的任何檔案都將始終被新增到倉庫中,無論它們的名稱是否與忽略條目匹配。一旦它們被新增到倉庫中,它們的更改將從此後被 git add -u 自動跟蹤。
Short (50 chars or less) summary of changes More detailed explanatory text, if necessary. Wrap it to about 72 characters or so. In some contexts, the first line is treated as the subject of an email and the rest of the text as the body. The blank line separating the summary from the body is critical (unless you omit the body entirely); tools like rebase can get confused if you run the two together. Write your commit message in the present tense: "Fix bug" and not "Fixed bug." This convention matches up with commit messages generated by commands like git merge and git revert. Further paragraphs come after blank lines. - Bullet points are okay, too - Typically a hyphen or asterisk is used for the bullet, preceded by a single space, with blank lines in between, but conventions vary here - Use a hanging indent
讓我們先從幾個將提交資訊換行到 72 列的原因開始。
- Git log 不會對提交資訊進行任何特殊換行。使用預設的
less -S分頁器時,這意味著你的段落會遠遠超出螢幕邊緣,難以閱讀。在一個 80 列的終端上,如果我們從左邊縮排 4 列,從右邊縮排 4 列,那麼我們只剩下 72 列。 git format-patch --stdout將一系列提交轉換為一系列電子郵件,使用資訊作為郵件正文。良好的電子郵件禮儀規定我們應該將純文字電子郵件換行,以便在 80 列的終端中,即使嵌套了多層回覆指示符也不會溢位。
Vim 使用者可以透過安裝我的 vim-git 執行時檔案,或者在你的 Git 提交資訊檔案中設定以下選項來滿足此要求
:set textwidth=72
對於 Textmate,你可以調整“換行列”選項(在檢視選單中),然後使用 ^Q 重新換行段落(確保段落之後有一個空行,避免與註釋混合)。以下是一個 shell 命令,可以將 72 新增到選單中,這樣你就不必每次都拖動選擇。
$ defaults write com.macromates.textmate OakWrapColumns '( 40, 72, 78 )'
比格式化正文的機制更重要的是使用主題行的習慣。如示例所示,你應該儘量使主題行大約 50 個字元(儘管這不是硬性限制),並始終在主題行後面加上一個空行。第一行應該是對提交引入的更改的簡潔摘要;如果存在任何技術細節無法用這些嚴格的尺寸限制來表達,請將它們放在正文中。主題行在 Git 中無處不在,如果資訊過長,通常會被截斷。以下是主題行出現的一些例子
git log --pretty=oneline顯示一個簡潔的歷史對映,包含提交 ID 和摘要git rebase --interactive在呼叫的編輯器中提供每個提交的摘要- 如果配置選項merge.summary被設定,所有合併提交的摘要將被合併到合併提交資訊中
git shortlog在它生成的類似變更日誌的輸出中使用摘要行- git-format-patch(1),git-send-email(1) 和相關工具將其用作郵件主題
- git-reflog(1),一個旨在幫助您從錯誤中恢復並獲取摘要副本的本地歷史記錄
gitk,一個具有摘要列的圖形介面- Gitweb 和其他 Web 介面,如 GitHub 在其使用者介面中的各個位置使用摘要。
主題/正文區分可能看起來不重要,但它是使 Git 歷史比 Subversion 更易於使用的一系列細微因素之一。
刪除檔案
[edit | edit source]讓我們繼續進行一些提交,以向您展示如何刪除檔案
$ echo 'more stuff' >> file.txt $ git status # On branch master # Changed but not updated: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: file.txt # no changes added to commit (use "git add" and/or "git commit -a")
雖然 git 不會強制使用者提交所有修改的檔案,但這是一種常見的情況。如 git status 的最後一行所述,使用 git commit -a 提交所有修改的檔案,而無需事先閱讀它們
$ git commit -a -m 'My second commit' [master e633787] My second commit 1 files changed, 1 insertions(+), 0 deletions(-)
看到提交後 git 輸出中的隨機字串(在上面的示例中加粗了嗎)?這是 git 用於跟蹤物件(在本例中為提交物件)的識別符號的縮寫。每個物件都使用 SHA-1 進行雜湊運算,並透過該字串進行引用。在本例中,完整字串是e6337879cbb42a2ddfc1a1602ee785b4bfbde518,但您通常只需要前 8 個字元左右即可唯一識別物件,因此這就是 git 顯示的所有內容。我們稍後將需要使用這些識別符號來引用特定的提交。
要刪除檔案,請使用 git 的“rm”子命令
$ git rm file.txt rm 'file.txt' $ git status # On branch master # Changes to be committed: # (use "git reset HEAD <file>..." to unstage) # # deleted: file.txt # $ ls -a . .. .git
請注意,這會從您的磁碟中刪除該檔案。如果您只想從 git 儲存庫中刪除該檔案,但想保留工作目錄中的檔案,請使用 git rm --cached。
$ git commit -m 'Removed file.txt' [master b7deafa] Removed file.txt 1 files changed, 0 insertions(+), 2 deletions(-) delete mode 100644 file.txt
撤銷提交
[edit | edit source]要使用另一個提交撤銷提交,請使用 git revert
$ git revert HEAD Finished one revert. [master 47e3b6c] Revert "My second commit" 1 files changed, 0 insertions(+), 1 deletions(-) $ ls -a . .. file.txt .git
您可以指定任何提交,而不是 HEAD。例如
- HEAD 之前的提交
git revert HEAD^- 向後五次的提交
git revert HEAD~5- 由給定雜湊標識的提交
git revert e6337879
重置提交
[edit | edit source]git reset 提供與 git revert 相同的選項,但它不會建立撤銷提交,而是直接取消提交,並將檔案置於未提交狀態。
要取消重置,請使用:git reset 'HEAD@{2}'。
丟棄本地未提交的更改
[edit | edit source]要丟棄您的更改並恢復到最近提交的狀態
$ git reset --hard HEAD
如上所述,您可以指定任何其他提交
$ git reset --hard e6337879
如果您只想重置一個檔案(您在上次提交後犯了一些愚蠢的錯誤),您可以使用
$ git checkout filename
這將刪除自上次提交以來對該檔案所做的所有更改,但保留其他檔案不變。
獲取檔案的特定版本
[edit | edit source]要獲取已提交檔案的特定版本,您需要該提交的雜湊值。您可以使用 git-log(1) 找到它
$ git log
commit 47e3b6cb6427f8ce0818f5d3a4b2e762b72dbd89
Author: Mike.lifeguard <myemail@example.com>
Date: Sat Mar 6 22:24:00 2010 -0400
Revert "My second commit"
This reverts commit e6337879cbb42a2ddfc1a1602ee785b4bfbde518.
commit e6337879cbb42a2ddfc1a1602ee785b4bfbde518
Author: Mike.lifeguard <myemail@example.com>
Date: Sat Mar 6 22:17:20 2010 -0400
My second commit
commit be8bf6da4db2ea32c10c74c7d6f366be114d18f0
Author: Mike.lifeguard <myemail@example.com>
Date: Sat Mar 6 22:11:57 2010 -0400
My first commit
然後,您可以使用 git show
$ git show e6337879cbb42a2ddfc1a1602ee785b4bfbde518:file.txt hack hack hack... more stuff
Git Checkout 與 Subversion Checkout 不同
[edit | edit source]如果您在使用過 Subversion 集中式版本控制系統後才開始使用 Git,您可能會認為 Git 中的簽出操作類似於 Subversion 中的操作。事實並非如此。雖然 Git 和 Subversion 都允許您從儲存庫中籤出源樹的舊版本,但只有 Subversion 會跟蹤您簽出了哪個修訂版。Git 不會。git-status(1) 只會顯示源樹與當前分支 HEAD 不匹配;它不會檢查源樹是否與歷史記錄中的某個先前提交相匹配。
diff和patch:開源協作的貨幣
[edit | edit source]重要的是要儘早瞭解diff(1)和patch(1)實用程式。diff是一個用於顯示兩個文字檔案之間逐行差異的工具。特別是,統一 diff 顯示新增/刪除/更改的行並排顯示,並用上下文行包圍,這些行在兩個版本中都是相同的。假設file1.txt的內容是
this is the first line. this is the same line. this is the last line.
而file2.txt包含以下內容
this is the first line. this line has changed. this is the last line.
然後統一 diff 如下所示
$ diff -u file1.txt file2.txt --- file1.txt 2014-04-18 11:56:35.307111991 +1200 +++ file2.txt 2014-04-18 11:56:51.611010079 +1200 @@ -1,3 +1,3 @@ this is the first line. -this is the same line. +this line has changed. this is the last line. $
注意每行開頭的額外列,其中包含一個“-”表示在第一個檔案中有但在第二個檔案中沒有的每一行,一個“+”表示在第二個檔案中中有但在第一個檔案中沒有的每一行,或者一個空格表示未更改的行。有一些額外的行,使用特殊格式,用於識別正在比較的檔案以及差異發現的行號;所有這些都可以被patch實用程式理解,以便更改file1.txt的副本,使其與file2.txt:
$ diff -u file1.txt file2.txt >patch.txt $ patch <patch.txt patching file file1.txt $ diff -u file1.txt file2.txt $
完全相同。注意第二個diff命令不再產生任何輸出:現在檔案完全相同!
這就是協作軟體開發的起源:人們不再交換整個原始檔,而是隻分發更改,以統一的diff或patch格式(相同內容)。其他人只需將補丁應用到他們的副本即可。並且只要更改之間沒有重疊,您甚至可以將補丁應用到已經應用了其他補丁的檔案diff來自其他人!因此,可以透過這種方式將來自多個來源的更改合併到一個公共版本中,其中包含社群貢獻的所有新功能和錯誤修復。
即使在今天,即使版本控制系統經常使用,這種 diff/補丁仍然是分發更改的基礎。git-diff(1) 和 git-format-patch(1) 都生成與 diff -u 相容的輸出,並且可以被相應地理解patch。因此,即使您的補丁的接收者沒有使用 Git,他們仍然可以接受您的補丁。或者您可能會收到來自沒有使用 Git 的人的補丁,因此他們沒有使用git-format-patch,因此您無法將其提供給 git-am(1) 以自動應用它並儲存提交;但這沒關係,您可以使用 git-apply(1),甚至使用patch本身在您的源樹上,然後代表他們進行提交。
結論
[edit | edit source]您現在知道如何建立一個新的儲存庫,或將您的源樹轉換為 git 儲存庫。您可以新增和提交檔案,還可以撤銷錯誤的提交。您可以刪除檔案、檢視某個提交中檔案的狀態,還可以丟棄未提交的更改。
接下來,我們將從命令列和一些 GUI 工具中檢視 git 專案的歷史記錄,並瞭解 git 的強大分支模型。