จะแสดงบันทึกของไฟล์ที่เปลี่ยนชื่อด้วย git ได้อย่างไร?


132

ฉันค่อนข้างใหม่กับคอมไพล์ฉันเคยใช้ Subversion มาก่อน

ฉันสังเกตเห็นว่าส่วนหน้าของคอมไพล์กราฟิกและปลั๊กอิน IDE ส่วนใหญ่ดูเหมือนจะไม่สามารถแสดงประวัติของไฟล์ได้หากไฟล์ถูกเปลี่ยนชื่อ เมื่อฉันใช้

git log --follow

ในบรรทัดคำสั่งฉันเห็นบันทึกทั้งหมดในการเปลี่ยนชื่อ

จากข้อมูลของ Linus Torvaldsสวิตช์ --follow เป็นคำสั่ง "SVN noob" ผู้ใช้คอมไพล์ที่จริงจังไม่ได้ใช้:

--follow เป็นแฮ็คทั้งหมดที่มีขึ้นเพื่อตอบสนองผู้ใช้อดีต SVN ที่ไม่เคยรู้อะไรเกี่ยวกับสิ่งต่างๆเช่นความเป็นพ่อแม่หรือกราฟการแก้ไขที่ดีเลย

มันไม่ได้เป็นพื้นฐานโดยสิ้นเชิง แต่การใช้งาน "--follow" ในปัจจุบันเป็นสิ่งที่ช่วยประมวลผลล่วงหน้าอย่างรวดเร็วที่ยึดติดกับตรรกะการเดินการแก้ไขแทนที่จะเป็นอะไรที่สำคัญจริงๆ

มันได้รับการออกแบบให้เป็น "SVN noob" อย่างแท้จริงไม่ใช่เป็น "ฟังก์ชันคอมไพล์จริง" แนวคิดก็คือคุณจะหลีกหนีจากความคิด (เสีย) ที่คิดว่าการเปลี่ยนชื่อมีความสำคัญในภาพรวม

คำถามของฉัน : ผู้ใช้คอมไพล์ฮาร์ดคอร์ในหมู่คุณจะได้รับประวัติของไฟล์อย่างไรเมื่อถูกเปลี่ยนชื่อ? วิธีที่ 'จริง' ในการทำเช่นนี้คืออะไร?


17
@David Hall: git mv oldfile newfileไม่ทำให้การเปลี่ยนชื่อถูกบันทึกเลย - มันเหมือนกับการลบไฟล์หนึ่งไฟล์และเพิ่มอีกไฟล์ git ใช้เฉพาะการเปลี่ยนชื่อและคัดลอกจากสถานะของทรีในการคอมมิตแต่ละครั้งหลังจากความเป็นจริง
Mark Longair

20
@David Hall: หากคุณเปลี่ยนชื่อไฟล์ด้วยเครื่องมืออื่นนอก git (เช่น/bin/mv oldfile newfile) แต่ทำแล้วgit add newfile; git rm oldfileผลลัพธ์จะแยกไม่ออกจากgit mv oldfile newfileไฟล์.
Mark Longair

1
อุดมการณ์นี้จะแตกสลายหากคุณเคยย้ายไฟล์ไปยังที่เก็บใหม่ซึ่งในกรณีนี้การไม่สามารถย้ายประวัติทั้งหมดได้อาจเป็นปัญหาหลัก แม้ว่าจะมีข้อ จำกัด ว่าประวัติที่แท้จริงจะมาพร้อมกับไฟล์ในโครงการที่ซับซ้อนได้มากเพียงใด
Roman Starkov

1
หมายเหตุ: git log --followปรับปรุงเล็กน้อยด้วย git 2.9 (มิถุนายน 2016): ดูคำตอบของฉันด้านล่าง
VonC

1
ตั้งแต่เวอร์ชัน 2.15 คุณอาจต้องการทดลองใช้--color-movedเมื่อdiffใด
Michael

