1. 程式人生 > >Git 程式設計師篇

Git 程式設計師篇

關於 Git

Git 背後的故事

  • 偉大的作品總是誕生於偉大的時代,正如 Git 同樣誕生於一個英雄輩出、極富紛爭的年代。
  • 2005 年,Linux 核心開發社群正面臨嚴峻的挑戰:他們不能繼續使用 BitKeeper 了(注:原因是當時BitKeeper 著作權所有者決定收回授權,核心開發團隊與其協商無果),而又沒有其他的 SCM (Software Configuration Management)可滿足他們的分散式系統的需求。Linux 之父 Linus Torvalds 接受了這個挑戰,決定開發一個新的版本控制系統,並制定了分散式、非線性、簡單快速的設計目標。週末他消失了,新的一週,Git 問世了。
  • 自誕生以來,Git 日臻成熟完善,在高度易用的同時,仍然保留著初期設定的目標。 它的速度飛快,極其適合管理大專案,有著令人難以置信的非線性分支管理系統。今天,Git 已經成為上萬個專案的版本控制系統,並且在程式設計師中引發了開源熱潮。

基本概念
和 SVN 相比,Git 有一些獨特的術語和概念,有必要了解一下。

  • 工作區(working tree):可以理解為客戶端電腦上的專案路徑
  • 版本庫(repository):工作區有一個隱藏目錄 .git,這個不算工作區,而是 Git 的版本庫
  • 暫存區(stage):存放在 .git 目錄下下的 index 檔案(.git/index)中,所以我們把暫存區有時也叫作索引(index)
  • 預設的遠端版本庫(origin):儘管 Git 是分散式的,但它不排斥中心伺服器
  • 工作區、版本庫中的暫存區和版本庫,以及遠端庫之間的關係如下:

圖中我們可以看出此時 “HEAD” 實際是指向 master 分支的一個"遊標"。所以圖示的命令中出現 HEAD 的地方可以用 master 來替換。

git add

  • 暫存區的目錄樹被更新,同時工作區修改(或新增)的檔案內容被寫入到物件庫中的一個新的物件中,而該物件的ID被記錄在暫存區的檔案索引中。

git commit

  • 暫存區的目錄樹寫到版本庫(物件庫)中,HEAD 指向分支會做相應的更新。

git reset HEAD

  • 暫存區的目錄樹會被重寫,被HEAD 指向分支的目錄樹所替換,但是工作區不受影響。

git rm --cached

  • 直接從暫存區刪除檔案,工作區則不做出改變。

git checkout 或者 git checkout –

  • 暫存區全部或指定的檔案替換工作區的檔案。這個操作很危險,會清除工作區中未新增到暫存區的改動。

git checkout HEAD 或者 git checkout HEAD

  • HEAD 指向的分支中的全部或者部分檔案替換暫存區和以及工作區中的檔案。這個命令也是極具危險性的,因為不但會清除工作區中未提交的改動,也會清除暫存區中未提交的改動。

git clone [email protected]:/sdyouth/git/yangmaosen.git

  • 從遠端庫克隆專案

git fetch

  • 從遠端的分支獲取最新的版本到本地。

get push

  • 將本地版本庫的分支推送到遠端庫上對應的分支。

術語中英文對照

