เลิกทำการแก้ไขการคัดลอกไฟล์เดียวใน Git หรือไม่


1632

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

อย่างไรก็ตามฉันต้องการยกเลิกการเปลี่ยนแปลงการคัดลอกที่ใช้งานได้ของไฟล์เพียงไฟล์เดียวโดยไม่ต้องทำอะไรเลย

ฉันจะทำอย่างไร

คำตอบ:


2212

คุณสามารถใช้ได้

git checkout -- file

คุณสามารถทำได้โดยไม่ต้อง--(ตามที่แนะนำโดยนิมม์มม) แต่ถ้าชื่อไฟล์ดูเหมือนสาขาหรือแท็ก (หรือตัวระบุการแก้ไขอื่น ๆ ) มันอาจสับสนได้ดังนั้นการใช้งาน--จะดีที่สุด

นอกจากนี้คุณยังสามารถตรวจสอบไฟล์เวอร์ชันที่ต้องการได้:

git checkout v1.2.3 -- file         # tag v1.2.3
git checkout stable -- file         # stable branch
git checkout origin/master -- file  # upstream master
git checkout HEAD -- file           # the version from the most recent commit
git checkout HEAD^ -- file          # the version before the most recent commit

34
ความแตกต่างระหว่าง HEAD และ HEAD ^ คืออะไร
hasen

62
HEAD เป็นความมุ่งมั่นล่าสุดของสาขาปัจจุบันและ HEAD ^ เป็นความมุ่งมั่นก่อนหน้านี้ในสาขาปัจจุบัน สำหรับสถานการณ์ที่คุณอธิบายคุณสามารถใช้หัวข้อการชำระเงิน git - ชื่อไฟล์
Paul

16
ในระยะสั้น "git checkout sha-reference - filename" โดยที่ sha-reference เป็นการอ้างอิงไปยัง sha of commit ในรูปแบบใด ๆ (สาขา, แท็ก, ผู้ปกครอง ฯลฯ )
Lakshman Prasad

29
หมายเหตุ: หากไฟล์ถูกจัดฉากไว้แล้วคุณต้องทำการรีเซ็ตก่อน git reset HEAD <filename> ; git checkout -- <filename>
Olie

14
@gwho ใช่คุณสามารถทำHEAD^^2 คอมมิทจากล่าสุดหรือคอมมิทHEAD^^^3 ครั้ง นอกจากนี้คุณยังสามารถใช้HEAD~2หรือHEAD~3ซึ่งจะสะดวกกว่าถ้าคุณต้องการย้อนกลับไปในขณะที่HEAD^2หมายถึง "ผู้ปกครองคนที่สองของการกระทำนี้"; เนื่องจากการรวมการคอมมิทการกระทำสามารถมีการคอมมิทก่อนหน้าได้มากกว่าหนึ่งรายการดังนั้นด้วยHEAD^การเลือกตัวเลขของผู้ปกครองเหล่านั้นในขณะที่HEAD~จำนวนจะเลือกพาเรนต์แรก แต่จำนวนคอมมิทกลับมา ดูgit help rev-parseรายละเอียดเพิ่มเติมได้ที่
Brian Campbell

139

เพียงแค่ใช้

git checkout filename

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

คำเตือน: การเปลี่ยนแปลงของคุณจะถูกยกเลิก - ไม่มีการสำรองข้อมูล


22
@duckx มันจะแก้ชื่อสาขาจากชื่อไฟล์ ถ้าคุณพูดgit checkout xและ x เกิดขึ้นเป็นชื่อสาขาเช่นเดียวกับชื่อไฟล์ฉันไม่แน่ใจว่าพฤติกรรมเริ่มต้นคืออะไร แต่ฉันคิดว่า git จะถือว่าคุณต้องการเปลี่ยนเป็นสาขา x เมื่อคุณใช้--คุณจะบอกว่าสิ่งที่ตามมาคือชื่อไฟล์
hasen

1
ขอบคุณสำหรับการล้างที่ขึ้น ทุกคนแค่สมมติว่าคุณรู้ว่าอะไร - หมายถึงเมื่อพวกเขาแสดงตัวอย่างให้คุณ และไม่ใช่สิ่งที่คุณสามารถ google ได้อย่างง่ายดายเช่นกัน
Patoshi パトシ

1
ดูเหมือนว่าคำตอบถูกแก้ไขเพื่อลบออก--จากมัน ในขณะที่ยังคงถูกต้องเนื่องจาก @hasen ชี้ให้เห็นหากมีความกำกวมระหว่างชื่อไฟล์และชื่อสาขาคุณอาจท้ายด้วยพฤติกรรมที่ไม่พึงประสงค์ที่นี่!
BrainSlugs83

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

133
git checkout <commit> <filename>

