<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Git目錄為什么這么大

          共 10628字,需瀏覽 22分鐘

           ·

          2021-06-02 01:09

          目錄

          • 1、介紹

          • 2、Git 存儲原理

            • 2.1 目錄結(jié)構(gòu)

            • 2.2 提交內(nèi)容

            • 2.3 如何徹底刪除一個文件

          • 3、解析 Object 存儲方式

          • 4、處理大文件

            • 4.1 大文件的產(chǎn)生

            • 4.2 尋找大文件的 ID

            • 4.3 刪除大文件

            • 4.4 按照 pack 文件直接操作

          • 5、大文件存儲的正確方式

          • 6、其他解決方案

          • 7、小結(jié)


          1、介紹

          Git作為一個分布式的版本控制工具,在每天高頻次的使用中難免遇到一些問題

          本文圍繞git的目錄過大,從git進行版本控制底層存儲出發(fā),簡要分析Git目錄過大的原因,以及如何處理

          2、Git 存儲原理

          2.1 目錄結(jié)構(gòu)

          使用版本控制的人都會知道,不管是svn還是更為流行的git,整個工程目錄下,除了項目代碼外,與版本控制相關(guān)的就是.svn.git目錄

          git為例,.git下的目錄結(jié)構(gòu)如下

          tree -L 1 .git
          .git
          ├── COMMIT_EDITMSG
          ├── FETCH_HEAD
          ├── HEAD
          ├── ORIG_HEAD
          ├── config
          ├── description
          ├── hooks
          ├── index
          ├── info
          ├── logs
          ├── objects
          ├── packed-refs
          ├── refs
          └── sourcetreeconfig

          其中一些目錄的說明

          • HEAD:表示當前本地簽出的分支

          • hooks:git鉤子目錄,關(guān)于鉤子的使用可以參考我之前的文章 利用 Git 鉤子實現(xiàn)代碼發(fā)布[1]

          • index:存儲緩沖區(qū)GitExtensions中的stage的內(nèi)容

          • objects:存儲對象的目錄,git中對象分為commit對象,tree對象(多叉樹),blob對象

          • refs:存儲指向branch的最近一次commit對象的指針,也就是commit對象的sha-1值,其中heads存儲branch對應的committags存儲tag對應的commit

          • config:倉庫配置,比如遠程的url,郵箱和用戶名等

          2.2 提交內(nèi)容

          git的一次提交包含4個部分:

          • 工作目錄快照名稱(一個哈希值)

          • 一條評論/注釋

          • 提交者信息

          • 父提交的哈希值

          每一個提交Commit相當于一個Patch應用在之前的項目上,借此一個項目可以回到任何一次提交時的文件狀態(tài)

          于是在Git中刪除一個文件時,Git只是記錄了該刪除操作,該記錄作為一個Patch存儲在 .git 中。刪除前的文件仍然在Git倉庫中保存著。直接刪除文件并提交起不到給Git倉庫瘦身的效果

          Git倉庫徹底刪除一個文件只有一種辦法:重寫Rewrite涉及該文件的所有提交。借助 git filter-branch 便可以重寫歷史提交,當然這也是Git中最危險的操作

          2.3 如何徹底刪除一個文件

          以一個文件的提交為例,這個文件可能會關(guān)聯(lián)很多次提交,只有將每一次與該文件有關(guān)的的提交記錄進行重寫,才能真正實現(xiàn)刪除這個文件,具體做法如下

          # git filter-branch -f --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch api/app/test.py' --tag-name-filter cat -- --all
          Rewrite 2771f50d45a0293668a30af77983d87886441640 (264/982)rm 'api/app/test.py'
          Rewrite 1a98ecb3f39e1f200e31754714eec18bc92848ce (265/982)rm 'api/app/test.py'
          Rewrite d4e61cfb1d88187b0561d283e663b81b738df2c7 (270/982)rm 'api/app/test.py'
          Rewrite 4ba0df06b26cf86fd39c2cda6b012c521cbc4dc1 (271/982)rm 'api/app/test.py'
          Rewrite 242ae98060c77863f5e826ba7e1ec47

          這里要刪除的文件位置是api/app/test.py

          --index-filter 參數(shù)用來指定一條Bash命令,--all 參數(shù)告訴Git我們需要重寫所有分支(或引用)

          Git會檢出checkout所有的提交, 執(zhí)行該命令,然后重新提交。我們在提交前移除了 test.py 文件, 這個文件便從Git的所有記錄中完全消失了

          3、解析 Object 存儲方式

          為了一步步熟悉Object存儲的方式,這里在本地創(chuàng)建一個空的git倉庫,且objects目錄中還沒有任何內(nèi)容,創(chuàng)建一個文件并提交

          # mkdir git-test && cd git-test && mkdir src
          # git init .
          Initialized empty Git repository in /Users/ssgeek/Git-workspace/git-test/.git/
          # echo "test project" > README.md
          # echo "hello world" > src/demo1.txt
          # git add .
          # git commit -sm "first commit"
          [master (root-commit) ca1114d] first commit
           2 files changed, 2 insertions(+)
           create mode 100644 README.md
           create mode 100644 src/demo1.txt

          從輸出可以看到,上面的命令創(chuàng)建了一個commit對象,該commit包含兩個文件

          查看.git/objects目錄,可以看到該目錄下增加了4個子目錄 32,3b, 4c, ca,d2,每個子目錄下有一個以一長串字母數(shù)字命名的文件

          # tree .git/objects
          .git/objects
          ├── 32
          │   └── 73e239f79eacf09654a5ecc18498bda0d2e7eb
          ├── 3b
          │   └── 18e512dba79e4c8300dd08aeb37f8e728b8dad
          ├── 4c
          │   └── 3ced11d9e650d74fa5e518b26b311f06d7069c
          ├── ca
          │   └── 1114de8da76527ec73cdf52100eb7ba58e1878
          ├── d2
          │   └── df53f517e6e2c85ff2b0c6b0970428889f265f
          ├── info
          └── pack

          前面提到object目錄下存放的是Git為對象生成一個文件,并根據(jù)文件信息生成一個SHA-1哈希值作為文件內(nèi)容的校驗和,創(chuàng)建以該校驗和前兩個字符為名稱的子目錄,并以 (校驗和) 剩下38個字符為文件命名 ,將該文件保存至子目錄下

          可以通過 git cat-file命令查看Git Object中存儲的內(nèi)容及對象類型,命令參數(shù)為Git ObjectSHA-1哈希值,即目錄名+文件名。一般不用輸入整個Hash,輸入前幾位即可

          當前分支的對象引用保存在HEAD文件中,可以查看該文件得到當前HEAD對應的branch,并通過branch查到對應的commit對象

          # cat .git/HEAD
          ref: refs/heads/master
          # cat .git/refs/heads/master
          ca1114de8da76527ec73cdf52100eb7ba58e1878

          使用-t參數(shù)查看文件類型,使用-p參數(shù)可以查看文件內(nèi)容

          # git cat-file -t ca1114
          commit
          # git cat-file -p ca1114
          tree d2df53f517e6e2c85ff2b0c6b0970428889f265f
          author ssgeek <[email protected]> 1622445604 +0800
          committer ssgeek <[email protected]> 1622445604 +0800

          first commit

          Signed-off-by: ssgeek <[email protected]>

          這是一個commit對象,commit對象中保存了commit的作者,commit的描述信息,簽名信息以及該commit中包含哪些tree對象和blob對象,繼續(xù)看該tree對象中的內(nèi)容

          # git cat-file -p d2df53
          100644 blob 4c3ced11d9e650d74fa5e518b26b311f06d7069c README.md
          040000 tree 3273e239f79eacf09654a5ecc18498bda0d2e7eb src
          # git cat-file -p 4c3ced
          test project
          # git cat-file -p 3273e2
          100644 blob 3b18e512dba79e4c8300dd08aeb37f8e728b8dad demo1.txt
          # git cat-file -p 3b18e5
          hello world

          因此可以得知,git 中存儲了三種類型的對象,commit,treeblob,三者分別對應git commit,此commit中的目錄和文件,這些對象之間的關(guān)系如下圖

          4、處理大文件

          4.1 大文件的產(chǎn)生

          由上面的詳細分析流程可以看出,git會為每一個提交到版本控制的文件進行追蹤,那么大文件究竟如何產(chǎn)生呢?

          在上面的object目錄下還存在著packinfo文件夾。Git往磁盤保存對象時默認使用的格式叫松散對象loose object格式,當你對同一個文件修改哪怕一行,git都會使用全新的文件存儲這個修改了的文件,放在了objects中。Git時不時地將這些對象打包至一個叫packfile的二進制文件以節(jié)省空間并提高效率,當版本庫中有太多的松散對象,或者你手動執(zhí)行 git gc 命令,或者你向遠程服務器執(zhí)行推送時,Git都會這樣做

          因此,往往在向git中提交了大文件,會造成pack文件過大,到這里“元兇”終于出現(xiàn)了

          4.2 尋找大文件的 ID

          以我的博客代碼為例操作

          首先查找出大文件

          # git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')"

          385321b5a0be589af4436ddc6dd0d08a687f8d80 pdf/test/1.gif
          21224e779a19dfe7f716eb031d7c69ad65fb684c pdf/test/2.gif
          b615ba62e75bdb1c1faca8c43a82c5ef810d7e20 pdf/test/3.gif
          cd5762af542f724ca44656f02936940cf6de6525 zip/1.zip
          089977cb9de0105969d57bb070f6df0240b9da63 pdf/test/search_index.json

          參數(shù)說明:

          rev-list 命令用來列出 Git 倉庫中的提交,我們用它來列出所有提交中涉及的文件名及其 ID。該命令可以指定只顯示某個引用(或分支)的上下游的提交 --objects 列出該提交涉及的所有文件 ID --all 所有分支的提交,相當于指定了位于/refs 下的所有引用 verify-pack 命令用于顯示已打包的內(nèi)容,我們用它來找到那些大文件 -v(verbose)參數(shù)是打印詳細信息

          4.3 刪除大文件

          # git filter-branch --force --prune-empty --index-filter 'git rm -rf --cached --ignore-unmatch YOU-FILE-NAME' --tag-name-filter cat -- --all

          參數(shù)說明:

          filter-branch 命令可以用來重寫 Git 倉庫中的提交 --index-filter 參數(shù)用來指定一條 Bash 命令,然后 Git 會檢出(checkout)所有的提交, 執(zhí)行該命令,然后重新提交 –all 參數(shù)表示我們需要重寫所有分支(或引用) YOU-FILE-NAME 你查找出來的大文件名字

          也可以將上面查找出的大文件輸出重定向輸入到某個文件,這樣更便于操作

          # 定向到文件
          # git rev-list --objects --all | grep "$(git verify-pack -v .git/objects/pack/*.idx | sort -k 3 -n | tail -5 | awk '{print$1}')" >> large-files.txt
          # 得到文件路徑并批量刪除
          # cat large-files.txt| awk '{print $2}' | tr '\n' ' ' >> large-files-inline.txt
          # git filter-branch -f --prune-empty --index-filter "git rm -rf --cached --ignore-unmatch `cat large-files-inline.txt`" --tag-name-filter cat -- --all
          # 或者直接刪除目錄下的所有文件
          # git filter-branch --force --index-filter 'git rm -rf --cached --ignore-unmatch  gitbook/**' --prune-empty --tag-name-filter cat -- --all

          強制推送

          # git push --force --all

          本地的repo里面仍然保留了這些objects, 等待GC垃圾回收,因此需要徹底清除并收回空間

          # rm -rf .git/refs/original/
          # git reflog expire --expire=now --all
          # git gc --prune=now
          Enumerating objects: 40395, done.
          Counting objects: 100% (40395/40395), done.
          Delta compression using up to 8 threads
          Compressing objects: 100% (5546/5546), done.
          Writing objects: 100% (40395/40395), done.
          Total 40395 (delta 19454), reused 35405 (delta 16700), pack-reused 0
          Removing duplicate objects: 100% (256/256), done.

          4.4 按照 pack 文件直接操作

          除了上面的方式,也可以通過直接找到大的pack文件,基于這些文件進行快速操作

          # 找到pack文件,重建索引
          git filter-branch --index-filter 'git rm -r --cached --ignore-unmatch .git/objects/pack/xxxxx.pack' --prune-empty
          # 刪除和重建的索引
          # git for-each-ref --format='delete %(refname)' refs/original | git update-ref --stdin
          # 設(shè)置reflog過期
          git reflog expire --expire=now --all
          # 清理垃圾
          git gc --aggressive --prune=now

          5、大文件存儲的正確方式

          大文件一般是不建議直接存儲到git倉庫中的,git倉庫是代碼倉庫,存放的應該是n個代碼文件(其實也可以認為是文本文件)

          如果是作為倉庫管理員,應該有意識的將git倉庫設(shè)置一個允許的文件大小限制

          如果是非變化性的大文件,可以存儲到專用的文件服務器、對象存儲等

          如果非要在版本庫中存儲大文件,更好的方式是通過git-lfs,及時使用 lfs 來追蹤、記錄和管理大文件。這樣大文件既不會污染我們的 .git 目錄,也可以讓我們更方便的使用,這里不多做原理展開,

          簡單來說操作方法如下

          # 1.開啟lfs功能
          # git lfs install
          # 2.追蹤所有后綴名為“.psd”的文件
          # git lfs track "*.iso"
          # 3.追蹤單個文件
          git lfs track "logo.png"
          # 4.提交存儲信息文件
          # git add .gitattributes
          # 5.提交并推送到GitHub倉庫
          # git add .
          # git commit -m "Add some files"
          # git push origin master

          關(guān)于git-lfs的使用及原理說明可以參考國內(nèi)的gitee官方幫助說明文檔Git LFS 操作指南[2]

          6、其他解決方案

          除了上面的操作,還可以利用更為好用的開源效率工具bfg進行清理,參考`bfg`文檔[3],配置好java環(huán)境后,操作如下

          # 下載封裝好的jar包
          $ wget https://repo1.maven.org/maven2/com/madgag/bfg/1.13.0/bfg-1.13.0.jar

          #
           克隆的時候需要--mirror參數(shù)
          $ git clone --mirror git://example.com/big-repo.git

          #
           運行BFG來清理存儲庫
          $ java -jar bfg.jar --strip-blobs-bigger-than 100M big-repo.git

          #
           去除臟數(shù)據(jù)
          $ cd big-repo.git
          $ git reflog expire --expire=now --all
          $ git gc --prune=now --aggressive

          #
           推送上去
          # 此推將更新遠程服務器上的所有refs分支
          $ git push

          其他用法

          # 刪除所有的名為'id_dsa''id_rsa'的文件
          $ java -jar bfg.jar --delete-files id_{dsa,rsa}  my-repo.git
          # 刪除所有大于50M的文件
          $ java -jar bfg.jar --strip-blobs-bigger-than 50M  my-repo.git
          # 刪除文件夾下所有的文件
          $ java -jar bfg.jar --delete-folders doc  my-repo.git

          7、小結(jié)

          本文分析了git底層版本控制的存儲實現(xiàn),分析了版本控制系統(tǒng)中大文件的產(chǎn)生,并通過一定手段進行解決。

          要提到的是,上面的操作難免也會出現(xiàn)風險,如果是作為一個規(guī)范的代碼倉庫,應該在前期就做好規(guī)劃,避免大文件提交到倉庫,規(guī)范每一次的提交記錄,做好code review及倉庫管理

          See you ~

          參考資料

          [1]

          利用 Git 鉤子實現(xiàn)代碼發(fā)布: https://www.ssgeek.com/post/li-yong-git-gou-zi-shi-xian-dai-ma-fa-bu/

          [2]

          Git LFS 操作指南: https://gitee.com/help/articles/4235#article-header9

          [3]

          bfg文檔: https://rtyley.github.io/bfg-repo-cleaner/

          [4]

          https://harttle.land/2016/03/22/purge-large-files-in-gitrepo.html#header-6

          瀏覽 82
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美中文字幕在线视频观看 | 五月天婷婷丁香综合 | 色乱视频 | 一区二区有限公司 | 亚欧精品在线视频 |