移动一个文件夹到另一个文件夹的git命令

255

我创建了一个名为common的文件夹,其中包含一些源文件和文件夹。

现在我想将common文件夹移动到include文件夹中,使其看起来像这样:include/common

我尝试了以下操作:

  1. git add include

  2. git mv common/ include/

    但是它失败并显示以下错误:

    fatal: bad source, source=myrepo/common, destination=myrepo/include

  3. 我尝试过git mv common/ include/common,但我得到相同的错误。

有什么办法可以实现这个目标吗?

15个回答

239

Git最好的一点就是你不需要显式地跟踪文件重命名。Git会通过比较文件内容来自动发现它们的重命名。

因此,在你的情况下,不要那么辛苦:参考:Git mv文档

$ mkdir include
$ git mv common include
$ git rm -r common
$ git add include/common

运行git status命令应该会显示如下信息:

$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    common/file.txt -> include/common/file.txt
#

66
对我来说这行不通(使用Windows 7,1.7.6.msysgit.0)。Git认为旧文件已被删除且新文件已添加。 - Bart
13
@OliverF. 更正:git mv 是等效的。 - Andres Jaan Tack
45
“Git 最好的一点是”——但这个优点的缺点之一是,当你对被重命名的文件做出重大修改时,它就开始失效,因为它会将其视为删除和添加了一个新文件,所以坦率地说,我更喜欢显式的重命名支持。 - Erik Kaplun
3
如果 Git 转换行尾符,就会导致 @Bart 描述的问题。为了解决这个问题,您需要执行以下操作:git config --global core.autocrlf false。 - Mariano Dupont
3
把它分成两个提交就可以解决问题了。请执行此操作。 - Joe Phillips
显示剩余7条评论

196
 git mv common include

should work.

From the git mv man page:

git mv [-f] [-n] [-k] <source> ... <destination directory>

