Liệt kê và xóa các cam kết Git không thuộc chi nhánh (lơ lửng?)


146

Tôi đã có một kho lưu trữ Git với rất nhiều cam kết không thuộc chi nhánh cụ thể nào, tôi có thể thực hiện git showchúng, nhưng khi tôi cố gắng liệt kê các nhánh có chứa chúng, nó không báo cáo gì cả.

Tôi nghĩ rằng đây là vấn đề cam kết / cây treo lủng lẳng (là kết quả của nhánh -D), vì vậy tôi đã cắt tỉa repo, nhưng tôi vẫn thấy hành vi tương tự sau đó:

$ git fetch origin

$ git fsck --unreachable
$ git fsck

Không có đầu ra, không có gì lơ lửng (phải không?). Nhưng cam kết tồn tại

$ git show 793db7f272ba4bbdd1e32f14410a52a412667042
commit 793db7f272ba4bbdd1e32f14410a52a412667042
Author: ...

và nó không thể truy cập thông qua bất kỳ chi nhánh như

$ git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

không cho đầu ra.

Chính xác thì trạng thái của cam kết đó là gì? Làm thế nào tôi có thể liệt kê tất cả các cam kết trong một trạng thái tương tự? Làm thế nào tôi có thể xóa các cam kết như thế?


Câu trả lời:


75

Không có đầu ra, không có gì lơ lửng (phải không?)

Lưu ý rằng các cam kết được đề cập từ reflog của bạn được coi là có thể truy cập.

Chính xác thì trạng thái của cam kết đó là gì? Làm thế nào tôi có thể liệt kê tất cả các cam kết với trạng thái tương tự

Vượt qua --no-reflogsđể thuyết phục git fsckđể hiển thị chúng cho bạn.

Làm thế nào tôi có thể xóa các cam kết như thế?

Khi các mục reflog của bạn đã hết hạn, các đối tượng đó cũng sẽ được dọn sạch git gc.

Hạn sử dụng được quy định bởi gc.pruneexpire, gc.reflogexpiregc.reflogexpireunreachablecài đặt. Cf. git help config.

Mặc định là khá hợp lý.


2
Vì vậy, về cơ bản bạn đang nói rằng các phản xạ cho các cam kết lơ lửng sẽ tự động bị xóa sau một thời gian?
MoralCode

2
Về cơ bản: có - ngoại trừ câu hỏi hơi khó hiểu. Tôi đang nói rằng tất cả các mục từ chối sẽ tự động bị xóa sau một thời gian, nhưng bạn có thể thay đổi thông qua cài đặt cấu hình. Và bởi vì một cam kết chỉ được gọi là lơ lửng khi nó không có gì để chỉ vào nó - bao gồm cả các mục nhập từ chối -, nên việc đăng ký lại cho các cam kết được treo lủng lẳng không phải là một điều. Họ sẽ được giới thiệu lại cho các cam kết không thể truy cập được .
Aristotle Pagaltzis

'Chúng sẽ là những lượt giới thiệu của người Viking vì những cam kết không thể truy cập được.' Nhưng bạn đã nói "các cam kết được đề cập từ reflog của bạn được coi là có thể truy cập." Vì vậy, làm thế nào "reflogs cho cam kết không thể truy cập" là một điều? Tôi thấy bối rối.
LarsH

1
Vâng, tôi đã không nhất quán. Thông thường mọi người không nghĩ về việc từ chối, và khi họ nói rằng không thể truy cập được thì điều đó có nghĩa là từ một giới thiệu. Thậm chí git help glossaryđịnh nghĩa nó theo cách đó trong khi định nghĩa của nó đối với mức độ có thể tiếp cận được thì không bị thu hẹp theo cách đó, do đó chúng trái ngược nhau. Hài hước - vì vậy những gì tôi nói thực sự phù hợp với sự nhầm lẫn trong gitglossarybài Nó không phải là những khái niệm khó hiểu, mặc dù, chỉ là thuật ngữ. Vấn đề là những lời cam kết của mối nguy hiểm là những thứ mà không có gì khác chỉ ra. Sẽ nó giúp đỡ nếu tôi nói “reflogs cho trường hợp cam kết unreachable” ...?
Aristotle Pagaltzis

Điều này rất khó hiểu. Hãy làm cho nó đơn giản. Khi ở chi nhánh master, bạn làm git commitvà nhận được một cam kết 000001. Sau đó, bạn làm git commit --amend, mà cung cấp cho bạn cam kết 000002. Không có thẻ hoặc nhánh nào trỏ đến 000001nữa và bạn không thể nhìn thấy nó trong nhật ký của mình mà không có --reflogtùy chọn, nhưng nếu bạn muốn, bạn vẫn có thể truy cập nó git checkout 000001. Bây giờ câu hỏi là, 000001một cam kết lơ lửng , hoặc một cam kết không thể truy cập , hoặc không, hoặc cả hai?
chharvey