English                       中        文
# ---------------------------------------------------------------------------------------------
3-way merge                 # 三方合併
abbreviate             # 簡寫(的 SHA-1 值)
alternate object database    # 備用物件庫
amend                # 修補
ancestor              # 祖先,祖先提交
annotated tag           # 附註標籤
bare repository          # 純倉庫
bisect                # 二分查詢
blob object             # 資料物件
branch                  # 分支
bundle                # 包
bypass                # 繞過
cache                # 索引(的別稱)
chain                # (提交)鏈
changeset              # 變更集
checkout               # 檢出
checksum               # 校驗,校驗和
cherry-picking           # 揀選
# ---------------------------------------------------------------------------------------------
clean                # 乾淨(的工作區)
clone                # 克隆
commit                # 提交
commit message          # 提交說明
commit object           # 提交物件
commit-ish (also committish) # 提交號
conflict           # 衝突
core Git           # 核心 Git 工具
DAG              # 有向無環圖
dangling object       # 搖擺物件
detached HEAD        # 分離頭指標
directory           # 目錄
dirty             # 髒(的工作區)
dumb HTTP protocol     # 啞 HTTP 協議
evil merge          # 壞合併(合併引入了父提交沒有的修改)
fast-forward        # 快進
fetch            # 獲取
file system        # 檔案系統
fork            # 派生
Git archive        # 倉庫(對於 arch 使用者)
gitfile          # gitfile(倉庫連結檔案)
grafts          # (提交)移植
# ---------------------------------------------------------------------------------------------
hash            # 雜湊值
HEAD            # HEAD(頭指標,亦即當前分支)
head            # 頭、分支
head ref          # 分支
header            # 頭資訊
hook            # 鉤子
hunk            # 補丁片段
index            # 索引
index entry         # 索引條目
loose object        # 鬆散物件
loose refs          # 鬆散引用
master            # master(預設分支名)
merge            # 合併
object            # 物件
object database      # 物件庫
object identifier      # 物件識別符號
object name        # 物件名稱
object type        # 物件型別
octopus          # 章魚式合併(兩分支以上的合併)
origin           # origin(預設的遠端名稱)
pack            # 包
pack index         # 包索引
packfile          # 包檔案
parent            # 父提交
patch            # 補丁
# ---------------------------------------------------------------------------------------------
pathspec          # 路徑規格
pattern          # 模式
pickaxe          # 挖掘
plumbing          # 管件(Git 底層核心命令的別稱)
porcelain          # 瓷件(Git 上層封裝命令的別稱)
precious-objects repo  # 珍品倉庫
prune            # 清除
pull            # 拉,拉取
push            # 推,推送
reachable         # 可達
rebase           # 變基
ref             # 引用
reflog           # 引用日誌
refspec          # 引用規格
remote           # 遠端,遠端倉庫
remote-tracking branch  # 遠端跟蹤分支
replay          # 重放
repo           # 倉庫
repository       # 倉庫
resolve        # (衝突)解決
revert        # 還原
revision       # 版本
rewind         # 回退
# ---------------------------------------------------------------------------------------------
SCM           # 原始碼管理(工具)
SHA-1           # SHA-1(安全雜湊演算法1)
shallow repository    # 淺(克隆)倉庫
signed tag         # 簽名標籤
smart HTTP protocol # 智慧 HTTP 協議
squash          # 壓縮
stage          # n. 暫存區(即索引); v. 暫存
stash         # n. 貯藏區; v. 貯藏
submodule      # 子模組
symref        # 符號引用
tag          # n. 標籤; v. 打標籤
tag object      # 標籤物件
tagger         # 打標籤者
topic branch      # 主題分支
track          # 跟蹤
trailer         # 尾部署名
tree           # 樹(工作區或樹物件)
tree object       # 樹物件
tree-ish (also treeish)  # 樹物件(或可以解析為一樹物件)
unmerged index      # 未合併索引
unpack           # 解包
unreachable object    # 不可達物件
unstage           # 取消暫存
upstream           # 上游
upstream branch       # 上游分支
working tree          # 工作區

 

安裝Git客戶端

Git 目前幾乎可以執行在包括 Linux/Unix、Solaris、Mac和 Windows 等所有平臺上。Git 各平臺安裝包下載地址為:http://git-scm.com/downloads

Linux 平臺安裝
Git 的工作需要呼叫 curl,zlib,openssl,expat,libiconv 等庫的程式碼,所以需要先安裝這些依賴工具。如果你碰巧用 Debian 或 Ubuntu,使用下面的命令,就可以直接完成Git的安裝。

sudo apt-get install git

 

Windows 平臺

寫這篇部落格的時候,最新的 Git 客戶端版本是 Git-2.19.1。點此下載最新的安裝檔案,然後執行,按預設選項安裝即可。我大概只更改了預設的編輯器,其他接受了預設選項。

