หากต้องการขยายคำตอบของ Ben Jacksonซึ่งเป็นเรื่องปกติเรามาดูคำถามเดิมอย่างใกล้ชิด (ดูคำตอบของเขาสำหรับคำถามประเภทที่น่ารำคาญนี่คือข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่เกิดขึ้น)
ฉันเพิ่งเริ่มใช้การควบคุมเวอร์ชันและฉันเข้าใจว่า "การกระทำ" คือการสร้างข้อมูลสำรองเป็นหลักในขณะที่อัปเดตเวอร์ชัน "ปัจจุบัน" ใหม่ของสิ่งที่คุณกำลังดำเนินการอยู่
นี้ไม่ได้ค่อนข้างขวา การสำรองข้อมูลและและการควบคุมเวอร์ชันมีความเกี่ยวข้องกันอย่างแน่นอน - ขึ้นอยู่กับบางสิ่งที่เกี่ยวข้องกับความคิดเห็นในระดับหนึ่ง - แต่มีความแตกต่างบางประการหากมีเจตนาเท่านั้น: โดยทั่วไปการสำรองข้อมูลได้รับการออกแบบมาเพื่อการกู้คืนจากภัยพิบัติ (เครื่องล้มเหลวไฟทำลาย ทั้งอาคารรวมทั้งสื่อบันทึกข้อมูล ฯลฯ ) โดยทั่วไปการควบคุมเวอร์ชันได้รับการออกแบบมาเพื่อการโต้ตอบที่ละเอียดยิ่งขึ้นและนำเสนอคุณลักษณะที่การสำรองข้อมูลไม่มี โดยทั่วไปการสำรองข้อมูลจะถูกจัดเก็บไว้เป็นระยะเวลาหนึ่งจากนั้นจะถูกบันทึกว่า "เก่าเกินไป": การสำรองข้อมูลที่ใหม่กว่าเป็นสิ่งสำคัญ โดยปกติการควบคุมเวอร์ชันจะบันทึกเวอร์ชันที่มุ่งมั่นไว้ตลอดไป
สิ่งที่ฉันไม่เข้าใจคือการจัดฉากนั้นมาจากมุมมองที่ใช้งานได้จริง การจัดเตรียมสิ่งที่มีอยู่ในชื่อเท่านั้นหรือมีจุดประสงค์หรือไม่? เมื่อคุณกระทำมันก็จะกระทำทุกอย่างใช่ไหม?
ใช่และไม่. การออกแบบของ Git ที่นี่ค่อนข้างแปลก มีระบบควบคุมเวอร์ชันที่ไม่ต้องการขั้นตอนการจัดเตรียมแยกต่างหาก ตัวอย่างเช่น Mercurial ซึ่งเหมือนกับ Git ในแง่ของการใช้งานไม่จำเป็นต้องมีhg addขั้นตอนแยกต่างหากนอกเหนือจากขั้นตอนแรกที่แนะนำไฟล์ใหม่ทั้งหมด ด้วย Mercurial คุณใช้hgคำสั่งที่เลือกการกระทำบางอย่างจากนั้นคุณก็ทำงานของคุณจากนั้นคุณก็รันhg commitและเสร็จสิ้น ด้วย Git คุณใช้git checkout, 1แล้วคุณจะทำผลงานของคุณแล้วคุณทำงานแล้วgit add git commitทำไมต้องเพิ่มgit addขั้นตอน?
ความลับในที่นี้คือสิ่งที่ Git เรียกไม่ว่าจะเป็นดัชนีหรือพื้นที่การแสดงละครหรือบางครั้งแทบจะไม่ค่อยมีวันนี้เลยนั่นคือแคช ทั้งหมดนี้เป็นชื่อของสิ่งเดียวกัน
แก้ไข: ฉันคิดว่าฉันอาจสับสนกับคำศัพท์ ไฟล์ 'staged' เหมือนกับไฟล์ 'tracked' หรือไม่?
ไม่ แต่สิ่งเหล่านี้เกี่ยวข้องกัน ติดตามไฟล์เป็นหนึ่งที่มีอยู่ในดัชนีของ Git เพื่อให้เข้าใจดัชนีอย่างถูกต้องควรเริ่มต้นด้วยการทำความเข้าใจ
ตั้งแต่ Git เวอร์ชัน 2.23 คุณสามารถใช้git switchแทนgit checkoutไฟล์. สำหรับกรณีนี้คำสั่งสองคำสั่งนี้ทำสิ่งเดียวกันทุกประการ คำสั่งใหม่มีอยู่เนื่องจากgit checkoutมีสิ่งต่างๆมากเกินไป พวกเขาแยกออกเป็นสองคำสั่งแยกกันgit switchและgit restoreเพื่อให้ง่ายและปลอดภัยยิ่งขึ้นในการใช้ Git
กระทำ
ใน Git คอมมิตจะบันทึกสแนปชอตแบบเต็มของทุกไฟล์ที่ Git รู้ (ไฟล์ใดที่ Git รู้เราจะเห็นในหัวข้อถัดไป) สแน็ปช็อตเหล่านี้จะถูกเก็บไว้ในรูปแบบพิเศษอ่านอย่างเดียว Git เท่านั้นบีบอัดและไม่ซ้ำกันซึ่งโดยทั่วไปมีเพียง Git เท่านั้นที่สามารถอ่านได้ . (แต่ละคอมมิตมีหลายอย่างมากกว่าแค่สแนปชอตนี้ แต่นั่นคือทั้งหมดที่เราจะกล่าวถึงที่นี่
การลดความซ้ำซ้อนช่วยเรื่องพื้นที่: โดยปกติเราจะเปลี่ยนไฟล์เพียงไม่กี่ไฟล์แล้วทำการคอมมิตใหม่ ดังนั้นส่วนใหญ่ของไฟล์ในการกระทำส่วนใหญ่จะเป็นเช่นเดียวกับไฟล์ในก่อนหน้านี้กระทำ เพียงแค่ใช้ไฟล์เหล่านั้นซ้ำโดยตรง Git จะช่วยประหยัดพื้นที่ได้มาก: หากเราแตะเพียงไฟล์เดียวคอมมิตใหม่จะใช้พื้นที่สำหรับสำเนาใหม่เพียงหนึ่งชุด แม้ว่าจะมีการบีบอัด - บางครั้งก็บีบอัดมากแม้ว่าจะเกิดขึ้นจริงในภายหลังก็ตามเพื่อให้.gitไดเร็กทอรีมีขนาดเล็กกว่าไฟล์ที่มีอยู่เมื่อขยายออกเป็นไฟล์ปกติทั่วไป การยกเลิกการทำซ้ำนั้นปลอดภัยเนื่องจากไฟล์ที่คอมมิตจะถูกตรึงไว้ตลอดเวลา ไม่มีใครสามารถเปลี่ยนแปลงได้ดังนั้นจึงปลอดภัยสำหรับการขึ้นอยู่กับสำเนาของแต่ละคน
เนื่องจากไฟล์ที่จัดเก็บอยู่ในรูปแบบพิเศษเฉพาะ Git เท่านั้น Git จึงต้องขยายไฟล์แต่ละไฟล์เป็นสำเนาทั่วไปในชีวิตประจำวัน สำเนาธรรมดานี้ไม่ใช่สำเนาของ Gitแต่เป็นสำเนาของคุณเพื่อดำเนินการตามที่คุณต้องการ Git จะเขียนถึงสิ่งเหล่านี้เมื่อคุณบอกให้ทำเพื่อให้คุณมีสำเนาที่จะใช้งานได้ สำเนาที่ใช้งานได้เหล่านี้อยู่ในแผนผังการทำงานหรือผังงานของคุณ
สิ่งนี้หมายความว่าเมื่อคุณตรวจสอบการกระทำบางอย่างไฟล์แต่ละไฟล์จะมีสำเนาสองชุดโดยอัตโนมัติ:
Git มีแช่แข็งสำหรับทุกเวลาสำเนา Git-ified ในปัจจุบันกระทำ คุณไม่สามารถเปลี่ยนสำเนานี้ได้ (แม้ว่าคุณจะสามารถเลือกคอมมิตอื่นหรือสร้างคอมมิตใหม่ได้)
คุณมีสำเนารูปแบบปกติในแผนผังงานของคุณ คุณสามารถทำอะไรก็ได้ที่ต้องการโดยใช้คำสั่งใดก็ได้บนคอมพิวเตอร์ของคุณ
ระบบควบคุมเวอร์ชันอื่น ๆ (รวมถึง Mercurial ตามที่กล่าวไว้ข้างต้น) จะหยุดที่นี่พร้อมกับสำเนาสองชุดนี้ คุณเพียงแค่แก้ไขสำเนาแผนผังงานของคุณแล้วคอมมิต Git ... ไม่
ดัชนี
ระหว่างสองสำเนานี้ Git จะจัดเก็บสำเนาที่สาม2ของทุกไฟล์ สำเนาที่สามนี้อยู่ในรูปแบบแช่แข็งแต่ไม่เหมือนกับสำเนาที่ถูกตรึงในคอมมิตคุณสามารถเปลี่ยนได้ git addที่จะเปลี่ยนคุณใช้
git addคำสั่งวิธีการทำสำเนาดัชนีของแฟ้มที่ตรงกับสำเนาการทำงานต้นไม้ นั่นคือคุณกำลังบอก Git: แทนที่ Frozen-format, de-du-duplicated copy ที่อยู่ในดัชนีในตอนนี้โดยการบีบอัดสำเนาแผนผังงานที่อัปเดตของฉัน, ยกเลิกการทำซ้ำและเตรียมให้พร้อมที่จะถูกตรึงเป็นคอมมิตใหม่ หากคุณไม่ใช้git addดัชนีจะยังคงเก็บสำเนารูปแบบที่ตรึงไว้จากการคอมมิตปัจจุบัน
เมื่อคุณเรียกใช้git commit, Git แพคเกจขึ้นสิ่งที่อยู่ในดัชนีที่เหมาะสมแล้วที่จะใช้เป็นภาพรวมใหม่ เนื่องจากมันอยู่ในรูปแบบที่ถูกตรึงไว้แล้วและก่อนการลบซ้ำ Git จึงไม่ต้องทำงานพิเศษมากนัก
นอกจากนี้ยังอธิบายว่าไฟล์ที่ไม่ได้ติดตามคืออะไร ไฟล์ที่ไม่ได้ติดตามเป็นไฟล์ที่อยู่ในการทำงานต้นไม้ของคุณ แต่ไม่ได้อยู่ในดัชนี Git ของคุณในขณะนี้ ไม่สำคัญว่าไฟล์จะเกิดขึ้นในสถานะนี้อย่างไร บางทีคุณอาจคัดลอกจากที่อื่นในคอมพิวเตอร์ของคุณไปยังแผนผังงานของคุณ บางทีคุณอาจจะสร้างขึ้นใหม่ที่นี่ บางทีนั่นอาจจะเป็นสำเนาในดัชนี Git git rm --cachedแต่คุณเอาสำเนาที่มี ไม่ทางใดก็ทางหนึ่งมีสำเนาที่นี่ในโครงสร้างงานของคุณ แต่ไม่มีสำเนาในดัชนีของ Git หากคุณทำการคอมมิตใหม่ตอนนี้ไฟล์นั้นจะไม่อยู่ในคอมมิตใหม่
โปรดทราบว่าgit checkoutในตอนแรกจะเติมดัชนีของ Git จากคอมมิตที่คุณเช็คเอาต์ ดังนั้นดัชนีจะเริ่มจากการจับคู่คอมมิต Git ยังเติมใน work-tree ของคุณจากแหล่งเดียวกันนี้ ดังนั้นในตอนแรกทั้งสามการแข่งขัน เมื่อคุณเปลี่ยนไฟล์ในแผนผังงานของคุณและgit addตอนนี้ดัชนีและแผนผังงานของคุณตรงกัน จากนั้นคุณเรียกใช้git commitและ Git ทำการคอมมิตใหม่จากดัชนีและตอนนี้ทั้งสามจะจับคู่อีกครั้ง
เนื่องจาก Git สร้างคอมมิชชันใหม่จากดัชนีเราจึงสามารถวางสิ่งนี้ได้: ดัชนีของ Git ถือการกระทำต่อไปที่คุณวางแผนจะทำ สิ่งนี้จะไม่สนใจบทบาทที่ขยายที่ดัชนีของ Git เกิดขึ้นในระหว่างการผสานที่ขัดแย้งกัน แต่ตอนนี้เราอยากจะเพิกเฉยต่อไป :-)
นั่นคือทั้งหมดที่มี - แต่ก็ยังค่อนข้างซับซ้อน! เป็นเรื่องยุ่งยากเป็นพิเศษเพราะไม่มีวิธีง่ายๆในการดูว่าอะไรอยู่ในดัชนีของ Git 3 แต่มีเป็นคำสั่ง Git git statusที่จะบอกคุณสิ่งที่เกิดขึ้นในทางที่เป็นประโยชน์สวยและคำสั่งที่
2 ในทางเทคนิคนี่ไม่ใช่สำเนาเลย แต่เป็นการอ้างอิงถึงไฟล์ Git-ified, pre-de-duplicated และทุกอย่าง ที่นี่ยังมีอีกหลายอย่างเช่นโหมดชื่อไฟล์หมายเลขการจัดเตรียมและข้อมูลแคชบางอย่างเพื่อให้ Git ทำงานได้อย่างรวดเร็ว แต่ถ้าคุณไม่ได้ทำงานกับคำสั่งระดับต่ำของ Git - git ls-files --stageและgit update-indexโดยเฉพาะอย่างยิ่งคุณสามารถคิดว่ามันเป็นสำเนา
3git ls-files --stageคำสั่งจะแสดงชื่อและหมายเลขการแสดงละครของไฟล์ทุกไฟล์ในดัชนี Git แต่มักจะไม่ได้อยู่แล้วที่มีประโยชน์มาก
git status
git statusคำสั่งใช้งานได้จริงโดยการทำงานสองแยกgit diffคำสั่งสำหรับคุณ (และทำบางสิ่งที่มีประโยชน์อื่น ๆ เช่นบอกคุณซึ่งสาขาที่คุณกำลัง)
ข้อแรกgit diffเปรียบเทียบการกระทำปัจจุบันซึ่งจำไว้ว่าถูกแช่แข็งตลอดเวลากับสิ่งที่อยู่ในดัชนีของ Git สำหรับไฟล์ที่เหมือนกัน Git จะไม่พูดอะไรเลย สำหรับไฟล์ที่มีความแตกต่างกัน , Git จะบอกคุณว่าไฟล์นี้เป็นฉากสำหรับการกระทำ ซึ่งรวมถึงการใหม่ทั้งหมดไฟล์ถ้ากระทำไม่ได้มีsub.pyอยู่ในนั้น แต่ดัชนีไม่ได้sub.pyอยู่ในนั้นแล้วไฟล์นี้จะมีการเพิ่มและไฟล์ที่ถูกลบใด ๆ ที่มี (และ) ในการกระทำ แต่ไม่ได้อยู่ใน ดัชนีอีกต่อไป ( git rmบางที)
ขั้นตอนที่สองgit diffเปรียบเทียบไฟล์ทั้งหมดในดัชนีของ Git กับไฟล์ในโครงสร้างงานของคุณ สำหรับไฟล์ที่เหมือนกัน Git บอกว่าไม่มีอะไรเลย สำหรับไฟล์ที่มีความแตกต่างกัน , Git จะบอกคุณว่าไฟล์นี้จะไม่ได้จัดฉากสำหรับการกระทำ ซึ่งแตกต่างจาก diff แรกรายการนี้โดยเฉพาะไม่รวมถึงไฟล์ที่มีใหม่ทั้งหมด: หากไฟล์ที่untrackedมีอยู่ในการทำงานของต้นไม้ของคุณ แต่ไม่ได้อยู่ในดัชนี Git ของ Git เพียงแค่เพิ่มในรายการของไฟล์ที่ไม่ได้ติดตาม 4
ในตอนท้ายเมื่อสะสมไฟล์ที่ไม่ได้ติดตามเหล่านี้ไว้ในรายการgit statusจะประกาศชื่อไฟล์เหล่านั้นด้วย แต่มีข้อยกเว้นพิเศษคือหากชื่อไฟล์อยู่ใน.gitignoreไฟล์จะเป็นการระงับรายการสุดท้ายนี้ โปรดทราบว่าการแสดงรายการไฟล์ที่ติดตามซึ่งเป็นไฟล์ที่อยู่ในดัชนีของ Git .gitignoreจะไม่มีผลใด ๆ ที่นี่ไฟล์อยู่ในดัชนีดังนั้นจึงได้รับการเปรียบเทียบและได้รับความมุ่งมั่นแม้ว่าจะอยู่ในรายการ.gitignore. ไฟล์ละเว้นจะระงับการร้องเรียน "ไฟล์ที่ไม่ได้ติดตาม" เท่านั้น 5
4เมื่อใช้เวอร์ชันสั้นของgit status- git status -s- ไฟล์ที่ไม่ได้ติดตามจะไม่ถูกแยกออก แต่หลักการก็เหมือนกัน การสะสมไฟล์เช่นนี้ยังช่วยให้สามารถgit statusสรุปชื่อไฟล์ที่ไม่ได้ติดตามจำนวนมากได้ด้วยการพิมพ์ชื่อไดเรกทอรีในบางครั้ง ที่จะได้รับรายการเต็มรูปแบบให้ใช้หรือgit status -uallgit status -u
5 การแสดงรายการไฟล์ยังทำให้ en-masse เพิ่มการทำงานของไฟล์จำนวนมากเช่นgit add .หรือgit add *ข้ามไฟล์ที่ไม่ได้ติดตาม ส่วนนี้จะซับซ้อนขึ้นเล็กน้อยเนื่องจากคุณสามารถใช้git add --forceเพื่อเพิ่มไฟล์ที่ปกติจะถูกข้ามไป มีกรณีพิเศษอื่น ๆ ตามปกติซึ่งทั้งหมดนี้รวมเข้าด้วยกัน: ไฟล์.gitignoreอาจถูกเรียกอย่างถูกต้องมากกว่า.git-do-not-complain-about-these-untracked-files-and-do-not-auto-add-themหรือมีบางอย่างที่เทอะทะพอ ๆ กัน แต่ที่ไร้สาระเกินไปดังนั้น.gitignoreมันเป็น
git add -u, git commit -aฯลฯ
มีทางลัดที่สะดวกมากมายที่ควรทราบเกี่ยวกับที่นี่:
git add .จะเพิ่มทุกไฟล์ที่อัปเดตในไดเรกทอรีปัจจุบันและใด ๆ ไดเรกทอรีย่อย ในกรณีนี้.gitignoreดังนั้นหากไฟล์ที่ไม่ได้ติดตามอยู่ในขณะนี้ไม่ได้รับการร้องเรียนgit statusไฟล์นั้นจะไม่ถูกเพิ่มอัตโนมัติ
git add -uอัตโนมัติจะเพิ่มทุกไฟล์ที่อัปเดตได้ทุกที่ในการทำงานต้นไม้ของคุณ 6 สิ่งนี้มีผลกับไฟล์ที่ติดตามเท่านั้น โปรดทราบว่าหากคุณลบสำเนาแผนผังงานออกจะเป็นการลบสำเนาดัชนีด้วย ( git addซึ่งเป็นส่วนหนึ่งของการทำให้ดัชนีตรงกับสิ่งที่ต้นไม้งาน )
git add -Aก็เหมือนกับการเรียกใช้git add .จากระดับบนสุดของโครงสร้างงานของคุณ (แต่ดูเชิงอรรถ 6)
นอกจากนี้คุณสามารถเรียกใช้git commit -aซึ่งเทียบเท่าประมาณ7กับการทำงานแล้วgit add -u git commitนั่นคือทำให้คุณมีพฤติกรรมแบบเดียวกับที่สะดวกใน Mercurial
โดยทั่วไปฉันแนะนำให้ใช้git commit -aรูปแบบนี้: ฉันพบว่าควรใช้git statusบ่อยๆดูผลลัพธ์อย่างใกล้ชิดและหากสถานะไม่เป็นไปตามที่คุณคาดหวังให้หาสาเหตุ การใช้git commit -aมันง่ายเกินไปที่จะแก้ไขไฟล์โดยไม่ได้ตั้งใจและทำการเปลี่ยนแปลงที่คุณไม่ได้ตั้งใจจะกระทำ แต่ส่วนใหญ่เป็นเรื่องของรสนิยม / ความคิดเห็น
6หากเวอร์ชัน Git ของคุณมาก่อน Git 2.0 โปรดระวังที่นี่: git add -uใช้ได้เฉพาะกับไดเรกทอรีปัจจุบันและไดเรกทอรีย่อยดังนั้นคุณต้องปีนขึ้นไปที่ระดับบนสุดของแผนผังงานก่อน git add -Aตัวเลือกที่มีปัญหาที่คล้ายกัน
7ฉันบอกว่าเทียบเท่าโดยประมาณเพราะgit commit -aใช้งานได้จริงโดยการสร้างดัชนีพิเศษและใช้ดัชนีอื่นเพื่อทำการคอมมิต หากกระทำการงานgit add -u && git commitคุณจะได้รับผลเช่นเดียวกับการทำ หากคอมมิตไม่ได้ผล - ถ้าคุณทำให้ Git ข้ามคอมมิตในหลาย ๆ วิธีที่คุณสามารถทำได้หลังจากนั้นจะไม่มีการแก้ไขไฟล์ใดgit addๆ เนื่องจาก Git จะพ่นดัชนีพิเศษชั่วคราวออกมาและกลับไปใช้ดัชนีหลัก .
มีภาวะแทรกซ้อนเพิ่มเติมที่เข้ามาหากคุณใช้git commit --onlyที่นี่ ในกรณีนี้ Git จะสร้างดัชนีที่สามและสิ่งต่าง ๆ จะยุ่งยากมากโดยเฉพาะอย่างยิ่งถ้าคุณใช้ pre -mit hooks นี่เป็นอีกเหตุผลหนึ่งที่ต้องใช้git addการดำเนินการแยกกัน