Yanıtlar:
Kullanma git rebase. Git'teki jenerik "taahhüt (ler) i ve bunları farklı bir üst (taban) üzerine çiz" komutudur.
Ancak bilinmesi gereken bazı şeyler:
Taahhütlü SHA'lar ebeveynlerini içerdiğinden, belirli bir taahhüdün ebeveynini değiştirdiğinizde, SHA'sı değişecektir - kalkınma çizgisinden sonra gelen (bundan daha yeni) tüm taahhütlerin SHA'ları değişecektir .
Başka insanlarla çalışıyorsanız ve söz konusu taahhüdü daha önce çektikleri yere zaten ittiyseniz, taahhüdü değiştirmek muhtemelen bir Bad Idea ™ olur. Bunun nedeni 1 numaradır ve bu nedenle diğer kullanıcıların depolarının SHA'larınızın artık "aynı" taahhütlerle eşleşmemesi nedeniyle ne olduğunu anlamaya çalışırken karşılaşacakları karışıklık. (Ayrıntılar için bağlantılı kılavuz sayfasının "YUKARI AKIM TAVANINDAN KURTARMA" bölümüne bakın.)
Bununla birlikte, şu anda yeni bir ebeveyne taşımak istediğiniz bazı taahhütleri olan bir şubedeyseniz, şöyle bir şey olurdu:
git rebase --onto <new-parent> <old-parent>
Bu her şeyi hareket edecek sonra <old-parent> üstüne oturup cari dalında <new-parent>yerine.
--ontoiçin kullanılır! Bu cevabı okuduktan sonra bile dokümantasyon her zaman tamamen karanlıktı !
git rebase --onto <new-parent> <old-parent>nasıl kullanılamayacağına rebase --ontodair her şeyi anlattım.
rebase this commit on that oneveya git rebase --on <new-parent> <commit>. Yaşlı ebeveyni burada kullanmak benim için bir anlam ifade etmiyor.
git rebase <new-parent>.
Sonraki taahhütlerin yeniden temellendirilmesinden kaçınmanız gerektiği ortaya çıkarsa (örneğin, bir geçmiş yeniden yazma işleminin yapılamayacağı için), git yerine kullanabilirsiniz (Git 1.6.5 ve sonraki sürümlerde kullanılabilir).
# …---o---A---o---o---…
#
# …---o---B---b---b---…
#
# We want to transplant B to be "on top of" A.
# The tree of descendants from B (and A) can be arbitrarily complex.
replace_first_parent() {
old_parent=$(git rev-parse --verify "${1}^1") || return 1
new_parent=$(git rev-parse --verify "${2}^0") || return 2
new_commit=$(
git cat-file commit "$1" |
sed -e '1,/^$/s/^parent '"$old_parent"'$/parent '"$new_parent"'/' |
git hash-object -t commit -w --stdin
) || return 3
git replace "$1" "$new_commit"
}
replace_first_parent B A
# …---o---A---o---o---…
# \
# C---b---b---…
#
# C is the replacement for B.
Yukarıdaki değiştirme oluşturulduğunda, B nesnesine yönelik tüm istekler aslında C nesnesini döndürür. C'nin içeriği, ilk ebeveyn (aynı ebeveynler (ilk hariç), aynı ağaç hariç, B içeriğiyle tam olarak aynıdır, aynı taahhüt mesajı).
Değiştirmeler varsayılan olarak etkindir ancak git--no-replace-objects seçeneğine (komut adından önce) veya ortam değişkenine ayarlanarak etkinleştirilebilir . Değiştirmeler iterek paylaşılabilir (normalin yanı sıra ).GIT_NO_REPLACE_OBJECTSrefs/replace/*refs/heads/*
Commit-munging'i beğenmediyseniz ( yukarıdaki sed ile yapılır ), daha yüksek seviye komutlarını kullanarak değiştirme taahhüdünüzü oluşturabilirsiniz:
git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -
Büyük fark, eğer B bir birleştirme taahhüdü ise, bu dizinin ek ebeveynleri yaymamasıdır.
refs/replace/Ref hiyerarşisini zorlamanız (ve getirmeniz) gerekir . Sadece bir kez yapmanız gerekiyorsa git push yourremote 'refs/replace/*', kaynak havuzda ve git fetch yourremote 'refs/replace/*:refs/replace/*'hedef depolarda yapabilirsiniz. Bunu birkaç kez yapmanız gerekiyorsa, bu refspec'leri bir remote.yourremote.pushyapılandırma değişkenine (kaynak havuzunda) ve bir remote.yourremote.fetchyapılandırma değişkenine (hedef depolarında) ekleyebilirsiniz .
git replace --grafts. Bunun ne zaman eklendiğinden emin değilim, ancak tüm amacı taahhüt edilen B ile aynı olan ancak belirtilen ebeveynlerle bir değiştirme oluşturmaktır .
Git'teki bir taahhüdü değiştirmenin, onu izleyen tüm taahhütlerin değiştirilmesi gerektiğini unutmayın. Tarihin bu bölümünü yayınladıysanız cesaretiniz kırılır ve birileri tarih üzerindeki çalışmalarını değişimden önceki haline getirmiş olabilir.
Amber'in yanıtındagit rebase bahsedilen alternatif çözüm , bir taahhüdün üst öğesini değiştirmek için greft mekanizmasını kullanmak ( Git Terimler Sözlüğü'ndeki Git greftlerinin tanımına ve Git Depo Düzeni dokümantasyonundaki dosyanın belgelerine bakın), bazı geçmiş görüntüleyiciyle doğru bir şey yaptığını kontrol etmektir ( , ve vb.) kullanın ve kalıcı hale getirmek için (kılavuzunun "Örnekler" bölümünde açıklandığı gibi) kullanın (ve ardından grefti çıkarın ve isteğe bağlı olarak yedeklenen veya yeniden depolanan orijinal referansları kaldırın ):.git/info/graftsgitkgit log --graphgit filter-branchgit filter-branch
echo "$ commit-id $ graft-id" >> .git / info / greft git filter-branch $ graft-id..HEAD
NOT !!! Bu çözüm Rebase çözümden farklı o git rebaserebase ediyorum / nakli değişiklikleri , greftler tabanlı çözüm basitçe Reparent onaylatabilirsiniz olacağını ise olduğu gibi , eski ebeveyn ve yeni ebeveyn arasındaki farklılıkları dikkate almayan!
git replacegit greftlerinin yerini aldı (git 1.6.5 veya üstü olduğu varsayılarak).
git-replace+ kullanamıyorum git-filter-branch? Benim testte, filter-branchtüm ağaç rebasing, değiştirme saygı gibiydi.
git filter-branchgreftlere ve replasmanlara saygılı olarak tarihi yeniden yazar (ancak yeniden yazılan geçmiş replasmanları tartışmalıdır); itme, ileri doğru olmayan değişikliklere neden olur. git replaceyerinde devredilebilir yedekler oluşturur; push hızlı ileri olacaktır, ancak refs/replacedüzeltmeleri geçmişe sahip olmak için değiştirmeleri aktarmak için push yapmanız gerekir . HTH
Yukarıdaki cevapları açıklığa kavuşturmak ve utanmazca kendi senaryomu eklemek için:
Bu, "yeniden" veya "tövbe etmek" isteyip istemediğinize bağlıdır. Bir Rebase tarafından önerildiği gibi, Amber , dolaşır diffs . Bir inkarcı , Jakub ve Chris'in önerdiği gibi , tüm ağacın anlık görüntüleri etrafında hareket eder . Tövbe etmek istiyorsanız git reparent, işi manuel olarak yapmak yerine kullanmanızı öneririm .
Resmin solda olduğunu ve sağdaki resim gibi görünmesini istediğinizi varsayalım:
C'
/
A---B---C A---B---C
Hem yeniden basma hem de yeniden oluşturma aynı resmi üretecektir, ancak tanımı C'farklıdır. İle git rebase --onto A B, C'tarafından getirilen herhangi bir değişiklik içermez B. İle git reparent -p A, C'özdeş olacak C(hariç B, tarihte olmayacak).
reparentSenaryoyu denedim; güzel çalışıyor; şiddetle tavsiye edilir . Ayrıca yeni bir branchetiket oluşturmanızı ve checkoutaramadan önce bu şubeye (şube etiketi hareket edeceği için) öneririm .
Kesinlikle @ Jakub'ın cevabı birkaç saat önce OP ile aynı şeyi denediğimde bana yardımcı oldu.
Ancak git replace --graftşimdi greftlerle ilgili daha kolay bir çözümdür. Ayrıca, bu çözümle ilgili büyük bir sorun, filtre şubesinin, HEAD'ın şubesiyle birleştirilmeyen her dalı kaybetmemi sağlamasıydı. Sonra git filter-repoişi mükemmel ve kusursuz bir şekilde yaptı.
$ git replace --graft <commit> <new parent commit>
$ git filter-repo --force
Daha fazla bilgi için: Dokümanlardaki "Geçmişi yeniden aşılama" bölümüne bakın