เหตุใดจึงมีสองวิธีในการเลิกเก็บไฟล์ใน Git


1168

บางครั้งคอมไพล์แสดงให้เห็นgit rm --cachedการ unstage git reset HEAD fileไฟล์บางครั้ง เมื่อใดที่ฉันควรใช้

แก้ไข:

D:\code\gt2>git init
Initialized empty Git repository in D:/code/gt2/.git/
D:\code\gt2>touch a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       a
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add a

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a
#
D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
 0 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 a

D:\code\gt2>touch b

D:\code\gt2>git status
# On branch master
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#       b
nothing added to commit but untracked files present (use "git add" to track)

D:\code\gt2>git add b

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

20
ทำไม? ฉันจะบอกว่าเป็นเพราะส่วนต่อประสาน commandline ของ git มีการพัฒนาแบบออร์แกนิกและไม่เคยได้รับการปรับโครงสร้างครั้งใหญ่เพื่อให้สิ่งต่าง ๆ สอดคล้องกัน (ถ้าคุณไม่เห็นด้วยทราบว่าgit rmทั้งสองเวทีลบและยังunstage นอกจาก )
โรมัน Starkov

3
@romkyns: ผมยอมรับว่าอินเตอร์เฟซ Git มีหลายแปลกประหลาดเพราะมันมีวิวัฒนาการตามธรรมชาติ แต่การกำจัดเป็นแน่นอนฟังก์ชันผกผันของนอกจากนี้เพื่อให้มันไม่ได้เป็นตรรกะสำหรับการrmที่จะเลิกadd? คุณคิดว่าrmควรประพฤติอย่างไร
Zaz

6
คำตอบที่แท้จริงสำหรับคำถามของคุณคือหลังจากที่git initไม่มีHEADการรีเซ็ตเป็น
Miles Rout

เอกสารที่ดีที่สุดสำหรับสิ่งนี้: help.github.com/articles/changing-a-remote-s-url
ScottyBlades

4
@Zaz ฉันจะให้ความคิดเห็นของฉัน rmหมายถึงการลบในบริบทยูนิกซ์ มันไม่ได้ตรงกันข้ามกับการเพิ่มไปยังดัชนี ฟังก์ชั่นสำหรับลบไฟล์ไม่ควรโอเวอร์โหลดพร้อมกับฟังก์ชั่นเพื่อเปลี่ยนสถานะ staging หากมีรายละเอียดการนำไปปฏิบัติที่ทำให้สะดวกในการรวมนั่นก็ชี้ไปที่การขาดเลเยอร์ที่เป็นนามธรรมของ git ซึ่งจะทำให้การใช้งานชัดเจน
Joshua Goldberg

คำตอบ:


1892

git rm --cached <filePath> ไม่ได้ยกเลิกการทำไฟล์ไฟล์จริง ๆ แล้วมันเป็นขั้นตอนการลบไฟล์ออกจาก repo (สมมติว่ามันได้กระทำก่อนหน้านี้) แต่ปล่อยไฟล์ไว้ในแผนผังการทำงานของคุณ

git reset -- <filePath>จะไม่ขึ้นเวทีการเปลี่ยนแปลงตามขั้นตอนใด ๆ สำหรับไฟล์ที่ระบุ

ที่กล่าวว่าหากคุณใช้git rm --cachedไฟล์ใหม่ที่มีการจัดฉากมันจะดูเหมือนว่าคุณเพิ่งไม่มีการแสดงเนื่องจากมันไม่เคยทำมาก่อน

ปรับปรุง git 2.24
ในรุ่นนี้ใหม่ของคอมไพล์คุณสามารถใช้แทนgit restore --staged git resetดูเอกสารคอมไพล์


70
ฉันจะบอกว่าgit rm --cachedunstages ไฟล์ แต่ไม่ลบออกจากไดเรกทอรีการทำงาน
Pierre de LESPINAY

4
หากต้องการลบไฟล์ที่ถูกจัดฉากไว้สำหรับการเพิ่มเพื่อที่จะไม่ได้ถูกจัดฉากอีกต่อไปสามารถเรียกได้ว่า "unstaging ไฟล์ที่ถูกจัดฉากเพื่อเพิ่ม" ใช่ไหม? ผลลัพธ์สุดท้ายนั้นไม่ใช่การลบแบบเป็นขั้นตอนแน่นอนดังนั้นฉันจึงคิดว่าความเข้าใจที่คลาดเคลื่อนเข้าใจได้โดยสิ้นเชิง
Roman Starkov

