Git一二三,版控真簡單

本文最後更新於:2024年11月19日 11:00

Git是一個多工團隊協作版控的好用工具,雖然一開始的概念有點小複雜,我也是花了好一陣子才漸漸搞懂它。因此,對於初學者而言,在了解Git指令之前,先要有 local (本地)、 remote (遠端) repository (儲存庫)及各自的 branch (分支)概念,還有就是多跟同事在實務上運用練習,才會更快上手。

Git指令全集

概念

  1. 初始化git工作目錄

    狀態:未初始化

    1
    git init

    初始化後,目錄下新增.git目錄。

  2. 工作區(Working Directory)

    狀態:未追蹤(Untracked files,對於新加入檔案或提交後新建立檔案)、已更改(Changes not staged for commit,對於提交後再被修改之檔案)

    直接編輯的地方,在桌機上可直接操作檔案。

    1
    2
    # 工作區 -> 暫存區
    $ git add . # 將檔案從工作目錄加入至暫存區
  3. 暫存區(Staging Area)

    狀態:等待提交(Changes to be committed)

    數據暫時存放的區域,介於工作區與儲存庫之間。

    1
    2
    3
    4
    5
    # 暫存區 -> 儲存庫
    $ git commit # 新開編輯器編輯提交訊息
    $ git commit -m "提交訊息" # 將暫存區內容移至儲存區
    $ git commit -am "提交訊息" # 從工作區移至儲存庫,對已在儲存庫的檔案有效
    $ git commit --amend # 編輯最新已提交訊息
  4. 儲存庫/數據庫(Repository,簡稱repo)

    狀態:已提交(Committed)

    存放已經提交的數據。

    1. 本地端儲存庫(Local)

      為了方便自己使用的儲存庫,通常是個人開發的電腦或機台。

      1
      2
      # 本地端複製
      $ git clone <本地目錄名稱> <新本地目錄名稱|路徑> # 複製本地儲存庫至指定目錄
    2. 遠端儲存庫(Remote)

      為了讓多人共享而建立的儲存庫,通常是一個共用的伺服器。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      # 遠端 -> 本地端儲存庫
      $ git fetch <遠端名稱> <遠端分支名稱> # 下載遠端指定分支
      $ git fetch --all # 下載遠端所有分支
      $ git clone <遠端名稱> # 下載遠端預設分支
      $ git clone <遠端名稱> -b <本地分支名稱> # 下載遠端預設分支至指定本地分支
      $ git clone <遠端名稱> <目錄名稱|路徑> # 下載遠端預設分支至指定目錄

      # 遠端儲存庫 -> 工作區
      $ git pull <遠端名稱> <遠端分支名稱>:<本地分支名稱> # git fetch + git merge

      # 本地端 -> 遠端儲存庫
      $ git push [遠端名稱] [遠端分支名稱] # 推送到遠端儲存庫

常用指令

1
2
3
4
5
6
7
git init                   # 進行版本控制(此時就會有.git目錄產生)
git status # git狀態
git add . # 將當前目錄以下檔案加至暫存區
git add -A # 將所有檔案加至暫存區
git commit -m "提交訊息" # 提交檔案至儲存庫
git commit --amend # 編輯最新已提交訊息
git filter-branch --tree-filter "rm -f config/database.yml" # 針對過去的提交批次修改紀錄並提交

將存放庫初始化

首先必須要先將Git本地的存放庫初始化啦!

到了想要透過Git監控的目錄 git init ,此時在目錄下會多出.git的隱藏目錄,這就相當於在VS code下在原始檔控制中的「將存放庫初始化」的功能。

1
git init  # 在當前目錄下創建.git以監控版更

查看當前git狀態

先透過 git status 來查看當前git狀態。

1
2
3
4
5
6
7
8
$ git status  # 查看當前git狀態
On branch master

No commits yet

Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore

從輸出的內容,可以看到目前本地的分支名稱為master、沒有任何commit(提交),以及哪些檔案還沒被add(加入)追蹤(untracked)。

目錄下可加入.gitignore檔案,裡面的內容可以加入不想同步追蹤的檔案名稱,以將這些檔案排除在git的檢查之外。

目錄下加入.gitkeep則是強制git在檢查時,將空目錄也涵蓋進來。

一、add(加入)untracked(未追蹤)檔案至暫存的變更

當在目錄當中新增的檔案,只要是非空目錄、未被.gitignore排除檢查的檔案,或者並非是另一個含有.git的目錄,都會被列為檢查對象。

利用VS code的原始檔控制,會自動偵測已變更的檔案(新增的檔案右方會顯示A(Add)、新增後變更的檔案會顯示M(Modified)),在「變更」欄的右方「+」號則可以將新增或變更的檔案加入到暫存的變更;在「暫存的變更」右方「-」號則是可以選擇取消變更的檔案,等修改好再加進去。

