Geçerli dalda taahhüt edilmemiş değişiklikler olduğunda başka bir şubeye göz at


353

Çoğu zaman varolan başka bir şubeyi kontrol etmeye çalıştığımda Git, geçerli şubede bazı taahhüt edilmemiş değişiklikler varsa bana izin vermiyor. Bu yüzden önce bu değişiklikleri yapmam veya saklamam gerekecek.

Ancak, zaman zaman Git, bu değişiklikleri taahhüt etmeden veya saklamadan başka bir şubeye ödeme yapmama izin verir ve bu değişiklikleri şubedeki satın alma işlemine götürür.

Buradaki kural nedir? Değişikliklerin aşamalı veya düzensiz olması önemli mi? Değişiklikleri başka bir şubeye taşımak benim için bir anlam ifade etmiyor, git neden bazen izin veriyor? Yani, bazı durumlarda yardımcı olur mu?

Yanıtlar:


355

Ön notlar

Buradaki gözlem, çalışmaya başladıktan sonra branch1( branch2önce farklı bir şubeye geçmenin iyi olacağını unutma ya da fark etmeme ), koşmanız:

git checkout branch2

Bazen Git "Tamam, şimdi branş2'desin!" Diyor. Bazen Git, "Bunu yapamam, bazı değişikliklerinizi kaybederim" der.

Git Eğer olmaz bunu yapalım bir yere kalıcı onları kurtarmak için, değişiklikleri işlemek zorunda. Bunları git stashkaydetmek için kullanmak isteyebilirsiniz ; bu onun için tasarlanmış şeylerden biri. Not olduğu git stash saveveya git stash pushgerçekte anlamı "Tüm Değişiklikleri uygulayın, ama hiç hiçbir dalda sonra şimdi nerede çıkarın." Bu, geçiş yapmayı mümkün kılar: Artık devam eden değişiklikleriniz yok. Daha sonra git stash applybunları değiştirdikten sonra yapabilirsiniz .

Kenar çubuğu: git stash saveeski sözdizimidir; git stash pushGit sürüm 2.13'te, argümanlarla ilgili bazı sorunları düzeltmek git stashve yeni seçeneklere izin vermek için tanıtıldı . Temel şekillerde kullanıldığında her ikisi de aynı şeyi yapar.

İsterseniz burada okumayı bırakabilirsiniz!

Git Eğer olmaz sen dönelim Zaten bir çare vardır: kullanılmasını git stashveya git commit; veya değişikliklerinizin yeniden oluşturulması önemsizse, git checkout -fzorlamak için kullanın . Bu cevap, Git'in bazı değişiklikler yapmaya başlamış olsanız bile size ne zaman izin vereceği ile ilgilidir git checkout branch2. Neden bazen çalışıyor , diğer zamanlarda çalışmıyor ?

Buradaki kural bir yönden basit, diğerinde karmaşık / açıklaması zor:

Yalnızca söz konusu anahtarlama bu değişikliklerin hızlandırılmasını gerektirmiyorsa, çalışma ağacında taahhüt edilmemiş değişikliklerle dalları değiştirebilirsiniz.

Bu - ve bunun hala basitleştirilmiş olduğunu lütfen unutmayın; kademeli git adds, git rms ve benzeri bazı ekstra zor köşe vakaları var - varsayalım branch1. A git checkout branch2bunu yapmak zorunda olurdu:

  • Her dosya için olduğu içinde branch1ve değil de branch2, 1 dosyası bu kaldır.
  • Her dosya için olduğu içinde branch2ve değil de branch1, (uygun içeriği ile) bu dosyayı oluşturun.
  • Her iki dalda bulunan her dosya için, sürüm branch2farklıysa, çalışma ağacı sürümünü güncelleyin.

Bu adımların her biri, çalışma ağacınızdaki bir şeyi tıkayabilir:

  • Çalışma ağacındaki sürüm, işlenen sürümle aynı ise, bir dosyayı kaldırmak "güvenlidir" branch1; değişiklik yaptıysanız "güvensiz" olur.
  • branch2Şu anda mevcut değilse, dosya göründüğü gibi oluşturmak "güvenlidir". 2 Varsa, ancak "yanlış" içeriğe sahipse "güvensizdir".
  • Ve elbette, iş ağacı sürümü zaten taahhüt edilmişse, bir dosyanın çalışma ağacı sürümünü farklı bir sürümle değiştirmek "güvenlidir" branch1.