4
ดังนั้นโดยทั่วไปแล้วจะใช้git rm --cached <filePath>ในการลบไฟล์บาง (s) จากซื้อคืนหลังจากที่ทราบว่ามันควรจะได้ไม่เคยอยู่ใน repo gitignoreนี้ดังนั้นส่วนใหญ่น่าจะใช้คำสั่งนี้และจากนั้นเพิ่มไฟล์ที่เกี่ยวข้องเพื่อ ฉันถูกไหม?
Adrien Be

13
ด้วยคะแนนเสียงจำนวนมากเกี่ยวกับเรื่องนี้ทั้งคำถามและคำตอบผมจะบอกว่าเห็นได้ชัดว่าเราต้องการที่จะมีคำสั่งในunstage git
milosmns

4
"สถานะ git" แนะนำในตอนนี้: ใช้ "git restore --staged <file> ... " เพื่อ unstage
yucer

334

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

git reset HEAD file(ซึ่งโดยค่าเริ่มต้นใช้--mixedแฟล็ก) จะแตกต่างกันในกรณีที่ไฟล์มีอยู่แล้วใน repo มันจะแทนที่เวอร์ชันดัชนีของไฟล์ด้วยหนึ่งจาก repo (HEAD) อย่างมีประสิทธิภาพไม่เปลี่ยนแปลงไป

ในกรณีของไฟล์ที่ไม่มีเวอร์ชันมันจะ unstage ไฟล์ทั้งหมดเนื่องจากไฟล์นั้นไม่มีอยู่ใน HEAD ในด้านนี้git reset HEAD fileและgit rm --cachedเหมือนกัน แต่ไม่เหมือนกัน (อธิบายไว้ในกรณีของไฟล์ที่มีอยู่แล้วใน repo)

สำหรับคำถามของWhy are there 2 ways to unstage a file in git?- ไม่มีทางเดียวที่จะทำอะไรในคอมไพล์ นั่นคือความงามของมัน :)


7
ทั้งคำตอบที่ได้รับการยอมรับและคำตอบนี้ดีมากและอธิบายว่าทำไมคุณจึงใช้คำตอบข้อหนึ่ง แต่พวกเขาไม่ตอบคำถามโดยปริยายว่าทำไม git จึงเสนอวิธีการที่แตกต่างกันสองวิธี ในกรณีแรกในตัวอย่างของ OP, init git เพิ่งเสร็จสิ้นไป ในกรณีนั้น git แนะนำ "git rm --cached" เพราะ ณ จุดนั้นไม่มีการคอมมิทในที่เก็บดังนั้น HEAD จึงไม่ถูกต้อง "git reset HEAD -" สร้าง: "ร้ายแรง: ล้มเหลวในการแก้ไข 'HEAD' เป็นข้อมูลอ้างอิงที่ถูกต้อง"
sootsnoot

5
ด้วย 'การชำระเงินคอมไพล์' คุณจะไม่สูญเสียการเปลี่ยนแปลงทั้งหมดที่คุณทำกับไฟล์ใช่หรือไม่นั่นไม่ใช่สิ่งเดียวกับการไม่แสดงสถานะไฟล์ยกเว้นว่าฉันเข้าใจผิด
John Deighan

there is never really only one way to do anything in git. that is the beauty of it- อืม ... ทำไม? มันยอดเยี่ยมเสมอเมื่อมีเพียงวิธีเดียวเท่านั้น สิ่งนี้ช่วยประหยัดเวลาและความทรงจำในสมองของเราได้มาก))
Oto Shavadze

128

ค่อนข้างง่าย:

  • git rm --cached <file> ทำให้คอมไพล์หยุดการติดตามไฟล์ได้อย่างสมบูรณ์ (ทิ้งไว้ในระบบไฟล์ไม่เหมือนกับไฟล์ธรรมดาgit rm*)
  • git reset HEAD <file> unstages การแก้ไขใด ๆ ที่ทำกับไฟล์ตั้งแต่คอมมิทล่าสุด (แต่ไม่เปลี่ยนกลับในระบบไฟล์, ตรงกันข้ามกับสิ่งที่ชื่อคำสั่งอาจแนะนำ **) ไฟล์ยังคงอยู่ภายใต้การควบคุมการแก้ไขใหม่

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

