将代码从主分支移动到现有分支

Move code from main to an existing branch

我在 main 上有代码 myscript.py,我正在尝试将其移至现有分支 branchA

% git branch
  branchA
* main 

myscript.pymain 上,我怎样才能将它移动到 branchA 以便它不再出现在 main 上?

如果您还没有提交文件,您可以简单地移动新分支并继续:

git switch -c branchA

如果您已经提交了文件,您可以在将其移动到新分支之前将其恢复到某个提交:

git restore -s <commit> myscript.py
git switch -c branchA

代码——或者更准确地说,文件——驻留在提交.

分支——或者更准确地说,分支 namesselect 提交。更准确地说,分支名称包含 一个特定提交 的原始哈希 ID。由于该特定提交,分支中的所有其他提交都在分支中。

任何提交,一旦做出,就永远无法更改。但存储在 分支名称 中的原始哈希 ID 可以 始终 更改。事实上,这就是分支名称的全部意义:存储不断变化的哈希 ID。

当我们进行 new 提交时,Git:

  • 打包新的源快照;
  • 添加一些 元数据 ,或者关于我们现在正在进行的新提交的信息;
  • 将所有这些作为提交写出,获得一个新的、唯一的哈希 ID;和
  • 提交的哈希ID写入分支名称

因为Git在第二步添加的一个元数据是当前提交的哈希ID,存储在当时的分支名称中我们 运行 git commit,这使得 new 提交 link 回到以前的当前提交。如果我们从带有哈希 ID 的提交链开始,我们用单个大写字母表示原始哈希 ID 以保持我们的个人理智,我们可能会有这样的东西:

... <-F <-G <-H

此处 H 代表 当前 (到目前为止,最后一个!)提交的哈希 ID。分支名称 main 存储了这个原始哈希 ID,因此名称 main 指向 commit H:

... <-F <-G <-H   <-- main

同时存储在提交H的元数据包含早期提交G的原始哈希ID,所以我们说H点到 G。那是从H出来的箭头。提交 G 当然是一个提交,所以它有一个存储的哈希 ID,在本例中它指向更早的提交 F,依此类推。

现在,再说一次,任何现有提交都不能更改。我们对Git的正常使用是添加新提交,像这样:

...--G--H   <-- main, somebranch

请注意我们有两个名称——mainsomebranch——*都指向提交 H

我们运行git checkout maingit switch main,编辑代码,运行git add,运行git commit。 Git 打包一个新的快照并进行一个新的提交,我们称之为 I。因为我们已经 main 签出,所以 Git 将新的哈希 ID 写入名称 main。为了记住我们签出的是哪一个,让我们将名称 HEAD 附加到 main,并绘制新的提交:

...--G--H   <-- somebranch
         \
          I   <-- main (HEAD)

注意 main 如何移动 ,而 somebranch 没有。如果我们现在 git checkout somebranchgit switch somebranch,我们得到:

...--G--H   <-- somebranch (HEAD)
         \
          I   <-- main

来自提交 I vanishfiles,相反,我们有来自提交 H 的文件. Git 删除了提交 I 的那些——它们安全地存储在 I 快照中——并用来自 H.

的文件替换它们

我们现在可以通过一种方式来回答您的问题:

myscript.py is on main, how can I move it to branchA so that it no longer appears on main?

我们应该画你有的东西。我不确定你的 branchA selects 提交了什么,也不知道你的 main selects 提交了什么,所以我不得不猜测:你自己的绘图会更好,或者您可以 运行 git log --all --decorate --oneline --graph 或来自 Pretty Git branch graphs 的任何其他奇特命令。但是假设我们有:

       G--H   <-- branchA
      /
...--F
      \
       I   <-- main (HEAD)

让我们进一步假设所有文件都已提交(因为如果没有,您有更多选择)。

你可以简单地 运行:

git rm myscript.py
git commit

main 上进行 new 提交, 缺少 文件:

       G--H   <-- branchA
      /
...--F
      \
       I--J   <-- main (HEAD)

提交 J 不再有该文件。现在你可以 git switch branchA 切换到现有分支 branchA 并提交 H:

       G--H   <-- branchA (HEAD)
      /
