我有这个:
git branch # I am on a feature branch "X"
git fetch origin dev;
git checkout -b "${new_branch}" "origin/dev"
问题是最后一个命令签出了一个以X为基础而不是“ origin / dev”为基础的新分支。为什么会那样做?我的印象是git checkout -b foo bar
将使用bar作为“基础”签出一个名为foo的新分支(请更正我的术语)。为什么那样行不通?
我使用的是git版本:2.17.1
也许我应该改用它:
git checkout -b "${new_branch}" --track origin/dev
?
更新:可能发生的情况是原始/开发人员正在使用本地要素分支中的更改进行更新。因此,我使用的第一个命令确实使用origin / dev作为基础,这是因为Origin / dev正在从功能分支中查看更新,因为已设置跟踪...
答案 0 :(得分:2)
您在自己的回答中提到:
git checkout -b "${new_branch}" "origin/dev"
表示新分支将跟踪origin / dev ...
这是正确的,尽管它使用了非常严重的重载单词“ track”。在过去的几年中,在我看来,Git文档一直在(缓慢地)远离这个词,这可能是一个好主意(尽管它仍然存在于--track
和--no-track
选项中! )。
更合适/更好/更现代的术语是,新分支将origin/dev
设置为其上游。每个分支名称可以具有一个上游设置。该上游仅仅是分支的名称(例如master
)或远程跟踪名称(例如origin/master
)。此设置及其实际值的存在会影响git status
报告状态,git merge
和git rebase
在不使用其他参数的情况下的行为,以及git pull
和git push
的行为没有其他参数。 1
或者,分支可以上游没有 no 。如果分支上游没有 no ,则git status
不会报告该分支与其不存在的上游的比较,git merge
和git rebase
需要更多参数,依此类推。请注意,上游设置(或缺少上游设置)与分支名称指向的提交哈希无关。
(另请参见Make an existing Git branch track a remote branch?)
我所做的修复工作是使用--no-track,就像这样:
git branch --no-track "${new_branch}" "remotes/origin/dev" git checkout "${new_branch}"
这可以完成工作,但是您也可以使用以下方法完成工作:
git checkout --no-track -b "${new_branch}" origin/dev
1 这并不意味着要列出完整的列表。特别是git branch -vv
还会查看上游设置,并且git for-each-ref
和git rev-parse
能够提取分支的上游设置。此外,Git的某些部分无需费心去验证上游设置的名称(如果已设置)是否有效,但是Git的其他部分却可以;所以这里有很多可能性。
git branch
和git checkout
的精确默认操作有些复杂。 Git试图提供帮助,但结果只是一团糟。
我认为记住分支名称充当指向一个特定提交的指针会有所帮助。 Git将此称为分支的 tip commit 。您可以选择整个存储库中的任何现有提交,然后在其中附加一个分支名称。例如,给定这样的提交链:
...--E--F--G
\
H--I--J <-- master (HEAD)
(在图形中有一个无法解释的纽结),我们可以查找提交G
的实际哈希ID,然后在此处附加一个新的分支名称。假设G
的实际哈希ID以491ab94
开头,因此我们运行:
git branch marker 491ab94
结果如下:
...--E--F--G <-- marker
\
H--I--J <-- master (HEAD)
现在有两个分支,以前只有一个。新分支名为marker
,标识提交G
。现有的master
不变:它将继续标识落实J
。
无论何时创建新的分支名称,都必须回答有关Git的问题:该分支名称应标识哪个现有提交?在这里,我们通过其哈希ID选择了G
。由于哈希ID不是名称,因此无法将该ID 设置为新分支的上游。
如果您省略git branch
中的哈希ID,则Git默认使用HEAD
:
git branch m2
由于HEAD
当前已附加到master
,因此m2
指向与master
相同的提交:
...--E--F--G <-- marker
\
H--I--J <-- master (HEAD), m2
在这种情况下,m2
的上游默认未设置。
您还可以使用git checkout
创建新的分支名称。使用git branch
和git checkout
的主要区别在于git checkout
还将HEAD
附加到新分支,从而移动(签出)另一个如果需要的话,提交比以前的当前提交要大。例如:
git checkout -b m2
(而不是git branch m2
)根本不需要移动,也不移动,但是确实重新附着了HEAD
,产生了:
...--E--F--G <-- marker
\
H--I--J <-- master, m2 (HEAD)
提交J
仍然是已签出的提交,但是现在HEAD
已附加到名称m2
上。和以前一样,m2
没有上游。
我们刚刚看到,如果让Git默认为HEAD
,则Git 不会为新分支设置上游。此外,如果您通过哈希ID 选择特定的提交,则Git 不会设置上游。但是有时候,如果您通过 name 选择特定的提交,则Git 会设置上游。
所以:当 Git设置上游时?好吧,考虑一下我们用于提交的名称的种类。其中一些是我们自己的分支名称,例如上面的master
和m2
和marker
。其中一些是标签名称,例如v1.2
。有些是远程跟踪名称,例如origin/master
或origin/develop
。哪些名称作为上游名称最有意义?
如果您说“远程跟踪名称”,那么恭喜您:您和Git的想法相同!如果不是,那么,也许这就是为什么您始终与Git交战。 :-)无论如何,使用远程跟踪名称作为起点告诉Git:我不仅要您创建这个新分支,还希望您将此远程跟踪名称设置为分支的上游。
您可以使用--track
明确地做同样的事情,在这种情况下,您可以让Git将上游设置为您自己的分支之一。例如,要在创建develop
时将master
的上游设置为develop
,可以使用:
git branch --track develop master
或:
git checkout --track -b develop master
如果您在所有时间内都不喜欢--track
的行为(即上游设置),则可以始终将--no-track
添加到命令中,或配置Git 使用
git config branch.autoSetupMerge false
如果您真的很喜欢 --track
,并且希望即使在使用本地分支名称作为起点时也会发生这种情况,您也可以配置Git来做到这一点:>
git config branch.autoSetupMerge always
如果使用--track
和--no-track
选项,请覆盖always
或false
或true
的配置默认值。如果您尚未配置branch.autoSetupMerge
,则Git会假装将其设置为true
,这就是我们上面概述的内容:如果名称是远程跟踪,则默认为--track
名称。
您随时可以使用git branch --set-upstream-to
或git branch --unset-upstream
随时更改或删除任何分支的上游。因此,您对--track
或--no-track
或branch.autoSetupMerge
做的任何事情 meant 都会很方便。将此设置为您觉得最方便的 。
上面,我说过创建一个新分支需要选择一个开始提交。这是事实,但几乎也是谎言。每个新的完全空的存储库中都有一个极端的情况。考虑:
$ mkdir newrepo
$ cd newrepo
$ git init
Initialized empty Git repository in ...
此时,您没有 个提交,git branch
显示您也没有没有分支。但是,git status
告诉您您在分支master
上。怎么会这样?
$ git status
On branch master
No commits yet
nothing to commit (create/copy files and use "git add" to track)
答案是您所在的分支名称不存在。尽管这听起来有些矛盾,但这实际上只是Git中的一个特例。您进行的下一个提交将是一个 root 提交:没有父项的提交。创建此提交的动作将产生一个提交哈希ID。该提交哈希ID将创建分支。
因此,您所在的分支(Git已将名称保存在HEAD
中)不存在,并且您无法设置其上游:
$ git branch --set-upstream-to=origin/master
fatal: branch 'master' does not exist
您必须首先通过提交来创建分支。分支存在后,然后您可以设置其上游。
对于新的空存储库,这是正确的,因为还没有提交。但是对于使用git checkout --orphan
创建的任何分支名称也是如此,它具有相同的技巧:它将新的分支 name 写入HEAD
,但实际上并没有创建分支。
这归结为分支创建是通过提交来进行的。因此,对于这种特殊情况(实际上还不存在的孤立分支,或新的空存储库中的master
分支),您可以“选择”分支将指向的提交,而不用查看现有的提交提交,但通过创建一个 new 提交,并在此过程中说:此新提交是我选择分支名称指向的位置。新提交是一个根提交(没有父项)并且分支现在存在,并且只有Git才能设置其上游。
因此,对于孤立分支,在分支实际存在之前,您无法设置上游。
答案 1 :(得分:1)
我的猜测是,该命令是
git checkout -b "${new_branch}" "origin/dev"
表示新分支将跟踪origin / dev,这意味着origin / dev将使用本地更改进行更新。
我所做的修复工作是使用--no-track,就像这样:
git branch --no-track "${new_branch}" "remotes/origin/dev"
git checkout "${new_branch}"
我还没有完全验证它是否有效,但是到目前为止。有点恶梦。
答案 2 :(得分:1)
我创建了这个别名来执行此任务:
git config --global alias.nb '!bash -c "git fetch --prune; git checkout -b $1 --no-track ${2-origin/dev}" -'
您将使用以下命令运行它:
$ git nb <branch name> [<source commitish>]
它使用prune执行完全读取,并接受两个参数,第一个是新的分支名称,第二个是源分支,但如果未提供,则默认为origin / dev。
使用--track
可以将您的功能直接推送到origin / dev。
还请记住,分支只是指针,因此该命令可以读为“在<branch name>
所引用的提交处创建名为origin/dev
的新分支指针,而无需设置远程跟踪并签出新的分支”