Birden çok git taahhüdü nasıl geri alınır?


982

Şuna benzer bir git deposu var:

A -> B -> C -> D -> HEAD

Şube başkanının A'yı işaret etmesini istiyorum, yani B, C, D ve HEAD'ın kaybolmasını istiyorum ve kafanın A ile eşanlamlı olmasını istiyorum.

Görünüşe göre ya yeniden pazarlamayı deneyebilirim (aradaki değişiklikleri ittiğim için geçerli değil) ya da geri dön. Ancak birden fazla taahhüdü nasıl geri alabilirim? Her seferinde birini geri alabilir miyim? Sipariş önemli mi?


3
Uzaktan kumandayı sıfırlamak istiyorsanız, herhangi bir şeyle tıkayabilirsiniz! Ama dördüncü taahhüdü önce kullanalım: git push -f HEAD~4:master(uzak dalın usta olduğu varsayılarak). Evet, böyle bir taahhütte bulunabilirsiniz.
u0b34a0f6ae

21
İnsanlar çektiyse, değişiklikleri kullanarak geri dönen bir taahhütte bulunmalısınız git revert.
Jakub Narębski

1
Uzaktan kumandayı sağa doğru ittiğinizden emin olmak için git show HEAD ~ 4'ü kullanın
Mâtt Frëëman


5
"Sipariş önemli mi?" Evet, taahhütler aynı dosyalarda aynı satırları etkiliyorsa. O zaman en son taahhüdü geri almaya başlamalı ve geri dönüş yolunda çalışmalısın.
avandeursen

Yanıtlar:


1335

Bir yorumda yazdıklarımı genişletme

Genel kural, yayınladığınız tarihi yeniden yazmamanız (değiştirmemeniz) çünkü birileri çalışmalarını buna dayandırmış olabilir. Geçmişi yeniden yazarsanız (değiştirirseniz), değişikliklerinin birleştirilmesi ve güncellenmesi ile ilgili sorunlar yaşarsınız.

Solüsyon yaratmaktır Yani bir yeni taahhüt hangi geri döner değişiklikler sen kurtulmak istiyorum. Bunu git revert komutunu kullanarak yapabilirsiniz .

Aşağıdaki durumunuz var:

A <- B <- C <- D <- master <- KAFA

(buradaki oklar işaretçinin yönünü belirtir: taahhütlerde "üst" referansı, şube başı durumunda üst taahhüt (şube ref) ve HEAD referansı durumunda dalın adı).

Oluşturmanız gereken şey şudur:

A <- B <- C <- D <- [(BCD) ^ - 1] <- master <- KAFA

burada "[(BCD) ^ - 1]" B, C, D komisyonlarındaki değişiklikleri geri alan taahhüt anlamına gelir. Matematik bize (BCD) ^ - 1 = D ^ -1 C ^ -1 B ^ -1, aşağıdaki komutları kullanarak gerekli durumu alabilirsiniz:

$ git revert --no-commit D
$ git revert --no-commit C
$ git revert --no-commit B
$ git commit -m "the commit message"

Alternatif çözüm, A taahhüdünün içeriğini kontrol etmek ve bu durumu taahhüt etmektir :

$ git checkout -f A -- .
$ git commit -a

O zaman aşağıdaki duruma sahip olursunuz:

A <- B <- C <- D <- A '<- master <- KAFA

A 'komutu, A komutu ile aynı içeriğe sahiptir, ancak farklı bir taahhüttür (taahhüt mesajı, ebeveynler, taahhüt tarihi).

Charles Bailey tarafından modifiye Jeff Ferland tarafından çözüm, aynı fikri üzerine inşa fakat kullanır git reset :

$ git reset --hard A
$ git reset --soft @{1}  # (or ORIG_HEAD), which is D
$ git commit

40
B, C veya D'ye dosya eklediyseniz git checkout -f A -- ., Bunları silmeyecek, manuel olarak yapmanız gerekecektir. Bu stratejiyi şimdi uyguladım, teşekkürler Jakub
oma