Yeni bir şube ( git checkout -b newbranch) oluşturmak her zaman "güvenli" olarak kabul edilir: bu işlemin bir parçası olarak çalışma ağacına hiçbir dosya eklenmeyecek, kaldırılmayacak veya değiştirilmeyecek ve indeks / aşamalandırma alanına da dokunulmayacaktır. (Uyarı: Yeni dalın başlangıç ​​noktasını değiştirmeden yeni bir dal oluştururken güvenlidir; ancak başka bir argüman eklerseniz, örneğin git checkout -b newbranch different-start-point, bu, bir şeyleri değiştirmek zorunda kalabilirsiniz different-start-point. Git daha sonra ödeme güvenliği kurallarını her zamanki gibi uygulayacaktır. .)


1 Bunun için, bir dosyanın bir dalda olmasının ne anlama geldiğini tanımlamamız gerekir; bu da, sözcük dalının doğru tanımlanmasını gerektirir . (Ayrıca bkz tam olarak "dalında" tarafından ne demek? ) Burada, gerçekten ortalama ne taahhüt hangi dal-name giderir: kimin yolu bir dosya olup içinde ise bir karma üretir. Bu dosya değil de bunun yerine bir hata mesajı alırsanız. Dizininizdeki veya çalışma ağacınızdaki yolun varlığı, bu özel soruyu yanıtlarken alakalı değildir. Böylece, burada sır her birinin sonucunu incelemektir.P branch1git rev-parse branch1:Pbranch1Pgit rev-parsebranch-name:path. Dosya en fazla bir dalda "içeride" olduğu için bu başarısız olur veya bize iki karma kimliği verir. İki karma kimliği aynı ise, dosya her iki dalda da aynıdır. Değişiklik gerekmez. Karma kimlikleri farklıysa, dosya iki dalda farklıdır ve dalları değiştirmek için değiştirilmesi gerekir.

Buradaki temel kavram, taahhütlerdeki dosyaların sonsuza dek dondurulmuş olmasıdır. Düzenleyeceğiniz dosyalar dondurulmuş değil . En azından başlangıçta, sadece iki donmuş komisyon arasındaki uyumsuzluklara bakıyoruz. Ne yazık ki, biz-veya Git-da dosyalarla uğraşmak zorunda değildir sen uzak geçmek için gidiyoruz ve taahhüt içinde olan sen geçiş gidiyoruz işlemek içinde. Bu, geriye kalan komplikasyonlara yol açar, çünkü dosyalar üzerinde çalıştığımız bu iki dondurulmuş taahhütün varlığına gerek kalmadan indekste ve / veya çalışma ağacında da bulunabilir.

2 Zaten "doğru içerikler" ile zaten mevcutsa, "güvenli sıralama" olarak kabul edilebilir, böylece Git'in bunu yaratması gerekmez. Git'in buna izin veren en azından bazı sürümlerini hatırlıyorum, ancak şimdi test etmek Git 1.8.5.4'te "güvensiz" olarak kabul edildiğini gösteriyor. Aynı argüman, değiştirilecek şubeyle eşleşecek şekilde değiştirilen değiştirilmiş bir dosya için de geçerlidir. Yine de, 1.8.5.4 sadece "üzerine yazılacak" diyor. Teknik notların sonuna da bakın: Git'i ilk sürüm 1.5.s'de kullanmaya başladığımdan beri okuma ağacı kurallarının değiştiğini düşünmediğim için belleğim arızalı olabilir.


Değişikliklerin aşamalı veya düzensiz olması önemli mi?

Evet, bazı açılardan. Özellikle, bir değişikliği aşamalıp çalışma ağacı dosyasını "kaldırabilirsiniz". İşte iki dalda bir dosya, branch1ve içinde farklı branch2:

$ git show branch1:inboth
this file is in both branches
$ git show branch2:inboth
this file is in both branches
but it has more stuff in branch2 now
$ git checkout branch1
Switched to branch 'branch1'
$ echo 'but it has more stuff in branch2 now' >> inboth

Bu noktada, çalışıyor olsak da , çalışan ağaç dosyası içindeki dosyayla inbotheşleşir . Bu değişiklik taahhüt için hazırlanmamıştır, bu da şudur :branch2branch1git status --short

$ git status --short
 M inboth

