Làm cách nào để truy xuất một tệp duy nhất từ ​​một sửa đổi cụ thể trong Git?


832

Tôi có một kho lưu trữ Git và tôi muốn xem một số tệp trông như thế nào vài tháng trước. Tôi tìm thấy bản sửa đổi vào ngày đó; nó 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8. Tôi cần xem một tệp trông như thế nào và cũng lưu nó dưới dạng tệp ("mới").

Tôi quản lý để xem tệp bằng cách sử dụng gitk, nhưng nó không có tùy chọn để lưu nó. Tôi đã thử với các công cụ dòng lệnh, gần nhất tôi nhận được là:

git-show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8 my_file.txt

Tuy nhiên, lệnh này hiển thị khác, và không phải nội dung tệp. Tôi biết sau này tôi có thể sử dụng một cái gì đó như PAGER=catvà chuyển hướng đầu ra sang một tệp, nhưng tôi không biết làm thế nào để đi đến nội dung tệp thực tế.

Về cơ bản, tôi đang tìm kiếm một cái gì đó như mèo svn .


73
Chìa khóa ở đây: git show(không hữu ích) sử dụng cú pháp khác nhau với dấu hai chấm. git show 2c7cf:my_file.txt
Steve Bennett

4
Để làm rõ hơn, lệnh trên đang yêu cầu git hiển thị hai đối tượng riêng biệt, một bản sửa đổi và một tệp. Câu trả lời được chấp nhận dưới đây, sử dụng dấu hai chấm giữa hai mục đang yêu cầu một tệp cụ thể tại một phiên bản cụ thể.
jhclark

2
Trên * nix bạn không cần PAGER, chỉ cần chuyển hướng đầu ra vỏ với>
Konstantin Pelepelin


Checat có một bình luận quan trọng, cho những ai muốn nội dung được xuất sang một số tệp. Bạn cần một cái gì đó như thế này: git show {sha}: my_file.txt> old_my_file.txt
ormurin

Câu trả lời:


744

Để hoàn thành câu trả lời của riêng bạn, cú pháp thực sự là

git show object
git show $REV:$FILE
git show somebranch:from/the/root/myfile.txt
git show HEAD^^^:test/test.py

Lệnh lấy kiểu sửa đổi thông thường, nghĩa là bạn có thể sử dụng bất kỳ thao tác nào sau đây:

  1. tên chi nhánh (theo gợi ý của tro )
  2. HEAD+ x số ^ký tự
  3. Hàm băm SHA1 của một sửa đổi đã cho
  4. Một vài ký tự đầu tiên (có thể là 5) của hàm băm SHA1 đã cho

Mẹo Điều quan trọng cần nhớ là khi sử dụng " git show", luôn chỉ định đường dẫn từ thư mục gốc của kho lưu trữ , không phải vị trí thư mục hiện tại của bạn.

(Mặc dù Mike Morearty đề cập rằng, ít nhất là với git 1.7.5.4, bạn có thể chỉ định một đường dẫn tương đối bằng cách đặt " ./" ở đầu đường dẫn - ví dụ:

git show HEAD^^:./test.py

)


Với Git 2.23+ (tháng 8 năm 2019), bạn cũng có thể sử dụng git restore thay thế lệnh khó hiểugit checkout

git restore -s <SHA1>     -- afile
git restore -s somebranch -- afile

Điều đó sẽ khôi phục trên cây làm việc chỉ tập tin như trong "nguồn" ( -s) cam kết SHA1 hoặc nhánh somebranch.
Để khôi phục lại chỉ mục:

git restore -s <SHA1> -SW -- afile

( -SW: viết tắt của --staged --worktree)


Trước git1.5.x, điều đó đã được thực hiện với một số hệ thống ống nước:

git ls-tree <rev>
hiển thị danh sách một hoặc nhiều đối tượng 'blob' trong một cam kết

