协慌网

登录 贡献 社区

合并,更新和拉取 Git 分支,而无需使用签出

我在一个有两个分支 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

虽然Amber 的答案在快进情况下也可以使用,但是git fetch相比强制移动分支引用要安全一些,因为git fetch就会自动防止意外的非快进。请在 refspec 中+

长答案

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

但是,在快速合并的情况下,这是可能的,因为按照定义,此类合并永远不会导致冲突。要做到这一点而无需先签出分支,可以将git fetch与 refspec 一起使用。

如果您签出了feature这是一个更新master (禁止非快速更改)的示例:

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 fetch用于(非)快进合并的语法

如果您希望fetch命令在更新不是非快进的情况下失败,那么您只需使用以下形式的 refspec 即可:

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

如果要允许非快进更新,则在 refspec 的前面+

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

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

git fetch . <sourceBranch>:<destinationBranch>

文档资料

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

<refspec>

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

获取<src>匹配的远程 ref, <dst> <src>快速转发与之匹配的本地 ref 。如果使用了可选的加号+ ,则即使不导致快速更新也将更新本地引用。

也可以看看

  1. Git 检出并合并而不触及工作树

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

不,那里没有。为了使您能够解决冲突,必须对目标分支进行检出(如果 Git 无法自动合并它们)。

但是,如果合并是快速进行的合并,则无需签出目标分支,因为您实际上不需要合并任何内容 - 您要做的就是更新分支以指向目标分支。新头号git branch -f来做到这一点:

git branch -f branch-b branch-a

将更新branch-b以点的头部branch-a

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

除非您完全确定合并将是快速进行的,否则请不要使用它。

正如 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:如果有人看到该脚本有任何问题,请发表评论!这项工作是一劳永逸的,但我很乐意加以改进。