天天看點

萬字詳解!Git 入門最佳實踐 !

前言

git簡介

實用主義

深入探索

總結

參考資料

 <code>git</code> 是程式員學習和工作都離不開發工具,今天和大家分享 <code>git</code> 常用指令總結。

<code>git</code> 是一種分布式版本控制系統,它可以不受網絡連接配接的限制,加上其它衆多優點,目前已經成為程式開發人員做項目版本管理時的首選,非開發人員也可以用 <code>git</code> 來做自己的文檔版本管理工具。

大概是大二的時候開始接觸和使用git,從一開始的零接觸到現在的重度依賴,真是感歎 <code>git</code> 的強大。

<code>git</code> 的api很多,但其實平時項目中90%的需求都隻需要用到幾個基本的功能即可,是以本文将從 <code>實用主義</code> 和 <code>深入探索</code> 2個方面去談談如何在項目中使用 <code>git</code>,一般來說,看完 <code>實用主義</code> 這一節就可以開始在項目中動手用。

“ 說明:本文的操作都是基于 mac 系統

進入 git官網 下載下傳合适你的安裝包,安裝好 <code>git</code> 後,打開指令行工具,進入工作檔案夾(為了便于了解我們在系統桌面上示範),建立一個新的demo檔案夾。

萬字詳解!Git 入門最佳實踐 !

進入 github網站 注冊一個賬号并登入,進入 我的部落格,點選 <code>clone or download</code> ,再點選<code>use https</code> ,複制項目位址 <code>https://github.com/gafish/gafish.github.com.git</code> 備用。

再回到指令行工具,一切就緒,接下來進入本文的重點。

所謂實用主義,就是掌握了以下知識就可以玩轉 <code>git</code>,輕松應對90%以上的需求。以下是實用主義型的git指令清單,先大緻看一下

<code>git clone</code>

<code>git config</code>

<code>git branch</code>

<code>git checkout</code>

<code>git status</code>

<code>git add</code>

<code>git commit</code>

<code>git push</code>

<code>git pull</code>

<code>git log</code>

<code>git tag</code>

接下來,将通過對 我的部落格 倉庫進行執行個體操作,講解如何使用 <code>git</code> 拉取代碼到送出代碼的整個流程。

從git伺服器拉取代碼

代碼下載下傳完成後在目前檔案夾中會有一個 <code>gafish.github.com</code> 的目錄,通過 <code>cd gafish.github.com</code> 指令進入目錄。

配置開發者使用者名和郵箱

每次代碼送出的時候都會生成一條送出記錄,其中會包含目前配置的使用者名和郵箱。

建立、重命名、檢視、删除項目分支,通過 <code>git</code> 做項目開發時,一般都是在開發分支中進行,開發完成後合并分支到主幹。

建立一個名為 <code>daily/0.0.0</code> 的日常開發分支,分支名隻要不包括特殊字元即可。

如果覺得之前的分支名不合适,可以為建立的分支重命名,重命名分支名為 <code>daily/0.0.1</code>

通過不帶參數的branch指令可以檢視目前項目分支清單

如果分支已經完成使命則可以通過 <code>-d</code> 參數将分支删除,這裡為了繼續下一步操作,暫不執行删除操作

切換分支

切換到 <code>daily/0.0.1</code> 分支,後續的操作将在這個分支上進行

檢視檔案變動狀态

通過任何你喜歡的編輯器對項目中的 <code>readme.md</code> 檔案做一些改動,儲存。

通過 <code>git status</code> 指令可以看到檔案目前狀态 <code>changes not staged for commit:</code> (改動檔案未送出到暫存區)

添加檔案變動到暫存區

通過指定檔案名 <code>readme.md</code> 可以将該檔案添加到暫存區,如果想添加所有檔案可用 <code>git add .</code> 指令,這時候可通過 <code>git status</code> 看到檔案目前狀态 <code>changes to be committed:</code> (檔案已送出到暫存區)

送出檔案變動到版本庫