* โปรดทราบข้อแม้ @DrewT กล่าวถึงในคำตอบของเขาเกี่ยวกับgit rm --cachedของไฟล์ที่เป็นความมุ่งมั่นที่ก่อนหน้านี้ในการเก็บข้อมูล ในบริบทของคำถามนี้ของไฟล์เพิ่งเพิ่มและยังไม่ได้มุ่งมั่นไม่มีอะไรต้องกังวล

** ฉันรู้สึกกลัวเป็นเวลานานที่จะใช้คำสั่ง git reset เนื่องจากชื่อของมัน - และในทุกวันนี้ฉันมักจะค้นหาไวยากรณ์เพื่อให้แน่ใจว่าฉันจะไม่พลาด ( อัปเดต : ในที่สุดฉันก็ใช้เวลาในการสรุปการใช้งานgit resetในหน้า tldrดังนั้นตอนนี้ฉันมีแบบจำลองทางจิตที่ดีขึ้นเกี่ยวกับวิธีการทำงานและการอ้างอิงโดยย่อเมื่อฉันลืมรายละเอียดบางอย่าง)


มันคือgit rm <file> --cached
neonmate

8
ฉันไม่คิดว่าการแก้ไขคำตอบนี้ในวันที่ 4 สิงหาคม 2558เป็นการปรับปรุงโดยรวม อาจมีความถูกต้องด้านเทคนิคคงที่ (ฉันไม่รู้สึกว่ามีคุณสมบัติพอที่จะประเมินว่า) แต่ฉันเกรงว่าจะทำให้น้ำเสียงของคำตอบที่เข้าถึงได้น้อยลงมากโดยการแนะนำภาษาเช่น "ไม่แน่ใจว่าจำเป็นต้องเริ่มติดตามไฟล์ที่ไม่ได้ติดตาม "และการใช้ศัพท์แสงเช่น" index "และ" HEAD "นั้นเป็นประเภทของสิ่งที่น่ากลัวสำหรับผู้เริ่มต้น หากมีใครทำได้โปรดแก้ไขเพื่อคืนค่าภาษาที่เป็นมิตรกับผู้มาใหม่มากขึ้น
วาลดีริอัส

5
เห็นด้วยกับ @waldyrious คำตอบดั้งเดิมอาจไม่ตรงกับตำราเรียน git แต่มันก็ตอบคำถามในระดับเทคนิคที่เพียงพอ รายละเอียดทางเทคนิคควรได้รับการชี้แจงในความคิดเห็นไม่ใช่การแก้ไขที่ปิดบังเจตนาดั้งเดิม
Simon Robb

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

หมายเหตุ @DrewT เตือนว่าหากใช้rm --cachedและผลักดันใครก็ตามที่ดึงสาขาเดียวกันจะถูกลบไฟล์ออกจากแผนผังการทำงานของจริง
Tom Hale

53

เธรดนี้ค่อนข้างเก่า แต่ฉันยังต้องการเพิ่มการสาธิตเล็กน้อยเนื่องจากมันยังไม่เป็นปัญหาที่ใช้งานง่าย:

me$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   to-be-added
#   modified:   to-be-modified
#   deleted:    to-be-removed
#

me$ git reset -q HEAD to-be-added

    # ok

me$ git reset -q HEAD to-be-modified

    # ok

me$ git reset -q HEAD to-be-removed

    # ok

# or alternatively:

me$ git reset -q HEAD to-be-added to-be-removed to-be-modified

    # ok

me$ 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:   to-be-modified
#   deleted:    to-be-removed
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   to-be-added
no changes added to commit (use "git add" and/or "git commit -a")

git reset HEAD(ไม่รวม-q) ให้คำเตือนเกี่ยวกับไฟล์ที่ถูกแก้ไขและรหัสการออกของมันคือ 1 ซึ่งจะถือเป็นข้อผิดพลาดในสคริปต์

แก้ไข: ใช้git checkout HEAD to-be-modified to-be-removedงานได้กับ unstaging แต่จะลบการเปลี่ยนแปลงทั้งหมดออกจากพื้นที่ทำงาน

อัพเดท git 2.23.0:บางครั้งคำสั่งจะเปลี่ยนไป ตอนนี้git statusพูดว่า:

  (use "git restore --staged <file>..." to unstage)

