การจัดการการเปลี่ยนชื่อไฟล์ในคอมไพล์


440

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

git mvแต่ทำเพียงแค่คืนนี้ฉันจบลงด้วยการย้อนกลับไป

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#

เปลี่ยนชื่อสไตล์ชีทของฉันใน Finder จากiphone.cssเป็นmobile.css

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    css/iphone.css
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   css/mobile.css

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

> $ git reset HEAD .
Unstaged changes after reset:
M   css/iphone.css
M   index.html

กลับไปที่ที่ฉันเริ่ม

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   index.html
#

ให้ใช้git mvแทน

> $ git mv css/iphone.css css/mobile.css
> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    css/iphone.css -> css/mobile.css
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   index.html
#

ดูเหมือนว่าเราจะดี เหตุใด git จึงไม่รู้จักการเปลี่ยนชื่อในครั้งแรกเมื่อฉันใช้ Finder


29
Git ติดตามเนื้อหาไม่ใช่ไฟล์ดังนั้นจึงไม่สำคัญว่าคุณจะจัดทำดัชนีของคุณอย่างไรให้อยู่ในสถานะที่เหมาะสมadd+rmหรือmvสร้างผลลัพธ์ที่เหมือนกัน Git จะใช้การตรวจจับการเปลี่ยนชื่อ / คัดลอกเพื่อแจ้งให้คุณทราบว่าเป็นการเปลี่ยนชื่อ แหล่งที่มาที่คุณเสนอมานั้นไม่ถูกต้องเช่นกัน ไม่สำคัญว่าคุณจะแก้ไข + เปลี่ยนชื่อในคอมมิทเดียวกันหรือไม่ เมื่อคุณทำทั้งการแก้ไขและการเปลี่ยนชื่อการตรวจจับการเปลี่ยนชื่อจะเห็นว่าเป็นการเปลี่ยนชื่อ + การดัดแปลงหรือหากการดัดแปลงเป็นการเขียนใหม่ทั้งหมดการแสดงนั้นจะแสดงเป็นการเพิ่มและลบ แต่ก็ไม่สำคัญว่าคุณจะทำอย่างไร มัน.
Cascabel

6
หากสิ่งนี้เป็นจริงทำไมมันไม่ตรวจพบด้วยการเปลี่ยนชื่อโดยใช้ Finder?
เกร็ก K

26
git mv old newอัพเดตดัชนีโดยอัตโนมัติ เมื่อคุณเปลี่ยนชื่อนอก Git คุณจะต้องทำgit add newและgit rm oldทำขั้นตอนการเปลี่ยนแปลงดัชนี เมื่อคุณทำเช่นนี้git statusจะทำงานตามที่คุณคาดหวัง
Chris Johnsen

4
ฉันเพิ่งย้ายไฟล์จำนวนมากไปเป็นpublic_htmldir ที่ถูกติดตามในคอมไพล์ เมื่อดำเนินการgit add .แล้วและgit commitยังคงแสดงไฟล์ 'ลบ' git statusหลายรายการ ฉันดำเนินการgit commit -aแล้วและการลบถูกผูกมัด แต่ตอนนี้ฉันไม่มีประวัติในไฟล์ที่ใช้งานอยู่ในpublic_htmlตอนนี้ กระบวนการทำงานนี้ไม่ราบรื่นอย่างที่ฉันต้องการ
เกร็ก K

คำตอบ:


352

สำหรับหน้าคู่มือ บอกว่าgit mv

ดัชนีจะได้รับการอัปเดตหลังจากเสร็จสมบูรณ์ […]

ดังนั้นในตอนแรกคุณต้องอัปเดตดัชนีด้วยตัวคุณเอง (โดยใช้git add mobile.css) อย่างไรก็ตาม
git status จะยังคงแสดงสองไฟล์ที่แตกต่างกัน

$ git status
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       new file:   mobile.css
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    iphone.css
#

คุณจะได้รับการส่งออกที่แตกต่างกันโดยการทำงาน git commit --dry-run -aซึ่งผลลัพธ์ในสิ่งที่คุณคาดหวัง:

Tanascius@H181 /d/temp/blo (master)
$ git commit --dry-run -a
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       renamed:    iphone.css -> mobile.css
#

ฉันไม่สามารถบอกคุณได้ว่าทำไมเราถึงเห็นความแตกต่างเหล่านี้ระหว่างgit statusและ
git commit --dry-run -aแต่นี่คือคำใบ้จาก Linus :

คอมไพล์จริงๆไม่สนใจเกี่ยวกับ "การตรวจจับการเปลี่ยนชื่อ" ทั้งหมดภายในและความมุ่งมั่นใด ๆ ที่คุณได้ทำกับการเปลี่ยนชื่อเป็นอิสระโดยสิ้นเชิงจากฮิวริสติกที่เราใช้เพื่อแสดงการเปลี่ยนชื่อ

