チェックアウトを使用せずにGitブランチをマージ、更新、プルする


629

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

git merge origin/branchB

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

git checkout branchB
git pull
git checkout branchA

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



1
最初のリンクされた質問に対するJakubの回答は、これが一般に不可能である理由を説明しています。別の(事後)説明は、ベアリポジトリにマージできないため、明らかに作業ツリーが必要です。
Cascabel 2010

3
@Eric:一般的な理由は、大規模なリポジトリではチェックアウトに時間がかかり、同じバージョンに戻った場合でもタイムスタンプが更新されるため、すべてを再構築する必要があるとmakeが考えているためです。
Cascabel 2010

私がリンクした2番目の質問は、異常なケースについて尋ねています。マージは早送りである可能性がありますが、OPは--no-ffオプションを使用してマージしたいため、マージコミットが記録されます。あなたがそれに興味があるなら、そこの私の答えはあなたがそれをどのように行うことができるかを示しています-ここに投稿した私の答えほど堅牢ではありませんが、2つの長所は確かに組み合わせることができます。
Cascabel 2010

回答:


972

短い答え

早送りマージを実行している限り、次のコマンドを使用できます

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:master:これにより、ローカルmasterがと同じ場所に早送りされupstream/masterます。

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

git fetch(非)早送りマージの構文

必要であればfetch更新は非早送りである場合、コマンドは失敗し、その後、あなたは、単にフォームのrefspecを使用します

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

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

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

次のコマンドを使用して、ローカルのリポジトリを「リモート」パラメータとして渡すことができます.

git fetch . <sourceBranch>:<destinationBranch>

ドキュメンテーション

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

<refspec>

<refspec>パラメータの形式は、オプションのプラス+、ソース参照<src>、コロン:、宛先参照の順<dst>です。

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

こちらもご覧ください

  1. 作業ツリーに触れずにGitチェックアウトとマージ

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


3
git checkout --quiet HEADgit checkout --quiet --detachGit 1.7.5のよう。
Rafa

6
私は私がしなければならなかったが見つかりました:git fetch . origin/foo:foo私の地元の起源/ fooへの私の地元のFOOを更新する
ウェストン

3
「git checkout HEAD --quiet」と「git checkout --quiet-」の部分が長い答えには含まれているが、短い答えには含まれていない理由はありますか?git pullを実行できたとしても、マスターをチェックアウトしたときにスクリプトが実行される可能性があるためでしょうか。
Sean

8
ここで「マージ」を行うコマンドを「フェッチ」するのはなぜですか...意味がありません。「pull」が「fetch」の後に「merge」が続く場合、「fetch」がすでに実行されています。
Ed Randall

1
git checkout -トリックをありがとう!と同じくらい簡単cd -です。
スティード

84

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

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

git branch -f branch-b branch-a

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

-fオプションの略--force、それを使用する際に注意しなければならないことを意味します。

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


46
マージが早送りになることを確実に確認しない限り、これを行わないように十分注意してください。後でコミットを誤った場所に配置したことに気付かないでください。
Cascabel 2010

@FuadSaud正確ではありません。git reset現在チェックアウトされているブランチでのみ機能します。
アンバー

9
同じ結果(早送り)はgit fetch upstream branch-b:branch-bこの回答から取得)によって達成されます。
オリバー

6
@Oliverのコメントを拡張するには、次のようにすることもできます git fetch <remote> B:A、BとAが完全に異なるブランチである、BをAに早送りマージできます。ローカルリポジトリを.リモートエイリアスとして使用する「リモート」として渡すこともできます。git fetch . B:A

8
OPの質問は、マージが実際に早送りになることをかなり明確にします。とにかく、branch -fあなたが指摘するように、危険である可能性があります。使用しないでください。を使用しますfetch origin branchB:branchB。これは、マージが早送りでない場合に安全に失敗します。
ベネットマケルウィー2016年

30

アンバーが言ったように、早送りマージは、おそらくこれを行うことができる唯一のケースです。他のマージでは、おそらく、3者間マージ全体、パッチの適用、競合の解決を行う必要があります。つまり、ファイルが存在する必要があります。

私はたまたまこのために使用するスクリプトを持っています。作業ツリーに触れずに早送りマージを実行します(HEADにマージしている場合を除く)。少なくとも少し堅牢であるため、少し長くなります。マージが早送りになることを確認し、ブランチをチェックアウトせずに実行しますが、同じ結果を生成します。diff --stat変更の概要、およびreflogのエントリは、を使用した場合に取得される「リセット」のものではなく、高速転送マージとまったく同じですbranch -f。名前を付けてgit-merge-ffbinディレクトリにドロップすると、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誰かがそのスクリプトに問題を見つけたら、コメントしてください!それは書き忘れの仕事でしたが、私はそれを改善したいと思います。


比較のために、stackoverflow.com / a / 5148202/717355に興味があるかもしれません
Philip Oakley、

@PhilipOakley簡単に見てみると、コア部分は私のものとまったく同じです。しかし、私は1組のブランチにハードコードされていません。より多くのエラー処理があり、git-mergeの出力をシミュレートします。実際にブランチ上にいるときにそれを呼び出すと、それが意味します。
Cascabel 2012年

私は自分の\ binディレクトリにあなたのものを持っています;-)私はそれを忘れて周りを見回して、そのスクリプトを見て、それが私の思い出を促しました!私のコピーにはこのリンクがあり# or git branch -f localbranch remote/remotebranch、ソースとオプションを思い出させます。他のリンクへのコメントを+1してください。
Philip Oakley

3
+1は、"$branch@{u}"kernel.org/pub/software/scm/git/docs/gitrevisions.htmlから)上流ブランチを取得するためにマージする
コミットメント

ありがとう!簡単な例の使用法を答えに入れることができる可能性はありますか?
nmr

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が提供されるかどうかを確認してください。)


4
早送りできない場合に失敗させる方法はありますか?
gman 2012年

2
@gmanを使用できますgit merge-base --is-ancestor <A> <B>。「B」は「A」にマージする必要があるものです。例としては、A = masterとB = developがあり、developがmasterに早送りできることを確認します。注:ff-ableでなければ0で存在し、ff-ableであれば1で存在します。
eddiemoya 2014

1
git-scmには記載されていませんが、kernal.orgにあります。 kernel.org/pub/software/scm/git/docs/git-merge-base.html
eddiemoya 14

私が話していることを簡単にまとめました(実際にはテストしませんでしたが、ほとんどそこに行くはずです)。gist.github.com/eddiemoya/ad4285b2d8a6bdabf432 ---- 付記として、私はこれのほとんどを便利に使っていました。ffをチェックし、そうでない場合はまず目的のブランチをリベースしてから、ffマージするスクリプトを持っています-何もチェックアウトせずにすべて。
eddiemoya 2014

12

あなたの場合、あなたは使うことができます

git fetch origin branchB:branchB

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

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

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

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

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

git fetch . remotes/origin/master:master

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


11

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

git fetch remote
git branch -f localbranch remote/remotebranch

これはローカルの古いブランチを捨て、同じ名前のブランチを再作成するので、注意して使用してください...


元の回答ですでにブランチ-fが言及されていることを確認しました。それでも、最初に説明したユースケースのreflogにマージすることには利点がありません。
kkoehne

7

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


4

git-forward-mergeと入力ます。

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

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

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


2
これはgit fetch <remote> <source>:<destination>よりも優れていると思います。なぜなら、早​​送りはマージではなくフェッチの操作であり、記述が簡単だからです。悪いことに、それはデフォルトのgitにはありません。
Binarian 2017

4

多くの場合(マージなど)、ローカルトラッキングブランチを更新せずに、リモートブランチを使用できます。reflogにメッセージを追加すると、やり過ぎのように聞こえ、迅速に停止します。回復を簡単にするために、以下をgit configに追加します

[core]
    logallrefupdates=true

次に、

git reflog show mybranch

ブランチの最近の履歴を確認するには


私はセクションがあることが必要だと思い[core]ませんか[user]?(そして、デフォルトでは、ワークスペース(つまり、裸でない)を持つリポジトリの場合はオンになっています。)
eckes '25

3

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

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


2

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

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

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

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

git checkout -b new-feature origin/develop

1

使用するマスターをチェックアウトせずにマスターをプルするだけです

git fetch origin master:master


1

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


1

マージしたいブランチの1つと同じツリーを保持したい場合(つまり、実際の「マージ」ではない場合)は、次のように実行できます。

# 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"

1
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]

あなたが試すことができます git worktreeは2つのブランチを並べて開く、これはそれがあなたが望むものかもしれないように聞こえますが、私がここで見た他のいくつかの答えとは非常に異なります。

この方法では、同じgitリポジトリで2つの別々のブランチを追跡できるため、両方の作業ツリーで更新を取得するために1回フェッチするだけで済みます(2回git cloneしてgit pullする必要はありません)。

Worktreeは、コードの新しい作業ディレクトリを作成します。ここでは、ブランチを所定の場所で交換する代わりに、別のブランチを同時にチェックアウトできます。

あなたがそれを取り除きたいとき、あなたは片付けることができます

git worktree remove [-f] <worktree>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.