出自 Arch Linux 中文维基
"I've met people who thought git is a front-end to GitHub. They were wrong, git is a front-end to the AUR." — Linus T.

Git 是一個由 Linux 內核作者 Linus Torvalds 編寫的版本控制系統(VCS),現在被用來維護 AUR 軟體包以及數以千計的其他項目,其中包括 Linux 內核。

安裝

安裝 git 軟體包。要使用開發版本,請安裝 git-gitAUR 軟體包。當使用 git svngit guigitk 等工具時請檢查可選依賴項是否安裝。

圖形化前端

參考 git GUI Clients

  • Giggle — 用於 git 的 GTK+ 前端。
https://wiki.gnome.org/Apps/giggle/ || giggle
  • GitAhead — 一個包含內置合併工具的 git 前端。
https://gitahead.github.io/gitahead.com/ || gitaheadAUR
  • Git Cola — 用 Python 編寫的絲滑而強大的 git 圖形前端。
https://git-cola.github.io/ || git-colaAUR
  • Git Extensions — 允許用戶不使用命令行就可以完成 git 各項操作的圖形前端。
https://gitextensions.github.io/ || gitextensionsAUR
  • gitg — 用於查看 git 倉庫的 GNOME GUI 客戶端。
https://wiki.gnome.org/Apps/Gitg || gitg
  • git-gui — Tcl/Tk 庫編寫的可移植 git 圖形前端。
https://git-scm.com/docs/git-gui || git + tk
注意: 要打開 git-gui 的拼寫檢查功能,請安裝 aspell,同時還需要與 LC_MESSAGES 環境變量 相對應的字典文件。參閱 FS#28181aspell
  • GitHub Desktop — 由 GitHub 開發的一個基於 Electron 的 GitHub 客戶端。
https://github.com/desktop/desktop || github-desktopAUR github-desktop-binAUR
  • gitk — Tcl/Tk 庫編寫的 Git 倉庫查看器。
https://git-scm.com/docs/gitk || git + tk
  • Guitar — 一個 git 的圖形化前端。
https://github.com/soramimi/Guitar || guitarAUR
  • lazygit — 一個簡潔的 Git TUI 工具。
https://github.com/jesseduffield/lazygit || lazygit
  • QGit — 可圖形化地按照不同開發分支顯示修訂歷史記錄、查閱補丁內容、查看被修改文件的 Git GUI 查看器。
https://github.com/tibirna/qgit || qgit
  • RabbitVCS — 一組圖形化工具,用於輕鬆、直接地訪問您使用的版本控制系統。
http://rabbitvcs.org/ || rabbitvcsAUR
  • Sublime Merge — 由 Sublime Text 開發商開發的 Git 前端。
https://www.sublimemerge.com/ || sublime-mergeAUR
  • Tig — 基於 ncurses 的 git 字符模式前端。
https://jonas.github.io/tig/ || tig
  • ungit — 在不犧牲 git 各種功能的情況下使其變得更加友好。
https://github.com/FredrikNoren/ungit || nodejs-ungitAUR

配置

你至少需要設置好姓名和郵箱之後才能開始使用 Git:

$ git config --global user.name  "John Doe"
$ git config --global user.email "johndoe@example.com"

參閱 起步 - 初次運行 Git 前的配置

更多設置選項可參閱 #提示與技巧

基本用法

一個 Git 版本庫包含在一個名為 .git 的目錄內,該目錄包含了修訂歷史以及其他元數據。版本庫所跟蹤的目錄(默認為父目錄)稱為工作目錄。在工作樹進行的更改在被提交 (commit) 前需要先暫存 (stage) 起來。Git 還可以讓你恢復以前提交的工作樹文件。

參閱 起步 - Git 基礎

獲取一個 Git 倉庫

  • 初始化一個版本庫
git init,參閱 git-init(1)
  • 克隆 (clone) 一個現有的版本庫
git clone repository,參閱 git-clone(1)

記錄更改

Git 管理的項目存在一個暫存區 (staging area),即 git 目錄中的 index 文件,其中保存了即將包含在你下一次提交中的文件更改。要將某個修改過的文件記錄下來,首先需要將修改後的文件添加到 index(暫存它),然後用 git commit 命令將當前的 index 保存為一次新的提交。

