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

cha*_*les 585 git git-pull git-merge git-checkout

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

git merge origin/branchB
Run Code Online (Sandbox Code Playgroud)

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

git checkout branchB
git pull
git checkout branchA
Run Code Online (Sandbox Code Playgroud)

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

小智 907

简答

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

git fetch <remote> <sourceBranch>:<destinationBranch>
Run Code Online (Sandbox Code Playgroud)

例子:

# 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
Run Code Online (Sandbox Code Playgroud)

虽然Amber的答案也适用于快进案例,但使用git fetch这种方式比仅仅强制移动分支参考更安全,因为git fetch只要你不使用它就会自动防止意外的非快进+.的Refspec.

答案很长

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

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

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

git fetch upstream master:master
Run Code Online (Sandbox Code Playgroud)

这个用例很常见,你可能想在你的git配置文件中为它做一个别名,比如这个:

[alias]
    sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'
Run Code Online (Sandbox Code Playgroud)

这个别名的作用如下:

  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>
Run Code Online (Sandbox Code Playgroud)

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

git fetch <remote> +<remoteBranch>:<localBranch>
Run Code Online (Sandbox Code Playgroud)

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

git fetch . <sourceBranch>:<destinationBranch>
Run Code Online (Sandbox Code Playgroud)

文档

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

<refspec>

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

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

也可以看看

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

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

  • 为什么'获取'命令在这里'合并'...它根本没有意义; 如果'pull'是'fetch'后跟'merge',那么必须有一个更合乎逻辑的'merge -ff-only'等价,它会在本地更新'origin'来自'origin/branch',因为'fetch'有已经运行了. (7认同)
  • 我发现我必须这样做:`git fetch.origin/foo:foo`将我的本地foo更新为我的本地origin/foo (6认同)
  • `git fetch uploading master:master` 太棒了!因为我不必先进行签出,而在我的存储库中签出是一项成本高昂的操作。 (4认同)
  • 从git 1.7.5开始,`git checkout --quiet HEAD`是`git checkout --quiet --detach`. (3认同)
  • 有没有理由将"git checkout HEAD --quiet"和"git checkout --quiet - "部分包含在长答案中,但不是简短的答案?我想这是因为你可以在你签出主人的时候运行脚本,即使你可以做一个git pull吗? (3认同)
  • 添加标志“-u”可确保命令成功[即使您签出了给定的分支](/sf/ask/2032008751/ a-非当前分支)。`git fetch -u origin master:master`。否则会失败并显示“致命:拒绝获取非裸存储库的当前分支 refs/heads/master” (2认同)

Amb*_*ber 82

不,那里没有.检查目标分支是必要的,以便您解决冲突等(如果Git无法自动合并它们).

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

git branch -f branch-b branch-a
Run Code Online (Sandbox Code Playgroud)

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

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

  • 除非你已经确定合并是快进的,否则要非常小心不要这样做!以后你不想意识到你错误的提交. (44认同)
  • 通过`git fetch upstream branch -b:branch-b`实现了相同的结果(快速转发)(取自[来自此答案](http://stackoverflow.com/a/17722977/177710)). (8认同)
  • OP的问题清楚地表明合并确实是快进的.无论如何,正如你所指出的那样,`branch -f`可能是危险的._所以不要使用它!_使用`fetch origin branchB:branchB`,如果合并不是快进,它将安全失败. (7认同)
  • 要扩展@Oliver的注释,您还可以执行`git fetch <remote> B:A`,其中B和A是完全不同的分支,但B可以快进合并到A中.您也可以将本地存储库作为使用`.`作为远程别名的"远程":`git fetch.B:A`. (5认同)
  • 我不同意使用 --force 来完成日常工作的建议。 (3认同)

Cas*_*bel 30

正如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
Run Code Online (Sandbox Code Playgroud)

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

  • +1,也可以默认为"$ branch @ {u}"`作为合并的提交来获取上游分支(来自http://www.kernel.org/pub/software/scm/git/docs/gitrevisions的.html) (3认同)

Cas*_*bel 19

如果合并是快进,则只能执行此操作.如果不是,那么git需要检出文件,以便合并它们!

要做到这一点的只有一个快进:

git fetch <branch that would be pulled for branchB>
git update-ref -m "merge <commit>: Fast forward" refs/heads/<branch> <commit>
Run Code Online (Sandbox Code Playgroud)

其中,<commit>被取出的承诺,你想要的快进到.这基本上就像使用git branch -f移动分支一样,除了它还将它记录在reflog中,就像你实际进行了合并一样.

请,请,不要的东西,这不是一个快进做到这一点,或者你只是被重设分支机构的其他承诺.(要检查,看看是否git merge-base <branch> <commit>给出了分支的SHA1.)

  • 如果不能快速前进,有没有办法让它失败? (4认同)
  • @gman你可以使用`git merge-base --is-ancestor <A> <B>`."B"是需要合并为"A"的东西.例子是A = master和B = develop,确保develop可以快速转发到master.注意:如果不存在则存在0,如果存在,则存在1. (2认同)

小智 10

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

git fetch remote
git branch -f localbranch remote/remotebranch
Run Code Online (Sandbox Code Playgroud)

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


Ben*_*wee 10

在你的情况下你可以使用

git fetch origin branchB:branchB
Run Code Online (Sandbox Code Playgroud)

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

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

git fetch <remote> <sourceBranch>:<destinationBranch>
Run Code Online (Sandbox Code Playgroud)

请注意,它<remote> 可以是本地存储库,也<sourceBranch>可以是跟踪分支.因此,即使未检出本地分支,也可以在不访问网络的情况下更新本地分支.

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

git fetch . remotes/origin/master:master
Run Code Online (Sandbox Code Playgroud)

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


wno*_*ise 6

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


lkr*_*der 6

输入git-forward-merge

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

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

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

  • 我认为这比 git fetch &lt;remote&gt; &lt;source&gt;:&lt;destination&gt; 更好,因为快进是合并而不是 fetch 的操作,并且更容易编写。但不好的是,它不在默认的 git 中。 (3认同)

Tho*_*est 6

问题很简单,答案应该也很简单。OP 所要求的只是在origin/branchB不切换分支的情况下将上游合并到他当前的分支中。

特尔;博士:

git fetch
git merge origin/branchB
Run Code Online (Sandbox Code Playgroud)

完整答案:

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

git fetch [<remote>]
git merge @{u}
Run Code Online (Sandbox Code Playgroud)

@{u}表示法是用于当前分支的配置的远程跟踪分支。如果branchBtrackorigin/branchB然后@{u}frombranchB与打字相同origin/branchBgit rev-parse --help有关更多信息,请参阅)。

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

但请注意,如果有从拉任何合并,包括,你倒是应该合并branchBbranchA后,成就了从拉branchB(并最终推动改变生产地/ branchB),但只要他们快进,他们将保持相同。

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


and*_*uso 6

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

git fetch origin master:master --update-head-ok
git fetch origin dev:dev --update-head-ok
Run Code Online (Sandbox Code Playgroud)

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

一个方便的别名.gitconfig

[alias]
    f=!git fetch origin master:master --update-head-ok && git fetch origin dev:dev --update-head-ok
Run Code Online (Sandbox Code Playgroud)