... ซึ่งใช้ได้กับการเปลี่ยนแปลงทั้งสามประเภท


ขอบคุณไม่ชัดเจนทั้งหมดจากคำตอบสองข้อแรก (อาจเป็นเพียงความไม่รู้ของฉันเกี่ยวกับคำศัพท์) ที่ git reset ออกจากการแก้ไขในไฟล์ภายในเครื่อง
soupdog

คุณควรใส่คำเตือนในการเริ่มต้นเกี่ยวกับเวอร์ชันเพราะเวอร์ชั่นเก่าลบไฟล์ในเวอร์ชันใหม่
Luis Mauricio

@LuisMauricio คำสั่งใดลบไฟล์?
Daniel Alder

@DanielAlder sry ฉันเพิ่งทดสอบอีกครั้งมันไม่ได้ลบความผิดพลาดของฉัน
Luis Mauricio

36

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

git stash
git stash pop

การดำเนินการนี้จะรีเซ็ตเป็น HEAD และนำการเปลี่ยนแปลงของคุณไปใช้อีกครั้งทำให้คุณสามารถแสดงไฟล์แต่ละไฟล์อีกครั้งสำหรับการคอมมิท สิ่งนี้จะเป็นประโยชน์หากคุณลืมสร้างสาขาฟีเจอร์สำหรับคำขอดึง ( git stash ; git checkout -b <feature> ; git stash pop)


3
นี่เป็นวิธีที่สะอาดและน่าเป็นห่วงน้อยกว่าการพิมพ์ "git rm"
Subimage