264

Để loại bỏ tất cả các cam kết lơ lửng và những thứ có thể truy cập từ các reflog hãy làm điều này:

git reflog expire --expire-unreachable=now --all
git gc --prune=now

Nhưng hãy chắc chắn rằng đây là những gì bạn muốn. Tôi khuyên bạn nên đọc các trang hướng dẫn nhưng đây là ý chính:

git gcloại bỏ các đối tượng không thể truy cập (cam kết, cây, đốm (tập tin)). Một đối tượng là không thể truy cập nếu nó không phải là một phần của lịch sử của một số chi nhánh. Thật ra nó phức tạp hơn một chút:

git gc làm một số điều khác nhưng chúng không liên quan ở đây và không nguy hiểm.

Các đối tượng --prune=nowkhông thể truy cập dưới hai tuần không bị xóa vì vậy chúng tôi sử dụng có nghĩa là "xóa các đối tượng không thể truy cập được tạo trước đó".

Các đối tượng cũng có thể đạt được thông qua các reflog. Trong khi các chi nhánh ghi lại lịch sử của một số dự án, các bản cập nhật ghi lại lịch sử của các chi nhánh này. Nếu bạn sửa đổi, thiết lập lại, vv các cam kết sẽ bị xóa khỏi lịch sử chi nhánh nhưng git sẽ giữ chúng xung quanh trong trường hợp bạn nhận ra rằng mình đã phạm sai lầm. Reflog là một cách thuận tiện để tìm hiểu những hoạt động phá hoại (và khác) đã được thực hiện trên một nhánh (hoặc HEAD), giúp dễ dàng hoàn tác một hoạt động phá hoại.

Vì vậy, chúng tôi cũng phải loại bỏ các reflog để thực sự loại bỏ mọi thứ không thể truy cập từ một chi nhánh. Chúng tôi làm như vậy bằng cách hết hạn --allreflogs. Một lần nữa git giữ một chút các reflog để bảo vệ người dùng, vì vậy chúng tôi lại phải bảo nó đừng làm như vậy : --expire-unreachable=now.

Vì tôi chủ yếu sử dụng reflog để phục hồi từ các hoạt động phá hoại mà tôi thường sử dụng --expire=nowthay vào đó, điều này làm thay đổi hoàn toàn các reflog .


1
Tôi nói với bạn những lệnh nào để sử dụng không rõ ràng - không nên gc là đủ? Nếu bạn chưa bao giờ sử dụng git-reflog trước khi bạn không biết. Vì vậy, bây giờ bạn biết những lệnh nào bạn phải sử dụng, bạn nên tìm kiếm các tùy chọn được đề cập trong trang man của họ. Tất nhiên thay vào đó tôi chỉ có thể sao chép thông tin đó từ đó ...
tarsius

1
Thật ra tôi nói chính xác những gì nó làm: "loại bỏ tất cả các cam kết lơ lửng và những thứ có thể tiếp cận được từ các reflog". Nếu bạn không biết reflog là gì: hãy đọc lại hướng dẫn.
tarsius

7
Mặc dù câu trả lời được đưa ra có thể đúng, nhưng @ erikb85 lại đúng khi chỉ ra rằng không có sự giáo dục nào về những gì bạn được bảo phải làm. Theo dõi với RTFM thậm chí còn ít hữu ích hơn. Vâng, tất cả chúng ta nên đọc tất cả các tài liệu. Trong một số trường hợp, có lẽ người thực hiện tìm kiếm không tìm hiểu tài liệu đủ để biết chuyện gì đang xảy ra. Vì vậy, một chút giáo dục về những gì các lệnh đang làm sẽ hữu ích cho mọi người tìm thấy câu trả lời này sau.
Lee Saferite

@LeeSaferite hy vọng tất cả các bạn đều hạnh phúc ngay bây giờ :-)
tarsius

12
git reflog expire --expire-unreachable=now --allgiảm tất cả các stash của bạn!
Vsevolod Golovanov

22

Tôi đã có cùng một vấn đề, vẫn sau khi làm theo tất cả các lời khuyên trong chủ đề này:

git reflog expire --expire-unreachable=now --all
git gc --prune=now
git fsck --unreachable --no-reflogs   # no output
git branch -a --contains <commit>     # no output
git show <commit>                     # still shows up

Nếu nó không phải là một reflog và không phải là một nhánh, ... nó phải là một thẻ !

git tag                             # showed several old tags created before the cleanup

Tôi đã xóa các thẻ với git tag -d <tagname>và làm sạch việc dọn dẹp, và các cam kết cũ đã biến mất.


Đã có câu trả lời về các thẻ ( stackoverflow.com/a/37335660/450127 ) và dường như điều này không bổ sung bất cứ điều gì mới. Không nên loại bỏ điều này có lợi cho câu trả lời trước đó?
Ian Dunn

Thật vậy, bằng cách nào đó tôi đã bỏ qua câu trả lời đó. Mặc dù 4 người thấy câu trả lời của tôi hữu ích, nhưng có lẽ nó không vô dụng? Ngoài ra tôi nhóm tất cả các khả năng thành một câu trả lời súc tích.
jakub.g

1
Ngay cả khi được sao chép, trang này có thể xuất hiện trong Kết quả của Google và nó ngay lập tức giúp những người có cùng vấn đề, tốt hơn là chỉ chuyển hướng mọi người nhiều lần đến các liên kết có thể có câu trả lời chính xác.
Alexandre T.

14
git branch --contains 793db7f272ba4bbdd1e32f14410a52a412667042

có lẽ chỉ cần là

git branch -a --contains 793db7f272ba4bbdd1e32f14410a52a412667042

để báo cáo về các chi nhánh từ xa


cảm ơn, bây giờ tôi đã tìm thấy điều khiển từ xa / nguồn gốc / tiếp theo mà vẫn giữ cam kết này. Làm thế nào để loại bỏ nó? git push -d origin nextkhông giúp được gì
iRaS


cảm ơn - git fetch --pruneđã làm mánh khóe nhưng trong tất cả các câu trả lời tôi thiếu một kiểm tra cho các thẻ đang tham chiếu cam kết này. Tôi vẫn không biết cách kiểm tra các thẻ bằng một cam kết (tôi đã xóa tất cả).
iRaS

Nhưng ... điều này có nghĩa là các cam kết chỉ có thể truy cập từ các nhánh từ xa (và không có các nhánh cục bộ) được coi là có thể truy cập, và do đó git fsck --unreachablethực sự giao tiếp qua mạng với điều khiển từ xa để tìm ra các cam kết nào có thể truy cập được?
LarsH

1
Đã trả lời câu hỏi của riêng tôi ... vâng, các cam kết chỉ có thể truy cập từ các chi nhánh từ xa (và không có chi nhánh địa phương) được coi là có thể truy cập; nhưng git fsck --unreachablekhông cần giao tiếp qua mạng với điều khiển từ xa để tìm ra các nhánh từ xa nào chứa các cam kết. Thông tin chi nhánh từ xa được lưu trữ cục bộ, ví dụ .git/refs/remotes/origin(hoặc trong packed-refs).
LarsH

8

Tôi đã có một vấn đề tương tự. Tôi đã chạy git branch --contains <commit>, và nó không trả lại kết quả nào giống như trong câu hỏi.

Nhưng ngay cả sau khi chạy

git reflog expire --expire-unreachable=now --all
git gc --prune=now

cam kết của tôi vẫn có thể truy cập bằng cách sử dụng git show <commit>. Điều này là do một trong những cam kết trong "nhánh" tách rời / được treo của nó đã được gắn thẻ. Tôi xóa thẻ, chạy lại các lệnh trên và tôi rất tuyệt. git show <commit>trả lại fatal: bad object <commit>- chính xác những gì tôi cần. Hy vọng rằng điều này sẽ giúp người khác bị mắc kẹt như tôi.


Làm thế nào bạn xóa thẻ?
bapors

@bapors Liệt kê tất cả các thẻ, tìm một thẻ tham chiếu đến cam kết trong câu hỏi và sau đó xóa nó. stackoverflow.com/questions/5480258/ Mạnh
Andrew Larsson

4

Tôi đã vô tình gặp phải tình huống tương tự và thấy các bản lưu trữ của mình chứa tham chiếu đến các cam kết không thể truy cập được và do đó, các cam kết không thể truy cập được cho là có thể truy cập được từ các bản sao lưu.

Đây là những gì tôi đã làm để làm cho nó thực sự không thể truy cập.

git stash clear
git reflog expire --expire-unreachable=now --all
git fsck --unreachable
git gc --prune=now

2

git gc --prune=<date>mặc định để cắt tỉa các đối tượng cũ hơn hai tuần trước. Bạn có thể thiết lập một ngày gần đây hơn. Tuy nhiên, các lệnh git tạo các đối tượng lỏng lẻo thường sẽ chạy git gc --auto (sẽ cắt các đối tượng lỏng lẻo nếu số lượng của chúng vượt quá giá trị của biến cấu hình gc.auto).

Bạn có chắc chắn muốn xóa những cam kết này không? Cài đặt mặc định của gc.auto sẽ đảm bảo rằng các đối tượng lỏng lẻo không chiếm một lượng bộ nhớ không hợp lý và lưu trữ các đối tượng lỏng lẻo trong một khoảng thời gian nói chung là một ý tưởng tốt. Bằng cách đó, nếu bạn nhận ra ngày mai rằng chi nhánh bị xóa của bạn chứa một cam kết bạn cần, bạn có thể khôi phục nó.

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.