暫存 (stage) 更改

  • 將工作樹中的文件更改添加至 index
git add pathspec,參閱 git-add(1)
  • 移除 index 中記錄的文件更改
git reset pathspec,參閱 git-reset(1)
  • 顯示即將提交的更改、未暫存的更改以及未被 git 跟蹤的文件
git status,參閱 git-status(1)

可以用 .gitignore 文件來讓 git 忽略某些未跟蹤的文件,請參閱 gitignore(5)

Git 不會跟蹤文件移動。合併時的文件移動檢測僅基於內容的相似性。git mv 命令僅僅是為了方便,它相當於:

$ mv -i foo bar
$ git reset -- foo
$ git add bar

提交 (commit) 更改

git commit 命令將暫存區的更改保存至版本庫,參閱 git-commit(1)

  • -m – 後面跟上提交消息作為參數直接提交,而不是打開默認的文本編輯器來寫提交信息後再提交
  • -a – 自動暫存已更改或已刪除的文件(不會添加未跟蹤的文件)
  • --amend – 重做上次提交,用於修改提交消息或修改提交的文件
提示:建議經常性的提交小更改,並附上有意義的提交信息。

選擇修訂版本

Git 提供了多種方式來指定修訂版本,參閱 gitrevisions(7)選擇修訂版本

許多 Git 命令需要用修訂版本作為參數。一次提交記錄可以用下列任何一種方式表示:

  • 某次提交的 SHA-1 哈希值(前7位通常足以唯一標識它)
  • 任意提交時的標籤,如分支名稱或 tag 名稱
  • 標籤 HEAD 總是指向當前 check out 的提交(通常是分支的頭部,除非你使用 git checkout 跳回到歷史記錄中的舊提交)
  • 以上任意一種表示方式加上 ~ 都可以表示之前的提交。例如,HEAD~ 指向 HEAD 的前一次提交,HEAD~5 指向 HEAD 5 次前的提交。

查看更改

查看不同提交間的修改處:

$ git diff HEAD HEAD~3

或者查看暫存區和工作樹之間的不同:

$ git diff

查看修改歷史(其中 "-N" 指定最近的 n 次修改):

$ git log -p (-N)

撤銷修改

  • git reset - 重置當前 HEAD 指針到指定狀態,參閱 git-reset(1)

分支 (branch)

Bug 修復和新功能通常在不同分支裡測試。當一切就緒時它們就可以合併至默認(主)分支。

創建一個分支,其名稱準確地反映了其目的:

$ git branch help-section-addition

列出已存在的分支:

$ git branch

切換分支:

$ git checkout branch

新建分支並切換至該分支:

$ git checkout -b branch

將一個分支合併回主分支:

$ git checkout master
$ git merge branch

如果不存在衝突的話,所有更改將被合併。否則 Git 將顯示一條錯誤信息,並通過給工作樹中的文件加注釋來記錄衝突。添加的注釋可以用 git diff 顯示。要解決衝突,必須編輯文件,刪除注釋,並提交最終版本。請參閱下面的 #處理合併 (merge)

當一個分支使用完畢,可以這樣刪除:

$ git branch -d branch

多人合作

一個典型的 Git 工作流像這樣:

  1. 創建一個新倉庫或克隆一個遠程倉庫。
  2. 新建一個分支用於修改文件,然後提交這些修改。
  3. 將多次提交合併在一起,這樣更便於組織項目或更好地理解。
  4. 把提交的分支合併回主分支。
  5. (可選)將更改推送至遠程伺服器。

合併請求 (pull requests)

在做出並提交更改後,貢獻者可以請求原作者合併更改。這被稱為 合併請求 (pull request)

如果用 pull:

$ git pull location master

pull 命令相當於 fetchmerge 命令的結合。如果存在衝突(比如原作者在同一時間段內在相同位置做了更改),那就有必要手動解決衝突。

另一種方式是,原作者可以選擇想要合併的更改。通過使用 fetch 命令(以及帶有特殊 FETCH_HEAD 標記的 log 命令),可以在決定如何處理合併請求 (pull request) 前查看該請求的內容:

$ git fetch location master
$ git log -p HEAD..FETCH_HEAD
$ git merge location master