git cat-file blob <file-SHA1>
cat một tập tin như nó đã được cam kết trong một phiên bản cụ thể (tương tự như mèo svn). sử dụng git ls-tree để lấy giá trị của tệp đã cho-sha1

git cat-file -p $(git-ls-tree $REV $file | cut -d " " -f 3 | cut -f 1)::

git-ls-tree liệt kê ID đối tượng cho tệp $ trong bản sửa đổi $ REV, phần này bị cắt ra khỏi đầu ra và được sử dụng làm đối số cho tệp git-cat-, thực sự nên được gọi là git-cat-object và chỉ đơn giản là kết xuất đối tượng đó đến stdout.


Lưu ý: kể từ Git 2.11 (Q4 2016), bạn có thể áp dụng bộ lọc nội dung cho git cat-fileđầu ra!

Xem cam kết 3214594 , cam kết 7bcf341 (09 tháng 9 năm 2016), cam kết 7bcf341 (09 tháng 9 năm 2016) và cam kết b9e62f6 , cam kết 16dcc29 (24 tháng 8 năm 2016) của Johannes Schindelin ( dscho) .
(Được hợp nhất bởi Junio ​​C Hamano - gitster- trong cam kết 7889ed2 , ngày 21 tháng 9 năm 2016)

cat-file: hỗ trợ --textconv/ --filterstrong chế độ hàng loạt

Mặc dù " git hash-objects", là công cụ lấy luồng dữ liệu trên hệ thống tệp và đưa nó vào kho đối tượng Git, được phép thực hiện chuyển đổi "ngoài thế giới sang Git" (ví dụ: chuyển đổi và ứng dụng cuối dòng của bộ lọc sạch) và mặc định nó có tính năng bật từ rất sớm, hoạt động ngược của nó " git cat-file", lấy một đối tượng từ kho đối tượng Git và đưa ra ngoài để tiêu thụ bởi thế giới bên ngoài, thiếu một cơ chế tương đương để chạy "Git-to-bên ngoài thế giới"

git config diff.txt.textconv "tr A-Za-z N-ZA-Mn-za-m <"
git cat-file --textconv --batch

Lưu ý: " git cat-file --textconv" đã bắt đầu segfaulting gần đây (2017), đã được sửa trong Git 2.15 (Q4 2017)

Xem cam kết cc0ea7c (ngày 21 tháng 9 năm 2017) của Jeff King ( peff) .
(Được hợp nhất bởi Junio ​​C Hamano - gitster- trong cam kết bfbc2fc , ngày 28 tháng 9 năm 2017)


Lưu ý rằng để ghi đè / thay thế tệp bằng nội dung trong quá khứ, bạn không nên sử dụng lệnh khó hiểugit checkout nữa, nhưng git restore(Git 2.23+, tháng 8 năm 2019)

git restore -s <SHA1> -- afile

Điều đó sẽ khôi phục trên cây làm việc chỉ tập tin như hiện diện trong "nguồn" ( -s) cam kết SHA1.
Để khôi phục lại chỉ mục:

git restore -s <SHA1> -SW -- afile

( -SW: viết tắt của --staged --worktree)


6
@Oscar vì git showvề cơ bản kết xuất nội dung trên stdout(đầu ra tiêu chuẩn), bạn có thể chỉ cần chuyển hướng đầu ra đó sang bất kỳ tệp nào bạn muốn ( tldp.org/LDP/abs/html/io-redirection.html ).
VonC

8
git checkout [branch | revision] filepathlà mệnh lệnh đúng
Gaui

12
@Gaui nhưng git checkoutsẽ ghi đè tệp của bạn bằng một phiên bản khác, ngược lại git show, cho phép bạn lưu nó dưới một tên khác, để bạn có thể xem và xem cả hai (phiên bản hiện tại và phiên bản cũ). Không rõ từ câu hỏi nếu OP muốn thay thế phiên bản hiện tại của nó bằng một phiên bản cũ.
VonC

