Hassas dosyaları ve bunların taahhütlerini Git geçmişinden kaldırın


354

GitHub'a bir Git projesi koymak istiyorum ama hassas verilerle (capistrano için /config/deploy.rb gibi kullanıcı adları ve şifreler) belirli dosyalar içeriyor.

Bu dosya adlarını .gitignore'a ekleyebileceğimi biliyorum , ancak bu Git'teki geçmişlerini kaldırmaz.

Ayrıca /.git dizinini silerek yeniden başlamak istemiyorum.

Git geçmişinizdeki belirli bir dosyanın tüm izlerini kaldırmanın bir yolu var mı ?



Yanıtlar:


449

Tüm pratik amaçlar için, endişelenmeniz gereken ilk şey ŞİFRELERİNİZİ DEĞİŞTİRMEK! Git deponuzun tamamen yerel olup olmadığı veya başka bir yerde uzak bir deponuzun olup olmadığı sorunuzdan net değil; uzak ve başkalarından korunmuyorsa bir sorun var. Bunu düzeltmeden önce birisi bu havuzu klonladıysa, yerel makinelerinde şifrelerinizin bir kopyasına sahip olacaklar ve onları geçmişten geçmiş olan "sabit" sürümünüze güncelleme yapmaya zorlamanın hiçbir yolu yoktur. Yapabileceğiniz tek güvenli şey, şifrenizi kullandığınız her yerde başka bir şeye değiştirmektir.


Yoldan çekilince, nasıl düzeltebileceğiniz aşağıda açıklanmıştır. GitHub bu soruyu SSS olarak tam olarak yanıtladı :

Windows kullanıcıları için not : bu komuttaki single'lar yerine çift tırnak (") kullanın

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

Güncelleme 2019:

Bu SSS'deki mevcut kod:

  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

Bu kodu GitHub gibi bir uzak depoya ittikten ve diğerlerinin bu uzak depoyu klonladığını, artık geçmişi yeniden yazdığınız bir durumda olduğunuzu unutmayın. Diğerleri bundan sonra son değişikliklerinizi aşağı çekmeye çalıştıklarında, değişikliklerin uygulanamayacağını belirten bir mesaj alırlar çünkü bu bir hızlı ileri değildir.

Bunu düzeltmek için, mevcut depolarını silmeleri ve yeniden klonlamaları veya git-rebase kılavuzundaki "UPSTREAM REBASE'DEN KURTARMA" bölümündeki talimatları izlemeleri gerekir .

İpucu : Yürütgit rebase --interactive


Gelecekte, hassas bilgilerle yanlışlıkla bazı değişiklikler yaparsanız, ancak uzak bir depoya göndermeden önce fark ederseniz , bazı daha kolay düzeltmeler vardır. Son bilgileri hassas bilgileri ekleyecekseniz, hassas bilgileri kaldırabilir ve ardından çalıştırabilirsiniz:

git commit -a --amend

Bu, a ile yapılan tüm dosya kaldırma işlemleri de dahil olmak üzere, yaptığınız tüm yeni değişikliklerle önceki taahhüdü değiştirir git rm. Değişiklikler geçmişte daha ilerideyse, ancak yine de uzak bir depoya aktarılmamışsa, etkileşimli bir rebase yapabilirsiniz:

git rebase -i origin/master

Bu, uzak depodaki son ortak atadan bu yana yaptığınız taahhütleri içeren bir düzenleyici açar. Hassas bilgiler içeren bir taahhüdü temsil eden herhangi bir satırda "seç" i "düzenle" olarak değiştirin ve kaydedin ve çıkın. Git değişiklikleri gözden geçirecek ve sizi aşağıdakileri yapabileceğiniz bir yerde bırakacaktır:

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

Hassas bilgiler içeren her değişiklik için. Sonunda, şubenize geri döneceksiniz ve yeni değişiklikleri güvenli bir şekilde zorlayabilirsiniz.


5
Mükemmel ahbap, bu harika bir cevap. Sen benim günümü kurtar.
zzeroo

18
Sadece bir bit eklemek için - Windows'ta, tekler yerine çift tırnak (") kullanmalısınız
ripper234

4
İşe yaradı. Çevirilerde kayboldum. Buradaki komut yerine bağlantıyı kullandım. Ayrıca, Windows komutu ripper234'ten çift tırnak, MigDus'un önerdiği gibi tam yol gerektiriyor ve bağlantının yeni satır kaydırma göstergeleri olarak yapıştırdığı "\" karakterleri içermiyordu. Son komut şuna benziyordu: git filter-branch --force --index-filter "git rm --cached --ignore-unmatch src [Proje] [Dosya]. [Ext]" --prune-empty --tag- isim filtresi kedi - --all
Eric Swanson

3
filter-branchKodunuzla bağlandığınız github sayfasındakinden bazı önemli farklılıklar var gibi görünüyor . Örneğin, 3. sıra --prune-empty --tag-name-filter cat -- --all. Çözüm değişti mi yoksa bir şey mi kaçırıyorum?
geotheory

