如何从带有index-filter&的git repo中提取具有提交历史记录的文件合作

时间:2011-09-11 00:19:58

标签: git extraction git-filter-branch

我的情况是,我有一个git repo从SVN转换为HG到GIT,我只想提取一个源文件。我也有一些奇怪的字符,如aÌ(编码不匹配损坏的Unicodeä)和文件名中的空格。

似乎它并不是特别容易,这就是为什么我会回答我自己的问题,尽管有很多类似的问题关于git [index-filter |子目录 - 过滤器| filter-tree],因为我需要使用以前的所有内容实现这一点!

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

5 个答案:

答案 0 :(得分:36)

更快速,更易于理解的过滤器可以完成同样的事情:

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

答案 1 :(得分:13)

首先请注意,即使是对Splitting a set of files within a git repo into their own repository, preserving relevant history

的评论中的咒语也是如此
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: (需要进行测试,因为文件早先在另一个目录中,请参阅: How can I move a directory in a Git repo for all commits?

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

答案 2 :(得分:10)

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

这可能是一个非常常见的用例(例如,将所需的单个文件移动到根目录) 我这样做(使用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')也可以在一个过滤器分支中完成,但它对我没用。

我没有尝试过奇怪的角色,但我希望无论如何这都有帮助。让事情变得简单对我来说总是一个好主意。

提示:
对于大型回购,这是一个耗时的行动。所以如果你想做几个动作(比如获取一堆文件,然后在'new_path / subdirs'中重新排列它们),最好尽快做'rm'部分以获得更小更快的树。< / p>

答案 3 :(得分:4)

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

以防万一,请按照以下步骤操作:

  1. 在原始存储库中,

    git log --pretty=email --patch-with-stat --reverse --full-index --binary -- path/to/file_or_folder > /tmp/patch
    
  2. 如果文件位于子目录中,或者要重命名

    sed -i -e 's/deep\/path\/that\/you\/want\/shorter/short\/path/g' /tmp/patch
    
  3. 在一个新的空仓库中

    git am < /tmp/patch
    

答案 4 :(得分:0)

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

func main() {
    gd := generateGenericData()
    fmt.Println(gd)
    fmt.Println(gd.(GenericData))

    // Doesn't work, but is straightforward
    // fmt.Println(gd.(Data))

    // Works, but is not straight forward
    fmt.Println(Data{gd.(GenericData).Hard, gd.(GenericData).Soft.(int)})


    genDataGenerator := returnsGenericDataGenerator()

    // Doesn't work, but is straightforward
    //genDataGenerator.(GenericDataGenerator)()

    // Works, but is not straight forward
    resp := genDataGenerator.(func() interface{})()
    fmt.Println(resp.(GenericData))
}

然后,您可以根据用例通过常规的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命令将该新分支合并到目标存储库中。