あるブランチを別のブランチのようにするためのgitコマンド


81

変更を加えたブランチを取得して、分岐元のアップストリームと同じになるように戻そうとしています。変更は、両方のローカルであり、そのどちらも、githubのにプッシュされていないgit resetgit rebase、彼らはすでにプッシュされていた分岐と悪いことである、歴史を変更するので、実際に実行可能です。

私もgit mergeさまざまな戦略を試しましたが、ローカルの変更を元に戻すことはできません。つまり、ファイルを追加した場合、マージによって他のファイルが元に戻る可能性がありますが、アップストリームにはないファイルが残っています。持ってる。

アップストリームから新しいブランチを作成することもできますが、リビジョン履歴の観点からすべての変更を適用してブランチを取得し、再びアップストリームと同一にするマージが本当に必要です。これにより、その変更を安全にプッシュできます。歴史を壊すことなく。そのようなコマンドまたは一連のコマンドはありますか?


9
変更を保持する必要がない場合は、ブランチを削除して再作成してみませんか?「プロジェクトの歴史」は神聖である必要はありません。Gitは、開発者のコ​​ミュニケーションを支援するツールです。これらの変更で問題が解決しない場合は、破棄してください。
wnoise 2011

変更はすでに統合された場合は特に- 。100 @wnoise
wuputah

7
コラボレーションのために公開されているので、歴史を保存することに関心があります。また、歴史に戻りたいと思うかもしれません。最新のものだけを保持しているのに、なぜわざわざリビジョン管理を使用するのですか?
Arne Claassen 2011

1
これは主観的な議論ですが、私にとって、VCSの目的は、プロジェクトの履歴のすべての細目を記録することではなく、コンテンツ(コミット)への変更を記録することだけであり、ツリー/履歴に基づいて操作できるようにしますそれらのコミット(分岐、マージ、リベース、リセットなど)で、履歴(差分、ログ、非難など)に基づいてレポートを表示できるようにします。gitは「ばかげたコンテンツトラッカー」です-私はそれをタイムマシンではなく、ソースコードを管理するためのツールと見なしています。
wuputah 2011

5
あなたが言ったように、それは主観的です。私は、放棄されたアプローチを確認できること、および過去のある時点でどのような決定が行われたかを確認できることに関心があります。そして、何かを放棄するという私の決定が、他の人が指しているかもしれないマージポイントを破壊しないことを私は気にかけています。
Arne Claassen 2011

回答:


107

あなたは、あなたにあなたの上流のブランチをマージすることができdevて、枝カスタムマージドライバ「keepTheirs」
参照してください「git merge -s theirs』必要-私はそれが存在しません知っています」。
あなたの場合、.gitattributes必要なのは1つだけで、次のkeepTheirsようなスクリプトが必要です。

mv -f $3 $2
exit 0

git merge --strategy=theirs シミュレーション#1

アップストリームを最初の親として、マージとして表示されます。

Jefromimerge -s ours、アップストリーム(またはアップストリームから開始する一時ブランチ)で作業をマージし、そのマージの結果にブランチを早送りすることで、(コメントで)言及しています。

git checkout -b tmp origin/upstream
git merge -s ours downstream         # ignoring all changes from downstream
git checkout downstream
git merge tmp                        # fast-forward to tmp HEAD
git branch -D tmp                    # deleting tmp

これには、アップストリームの祖先を最初の親として記録するという利点があります。そのため、マージは、「このトピックブランチを破棄してアップストリームに置き換える」のではなく、「この古いトピックブランチを吸収する」ことを意味します。

(2011年編集):

このワークフローは、OPによってこのブログ投稿で報告されています:

なぜこれがまた欲しいのですか?

私のレポジトリがパブリックバージョンとは関係がない限り、これはすべて問題ありませんでしたが、今から他のチームメンバーや外部の貢献者とWIPでコラボレーションできるようにしたいので、パブリックブランチが他の人が分岐してプルするのに信頼性があります。つまり、リモートバックアップにプッシュしたものをリベースしたりリセットしたりする必要はありません。これは、GitHubとパブリックにあるためです。

だから、それは私がどのように進むべきかを私に残します。
99%の確率でコピーがアップストリームマスターに送られるので、ほとんどの場合、マスターを操作してアップストリームにプッシュしたいと思います。
しかし、時々、私が持っているwipものは上流に入るものによって無効になり、私は私の一部を放棄しwipます。
その時点で、マスターをアップストリームと同期させたいのですが、公開されているマスターのコミットポイントを破棄したくありません。つまり、コピーをアップストリームと同一にするチェンジセットで終わるアップストリームとのマージが必要です
そして、それがgit merge --strategy=theirsすべきことです。


git merge --strategy=theirs シミュレーション#2

最初の親として私たちのものとのマージとして表示されます。

jcwengerによって提案されました

git checkout -b tmp upstream
git merge -s ours thebranch         # ignoring all changes from downstream
git checkout downstream
git merge --squash tmp               # apply changes from tmp but not as merge.
git rev-parse upstream > .git/MERGE_HEAD #record upstream 2nd merge head
git commit -m "rebaselined thebranch from upstream" # make the commit.
git branch -D tmp                    # deleting tmp

git merge --strategy=theirs シミュレーション#3

このブログ投稿は言及しています

git merge -s ours ref-to-be-merged
git diff --binary ref-to-be-merged | git apply -R --index
git commit -F .git/COMMIT_EDITMSG --amend

履歴に「がらくた」があるためではなく、リベースを回避する必要があるパブリックリポジトリでの開発のベースラインを変更したい場合は、これを実行したい場合があります。


git merge --strategy=theirs シミュレーション#4

(同じブログ投稿)

あるいは、ローカルのアップストリームブランチを早送り可能に保ちたい場合、潜在的な妥協案は、sid / unstableの場合、アップストリームブランチが時々リセット/リベースされる可能性があることを理解して作業することです(最終的にアウトになるイベントに基づいて)上流プロジェクト側のあなたのコントロールの)。
これは大したことではなく、この仮定を使用すると、ローカルのアップストリームブランチを早送りの更新のみが必要な状態に保つのが簡単になります。

git branch -m upstream-unstable upstream-unstable-save
git branch upstream-unstable upstream-remote/master
git merge -s ours upstream-unstable
git diff --binary ref-to-be-merged | git apply -R --index --exclude="debian/*"
git commit -F .git/COMMIT_EDITMSG --amend

git merge --strategy=theirs シミュレーション#5

バラクA.パールマターによって提案された):

git checkout MINE
git merge --no-commit -s ours HERS
git rm -rf .
git checkout HERS -- .
git checkout MINE -- debian # or whatever, as appropriate
git gui # edit commit message & click commit button

git merge --strategy=theirs シミュレーション#6

(同じMichael Gebetsroitherによって提案されました):

Michael Gebetsroitherは、私が「不正行為をしている」と主張して、チャイムを鳴らしました;)そして、より低いレベルの配管コマンドで別の解決策を与えました:

(git onlyコマンドでそれが不可能な場合はgitではありません。diff/ patch / applyを使用したgitのすべてが実際の解決策ではありません;)。

# get the contents of another branch
git read-tree -u --reset <ID>
# selectivly merge subdirectories
# e.g superseed upstream source with that from another branch
git merge -s ours --no-commit other_upstream
git read-tree --reset -u other_upstream     # or use --prefix=foo/
git checkout HEAD -- debian/
git checkout HEAD -- .gitignore
git commit -m 'superseed upstream source' -a

git merge --strategy=theirs シミュレーション#7

必要な手順は次のように説明できます。

  1. ワークツリーをアップストリームに置き換えます
  2. インデックスに変更を適用します
  3. 2番目の親としてアップストリームを追加します
  4. コミット

このコマンドgit read-treeは、インデックスを別のツリーで上書きして2番目のステップを実行し、作業ツリーを更新して最初のステップを実行するためのフラグを持っています。コミットするとき、gitは.git / MERGE_HEADのSHA1を2番目の親として使用するため、これを設定してマージコミットを作成できます。したがって、これは次の方法で実現できます。

git read-tree -u --reset upstream                 # update files and stage changes
git rev-parse upstream > .git/MERGE_HEAD          # setup merge commit
git commit -m "Merge branch 'upstream' into mine" # commit

7
あなたはいつでも彼らの代わりに私たちのものを使うことができます:他のブランチをチェックして、あなたのものをそれにマージして、そしてあなたのものをマージに早送りしてください。git checkout upstream; git merge -s ours downstream; git checkout downstream; git merge upstream。(必要に応じて、アップストリームで一時ブランチを使用します。)これには、アップストリームの祖先を最初の親として記録するという利点があります。そのため、マージは、「このトピックブランチを破棄して置き換える」ではなく、「この古いトピックブランチを吸収する」ことを意味します。アップストリームでそれ」。
Cascabel 2011

@Jefromi:いつものように素晴らしい点。私はそれを私の答えに含めました。
vonC 2011

別のオプション-gitmerge --strategy = theirs Simulation#1-のように-ただし、これは最初のマージ親として範囲を保持します:git checkout -b tmp origin / upload git merge -s oursdownstream#ダウンストリームgitからのすべての変更を無視しますチェックアウトダウンストリームgitmerge --squash tmp#tmpからの変更を適用しますが、マージとしては適用しません。gitrev-parseupstream> .git / MERGE_HEAD#2番目のマージヘッドとしてアップストリームを記録しますgitcommit -m "rebaselined ours fromupstream"#コミットします。git branch -D tmp#tmpの削除
jcwenger 2011年

2
うわー、-strategy = theirsは非常に多くの方法で実装できると誰が思ったでしょう。さて、それがgitの次のバージョンに含まれる可能性がある場合
Arne Claassen 2011

VonCと彼の知識は驚くべきものです。彼はgitのJonSkeetのようなものです。:)
sjas 2013年

13

あなたがする必要があるように私には聞こえます:

$ git reset --hard origin/master

アップストリームにプッシュする変更がなく、単にアップストリームブランチを現在のブランチにしたい場合は、これでそれが行われます。これをローカルで行うことは有害ではありません、あなたはマスターにプッシュされていない**ローカルな変更を失うことになります。

**実際には、ローカルでコミットした場合、変更はまだ存在します。コミットはgit reflog、通常は少なくとも30日間、まだあなたの中にあるからです。


これは、ブランチの履歴を変更する可能性があるため、任意の価格でその変更が必要な場合に機能します(-fでプッシュする必要があります)。これはブロックするように構成できるため、基本的には、実際には、自分のプライベートリポジトリでのみ機能します。
リバマー2017年

12

あなたは今これをかなり簡単に行うことができます:

$ git fetch origin
$ git merge origin/master -s recursive -Xtheirs

これにより、ローカルリポジトリがオリジンと同期し、履歴が保持されます。


5
git merge -s recursive -Xtheirsバイナリファイルを自動的にマージしないため、手動で解決する必要がある競合状況になります。に基づくワークフローgit merge -s oursはこれに悩まされません。
ステファン2013年

これは空のコミットを作成するように見えます。
ロビングリーン

申し訳ありませんが、実際には機能しますがgit show、マージコミットでは競合の解決のみが表示-Xtheirsされ、使用されている場合は明らかに競合の解決はありません。
ロビングリーン

5

の別のシミュレーションgit merge -s theirs ref-to-be-merged

git merge --no-ff -s ours ref-to-be-merged         # enforce a merge commit; content is still wrong
git reset --hard HEAD^2; git reset --soft HEAD@{1} # fix the content
git commit --amend

ダブルリセットの代わりに、リバースパッチを適用することもできます。

git diff --binary ref-to-be-merged | git apply -R --index

リバースパッチの興味深い使用法。+1
VonC 2014

リセットが機能しませんでした。「致命的:あいまいな引数 'HEAD2':不明なリビジョンまたはパスが作業ツリーにありません」というメッセージが表示されました。。(はい、入力しましたHEAD^2)パッチメソッドは機能しました。
user247702 2016

@Stijn:実際に^正しく入力しなかった可能性があります。「ctrl-c」のような他のキーストロークが「^ C」として表示されることがあります。-本当に正しい「^」を入力した場合、gitバージョンに重大なバグが見つかりました。
michas 2016

@michas Windowsでは、^文字はエスケープに使用されるため、リテラルとして使用するには、それ自体でエスケープする必要があります。git reset --hard HEAD^^2; git reset --soft HEAD@{1}
Joshua Walsh

4

配管コマンドの助けがほとんどない方法もあります-私見は最も簡単です。2つのブランチの場合の「それら」をエミュレートしたいとします。

head1=$(git show --pretty=format:"%H" -s foo)
head2=$(git show --pretty=format:"%H" -s bar)
tree=$(git show --pretty=format:"%T" -s bar)
newhead=$(git commit-tree $tree -p $head1 -p $head2 <<<"merge commit message")
git reset --hard $newhead

これは、任意の数のヘッド(上記の例では2)を、それらの1つのツリー(上記の例ではバー、「それらの」ツリーを提供)を使用してマージし、差分/ファイルの問題を無視します(commit-treeは低レベルのコマンドであるため、それらを気にしません)。頭は1つだけであることに注意してください(つまり、「彼ら」とのチェリーピックに相当します)。

どの親ヘッドが最初に指定されるかは、いくつかのものに影響を与える可能性があることに注意してください(たとえば、git-logコマンドの--first-parentを参照)-そのことを覚えておいてください。

git-showの代わりに、ツリーとコミットハッシュを出力できる他のすべてのものを使用できます-解析に使用されるものは何でも(cat-file、rev-listなど)。git commit --amendを使用してすべてを追跡し、コミットメッセージをインタラクティブに美化することができます。


これは私の目には最も簡単です。配管コマンドを使用して、「このツリー、この最初の親、この2番目の親で新しいコミットオブジェクトを作成します。次に、この新しいコミットに頭を向けます。」と言います。これはまさに「git merge-stheirs」です。やること。欠点は、この操作を機能させるために4つの異なるハッシュを保存する必要があることです。
Michael R

2

ヘビーハンドですが、地獄、何がうまくいかない可能性がありますか?

  • Yのように見せたいブランチXをチェックしてください
  • cp -r .git /tmp
  • ブランチYをチェックしてください git checkout y
  • rm -rf .git && cp -r /tmp/.git
  • 違いをコミットしてプッシュする
  • 完了。

これは、マージ履歴の維持を気にしないと仮定して、2つのブランチを同一にする最も単純でブルートフォースの方法です。
デイモン

1

リモートアップストリームブランチに変更git mergeし、マージ戦略をに設定してを実行しoursます。

git checkout origin/master
git merge dev --strategy=ours
git commit ...
git push

すべての履歴は引き続き存在しますが、追加のマージコミットがあります。ここで重要なことは、希望するバージョンから開始し、oursgithubが実際にあるブランチとマージすることです。


1
反対が必要です。これでブランチがアップストリームに統合されますが、アップストリームヘッドは変更されません。しかし、私は上流を取り、それを私のブランチに統合して、頭を上流のように見せておく必要があります。基本的にはのようなものですが--strategy=theirs、最も近いもの--strategy=recursive -X=theirsはそれを完全には行いません。
Arne Claassen 2011

--strategy=theirsの正反対です--strategy=ours。反対側から開始します(したがって、githubから開始し、反対方向にマージします)。
kelloti 2011

ありません--strategy=theirs、それが問題です。最も近いのは--strategy=recursive -X theirs、競合しない場合、無関係なローカル変更を削除しないため、まったく逆ではありません。
Arne Claassen 2011

これら2つは反対です:git checkout dev; git merge origin/master --strategy=oursそしてgit checkout origin/master; git merge dev --strategy=ours
wuputah 2011

2
@Arne:VonCの回答に関する私のコメントを参照してください。ours戦略の存在は、戦略を実行することを完全に可能にしますtheirs
カスカベル2011

1

git reset BACKWARDSを使用してください!

ブランチを他のコミットのように見せることができます git reset行う必要があります。

コミット時のブランチをコミットの<old>ように見せるために<new>、次のことができます。

git reset --hard <new>

作るために <new>作業ツリーの内容する。

次に、

git reset --mixed <old> 

ブランチを元のコミットに戻しますが、作業ツリーはその 状態のままにし<new> ますます。

次に、ブランチをコンテンツと完全に一致させるために、変更を追加してコミットできます。 <new>コミットの。

<old>状態から<new>あなたに移動するには、git reset から <new> 実行する必要があるというのは直感に反します<old>。ただし、オプションを使用--mixedすると、作業ツリーはそのままに<new>なり、分岐ポインタはに設定されます<old>ため、変更がコミットされると、ブランチは希望どおりに表示されます。

警告

コミットを忘れないでください。たとえば、何<old>をしているのかを忘れてくださいgit reset --hard <new>


0

私はそれらの役割に従いました:

原点をフェッチし、ブランチからハードリセットしてから、ブランチから再帰的にリセットし、ブランチに強制的にプッシュします

あなた自身の責任で

git fetch origin
git reset --hard origin/<branch>
git merge origin/<branch> -s recursive -Xtheirs
git push -f <remote> <branch>
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.