Skip to content

Latest commit

 

History

History
824 lines (509 loc) · 19.7 KB

note.md

File metadata and controls

824 lines (509 loc) · 19.7 KB

Git

DATE: 5, 6, 7 (2024) REF: 30 天精通 Git 版本控管

# 簡介

# 基礎

  • 指令(研究學習用)
    • git cat-file -p [HASH ID]
      • 查看該物件內容
      • hashID 會是該物件檔案名稱
      • 也可直接查看 ref,EX. git cat-file -p HEAD
    • git cat-file -t [HASH ID]
      • 查看該物件種類 (commit、tree、blob、tag)
    • git show-ref [NAME]
      • 查看符合 ref 所代表的所有 HASH ID 跟 path

      • 只會查詢 refs/ 中的內容

      • EX.

        $ git show-ref master
        
        c3bd002d4dcf4169512e94d66bf1db5d648cea17 refs/heads/master
        8gj3h4y38cc334d7060blm3c13jw748a3b75d9a8 refs/remotes/origin/master
        
        $ git show-ref HEAD
        
        8gj3h4y38cc334d7060blm3c13jw748a3b75d9a8 refs/remotes/origin/HEAD
    • git rev-parse [NAME]
      • 查看該名稱所代表的 HASH ID

      • EX.

        $ git rev-parse master
        c3bd002d4dcf4169512e94d66bf1db5d648cea17
  • 資料結構
    • 物件種類:commit、tree、blob、tag
      • commit:包含 tree、parent、author、committer
        • stash
          • stash,在 refs/ 中只會紀錄一個最新的 stash,也就是 stash@{0}。其他則是記錄在 logs/refs/stash
        • 合併三個以上分支,就會有三個以上的 parent。那什麼情況會造成合併三條以上分支?
          • EX. git stash save -u

            • 這個做法會分別將 worktree、index(untracked)、tracked 存成一個 stash
            • 則此時的表現方式是該 stash 會有三個 parent,分別為那三個狀態
            $ git cat-file -p stash
            tree 6381dc317d7a3c2cac9fd0bf383b6b5427b633bb     # worktree  (unmodified/modified)
            parent d5301505f50aa16b1f12cc195897185334c4f044   # HEAD
            parent f07891d84a88efc08f3f305dc59d255a4c1bb68a   # index     (staged)
            parent 18f5efab303dbb7920ae15e71e5b3e62b0381d16   # untracked
            
            WIP on master: d530150 TEST git 1
      • tree:包含 tree、blob
        • 像是一個資料夾
      • blob:包含完整一份 file 內容
        • 只對內容做 hash,檔案名稱等資訊都寫在 tree

        • EX. fileA.txt 與 fileB.txt,內容一模一樣,則只生成一個 blob

          # fileA.txt
          此部分為內文...ABC
          
          # fileB.txt
          此部分為內文...ABC
      • tag:包含 object、type、tag、tagger
    • 儲存流程
      • commit 後,會先將所有單一 file 以 zlib 演算法壓縮成 blob,包含整個完整的內容,而不只儲存檔案間的差異,存於 .git/objects/

      • 後面 commit 改回與更之前完全相同內容,也只有一份 blob ,因為 hash 一樣

      • 達到條件後,才利用 delta compression 演算法,封裝後存於 .git/objects/pack/ 中,每個 pack 包含兩個檔案 .idx & .pack

      • 自動封裝以 gc.auto 設定條件啟動封裝鬆散的 object,以 gc.autoPackLimit 設定條件啟動合併 pack

      • git gc 手動執行,封裝鬆散的 object,並合併 pack

  • config
    • 順序:local -> global -> system
      • 如果在多個地方設置同一屬性,則 --local 會蓋過 --global 再蓋過 --system
      ## EX.
      
      $ git config --list --system
      user.name=ocup1
      
      $ git config --list --global
      user.name=ocup2
      
      $ git config --list --local
      user.name=ocup3
      
      $ git config --list
      user.name=ocup1
      user.name=ocup2
      user.name=ocup3
      
      ## --> 則最後是 ocup3 生效
    • 常用設定
      • git config --global core.editor "code --wait" - 設定使用 vscode 編輯
      • git config --local commit.template "./.gitmessage.txt" - 設定 commit 模板
      • git config --global commit.cleanup "strip" - 設定 commit 依照哪個模式 cleanup。用 SourceTree 可能需要設定
    • 其他細節
      • windows 在設定 --system 時,可能需要切換成管理員權限,才能設定正確
      • macOS 中 credential.helper=osxkeychain 將你的憑證(如 GitHub、GitLab 的帳密)安全地存儲在 macOS 的鑰匙串中,以便進行身份驗證時自動填充,無需每次都手動輸入
  • reflog
    • 紀錄"使用指令"改變狀態的動作
      • 像是 ORIG_HEAD 那樣,只是它記錄了所有的動作
      • 包含:commit、checkout、pull、push、merge、reset、clone、branch、rebase、stash..etc
    • HEAD@{0}
      • 可用 git reflog 查詢
      • HEAD@{0} 來標記最新紀錄,也就是 HEAD
      • 依此類推 HEAD@{1}, HEAD@{2}..,數字越大越舊
    • 紀錄在 .git\logs\
      • 還分多種 EX. HEAD、master、remote..等等
    • 預設保留時間
      • 紀錄保留 90 天
      • 紀錄中已經不存在任何分支上的 commit 物件保留 30 天
      • gc.reflogExpire "90 days" & gc.reflogExpireUnreachable "30 days"
    • 常用指令
      • git reflog
      • git reflog delete "ref@{specifier}" (EX. HEAD@{0})
      • git reflog expire --expire=now --all
      • git config --global gc.reflogExpire "never"
      • git config --global gc.reflogExpireUnreachable "never"
  • tag
    • lightweight tag

      • git tag
      • 只會有一個 tag ref 指向 commit
      • 若加上 -m 也會自動升級為 annotated tag
    • annotated tag

      • git tag -a
      • 會新增一個 tag object,並有一個 tag ref 指向 tag
      • 才有 metadata 描述
      • 可以用 GnuPG 金鑰簽章
      • 通常使用此種
  • 整理已存在 commit
    • revert
      • 定義:恢復所選 commit 所做的動作,合併到 HEAD

        • EX. 若 commit1 是「將 1 改成 2」,則 git revert commit1 是「將 2 改成 1」,合併到 HEAD
      • 時機:想恢復某次 commit 所做動作

      • 指令:

        • git revert [commitID]

        • git revert -n

          • revert 後不直接 commit
          • 執行完後,並不是用 git commit 建立版本,正確方式:
            • git revert --continue: 代替 git commit
            • git revert --abort: 放棄 revert
    • cherry-pick
      • 定義:重新套用所選 commit 所做的動作,合併到 HEAD

      • 時機:只想將分支中,其中幾個 commit 合併到 master

      • 指令:

        • git cherry-pick -x: 在 commit 訊息中加入是從哪裡撿來

          • EX. cherry picked from commit dc070...
          • 需注意可能 pick 後,該分支刪除後也找不到反而困惑,所以不一定較好
        • git cherry-pick -e: 先編輯 commit 訊息

        • git cherry-pick -n: 不直接 commit

    • rebase
      • 定義:重新修改目前分支的「基礎版本」,把另外一個分支當成目前分支的 Base

        • EX. 處在 branch1,執行 git rebase master

          • 原本 branch1 有 branch1 自己的 Base,將 branch1 的 base 改成 master 的 Base
          原本:
            branch1:
              commit4
              commit3
              commit1
          
            master:
              commit2
              commit1
          
          使用後:
            branch1:
              commit4
              commit3
              commit2
              commit1
          
            master:
              commit2
              commit1
          
      • 時機:

      • 指令:

        • git rebase [commitID] -i

          • 可指定 Base 的起點 (rewinding head),從 commitID 開始

          • -i 可選擇要對途中每個 commit 做哪些動作 (pick, reword, edit, squash, fixup, ...)

          • 有修改過的 commit(以及後續的 commit) 都會建立新的 commit 物件

          • EX. 舉例以 edit 來移除該 commit 中的某個異動的流程

            # 1. 列出該 commit 所有的異動檔案名稱
            $ git diff --name-only HEAD~
            
            # 2. 將想要移出掉的異動檔案移出 index
            $ git reset HEAD~ [path]
            
            # 3. 將現況存進此 commit
            $ git commit --amend
            
            # 4. 繼續 rebase 後續動作
            $ git rebase --continue
  • remote
    • 分支
      • 分支 & 追蹤分支

        • 分支:refs/heads/ 中的那些

        • 追蹤分支:refs/remotes/ 中的那些

          • EX. 本地的 refs/remotes/origin/master,用來追蹤 origin remote 的 master
      • 在本地有:本地分支 & 本地追蹤分支

      • 在遠端有:遠端分支 & 遠端追蹤分支

    • refspec
      • 定義:用來設定直接以 git push & git fetch,未指定 remote repo & branch 時,要執行的 repo、branch 組合 (設定檔:.git/config)

      • 範例:

        [branch "master"]
          remote = git@your/url.git
          merge = refs/heads/master
        [remote "origin"]
          url = git@your/url.git
          fetch = +refs/heads/master:refs/remotes/origin/master
          fetch = +refs/heads/test:refs/remotes/origin/test
      • 其他:

        • 沒設定也可以 git push/fetch [remote_name] [branch_name] 方式來 push/fetch
      • 指令:

        • 新增與移除 fetch 設定

          git config --add remote.origin.fetch +refs/heads/test:refs/remotes/origin/test
          git config --unset remote.origin.fetch +refs/heads/test:refs/remotes/origin/test
        • git push --set-upstream 來設定 push 對應的 remote:branch

          • EX. 執行 git push --set-upstream origin branchA,會在 .git\config 增加以下內容:

          • 意義:當想將本地 branchA 推送到遠端,預設的遠端為 origin,推送的時候要將變更合併到遠端的 refs/heads/branchA

          [branch "branchA"]
          remote = origin
          merge = refs/heads/branchA
    • 初始化相關
      • git checkout [branch_name] 可直接建立本地分支

        • EX. clone 之後,只有 remotes/origin/hotfix/test,可以 git checkout hotfix/test 建立本地 hotfix/test
        $ git branch -a
        * master
          remotes/origin/HEAD -> origin/master
          remotes/origin/hotfix/test
          remotes/origin/master
        
        $ git checkout hotfix/test
          Branch hotfix/test set up to track remote branch hotfix/test from origin.
          Switched to a new branch 'hotfix/test'
        
        $ git branch -a
        * hotfix/test
          master
          remotes/origin/HEAD -> origin/master
          remotes/origin/hotfix/test
          remotes/origin/master

