gitにマージした後、git-svn dcommitは危険ですか?


133

git-svnを試す動機は、簡単なマージとブランチです。それから私は男git-svn(1)が言うことに気づきました:

コミットする予定のブランチでgit-mergeまたはgit-pullを実行することはお勧めしません。Subversionは、合理的または有用な方法でマージを表すものではありません。そのため、Subversionを使用しているユーザーは、作成したマージを見ることができません。さらに、SVNブランチのミラーであるgitブランチからマージまたはプルすると、dcommitが間違ったブランチにコミットする可能性があります。

これは、svn / trunk(またはブランチ)からローカルブランチを作成し、ハックして、svn / trunkにマージしてから、dcommitできないことを意味しますか?私は、svnユーザーがsvn pre 1.5.xでマージするのと同じ混乱を常に目にすることを理解していますが、他の欠点はありますか?その最後の文も私を心配しています。人々は日常的にこのようなことをしていますか?


14
これは間違っています。Subversionは、合理的または有用な方法でマージを表現しません。そのため、Subversionを使用しているユーザーは、作成したマージを見ることができません。さらに、SVNブランチのミラーであるgitブランチからマージまたはプルすると、dcommitが間違ったブランチにコミットする可能性があります。Subversionは、gitマージ追跡情報だけでなく、より詳細な情報も表すことができます。適切なsvn:mergeinfoを構成したり、適切なブランチにdcommitしたりできないのはgit-svnです。
Alexander Kitaev

2
@AlexanderKitaev:git-svn docsは変更されており、さらに--mergeinfo=<mergeinfo>マージ情報をSVNに渡すことができるオプションがあります。しかし、それがどのように使用されるべきかわかりません。
Mr_and_Mrs_D 2014

回答:


174

実際、私--no-ffはgit mergeのオプションでさらに良い方法を見つけました。以前使用していたこのスカッシュテクニックはすべて不要になりました。

私の新しいワークフローは次のとおりです。

  • 私はからdcommitことだけブランチとそのクローンSVNリポジトリである「マスター」ブランチを持っている(-sあなたがリポジトリ内の標準的なSVNのレイアウトを持っていると仮定しtrunk/branches/、およびtags/):

    git svn clone [-s] <svn-url>
    
  • ローカルブランチ「work」で作業してい-bます(ブランチ「work」を作成しています)

    git checkout -b work
    
  • 「作業」ブランチにローカルでコミット-sします(コミットメッセージをサインオフするため)。続編で、あなたは3つのローカルコミットをしたと思います

    ...
    (work)$> git commit -s -m "msg 1"
    ...
    (work)$> git commit -s -m "msg 2"
    ...
    (work)$> git commit -s -m "msg 3"
    