คำตอบ:


71

ฉันคิดว่าไดรฟ์ทั่วไปที่อยู่เบื้องหลัง Linus point คือ - และใช้สิ่งนี้ด้วยเกลือเล็กน้อย - ผู้ใช้ git ฮาร์ดคอร์ไม่เคยสนใจประวัติของ "ไฟล์" คุณใส่เนื้อหาในที่เก็บ git เนื่องจากเนื้อหาโดยรวมมีประวัติที่มีความหมาย

การเปลี่ยนชื่อไฟล์เป็นกรณีพิเศษเล็ก ๆ ของ "เนื้อหา" ที่ย้ายไปมาระหว่างเส้นทาง คุณอาจมีฟังก์ชันที่ย้ายไปมาระหว่างไฟล์ที่ผู้ใช้คอมไพล์อาจติดตามด้วย "pickaxe" ที่ใช้งานได้ (เช่นlog -S)

การเปลี่ยนแปลง "เส้นทาง" อื่น ๆ ได้แก่ การรวมและการแยกไฟล์ git ไม่สนใจว่าไฟล์ใดที่คุณคิดว่าจะเปลี่ยนชื่อและไฟล์ใดที่คุณคิดว่าจะคัดลอก (หรือเปลี่ยนชื่อและลบ) เพียงแค่ติดตามเนื้อหาทั้งหมดของต้นไม้ของคุณ

git กระตุ้นให้เกิดการคิดแบบ "ทั้งต้นไม้" โดยที่ระบบควบคุมเวอร์ชันต่างๆมีไฟล์เป็นศูนย์กลางมาก นี่คือสาเหตุที่ git อ้างถึง "เส้นทาง" บ่อยกว่าที่อ้างถึง "ชื่อไฟล์"


สวัสดี Charles ขอบคุณสำหรับคำตอบ ดูเหมือนว่าฉันจะใช้คอมไพล์แบบเดียวกับที่ฉันใช้ SVN แม้ว่าฉันจะเข้าใจว่าคอมไพล์นั้นแตกต่างจากระบบควบคุมเวอร์ชันอื่น ๆ มาก แต่แนวคิดหลายอย่างในคอมไพล์ก็ดูแปลกสำหรับฉัน ... ฉันน่าจะจบหนังสือคอมไพล์ที่เพิ่งซื้อมา
Mike

24
ประเด็นของ Linus คือ gui ที่ "เหมาะสม" จะสามารถติดตามโค้ดข้ามไฟล์ได้และเขาหวังว่าเราจะมีเครื่องมือดังกล่าวในตอนนี้ น่าเสียดายที่เรายังไม่มีความหรูหราและ - การติดตามก็ยังมีประโยชน์
Michael Parker

11
git ให้วิธีแก้ปัญหาอื่นนอกเหนือจาก--followนี้หรือไม่?
Griwes

13
ฉันขอยืนยันว่าการคิดแบบ "ต้นไม้ทั้งต้น" ได้รับการปรับปรุงโดย--followเป็นค่าเริ่มต้น สิ่งที่ฉันหมายถึงคือเมื่อฉันต้องการดูประวัติของรหัสภายในไฟล์ฉันมักจะไม่สนใจว่าไฟล์นั้นถูกเปลี่ยนชื่อหรือไม่ฉันแค่ต้องการดูประวัติของรหัสโดยไม่คำนึงถึงการเปลี่ยนชื่อ ดังนั้นในความคิดของฉันมันสมเหตุสมผลที่--followจะเป็นค่าเริ่มต้นเพราะฉันไม่สนใจไฟล์แต่ละไฟล์ --followช่วยให้ฉันเพิกเฉยต่อการเปลี่ยนชื่อไฟล์แต่ละไฟล์ซึ่งมักจะไม่สำคัญ
แก้ไข

