代码之家  ›  专栏  ›  技术社区  ›  charles

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

  •  519
  • charles  · 技术社区  · 14 年前

    我从事的项目有两个分支,a和B。我通常在分支A上工作,合并分支B的内容。对于合并,我通常会做:

    git merge origin/branchB
    

    不过,我也想保留分支机构B的本地副本,因为我可能偶尔会签出分支机构而不首先与分支机构a合并。为此,我会:

    git checkout branchB
    git pull
    git checkout branchA
    

    git update-ref 为了这个?怎么用?

    10 回复  |  直到 10 年前
        1
  •  1096
  •   Community CDub    4 年前

    简短的回答

    合并,然后您可以简单地使用

    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获取 + 在refspec中。

    长话短说

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

    然而, 在快进合并的情况下,这是可能的 git获取

    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 主人 不会动的,但我不记得那是不是真的从我脑子里蹦出来了。

    2. git fetch upstream master:master :这将快进您的本地 到同一个地方 upstream/master .

    3. git checkout - - 在这种情况下是这样的)。

    的语法 git获取 对于(非)快进合并

    如果你想要 fetch

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

    +

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

    . :

    git fetch . <sourceBranch>:<destinationBranch>
    

    git fetch documentation that explains this syntax (我的重点):

    <refspec>

    文件的格式 <参考规范> + <src> : ,后跟目标引用 <dst> .

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

    1. Git checkout and merge without touching working tree

    2. Merging without changing the working directory

        2
  •  86
  •   Community CDub    4 年前

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

    但是,如果合并是一个快速前进的分支,您不需要签出目标分支,因为您实际上不需要合并任何东西—您所要做的就是更新分支以指向新的head ref。您可以使用 git branch -f :

    git branch -f branch-b branch-a
    

    branch-b 指向…的头 branch-a .

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

    不要使用它,除非你绝对确定合并会很快进行。

        3
  •  31
  •   Cascabel    11 年前

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

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

        4
  •  20
  •   Cascabel    10 年前

    只有在合并是快进的情况下才能这样做。如果没有,那么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。)

        5
  •  14
  •   Bennett McElwee    9 年前

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

    git fetch origin branchB:branchB
    

    这就是你想要的(假设合并是快速进行的)。如果分支由于需要非快进合并而无法更新,则此操作会安全地失败,并显示一条消息。

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

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

    请注意 <remote> 可以是本地存储库 ,和 <sourceBranch> 不访问网络 .

    git fetch 更新所有遥控器,然后断开连接。那么,如果,比如说,远程主机发生了变化,我可以这样做

    git fetch . remotes/origin/master:master
    

    安全地使我的本地主人更新,即使我目前有一些其他分支机构检查了。不需要网络访问。

        6
  •  11
  •   kkoehne    14 年前

    另一种,公认的非常残忍的方法是重新创建分支:

    git fetch remote
    git branch -f localbranch remote/remotebranch
    

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

        7
  •  7
  •   wnoise    14 年前

    您可以克隆回购并在新回购中进行合并。在同一个文件系统上,这将硬链接而不是复制大部分数据。最后将结果拉入原始回购。

        8
  •  4
  •   lkraider    11 年前

    输入 :

    git-forward-merge <source> <destination> 将源合并到目标分支。

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

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

        9
  •  4
  •   eckes    10 年前

    [core]
        logallrefupdates=true
    

    然后键入

    git reflog show mybranch
    

        10
  •  4
  •   Thomas Guyot-Sionnest    4 年前

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

    热释光;博士:

    git fetch
    git merge origin/branchB
    

    完整答案:

    git pull 执行fetch+合并。下面的两个命令大致相同,其中 <remote> origin (默认),远程跟踪分支以 <remote>/

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

    这个 @{u} 表示法是为当前分支配置的远程跟踪分支。如果 branchB 起源/分支 然后 @{u} 起源/分支 (见 git rev-parse --help

    因为你已经和 起源/分支 git fetch (可以从任何分支运行)来更新远程跟踪分支。

    但是请注意,如果pull-to-include中有任何合并,您应该更愿意合并 布兰奇 branchA 在做了一个从 布兰奇 (并最终推动对orign/branchB的更改),但只要它们快速前进,它们将保持不变。

    记住当地的 除非您切换到它并执行实际的拉操作,否则不会更新,但是只要没有本地提交添加到此分支,它将保持到远程分支的快进。

        11
  •  3
  •   rkd    9 年前

    即使您不想使用 checkout ,以防其他人不介意这种限制。

    glmh (“git pull and merge here”)将自动 checkout branchB , pull 最新的,关于- checkout branchA ,和 merge branchB .

    没有解决保留branchA的本地副本的需要,但是可以通过在签出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} 
    }
    

    用法:

    # No argument given, will assume "develop"
    > glmh
    
    # Pass an argument to pull and merge a specific branch
    > glmh your-other-branch
    

    注意:这是 足够健壮,可以将分支名称以外的参数传递给 git merge

        12
  •  2
  •   dwj    6 年前

    另一种有效的方法是:

    git fetch
    git branch -d branchB
    git branch -t branchB origin/branchB
    

    因为它是小写的 -d -t 它将再次设置遥控器。

    我的需求与OP略有不同,后者是创建一个新的特性分支 develop master ),合并请求后。这可以在一个无需用力的内衬中完成,但它不会更新局部 发展 分支机构。这只是一个检查出一个新的分支,并有它的基础上 origin/develop :

    git checkout -b new-feature origin/develop
    
        13
  •  2
  •   grego    5 年前
    git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
    

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

    通过这种方式,您可以在同一个git repo中跟踪两个独立的分支,这样您只需获取一次就可以在两个工作树中获取更新(而不必两次git克隆和每次git拉取)

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

    当你想移除它时,你可以用

    git worktree remove [-f] <worktree>
    
        14
  •  2
  •   andruso    4 年前

    GitFlow 用户最有用的命令是:

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

    --update-head-ok 标志允许在打开时使用相同的命令 dev master

    .gitconfig :

    [alias]
        f=!git fetch origin master:master --update-head-ok && git fetch origin dev:dev --update-head-ok
    
        15
  •  1
  •   Prince Sodhi    5 年前

    只是为了不检查我使用的主机就拔出主机

    git fetch origin master:master

        16
  •  1
  •   zanerock    5 年前

    git checkout . 这个 worktree @grego的回答是一个很好的暗示。在此基础上展开:

    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 分支而不切换您的结帐。

        17
  •  1
  •   phkb    5 年前

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

    # 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"
    
        18
  •  0
  •   paulomc    4 年前

    git pull origin branchB 进入你的 branchA