在第二种形式中,最后一个参数必须是一个现有的目录;给定的源文件将被移动到该目录中。成功完成后会更新索引,但更改仍然需要提交。 在移动之前不应进行任何"git add"操作。
注意: 当目录B不存在时,"git mv A B/"应该报错,但实际上没有。请参见Git 1.9/2.0(2014年第一季度)的Matthieu Moy(moy提交c57f628
Git曾经会删除尾随斜杠,并使命令等效于“git mv file no-such-dir”,这将创建文件no-such-dir(而尾随斜杠明确表示它只能是目录)。此补丁跳过了对目标路径删除尾随斜杠的操作。将带有尾随斜杠的路径传递给rename(2),并显示相应的错误消息。
$ git mv file no-such-dir/
fatal: renaming 'file' failed: Not a directory

30

命令:

$ git mv oldFolderName newFolderName

它通常运行良好。

错误“bad source…”通常表示,在上一次提交后,源目录中有一些重命名,因此git mv找不到预期的文件。

解决方案很简单-在应用git mv之前进行提交即可。


25

在运行之前,请确保您将所有更改添加到暂存区。

git mv oldFolderName newFoldername

git出现错误

fatal: bad source, source=oldFolderName/somepath/somefile.foo, destination=newFolderName/somepath/somefile.foo

如果有任何未添加的文件,我刚刚发现。


5

抱歉,我没有足够的声望来评论"Andres Jaan Tack"的回答。

我认为我的消息会被删除((但我只是想警告"lurscher"和其他遇到相同错误的人们:在进行操作时要小心。

$ mkdir include
$ mv common include
$ git rm -r common
$ git add include/common
可能会导致你在新文件夹中看不到项目的git历史记录。
我尝试了。
$ git mv oldFolderName newFolderName

got

fatal: bad source, source=oldFolderName/somepath/__init__.py, dest
ination=ESWProj_Base/ESWProj_DebugControlsMenu/somepath/__init__.py

我已经完成

git rm -r oldFolderName

并且

git add newFolderName

我发现在我的项目中找不到旧的git历史记录。至少我的项目没有丢失。现在我把项目保存在了新文件夹中,但是没有历史记录。

只是想提醒一下,如果你不想失去git历史记录,请小心使用"Andres Jaan Tack"的建议。


请确保您已经添加了所有更改。如果存在未添加的更改,Git 会失败并显示“坏源”,这是我刚刚发现的。 - Kevin Pluck

4

我曾经遇到过与git mv类似的问题,想要将一个文件夹的内容移动到另一个已存在的文件夹中,最终写了这个“简单”的脚本:

pushd common; for f in $(git ls-files); do newdir="../include/$(dirname $f)"; mkdir -p $newdir; git mv $f $newdir/$(basename "$f"); done; popd

解释

  • git ls-files: 查找所有已经检入 git 的文件(在 common 文件夹中)
  • newdir="../include/$(dirname $f)"; mkdir -p $newdir;: 在 include 文件夹内创建一个新文件夹,其目录结构与 common 相同
  • git mv $f $newdir/$(basename "$f"): 将文件移动到新创建的文件夹中

这样做的原因是 git 似乎有问题将文件移动到现有文件夹中,并且如果您尝试将文件移动到不存在的文件夹中,它也会失败(因此需要使用 mkdir -p)。

这种方法的好处是它仅涉及已经检入 git 的文件。通过仅使用 git mv 移动整个文件夹,并且该文件夹包含未暂存的更改,git 将不知道该怎么办。

移动文件后,您可能希望清理仓库以删除任何剩余的未暂存更改-只要记得先进行干运行!

git clean -fd -n

注意:如果您的文件夹/文件名中有空格,这将会导致问题! - dejoma

3

将一个目录中所有的文件移动到子目录中的另一种方法(保留git历史记录):

$ for file in $(ls | grep -v 'subDir'); do git mv $file subDir; done;


注意:如果您的文件夹/文件名中有空格,这将会导致问题! - dejoma

1

移动你的文件并使用git stage

你可以使用你喜欢的工具(命令行,图形文件浏览器)来移动你的文件,一旦你执行了操作的暂存,如@Andres所建议的那样,git会知道你是在进行移动而不是删除和创建。但如果你进行了太多修改,就像Erik Kaplun指出的那样,明确地使用git mv可能会有帮助。

一些应用程序中不使用Stage

一些GUI git工具,如GitHub Desktop,不直接提供访问stage的方法:你可以选择和取消选择修改,但它不会修改底层的git stage。你需要使用更强大的git工具,如命令行工具或真正的git GUI来暂存更改,然后GitHub Desktop也将显示操作为重命名。


1
您可以使用这个脚本
# git mv a folder and sub folders in windows 

function Move-GitFolder {
    param (
        $target,
        $destination
    )
    
    Get-ChildItem $target -recurse |
    Where-Object { ! $_.PSIsContainer } |
    ForEach-Object { 
        $fullTargetFolder = [System.IO.Path]::GetFullPath((Join-Path (Get-Location) $target))
        $fullDestinationFolder = [System.IO.Path]::GetFullPath((Join-Path (Get-Location) $destination))
        $fileDestination = $_.Directory.FullName.Replace($fullTargetFolder.TrimEnd('\'), $fullDestinationFolder.TrimEnd('\'))

        New-Item -ItemType Directory -Force -Path $fileDestination | Out-Null

        $filePath = Join-Path $fileDestination $_.Name

        git mv $_.FullName $filePath
        
    }
}

使用方法

Move-GitFolder <Target folder> <Destination folder>

这种解决方案与其他方案相比的优点在于它可以递归地移动文件夹和文件,甚至可以在不存在文件夹结构的情况下创建文件夹结构。

请将问题转化为中文。只返回翻译后的文本:问题与Windows和PowerShell无关。 - undefined

0

我遇到过类似的问题,但是在我想要移动的文件夹中有一些未被追踪的文件。

假设我有这些文件:

a/file1
a/untracked1
b/file2
b/untracked2

我想要将仅被跟踪的文件移动到子文件夹subdir中,因此目标是:

subdir/a/file1
subdir/a/untracked1
subdir/b/file2
subdir/b/untracked2

我所做的是:
  • 创建新文件夹并移动我想要移动的所有文件:mkdir tmpdir && mv a b tmpdir
  • 检出旧文件:git checkout a b
  • 创建新目录并将干净的文件夹(没有未跟踪的文件)移动到新的子目录中:mkdir subdir && mv a b subdir
  • 从子目录添加所有文件(这样 Git 只能添加已跟踪的先前的文件——这是一种 git add --update 与目录更改 技巧):git add subdir(通常这会添加未跟踪的文件——这需要创建 .gitignore 文件)
  • git status 现在只显示移动的文件
  • 将 tmpdir 中剩余的文件移动到 subdir:mv tmpdir/* subdir
  • git status 看起来像我们执行了 git mv :)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接