18
Bu çözümler eşdeğer değildir. Birincisi yeni oluşturulan dosyaları silmez.
m33lky

10
@ Jerry: git checkout fooödeme dalı foo (şubeye geç) veya ödeme dosyası foo (dizinden) anlamına gelebilir . --disambiguate için kullanılır, örneğin git checkout -- fooher zaman dosya hakkında.
Jakub Narębski

87
Büyük cevaba ek olarak. Bu stenografi benim için çalışıyorgit revert --no-commit D C B
welldan97

9
@ welldan97: Yorumunuz için teşekkürler. Bu cevabı yazarken git revertbirden fazla taahhüt kabul etmedi; oldukça yeni bir ektir.
Jakub Narębski

248

Yararlı bulduğum temiz yol

git revert --no-commit HEAD~3..

Bu komut son 3 taahhüdü tek bir taahhütle geri alır.

Ayrıca geçmişi yeniden yazmaz.


16
Bu basit ve en iyi cevap
Julien Deniau

3
@JohnLittle değişiklikleri aşamalandırır. git commitoradan aslında taahhüt yapacağız.
x1a4

14
Bazı taahhütler birleştirme taahhütleri ise bu işe yaramaz.
MegaManX

5
Sondaki iki nokta ne yapıyor?
Kakule

5
@cardamom Bunlar bir aralık belirtir. HEAD~3..HEAD~3..HEAD
Toine H

238

Bunu yapmak için, geri almak istediğiniz taahhütlerin aralığını belirterek revert komutunu kullanmanız yeterlidir.

Örneğinizi göz önünde bulundurarak, bunu yapmanız gerekir ('master' dalında olduğunuzu varsayarak):

git revert master~3..master

Bu, B, C ve D'nin ters taahhüdü ile yerelinizde yeni bir taahhüt oluşturacaktır (yani, bu taahhütlerin getirdiği değişiklikleri geri alacaktır):

A <- B <- C <- D <- BCD' <- HEAD

129
git revert --no-commit HEAD~2..bunu yapmanın biraz daha deyimsel bir yoludur. Master dalındaysanız, master'ı tekrar belirtmenize gerek yoktur. Bu --no-commitseçenek, git seçeneğinin birden fazla iletiyle revert commit ...(istediğiniz şey olduğunu varsayarak) geçmişi değiştirmek yerine tüm taahhütleri bir kerede geri almaya çalışmasına izin verir .
kubi

6
@Victor Taahhüt aralığınızı düzelttim. Aralığın başlangıcı özeldir, yani dahil değildir. Bu nedenle, son 3 taahhüdü geri almak isterseniz, aralığı 3. taahhüdün üst öğesinden başlatmanız gerekir , yani master~3.

2
@kubi, SHA'ları bir taahhüt mesajına tek bir taahhüt kullanarak dahil etmenin bir yolu yok (yönteminiz ancak geri alınan taahhütleri manuel olarak girmek zorunda kalmadan)?
Chris S

@ChrisS İlk düşüncem kullanmamak --no-commit(böylece her bir geri dönüş için ayrı bir taahhüt elde etmek) ve daha sonra hepsini etkileşimli bir yeniden pazarda ezmek olacaktır . Birleştirilmiş kesinleştirme mesajı tüm SHA'ları içerecektir ve favori taahhüt mesajı düzenleyicinizi kullanarak istediğiniz gibi düzenleyebilirsiniz.
Radon Rosborough

71

Jakub'ın cevabına benzer şekilde, bu da geri dönmek için ardışık taahhütleri kolayca seçmenizi sağlar.

# revert all commits from B to HEAD, inclusively
$ git revert --no-commit B..HEAD  
$ git commit -m 'message'

9
Çözümünüz benim için iyi çalıştı, ancak küçük bir değişiklikle. Bu davaya sahipsek Z -> A -> B -> C -> D -> KAFA ve A durumuna dönmek istersem, o zaman garip bir şekilde git revert --no-commit Z .. KAFA
Bogdan

3
@Bogdan ile aynı fikirdeyim, geri dönüş aralığı şöyle: SHA_TO_REVERT_TO..HEAD
Vadym Tyemirov

11
Aralık yanlış. Olmalı B^..HEAD, aksi takdirde B hariç tutulur.
tessus

3
@Tessus ile aynı fikirde, yapılacak doğru şey: git revert --no-commit B^..HEADveyagit revert --no-commit A..HEAD
Yoho

64
git reset --hard a
git reset --mixed d
git commit

Bu bir anda hepsi için bir geri dönüş görevi görecektir. İyi bir taahhüt mesajı verin.


4
Eğer o HEADgibi görünmek Aistiyorsa, muhtemelen dizinin eşleşmesini istersiniz, bu yüzden git reset --soft Dmuhtemelen daha uygundur.
CB Bailey

2
--soft resetleme indeksi hareket ettirmez, bu yüzden taahhüt ettiğinde, taahhüt D yerine doğrudan gelen gibi görünecektir. -mixed değişiklikleri bırakır, ancak indeks işaretçisini hareket ettirir, böylece D ana üst öğe olur.
Jeff Ferland

5
Evet, sanırım git reset --keep tam olarak yukarıda ne var. Nisan 2010'da yayınlanan 1.7.1 sürümünde çıktı, bu yüzden cevap o zamanlar yoktu.
Jeff Ferland

git checkout Ao zaman git commityukarıdaki benim için işe yaramadı, ama bu cevap işe yaradı.
SimplGy

Neden git reset --mixed Dgerekli? Özellikle neden reset? Çünkü D'ye sıfırlamadan, HEAD A'yı işaret ederek B, C ve D'nin "sarkmasına" ve çöp toplanmasına neden olur - ki bu istediği şey değil mi? Peki neden --mixed? Zaten " --softsıfırlama dizini taşımaz ..." yanıtını verdiniz. Bu nedenle, dizini taşıyarak dizin D değişikliklerini içerecektir, Çalışma Dizini A değişikliklerini içerecektir - bu şekilde a git statusveya git diff(Dizin [D] ile Çalışma Dizini [A]) maddeyi gösterecektir; o kullanıcı D'den A'ya mı gidiyor?
Kırmızı Bezelye

39

Öncelikle, çalışan kopyanızın değiştirilmediğinden emin olun. Sonra:

git diff HEAD commit_sha_you_want_to_revert_to | git apply

ve sonra sadece taahhüt edin. Geri dönme nedeninin ne olduğunu belgelemeyi unutmayın.


1
Benim için çalıştı! Geliştirme dalında yapılan bazı değişiklikler (bazı hata düzeltmeleri) özellik dalı ile ilgili bir sorun vardı, böylece özellik dalı geliştirmede yapılan tüm değişikliklerin üzerine yazmak zorunda kaldı (bazı dosyaların silinmesi dahil).
silentser

1
İkili dosyalarla çalışmaz:error: cannot apply binary patch to 'some/image.png' without full index line error: some/image.png: patch does not apply
GabLeRoux

2
Bu, kabul edilen cevaptan çok daha esnek bir çözümdür. Teşekkürler!
Brian Kung

2
re: ikili dosyalar --binary seçeneğini kullanın: git diff --binary HEAD commit_sha_you_want_to_revert_to | git apply
weinerk

2
Birleştirme taahhütleri içeren bir dizi taahhüdü geri almak isteseniz bile bu işe yarar. Kullanırken git revert A..Zolsun istiyorumerror: commit X is a merge but no -m option was given.
Juliusz Gonera

35

Öyle sinirliyim ki bu soru cevaplanamaz. Diğer her soru, doğru bir şekilde nasıl geri döndürüleceği ve tarihin nasıl korunacağı ile ilgilidir. Bu soru "Şube başkanının A'yı göstermesini istiyorum, yani B, C, D ve HEAD'ın kaybolmasını istiyorum ve kafanın A ile eşanlamlı olmasını istiyorum."