通過 <code>-m</code> 參數可直接在指令行裡輸入送出描述文本

将本地的代碼改動推送到伺服器

<code>origin</code> 指代的是目前的git伺服器位址,這行指令的意思是把 <code>daily/0.0.1</code> 分支推送到伺服器,當看到指令行傳回如下字元表示推送成功了。

現在我們回到github網站的項目首頁,點選 <code>branch:master</code> 下拉按鈕,就會看到剛才推送的<code>daily/00.1</code> 分支了

将伺服器上的最新代碼拉取到本地

如果其它項目成員對項目做了改動并推送到伺服器,我們需要将最新的改動更新到本地,這裡我們來模拟一下這種情況。

進入github網站的項目首頁,再進入 <code>daily/0.0.1</code> 分支,線上對 <code>readme.md</code> 檔案做一些修改并儲存,然後在指令中執行以上指令,它将把剛才線上修改的部分拉取到本地,用編輯器打開 <code>readme.md</code> ,你會發現檔案已經跟線上的内容同步了。

如果線上代碼做了變動,而你本地的代碼也有變動,拉取的代碼就有可能會跟你本地的改動沖突,一般情況下 <code>git</code> 會自動處理這種沖突合并,但如果改動的是同一行,那就需要手動來合并代碼,編輯檔案,儲存最新的改動,再通過 <code>git add .</code> 和 <code>git commit -m 'xxx'</code> 來送出合并。

檢視版本送出記錄

通過以上指令,我們可以檢視整個項目的版本送出記錄,它裡面包含了<code>送出人</code>、<code>日期</code>、<code>送出原因</code>等資訊,得到的結果如下:

送出記錄可能會非常多,按 <code>j</code> 鍵往下翻,按 <code>k</code> 鍵往上翻,按 <code>q</code> 鍵退出檢視

為項目标記裡程碑

當我們完成某個功能需求準備釋出上線時,應該将此次完整的項目代碼做個标記,并将這個标記好的版本釋出到線上,這裡我們以 <code>publish/0.0.1</code> 為标記名并釋出,當看到指令行傳回如下内容則表示釋出成功了

設定哪些内容不需要推送到伺服器,這是一個配置檔案

<code>.gitignore</code> 不是 <code>git</code> 指令,而在項目中的一個檔案,通過設定 <code>.gitignore</code> 的内容告訴<code>git</code> 哪些檔案應該被忽略不需要推送到伺服器,通過以上指令可以建立一個 <code>.gitignore</code> 檔案,并在編輯器中打開檔案,每一行代表一個要忽略的檔案或目錄,如:

以上内容的意思是 <code>git</code> 将忽略 <code>demo.html</code> 檔案 和 <code>build/</code> 目錄,這些内容不會被推送到伺服器上

通過掌握以上這些基本指令就可以在項目中開始用起來了,如果追求實用,那關于 <code>git</code> 的學習就可以到此結束了,偶爾遇到的問題也基本上通過 <code>google</code> 也能找到答案,如果想深入探索 <code>git</code> 的高階功能,那就繼續往下看 <code>深入探索</code> 部分。

就是你在電腦裡能看到的目錄,比如上文中的 <code>gafish.github.com</code> 檔案夾就是一個工作區

工作區有一個隐藏目錄 <code>.git</code>,這個不算工作區,而是 <code>git</code> 的版本庫。

本地版本庫裡存了很多東西,其中最重要的就是稱為 <code>stage</code>(或者叫index)的暫存區,還有<code>git</code> 為我們自動建立的第一個分支 <code>master</code>,以及指向 <code>master</code> 的一個指針叫 <code>head</code>。

一般指的是 <code>git</code> 伺服器上所對應的倉庫,本文的示例所在的<code>github</code>倉庫就是一個遠端版本庫

<code>工作區</code>、<code>暫存區</code>、<code>本地版本庫</code>、<code>遠端版本庫</code>之間幾個常用的 <code>git</code> 操作流程如下圖所示:

