จุดประสงค์ของ git-mv คืออะไร?


287

จากสิ่งที่ฉันเข้าใจ Git ไม่จำเป็นต้องติดตามการเปลี่ยนชื่อไฟล์ / ย้าย / คัดลอกไฟล์ดังนั้นจุดประสงค์ที่แท้จริงของgit mvคืออะไร หน้าคนไม่บรรยายเป็นพิเศษ ...

มันล้าสมัยหรือไม่ มันเป็นคำสั่งภายในที่ไม่ได้มีไว้สำหรับผู้ใช้ทั่วไปหรือไม่?

คำตอบ:


390
git mv oldname newname

เป็นเพียงชวเลขสำหรับ:

mv oldname newname
git add newname
git rm oldname

เช่นอัปเดตดัชนีสำหรับเส้นทางเก่าและใหม่โดยอัตโนมัติ


38
นอกจากนี้ยังมีตู้เซฟสองสามตัวที่สร้างขึ้น
ยาคุบบNarębski

6
ขอบคุณ @CharlesBailey - ไม่คอมไพล์พิจารณาไฟล์ newNameFile และ oldNameFile หรือไม่? ถ้าใช่จะเกิดอะไรขึ้นถ้าเราต้องการรวมพวกเขา สมมติว่าเราแยกโครงการมดในสาขา A และสร้างสาขา B จากนั้นลบล้างโครงการใน B ชื่อไฟล์เหมือนกัน แต่ใส่เส้นทางที่แตกต่างกันเมื่อโครงสร้างโครงการเปลี่ยนไป สมมติว่าทั้งสองสาขาเติบโตขึ้นในเวลาเดียวกัน เมื่อถึงจุดหนึ่งถ้าเราต้องการรวมโครงการจะรู้ได้อย่างไรว่าเป็นไฟล์เดียวกันเพิ่งเปลี่ยนชื่อเป็นเส้นทาง (ถ้า "git mv" == "git เพิ่ม + git rm")
โรส

2
@SergeyOrshanskiy หากการตรวจจับอัตโนมัติผิดพลาดmv oldname newname; git add newname; git rm oldnameก็จะผิดไปด้วยgit mv oldname newname(ดูคำตอบนี้ )
Ajedi32

5
โปรดทราบว่าgit mvจะแตกต่างจากmv oldname newname; git add newname; git rm oldnameในกรณีที่คุณทำการเปลี่ยนแปลงไฟล์ก่อนgit mvที่จะทำการเปลี่ยนแปลงการเปลี่ยนแปลงเหล่านั้นจะไม่ถูกจัดทำจนกว่าคุณจะgit addเป็นไฟล์ใหม่
Ajedi32

2
git mv กำลังทำสิ่งที่แตกต่างเนื่องจากจัดการการเปลี่ยนแปลงในกรณีชื่อไฟล์ (foo.txt เป็น Foo.txt) ในขณะที่คำสั่งเหล่านั้นทำงานแยกกันทำไม่ได้ (บน OSX)
greg.kindel

66

จากGitFaq อย่างเป็นทางการ :

Git มีคำสั่งเปลี่ยนชื่อgit mvแต่นั่นเป็นเพียงความสะดวกสบาย เอฟเฟ็กต์นั้นแยกไม่ออกจากการลบไฟล์และเพิ่มไฟล์อื่นที่มีชื่อแตกต่างกันและเนื้อหาเดียวกัน


8
คุณหลวมประวัติไฟล์หรือไม่ ฉันสันนิษฐานว่าการเปลี่ยนชื่อจะเก็บประวัติเก่าแก่ของไดเรกทอรีนั้นไว้
Will Hancock

17
ก็ใช่และไม่ใช่ อ่านลิงค์ GitFaq อย่างเป็นทางการด้านบนเกี่ยวกับการเปลี่ยนชื่อจากนั้นอ่าน Linus Torvalds อีเมลที่มีความยาวว่าทำไมเขาไม่ชอบความคิดของไฟล์ติดตามเครื่องมือ SCM: permalink.gmane.org/gmane.comp.version-control.git/ 217
Adam Nofsinger

3
@ WillHancock ฉันใช้ git เพิ่มขึ้นอีกเล็กน้อยและสามารถตอบคุณได้อย่างชัดเจนยิ่งขึ้น: ขึ้นอยู่กับไคลเอ็นต์ git และตัวเลือกของมันคุณจะสามารถติดตามไฟล์ที่ผ่านการเปลี่ยนชื่อได้หากไฟล์เปลี่ยนภายในเล็กน้อยพอที่คิดว่าเป็น เปลี่ยนชื่อ หากคุณเปลี่ยนไฟล์มากเกินไปและเปลี่ยนชื่อแม้ว่า git จะไม่ตรวจจับไฟล์ - ในแง่ที่ว่า "ไม่คุณอาจพิจารณาว่าเป็นไฟล์ที่แตกต่างอย่างสิ้นเชิง!"
Adam Nofsinger

