首页 > 解决方案 > 使用 git 在各种提交中将文件移动到不同的文件夹

问题描述

这可能是一个菜鸟问题;使用 Android Studio 我进行了多次提交,在每次提交中创建新文件。

在最后三个提交中,新文件被放置在错误的目录中。我可以去那些提交并将它们放在正确的文件夹中并创建新的提交吗?

标签: git

解决方案


您不能更改任何现有的提交。但是您可以停止使用现有的提交。

因为提交保存了所有内容的完整状态(嗯,所有提交的内容),实际上您还可以通过检查较旧的提交来“及时返回”。

如果你给每个提交一个唯一的 ID,并按照它们的顺序绘制它们,你会得到这样的结果:

A <-B <-C <-D   <--master

也就是说,最新的提交 会D记住其先前提交或提交的ID C。同时C记住它的父母,B,它记住它的父母A。(如果A是存储库中的第一个提交,则它没有父级,因为它没有父级。否则A记住之前的任何提交A。)

Git找到这些的方式是名称 master记录了最后一次提交的哈希 ID。既然是这样D,就master发现D。既然是DrecordsC的 ID,那么 finds 就是它D自己,finds 就是C它自己,B以此类推。

因此,鉴于所有这些,假设您:

  1. 签出提交A。现在A是当前提交,您拥有所有文件,就像您制作A.

  2. 继续从复制所有文件B,然后移动其中一个。然后你做出一个新的提交,除了 B文件被移动(并且内部保存的新改进日期B是“现在”而不是“那时”)。你会得到一个新的提交,我们可以称之为B'

    A--B--C--D
     \
      B'
    

    B'is的父级A:Git 用来从每个提交向后工作到前一个提交的向后箭头,指向 commit A,就像B'points to commit一样AB和之间的区别在于B'您移动了文件(当然,它B'具有不同的 Git 哈希 ID——我们这里的字母代表 Git 为每个新提交弥补的实际又大又丑的哈希 ID)。

    但是,此图中缺少一些内容:名称指向B'什么?好吧,我们先别担心。相反,让我们继续...

  3. 现在,复制CC',沿途重命名一个文件,就像制作B'. 的父母C'将是B'

    A--B--C--D
     \
      B'-C'
    
  4. 重复D制作D'

    A--B--C--D
     \
      B'-C'-D'
    
  5. 让 Git 将 的哈希 ID 写入D'namemaster中。这是特别偷偷摸摸的部分:你现在有:

    A--B--C--D   [abandoned]
     \
      B'-C'-D'  <-- master (HEAD)
    

    也就是说,现在这个名称master标识了这个新的和改进D'的而不是旧的D

这样做的唯一问题是,如果任何其他Git 用户拥有包含提交序列的存储库副本A-B-C-D。他们可能会D在您的新D'. 如果他们没有准备好,这种过程将使他们的生活变得困难。因此,如果您确实有其他人使用此存储库的克隆,并且他们确实有这三个提交,请考虑要这样做。但是,如果没有其他人拥有该B-C-D链条,您可以自由地将它们替换为新的和改进的B'-C'-D'链。或者,如果参与该项目的其他人都期望这种重写,你仍然可以自由地去做。(如果没有其他人参与,很容易看到所有其他参与的人,他们都不会反对。)

好的,那么,我们如何才能实现这种重写呢?

简单的方法是使用git rebase -i. 这种交互式变基可让您在实际提交之前停止并更改将进入每个新提交的内容。您只需找到要保留的提交的实际哈希 ID ——在本例中为 commit A——然后在 branchmaster上运行:

git rebase -i <hash>

这会调出你最喜欢的编辑器——或者至少,你告诉 Git 的那个是你最喜欢的,通过设置core.editor——在一个包含三个pick命令的文件上。将每个更改为edit,保存指令文件,然后退出编辑器。

Git 现在将继续开始制作B'atop A,然后停止并让您编辑提交。跑:

git mv file1 sample/file1

(如果需要,首先制作目录sample),然后:

git commit --amend --no-edit

建立B'为期望的结果,然后:

git rebase --continue

这将松散地B'固定并开始复制过程C,然后停止,所以现在你git mv file2 sample/file2git commit --amend --no-edit移动git rebase --continue到复制-D步骤。像以前一样重复。

一旦更新的提交都完成并到位,git rebase将执行最后一步,将分支名称拉到master指向D'而不是D.

仅出于完整性考虑:这是中间提交的工作方式

当 Git 构建这个新的替换B'-C'-D'链时,找到每个提交的名称就是HEAD它本身。这使用了 Git 称为分离的 HEAD的模式。虽然这听起来很吓人,但这只是意味着HEADGit 始终需要的特殊名称根本没有附加到任何分支名称

例如,在变基的中间,你有:

A--B--C--D  <-- master
 \
  B'  <-- HEAD

随着更多提交的添加,您将获得:

A--B--C--D  <-- master
 \
  B'-C'  <-- HEAD

等等。当git rebase -i指令用完时——pick你更改为edits 的 s 是指令——rebase 完成,Git 将名称拉到适当的master位置,重新附加HEAD到它并为我们提供步骤 5 中显示的图表。


推荐阅读