...--F
      \
       I--J   <-- main

您现在可以看到来自提交 H 的所有文件,这当然意味着您 不会 看到 myscript.py。但是我们知道它永久保存在提交I中,所以我们需要告诉Git:从这个现有的提交.[=126=中获取这个保存的文件]

有多个命令可以执行此操作;我一般推荐的是 git restore,有 --source-SW 选项:

git restore --source main~1 -SW -- myscript.py

有点冗长,和旧命令做同样的事情:

git checkout main~1 -- myscript.py

即使用名称main后缀~1先查找提交Jmain),然后回退一次(~1) 提交 I。然后它找到名为 myscript.py 的文件 中提交并将该文件复制到两个地方:

  • -W 将文件复制到您的 工作树 ,您可以在其中查看和编辑它。
  • -S 将文件复制到 Git 的 暂存区 ,现在可以提交了。

git checkout 命令没有 -S-W 标志:它总是复制到两个地方。

现在你有了这个文件 并且 它已经 git add-ed,你只需要 运行 git commit 创建一个新的将更新当前 (HEAD) 分支名称的提交:

       G--H--K   <-- branchA (HEAD)
      /
...--F
      \
       I--J   <-- main

提交 K 与现有提交 H 完全一样,只是它添加了这个新文件。

请注意,这些分支中的这些提交历史。通过提交 F 的提交在两个分支 中是 。 Git 找到这些提交的方法是使用分支名称找到 last 提交然后向后工作。

还有更多选择

如果您还没有提交该文件,它目前只在您的工作树中,也许也在Git的暂存区中。请参阅 ,但请注意 git switch -c 尝试创建一个 new 分支;你想要 git switch,它使用 现有分支 .

在这里,我们可以指望由名称 branchA 标识的提交没有 包含名为 myscript.py 的文件。这在实践中变得极其复杂,尽管对于这种情况它很简单。有关所有血腥细节,请参阅 Checkout another branch when there are uncommitted changes on the current branch

以上所有内容都是关于添加更多提交。在某些情况下,我们有一些提交,但出于某种原因我们不喜欢它们。在这种情况下,我们可以——在限制范围内——告诉Git停止使用这些提交

考虑我们有的情况:

       G--H   <-- branchA
      /
...--F
      \
       I   <-- main (HEAD)

我们决定不喜欢 提交I。我们所做的是最初不理会它,并使用它将文件 myscript.py 复制到我们添加到 branchA:

的新提交中
       G--H--J   <-- branchA (HEAD)
      /
...--F
      \
       I   <-- main

使用相同的 git restore 技术(虽然这次我们使用 --source main,而不是 --source main~1,因为我们没有在 [=267] 上进行新的提交=] main).

然后,不过,我们 git switch 回到 main 和 运行:

git reset --hard HEAD~1

或:

git reset --hard main~1

~1 后缀与以前做同样的事情:找到提交,然后后退一步。这定位提交 F。我们可以 运行 git log 并使用鼠标剪切和粘贴提交 F:

的原始哈希 ID
git reset --hard <hash>

在这里。接下来,git reset:

  • 分支名称 移动到我们 select 编辑的提交;
  • 重置Git的索引/暂存区;和
  • 使您的工作树匹配(由于 --hard)。

这给我们留下了:

       G--H--J   <-- branchA
      /
...--F   <-- main (HEAD)
      \
       I   ???

请注意如何不再有任何 name 可以用来 find 提交 I。如果你记住了它的哈希 ID,或者把它写在纸上,或者什么的,你仍然可以通过这种方式找到它。 Git 提供了其他方法来取回它,默认至少 30 天。但大多数情况下,它似乎消失了,就好像它从未存在过一样。

所以现在看起来 lastmain 上的提交是提交 F,而不是提交 I。只要没有其他人提交 I——你从未将它发送到其他 Git 存储库——就可以安全地“摆脱”这样的提交。如果你 did 将它发送到某个地方,它可能会从那里返回,因为 Git 真的很喜欢 add 提交并且会像这样传播它们病毒,给一半的机会。因此,一旦提交(通常使用 git push,通常是 git push),“倒回”或“删除”提交通常是不明智的。