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で常にマージされているのと同じ混乱を見ると理解していますが、他に欠点はありますか?その最後の文も私を心配しています。人々は日常的にこのようなことをしますか?
実際、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サーバーにコミットされたくない修正を隠します(コンパイルを高速化し、特定の機能に焦点を合わせたいという理由だけで、メインファイルの一部のコードをコメントしたことがよくあります)。
(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
Git-svnを使用すると、ローカルブランチを作成できます。自分でローカルブランチを使用しているだけで、gitを使用して上流のsvnブランチ間をマージしようとしない限り、問題ありません。
SVNサーバーを追跡するために使用する「マスター」ブランチがあります。これは、私がコミットした唯一のブランチです。何らかの作業をしている場合は、トピックブランチを作成し、それを削除します。コミットしたいときは、次のことを行います。
また、svnにプッシュすべきではないローカルな変更(デバッグ用)を維持する必要がある別の状況もあります。そのために、私は上記のmasterブランチだけでなく、通常は仕事をする「work」というブランチも持っています。トピックブランチは作業から分岐されます。そこで作業をコミットしたい場合は、マスターをチェックアウトし、チェリーピックを使用して、svnにコミットする作業ブランチからコミットを選択します。これは、3つのローカル変更コミットのコミットを避けたいためです。次に、masterブランチからコミットし、すべてをリベースします。
実行する価値がありますgit svn dcommit -n
最初に、コミットしようとしているものを正確にコミットしようとしていることを確認します。 gitとは異なり、svnで履歴を書き換えるのは難しいです!
チェリーピックを使用するよりも、ローカルの変更のコミットをスキップしながらトピックブランチの変更をマージするより良い方法があるはずなので、誰かがアイデアを持っているなら歓迎します。
簡単な解決策:マージ後に「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を手伝うためだけ? )
上のグレッグ・ヒューギルの答えは安全ではありません! 2つの "git svn rebase"の間のトランクに新しいコミットが現れた場合、マージは早送りされません。
Git-mergeに「--ff-only」フラグを使用することで確認できますが、通常はブランチで「git svn rebase」を実行せず、「git rebase master」のみを実行します(ローカルのみであると仮定)ブランチ)。その後、「git merge thebranch」が早送りされることが保証されます。
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
ローカルgitブランチをマスターgitブランチにリベースしてからdcommitを実行すると、すべてのコミットを順番に実行したように見えるので、svnの人々は慣れたまま直線的に見ることができます。だからあなたができるというトピックと呼ばれるローカルブランチがあると仮定して
git rebase master topic
これにより、dcommitの準備ができたマスターブランチでコミットが再生されます。