Kami memiliki repositori Git dengan lebih dari 400 komit, beberapa lusin pertama adalah banyak trial-and-error. Kami ingin membersihkan komitmen ini dengan menekan banyak menjadi satu komit. Tentu saja, git-rebase sepertinya jalan yang harus ditempuh. Masalah saya adalah bahwa itu berakhir dengan menggabungkan konflik, dan konflik ini tidak mudah untuk diselesaikan. Saya tidak mengerti mengapa harus ada konflik sama sekali, karena saya hanya menekan komitmen (tidak menghapus atau mengatur ulang). Sangat mungkin, ini menunjukkan bahwa saya tidak sepenuhnya memahami bagaimana git-rebase melakukan squash-nya.
Ini versi modifikasi dari skrip yang saya gunakan:
repo_squash.sh (ini adalah skrip yang sebenarnya dijalankan):
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
GIT_EDITOR=../repo_squash_helper.sh git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
repo_squash_helper.sh (skrip ini hanya digunakan oleh repo_squash.sh):
if grep -q "pick " $1
then
# cp $1 ../repo_squash_history.txt
# emacs -nw $1
sed -f ../repo_squash_list.txt < $1 > $1.tmp
mv $1.tmp $1
else
if grep -q "initial import" $1
then
cp ../repo_squash_new_message1.txt $1
elif grep -q "fixing bad import" $1
then
cp ../repo_squash_new_message2.txt $1
else
emacs -nw $1
fi
fi
repo_squash_list.txt: (file ini hanya digunakan oleh repo_squash_helper.sh)
# Initial import
s/pick \(251a190\)/squash \1/g
# Leaving "Needed subdir" for now
# Fixing bad import
s/pick \(46c41d1\)/squash \1/g
s/pick \(5d7agf2\)/squash \1/g
s/pick \(3da63ed\)/squash \1/g
Saya akan meninggalkan konten "pesan baru" ke imajinasi Anda. Awalnya, saya melakukan ini tanpa opsi "--strategy theirs" (yaitu, menggunakan strategi default, yang jika saya memahami dokumentasi dengan benar adalah rekursif, tapi saya tidak yakin strategi rekursif mana yang digunakan), dan juga tidak t bekerja. Juga, saya harus menunjukkan bahwa, dengan menggunakan kode komentar di repo_squash_helper.sh, saya menyimpan file asli yang digunakan pada skrip sed dan menjalankan skrip sed terhadapnya untuk memastikan ia melakukan apa yang saya inginkan ( dulu). Sekali lagi, saya bahkan tidak tahu mengapa akan ada konflik, jadi sepertinya tidak masalah strategi mana yang digunakan. Setiap saran atau wawasan akan sangat membantu, tetapi kebanyakan saya hanya ingin agar squashing ini bekerja.
Diperbarui dengan informasi tambahan dari diskusi dengan Jefromi:
Sebelum mengerjakan repositori "nyata" kami yang besar, saya menggunakan skrip yang mirip pada repositori uji. Itu adalah repositori yang sangat sederhana dan tesnya bekerja dengan baik.
Pesan yang saya dapatkan saat gagal adalah:
Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir
Ini adalah pilihan pertama setelah komit squash pertama. Menjalankan git status
menghasilkan direktori kerja yang bersih. Jika saya kemudian melakukan git rebase --continue
, saya mendapatkan pesan yang sangat mirip setelah beberapa komitmen lagi. Jika saya melakukannya lagi, saya mendapatkan pesan yang sangat mirip setelah beberapa lusin melakukan. Jika saya melakukannya lagi, kali ini melewati sekitar seratus komit, dan menghasilkan pesan ini:
Automatic cherry-pick failed. After resolving the conflicts,
mark the corrected paths with 'git add <paths>', and
run 'git rebase --continue'
Could not apply f1de3bc... Incremental
Jika saya lari git status
, saya dapat:
# Not currently on any branch.
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
# modified: repo/file_A.cpp
# modified: repo/file_B.cpp
#
# Unmerged paths:
# (use "git reset HEAD <file>..." to unstage)
# (use "git add/rm <file>..." as appropriate to mark resolution)
#
# both modified: repo/file_X.cpp
#
# Changed but not updated:
# (use "git add/rm <file>..." to update what will be committed)
# (use "git checkout -- <file>..." to discard changes in working directory)
#
# deleted: repo/file_Z.imp
Bit "keduanya dimodifikasi" terdengar aneh bagi saya, karena ini hanya hasil dari pick. Perlu juga dicatat bahwa jika saya melihat "konflik", itu bermuara pada satu baris dengan satu versi memulainya dengan karakter [tab], dan yang lainnya dengan empat spasi. Ini terdengar seperti itu mungkin masalah dengan bagaimana saya mengatur file konfigurasi saya, tetapi tidak ada yang seperti itu di dalamnya. (Saya memang mencatat bahwa core.ignorecase disetel ke true, tetapi ternyata git-clone melakukannya secara otomatis. Saya tidak sepenuhnya terkejut dengan hal itu mengingat sumber aslinya ada di mesin Windows.)
Jika saya memperbaiki file_X.cpp secara manual, kemudian gagal tidak lama kemudian dengan konflik lain, kali ini antara file (CMakeLists.txt) yang menurut satu versi seharusnya ada dan satu versi berpikir tidak seharusnya. Jika saya memperbaiki konflik ini dengan mengatakan saya ingin file ini (yang saya lakukan), beberapa komitmen kemudian saya mendapatkan konflik lain (dalam file yang sama) di mana sekarang ada beberapa perubahan yang tidak sepele. Masih hanya sekitar 25% dari jalan melalui konflik.
Saya juga harus menunjukkan, karena ini mungkin sangat penting, bahwa proyek ini dimulai dalam repositori svn. Sejarah awal itu sangat mungkin diimpor dari repositori svn itu.
Perbarui # 2:
Pada lark (dipengaruhi oleh komentar Jefromi), saya memutuskan untuk melakukan perubahan repo_squash.sh saya menjadi:
rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Dan kemudian, saya baru saja menerima entri asli, sebagaimana adanya. Yaitu, "rebase" seharusnya tidak mengubah apa pun. Itu berakhir dengan hasil yang sama dengan yang dijelaskan sebelumnya.
Perbarui # 3:
Atau, jika saya menghilangkan strategi dan mengganti perintah terakhir dengan:
git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a
Saya tidak lagi mendapatkan masalah rebase "apa-apa untuk dilakukan", tetapi saya masih memiliki konflik lainnya.
Perbarui dengan repositori mainan yang menciptakan masalah:
test_squash.sh (ini adalah file yang sebenarnya Anda jalankan):
#========================================================
# Initialize directories
#========================================================
rm -rf test_squash/ test_squash_clone/
mkdir -p test_squash
mkdir -p test_squash_clone
#========================================================
#========================================================
# Create repository with history
#========================================================
cd test_squash/
git init
echo "README">README
git add README
git commit -m"Initial commit: can't easily access for rebasing"
echo "Line 1">test_file.txt
git add test_file.txt
git commit -m"Created single line file"
echo "Line 2">>test_file.txt
git add test_file.txt
git commit -m"Meant for it to be two lines"
git checkout -b dev
echo Meaningful code>new_file.txt
git add new_file.txt
git commit -m"Meaningful commit"
git checkout master
echo Conflicting meaningful code>new_file.txt
git add new_file.txt
git commit -m"Conflicting meaningful commit"
# This will conflict
git merge dev
# Fixes conflict
echo Merged meaningful code>new_file.txt
git add new_file.txt
git commit -m"Merged dev with master"
cd ..
#========================================================
# Save off a clone of the repository prior to squashing
#========================================================
git clone test_squash test_squash_clone
#========================================================
#========================================================
# Do the squash
#========================================================
cd test_squash
GIT_EDITOR=../test_squash_helper.sh git rebase -i HEAD@{7}
#========================================================
#========================================================
# Show the results
#========================================================
git log
git gc
git reflog
#========================================================
test_squash_helper.sh (digunakan oleh test_sqash.sh):
# If the file has the phrase "pick " in it, assume it's the log file
if grep -q "pick " $1
then
sed -e "s/pick \(.*\) \(Meant for it to be two lines\)/squash \1 \2/g" < $1 > $1.tmp
mv $1.tmp $1
# Else, assume it's the commit message file
else
# Use our pre-canned message
echo "Created two line file" > $1
fi
PS: Ya, saya tahu beberapa dari Anda merasa ngeri ketika melihat saya menggunakan emacs sebagai editor yang mundur.
PPS: Kami tahu kami harus membuang semua klon dari repositori yang ada setelah rebase. (Di sepanjang baris "kamu tidak boleh rebase repositori setelah itu diterbitkan".)
PPPS: Adakah yang bisa memberi tahu saya cara menambahkan hadiah untuk ini? Saya tidak melihat opsi di mana pun di layar ini apakah saya dalam mode edit atau mode tampilan.
sumber
rebase -p
lagi)rebase --interactive
- itu adalah semacam daftar tindakan untuk git untuk mencoba. Saya berharap Anda dapat mengurangi ini menjadi satu squash yang menyebabkan konflik, dan menghindari semua kerumitan tambahan dari skrip pembantu Anda. Informasi lain yang hilang adalah ketika konflik terjadi - kapan git menerapkan tambalan untuk membentuk squash, atau ketika ia mencoba bergerak melewati squash dan menerapkan tambalan berikutnya? (Dan Anda yakin tidak ada hal buruk terjadi dengan GIT_EDITOR kludge Anda? Voting lain untuk test case sederhana.)Jawaban:
Baiklah, saya cukup percaya diri untuk mengeluarkan jawaban. Mungkin harus mengeditnya, tapi saya yakin saya tahu apa masalah Anda.
Kasing pengujian mainan repo Anda memiliki penggabungan - yang lebih buruk, penggabungan dengan konflik. Dan Anda rebasing melintasi penggabungan. Tanpa
-p
(yang tidak sepenuhnya bekerja dengan-i
), gabungan diabaikan. Ini berarti bahwa apa pun yang Anda lakukan dalam resolusi konflik Anda tidak ada ketika rebase mencoba untuk memilih komit berikutnya, jadi tambalannya mungkin tidak berlaku. (Saya percaya ini ditampilkan sebagai konflik penggabungan karenagit cherry-pick
dapat menerapkan tambalan dengan melakukan penggabungan tiga arah antara komit asli, komit saat ini, dan leluhur bersama.)Sayangnya, seperti yang kita catat di komentar,
-i
dan-p
(pertahankan penggabungan) tidak rukun. Saya tahu pekerjaan pengeditan / penulisan ulang, dan pemesanan ulang itu tidak. Namun, saya percaya itu berfungsi baik dengan squash. Ini tidak didokumentasikan, tetapi berfungsi untuk kasus uji yang saya jelaskan di bawah ini. Jika kasing Anda adalah jalan, jauh lebih rumit, Anda mungkin memiliki banyak kesulitan untuk melakukan apa yang Anda inginkan, meskipun itu masih mungkin. (Moral cerita: bersihkan denganrebase -i
sebelum penggabungan.)Jadi, anggaplah kita memiliki kasus yang sangat sederhana, di mana kita ingin menyatukan A, B, dan C:
Sekarang, seperti yang saya katakan, jika tidak ada konflik di X,
git rebase -i -p
berfungsi seperti yang Anda harapkan.Jika ada konflik, segalanya menjadi sedikit rumit. Ini akan melakukan squashing yang baik, tetapi kemudian ketika ia mencoba untuk membuat ulang penggabungan, konflik akan terjadi lagi. Anda harus menyelesaikannya lagi, menambahkannya ke indeks, lalu gunakan
git rebase --continue
untuk melanjutkan. (Tentu saja, Anda dapat menyelesaikannya lagi dengan memeriksa versi dari komit gabungan asli.)Jika Anda kebetulan telah
rerere
diaktifkan pada repo Anda (rerere.enabled
set ke true), ini akan menjadi cara yang lebih mudah - git akan dapat kembali menggunakan kembali dijalin dgn tali re solusi dari ketika Anda awalnya memiliki konflik, dan semua yang harus Anda lakukan yaitu memeriksanya untuk memastikan itu berfungsi dengan benar, tambahkan file ke indeks, dan lanjutkan. (Anda bahkan dapat melangkah lebih jauh, menyalakanrerere.autoupdate
, dan itu akan menambahkannya untuk Anda, sehingga penggabungan bahkan tidak akan gagal). Saya menduga, bagaimanapun, bahwa Anda tidak pernah mengaktifkan rerere, jadi Anda harus melakukan resolusi konflik sendiri. ** Atau, Anda dapat mencoba
rerere-train.sh
skrip dari git-contrib, yang mencoba "Perdana [yang] menjalankan kembali database dari komit gabungan yang ada" - pada dasarnya, ia memeriksa semua komit gabungan, mencoba untuk menggabungkannya, dan jika gabungan tersebut gagal, itu meraih hasil dan menunjukkannyagit-rerere
. Ini bisa menghabiskan waktu, dan saya belum pernah menggunakannya, tetapi mungkin akan sangat membantu.sumber
-p
opsi.git commit -a -m"Some message"
dangit rebase --continue
, dan itu akan berlanjut. Ini berfungsi bahkan tanpa-p
opsi, tetapi bekerja lebih baik dengan-p
opsi (karena saya tidak melakukan pemesanan ulang, tampaknya itu-p
baik-baik saja). Bagaimanapun, saya akan membuat Anda tetap diposting.git config --global rerere.enabled true
dangit config --global rerere.autoupdate true
sebelum menjalankan contoh pengujian tidak menyelesaikan masalah utama. Cukup menarik, bagaimanapun, itu tidak mempertahankan penggabungan, bahkan ketika menentukan--preserve-merges
. Namun, jika saya tidak memiliki set itu, dan saya mengetikgit commit -a -m"Meaningful commit"
dangit rebase --continue
saat menentukan--preserve-merges
, itu mempertahankan penggabungan. Bagaimanapun, terima kasih telah membantu saya mengatasi masalah ini!Jika Anda tidak keberatan membuat cabang baru, inilah cara saya menangani masalah ini:
Menjadi yang utama:
sumber
git diff new_clean_branch..original_messy_branch
haruskah mereka sama? Bagi saya mereka berbeda.Saya sedang mencari persyaratan yang sama, yaitu membuang komitmen intermeiate dari cabang pengembangan saya, saya telah menemukan prosedur ini bekerja untuk saya.
di cabang kerja saya
viola kami telah menghubungkan komit terbaru ke komit awal cabang! Tidak ada masalah konflik penggabungan! Dalam praktik belajar saya, saya sampai pada kesimpulan ini pada tahap ini, Apakah ada pendekatan yang lebih baik untuk tujuan tersebut.
sumber
Membangun di atas jawaban hebat @ hlidka di atas yang meminimalkan intervensi manual, saya ingin menambahkan versi yang mempertahankan setiap komit baru pada master yang tidak di cabang untuk squash.
Karena saya percaya ini bisa dengan mudah hilang pada
git reset
langkah dalam contoh itu.sumber
Perhatikan bahwa
-X
dan opsi strategi diabaikan ketika digunakan dalam rebase interaktif.Lihat commit db2b3b820e2b28da268cc88adff076b396392dfe (Juli 2013, git 1.8.4+),
Itu berarti
-X
dan strategi sekarang bekerja dengan rebase interaktif, serta rebase sederhana, dan skrip awal Anda sekarang bisa berfungsi lebih baik.sumber
Saya mengalami masalah yang lebih sederhana namun serupa, di mana saya telah 1) menyelesaikan konflik gabungan di cabang lokal, 2) terus bekerja menambahkan banyak lebih sedikit komitmen, 3) ingin rebase dan memukul konflik gabungan.
Bagi saya,
git rebase -p -i master
berhasil. Itu membuat resolusi konflik asli berkomitmen dan memungkinkan saya untuk menekan yang lain di atas.Semoga itu bisa membantu seseorang!
sumber