使用遠程倉庫 (remote)

遠程 (remote) 是和本地相關聯的遠程倉庫的別名。其實就是創建一個 label 來定義一個位置。這些 label 用於標識經常訪問的倉庫。

添加一個遠程倉庫:

$ git remote add label location

獲取遠程庫裡的內容:

$ git fetch label

顯示本地主分支與遠程主分支之間的差異:

$ git log -p master..label/master

查看當前倉庫相關聯的遠程倉庫:

$ git remote -v

當設置的遠程倉庫是本倉庫的 fork 來源(項目的領導者),這個遠程倉庫會被定義為 upstream(上游)。

向某個倉庫推送 (push) 修改

從原作者處獲得推送修改的權限之後,使用以下命令推送修改:

$ git push location branch

當使用 git clone 獲得這個倉庫後,git 會把倉庫的原始地址記錄在名為 origin 的變量中。

所以一次 典型的 推送可以這樣做:

$ git push origin master

如果使用了 -u (--set-upstream-to) 選項,地址將會被記錄下來,下次只要使用 git push 就可以了。

處理合併 (merge)

可以查看 Git Book 的 遇到衝突時的分支合併 部分了解如何處理合併衝突。合併操作通常是可逆的,如果想返回合併前,可以使用 --abort 命令(比如 git merge --abortgit pull --abort)。

歷史記錄和版本記錄

在歷史記錄中搜索

git log 命令可以顯示歷史記錄信息,其中包含每次提交的校驗和、作者、日期,以及簡略信息。校驗和 就是一次提交對象的 "對象名稱",通常是一個 40 位的 SHA-1 哈希值。

對於具有較長信息的歷史記錄(其中 "checksum" 可以截取前幾位,只要它是唯一的):

$ git show (checksum)

在被跟蹤的文件中搜索 pattern

$ git grep pattern

.c.h 文件中搜索:

$ git grep pattern -- '*.[ch]'

使用標籤 (tag)

給某次提交打標籤來標記這個版本:

$ git tag 2.14 checksum

Tag 通常是用於 發布/標記版本 的,但它可以是任何字符串。通常使用帶注釋的標籤,因為它們會被添加到 Git 資料庫中。

標記當前的提交:

$ git tag -a 2.14 -m "Version 2.14"

列出標籤:

$ git tag -l

刪除某個標籤:

$ git tag -d 2.08

更新遠程庫中的標籤:

$ git push --tags

重新組織 commit

在提交合併請求之前,可能需要合併/重新組織 commit。這是通過 git rebase(變基)--interactive完成的:

$ git rebase -i checksum

然後會打開文本編輯器,其中包含指定範圍內所有提交的摘要;這種情況下會包括最新提交 (HEAD),但不包括 checksum 表示的那次 commit。也可以使用數字來標記,例如用 HEAD~3,這會把最後三次提交變基:

pick d146cc7 Mountpoint test.
pick 4f47712 Explain -o option in readme.
pick 8a4d479 Rename documentation.

修改第一欄中的動作可以決定如何執行變基操作。可選的動作有:

  • pick — 原樣保留每次提交(默認)。
  • edit — 編輯文件和/或 commit 信息。
  • reword — 編輯 commit 信息。
  • squash — 合併/摺疊到先前的提交中。
  • fixup — 合併/摺疊到先前的提交中並丟棄它們的信息。

提交會被重新排序或從歷史記錄中擦除(所以要非常小心)。編輯文件後,Git 將執行指定的操作;如果提示有合併問題待解決,請解決它們並使用 git rebase --continue 來繼續,或使用 git rebase --abort 命令來取消操作。

注意: 合併多次提交的操作只能應用於本地提交,它會導致其他人共享的存儲庫出現問題。

提示與技巧

使用 git-config

Git 從 4 個 ini 類型的配置文件裡讀取配置:

  • /etc/gitconfig 是應用於整個系統的默認配置文件
  • ~/.gitconfig~/.config/git/config (自 1.7.12 起)是應用於特定用戶的配置文件
  • .git/config 是應用於特定倉庫的配置文件

這些文件可以直接編輯,但是更常用的方法是使用 git config,下面是一些示範。

列出當前已配置的變量:

$ git config {--local,--global,--system} --list

