Tüm git taahhütlerini nasıl ezebilirim?


480

İlk deponuza kadar tüm deponuzu nasıl eziyorsunuz?

İlk taahhüde geri dönebilirim, ama bu beni 2 taahhütle bırakacaktı. İlkinden önce taahhüde başvurmanın bir yolu var mı?


8
"Birincisinden önceki taahhüt"?
innaM

31
@innaM - Git'i başlatan ilkel taahhüt . (Umutlar mizah iç içe geçerek yeterince iyi geçer).
ripper234

11
Bu sorunun daha sonra gelenler için , daha modern bir cevap kullandığınızdan emin olun .
Droogans

1
İlgili, ancak bir kopya değil ( --rootaslında ezilecek çok fazla varsa tüm taahhütleri ezmek için en iyi çözüm değildir ): Git deposunun ilk iki taahhüdünü birleştirin mi? .

2
Imo: bu @MTTux: stackoverflow.com/questions/30236694/… en iyisidir .
J0hnG4lt

Yanıtlar:


130

Belki de en kolay yol sadece çalışan kopyanın mevcut durumu ile yeni bir depo oluşturmaktır. Öncelikle yapabileceğiniz tüm taahhüt mesajlarını saklamak git log > original.logve ardından bunu yeni depodaki ilk taahhüt mesajınız için düzenlemek istiyorsanız:

rm -rf .git
git init
git add .
git commit

veya

git log > original.log
# edit original.log as desired
rm -rf .git
git init
git add .
git commit -F original.log

62
ama bu yöntemle şubeleri
kaybediyorsunuz

150
Bu cevap verildiğinden beri Git gelişti. Hayır, daha basit ve daha iyi bir yol yoktur: git rebase -i --root. Bakınız: stackoverflow.com/a/9254257/109618
David J.

5
Bu bazı durumlarda işe yarayabilir, ancak aslında sorunun cevabı değildir. Bu tarifle, tüm yapılandırmanızı ve diğer tüm dalları da kaybedersiniz.
iwein

3
Bu, gereksiz yere yıkıcı olan korkunç bir çözüm. Lütfen kullanma.
Daniel Kamil Kozar

5
ayrıca alt modülleri kıracaktır. -1
Krum

694

İtibariyle Git 1.6.2 , kullanabilirsiniz git rebase --root -i.

İlk hariç her taahhüt için değişiklik pick etmek squash.


49
Lütfen orijinal soruyu cevaplayan eksiksiz, çalışan bir komut örneği ekleyin.
Jake

29
Keşke tüm depomu kabul edilen yanıtın söylediği gibi havaya uçurmadan önce okumuş olsaydım: /
Mike Chamberlain

38
Bu cevap tamam , ancak etkileşimli olarak diyelim ki 20 taahhütten daha fazlasına dayanıyorsanız, etkileşimli rebase muhtemelen çok yavaş ve hantal olacaktır. Muhtemelen yüzlerce veya binlerce taahhüdü ezmeye çalışmakta zorlanacaksınız. Kök taahhüdüne yumuşak veya karışık bir sıfırlama ile giderim, sonra bu durumda tavsiye ederim.

20
@Pred squashTüm taahhütler için kullanmayın . İlkinin olması gerekiyor pick.
Geert

14
Çok fazla taahhüdünüz varsa, 'seçim'i' squash 'olarak manuel olarak değiştirmek zordur. Kullanım :% s / almak / squash / g VIM komut satırında bu hızlı yapmak.
eilas

314

Güncelleme

Bir takma ad yaptım git squash-all.
Örnek kullanım : git squash-all "a brand new start".

[alias]
  squash-all = "!f(){ git reset $(git commit-tree HEAD^{tree} -m \"${1:-A new start}\");};f"

Uyarı : bir yorum sağlamayı unutmayın, aksi takdirde varsayılan yeni ileti "Yeni bir başlangıç" kullanılır.

Veya aşağıdaki komutu kullanarak diğer adı oluşturabilirsiniz:

git config --global alias.squash-all '!f(){ git reset $(git commit-tree HEAD^{tree} -m "${1:-A new start}");};f'

Bir Astar

git reset $(git commit-tree HEAD^{tree} -m "A new start")

Not : burada "A new start " sadece bir örnektir, kendi dilinizi kullanmaktan çekinmeyin.

TL; DR

Ezmeye gerek yok, kullanın git commit-tree yetim bir taahhüt oluşturmak için ve onunla devam edin.

