Làm cách nào để xóa lịch sử cũ khỏi kho git?


208

Tôi sợ rằng tôi không thể tìm thấy bất cứ điều gì khá giống kịch bản cụ thể này.

Tôi có một kho lưu trữ git với rất nhiều lịch sử: 500+ chi nhánh, hơn 500 thẻ, trở lại giữa năm 2007. Nó chứa ~ 19.500 cam kết. Chúng tôi muốn xóa tất cả lịch sử trước ngày 1 tháng 1 năm 2010, để làm cho nó nhỏ hơn và dễ xử lý hơn (chúng tôi sẽ giữ một bản sao hoàn chỉnh của lịch sử trong kho lưu trữ).

Tôi biết cam kết mà tôi muốn trở thành gốc của kho lưu trữ mới. Tuy nhiên, tôi không thể tìm ra git mojo chính xác để cắt bớt repo để bắt đầu với cam kết đó. Tôi đoán một số biến thể của

git filter-branch

liên quan đến ghép sẽ là cần thiết; nó cũng có thể là cần thiết để đối xử với nhau của hơn 200 chi nhánh, chúng tôi muốn giữ riêng biệt và sau đó vá các repo trở lại với nhau (cái gì tôi làm biết làm thế nào để làm).

Có ai đã từng làm một cái gì đó như thế này? Tôi đã có git 1.7.2.3 nếu vấn đề đó.

Câu trả lời:


118

Chỉ cần tạo một bản ghép của cha mẹ của cam kết gốc mới của bạn không có cha mẹ (hoặc với một cam kết trống, ví dụ như cam kết gốc thực sự của kho lưu trữ của bạn). Ví dụecho "<NEW-ROOT-SHA1>" > .git/info/grafts

Sau khi tạo mảnh ghép, nó có hiệu lực ngay lập tức; bạn sẽ có thể nhìn vào git logvà thấy rằng các cam kết cũ không mong muốn đã biến mất:

$ echo 4a46bc886318679d8b15e05aea40b83ff6c3bd47 > .git/info/grafts
$ git log --decorate | tail --lines=11
commit cb3da2d4d8c3378919844b29e815bfd5fdc0210c
Author: Your Name <your.email@example.com>
Date:   Fri May 24 14:04:10 2013 +0200

    Another message

commit 4a46bc886318679d8b15e05aea40b83ff6c3bd47 (grafted)
Author: Your Name <your.email@example.com>
Date:   Thu May 23 22:27:48 2013 +0200

    Some message

Nếu tất cả trông như dự định, bạn chỉ có thể làm một cách đơn giản git filter-branch -- --allđể làm cho nó vĩnh viễn.

THƯỞNG: sau khi thực hiện bước phân nhánh bộ lọc , tất cả các id xác nhận sẽ thay đổi, vì vậy bất kỳ ai sử dụng repo cũ không bao giờ được hợp nhất với bất kỳ ai sử dụng repo mới.


6
Tôi đã phải làm git filter-branch --tag-name-filter cat -- --allđể cập nhật thẻ. Nhưng tôi cũng có các thẻ cũ hơn chỉ vào lịch sử cũ mà tôi muốn xóa. Làm thế nào tôi có thể thoát khỏi tất cả các thẻ cũ? Nếu tôi không xóa chúng, thì lịch sử cũ hơn sẽ không biến mất và tôi vẫn có thể xem nó với gitk --all.
Craig McQueen

9
"Chỉ cần tạo một mảnh ghép của cha mẹ của cam kết gốc mới của bạn không có cha mẹ" cần một số chi tiết. Tôi đã thử điều đó và thất bại trong việc tìm ra cú pháp cho "không có cha mẹ". Trang thủ công yêu cầu ID cam kết cha mẹ là bắt buộc; sử dụng tất cả các số 0 chỉ cho tôi một lỗi.
Marius Gedminas

6
Trong trường hợp bất cứ ai khác tự hỏi làm thế nào nó hoạt động chính xác, nó khá dễ dàng:echo "<NEW-ROOT-HASH>" > .git/info/grafts
Friederbluemle 9/12/13

3
Tôi đồng ý, giải thích những gì một mảnh ghép sẽ hữu ích hơn
Charles Martin

