不使用 checkout 合并、更新和拉取 Git 分支
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3216360/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Merge, update, and pull Git branches without using checkouts
提问by charles
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:
我在一个有 2 个分支 A 和 B 的项目上工作。我通常在分支 A 上工作,并从分支 B 合并东西。对于合并,我通常会这样做:
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:
但是,我还想保留分支 B 的本地副本,因为我可能偶尔会在没有先与分支 A 合并的情况下检查分支。为此,我会这样做:
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?
有没有办法在一个命令中完成上述操作,而不必来回切换分支?我应该git update-ref
为此使用吗?如何?
回答by
The Short Answer
简短的回答
As long as you're doing a fast-forwardmerge, then you can simply use
只要你在做一个快进合并,那么你可以简单地使用
git fetch <remote> <sourceBranch>:<destinationBranch>
Examples:
例子:
# Merge local branch foo into local branch master,
# without having to checkout master first.
# Here `.` means to use the local repository as the "remote":
git fetch . foo:master
# Merge remote branch origin/foo into local branch foo,
# without having to checkout foo first:
git fetch origin foo:foo
While Amber's answerwill also work in fast-forward cases, using git fetch
in this way instead is a little safer than just force-moving the branch reference, since git fetch
will automatically prevent accidental non-fast-forwards as long as you don't use +
in the refspec.
虽然Amber的答案也将在快进的情况下,利用工作git fetch
中,而不是这种方式不仅仅是力移动分支参考稍微安全一点,因为git fetch
会自动防止意外非快进,只要你不使用+
的参考规格
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.
如果分支 B 会导致非快进合并,则不能在不先检查 A 的情况下将分支 B 合并到分支 A 中。这是因为需要一个工作副本来解决任何潜在的冲突。
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.
然而,在快进合并的情况下,这是可能的,因为根据定义,这种合并永远不会导致冲突。要在不先检出分支的情况下执行此操作,您可以使用git fetch
refspec。
Here's an example of updating master
(disallowing non-fast-forward changes) if you have another branch feature
checked out:
master
如果您已feature
签出另一个分支,则这是一个更新示例(不允许非快进更改):
git fetch upstream master:master
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:
这个用例非常常见,你可能想在你的 git 配置文件中为它创建一个别名,像这样:
[alias]
sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'
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).
git checkout HEAD
:这会将您的工作副本置于分离头状态。如果您想master
在碰巧签出时进行更新,这将非常有用。我认为这是必要的,因为否则分支引用master
不会移动,但我不记得这是否真的是我的想法。git fetch upstream master:master
:这会将您的本地快进master
到与upstream/master
.git checkout -
签出您之前签出的分支(-
在这种情况下就是这样做的)。
The syntax of git fetch
for (non-)fast-forward merges
git fetch
for(非)快进合并的语法
If you want the fetch
command to fail if the update is non-fast-forward, then you simply use a refspec of the form
如果您希望fetch
命令在更新为非快进时失败,那么您只需使用以下形式的 refspec
git fetch <remote> <remoteBranch>:<localBranch>
If you want to allow non-fast-forward updates, then you add a +
to the front of the refspec:
如果要允许非快进更新,则+
在 refspec 的前面添加一个:
git fetch <remote> +<remoteBranch>:<localBranch>
Note that you can pass your local repo as the "remote" parameter using .
:
请注意,您可以使用以下命令将本地存储库作为“远程”参数传递.
:
git fetch . <sourceBranch>:<destinationBranch>
The Documentation
文档
From the git fetch
documentation that explains this syntax(emphasis mine):
从git fetch
解释此语法的文档(强调我的):
<refspec>
The format of a
<refspec>
parameter is an optional plus+
, followed by the source ref<src>
, followed by a colon:
, followed by the destination ref<dst>
.The remote ref that matches
<src>
is fetched, and if<dst>
is not empty string, the local ref that matches it is fast-forwarded using<src>
. If the optional plus+
is used, the local ref is updated even if it does not result in a fast-forward update.
<refspec>
<refspec>
参数的格式是一个可选的加号+
,后跟源引用<src>
,后跟冒号:
,后跟目标引用<dst>
。
<src>
获取匹配的远程 ref,如果<dst>
不是空字符串,则匹配它的本地 ref 使用<src>
. 如果使用了可选的加号+
,即使不会导致快进更新,也会更新本地引用。
See Also
也可以看看
回答by Amber
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).
不,那里没有。签出目标分支是必要的,以允许您解决冲突等(如果 Git 无法自动合并它们)。
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
:
但是,如果合并是快进的,则您不需要检查目标分支,因为您实际上不需要合并任何东西 - 您所要做的就是更新分支以指向目标分支新头参考。你可以这样做git branch -f
:
git branch -f branch-b branch-a
Will update branch-b
to point to the head of branch-a
.
将更新branch-b
以指向branch-a
.
The -f
option stands for --force
, which means you must be careful when using it.
该-f
选项代表--force
,这意味着您在使用它时必须小心。
Don't use it unless you are absolutely sure the merge will be fast-forward.
除非您绝对确定合并会快进,否则不要使用它。
回答by Cascabel
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.
正如 Amber 所说,快进合并是唯一可以做到这一点的情况。可以想象,任何其他合并都需要经过整个三向合并、应用补丁、解决冲突处理——这意味着需要有文件。
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 use branch -f
. If you name it git-merge-ff
and drop it in your bin directory, you can call it as a git command: git merge-ff
.
我碰巧有一个脚本,我正好用于此目的:在不触及工作树的情况下进行快进合并(除非您合并到 HEAD)。它有点长,因为它至少有点健壮 - 它检查以确保合并是快进的,然后在不检查分支的情况下执行它,但产生与你相同的结果 - 你看到diff --stat
更改摘要,并且 reflog 中的条目与快速向前合并完全一样,而不是使用branch -f
. 如果您将其命名git-merge-ff
并将其放在 bin 目录中,则可以将其称为 git 命令:git merge-ff
.
#!/bin/bash
_usage() {
echo "Usage: git merge-ff <branch> <committish-to-merge>" 1>&2
exit 1
}
_merge_ff() {
branch=""
commit=""
branch_orig_hash="$(git show-ref -s --verify refs/heads/$branch 2> /dev/null)"
if [ $? -ne 0 ]; then
echo "Error: unknown branch $branch" 1>&2
_usage
fi
commit_orig_hash="$(git rev-parse --verify $commit 2> /dev/null)"
if [ $? -ne 0 ]; then
echo "Error: unknown revision $commit" 1>&2
_usage
fi
if [ "$(git symbolic-ref HEAD)" = "refs/heads/$branch" ]; then
git merge $quiet --ff-only "$commit"
else
if [ "$(git merge-base $branch_orig_hash $commit_orig_hash)" != "$branch_orig_hash" ]; then
echo "Error: merging $commit into $branch would not be a fast-forward" 1>&2
exit 1
fi
echo "Updating ${branch_orig_hash:0:7}..${commit_orig_hash:0:7}"
if git update-ref -m "merge $commit: Fast forward" "refs/heads/$branch" "$commit_orig_hash" "$branch_orig_hash"; then
if [ -z $quiet ]; then
echo "Fast forward"
git diff --stat "$branch@{1}" "$branch"
fi
else
echo "Error: fast forward using update-ref failed" 1>&2
fi
fi
}
while getopts "q" opt; do
case $opt in
q ) quiet="-q";;
* ) ;;
esac
done
shift $((OPTIND-1))
case $# in
2 ) _merge_ff "" "";;
* ) _usage
esac
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.
PS如果有人看到该脚本有任何问题,请发表评论!这是一个写完就忘记的工作,但我很乐意改进它。
回答by Cascabel
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!
如果合并是快进的,您只能这样做。如果不是,那么 git 需要检出文件,以便合并它们!
To do it for a fast-forward only:
要做到这一点的快进只:
git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>
where <commit>
is the fetched commit, the one you want to fast-forward to. This is basically like using git branch -f
to move the branch, except it also records it in the reflog as if you actually did the merge.
<commit>
获取的提交在哪里,您想要快进到的提交。这基本上就像git branch -f
用于移动分支,除了它还将它记录在 reflog 中,就好像您确实进行了合并一样。
Please, please, pleasedon'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.)
拜托,拜托,请不要为不是快进的事情做这件事,否则你只会将你的分支重置为另一个提交。(要检查,看看是否git merge-base <branch> <commit>
给出了分支的 SHA1。)
回答by Bennett McElwee
In your case you can use
在您的情况下,您可以使用
git fetch origin branchB:branchB
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:
这种形式的 fetch 也有一些更有用的选项:
git fetch <remote> <sourceBranch>:<destinationBranch>
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.
注意<remote>
可以是本地仓库,也<sourceBranch>
可以是跟踪分支。所以你可以更新一个本地分支,即使它没有被检出,而无需访问网络。
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 do
目前,我的上游服务器访问是通过慢速 VPN 进行的,因此我会定期连接,git fetch
更新所有遥控器,然后断开连接。然后,如果说,远程主人已经改变,我可以做
git fetch . remotes/origin/master:master
to safely bring my local master up to date, even if I currently have some other branch checked out. No network access required.
安全地使我的本地 master 保持最新状态,即使我目前已检出其他一些分支。无需网络访问。
回答by kkoehne
Another, admittedly pretty brute way is to just re-create the branch:
另一种公认的非常粗暴的方法是重新创建分支:
git fetch remote
git branch -f localbranch remote/remotebranch
This throws away the local outdated branch and re-creates one with the same name, so use with care ...
这会丢弃本地过时的分支并重新创建一个具有相同名称的分支,因此请谨慎使用...
回答by wnoise
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.
您可以克隆存储库并在新存储库中进行合并。在同一个文件系统上,这将硬链接而不是复制大部分数据。通过将结果拉入原始存储库来完成。
回答by Casebash
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
对于很多情况(比如合并),你可以只使用远程分支,而不必更新本地跟踪分支。在 reflog 中添加一条消息听起来有点矫枉过正,会阻止它变得更快。为了更容易恢复,请将以下内容添加到您的 git 配置中
[core]
logallrefupdates=true
Then type
然后输入
git reflog show mybranch
to see the recent history for your branch
查看您分支机构的最近历史记录
回答by lkraider
Enter git-forward-merge:
输入git-forward-merge:
Without needing to checkout destination,
git-forward-merge <source> <destination>
merges source into destination branch.
无需签出目标,
git-forward-merge <source> <destination>
将源合并到目标分支。
https://github.com/schuyler1d/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.
仅适用于自动合并,如果存在冲突,则需要使用常规合并。
回答by rkd
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.
我为每天在项目中遇到的类似用例编写了一个 shell 函数。这基本上是在打开 PR 等之前使本地分支与公共分支保持同步的捷径,例如开发等。
Posting this even though you don't want to use
checkout
, in case others don't mind that constraint.
即使您不想使用
checkout
,也可以发布此内容,以防其他人不介意这种限制。
glmh
("git pull and merge here") will automatically checkout branchB
, pull
the latest, re-checkout branchA
, and merge branchB
.
glmh
("git pull and merge here") 将自动checkout branchB
,pull
最新,重新checkout branchA
,和merge 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...
没有解决保留 branchA 的本地副本的需要,但可以通过在检出 branchB 之前添加一个步骤来轻松修改以实现此目的。就像是...
git branch ${branchA}-no-branchB ${branchA}
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:
要设置,添加到.bashrc
or.zshrc
等:
glmh() {
branchB=
[ $# -eq 0 ] && { branchB="develop" }
branchA="$(git branch | grep '*' | sed 's/* //g')"
git checkout ${branchB} && git pull
git checkout ${branchA} && git merge ${branchB}
}
Usage:
用法:
# No argument given, will assume "develop"
> glmh
# Pass an argument to pull and merge a specific branch
> glmh your-other-branch
Note: This is notrobust enough to hand-off of args beyond branch name to
git merge
注:这是不是足够强大的手断超越分支名参数的个数来
git merge