A dry-runใช้กลไกการเปลี่ยนชื่อจริงในขณะที่ git statusอาจไม่


1
git add mobile.cssคุณไม่ได้กล่าวถึงขั้นตอนที่คุณทำ ไม่ว่าgit status -aจะมีเพียงแค่ 'เห็น' กำจัดของการติดตามก่อนหน้านี้iphone.cssไฟล์ แต่จะไม่ได้สัมผัสใหม่ที่ไม่ได้ติดตามmobile.cssไฟล์ นอกจากนี้ยังใช้git status -aไม่ได้กับ Git 1.7.0 และใหม่กว่า “ "สถานะ git" ไม่ใช่ "git commit --dry-run" อีกต่อไป " ในkernel.org/pub/software/scm/git/docs/RelNotes-1.7.0.txt ใช้git commit --dry-run -aถ้าคุณต้องการฟังก์ชั่นนี้ ดังที่คนอื่น ๆ บอกไว้เพียงแค่อัปเดตดัชนีและgit statusจะทำงานตามที่ OP คาดไว้
Chris Johnsen

3
หากคุณทำตามปกติgit commitมันจะไม่ส่งไฟล์ที่เปลี่ยนชื่อและแผนผังการทำงานยังคงเหมือนเดิม git commit -aเอาชนะรูปแบบเวิร์กโฟลว์ / ความคิดของ git ได้มากทีเดียว - การเปลี่ยนแปลงทุกอย่างเกิดขึ้น จะทำอย่างไรถ้าคุณต้องการเปลี่ยนชื่อไฟล์ แต่กระทำการเปลี่ยนแปลงindex.htmlในกระทำอื่น?
knittl

@Chris: ใช่แน่นอนฉันเพิ่มmobile.cssซึ่งฉันควรจะกล่าวถึง แต่นั่นเป็นจุดของคำตอบของฉัน: หน้าคนบอกว่าเมื่อคุณใช้the index is updated git-mvขอบคุณสำหรับความstatus -aกระจ่างฉันใช้ git 1.6.4
tanascius

4
คำตอบที่ดี! ฉันต่อสู้กับกำแพงพยายามคิดออกว่าทำไมgit statusไม่ตรวจจับการเปลี่ยนชื่อ การทำงานgit commit -a --dry-runหลังจากเพิ่มไฟล์ "ใหม่" ของฉันแสดงการเปลี่ยนชื่อและในที่สุดก็ทำให้ฉันมั่นใจที่จะกระทำ!
stephen.hanson

1
ในคอมไพล์ 1.9.1 ตอนนี้พฤติกรรมเช่นgit status git commit
Jacques René Mesrine

77

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

ข้อแตกต่างระหว่างmv old newและgit mv old newคือ git mv ยังเพิ่มไฟล์ในดัชนี

mv old newจากนั้นก็git add -Aจะได้ผลเช่นกัน

โปรดทราบว่าคุณไม่สามารถใช้งานได้เพียงgit add .เพราะไม่ได้เพิ่มการลบลงในดัชนี

ดูความแตกต่างระหว่าง "git เพิ่ม -A" และ "git เพิ่ม"


3
ขอบคุณสำหรับgit add -Aลิงค์มีประโยชน์มากเพราะฉันกำลังมองหาทางลัด!
PhiLho

5
โปรดทราบว่าด้วย git 2 git add . จะเพิ่มการลบออกไปยังดัชนี
Nick McCurdy

19

สิ่งที่ดีที่สุดคือลองด้วยตัวคุณเอง

mkdir test
cd test
git init
touch aaa.txt
git add .
git commit -a -m "New file"
mv aaa.txt bbb.txt
git add .
git status
git commit --dry-run -a

ตอนนี้สถานะ git และ git commit --dry-run -a แสดงผลลัพธ์ที่แตกต่างกันสองรายการซึ่งสถานะ git แสดง bbb.txt เมื่อไฟล์ใหม่ / aaa.txt ถูกลบและคำสั่ง --dry-run แสดงการเปลี่ยนชื่อจริง

~/test$ git status

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   bbb.txt
#
# 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)
#
#   deleted:    aaa.txt
#


/test$ git commit --dry-run -a

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

ตอนนี้ไปข้างหน้าและทำการเช็คอิน

git commit -a -m "Rename"

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

คุณธรรมของเรื่องราว: หากคุณไม่แน่ใจว่าไฟล์ของคุณถูกเปลี่ยนชื่อหรือไม่ให้ออก "git commit --dry-run -a" หากมันแสดงว่าไฟล์นั้นถูกเปลี่ยนชื่อคุณก็พร้อมที่จะไป


