Xóa các tệp nhạy cảm và cam kết của chúng khỏi lịch sử Git


353

Tôi muốn đặt một dự án Git trên GitHub nhưng nó chứa một số tệp nhất định có dữ liệu nhạy cảm (tên người dùng và mật khẩu, như /config/deploy.rb cho capistrano).

Tôi biết tôi có thể thêm các tên tệp này vào .gitignore , nhưng điều này sẽ không xóa lịch sử của chúng trong Git.

Tôi cũng không muốn bắt đầu lại bằng cách xóa thư mục /.git.

Có cách nào để xóa tất cả dấu vết của một tệp cụ thể trong lịch sử Git của bạn không?



Câu trả lời:


448

Đối với tất cả các mục đích thực tế, điều đầu tiên bạn nên lo lắng là THAY ĐỔI MẬT KHẨU CỦA BẠN! Không rõ câu hỏi của bạn cho dù kho git của bạn là hoàn toàn cục bộ hay bạn đã có một kho lưu trữ từ xa ở nơi khác chưa; nếu nó ở xa và không được bảo mật từ những người khác thì bạn có vấn đề. Nếu bất cứ ai đã sao chép kho lưu trữ đó trước khi bạn sửa lỗi này, họ sẽ có một bản sao mật khẩu của bạn trên máy cục bộ của họ và không có cách nào bạn có thể buộc họ cập nhật lên phiên bản "đã sửa" của bạn từ lịch sử. Điều an toàn duy nhất bạn có thể làm là thay đổi mật khẩu của mình thành một thứ khác ở mọi nơi bạn đã sử dụng nó.


Với cách đó, đây là cách khắc phục. GitHub đã trả lời chính xác câu hỏi đó dưới dạng Câu hỏi thường gặp :

