【问题标题】:How can I split a single file from a git repo into a new repo?如何将单个文件从 git repo 拆分为新的 repo?
【发布时间】:2016-09-13 21:20:17
【问题描述】:

我有一个包含多个目录和一个文件 MyFile.ext 的 git 存储库。

/
  LargeDir1/
  LargeDir2/
  LargeDir3/
      .
      .
      .
  MyFile.ext

我想开始一个只包含MyFile.ext 的新存储库,并保留与之相关的所有历史记录,但忽略其他所有内容(所有LargeDirs)。我该怎么做?

对于目录,我已经成功使用this answer,但是我在单个文件上尝试过,它不起作用。

我也试过this answer,它确实删除了我的文件以外的所有内容,但它似乎也留下了所有的历史记录。

【问题讨论】:

  • 看看能不能 'git mv' 把文件放到子目录中,然后使用 'git subtree'。
  • @Gregg 我会尝试一下,但我很确定它不会起作用,因为当您移动文件时,您会丢失 git 历史记录。我之前在重命名目录上使用 git subtree split 时遇到过问题。
  • @Gregg 是的,刚刚尝试过。进入新仓库的唯一提交是文件被移动到新目录的提交。
  • 我遇到了同样的问题,我尝试了git subtree split ...git filter-branch ... 两种解决方案都没有成功。这些基本上只适用于其中所有内容从未在该目录之外更改过的子目录。我想要的是你在运行git log MyFile.ext 时看到的提交和日志历史记录。
  • @hepcat72 是的,我不知道这是否可能。我最终放弃了,失去了历史。

标签: git


【解决方案1】:

使用git fast-export

首先将文件的历史导出到快速导入流。确保在 master 分支上执行此操作。

cd oldrepo
git fast-export HEAD -- MyFile.ext >../myfile.fi

然后你创建一个新的 repo 并导入。

cd ..
mkdir newrepo
cd newrepo
git init
git fast-import <../myfile.fi
git checkout

【讨论】:

  • 我刚试过这个,但文件没有出现在新的仓库中。
  • @KrisHarper fast-import 创建提交,但不是工作副本。查看更新的答案。
  • 我得到“致命的:你在一个尚未出生的分支上”。
  • 您使用的是哪个版本的git?这适用于 git 2.11。对于一些旧版本的 git,您可能必须先创建一个初始提交才能创建 master 分支。
  • 供将来参考:可以添加多个文件名甚至目录名而不是 MyFile.ext,这对于将 repo 拆分为多个部分非常有用。
【解决方案2】:

我遇到了同样的问题,我终于弄明白了。我有一个旧的脚本目录——太旧了,它们最初是在 RCS 控制下的。几年前,我把它变成了一个 git repo(并不知道我在做什么),我转换了 RCS 日志并更新了 git 日志。但我选择了其中一个脚本的开发,并决定它需要自己的 repo。那里的各种解决方案(子树和过滤器分支)取决于您要拆分为目录的部分。您可以将文件放在目录中并以这种方式将其拆分,但您不会获得修订历史记录。所以这就是我想出如何提取单个文件的修订历史并用它创建一个新的 repo 的方法:

  1. 创建一个分支新repo [我是在与source-repo相同的级别上做的]

    git init <new-repo>
    
  2. 现在进入您的源代码库并创建一个文件,我们稍后将使用它来挑选文件的提交:

    cd <source-repo>
    git log --reverse <target-file.ext> | \
        grep ^commit | cut -d ' ' -f 2 | cut -c 1-7 | \
        perl -ne 'print("pick $_")' > ../commits-to-keep.txt
    
  3. 创建一个临时分支并将其推送到您的新仓库(然后将其删除)

    git checkout -b tmpbranch
    git push ../new-repo tmpbranch
    git checkout master
    git branch -d tmpbranch
    
  4. 现在转到您的新存储库并创建一个空提交,我们将对其进行变基:

     cd ../<new-repo>
     git commit --allow-empty -m 'root commit'
     git rebase --onto master --root tmpbranch -i
    
  5. [唯一的手动步骤] 在上面最后一个命令出现的编辑器中,删除所有内容并粘贴您之前创建的文件的内容:../commits-to-keep.txt

  6. 现在可以切换回master分支,合并,然后清理临时分支:

     git checkout master
     git merge tmpbranch
     git branch -d  tmpbranch
    

这里唯一的缺点是你最终得到了额外的空根提交。我发现有一些方法可以删除它,但就我的目的而言,这已经足够了。

【讨论】:

  • 尽管@RolandSmith 的答案是正确的方法,但我想我要补充一点,我的手动步骤可以通过在 rebase 命令上方插入来更改为自动:setenv GIT_EDITOR 'vim +"%d | r ../commits-to-keep.txt | wq"'。这样就可以跳过第 5 步。尽管您可能不想丢失 GIT_EDITOR 中可能存在的内容。代替@RolandSmith 的回答,我想把我的回答记下来,但我想我会留下它,只是为了展示另一种方法。
【解决方案3】:
  1. 克隆存储库。
  2. 过滤掉除该文件之外的所有内容。

使用git clone 可以正常进行克隆。这在git clone /path/to/the/repo 之类的目录上可以正常工作。然后删除远程指向克隆。

git clone /path/to/the/repo
git remote rm origin

然后使用git filter-branch 过滤掉除该文件之外的所有内容。使用删除所有文件然后只恢复一个文件的索引过滤器最容易做到这一点。

git rm --cached -qr -- . && git reset -q $GIT_COMMIT -- YOURFILENAME

索引过滤器通过检查每个单独的提交以及所有暂存的更改来工作。您正在运行此命令,然后重新提交它。它首先从暂存中删除所有更改,然后将该文件恢复到该提交中的状态。 $GIT_COMMIT 是被重写的提交。 YOURFILENAME 是您要保留的文件。

如果您使用--all 处理所有分支和标签,请添加标签过滤器以确保重写标签。这就像--tag-name-filter cat 一样简单。它不会改变标签的内容,但会确保它们被移动到重写的提交中。

最后,您需要--prune-empty 删除任何不涉及该文件的空提交。会有很多。

这一切都在一起了。

git filter-branch \
    --index-filter 'git rm --cached -qr -- . && git reset -q $GIT_COMMIT -- YOURFILENAME' \
    --tag-name-filter cat
    --prune-empty \
    -- --all

【讨论】:

    猜你喜欢
    • 2011-06-20
    • 2019-07-29
    • 2016-08-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-10
    • 2014-02-16
    相关资源
    最近更新 更多