合并、更新和拉取 Git 分支,无需使用签出
我工作的项目有 2 个分支 A 和 B。我通常在分支 A 上工作,并合并分支 B 中的内容。对于合并,我通常会这样做:
git merge origin/branchB
但是,我还想保留分支的本地副本B,因为我有时可能会在不先与分支 A 合并的情况下检查分支。为此,我会这样做:
git checkout branchB
git pull
git checkout branchA
有没有一种方法可以在一个命令中执行上述操作,而无需来回切换分支?我应该使用 git update-ref 吗?如何?
I work on a project that has 2 branches, A and B. I typically work on branch A, and merge stuff from branch B. For the merging, I would typically do:
git merge origin/branchB
However, I would also like to keep a local copy of branch B, as I may occasionally check out the branch without first merging with my branch A. For this, I would do:
git checkout branchB
git pull
git checkout branchA
Is there a way to do the above in one command, and without having to switch branch back and forth? Should I be using git update-ref
for that? How?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(20)
简短的答案
只要您正在进行快进合并,那么您可以简单地使用
示例:
虽然 Amber 的答案 也适用于快进情况,以这种方式使用 git fetch 比仅仅强制移动分支引用更安全,因为 git fetch<只要您不在引用规范中使用
+
,/code> 就会自动防止意外的非快进。长答案
如果不先检查 A,则无法将分支 B 合并到分支 A,否则会导致非快进合并。这是因为需要工作副本来解决任何潜在的冲突。
但是,在快进合并的情况下,这是可能的,因为根据定义,此类合并永远不会导致冲突。要在不先签出分支的情况下执行此操作,您可以将 git fetch 与 refspec 结合使用。
如果您签出另一个分支
feature
,则以下是更新master
(不允许非快进更改)的示例:此用例非常常见,因此您将可能想在你的 git 配置文件中为其创建一个别名,如下所示:
这个别名的作用如下:
git checkout HEAD
:这会将你的工作副本放入分离头中状态。如果您想在签出 master 的同时更新它,这会非常有用。我认为有必要这样做,因为否则master
的分支引用将不会移动,但我不记得这是否真的是我的想法。git fetchupstreammaster:master
:这会将您的本地master
快进到与upstream/master
相同的位置。git checkout -
签出您之前签出的分支(这就是-
在本例中的作用)。用于(非)快进合并的 git fetch 语法
如果您希望 fetch 命令在更新为非快进时失败,那么您只需使用 如果您想允许非快进更新
,则可以在 refspec 的前面添加一个
+
:请注意,您可以将本地存储库作为“远程”参数传递使用
.
:文档
来自
git fetch
文档的 解释这个语法(强调我的):另请参阅
Git 签出和合并,无需触及工作树
< a href="https://stackoverflow.com/questions/3408532/merging-without-change-the-working-directory">合并而不更改工作目录
The Short Answer
As long as you're doing a fast-forward merge, then you can simply use
Examples:
While Amber's answer will also work in fast-forward cases, using
git fetch
in this way instead is a little safer than just force-moving the branch reference, sincegit fetch
will automatically prevent accidental non-fast-forwards as long as you don't use+
in the refspec.The Long Answer
You cannot merge a branch B into branch A without checking out A first if it would result in a non-fast-forward merge. This is because a working copy is needed to resolve any potential conflicts.
However, in the case of fast-forward merges, this is possible, because such merges can never result in conflicts, by definition. To do this without checking out a branch first, you can use
git fetch
with a refspec.Here's an example of updating
master
(disallowing non-fast-forward changes) if you have another branchfeature
checked out:This use-case is so common, that you'll probably want to make an alias for it in your git configuration file, like this one:
What this alias does is the following:
git checkout HEAD
: this puts your working copy into a detached-head state. This is useful if you want to updatemaster
while you happen to have it checked-out. I think it was necessary to do with because otherwise the branch reference formaster
won't move, but I don't remember if that's really right off-the-top of my head.git fetch upstream master:master
: this fast-forwards your localmaster
to the same place asupstream/master
.git checkout -
checks out your previously checked-out branch (that's what the-
does in this case).The syntax of
git fetch
for (non-)fast-forward mergesIf you want the
fetch
command to fail if the update is non-fast-forward, then you simply use a refspec of the formIf you want to allow non-fast-forward updates, then you add a
+
to the front of the refspec:Note that you can pass your local repo as the "remote" parameter using
.
:The Documentation
From the
git fetch
documentation that explains this syntax (emphasis mine):See Also
Git checkout and merge without touching working tree
Merging without changing the working directory
不,没有。签出目标分支是必要的,以便您可以解决冲突等问题(如果 Git 无法自动合并它们)。
但是,如果合并是快进的,则不需要检查目标分支,因为您实际上不需要合并任何内容 - 您所要做的就是更新分支以指向新头参考号您可以使用 gitbranch -f 来完成此操作:
将更新branch-b以指向branch-a的头部。
-f
选项代表--force
,这意味着branch-b
将被覆盖。警告:更安全的选择是使用
git fetch
,它只允许快进。这个方法可以像这样使用:
或更短地
强制更新分支,而不检查它(假设它们在变基后是否有分歧)
No, there is not. A checkout of the target branch is necessary to allow you to resolve conflicts, among other things (if Git is unable to automatically merge them).
However, if the merge is one that would be fast-forward, you don't need to check out the target branch, because you don't actually need to merge anything - all you have to do is update the branch to point to the new head ref. You can do this with
git branch -f
:Will update
branch-b
to point to the head ofbranch-a
.The
-f
option stands for--force
, which meansbranch-b
will be overwritten.Caution: A safer option is using
git fetch
which will only allow a fast-forward.This method can be used like so:
or shorter
To force update a branch, without checking it out (say if they have diverged after rebase)
正如 Amber 所说,快进合并是唯一可以做到这一点的情况。可以想象,任何其他合并都需要经历整个三向合并、应用补丁、解决冲突交易——这意味着需要有文件。
我碰巧有一个脚本正好用于此目的:在不接触工作树的情况下进行快进合并(除非您合并到 HEAD 中)。它有点长,因为它至少有点健壮 - 它检查以确保合并将快进,然后在不检查分支的情况下执行它,但产生与您相同的结果 - 你看到
diff --stat
更改摘要,并且引用日志中的条目与快进合并完全相同,而不是使用branch -f
。如果将其命名为git-merge-ff
并将其放入 bin 目录中,则可以将其作为 git 命令调用:git merge-ff
。PS如果有人发现该脚本有任何问题,请发表评论!这是一项写完就忘记的工作,但我很乐意改进它。
As Amber said, fast-forward merges are the only case in which you could conceivably do this. Any other merge conceivably needs to go through the whole three-way merge, applying patches, resolving conflicts deal - and that means there need to be files around.
I happen to have a script around I use for exactly this: doing fast-forward merges without touching the work tree (unless you're merging into HEAD). It's a little long, because it's at least a bit robust - it checks to make sure that the merge would be a fast-forward, then performs it without checking out the branch, but producing the same results as if you had - you see the
diff --stat
summary of changes, and the entry in the reflog is exactly like a fast forward merge, instead of the "reset" one you get if you usebranch -f
. If you name itgit-merge-ff
and drop it in your bin directory, you can call it as a git command:git merge-ff
.P.S. If anyone sees any issues with that script, please comment! It was a write-and-forget job, but I'd be happy to improve it.
仅当合并是快进时才可以执行此操作。如果不是,那么 git 需要签出文件,以便合并它们!
仅用于快进:
其中
是获取的提交,即您想要快进的提交。这基本上就像使用 gitbranch -f 移动分支一样,只不过它还会将其记录在引用日志中,就像您实际进行了合并一样。请,请,请不要对非快进的内容执行此操作,否则您只是将分支重置为其他提交。 (要进行检查,请查看 git merge-base是否给出了分支的 SHA1。)
You can only do this if the merge is a fast-forward. If it's not, then git needs to have the files checked out so it can merge them!
To do it for a fast-forward only:
where
<commit>
is the fetched commit, the one you want to fast-forward to. This is basically like usinggit branch -f
to move the branch, except it also records it in the reflog as if you actually did the merge.Please, please, please don't do this for something that's not a fast-forward, or you'll just be resetting your branch to the other commit. (To check, see if
git merge-base <branch> <commit>
gives the branch's SHA1.)在您的情况下,您可以使用
which 执行您想要的操作(假设合并是快进的)。如果分支由于需要非快进合并而无法更新,则此操作会安全失败并显示一条消息。
这种形式的获取也有一些更有用的选项:
请注意,
可以是本地存储库,
可以是一个跟踪分支。因此,您可以更新本地分支,即使它尚未签出,无需访问网络。目前,我的上游服务器访问是通过缓慢的 VPN 进行的,因此我定期连接,
git fetch
来更新所有遥控器,然后断开连接。然后,如果远程主控发生了变化,我可以安全地将本地主控更新为最新,即使我当前签出了其他一些分支。无需网络访问。
In your case you can use
which does what you want (assuming the merge is fast-forward). If the branch can't be updated because it requires a non-fast-forward merge, then this fails safely with a message.
This form of fetch has some more useful options too:
Note that
<remote>
can be a local repository, and<sourceBranch>
can be a tracking branch. So you can update a local branch, even if it's not checked out, without accessing the network.Currently, my upstream server access is via a slow VPN, so I periodically connect,
git fetch
to update all remotes, and then disconnect. Then if, say, the remote master has changed, I can doto safely bring my local master up to date, even if I currently have some other branch checked out. No network access required.
另一种无可否认的相当粗暴的方法是重新创建分支:
这会丢弃本地过时的分支并重新创建同名的分支,因此请小心使用......
Another, admittedly pretty brute way is to just re-create the branch:
This throws away the local outdated branch and re-creates one with the same name, so use with care ...
您可以克隆存储库并在新存储库中进行合并。在同一文件系统上,这将硬链接而不是复制大部分数据。通过将结果拉入原始存储库来完成。
You can clone the repo and do the merge in the new repo. On the same filesystem, this will hardlink rather than copy most of the data. Finish by pulling the results into the original repo.
输入git-forward-merge:
https://github.com/schuyler1d/git-forward-merge
仅适用于自动合并,如果存在冲突则需要使用常规合并。
Enter git-forward-merge:
https://github.com/schuyler1d/git-forward-merge
Only works for automatic merges, if there are conflicts you need to use the regular merge.
对于许多 GitFlow 用户来说,最有用的命令是:
--update-head-ok 标志允许在 dev 或 master 分支上使用相同的命令。
.gitconfig
中的一个方便的别名:For many GitFlow users the most useful commands are:
The
--update-head-ok
flag allows using the same command while ondev
ormaster
branches.A handy alias in
.gitconfig
:我在这个问题中遗漏了一些东西,如果您无意处理它,为什么需要签出本地
branchB
?在您的示例中,您只想更新本地branchB
,同时保留在branchA
上?我最初假设您这样做是为了 fetch
origin/branchB
,您已经可以使用git fetch
来做到这一点,所以这个答案实际上是基于这。在您需要处理之前,无需拉取branchB
,并且当您需要从origin/branchB
合并时,您始终可以获取origin
>。如果您想跟踪branchB 在某个时间点的位置,您可以从最新获取的
origin/branchB
创建一个标签或另一个分支。因此,您需要从
origin/branchB
合并:然后下次您需要在
branchB
上工作时:此时您将获得更新的本地副本。虽然有一些方法可以在不检查的情况下完成此操作,但这种方法很少有用,并且在某些情况下可能不安全。现有的答案涵盖了这一点。
详细答案:
git pull
执行获取+合并。它与下面的两个命令大致相同,其中
通常是origin
(默认),远程跟踪分支以开头。 /
后跟远程分支名称:@{u}
表示法是为当前分支配置的远程跟踪分支。如果branchB
跟踪origin/branchB
,则来自branchB
的@{u}
与输入origin 相同/branchB
(有关更多信息,请参阅 git rev-parse --help)。由于您已经与
origin/branchB
合并,因此缺少的只是git fetch
(可以从任何分支运行)来更新该远程跟踪分支。但请注意,如果在拉入本地
branchB
时创建了任何合并,您应该在从branchB
拉取后将branchB
合并到branchA
中。 code>branchB (并最终将更改推送回orign/branchB
,但只要它们快进,它们就会保持不变)。请记住,本地
branchB
在您切换到它并执行实际拉取之前不会更新,但是只要没有本地提交添加到该分支,它就会保持快进状态远程分支。There is something I am missing from the question, why would you need to checkout the local
branchB
if you have no intent on working on it? In your example you just want to update the localbranchB
while on remaining onbranchA
?I originally assumed you were doing so to fetch
origin/branchB
, which you can already do withgit fetch
, so this answer is really based on this. There is no need to pullbranchB
until you need to work on it, and you can always fetchorigin
when you need to merge fromorigin/branchB
.If you want to keep track of where branchB is at at a point in time, you can create a tag or another branch from the latest fetch of
origin/branchB
.So all you should need ever need to merge from
origin/branchB
:Then next time you need to work on
branchB
:At this point you will get an updated local copy. While there are ways to do it without checking out, this is the rarely useful and can be unsafe in some cases. There are existing answers covering this.
Detailed answer:
git pull
does a fetch + merge. It's roughly the the same the two commands below, where<remote>
is usuallyorigin
(default), and the remote tracking branch starts with<remote>/
followed by the remote branch name:The
@{u}
notation is the configured remote tracking branch for the current branch. IfbranchB
tracksorigin/branchB
then@{u}
frombranchB
is the same as typingorigin/branchB
(seegit rev-parse --help
for more info).Since you already merge with
origin/branchB
, all that is missing is thegit fetch
(which can run from any branch) to update that remote-tracking branch.Note though that if there was any merge created while pulling into local
branchB
, you should rather mergebranchB
intobranchA
after having done a pull frombranchB
(and eventually push the changes back toorign/branchB
, but as long as they're fast-forward they would remain the same).Keep in mind the local
branchB
will not be updated until you switch to it and do an actual pull, however as long as there are no local commits added to this branch it will just remain a fast-forward to the remote branch.对于许多情况(例如合并),您可以只使用远程分支,而无需更新本地跟踪分支。在转发日志中添加一条消息听起来有些过分,而且会阻止它变得更快。为了更容易恢复,请将以下内容添加到您的 git 配置中,
然后键入
以查看分支的最近历史记录
For many cases (such as merging), you can just use the remote branch without having to update the local tracking branch. Adding a message in the reflog sounds like overkill and will stop it being quicker. To make it easier to recover, add the following into your git config
Then type
to see the recent history for your branch
我为每天在项目中遇到的类似用例编写了一个 shell 函数。这基本上是让本地分支与公共分支保持同步的捷径,例如在打开 PR 之前进行开发等。
glmh
(“git pull并合并到这里”)会自动checkoutbranchB
,pull
最新的,重新< code>签出branchA,并合并branchB
。没有解决保留branchA本地副本的需要,但可以通过在签出branchB之前添加一个步骤来轻松修改以实现此目的。
就像...
对于简单的快进合并,这会跳到提交消息提示。
对于非快进合并,这会将您的分支置于冲突解决状态(您可能需要干预)。
要进行设置,请添加到
.bashrc
或.zshrc
等:用法:
I wrote a shell function for a similar use case I encounter daily on projects. This is basically a shortcut for keeping local branches up to date with a common branch like develop before opening a PR, etc.
glmh
("git pull and merge here") will automaticallycheckout branchB
,pull
the latest, re-checkout branchA
, andmerge branchB
.Doesn't address the need to keep a local copy of branchA, but could easily be modified to do so by adding a step before checking out branchB.
Something like...
For simple fast-forward merges, this skips to the commit message prompt.
For non fast-forward merges, this places your branch in the conflict resolution state (you likely need to intervene).
To setup, add to
.bashrc
or.zshrc
, etc:Usage:
只是为了拉出 master 而不检查 master 我使用
git fetch origin master:master
just to pull the master without checking out the master I use
git fetch origin master:master
您可以尝试
git worktree
让两个分支打开另一方面,这听起来可能就是您想要的,但与我在这里看到的其他一些答案非常不同。通过这种方式,您可以在同一个 git 存储库中跟踪两个单独的分支,因此您只需获取一次即可在两个工作树中获取更新(而不是必须 git clone 两次并在每个分支上 git pull)
Worktree 将创建一个新的工作目录对于您的代码,您可以同时签出不同的分支,而不是就地交换分支。
当你想删除它时,你可以用
You can try
git worktree
to have two branches open side by side, this sounds like it might be what you want but very different than some of the other answers I've seen here.In this way you can have two separate branches tracking in the same git repo so you only have to fetch once to get updates in both work trees (rather than having to git clone twice and git pull on each)
Worktree will create a new working directory for your code where you can have a different branch checked out simultaneously instead of swapping branches in place.
When you want to remove it you can clean up with
有效执行此操作的另一种方法是:
因为它是小写的
-d
,所以只有当数据仍然存在于某处时才会将其删除。它与 @kkoehne 的答案类似,只是它不强制。由于-t
它将再次设置遥控器。我的需求与 OP 略有不同,即在合并拉取请求后,从
develop
(或master
)创建一个新功能分支。这可以在不强制的情况下通过一行完成,但它不会更新本地develop
分支。只需检查一个新分支并使其基于origin/develop
即可:Another way to effectively do this is:
Because it's a lower case
-d
, it will only delete it if the data will still exist somewhere. It's similar to @kkoehne's answer except it doesn't force. Because of the-t
it will set up the remote again.I had a slightly different need than OP, which was to create a new feature branch off
develop
(ormaster
), after merging a pull request. That can be accomplished in a one-liner without force, but it doesn't update the localdevelop
branch. It's just a matter of checking out a new branch and having it be based offorigin/develop
:完全可以在没有 git checkout 的情况下进行任何合并,甚至是非快进合并。 @grego 的
worktree
回答是一个很好的提示。扩展一下:您现在已将本地工作分支合并到本地
master
分支,而无需切换结账。It is absolutely possible to do any merge, even non-fast forward merges, without
git checkout
. Theworktree
answer by @grego is a good hint. To expand on that:You have now merged the local work branch to the local
master
branch without switching your checkout.如果您想保留与要合并的分支之一相同的树(即不是真正的“合并”),您可以这样做。
If you want to keep the same tree as one of the branch you want to merge (ie. not a real "merge"), you can do it like this.
这实际上是我日常工作流程中非常重要的功能。应该记录在这里以供参考。
假设我正在
feat1
分支中工作,但我有一些东西应该提交到feat2
中,它是当前HEAD
后面的分支。我曾经执行以下步骤:首先签出feat2
,然后执行git rebase feat
。但这通常会导致我正在运行的开发服务器检测到更改并重新渲染到旧版本,如果您的工作树与当前分支差异太大,通常会崩溃。为了避免工作树发生巨大变化,在停留在分支feat1
上时,我只需执行git fetch;:
,在上述情况下将是:或
之后
feat2
将指向与feat1
相同的提交。然后,您可以安全地签出feat2
,而不会导致开发服务器崩溃。This is actually quite important feature in my daily workflow. Should be documented here for reference.
Suppose I am working in a
feat1
branch but I have something that should be committed intofeat2
, which is a branch way behind the currentHEAD
. I used to be doing these steps: first checkoutfeat2
, then do agit rebase feat
. But that would usually cause my running development server to detect a change and re-render to the old version, which will usually crash if your working tree is too different from the current branch. To avoid huge change of the working tree, while staying on branchfeat1
, I just dogit fetch <repo> <src>:<dst>
, in the above case that would be:or
After that
feat2
will be pointing to same commit asfeat1
. You can then safely checkoutfeat2
without crashing your development server.我使用以下别名:
有关详细说明,请参阅 https://stackoverflow.com/a/76267753/9878263
I use the following alias:
See https://stackoverflow.com/a/76267753/9878263 for the detailed explanation
您只需
git pull originbranchB
到您的branchA
中,git就会为您完成任务。You can simply
git pull origin branchB
into yourbranchA
and git will do the trick for you.