Apakah git-svn dcommit setelah bergabung di git berbahaya?

133

Motivasi saya untuk mencoba git-svn adalah penggabungan dan percabangan yang mudah. Kemudian saya perhatikan bahwa pria git-svn (1) mengatakan:

Menjalankan git-merge atau git-pull TIDAK disarankan pada cabang yang Anda rencanakan untuk dikecewakan. Subversi tidak mewakili penggabungan dengan cara yang masuk akal atau berguna; jadi pengguna yang menggunakan Subversion tidak dapat melihat penggabungan yang Anda buat. Selain itu, jika Anda menggabungkan atau menarik dari cabang git yang merupakan cermin dari cabang SVN, dcommit dapat melakukan ke cabang yang salah.

Apakah ini berarti saya tidak dapat membuat cabang lokal dari svn / trunk (atau cabang), meretas, menggabungkan kembali ke svn / trunk, lalu dcommit? Saya mengerti bahwa pengguna svn akan melihat kekacauan yang sama dengan penggabungan svn pra 1.5.x selalu, tetapi apakah ada kelemahan lain? Kalimat terakhir itu juga membuatku khawatir. Apakah orang secara rutin melakukan hal-hal seperti ini?

Knut Eldhuset
sumber
14
Ini hanya salah: Subversi tidak mewakili penggabungan dengan cara yang masuk akal atau berguna; jadi pengguna yang menggunakan Subversion tidak dapat melihat penggabungan yang Anda buat. Selain itu, jika Anda menggabungkan atau menarik dari cabang git yang merupakan cermin dari cabang SVN, dcommit dapat melakukan ke cabang yang salah. Subversion dapat mewakili tidak hanya git merge tracking information, tetapi juga banyak info yang lebih bagus. Ini adalah git-svn yang gagal menyusun svn: mergeinfo yang tepat atau membuat dcommit ke cabang yang benar.
Alexander Kitaev
2
@AlexanderKitaev: git-svn docs telah berubah sejak itu dan ada --mergeinfo=<mergeinfo>opsi yang dapat meneruskan info penggabungan ke SVN. Tidak yakin bagaimana itu harus digunakan.
Mr_and_Mrs_D

Jawaban:

174

Sebenarnya, saya menemukan cara yang lebih baik dengan --no-ffopsi pada git merge. Semua teknik squash yang saya gunakan sebelumnya tidak lagi diperlukan.

Alur kerja baru saya sekarang adalah sebagai berikut:

  • Saya memiliki "master" cabang yang merupakan satu-satunya cabang yang saya dcommit dari dan clone bahwa repositori SVN ( -smenganggap Anda memiliki tata letak SVN standar dalam repositori trunk/, branches/dan tags/):

    git svn clone [-s] <svn-url>
    
  • Saya mengerjakan "kantor" cabang lokal ( -bmembuat cabang "kantor")

    git checkout -b work
    
  • komit secara lokal ke cabang "work" ( -suntuk menandatangani pesan komit Anda). Dalam sekuelnya, saya anggap Anda membuat 3 commit lokal

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