git checkout <branch_name>
git reset --hard <commit Hash for A>
git push -f

Jakub'un gönderisini okuyarak çok şey öğrendim, ancak şirketteki bazı adamlar (Pull-Request olmadan "test" şubemize itme erişimine sahip), 5 işlemden önce yaptığı bir hatayı düzeltmeye ve düzeltmeye ve düzeltmeye çalışan 5 kötü taahhüt gibi itti. Sadece bu değil, bir veya iki Çekme İsteği de kabul edildi, bu şimdi kötüydü. Bu yüzden unut gitsin, son iyi taahhüdü (abc1234) buldum ve sadece temel senaryoyu çalıştırdım:

git checkout testing
git reset --hard abc1234
git push -f

Bu repoda çalışan diğer 5 adama son birkaç saatteki değişikliklerini daha iyi not ettiklerini ve son testten Wipe / Re-Branch'ten bahsettiklerini söyledim. Hikayenin sonu.


2
Taahhütleri yayınlamamıştım, bu yüzden ihtiyacım olan cevap buydu. Teşekkürler @Suamere.
Tom Barron

Bunu yapmanın daha iyi bir yolu git push --force-with-lease, tarihin sadece kimsenin buhara vapourize etme taahhüdünden sonra veya menzil içinde başka bir taahhütte bulunmaması durumunda yeniden yazılmasıdır. Diğer insanlar şubeyi kullandıysa, tarihi asla yeniden yazılmamalı ve taahhüt sadece gözle görülür şekilde geri çevrilmelidir.
frandroid

1
@frandroid "tarih asla yeniden yazılmamalıdır", sadece mutlakla ilgilenir. Bu konunun sorusu ve cevabımın anlamı, belirli bir senaryo için tüm tarihin silinmesi gerektiğidir.
Suamere

@Suamere Elbette, soru bu. Ama cevabınız diğer adamlara ne anlatmanız gerektiğinden bahsederken, sorun potansiyeli var. Kişisel deneyimlerden, push -f, başkaları silmeye çalıştığınızdan sonra taahhütte bulunursa kod tabanınızı bozabilir. - force-lease aynı sonucu elde eder, ancak repoyu mahvetmek üzereyken kıçını kurtarır. Neden şansa bakalım? - kiralama ile zorla başarısız olursa, hangi taahhüdün yoluna girdiğini görebilir, düzgün bir şekilde değerlendirebilir, ayarlayabilir ve tekrar deneyebilirsiniz.
frandroid

1
@Suamere Teşekkürler! Sorunun, tarihi yeniden yazmak istediğini açıkça belirttiğine katılıyorum. Seninle aynı durumdayım ve OP'nin sanırım birileri onlarca çirkin geri dönüş ve garip taahhütler ve geri dönüşleri (tatilde iken) ve devletin geri döndürülmesi gerektiğini tahmin ediyorum. Her durumda iyi bir sağlıklı uyarı ile birlikte bu kabul edilen cevap olmalıdır.
Lee Richardson

9

Bu, Jakub'un cevabında sunulan çözümlerden birinin genişlemesi

Geri almam gereken taahhütlerin biraz karmaşık olduğu, bazı taahhütlerin birleştirme taahhütleri olduğu bir durumla karşı karşıya kaldım ve tarihi yeniden yazmaktan kaçınmalıydım. Bir dizi git revertkomut kullanamadım çünkü sonunda eklenen geri dönüş değişiklikleri arasında çakışmalarla karşılaştım. Sonunda aşağıdaki adımları uyguladım.

İlk olarak, HEAD'ı dalın ucunda bırakırken hedef taahhüdün içeriğini kontrol edin:

$ git checkout -f <target-commit> -- .

