Git-rebase neden yaptığım tek şey eziyet etmek olduğunda bana birleşme çatışmaları veriyor?


146

İlk birkaç düzine deneme-yanılma içeren 400'den fazla taahhüt içeren bir Git depomuz var. Bu taahhütleri birçoğunu tek bir taahhütte ezerek temizlemek istiyoruz. Doğal olarak, git-rebase gidilecek yol gibi görünüyor. Benim sorunum, birleştirme çakışmaları ile sonuçlanması ve bu çatışmaların çözülmesi kolay olmamasıdır. Neden herhangi bir çatışma olması gerektiğini anlamıyorum, çünkü sadece taahhütleri eziyorum (silme veya yeniden düzenleme değil). Büyük olasılıkla, bu git-rebase'in kabaklarını nasıl yaptığını tam olarak anlamadığımı gösteriyor.

İşte kullandığım komut dosyalarının değiştirilmiş bir sürümü:


repo_squash.sh (bu aslında çalıştırılan komut dosyasıdır):


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 (bu script sadece repo_squash.sh tarafından kullanılır):


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: (bu dosya sadece repo_squash_helper.sh tarafından kullanılır)


# 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

"Yeni mesaj" içeriğini hayal gücünüze bırakacağım. Başlangıçta, bunu "--strategy onların" seçeneği olmadan yaptım (yani, belgelerin doğru olduğunu anlarsam özyinelemeli, ancak hangi özyinelemeli stratejinin kullanıldığından emin değilim) ve aynı zamanda ' Çalışmıyor. Ayrıca, repo_squash_helper.sh içinde yorumlanmış kodu kullanarak, sed komut dosyasının üzerinde çalıştığı orijinal dosyayı kaydettiğimi ve bunu yapmak istediğim şeyi yaptığından emin olmak için sed komut dosyasını çalıştırdığımı belirtmeliyim ( öyleydi). Yine, neden bir çatışma olacağını bile bilmiyorum , bu yüzden hangi stratejinin kullanıldığı çok önemli değil gibi görünüyor. Herhangi bir tavsiye veya fikir yardımcı olacaktır, ama çoğunlukla sadece bu ezme çalışma almak istiyorum.

Jefromi ile yapılan tartışmalardan ek bilgilerle güncellendi:

Büyük "gerçek" veri havuzumuz üzerinde çalışmadan önce, bir test veri havuzunda benzer komut dosyaları kullandım. Çok basit bir havuzdu ve test temiz çalıştı.

Başarısız olduğunda aldığım mesaj:

Finished one cherry-pick.
# Not currently on any branch.
nothing to commit (working directory clean)
Could not apply 66c45e2... Needed subdir

Bu ilk squash taahhüdünden sonraki ilk seçim. Koşmak git statustemiz bir çalışma dizini verir. Daha sonra bir şey yaparsam git rebase --continue, birkaç işlemden sonra çok benzer bir mesaj alırım. Daha sonra tekrar yaparsam, birkaç düzine işlemden sonra çok benzer bir mesaj alırım. Yine yaparsam, bu sefer yaklaşık yüz taahhütten geçer ve bu mesajı verir:

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

Eğer koşarsam git status, şunu elde ederim:

# 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

