我在一个有两个分支a和B的项目上工作。我通常在分支a上工作,并合并分支B中的内容。对于合并,我通常会这样做:

git merge origin/branchB

但是,我也希望保留分支B的本地副本,因为我可能会偶尔检查分支,而不必首先与分支a合并。为此,我会:

git checkout branchB
git pull
git checkout branchA

有没有一种方法可以在一个命令中完成上述操作,而不必来回切换分支?我应该使用gitupdate-ref吗?怎样


当前回答

如果您想保持与要合并的分支相同的树(即,不是真正的“合并”),可以这样做。

# Check if you can fast-forward
if git merge-base --is-ancestor a b; then
    git update-ref refs/heads/a refs/heads/b
    exit
fi

# Else, create a "merge" commit
commit="$(git commit-tree -p a -p b -m "merge b into a" "$(git show -s --pretty=format:%T b)")"
# And update the branch to point to that commit
git update-ref refs/heads/a "$commit"

其他回答

我为每天在项目中遇到的类似用例编写了一个shell函数。这基本上是一个快捷方式,可以让本地分支机构在打开PR之前保持与公共分支机构的最新状态,如开发等。

即使你不想使用结账,也要发布这个,以防其他人不介意这种限制。

glmh(“git pull and merge here”)将自动签出分支B,拉取最新的分支,重新签出分支A,并合并分支B。

不需要保留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

注意:这不足以将分支名称以外的参数传递给gitmerge

git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]

你可以尝试git worktree让两个分支并排打开,这听起来可能是你想要的,但与我在这里看到的其他一些答案非常不同。

通过这种方式,您可以在同一个git repo中有两个单独的分支进行跟踪,因此您只需获取一次即可在两个工作树中获取更新(而不必分别获取两次git clone和git pull)

Worktree将为您的代码创建一个新的工作目录,您可以在其中同时签出不同的分支,而不是在原地交换分支。

当您想要删除它时,可以使用

git worktree remove [-f] <worktree>

简短的回答

只要您正在进行快速合并,就可以简单地使用

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的答案也适用于快进情况,但以这种方式使用git fetch比强制移动分支引用更安全,因为只要在refspec中不使用+,git fetch就会自动防止意外的非快进。

答案很长

如果不先签出a,就不能将分支B合并到分支a,否则会导致非快速合并。这是因为需要一个工作副本来解决任何潜在的冲突。

然而,在快速合并的情况下,这是可能的,因为根据定义,这种合并永远不会导致冲突。要在不首先检查分支的情况下执行此操作,可以使用gitfetch和refspec。

如果您签出了另一个分支功能,以下是更新master(不允许非快进更改)的示例:

git fetch upstream master:master

这个用例非常常见,您可能需要在git配置文件中为它创建一个别名,如下所示:

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

此别名的作用如下:

gitcheckout HEAD:这将使您的工作副本进入分离的头状态。如果您想在碰巧签出master时更新它,这很有用。我认为这是必要的,因为否则master的分支引用不会移动,但我不记得这是否真的就在我的头顶上。gitfetchupstream-master:master:这将快速将本地主机转发到与upstream/master相同的位置。gitcheckout-签出您以前签出的分支(在本例中-就是这么做的)。

gitfetch用于(非)快进合并的语法

如果您希望fetch命令在非快速更新时失败,那么只需使用表单的refspec

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

如果您希望允许非快速更新,那么在refspec前面添加一个+:

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

请注意,您可以使用传递本地回购作为“远程”参数。:

git fetch . <sourceBranch>:<destinationBranch>

文件

从解释此语法的git fetch文档中(重点是我的):

<参考规范><refspec>参数的格式是可选的加号+,后跟源ref<src>,后跟冒号:,后跟目标ref<dst>。获取与<src>匹配的远程引用,如果<dst>不是空字符串,则使用<src>快速转发与之匹配的本地引用。如果使用可选的加号+,则即使不会导致快速更新,也会更新本地ref。

另请参见

Git签出和合并而不接触工作树合并而不更改工作目录

另一种公认的蛮力方法是重新创建分支:

git fetch remote
git branch -f localbranch remote/remotebranch

这将丢弃本地过时的分支,并重新创建一个同名分支,因此请谨慎使用。。。

只有当合并是快进时,才能执行此操作。如果不是,那么git需要签出文件,以便合并它们!

仅为快进操作:

git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>

其中<commit>是提取的commit,即您要快进的commit。这基本上就像使用gitbranch-f移动分支一样,只是它还将其记录在reflog中,就像您实际执行了合并一样。

拜托,拜托,请不要为不快速前进的事情而这样做,否则您只会将分支重置为另一个提交。(要检查,请查看gitmerge-base<branch><commit>是否给出了分支的SHA1。)