【问题标题】:Move file and directory into a sub-directory along with commit history将文件和目录连同提交历史一起移动到子目录中
【发布时间】:2013-09-11 02:47:59
【问题描述】:

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

例如:

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

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

【问题讨论】:

  • 我不明白这个问题的大部分答案,我尝试了所有方法我总是出错,或者根本没有发生任何事情,所以我只是剪切 .git 文件夹并将其粘贴到子文件夹...猜猜是什么?成功了!
  • 查看相关/重复问题stackoverflow.com/q/33002612/421049。我的回答 stackoverflow.com/a/50547331/421049 涵盖了 Windows。
  • 有谁知道如何反其道而行之。在子文件夹中有一个 repro,然后向这个 repro 添加一些父文件/文件夹?

标签: git git-log


【解决方案1】:

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

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

    混帐状态

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

    mkdir 新文件夹 mv old_folder new_folder

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

    mv new_folder/old_folder/.git new_folder/

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

【讨论】:

    【解决方案2】:

    即使内容被移动,Git 也能很好地跟踪内容,所以git mv 显然是移动文件的最佳选择,因为它们曾经属于 x,但现在它们属于 @987654324 @ 因为项目就是这样发展的。

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

    问题陈述“连同提交历史”,我将其解释为以这种方式重写历史。这可以通过

    git filter-branch --tree-filter "cd x; mkdir -p p/q; mv [files & sub-dirs] p/q" HEAD
    

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

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

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

    【讨论】:

    • 谢谢你,让我开心!我想将树的所有文件“转移”到一个新的子目录中,并将您的方法与mv * ./the-subdir 一起使用,但出现错误:“无法将-subdir 移动到其自身的子目录。我的解决方案是在内部使用mv the-subdir /tmp; mv * /tmp/the-subdir mv /tmp/the-subdir ./ tree-filter 命令。就像一个魅力。
    • 除非我 git mv [files & sub-dirs] p/q 而不是调用原始的 mv,否则我无法让它工作;在 Mac 上运行。但它最终对我来说效果很好,谢谢!
    • 我使用mv * .* the/subdir/ || true 移动文件和点文件并忽略错误消息。
    【解决方案3】:

    我可以看到这是一个老问题,但我仍然觉得有义务回答我目前对该问题的解决方案,我从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
    

    【讨论】:

    • 有了你的命令行,我得到了fatal: malformed index info -n 100644。使用git filter-branch -f --index-filter 'git ls-files -s | sed "s/&lt;old_location&gt;/&lt;new_location&gt;/" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info &amp;&amp; mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD,它适用于我的情况。不要忘记使用\/ 转义/
    • @tanguy_k 如果您使用: 作为sed 的分隔符,那么您不必担心转义/
    【解决方案4】:

    要添加到bmarguliescomment,完整的顺序是:

    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
    

    Wolfgangcomments:

    如果您使用的是 bash,则可以通过使用类似这样的扩展 glob(使用 the shopt built-in)来避免尝试将文件夹移动到自身中的问题:

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

    Captain Man 报告in the comments 不得不做:

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

    上下文:

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


    作为替代方案,spanky 提到 in the comments-k option of git mv

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

    git mv -k * target/
    

    这样可以避免“can not move directory into itself”错误。

    【讨论】:

    • 您不能将x 移动到其自身的子目录中。 git mv ... 完成这项工作,如果没有进一步的更改,则无需 git add ... 任何东西。
    • 这里如何使用git mv?它抱怨说我不能将目录移动到它自己的子目录中。
    • 如果您使用的是 bash,则可以通过使用扩展的 glob 来避免尝试将文件夹移动到自身中的问题,如下所示:shopt -s extglob; git mv !(folder) folder
    • 我刚刚了解到您可以使用git mv -k * target/ 来避免“无法将目录移入自身”错误。
    • @Spanky 有趣。我已将您的评论包含在答案中以提高知名度。
    【解决方案5】:

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

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

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

    【讨论】:

    • 注意:-k 标志不适用于 Mac OS X (Sierra),显然也不适用于 Ubuntu。
    猜你喜欢
    • 2023-03-23
    • 2023-04-07
    • 1970-01-01
    • 1970-01-01
    • 2012-11-08
    • 1970-01-01
    • 2013-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多