"Her ikisi de değiştirilmiş" bit bana garip geliyor, çünkü bu sadece bir seçimin sonucuydu. Ayrıca, "çatışmaya" bakarsam, bir sürümü [sekme] karakteriyle başlayan diğeri dört boşluklu tek bir satıra kaynar. Bu, yapılandırma dosyamı nasıl ayarladığımla ilgili bir sorun olabileceği gibi geldi, ancak içinde bir şey yok. (Core.ignorecase'in true olarak ayarlandığını fark ettim, ama açıkça git-clone bunu otomatik olarak yaptı. Orijinal kaynağın bir Windows makinesinde olduğu düşünüldüğünde tamamen şaşırmadım.)

File_X.cpp dosyasını el ile düzeltirsem, kısa bir süre sonra başka bir çakışma ile başarısız olur, bu kez bir sürümün var olduğunu düşündüğü ve bir sürümün olmaması gerektiğini düşündüğü bir dosya (CMakeLists.txt) arasında. Bu çatışmayı (yaptığım) istediğimi söyleyerek düzeltirsem, birkaç işlemin ardından şimdi önemsiz olmayan bazı değişiklikler olduğu başka bir çakışma (aynı dosyada) alırım. Hala çatışmaların yolunun sadece% 25'i.

Ayrıca, bu çok önemli olabileceğinden, bu projenin bir svn deposunda başladığını belirtmeliyim. Bu başlangıç ​​tarihi büyük olasılıkla bu svn deposundan içe aktarılmıştır.

Güncelleme # 2:

Bir toygarda (Jefromi'nin yorumlarından etkilenerek), repo_squash.sh dosyamı şu şekilde değiştirmeye karar verdim:

rm -rf repo_squash
git clone repo repo_squash
cd repo_squash/
git rebase --strategy theirs -i bd6a09a484b8230d0810e6689cf08a24f26f287a

Ve sonra, orijinal girişleri olduğu gibi kabul ettim. Yani, "rebase" bir şeyi değiştirmemeliydi. Daha önce açıklanan aynı sonuçlarla sonuçlandı.

Güncelleme # 3:

Alternatif olarak, stratejiyi atlar ve son komutu yerine koyarsam:

git rebase -i bd6a09a484b8230d0810e6689cf08a24f26f287a

Artık "taahhüt edilecek hiçbir şey yok" sorununu çözmüyorum, ama yine de diğer çatışmalardan ayrıldım.

Sorunu yeniden oluşturan oyuncak deposuyla güncelleme:

test_squash.sh (bu gerçekten çalıştırdığınız dosyadır):

#========================================================
# 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 (test_sqash.sh tarafından kullanılır):

# 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: Evet, emacs'ı geri dönüş düzenleyicisi olarak kullandığımı gördüğünüzde bazılarınızın kötü olduğunu biliyorum.

PPS: Rebase'den sonra mevcut depodaki tüm klonlarımızı patlatmamız gerektiğini biliyoruz. ("Yayımlandıktan sonra bir havuzu yeniden oluşturmayacaksınız" satırları boyunca.)

PPPS: Birisi bana buna nasıl ödül ekleyeceğini söyleyebilir mi? Düzenleme modunda veya görüntüleme modundayken bu ekranın hiçbir yerinde bu seçeneği görmüyorum.


Kullanılan komut dosyalarından daha alakalı olan son girişim eylemidir - karıştırılmış seçim ve squash listesi olacağından emin olabilirsiniz, değil mi? Ve yeniden temsili şubede herhangi bir birleştirme komisyonu var mı? (Yine de kullanmamanıza rağmen rebase -p)
Cascabel

Eminim sen "nihai teşebbüs eylem" ile ne demek değilim ama olan son 400 ile veya tüm seçim Durum böyleyken, sadece karışan çekme ve squash listesi. Yeniden baslamanın kendisi kendi birleştirmelerini gerçekleştirmesine rağmen, bu listede birleştirme yoktur. Okuduğum şeye göre, "rebase -p" etkileşimli modda önerilmiyor (ki benim durumumda bu kadar etkileşimli değil elbette). Gönderen kernel.org/pub/software/scm/git/docs/git-rebase.html "Bu içten --interactive makine kullanır, ancak --interactive seçeneği ile birleştirerek açıkça genelde iyi bir fikir değil":
Ben Hocking

"Nihai teşebbüs eylemi" ile geri dönme seçme / squash listesi kastedildi rebase --interactive- bunlar git denemesi için bir tür eylemler listesidir. Bunu, çatışmalara neden olan tek bir squash'e indirgemenizi ve yardımcı betiklerinizin tüm ekstra karmaşıklığından kaçınmayı umuyordum. Diğer eksik bilgiler, çatışmaların ortaya çıktığı zamandır - git yamaları kabak oluşturmak için uyguladığında mı, yoksa kabaktan geçip bir sonraki yamayı uyguladığında mı? (Ve GIT_EDITOR kludge'nizde kötü bir şey olmadığından emin misiniz? Basit test davası için başka bir oy.)
Cascabel

Yararlı yorumlar için teşekkürler. Soruyu cevaplarımı yansıtacak şekilde güncelledim.
Ben Hocking

2
Oyuncak deposu durumu, komut dosyalarınızdan çok, anlaşılması çok daha kolaydır - ve komut dosyalarının bununla hiçbir ilgisi olmadığını gösterir, yalnızca çatışma çözünürlüğü olan bir birleşmeyi (hafif bir şeytani birleştirme) yeniden adlandırmaya çalıştığınız gerçeği ).
Cascabel

Yanıtlar:


69

Pekala, bir cevap atacak kadar eminim. Belki onu düzenlemek zorunda kalacak, ama senin sorununun ne olduğunu bildiğime inanıyorum.