9
Tôi muốn lưu ý rằng ^^^cũng có thể được viết chung chung là ~~~hay, tốt hơn , ~3. Sử dụng dấu ngã cũng có ưu điểm là không kích hoạt khớp tên tệp của một số shell (ví dụ zsh).
Eric O Lebigot

2
Tôi không có một git đủ cũ để kiểm tra: pre-1.5.x có git rev-parsexử lý rev:pathcú pháp không? (Trong một git gần đây hơn bạn có thể git cat-file -p $REV:path. Tuy nhiên, cũng git showhoạt động cho các đường dẫn thư mục, vì vậy nó không chỉ ngắn hơn, nó thường gần với những gì người ta muốn.)
torek

510

Nếu bạn muốn thay thế / ghi đè nội dung của tệp trong nhánh hiện tại của bạn bằng nội dung của tệp từ một cam kết trước đó hoặc một nhánh khác, bạn có thể làm như vậy với các lệnh sau:

git checkout 08618129e66127921fbfcbc205a06153c92622fe path/to/file.txt

hoặc là

git checkout mybranchname path/to/file.txt

Sau đó, bạn sẽ phải cam kết những thay đổi đó để chúng có hiệu lực trong chi nhánh hiện tại.


4
giải pháp đơn giản nhất và đây là những gì git-checkout được thiết kế cho - chỉ định tên đường dẫn có nghĩa là chỉ có tệp phù hợp được kiểm tra. Từ trang man git-checkout: git checkout master ~ 2 Makefile
RichVel 28/03/13

1
Sau đó, làm thế nào để bạn quay trở lại trạng thái trước khi bạn chạy lệnh này?
Flint

@Flint nếu bạn đến từ trạng thái CHÍNH, nó sẽ đơn giản như kiểm tra git ĐẦU - [đường dẫn đầy đủ].
Tiago Espinha

72
Lưu ý rằng điều này ghi đè lên tệp hiện có trong đường dẫn đó, trong khi git show SHA1:PATHgiải pháp chỉ in ra thiết bị xuất chuẩn.
Flimm

Đẹp! Tôi sẽ không thể tìm ra điều này bằng cách nhìn vào git help checkout. Tôi đã phải kiểm tra thư mục con vào một ngày nhất định và sử dụng phương pháp này, tôi có thể làm cho cú pháp này hoạt động:git checkout @{YYYY-MM-DD} sub-dir
haridsv

150

Bạn cần cung cấp đường dẫn đầy đủ đến tệp:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:full/repo/path/to/my_file.txt

