如何使用 index-filter & co 从 git repo 中提取一个带有提交历史记录的文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7375528/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-19 05:59:19  来源:igfitidea点击:

How to extract one file with commit history from a git repo with index-filter & co

gitextractiongit-filter-branch

提问by peterhil

My situation was, I have a git repo converted from SVN to HG to GIT, and I wanted to extract just one source file. I also had weird characters like aì (an encoding mismatch corrupted Unicode ?) and spaces in the filenames.

我的情况是,我有一个从 SVN 到 HG 再到 GIT 的 git 存储库,我只想提取一个源文件。我也有奇怪的字符,比如 aì(编码不匹配损坏的 Unicode ?)和文件名中的空格。

Seems it's not particularly easy, and that's the reason I'll be answering my own question despite many similar questions regarding git [index-filter|subdirectory-filter|filter-tree], as I needed to use all the previous to achieve this!

似乎这不是特别容易,这就是我将回答我自己的问题的原因,尽管有许多关于 git [index-filter|subdirectory-filter|filter-tree] 的类似问题,因为我需要使用以前的所有方法来实现这一点!

So the question is: "How can I extract one file from a repository and place it at the root of the new repo?"

所以问题是:“如何从存储库中提取一个文件并将其放在新存储库的根目录下?”

采纳答案by peterhil

First a quick note, that even a spell like in a comment on Splitting a set of files within a git repo into their own repository, preserving relevant history

首先是一个简短的说明,即使是在将 git repo 中的一组文件拆分到他们自己的存储库中的评论中的拼写,保留相关历史记录

SPELL='git ls-tree -r --name-only --full-tree "$GIT_COMMIT" | grep -v "trie.lisp" | tr "\n" "
git filter-branch -f --subdirectory-filter lisp/source/model HEAD
" | xargs -0 git rm --cached -r --ignore-unmatch' git filter-branch --prune-empty --index-filter "$SPELL" -- --all

will not help with files named like imaging/DrinkkejaI<0300>$'\302\210'.txt_74x2032.gif. The aI<0300>$'\302\210'part once was a single letter: ?.

对命名为imaging/DrinkkejaI<0300>$'\302\210'.txt_74x2032.gif. 该aI<0300>$'\302\210'部分曾经是一个字母:?.

So in order to extract a single file, in addition to filter-branch I also needed to do:

所以为了提取单个文件,除了 filter-branch 我还需要做:

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

Alternatively, you can use --tree-filter: (the test is needed, because the file was at another directory earlier, see: How can I move a directory in a Git repo for all commits?)

或者,您可以使用 --tree-filter:(需要测试,因为该文件之前位于另一个目录中,请参阅: 如何为所有提交移动 Git 存储库中的目录?

git log --pretty=oneline --follow --name-only git-path/to/file | grep -v ' ' | sort -u

To see all the names a file have had, use:

要查看文件的所有名称,请使用:

$ git reset --hard
$ git gc --aggressive
$ git prune
$ git remote rm origin # Otherwise changes will be pushed to where the repo was cloned from

As described at http://whileimautomaton.net/2010/04/03012432

http://whileimautomaton.net/2010/04/03012432 所述

Also follow the steps on afterwards:

也请按照之后的步骤操作:

git filter-branch --index-filter '
                        git read-tree --empty
                        git reset $GIT_COMMIT -- $your $files $here
                ' \
        -- --all -- $your $files $here

回答by jthill

A faster and easier-to-understand filter that accomplishes the same thing:

一个更快、更容易理解的过滤器,可以完成同样的事情:

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'

回答by Roman

Note that things get much easier if you combine this with the additional step of moving the desired file(s) into a new directory.

请注意,如果将此与将所需文件移动到新目录的附加步骤相结合,事情会变得容易得多。

This might be a quite common use case (e.g. moving the desired single file to the root dir).
I did it (using git 1.9) like this (first moving the file(s), then deleting the old tree):

这可能是一个非常常见的用例(例如,将所需的单个文件移动到根目录)。
我这样做(使用 git 1.9)是这样的(首先移动文件,然后删除旧树):

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

You can even easily use wildcards for the desired files (without messing around with grep -v ).

您甚至可以轻松地为所需文件使用通配符(无需使用 grep -v )。

I'd think that this ('mv' and 'rm') could also be done in one filter-branch but it did'n work for me.

我认为这个('mv' 和 'rm')也可以在一个过滤器分支中完成,但它对我不起作用。

I didn't try it with weird characters but I hope this helps anyway. Making things easier seems always to be a good idea to me.

我没有用奇怪的角色尝试过,但我希望这会有所帮助。让事情变得更容易对我来说似乎总是一个好主意。

Hint:
This is a time consuming action on large repos. So if you want to do several actions (like getting a bunch of files and then rearrange them in 'new_path/subdirs') it's a good idea to do the 'rm' part as soon as possible to get a smaller and faster tree.

提示:
这是对大型 repos 的耗时操作。因此,如果您想执行多个操作(例如获取一堆文件,然后在“new_path/subdirs”中重新排列它们),最好尽快执行“rm”部分以获得更小更快的树。

回答by PowerKiKi

The following will rewrite the history and keep only commits that touch the list of files you give. You probably want to do that in a clone of your repository to avoid losing the original history.

以下将重写历史记录并仅保留涉及您提供的文件列表的提交。您可能希望在存储库的克隆中执行此操作以避免丢失原始历史记录。

git log --pretty=email --patch-with-stat --reverse --full-index --binary -- path/to/file_or_folder > /tmp/patch

Then you can merge that new branch into your target repository, via normal mergeor rebasecommands according to your use-case.

然后,您可以根据您的用例通过普通mergerebase命令将该新分支合并到您的目标存储库中。

回答by Marius Gedminas

I've found an elegant solution using git log and git am here: https://www.pixelite.co.nz/article/extracting-file-folder-from-git-repository-with-full-git-history/

我在这里使用 git log 和 git am 找到了一个优雅的解决方案:https: //www.pixelite.co.nz/article/extracting-file-folder-from-git-repository-with-full-git-history/

In case it goes away, here's how you do it:

万一它消失了,你可以这样做:

  1. in the original repo,

    sed -i -e 's/deep\/path\/that\/you\/want\/shorter/short\/path/g' /tmp/patch
    
  2. if the file was in a subdirectory, or if you want to rename it

    git am < /tmp/patch
    
  3. in a new, empty repo

    git log --pretty=email --patch-with-stat --reverse --full-index --binary -- path/to/file_or_folder > /tmp/patch
    
  1. 在原始回购中,

    sed -i -e 's/deep\/path\/that\/you\/want\/shorter/short\/path/g' /tmp/patch
    
  2. 如果文件在子目录中,或者您想重命名它

    git am < /tmp/patch
    
  3. 在一个新的空仓库中

    ##代码##