Oyuncak repo test çantanızda bir birleştirme var - daha da kötüsü, çatışmalarla bir birleşmesi var. Ve birleştirme boyunca yeniden temel atıyorsun. Olmadan -p(tamamen işe yaramaz -i), birleştirmeler yok sayılır. Bu , rebase bir sonraki taahhüdü kiraz seçmeye çalıştığında çatışma çözümünüzde yaptığınız her şeyin orada olmadığı anlamına gelir , bu nedenle yaması geçerli olmayabilir. (Bunun bir birleşme çatışması olarak gösterildiğine inanıyorum çünkü git cherry-pickorijinal taahhüt, mevcut taahhüt ve ortak ata arasında üç yönlü birleştirme yaparak yamayı uygulayabilir.)

Ne yazık ki, yorumlarda belirtildiği gibi -ive -p(birleştirme koru) çok iyi anlaşamıyorum. Düzenleme / yeniden yazma işini biliyorum ve yeniden sıralama işe yaramaz. Ancak, kabaklarla iyi çalıştığına inanıyorum . Bu belgelenmemiştir, ancak aşağıda tarif ettiğim test senaryolarında işe yaramıştır. Durumunuz çok daha karmaşıksa, istediğinizi yapmakta zorlanabilirsiniz, ancak yine de mümkün olacaktır. (Hikayenin ahlakı: birleşmeden rebase -i önce temizleyin .)

Diyelim ki A, B ve C'yi birlikte ezmek istediğimiz çok basit bir vakamız var:

- o - A - B - C - X - D - E - F (master)
   \             /
    Z -----------

Şimdi, dediğim gibi, X'te bir çatışma yoksa, git rebase -i -pbeklediğiniz gibi çalışır.

Çatışmalar varsa, işler biraz daha karmaşıklaşır. İnce ezme yapacak, ancak daha sonra birleşmeyi yeniden yaratmaya çalıştığında, çatışmalar tekrar ortaya çıkacak. Bunları tekrar çözmeniz, dizine eklemeniz ve devam etmek için kullanmanız git rebase --continuegerekir. (Elbette, orijinal birleştirme taahhüdünden sürümü kontrol ederek bunları tekrar çözebilirsiniz.)

Reponuzda rerereetkinleştirdiyseniz ( rerere.enabledtrue değerine ayarlanmışsa), bu çok daha kolay olacaktır - git , çakışmaların yeniden başladığı andan itibaren yeniden kablolu çözümünü yeniden kullanabilecektir ve tek yapmanız gereken bunu incelemek düzgün çalıştığından emin olmak için dosyaları dizine ekleyin ve devam edin. (Hatta bir adım daha ileri gidebilir, açabilirsiniz ve bunları sizin için ekleyecektir, böylece birleştirme başarısız bile olmaz). Bununla birlikte, hiç tekrar etkinleştirmediğinizi tahmin ediyorum, bu yüzden çatışma çözümünü kendiniz yapmanız gerekecek. *rerere.autoupdate

* Veya, rerere-train.sh"mevcut birleştirme işlemlerinden veritabanını yeniden başlat" girişiminde bulunan git-antre komut dosyasını deneyebilirsiniz - temel olarak, tüm birleştirme işlemlerini denetler, birleştirmeye çalışır ve birleştirme başarısız olursa, sonuçları alır ve gösterir git-rerere. Bu zaman alıcı olabilir ve aslında hiç kullanmadım, ama çok yardımcı olabilir.


PS Yorumlarıma baktığımda, bunu daha erken yakalamam gerektiğini görüyorum. Birleştirme içeren bir şubeye yeniden baslayıp basmadığınızı sordum ve etkileşimli rebase listesinde birleşme olmadığını söylediniz, ki bu aynı şey değil - -pseçeneği sağlamadığınız için orada birleştirme yoktu .
Cascabel

Bunu kesinlikle deneyeceğim. Bunu yayınladığımdan beri, bazı durumlarda da fark ettim , sadece yazabilirim git commit -a -m"Some message"ve git rebase --continuedevam edecek. Bu -pseçenek olmadan bile çalışır , ancak seçenekle daha da iyi çalışır -p(herhangi bir yeniden sipariş vermediğim için -p, iyi görünüyor ). Her neyse, sizi haberdar edeceğim.
Ben Hocking

