不使用 checkout 合并、更新和拉取 Git 分支

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3216360/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 08:42:55  来源:igfitidea点击:

Merge, update, and pull Git branches without using checkouts

gitgit-mergegit-pullgit-checkout

提问by charles

I work on a project that has 2 branches, A and B. I typically work on branch A, and merge stuff from branch B. For the merging, I would typically do:

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

git merge origin/branchB

However, I would also like to keep a local copy of branch B, as I may occasionally check out the branch without first merging with my branch A. For this, I would do:

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

git checkout branchB
git pull
git checkout branchA

Is there a way to do the above in one command, and without having to switch branch back and forth? Should I be using git update-reffor that? How?

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

回答by

The Short Answer

简短的回答

As long as you're doing a fast-forwardmerge, then you can simply use

只要你在做一个快进合并,那么你可以简单地使用

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

Examples:

例子:

# 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

While Amber's answerwill also work in fast-forward cases, using git fetchin this way instead is a little safer than just force-moving the branch reference, since git fetchwill automatically prevent accidental non-fast-forwards as long as you don't use +in the refspec.

虽然Amber的答案也将在快进的情况下,利用工作git fetch中,而不是这种方式不仅仅是力移动分支参考稍微安全一点,因为git fetch会自动防止意外非快进,只要你不使用+的参考规格

The Long Answer

长答案

You cannot merge a branch B into branch A without checking out A first if it would result in a non-fast-forward merge. This is because a working copy is needed to resolve any potential conflicts.

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

However, in the case of fast-forward merges, this is possible, because such merges can never result in conflicts, by definition. To do this without checking out a branch first, you can use git fetchwith a refspec.

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

Here's an example of updating master(disallowing non-fast-forward changes) if you have another branch featurechecked out:

master如果您已feature签出另一个分支,则这是一个更新示例(不允许非快进更改):

git fetch upstream master:master

This use-case is so common, that you'll probably want to make an alias for it in your git configuration file, like this one:

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

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

What this alias does is the following:

这个别名的作用如下:

  1. git checkout HEAD: this puts your working copy into a detached-head state. This is useful if you want to update masterwhile you happen to have it checked-out. I think it was necessary to do with because otherwise the branch reference for masterwon't move, but I don't remember if that's really right off-the-top of my head.

  2. git fetch upstream master:master: this fast-forwards your local masterto the same place as upstream/master.

  3. git checkout -checks out your previously checked-out branch (that's what the -does in this case).

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

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

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

The syntax of git fetchfor (non-)fast-forward merges

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

If you want the fetchcommand to fail if the update is non-fast-forward, then you simply use a refspec of the form

如果您希望fetch命令在更新为非快进时失败,那么您只需使用以下形式的 refspec

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

If you want to allow non-fast-forward updates, then you add a +to the front of the refspec:

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

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

Note that you can pass your local repo as the "remote" parameter using .:

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

git fetch . <sourceBranch>:<destinationBranch>

The Documentation

文档

From the git fetchdocumentation that explains this syntax(emphasis mine):

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

<refspec>

The format of a <refspec>parameter is an optional plus +, followed by the source ref <src>, followed by a colon :, followed by the destination ref <dst>.

The remote ref that matches <src>is fetched, and if <dst>is not empty string, the local ref that matches it is fast-forwarded using <src>. If the optional plus +is used, the local ref is updated even if it does not result in a fast-forward update.

<refspec>

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

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

See Also

也可以看看

  1. Git checkout and merge without touching working tree

  2. Merging without changing the working directory

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

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

回答by Amber

No, there is not. A checkout of the target branch is necessary to allow you to resolve conflicts, among other things (if Git is unable to automatically merge them).

不,那里没有。签出目标分支是必要的,以允许您解决冲突等(如果 Git 无法自动合并它们)。

However, if the merge is one that would be fast-forward, you don't need to check out the target branch, because you don't actually need to merge anything - all you have to do is update the branch to point to the new head ref. You can do this with git branch -f:

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

git branch -f branch-b branch-a

Will update branch-bto point to the head of branch-a.

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

The -foption stands for --force, which means you must be careful when using it.

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

Don't use it unless you are absolutely sure the merge will be fast-forward.

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

回答by Cascabel

As Amber said, fast-forward merges are the only case in which you could conceivably do this. Any other merge conceivably needs to go through the whole three-way merge, applying patches, resolving conflicts deal - and that means there need to be files around.

正如 Amber 所说,快进合并是唯一可以做到这一点的情况。可以想象,任何其他合并都需要经过整个三向合并、应用补丁、解决冲突处理——这意味着需要有文件。

I happen to have a script around I use for exactly this: doing fast-forward merges without touching the work tree (unless you're merging into HEAD). It's a little long, because it's at least a bit robust - it checks to make sure that the merge would be a fast-forward, then performs it without checking out the branch, but producing the same results as if you had - you see the diff --statsummary of changes, and the entry in the reflog is exactly like a fast forward merge, instead of the "reset" one you get if you use branch -f. If you name it git-merge-ffand drop it in your bin directory, you can call it as a git command: git merge-ff.

我碰巧有一个脚本,我正好用于此目的:在不触及工作树的情况下进行快进合并(除非您合并到 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=""
    commit=""

    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 "" "";;
    * ) _usage
esac

P.S. If anyone sees any issues with that script, please comment! It was a write-and-forget job, but I'd be happy to improve it.

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

回答by Cascabel

You can only do this if the merge is a fast-forward. If it's not, then git needs to have the files checked out so it can merge them!

如果合并是快进的,您只能这样做。如果不是,那么 git 需要检出文件,以便合并它们!

To do it for a fast-forward only:

要做到这一点的快进只

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

where <commit>is the fetched commit, the one you want to fast-forward to. This is basically like using git branch -fto move the branch, except it also records it in the reflog as if you actually did the merge.

<commit>获取的提交在哪里,您想要快进到的提交。这基本上就像git branch -f用于移动分支,除了它还将它记录在 reflog 中,就好像您确实进行了合并一样。

Please, please, pleasedon't do this for something that's not a fast-forward, or you'll just be resetting your branch to the other commit. (To check, see if git merge-base <branch> <commit>gives the branch's SHA1.)

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

回答by Bennett McElwee

In your case you can use

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

git fetch origin branchB:branchB

which does what you want (assuming the merge is fast-forward). If the branch can't be updated because it requires a non-fast-forward merge, then this fails safely with a message.

它可以满足您的需求(假设合并是快进的)。如果分支因为需要非快进合并而无法更新,那么这将安全地失败并显示一条消息。

This form of fetch has some more useful options too:

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

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

Note that <remote>can be a local repository, and <sourceBranch>can be a tracking branch. So you can update a local branch, even if it's not checked out, without accessing the network.

注意<remote>可以是本地仓库,也<sourceBranch>可以是跟踪分支。所以你可以更新一个本地分支,即使它没有被检出,而无需访问网络

Currently, my upstream server access is via a slow VPN, so I periodically connect, git fetchto update all remotes, and then disconnect. Then if, say, the remote master has changed, I can do

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

git fetch . remotes/origin/master:master

to safely bring my local master up to date, even if I currently have some other branch checked out. No network access required.

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

回答by kkoehne

Another, admittedly pretty brute way is to just re-create the branch:

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

git fetch remote
git branch -f localbranch remote/remotebranch

This throws away the local outdated branch and re-creates one with the same name, so use with care ...

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

回答by wnoise

You can clone the repo and do the merge in the new repo. On the same filesystem, this will hardlink rather than copy most of the data. Finish by pulling the results into the original repo.

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

回答by Casebash

For many cases (such as merging), you can just use the remote branch without having to update the local tracking branch. Adding a message in the reflog sounds like overkill and will stop it being quicker. To make it easier to recover, add the following into your git config

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

[core]
    logallrefupdates=true

Then type

然后输入

git reflog show mybranch

to see the recent history for your branch

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

回答by lkraider

Enter git-forward-merge:

输入git-forward-merge

Without needing to checkout destination, git-forward-merge <source> <destination>merges source into destination branch.

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

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

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

Only works for automatic merges, if there are conflicts you need to use the regular merge.

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

回答by rkd

I wrote a shell function for a similar use case I encounter daily on projects. This is basically a shortcut for keeping local branches up to date with a common branch like develop before opening a PR, etc.

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

Posting this even though you don't want to use checkout, in case others don't mind that constraint.

即使您不想使用checkout,也可以发布此内容,以防其他人不介意这种限制。

glmh("git pull and merge here") will automatically checkout branchB, pullthe latest, re-checkout branchA, and merge branchB.

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

Doesn't address the need to keep a local copy of branchA, but could easily be modified to do so by adding a step before checking out branchB. Something like...

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

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

For simple fast-forward merges, this skips to the commit message prompt.

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

For non fast-forward merges, this places your branch in the conflict resolution state (you likely need to intervene).

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

To setup, add to .bashrcor .zshrc, etc:

要设置,添加到.bashrcor.zshrc等:

glmh() {
    branchB=
    [ $# -eq 0 ] && { branchB="develop" }
    branchA="$(git branch | grep '*' | sed 's/* //g')"
    git checkout ${branchB} && git pull
    git checkout ${branchA} && git merge ${branchB} 
}

Usage:

用法:

# No argument given, will assume "develop"
> glmh

# Pass an argument to pull and merge a specific branch
> glmh your-other-branch

Note: This is notrobust enough to hand-off of args beyond branch name to git merge

注:这是不是足够强大的手断超越分支名参数的个数来git merge