我在一个有两个分支A和B的项目上工作。我通常在分支A上工作,并从分支B合并东西。对于合并,我通常会这样做:
git merge origin/branchB
但是,我还想保留分支B的本地副本,因为我可能偶尔会检查分支而不先与我的分支A合并。为此,我会这样做:
git checkout branchB
git pull
git checkout branchA
有没有办法在一个命令中执行上述操作,而无需来回切换分支?我应该使用git update-ref
吗?怎么样?
只要您正在进行快进合并,那么您可以简单地使用
git fetch <remote> <sourceBranch>:<destinationBranch>
例子:
# 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
虽然Amber's answer也适用于快进案例,但是使用git fetch
这样做比使用强制移动分支参考更安全一点,因为只要你不使用git fetch
,+
就会自动防止意外的非快速前进在refspec中。
您不能将分支B合并到分支A而不先检出A是否会导致非快进合并。这是因为需要工作副本来解决任何潜在的冲突。
但是,在快进合并的情况下,这是可能的,因为根据定义,这种合并永远不会导致冲突。要在不先检查分支的情况下执行此操作,可以将git fetch
与refspec一起使用。
这是一个更新master
(禁止非快进更改)的示例,如果您检查了另一个分支feature
:
git fetch upstream master:master
这个用例很常见,你可能想在你的git配置文件中为它做一个别名,比如这个:
[alias]
sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'
这个别名的作用如下:
git checkout HEAD
:这会将您的工作副本置于一个独立的状态。如果您想要在签出时更新master
,这非常有用。我认为有必要这样做,因为否则master
的分支参考不会移动,但我不记得这是否真的是我的头脑。git fetch upstream master:master
:这快速将你当地的master
转发到与upstream/master
相同的地方。git checkout -
检查你之前签出的分支(这就是-
在这种情况下所做的)。git fetch
用于(非)快进合并的语法如果您希望fetch
命令在更新非快进时失败,那么您只需使用表单的refspec
git fetch <remote> <remoteBranch>:<localBranch>
如果您想允许非快进更新,那么您将+
添加到refspec的前面:
git fetch <remote> +<remoteBranch>:<localBranch>
请注意,您可以使用.
将本地仓库作为“远程”参数传递:
git fetch . <sourceBranch>:<destinationBranch>
来自git fetch
documentation that explains this syntax(强调我的):
<refspec>
<refspec>
参数的格式是可选的加+
,然后是源ref<src>
,接着是冒号:
,接着是目标ref<dst>
。获取与
<src>
匹配的远程引用,如果<dst>
不是空字符串,则使用<src>
快速转发与其匹配的本地引用。如果使用可选的加号+
,则即使不导致快进更新,也会更新本地引用。
我为每天在项目中遇到的类似用例编写了一个shell函数。这基本上是一种快捷方式,用于在打开PR等之前使本地分支机构与开发等公共分支保持同步。
即使您不想使用
checkout
,也请发布此信息,以防其他人不介意该约束。
glmh
(“git pull and merge here”)将自动checkout branchB
,pull
最新,重新checkout branchA
和merge branchB
。
不解决保留branchA的本地副本的需要,但可以通过在签出branchB之前添加一个步骤来轻松修改。就像是...
git branch ${branchA}-no-branchB ${branchA}
对于简单的快进合并,它会跳转到提交消息提示。
对于非快进合并,这会将您的分支置于冲突解决状态(您可能需要进行干预)。
.bashrc
or .zshrc
, etc:glmh() {
branchB=$1
[ $# -eq 0 ] && { branchB="develop" }
branchA="$(git branch | grep '*' | sed 's/* //g')"
git checkout ${branchB} && git pull
git checkout ${branchA} && git merge ${branchB}
}
# No argument given, will assume "develop"
> glmh
# Pass an argument to pull and merge a specific branch
> glmh your-other-branch
注意:这不足以将分支名称之外的args移交给
git merge
有效地做到这一点的另一种方法是:
git fetch
git branch -d branchB
git branch -t branchB origin/branchB
因为它是一个小写的-d
,它只会删除它,如果数据仍然存在于某处。它类似于@kkoehne的回答,除了它没有强制。由于-t
它将再次设置遥控器。
我有一个与OP略有不同的需求,即在合并拉取请求之后在develop
(或master
)上创建一个新的特征分支。这可以在没有力量的单线程中完成,但它不会更新本地develop
分支。这只是检查一个新分支并将其基于origin/develop
的问题:
git checkout -b new-feature origin/develop
不,那里没有。检查目标分支是必要的,以便您解决冲突等(如果Git无法自动合并它们)。
但是,如果合并是快进的,那么您不需要检查目标分支,因为您实际上不需要合并任何东西 - 您只需要更新分支以指向新头参考你可以用git branch -f
做到这一点:
git branch -f branch-b branch-a
将更新branch-b
指向branch-a
的头。
-f
选项代表--force
,这意味着使用它时必须小心。除非您确定合并将是快进的,否则不要使用它。
正如Amber所说,快进合并是唯一可以令人信服的情况。可以想象,任何其他合并都需要经历整个三向合并,应用补丁,解决冲突交易 - 这意味着需要存在文件。
我碰巧有一个脚本我用于这个:完成快进合并而不触及工作树(除非你合并到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="$1"
commit="$2"
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 "$1" "$2";;
* ) _usage
esac
附:如果有人发现该脚本有任何问题,请发表评论!这是一个写不出来的工作,但我很乐意改进它。
如果合并是快进,则只能执行此操作。如果不是,那么git需要检出文件,以便合并它们!
仅针对快进进行:
git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>
其中<commit>
是提取的提交,是你要快进的提交。这基本上就像使用git branch -f
来移动分支一样,除了它还将它记录在reflog中,就像你实际上进行了合并一样。
拜托,请不要为那些不是快进的东西做这件事,或者你只是将你的分支重置为另一个提交。 (要检查,看看git merge-base <branch> <commit>
是否给出了分支的SHA1。)
另一个,无可否认的方式是重新创建分支:
git fetch remote
git branch -f localbranch remote/remotebranch
这会抛弃本地过时的分支并重新创建一个具有相同名称的分支,因此请小心使用...
在你的情况下你可以使用
git fetch origin branchB:branchB
你想做什么(假设合并是快进的)。如果由于需要非快进合并而无法更新分支,则会因消息安全失败。
这种形式的fetch也有一些更有用的选项:
git fetch <remote> <sourceBranch>:<destinationBranch>
请注意,<remote>
可以是本地存储库,<sourceBranch>
可以是跟踪分支。因此,即使未检出本地分支,也可以在不访问网络的情况下更新本地分支。
目前,我的上游服务器访问是通过慢速VPN,所以我定期连接,git fetch
更新所有遥控器,然后断开连接。然后,如果远程主机已经改变,我可以这样做
git fetch . remotes/origin/master:master
安全地让我的本地主人更新,即使我目前有一些其他分支检查。无需网络访问。
您可以克隆仓库并在新仓库中进行合并。在相同的文件系统上,这将硬链接而不是复制大多数数据。完成将结果拉入原始仓库。
输入git-forward-merge:
无需结账目的地,
git-forward-merge <source> <destination>
将源合并到目标分支。
https://github.com/schuyler1d/git-forward-merge
仅适用于自动合并,如果存在冲突则需要使用常规合并。
对于许多情况(例如合并),您只需使用远程分支而无需更新本地跟踪分支。在reflog中添加消息听起来有点矫枉过正,会让它更快停止。为了更容易恢复,请将以下内容添加到您的git配置中
[core]
logallrefupdates=true
然后输入
git reflog show mybranch
查看您分支机构的近期历史记录