Soru En son taahhütleri Git ile yeni bir şubeye taşıyın


Yeni bir dalda ustalaşmayı taahhüt ettiğim son birkaç işi taşımak ve bu taahhütlerin yapılmasından önce ustayı geri almak istiyorum. Ne yazık ki, Git-fu'm henüz yeterince güçlü değil, herhangi bir yardım?

Yani Bundan nasıl çıkabilirim

master A - B - C - D - E

buna?

newbranch     C - D - E
             /
master A - B 

3763
2017-10-27 03:07


Menşei


Not: karşı soruyu sordum İşte - Benjol
olası kopyası Git komutunu kullanarak master'dan bir dalı kullanmaya - Chris Moschini
eddmann.com/posts/... bu bir çalışır - Sagar Naliyapara
Yorumlar burada temizlendi mi? Bunu soruyorum çünkü bu soruya iki ayda bir yaptığım ziyaret sırasında, her zaman bu yoruma göre kaydım. - Tejas Kale


Cevaplar:


Yeni bir şubeye taşınıyor

UYARI: Bu yöntem, ilk komutla yeni bir dal oluşturduğunuz için çalışır: git branch newbranch. İşlemleri bir yere taşımak istiyorsanız mevcut şube Değişikliklerinizi yürütmeden önce mevcut şubeye birleştirmeniz gerekir. git reset --hard HEAD~3 (görmek Mevcut bir şubeye taşınıyor altında). İlk önce değişikliklerinizi birleştirmezseniz, bunlar kaybolacaktır.

Başka durumlar söz konusu olmadıkça, bu, dallanma ve geri sarma yoluyla kolayca yapılabilir.

# Note: Any changes not committed will be lost.
git branch newbranch      # Create a new branch, saving the desired commits
git reset --hard HEAD~3   # Move master back by 3 commits (GONE from master)
git checkout newbranch    # Go to the new branch that still has the desired commits

Ama geri dönmek için kaç taahhüt emin olun. Alternatif olarak, yerine HEAD~3basitçe, işlemin karmasını sağlayın (veya referans gibi köken / ana) "geri" geri almak istiyorum ana (/ current) dalı, ör.

git reset --hard a1b2c3d4

* 1 yapacaksın bir tek ana daldan "kaybetmek" ister, ama endişelenmeyin, bu ilişkilere yeni üye olarak gireceksiniz!

UYARI: Git sürüm 2.0 ve üstü ile daha sonra git rebase Orijinalin üzerine yeni şube (master) şubesi, bir açık gerekebilir --no-fork-point Taşınan komisyonları kaybetmemek için rebase sırasında seçeneği. sahip olan branch.autosetuprebase always set bunu daha olası hale getirir. Görmek John Mellor'un cevabı detaylar için.

Mevcut bir şubeye taşınıyor

İşlemlerinizi bir mevcut şube, Bunun gibi görünecek:

git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch

4796
2017-07-22 22:37



Ve özellikle, yapamaz En son ittiğiniz noktanın, başkasının çekebileceği başka bir depoya gitmekten daha fazla geri gitmeye çalışın. - Greg Hewgill
Neyin çalıştığını açıklayabileceğini merak ediyorum. Benim için yeni bir şube oluşturuyorsunuz, hala devam ettiğiniz eski daldan 3 taahhüt kaldırarak ve yaptığınız şubeye göz atıyorsunuz. Peki büyülü olarak kaldırdığınız işlemler yeni dalda nasıl görünür? - Jonathan Dumaine
@Jonathan Dumaine: Çünkü eski şubeden gelen işleri kaldırmadan önce yeni şubeyi oluşturdum. Hala yeni daldalar. - sykora
git şubeleri, tarihte taahhütlere işaret eden işaretçilerdir, klonlanmış, oluşturulmamış veya silinmiş hiçbir şey yoktur (işaretçiler hariç) - knittl
Ayrıca, not: Çalışma kopyanızda kabul edilmeyen değişikliklerle bunu yapmayın! Bu sadece beni ısırdı! :( - Adam Tuttle


Nedenini merak edenler için (ilk başta olduğum gibi):

C'ye geri dönmek ve D ve E'yi yeni şubeye taşımak istiyorsunuz. İlk bakışta göründüğü gibi:

A-B-C-D-E (HEAD)
        ↑
      master

Sonra git branch newBranch:

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

Sonra git reset --hard HEAD~2:

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

Bir dal sadece bir işaretçi olduğu için ana Son söze işaret etti. Ne zaman yaptın yeni dalSadece son işleme yeni bir işaretçi yaptın. Sonra kullanarak git reset sen taşındın ana işaretçi geri iki taahhüt. Ama hareket etmediğinden beri yeni dal, hala orijinal olarak yaptığı taahhüt işaret eder.


848
2018-02-07 16:58



Gerekirse - aksi takdirde, gitme, çalışma dizinindeki (veya en azından benim için) sıfırlama işlemlerinden değişiklikleri bırakır. - Andrew
Ayrıca bir de git push origin master --force Değişikliğin ana depoda görünmesi için. - Dženan
Bu cevap, taahhütlerin kaybedilmesine neden oluyor: bir dahaki sefere git rebase, 3 taahhüt sessizce atılacak newbranch. Görmek cevabım detaylar ve daha güvenli alternatifler için. - John Mellor
@John, bu saçmalık. Ne yaptığınızı bilmeden rebasing nedenleri kaybetmeyi taahhüt eder. Eğer işleri kaybettiysen, senin için üzüldüm ama bu cevap işlerini kaybetmedi. Bunu not et origin/master yukarıdaki şemada görünmüyor. Sen ittiysen origin/master ve sonra yukarıdaki değişiklikleri yaptık, elbette, işler eğlenceli olur. Ama bu bir "Doktor, bunu yaptığımda acıyor" bir tür problem. Ve asıl sorulan soru için kapsam dışı. Bunu ele geçirmek yerine senaryonuzu araştırmak için kendi sorunuzu yazmanızı öneririm. - Ryan Lundy
@John, cevabın içinde, "Bunu yapma!" Dedin. git branch -t newbranchGeri dön ve cevapları tekrar oku. Kimse Bunu yapmayı önerdi. - Ryan Lundy


Genel olarak...

Bu durumda, sykora'nın maruz kaldığı yöntem en iyi seçenektir. Ancak bazen en kolay ve genel bir yöntem değildir. Genel bir yöntem kullanımı için git kiraz toplama:

OP'nin istediği şeyi başarmak için, 2 adımlı bir süreç:

Adım 1 - istediğiniz bir ustanın ne yapacağını not edin newbranch

gerçekleştirmek

git checkout master
git log

İstediğiniz (3) kelimesinin karmalarını not edin newbranch. İşte kullanacağım:
C taahhüt: 9aa1233
D taahhüdü: 453ac3d
E taahhüt: 612ecb3 

Not: İlk yedi karakteri kullanabilirsiniz veya   bütün bağlılık hash

Adım 2 - Onları koymak newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

VEYA (Git 1.7.2+ üzerinde, aralıkları kullanın)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

git kiraz toplama Bu üç sözleşmeyi yenisine uygular.


343
2018-03-26 08:13



OP hareket etmeye çalışmıyordu itibaren ana için yeni dal? Eğer ustalıkla kiraz toplarsanız ana dalına ekliyorsunuz - aslında sahip olduğu işleri ekliyorsunuz. Ve o da dal kafasını B'ye geri götürmez. Ya da ince ve havalı bir şey var mı? - RaveTheTadpole
Yeni bir özellik dalı oluşturmuş olmanız gerektiğinde, yanlışlıkla, master olmayan bir dalı yanlışlıkla işlediyseniz, bu çok iyi çalışır. - julianc
Bazı durumlarda yararlı bir yaklaşım için +1. Bu, sadece kendi komisyonlarınızı (başkalarıyla serpiştirilmiş) yeni bir şubeye çekmek istiyorsanız iyi olur. - Tyler V.
Daha iyi bir cevap. Bu şekilde, taahhütleri herhangi bir şubeye taşıyabilirsiniz. - skywinder
Vişne toplamanın sırası önemli mi? - kon psych


Bunu yapmanın bir başka yolu, sadece 2 komutu kullanarak. Ayrıca mevcut çalışan ağacınızı sağlam tutar.

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

Eski versiyon - öğrenmeden önce git branch -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

Yapabilmek push için . bilmek güzel bir hiledir.


252
2018-04-06 22:38



Geçerli dizin Bu sadece bir üst dizinde iseniz çalışırdı sanırım. - aragaer
Yerel itme sırıtıyor ama yansıma üzerinde nasıl farklı git branch -f İşte? - jthill
@GerardSexton . güncel direktör. git REMOTES veya GIT URL'lerine basabilir. path to local directory Git Git URL'leri sözdizimi. İçindeki GIT URLS bölümüne bakın. git help clone. - weakish
Bunun neden daha yüksek olmadığını bilmiyorum. Ölü basit ve küçük ama potansiyel olarak sıfırlama tehlikesi yok -. - Godsmith
@Godsmith Tahminimce, insanlar iki tane daha anlaşılmaz komut için üç basit komutu tercih ediyor. Ayrıca, en çok oylanan cevaplar, önce gösterilmenin doğası gereği daha fazla ayrıntı alır. - JS_Riddler


Önceki cevapların çoğu tehlikeli yanlıştır!

Bunu yapma:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

Bir dahaki sefere koştuğunuzda git rebase (veya git pull --rebase) Bu 3 taahhüt sessizce atılır newbranch! (aşağıdaki açıklamaya bakınız)

Bunun yerine şunu yapın:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • İlk önce 3 en son işi atar (--keep gibi --hardAncak, daha güvenli, başarısızlığa uğramış değişiklikler atmak yerine başarısız olarak).
  • Sonra söküldü newbranch.
  • Ardından bu 3 işi geri alır. newbranch. Artık bir şube tarafından başvurmadıklarından, bunu git'in kullanarak yapar. reflog: HEAD@{2} taahhüt HEAD 2 işlemden önce bahsetmek için kullanılır, yani bizden önce newbranch ve 2. kullanılmış git reset 3 sözleşmeyi atmak.

Uyarı: reflog varsayılan olarak etkindir, ancak el ile devre dışı bıraktıysanız (ör. "Çıplak" git deposu kullanarak), 3 postayı çalışmaya başladıktan sonra geri alamazsınız. git reset --keep HEAD~3.

Reflo'ya dayanmayan bir alternatif:

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(eğer istersen yazabilirsin @{-1} - önceden kontrol edilmiş şube - yerine oldbranch).


Teknik açıklama

Neden git rebase İlk örnekten sonra 3 komisyonu atın? Çünkü git rebase argümanlar olmadan --fork-point varsayılan olarak, zorla itilen üst akışa karşı sağlam olmak için yerel refloğu kullanan seçenek.

M1, M2, M3 işlemlerini içerdiğinde orijini / efendiyi dallara ayırdığınızı varsayalım.

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

ama sonra birisi, zorla menşei / efendiyi M2'yi kaldırmak için zorlayarak geçmişi yeniden yazar:

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

Yerel referansınızı kullanarak, git rebase Orijin / ana dalın daha önceki bir enkarnasyonundan çatal attığınızı görebilir ve böylece M2 ve M3 taahhütleri gerçekten konu dalınızın bir parçası değildir. Bu yüzden, M2'nin yukarı yöndeki şubesinden kaldırıldığından, konu dalının yeniden oluşturulmasından sonra artık konu dalınızda istemediğinizi varsaymaktadır:

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

Bu davranış mantıklı ve genellikle rebasing yaparken yapılacak doğru şeydir.

Bu yüzden aşağıdaki komutların başarısız olması:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

Reflog'u yanlış durumda bırakıyorlar. Git görüyor newbranch 3 komisyonu içeren bir revizyonda upstream şubesinden çatallanmış olarak reset --hard İşlemleri kaldırmak için akış yukarı tarihini yeniden yazar ve böylece bir dahaki sefere git rebase Onları, yukarı akıştan kaldırılmış olan başka herhangi bir taahhüt gibi atıyor.

Ancak bu durumda, bu 3 sözleşmenin konu dalının bir parçası olarak görülmesini istiyoruz. Bunu başarmak için, 3 güncellemeyi içermeyen önceki revizyonda yukarı havzadan koparmamız gerekiyor. Benim önerdiğim çözümler budur, dolayısıyla ikisi de reflog'u doğru durumda bırakır.

Daha fazla bilgi için, tanımına bakın --fork-point içinde git rebase ve git birleştirme üssüdocs.


210
2017-10-19 14:12



Bu cevap "YAPMAYIN!" Diyor. Kimsenin önermediği bir şeyin üstünde. - Ryan Lundy
@Kyralessa, -t sen atıfta bulunuyorsun git branch eğer varsa örtülü olur git config --global branch.autosetuprebase always ayarlayın. Yapmasan bile, ben zaten açıkladı size, bu komutları gerçekleştirdikten sonra izlemeye başlarsanız, aynı problemin ortaya çıkması halinde, OP'nin kendi sorularını vermesi muhtemeldir. - John Mellor
@RockLee, evet, genel olarak, bu tür durumları düzeltmenin yolu, güvenli bir başlangıç ​​noktasından yeni bir şube (newbranch2) oluşturmak ve daha sonra tutmak istediğiniz tüm işleri (badnewbranch'ten newbranch2'ye) almaktır. Kiraz toplama, yeni hash'lar verir, böylece newbranch2'yi güvenli bir şekilde yeniden oluşturabilirsiniz (ve artık badnewbranch'i silebilirsiniz). - John Mellor
@Walf, yanlış anladınız: git rebase tarihlerinin yeniden yazıldığı yerlere karşı sağlam olacak şekilde tasarlanmıştır. Ne yazık ki, bu sağlamlığın yan etkileri, ne onlar ne de yukarı akışları tarihin yeniden yazılmasa bile herkesi etkiler. - John Mellor
Ayrıca kullanabilirsiniz --no-fork-point koştuğunda git rebase. - torek


Bu, onları teknik anlamda "hareket ettirmez" ancak aynı etkiye sahiptir:

A--B--C  (branch-foo)
 \    ^-- I wanted them here!
  \
   D--E--F--G  (branch-bar)
      ^--^--^-- Opps wrong branch!

While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)

A--B--C  (branch-foo)
 \
  \
   D-(E--F--G) detached
   ^-- (branch-bar)

Switch to branch-foo
$ git cherry-pick E..G

A--B--C--E'--F'--G' (branch-foo)
 \   E--F--G detached (This can be ignored)
  \ /
   D--H--I (branch-bar)

Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:

A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
 \
  \
   D--H--I--J--K--.... (branch-bar)

27
2018-01-21 16:10



Kullanamaz mısın rebase Aynı şey için? - Bergi
Evet alternatif olarak kullanabilirsiniz rebase Yukarıdaki senaryodaki müstakil dal üzerinde. - Sukima


Bunu, geçmişi yeniden yazmadan yapmak için (diğer bir deyişle, işlemleri zaten zorladıysanız):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

Her iki dal da zorlanmadan itilebilir!


19
2017-09-20 10:17



Ama sonra durumunuza bağlı olarak, çok daha zorlayıcı olabilir, geri dönüş senaryosu ile uğraşmak zorunda. Şube üzerinde bir taahhüdü geri alırsanız, Git bu taahhütleri hala olduğu gibi görecektir, bu yüzden geri almak için geri dönüşü geri almanız gerekir. Bu, özellikle bir birleşmeyi geri döndürdüğünde ve şubeyi geri birleştirmeye çalıştıklarında, sadece Git'in bu şubeyi (bu tamamen doğru olan) birleştirdiğini düşündüğüne karar vermek için oldukça az insan yakar. - Makoto
İşte bu yüzden sonundaki taahhütleri yeni bir dalda alacağım. Bu şekilde git, onları sorununuzu çözen yeni işler olarak görüyor. - teh_senaus
Bu, ilk bakışta göründüğünden daha tehlikelidir, çünkü bu devletin etkilerini anlamadan, depo tarihini değiştiriyorsunuz. - Makoto
Argümanınızı takip etmiyorum - bu cevabın amacı, geçmişi değiştirmemeniz, basitçe yeni taahütler eklemektir (ki bu da değişiklikleri tekrar tekrar geçersiz kılar). Bu yeni taahhütler normal olarak itilebilir ve birleştirilebilir. - teh_senaus


Sadece bu durum vardı:

Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N

Gerçekleştirdim:

git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch

Bu işi bekledim, HEAD olacaktım, ama L işte şimdi ...

Tarihte doğru noktaya indiğinizden emin olmak için, tesisteki karmaşa ile çalışmak daha kolaydır.

git branch newbranch 
git reset --hard #########
git checkout newbranch

12
2018-05-01 07:50





Çok daha basit bir çözüm

Eğer:

  1. Asıl amacın geri dönmek master, ve
  2. Değişiklikleri korumak istiyor, ancak özellikle de bireysel taahhüt eder, ve
  3. Henüz itmedin,

Daha sonra aşağıdakiler daha basittir (daldan başlayarak) master yanlış taahhütler ile):

