0%

[week 1] 版本控制 - Git 進階指令 & GitHub

本篇為 [GIT101] Git 超新手入門 這門課程的學習筆記。

在上一節我們學到 Git 基本指令,接下來要學習什麼是 Branch(分支),以及如何送出的 Pull Request 進行共同開發,以及介紹提供視覺化介面操作 Git 的 GitHub。

學習目標:

 知道怎麼使用 branch 並送出 Pull Request
 熟悉 Git Workflow(也就是交作業的流程)

為什麼需要 Branch(分支)?

簡言之,Branch 的作用就是讓開發過程各自獨立。

例如:今天公司產品在開發新功能,途中發現有個嚴重的 bug 必須修正

若以一條線的開發模式(如同上一節學的 Git 基本指令):

  1. 在同一個 commit 上進行修改,容易產生衝突
  2. 穩定版 + bug fix 還參雜未完成的新功能,也可能產生更多問題

一條線開發

若能分成「穩定版本」和「新功能開發」,兩條分支就不會互相干擾:

  1. 穩定版 + 修好 bug → 得到新的穩定版
  2. 待新功能開發完成,即可合併得到有新功能的版本,成為最新的穩定版

兩條線開發


操作 branch 基本指令

使用 Git 進行版本控制時,系統最初會自動建立第一個 branch ,也就是 master

預設分支為 master

git branch -v:查看目前在哪個分支

下列訊息分別表示:分支名稱、版本號、版本訊息。

git branch -v 指令

  • git branch :如果後面沒接任何參數,只會顯示目前有哪些分支。

git branch <branchName>:新增分支

提醒:開發新功能之前,養成開新分支的好習慣!

git branch

git branch -d <branchName>:刪除分支

git branch -d 指令

git checkout <branchName>:切換分支

和上一節的切換版本指令原理相同,這裡是將 <版本號碼> 換成 <分支名稱>

  1. git checkout <版本名稱>:切換版本
  2. git checkout <branchName>:切換分支
  3. git branch checkout -b <branchName>:「新增」並「切換」到該分支

git checkout 指令

如何合併分支?

git merge <branchName>:把分支合併進來

範例:把分支 new-feature 合併到 master

  1. 輸入 git checkout master 切換到 master 這個分支
  2. 輸入 git merge new-feature,完成基本的分支合併
    git log 查看版本紀錄,可知「合併」的過程會自動建立一個新版本

git merge 指令

  1. 成功合併後,即可刪除分支 new-feature

合併後刪除分支

那在合併時發生 conflict(衝突)怎麼辦?

所謂的衝突,就是「同一份檔案的兩個版本,裡面有一個或多個不同的內容」。

但是 Git 無法幫你選擇哪一個當作最終版本,所以當發生衝突時,只能「自己手動調整」。

範例:在 masternew-feature 這兩條支線上,均改了同一個檔案 code.js

  1. 此時若把分支 new-feature 合併到 master
  2. 顯示發生衝突,輸入 git status 可見提示說明:該檔案的兩個版本均有修改

conflict

  1. 進入該檔案會顯示衝突位置如下,需手動調整檔案內容

conflict 手動調整

調整完成

  1. 修改完後重新 commit,成功解決衝突

conflict 解決


如何共同開發專案?

熟悉 Git 的基本操作後,來談談最一開始提到的多人協作。這些被版本控制的專案,我們稱為「Repository(檔案庫)」,多人協作就是共享同一份 Repository 來完成共同開發。

Git vs GitHub

Git 是用來版本控制的程式。

GitHub 是目前全球最大的 Git Server,有許多開放原始碼的專案都是使用 Github 進行程式碼的管理。可以想成「提供存放使用 Git 專案倉庫(Repository) 的服務」。也可以不用 GitHub 選擇其他服務,或是自己架一個 Git Sever。

GitHub:視覺化的 repository

透過 GitHub 的 GUI 介面能夠視覺化 repository,如此就可以在 repository 頁面查看該專案的 Commit 紀錄、檔案修改的歷史紀錄、修改者是誰等資訊,也可在 GitHub 執行 pull request 來進行合併。

建立新專案

建立新專案

將本地端資料推上遠端 GitHub(Local→Remote)

建立好新專案後,可分兩種方式開始專案:

  1. 開始全新專案,再同步到 GitHub
  2. 已存在專案,將本地端的現存專案同步到 GitHub

上傳本地專案到 GitHub

git remote:主要是跟遠端有關的操作

  • git remote add origin https://github.com/heidiliu2020/git101_test.git
    代表加入遠端節點。也就是「為本地端檔案庫增加一個叫做 origin 的遠端檔案庫」。
  • 這裡的 origin 是遠端檔案庫的代名詞,代表後面那串 GitHub 伺服器位置。如果從 Server 上 clone 下來,origin 是預設的遠端節點名稱。

git push:把本地資料同步到遠端

  • git push -u origin <branchName> :將本地端分支資料推到遠端分支
  • git push -u origin master:將本地端檔案庫推到遠端檔案庫的 master 分支

將遠端資料拉回本地端(Remote→Local)

git pull:把遠端資料同步到本地端

  • git pull origin <branchName>:將遠端分支資料拉回並合併本地端分支

從 GitHub Repository 複製專案

上面介紹的 Push 及 Pull 均使用於本地端現有的專案。

git clone:從 GitHub 下載 Repository 到本地端

  1. 在 GitHub 點選 Clone or download,可直接下載檔案

GitHub 點選 Clone

  1. 或是複製網址,到 Terminal 輸入 git clone <網址>:可將內容下載到本地端

