私は、AとBの2つのブランチを持つプロジェクトに取り組んでいます。通常、ブランチAで作業し、ブランチBからのものをマージします。マージのために、通常、私は行います。
git merge Origin/branchB
ただし、最初にブランチAとマージせずにブランチをチェックアウトすることがあるので、ブランチBのローカルコピーも保持したいと思います。このために、次のようにします。
git checkout branchB
git pull
git checkout branchA
上記のことを1つのコマンドで実行する方法はありますか。分岐を前後に切り替える必要はありません。そのためには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
を使用する方がブランチ参照を強制移動するよりも少し安全です。 refspecでgit fetch
を使用していない限り、+
は、偶発的な早送りを自動的に防止します。
早送りではないマージになる場合は、最初にAをチェックアウトせずにブランチBをブランチAにマージすることはできません。これは、潜在的な競合を解決するために作業コピーが必要だからです。
しかし、早送りマージの場合は、が可能です。定義上、このようなマージでは競合が発生しないからです。最初にブランチをチェックアウトせずにこれを行うには、refspecとともにgit fetch
を使用できます。
別のブランチmaster
がチェックアウトされている場合にfeature
を更新する(早送り以外の変更を許可しない)例を示します。
git fetch upstream master:master
このユースケースは非常に一般的であるため、git設定ファイルでエイリアスを作成したいと思うでしょう。
[alias]
sync = !sh -c 'git checkout --quiet HEAD; git fetch upstream master:master; git checkout --quiet -'
この別名が行うことは以下のとおりです。
git checkout HEAD
:作業コピーをデタッチヘッド状態にします。チェックアウトしている間にmaster
を更新したい場合に便利です。そうでなければmaster
のブランチ参照は動かないので、それを行う必要があると思いますが、それが本当に頭のすぐそばにあるのかどうか覚えていません。
git fetch upstream master:master
:これはあなたのローカルのmaster
をupstream/master
と同じ場所に早送りします。
git checkout -
は以前にチェックアウトしたブランチをチェックアウトします(これが-
の場合の動作です)。
git fetch
の構文更新が早送りでない場合にfetch
コマンドを失敗させたい場合は、単に次の形式のrefspecを使用します。
git fetch <remote> <remoteBranch>:<localBranch>
早送り以外の更新を許可する場合は、refspecの先頭に+
を追加します。
git fetch <remote> +<remoteBranch>:<localBranch>
.
を使用して、あなたのローカルレポを "remote"パラメータとして渡すことができることに注意してください。
git fetch . <sourceBranch>:<destinationBranch>
この構文を説明する git fetch
のドキュメント から(強調表示)
<refspec>
<refspec>
パラメーターのフォーマットは、オプションのプラス+
、それに続くソース参照<src>
、それに続くコロン:
、およびそれに続く宛先参照<dst>
です。
<src>
に一致するリモート参照が取得され、<dst>
が空の文字列でない場合は、それに一致するローカル参照が<src>
を使用して転送されます。 。オプションのプラス+
が使用されると、ローカル参照は早送り更新にならない場合でも更新されます。
いいえ、ありません。特にGitが自動的にそれらをマージすることができない場合、ターゲットブランチのチェックアウトはあなたが衝突を解決することを可能にするために必要です。
しかし、マージが早送りである場合は、実際には何もマージする必要がないため、ターゲットブランチをチェックアウトする必要はありません。ブランチを更新して、新しいヘッドリファレンスgit branch -f
でこれを行うことができます。
git branch -f branch-b branch-a
branch-b
をbranch-a
の先頭を指すように更新します。
-f
オプションは--force
を表します。つまり、使用するときは注意が必要です。マージが早送りされることが確実でない限り、使用しないでください。
アンバーが言ったように、早送りマージはあなたがおそらくこれをすることができる唯一のケースです。他のマージでは、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
P.S誰かがそのスクリプトに関する問題を見た場合、コメントしてください!それは書き込み忘れの仕事でしたが、私はそれを改善して幸せです。
これが可能なのは、マージが早送りの場合だけです。そうでない場合は、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を与えるかどうかを確認してください。)
もう1つの、明らかにかなり野蛮な方法は、単にブランチを再作成することです。
git fetch remote
git branch -f localbranch remote/remotebranch
これはローカルの古いブランチを捨てて同じ名前のものを作り直すので、注意して使ってください...
あなたのケースではあなたが使用することができます
git fetch Origin branchB:branchB
これはあなたが望むことをする(マージが早送りであると仮定して)。早送り以外のマージが必要なためにブランチを更新できない場合は、メッセージが表示されて安全に失敗します。
この形式のフェッチには、さらに便利なオプションがいくつかあります。
git fetch <remote> <sourceBranch>:<destinationBranch>
<remote>
をローカルリポジトリに、<sourceBranch>
を追跡ブランチにすることができます。そのため、チェックアウトしていなくてもローカルブランチをネットワークにアクセスせずに更新できます。
現在、私のアップストリームサーバーへのアクセスは遅いVPNを経由しているので、定期的に接続し、すべてのリモートを更新するためにgit fetch
を接続してから切断します。それなら、もしリモートマスターが変わったら、私はそうすることができます
git fetch . remotes/Origin/master:master
現在他のブランチをチェックアウトしている場合でも、ローカルマスターを安全に最新の状態にするため。ネットワークアクセスは必要ありません。
リポジトリを複製して、新しいリポジトリでマージを実行できます。同じファイルシステムでは、これはデータの大部分をコピーするのではなくハードリンクします。結果を元のリポジトリにプルして終了します。
多くの場合(マージなど)、ローカルトラッキングブランチを更新しなくてもリモートブランチを使用できます。 reflogにメッセージを追加することはやり過ぎであるように聞こえ、速くなるのを止めます。回復しやすくするために、git configに以下を追加してください。
[core]
logallrefupdates=true
それからタイプ
git reflog show mybranch
あなたのブランチの最近の歴史を見るために
git-forward-mergeを入力してください。
宛先をチェックアウトする必要なしに、
git-forward-merge <source> <destination>
はソースを宛先ブランチにマージします。
https://github.com/schuyler1d/git-forward-merge
通常のマージを使用する必要がある場合は、自動マージでのみ機能します。
私は毎日プロジェクトで遭遇する同様のユースケースのためにシェル関数を書きました。これは基本的に、ローカルブランチをPRを開く前にDevelopのような共通のブランチで最新に保つための近道です。
他の人がその制約を気にしないのであれば、
checkout
を使いたくないとしてもこれを投稿してください。
glmh
( "git pull and merge here")は自動的にcheckout branchB
、pull
、最新のもの、re -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
に渡すのに十分なほど堅牢ではありません
これを効果的に行う別の方法は、次のとおりです。
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
使用するマスターをチェックアウトせずにマスターをプルするだけです
git fetch Origin master:master
git worktree add [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<commit-ish>]
git worktree
を試して、2つのブランチを並べて開くことができます。これは、あなたが何をしているように聞こえるかもしれませんが、私がここで見た他の回答のいくつかとは非常に異なっています。
この方法では、同じgitリポジトリで追跡する2つの別々のブランチを持つことができるため、両方の作業ツリーで更新を取得するために一度フェッチするだけです(git cloneを2回行い、それぞれをgit pullする必要はありません)
Worktreeは、コードの新しい作業ディレクトリを作成します。このディレクトリでは、ブランチを所定の場所にスワップする代わりに、異なるブランチを同時にチェックアウトできます。
あなたがそれを削除したいとき、あなたはでクリーンアップすることができます
git worktree remove [-f] <worktree>