【问题标题】:How can I move a directory in a Git repo for all commits?如何在 Git 存储库中为所有提交移动目录?
【发布时间】:2010-06-29 15:54:42
【问题描述】:

假设我有一个包含此目录结构的存储库:

repo/
  blog/
    _posts/
      some-post.html
  another-file.txt

我想将 _posts 移动到 repo 的顶层,因此结构将如下所示:

repo/
  _posts/
    some-post.html
  another-file.txt

这对git mv 来说很简单,但我想让历史看起来好像_posts 总是 存在于 repo 的根目录中,我希望能够获得some-post.html 通过git log -- _posts/some-post.html 的整个历史。我想我可以通过git filter-branch 使用一些魔法来实现这一点,但我还没有弄清楚如何做到这一点。有什么想法吗?

【问题讨论】:

    标签: git git-filter-branch


    【解决方案1】:

    您可以使用子目录过滤器来实现这一点

     $ git filter-branch --subdirectory-filter blog/ -- --all
    

    编辑 1:如果您不想有效地将​​ _posts 设为根,请改用树过滤器:

     $ git filter-branch --tree-filter 'mv blog/_posts .' HEAD
    

    编辑 2: 如果 blog/_posts 在某些提交中不存在,则上述操作将失败。改用这个:

     $ git filter-branch --tree-filter 'test -d blog/_posts && mv blog/_posts . || echo "Nothing to do"' HEAD
    

    【讨论】:

    • 使用--index-filter快得多,因为它不必检查树。
    • 是的,索引过滤器更快,但它不起作用,因为显示的命令影响索引。仅当您想使用索引过滤器时才需要进行索引操作(例如 git rm --cached 而不是 rm
    • 我也可以保留标签吗?在我的情况下,它们似乎已经消失了。
    • git filter-branch --index-filter 'git read-tree --prefix=/ $GIT_COMMIT:_posts; git rm -r --cached _posts' -- --all。如果您的标签未签名并且您想移动它们/使旧标签无效,请添加 --tag-name-filter cat
    • 对于那些在这里只是为了复制粘贴的人:记得git push --force --all 为所有分支。否则你可能会遇到有趣的情况。
    【解决方案2】:

    虽然 Ramkumar 的回答非常有用且有价值,但它在许多情况下都行不通。 例如,当您想将包含其他子目录的目录移动到新位置时。

    为此,手册页包含完美的命令:

    git filter-branch --index-filter \
      'git ls-files -s | sed "s-\t\"*-&NEWSUBDIR/-" |
       GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
       git update-index --index-info &&
       mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD
    

    只需将 NEWSUBDIR 替换为您想要的新目录即可。 您还可以使用嵌套目录,例如 dir1/dir2/dir3/-"

    【讨论】:

    • 而且由于从查看该命令或由此产生的错误来看并不是很明显,因此 \t 在 os x 的 sed 版本上不起作用。有很多方法可以解决这个问题,但最快的方法可能是删除 \t 并通过键入 ctrl-v, . 将其替换为文字选项卡。
    • 如何指定原始文件夹,还是只是移动整个分支?当我尝试从我想移动的文件夹中运行它时,我得到You need to run this command from the toplevel of the working tree
    • sed 命令有什么作用?我正在 Windows 上尝试这个,需要一个替代方案。
    • 太棒了。但是,是的,sed 令人困惑,所以请单独尝试第二行来测试它。 IE。为了删除不必要的顶级目录,我做了一个简单的git ls-files -s | sed "s-\tdir1/dir2/dir3/-\t-"
    • 如果你过滤一个有效删除所有文件的提交,你最终会得到git update-index 没有创建文件“$GIT_INDEX_FILE.new”,因此 mv 命令失败。我最终在 filter-branch 脚本中找到了test -f \$GIT_INDEX_FILE.new && mv \$GIT_INDEX_FILE.new \$GIT_INDEX_FILE || touch \$GIT_INDEX_FILE
    【解决方案3】:

    为任意移动/重命名创建了一个通用脚本:https://gist.github.com/xkr47/f766f4082112c086af63ef8d378c4304

    例子:

    git filter-mv 's!^!subdir/!'
    

    ➜ 将所有文件移动到当前分支的所有提交中的子目录“subdir/”

    git filter-mv 's!^foo/bar.txt$!foo/barbar.txt!'
    

    ➜ 在当前分支的所有提交中将 foo/bar.txt 重命名为 foo/barbar.txt

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-31
      • 2013-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多