Git geçmişinden eski geçmişi nasıl silebilirim?


208

Korkarım bu senaryo gibi bir şey bulamadım.

Geçmişi çok olan bir git depom var: 500+ şube, 500+ etiket, 2007 ortasına kadar geri dönüyor. ~ 19.500 taahhüt içerir. Daha küçük ve daha kolay başa çıkabilmek için tüm geçmişi 1 Ocak 2010'dan kaldırmak istiyoruz (geçmişin tam bir kopyasını bir arşiv havuzunda saklayacağız).

Yeni deponun kökü haline gelmek istediğim taahhüdü biliyorum. Ancak, bu taahhüt ile başlamak için repo kesmek için doğru git mojo anlayamıyorum. Bazı varyantlarını tahmin ediyorum

git filter-branch

greftlerin dahil edilmesi gerekli olacaktır; ayrıca ayrı tutmak istediğimiz 200+ dalın her birini tedavi etmek ve daha sonra repoyu tekrar bir araya getirmek ( nasıl yapacağımı bildiğim bir şey) gerekli olabilir.

Hiç böyle bir şey yaptı mı? Önemli olursa git 1.7.2.3'e sahibim.

Yanıtlar:


118

Sadece bir oluşturmak grefti (veya boş işlemeye, örneğin gerçek kök Deponuzun taahhüt) hiçbir ebeveyn taahhüt yeni kök ebeveyn. Örneğinecho "<NEW-ROOT-SHA1>" > .git/info/grafts

Grefti oluşturduktan sonra hemen yürürlüğe girer; git logİstenmeyen eski taahhütlere bakıp bakabilmelisiniz :

$ 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

Her şey amaçlandığı gibi görünüyorsa, git filter-branch -- --allkalıcı hale getirmek için bir basit yapabilirsiniz.

DİKKAT: filtre dalı adımını yaptıktan sonra , tüm taahhüt kimlikleri değişmiş olacaktır, bu nedenle eski depoyu kullanan herkes yeni depoyu kullanan herhangi biriyle asla birleşmemelidir.


6
git filter-branch --tag-name-filter cat -- --allEtiketleri güncellemek zorunda kaldım . Ancak silmek istediğim eski tarihe işaret eden eski etiketlerim de var. Tüm bu eski etiketlerden nasıl kurtulabilirim? Onları silmezsem, eski tarih kaybolmaz ve hala görebiliyorum gitk --all.
Craig McQueen

9
"Sadece hiçbir kök için yeni kök taahhüt üst bir greft oluşturmak" biraz detaylandırılması gerekiyor. Bunu denedim ve "ebeveyn yok" sözdizimini anlayamadım. Manuel sayfa, bir üst taahhüt kimliğinin gerekli olduğunu iddia ediyor; tüm sıfırları kullanmak bana bir hata veriyor.
Marius Gedminas

6
Başka birinin tam olarak nasıl çalıştığını merak etmesi durumunda, oldukça kolay:echo "<NEW-ROOT-HASH>" > .git/info/grafts
friederbluemle

3
Katılıyorum, bir greftin ne olduğunu açıklamak yararlı olmaktan daha fazlasıdır
Charles Martin

4
Greftlerdeki bağlantılı wiki sayfasından alıntılanmıştır. "Git 1.6.5'ten itibaren, daha esnek bir git değiştirme eklendi, bu da herhangi bir nesneyi başka bir nesneyle değiştirmenize izin veriyor ve ilişkilendirmelerde depolar arasında itilebilen ve çekilebilen refs aracılığıyla izliyor. Yani bu cevap git'in güncel sürümleri için güncel olmayabilir .
ThorSummoner

130

Belki bir yanıt göndermek için çok geç, ancak bu sayfa ilk Google'ın sonucu olduğu için yine de yardımcı olabilir.

Git deponuzda biraz yer açmak istiyorsanız, ancak tüm taahhütlerinizi (rebase veya greft) yeniden oluşturmak istemiyorsanız ve yine de tam repoya sahip olanlardan itme / çekme / birleştirme yapabiliyorsanız, git'i kullanabilirsiniz. klon sığ klonu ( --depth parametresi).

; 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

Aşağıdaki adımları izleyerek mevcut repo'nuzu sığlaştırabilirsiniz:

; 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

Tüm git yerel etiketleri nasıl kaldırılır?