將默認文本編輯器從 vim 改成 nano

$ git config --global core.editor "nano -w"

設置默認的推送 (push) 行為:

$ git config --global push.default simple

設置不同的 git difftool 工具(默認是 meld):

$ git config --global diff.tool vimdiff

更多信息請參閱 git-config(1)配置 Git

保持良好的禮儀

  • 當你想為一個現有的項目貢獻時,請先閱讀並理解這個項目的許可,因為它可能會過度限制你更改代碼的權力。有些許可會在代碼的所有權方面引起爭議。
  • 理解這個項目的社區,以及你可以融入其中的程度。要了解項目的主要方向,可以閱讀所有文檔甚至是代碼庫的 log
  • 當發起一個合併請求,或者提交一個補丁時,保證它是小改動並且有完善的文檔;這將有助於項目維護者理解你的改動,並決定是否合併這些改動或是讓你再改一下。
  • 如果貢獻被拒絕,不要氣餒,畢竟這是他們的項目。如果它很重要,請儘可能清楚和耐心地討論這次貢獻的理由,最終可能通過這種方法解決問題。

加快身份驗證

每次向 Git 伺服器推送時都要認證身份,你可能會想要避免這種麻煩。

默認通訊協議

如果你正在使用一個上述那種復用的 SSH 連接,讓 Git 使用 SSH 可能比使用 HTTPS 更快。同時,一些伺服器(比如 AUR)只允許通過 SSH 推送更改。例如,像下面這樣配置可以使得 Git 通過 SSH 訪問 AUR 上的任何倉庫。

~/.gitconfig
[url "ssh://aur@aur.archlinux.org/"]
	insteadOf = https://aur.archlinux.org/
	insteadOf = http://aur.archlinux.org/
	insteadOf = git://aur.archlinux.org/

Bash 自動補全

要啟用 Bash 的自動補全,請在 Bash 啟動文件 裡用 source 加載 /usr/share/git/completion/git-completion.bash 文件。或者也可以安裝 bash-completion

Git 提示符

Git 包帶有一個提示符腳本。要啟用它,請用 source 加載 /usr/share/git/completion/git-prompt.sh 腳本,然後使用 %s 參數設置一個自定義 shell 提示符:

  • Bash 用戶: PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
  • zsh 用戶: setopt PROMPT_SUBST ; PS1='[%n@%m %c$(__git_ps1 " (%s)")]\$ '

要自動完成這項工作,請參閱 Command-line shell#Configuration files

當切換至一個 Git 倉庫所在目錄時,shell 提示符會變成所在分支名稱。也可以配置提示符來顯示其他信息:

Shell variable Information
GIT_PS1_SHOWDIRTYSTATE 已暫存 (staged) 顯示 +,未暫存 (unstaged) 顯示 *
GIT_PS1_SHOWSTASHSTATE 已儲藏 (stashed) 顯示 $
GIT_PS1_SHOWUNTRACKEDFILES 有未跟蹤文件時顯示 %
GIT_PS1_SHOWUPSTREAM <,>,<> 分別表示落後於上游、領先於上游、偏離上游。

GIT_PS1_SHOWUPSTREAM 需要設置為 auto 才能使更改生效。

注意: 如果發生了 $(__git_ps1) 返回 ((unknown)) 的情況,是因為有一個 .git 文件夾在你當前的文件夾裡面,但卻不包含任何存儲庫,因此 Git 不認識它。這有可能發生在你把 ~/.git/config 誤認為是 Git 的配置文件而不是 ~/.gitconfig

你也可以使用來自 AUR 的自定義 git shell 提示符軟體包,例如 bash-git-promptAURgittifyAUR

可視化顯示

要了解已經完成了多少工作:

$ git diff --stat

帶有 fork 顯示的 git log

$ git log --graph --oneline --decorate

給圖形化的 git log 做一個別名(使用 git graph 即可顯示經過修飾的 log):

$ git config --global alias.graph 'log --graph --oneline --decorate'

關於提交 (commit) 的小提示

重置為以前的提交(非常危險,這將會擦除所有內容並改寫為特定提交):

$ git reset --hard HEAD^

如果遠程倉庫的地址發生變化,可以這樣更新它的位置:

$ git remote set-url origin git@address:user/repo.git