git clone

但要注意使用 Clone 的 Repository 沒有權限修改,也就無法再 push 回遠端。

Fork:將別人的 Repository 複製一份成為自己的 Repository

  1. 到想要複製的 Repository 頁面,點選 Fork

點選 fork

  1. 即可複製一份 Repository 到自己的帳號底下

fork 專案

  1. 把專案 clone 到本地端,開新的 branch 進行修改
  2. Push 回自己的專案
  3. 若為多人協作,可在自己的頁面提出 PR(pull request)給原作

  1. 若原作同意修改內容,就會在他的頁面按下「Merge pull request」合併 commit


補充:如何在 GitHub上同步 Fork 過來的 Repository

  1. git remote -v:查看目前的遠端節點。只有最一開始設的 origin 這個節點。

git remote -v 指令

  1. git remote add <節點名稱> <網址>:新增一個遠端節點。
  • git remote add upstream https://github.com/Lidemy/mentor-program-4th.git:為本地端檔案庫增加一個叫做 upstream 的遠端檔案庫。

git remote add

  1. git pull upstream master:把遠端 Repo 同步回本地端電腦。若發生衝突,則手動修改解決。

git pull

  1. 修改完成後,使用 git commit -am "同步課綱 repo" 重新 commit。
  2. git push origin master:同步回自己在 GitHub 的 遠端 Repo。

若要刪除舊有的 remote,可參考 [Git筆記] 如何移除 remote origin

想請問同步課綱 Repo,是不是就和「同步之前 Fork 過來的 Repo」情況相同呢?

  1. git remote add 新增課綱 Repo 這個遠端節點
  2. git pull 把新課綱 Repo 同步到本地端,若發生衝突則手動解決再 commit
  3. git push 同步回自己在 GitHub 的遠端 Repo

自己依照這些步驟有成功把課綱 Repo 同步到 GitHub,想確認看看這觀念正不正確@@


常見狀況劇:

commit message 送出後,發現打錯字了想要修改

  1. git commit --amend:進入 vim 編輯器,即可修改 commit message。但這個方法只能修改最後一次的 commit message

git commit --amend

  • vim 編輯器介面如下,編輯完成後回到普通模式,輸入 :wq 存檔退出

Vim 編輯器

  1. git rebase:可以修改更之前的 commit message,可參考詳細操作流程

補充:若是在已經 commit 而且又 push 的情形下,在本地端進行修改 commit message 可能會造成其他人的困擾。最好的方法還是 push 之前先檢查一下,避免錯的東西被放到遠端。

我 commit 了可是我又不想 commit 了

git reset HEAD^:回到上一個 commit 的狀態,保留檔案變更,需要再 commit 來新增版本。預設是 –mixed,可參考 Git reset 的三種模式( soft mixed hard )比較

  • HEAD 代表最新的,^ 代表前一個。
  • 因此 HEAD^ 就是最新 commit 的前一個狀態,也就是上一個 commit 的狀態。

我還沒 commit,但我想把檔案回復到上次 commit 的狀態

  1. git checkout -- <file>:把檔案回復到上一次 commit 的狀態
  2. git checkout -- .:把專案內的所有檔案都回復

git checkout --

  • 也可使用 git restore <file>,和 git checkout -- <file> 作用相同。

git restore

改 branch 的名稱

git branch -m feature:將分支名稱改成 feature。

git branch -m 指令


GitHub Flow

GitHub 建議管理專案的流程:官網說明

當參與多人開發時:(圖片出自 arccwiki

GitHub Flow step

  1. 先 Fork 專案到自己的 repository,開新的 branch 進行開發
  2. 修改內容後 commit 新增版本,再 push 回自己的 repository
  3. pull request 到原本專案的 repository 請求合併
  4. 在上面進行討論,等待對方的 code review
  5. 在合併前進行最終測試
  6. 合併成功,刪掉 branch

實戰演練

假設今天已經建立好本地端和遠端的 resposity,那要如何進行往後的更新呢?

  1. 新建一個 branch:git branch joke_1
  2. 切換到該 branch:git checkout joke_1
  3. 編輯檔案笑話大全.txt
  4. 完成後,若有新增檔案,可使用 git add . 把檔案加入版本控制
  5. 進行 commit:git commit -am “new joke”
  6. push 本地端分支回遠端 GitHub:git push origin joke_1
  7. 到 Github 查看 branch 頁面,提出 PR(pull request)
  8. 完成合併到遠端 master,即可刪除分支 joke_1
  9. 把 GitHub 更新的 master 同步到本地端:git push origin master
  10. 回到本地端,使用 git branch -d joke_1 刪除分支,結束!

客製化各個階段:Git hook

用途是「當發生某事的時候通知我」。例如:當有人 commit、有人push 時通知我。

通常使用在 commit 或是 push 之前,用來檢查程式碼是否符合規範、是否改到不能更改的檔案、是否有放一些不該放的檔案或帳號密碼。

補充:本地端與遠端 - 相關指令

  • 以下示意圖出自此處

git terminology

參考資料:

  1. Git 與 Github 版本控制基本指令與操作入門教學
  2. [第二週]Git 進階使用 Branch、Merge
  3. Git教學:如何 Push 上傳到 GitHub?
  4. GitHub Guides - Understanding the GitHub flow
  5. Git Workflow - arccwiki
  6. 【狀況題】修改歷史訊息
  7. CSCC01: Configuration Management
  8. 【狀況題】怎麼跟上當初 fork 專案的進度?