友情提示:關於行結束符,預設的選擇是 checkout 時從 LF 轉 CRLF,commit 時 從 CRLF 轉 LF。這樣設定適合跨平臺的開發,不過 add 操作時會出現警告,忽略即可。

 

 

 

 

 

 

 

 

 

 

 

 

 

安裝完成後,右鍵選單會增加 Git GUI 和 Git Bash 兩項。使用者可以根據自己的習慣選擇使用 Git GUI 或 Git Bash 來建立、管理自己的版本庫。

 

在非本地版本庫的工作區開啟 Git GUI,介面如下:

這裡,可以建立本地版本庫,可以克隆遠端庫,也可以開啟本地已經存在的版本庫。

在本地版本庫的工作區開啟 Git GUI,介面如下:

  • 版本庫中新增加的或者修改過的檔案將會出現在左側上方的視窗中。也可以點選“Rescan”按鈕重新檢查新增加的或者修改過的檔案,以更新左側上方的視窗顯示內容。
  • 點選“Stage Changed”按鈕,新增加的或者修改過的檔案將會從左側上方的視窗移到左側下方的視窗中,完成檔案暫存。點選暫存區的單個檔案圖示,該檔案將回退到未暫存的狀態。
  • 點選“Commit”按鈕,已經暫存的檔案將被提交到版本庫——特別說明,這裡的版本庫是指本地庫,而非遠端庫。點選“Push”按鈕,才可以將本地庫的當前分支提交到遠端庫。關於分支,將在後面詳細說明。

 

使用 Git

  • 理解了 Git 的理念,使用 Git 是一個很自然的過程,不管是在 windows 平臺還是 linux 平臺上,不管用 Git GUI 還是 Git Bash。本博文主要介紹 windows 平臺上的使用,以 Git GUI 為主,間或使用 Git Bash。

客戶端生成數字證書

  • Git 是分散式的,可以不依賴於遠端庫而獨立工作。因此,數字證書不是必須的。如果打算連線遠端庫,這項準備工作只需要做一次。

假定有一個遠端庫:

因為使用了的是預設埠,可以簡寫為:

  • [email protected]:/sdyouth/git/GitTest.git
  • 出於安全考慮,只有數字證書公鑰儲存在 Git 伺服器的 Git 客戶端才能連線這個遠端庫。通常,Git 客戶端的數字證書儲存在 C:\Users\使用者名稱.ssh 路徑下。
  • 如果 Git 客戶端不存在 C:\Users\使用者名稱.ssh 路徑,請啟動 Git Bash,嘗試從遠端庫克隆 test 專案:
$ git clone [email protected]:/sdyouth/git/GitTest.git

第一次連線到目標 Git 伺服器時會得到一個提示:

Cloning into 'GitTest'...
The authenticity of host 'sdysit.com (120.55.169.217)' can't be established.
ECDSA key fingerprint is SHA256:tTXVyJPoqCiEZP3+3E2uOOmvXIWGEWtCcUOgBhlb+iQ.
Are you sure you want to continue connecting (yes/no)? yes

選擇 yes

Warning: Permanently added 'sdysit.com,120.55.169.217' (ECDSA) to the list of known hosts.
[email protected]'s password:
Connection closed by 120.55.169.217 port 22
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
  • 最終的結果肯定是失敗的,即使你輸入了正確的密碼。但我們已經將此伺服器加入到了已知伺服器列表中。此時 C:\Users\使用者名稱.ssh 下會多出一個檔案 known_hosts,以後在這臺電腦上再次連線目標 Git 伺服器時不會再提示上面的語句。

如果 Git 客戶端存在 C:\Users\使用者名稱.ssh 路徑,則直接在 Git Bash 執行以下命令,生成數字證書:

$ ssh-keygen -t rsa -C "[email protected]"
  • [email protected] 是我的使用者名稱。每個使用者都應該有自己的有別於其他使用者的使用者名稱。雖然 Git 不限定使用者的操作許可權,但是管理員需要知道來訪者是誰,以便於在該使用者離開團隊時,從服務端刪除他的數字證書公鑰。
  • 現在,C:\Users\使用者名稱.ssh 下會多出兩個檔案 id_rsa 和 id_rsa.pub,id_rsa 是私鑰,id_rsa.pub 是公鑰。請把公鑰檔案交給管理員(出於安全考慮,這裡不討論管理員的工作),很快你就成為遠端庫的合法使用者了。管理員也許會給你一個密碼,請牢記並妥善保管。

 

從遠端庫克隆專案

前文已經展示過使用 Git Bash 克隆版本庫,下圖為使用 Git GUI 從遠端庫克隆 FY-3 專案到本地 D:\YouthGit 路徑下。友情提示:如果 D:\YouthGit 路徑下已經存在 FY-3 資料夾,將無法完成克隆。

 

 

建立本地版本庫

以在本地 D:\YouthGit 路徑下建立 FY-2 專案為例。啟動 Git Bash,執行以下操作即可。

yms@LAPTOP-07EQCT4C MINGW64 /d/YouthGit
$ pwd
/d/YouthGit

yms@LAPTOP-07EQCT4C MINGW64 /d/YouthGit
$ mkdir FY-2

yms@LAPTOP-07EQCT4C MINGW64 /d/YouthGit
$ cd FY-2

yms@LAPTOP-07EQCT4C MINGW64 /d/YouthGit/FY-2
$ git init
Initialized empty Git repository in D:/YouthGit/FY-2/.git/

yms@LAPTOP-07EQCT4C MINGW64 /d/YouthGit/FY-2 (master)

如果使用 Git GUI,建立本地專案版本庫如下圖所示。

 

關聯本地版本庫到遠端庫
在本地建立的版本庫,要關聯到遠端庫,前提是被關聯的遠端庫必須存在。以在本地 D:\YouthGit 路徑下建立 的FY-2 專案為例,使用 Git GUI 關聯到遠端庫 [email protected]:/sdyouth/git/FY-2.git,需要新增遠端庫。點選選單Remote --> Add…,在彈出視窗中正確填入遠端庫路徑,並給遠端庫取一個恰當的名字,點選 Add 按鈕即可完成操作。

 

檢查工作區

  • 當工作區新增加了檔案,或者原有的檔案有改變時,啟動 Git GUI 後會自動顯示在未暫存檔案列表中。點選“Rescan”按鈕,也可以重新檢查新增加的或者修改過的檔案,以更新左側上方的視窗顯示內容。

儲存更新暫存區

  • 點選“Stage Changed”按鈕,新增加的或者修改過的檔案將會從左側上方的視窗移到左側下方的視窗中,完成檔案暫存。點選暫存區的單個檔案圖示,該檔案將回退到未暫存的狀態。

提交更新至版本庫

  • 點選“Commit”按鈕,已經暫存的檔案將被提交到版本庫——特別說明,這裡的版本庫是指本地庫,而非遠端庫。每次 commit 之前,請務必填寫說明,否則 Git GUI 將會拒絕執行命令。關於本次 commit 的說明,對於合作者,以及以後的開發工作有著非常重要的作用。

建立分支
假定在 FY-2 專案中建立以 master 分支當前程式碼為起點的 demo 分支,點選 Git GUI 選單 Branch --> Create…,在彈出視窗中照下圖操作即可。建立新的分支後,Git GUI 自動切換到新的分支,並更新工作區。

 

切換分支
點選 Git GUI 選單 Branch --> Checkout…,在彈出視窗中照下圖操作,即可切換到 master 分支,同時更新工作區。

 

合併分支
點選 Git GUI 選單 Merge --> Local Merge…,在彈出視窗中照下圖操作,即將 domo 分支合併到 master 分支上。

 

將本地的分支推送到遠端庫
點選 Push 按鈕,在彈出視窗上照下圖操作,即可將本地庫的 master 分支推送到遠端庫。

 

獲取遠端庫的最新版本
點選 Git GUI 選單 Remote --> Fetch from,在第三級選單中選擇恰當的遠端庫,在彈出視窗中照下圖操作,即可獲取遠端庫的最新版本。

另外,我們還可以選擇 Remote --> Rrune from 來完成更新。git fetch 和 git pull 的區別在於:

  • git fetch:相當於是從遠端獲取最新版本到本地,不會自動merge
  • git pull:相當於是從遠端獲取最新版本並merge到本地

在實際使用中,git fetch更安全一些,因為在merge前,我們可以檢視更新情況,然後再決定是否合併。比如,使用 Git Bash 執行 git fetch,可以完成更精確的操作。

git fetch origin master:tmp
git diff tmp 
git merge tmp

 

Git 常用命令集

符號說明:

  • 尖括號(< >)表示需要替換成尖括號內文字描述的內容
  • 方括號([ ])表示可選項
  • 遠端庫名,形如 [email protected]:/sdyouth/git/GitTest.git
  • 路徑名,形如 D:\YouthGit\GitTest 或者 GitTest

初始化操作:

$ git config --list   # 檢查已有的配置資訊
$ git config --global user.name <name>   # 設定提交者名字
$ git config --global user.email <email>   # 設定提交者郵箱
$ git config --global core.editor <editor>   # 設定預設文字編輯器
$ git config --global merge.tool <tool>      # 設定解決合併衝突時差異分析工具

建立新的版本庫:

$ git init [--bare] [path] # 在當前路徑或 path 下建立版本庫。客戶端不建議使用 bare 引數

克隆已經存在的版本庫:

$ git clone <remote> [path] # 在當前路徑或 path 下克隆遠端庫

修改和提交:

$ git add .   # 新增所有改動過的檔案
$ git add <file>   # 新增指定的檔案
$ git mv <old> <new>   # 檔案重新命名
$ git rm <file>   # 刪除檔案
$ git rm -cached <file>   # 停止跟蹤檔案但不刪除
$ git commit -m <file>   # 提交指定檔案
$ git commit -m “commit message”   # 提交所有更新過的檔案
$ git commit -amend   # 修改最後一次提交
$ git commit -C HEAD -a -amend   # 增補提交(不會產生新的提交歷史紀錄)

檢視提交歷史:

$ git log   # 檢視提交歷史
$ git log -p <file>   # 檢視指定檔案的提交歷史
$ git blame <file>   # 以列表方式檢視指定檔案的提交歷史
$ gitk   # 檢視當前分支歷史紀錄
$ gitk <branch>   # 檢視某分支歷史紀錄
$ gitk --all   # 檢視所有分支歷史紀錄
$ git branch -v   # 每個分支最後的提交
$ git status   # 檢視當前狀態
$ git diff   # 檢視變更內容

撤消操作:

$ git reset -hard HEAD   # 撤消工作目錄中所有未提交檔案的修改內容
$ git checkout HEAD <file1> <file2>  # 撤消指定的未提交檔案的修改內容
$ git checkout HEAD.  # 撤消所有檔案
$ git revert <commit> # 撤消指定的提交

分支與標籤:

$ git branch     # 顯示所有本地分支
$ git checkout <branch/tagname>   # 切換到指定分支或標籤
$ git branch <new-branch>   # 建立新分支
$ git branch -d <branch>   # 刪除本地分支
$ git tag   # 列出所有本地標籤
$ git tag <tagname>   # 基於最新提交建立標籤
$ git tag -d <tagname>   # 刪除標籤

合併與衍合:

$ git merge <branch>  # 合併指定分支到當前分支
$ git rebase <branch>   # 衍合指定分支到當前分支

遠端操作:

$ git remote -v         # 檢視遠端版本庫資訊
$ git remote show <remote>     # 檢視指定遠端版本庫資訊
$ git remote add <remote> <url>  # 新增遠端版本庫
$ git fetch <remote>         # 從遠端庫獲取程式碼
$ git pull <remote> <branch>     # 下載程式碼及快速合併
$ git push <remote> <branch>      # 上傳程式碼及快速合併
$ git push <remote> : <branch>/<tagname> # 刪除遠端分支或標籤
$ git push -tags                 # 上傳所有標籤