3
สำหรับสิ่งที่สำคัญสำหรับ Git, ทั้งสองมีความถูกต้อง อย่างหลังกำลังใกล้ชิดกับคุณมากขึ้นในขณะที่ผู้ใช้อาจมองเห็นมัน ความแตกต่างที่แท้จริงระหว่างการเปลี่ยนชื่อและลบ + สร้างนั้นอยู่ที่ระดับ OS / ระบบแฟ้ม (เช่น inode # กับ vs. inode # ใหม่) ซึ่ง Git ไม่สนใจมากนัก
Alois Mahdal

17

สำหรับ git 1.7.x คำสั่งต่อไปนี้ใช้งานได้สำหรับฉัน:

git mv css/iphone.css css/mobile.css
git commit -m 'Rename folder.' 

ไม่จำเป็นต้องมีการเพิ่ม git เนื่องจากไฟล์ต้นฉบับ (เช่น css / mobile.css) มีอยู่ในไฟล์ที่กำหนดแล้ว


6
นี้. คำตอบอื่น ๆ ทั้งหมดมีความซับซ้อนและไร้เหตุผล สิ่งนี้จะรักษาประวัติไฟล์ระหว่างการคอมมิทเพื่อให้การผสานก่อน / หลังการเปลี่ยนชื่อไฟล์ไม่เสียหาย
Phlucious

10

คุณต้องไปgit add css/mobile.cssที่ไฟล์ใหม่และgit rm css/iphone.cssดังนั้นคอมไพล์จึงรู้ จากนั้นจะแสดงผลลัพธ์เดียวกันในgit status

คุณสามารถดูได้อย่างชัดเจนในผลลัพธ์สถานะ (ชื่อใหม่ของไฟล์):

# Untracked files:
#   (use "git add <file>..." to include in what will be committed)

และ (ชื่อเก่า):

# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)

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


ฉันไม่คิดว่าฉันต้องทำgit rm css/iphone.cssเพราะฉันคิดว่านี่จะลบประวัติที่มีอยู่ บางทีฉันอาจเข้าใจผิดเกี่ยวกับขั้นตอนการทำงานในคอมไพล์
เกร็ก K

4
@Greg K: git rmจะไม่ลบประวัติ มันจะลบรายการจากดัชนีเพื่อให้การกระทำครั้งต่อไปจะไม่มีรายการ อย่างไรก็ตามมันจะยังคงอยู่ในการกระทำของบรรพบุรุษ สิ่งที่คุณอาจจะสับสนเกี่ยวกับที่ (ตัวอย่าง) จะหยุดที่จุดที่คุณมุ่งมั่นgit log -- new git mv old newหากคุณต้องการที่จะทำตามการเปลี่ยนชื่อ, git log --follow -- newการใช้งาน
Chris Johnsen

9

ลองคิดถึงไฟล์ของคุณจากมุมมอง git

โปรดทราบว่าคอมไพล์ไม่ได้ติดตามข้อมูลเมตาเกี่ยวกับไฟล์ของคุณ

พื้นที่เก็บข้อมูลของคุณมี (อื่น ๆ )

$ cd repo
$ ls
...
iphone.css
...

และอยู่ภายใต้การควบคุมคอมไพล์:

$ git ls-files --error-unmatch iphone.css &>/dev/null && echo file is tracked
file is tracked

ทดสอบสิ่งนี้ด้วย:

$ touch newfile
$ git ls-files --error-unmatch newfile &>/dev/null && echo file is tracked
(no output, it is not tracked)
$ rm newfile

เมื่อคุณทำ

$ mv iphone.css mobile.css

จากมุมมองของคอมไพล์

  • ไม่มีiphone.css (ถูกลบ -git เตือนเกี่ยวกับเรื่องนั้น -)
  • มีไฟล์ใหม่mobile.css
  • ไฟล์เหล่านั้นไม่เกี่ยวข้องทั้งหมด

ดังนั้น git ให้คำแนะนำเกี่ยวกับไฟล์ที่มีอยู่แล้ว ( iphone.css ) และไฟล์ใหม่ที่ตรวจพบ ( mobile.css ) แต่เฉพาะเมื่อไฟล์อยู่ในดัชนีหรือHEIT git เริ่มตรวจสอบเนื้อหาของพวกเขา

ในขณะนี้การลบ " iphone.css " และmobile.css ไม่อยู่ในดัชนี

เพิ่มการลบ iphone.css ให้กับดัชนี

$ git rm iphone.css

git บอกคุณอย่างชัดเจนว่าเกิดอะไรขึ้น: ( iphone.cssถูกลบไม่มีอะไรเกิดขึ้นอีกแล้ว)

จากนั้นเพิ่มไฟล์mobile.cssใหม่

$ git add mobile.css

เวลานี้ทั้งการลบและไฟล์ใหม่อยู่ในดัชนี ตอนนี้ git ตรวจพบบริบทเหมือนกันและแสดงเป็นเปลี่ยนชื่อ ในความเป็นจริงหากไฟล์นั้นมีความคล้ายคลึงกัน 50% จะตรวจพบว่าเป็นการเปลี่ยนชื่อซึ่งจะช่วยให้คุณเปลี่ยนmobile.cssเล็กน้อยในขณะที่ยังคงการดำเนินการเป็นเปลี่ยนชื่อ

ดูสิ่งนี้ทำซ้ำgit diffได้ --cachedตอนนี้ว่าไฟล์ของคุณอยู่ในดัชนีคุณต้องใช้ แก้ไขmobile.cssเล็กน้อยเพิ่มเข้าไปในดัชนีและดูความแตกต่างระหว่าง:

$ git diff --cached 

และ

$ git diff --cached -M

-Mคือ "การตรวจสอบการเปลี่ยนชื่อ" git diffตัวเลือกสำหรับการ -Mย่อมาจาก-M50%(ความคล้ายคลึงกัน 50% หรือมากกว่านั้นจะทำให้ git แสดงเป็นชื่อเปลี่ยนได้) แต่คุณสามารถลดสิ่งนี้เป็น-M20%(20%) หากคุณแก้ไข mobile.css มาก


8

ขั้นที่ 1: เปลี่ยนชื่อไฟล์จาก oldfile เป็น newfile

git mv #oldfile #newfile

ขั้นที่ 2: คอมไพล์กระทำและเพิ่มความคิดเห็น

git commit -m "rename oldfile to newfile"

ขั้นที่ 3: ผลักดันการเปลี่ยนแปลงนี้ไปยังเซิร์ฟเวอร์ระยะไกล

git push origin #localbranch:#remotebranch

1
โปรดเพิ่มความคิดเห็นเพื่อที่จะเป็นประโยชน์กับ OP
Devrath

ขั้นตอนที่ 2 ไม่จำเป็น หลังจากgit mvไฟล์ใหม่มีอยู่แล้วในดัชนี
Alois Mahdal

7

Git จะรับรู้ไฟล์จากเนื้อหาแทนที่จะมองว่าเป็นไฟล์ที่ไม่ได้ติดตามใหม่

นั่นคือสิ่งที่คุณผิดพลาด

มันเป็นเพียงหลังจากที่คุณเพิ่มไฟล์คอมไพล์ที่จะรู้ได้จากเนื้อหา


เผง เมื่อ git staged จะแสดงการเปลี่ยนชื่ออย่างถูกต้อง
Max MacLeod

3

คุณไม่ได้แสดงผลการค้นหาของคุณเป็นระยะ ฉันเชื่อว่าถ้าคุณทำการย้ายผ่าน Finder แล้วทำเช่นนั้นgit add css/mobile.css ; git rm css/iphone.cssgit จะคำนวณแฮชของไฟล์ใหม่และจากนั้นก็ตระหนักว่าแฮชของไฟล์นั้นตรงกัน (และทำให้มันเปลี่ยนชื่อ)


2

ในกรณีที่คุณต้องเปลี่ยนชื่อไฟล์ด้วยตนเองเช่น ใช้สคริปต์เพื่อแบทช์เปลี่ยนชื่อกลุ่มของไฟล์จากนั้นใช้git add -A .งานได้สำหรับฉัน


2

สำหรับผู้ใช้ Xcode: หากคุณเปลี่ยนชื่อไฟล์ใน Xcode คุณจะเห็นไอคอนป้ายเปลี่ยนเพื่อต่อท้าย หากคุณกระทำการใช้ XCode คุณจะสร้างไฟล์ใหม่และสูญเสียประวัติ

วิธีแก้ปัญหาง่าย แต่คุณต้องทำก่อนที่จะใช้ Xcode:

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

เปลี่ยนชื่อ: โครงการ / OldName.h -> โครงการ / NewName.h เปลี่ยนชื่อ: โครงการ / OldName.m -> โครงการ / NewName.m

  1. กระทำการ -m 'การเปลี่ยนชื่อ'

จากนั้นกลับไปที่ XCode และคุณจะเห็นป้ายเปลี่ยนจาก A เป็น M และมันเป็นการบันทึกเพื่อทำการเปลี่ยนแปลง furtur ในการใช้ xcode ทันที

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