ฉันใช้สิ่งนี้ในวันนี้เพราะฉันตระหนักว่า favicon ของฉันถูกเขียนทับคอมมิชชันเมื่อไม่นานมานี้เมื่อฉันอัพเกรดเป็น drupal 6.10 ดังนั้นฉันจึงต้องเอามันกลับมา นี่คือสิ่งที่ฉันทำ:

git checkout 088ecd favicon.ico

1
ฉันจะรับคอมมิชชัน (ของไฟล์ที่ถูกลบไปก่อนหน้านี้) ได้อย่างไรยกเว้นการเลื่อน "git log --stat" เอาท์พุท?
Alex

4
IMO เป็นเรื่องยากผ่าน commandline เพื่อสแกนผ่าน gits log และค้นหาไฟล์ที่ถูกต้อง มันง่ายกว่ามากด้วยแอพ GUI เช่นsourcetreeapp.com
neoneye

6
git log --oneline <filename>จะให้บันทึกที่มีขนาดกะทัดรัดมากขึ้นและรวมเฉพาะการเปลี่ยนแปลงกับไฟล์ที่ระบุ
rjmunro

1
หรือคุณสามารถใช้git reflog <filename>
ygesher

70

หากไฟล์ของคุณถูกจัดฉากแล้ว (เกิดขึ้นเมื่อคุณเพิ่มคอมไพล์ ฯลฯ หลังจากแก้ไขไฟล์แล้ว) เพื่อยกเลิกการเปลี่ยนแปลง

ใช้

git reset HEAD <file>

แล้วก็

git checkout <file>

หากยังไม่จัดฉากให้ใช้

git checkout <file>

2
สิ่งนี้มีประโยชน์มากกว่าที่ยอมรับได้ฮ่าฮ่า มันง่ายที่จะลืมว่าการเปลี่ยนแปลงใดที่จัดเตรียมไว้และสิ่งใดที่ไม่ดำเนินการดังนั้นการรีเซ็ตจึงช่วยได้ แม้ว่าฉันจะลอง "รีเซ็ต git - ฮาร์ด" ก่อนหน้านี้ แต่ก็ไม่ได้ทำในสิ่งที่ "รีเซ็ต git HEAD" ทำ ฉันสงสัยว่าทำไม?
Arman Bimatov

20

หากคุณต้องการเลิกทำการเปลี่ยนแปลงการกระทำก่อนหน้านี้กับไฟล์เดียวคุณสามารถลองทำสิ่งนี้:

git checkout branchname^ filename

นี่จะทำการเช็กเอาต์ไฟล์เหมือนเดิมก่อนที่จะส่งมอบครั้งล่าสุด หากคุณต้องการย้อนกลับอีกสองสามข้อให้ใช้branchname~nเครื่องหมาย


สิ่งนี้จะไม่ลบการเปลี่ยนแปลงออกจากการคอมมิทมันจะใช้ดิฟกับดิฟบน HEAD
FernandoEscher

2
ในขณะที่ความจริงผู้โพสต์ต้นฉบับเพียงแค่ต้องการย้อนกลับแก้ไขการทำงานสำเนาของเขา (ฉันคิดว่า) ไม่เปลี่ยนการเปลี่ยนแปลงจากการกระทำที่ผ่านมา คำถามต้นฉบับของโปสเตอร์นั้นไม่ค่อยชัดเจนนักดังนั้นฉันสามารถเข้าใจความสับสนได้

อาจไม่ใช่การใช้งานของ OP แต่ฉันกำลังมองหาวิธีเขียนทับสาขาของฉันด้วยสำเนาต้นแบบ - มันทำงานค่อนข้างดีเมื่อแทนที่branchname^
Alex

15

ฉันทำผ่าน git bash แล้ว:

(use "git checkout -- <file>..." to discard changes in working directory)

  1. สถานะ Git [ดังนั้นเราจึงได้เห็นหนึ่งไฟล์ปึกแก้ไข]
  2. git checkout - index.html [ฉันมีการเปลี่ยนแปลงในไฟล์ index.html:
  3. สถานะ git [ตอนนี้การเปลี่ยนแปลงเหล่านั้นถูกลบออก]

ป้อนคำอธิบายรูปภาพที่นี่


8

ฉันสับสนกับสิ่งนี้อยู่เสมอดังนั้นนี่คือกรณีทดสอบเตือนความจำ สมมติว่าเรามีbashสคริปต์นี้เพื่อทดสอบgit:

set -x
rm -rf test
mkdir test
cd test
git init
git config user.name test
git config user.email test@test.com
echo 1 > a.txt
echo 1 > b.txt
git add *
git commit -m "initial commit"
echo 2 >> b.txt
git add b.txt
git commit -m "second commit"
echo 3 >> b.txt

ณ จุดนี้การเปลี่ยนแปลงไม่ได้ถูกจัดเตรียมไว้ในแคชดังนั้นgit status:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

หากมาจากจุดนี้เราทำgit checkoutผลที่ได้คือ:

$ git checkout HEAD -- b.txt
$ git status
On branch master
nothing to commit, working directory clean

หากเราทำเช่นgit resetนั้นผลลัพธ์ก็คือ:

$ git reset HEAD -- b.txt
Unstaged changes after reset:
M   b.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

ดังนั้นในกรณีนี้ - หากการเปลี่ยนแปลงไม่ได้จัดฉากก็git resetไม่ต่างอะไรในขณะที่git checkoutเขียนทับการเปลี่ยนแปลง


ทีนี้สมมติว่าการเปลี่ยนแปลงครั้งสุดท้ายจากสคริปต์ด้านบนนั้นถูกจัดฉาก / แคชนั่นก็คือการบอกเราว่าgit add b.txtท้ายที่สุดแล้ว

ในกรณีนี้git statusณ จุดนี้คือ:

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

    modified:   b.txt

หากมาจากจุดนี้เราทำgit checkoutผลที่ได้คือ:

$ git checkout HEAD -- b.txt
$ git status
On branch master
nothing to commit, working directory clean

หากเราทำเช่นgit resetนั้นผลลัพธ์ก็คือ:

$ git reset HEAD -- b.txt
Unstaged changes after reset:
M   b.txt
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   b.txt

no changes added to commit (use "git add" and/or "git commit -a")

ดังนั้นในกรณีนี้ - ถ้าการเปลี่ยนแปลงถูกจัดฉากgit resetโดยทั่วไปจะทำการเปลี่ยนแปลงตามฉากเป็นการเปลี่ยนแปลงที่ไม่มีการจัดฉาก - ในขณะที่git checkoutจะเขียนทับการเปลี่ยนแปลงทั้งหมด


7

คำตอบนี้สำหรับคำสั่งที่จำเป็นสำหรับการเลิกทำการเปลี่ยนแปลงในเครื่องซึ่งอยู่ในไฟล์หลายไฟล์ในโฟลเดอร์เดียวกันหรือหลายโฟลเดอร์ (หรือไดเรกทอรี) คำตอบนี้ตอบคำถามเฉพาะที่ผู้ใช้มีมากกว่าหนึ่งไฟล์ แต่ผู้ใช้ไม่ต้องการเลิกทำการเปลี่ยนแปลงในเครื่องทั้งหมด:

หากคุณมีไฟล์อย่างน้อยหนึ่งไฟล์คุณสามารถใช้คำสั่งเดียวกัน ( git checkout -- file) กับไฟล์แต่ละไฟล์โดยแสดงรายการตำแหน่งแต่ละตำแหน่งโดยคั่นด้วยช่องว่างเช่นเดียวกับใน:

git checkout -- name1/name2/fileOne.ext nameA/subFolder/fileTwo.ext

คำนึงถึงพื้นที่ด้านบนระหว่าง name1 / name2 / fileOne.ext nameA / subFolder / fileTwo.ext

สำหรับหลายไฟล์ในโฟลเดอร์เดียวกัน:

หากคุณต้องการยกเลิกการเปลี่ยนแปลงไฟล์ทั้งหมดในไดเรกทอรีบางรายการให้ใช้การชำระเงิน git ดังนี้:

git checkout -- name1/name2/*

เครื่องหมายดอกจันในรายการด้านบนนี้เป็นการหลอกลวงการยกเลิกไฟล์ทั้งหมดในตำแหน่งนั้นภายใต้ name1 / name2

และในทำนองเดียวกันต่อไปนี้สามารถยกเลิกการเปลี่ยนแปลงในไฟล์ทั้งหมดสำหรับหลาย ๆ โฟลเดอร์:

git checkout -- name1/name2/* nameA/subFolder/*

ให้คำนึงถึงช่องว่างระหว่าง name1 / name2 / * nameA / subFolder / * ข้างต้น

หมายเหตุ: name1, name2, nameA, โฟลเดอร์ย่อย - ชื่อโฟลเดอร์ตัวอย่างทั้งหมดเหล่านี้ระบุถึงโฟลเดอร์หรือแพ็คเกจที่ไฟล์ดังกล่าวอาจอยู่


5

ฉันกู้คืนไฟล์โดยใช้รหัส SHA, สิ่งที่ฉันทำคือ git checkout <sha hash id> <file name>



2

หากคุณยังไม่ได้กดหรือแชร์การกระทำของคุณ:

git diff --stat HEAD^...HEAD | \
fgrep filename_snippet_to_revert | cut -d' ' -f2 | xargs git checkout HEAD^ --
git commit -a --amend

0

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


1
การเพิ่มคำสั่งเฉพาะเพื่อใช้จะช่วยโปสเตอร์ต้นฉบับและผู้เยี่ยมชมในอนาคต
Adrian

0

ฉันไม่รู้ว่าทำไม แต่เมื่อฉันพยายามป้อนรหัสของฉันมันจะเกิดขึ้นเป็นรูปภาพ

ป้อนคำอธิบายรูปภาพที่นี่

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