Space-then-M, "değiştirilmiş ancak aşamalı değil" anlamına gelir (veya daha kesin olarak, çalışma ağacı kopyası aşamalı / dizin kopyasından farklıdır).

$ git checkout branch2
error: Your local changes ...

Tamam, şimdi zaten bildiğimiz çalışma ağacı kopyasını da kopyayla eşleştirelim branch2.

$ git add inboth
$ git status --short
M  inboth
$ git checkout branch2
Switched to branch 'branch2'

Burada aşamalı ve çalışan kopyaların ikisi de içinde olanlarla eşleşti branch2, bu yüzden kasaya izin verildi.

Bir adım daha deneyelim:

$ git checkout branch1
Switched to branch 'branch1'
$ cat inboth
this file is in both branches

Yaptığım değişiklik artık aşamalandırma alanından kayboluyor (çünkü ödeme aşamalandırma alanı içerisinden yazıyor). Bu biraz köşe davası. Değişiklik gitmiş değil, ancak bunu sahnelenen olduğu gerçeği, bir gitti.

Şube kopyasından farklı olarak dosyanın üçüncü bir varyantını hazırlayalım, ardından çalışma kopyasını geçerli dal sürümüyle eşleşecek şekilde ayarlayalım:

$ echo 'staged version different from all' > inboth
$ git add inboth
$ git show branch1:inboth > inboth
$ git status --short
MM inboth

MBurada iki s: aşamalı dosya HEADdosyadan ve çalışma ağacı dosyası aşamalı dosyadan farklıdır. Çalışma ağacı sürümü branch1(aka HEAD) sürümüyle eşleşiyor :

$ git diff HEAD
$

Ancak git checkoutödeme işlemine izin vermeyecektir:

$ git checkout branch2
error: Your local changes ...

branch2Sürümü çalışan sürüm olarak ayarlayalım :

$ git show branch2:inboth > inboth
$ git status --short
MM inboth
$ git diff HEAD
diff --git a/inboth b/inboth
index ecb07f7..aee20fb 100644
--- a/inboth
+++ b/inboth
@@ -1 +1,2 @@
 this file is in both branches
+but it has more stuff in branch2 now
$ git diff branch2 -- inboth
$ git checkout branch2
error: Your local changes ...

Geçerli çalışan kopya branch2, içindeki kopyayla eşleşse de , hazırlanan dosya eşleşmez , bu nedenle git checkoutbu kopya kaybolur ve git checkoutreddedilir.

Teknik notlar - sadece delicesine meraklı olanlar için :-)

Tüm bunların altında yatan uygulama mekanizması Git'in dizinidir . "Hazırlama alanı" olarak da adlandırılan dizin, bir sonraki taahhüdü oluşturduğunuz yerdir : geçerli taahhüdü, yani, şimdi teslim aldığınız her şeyi eşleştirmeye başlar ve her git adddosya açışınızda dizin sürümünü değiştirirsiniz çalışma ağacında ne varsa.

Unutmayın, çalışma ağacı dosyalarınız üzerinde çalıştığınız yerdir. Burada, taahhütlerde ve dizinde olduğu gibi sadece Git için yararlı olan bazı özel formlardan ziyade normal formları vardır. Bir dosyayı ayıklamak Yani gelen bir işlemeye aracılığıyla iş ağaca daha sonra endeksi, vb. Değiştirdikten sonra git add, dizine getirin. Yani aslında her dosya için üç yer vardır: mevcut işlem, dizin ve çalışma ağacı.

Eğer çalıştırdığınızda git checkout branch2, Git kapakları altından ne karşılaştırmaktır ucu taahhüt ait branch2akım işlemek ve şimdi dizinde hem de her ne kadar. Şu an orada bulunan dosyalarla eşleşen Git, yalnız bırakılabilir. Hepsi el değmemiş. Her iki işlemde de aynı olan herhangi bir dosya , Git'i de yalnız bırakabilir - bunlar da dalları değiştirmenize izin veren dosyalardır.

Git anahtarlama dahil Git'in çoğu, bu dizin nedeniyle nispeten hızlıdır . Aslında dizinde olan her dosyanın kendisi değil, her dosyanın karmasıdır . Dosyanın kendisinin kopyası Git'in depoda bir blob nesnesi olarak adlandırdığı şekilde saklanır . Bu, dosyaların komisyonlarda nasıl depolandığına benzer: komisyonlar aslında dosyaları içermez, sadece Git'i her dosyanın karma kimliğine yönlendirir. Böylece Git, X ve Y'nin aynı dosyaya sahip olup olmadığına karar vermek için şu anda 160 bit uzunluğundaki karma kimlikleri karşılaştırabilir . Daha sonra bu karma kimlikleri dizindeki karma kimliğiyle de karşılaştırabilir.

