在不使用签出的情况下合并,更新和拉取Git分支

时间:2010-07-09 20:32:52

标签: git git-merge git-pull git-checkout

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

git merge origin/branchB

但是,我还想保留分支B的本地副本,因为我可能偶尔会检查分支而不先与我的分支A合并。为此,我会这样做:

git checkout branchB
git pull
git checkout branchA

有没有办法在一个命令中执行上述操作,而无需来回切换分支?我应该使用git update-ref吗?怎么样?

18 个答案:

答案 0 :(得分:833)

简答

只要您正在进行快进合并,那么您只需使用

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中使用+,就意外的非快进。

长答案

如果没有先检查A,如果它会导致非快进合并,则无法将分支B合并到分支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 -'

这个别名的作用如下:

  1. git checkout HEAD:这会将您的工作副本置于分离头状态。如果您想要在签出时更新master,这非常有用。我认为有必要这样做,因为否则master的分支引用不会移动,但我不记得这是否真的是我的头脑。

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

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

  4. (非)快进合并的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>参数的格式是可选加+,后跟源引用<src>,后跟冒号:,后跟目标引用{ {1}}。

         

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

    另见

    1. Git checkout and merge without touching working tree

    2. Merging without changing the working directory

答案 1 :(得分:80)

不,没有。检查目标分支是必要的,以便您解决冲突,以及其他事情(如果Git无法自动合并它们)。

但是,如果合并是快进的,那么您不需要检查目标分支,因为您实际上不需要合并任何东西 - 您只需将分支更新为指向新的头部参考。您可以使用git branch -f

执行此操作
git branch -f branch-b branch-a

branch-b更新为指向branch-a的头部。

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

答案 2 :(得分:29)

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

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

答案 3 :(得分:19)

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

答案 4 :(得分:10)

另一种,无可否认的方法是重新创建分支:

git fetch remote
git branch -f localbranch remote/remotebranch

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

答案 5 :(得分:9)

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

git fetch origin branchB:branchB

做你想要的(假设合并是快进的)。如果由于需要非快进合并而无法更新分支,则会因消息安全失败。

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

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

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

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

git fetch . remotes/origin/master:master

安全地让我的本地主人更新,即使我目前已经检查了一些其他分支。无需网络访问。

答案 6 :(得分:6)

您可以克隆存储库并在新存储库中执行合并。在相同的文件系统上,这将硬链接而不是复制大多数数据。完成将结果拉入原始仓库。

答案 7 :(得分:3)

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

[core]
    logallrefupdates=true

然后输入

git reflog show mybranch

查看分支机构的近期历史记录

答案 8 :(得分:3)

输入 git-forward-merge

  

无需结帐目的地,git-forward-merge <source> <destination>将来源合并到目的地分支。

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

仅适用于自动合并,如果存在冲突则需要使用常规合并。

答案 9 :(得分:2)

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

  

即使您不想使用checkout,也请发布此信息,以防其他人不介意约束。

glmh (&#34; git pull and merge here&#34;)将自动checkout branchBpull最新,重新{{1 }和checkout branchA

没有解决保留branchA本地副本的需要,但可以通过在签出branchB之前添加一个步骤来轻松修改。 有点像...

merge 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} 
}
  

注意: 足以将分支名称之外的args移交给# No argument given, will assume "develop" > glmh # Pass an argument to pull and merge a specific branch > glmh your-other-branch

答案 10 :(得分:2)

问题很简单,答案也应该很简单。 OP所要求的只是将上游origin/branchB合并到其当前分支中,而无需切换分支。

TL; DR:

git fetch
git merge origin/branchB

完整答案:

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

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

@{u}表示法是当前分支的已配置远程跟踪分支。如果branchB跟踪origin/branchB,则来自@{u}的{​​{1}}与键入branchB相同(有关更多信息,请参见origin/branchB)。

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

但是请注意,如果从合并到包含的操作中存在任何合并,则应该在从git fetch进行合并之后将branchB合并到branchA中(并最终将更改推送到orign / branchB),但只要它们快进,它们就保持不变。

请记住,只有在切换到本地branchB并进行实际拉取后,该本地branchB才会更新,但是只要没有向该分支添加本地提交,它就将保持快速前进。远程分支。

答案 11 :(得分:1)

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

您可以尝试quadratic equation并排打开两个分支,这听起来像是您所要的,但与我在这里看到的其他一些答案有很大不同。

这样,您可以在同一个git repo中跟踪两个独立的分支,因此您只需获取一次即可在两个工作树中获取更新(而不是git克隆两次并git pull上一次)

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

要删除它,可以使用

进行清理。
git worktree remove [-f] <worktree>

答案 12 :(得分: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分支,而无需切换结帐。

答案 13 :(得分: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"

答案 14 :(得分:1)

对于许多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

答案 15 :(得分:0)

仅拉动主机而不检查我使用的主机

ngModel

答案 16 :(得分:0)

您可以简单地将git pull origin branchB插入branchA中,而git将为您解决问题。

答案 17 :(得分:0)

另一种有效的方法是:

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