Git/簡介

這裡,我們將介紹最簡單的 Git 命令:建立新的倉庫、新增和提交檔案、刪除檔案、回退錯誤提交、丟棄未提交的更改以及檢視特定提交時的檔案。
建立新的 Git 倉庫很簡單。有兩個命令可以實現此功能:git-init(1) 和 git-clone(1)。克隆現有的倉庫將在後面介紹。現在,讓我們在新的目錄中建立一個新的倉庫
$ git init myrepo
在
- /home/username/myrepo/.git/ 初始化空 Git 倉庫 (在 Linux 上)。
- C:/Users/username/myrepo/.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 中的 checkout 操作與 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 倉庫。您可以新增和提交檔案,還可以撤銷錯誤的提交。您可以刪除檔案,檢視特定提交中檔案的狀態,還可以丟棄未提交的更改。
接下來,我們將看看 Git 專案的歷史記錄,使用命令列和一些 GUI 工具,並瞭解 Git 的強大分支模型。