Açıklamak

  1. üzerinden tek bir taahhüt oluştur git commit-tree

    Ne git commit-tree HEAD^{tree} -m "A new start"yapar:

    Sağlanan ağaç nesnesini temel alan yeni bir tamamlama nesnesi oluşturur ve stdout'ta yeni tamamlama nesnesi kimliğini yayar. -M veya -F seçenekleri belirtilmedikçe, günlük mesajı standart girişten okunur.

    İfade , geçerli dalınızın ucuna HEAD^{tree}karşılık gelen ağaç nesnesi anlamına gelir HEAD. bkz. Ağaç Nesneleri ve Tamamlama Nesneleri .

  2. geçerli dalı yeni işleme sıfırla

    Ardından git reset, geçerli dalı yeni oluşturulan tamamlama nesnesine sıfırlayın.

Bu şekilde, çalışma alanındaki hiçbir şeye dokunulmaz ve rebase / squash'a ihtiyaç yoktur, bu da onu gerçekten hızlı hale getirir. Ve gereken zaman, havuz büyüklüğü veya geçmiş derinliği ile ilgisizdir.

Varyasyon: Proje Şablonundan Yeni Repo

Bu, şablon / arketip / tohum / iskelet olarak başka bir depo kullanarak yeni bir projede "başlangıç ​​taahhüdü" oluşturmak için kullanışlıdır. Örneğin:

cd my-new-project
git init
git fetch --depth=1 -n https://github.com/toolbear/panda.git
git reset --hard $(git commit-tree FETCH_HEAD^{tree} -m "initial commit")

Bu, şablon deposunu uzak ( originveya başka bir şekilde) eklemekten kaçınır ve şablon deposunun geçmişini ilk taahhüdünüze daraltır.


6
Git revizyon sözdizimi (HEAD ^ {tree}) sözdizimi, başka birinin merak etmesi
Colin Bowern

1
Bu hem yerel hem de uzak depoyu veya bunlardan sadece birini sıfırlıyor mu?
aleclarson

4
@aleclarson, bu yalnızca yerel depodaki geçerli dalı sıfırlar, git push -fçoğaltma için kullanın .
ryenus

2
Bu cevabı, içermeyen bir proje şablonu havuzundan yeni bir proje başlatmanın bir yolunu ararken buldum git clone. Eğer eklerseniz --hardiçin git resetve anahtar HEADile FETCH_HEADde git commit-treebir başlangıç şablon repo getirilirken sonra taahhüt oluşturabilir. Cevabı, sonunda bunu gösteren bir bölümle düzenledim.
toolbear

4
O "Caveat" kurtulmak ama sadece kullanarak${1?Please enter a message}
Elliot Cameron

172

Tüm yapmak istediğiniz tüm taahhütlerinizi kök taahhüdüne kadar ezmekse,

git rebase --interactive --root

işe yarayabilir, çok sayıda taahhüt (örneğin, yüzlerce taahhüt) için pratik değildir, çünkü rebase işlemi, etkileşimli rebase editörü taahhüt listesini oluşturmak ve rebase'in kendisini çalıştırmak için muhtemelen çok yavaş çalışacaktır.

Çok sayıda taahhüdü ezerken iki daha hızlı ve daha verimli çözüm:

Alternatif çözüm # 1: yetim şubeleri

Mevcut dalınızın ucunda (ör. En son taahhüt) yeni bir yetim dalı oluşturabilirsiniz. Bu yetim dalı, tamamen yeni ve ayrı bir taahhüt geçmişi ağacının ilk kök taahhüdünü oluşturur ve bu da tüm taahhütlerinizi ezmeye eşittir:

git checkout --orphan new-master master
git commit -m "Enter commit message for your new initial commit"

# Overwrite the old master branch reference with the new one
git branch -M new-master master

Belgeler:

Alternatif çözüm # 2: Yazılımdan sıfırlama

Başka bir verimli çözüm, kök işleme için karışık veya yumuşak bir sıfırlama kullanmaktır <root>:

git branch beforeReset

git reset --soft <root>
git commit --amend

# Verify that the new amended root is no different
# from the previous branch state
git diff beforeReset

Belgeler:


22
Alternatif çözüm # 1: yetim dalları - kayalar!
Thomas

8
Alternatif çözüm # 1 FTW. Sadece eklemek için, değişikliklerinizi uzaktan kumandaya itmek istiyorsanız, yapın git push origin master --force.
Eddy Verbruggen

1
unutmamalıgit push --force
NecipAllef

Açık bir Github çekme isteğine itmek üzereyseniz, kod üzerinde yetim dalı yapmayın (örn. Yukarıdaki 1 numaralı alternatif çözümü yapmayın)! Github PR'nizi kapatacaktır çünkü mevcut kafa depolanan kafa sha'nın torunu değildir .
Andrew Mackie

