Hoàn tác sửa đổi bản sao của một tệp trong Git?


1633

Sau lần xác nhận cuối cùng, tôi đã sửa đổi một loạt các tệp trong bản sao làm việc của mình, nhưng tôi muốn hoàn tác các thay đổi đối với một trong các tệp đó, như đặt lại nó về trạng thái giống như cam kết gần đây nhất.

Tuy nhiên, tôi chỉ muốn hoàn tác các thay đổi bản sao làm việc của chỉ một tệp đó, không có gì khác với nó.

Làm thế nào để làm điều đó?

Câu trả lời:


2214

Bạn có thể dùng

git checkout -- file

Bạn có thể làm điều đó mà không cần --(như được đề xuất bởi nimrodm), nhưng nếu tên tệp trông giống như một nhánh hoặc thẻ (hoặc định danh sửa đổi khác), nó có thể bị nhầm lẫn, vì vậy sử dụng --là tốt nhất.

Bạn cũng có thể kiểm tra phiên bản cụ thể của tệp:

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
sự khác biệt giữa ĐẦU và ĐẦU ^?
hasen

62
HEAD là cam kết gần đây nhất trên nhánh hiện tại và HEAD ^ là cam kết trước đó trên nhánh hiện tại. Đối với tình huống bạn mô tả, bạn có thể sử dụng git checkout HEAD - tên tệp.
Paul

16
Trong "git checkout sha-Reference - tên tệp" ngắn trong đó tham chiếu sha là một tham chiếu đến sha của một cam kết, dưới bất kỳ hình thức nào (chi nhánh, thẻ, phụ huynh, v.v.)
Lakshman Prasad

29
LƯU Ý: Nếu tệp đã được dàn dựng, trước tiên bạn cần đặt lại tệp. git reset HEAD <filename> ; git checkout -- <filename>
Olie

14
@gwho Có, bạn có thể thực hiện HEAD^^cho 2 lần xác nhận gần đây nhất hoặc HEAD^^^cho 3 lần xác nhận lại. Bạn cũng có thể sử dụng HEAD~2, hoặc HEAD~3, sẽ thuận tiện hơn nếu bạn muốn quay lại nhiều cam kết hơn, trong khi đó HEAD^2có nghĩa là "cha mẹ thứ hai của cam kết này"; do các cam kết hợp nhất, một cam kết có thể có nhiều hơn một cam kết trước đó, do đó, với HEAD^một số lựa chọn của cha mẹ đó, trong khi với HEAD~một số luôn chọn cha mẹ đầu tiên nhưng số lần xác nhận đó lại. Xem git help rev-parseđể biết thêm chi tiết.
Brian Campbell

139

Chỉ dùng

git checkout filename

Điều này sẽ thay thế tên tệp bằng phiên bản mới nhất từ ​​chi nhánh hiện tại.

CẢNH BÁO: các thay đổi của bạn sẽ bị loại bỏ - không có bản sao lưu nào được lưu giữ.


22
@duckx đó là để phân tán tên chi nhánh từ tên tệp. nếu bạn nói git checkout xvà x tình cờ là tên nhánh cũng như tên tệp, tôi không chắc hành vi mặc định là gì nhưng tôi nghĩ git sẽ cho rằng bạn muốn chuyển sang nhánh x. Khi bạn sử dụng, --bạn đang nói rằng cái sau đây là tên tệp.
hasen

1
ic cảm ơn đã xóa nó lên. mọi người chỉ cho rằng bạn biết điều gì - có nghĩa là khi họ chỉ cho bạn ví dụ. và nó không phải là một cái gì đó bạn có thể google dễ dàng quá.
Patoshi パ ト シ

1
Có vẻ như câu trả lời đã được chỉnh sửa để loại bỏ --nó. Mặc dù vẫn đúng, như @hasen chỉ ra, nếu có sự mơ hồ giữa tên tệp và tên nhánh, bạn có thể sẽ có hành vi rất không mong muốn ở đây!
BrainSlugs83

2
Tôi thích nó theo cách nó, mà không --, tốt đẹp và dễ dàng. Khi bạn đặt tên cho các chi nhánh bằng tên tệp, chắc chắn phải có suy nghĩ tồi tệ ở đâu đó ...
Marco Faustinelli

133
git checkout <commit> <filename>

Tôi đã sử dụng nó ngày hôm nay bởi vì tôi nhận ra rằng favicon của tôi đã bị ghi đè lên một vài lần xác nhận trước đây khi tôi nâng cấp lên drupal 6.10, vì vậy tôi phải lấy lại. Đây là những gì tôi đã làm:

git checkout 088ecd favicon.ico

1
Làm cách nào để tôi nhận được cam kết (của một tệp đã bị xóa trước đó) ngoại trừ cuộn hàng tấn đầu ra "git log --stat"?
Alex

4
IMO thật khó khăn thông qua dòng lệnh để quét qua nhật ký gits và tìm đúng tệp. Ứng dụng GUI dễ dàng hơn nhiều, chẳng hạn như sourcetre Ứng dụng.com
neoneye

6
git log --oneline <filename>sẽ cung cấp cho bạn một bản ghi nhỏ gọn hơn và chỉ bao gồm các thay đổi đối với tệp cụ thể
rjmunro

1
cách khác, bạn có thể sử dụnggit reflog <filename>
ygesher

70

Nếu tệp của bạn đã được sắp xếp (xảy ra khi bạn thực hiện thêm git, v.v. sau khi tệp được chỉnh sửa) để bỏ qua các thay đổi của bạn.

Sử dụng

git reset HEAD <file>

Sau đó

git checkout <file>

Nếu chưa được dàn dựng, chỉ cần sử dụng

git checkout <file>