2
Bu çözüm oldukça iyi görünüyor, ancak ilk taahhütte kaldırmak için dosyayı tanıttıysanız <introduction-revision-sha1>..HEADişe yaramaz. Yalnızca dosyayı ikinci işlemden itibaren kaldırır. ( Taahhüt aralığına ilk taahhüdü nasıl dahil edebilirim?) Kaydetme yolu burada belirtilmiştir: help.github.com/articles/…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

Parolalarınızı değiştirmek iyi bir fikirdir, ancak parolaların repo geçmişinizden kaldırılması işlemi için, Git depolarındaki özel verileri açıkça kaldırmak için daha hızlı ve daha basit bir alternatif olan BFG Repo-Cleaner'ı öneriyorum git-filter-branch.

private.txtKaldırmak istediğiniz parolaları vb. Listeleyen bir dosya oluşturun (her satıra bir giriş) ve ardından şu komutu çalıştırın:

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

Repo geçmişinizde bir eşik boyutu (varsayılan olarak 1 MB) altındaki tüm dosyalar taranır ve eşleşen tüm dizeler ( en son işleminizde bulunmayan) "*** REMOVED ***" dizesiyle değiştirilir. Daha sonra git gcölü verileri temizlemek için kullanabilirsiniz :

$ git gc --prune=now --aggressive

BFG tipik olarak koşudan 10-50x daha hızlıdır git-filter-branchve seçenekler bu iki yaygın kullanım senaryosuna göre basitleştirilmiştir ve uyarlanmıştır:

  • Crazy Big Dosyalarını Kaldırma
  • Parolaları, Kimlik Bilgilerini ve diğer Özel verileri kaldırma

Tam açıklama: BFG Repo-Cleaner'ın yazarıyım.


6
Bu burada büyük bir kazanç. Birkaç denemeden sonra, özel bir repodan hassas bilgiler içeren taahhütleri çok kapsamlı bir şekilde şeritlemek ve uzaktan repoyu gözden geçirilmiş geçmişle zorla güncellemek için bunu kullanabildim. Bir yan not, repo'nuzun (HEAD) ucunun kendisinin temiz olmasını sağlamak zorunda olmanızdır, çünkü bu taahhüt "korumalı" olarak kabul edilir ve bu araç tarafından revize edilmeyecektir. Değilse, manuel olarak temizleyin ve değiştirin git commit. Aksi takdirde, geliştiricinin araç kutusundaki yeni araç için +1 :)
Matt Borja

1
@Henridv Son yorumuma göre, başvurunuzun şu anda şubenizin ucunda veya başında olduğu varsayılarak (örn. En son taahhüt) tahmin edebileceğiniz gibi başvurunuzu bozmamalıdır. Bu araç, kesin These are your protected commits, and so their contents will NOT be alteredgeçmişinizin geri kalanını gözden geçirirken ve gözden geçirirken son taahhüdünüzü açıkça bildirecektir . Ancak, geri almanız gerekiyorsa, evet, sadece ***REMOVED***geri döndüğünüz taahhütte bir arama yapmanız gerekir.
Matt Borja

1
BFG için +1 (Java yüklüyse veya yüklemeyi önemsemiyorsanız). Bir yakalama, BFG'nin HEAD'de bulunuyorsa bir dosyayı silmeyi reddetmesidir. Bu yüzden önce istenen dosyaların silineceği bir taahhütte bulunmak ve daha sonra BFG'yi çalıştırmak daha iyidir. Bundan sonra, son taahhüdü geri alabilirsiniz, şimdi bir şey değiştirmez.
Fr0sT

1
Bu aslında doğru cevap olarak kabul edilmelidir. Kutuda ne yazıyor!
gjoris

1
Seni bu cevap için sevmediysem, şimdi oluşturduğun .jar kullandıktan sonra yapıyorum:"You can rewrite history in Git - don't let Trump do it for real! Trump's administration has lied consistently, to make people give up on ever being told the truth. Don't give up: https://www.aclu.org/"
monstermac77

21

GitHub'a ittiyseniz, zorla itme yeterli değildir, havuzu silin veya desteğe başvurun

Daha sonra bir saniye itmeye zorlasanız bile, aşağıda açıklandığı gibi yeterli değildir.

Tek geçerli eylem türleri:

  • şifre gibi değiştirilebilir bir kimlik sızıntısı nedir?

    • evet: şifrelerinizi hemen değiştirin ve daha fazla OAuth ve API anahtarı kullanmayı düşünün!
    • hayır (çıplak resimler):

      • depodaki tüm sorunların boşa çıkmasını umursuyor musunuz?

        • hayır: veri havuzunu sil
        • Evet:

          • iletişim desteği
          • sızıntı sizin için çok kritikse, sızıntı olasılığını azaltmak için depo kesintisi almak istediğiniz noktaya kadar, GitHub desteğinin size cevap vermesini beklerken özel yapın