4
Trích dẫn từ trang wiki được liên kết trên các mảnh ghép. "Kể từ Git 1.6.5, thay thế git linh hoạt hơn đã được thêm vào, cho phép bạn thay thế bất kỳ đối tượng nào bằng bất kỳ đối tượng nào khác và theo dõi các liên kết thông qua các ref có thể được đẩy và kéo giữa các repos." Vì vậy, câu trả lời này thể đã lỗi thời đối với các phiên bản hiện tại của git.
ThorSummoner

129

Có lẽ đã quá muộn để gửi bài trả lời, nhưng vì trang này là kết quả đầu tiên của Google, nên nó vẫn có thể hữu ích.

Nếu bạn muốn giải phóng một số không gian trong repo git của mình, nhưng không muốn xây dựng lại tất cả các cam kết của mình (rebase hoặc ghép) và vẫn có thể đẩy / kéo / hợp nhất từ ​​những người có repo đầy đủ, bạn có thể sử dụng git clone nông clone ( tham số --depth ).

; Clone the original repo into limitedRepo
git clone file:///path_to/originalRepo limitedRepo --depth=10

; Remove the original repo, to free up some space
rm -rf originalRepo
cd limitedRepo
git remote rm origin

Bạn có thể nông cạn repo hiện tại của mình, bằng cách làm theo các bước sau:

; Shallow to last 5 commits
git rev-parse HEAD~5 > .git/shallow

; Manually remove all other branches, tags and remotes that refers to old commits

; Prune unreachable objects
git fsck --unreachable ; Will show you the list of what will be deleted
git gc --prune=now     ; Will actually delete your data

Làm thế nào để loại bỏ tất cả các thẻ địa phương git?

Ps: Các phiên bản cũ hơn của git không hỗ trợ sao chép / đẩy / kéo từ / đến repos nông.


9
1 Đây là những câu trả lời đúng cho các phiên bản mới hơn của Git. (Ồ, và vui lòng quay lại PPCG !)
wizzwizz4

6
Làm thế nào bạn có thể cdđến một thư mục vừa bị xóa? Tôi cảm thấy như thiếu một số thông tin ở đây. Ngoài ra, có cách nào để áp dụng những thay đổi này cho repo từ xa không?
Trogdor

4
@Jez Đó sẽ là câu trả lời được bình chọn hàng đầu khác. Câu trả lời này không dành cho bạn nếu bạn muốn thoát khỏi lịch sử vĩnh viễn. Đó là để làm việc với lịch sử lớn.
Không ai là

4
Để trả lời câu hỏi của riêng tôi: git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02Hoạt động như một lá bùa!
Kính hiển vi

5
@Jez bạn có thể chuyển đổi repo nông của bạn thành bình thường bằng cách chạy git filter-branch -- --all. Điều này sẽ thay đổi tất cả các giá trị băm trong đó nhưng sau đó bạn sẽ có thể đẩy nó sang một repo mới
Ed'ka

60

Đây phương pháp rất dễ dàng để hiểu và hoạt động tốt. Đối số cho tập lệnh ( $1) là một tham chiếu (thẻ, hàm băm, ...) cho cam kết bắt đầu từ đó bạn muốn giữ lịch sử của mình.

#!/bin/bash
git checkout --orphan temp $1 # create a new branch without parent history
git commit -m "Truncated history" # create a first commit on this branch
git rebase --onto temp $1 master # now rebase the part of master branch that we want to keep onto this branch
git branch -D temp # delete the temp branch

# The following 2 commands are optional - they keep your git repo in good shape.
git prune --progress # delete all the objects w/o references
git gc --aggressive # aggressively collect garbage; may take a lot of time on large repos

LƯU Ý rằng các thẻ cũ sẽ vẫn còn hiện diện; vì vậy bạn có thể cần phải loại bỏ chúng bằng tay

nhận xét: Tôi biết điều này gần giống với aswer như @yoyodin, nhưng có một số lệnh và thông tin bổ sung quan trọng ở đây. Tôi đã cố chỉnh sửa câu trả lời, nhưng vì đó là một thay đổi đáng kể đối với câu trả lời của @ yoyodin, bản chỉnh sửa của tôi đã bị từ chối, vì vậy đây là thông tin!