自動附加簽名行到提交(將某個 姓名-電郵 簽名添加到提交中,某些項目會要求這樣做):

$ git commit -s

自動附加簽名到補丁(使用 git format-patch commit 時生效):

$ git config --local format.signoff true

提交已更改文件的特定部分。如果有大量更改時,最好拆分成多個提交,這種情況下這個命令通常很有用:

$ git add -p

對提交 (commit) 簽名

Git 允許使用 GnuPG 對提交和標籤進行簽名,請參見 簽署工作

注意: 如果是藉助 pinentry 來進行 GPG 簽名,請確保 export GPG_TTY=$(tty)(或者使用 pinentry-tty),否則當 GPG 處於鎖定狀態時簽名這一步會失敗(因為它無法在 shell 提示符裡詢問 pin 碼)。

配置 Git 使它自動對提交進行簽名:

$ git config --global commit.gpgSign true

在非主分支上工作

偶爾項目維護人員會要求你在其他分支上完成工作。這些分支通常被稱為 develtesting。首先要克隆存儲庫。

要進入不是主分支的分支(git clone 只會顯示主分支,但其他分支其實也是存在的,用 git branch -a 可以顯示出來):

$ git checkout -b branch origin/branch

然後就可以像平常一樣編輯文件,但是要使得整個倉庫都保持同步,下面這兩個命令都要用:

$ git pull --all
$ git push --all

直接將補丁發送至郵件列表

如果你想直接將補丁發送至一個郵件列表,需要安裝以下軟體包:perl-authen-saslperl-net-smtp-sslperl-mime-tools

確保你已經配置了用戶名和郵件地址,可參閱 #配置

配置你的郵箱設置:

$ git config --global sendemail.smtpserver smtp.example.com
$ git config --global sendemail.smtpserverport 587
$ git config --global sendemail.smtpencryption tls
$ git config --global sendemail.smtpuser foobar@example.com

