Sharing

2012年10月21日 星期日

Git filter-branch 的使用


最近因為幫忙重新整理 git repository, 所以有機會接觸 git filter-branch, 過去因為 policy 沒有定義好, 所以大家都將 binary 檔也加進 repository, 造成整個 repository 很肥大, 要 checkout 時也必須要等很久, 所以我的任務就是把這些 binary 檔拔掉, 改成用 md5 or sha1 檔案代替, 如果真的需要用到時, 再去另外一個 http server 下載.
http://www.kernel.org/pub/software/scm/git/docs/git-filter-branch.html

假設整個 repository 有 1000 個版本, 那filter-branch 執行的過程, 其實就是依序把這些版本 checkout, 然後執行你指定的指令來改變檔案的內容, 或是甚至新增/刪除檔案, 之後再把修改過的檔案 commit 進去新的 repository. 所以執行完之後, 整個 repository 中每一筆項目的 SHA ID 都會改變, 是個很暴力的作法, 但也因為很暴力, 所以基本上你想要做什麼事應該都可以達成.

Filter

filter-branch 提供了許多不同 filter, 可以幫助你在正確的時間點執行指令

--env-filter

幫助你修改 author name 或是 author e-mail, 可以參考以下網址提供的 script
https://help.github.com/articles/changing-author-info

--tree-filter

幫助你在每一個版本去修改檔案的內容, 新增/刪除檔案, 最常使用的應該也是這個, 以我自己的例子來說, 我會寫一個像以下的 script 把大檔案轉成 sha1 檔
#!/bin/bash
function transform {

    file=$1
    sha1_name=$file.sha1

    if [ -f /tmp/git/$file ]; then
        rm -f $file
        cp /tmp/git/$sha1_name $sha1_name
    else
        sha1sum $file > $sha1_name
        mv $file /tmp/git
        cp $sha1_name /tmp/git
    fi

}

pushd ./bigfile
for file in *.tgz *.zip; do
    if [ ! -f $file ]; then
        continue;
    fi

    transform $file
popd

--index-filter

這個其實是 --tree-filter 的快速版本, 如果你沒有要改變檔案的內容, 只是單純改變 repository 的 history, 那可以使用這個, 因為他不會真的 checkout 檔案, 速度上快很多, 下面兩個網址都有示範, 最常用的功能就是把某一個特定的檔案永久的從 repository 中刪除.
https://help.github.com/articles/remove-sensitive-data
http://dalibornasevic.com/posts/2-permanently-remove-files-and-folders-from-a-git-repository

--msg-filter

這個是用來改變 commit message 的內容, 原來的內容會介由 standard input 輸入, 而你輸出到 standard output 的內容就會作為新的 commit message.
# 舉例來說, 因為 cat 就不會改變內容, 所以 commit message 完全不變
$ git filter-branch --msg-filter cat
# tac 會把內容全部反過來
$ git filter-branch --msg-filter tac

--tag-name-filter

用來改變 tag name, 如果該版本有加上 tag, 那當 checkout 這個版本時, 就會乎叫你指定的指令, 原來的內容會介由 standard input 輸入, 而你輸出到 standard output 的內容就會作為新的 tag name

--subdirectory-filter

把某一個 folder 下的 commit 獨立出來變成一個新的 repository, 如果當你的專案越來越大時, 你可能會想把某個資料夾獨立出來變成一個新專案, 那這個功能就很好用
http://gugod.org/2012/07/split-a-git-repository/

Other Command

--prune-empty

如果有些 filter 產生了空的 commit, 那會主動清除掉

-f --force

強制把暫存區的東西清掉

沒有留言: