我有一个从SVN转换到Mercurial再到Git的Git仓库,我想要提取其中一个源文件。此外,我的文件名中有空格和奇怪的字符,如aÌ
(编码不匹配导致Unicode ä
损坏)。
我该如何从仓库中提取一个文件并将其放置在新仓库的根目录下?
我有一个从SVN转换到Mercurial再到Git的Git仓库,我想要提取其中一个源文件。此外,我的文件名中有空格和奇怪的字符,如aÌ
(编码不匹配导致Unicode ä
损坏)。
我该如何从仓库中提取一个文件并将其放置在新仓库的根目录下?
一个更快,更易于理解的过滤器,可以实现相同的功能:
git filter-branch --index-filter '
git read-tree --empty
git reset $GIT_COMMIT -- $your $files $here
' \
-- --all -- $your $files $here
--prune-empty
参数来删除任何空提交。 - Aaron Jensen--all -- $your $files $here
会传递给 git rev-list
,这个命令会在 filter-branch
运行之前对提交记录进行修剪。这比让 filter-branch 无意义地加载索引、运行过滤器、创建新树和提交再将其全部丢弃要快得多,因为它只处理那些与指定文件有关的提交。尽管如此,添加它也不会有任何影响。 - jthill看起来并不是特别容易,这就是为什么尽管有许多类似于git [index-filter|subdirectory-filter|filter-tree]的问题,我仍然会回答自己的问题,因为我需要使用所有之前的方法才能实现这个!
首先,一个快速提醒,即使在将Git存储库中的一组文件拆分为它们自己的存储库,保留相关历史记录的评论中,也有类似的拼写错误。
SPELL='git ls-tree -r --name-only --full-tree "$GIT_COMMIT" | grep -v "trie.lisp" | tr "\n" "\0" | xargs -0 git rm --cached -r --ignore-unmatch'
git filter-branch --prune-empty --index-filter "$SPELL" -- --all
无法处理像 imaging/DrinkkejaI<0300>$'\302\210'.txt_74x2032.gif
这样命名的文件。
aI<0300>$'\302\210'
部分曾经是一个单独的字母:ä
。
因此,为了提取单个文件,除了使用 filter-branch,我还需要执行以下操作:
git filter-branch -f --subdirectory-filter lisp/source/model HEAD
或者,您可以使用 --tree-filter:
(这个测试很有必要,因为文件先前在另一个目录中,请参见:如何将Git存储库中的目录移动到所有提交中?)MV_FILTER='test -f source/model/trie.lisp && mv ./source/model/trie.lisp . || echo "Nothing to do."'
git filter-branch --tree-filter $MV_FILTER HEAD --all
要查看文件曾用过的所有名称,请使用:
git log --pretty=oneline --follow --name-only git-path/to/file | grep -v ' ' | sort -u
根据http://whileimautomaton.net/2010/04/03012432所述:
之后按照以下步骤进行:
$ git reset --hard
$ git gc --aggressive
$ git prune
$ git remote rm origin # Otherwise changes will be pushed to where the repo was cloned from
- http://git-scm.com/docs/git-filter-branch
- http://git-scm.com/book/en/v2/Git-Tools-Rewriting-History
- peterhil请注意,如果您将此步骤与将所需文件移动到新目录的附加步骤相结合,事情会变得更加容易。
这可能是一个非常常见的用例(例如,将所需的单个文件移动到根目录)。
我使用git 1.9这样做(先移动文件,然后删除旧树):
git filter-branch -f --tree-filter 'mkdir -p new_path && git mv -k -f old_path/to/file new_path/'
git filter-branch -f --prune-empty --index-filter 'git rm -r --cached --ignore-unmatch old_path'
您甚至可以轻松使用通配符来获取所需文件(而不必使用grep -v等工具)。
我认为('mv'和'rm')也可以在一个filter-branch中完成,但对我没用。
我没有尝试过使用奇怪的字符,但我希望这样能有所帮助。让事情变得更加简单似乎总是一个好主意。
提示:
对于大型仓库,这是一项耗时的操作。因此,如果您想执行多个操作(例如获取一堆文件,然后将它们重新排列在“new_path/subdirs”中),那么尽早执行“rm”部分是一个很好的主意,以便获得更小、更快的树。
。
),为什么不使用 git filter-branch -f --subdirectory-filter new_path -- --all
呢? - jan-glx我在这里找到了一种使用git log和git am的优雅解决方案:
https://www.pixelite.co.nz/article/extracting-file-folder-from-git-repository-with-full-git-history/如果链接失效,以下是操作步骤:
in the original repo,
git log --pretty=email --patch-with-stat --reverse --full-index --binary -- path/to/file_or_folder > /tmp/patch
if the file was in a subdirectory, or if you want to rename it
sed -i -e 's/deep\/path\/that\/you\/want\/shorter/short\/path/g' /tmp/patch
in a new, empty repo
git am < /tmp/patch
FILES='path/to/file1 other-path/to/file2 file3'
git filter-branch --prune-empty --index-filter "
git read-tree --empty
git reset \$GIT_COMMIT -- $FILES
" \
-- --all -- $FILES
然后您可以通过常规的合并(merge)
或者变基(rebase)
命令,将新分支合并到目标代码库中,具体取决于您的用例。