次に、SVNサーバーにコミットします

  • [最終的に] SVNサーバーでコミットしたくない変更を隠します(多くの場合、コンパイルを加速して特定の機能に集中したいという理由だけで、メインファイルのコードにコメントしました)

    (work)$> git stash
    
  • masterブランチをSVNリポジトリでリベース(SVNサーバーから更新するため)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • 作業ブランチに戻り、マスターでリベースします

    (master)$> git checkout work
    (work)$> git rebase master
    
  • 以下を使用して、すべてが正常であることを確認します。

    (work)$> git log --graph --oneline --decorate
    
  • この素晴らしい--no-ffオプションを使用して、「work」ブランチから「master」に3つすべてのコミットをマージする時が来ました。

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • ログのステータスを確認できます。

    (master)$> git log --graph --oneline --decorate
    * 56a779b (work, master) Merge branch 'work'
    |\  
    | * af6f7ae msg 3
    | * 8750643 msg 2
    | * 08464ae msg 1
    |/  
    * 21e20fa (git-svn) last svn commit
    
  • 今、あなたはおそらくamendあなたのSVN男の最後のコミットを編集()したいと思います(そうでなければ、彼らはメッセージ "Merge branch 'work'"を持つ単一のコミットのみを見るでしょう。

    (master)$> git commit --amend
    
  • 最後にSVNサーバーでコミットします

    (master)$> git svn dcommit
    
  • 作業に戻り、最終的に隠されたファイルを回復します。

    (master)$> git checkout work
    (work)$> git stash pop
    

4
これはまさにこのgit n00bが探していたワークフローです。ローカルブランチを作成してバグを修正し、何をしても、SINGLEコミットでsvnに送信します。ここで最も高い評価の答えを使用すると、私がやっていたことを台無しにしました-エラーなしでgit svn rebaseを実行できませんでした。
ジョアン・ブラガンサ

19
これは、op-で引用されているgit-svn docsが警告しているものとまったく同じではありませんか?マージを実行すると--no-ffオプションは、明示的に代わりに早送りのマージコミット(2人の両親とコミット)を作成しています。svn-trackingブランチのすべてのコミットが単一の親のコミットであることを確認するには、すべてのマージが早送りである必要があります(--ff-onlyこれに役立つ可能性があります)--squash
ジェモン

4
これは、すぐに削除する1回限りのブランチで機能しますがgit svn dcommit、指定したgit commitを書き換えます。これは、他の親を失うことを意味し、gitリポジトリには、そのブランチをにマージした記録がありませんmaster。これにより、作業ディレクトリが不整合な状態になることもあります。
rescdsk

9
git merge --no-ff work -m "commit message"追加のgit commit --amendステップを使用する代わりに使用
tekumara

3
私にとっては、最後のコミットだけでなく、すべてのコミットを保持したいので、「git merge --ff-only work」を使用したいと思います
Moataz Elmasry

49

ローカルブランチの作成はgit-svnで間違いなく可能です。自分でローカルブランチを使用していて、gitを使用して上流のsvnブランチ間をマージしない限り、問題はありません。

私はsvnサーバーの追跡に使用する「マスター」ブランチを持っています。これは私がコミットする唯一のブランチです。何らかの作業をしている場合は、トピックブランチを作成し、そのブランチを作成します。コミットしたいときは、次のようにします。

  1. すべてをトピックブランチにコミットします
  2. git svn rebase (作業とsvn間の競合を解決)
  3. gitチェックアウトマスター
  4. git svn rebase (これにより、次のステップが早送りマージになります。以下のAaronのコメントを参照してください)
  5. git merge topic_branch
  6. マージの競合を解決します(現時点ではありません)
  7. git svn dcommit

また、svnにプッシュしないでください(デバッグのために)ローカルの変更を維持する必要がある別の状況もあります。そのため、私は上記のマスターブランチを持っていますが、通常は作業を行う「work」というブランチもあります。トピックブランチは作業から分岐されます。そこで作業をコミットするときは、マスターをチェックアウトし、cherry-pickを使用して、svnにコミットする作業ブランチからコミットを選択します。これは、3つのローカル変更コミットのコミットを避けたいためです。次に、masterブランチからコミットして、すべてをリベースします。

git svn dcommit -n最初に実行して、コミットしようとしているものを正確にコミットしようとしていることを確認することは価値があります。gitとは異なり、svnでの履歴の書き換えは難しいです!

チェリーピックを使用するよりも、ローカルの変更コミットをスキップしながらトピックブランチの変更をマージするためのより良い方法があるはずだと思います。


私がこれを正しく理解している場合、gitでは、gitとsvnのマージは問題ありませんが、svnとsvnのマージは問題ありませんか?だから私はgitでsvn / branchをsvn / trunkとマージできないのですか?
Knut Eldhuset 2008年

1
些細なことをしたい場合、SVNでの履歴の書き換えは困難です。重要なケースでは、それは事実上不可能です。
アリストテレスPagaltzis 08年

19
グレッグ・ヒューギルの答えには多くの票がありますが、私はそれが間違っていると思います。topic_branchからmasterへのマージは、それが早送りマージである場合にのみ安全です。マージコミットが必要な実際のマージの場合、dcommitするとマージコミットは失われます。次回topic_branchをマスターにマージしようとすると、Gitは最初のマージが行われなかったと考えて、すべてが完全に壊れます。質問を参照してくださいgitのはsvn dcommit支店のマージコミットの歴史を失うないのはなぜか?
アーロン

1
@グレッグ・ヒューギル、編集は明確ではありません。あなたはリベースが「次のコミットを早送りマージにする」と書いています。早送りマージにはコミットが含まれないため、それが何を意味するのかはわかりませんが、おそらく「次のマージを早送りマージにする」ことを意味したのでしょう。しかし、それがあなたが意図したことである場合、それは正しくありません。topic_branchからマスターへのマージが早送りマージであるかどうかは、ブランチポイント以降にマスターでコミットが行われたかどうかによって異なります。マスターをリベースすると、マスターに新しいコミットが作成されるため、後続のマージは必ずしも早送りマージではありません
アーロン

1
@Aaron:ええ、私がそこで何を考えていたかはわかりません。私はそれを再び修正しました、うまくいけばそれは理にかなっています。問題の1つは、Gitで実行する方法が非常に多く、特定の一連のアクションを説明するにはかなりの説明が必要になることです。
グレッグヒューギル

33

簡単な解決策:マージ後に「work」ブランチを削除する

短い答え:マージを含め、好きなようにgitを使用できます(簡単なワークフローについては以下を参照)。ただ一時的な作業ブランチを削除するために、それぞれの「git merge work」と「git branch -d work」を必ず確認してください。

背景の説明: マージ/ dcommitの問題は、ブランチを「git svn dcommit」すると、そのブランチのマージ履歴が「フラット化」されることです:gitは、このブランチに入ったすべてのマージ操作を忘れます:ファイルの内容のみが保持されますが、しかし、このコンテンツが(部分的に)特定の他のブランチからのものであるという事実は失われます。参照:git svn dcommitがローカルブランチのマージコミットの履歴を失うのはなぜですか?

(注:git-svnができることはそれほど多くありません。svnは、はるかに強力なgitマージを理解できません。そのため、svnリポジトリ内では、このマージ情報をどのようにも表すことができません。)

しかし、これが全体の問題です。「master」ブランチにマージされた後で「work」ブランチを削除すると、gitリポジトリは100%クリーンになり、svnリポジトリとまったく同じように見えます。

私のワークフロー: もちろん、最初にリモートのsvnリポジトリーをローカルのgitリポジトリーに複製しました(これには時間がかかる場合があります)。

$> git svn clone <svn-repository-url> <local-directory>

すべての作業は「ローカルディレクトリ」内で行われます。サーバーから更新を取得する必要があるときはいつでも(「svn update」のように)、次のようにします。

$> git checkout master
$> git svn rebase

私はすべての開発作業を、次のように作成された別のブランチ「作業」で行います。

$> git checkout -b work

もちろん、好きなだけブランチを作成し、ブランチ間でマージしてリベースすることができます(以下で説明するように、ブランチが終わったら削除してください)。私の通常の作業では、非常に頻繁にコミットします。

$> git commit -am '-- finished a little piece of work'

次のステップ(git rebase -i)はオプションです--- svnでアーカイブする前に履歴をクリーンアップするだけです。他の人と共有したい安定したマイルストーンに到達したら、この「作品」の履歴を書き換えますコミットメッセージを分岐してクリーンアップします(他の開発者は、途中で行った小さな手順やミスをすべて見る必要はありません---結果だけです)。これのために、私はします

$> git log

そしてsvnリポジトリにある最後のコミットのsha-1ハッシュをコピーします(git-svn-idで示されます)。次に電話します

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

私の代わりに最後のsvn commitのsha-1ハッシュを貼り付けてください。詳細については、「git help rebase」のドキュメントをご覧ください。つまり、このコマンドは最初にコミットを表示するエディターを開きます-----以前のコミットでスカッシュしたいすべてのコミットの 'pick'を 'squash'に変更するだけです。もちろん、最初の行は「ピック」のままである必要があります。このようにして、多くの小さなコミットを1つ以上の意味のある単位にまとめることができます。保存してエディターを終了します。コミットログメッセージを書き換えるように求める別のエディタが表示されます。

簡単に言うと、「コードハッキング」を終えた後、他のプログラマにどのように提示したいか(または、履歴を参照して数週間で作業を確認したい方法)になるまで、「作業」ブランチをマッサージします。 。

変更をsvnリポジトリーにプッシュするために、次のようにします。

$> git checkout master
$> git svn rebase

これで、svnリポジトリーでその間に発生したすべての変更で更新された古い「master」ブランチに戻りました(新しい変更は「work」ブランチで非表示になっています)。

新しい「作業」の変更と衝突する可能性のある変更がある場合は、新しい作業をプッシュする前にローカルで解決する必要があります(以下の詳細を参照)。次に、変更をsvnにプッシュできます。

$> git checkout master
$> git merge work        # (1) merge your 'work' into 'master'
$> git branch -d work    # (2) remove the work branch immediately after merging
$> git svn dcommit       # (3) push your changes to the svn repository

注1:コマンド 'git branch -d work'は非常に安全です。これは、不要になったブランチを削除することのみを許可します(現在のブランチに既にマージされているため)。作業を 'master'ブランチとマージする前にこのコマンドを誤って実行すると、エラーメッセージが表示されます。

注2:マージとdcommit の間に 'git branch -d work'を使用してブランチを必ず削除してください:dcommitの後にブランチを削除しようとすると、エラーメッセージが表示されます: 'git svn dcommit'を実行すると、gitはそれを忘れますあなたのブランチは 'master'とマージされました。安全性チェックを行わない「git branch -D work」で削除する必要があります。

ここで、すぐに新しい「work」ブランチを作成して、誤って「master」ブランチをハッキングしないようにします。

$> git checkout -b work
$> git branch            # show my branches:
  master
* work

「作業」とsvnの変更の統合: 「git svn rebase」が「work」ブランチの作業中に他の人がsvnリポジトリーを変更したことを明らかにしたとき、私は次のようにします。

$> git checkout master
$> git svn rebase              # 'svn pull' changes
$> git checkout work           # go to my work
$> git checkout -b integration # make a copy of the branch
$> git merge master            # integrate my changes with theirs
$> ... check/fix/debug ...
$> ... rewrite history with rebase -i if needed

$> git checkout master         # try again to push my changes
$> git svn rebase              # hopefully no further changes to merge
$> git merge integration       # (1) merge your work with theirs
$> git branch -d work          # (2) remove branches that are merged
$> git branch -d integration   # (2) remove branches that are merged
$> git svn dcommit             # (3) push your changes to the svn repository

より強力なソリューションが存在 します提示されたワークフローは単純化されています:「更新/ハック/ dcommit」の各ラウンド内でのみgitの機能を使用します---長期プロジェクトの履歴はsvnリポジトリと同じように線形のままです。これは、レガシーsvnプロジェクトの小さな最初のステップでgitマージの使用を開始したいだけの場合は問題ありません。

gitマージに慣れてきたら、他のワークフローを自由に探索してください:何をしているのかわかっている場合は、gitマージとsvnマージを混在させることができます(svnマージを支援するためだけにgit-svn(または類似の)を使用しますか?


これは不必要に複雑に思えます。人がをやっているgit merge --squash workと聞いたことがありますが、どうしてこれをしないのですか?複数の「ピック」を作成している場合、マージする前にブランチでスカッシュコミットを実行していることを確認できます(8つのコミットがあり、4つのコミットをそれぞれ1つにして、2つのコミットをマスターにマージしているとします)。また、「作業」ブランチを更新するときはrebase、ブランチに別のブランチを作成してマージを行うよりも簡単です...
void.pointer

8

上のグレッグ・ヒューギルの答えは安全ではありません!2つの「git svn rebase」間のトランクに新しいコミットが出現した場合、マージは早送りされません。

これはgit-mergeに「--ff-only」フラグを使用することで確認できますが、通常はブランチで「git svn rebase」を実行せず、「git rebase master」のみを実行します(ローカルにあると仮定した場合)ブランチ)。その後、「git merge thebranch」が早送りされることが保証されます。


6

gitでsvnブランチをマージする安全な方法は、git merge --squashを使用することです。これにより、単一のコミットが作成され、メッセージを追加するために停止します。

svn-branchというトピックのsvnブランチがあるとします。

git svn fetch
git checkout remotes/trunk -b big-merge
git merge --squash svn-branch

この時点で、svn-branchからのすべての変更が、インデックスで待機している1つのコミットに押しつぶされています。

git commit

他の人が指摘したように、しかし、これはコミットの粒度を失います。
Kzqai 2010年

@Tchalvak:時々それだけが欲しい。私は「前のコミットからの固定された混乱」という機能ブランチでしばしばコミットしますが、これらの行き止まりは評判が良いために隠したいです:-)
schoetbi

1
ええ、私はそれを綱渡りであると考えていますが、つぶすたびに、後で個々のコミットを引き離す能力も失うためです。コミットメント基準のさまざまな選択の問題だけです。
Kzqai 2011年

1
@schoetbiはい、時々、乱雑な履歴を公開前に整理する必要があります。これは、 'git rebase -i <trunk>'が非常に役立つ場所です。コミット、split(!)コミットに参加/並べ替え/削除したり、メッセージを修正したりできます。
歌手

5

ローカルのgitブランチをマスターgitブランチにリベースしてからdcommitを実行すると、svnのユーザーが慣れているように直線的に確認できるように、すべてのコミットを順番に実行したように見えます。したがって、トピックというローカルブランチがあるとすると、

git rebase master topic

次に、commitを実行できるように、masterブランチでコミットを再生します。

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