Catatan awal
Pengamatan di sini adalah bahwa, setelah Anda mulai bekerja branch1
(lupa atau tidak menyadari bahwa akan lebih baik untuk beralih ke cabang yang berbeda branch2
terlebih dahulu), Anda menjalankan:
git checkout branch2
Terkadang Git berkata, "Oke, kamu ada di branch2 sekarang!" Terkadang, Git berkata, "Aku tidak bisa melakukan itu, aku akan kehilangan beberapa perubahanmu."
Jika Git tidak akan membiarkan Anda melakukannya, Anda harus melakukan perubahan Anda, untuk menyimpannya di tempat permanen. Anda mungkin ingin menggunakannya git stash
untuk menyimpannya; ini adalah salah satu hal yang dirancang untuk itu. Perhatikan bahwa git stash save
atau git stash push
sebenarnya berarti "Komit semua perubahan, tetapi tidak pada cabang sama sekali, lalu hapus dari tempat saya sekarang." Itu memungkinkan untuk beralih: Anda sekarang tidak memiliki perubahan dalam proses. Anda bisa kemudian git stash apply
setelah berpindah.
Sidebar: git stash save
adalah sintaks lama; git stash push
diperkenalkan di Git versi 2.13, untuk memperbaiki beberapa masalah dengan argumen git stash
dan memungkinkan untuk opsi baru. Keduanya melakukan hal yang sama, ketika digunakan dengan cara dasar.
Anda bisa berhenti membaca di sini, jika mau!
Jika Git tidak akan membiarkan Anda beralih, Anda sudah memiliki obat: gunakan git stash
atau git commit
; atau, jika perubahan Anda sepele untuk dibuat kembali, gunakan git checkout -f
untuk memaksanya. Jawaban ini adalah tentang kapan Git akan membiarkan Anda git checkout branch2
meskipun Anda mulai membuat beberapa perubahan. Mengapa kadang-kadang itu berhasil , dan tidak kali lain ?
Aturan di sini sederhana dalam satu cara, dan rumit / sulit untuk dijelaskan dengan cara lain:
Anda bisa berganti cabang dengan perubahan yang tidak dikomit di pohon-kerja jika dan hanya jika mengatakan pergantian tidak membutuhkan clobbering perubahan itu.
Yaitu — dan harap dicatat bahwa ini masih disederhanakan; ada beberapa kasus sudut yang sangat sulit dengan git add
s, git rm
s dan semacamnya — anggaplah Anda sedang aktif branch1
. A git checkout branch2
harus melakukan ini:
- Untuk setiap file yang adalah di
branch1
dan tidak di branch2
, 1 menghapus file.
- Untuk setiap file yang adalah di
branch2
dan tidak di branch1
, membuat file yang (dengan isi yang sesuai).
- Untuk setiap file yang ada di kedua cabang, jika versi dalam
branch2
berbeda, perbarui versi pohon kerja.
Masing-masing langkah ini bisa mengalahkan sesuatu di pohon pekerjaan Anda:
- Menghapus file adalah "aman" jika versi di pohon-kerja sama dengan versi yang dilakukan di
branch1
; "tidak aman" jika Anda melakukan perubahan.
- Membuat file dengan cara itu muncul
branch2
adalah "aman" jika tidak ada sekarang. 2 "Tidak aman" jika memang ada sekarang tetapi memiliki konten "salah".
- Dan tentu saja, mengganti versi work-tree dari file dengan versi yang berbeda adalah "aman" jika versi work-tree sudah berkomitmen
branch1
.
Membuat cabang baru ( git checkout -b newbranch
) selalu dianggap "aman": tidak ada file yang akan ditambahkan, dihapus, atau diubah di pohon-kerja sebagai bagian dari proses ini, dan area indeks / staging juga tidak tersentuh. (Peringatan: aman saat membuat cabang baru tanpa mengubah titik awal cabang baru; tetapi jika Anda menambahkan argumen lain, misalnya git checkout -b newbranch different-start-point
, ini mungkin harus mengubah hal-hal, untuk pindah different-start-point
. Git kemudian akan menerapkan aturan keselamatan checkout seperti biasa .)
1 Ini mengharuskan kita mendefinisikan apa artinya file berada dalam cabang, yang pada gilirannya mengharuskan mendefinisikan kata cabang dengan benar. (Lihat juga Apa sebenarnya yang kita maksud dengan "cabang"? ) Di sini, apa yang saya benar-benar berarti adalah komit untuk yang cabang-nama resolves: sebuah file yang jalan adalah adalah dalam jika menghasilkan hash. File yang tidak di jika Anda mendapatkan pesan kesalahan sebagai gantinya. Keberadaan jalur di indeks atau pohon kerja Anda tidak relevan saat menjawab pertanyaan khusus ini. Jadi, rahasianya di sini adalah untuk memeriksa hasil masing-masingP
branch1
git rev-parse branch1:P
branch1
P
git rev-parse
branch-name:path
. Ini gagal karena file "dalam" paling banyak satu cabang, atau memberi kita dua ID hash. Jika kedua hash ID adalah sama , file tersebut sama di kedua cabang. Tidak diperlukan perubahan. Jika ID hash berbeda, file berbeda di dua cabang, dan harus diubah untuk beralih cabang.
Gagasan utama di sini adalah bahwa file dalam komit dibekukan selamanya. File yang akan Anda edit jelas tidak beku. Kami, setidaknya pada awalnya, hanya melihat ketidakcocokan antara dua komitmen beku. Sayangnya, kami — atau Git — juga harus berurusan dengan file yang tidak ada dalam komit yang akan Anda alihkan dan berada di komit yang akan Anda alihkan. Ini mengarah pada komplikasi yang tersisa, karena file juga dapat ada di indeks dan / atau di pohon-kerja, tanpa harus ada dua komit beku tertentu yang sedang kami kerjakan.
2 Ini mungkin dianggap "semacam aman" jika sudah ada dengan "konten yang tepat", sehingga Git tidak harus membuatnya lagi. Saya ingat setidaknya beberapa versi Git mengizinkan ini, tetapi pengujian barusan menunjukkan itu dianggap "tidak aman" di Git 1.8.5.4. Argumen yang sama akan berlaku untuk file yang dimodifikasi yang kebetulan dimodifikasi agar sesuai dengan cabang to-be-switch-to. Sekali lagi, 1.8.5.4 hanya mengatakan "akan ditimpa", meskipun. Lihat akhir catatan teknis juga: ingatan saya mungkin salah karena saya tidak berpikir aturan baca-pohon telah berubah sejak saya mulai menggunakan Git pada versi 1.5.something.
Apakah penting apakah perubahan itu dilakukan atau tidak?
Ya, dalam beberapa hal. Secara khusus, Anda dapat melakukan perubahan, lalu "membatalkan modifikasi" file pohon kerja. Berikut adalah file dalam dua cabang, yang berbeda dalam branch1
dan 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
Pada titik ini, file pohon kerja inboth
cocok dengan yang ada di branch2
, meskipun kita aktif branch1
. Perubahan ini tidak dilakukan untuk komit, yang git status --short
ditunjukkan di sini:
$ git status --short
M inboth
Space-then-M berarti "dimodifikasi tetapi tidak dipentaskan" (atau lebih tepatnya, copy pohon kerja berbeda dari salinan staged / index).
$ git checkout branch2
error: Your local changes ...
Oke, sekarang mari kita buat salinan pohon kerja, yang sudah kita ketahui juga cocok dengan salinannya branch2
.
$ git add inboth
$ git status --short
M inboth
$ git checkout branch2
Switched to branch 'branch2'
Di sini, salinan yang dipentaskan dan bekerja cocok dengan apa yang ada di dalam branch2
, sehingga checkout diizinkan.
Mari kita coba langkah lain:
$ git checkout branch1
Switched to branch 'branch1'
$ cat inboth
this file is in both branches
Perubahan yang saya buat hilang dari area pementasan sekarang (karena checkout menulis melalui area pementasan). Ini sedikit kasus sudut. Perubahan itu tidak hilang, tapi fakta bahwa aku telah dipentaskan itu, yang hilang.
Mari kita buat varian ketiga dari file tersebut, berbeda dari kedua cabang-salinan, lalu atur copy pekerjaan agar sesuai dengan versi cabang saat ini:
$ echo 'staged version different from all' > inboth
$ git add inboth
$ git show branch1:inboth > inboth
$ git status --short
MM inboth
Dua M
di sini berarti: file bertahap berbeda dari HEAD
file, dan , file pohon kerja berbeda dari file bertahap. Versi pohon kerja tidak cocok dengan versi branch1
(alias HEAD
):
$ git diff HEAD
$
Tetapi git checkout
tidak akan mengizinkan checkout:
$ git checkout branch2
error: Your local changes ...
Mari kita tetapkan branch2
versi sebagai versi kerja:
$ 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 ...
Meskipun copy pekerjaan saat ini cocok dengan yang ada di branch2
, file yang dipentaskan tidak, jadi git checkout
akan kehilangan salinan itu, dan git checkout
ditolak.
Catatan teknis — hanya untuk yang penasaran :-)
Mekanisme implementasi yang mendasari semua ini adalah indeks Git . Indeks, juga disebut "area pementasan", adalah tempat Anda membangun komit berikutnya : mulai cocok dengan komit saat ini, yaitu, apa pun yang Anda telah check-out sekarang, dan kemudian setiap kali Anda git add
file, Anda mengganti versi indeks dengan apa pun yang Anda miliki di pohon pekerjaan Anda.
Ingat, pohon-kerja adalah tempat Anda mengerjakan file Anda. Di sini, mereka memiliki bentuk normal, daripada beberapa bentuk khusus yang hanya berguna untuk Git seperti yang mereka lakukan di commit dan di indeks. Jadi, Anda mengekstrak file dari komit, melalui indeks, dan kemudian ke pohon-kerja. Setelah mengubahnya, Anda git add
ke indeks. Jadi sebenarnya ada tiga tempat untuk setiap file: komit saat ini, indeks, dan pohon kerja.
Ketika Anda menjalankan git checkout branch2
, apa Git tidak di bawah selimut adalah untuk membandingkan ujung berkomitmen dari branch2
apa pun adalah baik di saat melakukan dan indeks sekarang. File apa pun yang cocok dengan yang ada di sana sekarang, Git dapat dibiarkan sendiri. Semuanya tidak tersentuh. File apa pun yang sama di kedua komit , Git juga dapat dibiarkan sendiri — dan ini adalah yang memungkinkan Anda beralih cabang.
Banyak Git, termasuk komit-switching, relatif cepat karena indeks ini. Apa yang sebenarnya ada di indeks bukanlah masing-masing file itu sendiri, melainkan setiap hash file . Salinan file itu sendiri disimpan sebagai apa yang Git sebut sebagai objek gumpalan , dalam repositori. Ini mirip dengan bagaimana file disimpan dalam komit juga: komit sebenarnya tidak mengandung file , mereka hanya membawa Git ke ID hash dari setiap file. Jadi Git dapat membandingkan ID hash — string 160-bit-saat ini — untuk memutuskan apakah komit X dan Y memiliki file yang sama atau tidak. Kemudian dapat membandingkan ID hash tersebut dengan ID hash dalam indeks juga.
Inilah yang mengarah ke semua kasus sudut aneh di atas. Kami memiliki X dan Y yang keduanya memiliki file path/to/name.txt
, dan kami memiliki entri indeks untuk path/to/name.txt
. Mungkin ketiga hash cocok. Mungkin dua dari mereka cocok dan satu tidak. Mungkin ketiganya berbeda. Dan, kita mungkin juga memiliki another/file.txt
itu hanya di X atau hanya di Y dan sedang atau tidak dalam indeks sekarang. Masing-masing dari berbagai kasus ini memerlukan pertimbangan tersendiri: apakah Git perlu menyalin file dari commit ke index, atau menghapusnya dari index, untuk beralih dari X ke Y ? Jika demikian, itu juga harussalin file ke work-tree, atau hapus dari work-tree. Dan jika itu masalahnya, versi indeks dan versi work-tree lebih cocok dengan setidaknya satu versi yang dikomit; jika tidak, Git akan menghancurkan beberapa data.
(Aturan yang lengkap untuk semua ini dijelaskan dalam, bukan git checkout
dokumentasi seperti yang Anda harapkan, melainkan para git read-tree
dokumentasi, di bawah bagian berjudul "Two Tree Merge" .)
git checkout -m
, yang menggabungkan pohon kerja Anda dan perubahan indeks ke checkout baru.Anda memiliki dua pilihan: menyimpan perubahan Anda:
kemudian untuk mendapatkannya kembali:
atau letakkan perubahan Anda di cabang sehingga Anda bisa mendapatkan cabang jarak jauh dan kemudian gabungkan perubahan Anda ke cabang itu. Itu salah satu hal terbesar tentang git: Anda bisa membuat cabang, berkomitmen padanya, lalu mengambil perubahan lain ke cabang tempat Anda berada.
Anda mengatakan itu tidak masuk akal, tetapi Anda hanya melakukannya sehingga Anda dapat menggabungkannya sesuka hati setelah melakukan tarikan. Tentunya pilihan Anda yang lain adalah berkomitmen pada salinan cabang Anda dan kemudian melakukan penarikan. Anggapannya adalah Anda tidak ingin melakukan itu (dalam hal ini saya bingung bahwa Anda tidak menginginkan cabang) atau Anda takut akan konflik.
sumber
git stash apply
? di sini dokumen.git stash pop
dan simpanan akan dikeluarkan dari daftar jika berhasil.git stash pop
, kecuali Anda berniat menyimpan catatan simpanan dalam riwayat repo AndaJika cabang baru berisi pengeditan yang berbeda dari cabang saat ini untuk file yang diubah tertentu, maka itu tidak akan memungkinkan Anda untuk beralih cabang sampai perubahan dilakukan atau disimpan. Jika file yang diubah sama di kedua cabang (yaitu, versi file yang berkomitmen), maka Anda dapat beralih dengan bebas.
Contoh:
Ini berlaku untuk file yang tidak dilacak serta file yang dilacak. Berikut adalah contoh untuk file yang tidak dilacak.
Contoh:
Contoh yang bagus tentang mengapa Anda AKAN ingin berpindah di antara cabang sambil membuat perubahan adalah jika Anda melakukan beberapa percobaan pada master, ingin mengkomitnya, tetapi belum untuk menguasainya ...
sumber
Jawaban yang benar adalah
git checkout -m origin/master
Itu menggabungkan perubahan dari cabang master asal dengan perubahan lokal Anda bahkan tanpa komitmen.
sumber
Jika Anda tidak ingin perubahan ini dilakukan sama sekali
git reset --hard
.Selanjutnya Anda dapat checkout ke cabang yang diinginkan, tetapi ingat bahwa perubahan yang tidak dikomit akan hilang.
sumber