Hg: Cara melakukan rebase seperti rebit git

207

Di Git saya bisa melakukan ini:

1. Mulai bekerja pada fitur baru:
$ git co -b newfeature-123 # (cabang pengembangan fitur lokal)
lakukan beberapa komit (M, N, O)

master A --- B --- C
                \
newfeature-123 M --- N --- O

2. Tarik perubahan baru dari master hulu:
$ git pull
(master diperbarui dengan ff-commit)

master A --- B --- C --- D --- E --- F
                \
newfeature-123 M --- N --- O

3. Rebase off master sehingga fitur baru saya 
dapat dikembangkan melawan perubahan hulu terbaru:
(dari newfeature-123)
$ git rebase master

master A --- B --- C --- D --- E --- F
                            \
newfeature-123 M --- N --- O


Saya ingin tahu bagaimana melakukan hal yang sama di Mercurial, dan saya telah menjelajahi web untuk mendapatkan jawaban, tetapi yang terbaik yang bisa saya temukan adalah: git rebase - dapat hg melakukan itu

Tautan itu memberikan 2 contoh:
1. Saya akui ini: (mengganti revisi dari contoh dengan yang dari contoh saya sendiri)

hg up -CF  
cabang hg -f fitur baru-123  
hg transplantasi -a -b newfeature-123 

tidak terlalu buruk, kecuali bahwa ia meninggalkan MNO pra-rebase sebagai kepala yang tidak bermutasi dan menciptakan 3 komit baru M ', N', O 'yang mewakili mereka bercabang dari arus utama yang diperbarui.

Pada dasarnya masalahnya adalah saya berakhir dengan ini:

master A --- B --- C --- D --- E --- F
                \ \
newfeature-123 \ M '--- N' --- O '
                  \
newfeature-123 M --- N --- O

ini tidak baik karena meninggalkan komitmen lokal yang tidak diinginkan yang harus dijatuhkan.

  1. Opsi lain dari tautan yang sama adalah
hg qimport -r M: O
hg qpop -a
hg up F
cabang hg newfeature-123
hg qpush -a
hg qdel -r qbase: qtip

dan ini menghasilkan grafik yang diinginkan:

master A --- B --- C --- D --- E --- F
                            \
newfeature-123 M --- N --- O

tetapi perintah ini (semuanya 6)! Tampaknya jauh lebih rumit daripada

$ git rebase master

Saya ingin tahu apakah ini satu-satunya yang setara dalam Hg atau jika ada cara lain yang tersedia yang sederhana seperti Git.

jpswain
sumber
7
"Ini tidak baik karena meninggalkan komitmen lokal yang tidak diinginkan yang harus dijatuhkan." - sebenarnya, git melakukan hal yang sama. Itu tidak mengubah atau menghapus komit di cabang asli, itu hanya membuat yang baru menerapkan set perubahan yang sama di atas master. Anda masih dapat mengakses yang lama menggunakan git reflogdan mereka tidak benar-benar pergi sampai mereka mendapatkan sampah. Jika Anda ingin menyimpannya di cabang bernama sehingga Anda tidak harus menggunakan reflog, lakukan saja git branch feature-123_originalsebelum rebasing.
MatrixFrog
5
Pertanyaan acak: apakah Anda ascii-draw sendiri perubahan / cabang atau ada alat yang melakukan itu?
Amir Rachum
2
Hanya melakukannya sendiri dengan TextWrangler diatur ke "menimpa."
jpswain

Jawaban:

235

VonC memiliki jawaban yang Anda cari , Ekstensi Rebase. Namun demikian, layak menghabiskan satu atau dua detik untuk memikirkan mengapa mq atau rebase tidak diaktifkan secara default di mercurial: karena mercurial adalah semua tentang perubahan yang tidak terhapuskan. Ketika saya bekerja dengan cara yang Anda gambarkan, yang hampir setiap hari, inilah pola yang saya ambil:

1. Start working on a new feature:
$ hg clone mainline-repo newfeature-123
do a few commits (M, N, O)