現在你應該可以將補丁發送至某個郵件列表了(可參閱OpenEmbedded:How to submit a patch to OpenEmbedded#Sending patches):

$ git add filename
$ git commit -s
$ git send-email --to=openembedded-core@lists.openembedded.org --confirm=always -M -1

遠程庫很大時的注意事項

Tango-edit-clear.png本文或本章節的語言、語法或風格需要改進。參考:Help:StyleTango-edit-clear.png

原因: 此處使用了非正式的表達、縮寫、HTML 標籤(而不是代碼模板),且沒有使用 wiki 內連結。(在Talk:Git討論)

當遠程庫很大時該怎麼辦?請參考這一節。其中的示例來自於 linux kernel。

最簡單的方式:接收整個倉庫

你可以這樣接收整個倉庫:

$ git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

下載時間會很長,而且 "git clone" 無法斷點續傳(截止至 2018 年 8 月),還會占用很多硬碟空間。

可以用這個命令更新倉庫

$ git pull

部分接收

也許你想把本地倉庫的大小限制得小一點,比如只保留 v4.14 以後的代碼來分離出一個 bug,那麼可以這麼做:

$ git clone --shallow-exclude v4.13   git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git # 這樣就只會下載 v4.14 及以後的文件,v4.13 以前的不會下載。

也許你只需要最新的倉庫快照,忽略所有的歷史記錄。(如果有壓縮包提供且足夠使用,那就下載壓縮包,獲取 git 倉庫快照開銷要大一點。)可以這樣做:

$ git clone --depth 1 git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git

之後也可以這樣獲取歷史提交記錄:

$ git fetch --tags --shallow-exclude v4.1 # 获取 v4.1 之后的提交记录
$ git fetch --tags --shallow-since 2016-01-01

如果沒有 --tags,那麼就接收不到 tags。

獲取其他分支

在上面的示例中,你本地的倉庫僅跟蹤主線內核,即「最近開發完成」的內核。假設你想獲取最近的 「LTS」 版內核,比如最新的 4.14 分支,可以這麼做:

$ git remote set-branches --add origin linux-4.17.y
$ git fetch
$ git branch --track linux-4.17.y origin/linux-4.17.y

最後一行不是必須的,但你應該需要執行它。 (要獲取你需要的那個分支的具體名稱,沒有什麼通用的方法,或許可以靠 web 頁面的 "ref" 連結來猜測)

如果需要 linux-4.17.y 的快照,這樣做:

$ git checkout -b linux-4.17.y

或者這樣做,將它解壓到其他目錄裡:

$ mkdir /foo/bar/src-4.17; cd /foo/bar/src-4.17
$ git clone --no-local --depth 1 -b linux-4.17.y  ../linux-stable

然後像平常一樣,執行 git pull 來更新你的快照。

未來可能出現的其他方案

Git 虛擬文件系統 (Git Virtual Filesystem, GVFS) 由微軟開發,允許在不克隆倉庫至本地的情況下使用 git 倉庫。(參閱 this Microsoft blogWikipedia artcile。)這個功能在 Linux 暫不可用。

無論如何這個功能暫不適用於上述示例中的 Linux 內核倉庫。

Git 伺服器

這一節講述如何配置使用不同的協議連接到存儲庫。

SSH 協議

要使用 SSH 協議,首先要準備一個 SSH 公鑰,可以按照 SSH keys 的指導來完成。要配置一個 SSH 伺服器,請遵循 Secure Shell 的指導。

當 SSH 生成了密鑰之後,將 ~/.ssh/id_rsa.pub 文件的內容粘貼至伺服器上的 ~/.ssh/authorized_keys 文件裡(一行一個,同一個公鑰確保在同一行)。現在 Git 倉庫可以通過 SSH 來訪問:

$ git clone user@foobar.com:my_repository.git

現在,如果你的 SSH 客戶端的 StrictHostKeyChecking 選項設為了 ask(默認),你應該會收到來自 SSH 的問題,要你回答 yes/no。輸入 yes 然後回車,你的倉庫就能被取出。同時,由於通過 SSH 協議訪問,你現在應該有提交權限。

要把一個已存在的倉庫改成使用 SSH 訪問,需要重新定義一下遠程地址:

$ git remote set-url origin git@localhost:my_repository.git

要從非 22 埠連接,可以在每台主機的 /etc/ssh/ssh_config~/.ssh/config 裡配置。要為某個本地倉庫設置埠(示例中用的 443 埠):

.git/config
[remote "origin"]
    url = ssh://user@foobar.com:443/~my_repository/repo.git

你可以通過只允許用戶執行 push 和 pull 操作來進一步提高 SSH 帳戶的安全性。這是通過將該帳戶的默認登錄 shell 換成 git-shell 來實現的。在 配置伺服器 中對此有所描述。

Smart HTTP 協議

通過使用 git-http 後端,Git 可以像使用 SSH 協議或 Git 協議一樣高效地使用 HTTP(S) 協議。此外,它不僅可以從倉庫中克隆或拉取更改,還可以通過 HTTP(S) 推送更改。

這個設置相當簡單,因為你只需要安裝 Apache Web 伺服器(apache,啟用 mod_cgimod_aliasmod_env),當然還要安裝 git

當你正在進行基本設置時,請將以下內容添加到 Apache 配置文件中,該配置文件通常位於:

/etc/httpd/conf/httpd.conf
<Directory "/usr/lib/git-core*">
    Require all granted
</Directory>
 
SetEnv GIT_PROJECT_ROOT /srv/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/lib/git-core/git-http-backend/

這裡假設你的 Git 倉庫位於 /srv/git,並且你想用類似 http(s)://your_address.tld/git/your_repo.git 的方式來訪問它們。

注意: 請確保 Apache 對你的倉庫有讀寫權限。

如果需要更多詳細文檔,請訪問:

Git 協議

注意: Git 協議沒有加密或認證機制,且只允許讀取。

Start 並且 enable git-daemon.socket 這個 systemd 單元。

守護程序會帶有以下選項啟動:

ExecStart=-/usr/lib/git-core/git-daemon --inetd --export-all --base-path=/srv/git

位於 /srv/git/ 目錄下的倉庫會被守護程序識別。客戶端能以類似這樣的方式連接:

$ git clone git://location/repository.git

設置訪問權限

要限制讀取和/或寫入權限,可以使用常規 Unix 權限控制。更多信息請參考 when gitolite is overkill

如果需要更加精細的訪問控制,請參考 gitolitegitosis

參考資料