如果利用指令列,則須透過 git status 來看各個檔案的情況。

假設我們先新增一個test.md檔,這時候git status會列在untracked,於是我們 git add

1
git add test.md

這時候再git status,會看到檔案已被加入:

1
2
3
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: test.md

如果再修改test.md檔案中的文字,再次git status則會看到:

1
2
3
4
5
6
7
8
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: test.md

Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: "test.md"

代表有一個版本已經加入暫存的變更等待提交,而另一個則是因為我們後來的修改而產生的版本,尚未加入提交的行列。

若是目錄中含有其他git監測的目錄,則無法加入追蹤。

1
2
3
Untracked files:
(use "git add <file>..." to include in what will be committed)
themes/fluid/

像是Hexo部分目錄本身即含有git版控功能,因此部分目錄無法同步加入追蹤。

二、新增訊息並commit(提交)

當我們新增檔案或修改某些檔案裡的文字時,可以利用 git commit 分批提交更新訊息,把每一次的更新訊息記錄下來:

1
2
3
4
$ git commit -m "add test.md"
[github 1da2962] revise Git相關指令.md
0 file changed, 1 insertions(+)
create mode 100644 "test.md"

提交之後會顯示提交的分支名稱、提交ID、提交訊息、幾個檔案新增或者被改變。

每次修改檔案之後,都要記得將檔案再度git add到要提交的行列當中。

在提交之後,會發現本地端也新增了master分支,代表我們提交的內容會在我們本地的master分支裡。每修改某個檔案都可以在訊息欄做備註。但我們要推送的是遠端分支,而不是本地分支,要確定要提交上去的是哪一個分支。

三、push(推送)至remote repo(遠端儲存庫)

緊接著,你在工作目錄中陸陸續續新增的檔案,要放到遠端儲存庫供他人使用,就必須先將遠端儲存庫加入路徑之中。Github儲存庫URL在頁面右上角的「code」當中,使用HTTPS或SSH都可以,將想要放置檔案的Github儲存庫URL透過 git remote add 加入即可。

1
2
3
4
git remote add <遠端名稱> <遠端儲存庫URL>    # 將該遠端加入fetch跟push的路徑
git remote -v # 查看遠端分支名稱及路徑
git remote remove <遠端名稱> # 對應之移除指令
git remote rename <原遠端名稱> <新遠端名稱> # 更改遠端名稱

以名稱為github加入遠端儲存庫為例:

1
2
3
4
$ git remote add github https://github.com/weijhih1226/blog.git
$ git remote -v
github https://github.com/weijhih1226/blog.git (fetch)
github https://github.com/weijhih1226/blog.git (push)

如果想推送至第2個遠端儲存庫的話,也可以再加入:

1
2
3
4
5
6
$ git remote add gerrit ssh://weijhih1226@gerrit.twindy.com:29418/weather
$ git remote -v # 此時
github https://github.com/weijhih1226/blog.git (fetch)
github https://github.com/weijhih1226/blog.git (push)
gerrit ssh://weijhih1226@gerrit.twindy.com:29418/weather (fetch)
gerrit ssh://weijhih1226@gerrit.twindy.com:29418/weather (push)

此時透過指令 git remote -v 就可以看到有兩個remote repo了。

如果要查看本地分支與遠端分支的對應可以透過以下指令:

1
2
3
4
5
6
7
8
$ git branch -v               # 查看branch名稱、commit訊息
$ git branch -vv # 查看branch名稱(有upstream則會顯示)、commit訊息
$ git branch -l # 查看分支名稱(本地)
$ git branch -r # 查看分支名稱(遠端)
$ git branch -a # 查看所有分支名稱(本地及遠端)
* master # 本地分支(目前的分支)
remotes/github/gh-pages # 遠端分支
remotes/github/main # 遠端分支

從上面 git branch -a 當中,可以看到因為目前本地端是在master分支底下,而遠端則在github底下有自動抓到2個分支。

如果要將目前的本地分支的fetch及push與遠端分支對應,使得每次 git fetch / git pullgit push 不用指定remote repo與branch,可以使用以下指令:

1
git branch -u <remote repo>/<remote branch>   # 將遠端儲存庫的分支加入目前本地分支

利用 git checkout 則可以切換分支,若要新建分支並切換過去,要加 -b

1
2
git checkout <分支名稱>
git checkout -b <新的分支名稱> # git branch + git checkout <name>

例如我們如果想將本地端main分支的檔案同步到遠端的main分支上:

1
2
git checkout main       # 切換本地分支至main
git push github main # 推送至遠端名為github的main分支

