753

我在一个有 2 个分支 A 和 B 的项目上工作。我通常在分支 A 上工作,并合并来自分支 B 的东西。对于合并,我通常会这样做:

git merge origin/branchB

但是,我还想保留分支 B 的本地副本,因为我可能偶尔会在没有先与分支 A 合并的情况下签出分支。为此,我会这样做:

git checkout branchB
git pull
git checkout branchA

有没有办法在一个命令中完成上述操作,而不必来回切换分支?我应该为此使用git update-ref吗?如何?

4

18 回答 18

1164

简短的回答

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

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以这种方式使用比仅强制移动分支引用更安全一些,因为git fetch只要您+不在参考规范。

长答案

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

但是,在快进合并的情况下,这是可能的,因为根据定义,这种合并永远不会导致冲突。要在不先检查分支的情况下执行此操作,您可以使用git fetchrefspec。

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

git fetch upstream master:master

这个用例很常见,你可能想在你的 git 配置文件中为它创建一个别名,就像这个:

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

这个别名的作用如下:

  1. git checkout HEAD:这会使您的工作副本进入分离头状态。master如果您想在碰巧将其签出时进行更新,这将很有用。我认为这是必要的,因为否则分支引用master不会移动,但我不记得这是否真的是我的想法。

  2. git fetch upstream master:master:这会将您的本地快速转发master到与upstream/master.

  3. git checkout -检查您之前签出的分支(这就是-在这种情况下所做的)。

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

如果您希望在fetch更新是非快进的情况下命令失败,那么您只需使用表单的 refspec

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

如果要允许非快进更新,则+在 refspec 的前面添加一个:

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

请注意,您可以使用以下命令将本地存储库作为“远程”参数传递.

git fetch . <sourceBranch>:<destinationBranch>

文档

git fetch解释此语法的文档中(强调我的):

<refspec>

参数的格式<refspec>是可选的加号+,后跟源引用<src>,后跟冒号:,然后是目标引用<dst>

获取匹配的远程引用,<src>如果<dst>不是空字符串,则使用 快速转发匹配它的本地引用<src>。如果使用可选加+号,则更新本地 ref,即使它不会导致快进更新。

也可以看看

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

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

于 2013-07-18T12:06:05.933 回答
91

不,那里没有。必须检查目标分支才能解决冲突等问题(如果 Git 无法自动合并它们)。

但是,如果合并是快进的,则不需要检查目标分支,因为您实际上不需要合并任何东西 - 您所要做的就是更新分支以指向新的首席裁判。你可以这样做git branch -f

git branch -f branch-b branch-a

将更新branch-b指向branch-a.

-f选项代表--force,这意味着您在使用它时必须小心。

除非您绝对确定合并将快进,否则不要使用它。

于 2010-11-11T17:10:31.610 回答
32

正如 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

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

于 2010-11-11T17:40:57.950 回答
20

只有当合并是快进时,您才能执行此操作。如果不是,那么 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。)

于 2010-07-10T00:59:52.940 回答
16

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

git fetch origin branchB:branchB

可以满足您的要求(假设合并是快进的)。如果由于需要非快进合并而无法更新分支,则此操作将安全地失败并显示一条消息。

这种形式的 fetch 也有一些更有用的选项:

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

请注意,它<remote> 可以是本地存储库<sourceBranch>也可以是跟踪分支。因此,即使没有签出,您也可以更新本地分支,而无需访问网络

目前,我的上游服务器访问是通过慢速 VPN 进行的,所以我会定期连接,git fetch更新所有遥控器,然后断开连接。那么,如果远程主控发生了变化,我可以做

git fetch . remotes/origin/master:master

即使我目前有其他一些分支已签出,也可以安全地使我的本地 master 保持最新状态。无需网络访问。

于 2015-11-17T00:07:28.237 回答
12

另一种公认的非常粗暴的方法是重新创建分支:

git fetch remote
git branch -f localbranch remote/remotebranch

这会丢弃本地过时的分支并重新创建一个具有相同名称的分支,因此请小心使用...

于 2011-06-08T10:08:14.440 回答
7

您可以克隆存储库并在新存储库中进行合并。在同一个文件系统上,这将硬链接而不是复制大部分数据。通过将结果拉入原始存储库来完成。

于 2010-11-11T18:00:02.837 回答
5

我从这个问题中遗漏了一些东西,branchB如果您不打算处理它,为什么需要结帐本地?在您的示例中,您只想branchB在保持状态时更新本地branchA

