[英]How to split a git repository while preserving subdirectories?
您確實可以使用子目錄過濾器后跟索引過濾器將內容放回子目錄中,但是為什么要麻煩,當您可以單獨使用索引過濾器時呢?
這是手冊頁中的示例:
git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD
這只是刪除一個文件名; 您想要做的是刪除除給定子目錄之外的所有內容。 如果您想保持謹慎,可以明確列出要刪除的每條路徑,但如果您只想全力以赴,則可以執行以下操作:
git filter-branch --index-filter 'git ls-tree -z --name-only --full-tree $GIT_COMMIT | grep -zv "^directory-to-keep$" | xargs -0 git rm --cached -r' -- --all
我希望可能有一種更優雅的方式; 如果有人有什么,請提出建議!
關於該命令的一些說明:
--full-tree
是必要的,但顯然 filter-branch 從.git-rewrite/t
目錄而不是 repo 的頂層運行索引過濾器。--all
將此應用於所有引用; 我想你真的想要那個。 ( --
將它與過濾器分支選項分開)-z
和-0
告訴 ls-tree、grep 和 xargs 使用 NUL 終止來處理文件名中的空格。編輯,很久以后:Thomas 很有幫助地提出了一種刪除現在為空的提交的方法,但它現在已經過時了。 如果您有舊版本的 git,請查看編輯歷史記錄,但是對於現代 git,您需要做的就是添加此選項:
--prune-empty
這將刪除在應用索引過濾器后所有為空的提交。
我想做一個類似的事情,但由於我想保留的文件列表很長,使用無數的 grep 來做這件事沒有意義。 我寫了一個從文件中讀取文件列表的腳本:
#!/bin/bash
# usage:
# git filter-branch --prune-empty --index-filter \
# 'this-script file-with-list-of-files-to-be-kept' -- --all
if [ -z $1 ]; then
echo "Too few arguments."
echo "Please specify an absolute path to the file"
echo "which contains the list of files that should"
echo "remain in the repository after filtering."
exit 1
fi
# save a list of files present in the commit
# which is currently being modified.
git ls-tree -r --name-only --full-tree $GIT_COMMIT > files.txt
# delete all files that shouldn't be removed
while read string; do
grep -v "$string" files.txt > files.txt.temp
mv -f files.txt.temp files.txt
done < $1
# remove unwanted files (i.e. everything that remained in the list).
# warning: 'git rm' will exit with non-zero status if it gets
# an invalid (non-existent) filename OR if it gets no arguments.
# If something exits with non-zero status, filter-branch will abort.
# That's why we have to check carefully what is passed to git rm.
if [ "$(cat files.txt)" != "" ]; then
cat files.txt | \
# enclose filenames in "" in case they contain spaces
sed -e 's/^/"/g' -e 's/$/"/g' | \
xargs git rm --cached --quiet
fi
令人驚訝的是,結果證明這比我最初預期的要多得多,所以我決定把它貼在這里。
當我自己遇到這個問題時,這就是我最終為解決這個問題所做的:
git filter-branch --index-filter \
'git ls-tree --name-only --full-tree $GIT_COMMIT | \
grep -v "^directory-to-keep$" | \
sed -e "s/^/\"/g" -e "s/$/\"/g" | \
xargs git rm --cached -r -f --ignore-unmatch \
' \
--prune-empty -- --all
該解決方案基於 Jefromi 的回答和Detach (move) 子目錄到單獨的 Git 存儲庫中,以及此處關於 SO 的許多評論。
Jefromi 的解決方案對我不起作用的原因是,我的存儲庫中有文件和文件夾,其名稱包含特殊字符(主要是空格)。 此外git rm
抱怨不匹配的文件(用--ignore-unmatch
解決)。
您可以保持過濾不可知的目錄不在 repo 的根目錄中或被移動:
grep --invert-match "^.*directory-to-keep$"
最后,您可以使用它來過濾掉固定的文件或目錄子集:
egrep --invert-match "^(.*file-or-directory-to-keep-1$|.*file-or-directory-to-keep-2$|…)"
要在之后進行清理,您可以使用以下命令:
$ git reset --hard
$ git show-ref refs/original/* --hash | xargs -n 1 git update-ref -d
$ git reflog expire --expire=now --all
$ git gc --aggressive --prune=now
使用git-filter-repo從 2.25 版開始,這不是 git 的一部分。 這需要 Python3 (>=3.5) 和 git 2.22.0
mkdir new_repoA
mkdir new_repoB
git clone originalRepo newRepoA
git clone originalRepo newRepoB
pushd
cd new_repoA
git filter-repo --path foo/bar --path foo/baz
popd
cd new_repoB
git filter-repo --path foo/qux
對於包含 ~12000 次提交的存儲庫, git-filter-branch花費了 24 多個小時,而git-filter-repo花費了不到一分鍾。
更清潔的方法:
git filter-branch --index-filter '
git read-tree --empty
git reset $GIT_COMMIT path/to/dir
' \
-- --all -- path/to/dir
或者堅持只使用核心命令,在git read-tree --prefix=path/to/dir/ $GIT_COMMIT:path/to/dir
進行重置。
在 rev-list args 上指定path/to/dir
會盡早進行修剪,使用如此便宜的過濾器並不重要,但無論如何避免浪費精力是件好事。
如果您希望將單個目錄拆分為單獨的 git 存儲庫
git-filter-branch有--subdirectory-filter
選項,它比前面提到的解決方案簡單得多,只是:
git filter-branch --subdirectory-filter foodir -- --all
此外,它更改路徑並將目錄內容放置在新存儲庫的頂部,而不僅僅是過濾和刪除其他內容。
我將git-filter-repo
與filename-callback
。
stephen@B450-AORUS-M:~/source/linux$ git filter-repo --force --filename-callback '
if b"it87.c" in filename:
return filename
else:
# Keep the filename and do not rename it
return None
'
warning: Tag points to object of unexpected type tree, skipping.
warning: Tag points to object of unexpected type tree, skipping.
Parsed 935794 commitswarning: Omitting tag 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c,
since tags of trees (or tags of tags of trees, etc.) are not supported.
warning: Omitting tag 5dc01c595e6c6ec9ccda4f6f69c131c0dd945f8c,
since tags of trees (or tags of tags of trees, etc.) are not supported.
Parsed 937142 commits
New history written in 177.03 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
HEAD is now at a57e6edb85a3 treewide: Replace GPLv2 boilerplate/reference with SPDX - rule 157
Enumerating objects: 20210, done.
Counting objects: 100% (20210/20210), done.
Delta compression using up to 12 threads
Compressing objects: 100% (17718/17718), done.
Writing objects: 100% (20210/20210), done.
Total 20210 (delta 1841), reused 20038 (delta 1669), pack-reused 0
Completely finished after 179.76 seconds.
它沒有刪除空的合並提交,可能是由於一堆與樹的一側相關聯的標簽。
我嘗試使用投票最多的答案,它似乎沒有刪除任何內容,並且花了很長時間。
Rewrite 3e80e1395bd4f410b79dc0f17113f5b6b409c7d8 (329/937142) (8 seconds passed, remaining 22779 predicted)
22779 秒 = 6.3275 小時
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.