分支是為了将修改記錄的整個流程分開存儲,讓分開的分支不受其它分支的影響,是以在同一個資料庫裡可以同時進行多個不同的修改

前面提到過 <code>master</code> 是 <code>git</code> 為我們自動建立的第一個分支,也叫主分支,其它分支開發完成後都要合并到 <code>master</code>

<code></code>

标簽是用于标記特定的點或送出的曆史,通常會用來标記釋出版本的名稱或版本号(如:<code>publish/0.0.1</code>),雖然标簽看起來有點像分支,但打上标簽的送出是固定的,不能随意的改動,參見上圖中的<code>1.0</code> / <code>2.0</code> / <code>3.0</code>

<code>head</code> 指向的就是目前分支的最新送出

以上概念了解的差不多,那就可以繼續往下看,下面将以具體的操作類型來講解 <code>git</code>的高階用法
添加檔案到暫存區

通過此指令将打開互動式子指令系統,你将看到如下子指令

通過輸入序列号或首字母可以選擇相應的功能,具體的功能解釋如下:

<code>status</code>:功能上和 <code>git add -i</code> 相似,沒什麼鳥用

<code>update</code>:詳見下方 <code>git add -u</code>

<code>revert</code>:把已經添加到暫存區的檔案從暫存區剔除,其操作方式和 <code>update</code> 類似

<code>add untracked</code>:可以把新增的檔案添加到暫存區,其操作方式和 <code>update</code> 類似

<code>patch</code>:詳見下方 <code>git add -p</code>

<code>diff</code>:比較暫存區檔案和本地版本庫的差異,其操作方式和 <code>update</code> 類似

<code>quit</code>:退出 <code>git add -i</code> 指令系統

<code>help</code>:檢視幫助資訊

直接進入互動指令中最有用的 <code>patch</code> 模式

這是互動指令中最有用的模式,其操作方式和 <code>update</code> 類似,選擇後 <code>git</code> 會顯示這些檔案的目前内容與本地版本庫中的差異,然後您可以自己決定是否添加這些修改到暫存區,在指令行 <code>stage deletion [y,n,q,a,d,/,?]?</code> 後輸入 <code>y,n,q,a,d,/,?</code> 其中一項選擇操作方式,具體功能解釋如下:

y:接受修改

n:忽略修改

q:退出目前指令

a:添加修改

d:放棄修改

/:通過正規表達式比對修改内容

?:檢視幫助資訊

直接進入互動指令中的 <code>update</code> 模式

它會先列出工作區 <code>修改</code> 或 <code>删除</code> 的檔案清單,<code>新增</code> 的檔案不會被顯示,在指令行 <code>update&gt;&gt;</code> 後輸入相應的清單序列号表示選中該項,回車繼續選擇,如果已選好,直接回車回到指令主界面

添加工作區 <code>修改</code> 或 <code>新增</code> 的檔案清單, <code>删除</code> 的檔案不會被添加

把暫存區的檔案送出到本地版本庫

不打開編輯器,直接在指令行中輸入多行送出原因

将工作區 <code>修改</code> 或 <code>删除</code> 的檔案送出到本地版本庫, <code>新增</code> 的檔案不會被送出

修改最新一條送出記錄的送出原因

将目前檔案改動送出到 <code>head</code> 或目前分支的曆史id

移動或重命名檔案、目錄

将 <code>a.md</code> 重命名為 <code>b.md</code> ,同時添加變動到暫存區,加 <code>-f</code> 參數可以強制重命名,相比用 <code>mv a.md b.md</code> 指令省去了 <code>git add</code> 操作

從工作區和暫存區移除檔案

從工作區和暫存區移除檔案 <code>b.md</code> ,同時添加變動到暫存區,相比用 <code>rm b.md</code> 指令省去了<code>git add</code> 操作

允許從工作區和暫存區移除目錄

以簡短方式檢視工作區和暫存區檔案狀态,示例如下:

檢視工作區和暫存區檔案狀态,包括被忽略的檔案

檢視、建立、删除分支

檢視本地版本庫和遠端版本庫上的分支清單

檢視遠端版本庫上的分支清單,加上 <code>-d</code> 參數可以删除遠端版本庫上的分支

分支未送出到本地版本庫前強制删除分支

檢視帶有最後送出id、最近送出原因等資訊的本地版本庫分支清單

将其它分支合并到目前分支

将待合并分支上的 <code>commit</code> 合并成一個新的 <code>commit</code> 放入目前分支,适用于待合并分支的送出記錄不需要保留的情況

預設情況下,<code>git</code> 執行"<code>快進式合并</code>"(fast-farward merge),會直接将 <code>master</code> 分支指向<code>develop</code> 分支,使用 <code>--no-ff</code> 參數後,會執行正常合并,在 <code>master</code> 分支上生成一個新節點,保證版本演進更清晰。

在沒有沖突的情況下合并,不想手動編輯送出原因,而是用 <code>git</code> 自動生成的類似 <code>merge branch 'test'</code> 的文字直接送出

建立 <code>daily/0.0.1</code> 分支,同時切換到這個新建立的分支

從本地版本庫的 <code>head</code>(也可以是送出id、分支名、tag名) 曆史中檢出 <code>demo.html</code> 覆寫目前工作區的檔案,如果省略 <code>head</code> 則是從暫存區檢出

這個指令會建立一個全新的,完全沒有曆史記錄的新分支,但目前源分支上所有的最新檔案都還在,真是強迫症患者的福音,但這個新分支必須做一次 <code>git commit</code> 操作後才會真正成為一個新分支。

這個指令主要用來比較兩個分支間的差異内容,并提供互動式的界面來選擇進一步的操作,這個指令不僅可以比較兩個分支間的差異,還可以比較單個檔案的差異。

在 <code>git</code> 的棧中儲存目前修改或删除的工作進度,當你在一個分支裡做某項功能開發時,接到通知把昨天已經測試完沒問題的代碼釋出到線上,但這時你已經在這個分支裡加入了其它未送出的代碼,這個時候就可以把這些未送出的代碼存到棧裡。

将未送出的檔案儲存到git棧中

檢視棧中儲存的清單

顯示棧中其中一條記錄

移除棧中其中一條記錄

從git棧中檢出最新儲存的一條記錄,并将它從棧中移除

從git棧中檢出其中一條記錄,但不從棧中移除

把目前棧中最近一次記錄檢出并建立一個新分支

清空棧裡的所有記錄

為目前修改或删除的檔案建立一個自定義的棧并傳回一個id,此時并未真正存儲到棧裡

将 <code>create</code> 方法裡傳回的id放到 <code>store</code> 後面,此時在棧裡真正建立了一個記錄,但目前修改或删除的檔案并未從工作區移除

顯示送出曆史記錄

顯示帶送出差異對比的曆史記錄

顯示 <code>demo.html</code> 檔案的曆史記錄

顯示2周前開始到現在的曆史記錄,其它時間可以類推

顯示截止到2周前的曆史記錄,其它時間可以類推

顯示最近10條曆史記錄

顯示從送出id <code>f5f630a</code> 到 <code>head</code> 之間的記錄,<code>head</code> 可以為空或其它送出id

在一行中輸出簡短的曆史記錄

格式化輸出曆史記錄

<code>git</code> 用各種 <code>placeholder</code> 來決定各種顯示内容,我挑幾個常用的顯示如下:

%h: commit hash

%h: 縮短的commit hash

%t: tree hash

%t: 縮短的 tree hash

%p: parent hashes

%p: 縮短的 parent hashes

%an: 作者名字

%an: mailmap的作者名

%ae: 作者郵箱

%ad: 日期 (--date= 制定的格式)

%ar: 日期, 相對格式(1 day ago)

%cn: 送出者名字

%ce: 送出者 email

%cd: 送出日期 (--date= 制定的格式)