Yukarıdaki tüm tuhaf köşe vakalarına yol açan şey budur. Her ikisinin de dosyası olan X ve Y taahhütlerimiz var path/to/name.txtve için bir dizin girişimiz var path/to/name.txt. Belki her üç karma eşleşir. Belki ikisi eşleşir, biri eşleşmez. Belki üçü de farklıdır. Ayrıca, bunun another/file.txtyalnızca X veya yalnızca Y'de olduğunu ve şimdi dizinde olduğunu veya olmadığını gösterir. Bu çeşitli vakaların her biri kendi ayrı değerlendirilmesi gerekir: Git gelmez ihtiyaç dosyayı kopyalamak dan endeksi taahhüt veya gelen anahtara, dizinden çıkarmak için X için Y ? Eğer öyleyse, o da vardırdosyayı çalışma ağacına kopyalayın veya çalışma ağacından kaldırın. Ve eğer o davasını s, endeks ve iş-ağaç versiyonları daha iyi maçı taahhüt sürümleri en az biri vardı; aksi halde Git bazı verileri tıkayacaktır.

(Tüm kurallar, git checkoutbeklediğiniz gibi belgelerde değil , git read-tree"İki Ağaç Birleştirme" bölümü altında yer alan belgelerde açıklanmıştır .)


3
... ayrıca git checkout -m, çalışma ağacınızı ve dizin değişikliklerinizi yeni kasada birleştiren de var .
jthill

1
Bu mükemmel açıklama için teşekkürler! Ancak resmi dokümanlardaki bilgileri nerede bulabilirim? Yoksa eksik mi? Eğer öyleyse, git için yetkili referans nedir (umarım kaynak kodu dışında)?
en fazla

1
(1) yapamazsınız ve (2) kaynak kodu. Asıl sorun Git'in sürekli gelişmesidir. Örneğin, şu anda SHA-1'i SHA-256 ile veya lehine artırmak için büyük bir itme var. Git'in bu kısmı uzun zamandır oldukça kararlıydı ve temeldeki mekanizma basit: Git, geçerli dizini geçerli ve hedef taahhütlerle karşılaştırıyor ve hedef taahhüdüne göre hangi dosyaların (varsa) değiştirileceğine karar veriyor , dizin girdisinin değiştirilmesi gerekiyorsa çalışma ağacı dosyalarının "temizliğini" sınar.
torek

6
Kısa cevap: Bir kural vardır, ancak ortalama bir kullanıcının hatırlamak yerine herhangi bir anlayış umuduna sahip olması çok zordur, bu nedenle akıllıca davranmak için araca güvenmek yerine, yalnızca mevcut şube kararlı ve temiz. Bunun, başka bir şubeye olağanüstü değişiklikler yapmanın ne zaman yararlı olacağı sorusunu nasıl cevapladığını görmüyorum, ama bunu kaçırmış olabilirim çünkü anlamak için mücadele ediyorum.
Neutrino

2
@HawkeyeParker: Bu cevap çok sayıda düzenlemeye tabi tutuldu ve bunların herhangi birinin onu çok geliştirdiğinden emin değilim, ancak bir dosyanın "bir dalda" olmasının ne anlama geldiğiyle ilgili bir şey eklemeyi deneyeceğim. Sonuçta bu sallantılı olacaktır çünkü burada "dal" kavramı ilk etapta doğru bir şekilde tanımlanmamıştır, ancak bu yine de başka bir öğedir.
torek

51

İki seçeneğiniz var: değişikliklerinizi saklayın:

git stash

daha sonra onları geri almak için:

git stash apply

veya uzak dalı elde edip değişikliklerinizi dalda birleştirebilmeniz için değişikliklerinizi bir dala yerleştirebilirsiniz. Bu git ile ilgili en iyi şeylerden biri: bir dal yapabilir, taahhütte bulunabilir ve daha sonra bulunduğunuz dalda başka değişiklikler getirebilirsiniz.

