บันทึกเบื้องต้น
การสังเกตที่นี่คือหลังจากที่คุณเริ่มทำงานbranch1
(ลืมหรือไม่ทราบว่ามันจะเป็นการดีถ้าคุณเปลี่ยนเป็นสาขาอื่นbranch2
ก่อน) คุณรัน:
git checkout branch2
บางครั้ง Git พูดว่า "โอเคคุณอยู่ใน branch2 แล้ว!" บางครั้ง Git พูดว่า "ฉันทำไม่ได้ฉันจะสูญเสียการเปลี่ยนแปลงบางส่วน"
หาก Git ไม่ยอมให้คุณทำคุณต้องยอมรับการเปลี่ยนแปลงของคุณเพื่อบันทึกสิ่งเหล่านั้นอย่างถาวร คุณอาจต้องการใช้git stash
เพื่อบันทึก นี่คือหนึ่งในสิ่งที่ออกแบบมา โปรดทราบว่าgit stash save
หรือgit stash push
จริง ๆ แล้วหมายถึง "ยอมรับการเปลี่ยนแปลงทั้งหมด แต่ไม่มีสาขาใดเลยให้ลบออกจากที่ฉันเป็นอยู่ตอนนี้" ซึ่งทำให้เป็นไปได้ที่จะเปลี่ยน: ตอนนี้คุณไม่มีการเปลี่ยนแปลงที่กำลังดำเนินอยู่ จากนั้นคุณสามารถgit stash apply
เปลี่ยนมันได้
Sidebar: git stash save
เป็นไวยากรณ์เก่า git stash push
ถูกนำมาใช้ใน Git เวอร์ชัน 2.13 เพื่อแก้ไขปัญหาบางอย่างกับข้อโต้แย้งgit stash
และอนุญาตตัวเลือกใหม่ ทั้งสองทำสิ่งเดียวกันเมื่อใช้ในวิธีพื้นฐาน
คุณสามารถหยุดอ่านที่นี่ถ้าคุณชอบ!
หาก Git ไม่ยอมให้คุณเปลี่ยนคุณมีวิธีแก้ไข: ใช้git stash
หรือgit commit
; หรือหากการเปลี่ยนแปลงของคุณไม่สำคัญที่จะสร้างใหม่ให้ใช้git checkout -f
เพื่อบังคับ คำตอบนี้เกี่ยวกับเมื่อ Git จะให้คุณgit checkout branch2
แม้ว่าคุณจะเริ่มทำการเปลี่ยนแปลงบางอย่าง ทำไมบางครั้งมันทำงานและไม่ใช่เวลาอื่น
กฎที่นี่เรียบง่ายในทางเดียวและซับซ้อน / ยากที่จะอธิบายในอีก:
คุณสามารถสลับสาขาที่มีการเปลี่ยนแปลงแบบไม่มีข้อผูกมัดในแผนผังงานได้หากว่าหากว่าการสลับไม่จำเป็นต้องปิดกั้นการเปลี่ยนแปลงเหล่านั้น
นั่นคือ - และโปรดทราบว่านี่ยังง่าย มีบางอย่างที่พิเศษที่ยากกรณีมุมกับฉากgit add
s, s-และเช่นสมมติว่าคุณอยู่ในgit rm
จะต้องทำเช่นนี้:branch1
git checkout branch2
- สำหรับทุกไฟล์ที่เป็นใน
branch1
และไม่ได้อยู่ในbranch2
, 1ลบไฟล์ที่
- สำหรับไฟล์ที่ทุกคนเป็นใน
branch2
และไม่ได้ในการbranch1
สร้างแฟ้มที่ (ที่มีเนื้อหาเหมาะสม)
- สำหรับทุกไฟล์ที่อยู่ในทั้งสองสาขาหากเวอร์ชัน
branch2
ต่างกันให้อัพเดตเวอร์ชันแผนผังการทำงาน
แต่ละขั้นตอนเหล่านี้อาจขัดขวางบางสิ่งในต้นไม้ทำงานของคุณ:
- การลบไฟล์นั้น "ปลอดภัย" หากรุ่นในแผนผังต้นไม้เหมือนกันกับเวอร์ชันที่
branch1
กำหนด เป็น "ไม่ปลอดภัย" หากคุณทำการเปลี่ยนแปลง
- การสร้างไฟล์ในแบบที่ปรากฏ
branch2
คือ "ปลอดภัย" หากไม่มีอยู่ในตอนนี้ 2 เป็น "ไม่ปลอดภัย" ถ้ามีอยู่ตอนนี้ แต่มีเนื้อหา "ผิด"
- และแน่นอนการเปลี่ยนรุ่นการทำงานต้นไม้ของไฟล์ที่มีรุ่นที่แตกต่างกันคือ "ปลอดภัย"
branch1
หากรุ่นทำงานต้นไม้มีความมุ่งมั่นแล้ว
การสร้างสาขาใหม่ ( git checkout -b newbranch
) ถือเป็น "ปลอดภัย" เสมอ : ไม่มีไฟล์ใดที่จะถูกเพิ่มลบหรือแก้ไขในแผนผังงานซึ่งเป็นส่วนหนึ่งของกระบวนการนี้และดัชนี / พื้นที่จัดเตรียมก็ไม่ถูกแตะต้อง (Caveat: ปลอดภัยเมื่อสร้างสาขาใหม่โดยไม่เปลี่ยนจุดเริ่มต้นของสาขาใหม่ แต่ถ้าคุณเพิ่มอาร์กิวเมนต์อื่นเช่นgit checkout -b newbranch different-start-point
อาจต้องเปลี่ยนสิ่งต่าง ๆ เพื่อย้ายไปdifferent-start-point
Git จะใช้กฎความปลอดภัยการชำระเงินตามปกติ .)
1สิ่งนี้ต้องการให้เรากำหนดความหมายของไฟล์ที่จะอยู่ในสาขาซึ่งจะต้องกำหนดคำว่าสาขาอย่างถูกต้อง (ดูเพิ่มเติมเราหมายถึงอะไรโดย "branch"? ) ที่นี่สิ่งที่ฉันหมายถึงจริงๆคือความมุ่งมั่นที่จะแก้ไขชื่อสาขา:ไฟล์ที่มีเส้นทางอยู่ในถ้าผลิตแฮช ไฟล์นั้นไม่อยู่ในหากคุณได้รับข้อความแสดงข้อผิดพลาดแทน การมีอยู่ของพา ธในดัชนีหรือแผนผังต้นไม้ของคุณนั้นไม่เกี่ยวข้องเมื่อตอบคำถามนี้โดยเฉพาะ ดังนั้นความลับที่นี่คือการตรวจสอบผลลัพธ์ของในแต่ละP
branch1
git rev-parse branch1:P
branch1
P
git rev-parse
branch-name:path
. สิ่งนี้อาจล้มเหลวเนื่องจากไฟล์ "ใน" ไม่เกินหนึ่งสาขาหรือให้รหัสแฮชสองรายการแก่เรา หากแฮช ID ทั้งสองเหมือนกันไฟล์จะเหมือนกันในทั้งสองสาขา ไม่จำเป็นต้องมีการเปลี่ยนแปลง หากรหัสแฮชต่างกันไฟล์จะแตกต่างกันในสองสาขาและจะต้องเปลี่ยนเป็นสลับสาขา
ความคิดที่สำคัญที่นี่คือไฟล์ที่กระทำจะถูกแช่แข็งตลอดไป แก้ไขไฟล์ที่คุณจะเห็นได้ชัดไม่ได้แช่แข็ง อย่างน้อยเราในตอนแรกมองเฉพาะที่ไม่ตรงกันระหว่างสองคอมมิชชัน น่าเสียดายที่เรา - หรือ Git - ต้องจัดการกับไฟล์ที่ไม่ได้อยู่ในความมุ่งมั่นที่คุณจะเปลี่ยนจากและอยู่ในความมุ่งมั่นที่คุณจะเปลี่ยนไป สิ่งนี้นำไปสู่ภาวะแทรกซ้อนที่เหลือเนื่องจากไฟล์ยังสามารถมีอยู่ในดัชนีและ / หรือในต้นไม้ทำงานโดยไม่จำเป็นต้องมีข้อผูกมัดสองข้อนี้ที่เรากำลังดำเนินการอยู่
2มันอาจถูกพิจารณาว่า "เรียงลำดับจากที่ปลอดภัย" ถ้ามันมีอยู่แล้วใน "เนื้อหาที่ถูกต้อง" ดังนั้น Git จึงไม่จำเป็นต้องสร้างมันขึ้นมาเลย ฉันจำได้ว่า Git บางรุ่นอนุญาตอย่างน้อย แต่การทดสอบในตอนนี้แสดงว่าถือว่าไม่ปลอดภัยใน Git 1.8.5.4 อาร์กิวเมนต์เดียวกันนี้จะนำไปใช้กับไฟล์ที่ถูกแก้ไขซึ่งมีการปรับให้ตรงกับสาขาที่ต้องเปลี่ยนไป อีกครั้ง 1.8.5.4 เพิ่งพูดว่า "จะถูกเขียนทับ" ดูจุดสิ้นสุดของบันทึกทางเทคนิคเช่นกัน: หน่วยความจำของฉันอาจมีข้อผิดพลาดเนื่องจากฉันไม่คิดว่ากฎแบบอ่านทรีมีการเปลี่ยนแปลงตั้งแต่ฉันเริ่มใช้ Git ในรุ่น 1.5.something
มันมีความสำคัญไม่ว่าการเปลี่ยนแปลงจะถูกจัดฉากหรือไม่เป็นฉาก?
ใช่ในบางวิธี โดยเฉพาะอย่างยิ่งคุณสามารถจัดลำดับการเปลี่ยนแปลงจากนั้น "ยกเลิกการแก้ไข" ไฟล์แผนผังต้นไม้ นี่คือไฟล์ในสองสาขาซึ่งแตกต่างกันในbranch1
และ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
ณ จุดนี้แฟ้มต้นไม้ทำงานinboth
ตรงกับหนึ่งในแม้ว่าเราจะอยู่บนbranch2
branch1
การเปลี่ยนแปลงนี้ไม่ได้จัดเตรียมไว้สำหรับคอมมิชชันซึ่งเป็นสิ่งที่git status --short
แสดงให้เห็นที่นี่:
$ git status --short
M inboth
space-then-M หมายถึง "แก้ไข แต่ไม่จัดเตรียม" (หรือมากกว่านั้นอย่างแม่นยำสำเนาแผนผังการทำงานแตกต่างจากสำเนาตามขั้นตอน / ดัชนี)
$ git checkout branch2
error: Your local changes ...
branch2
ตกลงตอนนี้ขอเวทีสำเนาการทำงานต้นไม้ซึ่งเรารู้อยู่แล้วว่ายังตรงกับสำเนาใน
$ git add inboth
$ git status --short
M inboth
$ git checkout branch2
Switched to branch 'branch2'
ที่นี่ทั้งแบบฉากและการทำงานที่ตรงกับสิ่งที่อยู่ในbranch2
ดังนั้นการชำระเงินได้รับอนุญาต
ลองอีกขั้นตอน:
$ git checkout branch1
Switched to branch 'branch1'
$ cat inboth
this file is in both branches
การเปลี่ยนแปลงที่ฉันทำหายไปจากพื้นที่การแสดงละครในขณะนี้ (เนื่องจากเช็คเอาต์เขียนผ่านพื้นที่การจัดเตรียม) นี่เป็นมุมเล็ก ๆ การเปลี่ยนแปลงไม่ได้หายไป แต่ความจริงที่ว่าผมมีฉากมันเป็นไปแล้ว
มาขั้นที่สามของไฟล์ที่แตกต่างจากการคัดลอกสาขาจากนั้นตั้งค่าสำเนาการทำงานให้ตรงกับรุ่นสาขาปัจจุบัน:
$ echo 'staged version different from all' > inboth
$ git add inboth
$ git show branch1:inboth > inboth
$ git status --short
MM inboth
ทั้งสองM
ที่นี่หมายถึง: ไฟล์ฉากแตกต่างจากHEAD
ไฟล์และไฟล์ต้นไม้ทำงานแตกต่างจากไฟล์ฉาก เวอร์ชันต้นไม้ทำงานไม่ตรงกับรุ่นbranch1
(aka HEAD
):
$ git diff HEAD
$
แต่git checkout
จะไม่อนุญาตให้ชำระเงิน:
$ git checkout branch2
error: Your local changes ...
มาตั้งbranch2
เวอร์ชั่นกันดีกว่า:
$ 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 ...
แม้ว่าสำเนาการทำงานปัจจุบันจะตรงกับที่อยู่ในbranch2
ไฟล์ staged จะไม่ทำเช่นนั้นการgit checkout
คัดลอกนั้นจะสูญหายและการคัดลอกgit checkout
จะถูกปฏิเสธ
หมายเหตุด้านเทคนิค - เฉพาะสำหรับ :-) ที่อยากรู้อยากเห็นอย่างบ้าคลั่ง
กลไกการดำเนินงานพื้นฐานสำหรับทั้งหมดนี้คือ Git ของดัชนี ดัชนีเรียกว่า "พื้นที่การแสดงละคร" คือที่ที่คุณสร้างต่อไปกระทำมันเริ่มออกมาตรงกับปัจจุบันกระทำคือสิ่งที่คุณได้ตรวจสอบออกในขณะนี้และจากนั้นทุกครั้งที่คุณgit add
ไฟล์คุณแทนที่รุ่นดัชนี กับสิ่งที่คุณมีในต้นไม้ทำงานของคุณ
จำไว้ว่าต้นไม้ทำงานเป็นที่ที่คุณทำงานกับไฟล์ของคุณ ที่นี่พวกเขามีรูปแบบปกติของพวกเขามากกว่ารูปแบบพิเศษที่มีประโยชน์เฉพาะกับ Git บางอย่างเช่นที่พวกเขาทำในการกระทำและในดัชนี ดังนั้นคุณแยกไฟล์จากการคอมมิชชันผ่านดัชนีและจากนั้นไปยังแผนผังต้นไม้ หลังจากเปลี่ยนคุณgit add
ไปยังดัชนี ในความเป็นจริงแล้วมีสามที่สำหรับแต่ละไฟล์: การกระทำปัจจุบัน, ดัชนี, และแผนผังต้นไม้
เมื่อคุณใช้git checkout branch2
งานสิ่งที่ Git ทำภายใต้ฝาปิดคือการเปรียบเทียบทิปการกระทำของbranch2
สิ่งที่อยู่ในการกระทำปัจจุบันและดัชนีตอนนี้ ไฟล์ใด ๆ ที่ตรงกับที่มีอยู่ตอนนี้ Git สามารถทิ้งไว้คนเดียว มันไม่ถูกแตะต้องเลย ไฟล์ใด ๆ ที่เหมือนกันทั้งสองกระทำ , Git สามารถยังปล่อยให้อยู่คนเดียวและเหล่านี้เป็นคนที่ช่วยให้คุณสามารถสลับสาขา
Git ส่วนใหญ่รวมถึงการคอมมิชชัน - สับเปลี่ยนนั้นค่อนข้างเร็วเนื่องจากดัชนีนี้ สิ่งที่เป็นจริงในดัชนีไม่ได้แต่ละไฟล์ตัวเอง แต่แต่ละไฟล์ของกัญชา สำเนาของไฟล์นั้นจะถูกเก็บไว้เป็นสิ่งที่ Git เรียกว่าวัตถุหยดในพื้นที่เก็บข้อมูล วิธีนี้คล้ายกับวิธีการจัดเก็บไฟล์ในคอมมิทเช่นกัน: คอมมิทไม่ได้มีไฟล์จริง ๆพวกมันแค่นำ Git ไปที่ ID แฮชของแต่ละไฟล์ ดังนั้น Git จึงสามารถเปรียบเทียบ hash IDs ซึ่งปัจจุบันเป็นสตริงที่มีความยาว 160 บิตเพื่อตัดสินใจว่า commits XและYมีไฟล์เดียวกันหรือไม่ จากนั้นสามารถเปรียบเทียบรหัสแฮชเหล่านั้นกับรหัสแฮชในดัชนีได้เช่นกัน
นี่คือสิ่งที่นำไปสู่กรณีมุมคี่บอลทั้งหมดข้างบน เรามีกระทำXและYที่ทั้งสองมีไฟล์และเรามีรายการดัชนีสำหรับpath/to/name.txt
path/to/name.txt
บางทีทั้งสามแฮชตรงกัน บางทีพวกเขาสองคนจับคู่และหนึ่งไม่ตรง บางทีทั้งสามอาจแตกต่างกัน และเราอาจมีanother/file.txt
เฉพาะในXหรือเฉพาะในYและอยู่หรือไม่อยู่ในดัชนีตอนนี้ แต่ละกรณีต่าง ๆ เหล่านี้จำเป็นต้องมีการพิจารณาแยกต่างหาก: Git จำเป็นต้องคัดลอกไฟล์ออกจากกระทำไปยังดัชนีหรือลบออกจากดัชนีเพื่อเปลี่ยนจากXเป็นYหรือไม่ ถ้าเป็นเช่นนั้นก็ยังต้องคัดลอกไฟล์ไปยังแผนผังต้นไม้หรือลบออกจากแผนผังต้นไม้ และถ้าว่าเป็นกรณีที่ดัชนีและการทำงานต้นไม้รุ่นที่มีการแข่งขันที่ดีกว่าอย่างน้อยหนึ่งในรุ่นที่มุ่งมั่น; มิฉะนั้น Git จะปิดบังข้อมูลบางอย่าง
(กฎที่สมบูรณ์สำหรับการทั้งหมดนี้ได้อธิบายไว้ในไม่ใช่git checkout
เอกสารที่คุณอาจคาดหวัง แต่เอกสารภายใต้หัวข้อ "สองต้นไม้ผสาน" .)git read-tree
git checkout -m
รวม worktree ของคุณและการเปลี่ยนแปลงดัชนีในการชำระเงินใหม่