使用git在各种提交中将文件移动到其他文件夹

时间:2019-01-18 06:30:38

标签: git

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

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

  • 提交4->将/ file3移至/ sample / file3
  • 提交3->将/ file2移至/ sample / file2
  • 提交2->将/ file1移至/ sample / file1
  • 提交1->一切正常

2 个答案:

答案 0 :(得分:1)

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

由于提交保存了所有内容的完整状态(嗯,所有提交的内容),实际上,您还可以通过检出较早的提交来“回到过去”。

如果给每个提交一个唯一的ID,并按其顺序将其绘制出来,则会得到以下内容:

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

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

Git查找 的方式是 name master记录 last 提交的哈希ID。由于那是D,因此master找到D。由于D记录了C的ID,因此D本身会找到C,而B依此类推。

因此,考虑到所有这些,假设您:

  1. 检出提交A。现在A是当前的提交,并且您拥有制作A时的所有文件。

  2. 复制来自B的所有文件,然后移动其中之一。然后,您进行与 B相似的新提交,除了文件被移动(并且进行new-proved-B的内部保存日期为“现在”)而不是“然后返回”)。您将获得一个新的提交,我们可以将其称为B'

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

    B'的父级是A:Git用于从每次提交到之前的提交向后工作的向后箭头,指向提交A,就像B指向提交A的点。 BB'之间的区别在于,您移动了文件(当然,B'具有不同的Git哈希ID-我们的字母代表实际的大丑陋哈希) Git为每次新提交构成的ID。

    不过,该图中缺少一些内容: name 指向B'的含义是什么?好吧,我们现在不必担心。相反,让我们继续...

  3. 现在,将C复制到C',并像创建B'一样重命名一个文件。 C'的父级将是B'

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

    A--B--C--D
     \
      B'-C'-D'
    
  5. 已将Git D'的哈希ID写入名称master中。这是特别偷偷摸摸的部分:您现在拥有:

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

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

执行所有这些操作的唯一问题是,如果有任何 other Git用户具有其提交顺序为A-B-C-D的存储库副本。他们可以在D之上而不是在新的D'之上构建自己的提交。如果他们还没有做好准备,这种过程将使他们的生活变得困难。因此,如果您有其他人使用此存储库的克隆,而他们具有这三个提交,请考虑执行任何此操作。但是,如果没有其他人拥有B-C-D链,则可以随意将它们替换为新的和经过改进的B'-C'-D'链。或者,如果参与此项目的其他所有人都希望进行这种重写,那么您仍然可以自由进行。 (如果没有其他人参与,很容易看到所有其他人参与其中,没有人会反对。)

好,那么,我们如何实现这种重写?

最简单的方法是使用git rebase -i。这个 interactive rebase 可让您在实际进行提交之前停止并更改每个新提交中要包含的内容。您只需找到要保留的提交的实际哈希ID(在这种情况下,提交A),然后在分支master上运行:

git rebase -i <hash>

这会在具有三个core.editor命令的文件上显示您喜欢的编辑器,或者至少通过设置pick告诉您Git是您喜欢的编辑器。分别将其更改为edit,保存说明文件,然后退出编辑器。

Git现在将开始在B'上进行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-editgit rebase --continue移至copy-D步骤。像以前一样重复。

一旦更新的提交全部完成并就位,git rebase将执行最后一步,将分支名称master移至指向D'而不是{{1} }。

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

虽然Git正在构建这个新的替代D链,但查找这些提交中的每个提交的名称本身就是B'-C'-D'。这使用Git调用分离式HEAD 的模式。虽然听起来很吓人,但这仅意味着Git始终需要的特殊名称HEAD根本没有附加到任何分支名称

例如,在rebase的中间,您有:

HEAD

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

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

,依此类推。当A--B--C--D <-- master \ B'-C' <-- HEAD 的指令用完时(您已更改为git rebase -i的{​​{1}}是指令),完成了基准确定,Git将名称pick拖入到位,重新-将edit附加到它上,并获得步骤5中所示的图。

答案 1 :(得分:-1)

它是在评论中建议的,但值得回答。 您无法编辑过去提交的目录, 通常在这种情况下,您应该更喜欢更改目录并提交更改。除非您真的希望在过去的提交中这样做,否则您将需要还原到要更改的第一个提交,并在重新提交所有内容的同时开始更改它。

要恢复为提交,您可以执行git reset --hard <commit sha1>或代替sha1,对当前提交的第三次提交使用HEAD~3。 然后重新提交您的更改。

如果您担心丢失所做的更改,则可以进行一次可修改的提交,先进行一次提交以返回到某个提交,然后再一次提交您的更改,反向提交,然后更改目录,然后再提交提交,等等。

git revert HEAD~3 更改目录。 git commit -a -f "directory fix1"

git revert HEAD~2 更改目录。 git commit -a -f "directory fix2"

git revert HEAD~1 更改目录。 git commit -a -f "directory fix3"