90

如何将目录和文件连同提交历史一起移动到子目录?

例如:

  • 源码目录结构:[project]/x/[files & sub-dirs]

  • 目标目录结构:[project]/x/p/q/[files & sub-dirs]

4

6 回答 6

90

要添加到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"

先试一试,看看这个动作的预览:

git mv -n x/* x/p/q

沃尔夫冈 评论

如果您使用的是 bash,则可以避免尝试将文件夹移动到自身中的问题,方法是使用这样的扩展 glob(使用内置的shopt

shopt -s extglob; git mv !(folder) folder

曼船长在评论中报告 必须这样做:

mkdir temp 
git mv x/* temp
mkdir -p x/p/q
git mv temp x/p/q
rmdir temp;

语境:

我在带有 Cygwin 的 Windows 上。
我刚刚意识到我shopt -s extglob做错了这个例子,所以我的方法可能没有必要,但我通常使用 zsh 而不是 bash,并且它没有命令shopt -s extglob(尽管我确信有替代方法),所以这种方法应该跨外壳工作(在您的外壳中进行替换mkdirrmdir如果它特别陌生)


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

跳过会导致错误情况的移动或重命名操作。

git mv -k * target/

这将避免“ can not move directory into itself”错误。

于 2013-09-08T00:23:32.470 回答
48

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

这些文件随后会出现在p/q整个历史记录中的子目录中。

树过滤器非常适合小型项目,它的优点是命令简单易懂。对于大型项目,此解决方案不能很好地扩展,如果性能很重要,请考虑使用此答案中概述的索引过滤器。

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

于 2014-02-04T16:54:17.847 回答
13

我可以看到这是一个老问题,但我仍然觉得有义务用我目前对该问题的解决方案来回答,我是从git book中的一个示例中得出的。我没有使用低效的方法,--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

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

例子:

Project Directory
                 |-a
                 |  |-a1.txt
                 |  |-b
                 |  |  |-b1.txt

将 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

结果:

Project Directory
                 |-a
                 |  |-a1.txt
                 |
                 |-b
                 |  |-b1.txt
于 2015-01-14T22:31:04.847 回答
7

git mv 命令是最简单的方法。但是,至少在我的 Linux 机器上,我需要提供 -k 标志以避免返回文件夹无法移动到自身中的错误。我能够使用...执行该操作

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

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

于 2016-05-06T19:04:12.913 回答
0

如果您出于某种原因不想使用 git mv 命令,这是另一种方法:

  • 确保您没有任何未提交的更改。

    状态

  • 只需将包含 .git 文件夹的文件夹移动到新文件夹即可:

    mkdir new_folder mv old_folder new_folder

  • 然后将 .git old_folder 从移动的文件夹移回基本文件夹:

    mv new_folder/old_folder/.git new_folder/

  • 然后暂存并提交检测到的所有更改(列为将文件添加到新位置并从旧位置删除)

于 2020-10-19T11:23:22.327 回答
0

我自己的建议:git fast-export然后编辑导出文件,然后git fast-import.

于 2022-02-14T00:27:29.953 回答