Alternatif çözüm # 1 , ezme işlemi sırasında meydana gelebilecek birleştirme çakışmalarını da önler
FernAndr

52
echo "message" | git commit-tree HEAD^{tree}

Bu, HEAD ağacı ile yetim bir taahhüt oluşturacak ve stdout'ta adını (SHA-1) çıktılayacaktır. Sonra da şubenizi sıfırlayın.

git reset SHA-1

27
git reset $(git commit-tree HEAD^{tree} -m "commit message")kolaylaştırır.
ryenus

4
^ BU! - cevap olmalı. Yazarın niyeti olup olmadığından tam olarak emin değilim, ama benimdi (tek bir taahhütle bozulmamış bir repoya ihtiyaç duydu ve bu işi bitirdi).
chesterbr

@ryenus, çözümünüz tam olarak aradığım şeyi yaptı. Yorumunuzu cevap olarak eklerseniz, kabul edeceğim.
tldr

2
Subshell-variant'ı kendim önermememin nedeni, Windows'ta cmd.exe'de çalışmaz.
kusma

Windows komut istemlerinde, son parametreyi belirtmeniz gerekebilir: echo "message" | git commit-tree "HEAD^{tree}"
Bernard

41

Başka biri için işe yaradığında, bunu nasıl yaptım:

Bunun gibi şeyler yapmak için her zaman risk olduğunu ve başlamadan önce bir tasarruf dalı oluşturmak asla kötü bir fikir olmadığını unutmayın.

Giriş yaparak başlayın

git log --oneline

İlk işleme gidin, SHA'yı kopyalayın

git reset --soft <#sha#>

<#sha#>Günlükten kopyalanan SHA ile değiştirin

git status

Her şeyin yeşil olduğundan emin olun, aksi takdirde çalıştırın git add -A

git commit --amend

Mevcut tüm değişiklikleri geçerli ilk taahhütte değiştir

Şimdi bu dalı zorla itin ve orada olanın üzerine yazacaktır.


1
Mükemmel seçenek! Gerçekten basit.
twicejr

1
Bu fantastik olmaktan daha fazlası! Teşekkürler!
Matt Komarnicki

1
Bunun aslında tarihi etrafında bıraktığı görülüyor. Artık yetimsin, ama hala orada.
Brad

Çok yararlı bir cevap ... ama değişiklik komutundan sonra kendinizi özel sözdizimi ile vim editöründe bulacağınızın farkında olmalısınız. ESC, ENTER,: x arkadaşın.
Erich Kuester

Mükemmel seçenek!
danivicario

36

Greft kullanma hakkında bir şeyler okudum ama asla fazla araştırmadım.

Her neyse, bu son 2 taahhüdü elle böyle bir şeyle ezebilirsiniz:

git reset HEAD~1
git add -A
git commit --amend

4
Bu aradığım cevap, kabul edilen cevap olsaydı!
Jay

Bu inanılmaz bir cevap
Usta Yoda

36

En kolay yol 'sıhhi tesisat' komutunu kullanmaktır update-ref geçerli dalı silmek için kullanmaktır.

Kullanamazsın git branch -DGeçerli dalı silmenizi durdurmak için bir emniyet valfi olduğu için .

Bu sizi yeni bir başlangıç ​​taahhüdü ile başlayabileceğiniz 'başlangıç ​​taahhüdü' durumuna geri döndürür.

git update-ref -d refs/heads/master
git commit -m "New initial commit"


15

6 kelimenin bir satırında

git checkout --orphan new_root_branch  &&  git commit

@AlexanderMills, git help checkouthakkında okumalısınız--orphan
kyb

bunun için dokümanlara bağlantı verebilir misiniz, böylece bunu okuyan herkes manuel olarak aramak zorunda kalmaz mı?
Alexander Mills

1
kolay. işte buradagit help checkout --orphan
kyb

8

yedek oluştur

git branch backup

belirtilen işleme sıfırla

git reset --soft <#root>

sonra tüm dosyaları sahnelemeye ekle

git add .

mesajı güncellemeden taahhüt et

git commit --amend --no-edit

repo için ezilmiş taahhütleri ile yeni şube itmek

git push -f

Bu önceki taahhüt mesajlarını koruyacak mı?
not2qubit

1
@ not2qubit no, bu önceki taahhüt mesajlarını korumayacaktır, yerine # 1, taahhüt # 2, taahhüt # 3 yerine, bu taahhütlerdeki tüm değişiklikleri tek bir taahhüt # 1'e paketlenmiş olarak alacaksınız. İşlem 1, <root>sıfırladığınız işlem olacaktır. taahhüt mesajını düzenlemeye gerek kalmadan git commit --amend --no-editmevcut taahhütte yapılacak tüm değişiklikleri yapar <root>.
David Morton