Bunun bir anlam ifade etmediğini söylüyorsunuz, ancak bunu sadece çektiğinizden sonra istediğiniz zaman birleştirebilmeniz için yapıyorsunuz. Açıkçası, diğer seçiminiz dalınızın kopyasını almak ve sonra çekme yapmaktır. Varsayım ya bunu yapmak istemediğinizdir (bu durumda bir şube istemediğinize şaşkınımdır) ya da çatışmalardan korkuyorsunuz.


1
Doğru komut değil git stash applymi? burada dokümanlar.
Thomas8

1
Tam aradığım şey, geçici olarak farklı dallara geçmek, bir şey aramak ve üzerinde çalıştığım dalın aynı durumuna geri dönmek için. Teşekkürler Rob!
Naishta

1
Evet, bunu yapmanın doğru yolu bu. Kabul edilen cevaptaki ayrıntıyı takdir ediyorum, ama bu işleri olması gerekenden daha zorlaştırıyor.
Michael Leonard

5
Ayrıca, zulayı saklamak için herhangi bir gereksiniminiz yoksa, kullanabilirsiniz git stash popve başarıyla uygulandığında zulayı listenizden bırakır.
Michael Leonard

1
git stash popRepo geçmişinizde bir miktar günlük tutmak istemiyorsanız daha iyi kullanım
yığın

14

Yeni dal, söz konusu değiştirilen dosya için geçerli daldan farklı düzenlemeler içeriyorsa, değişiklik tamamlanana veya saklanana kadar dallar arasında geçiş yapmanıza izin vermez. Değiştirilen dosya her iki dalda da aynıysa (yani, dosyanın taahhütlü sürümü), serbestçe geçiş yapabilirsiniz.

Misal:

$ echo 'hello world' > file.txt
$ git add file.txt
$ git commit -m "adding file.txt"

$ git checkout -b experiment
$ echo 'goodbye world' >> file.txt
$ git add file.txt
$ git commit -m "added text"
     # experiment now contains changes that master doesn't have
     # any future changes to this file will keep you from changing branches
     # until the changes are stashed or committed

$ echo "and we're back" >> file.txt  # making additional changes
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
    file.txt
Please, commit your changes or stash them before you can switch branches.
Aborting

Bu, izlenmeyen dosyalar kadar izlenen dosyalar için de geçerlidir. İzlenmemiş bir dosya örneği.

Misal:

$ git checkout -b experimental  # creates new branch 'experimental'
$ echo 'hello world' > file.txt
$ git add file.txt
$ git commit -m "added file.txt"

$ git checkout master # master does not have file.txt
$ echo 'goodbye world' > file.txt
$ git checkout experimental
error: The following untracked working tree files would be overwritten by checkout:
    file.txt
Please move or remove them before you can switch branches.
Aborting

Değişiklik yaparken dallar arasında neden hareket etmek isteyeceğinize iyi bir örnek, usta üzerinde bazı deneyler yapıyorsanız, bunları yapmak istedim, ancak henüz ustalaşmamaktı ...

$ echo 'experimental change' >> file.txt # change to existing tracked file
   # I want to save these, but not on master

$ git checkout -b experiment
M       file.txt
Switched to branch 'experiment'
$ git add file.txt
$ git commit -m "possible modification for file.txt"

Aslında hala tam anlamıyorum. İlk örneğinizde, "ve geri döndük" ifadesini ekledikten sonra, yerel değişikliğin üzerine yazılacağını söylüyor, tam olarak hangi yerel değişiklik? "ve geri döndük"? Neden git bu değişikliği master yapmak için taşımıyor, böylece master'da dosya "merhaba dünya" ve "içeriyor ve geri döndük"
Xufeng

İlk örnekte ustanın yalnızca 'merhaba dünyası' vardır. deney 'merhaba dünya \ ngoodbye dünya' taahhüt etti. Şube değişikliğinin gerçekleşmesi için, file.txt dosyasının değiştirilmesi gerekiyor, sorun şu, "merhaba world \ ngoodbye world \ nand biz geri" olarak onaylanmamış değişiklikler var.
Gordolio

1

Doğru cevap

git checkout -m origin/master

Orijin ana dalından gelen değişiklikleri yerel, hatta taahhüt edilmemiş değişikliklerinizle birleştirir.


0

Bu değişikliklerin yapılmasını istemiyorsanız, git reset --hard .

Daha sonra istenen şubeye ödeme yapabilirsiniz, ancak taahhüt edilmemiş değişikliklerin kaybedileceğini unutmayın.

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.