Lưu ý cho người dùng Windows : sử dụng dấu ngoặc kép (") thay vì đơn trong lệnh này

git filter-branch --index-filter \
'git update-index --remove PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' <introduction-revision-sha1>..HEAD
git push --force --verbose --dry-run
git push --force

Cập nhật 2019:

Đây là mã hiện tại từ FAQ:

  git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA" \
  --prune-empty --tag-name-filter cat -- --all
  git push --force --verbose --dry-run
  git push --force

Hãy nhớ rằng một khi bạn đã đẩy mã này vào một kho lưu trữ từ xa như GitHub và những người khác đã sao chép kho lưu trữ từ xa đó, thì bây giờ bạn đang ở trong tình huống bạn đang viết lại lịch sử. Khi những người khác thử kéo xuống những thay đổi mới nhất của bạn sau đó, họ sẽ nhận được một thông báo cho biết rằng những thay đổi đó không thể được áp dụng vì đó không phải là một chuyển tiếp nhanh.

Để khắc phục điều này, họ sẽ phải xóa kho lưu trữ hiện tại của mình và sao chép lại hoặc làm theo hướng dẫn trong phần "TÌM KIẾM TỪ UPSTREAM REBASE" trong trang web git-rebase .

Mẹo : Thực thigit rebase --interactive


Trong tương lai, nếu bạn vô tình thực hiện một số thay đổi với thông tin nhạy cảm nhưng bạn nhận thấy trước khi đẩy vào kho lưu trữ từ xa, có một số cách khắc phục dễ dàng hơn. Nếu lần cam kết cuối cùng của bạn là lần thêm thông tin nhạy cảm, bạn chỉ cần xóa thông tin nhạy cảm, sau đó chạy:

git commit -a --amend

Điều đó sẽ sửa đổi cam kết trước đó với bất kỳ thay đổi mới nào bạn đã thực hiện, bao gồm toàn bộ xóa tệp được thực hiện với a git rm. Nếu các thay đổi được quay lại trong lịch sử nhưng vẫn không được đẩy vào kho lưu trữ từ xa, bạn có thể thực hiện phản hồi tương tác:

git rebase -i origin/master

Điều đó mở ra một trình soạn thảo với các cam kết bạn đã thực hiện kể từ tổ tiên chung cuối cùng của bạn với kho lưu trữ từ xa. Thay đổi "chọn" thành "chỉnh sửa" trên bất kỳ dòng nào thể hiện cam kết với thông tin nhạy cảm, đồng thời lưu và thoát. Git sẽ duyệt qua các thay đổi và để bạn ở một nơi mà bạn có thể:

$EDITOR file-to-fix
git commit -a --amend
git rebase --continue

Đối với mỗi thay đổi với thông tin nhạy cảm. Cuối cùng, bạn sẽ trở lại chi nhánh của mình và bạn có thể đẩy những thay đổi mới một cách an toàn.


5
Anh chàng hoàn hảo, đó là một câu trả lời tuyệt vời. Bạn tiết kiệm ngày của tôi.
zzeroo

18
Chỉ cần thêm một bit - trên Windows, bạn nên sử dụng dấu ngoặc kép (") thay vì đơn.
ripper234

4
Có cái này để làm việc. Tôi đã bị mất trong bản dịch. Tôi đã sử dụng liên kết thay vì lệnh ở đây. Ngoài ra, lệnh Windows cuối cùng yêu cầu dấu ngoặc kép như ripper234 đề cập, đường dẫn đầy đủ như MigDus gợi ý và không bao gồm các ký tự "\" mà liên kết đã dán làm chỉ báo gói dòng mới. Lệnh cuối cùng trông giống như: git filter-Branch --force --index-filter "git rm --cached --ignore-unmatch src [Project] [File]. [Ext]" --prune-blank --tag- mèo lọc tên
Eric Swanson

3
Dường như có một số khác biệt đáng kể giữa filter-branchmã của bạn và trong trang github mà bạn đã liên kết đến. Ví dụ: dòng thứ 3 của họ --prune-empty --tag-name-filter cat -- --all. Có giải pháp thay đổi hoặc tôi đang thiếu một cái gì đó?
địa lý

2
Giải pháp này có vẻ khá tốt, nhưng nếu tôi đã giới thiệu tệp cần xóa trong cam kết ban đầu <introduction-revision-sha1>..HEADthì không hoạt động. Nó chỉ loại bỏ các tập tin từ cam kết thứ hai trở đi. (Làm cách nào để đưa cam kết ban đầu vào phạm vi cam kết?) Cách lưu được chỉ ra ở đây: help.github.com/articles/ợi git filter-branch --force --index-filter \ 'git rm --cached --ignore-unmatch PATH-TO-YOUR-FILE-WITH-SENSITIVE-DATA' \ --prune-empty --tag-name-filter cat -- --all
white_gecko

91

Thay đổi mật khẩu của bạn là một ý tưởng hay, nhưng đối với quá trình xóa mật khẩu khỏi lịch sử của bạn, tôi khuyên dùng BFG Repo-Cleaner , một cách thay thế nhanh hơn, đơn giản hơn để git-filter-branchthiết kế rõ ràng để xóa dữ liệu riêng tư khỏi repit Git.

Tạo một private.txt tệp liệt kê các mật khẩu, v.v., mà bạn muốn xóa (một mục nhập trên mỗi dòng) và sau đó chạy lệnh này:

$ java -jar bfg.jar  --replace-text private.txt  my-repo.git

Tất cả các tệp có kích thước ngưỡng (1MB theo mặc định) trong lịch sử của người dùng của bạn sẽ được quét và bất kỳ chuỗi phù hợp nào (không có trong cam kết mới nhất của bạn ) sẽ được thay thế bằng chuỗi "*** REMOVED ***". Sau đó bạn có thể sử dụnggit gc để dọn sạch dữ liệu chết:

$ git gc --prune=now --aggressive

BFG thường nhanh hơn 10-50 lần so với khi chạy git-filter-branchvà các tùy chọn được đơn giản hóa và tùy chỉnh xung quanh hai trường hợp sử dụng phổ biến này:

  • Xóa tập tin Crazy Big
  • Xóa mật khẩu, thông tin xác thựcdữ liệu riêng tư khác

Tiết lộ đầy đủ: Tôi là tác giả của BFG Repo-Cleaner.


Đây là một tùy chọn, nhưng nó có thể phá vỡ ứng dụng của bạn khi mật khẩu được sử dụng, ví dụ để thiết lập kết nối cơ sở dữ liệu. Tôi thích câu trả lời hiện được chấp nhận vì có thể vẫn giữ mật khẩu trong bản sao làm việc của bạn và bỏ qua các tệp có chứa .gitignore.
Henridv

6
Đây là một chiến thắng lớn ngay tại đây. Sau một vài lần thử, tôi đã có thể sử dụng điều này để loại bỏ các cam kết có chứa thông tin nhạy cảm từ một repo riêng rất kỹ và cập nhật mạnh mẽ repo từ xa với lịch sử sửa đổi. Một lưu ý là bạn phải đảm bảo rằng phần đầu của repo của bạn sạch sẽ không có dữ liệu nhạy cảm vì cam kết này được coi là "được bảo vệ" và sẽ không được sửa đổi bởi công cụ này. Nếu không, chỉ cần làm sạch / thay thế bằng tay và git commit. Mặt khác, +1 cho công cụ mới trong hộp công cụ của nhà phát triển :)
Matt Borja

1
@Henridv Theo nhận xét gần đây của tôi, nó không nên phá vỡ ứng dụng của bạn như bạn có thể dự đoán, giả sử ứng dụng của bạn hiện đang nằm ở đầu hoặc đầu chi nhánh của bạn (tức là cam kết mới nhất). Công cụ này sẽ báo cáo rõ ràng cho cam kết cuối cùng của bạn These are your protected commits, and so their contents will NOT be alteredtrong khi duyệt qua và sửa đổi phần còn lại của lịch sử cam kết của bạn. Tuy nhiên, nếu bạn cần quay ngược lại, thì có, bạn sẽ chỉ cần thực hiện tìm kiếm ***REMOVED***trong cam kết mà bạn vừa quay lại.
Matt Borja

1
+1 cho BFG (nếu bạn đã cài đặt Java hoặc không nhớ cài đặt nó). Một lưu ý là BFG từ chối xóa một tập tin nếu nó được chứa trong HEAD. Vì vậy, tốt hơn hết là trước tiên hãy thực hiện một cam kết trong đó các tệp mong muốn sẽ bị xóa và chỉ sau đó chạy BFG. Sau đó, bạn có thể hoàn nguyên cam kết cuối cùng đó, bây giờ nó không thay đổi điều gì.
Fr0sT

1
Điều này thực sự nên được chấp nhận là câu trả lời chính xác. Liệu những gì nó nói trên hộp!
gjoris

21

Nếu bạn đã đẩy lên GitHub, lực đẩy không đủ, hãy xóa kho lưu trữ hoặc liên hệ với bộ phận hỗ trợ

Ngay cả khi bạn buộc đẩy một giây sau đó, nó vẫn không đủ như được giải thích dưới đây.

Các khóa học hành động hợp lệ duy nhất là:

  • là những gì rò rỉ một thông tin có thể thay đổi như mật khẩu?

    • có: sửa đổi mật khẩu của bạn ngay lập tức và xem xét sử dụng nhiều khóa OAuth và API hơn!
    • không (ảnh khỏa thân):

      • Bạn có quan tâm nếu tất cả các vấn đề trong kho lưu trữ bị cấm?

        • không: xóa kho lưu trữ
        • Đúng:

          • liên hệ hỗ trợ
          • nếu rò rỉ là rất quan trọng đối với bạn, đến mức bạn sẵn sàng nhận một số thời gian chết của kho lưu trữ để làm cho nó ít bị rò rỉ hơn, hãy đặt nó ở chế độ riêng tư trong khi bạn chờ hỗ trợ của GitHub trả lời cho bạn

Lực đẩy một giây sau là không đủ vì:

Tuy nhiên, nếu bạn xóa kho lưu trữ thay vì chỉ ép, các cam kết sẽ biến mất ngay cả từ API ngay lập tức và cung cấp 404, ví dụ: https://api.github.com/repose/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3bbbb tác phẩm này ngay cả khi bạn tạo lại một kho lưu trữ khác có cùng tên.

Để kiểm tra điều này, tôi đã tạo một repo: https://github.com/cirosantilli/test-dangling và đã làm:

git init
git remote add origin git@github.com:cirosantilli/test-dangling.git

touch a
git add .
git commit -m 0
git push

touch b
git add .
git commit -m 1
git push

touch c
git rm b
git add .
git commit --amend --no-edit
git push -f

Xem thêm: Làm thế nào để loại bỏ một cam kết lơ lửng khỏi GitHub?


20

Tôi đề nghị kịch bản này của David Underhill, làm việc như một cơ duyên đối với tôi.

Nó thêm các lệnh này ngoài nhánh lọc của natacado để dọn dẹp mớ hỗn độn mà nó để lại:

rm -rf .git/refs/original/
git reflog expire --all
git gc --aggressive --prune

Kịch bản đầy đủ (tất cả tín dụng cho David Underhill)

#!/bin/bash
set -o errexit

# Author: David Underhill
# Script to permanently delete files/folders from your git repository.  To use 
# it, cd to your repository's root and then run the script with a list of paths
# you want to delete, e.g., git-delete-history path1 path2

if [ $# -eq 0 ]; then
    exit 0
fi

# make sure we're at the root of git repo
if [ ! -d .git ]; then
    echo "Error: must run this script from the root of a git repository"
    exit 1
fi

# remove all paths passed as arguments from the history of the repo
files=$@
git filter-branch --index-filter \
"git rm -rf --cached --ignore-unmatch $files" HEAD

# remove the temporary history git-filter-branch
# otherwise leaves behind for a long time
rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --prune

Hai lệnh cuối có thể hoạt động tốt hơn nếu được thay đổi thành như sau:

git reflog expire --expire=now --all && \
git gc --aggressive --prune=now

1
Lưu ý rằng việc sử dụng hết hạn và cắt tỉa của bạn là không chính xác, nếu bạn không chỉ định ngày thì nó sẽ mặc định cho tất cả các cam kết cũ hơn 2 tuần để cắt tỉa. Những gì bạn muốn là tất cả các cam kết cũng vậy:git gc --aggressive --prune=now
Adam Parkin

@Adam Parkin Tôi sẽ để lại mã trong câu trả lời tương tự vì nó là từ kịch bản trên trang web của David Underhill, bạn có thể nhận xét ở đó và nếu anh ấy thay đổi, tôi sẽ thay đổi câu trả lời này vì tôi thực sự không biết rằng tốt. Lệnh hết hạn trước khi cắt tỉa không ảnh hưởng đến nó?
Jason Goemaat

1
@MarkusUnterwaditzer: Cái đó sẽ không hoạt động cho các cam kết được đẩy.
Max Beikirch

Có lẽ bạn chỉ nên đặt tất cả các lệnh trong câu trả lời của bạn; nó sẽ phù hợp hơn nhiều và sẽ không yêu cầu kết hợp tinh thần các bài viết riêng biệt :)
Andrew Mao

9

Để rõ ràng: Câu trả lời được chấp nhận là chính xác. Hãy thử nó trước. Tuy nhiên, nó có thể phức tạp không cần thiết đối với một số trường hợp sử dụng, đặc biệt nếu bạn gặp phải các lỗi đáng ghét, chẳng hạn như 'gây tử vong: sửa đổi xấu - trống rỗng' hoặc thực sự không quan tâm đến lịch sử repo của bạn.

Một thay thế sẽ là:

  1. cd đến chi nhánh cơ sở của dự án
  2. Xóa mã / tệp nhạy cảm
  3. rm -rf .git / # Xóa tất cả thông tin git khỏi mã của bạn
  4. Truy cập github và xóa kho lưu trữ của bạn
  5. Thực hiện theo hướng dẫn này để đẩy mã của bạn sang kho lưu trữ mới như bình thường - https://help.github.com/articles/adding-an-ex hiện - project -to- github - USE - the - common - line /

Điều này tất nhiên sẽ loại bỏ tất cả các nhánh lịch sử cam kết và các vấn đề từ cả repo github của bạn và repo git cục bộ của bạn. Nếu điều này không được chấp nhận, bạn sẽ phải sử dụng một phương pháp thay thế.

Gọi đây là lựa chọn hạt nhân.


9

Bạn có thể sử dụng git forget-blob.

Cách sử dụng khá đơn giản. git forget-blob file-to-forget . Bạn có thể biết thêm thông tin ở đây

https://ownyourbits.com/2017/01/18/completely-remove-a-file-from-a-git-reposeective-with-git-forget-blob/

Nó sẽ biến mất khỏi tất cả các cam kết trong lịch sử, reflog, thẻ của bạn, v.v.

Tôi gặp phải vấn đề tương tự mọi lúc, và mỗi khi tôi phải quay lại bài đăng này và những người khác, đó là lý do tại sao tôi tự động hóa quy trình.

Tín dụng cho những người đóng góp từ Stack Overflow cho phép tôi kết hợp điều này với nhau


8

Đây là giải pháp của tôi trong windows

git filter-Branch --tree-filter "rm -f 'filir / filename'" HEAD

git đẩy - lực lượng

đảm bảo rằng đường dẫn là chính xác nếu không nó sẽ không hoạt động

Tôi hy vọng nó sẽ giúp


8

Sử dụng nhánh lọc :

git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch *file_path_relative_to_git_repo*' --prune-empty --tag-name-filter cat -- --all

git push origin *branch_name* -f

3

Tôi đã phải làm điều này một vài lần cho đến nay. Lưu ý rằng điều này chỉ hoạt động trên 1 tệp tại một thời điểm.

  1. Lấy một danh sách tất cả các cam kết đã sửa đổi một tập tin. Người ở phía dưới sẽ cam kết đầu tiên:

    git log --pretty=oneline --branches -- pathToFile

  2. Để xóa tệp khỏi lịch sử, hãy sử dụng cam kết đầu tiên sha1 và đường dẫn đến tệp từ lệnh trước đó và điền chúng vào lệnh này:

    git filter-branch --index-filter 'git rm --cached --ignore-unmatch <path-to-file>' -- <sha1-where-the-file-was-first-added>..


3

Vì vậy, nó trông giống như thế này:

git rm --cached /config/deploy.rb
echo /config/deploy.rb >> .gitignore

Xóa bộ nhớ cache cho tệp được theo dõi khỏi git và thêm tệp đó vào .gitignoredanh sách


2

Trong dự án Android của tôi, tôi đã có admob_keys.xml dưới dạng tệp xml được phân tách trong thư mục app / src / main / res / value / . Để loại bỏ tập tin nhạy cảm này, tôi đã sử dụng tập lệnh bên dưới và hoạt động hoàn hảo.

git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch  app/src/main/res/values/admob_keys.xml' \
--prune-empty --tag-name-filter cat -- --all
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.