2
ดังนั้น ... หากฉันตัดสินใจที่จะสนใจ "เนื้อหา" แทนไฟล์ฉันจะพิมพ์คอมมิตที่เกี่ยวข้องกับเนื้อหาที่อยู่ในไฟล์นี้ได้อย่างไร ฉันจะดีใจมากถ้าคอมไพล์ติดตามมันในไฟล์ต่างๆสำหรับฉันและรายงานบันทึกการเปลี่ยนแปลงทั้งหมด - ฉันไม่เห็นวิธีรับ
Ed Avis

36

ฉันมีปัญหาเดียวกันกับที่คุณกำลังเผชิญอยู่ แม้ว่าฉันจะไม่สามารถให้คำตอบคุณได้ แต่ฉันเชื่อว่าคุณสามารถอ่านอีเมลฉบับนี้ที่ Linus เขียนไว้ในปี 2548 ได้ซึ่งเป็นเรื่องที่เกี่ยวข้องมากและอาจให้คำแนะนำเกี่ยวกับวิธีจัดการกับปัญหา:

…ฉันอ้างว่า SCM ใด ๆ ที่พยายามติดตามการเปลี่ยนชื่อนั้นเสียโดยพื้นฐานเว้นแต่จะทำเช่นนั้นด้วยเหตุผลภายใน (เช่นเพื่อให้เดลต้ามีประสิทธิภาพ) เนื่องจากการเปลี่ยนชื่อไม่สำคัญ พวกเขาไม่ได้ช่วยให้คุณและพวกเขาจะไม่ได้สิ่งที่คุณกำลังสนใจในการอยู่แล้ว

สิ่งที่สำคัญคือการค้นหาว่า "สิ่งนี้มาจากไหน" และสถาปัตยกรรม git ทำได้ดีมาก - ดีกว่าสิ่งอื่นใด ...

ฉันพบว่ามันอ้างอิงโดยโพสต์บล็อกนี้ซึ่งอาจเป็นประโยชน์สำหรับคุณในการค้นหาวิธีแก้ปัญหาที่เป็นไปได้:

ในข้อความ Linus อธิบายว่าระบบติดตามเนื้อหาในอุดมคติจะช่วยให้คุณทราบได้อย่างไรว่าบล็อกของโค้ดเข้ามาอยู่ในรูปร่างปัจจุบันได้อย่างไร คุณจะเริ่มจากบล็อกโค้ดปัจจุบันในไฟล์ย้อนกลับไปในประวัติเพื่อค้นหาคอมมิตที่เปลี่ยนไฟล์ จากนั้นคุณตรวจสอบการเปลี่ยนแปลงของการคอมมิตเพื่อดูว่าบล็อกของโค้ดที่คุณสนใจมีการแก้ไขหรือไม่เนื่องจากการคอมมิตที่เปลี่ยนแปลงไฟล์อาจไม่สัมผัสกับบล็อกของโค้ดที่คุณสนใจ แต่จะมีเพียงส่วนอื่น ๆ ของ ไฟล์.

เมื่อคุณพบว่าก่อนที่จะคอมมิตบล็อกโค้ดไม่มีอยู่ในไฟล์ให้คุณตรวจสอบคอมมิตให้ลึกขึ้น คุณอาจพบว่านี่เป็นหนึ่งในสถานการณ์ที่เป็นไปได้มากมาย ได้แก่ :

  1. การกระทำนี้ได้แนะนำบล็อกของโค้ดอย่างแท้จริง ผู้เขียนคอมมิตเป็นผู้ประดิษฐ์ฟีเจอร์เจ๋ง ๆ ที่คุณตามล่าหาต้นตอของมัน (หรือผู้กระทำผิดที่แนะนำบั๊ก); หรือ
  2. ไม่มีบล็อกของโค้ดในไฟล์ แต่มีสำเนาที่เหมือนกันห้าชุดในไฟล์ต่างกันซึ่งทั้งหมดนี้หายไปหลังจากการคอมมิต ผู้สร้างรหัสที่ซ้ำกันที่ถูกปรับโครงสร้างโดยการแนะนำฟังก์ชันตัวช่วยเดียว หรือ
  3. (เป็นกรณีพิเศษ) ก่อนทำการคอมมิตไฟล์ที่มีบล็อกของโค้ดที่คุณสนใจนั้นไม่มีอยู่จริง แต่มีไฟล์อื่นที่มีเนื้อหาเกือบเหมือนกันอยู่และบล็อกของโค้ดที่คุณสนใจ พร้อมกับเนื้อหาอื่น ๆ ทั้งหมดในไฟล์ที่มีอยู่ในตอนนั้นมีอยู่ในไฟล์อื่นนั้น มันหายไปหลังจากการกระทำ ผู้เขียนคอมมิตเปลี่ยนชื่อไฟล์ในขณะที่ทำการแก้ไขเล็กน้อย

