Git: Bagaimana cara rebase ke komit tertentu?

154

Saya ingin rebase ke komit tertentu, bukan ke KEPALA cabang lainnya:

A --- B --- C          master
 \
  \-- D                topic

untuk

A --- B --- C          master
       \
        \-- D          topic

dari pada

A --- B --- C          master
             \
              \-- D    topic

Bagaimana saya bisa mencapainya?

Ondra Žižka
sumber
4
Sudahkah Anda mencoba melakukan git checkout Bsebelum menjalankan git rebase?
Peter-Paul van Gemerden
Tidak, haruskah itu membantu? Saya kira hanya referensi dari rebaseperintah yang penting.
Ondra Žižka

Jawaban:

98

Anda dapat menghindari menggunakan parameter --onto dengan membuat cabang temp pada komit yang Anda sukai dan kemudian menggunakan rebase dalam bentuknya yang sederhana:

git branch temp master^
git checkout topic
git rebase temp
git branch -d temp
Adam Dymitruk
sumber
5
Saya lebih suka pendekatan seperti RISC ini :) Akan mencoba. Terima kasih.
Ondra Žižka
10
Saya bertanya-tanya mengapa itu tidak berhasil untuk saya, dalam skenario yang sedikit berbeda . Saya ingin grup melewati pep8 dan didasarkan pada master . git rebase temp(ketika di grup ) menyerah dengan "Grup cabang saat ini adalah yang terbaru.".
Alois Mahdal
4
Solusi ini tidak akan berfungsi untuk skenario di mana topik telah diubah menjadi master, tetapi Anda ingin rebase pada leluhur untuk dikuasai. Dalam hal ini Anda harus menggunakan git rebase --onto <target> <from> <to>sehingga Anda dapat menentukan komit <dari>.
mirzmaster
Sejauh ini, ini adalah opsi termudah jika Anda ingin rebase ke komit yang sama dengan cabang.
Ash
1
Saya tampaknya bekerja, tetapi GitLab mengatakan sesuatu yang lain. Jika saya memiliki 10 komitmen di belakang, 5 komitmen di depan, harapan saya adalah memiliki 8 komitmen di belakang, 5 komitmen di depan setelah mendapatkan 2 komitmen. Tetapi alih-alih ini, ia menambahkan lebih banyak komitmen ke 5. mereka.
ROMANIA_engineer
67

Anda bahkan dapat mengambil pendekatan langsung:

git checkout topic
git rebase <commitB>
r0hitsharma
sumber
6
Bagi saya, ini sebenarnya tidak melakukan apa yang dimaksudkan. Sejauh yang saya tahu, itu mencoba untuk rebase ke "leluhur bersama terakhir" dari topicdan commitB.
Dan Lenski
2
@DanLenski, bukan itu cara rebase bekerja. Mengutip dokumen , It works by going to the common ancestor of the two branches (the one you’re on and the one you’re rebasing onto), getting the diff introduced by each commit of the branch you’re on, saving those diffs to temporary files, resetting the current branch to the same commit as the branch you are rebasing onto, and finally applying each change in turn. saya mencobanya lagi sekarang dan sepertinya berfungsi dengan baik.
r0hitsharma
1
Sangat mudah dan bekerja! Saya sekarang memiliki: commitB_from_master-> topicCommit1-> topicCommit2.
Martin Konicek
Itu tidak berhasil untuk saya. Sebelum ini, GitLab mengatakan "n berkomitmen di depan". Dan sekarang, dikatakan "aku berkomitmen di depan" di mana m > n.
ROMANIA_engineer
48

Gunakan opsi "ke":

git rebase --onto master^ D^ D
Adam Dymitruk
sumber
2
Ddan D^apakah hash dari commit terakhir dan selanjutnya dari "topik"?
Ondra Žižka
39
Sintaksnya seperti git rebase --onto <new-parent> <old-parent>. Lihat Mengatur pointer git parent ke induk yang berbeda . Dalam kasus Anda, <new-parent> adalah B, dan <old-parent> adalah A.
jsz
7
Saya selalu menggunakan 3 argumen: desitnation, mulai dan akhir dari komitmen untuk rebase.
Adam Dymitruk
14
Ini bekerja untuk saya:git rebase --onto <commit-ID> master
4
Komentar @ jsz benar, bertentangan dengan komentar Simon South, itu sebaliknya: git rebase --onto master <commit-ID-of-old-parent>dan untuk OP git rebase --onto B A.
Gaborous
19