master A---B---C
                \
newfeature-123   M---N---O

2. Pull new changes from upstream mainline:
$ hg pull

master A---B---C---D---E---F
                \
newfeature-123   M---N---O

3. merge master into my clone so that my new feature 
can be developed against the latest upstream changes:
(from newfeature-123)
$ hg merge F

master A---B---C---D---E---F
                \           \
newfeature-123   M---N---O---P

dan hanya itu yang perlu. Saya berakhir dengan klon fitur-123 yang baru. Saya dapat dengan mudah mendorong kembali ke garis utama ketika saya senang dengan itu. Namun yang paling penting, saya tidak pernah mengubah sejarah . Seseorang dapat melihat csets saya dan melihat apa yang semula dikodekan terhadapnya dan bagaimana saya bereaksi terhadap perubahan pada arus utama sepanjang pekerjaan saya. Tidak semua orang berpikir memiliki nilai, tapi saya sangat percaya bahwa tugas pengendalian sumber bukan untuk menunjukkan kepada kita apa yang kita harapkan terjadi, tetapi apa yang sebenarnya terjadi - setiap deadend dan setiap refactor harus meninggalkan jejak yang tidak terhapuskan, dan rebasing dan teknik penyuntingan riwayat lainnya menyembunyikannya.

Sekarang pilih jawaban VonC sementara saya menyimpan kotak sabun saya. :)

Ry4an Brase
sumber
18
Catatan: tentu saja, Git tidak persis memungkinkan Anda untuk menulis ulang sejarah, hanya untuk membuat yang baru dengan mudah ( utcc.utoronto.ca/~cks/space/blog/tech/GitNewHistory ). Dengan menambahkan RebaseExtension, Mercurial menyediakan cara mudah yang persis sama untuk mengganti riwayat lama dengan yang baru. Mengapa? Karena penggabungan tidak selalu merupakan jawaban yang tepat, terutama ketika perubahan Anda harus dilihat sebagai evolusi di atas F, dan bukan sebaliknya (P digabung di atas O)
VonC
19
VonC, saya setuju, penggabungan tidak selalu merupakan pilihan yang tepat, tetapi saya pikir perbedaannya adalah apa yang orang ingin agar riwayat VCS seseorang dapat memberi tahu mereka. Saya pikir sejarah harus selalu dapat menjawab pertanyaan seperti "Apa itu cara saya mencoba mengintegrasikannya pada awalnya yang tidak berhasil dan saya pikir tidak berguna pada saat itu". Para ilmuwan menyimpan buku catatan dengan halaman bernomor, dan insinyur perangkat lunak AFAIC harus menyimpan setiap byte yang pernah mereka ketikkan. Menulis ulang riwayat, bahkan asal usul, tetapi tentu saja CollapseExtension, HistEdit, dll. Melanggar itu. Ini benar-benar masalah pilihan pribadi.
Ry4an Brase
14
+1 untuk pilihan pribadi. Dengan Git saya akibatnya menggunakan rebase untuk menyimpang sepele dan bergabung untuk non-sepele. Ini memungkinkan saya untuk melestarikan menggabungkan sejarah di mana saya merasa penting, tetapi menjaga log bersih dan linier sebagian besar waktu.
Kos
16
Saya juga melakukan banyak rebases interaktif karena saya cenderung pertama melakukan banyak komit kecil, kemudian bergabung, memberi label dan membersihkannya, kemudian menggabungkannya kembali dengan (atau rebase di atas) cabang utama. Saya suka coding dan mengelola perubahan menjadi langkah terpisah.
Kos
6
Filosofi "selamatkan setiap byte di sepanjang jalur pengembangan" tidak benar-benar sesuai untuk proyek-proyek open-source besar, di mana kontributor mengusulkan satu set patch, kemudian mengolahnya berdasarkan umpan balik dari pengelola. Kemudian akhirnya repositori proyek utama hanya memiliki versi yang tepat dari semua perubahan, dengan semua perubahan terkait dalam satu komit. git rebase interaktif sangat baik untuk membersihkan perubahan yang bekerja menjadi urutan komit yang tidak membiarkan pohon rusak, dan dengan pesan komit yang mencerminkan apa yang Anda putuskan untuk dikatakan setelah Anda selesai mengerjakan semuanya.
Peter Cordes
104

