【问题标题】:How do I reset master and keep my branch in git?如何重置 master 并将我的分支保留在 git 中?
【发布时间】:2017-03-28 20:02:58
【问题描述】:

假设我这样做

$ git checkout master
$ touch foo.py
$ git commit -m "oops" foo.py
$ git checkout -b new_branch
$ touch bar.py
$ git commit -m "changes" bar.py

现在,当我尝试在 new_branch 上推回更改时,我得到了

Local branch 'master' is ahead of remote branch 'origin/master'

如何在不丢失 new_branch 上的更改(foo.py、bar.py)的情况下重置 master?

我阅读了git reset page,它看起来可能涉及--keep,但我无法确定。

【问题讨论】:

  • 您的意思是要重置您的本地 master 分支,使其不再位于 origin/master 之前?
  • @Chrs 是的,正确。

标签: git git-reset


【解决方案1】:

一开始这可能会让人很困惑,而您需要的是对 Git 如何实现分支的正确介绍;但此时我们将使用改造方法。 :-) 理解这一切的诀窍在于 Git 的 commits 是永久不变的,但它的 分支——或者更准确地说,分支 names——是暂时的,实际上大多是无关紧要的。

在构建 new 提交时,有三件事很重要(它们是 HEADindexwork-tree em>),但是一旦您构建并提交了提交,它就非常永久,并且很难让 Git 完全失去它。不过,不小心放错位置很容易,所以让我们尽量避免这种情况。 :-)

如果我们完全忽略分支名称,我们可以为您的存储库中存在的提交绘制一个图表。鉴于你所做的——创建两个新的提交——让我们像这样绘制它们,其中圆形 os 代表提交,AB 是你的两个 new 提交:

...--o--o--o
            \
             A
              \
               B

我们可以将它们全部画在一条线上,但我想在右边留出空间来写标签。提交A 是您的“哎呀”,B 是您的“更改”。

关于这个图形绘制的主要值得注意的事情是每个提交指向(存储哈希ID)它的前一个提交。这意味着提交B 指向提交A。提交 A 指向下一个最近的提交,该提交指向更远的地方,依此类推。

现在我们添加标签——分支名称。最后一个无聊的提交o 仍然可能有一个标签origin/master。提交A 有标签master,提交B 有标签new_branch,所以让我们把它们画进去:

...--o--o--o   <-- origin/master
            \
             A   <-- master
              \
               B   <-- new_branch (HEAD)

这就是分支名称和为您所做的:它们是提交的指针;他们会为您记住每个提交的大而丑陋的哈希 ID。

当您在某个分支上并进行 new 提交时,分支 name 会随之而来。特殊名称 HEAD 会记住您所在的分支,以便 Git 知道将哪个名称 移动 到新提交。 (我们至少暂时需要这个,当masternew_branch 都短暂地指向提交A。)

您现在要做的是将master back 移动到最后一个无聊的o 提交。为此,您可以使用git reset,它可以让您以任意方式移动名称:

git checkout master
git reset --hard origin/master

假设(参见下面的最后一节)origin/master 确实确实指向了最后一个无聊的 o 提交。 git reset --hard 说:清除我当前的索引和工作树,并根据HEAD 移动我的当前分支,以便它指向我在这里命名的提交。我们必须@987654351 @first,所以 HEAD 命名为 master。然后git reset 这样做:

...--o--o--o   <-- master (HEAD), origin/master
            \
             A
              \
               B   <-- new_branch

所以现在您的两个新提交 AB 只能通过 new_branch 而不是通过 master 找到。

(分支名称还可以为您做更多的事情。特别是,它们保护提交不会被 Git 的“垃圾收集器”删除。如果提交有任何我们可以找到的名称它,它是受保护的。如果它有 no 名称,它就不再受保护。有一些半隐藏的名称可以保护所有内容一段时间(默认情况下至少 30 天)以确保提交不会不会被意外丢弃,但是通过这些 reflog 名称找到它们很烦人,所以我们尽量不要太依赖它。

分支名称也用于git push,因此它们很重要。)