Tôi đánh giá cao những giải thích được đưa ra cho git prunegit gccác lệnh. Có một lời giải thích cho phần còn lại của các lệnh trong kịch bản? Khi nó đứng, không rõ đối số nào đang được truyền cho nó và mỗi lệnh đang làm gì. Cảm ơn.
dùng5359531

2
@ user5359531 cảm ơn bạn đã nhận xét, tôi đã thêm một số nhận xét cho mỗi lệnh. Hi vọng điêu nay co ich.
Chris Maes

4
Hợp nhất các xung đột ở khắp mọi nơi ... không hữu ích lắm
Warpzit

3
@Warpzit Tôi đã thoát khỏi xung đột hợp nhất bằng cách thêm -pvào rebaselệnh, như được đề xuất trong câu trả lời khác
leonbloy

1
Tôi đã làm theo chính xác điều này, và tất cả những gì tôi nhận được là cùng một lịch sử như trước đây với một chi nhánh mới bắt đầu từ cam kết mà tôi muốn cắt tỉa với tất cả lịch sử giống như trước đây. Không có lịch sử đã được gỡ bỏ.
DrStrangepork

51

Hãy thử phương pháp này Làm thế nào để cắt bớt lịch sử git :

#!/bin/bash
git checkout --orphan temp $1
git commit -m "Truncated history"
git rebase --onto temp $1 master
git branch -D temp

Dưới đây $1là SHA-1 trong tổng số cam kết mà bạn muốn giữ và kịch bản sẽ tạo ra chi nhánh mới có chứa tất cả các cam kết giữa $1mastervà tất cả lịch sử cũ được giảm. Lưu ý rằng tập lệnh đơn giản này giả định rằng bạn không có nhánh hiện có được gọi temp. Cũng lưu ý rằng tập lệnh này không xóa dữ liệu git cho lịch sử cũ. Chạy git gc --prune=all && git repack -a -f -F -dsau khi bạn xác minh rằng bạn thực sự muốn mất tất cả lịch sử. Bạn cũng có thể cần rebase --preserve-mergesnhưng được cảnh báo rằng việc thực hiện git của tính năng đó là không hoàn hảo. Kiểm tra kết quả bằng tay nếu bạn sử dụng đó.


22
Tôi đã thử điều này, nhưng đã có xung đột hợp nhất trong rebasebước này. Lạ - Tôi không ngờ rằng những xung đột hợp nhất có thể xảy ra trong những trường hợp này.
Craig McQueen

2
Sử dụng git commit --allow-empty -m "Truncate history"nếu cam kết bạn đã kiểm tra không chứa bất kỳ tệp nào.
Friederbluemle

2
Làm thế nào để tôi đẩy nó trở lại chủ từ xa? Khi tôi làm điều đó, tôi kết thúc với cả lịch sử cũ và mới.
rustyx

1
'Temp' được cho là gì? Những gì bạn có nghĩa vụ phải vượt qua như là một đối số cho điều này? Có một ví dụ về những gì các lệnh này được cho là trông giống như khi bạn thực sự chạy chúng? Cảm ơn.
dùng5359531

1
Tôi tin rằng $ 1 là hàm băm cam kết. (Có nhiều chi tiết được cung cấp trong bài viết được liên kết).
Chris Nolet

34

Để thay thế cho viết lại lịch sử, xem xét sử dụng git replacenhư trong bài viết này từ Git Pro cuốn sách . Ví dụ được thảo luận liên quan đến việc thay thế một cam kết cha mẹ để mô phỏng sự khởi đầu của một cây, trong khi vẫn giữ toàn bộ lịch sử như một nhánh riêng để giữ an toàn.


Vâng, tôi nghĩ rằng bạn có thể có thể làm những gì chúng tôi muốn với điều đó, nếu bạn cũng giành được nhánh lịch sử đầy đủ riêng biệt. (Chúng tôi đã cố gắng thu nhỏ kho lưu trữ.)
ebneter

1
Tôi đã nản lòng với câu trả lời ngoài trang web; nhưng nó liên kết đến trang GitScm và hướng dẫn mà nó liên kết đến được viết rất tốt và dường như trực tiếp đến điểm của câu hỏi của OP.
ThorSummoner

@ThorSummoner Xin lỗi về điều đó! Tôi sẽ phát triển câu trả lời đầy đủ hơn một chút tại chỗ
Jeff Bowman

Thật không may, đây không phải là một thay thế để viết lại lịch sử. Có một câu khó hiểu trong phần đầu của bài viết có lẽ đã mang lại ấn tượng này. Điều đó có thể được loại bỏ khỏi câu trả lời này? Bạn sẽ thấy trong bài viết rằng tác giả viết lại lịch sử của nhánh bị cắt, nhưng đề xuất một cách gắn lại nhánh "lịch sử" kế thừa bằng cách sử dụng git replace. Tôi tin rằng điều này đã được sửa chữa trên một câu hỏi khác, nơi bạn đăng câu trả lời này.
Mitch

1
Một cuộc thảo luận về git replaceso với git graftđược thực hiện tại stackoverflow.com/q/6800692/873282
koppor

25

Nếu bạn muốn giữ các thượng nguồn kho với lịch sử đầy đủ , nhưng Thanh toán nhỏ địa phương, làm một bản sao cạn với git clone --depth=1 [repo].

Sau khi đẩy một cam kết, bạn có thể làm

  1. git fetch --depth=1để cắt tỉa các cam kết cũ. Điều này làm cho các cam kết cũ và các đối tượng của họ không thể truy cập được.
  2. git reflog expire --expire-unreachable=now --all. Để hết hạn các cam kết cũ và các đối tượng của họ
  3. git gc --aggressive --prune=all để loại bỏ các đối tượng cũ

Xem thêm Làm thế nào để xóa lịch sử git cục bộ sau khi cam kết? .

Lưu ý rằng bạn không thể đẩy kho lưu trữ "nông" này sang nơi khác: "không được phép cập nhật nông". Xem Remote bị từ chối (không được phép cập nhật nông) sau khi thay đổi URL từ xa Git . Nếu bạn muốn điều đó, bạn phải gắn bó với ghép.


1
Điểm số 1. đã tạo ra sự khác biệt cho tôi. Chúc mừng
clapas

21

Tôi cần đọc một số câu trả lời và một số thông tin khác để hiểu những gì tôi đang làm.

1. Bỏ qua mọi thứ cũ hơn một cam kết nhất định

Các tập tin .git/info/graftscó thể xác định cha mẹ giả cho một cam kết. Một dòng chỉ có một id xác nhận, nói rằng cam kết không có cha mẹ. Nếu chúng tôi muốn nói rằng chúng tôi chỉ quan tâm đến 2000 lần xác nhận gần nhất, chúng tôi có thể gõ:

git rev-parse HEAD~2000 > .git/info/grafts

git rev-parse cung cấp cho chúng tôi id xác nhận của cha mẹ thứ 2000 của cam kết hiện tại. Lệnh trên sẽ ghi đè lên tệp ghép nếu có. Kiểm tra nếu nó ở đó đầu tiên.

2. Viết lại lịch sử Git (tùy chọn)

Nếu bạn muốn làm cho cha mẹ giả ghép này thành một người thật, thì hãy chạy:

git filter-branch -- --all

Nó sẽ thay đổi tất cả các id cam kết. Mỗi bản sao của kho lưu trữ này cần phải được cập nhật mạnh mẽ.

3. Dọn dẹp không gian đĩa

Tôi đã không thực hiện bước 2, vì tôi muốn bản sao của mình tương thích với dòng ngược. Tôi chỉ muốn tiết kiệm một số không gian đĩa. Để quên tất cả các cam kết cũ:

git prune
git gc

Thay thế: bản sao nông

Nếu bạn có một bản sao nông của một kho lưu trữ khác và chỉ muốn tiết kiệm một số dung lượng đĩa, bạn có thể cập nhật .git/shallow. Nhưng hãy cẩn thận rằng không có gì chỉ vào một cam kết từ trước. Vì vậy, bạn có thể chạy một cái gì đó như thế này:

git fetch --prune
git rev-parse HEAD~2000 > .git/shallow
git prune
git gc

Các mục trong các công trình nông như ghép. Nhưng hãy cẩn thận không sử dụng mảnh ghép và nông cùng một lúc. Ít nhất, không có các mục tương tự trong đó, nó sẽ thất bại.

Nếu bạn vẫn còn một số tài liệu tham khảo cũ (thẻ, chi nhánh, đầu từ xa) trỏ đến các cam kết cũ hơn, chúng sẽ không được dọn sạch và bạn sẽ không tiết kiệm thêm dung lượng đĩa.


