git 将文件和目录与提交历史一起移动到子目录中

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18667308/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 16:53:57  来源:igfitidea点击:

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

gitgit-log

提问by rst.ptl

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]

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

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

回答by VonC

To add to bmargulies's comment, the complete sequence is:

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

Try it first to see a preview of the move:

首先尝试查看移动的预览:

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


Wolfgangcomments:

沃尔夫冈评论

If you're using bash, you can avoid the issue of trying to move a folder into itself by using an extended glob like so (using the shoptbuilt-in):

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

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


Captain Manreports in the commentshaving to do:

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

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

Context:

语境:

I am on Windows with Cygwin.
I just realized I did the shopt -s extglobexample wrong so my way may not have be necessary, but I typically do use zsh instead of bash, and it didn't have the command shopt -s extglob(though I'm sure there is an alternative), so this approach should work across shells (subbing in your shell's mkdirand rmdirif it's especially foreign)

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



As an alternative, spankymentions in the commentsthe -koption of git mv:

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

Skip move or rename actions which would lead to an error condition.

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

git mv -k * target/

That would avoid the "can not move directory into itself" error.

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

回答by PiQuer

Git does a very good job to track content even if it is moved around, so git mvis clearly the way to go if you move files because they used to belong in x, but now they belong in x/p/qbecause the project evolved that way.

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

Sometimes, however, there is a reason to move files to a subdirectory throughout the history of a project. For example if the files have been put somewhere by mistake and some more commits have been made since, but the intermittent commits don't even make sense with the files in the wrong place. If all that happened on a local branch, we want to clean up the mess before pushing.

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

The question states "along with commit history", which I would interpret as rewriting the history in exactly that way. This can be done with

问题说明“连同提交历史”,我将其解释为完全以这种方式重写历史。这可以用

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

The files then appear in the p/qsubdirectory throughout the history.

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

The tree filter is well suited for small projects, its advantage is that the command is simple and easy to understand. For large projects this solution does not scale very well, if performance matters then consider to use an index filter as outlined in this answer.

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

Please note that the result should not be pushed to a public server if the rewrite touches commits which were already pushed before.

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

回答by peroyhav

I can see this is an old question, but I still feel obliged to answer With my current solution to the problem, that I have derived from one of the examples in the git book. In stead of using an inefficient --tree-filterI move the files directly on the index With an --index-filter.

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

this is a specialization of one of the examples from the book I've also used the same example to mass rename files in the commit history in one special case. if moving files in subdirectories: remember to escape the / character in the paths With a \ to make the sed command work as expected.

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

Example:

例子:

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

to move the b directory to the project root:

将 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

Result:

结果:

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

回答by Jeremy Hutchins

The git mv command is the simplest way. However, at least on my Linux box, I needed to supply the -k flag to avoid getting an error back that a folder cannot be moved into itself. I was able to perform the action by using...

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

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

As a warning, this will skip allmoves which would lead to an error condition, so you will probably want to check that everything worked properly after the move and before a commit.

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