(我在上面提到“破坏”索引和工作树,这并不完全正确。git reset 命令,使用我们在这里使用它的方式,它可以做三个工作:

  • 移动当前分支
  • 重置索引
  • 重置工作树

它总是做第一个,可选地添加第二个工作,然后可选地添加第三个​​工作。 --hard 模式执行所有三个,--mixed 模式执行前两个,--soft 模式仅执行一个。为了正确理解它们,我们必须仔细查看 indexwork-tree 的定义,我将把它留给其他 SO 问题/答案。不过,这里的关键是您想要git reset --hard,直到您将所有内容都保存在提交中。)

如果没有origin/master(或者它指向太远)怎么办?

我们假设,上面有一个方便的标签——一个所谓的远程跟踪分支——标识我们想要git resetmaster 指向的提交。如果我们没有这个标签,或者它指向更早的提交,我们必须以其他方式定位该提交。

有很多方法可以做到这一点。最直接的方法是通过它的哈希 ID。如果你在某个分支上运行git log,你会看到每个提交都“打开”或“包含在”该分支中,通常按照 Git 通常的向后顺序。例如,我们可能会在git log 看到提交 B 及其大而丑陋的哈希 ID,然后提交 A 及其哈希 ID,然后是无聊的提交 o 及其大而丑陋的哈希 ID。

我们可以使用原始哈希 ID:

git checkout master && git reset --hard <hash-id>

如果我们没有标签。如果我们确实有标签,我们可能应该只使用它。

您可能记得通过git log 向“A DOG”寻求帮助:

git log --all --decorate --oneline --graph

all 使 Git 显示所有分支和所有远程跟踪分支(以及其他所有内容:标签、“存储”、注释等)。 decorate 选项将标签名称附加到提交。 oneline 选项使每次提交的输出仅显示一行,而 graph 选项使 Git 尝试绘制提交图。

【讨论】:

  • 谢谢,我会接受这个答案。我没有测试它,因为我用另一种(更老套的)方式解决了我的问题,但这就是我想要的。
  • @torek,很好的解释,分支与提交无关,我想知道为什么没有任何 git 指南这样解释。它是新颖的还是您在其他地方看到过这样的解释?
  • @Pacerier:并不是说它们完全不相关:分支名称让我们(和 Git)find 特定的提交。但是一旦提交被找到,名字就不再重要了:那些被找到的提交会找到之前的提交。我们只需要分支名称即可找到 last 提交,为此,Git 实际上将“最后”提交定义为“名称指向的那个”。
  • 我不确定这个公式的确切来源。我用它,Think Like (a) Git 用它。诀窍是强调它恰到好处,但不要过多。
【解决方案2】:

如何在不丢失 new_branch 上的更改(foo.py、bar.py)的情况下重置 master?

是的,foo.py 和 bar.py 在你的新分支中。

  • 您已从 master 创建了一个未推送到源的分支。因此,当您尝试推送主服务器时,您会收到错误,因为它在远程之前。

  • 您收到该消息是因为您在本地 master 中进行了更改,但您没有将它们推送到远程。

您有多种“解决”方法,通常取决于您的工作流程:

  • 在良好的工作流程中,您的 master 远程副本应该是好的,而您的本地 master 副本只是远程副本的副本。使用此工作流程,您将永远不会再收到此消息。

  • 如果您以其他方式工作并且应该推送您的本地更改,那么只需 git push origin 假设 origin 是您的远程

我将如何做:

git checkout master
git create -b la_lalalla
touch foo.py bar.py
git add drama.py bar.py
git commit -m "Drama commit"

将分支合并到你的master,这取决于你是做merge还是做rebase,然后,

git push origin master. 

您已将两个文件添加到 origin/master。

【讨论】:

  • 这只是再次重做所有更改,并完全忽略 new_branch。但实际上我的提交中有 50 个文件,有些是新的,有些是更改的。我不希望仔细重新执行所有更改,我想使用 new_branch 上的信息。
猜你喜欢
  • 2021-06-06
  • 2017-07-10
  • 1970-01-01
  • 2019-12-16
  • 2016-04-30
  • 2012-02-04
  • 1970-01-01
  • 2023-02-11
  • 1970-01-01
相关资源
最近更新 更多