Anda mungkin mencari Ekstensi Rebase . (diimplementasikan sebagai bagian dari SummerOfCode 2008 )

Dalam kasus-kasus tersebut, akan berguna untuk "melepaskan" perubahan lokal, menyinkronkan repositori dengan arus utama dan kemudian menambahkan perubahan pribadi di atas perubahan jarak jauh yang baru. Operasi ini disebut rebase.

Dapatkan dari :

teks alternatif

untuk:

teks alternatif


Seperti yang dikomentari di bawah ini oleh steprobe :

Dalam kasus di mana Anda tidak menarik perubahan, dan Anda memiliki dua cabang di repo Anda, Anda dapat melakukan ( menggunakankeepbranches ):

hg up newfeature-123 
hg rebase -d master --keepbranches

( --keepbranches: Mewarisi nama cabang asli.)

Mojca menyebutkan:

Saya suka menggunakan hg rebase --source {L1's-sha} --dest {R2's-sha}, tetapi saya tidak tahu saya bisa menambahkan --keepbranchesdi akhir.

Seperti diilustrasikan di bawah ini oleh Jonathan Blackburn :

 hg rebase -d default --keepbranches
VONC
sumber
1
Saya telah melihat Ekstensi Rebase, tetapi masih belum jelas bagi saya. Bisakah Anda jelaskan langkah-langkah untuk melakukan apa yang saya jelaskan di atas?
jpswain
6
Dalam kasus di mana Anda tidak menarik perubahan, dan Anda memiliki dua cabang di repo Anda, Anda dapat melakukan: hg up newfeature-123diikuti olehhg rebase -d master --keepbranches
steprobe
2
Saya percaya tidak ada yang salah dengan rebase, itu hanya masalah pilihan. Masalahnya adalah rebase disalahgunakan dan saya dengan @ Ry4an tentang ini jangan menulis ulang sejarah sehingga Anda bisa tahu apa yang terjadi dan kapan.
Jorge Vargas
@steprobe: terima kasih atas petunjuknya untuk menggunakan --keepbranches. Saya suka menggunakan hg rebase --source {L1's-sha} --dest {R2's-sha}, tetapi saya tidak tahu saya bisa menambahkan --keepbranchesdi akhir. Ini disebutkan dalam jawaban peringkat bawah lainnya, tetapi alangkah baiknya jika menuliskannya secara eksplisit untuk jawaban ini juga.
Mojca
@Mojca Tidak masalah. Saya telah mengedit jawaban sesuai dengan itu.
VonC
44

Dengan asumsi Anda memiliki instalasi Hg modern, Anda dapat menambahkan:

[extensions]
rebase = 

ke ~ / .hgrc.

Kemudian Anda dapat menggunakan perintah hg rebase, hg pull --rebaseatau hg help rebase.

sblom
sumber
2
Hanya dengan menambahkan perintah ini maka Anda perlu mengeksekusi akan menjadi: hg rebase -s o -d f
Simple-Solution
21

Saya tidak berpikir jawaban di atas mencapai tujuan OP, yaitu untuk mempertahankan cabang tugasnya, baru saja diubah terhadap poin selanjutnya pada cabang induk.

Katakanlah saya mulai dengan grafik ini (dihasilkan menggunakan ekstensi graphlog. Geek cinta serius untuk graphlog).

@  9a4c0eb66429 Feature 3 commit 2 tip feature3
|
| o  af630ccb4a80 default againagainagain  
| |
o |  98bdde5d2185 Feature 3 branch commit 1  feature3
|/
o  e9f850ac41da foo   