ใน git เครื่องมือติดตามเนื้อหาที่ดีที่สุดของ Linus ยังไม่มีอยู่ในรูปแบบอัตโนมัติทั้งหมด แต่ส่วนผสมที่สำคัญส่วนใหญ่จะมีอยู่แล้ว

โปรดแจ้งให้เราทราบเกี่ยวกับความคืบหน้าของคุณในเรื่องนี้


ขอบคุณสำหรับการโพสต์บทความเหล่านั้น จนกระทั่งฉันได้อ่านพวกเขาฉันจึงเข้าใจความคิดเกี่ยวกับประวัติเนื้อหาอย่างถ่องแท้! ฉันคิดเกี่ยวกับเรื่องนี้ในทางที่ผิด!
DavidG

อีเมลจาก Linus นั้นดีมากขอบคุณที่โพสต์ข้อความนี้
mik01aj

ความจริงแล้วGit v2.15 ช่วยเพิ่ม--color-movedการก้าวไปสู่ ​​"ระบบติดตามในอุดมคติ" ฉันกำลังเล่นกับมันเพื่อดูว่าแทร็กย้ายบรรทัดภายในไฟล์ แต่บังเอิญว่ามันติดตามเส้นที่เคลื่อนไหวในความแตกต่างทั้งหมด
Michael

2
Linus อธิบายสถานการณ์ที่ซับซ้อน แต่ที่นี่เรามีสถานการณ์ง่ายๆคือไฟล์เพิ่งเปลี่ยนชื่อ (หรือย้ายไปยังไดเรกทอรีอื่น) ดังนั้นควรมีวิธีง่ายๆ ฉันคิดว่าปัญหามาจากความจริงมากกว่าตรงกันข้ามกับการโค่นล้มผู้ใช้ไม่สามารถสั่ง Git ในเวลาคอมมิตว่าไฟล์มาจากไหนและ--followอาจผิดพลาดได้ (เช่นหาก 2 ไฟล์มีเนื้อหาเหมือนกันหรือมีการแก้ไข นอกเหนือจากการย้ายไฟล์)
vinc17

13

ฉันสังเกตเห็นว่าส่วนหน้าของคอมไพล์กราฟิกและปลั๊กอิน IDE ส่วนใหญ่ดูเหมือนจะไม่สามารถแสดงประวัติของไฟล์ได้หากไฟล์ถูกเปลี่ยนชื่อ

คุณยินดีที่ทราบว่าขณะนี้เครื่องมือ Git UI ยอดนิยมบางตัวรองรับสิ่งนี้แล้ว มีเครื่องมือ Git UI มากมายดังนั้นฉันจะไม่แสดงรายการทั้งหมด แต่ตัวอย่างเช่น:

  • SourceTree เมื่อดูบันทึกไฟล์จะมีช่องทำเครื่องหมาย "ติดตามไฟล์ที่เปลี่ยนชื่อ" ที่ด้านล่างซ้าย
  • TortoiseGit มีช่องทำเครื่องหมาย "follow renames" ในหน้าต่างบันทึกที่ด้านล่างซ้าย