(-, <target-commit>bir dosya yerine bir kesinleştirme olarak yorumlandığından emin olur;., Geçerli dizine başvurur.)

Ardından, geri alınan komisyonlara hangi dosyaların eklendiğini belirleyin ve bu nedenle silinmeleri gerekir:

$ git diff --name-status --cached <target-commit>

Eklenen dosyalar satırın başında "A" ile gösterilmeli ve başka bir fark olmamalıdır. Şimdi, herhangi bir dosyanın kaldırılması gerekiyorsa, kaldırmak için şu dosyaları hazırlayın:

$ git rm <filespec>[ <filespec> ...]

Son olarak, geri dönüşü uygulayın:

$ git commit -m 'revert to <target-commit>'

İstenirse, istenen duruma geri döndüğümüzden emin olun:

$git diff <target-commit> <current-commit>

Hiçbir fark olmamalıdır.


gitŞubenin sadece ucu ile KAFA yapabileceğinizden emin misiniz ?
Suamere

2
Bu benim için çok daha iyi bir çözümdü, çünkü benimkinde birleştirme taahhütleri vardı.
sovemp

3

Paylaşılan depodaki bir grup taahhüdü geri almanın kolay yolu (insanların kullandığı ve geçmişi korumak istediğiniz) git revertgit ile birlikte kullanmaktır rev-list. İkincisi size taahhütlerin bir listesini sunacak, birincisi geri dönüşü kendisi yapacak.

Bunu yapmanın iki yolu var. Tek bir taahhütte birden fazla işlemin geri alınmasını istiyorsanız:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert -n $i; done

bu, ihtiyacınız olan bir grup taahhüdü geri alacak, ancak tüm değişiklikleri çalışma ağacınızda bırakacak, hepsini her zamanki gibi yapmalısınız.

Başka bir seçenek, geri döndürülen değişiklik başına tek bir taahhütte bulunmaktır:

for i in `git rev-list <first-commit-sha>^..<last-commit-sha>`; do git revert --no-edit -s $i; done

Örneğin,

 o---o---o---o---o---o--->    
fff eee ddd ccc bbb aaa

değişiklikleri geri almak eee için bbb , run

for i in `git rev-list eee^..bbb`; do git revert --no-edit -s $i; done

sadece bunu kullandım. Teşekkürler!
Bi Bi

2

Bunların hiçbiri benim için çalışmadı, bu yüzden geri dönmek için üç taahhüdüm vardı (son üç taahhüt), bu yüzden yaptım:

git revert HEAD
git revert HEAD~2
git revert HEAD~4
git rebase -i HEAD~3 # pick, squash, squash

Bir cazibe gibi çalıştı :)


2
Bu, yalnızca değişiklikleriniz henüz yapılmadıysa geçerli bir seçenektir.
kboom

0

Bence çok kolay ve temiz bir yol olabilir:

A'ya geri dön

git checkout -f A

ustanın başını mevcut duruma yönlendir

git symbolic-ref HEAD refs/heads/master

kayıt etmek

git commit

1
Aşağı oylamanın nedenini açıklar mısınız?
nulll

Bu iyi çalışıyor. Bu yararlı cevap için aşağı oy vermenin anlamı nedir veya en iyi uygulamanın ne olduğunu herkes açıklayabilir mi?
Levent Divilioglu

Bu aynı git checkout master; git reset --hard Amı? Yoksa, bunun ne hakkında biraz daha açıklayabilir misiniz?
MM

Sadece çalışır, ancak sembolik-ref BAŞ "güvenli" bir komut olmayacak gibi görünüyor
Sergio

err fix master istemiyorum, bir dal düzeltmek istiyorum
Sérgio

-6

Bir özelliğin taahhütlerini geçici olarak geri almak istiyorsanız, aşağıdaki komutların serisini kullanabilirsiniz.

İşte böyle

git log --pretty = oneline | grep 'özellik_adı' | kes -d '' -f1 | xargs -n1 git revert - düzenleme yok

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.