Jika saya berada di cabang feature3 dan ingin mengembalikannya dari komitmen lagi lagi, saya mengerti bahwa saya akan lari hg rebase -d default. Ini memiliki hasil sebagai berikut:

@  89dada24591e Feature 3 commit 2 tip 
|
o  77dcce88786d Feature 3 branch commit 1  
|
o  af630ccb4a80 default againagainagain  
|
o  e9f850ac41da foo  

Misi selesai? Saya kira tidak. Masalahnya adalah ketika komit pada cabang feature3 direstrukturisasi kembali lagi, cabang fitur3 dihapus . Komit saya telah dipindahkan ke cabang default, yang merupakan hal yang saya coba hindari sejak awal.

Di Git, hasilnya akan terlihat seperti ini:

@  9a4c0eb66429 Feature 3 commit 2 tip
|
o  98bdde5d2185 Feature 3 branch commit 1 **feature3**
|
o  af630ccb4a80 default againagainagain
|
o  e9f850ac41da foo

Perhatikan bahwa cabang feature3 masih ada, kedua komit masih pada cabang feature3, dan tidak terlihat secara default. Tanpa mempertahankan cabang tugas, saya tidak melihat bagaimana ini secara fungsional berbeda dari gabungan.

UPDATE : Saya menemukan --keepbranchesbendera yang didukung oleh hg rebase, dan saya senang melaporkan semuanya okey-dokey. Dengan menggunakan hg rebase -d default --keepbranches, saya mereplikasi perilaku Git yang saya inginkan. Beberapa alias kemudian dan saya rebasing seperti urusan siapa pun.

Jonathan Blackburn
sumber
7
Baru saja menemukan bendera --keepbranches di rebase. Masalah terpecahkan. Kalau ini kode saya, saya akan menjadikannya default, tapi hanya saya.
Jonathan Blackburn
1
Saya pikir ini akan berguna jika Anda menambahkan sedikit informasi itu ke respons Anda di atas - butuh beberapa saat untuk menemukannya.
HansMari
3

Karena beberapa orang telah menimpali dengan mengatakan mereka pikir itu baik untuk menjaga setiap iterasi dari segalanya, saya akan menunjukkan bahwa untuk proyek open-source yang lebih besar, menerima perubahan yang penuh dengan penggabungan dan pengembangan iterasi akan membuat sejarah revisi garis utama berantakan, dan membuat riwayat revisi kurang bermanfaat untuk melihat bagaimana versi saat ini sampai di sana.

Ini berfungsi dengan baik ketika perubahan yang diajukan ditinjau oleh orang-orang yang tidak menulisnya, sebelum diterima, sehingga perubahan yang masuk ke jalur utama umumnya didebug dan berfungsi. Lalu ketika Anda mundur ke asal suatu baris, Anda melihat semua perubahan yang menyertainya, bukan titik di tengah pengembangan perubahan itu bagian dari.

The kontributor x265 Halaman menjelaskan bagaimana untuk kembali melakukan serangkaian perubahan yang sedang Anda kerjakan, untuk membuat mereka siap untuk diserahkan ke proyek x265. (Termasuk penggunaan TortoiseHG untuk mengkomit beberapa tetapi tidak semua perubahan dalam file individual, seperti stage git gui / unstage diff hunk untuk commit).

Prosesnya adalah untuk mendapatkan hg diperbarui ke ujung hulu, dan kemudian dapatkan semua perubahan Anda tidak dikomit di direktori kerja. Singkirkan apa pun yang bukan bagian dari apa yang ingin Anda kirim, kemudian bagi sisanya menjadi banyak komit terpisah yang sesuai, dengan pesan komit yang bagus.

Saya kira Anda akan menyalin / menempel dan kemudian mengedit pesan komit dari iterasi sebelumnya dari patchset yang sedang Anda revisi. Atau mungkin Anda bisa mencangkokkan komitmen lama Anda (pilih-ceri dalam bahasa git), dan kemudian ubah satu per satu, untuk mendapatkan pesan komit lama Anda sebagai titik awal untuk mengedit.

Peter Cordes
sumber