Komentar oleh jsz di atas menyelamatkan saya dari banyak rasa sakit, jadi inilah penerima langkah-demi-langkah berdasarkan itu yang telah saya gunakan untuk rebase / memindahkan komit di atas komit lainnya:

  1. Temukan titik percabangan sebelumnya dari cabang yang akan diubah (dipindahkan) - sebut saja orang tua itu. Dalam contoh di atas itu A
  2. Temukan komit yang ingin Anda pindahkan cabang ke - sebut saja induk baru. Dalam contoh itu adalah B
  3. Anda harus berada di cabang Anda (yang Anda pindahkan):
  4. Terapkan rebase Anda: git rebase --onto <new parent> <old parent>

Dalam contoh di atas itu sesederhana:

   git checkout topic
   git rebase --onto B A
Nestor Milyaev
sumber
6
Ini seharusnya jawaban yang benar. Kecuali yang saya gunakan git rebase --onto B master, lihat jawaban saya untuk penjelasan yang lebih menyeluruh.
Zack Morris
Itu tidak berfungsi dengan baik untuk saya. Saya memilih 2 komit berturut-turut (yang terakhir dari master yang ada di cabang saat ini dan yang pertama dari master yang tidak ada di cabang saat ini). Saya mulai dengan 100 di belakang - 10 di depan dan bukannya memiliki 99 di belakang - 10 di depan, sekarang saya memiliki 105 di belakang - 13 di depan.
ROMANIA_engineer
Maaf mendengarnya tidak berhasil. Kedengarannya cabang Anda sedikit berbeda - saya sarankan jepit dulu sebelum mencoba rebase cabang dengan banyak perbedaan ini.
Nestor Milyaev
11

Solusi Topik

Perintah yang benar untuk menjawab pertanyaan yang diposting dapat berupa salah satu dari yang berikut (dengan asumsi cabang topicsudah diperiksa):

git rebase --onto B master
git rebase --onto master~1 master
git rebase --onto B A
git rebase --onto B C
git rebase --onto B

Jika topictidak dicentang, Anda cukup menambahkan topicperintah (kecuali yang terakhir) seperti:

git rebase --onto B master topic

Atau, periksa cabang pertama dengan:

git checkout topic

Rebase Setiap String Komit ke Komit Target

Bentuk dasar dari perintah yang kita butuhkan, yang dikutip dari dokumentasi, adalah:

git rebase --onto <Target> [<Upstream> [<Branch>]]

<Branch>bersifat opsional dan yang dilakukannya hanyalah memeriksa cabang yang ditentukan sebelum menjalankan sisa perintah. Jika Anda sudah memeriksa cabang yang ingin Anda rebase, maka Anda tidak perlu ini. Perhatikan bahwa Anda harus menentukan <Upstream>untuk menentukan <Branch>atau git akan berpikir Anda menentukan <Upstream>.

<Target>adalah komitmen yang akan kami lampirkan pada komitmen kami. Saat memberikan nama cabang, Anda cukup menentukan komit kepala cabang itu. <Target>bisa berupa komit apa pun yang tidak akan dimuat dalam string komit yang dipindahkan. Sebagai contoh:

A --- B --- C --- D         master
      \
       \-- X --- Y --- Z    feature

Untuk memindahkan seluruh cabang fitur, Anda tidak dapat memilih X, Y, Z, atau featuresebagai <Target>karena mereka semua komit dalam kelompok dipindahkan.

<Upstream>istimewa karena dapat berarti dua hal yang berbeda. Jika itu adalah komit yang merupakan leluhur dari cabang yang diperiksa, maka itu berfungsi sebagai titik potong. Dalam contoh saya disediakan, ini akan menjadi sesuatu yang tidak C, Datau master. Semua komit setelah <Upstream>sampai kepala cabang yang diperiksa adalah yang akan dipindahkan.

Namun, jika <Upstream>bukan leluhur, maka git mencadangkan rantai dari komit yang ditentukan sampai jika menemukan leluhur yang sama dengan cabang yang dicentang (dan batal jika tidak dapat menemukannya). Dalam kasus kami, seorang <Upstream>dari B, C, D, atau mastersemua akan mengakibatkan berkomitmen Bmelayani sebagai titik potong. <Upstream>itu sendiri merupakan perintah opsional dan jika tidak ditentukan, maka git melihat induk dari cabang yang diperiksa yang setara dengan memasukkan master.

