Git 变基没有远程的本地分支
Git rebase a local branch that doesn't have a remote
我创建了一个本地分支 (git checkout -b
),我在其中进行了一些肮脏的提交("WIP" 等)。我从来没有推过遥控器。我现在想 rename/squash 在推送之前进行一些提交:
$ git rebase -i
There is no tracking information for the current branch.
Please specify which branch you want to rebase against.
See git-rebase(1) for details
git rebase <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> feature/position-preview
然后我按照建议做:
$ git branch --set-upstream-to=origin/feature/my-feature feature/my-feature
error: the requested upstream branch 'origin/position-preview' does not exist
hint:
hint: If you are planning on basing your work on an upstream
hint: branch that already exists at the remote, you may need to
hint: run "git fetch" to retrieve it.
hint:
hint: If you are planning to push out a new local branch that
hint: will track its remote counterpart, you may want to use
hint: "git push -u" to set the upstream config as you push.
问题是,我不想 push -u
原样,因为这会导致糟糕的历史。
如何重新定位我的新本地分支?
问题是,默认情况下,不带参数的变基建议从上次推送的提交变基。然而,我们可以将它推向正确的方向:
git rebase -i <hash>
选择 <hash>
提交 就在 之前 您想要在历史记录中看到的提交。这些哈希值可以通过 git log
.
获得
Git 的文档是 much derision 的主题......有充分的理由:
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>] [<upstream> [<branch>]]
此处的参数标记为 upstream
,命令的有用 (?) 输出指的是 git branch --set-upstream-to
和 origin/<branch>
,这意味着您的本地分支必须有一个像 origin/my-feature
这样的远程分支才能工作。但事实并非如此。
git rebase
所做的是 复制 一系列提交,每次复制就像你 运行 git cherry-pick
一个特定的承诺。通过在 commit-graph 内的某个线性提交链中复制 every 提交,您可以获得复制 branch-of-development.1[=170 的效果=] 我认为以图形形式表达更有意义。例如,假设您有这个序列:
... <- o <- A <- B <-- master
\
C <- D <- E <-- my-feature
其中每个字母代表某个特定的提交——那些丑陋的 40 字符 SHA-1 数字中的一个——my-feature
的提交历史与 [=25] 的 commit-history 连接在一起=] 在提交时 o
。如果您在 master
上创建一个新分支 my-feature
,然后返回到分支 master
,进行两次提交,然后转到 [=24],就会得到这种图表=] 并进行了三次提交(每次提交的实际顺序并不重要:我只需要你在 master
上进行两次提交,然后我将它们绘制在 now 作为 first-of-those A
和 second-one B
,以及三个 my-feature
).
现在,正如开发过程中发生的那样,您可能会认为根据 master
的当前提示 C
-through-E
更有意义,即,让 C
的 parent 提交 B
.
实现此目的的一种非常手动的方法是:
$ git checkout master
$ git checkout -b new-my-feature
$ git cherry-pick <id of commit C>
$ git cherry-pick <id of commit D>
$ git cherry-pick <id of commit E>
这会创建一个新分支,new-my-feature
,并复制三个提交,2,结果如下:
C' - D' - E' <-- new-my-feature
/
... o - A - B <-- master
\
C - D - E <-- my-feature
git 构造新提交 C'
的方式是将 C
的树(关联的源快照)与其 parent 提交 [=26] 的树进行比较=],将相同的更改应用于 B
的树,并进行新提交 re-uses C
的提交消息。换句话说,提交 C'
是 C
的 "copy"。 D
中的 D'
和 E
中的 E'
也是如此。
无论如何,如果你拍了这第三张照片,然后删除my-feature
分支标签并将new-my-feature
重命名为my-feature
,你得到的是:
C' - D' - E' <-- my-feature
/
... o - A - B <-- master
旧 my-feature
上的原始三个提交是 "lost"(它们通过 "reflog" 条目保留了 30 天,但它们通常是不可见的,以避免混乱你的看法)。
git rebase
所做的是自动执行此过程:它找到要复制的提交序列,将序列复制到其他提交上,然后移动分支标签。
使用所有 not-terribly-well-documented 选项,您可以指定的是分支名称——默认是您当前的分支——以及通常有点间接地 停止复制。 "target" 提交,rebase 调用 --onto <newbase>
,默认为您为 rebase 调用 <upstream>
.
提供的参数(如果有)
它的工作方式依赖于理解其他相当重要的东西,这会影响 two-dot 表示法。
让我们再次回到原始图表,在我们做任何 cherry-picking 和 branch-label-moving 之前,即我们在 rebase 之前拥有的:
... - o - A - B <-- master
\
C - D - E <-- my-feature
现在让我们问一下git master..my-feature
中有哪些修改:
$ git rev-list master..my-feature
e59f6c2d348d465e3147b11098126d3965686098
8413a79e67177d026d2d8e1ac66451b80bb25d62
1f9e0a53489aaca7859722e037a47e93858cbc42
(这些是我编造的,但实际上使用此设置您实际上只会得到三个 SHA-1)。
我认为解释这一点的一个好方法是 color-filling / 突出显示(当然我不能在本文中这样做,所以你必须想象它)。
假设您从 my-feature
的提示开始,即提交 E
。将此提交涂成绿色:它是 "to be used"。然后,跟随其 parent 指针返回提交 D
。也将此提交涂成绿色。跟随它到 C
并涂上绿色;跟随它到 o
并将那个也涂成绿色,然后继续到 o
的 parent(s) 一直到整个图表。
然后,从master
的提示开始,即提交B
。将此提交涂成红色:"stop"。然后,跟随它的 parent 提交 A
,并将这个涂成红色。按照那个到 o
,然后用红色油漆覆盖绿色油漆:"stop"。一路跟随o
的parent(s),用红色油漆覆盖他们所有的绿色油漆。
(如果你愿意,你可以先做红色,然后做绿色,只要你不要用绿色覆盖任何红色。)
结果——git rev-list
应该列出的提交——正是那些(仍然)绿色的提交,这正是我们想要变基的三个。即使我们从提交 B
开始 "red list",master
.
的提示也是如此
所有这一切的简短版本是,为了变基,我们想要的——至少现在——是 git rebase
将 master
视为 "upstream". rebase 的 "upstream" 参数提供了 "red paint" 参数,master..my-feature
的左半部分 revision-specifier,an --onto
参数:我们想要复制提交,以便它们出现在 master
的 tip-most 提交之后。
实际上,您可以使用git branch --set-upstream-to
将master
(在您自己的私有存储库中)设置为my-feature
的"upstream"。上游不一定是像 origin/branch
这样的 remote-tracking 分支。您自己的本地分支机构在这里可以正常工作。 (但请记住,如果你确实开始共享这个分支——通过将它推送到远程 git 存储库,比如 origin
——你可能想在那个时候开始使用 remote-tracking 分支作为"upstream" 从那以后,提醒自己你正在分享,并开始一个更典型的 rebase 流程。你可以在任何时候通过做一个新的 git branch --set-upstream-to
来改变 "upstream"。 )
1单词 "branch" 是 git 文档存在问题的另一个示例。它至少有两个不同的含义。在作为脚注的段落的一部分的图表中,master
是 "branch"(我喜欢称它为 "branch label" 或 "branch name")。同样,my-feature
是一个分支。在这种情况下,这两个都简单地标记了一个特定的提交——分别为 B
和 E
。但是,每个提交都有自己指向其 parent 提交的指针:B
指向 A
,E
指向 D
,以及很快。遵循这些 parent-pointing 箭头产生的提交链也是 "branch".
停止将这样的链称为 "branch" 的确切点取决于您的观点:my-feature
可能会在提交 o
时停止,而 master
可能会继续返回 o
。 Git 在这里并没有多大帮助,尽管无论如何都没有 always-right 答案:它确实取决于您想要包含的内容。
2有一种更简单的方法,因为 git cherry-pick
可以将多个提交作为参数:
$ git cherry-pick <id> <id> <id>
例如,或:
$ git cherry-pick my-feature~3..my-feature
它使用 the gitrevisions documentation 中描述的说明符得到 git 以找到三个 ID。
我创建了一个本地分支 (git checkout -b
),我在其中进行了一些肮脏的提交("WIP" 等)。我从来没有推过遥控器。我现在想 rename/squash 在推送之前进行一些提交:
$ git rebase -i
There is no tracking information for the current branch.
Please specify which branch you want to rebase against.
See git-rebase(1) for details
git rebase <branch>
If you wish to set tracking information for this branch you can do so with:
git branch --set-upstream-to=origin/<branch> feature/position-preview
然后我按照建议做:
$ git branch --set-upstream-to=origin/feature/my-feature feature/my-feature
error: the requested upstream branch 'origin/position-preview' does not exist
hint:
hint: If you are planning on basing your work on an upstream
hint: branch that already exists at the remote, you may need to
hint: run "git fetch" to retrieve it.
hint:
hint: If you are planning to push out a new local branch that
hint: will track its remote counterpart, you may want to use
hint: "git push -u" to set the upstream config as you push.
问题是,我不想 push -u
原样,因为这会导致糟糕的历史。
如何重新定位我的新本地分支?
问题是,默认情况下,不带参数的变基建议从上次推送的提交变基。然而,我们可以将它推向正确的方向:
git rebase -i <hash>
选择 <hash>
提交 就在 之前 您想要在历史记录中看到的提交。这些哈希值可以通过 git log
.
Git 的文档是 much derision 的主题......有充分的理由:
git rebase [-i | --interactive] [options] [--exec <cmd>] [--onto <newbase>] [<upstream> [<branch>]]
此处的参数标记为 upstream
,命令的有用 (?) 输出指的是 git branch --set-upstream-to
和 origin/<branch>
,这意味着您的本地分支必须有一个像 origin/my-feature
这样的远程分支才能工作。但事实并非如此。
git rebase
所做的是 复制 一系列提交,每次复制就像你 运行 git cherry-pick
一个特定的承诺。通过在 commit-graph 内的某个线性提交链中复制 every 提交,您可以获得复制 branch-of-development.1[=170 的效果=] 我认为以图形形式表达更有意义。例如,假设您有这个序列:
... <- o <- A <- B <-- master
\
C <- D <- E <-- my-feature
其中每个字母代表某个特定的提交——那些丑陋的 40 字符 SHA-1 数字中的一个——my-feature
的提交历史与 [=25] 的 commit-history 连接在一起=] 在提交时 o
。如果您在 master
上创建一个新分支 my-feature
,然后返回到分支 master
,进行两次提交,然后转到 [=24],就会得到这种图表=] 并进行了三次提交(每次提交的实际顺序并不重要:我只需要你在 master
上进行两次提交,然后我将它们绘制在 now 作为 first-of-those A
和 second-one B
,以及三个 my-feature
).
现在,正如开发过程中发生的那样,您可能会认为根据 master
的当前提示 C
-through-E
更有意义,即,让 C
的 parent 提交 B
.
实现此目的的一种非常手动的方法是:
$ git checkout master
$ git checkout -b new-my-feature
$ git cherry-pick <id of commit C>
$ git cherry-pick <id of commit D>
$ git cherry-pick <id of commit E>
这会创建一个新分支,new-my-feature
,并复制三个提交,2,结果如下:
C' - D' - E' <-- new-my-feature
/
... o - A - B <-- master
\
C - D - E <-- my-feature
git 构造新提交 C'
的方式是将 C
的树(关联的源快照)与其 parent 提交 [=26] 的树进行比较=],将相同的更改应用于 B
的树,并进行新提交 re-uses C
的提交消息。换句话说,提交 C'
是 C
的 "copy"。 D
中的 D'
和 E
中的 E'
也是如此。
无论如何,如果你拍了这第三张照片,然后删除my-feature
分支标签并将new-my-feature
重命名为my-feature
,你得到的是:
C' - D' - E' <-- my-feature
/
... o - A - B <-- master
旧 my-feature
上的原始三个提交是 "lost"(它们通过 "reflog" 条目保留了 30 天,但它们通常是不可见的,以避免混乱你的看法)。
git rebase
所做的是自动执行此过程:它找到要复制的提交序列,将序列复制到其他提交上,然后移动分支标签。
使用所有 not-terribly-well-documented 选项,您可以指定的是分支名称——默认是您当前的分支——以及通常有点间接地 停止复制。 "target" 提交,rebase 调用 --onto <newbase>
,默认为您为 rebase 调用 <upstream>
.
它的工作方式依赖于理解其他相当重要的东西,这会影响 two-dot 表示法。
让我们再次回到原始图表,在我们做任何 cherry-picking 和 branch-label-moving 之前,即我们在 rebase 之前拥有的:
... - o - A - B <-- master
\
C - D - E <-- my-feature
现在让我们问一下git master..my-feature
中有哪些修改:
$ git rev-list master..my-feature
e59f6c2d348d465e3147b11098126d3965686098
8413a79e67177d026d2d8e1ac66451b80bb25d62
1f9e0a53489aaca7859722e037a47e93858cbc42
(这些是我编造的,但实际上使用此设置您实际上只会得到三个 SHA-1)。
我认为解释这一点的一个好方法是 color-filling / 突出显示(当然我不能在本文中这样做,所以你必须想象它)。
假设您从 my-feature
的提示开始,即提交 E
。将此提交涂成绿色:它是 "to be used"。然后,跟随其 parent 指针返回提交 D
。也将此提交涂成绿色。跟随它到 C
并涂上绿色;跟随它到 o
并将那个也涂成绿色,然后继续到 o
的 parent(s) 一直到整个图表。
然后,从master
的提示开始,即提交B
。将此提交涂成红色:"stop"。然后,跟随它的 parent 提交 A
,并将这个涂成红色。按照那个到 o
,然后用红色油漆覆盖绿色油漆:"stop"。一路跟随o
的parent(s),用红色油漆覆盖他们所有的绿色油漆。
(如果你愿意,你可以先做红色,然后做绿色,只要你不要用绿色覆盖任何红色。)
结果——git rev-list
应该列出的提交——正是那些(仍然)绿色的提交,这正是我们想要变基的三个。即使我们从提交 B
开始 "red list",master
.
所有这一切的简短版本是,为了变基,我们想要的——至少现在——是 git rebase
将 master
视为 "upstream". rebase 的 "upstream" 参数提供了 "red paint" 参数,master..my-feature
的左半部分 revision-specifier,an --onto
参数:我们想要复制提交,以便它们出现在 master
的 tip-most 提交之后。
实际上,您可以使用git branch --set-upstream-to
将master
(在您自己的私有存储库中)设置为my-feature
的"upstream"。上游不一定是像 origin/branch
这样的 remote-tracking 分支。您自己的本地分支机构在这里可以正常工作。 (但请记住,如果你确实开始共享这个分支——通过将它推送到远程 git 存储库,比如 origin
——你可能想在那个时候开始使用 remote-tracking 分支作为"upstream" 从那以后,提醒自己你正在分享,并开始一个更典型的 rebase 流程。你可以在任何时候通过做一个新的 git branch --set-upstream-to
来改变 "upstream"。 )
1单词 "branch" 是 git 文档存在问题的另一个示例。它至少有两个不同的含义。在作为脚注的段落的一部分的图表中,master
是 "branch"(我喜欢称它为 "branch label" 或 "branch name")。同样,my-feature
是一个分支。在这种情况下,这两个都简单地标记了一个特定的提交——分别为 B
和 E
。但是,每个提交都有自己指向其 parent 提交的指针:B
指向 A
,E
指向 D
,以及很快。遵循这些 parent-pointing 箭头产生的提交链也是 "branch".
停止将这样的链称为 "branch" 的确切点取决于您的观点:my-feature
可能会在提交 o
时停止,而 master
可能会继续返回 o
。 Git 在这里并没有多大帮助,尽管无论如何都没有 always-right 答案:它确实取决于您想要包含的内容。
2有一种更简单的方法,因为 git cherry-pick
可以将多个提交作为参数:
$ git cherry-pick <id> <id> <id>
例如,或:
$ git cherry-pick my-feature~3..my-feature
它使用 the gitrevisions documentation 中描述的说明符得到 git 以找到三个 ID。