2
Điều này đã hữu ích hơn một haha ​​được chấp nhận. Thật dễ dàng để quên những thay đổi đã được dàn dựng và những gì chưa có, vì vậy việc đặt lại đã giúp. Mặc dù tôi cũng đã thử "git reset --hard" trước đây, nhưng nó không làm được điều mà "git reset HEAD" đã làm. Tôi tự hỏi tại sao?
Arman Bimatov

20

Nếu bạn muốn hoàn tác các thay đổi của cam kết trước đó với một tệp đó, bạn có thể thử điều này:

git checkout branchname^ filename

Điều này sẽ kiểm tra các tập tin như trước khi cam kết cuối cùng. Nếu bạn muốn quay lại một vài lần nữa, hãy sử dụng branchname~nký hiệu.


Điều này sẽ không loại bỏ các thay đổi từ cam kết, nó sẽ chỉ áp dụng diff cho phiên bản trên HEAD.
FernandoEscher

2
Mặc dù đúng, người đăng ban đầu chỉ muốn hoàn nguyên các sửa đổi bản sao làm việc của mình (tôi nghĩ), không hoàn nguyên các thay đổi từ lần cam kết cuối cùng. Câu hỏi ban đầu của người đăng có chút không rõ ràng, vì vậy tôi có thể hiểu được sự nhầm lẫn.

có thể không sử dụng OP, nhưng tôi đang tìm cách ghi đè lên chi nhánh của mình bằng bản sao của chủ - điều này hoạt động khá độc đáo khi thay thếbranchname^
Alex

15

Tôi đã thực hiện thông qua git bash:

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

  1. Tình trạng Git. [Vì vậy, chúng tôi đã thấy một tập tin đã được sửa đổi.]
  2. kiểm tra git - index.html [tôi đã thay đổi trong tệp index.html:
  3. trạng thái git [bây giờ những thay đổi đã được xóa]

nhập mô tả hình ảnh ở đây


8

Tôi luôn luôn bối rối với điều này, vì vậy đây là một trường hợp kiểm tra nhắc nhở; giả sử chúng ta có bashkịch bản này để kiểm tra 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

Tại thời điểm này, thay đổi không được tổ chức trong bộ đệm, vì vậy 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")

Nếu từ thời điểm này, chúng tôi làm git checkout, kết quả là:

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

Nếu thay vào đó chúng tôi làm git reset, kết quả là:

$ 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")

Vì vậy, trong trường hợp này - nếu các thay đổi không được dàn dựng, sẽ git resetkhông có sự khác biệt, trong khi git checkoutghi đè lên các thay đổi.


Bây giờ, hãy nói rằng thay đổi cuối cùng từ tập lệnh ở trên được dàn dựng / lưu vào bộ đệm, nghĩa là chúng ta cũng đã làm git add b.txtở cuối.

Trong trường hợp này, git statustại thời điểm này là:

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

    modified:   b.txt

Nếu từ thời điểm này, chúng tôi làm git checkout, kết quả là:

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

Nếu thay vào đó chúng tôi làm git reset, kết quả là:

$ 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")

Vì vậy, trong trường hợp này - nếu các thay đổi được dàn dựng, git resetvề cơ bản sẽ thực hiện các thay đổi theo giai đoạn thành các thay đổi không theo giai đoạn - trong khi git checkoutsẽ ghi đè lên hoàn toàn các thay đổi.


7

Câu trả lời này là cho lệnh cần thiết để hoàn tác các thay đổi cục bộ trong nhiều tệp cụ thể trong cùng hoặc nhiều thư mục (hoặc thư mục). Câu trả lời này đặc biệt giải quyết câu hỏi trong đó người dùng có nhiều tệp nhưng người dùng không muốn hoàn tác tất cả các thay đổi cục bộ:

nếu bạn có một hoặc nhiều tệp, bạn có thể áp dụng cùng một lệnh ( git checkout -- file) cho từng tệp đó bằng cách liệt kê từng vị trí của chúng được phân tách bằng dấu cách như trong:

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

lưu ý khoảng trắng ở trên giữa name1 / name2 / fileOne.ext nameA / subFolder / fileTwo.ext

Đối với nhiều tệp trong cùng một thư mục:

Nếu bạn tình cờ cần loại bỏ các thay đổi cho tất cả các tệp trong một thư mục nhất định, hãy sử dụng thanh toán git như sau:

git checkout -- name1/name2/*

Dấu hoa thị ở trên thực hiện thủ thuật hoàn tác tất cả các tệp tại vị trí đó dưới tên1 / name2.

Và, tương tự như sau có thể hoàn tác các thay đổi trong tất cả các tệp cho nhiều thư mục:

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

một lần nữa nhớ khoảng trống giữa name1 / name2 / * nameA / subFolder / * ở trên.

Lưu ý: name1, name2, nameA, subFolder - tất cả các tên thư mục ví dụ này cho biết thư mục hoặc gói nơi tệp (s) trong câu hỏi có thể cư trú.


5

Tôi khôi phục các tệp của mình bằng id SHA, những gì tôi làm là git checkout <sha hash id> <file name>



2

Nếu bạn chưa thúc đẩy hoặc chia sẻ cam kết của mình:

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

0

Nếu nó đã được cam kết, bạn có thể hoàn nguyên thay đổi cho tệp và xác nhận lại, sau đó xóa cam kết mới với lần xác nhận cuối cùng.


1
Thêm các lệnh cụ thể để sử dụng sẽ giúp người đăng ban đầu và khách truy cập trong tương lai.
Adrian

0

Tôi không biết tại sao nhưng khi tôi cố gắng nhập mã của mình, nó xuất hiện dưới dạng hình ảnh.

nhập mô tả hình ảnh ở đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.