Ps: Git'in eski sürümleri sığ depoları klonla / it / çek özelliğini desteklemedi.


9
1 Bu Git yeni sürümlerini doğru cevap. (Oh, ve lütfen PPCG'ye geri dönün !)
wizzwizz4

6
Yeni cdsilinmiş bir klasöre nasıl gidebilirsiniz ? Burada eksik bilgiler varmış gibi hissediyorum. Ayrıca, bu değişiklikleri uzak repoya uygulamanın bir yolu var mı?
Trogdor

4
@Jez En çok oy alan diğer cevap bu olurdu. Tarihten kalıcı olarak kurtulmak istiyorsanız bu cevap sizin için değil. Bu çalışma için var olan büyük geçmişleri.
Kimse

4
Kendi sorumu cevaplamak için: git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02Bir cazibe gibi çalışır!
Micros

5
@Jez sığ repo çalıştırarak normal bir dönüştürebilirsiniz git filter-branch -- --all. Bu, içindeki tüm karmaları değiştirecek, ancak bundan sonra yeni bir repoya
itebileceksiniz

61

Bu yöntemin anlaşılması kolaydır ve iyi çalışır. Script ( $1) argümanı , geçmişinizi korumak istediğiniz taahhüde bir referanstır (tag, hash, ...).

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

NOT eski etiketleri hala mevcut kalacağını; bu yüzden bunları manuel olarak kaldırmanız gerekebilir

açıklama: Bunun @yoyodin ile neredeyse aynı olduğunu biliyorum, ama burada bazı önemli ekstra komutlar ve bilgiler var. Cevabı düzenlemeye çalıştım, ancak @ yoyodin'in cevabında önemli bir değişiklik olduğu için düzenlemem reddedildi, işte bilgi!


git pruneVe git gckomutları için verilen açıklamaları takdir ediyorum . Komut dosyasındaki komutların geri kalanı için bir açıklama var mı? Haliyle, kendisine hangi argümanların iletildiğini ve her komutun ne yaptığını netleştirmiyoruz. Teşekkürler.
user5359531

2
@ user5359531 Yorumunuz için teşekkürler, her komut için biraz daha yorum ekledim. Bu yardımcı olur umarım.
Chris Maes

4
Her yerde çatışmaları birleştirin ... çok yararlı değil
Warpzit

3
@Warpzit Ben ekleyerek birleştirme çatışmaları kurtuldum -piçin rebasediğer cevabı önerildiği üzere, komuta
leonbloy

1
Bunu tam olarak takip ettim ve elimde olan tek şey eskisi gibi aynı tarihti. Geçmiş kaldırılmadı.
DrStrangepork

51

Bu yöntemi deneyin Git geçmişini kısaltmak için :

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

İşte $1tutmak istediğiniz taahhüdün SHA-1'i ve komut dosyası $1ve arasındaki tüm taahhütleri içeren yeni bir şube oluşturacak masterve tüm eski tarih bırakılacak. Bu basit komut dosyasının, var olan bir dalı olmadığını varsaydığını unutmayın temp. Ayrıca bu komut dosyasının eski geçmiş için git verilerini temizlemediğini unutmayın. git gc --prune=all && git repack -a -f -F -dTüm geçmişi gerçekten kaybetmek istediğinizi doğruladıktan sonra çalıştırın . Ayrıca rebase --preserve-merges, bu özelliğin git uygulamasının mükemmel olmadığı konusunda da uyarmanız gerekebilir . Bunu kullanıyorsanız sonuçları manuel olarak inceleyin.


22
Bunu denedim, ancak rebaseadımda birleştirme çatışmaları var . Garip - Bu koşullarda birleşme çatışmalarının mümkün olacağını beklemiyordum.
Craig McQueen

2
git commit --allow-empty -m "Truncate history"Teslim aldığınız taahhüt herhangi bir dosya içermiyorsa kullanın .
friederbluemle

2
Bunu uzak ana bilgisayara nasıl geri gönderebilirim? Bunu yaptığımda hem eski hem de yeni bir tarihle karşılaşıyorum.
rustyx

1
'Temp' ne olmalı? Bunun için bir argüman olarak neyi geçmeniz gerekiyor? Bu komutları gerçekten çalıştırdığınızda nasıl görünmesi gerektiğine dair bir örnek var mı? Teşekkürler.
user5359531

1
Ben 1 $ taahhüt karma olduğuna inanıyorum. (Bağlantılı makalede daha fazla ayrıntı verilmiştir).
Chris Nolet

34

Geçmişi yeniden yazmaya alternatif git replaceolarak , Pro Git kitabındaki bu makalede olduğu gibi kullanmayı düşünün . Tartışılan örnek, ağacın başlangıcını simüle etmek için bir ebeveyn taahhüdünün değiştirilmesini ve aynı zamanda da tarihin tamamını güvenlik için ayrı bir dal olarak tutmayı içerir.


Evet, sanırım ayrı tarih dalını da çekerseniz, muhtemelen istediğimiz şeyi yapabilirdiniz. (Depoyu küçültmeye çalışıyorduk.)
ebneter

1
Yanıtın tesis dışında olması beni cesaretlendirdi; ancak GitScm sitesine bağlanır ve bağlandığı öğretici çok iyi yazılmıştır ve doğrudan OP'nin sorusunun noktasına doğru görünür.
ThorSummoner

@ThorSummoner Üzgünüm! Cevabı biraz daha yerinde yapacağım
Jeff Bowman

Maalesef bu, tarihin yeniden yazılmasına alternatif değil. Makalenin başında muhtemelen bu izlenimi veren kafa karıştırıcı bir cümle var. Bu cevaptan çıkarılabilir mi? Makalede yazarın kesilmiş dalın tarihini yeniden yazdığını, ancak eski "tarih" dalını kullanarak yeniden eklemenin bir yolunu önerdiğini göreceksiniz git replace. Bunun, bu yanıtı yayınladığınız başka bir soruda düzeltildiğine inanıyorum.
Mitch

1
Bir tartışma git replacekarşı git graftyapılır stackoverflow.com/q/6800692/873282
koppor

25

İsterseniz tutmak memba ile depo dolu tarihinin , ancak yerel küçük kullanıma alma, sığ bir klon yapmak git clone --depth=1 [repo].

Bir taahhüdü zorladıktan sonra şunları yapabilirsiniz:

  1. git fetch --depth=1eski taahhütleri budamak. Bu, eski taahhütleri ve onların nesnelerini ulaşılmaz kılar.
  2. git reflog expire --expire-unreachable=now --all. Tüm eski taahhütlerin ve nesnelerinin süresinin dolması
  3. git gc --aggressive --prune=all eski nesneleri kaldırmak için

Ayrıca bkz. Bir taahhütten sonra yerel git geçmişi nasıl kaldırılır? .

Bu "sığ" havuzu başka bir yere gönderemeyeceğinizi unutmayın: "sığ güncellemeye izin verilmiyor". Bkz Uzaktan Git uzak URL'yi değiştirdikten sonra (sığ güncelleme izin verilmez) reddedilir . Bunu yapmak istiyorsanız, aşılama ile uğraşmak zorundasınız.


1
1. nokta benim için fark yarattı. Şerefe
clapas

21

Ne yaptığımı anlamak için birkaç cevap ve başka bilgileri okumam gerekiyordu.

1. Belirli bir işlemden daha eski olan her şeyi yoksay

Dosya .git/info/grafts, bir taahhüt için sahte ebeveynleri tanımlayabilir. Yalnızca taahhüt kimliğine sahip bir satır, taahhüdün bir ebeveyni olmadığını söylüyor. Sadece son 2000 taahhüdünü önemsediğimizi söylemek istersek şunu yazabiliriz:

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

git rev-parse bize şu anki taahhüdün 2000. ebeveyninin taahhüt kimliğini verir. Yukarıdaki komut, varsa greft dosyasının üzerine yazacaktır. Önce orada olup olmadığını kontrol edin.

2. Git geçmişini yeniden yazın (isteğe bağlı)

Bu aşılanmış sahte ebeveynin gerçek olmasını istiyorsanız, çalıştırın:

git filter-branch -- --all

Tüm taahhüt kimliklerini değiştirecektir. Bu deponun her kopyası zorla güncellenmelidir.

3. Disk alanını temizleyin

2. adımı ben yapmadım çünkü kopyamın yukarı akışla uyumlu kalmasını istedim. Sadece disk alanından tasarruf etmek istedim. Tüm eski taahhütleri unutmak için:

git prune
git gc

Alternatif: sığ kopyalar

Başka bir deponun sığ bir kopyasına sahipseniz ve sadece disk alanından tasarruf etmek istiyorsanız, güncelleyebilirsiniz .git/shallow. Ancak, hiçbir şeyin daha önce bir taahhüde işaret etmediğine dikkat edin. Böylece şöyle bir şey çalıştırabilirsiniz:

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

Sığ giriş bir greft gibi çalışır. Ancak aynı zamanda greft ve sığ kullanmamaya dikkat edin. En azından orada aynı girişler yok, başarısız olacak.

Hala eski işlemlere işaret eden bazı eski referanslarınız (etiketler, dallar, uzak kafalar) varsa, bunlar temizlenmez ve daha fazla disk alanı kazanmazsınız.


<GIT_DIR> / info / greft desteği kullanımdan kaldırılmıştır ve gelecekteki Git sürümünde kaldırılacaktır.
danny

Lütfen git replacebunun yerine kullanmayı düşünün . Bkz. Stackoverflow.com/questions/6800692/…
Joel AZEMAR

3

Tüm Rebase veya itme için baş / ana bu hata meydana olabilir

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'

Git kontrol panelinde bu sorunu çözmek için ana dalı "Korumalı dallar" dan kaldırmalısınız

resim açıklamasını buraya girin

o zaman bu komutu çalıştırabilirsiniz

git push -f origin master

veya

git rebase --onto temp $1 master

0

Burada güncel olmayan çok fazla cevap var ve bazıları sonuçları tam olarak açıklamıyor. İşte benim en son git 2.26 kullanarak tarihi kısaltmak için benim için çalıştı:

İlk önce bir kukla taahhüt oluşturun. Bu taahhüt, kesilen repodaki ilk taahhüt olarak görünecektir. Buna ihtiyacınız var çünkü bu işlem, tuttuğunuz tarih için tüm temel dosyaları tutacaktır. SHA, tutmak istediğiniz taahhüdün önceki taahhüdünün kimliğidir (bu örnekte 8365366). 'Initial' dizesi ilk taahhüdün taahhüt mesajı olarak görünecektir. Windows kullanıyorsanız Git Bash komut isteminden aşağıdaki komutu yazın.

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

Yukarıdaki komut SHA'yı yazdırır, örneğin d10f7503bc1ec9d367da15b540887730db862023.

Şimdi şunu yazın:

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

Bu, ilk önce tüm dosyaları 8365366kukla taahhüde taahhütlü olarak koyacaktır d10f750. Daha sonra 8365366'dan sonra tüm taahhütleri oynatacaktır d10f750. Son olarak, masterşube işaretçisi oynatılan son işleme kadar güncellenecektir.

Şimdi bu kesilmiş repoyu itmek istiyorsanız, sadece yapın git push -f.

Akılda tutulması gereken birkaç şey (bunlar diğer yöntemlerin yanı sıra bunun için de geçerlidir): Etiketler aktarılmaz. Taahhüt kimlikleri ve zaman damgaları korunurken, GitHub'ın bu taahhütleri lumpum başlığında olduğu gibi göreceksiniz Commits on XY date.

Neyse ki kesilmiş tarihi "arşiv" olarak tutmak mümkündür ve daha sonra kesilen repoya arşiv repo ile tekrar katılabilirsiniz. Bunu yapmak için bu kılavuza bakın .


-3

dizin, dosyalar ve ayrıca direk veya dosyayla ilgili tüm geçmişi, aşağıda belirtilen jar [indir] ve komutları kullanarak silebilirsiniz

bfg.jar dosyası: https://rtyley.github.io/bfg-repo-cleaner/

git clone - çıplak repo-url cd repo_dir java -jar bfg.jar --delete-folders klasör_adı git reflog süresi doluyor --expire = şimdi --all && git gc --prune = şimdi - agresif git push --mirror repo_url


-10
  1. git verisini kaldır, rm .git
  2. git init
  3. git uzaktan kumandası ekle
  4. zorla itme

6
TÜM geçmişi kaldırmak için çalışacak, ancak sorduğu şey için değil: Ocak 2010'dan beri tarihi koruyun
Chris Maes

1
Sadece bu soruya doğru cevap olmasa bile senaryomda bana yardımcı olduğu için teşekkür etmek istedim
6'da apnerve
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.