Hỗ trợ cho <GIT_DIR> / thông tin / ghép không được chấp nhận và sẽ bị xóa trong phiên bản Git trong tương lai.
danny

Hãy xem xét sử dụng git replacethay thế. Xem stackoverflow.com/questions/6800692/
Joel AZEMAR

3

Khi rebase hoặc đẩy lên đầu / chủ lỗi này có thể xảy ra

remote: GitLab: You are not allowed to access some of the refs!
To git@giturl:main/xyz.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'git@giturl:main/xyz.git'

Để giải quyết vấn đề này trong bảng điều khiển git, nên xóa nhánh chính khỏi "Nhánh được bảo vệ"

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

sau đó bạn có thể chạy lệnh này

git push -f origin master

hoặc là

git rebase --onto temp $1 master

0

Có quá nhiều câu trả lời ở đây không hiện tại và một số không giải thích đầy đủ về hậu quả. Đây là những gì làm việc cho tôi để cắt giảm lịch sử bằng cách sử dụng git 2.26 mới nhất:

Đầu tiên tạo ra một cam kết giả. Cam kết này sẽ xuất hiện dưới dạng cam kết đầu tiên trong repo bị cắt ngắn của bạn. Bạn cần điều này bởi vì cam kết này sẽ giữ tất cả các tệp cơ sở cho lịch sử bạn đang lưu giữ. SHA là ID của cam kết trước đó của cam kết bạn muốn giữ (trong ví dụ này, 8365366). Chuỗi 'Ban đầu' sẽ hiển thị dưới dạng thông báo cam kết của lần xác nhận đầu tiên. Nếu bạn đang sử dụng Windows, hãy gõ lệnh dưới đây từ dấu nhắc lệnh Git Bash.

# 8365366 is id of parent commit after which you want to preserve history
echo 'Initial' | git commit-tree 8365366^{tree}

Lệnh trên sẽ in SHA, ví dụ , d10f7503bc1ec9d367da15b540887730db862023.

Bây giờ chỉ cần gõ:

# d10f750 is commit ID from previous command
git rebase --onto d10f750 8365366

Điều này trước tiên sẽ đặt tất cả các tệp theo cam kết 8365366vào cam kết giả d10f750. Sau đó, nó sẽ phát lại tất cả các cam kết sau 8365366 trên đầu trang d10f750. Cuối cùng, mastercon trỏ nhánh sẽ được cập nhật để phát lại lần cuối.

Bây giờ nếu bạn muốn đẩy các repo bị cắt ngắn này, chỉ cần làm git push -f.

Vài điều cần lưu ý (những điều này áp dụng cho các phương pháp khác cũng như phương pháp này): Thẻ không được chuyển. Trong khi ID cam kết và dấu thời gian được giữ nguyên, bạn sẽ thấy GitHub hiển thị các cam kết này trong tiêu đề lumpum như thế nào Commits on XY date.

May mắn thay, có thể giữ lịch sử cắt ngắn là "lưu trữ" và sau đó bạn có thể tham gia repo cắt lại với kho lưu trữ repo. Để làm điều này, xem hướng dẫn này .


-3

bạn có thể xóa thư mục, tệp và toàn bộ lịch sử liên quan đến thư mục hoặc tệp bằng cách sử dụng tệp jar được đề cập bên dưới [tải xuống] và các lệnh

tệp bfg.jar: https://rtyley.github.io/bfg-repo-cleaner/

git clone --bare repo-url cd repo_dir java -jar bfg.jar --delete-thư mục_name git reflog expire --Exire = now --all && git gc --prune = now --agultive git đẩy --mirror repo_url


-10
  1. xóa dữ liệu git, rm .git
  2. git init
  3. thêm một điều khiển từ xa
  4. lực đẩy

6
điều đó sẽ có tác dụng xóa bỏ TẤT CẢ lịch sử, nhưng không phải vì những gì anh ta yêu cầu: hãy giữ lịch sử kể từ tháng 1 năm 2010
Chris Maes

1
Chỉ muốn nói lời cảm ơn vì nó đã giúp tôi trong kịch bản của mình mặc dù điều này có thể không phải là câu trả lời đúng cho câu hỏi
xin lỗi
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.