7
@ AdamNofsinger ลิงก์นั้นเสียชีวิต นี่คือกระจก: web.archive.org/web/20150209075907/http://…
Carl Walsh

2
มีการอ้างอิงอย่างเป็นทางการ (เช่นมีค่าที่ดีกว่าคำถามที่พบบ่อย) ที่ระบุความแตกต่างระหว่างgit mvและวิธีการด้วยตนเองหรือไม่? git help mvมันไม่ได้เป็นที่เห็นได้ชัดจาก
tvo

40

Git กำลังพยายามเดาว่าคุณกำลังทำอะไรอยู่ มันกำลังพยายามทุกวิถีทางเพื่อรักษาประวัติศาสตร์ที่ไม่เสียหาย แน่นอนว่ามันไม่สมบูรณ์แบบ ดังนั้นgit mvช่วยให้คุณมีความชัดเจนด้วยความตั้งใจของคุณและเพื่อหลีกเลี่ยงข้อผิดพลาดบางอย่าง

ลองพิจารณาตัวอย่างนี้ เริ่มต้นด้วย repo ที่ว่างเปล่า

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
mv a c
mv b a
git status

ผลลัพธ์:

# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   a
#   deleted:    b
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   c
no changes added to commit (use "git add" and/or "git commit -a")

การตรวจสอบอัตโนมัติล้มเหลว :( หรือไม่

$ git add *
$ git commit -m "change"
$ git log c

commit 0c5425be1121c20cc45df04734398dfbac689c39
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

แล้ว

$ git log --follow c

Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

commit 50c2a4604a27be2a1f4b95399d5e0f96c3dbf70a
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:45 2013 -0400

    initial commit

ตอนนี้ลองแทน (อย่าลืมลบ.gitโฟลเดอร์เมื่อทำการทดลอง):

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
git mv a c
git status

จนถึงตอนนี้ดีมาก:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    a -> c


git mv b a
git status

ตอนนี้ไม่มีใครสมบูรณ์แบบ:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a
#   deleted:    b
#   new file:   c
#

จริงๆ? แต่แน่นอน...

git add *
git commit -m "change"
git log c
git log --follow c

... และผลลัพธ์จะเหมือนกับข้างบน: --followแสดงประวัติเต็มเท่านั้น


ตอนนี้ระวังการเปลี่ยนชื่อเนื่องจากตัวเลือกใดตัวหนึ่งยังสามารถสร้างเอฟเฟกต์แปลก ๆได้ ตัวอย่าง:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git commit -m "first move"
git mv b a
git commit -m "second move"

git log --follow a

commit 81b80f5690deec1864ebff294f875980216a059d
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:35:58 2013 -0400

    second move

commit f284fba9dc8455295b1abdaae9cc6ee941b66e7f
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:34:54 2013 -0400

    initial b

ตัดกันด้วย:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git mv b a
git commit -m "both moves at the same time"

git log --follow a

ผลลัพธ์:

commit 84bf29b01f32ea6b746857e0d8401654c4413ecd
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:37:13 2013 -0400

    both moves at the same time

commit ec0de3c5358758ffda462913f6e6294731400455
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:36:52 2013 -0400

    initial a

อัพ ... ตอนนี้ประวัติศาสตร์จะกลับไปเริ่มต้น aแทนค่าเริ่มต้น bซึ่งผิด ดังนั้นเมื่อเราทำการเคลื่อนย้ายครั้งละสองครั้ง Git ก็สับสนและไม่ได้ติดตามการเปลี่ยนแปลงอย่างถูกต้อง โดยวิธีการในการทดลองของฉันเหมือนที่เกิดขึ้นเมื่อฉันลบ / git mvไฟล์ที่สร้างขึ้นแทนการใช้ ดำเนินการต่อด้วยความระมัดระวัง คุณได้รับการเตือน ...


5
+1 สำหรับคำอธิบายโดยละเอียด ฉันกำลังมองหาปัญหาที่อาจเกิดขึ้นในบันทึกประวัติหากไฟล์ถูกย้ายในคอมไพล์คำตอบของคุณน่าสนใจจริงๆ ขอบคุณ! Btw คุณรู้ข้อผิดพลาดอื่น ๆ ที่เราควรหลีกเลี่ยงในขณะที่ย้ายไฟล์ในคอมไพล์หรือไม่? (หรือการอ้างอิงใด ๆ ที่คุณสามารถชี้ไปที่ .... ไม่ได้เป็น googling ที่โชคดีมากสำหรับมัน)
pabrantes

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

27

@Charles พูดว่าgit mvเป็นชวเลข

คำถามจริงที่นี่คือ "ระบบควบคุมเวอร์ชันอื่น ๆ (เช่นการโค่นล้มและ Perforce) ปฏิบัติต่อการเปลี่ยนชื่อไฟล์เป็นพิเศษทำไม Git ไม่?

Linus อธิบายที่http://permalink.gmane.org/gmane.comp.version-control.git/217พร้อมกับแทคพิเศษ:

โปรดหยุดอึ "track files" นี้ แทร็ค Git ว่าสิ่งที่สำคัญคือ "คอลเลกชันของแฟ้ม" ไม่มีอะไรเกี่ยวข้องและแม้แต่ คิดว่ามันมีความเกี่ยวข้องเพียง จำกัด มุมมองโลกของคุณ โปรดสังเกตว่าแนวคิดของ CVS "คำอธิบายประกอบ" มักจะจบลงด้วยการ จำกัด ว่าผู้คนใช้งานอย่างไร ฉันคิดว่ามันเป็นเรื่องไร้สาระโดยสิ้นเชิงและฉันได้อธิบายบางสิ่งบางอย่างที่ฉันคิดว่ามีประโยชน์มากกว่าล้านเท่าและมันก็หลุดออกมา อย่างแน่นอนเพราะฉันไม่ได้จำกัดความคิดของฉันในแบบจำลองที่ผิดของโลก


9

มีประโยชน์อื่นฉันgit mvไม่ได้กล่าวถึงข้างต้น

ตั้งแต่การค้นพบgit add -p(โหมดแก้ไขของ git add ดูที่http://git-scm.com/docs/git-add ) ฉันชอบที่จะใช้มันเพื่อตรวจสอบการเปลี่ยนแปลงเมื่อฉันเพิ่มลงในดัชนี ดังนั้นเวิร์กโฟลว์ของฉันกลายเป็น (1) ทำงานกับโค้ด (2) ตรวจสอบและเพิ่มลงในดัชนี (3) กระทำ

วิธีการที่ไม่git mvพอดี? หากการย้ายไฟล์โดยตรงจากการใช้git rmและgit addการเปลี่ยนแปลงทั้งหมดจะถูกเพิ่มเข้าไปในดัชนีและการใช้ git diff เพื่อดูการเปลี่ยนแปลงนั้นง่ายกว่า (ก่อนที่จะกระทำ) git mvอย่างไรก็ตามการใช้เพิ่มพา ธ ใหม่ไปยังดัชนี แต่ไม่ได้ทำการเปลี่ยนแปลงกับไฟล์ดังนั้นการอนุญาตgit diffและgit add -pการทำงานตามปกติ


5

มีกรณีเฉพาะที่git mvยังคงมีประโยชน์มากอยู่: เมื่อคุณต้องการเปลี่ยนปลอกของชื่อไฟล์ในระบบไฟล์แบบไม่คำนึงถึงขนาดตัวพิมพ์ ทั้ง APFS (mac) และ NTFS (windows) โดยค่าเริ่มต้นจะคำนึงถึงขนาดตัวพิมพ์

greg.kindel พูดถึงสิ่งนี้ในความคิดเห็นต่อคำตอบของ CB Bailey

สมมติว่าคุณกำลังทำงานกับ mac และมีไฟล์ที่Mytest.txtจัดการโดย git MyTest.txtคุณต้องการที่จะเปลี่ยนชื่อไฟล์เพื่อ

คุณสามารถลอง:

$ mv Mytest.txt MyTest.txt
overwrite MyTest.txt? (y/n [n]) y
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

โอ้ที่รัก Git ไม่ยอมรับว่ามีการเปลี่ยนแปลงกับไฟล์

คุณสามารถแก้ไขได้ด้วยการเปลี่ยนชื่อไฟล์อย่างสมบูรณ์จากนั้นเปลี่ยนชื่อกลับเป็น:

$ mv Mytest.txt temp.txt
$ git rm Mytest.txt
rm 'Mytest.txt'
$ mv temp.txt MyTest.txt
$ git add MyTest.txt 
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    Mytest.txt -> MyTest.txt

เย่!

หรือคุณอาจจะช่วยตัวเองทั้งหมดที่รำคาญโดยใช้git mv:

$ git mv Mytest.txt MyTest.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

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