ข้อมูลเพิ่มเติมเกี่ยวกับเครื่องมือ Git UI:


แหล่งที่มาใช้งานได้ดีเมื่อเปลี่ยนชื่อครั้งเดียวเมื่อเปลี่ยนชื่อสองครั้งจะไม่มีรายละเอียดการเปลี่ยนแปลงสำหรับคอมมิตก่อนเปลี่ยนชื่อ ฉันรายงานข้อผิดพลาดที่นี่: jira.atlassian.com/browse/SRCTREE-5715
Intel

gitk ใช้งานได้ดีแม้ว่าไฟล์จะถูกเปลี่ยนชื่อสองครั้งในประวัติ คำสั่งมีลักษณะเช่นนี้ "gitk --follow path / to / file"
Intel

6

หมายเหตุ: git 2.9 (มิถุนายน 2016) จะปรับปรุงลักษณะ "buggy" ของgit log --follow:

ดูกระทำ ca4e3ca (30 มีนาคม 2016) โดยSzeder Gábor (szeder )
(รวมโดยJunio C Hamano - gitster-ในการกระทำ 26effb8 , 13 เมษายน 2016)

diffcore: แก้ไขลำดับการวนซ้ำของไฟล์ที่เหมือนกันระหว่างการตรวจจับการเปลี่ยนชื่อ

หากสองเส้นทาง ' dir/A/file' และ ' dir/B/file' มีเนื้อหาเหมือนกันและไดเร็กทอรีหลักถูกเปลี่ยนชื่อเช่น ' git mv dir other-dir' จากนั้น diffcoreรายงานการเปลี่ยนชื่อที่แน่นอนดังต่อไปนี้:

renamed:    dir/B/file -> other-dir/A/file
renamed:    dir/A/file -> other-dir/B/file

(สังเกตการผกผันที่นี่: B/file -> A/fileและA/file -> B/file)

แม้ว่าในทางเทคนิคจะไม่ผิด แต่สิ่งนี้ก็สร้างความสับสนไม่เพียง แต่สำหรับผู้ใช้เท่านั้น แต่ยังรวมถึงคำสั่ง git ที่ทำการตัดสินใจโดยอาศัยข้อมูลการเปลี่ยนชื่อเช่น ' git log --follow other-dir/A/file' ติดตาม ' dir/B/file' ผ่านการเปลี่ยนชื่อ

ลักษณะการทำงานนี้เป็นผลข้างเคียงของการกระทำ v2.0.0-rc4 ~ 8 ^ 2 ~ 14 ( diffcore-rename.c: ลดความซับซ้อนในการค้นหาการเปลี่ยนชื่อที่แน่นอน 2013-11-14): แฮชแมปที่จัดเก็บแหล่งที่มาจะส่งคืนรายการจากที่เก็บข้อมูลเดียวกันนั่นคือแหล่งที่มาที่ตรงกับปลายทางปัจจุบัน ตามลำดับ LIFO
ดังนั้นการวนซ้ำจะตรวจสอบ ' other-dir/A/file' และ ' dir/B/file' ก่อนและเมื่อพบเนื้อหาและชื่อฐานที่เหมือนกันจะรายงานการเปลี่ยนชื่อที่แน่นอน


2

บน Linux ฉันได้ตรวจสอบแล้วว่า SmartGit และ GitEye สามารถติดตามการเปลี่ยนชื่อได้เมื่อติดตามประวัติของไฟล์ใดไฟล์หนึ่ง อย่างไรก็ตามไม่เหมือนกับ gitk และ GitEye SmartGit จะแสดงมุมมองไฟล์และมุมมองที่เก็บแยกกัน (ซึ่งมีโครงสร้างไดเร็กทอรี แต่ไม่ใช่รายการไฟล์ที่อยู่ภายใน)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.