web-dev-qa-db-ja.com

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

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で常にマージされているのと同じ混乱を見ると理解していますが、他に欠点はありますか?その最後の文も私を心配しています。人々は日常的にこのようなことをしますか?

133
Knut Eldhuset

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

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

  • 私がコミットし、SVNリポジトリを複製する唯一のブランチである「マスター」ブランチがあります(-sリポジトリに標準SVNレイアウトがあると仮定しますtrunk/branches/、およびtags/):

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

    git checkout -b work
    
  • ローカルで「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
    
  • sVNリポジトリを使用してmasterブランチをリベースします(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」ブランチから3つのコミットすべてを「master」にマージします。

    (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
    
  • SVNの最後のコミットを編集(amend)する必要があります(そうしないと、「マージブランチ '作業'」というメッセージのコミットが1つだけ表示されます。

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

    (master)$> git svn dcommit
    
  • 仕事に戻り、最終的にスタッシュファイルを復元します。

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

Git-svnを使用すると、ローカルブランチを作成できます。自分でローカルブランチを使用しているだけで、gitを使用して上流のsvnブランチ間をマージしようとしない限り、問題ありません。

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

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

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

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

チェリーピックを使用するよりも、ローカルの変更のコミットをスキップしながらトピックブランチの変更をマージするより良い方法があるはずなので、誰かがアイデアを持っているなら歓迎します。

49
Greg Hewgill

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

短い答え:gitを好きなように使用できます(単純なワークフローについては下記を参照)。マージも含まれます。それぞれの「git merge work」の後に「git branch -d work '一時的な作業ブランチを削除します。

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

(注:git-svnでできることはあまりありません。svnは、より強力なgitマージを単に理解していません。したがって、svnリポジトリ内では、このマージ情報は決して表現できません。)

しかし、これはwhole問題です。 'work'ブランチを 'masterブランチ'にマージした後に削除すると、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コミットのsha-1ハッシュを貼り付けてください。詳細については、「git help rebase」を使用してドキュメントを読むことをお勧めします。要するに、このコマンドは最初にコミットを表示するエディターを開きます-以前のコミットで押しつぶしたいすべてのコミットの「pick」を「squash」に変更するだけです。もちろん、最初の行は「ピック」のままにしてください。このようにして、多くの小さなコミットを1つ以上の意味のあるユニットに凝縮できます。エディターを保存して終了します。コミットログメッセージの書き換えを求める別のエディターが表示されます。

要するに、「コードハッキング」を終えた後、他のプログラマにどのように見せたいか(または履歴を閲覧する数週間後にどのように作品を見たいか)がわかるまで、「作業」ブランチをマッサージします。 。

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

$> git checkout master
$> git svn rebase

これで、svnリポジトリでその間に行われたすべての変更で更新された古い「マスター」ブランチに戻りました(新しい変更は「作業」ブランチに隠されています)。

新しい「作業」の変更と衝突する可能性のある変更がある場合、新しい作業をプッシュする前にそれらをローカルで解決する必要があります(詳細は以下を参照)。その後、変更を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:「git branch -d work」betweenマージとdcommitを使用して、必ずブランチを削除してください: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」が他の人が作業中に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

より強力なソリューションが存在します:提示されたワークフローは単純です: 'update/hack/dcommit'の各ラウンドでのみgitの機能を使用します---しかし、svnリポジトリと同じくらい長期的なプロジェクト履歴を残します。レガシーsvnプロジェクトの小さな最初のステップでgit mergesを使い始めたいだけなら大丈夫です。

Gitのマージに慣れてきたら、他のワークフローを自由に検討してください:自分が何をしているのかを知っているなら、cangit mergeとsvn mergesを混在させる( sing git-svn(または同様の)svn mergeを手伝うためだけ?

33
Yaakov Belch

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

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

8
Marius K

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
6
luntain

ローカルgitブランチをマスターgitブランチにリベースしてからdcommitを実行すると、すべてのコミットを順番に実行したように見えるので、svnの人々は慣れたまま直線的に見ることができます。だからあなたができるというトピックと呼ばれるローカルブランチがあると仮定して

git rebase master topic

これにより、dcommitの準備ができたマスターブランチでコミットが再生されます。

5
JoeyJ