1
git stashมีประโยชน์ที่เกี่ยวข้องอื่น ๆ เพราะจะสร้างรายการใน reflog ซึ่งจะมีอยู่ในอนาคต เมื่อมีข้อสงสัยให้ไปข้างหน้าและทำgit stash(เช่นgit stash save -u "WIP notes to self"('-u' คือการรวมไฟล์ใหม่ / ไม่ได้ติดตามในการกระทำที่สะสม) ... จากนั้นลองgit reflog show stashดูรายการของการกระทำที่เก็บซ่อนและ sha ของพวกเขาฉันขอแนะนำเปลือก นามแฝงเช่นalias grs="git reflog show stash"
cweekly

15

คำสั่ง 2 เหล่านี้มีความแตกต่างเล็กน้อยหลายประการหากไฟล์ที่เป็นปัญหามีอยู่แล้วใน repo และอยู่ภายใต้การควบคุมเวอร์ชัน (ข้อตกลงก่อนหน้านี้ ฯลฯ ):

  • git reset HEAD <file> unstages ไฟล์ในการกระทำปัจจุบัน
  • git rm --cached <file>จะเลิกทำไฟล์สำหรับการคอมมิทในอนาคตด้วย มันจนกว่า unstaged git add <file>จะได้รับเพิ่มอีกครั้งกับ

และมีความแตกต่างที่สำคัญอีกอย่างหนึ่ง:

  • หลังจากทำงานgit rm --cached <file>และผลักดันสาขาระยะไกลทุกคนดึงสาขาของคุณจากระยะไกลจะได้รับไฟล์จริงลบออกจากโฟลเดอร์ของพวกเขาแม้ว่าในการทำงานในพื้นที่ของคุณตั้งค่าไฟล์เพียงกลายเป็นที่ไม่ได้ติดตาม (เช่นไม่ถูกลบออกจากร่างกายโฟลเดอร์)

ความแตกต่างสุดท้ายนี้มีความสำคัญสำหรับโครงการที่มีไฟล์กำหนดค่าที่นักพัฒนาแต่ละคนในทีมมีการกำหนดค่าที่แตกต่างกัน (เช่นการตั้งค่า url พื้นฐานที่แตกต่างกัน, ip หรือพอร์ต) ดังนั้นหากคุณใช้git rm --cached <file>ใครก็ตามที่ดึงสาขาของคุณ สร้างการกำหนดค่าหรือคุณสามารถส่งของพวกเขาและพวกเขาสามารถแก้ไขได้กลับไปที่การตั้งค่า IP ของพวกเขา (ฯลฯ ) เพราะการลบเพียงผลคนดึงสาขาของคุณจากระยะไกล


10

สมมติว่าคุณstageทั้งไดเรกทอรีผ่านgit add <folder>แต่คุณต้องการรวมไฟล์จากรายการ (เช่นรายการที่สร้างเมื่อทำงานฉากgit status) และให้การปรับเปลี่ยนภายในไฟล์ยกเว้น (คุณได้ทำงานในสิ่งที่และก็ไม่พร้อมสำหรับการกระทำ แต่ คุณไม่ต้องการสูญเสียงานของคุณ ... ) คุณสามารถใช้:

git reset <file>

เมื่อคุณเรียกใช้git statusคุณจะเห็นว่าไฟล์ใดที่คุณresetเป็นunstagedและไฟล์ที่เหลือที่คุณaddedยังอยู่ในstagedรายการ


10

1

D:\code\gt2>git status
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#       new file:   a

(ใช้ "git rm --cached ... " เพื่อไม่ขึ้นเวที)

  • git เป็นระบบของพอยน์เตอร์

  • คุณยังไม่ได้คอมมิทยังต้องเปลี่ยนพอยน์เตอร์ของคุณเป็น

  • วิธีเดียวที่จะ 'นำไฟล์ออกจากที่เก็บข้อมูลที่ชี้ไปยัง' คือการลบไฟล์ที่คุณบอกคอมไพล์เพื่อดูการเปลี่ยนแปลง

2

D:\code\gt2>git commit -m a
[master (root-commit) c271e05] a
0 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 a

คอมไพล์กระทำ

  • คุณกระทำ ' บันทึก '

3

D:\code\gt2>git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       new file:   b
#

(ใช้ "git reset HEAD ... " เป็น unstage)

  • คุณได้กระทำการในรหัสของคุณในเวลานี้
  • ตอนนี้คุณสามารถรีเซ็ตตัวชี้ของคุณเป็น ' ย้อนกลับไปสู่การบันทึกครั้งสุดท้าย '

1
นี่เป็นคำตอบเดียวที่ตอบคำถามได้อย่างถูกต้องนั่นคือ IMO จริง ๆ แล้วมันตอบคำถามซึ่งไม่ใช่ 'ความแตกต่างระหว่าง' git rm --cached 'และ' git reset HEAD 'แต่' ทำไม git จึงให้ทั้งสองอย่างเป็นตัวเลือก 'คำตอบคือไม่มี HEAD ที่จะรีเซ็ต ถึงเมื่อคุณgit initเป็นครั้งแรก
Miles Rout

5

ฉันประหลาดใจที่ไม่มีใครพูดถึงการอ้างอิง git ( http://git-scm.com/docs/git-reflog ):

# git reflog
<find the place before your staged anything>
# git reset HEAD@{1}

reflog เป็นประวัติ git ที่ไม่เพียง แต่ติดตามการเปลี่ยนแปลงของ repo แต่ยังติดตามการกระทำของผู้ใช้ (เช่น pull, checkout ไปยังสาขาต่าง ๆ ฯลฯ ) และอนุญาตให้เลิกทำการกระทำเหล่านั้นได้ ดังนั้นแทนที่จะยกเลิกการจัดเก็บไฟล์ที่จัดฉากผิดพลาดซึ่งคุณสามารถย้อนกลับไปยังจุดที่คุณไม่ได้จัดวางไฟล์ได้

สิ่งนี้คล้ายกับgit reset HEAD <file>แต่ในบางกรณีอาจมีความละเอียดมากกว่า

ขออภัยไม่ตอบคำถามของคุณจริงๆ แต่เพียงชี้ไปยังอีกวิธีหนึ่งในการหยุดไฟล์ที่ฉันใช้บ่อยๆ (ฉันชอบคำตอบอย่างไรอันสจ๊วตและวาลดีเรียมาก);) ฉันหวังว่ามันจะช่วยได้


5

เพียงใช้:

git reset HEAD <filename>

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


3

ดูเหมือนว่าฉันgit rm --cached <file>จะลบไฟล์ออกจากดัชนีโดยไม่ลบมันออกจากไดเรกทอรีที่ธรรมดาgit rm <file>จะทำทั้งสองอย่างเช่นเดียวกับที่ OS rm <file>จะลบไฟล์ออกจากไดเรกทอรีโดยไม่ลบเวอร์ชันออก


1

สำหรับรุ่น 2.23 ขึ้นไปเท่านั้น

แต่คำแนะนำเหล่านี้คุณสามารถใช้ git restore --staged <file>เพื่อunstageแฟ้ม (s)


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