Move file and directory into a sub-directory along with commit history

rst*_*ptl 67 git git-log

How can I move a directory and files to a sub-directory along with commit history?

For example:

  • Source directory structure: [project]/x/[files & sub-dirs]

  • Target directory structure: [project]/x/p/q/[files & sub-dirs]

Von*_*onC 64

要添加bmargulies评论,完整的序列是:

mkdir -p x/p/q      # make sure the parent directories exist first
git mv x/* x/p/q    # move folder, with history preserved
git commit -m "changed the foldername x into x/p/q"
Run Code Online (Sandbox Code Playgroud)

首先尝试查看移动预览:

git mv -n x/* x/p/q
Run Code Online (Sandbox Code Playgroud)

沃尔夫冈 评论:

如果你正在使用bash,就可以避免试图通过使用扩展水珠像这样的一个文件夹移动到其自身的问题(使用shopt内置的):

shopt -s extglob; git mv !(folder) folder
Run Code Online (Sandbox Code Playgroud)

Man船长在评论 中报道:

mkdir temp 
git mv x/* temp
mkdir -p x/p/q
git mv temp x/p/q
rmdir temp;
Run Code Online (Sandbox Code Playgroud)

语境:

我和Cygwin在Windows上.
我刚刚意识到我的shopt -s extglob例子是错误的,所以我的方式可能没有必要,但我通常使用zsh而不是bash,而且它没有命令shopt -s extglob(虽然我确定有另一种选择),所以这种方法应该在shell中工作(在你的shell中进行修复mkdir,rmdir如果它特别是外来的)


作为替代方案,spanky 在评论中提到以下-k选项git mv:

跳过移动或重命名可能导致错误情况的操作.

git mv -k * target/
Run Code Online (Sandbox Code Playgroud)

这样可以避免" can not move directory into itself"错误.

  • 我刚学会了你可以使用`git mv -k*target /`来避免"无法将目录移动到自身"错误. (4认同)
  • 你不能将`x`移动到它自己的子目录中.`git mv ...`做的工作,如果没有进一步的改变,就不需要`git add ...`. (2认同)
  • 如果你正在使用bash,你可以通过使用像这样的扩展glob来避免尝试将文件夹移动到自身的问题:`shopt -s extglob; git mv!(文件夹)文件夹` (2认同)

PiQ*_*uer 41

即使内容被移动,Git也能很好地跟踪内容,因此git mv,如果您移动文件是显而易见的,因为它们曾经属于x,但现在它们属于,x/p/q因为项目就是这样发展的.

但是,有时在整个项目历史记录中将文件移动到子目录是有原因的.例如,如果文件被错误地放在某处并且之后已经进行了一些提交,但是间歇性提交甚至对错误位置的文件没有意义.如果在本地分支上发生了所有这些,我们希望在推送之前清理混乱.

问题陈述"与提交历史一起",我将其解释为以完全相同的方式重写历史.这可以通过以下方式完成

git filter-branch --tree-filter "cd x; mkdir -p p/q; mv [files & sub-dirs] p/q" HEAD
Run Code Online (Sandbox Code Playgroud)

然后,文件将显示在p/q整个历史记录的子目录中.

请注意,如果重写触及之前已经推送的提交,则不应将结果推送到公共服务器.

  • 谢谢你,让我的一天!我想一棵树的所有文件"转移"到一个新的子目录,并用你的方法用'MV*/的-subdir`但得到了错误:"不能,子目录移动到其自身的子目录,我的解决办法是在tree-filter命令中使用`mv the-subdir/tmp; mv*/tmp/the-subdir mv/tmp/the-subdir./`.就像一个魅力. (7认同)

小智 10

我可以看到这是一个老问题,但我仍然觉得有必要回答我目前的问题解决方案,我从git书中的一个例子中得出.而不是使用低效率--tree-filter我直接在索引上移动文件--index-filter.

git filter-branch -f --index-filter 'PATHS=`git ls-files -s | sed "s/^<old_location>/<new_location>/"`;GIT_INDEX_FILE=$GIT_INDEX_FILE.new; echo -n "$PATHS" | git update-index --index-info && if [ -e "$GIT_INDEX_FILE.new" ]; then mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"; fi' -- --all
Run Code Online (Sandbox Code Playgroud)

这是本书中的一个例子的专门化我也使用相同的例子在一个特殊情况下在提交历史中批量重命名文件.如果在子目录中移动文件:记住使用\来转义路径中的/字符以使sed命令按预期工作.

例:

Project Directory
                 |-a
                 |  |-a1.txt
                 |  |-b
                 |  |  |-b1.txt
Run Code Online (Sandbox Code Playgroud)

将b目录移动到项目根目录:

git filter-branch -f --index-filter 'PATHS=`git ls-files -s | sed "s/a\/b\//b\//"`;GIT_INDEX_FILE=$GIT_INDEX_FILE.new; echo -n "$PATHS" | git update-index --index-info && if [ -e "$GIT_INDEX_FILE.new" ]; then mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"; fi' -- --all
Run Code Online (Sandbox Code Playgroud)

结果:

Project Directory
                 |-a
                 |  |-a1.txt
                 |
                 |-b
                 |  |-b1.txt
Run Code Online (Sandbox Code Playgroud)


小智 6

git mv命令是最简单的方法。但是,至少在我的Linux机器上,我需要提供-k标志,以免出现无法将文件夹移入其自身的错误。我能够通过使用...执行操作

mkdir subdirectory
git mv -k ./* ./subdirectory
# check to make sure everything moved (see below)
git commit
Run Code Online (Sandbox Code Playgroud)

作为警告,这将跳过所有会导致错误情况的移动,因此您可能需要检查在移动之后和提交之前,一切是否正常。