如何重置 master 并将我的分支保留在 git 中?
How do I reset master and keep my branch in git?
假设我这样做
$ 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,但我不知道。
如何在不丢失 new_branch 上的更改(foo.py、bar.py)的情况下重置 master?
Yes, foo.py and bar.py is in your new branch.
您已经从未推送到原点的主分支创建了一个分支。所以当你尝试推送 master 时你得到了错误,因为它领先于远程。
您收到该消息是因为您在本地 master 中进行了更改,但没有将它们推送到远程。
您有多种方法 "solve" 它通常取决于您的工作流程:
在一个好的工作流程中,master 的远程副本应该是好的,而 master 的本地副本只是远程副本的副本。使用此工作流程,您将再也不会收到此消息。
如果您以另一种方式工作并且应该推送您的本地更改,那么只需 git 推送原点,假设原点是您的远程
我会怎么做:
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。
一开始这可能会让人很困惑,您需要的是正确介绍 Git 如何实现分支;但此时我们将使用改造方法。 :-) 理解这一切的诀窍是 Git 的 提交 是永久不变的,但它的 分支 — 或更多确切地说,分支 名称— 是临时的,实际上大部分是无关紧要的。
在构建 new 提交时,有三件事很重要(它们是 HEAD
、index、和 work-tree),但是一旦你构建并提交了提交,它就非常永久并且很难 Git 完全失去它。不过,很容易不小心放错地方,所以让我们尽量避免这种情况。 :-)
如果我们完全忽略分支名称,我们可以绘制存储库中存在的提交的 图 。考虑到你所做的——做出两个新的提交——让我们把它们画成这样,其中圆形 o
s 代表提交,A
和 B
是你的两个 new 提交:
...--o--o--o
\
A
\
B
我们可以把它们都画在一条线上,但我想在右边留出空间来写标签。提交 A
是你的 "oops",B
是你的 "changes"。
这张图的主要值得注意之处在于每个提交 指向 (存储哈希 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 知道要将哪个名称 移动 到新提交。 (我们至少暂时需要这个,当 master
和 new_branch
都短暂地指向提交 A
时。)
你现在要做的是移动 master
back 指向最后一个无聊的 o
提交。为此,您可以使用 git reset
,它可以让您以任意方式移动名称:
git checkout master
git reset --hard origin/master
这个假设(见下面的最后一节)origin/master
确实确实指向无聊的最后一个o
提交。 git reset --hard
说:清除我当前的索引和工作树,并根据 HEAD
移动我当前的分支,以便它指向我在这里命名的提交。我们必须先git checkout master
,这样HEAD
名master
。然后 git reset
这样做:
...--o--o--o <-- master (HEAD), origin/master
\
A
\
B <-- new_branch
所以现在你的两个新提交 A
和 B
只能通过 new_branch
而不是通过 master
.
找到
(分支名称还可以为您做更多的事情。特别是,它们 保护 提交不被 Git 的 "garbage collector" 删除. 如果一个提交有任何我们可以找到它的名字,它是受保护的。如果它有 no 名字,它就不再受保护。有一些半隐藏的名字可以保护一切虽然——默认至少 30 天——以确保提交不会被意外丢弃,但是通过这些 reflog 名称找到它们很烦人,所以我们尽量不要太依赖它.
分支名称也用于 git push
,因此它们很重要。)
(我提到 "trashing" 上面的索引和工作树,这并不完全正确。git reset
命令,使用我们在这里使用它的方式,有三个工作可以做:
- 移动当前分支
- 重置索引
- 重置工作树
它总是做第一个,选择性地 添加 第二个作业,然后选择性地 添加 第三个。 --hard
模式执行全部三个,--mixed
模式执行前两个,而 --soft
模式只执行一个。为了正确理解它们,我们必须仔细查看 index 和 work-tree 的定义,我会把它留给其他人questions/answers。不过,这里的关键是您 不想 git reset --hard
直到您将所有内容都保存在提交中。)
如果没有 origin/master
(或者指向太远)怎么办?
我们在上面假设有一个方便的标签——所谓的远程跟踪分支——标识我们想要git reset
我们的master
指向。如果我们没有这个标签,或者它指向更早的提交,我们必须以其他方式找到该提交。
有很多方法可以做到这一点。最直接的方法是通过其哈希 ID。如果你 运行 git log
在某个分支上,你会看到 "on" 或 "contained in" 该分支的每个提交,通常在 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 显示所有分支和所有远程跟踪分支(以及其他所有内容:标签、"stash"、注释等). decorate 选项将标签名称附加到提交。 oneline 选项使每次提交只显示一行,而 graph 选项使 Git 尝试绘制提交图。
假设我这样做
$ 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,但我不知道。
如何在不丢失 new_branch 上的更改(foo.py、bar.py)的情况下重置 master?
Yes, foo.py and bar.py is in your new branch.
您已经从未推送到原点的主分支创建了一个分支。所以当你尝试推送 master 时你得到了错误,因为它领先于远程。
您收到该消息是因为您在本地 master 中进行了更改,但没有将它们推送到远程。
您有多种方法 "solve" 它通常取决于您的工作流程:
在一个好的工作流程中,master 的远程副本应该是好的,而 master 的本地副本只是远程副本的副本。使用此工作流程,您将再也不会收到此消息。
如果您以另一种方式工作并且应该推送您的本地更改,那么只需 git 推送原点,假设原点是您的远程
我会怎么做:
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。
一开始这可能会让人很困惑,您需要的是正确介绍 Git 如何实现分支;但此时我们将使用改造方法。 :-) 理解这一切的诀窍是 Git 的 提交 是永久不变的,但它的 分支 — 或更多确切地说,分支 名称— 是临时的,实际上大部分是无关紧要的。
在构建 new 提交时,有三件事很重要(它们是 HEAD
、index、和 work-tree),但是一旦你构建并提交了提交,它就非常永久并且很难 Git 完全失去它。不过,很容易不小心放错地方,所以让我们尽量避免这种情况。 :-)
如果我们完全忽略分支名称,我们可以绘制存储库中存在的提交的 图 。考虑到你所做的——做出两个新的提交——让我们把它们画成这样,其中圆形 o
s 代表提交,A
和 B
是你的两个 new 提交:
...--o--o--o
\
A
\
B
我们可以把它们都画在一条线上,但我想在右边留出空间来写标签。提交 A
是你的 "oops",B
是你的 "changes"。
这张图的主要值得注意之处在于每个提交 指向 (存储哈希 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 知道要将哪个名称 移动 到新提交。 (我们至少暂时需要这个,当 master
和 new_branch
都短暂地指向提交 A
时。)
你现在要做的是移动 master
back 指向最后一个无聊的 o
提交。为此,您可以使用 git reset
,它可以让您以任意方式移动名称:
git checkout master
git reset --hard origin/master
这个假设(见下面的最后一节)origin/master
确实确实指向无聊的最后一个o
提交。 git reset --hard
说:清除我当前的索引和工作树,并根据 HEAD
移动我当前的分支,以便它指向我在这里命名的提交。我们必须先git checkout master
,这样HEAD
名master
。然后 git reset
这样做:
...--o--o--o <-- master (HEAD), origin/master
\
A
\
B <-- new_branch
所以现在你的两个新提交 A
和 B
只能通过 new_branch
而不是通过 master
.
(分支名称还可以为您做更多的事情。特别是,它们 保护 提交不被 Git 的 "garbage collector" 删除. 如果一个提交有任何我们可以找到它的名字,它是受保护的。如果它有 no 名字,它就不再受保护。有一些半隐藏的名字可以保护一切虽然——默认至少 30 天——以确保提交不会被意外丢弃,但是通过这些 reflog 名称找到它们很烦人,所以我们尽量不要太依赖它.
分支名称也用于 git push
,因此它们很重要。)
(我提到 "trashing" 上面的索引和工作树,这并不完全正确。git reset
命令,使用我们在这里使用它的方式,有三个工作可以做:
- 移动当前分支
- 重置索引
- 重置工作树
它总是做第一个,选择性地 添加 第二个作业,然后选择性地 添加 第三个。 --hard
模式执行全部三个,--mixed
模式执行前两个,而 --soft
模式只执行一个。为了正确理解它们,我们必须仔细查看 index 和 work-tree 的定义,我会把它留给其他人questions/answers。不过,这里的关键是您 不想 git reset --hard
直到您将所有内容都保存在提交中。)
如果没有 origin/master
(或者指向太远)怎么办?
我们在上面假设有一个方便的标签——所谓的远程跟踪分支——标识我们想要git reset
我们的master
指向。如果我们没有这个标签,或者它指向更早的提交,我们必须以其他方式找到该提交。
有很多方法可以做到这一点。最直接的方法是通过其哈希 ID。如果你 运行 git log
在某个分支上,你会看到 "on" 或 "contained in" 该分支的每个提交,通常在 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 显示所有分支和所有远程跟踪分支(以及其他所有内容:标签、"stash"、注释等). decorate 选项将标签名称附加到提交。 oneline 选项使每次提交只显示一行,而 graph 选项使 Git 尝试绘制提交图。