# Workflow

  • Git Flow

  • GitHub Flow
    • REF: GitHub Doc: GitHub Flow

    • NOTE:

      • 簡單流程快速迭代
      • PR、code review 流程
      • 相較於 TBD,GitHub Flow 的 Feature 分支存活較久

  • Trunk-Based Development (TBD)
    • REF: TBD - TW

    • NOTE:

      • 盡可能讓所有變動都能在一個 main 上看見

# 問題

  • 關鍵字
    • Social Coding

# 補充

  • 注意事項:

    • 關於指令選用
      • 我認為推薦使用的指令隨更新不斷在進化,所以有些相同公用的指令,可以參考 shell 上給的回饋來使用

        EX. git status 後會顯示 (use "git restore --staged <file>..." to unstage),以前的版本曾經是 (use "git reset HEAD <file>..." to unstage),但可以盡量使用最新版本建議方式來完成

      • 或是在觀念非常清楚後,學習使用 GUI,指令改 GUI 也會跟著更新,而使用 GUI 需要的是清楚的概念

  • 小技巧:

    • git shortlog
      • 查看每個 user 的統計
      • git shortlog -sne
    • git clean
      • 清除未被 git 追蹤的檔案
      • git clean -n
      • git clean -x:會刪除包含被 .gitignore 的檔案
    • git blame
      • 查看特定檔案的 commit 紀錄
  • 小工具:

  • 補充學習:

    • 指令文件:git help & git -h
      • 指令簡易版文件參考 git -h (EX. git commit -h)
      • 指令更多細節可參考 git help (EX. git help commit)

# 踩雷實錄


# 延伸討論


# TODO:

  • 擬定一些 SOP

  • 引入 GUI 使用習慣