Sekarang git telah memilih commit yang akan dipotong dan dipindahkan, ia menerapkannya untuk <Target>, melompati semua yang sudah diterapkan pada target.

Contoh dan Hasil yang Menarik

Menggunakan titik awal ini:

A --- B --- C --- D --- E         master
            \
             \-- X --- Y --- Z    feature
  • git rebase --onto D A feature
    Akan berlaku komit B, C, X, Y, Zuntuk melakukan Ddan berakhir melompat-lompat Bdan Ckarena mereka sudah telah diterapkan.

  • git rebase --onto C X feature
    Akan menerapkan komit Ydan Zberkomitmen C, menghapus komit secara efektifX

Isaac Brown
sumber
4

Solusi yang lebih sederhana adalah git rebase <SHA1 of B> topic. Ini berfungsi terlepas dari di mana Anda HEADberada.

Kami dapat mengkonfirmasi perilaku ini dari git rebase doc

<upstream>Cabang hulu untuk dibandingkan. Mungkin komit apa pun yang valid , bukan hanya nama cabang yang ada. Default ke upstream yang dikonfigurasi untuk cabang saat ini.


Anda mungkin berpikir apa yang akan terjadi jika saya menyebutkan SHA1 topicjuga dalam perintah di atas?

git rebase <SHA1 of B> <SHA1 of topic>

Ini juga akan berfungsi tetapi rebase tidak akan Topicmenunjukkan cabang baru yang dibuat dan HEADakan berada dalam kondisi terpisah. Jadi dari sini Anda harus menghapus secara manual Topicdan membuat referensi cabang baru pada cabang baru yang dibuat oleh rebase.

Number945
sumber
3

Saya telah menggunakan campuran solusi yang dijelaskan di atas:

$ git branch temp <specific sha1>
$ git rebase --onto temp master topic
$ git branch -d temp

Saya merasa lebih mudah untuk membaca dan memahami. Solusi yang diterima membawa saya ke konflik gabungan (terlalu malas untuk diperbaiki dengan tangan):

$ git rebase temp
First, rewinding head to replay your work on top of it...
Applying: <git comment>
Using index info to reconstruct a base tree...
M       pom.xml
.git/rebase-apply/patch:10: trailing whitespace.
    <some code>
.git/rebase-apply/patch:17: trailing whitespace.
        <some other code>
warning: 2 lines add whitespace errors.
Falling back to patching base and 3-way merge...
Auto-merging pom.xml
CONFLICT (content): Merge conflict in pom.xml
error: Failed to merge in the changes.
Patch failed at 0001 <git comment>
The copy of the patch that failed is found in: .git/rebase-apply/patch

When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
malat
sumber
1
sama di sini, beberapa file mengalami konflik ketika saya menggunakan 2 jawaban paling populer (yaitu oleh r0hitsharma dan Dymitruk)
Oliver
3

Karena rebasing sangat mendasar, inilah perluasan jawaban Nestor Milyaev . Menggabungkan komentar jsz dan Simon South dari jawaban Adam Dymitruk menghasilkan perintah ini yang bekerja di topiccabang terlepas dari apakah itu cabang dari masterkomit cabang Aatau C:

git checkout topic
git rebase --onto <commit-B> <pre-rebase-A-or-post-rebase-C-or-base-branch-name>

Perhatikan bahwa argumen terakhir diperlukan (jika tidak, akan memutar kembali cabang Anda untuk melakukan B).

Contoh:

# if topic branches from master commit A:
git checkout topic
git rebase --onto <commit-B> <commit-A>
# if topic branches from master commit C:
git checkout topic
git rebase --onto <commit-B> <commit-C>
# regardless of whether topic branches from master commit A or C:
git checkout topic
git rebase --onto <commit-B> master

Jadi perintah terakhir adalah yang biasanya saya gunakan.

Zack Morris
sumber
-2

Ada cara lain untuk melakukannya atau jika Anda ingin kembali ke lebih dari satu komitmen.

Berikut adalah contoh untuk kembali ke nsejumlah komitmen:

git branch topic master~n

Demi pertanyaan ini, ini juga dapat dilakukan:

git branch topic master~1

Perintah ini bekerja dengan sempurna git version 2.7.4. Belum mengujinya pada versi lain.

Talha Ashraf
sumber
Apakah Anda salah menafsirkan ini sebagai pertanyaan tentang percabangan? Ini sebenarnya pertanyaan tentang rebasing.
NetherGranite