将文件和目录以及提交历史记录移到子目录中


77

如何将目录和文件以及提交历史记录移到子目录?

例如:

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

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


我不了解这个问题的大部分答案,我尝试了各种方法,总是遇到错误,或者什么也没发生,所以我只剪了.git文件夹并将其粘贴到子文件夹中……猜怎么着?有效!
Ayyash

请参阅相关/重复问题stackoverflow.com/q/33002612/421049。我的答案stackoverflow.com/a/50547331/421049涵盖了Windows。
Garret Wilson

有谁知道如何相反。在子文件夹中有一个副本,然后向该副本添加一些父文件/文件夹?
Martin Krung

Answers:


78

要添加到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,就可以避免试图通过使用扩展水珠像这样的一个文件夹移动到其自身的问题(使用shopt内置的):

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

Man上尉报告 必须做的评论

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底涂mkdirrmdir它是否特别是外国)


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

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

git mv -k * target/

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


2
您不能x进入自身的子目录。git mv ...做这项工作,git add ...如果没有进一步的更改,则无需任何操作。
vonbrand

如何在这里使用git mv?它抱怨说我不能将目录移到它本身的子目录中。
Abhinav Manchanda

2
如果您使用的是bash,则可以通过使用如下所示的扩展glob避免尝试将文件夹移入自身的问题:shopt -s extglob; git mv !(folder) folder
Wolfgang

4
我刚刚了解到可以使用它git mv -k * target/来避免“无法将目录移入自身”错误。
Spanky

1
@Spanky有趣。我已将您的评论包含在答案中,以提高知名度。
VonC

45

即使内容四处移动,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整个历史记录中子目录中。

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

请注意,如果重写接触已提交过,则不应将结果推送到公共服务器。


8
谢谢,让我开心!我想将树的所有文件“移动”到一个新的子目录中,并使用您的方法,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不打电话给raw mv,否则我无法使它工作;在Mac上运行。但这最终对我来说效果很好,谢谢!
d4vidi

我曾经mv * .* the/subdir/ || true移动文件和点文件而忽略错误消息。
joeytwiddle19年

12

我可以看到这是一个古老的问题,但是我仍然有义务使用当前解决方案来回答这个问题,我是从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

这是本书中的一个示例的一种特殊化,在一种特殊情况下,我还使用相同的示例在提交历史记录中批量重命名文件。如果在子目录中移动文件:请记住在路径中使用\来转义/字符,以使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

1
通过您的命令行,我得到了fatal: malformed index info -n 100644。随着git filter-branch -f --index-filter 'git ls-files -s | sed "s/<old_location>/<new_location>/" | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info && mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD它的工作在我的情况。不要忘了逃跑/\/
tanguy_k

1
@tanguy_k如果:用作的分隔符sed,则不必担心转义/
Mateen Ulhaq '19

6

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。
朱诺·伍兹

0

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

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

    git状态

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

    mkdir new_folder mv old_folder new_folder

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

    mv new_folder / old_folder / .git new_folder /

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.