星期一, 6月 29, 2026

git-filter-repo 切分子專案小記

git-filter-repo 切分子專案小記



環境:

  • macOS 26.5.1
  • git 2.50.1


有的時候是這樣的, 專案會在測試目錄中發想, 然後慢慢測試, 慢慢就長成可以測試的小專案, 這個時候就會想把它獨立出來, 但是又想要保留當初開發的 git status.

今天要實作的是把 Git repo 裡面的子目錄切出去,變成一個獨立 repo,而且希望 Git history 可以一起帶過去。

我的原本 repo 在這裡:

/Users/max/Downloads/local_lab/lab_temp

要切出來的子目錄是這個:

02_實作中/20260627_chatbot_rag/


工具使用 `git-filter-repo`


Step 1: 先 clone 一份出來

重點是這裡要加 `--no-local`。如果只是從本機路徑直接 clone,`git-filter-repo` 可能會認為這不是乾淨的 fresh clone,然後拒絕改寫 history。

指令如下

cd /Users/max/Downloads/local_lab

git clone --no-local ./lab_temp chatbot_rag_split


  • 如果沒有加 `--no-local`,會遇到以下錯誤:

Aborting: Refusing to destructively overwrite repo history since
this does not look like a fresh clone.
  (expected freshly packed repo)
Note: when cloning local repositories, you need to pass --no-local to git clone to avoid this issue.


Step 2: 執行 git-filter-repo

接著只保留目標子目錄,並把它搬到新 repo 的根目錄:

cd chatbot_rag_split
git-filter-repo
\ --path '02_實作中/20260627_chatbot_rag/' \ --path-rename '02_實作中/20260627_chatbot_rag/':


這邊最容易看錯的是最後的冒號。`--path-rename` 的格式是 `OLD_PATH:NEW_PATH`,所以這行的意思是:

  • `OLD_PATH` 是 `02_實作中/20260627_chatbot_rag/`
  • `NEW_PATH` 是空字串
  • 結果就是把該子目錄底下的內容搬到 repo 根目錄

所以不是寫成 `:/`。Git repo 裡的 path 是相對路徑,不是檔案系統的 `/` 根目錄。要搬到 repo root,就是冒號後面留空。


成功輸出

最後成功時輸出長這樣:

NOTICE: Removing 'origin' remote; see 'Why is my origin removed?'
        in the manual if you want to push back there.
        (was /Users/max/Downloads/local_lab/./lab_temp)
Parsed 36 commits
New history written in 0.14 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
HEAD is now at 54dab99 build: 加入 Cloud Run 入口與資源保護
Enumerating objects: 296, done.
Counting objects: 100% (296/296), done.
Delta compression using up to 8 threads
Compressing objects: 100% (157/157), done.
Writing objects: 100% (296/296), done.
Total 296 (delta 136), reused 276 (delta 136), pack-reused 0 (from 0)
Completely finished after 0.25 seconds.

  • 這裡看到 `Removing 'origin' remote` 是正常的。`git-filter-repo` 會移除原本的 `origin`,避免不小心把改寫後的 history 推回原 repo。這個設計滿貼心的,is not really useful :)


檢查結果

跑完後可以先看一下目前內容、commit,以及 remote 狀態:

ls
git log --oneline --decorate -n 10
git status
git remote -v

  • 如果 `git remote -v` 沒有輸出,這次反而是正常狀態,因為 origin 已經被移除了。


Step 3: 接到新的 remote

確認內容沒問題後,再把它接到新的 GitLab 或 GitHub repo。

  • 建議新 repo 不要先初始化 README、`.gitignore` 或 license,避免第一推就要處理 unrelated history。

git remote add origin git@github.com:your-org/chatbot_rag.git

git push -u origin HEAD:main

  • 可用 `HEAD:main`,因為不用先管目前 local branch 叫 `main` 還是 `master`,直接把目前所在的 HEAD 推到遠端 `main`。


這次的完整版本

cd /Users/max/Downloads/local_lab

git clone --no-local ./lab_temp chatbot_rag_split

cd chatbot_rag_split

git-filter-repo \
  --path '02_實作中/20260627_chatbot_rag/' \
  --path-rename '02_實作中/20260627_chatbot_rag/':

git remote add origin git@github.com:your-org/chatbot_rag.git

git push -u origin HEAD:main

  • 這次的關鍵就是三個:本機 clone 要加 `--no-local`、實際指令用 `git-filter-repo`、`--path-rename` 最後的冒號要留下來。


又前進一步 ~ enjoy it

Reference

  • https://github.com/newren/git-filter-repo

沒有留言: