88

変更のあるブランチを取り、分岐元の上流と同じになるように戻そうとしています。変更は両方ともローカルであり、github にプッシュされているため、履歴が変更されるため、どちらgit resetgit rebase実際には実行可能ではありません。これは、既にプッシュされているブランチでは悪いことです。

私もgit mergeさまざまな戦略を試しましたが、ローカルの変更を元に戻すものはありません。つまり、ファイルを追加した場合、マージにより他のファイルが元に戻る可能性がありますが、上流にないファイルがまだ残っています持ってる。

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

4

9 に答える 9

119

カスタム マージ ドライバ "keepTheirs"を 使用して、アップストリーム ブランチを自分のブランチにマージdevできます。 あなたの場合、必要なのは1つだけで、スクリプトは次のようになります。

.gitattributeskeepTheirs

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

( Barak A. Pearlmutterが提案):

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 のみのコマンドでは不可能な場合、それは 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
于 2011-02-06T08:03:35.410 に答える
13

これは今ではかなり簡単に行うことができます。

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

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

于 2012-11-16T04:30:58.767 に答える
13

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

$ git reset --hard origin/master

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

**実際には、ローカルでコミットした場合、変更はまだ残っています。コミットはgit reflog、通常少なくとも 30 日間は に残っているためです。

于 2011-02-06T08:13:27.287 に答える
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を使用してすべてをフォローし、コミットメッセージをインタラクティブに美化できます。

于 2012-09-06T13:22:35.617 に答える
1

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

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

すべての履歴は引き続き存在しますが、余分なマージ コミットが発生します。ここで重要なことは、目的のバージョンから開始して、ours実際にあるブランチ github とマージすることです。

于 2011-02-06T05:41:56.657 に答える