Sekarang Anda ingin komit ke server SVN

  • [Akhirnya] menyimpan modifikasi yang tidak ingin Anda lihat dilakukan pada server SVN (sering Anda berkomentar beberapa kode dalam file utama hanya karena Anda ingin mempercepat kompilasi dan fokus pada fitur yang diberikan)

    (work)$> git stash
    
  • rebase cabang master dengan repositori SVN (untuk memperbarui dari server SVN)

    (work)$> git checkout master
    (master)$> git svn rebase
    
  • kembali ke cabang kerja dan rebase dengan master

    (master)$> git checkout work
    (work)$> git rebase master
    
  • Pastikan semuanya baik-baik saja, misalnya:

    (work)$> git log --graph --oneline --decorate
    
  • Sekarang saatnya untuk menggabungkan ketiga commit dari cabang "work" menjadi "master" menggunakan --no-ffopsi yang luar biasa ini

    (work)$> git checkout master
    (master)$> git merge --no-ff work
    
  • Anda dapat melihat status log:

    (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
    
  • Sekarang Anda mungkin ingin mengedit ( amend) komit terakhir untuk dudes SVN Anda (jika tidak, mereka hanya akan melihat satu komit dengan pesan "Gabung cabang 'pekerjaan'"

    (master)$> git commit --amend
    
  • Akhirnya komit di server SVN

    (master)$> git svn dcommit
    
  • Kembali bekerja dan akhirnya pulihkan file simpanan Anda:

    (master)$> git checkout work
    (work)$> git stash pop
    
Sebastien Varrette
sumber
4
Ini PERSIS alur kerja yang dicari git n00b ini. Buat cabang lokal untuk memperbaiki bug, lakukan apa saja, lalu kirim ke svn dengan komit TUNGGAL. Menggunakan jawaban berperingkat tertinggi di sini mengacaukan apa yang saya lakukan - tidak bisa git svn rebase tanpa kesalahan.
João Bragança
19
Bukankah ini persis seperti yang diperingatkan oleh git-svn docs di op? Dengan menjalankan penggabungan dengan --no-ffopsi, Anda secara eksplisit membuat komit gabungan (komit dengan dua orang tua) alih-alih fast-forward. Untuk memastikan semua komit di cabang svn-tracking adalah komit induk tunggal, semua penggabungan harus maju cepat ( --ff-onlydapat membantu dengan ini) atau, jika trunk telah berubah di belakang Anda, a --squash, kan?
jemmons
4
Ini berfungsi untuk cabang sekali saja yang segera Anda hapus, tetapi git svn dcommitmenulis ulang komit git yang Anda berikan. Ini berarti Anda kehilangan orang tua yang lain, dan sekarang repositori git Anda tidak memiliki catatan bahwa Anda pernah menggabungkan cabang tersebut master. Ini juga dapat meninggalkan direktori kerja Anda dalam keadaan tidak konsisten.
rescdsk
9
gunakan git merge --no-ff work -m "commit message"alih-alih memiliki git commit --amendlangkah ekstra
tekumara
3
untuk saya, saya lebih suka menggunakan "git merge - hanya bekerja" karena saya ingin mempertahankan semua komitmen saya dan bukan hanya yang terakhir
Moataz Elmasry
49

Membuat cabang lokal pasti dimungkinkan dengan git-svn. Selama Anda hanya menggunakan cabang lokal untuk diri Anda sendiri, dan tidak mencoba menggunakan git untuk menggabungkan antara cabang svn hulu, Anda harus baik-baik saja.

Saya memiliki cabang "master" yang saya gunakan untuk melacak server svn. Ini adalah satu-satunya cabang yang saya pilih. Jika saya melakukan beberapa pekerjaan, saya membuat cabang topik dan mengerjakannya. Ketika saya ingin mengkomitnya, saya melakukan hal berikut:

  1. Komit semuanya ke cabang topik
  2. git svn rebase (menyelesaikan konflik antara pekerjaan Anda dan svn)
  3. git checkout master
  4. git svn rebase (ini membuat langkah selanjutnya menjadi penggabungan maju cepat, lihat komentar Harun di bawah)
  5. git gabungkan topic_branch
  6. menyelesaikan konflik penggabungan (seharusnya tidak ada pada saat ini)
  7. git svn dcommit

Saya juga memiliki situasi lain di mana saya perlu mempertahankan beberapa perubahan lokal (untuk debugging) yang tidak boleh didorong ke svn. Untuk itu, saya memiliki cabang master di atas tetapi juga cabang yang disebut "bekerja" di mana saya biasanya bekerja. Cabang-cabang topik bercabang dari pekerjaan. Ketika saya ingin melakukan pekerjaan di sana, saya checkout master dan menggunakan cherry-pick untuk memilih commit dari cabang kerja yang ingin saya komit ke svn. Ini karena saya ingin menghindari melakukan tiga komitmen perubahan lokal. Kemudian, saya hengkang dari cabang utama dan rebase segalanya.

Penting untuk dijalankan git svn dcommit -nterlebih dahulu untuk memastikan bahwa Anda akan melakukan apa yang ingin Anda lakukan. Tidak seperti git, menulis ulang sejarah di svn itu sulit!

Saya merasa bahwa harus ada cara yang lebih baik untuk menggabungkan perubahan pada cabang topik sambil melewatkan perubahan lokal yang dilakukan daripada menggunakan cherry-pick, jadi jika ada yang punya ide mereka akan diterima.

Greg Hewgill
sumber
Jika saya mengerti ini dengan benar, di git, menggabungkan git dengan svn tidak apa-apa, tetapi tidak svn dengan svn? Jadi saya tidak bisa menggabungkan svn / branch saya dengan svn / trunk di git?
Knut Eldhuset
1
Menulis ulang sejarah di SVN sulit jika Anda ingin melakukan sesuatu yang sepele. Dalam kasus-kasus non-sepele itu praktis mustahil.
Aristoteles Pagaltzis
19
Jawaban Greg Hewgill memiliki banyak suara, tetapi saya yakin itu salah. Menggabungkan dari topic_branch ke master hanya aman jika itu hanya penggabungan maju-cepat. Jika itu adalah penggabungan nyata yang membutuhkan komit gabungan, maka komit gabungan akan hilang ketika Anda berkomitmen. Lain kali Anda mencoba untuk menggabungkan topic_branch menjadi master, git berpikir penggabungan pertama tidak pernah terjadi, dan semua neraka terlepas. Lihat pertanyaan. Mengapa git svn dcommit kehilangan riwayat gabungan yang dilakukan untuk cabang lokal?
Aaron
1
@ Greg Hewgill, hasil edit tidak jelas. Anda menulis bahwa rebase "membuat komit berikutnya menggabungkan cepat-maju." Saya tidak tahu apa artinya itu, karena penggabungan maju-cepat tidak melibatkan komit, tetapi mungkin Anda berarti "membuat penggabungan berikutnya menjadi penggabungan maju-cepat". Tetapi jika itu yang Anda maksud, itu tidak benar. Apakah penggabungan dari topic_branch menjadi master adalah penggabungan maju cepat atau tidak tergantung pada apakah ada komit yang telah dibuat pada master sejak titik cabang. Saat Anda rebase master, itu menciptakan komit baru pada master, jadi penggabungan berikutnya tidak harus merupakan penggabungan maju-cepat.
Aaron
1
@ Harun: Ya saya tidak tahu apa yang saya pikirkan di sana. Saya sudah memperbaikinya lagi, mudah-mudahan itu masuk akal. Salah satu masalah adalah bahwa ada begitu banyak cara untuk melakukan hal-hal di Git, bahwa dibutuhkan beberapa penjelasan untuk menggambarkan tertentu urutan tindakan.
Greg Hewgill
33

Solusi sederhana: Hapus cabang 'kerja' setelah penggabungan

Jawaban singkat: Anda dapat menggunakan git sesuka Anda (lihat di bawah untuk alur kerja sederhana), termasuk penggabungan. Pastikan saja mengikuti setiap ' git merge work ' dengan ' git branch -d work ' untuk menghapus cabang kerja sementara.

Penjelasan latar belakang: Masalah penggabungan / dcommit adalah bahwa setiap kali Anda 'git svn dcommit' cabang, riwayat gabungan cabang itu 'diratakan': git lupa tentang semua operasi penggabungan yang masuk ke cabang ini: Hanya isi file dipertahankan, tetapi fakta bahwa konten ini (sebagian) berasal dari cabang lain yang spesifik hilang. Lihat: Mengapa git svn dcommit kehilangan riwayat komit gabungan untuk cabang lokal?

(Catatan: Tidak banyak yang bisa dilakukan git-svn tentang hal itu: svn sama sekali tidak memahami penggabungan git yang jauh lebih kuat. Jadi, di dalam repositori svn, informasi penggabungan ini tidak dapat direpresentasikan dengan cara apa pun.)

Tapi ini adalah seluruh masalah. Jika Anda menghapus cabang 'work' setelah digabungkan ke dalam 'cabang master' maka repositori git Anda 100% bersih dan terlihat persis seperti repositori svn Anda.

Alur kerja saya: Tentu saja, saya pertama kali mengkloning repositori svn remote ke repositori git lokal (ini mungkin membutuhkan waktu):

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

Semua pekerjaan kemudian terjadi di dalam "direktori-lokal". Setiap kali saya perlu mendapatkan pembaruan dari server (seperti 'pembaruan svn'), saya lakukan:

$> git checkout master
$> git svn rebase

Saya melakukan semua pekerjaan pengembangan saya di 'pekerjaan' cabang terpisah yang dibuat seperti ini:

$> git checkout -b work

Tentu saja, Anda dapat membuat cabang sebanyak mungkin untuk pekerjaan Anda dan menggabungkan serta menyusun ulang di antara mereka sebanyak yang Anda inginkan (hapus saja ketika Anda selesai melakukannya --- seperti yang dibahas di bawah). Dalam pekerjaan normal saya, saya sangat sering melakukan:

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

Langkah selanjutnya (git rebase -i) adalah opsional --- itu hanya membersihkan sejarah sebelum mengarsipkannya di svn: Setelah saya mencapai batu stabil mil yang ingin saya bagikan dengan orang lain, saya menulis ulang sejarah 'pekerjaan' ini cabang dan bersihkan pesan komit (pengembang lain tidak perlu melihat semua langkah kecil dan kesalahan yang saya buat di jalan --- hanya hasilnya). Untuk ini, saya lakukan

$> git log

dan salin hash sha-1 dari commit terakhir yang aktif di repositori svn (seperti yang ditunjukkan oleh git-svn-id). Lalu aku menelepon

$> git rebase -i 74e4068360e34b2ccf0c5869703af458cde0cdcb

Cukup tempel sha-1 hash svn commit terakhir kami alih-alih milikku. Anda mungkin ingin membaca dokumentasi dengan 'git help rebase' untuk detailnya. Singkatnya: perintah ini pertama kali membuka editor yang menyajikan komit Anda ---- cukup ubah 'pilih' menjadi 'squash' untuk semua komit yang ingin Anda hancurkan dengan komit sebelumnya. Tentu saja, baris pertama harus tetap sebagai 'pick'. Dengan cara ini, Anda dapat menyingkat banyak komitmen kecil Anda menjadi satu atau lebih unit yang berarti. Simpan dan keluar dari editor. Anda akan mendapatkan editor lain yang meminta Anda untuk menulis ulang pesan log komit.

Singkatnya: Setelah saya menyelesaikan 'peretasan kode', saya memijat cabang 'pekerjaan' saya sampai terlihat bagaimana saya ingin mempresentasikannya kepada programmer lain (atau bagaimana saya ingin melihat pekerjaan dalam waktu beberapa minggu ketika saya menelusuri sejarah) .

Untuk mendorong perubahan ke repositori svn, saya lakukan:

$> git checkout master
$> git svn rebase

Sekarang kita kembali ke cabang 'master' lama yang diperbarui dengan semua perubahan yang terjadi pada waktu yang bersamaan di repositori svn (perubahan baru Anda disembunyikan di cabang 'kerja').

Jika ada perubahan yang mungkin berbenturan dengan perubahan 'pekerjaan' baru Anda, Anda harus menyelesaikannya secara lokal sebelum Anda mendorong pekerjaan baru Anda (lihat detail lebih lanjut di bawah). Kemudian, kita bisa mendorong perubahan kita ke 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

Catatan 1: Perintah 'git branch -d work' cukup aman: Perintah ini hanya memungkinkan Anda untuk menghapus cabang yang tidak Anda perlukan lagi (karena mereka sudah bergabung ke cabang Anda saat ini). Jika Anda mengeksekusi perintah ini secara tidak sengaja sebelum menggabungkan pekerjaan Anda dengan cabang 'master', Anda mendapatkan pesan kesalahan.

Catatan 2: Pastikan untuk menghapus cabang Anda dengan 'git branch -d work' antara penggabungan dan dcommit: Jika Anda mencoba untuk menghapus cabang setelah dcommit, Anda mendapatkan pesan kesalahan: Ketika Anda melakukan 'git svn dcommit', git lupa bahwa cabang Anda telah digabung dengan 'master'. Anda harus menghapusnya dengan 'git branch -D work' yang tidak melakukan pemeriksaan keamanan.

Sekarang, saya segera membuat cabang 'pekerjaan' baru untuk menghindari peretasan di cabang 'master' secara tidak sengaja:

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

Mengintegrasikan 'pekerjaan' Anda dengan perubahan pada svn: Inilah yang saya lakukan ketika 'git svn rebase' mengungkapkan bahwa orang lain mengubah repositori svn ketika saya sedang mengerjakan cabang 'work' saya:

$> 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

Ada solusi yang lebih kuat: Alur kerja yang disajikan sederhana: Menggunakan kekuatan git hanya dalam setiap putaran 'update / hack / dcommit' --- tetapi meninggalkan sejarah proyek jangka panjang sama linearnya dengan repositori svn. Ini tidak apa-apa jika Anda hanya ingin mulai menggunakan git merge dalam langkah pertama kecil dalam proyek svn lawas.

Ketika Anda menjadi lebih akrab dengan git gabung, silakan menjelajahi alur kerja lainnya: Jika Anda tahu apa yang Anda lakukan, Anda dapat mencampur git gabung dengan svn merge ( Menggunakan git-svn (atau serupa) hanya untuk membantu penggabungan svn? )

Yaakov Belch
sumber
Ini tampaknya tidak perlu rumit. Saya pernah mendengar ada yang melakukannya git merge --squash work, mengapa tidak melakukan ini? Saya dapat melihat melakukan squash commit di cabang sebelum bergabung jika Anda membuat lebih dari satu 'pick' (katakanlah Anda memiliki 8 commit dan Anda mengubah masing-masing 4 commit menjadi 1, dan menggabungkan 2 commit untuk dikuasai). Juga ketika memperbarui cabang 'kantor' saya, saya lakukan rebase, itu lebih sederhana daripada membuat cabang lain untuk cabang saya dan melakukan penggabungan ...
void.pointer
8

Jawaban Greg Hewgill di atas tidak aman! Jika ada komit baru muncul di trunk antara dua "git svn rebase", penggabungan tidak akan maju cepat.

Ini dapat dipastikan dengan menggunakan flag "--ff-only" ke git-merge, tapi saya biasanya tidak menjalankan "git svn rebase" di cabang, hanya "git rebase master" di atasnya (dengan asumsi itu hanya lokal cabang). Kemudian setelah itu "git merge thebranch" dijamin akan maju cepat.

Marius K
sumber
6

Cara aman untuk menggabungkan cabang svn di git adalah dengan menggunakan git merge --squash. Ini akan membuat satu komit dan berhenti bagi Anda untuk menambahkan pesan.

Katakanlah Anda memiliki cabang topik svn, yang disebut cabang svn.

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

pada titik ini Anda memiliki semua perubahan dari svn-cabang tergencet menjadi satu komit menunggu dalam indeks

git commit
luntain
sumber
Namun, seperti yang telah ditunjukkan oleh orang lain, ini benar-benar kehilangan rincian komitmen.
Kzqai
@Tchalvak: Terkadang Anda menginginkan hal itu. Saya sering melakukan di cabang fitur ala "mess tetap dari komit sebelumnya" dan jalan buntu ini saya suka sembunyikan karena reputasi yang baik :-)
schoetbi
1
Ya, meskipun saya menganggap itu sebagai jalan tali, karena setiap kali Anda squash, Anda juga kehilangan kemampuan untuk memisahkan komitmen individu nanti. Hanya masalah pilihan yang berbeda dari melakukan standar.
Kzqai
1
@schoetbi ya, terkadang Anda perlu membersihkan riwayat berantakan sebelum diterbitkan. Di sinilah 'git rebase -i <trunk>' sangat membantu: Anda dapat bergabung / menyusun ulang / menghapus dan bahkan membagi komit (!), Memperbaiki pesan secara selektif.
inger
5

Rebase cabang git lokal ke cabang master git lalu dcommit dan dengan cara itu Anda melakukan semua yang dilakukan secara berurutan sehingga orang-orang dapat melihatnya secara linear seperti yang biasa mereka lakukan. Jadi anggap Anda memiliki cabang lokal bernama topik yang bisa Anda lakukan

git rebase master topic

yang kemudian akan memainkan komit Anda di cabang master yang siap untuk Anda lakukan

JoeyJ
sumber