Test örneğini ayarlama git config --global rerere.enabled trueve git config --global rerere.autoupdate trueçalıştırmadan önce birincil sorunu çözer. Ancak ilginç bir şekilde, belirtirken bile birleştirmeleri korumaz --preserve-merges. Ancak, ben bu seti yoksa ve ben yazarken git commit -a -m"Meaningful commit"ve git rebase --continuebelirtirken --preserve-merges, birleştirme korur. Her neyse, bu sorunu aşmama yardım ettiğin için teşekkürler!
Ben Hocking

84

Yeni bir şube oluşturmak sakıncası yoksa, ben bu sorunu ele:

Ana olmak:

# create a new branch
git checkout -b new_clean_branch

# apply all changes
git merge original_messy_branch

# forget the commits but have the changes staged for commit
git reset --soft main        

git commit -m "Squashed changes from original_messy_branch"

3
En hızlı ve verimli çözüm :)
IslamTaha

2
Bu harika bir öneriydi! Birkaç taahhüdü hızlı bir şekilde birleştirmek için çok iyi çalıştı.
philidem

3
Bu çok daha güvenli bir çözümdür. Ben de birleştirme - squash ekledim. Being: git merge --squash original_messy_branch
jezpez

1

Ne zaman git diff new_clean_branch..original_messy_branchaynı olmalılar? Benim için farklılar.
LeonardChallis

5

Benzer bir gereksinim arıyordum, yani geliştirme branşımın geçici taahhütlerini atmak, bu prosedürün benim için işe yaradığını gördüm.
çalışma şubemde

git reset –hard mybranch-start-commit
git checkout mybranch-end-commit . // files only of the latest commit
git add -a
git commit -m”New Message intermediate commits discarded”

viola şubenin başlangıç ​​taahhüdüne en son taahhüdü bağladık! Birleştirme çatışması sorunu yok! Öğrenme pratiğimde bu aşamada bu sonuca geldim, Amaç için daha iyi bir yaklaşım var mı.


FYI - Bunu deniyordum ve mybranch-end-taahhüt ödeme yapmanın bana ara taahhütlerde meydana gelen silmeleri almadığını buldum. Bu yüzden sadece mybranch-end-commit'de bulunan dosyaları kontrol eder.
ahains

1

Manuel müdahaleyi en aza indiren @ hlidka'nın harika cevabına dayanarak, ustalaşmak için dalda olmayan yeni taahhütleri koruyan bir versiyon eklemek istedim.

İnandığım gibi git reset, bu örnekteki adımda bunlar kolayca kaybedilebilir .

# create a new branch 
# ...from the commit in master original_messy_branch was originally based on. eg 5654da06
git checkout -b new_clean_branch 5654da06

# apply all changes
git merge original_messy_branch

# forget the commits but have the changes staged for commit
# ...base the reset on the base commit from Master
git reset --soft 5654da06       

git commit -m "Squashed changes from original_messy_branch"

# Rebase onto HEAD of master
git rebase origin/master

# Resolve any new conflicts from the new commits

0

-XEtkileşimli bir yeniden paylaşımda kullanıldığında ve strateji seçeneklerinin yok sayıldığını unutmayın .

Bkz. Taahhüt db2b3b820e2b28da268cc88adff076b396392dfe (Temmuz 2013, git 1.8.4+),

Etkileşimli rebase'deki birleştirme seçeneklerini yok saymayın

Birleştirme stratejisi ve seçenekleri içinde belirtilebilir git rebase, ancak bunlarla -- interactivetamamen yok sayılırlar.

İmzalayan: Arnaud Fontaine

Bu, -Xstrateji ve etkileşimli rebase ve düz rebase ile çalışıyor ve ilk komut dosyanız artık daha iyi çalışabilir.


0

Daha basit ama benzer bir sorunla karşı karşıya kaldım, 1) yerel bir şubede bir birleşme çatışmasını çözdüm, 2) daha fazla küçük taahhüt ekleyerek çalışmaya devam ettim, 3) yeniden birleştirmek ve birleştirme çatışmalarına çarpmak istedim.

Benim için git rebase -p -i masterçalıştı. Orijinal çatışma çözümünü taahhüt etti ve diğerlerini en üste sıkıştırmama izin verdi.

Umarım birine yardım eder!


Hala -p ile rebase girişiminde çalışırken çözmek için birkaç çatışma var Kuşkusuz çok daha az çatışma vardı ve onlar sadece daha önce ile çakışma alıyorum tüm kod dosyaları yerine proje dosyası ile sınırlıydı. Görünüşe göre "Birleştirme çakışmaları veya taahhütleri birleştirmek için yapılan manuel değişiklikler korunmuyor." - stackoverflow.com/a/35714301/727345
JonoB
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.