753

私は、AとBの2つのブランチを持つプロジェクトに取り組んでいます。通常、ブランチAに取り組んでおり、ブランチBからのものをマージします。マージには、通常、次のようにします。

git merge origin/branchB

ただし、ブランチAと最初にマージせずにブランチをチェックアウトすることがあるため、ブランチBのローカルコピーも保持したいと思います。これには、次のようにします。

git checkout branchB
git pull
git checkout branchA

ブランチを前後に切り替えることなく、1つのコマンドで上記を実行する方法はありますか?そのために使用する必要がありgit update-refますか?どのように?

4

18 に答える 18

1164

短い答え

早送りマージを行っている限り、単純に使用できます

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

アンバーの答えは早送りの場合にも機能しますが、代わりにこの方法で使用する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:mastermaster: これにより、ローカルが と同じ場所に早送りされupstream/masterます。

  3. git checkout -以前にチェックアウトしたブランチをチェックアウトします (-これがこの場合の動作です)。

git fetchfor (non-)fast-forward マージの構文

fetch更新が早送りでない場合にコマンドを失敗させたい場合は、次の形式の refspec を使用するだけです。

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

非早送り更新を許可する場合+は、refspec の前に a を追加します。

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

次を使用して、ローカルリポジトリを「リモート」パラメーターとして渡すことができることに注意してください.

git fetch . <sourceBranch>:<destinationBranch>

ドキュメンテーション

git fetchこの構文を説明するドキュメントから(強調鉱山):

<refspec>

パラメータの形式<refspec>は、オプションのプラス+、その後にソース ref <src>、コロン:、宛先 ref が続きます<dst>

一致するリモート ref<src>がフェッチされ、<dst>が空の文字列でない場合は、一致するローカル ref が を使用して早送りされ<src>ます。オプションのプラス+を使用すると、早送り更新にならなくても、ローカル ref が更新されます。

関連項目

  1. ワーキングツリーに触れずにGitのチェックアウトとマージ

  2. 作業ディレクトリを変更せずにマージする

于 2013-07-18T12:06:05.933 に答える
91

いいえ、ありません。ターゲット ブランチのチェックアウトは、特に競合を解決できるようにするために必要です (Git が競合を自動的にマージできない場合)。

ただし、マージが早送りになるものである場合は、実際には何もマージする必要がないため、ターゲット ブランチをチェックアウトする必要はありません。ブランチを更新して、新しいヘッド参照。あなたはこれを行うことができますgit branch -f

git branch -f branch-b branch-a

branch-bの頭を指すように更新されbranch-aます。

この-fオプションは の略で--force、使用する際には注意が必要です。

マージが早送りされることが確実でない限り、使用しないでください。

于 2010-11-11T17:10:31.610 に答える
32

Amber が言ったように、早送りマージは、おそらくこれを行うことができる唯一のケースです。他のマージでは、パッチの適用、競合の解決という 3 方向のマージ全体を実行する必要があると考えられます。つまり、周りにファイルが必要です。

たまたま、まさにこれに使用するスクリプトがあります。作業ツリーに触れずに早送りマージを実行します (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誰かがそのスクリプトに問題がある場合は、コメントしてください! それは書いて忘れる仕事でしたが、私はそれを改善したいと思います.

于 2010-11-11T17:40:57.950 に答える
20

これは、マージが早送りの場合にのみ実行できます。そうでない場合、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 を指定するかどうかを確認してください。)

于 2010-07-10T00:59:52.940 に答える
16

あなたの場合、使用できます

git fetch origin branchB:branchB

これはあなたが望むことをします(マージが早送りであると仮定します)。非早送りマージが必要なためにブランチを更新できない場合、これは安全に失敗し、メッセージが表示されます。

この形式のフェッチには、さらに便利なオプションがいくつかあります。

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

<remote> ローカル リポジトリ<sourceBranch>することも、追跡ブランチにすることもできます。そのため、ローカル ブランチがチェックアウトされていなくても、ネットワークにアクセスせずに更新できます。

現在、アップストリーム サーバーへのアクセスは低速の VPN 経由であるため、定期的に接続git fetchしてすべてのリモートを更新し、その後切断しています。次に、たとえば、リモートマスターが変更された場合、私はできる

git fetch . remotes/origin/master:master

現在、他のブランチをチェックアウトしている場合でも、ローカル マスターを安全に最新の状態に保つことができます。ネットワーク アクセスは必要ありません。

于 2015-11-17T00:07:28.237 に答える
12

別の確かに非常に野蛮な方法は、ブランチを再作成することです。

git fetch remote
git branch -f localbranch remote/remotebranch

これにより、ローカルの古いブランチが破棄され、同じ名前のブランチが再作成されるため、注意して使用してください...

于 2011-06-08T10:08:14.440 に答える
7

リポジトリを複製して、新しいリポジトリでマージを行うことができます。同じファイルシステムで、これはほとんどのデータをコピーするのではなく、ハードリンクします。結果を元のリポジトリにプルして終了します。

于 2010-11-11T18:00:02.837 に答える
4

多くの場合 (マージなど)、ローカルの追跡ブランチを更新することなく、リモート ブランチを使用できます。reflog にメッセージを追加するのはやり過ぎのように聞こえ、処理が速くならなくなります。回復を容易にするために、以下を git config に追加します。

[core]
    logallrefupdates=true

次に、入力します

git reflog show mybranch

ブランチの最近の履歴を表示するには

于 2011-12-20T00:22:02.787 に答える
4

git-forward-merge を入力してください:

宛先をチェックアウトする必要なく、git-forward-merge <source> <destination>ソースを宛先ブランチにマージします。

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

通常のマージを使用する必要がある競合がある場合は、自動マージでのみ機能します。

于 2014-05-29T17:15:10.183 に答える
3

プロジェクトで毎日遭遇する同様のユースケース用のシェル関数を作成しました。これは基本的に、PR を開く前に、develop などの共通ブランチでローカル ブランチを最新の状態に保つためのショートカットです。

checkout他の人がその制約を気にしない場合に備えて、使用したくない場合でもこれを投稿してください。

glmh(「git pull and merge here」) はcheckout branchBpull最新、再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

于 2016-02-13T00:03:02.420 に答える
2

これを効果的に行う別の方法は次のとおりです。

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

小文字なので-d、データがまだどこかに存在する場合にのみ削除されます。強制しないことを除いて、@ kkoehneの答えに似ています。そのため-t、リモートを再度セットアップします。

プルリクエストをマージした後、新しい機能ブランチdevelop(または)を作成するという OP とは少し異なるニーズがありました。masterこれは力を入れずにワンライナーで実現できますが、ローカルdevelopブランチは更新されません。新しいブランチをチェックアウトして、それをベースにするだけorigin/developです:

git checkout -b new-feature origin/develop
于 2019-03-25T18:59:01.803 に答える
1

を使用せずに、早送りでないマージであっても、マージを実行することは絶対に可能ですgit checkout。@gregoworktreeによる回答は良いヒントです。それを拡張するには:

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チェックアウトを切り替えることなく、ローカル作業ブランチをローカル ブランチにマージしました。

于 2019-10-30T17:22:02.740 に答える