Bir saniye sonra zorlamak yeterli değildir çünkü:

Bununla birlikte, depoyu yalnızca zorla itmek yerine silerseniz, taahhütler hemen API'dan kaybolur ve 404 verir, örneğin https://api.github.com/repos/cirosantilli/test-dangling-delete/commits/8c08448b5fbf0f891696819f3b2b2d653 Bu çalışır aynı ada sahip başka bir havuzu yeniden oluştursanız bile.

Bunu test etmek için bir repo oluşturdum: https://github.com/cirosantilli/test-dangling ve yaptı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

Ayrıca bkz: GitHub'dan sarkan bir taahhüt nasıl kaldırılır?


20

David Underhill'in bu senaryosunu tavsiye ederim , benim için bir cazibe gibi çalıştı.

Arkasında bıraktığı pisliği temizlemek için natacado'nun filtre dalına ek olarak bu komutları ekler:

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

Tam komut dosyası (tümü David Underhill'e aittir)

#!/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

Son iki komut, aşağıdakine değiştirilirse daha iyi çalışabilir:

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

1
Geçerlilik süresi ve kuru erik kullanımınızın yanlış olduğunu unutmayın, tarihi belirtmezseniz, bu süre için budama için 2 haftadan eski olan tüm taahhütlerin varsayılan değeri olduğunu unutmayın. Ne istiyorsun hepsi taahhüt eder:git gc --aggressive --prune=now
Adam Parkin

@Adam Parkin Ben David Underhill sitesindeki senaryodan olduğu için kodu aynı cevapta bırakacağım, orada yorum yapabilirsiniz ve eğer değiştirirse bu cevabı değiştireceğim çünkü gerçekten git bilmiyorum iyi. Kuru erimden önceki expire komutu bunu etkilemez mi?
Jason Goemaat

1
@MarkusUnterwaditzer: Bu itilmiş işler için işe yaramayacak.
Max Beikirch

Belki de tüm komutları cevabınıza koymalısınız; çok daha tutarlı olurdu ve ayrı mesajların zihinsel birleştirilmesini gerektirmez :)
Andrew Mao

9

Açık olmak gerekirse: Kabul edilen cevap doğrudur. Önce deneyin. Bununla birlikte, özellikle 'ölümcül: kötü revizyon - boş-boş' gibi iğrenç hatalarla karşılaşırsanız veya repounuzun geçmişini gerçekten umursamıyorsanız, bazı kullanım durumları için gereksiz yere karmaşık olabilir.

Bir alternatif:

  1. Projenin temel şubesine cd
  2. Hassas kodu / dosyayı kaldırın
  3. rm -rf .git / # Kodunuzdaki tüm git bilgilerini kaldır
  4. Github'a gidin ve deponuzu silin
  5. Kodunuzu normalde yaptığınız gibi yeni bir depoya göndermek için bu kılavuzu izleyin - https://help.github.com/articles/adding-an-existing-project-to-github-using-the-command-line/

Bu elbette tüm taahhüt geçmişi dallarını ve hem github deponuzdan hem de yerel git repodan gelen sorunları kaldıracaktır. Bu kabul edilemezse, alternatif bir yaklaşım kullanmanız gerekecektir.

Buna nükleer seçenek deyin.


9

Kullanabilirsiniz git forget-blob.

Kullanımı oldukça basit git forget-blob file-to-forget. Buradan daha fazla bilgi alabilirsiniz

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

Geçmişiniz, reflogunuz, etiketleriniz vb. Tüm taahhütlerden kaybolacaktır.

Her seferinde aynı problemle karşılaşıyorum ve her zaman bu gönderiye ve diğerlerine geri dönmem gerektiğinde, bu yüzden süreci otomatikleştirdim.

Stack Overflow katılımcılarına bunu bir araya getirmeme olanak tanıyan krediler


8

İşte benim çözümüm pencerelerde

git filter-branch - ağaç-filtre "rm -f 'filedir / dosyaadı'" KAFA

git push - kuvvet

yolun doğru olduğundan emin olun, aksi takdirde çalışmaz

Umut ediyorum bu yardım eder


8

Filtre kolu kullanın :

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

Bunu bugüne kadar birkaç kez yapmak zorunda kaldım. Bunun aynı anda yalnızca 1 dosyada çalıştığını unutmayın.

  1. Bir dosyayı değiştiren tüm taahhütlerin bir listesini alın. En alttaki ilk taahhüt:

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

  2. Dosyayı geçmişten kaldırmak için, ilk komut sha1'i ve önceki komuttan dosyaya giden yolu kullanın ve bu komutla doldurun:

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


3

Yani, şuna benzer:

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

İzlenen dosya için önbelleği git'ten kaldır ve bu dosyayı .gitignorelisteye ekle


2

Benim android projede app / src / main / res / değerleri / klasöründe ayrılmış xml dosyası olarak admob_keys.xml vardı . Bu hassas dosyayı kaldırmak için komut dosyasının altında kullandım ve mükemmel çalıştım.

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.