我最初假设您这样做是为了fetch origin/branchB,您已经可以使用 fetch 了git fetch,所以这个答案确实基于此。branchB在需要处理之前无需拉取,并且origin在需要从origin/branchB.

如果您想跟踪 branchB 在某个时间点的位置,您可以从origin/branchB.

因此,您需要的所有内容都需要从以下位置合并origin/branchB

git fetch
git merge origin/branchB

然后下次你需要工作branchB

git checkout branchB
git pull

此时,您将获得更新的本地副本。虽然有一些方法可以在不签出的情况下完成,但这很少有用,并且在某些情况下可能不安全。现有的答案涵盖了这一点。

详细解答:

git pull进行提取+合并。下面两个命令大致相同,其中<remote>通常是origin(默认),远程跟踪分支以<remote>/远程分支名称开头:

git fetch [<remote>]
git merge @{u}

表示法@{u}是为当前分支配置的远程跟踪分支。如果branchB轨道,origin/branchB@{u}frombranchB与键入相同origin/branchBgit rev-parse --help有关更多信息,请参阅)。

由于您已经与 合并origin/branchB,因此缺少的是git fetch(可以从任何分支运行)来更新该远程跟踪分支。

请注意,如果在拉入 local 时创建了任何合并branchB,您应该在拉branchBbranchA后合并branchB(并最终将更改推回orign/branchB,但只要它们快进,它们将保持不变) .

请记住,在branchB您切换到本地并进行实际拉取之前,本地不会更新,但是只要没有本地提交添加到此分支,它就会保持快速转发到远程分支。

于 2020-09-16T07:05:41.333 回答
4

对于很多情况(例如合并),您可以只使用远程分支,而无需更新本地跟踪分支。在 reflog 中添加一条消息听起来有点矫枉过正,并且会阻止它变得更快。为了更容易恢复,请将以下内容添加到您的 git 配置中

[core]
    logallrefupdates=true

然后输入

git reflog show mybranch

查看您分支的最近历史记录

于 2011-12-20T00:22:02.787 回答
4

输入git-forward-merge

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

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

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

于 2014-05-29T17:15:10.183 回答
4

对于许多GitFlow用户来说,最有用的命令是:

git fetch origin master:master --update-head-ok
git fetch origin dev:dev --update-head-ok

--update-head-ok标志允许在devmaster分支时使用相同的命令。

一个方便的别名.gitconfig

[alias]
    f=!git fetch origin master:master --update-head-ok && git fetch origin dev:dev --update-head-ok
于 2020-11-25T18:32:08.910 回答
3

我为我每天在项目中遇到的类似用例编写了一个 shell 函数。这基本上是在打开 PR 之前使本地分支与公共分支(如开发)保持最新的捷径等。

即使您不想使用也发布此内容checkout,以防其他人不介意该约束。

glmh("git pull and merge here") 会自动checkout branchBpull最新,重新checkout branchA,和merge branchB

没有解决保留分支 A 的本地副本的需要,但可以通过在签出分支 B 之前添加一个步骤轻松地进行修改。就像是...

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

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

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

要设置,添加到.bashrcor.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

于 2016-02-13T00:03:02.420 回答
2

有效地做到这一点的另一种方法是:

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
于 2019-03-25T18:59:01.803 回答
2
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]

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

通过这种方式,您可以在同一个 git 存储库中跟踪两个单独的分支,因此您只需获取一次即可在两个工作树中获取更新(而不是必须 git clone 两次并在每个分支上 git pull)

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

当你想删除它时,你可以清理

git worktree remove [-f] <worktree>
于 2019-08-12T18:05:23.417 回答
1

只是为了拉主人而不检查我使用的主人

git fetch origin master:master

于 2019-08-29T07:58:48.787 回答
1

在没有git checkout. @grego的worktree回答是一个很好的提示。对此进行扩展:

cd local_repo
git worktree add _master_wt master
cd _master_wt
git pull origin master:master
git merge --no-ff -m "merging workbranch" my_work_branch
cd ..
git worktree remove _master_wt

您现在已将本地工作分支合并到本地master分支,而无需切换结帐。

于 2019-10-30T17:22:02.740 回答
1

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

# 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"
于 2019-12-11T05:42:51.703 回答
-2

你可以简单地git pull origin branchB进入你的branchA和 git 会为你做的伎俩。

于 2020-08-09T12:22:07.047 回答