7
không phải là con đường đầy đủ. Đường dẫn từ thư mục gốc git (những người đã vào git show --name-onlylà quá đủ
Mohsen

7
Erm, đường dẫn đầy đủ từ kho lưu trữ gốc. Hãy nhìn rõ hơn vào ví dụ tôi đã đưa ra. Không có dấu gạch chéo hàng đầu trước khi "đầy đủ".
Milan Babuškov

7
FYI, nếu bạn đang ở trong một thư mục con, bạn cũng có thể sử dụng ./filename.ext thành công.
Du khách

Tôi nghĩ vấn đề là, nếu bạn tham gia full/repo/path/tovà bạn thử : git show 27cf8e84:my_file.txt, bạn sẽ được thưởng một thông báo như: fatal: Path 'full / repo / path / to / my_file.txt', nhưng không phải 'my_file.txt' . Ý bạn là '27cf8e84: full / repo / path / to / my_file.txt' aka '27cf8e84: ./ my_file.txt'? Giống như, Git có thể đã giúp đỡ trực tiếp, nhưng đã chọn trở thành nhà mô phạm ở đây.
Ed Randall

101

Các đơn giản nhất cách là ghi:

git show HASH:file/path/name.ext > some_new_name.ext

Ở đâu:

  • HASH là số băm SHA-1 sửa đổi Git
  • file / path / name.ext là tên của tệp bạn đang tìm kiếm
  • some_new_name.ext là đường dẫn và tên nơi lưu tệp cũ

Thí dụ

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8:my_file.txt > my_file.txt.OLD

Điều này sẽ lưu my_file.txt từ phiên bản 27cf8e dưới dạng tệp mới có tên my_file.txt.OLD

Nó đã được thử nghiệm với Git 2.4.5.

Nếu bạn muốn truy xuất tệp đã xóa, bạn có thể sử dụng HASH~1(một cam kết trước khi HASH được chỉ định).

THÍ DỤ:

git show 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8~1:deleted_file.txt > deleted_file.txt

1
Thông tin bổ sung: Bạn có thể nhận HASH, ví dụ như với nhật ký git
xotix

@xotix Cảm ơn. Tôi đã nhận được tất cả lịch sử HASH cho một tệp cụ thể bằng cách sử dụnggit log file/path/name.ext
Sriram Kannan

11

Trong Windows, với Git Bash:

  • trong không gian làm việc của bạn, thay đổi thư mục vào thư mục chứa tệp của bạn
  • git show cab485c83b53d56846eb883babaaf4dff2f2cc46:./your_file.ext > old.ext

8

Và để kết xuất nó thành một tệp (ít nhất là trên Windows) - Git Bash:

$ echo "`git show 60d8bdfc:src/services/LocationMonitor.java`" >> LM_60d8bdfc.java

Các "trích dẫn là cần thiết để nó bảo tồn các dòng mới.


Đẹp một. +1. Bổ sung tốt về git showcú pháp tôi đề cập ở trên.
VonC

23
Tôi thực sự không hiểu tại sao bạn sẽ sử dụng echo, có hoặc không có dấu ngoặc kép. Và tôi không hiểu tại sao bạn lại muốn hình thức chuyển hướng đầu ra. Sẽ không tốt hơn nếu chỉ viết Nhưng tôi chưa bao giờ thấy nó hữu ích nhất khi giữ lại các kết thúc dòng dos trên Windows, vì bất kỳ công cụ văn bản nào khác ngoài notepad mà tôi đã sử dụng trên Windows đều xử lý tốt các dòng kiểu unix.
sootsnoot

4
git show 60d8bdfc: src / services / LocationMonitor.java >> LM_60d8bdfc.java làm việc cho tôi.
Mike6679

@Mike: bạn có trên windows không?
Mr_and_Mrs_D

2
không sử dụng dấu ngoặc kép vì nếu các ký tự tệp của bạn trông giống như một biến shell, tức là $ LANG, nó sẽ được thay thế. @ LưuViênPhúc là tiết kiệm. cũng không sử dụng >> Nó sẽ nối thêm tệp nếu nó tồn tại và có thể dẫn đến lỗi
vào

3

Điều này sẽ giúp bạn có được tất cả các tệp bị xóa giữa các lần xác nhận mà không chỉ định đường dẫn, hữu ích nếu có nhiều tệp bị xóa.

git diff --name-only --diff-filter=D $commit~1 $commit | xargs git checkout $commit~1

1
git checkout {SHA1} -- filename

lệnh này nhận được các tập tin sao chép từ cam kết cụ thể.


-2

Nhận tệp từ một cam kết trước đó thông qua kiểm tra cam kết trước đó và sao chép tệp.

  • Lưu ý bạn đang ở chi nhánh nào: chi nhánh git
  • Kiểm tra các cam kết trước đó bạn muốn: git checkout 27cf8e84bb88e24ae4b4b3df2b77aab91a3735d8
  • Sao chép tệp bạn muốn đến một vị trí tạm thời
  • Kiểm tra chi nhánh bạn bắt đầu từ: git checkout theBranchYouNoted
  • Sao chép trong tập tin bạn đã đặt ở một vị trí tạm thời
  • Cam kết thay đổi của bạn để git: git commit -m "added file ?? from previous commit"
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.