5

Greft kullanarak ezmek

Bir dosya ekleyin .git/info/grafts, kökünüz olmak istediğiniz taahhüt karmasını buraya koyun

git log şimdi bu taahhütten başlayacak

'Gerçek' koşmak için git filter-branch


1

Bu cevap oluşturmanın yanı sıra bir (no-veli no-tarih), size taahhüt varsayarak (onları oy lütfen) üstünde bir çift geliştirir da bunun uygula-verilerin tümünü korumak istiyorum:

  • Yazar (ad ve e-posta)
  • Yazıldığı tarih
  • Komite (ad ve e-posta)
  • Teslim tarihi
  • Commmit günlük mesajı

Elbette, yeni / tekli taahhüdün SHA'sı değişecektir, çünkü yeni (tarihsiz) bir geçmişi temsil eder, ebeveynsiz / kök-taahhüt haline gelir.

Bu, için git logbazı değişkenler okunarak ve ayarlanarak yapılabilir git commit-tree. masterYeni bir şubeden tek bir taahhüt oluşturmak istediğinizi varsayarsak, one-commityukarıdaki taahhüt verilerini koruyarak:

git checkout -b one-commit master ## create new branch to reset
git reset --hard \
$(eval "$(git log master -n1 --format='\
COMMIT_MESSAGE="%B" \
GIT_AUTHOR_NAME="%an" \
GIT_AUTHOR_EMAIL="%ae" \
GIT_AUTHOR_DATE="%ad" \
GIT_COMMITTER_NAME="%cn" \
GIT_COMMITTER_EMAIL="%ce" \
GIT_COMMITTER_DATE="%cd"')" 'git commit-tree master^{tree} <<COMMITMESSAGE
$COMMIT_MESSAGE
COMMITMESSAGE
')

1

Bunu yapmak için, yerel git deposunu ilk tamamlama hashtag'ine sıfırlayabilirsiniz, böylece bu tamamlamadan sonraki tüm değişiklikleriniz dengesiz olur, ardından --amend seçeneğiyle tamamlayabilirsiniz.

git reset your-first-commit-hashtag
git add .
git commit --amend

Ardından gerekirse ilk kaydetme adını düzenleyin ve dosyayı kaydedin.


1

Benim için şu şekilde çalıştı: Toplamda 4 taahhüdüm vardı ve etkileşimli rebase kullandım:

git rebase -i HEAD~3

İlk taahhüt kalır ve ben 3 son taahhüt aldı.

Daha sonra görünen düzenleyicide takılı kalmanız durumunda, şu şekilde görüyorsunuz:

pick fda59df commit 1
pick x536897 commit 2
pick c01a668 commit 3

İlk taahhütte bulunmalı ve üzerine başkalarını ezmelisiniz. Sahip olmanız gereken şey:

pick fda59df commit 1
squash x536897 commit 2
squash c01a668 commit 3

Bunun için 'insert' ve 'edit' modunu değiştirmek için INSERT tuşunu kullanın.

Kaydediciyi kaydetmek ve çıkmak için tuşunu kullanın :wq. İmleciniz bu komut satırları arasındaysa veya başka bir yerdeyse ESC'yi itip tekrar deneyin.

Sonuç olarak iki taahhüt vardı: ilk kalan ve ikincisi "Bu 3 taahhüt bir kombinasyonudur."

Ayrıntılar için buraya bakın: https://makandracards.com/makandra/527-squash-several-git-commits-into-a-single-commit


0

Genellikle böyle yaparım:

  • Her şeyin yerine getirildiğinden emin olun ve bir şeyler ters gittiğinde en son taahhüt kimliğini yazın veya yedek olarak ayrı bir şube oluşturun

  • git reset --soft `git rev-list --max-parents=0 --abbrev-commit HEAD`Başınızı ilk işleme sıfırlamak için çalıştırın , ancak dizininizi değiştirmeden bırakın. İlk taahhütten bu yana yapılan tüm değişiklikler artık taahhüt altına alınmaya hazır görünecek.

  • Run git commit --amend -m "initial commit"senin taahhüt değiştirme ilk taahhüt ve mesaj taahhüt veya mevcut işlemek mesajı tutmak istiyorsanız, Çalıştırabileceğiniz değiştirmekgit commit --amend --no-edit

  • git push -fDeğişikliklerinizi zorlamak için koşun

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.