%cr: 送出日期, 相對格式(1 day ago)

%d: ref名稱

%s: commit資訊标題

%b: commit資訊内容

%n: 換行

合并分支的一條或幾條送出記錄到目前分支末梢

合并送出id <code>170a305</code> 到目前分支末梢

将目前的分支重設(reset)到指定的 <code>&lt;commit&gt;</code> 或者 <code>head</code>

<code>--mixed</code> 是不帶參數時的預設參數,它退回到某個版本,保留檔案内容,回退送出曆史

暫存區和工作區中的内容不作任何改變,僅僅把 <code>head</code> 指向 <code>&lt;commit&gt;</code>

自從 <code>&lt;commit&gt;</code> 以來在工作區中的任何改變都被丢棄,并把 <code>head</code> 指向 <code>&lt;commit&gt;</code>

重新定義分支的版本庫狀态

合并分支,這跟 <code>merge</code> 很像,但還是有本質差別,看下圖:

合并過程中可能需要先解決沖突,然後執行 <code>git rebase --continue</code>

打開文本編輯器,将看到從 <code>head</code> 到 <code>head~~</code> 的送出如下

将第一行的 <code>pick</code> 改成 <code>commands</code> 中所列出來的指令,然後儲存并退出,所對應的修改将會生效。如果移動送出記錄的順序,将改變曆史記錄中的排序。

撤銷某次操作,此次操作之前和之後的 <code>commit</code> 和 <code>history</code> 都會保留,并且把這次撤銷作為一次最新的送出

撤銷前一次送出操作

撤銷前一次送出操作,并以預設的 <code>revert "xxx"</code> 為送出原因

需要撤銷多次操作的時候加 <code>-n</code> 參數,這樣不會每次撤銷操作都送出,而是等所有撤銷都完成後一起送出

檢視工作區、暫存區、本地版本庫之間的檔案差異,用一張圖來解釋
萬字詳解!Git 入門最佳實踐 !

通過 <code>--stat</code> 參數可以檢視變更統計資料

<code>reflog</code> 可以檢視所有分支的所有操作記錄(包括commit和reset的操作、已經被删除的commit記錄,跟 <code>git log</code> 的差別在于它不能檢視已經删除了的commit記錄

萬字詳解!Git 入門最佳實踐 !

如果在github項目初始化之前,檔案已經存在于本地目錄中,那可以在本地初始化本地版本庫,再将本地版本庫跟遠端版本庫連接配接起來

在本地目錄内部會生成.git檔案夾

不帶參數,列出已經存在的遠端分支,加上 <code>-v</code> 列出詳細資訊,在每一個名字後面列出其遠端url

添加一個新的遠端倉庫,指定一個名字,以便引用後面帶的url

将遠端版本庫的更新取回到本地版本庫

預設情況下,<code>git fetch</code> 取回所有分支的更新。如果隻想取回特定分支的更新,可以指定分支名。

檢視檔案每行代碼塊的曆史資訊

截取 <code>demo.html</code> 檔案1-10行曆史資訊

二分查找曆史記錄,排查bug

開始二分查找

标記目前二分送出id為有問題的點

标記目前二分送出id為沒問題的點

查到有問題的送出id後回到原分支

通過 git 子子產品可以跟蹤外部版本庫,它允許在某一版本庫中再存儲另一版本庫,并且能夠保持2個版本庫完全獨立

将 <code>demo</code> 倉庫添加為子子產品

更新子子產品 <code>demo</code>

運作git的垃圾回收功能,清理備援的曆史快照
将加了tag的某個版本打包提取

<code>--format</code> 表示打包的格式,如 <code>zip</code>,<code>-v</code> 表示對應的tag名,後面跟的是tag名,如 <code>v0.1</code>。

本文隻是對 <code>git</code> 的所有功能中的部分實用功能做了一次探秘,git非常強大,還有很多功能有待我們去發現,限于本文篇幅,咱就此打住吧,預知更多好用功能,請善用谷歌。