git reset HEAD~3
git stash
git checkout newbranch
git stash pop

Bu ne yapar:

  1. Son üç işi (ve mesajlarını) geri alır. masterancak tüm çalışan dosyaları bozulmadan bırakır
  2. Tüm çalışma dosyalarını değiştirir. master çalışan ağaç HEAD ~ 3 durumuna eşittir.
  3. Mevcut bir şubeye geçer newbranch
  4. Değiştirilen dosyaları stash dışına ve çalışma dizininize çeker.

Şimdi kullanabilirsiniz git add ve git commit normalde yaptığınız gibi. Tüm değişiklikler newbranch.

Bu ne yapmaz:

  • Ağacınızı parçalayan rastgele geçici dallar bırakmaz.
  • Yanlış taahhütleri ve taahhüt mesajlarını korumaz. Bu yeni işlem için yeni bir taahhüt mesajı eklemeniz gerekecek. Ancak OP, hedefin "değişiklik yapılmaksızın bu taahhütlerin yapılmasından önce ustayı geri almak" olduğunu ve bu çözümün bunu yaptığını belirtti.

Bunu en az haftada bir kez yanlışlıkla kazandığımda master yerine develop. Genellikle hangi durumda geri alma işleminde tek bir işlem yapacağım git reset HEAD^ sadece bir taahhüt geri almak için çalışır.


8
2018-06-01 05:40





1) Tüm değişikliklerinizi new_branch'e taşıyan yeni bir şube oluşturun.

git checkout -b new_branch

2) Daha sonra eski şubeye geri dönün.

git checkout master

3) git rebase yapın

git rebase -i <short-hash-of-B-commit>

4) Ardından açılan editör son 3 taahhüt bilgisini içerir.

...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...

5) Değişim pick için drop tüm bu 3 taahhütte. Ardından editörü kaydedin ve kapatın.

...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...

6) Şimdi son 3 işlem mevcut şubeden kaldırıldı (master). Şimdi dalı zorla, + şube isminden önce imzala.

git push origin +master

1
2018-05-25 14:14