Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/git/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在Git repo中为所有提交移动目录?_Git_Git Filter Branch - Fatal编程技术网

如何在Git repo中为所有提交移动目录?

如何在Git repo中为所有提交移动目录?,git,git-filter-branch,Git,Git Filter Branch,假设我有一个包含以下目录结构的回购协议: repo/ blog/ _posts/ some-post.html another-file.txt 我想将\u posts移动到回购协议的顶层,因此结构如下所示: repo/ _posts/ some-post.html another-file.txt 这对于git mv来说非常简单,但是我想让历史看起来好像\u posts始终存在于repo的根目录中,并且我想能够通过git log--\u post

假设我有一个包含以下目录结构的回购协议:

repo/
  blog/
    _posts/
      some-post.html
  another-file.txt
我想将
\u posts
移动到回购协议的顶层,因此结构如下所示:

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

这对于
git mv
来说非常简单,但是我想让历史看起来好像
\u posts
始终存在于repo的根目录中,并且我想能够通过
git log--\u posts/some post.html
获得
some post.html
的整个历史。我想我可以通过
git-filter-branch
来实现这一点,但我还没有弄清楚如何做到这一点。有什么想法吗?

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

 $ git filter-branch --subdirectory-filter blog/ -- --all
编辑1:如果您不想有效地将
\u posts
设置为根目录,请改用树过滤器:

 $ git filter-branch --tree-filter 'mv blog/_posts .' HEAD
编辑2:如果部分提交中不存在
blog/\u帖子
,则上述操作将失败。改用这个:

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

虽然拉姆库马尔的答案非常有用,也很有价值,但在很多情况下都不起作用。 例如,当您要将一个目录和其他子目录移动到一个新位置时

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

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替换为所需的新目录即可。
您还可以使用嵌套的dir,如dir1/dir2/dir3/-“

为任意移动/重命名创建通用脚本:

示例:

git filter-mv 's!^!subdir/!'
➜ 在当前分支的所有提交中将所有文件移动到子目录“subdir/”

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

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

使用
--索引过滤器
也要快得多,因为它不必签出树。是的,索引过滤器更快,但它不会工作,因为显示的命令不会影响索引。只有当您想使用索引过滤器时,才需要进行索引操作r(例如,
git rm——缓存的
而不是
rm
)我也可以保留标记吗?在我的情况下,它们看起来已经不存在了。
git filter branch--index filter'git read tree--prefix=/$git\u COMMIT:\u posts;git rm-r--cached--u posts--all
。如果您的标记没有签名,并且您想移动它们/使旧的无效,则添加
--tag name filter cat
:请记住对所有分支执行
git push--force--all
。否则,您可能会遇到一些有趣的情况。由于查看该命令或由此产生的错误并不明显,因此\t在os x版本的sed上不起作用。有很多方法可以解决此问题,但也许最快的方法是删除\t并替换它通过键入ctrl-v,可以使用文本选项卡进行编辑。您如何指定原始文件夹,还是只移动整个分支?当我尝试从要移动的文件夹运行此命令时,我得到
您需要从工作树的顶层运行此命令
sed
命令的作用是什么?我正在Windows上尝试此操作,需要一个alternative.Brilliant。但是sed是令人困惑的,所以请尝试单独使用第二行来测试它。例如,为了删除不必要的顶级目录,我做了一个简单的
git ls files-s | sed“s-\tdir1/dir2/dir3/-\t-”
如果您过滤的提交有效地删除了所有文件,那么您最终得到的
git update index
不会创建文件$GIT_INDEX_FILE.new”,因此mv命令失败。我在过滤器分支脚本中使用了
test-f\$GIT_INDEX_FILE.new和&mv\$GIT_INDEX_FILE.new\$GIT_INDEX_FILE | touch\$GIT_INDEX_FILE
git filter-mv 's!^foo/bar.txt$!foo/barbar.txt!'