其他常用指令

  • 更動repo或branch相關

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    git branch <分支名稱>                                 # 建立本地分支
    git branch (-d | --delete) <分支名稱> # 刪除本地分支
    git branch (-D | --delete --force) <分支名稱> # 強制刪除本地分支
    git branch (-m | --move) [<舊分支>] <新分支> # 移動/更名分支
    git branch (-M | --move --force) [<舊分支>] <新分支> # 強制移動/更名分支
    git push [遠端名稱] [遠端分支名稱] # 推送至遠端分支(若upstream有指定,可省略名稱)
    git push <遠端名稱> --delete <遠端分支名稱> # 刪除遠端分支
    git push <遠端名稱> :<遠端分支名稱> # 刪除遠端分支
    git clone <遠端專案URL> -b <分支名稱> # 複製遠端分支
    git pull <遠端名稱> <遠端分支名稱>:<本地分支名稱> # git fetch + git merge
  • 查看log相關

    1
    2
    3
    git log                                # 查看已提交commit
    git log --oneline # 僅顯示commit標題
    git log --graph # 以圖形顯示branch之間的commit
  • 回復到某個commit

    1
    2
    3
    4
    5
    6
    7
    git reset --hard [HEAD]                # 回復到最新提交版本(預設為HEAD)
    git reset --hard HEAD~ # 回復到前一個提交版本
    git reset --hard HEAD~n # 回復到前n個提交版本
    git reset --hard <commit_id> # 回復到指定commit
    git reflog # 查看所有變動訊息
    git reset --soft [HEAD | <commit_id>] # 回復到提交版本但會保留工作區及暫存區內容
    git revert [HEAD | <commit_id>] # 撤銷到提交版本但會留下commit
  • 暫存目前進度切換成新任務

    1
    2
    3
    4
    5
    6
    git stash                  # 暫存目前進度
    git stash -u # 暫存目前進度(包含untracked的檔案)
    git stash list # 查看stash列表
    git stash apply stash@{n} # 把某個stash套用回來目前分支
    git stash drop stash@{n} # 把某個stash刪除
    git stash pop stash@{n} # 把某個stash套用回來目前分支並刪除stash(apply + drop)

關於git組態設定

  1. git的組態設定分為三個階層,有 SystemGlobalLocal ,相關的設定檔路徑如下:

    • System: /etc/gitconfig

    • Global: $home/.gitconfig

    • Local: $repo/.git/config

  2. 常用設定如下:

    1
    2
    3
    4
    5
    6
    7
    git config --list [--system | --global | --local]  # 列出所有組態設定
    git config --global user.name "xxx" # 使用者名稱
    git config --global user.email "xxx@xx.com" # 使用者電子郵件
    git config --global core.autocrlf true # 自動轉換CRLF
    git config remote.<repo name>.url "<repo url>" # 更改遠端儲存庫URL
    git config core.editor "vim" # 設定編輯器為Vim
    git config push.default [upstream|current|simple] # 預設推送到[upstream|同名分支|同名分支但會拒絕推送到不同名分支]
    • For Gerrit:

      1
      git config remote.<repo name>.push "HEAD:refs/for/<branch name>%r=<reviewer email>,cc=<cc email>,topic=<topic>"   # 設定要推到哪個branch,以及指定reviewer、cc,或topic

git忽略追蹤

  • 狀況一:新增過濾條件後新增的檔案
    • 符合規則 Git 就不會去追蹤。
  • 狀況二:新增過濾條件前新增的檔案
    • 沒有額外處理還是會被追蹤。
    1. 於專案根目錄新增 .gitignore 檔案。

    2. 於檔案內新增需要忽略的檔案、目錄等,例如:

      1
      2
      *.conf          # 檔案
      __pycache__/ # 目錄
    3. 若以 git status 查看,會發現記錄只有新增一個檔案而已,而這就是因為先有 Git 記錄才新增 .gitignore 檔案,因此進行以下步驟清除快取並重新加入追蹤,才會套用新的 .gitignore 設定:

      1
      2
      3
      4
      5
      6
      # 清除本機 Git 的快取,就是將所有檔案移除 Git 的追蹤,但沒有刪除檔案
      $ git rm -r --cached .
      # 重新加入 Git 追蹤,這時就會重新套入 .gitignore 設定
      $ git add .
      # 重新 commit ,並會忽略設定在 .gitignore 的檔案
      $ git commit -m 'update .gitignore'

參考資料

  1. .gitignore 忽略那些不該上傳的 Git 檔案 - 釋迦的碼農之路 - Medium

Git一二三,版控真簡單
https://weijhih1226.github.io/blog/2022/08/24/Git相關指令/
作者
Satoshi
發布於
2022年8月24日
許可協議