合并,更新,并拉动Git分支,而不使用结帐

我工作的项目有2个分支,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 

虽然琥珀的答案也将在快速的情况下工作,使用这种方式的git fetch取代比强制移动分支引用有点安全,因为git fetch会自动防止意外的非快进,只要你不'在refspec中使用+

长的答案

如果没有检出A分支,将无法将分支B合并到分支A中,如果这会导致非快进合并。 这是因为需要工作副本来解决任何潜在的冲突。

但是, 在快速合并的情况下,这是可能的 ,因为根据定义,这样的合并永远不会导致冲突。 要做到这一点,而不先检查分支,你可以使用git fetch与refspec。

以下是更新master (禁止非快进更改)的示例,如果您有另一个分支feature检出:

 git fetch upstream master:master 

这个用例很常见,你可能想在你的gitconfiguration文件中为它做一个别名,就像这样:

 [alias] sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -' 

这个别名的作用如下:

  1. git checkout HEAD :这会把你的工作副本放到一个分离状态。 如果您想要更新master而您恰好将其签出,则此function非常有用。 我认为这是必要的,否则master的分支参考不会移动,但我不记得是否真的是我的脑袋。

  2. git fetch upstream master:master :这快速转发你的本地masterupstream/master相同的地方。

  3. git checkout -签出你以前签出的分支(这是什么-在这种情况下)。

(非)快进合并的git fetch语法

如果你想要fetch命令失败,如果更新是非快进的,那么你只需使用表格的refspec

 git fetch <remote> <remoteBranch>:<localBranch> 

如果你想允许非快进更新,那么你在refspec的前面添加一个+

 git fetch <remote> +<remoteBranch>:<localBranch> 

请注意,您可以使用“远程”parameter passing本地回购.

 git fetch . <sourceBranch>:<destinationBranch> 

文档

从解释此语法的git fetch文档 (重点介绍 ):

<refspec>

参数<refspec>的格式是一个可选的plus + ,后面跟着source ref <src> ,后跟一个冒号: ,后面跟着目的地ref <dst>

<src>匹配的远程ref被取出,如果<dst>不是空string,与之匹配的本地ref就使用<src>快速转发 。 如果使用可选的plus + ,即使不导致快速更新,本地ref也会被更新。

也可以看看

  1. Git结帐和合并而不接触工作树

  2. 合并而不改变工作目录

不,那里没有。 目标分支的结帐是必要的,以允许您解决冲突等(如果Git无法自动合并它们)。

但是,如果合并是快进的,则不需要检出目标分支,因为实际上并不需要合并任何东西 – 只需要将分支更新为指向新头参考 你可以用git branch -f来做到这一点git branch -f

 git branch -f branch-b branch-a 

将更新branch-b指向branch-a的头。

-f选项代表--force ,这意味着在使用它时必须小心。 除非您确定合并是快速的,否则不要使用它。

正如琥珀所说,快速合并是唯一可以设想的做法。 任何其他的合并可能需要经历整个三路合并,应用补丁,解决冲突交易 – 这意味着需要有文件。

我碰巧有一个脚本,我正是这样使用的:做快进合并而不碰触工作树(除非你合并到HEAD中)。 这有点长,因为它至less有点强壮 – 它会检查并确保合并是快进的,然后在不检出分支的情况下执行合并,但会产生与以前相同的结果 – 您会看到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 

PS如果有人看到有关该脚本的任何问题,请评论! 这是一个写得忘记的工作,但我很乐意改进它。

只有合并是快进才能做到这一点。 如果不是,那么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来移动分支,除了它也logging在reflog,就像你真的做了合并。

请,请不要这样做,这不是一个快速的,或者你只是重置你的分支到另一个提交。 (要检查,看看git merge-base <branch> <commit>给出分支的SHA1。)

另外,无可否认的方法是重新创build分支:

 git fetch remote git branch -f localbranch remote/remotebranch 

这扔掉了当地过时的分支,并重新创build一个同名的,所以小心使用…

您可以克隆回购,并在新的回购合并。 在相同的文件系统上,这将硬链接而不是复制大部分数据。 把结果拖到原来的回购结束。

在你的情况下,你可以使用

 git fetch origin branchB:branchB 

这是你想要的(假设合并是快进)。 如果分支不能被更新,因为它需要一个非快进合并,那么这将失败并带有一条消息。

这种forms的获取也有一些更有用的选项:

 git fetch <remote> <sourceBranch>:<destinationBranch> 

请注意, <remote> 可以是本地存储库<sourceBranch>可以是跟踪分支。 所以你可以在不访问networking的情况下更新本地分支,即使没有检出。

目前,我的上游服务器访问是通过一个慢速VPN,所以我定期连接, git fetch更新所有远程,然后断开连接。 那么,如果说,远程主人已经改变,我可以做

 git fetch . remotes/origin/master:master 

安全地把我的本地主人带到最新的,即使我目前有其他分支签出。 不需要networking访问。

inputgit-forward-merge

不需要签出目的地, git-forward-merge <source> <destination>将源合并到目标分支。

https://github.com/schuyler1d/git-forward-merge

只适用于自动合并,如果有冲突,您需要使用常规合并。

对于很多情况(如合并),只需使用远程分支,而无需更新本地跟踪分支。 在reflog中添加一条消息听起来像是矫枉过正,并会停止更快。 为了更容易恢复,请将以下内容添加到您的gitconfiguration中

 [core] logallrefupdates=true 

然后键入

 git reflog show mybranch 

看你的分支最近的历史

我为每天在项目中遇到的类似用例写了一个shell函数。 这基本上是一个捷径,使当地的分支机构在开设公关之前像一个共同的分支一样保持最新状态。

即使你不想使用checkout ,也可以发布这个,以防其他人不介意这个限制。

glmh (“git pull and merge here”)会自动checkout branchBcheckout branchB最新的重新checkout branchA ,并merge branchB

没有解决需要保留branchA的本地副本,但可以很容易地通过在检出branchB之前添加一个步骤来进行修改。 就像是…

 git branch ${branchA}-no-branchB ${branchA} 

对于简单的快进合并,这将跳转到提交消息提示。

对于非快进合并,这将您的分支置于冲突解决状态(您可能需要进行干预)。

要设置,添加到.bashrc.zshrc等:

 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 

注意:这足够强壮,可以将分支名称之外的参数移交给git merge