Geçerli depoyu Git deposundaki tek (başlangıç) işlemek mi yapıyorsunuz?


664

Şu anda bir Github deposuna aktardığım yerel bir Git depom var.

Yerel veri havuzunun ~ 10 komutu vardır ve Github veri havuzu bunun senkronize bir kopyasıdır.

Ne yapmak istiyorum, yerel Git deposundan TÜM sürüm geçmişini kaldırmak, bu nedenle deponun mevcut içeriği tek taahhüt olarak görünür (ve bu nedenle depo içindeki dosyaların eski sürümleri saklanmaz).

Daha sonra bu değişiklikleri Github'a aktarmak istiyorum.

Git rebase'i araştırdım, ancak bu belirli sürümleri kaldırmak için daha uygun görünüyor. Başka bir potansiyel çözüm, yerel repoyu silmek ve yeni bir tane oluşturmaktır - bu muhtemelen çok fazla iş yaratacaktır!

ETA: İzlenmeyen belirli dizinler / dosyalar var - mümkünse bu dosyaların izlenmesini sürdürmek istiyorum.


6
Ayrıca bkz. Stackoverflow.com/questions/435646/… ("Git deposunun ilk iki işlemini nasıl birleştiririm?")
Anonymoose


Yanıtlar:


981

İşte kaba kuvvet yaklaşımı. Ayrıca deponun yapılandırmasını da kaldırır.

Not : Havuzda alt modüller varsa bu çalışmaz! Alt modüller kullanıyorsanız, örneğin etkileşimli rebase kullanmalısınız

1.Adım: Tüm geçmişi kaldırın ( Yedeklemeniz olduğundan emin olun, bu geri alınamaz )

cat .git/config  # note <github-uri>
rm -rf .git

2.Adım: Git deposunu yalnızca geçerli içerikle yeniden oluşturun

git init
git add .
git commit -m "Initial commit"

3.Adım: GitHub'a gidin.

git remote add origin <github-uri>
git push -u --force origin master

3
Teşekkürler larsmans - Bunu benim çözümüm olarak kullanmayı seçtim. Git deposunun başlatılması eski depodaki izlenmemiş dosyaların kaydını kaybetse de, bu muhtemelen sorunum için daha basit bir çözümdür.
Kaese

5
@kaese: Bence .gitignorebunları halletmelisin, değil mi?
Fred Foo

48
.Git / config dosyanızı daha önce kaydedin ve sonra geri yükleyin.
lalebarde

@lalebarde .git / config dosyasını sonra geri yüklerseniz , zaten yapılandırmada olduğunu varsayarak parçayı git commit -m "Initial commit"atlayabilir git remote add ...ve doğrudan itmeye geçebilirsiniz. Benim için çalıştı.
Buttle Butkus

24
Hassas verileri kaldırmaya çalışıyorsanız buna dikkat edin: yeni itilen ana dalda yalnızca tek bir taahhüdün varlığı yanıltıcıdır - tarih hala var olacaktır, bu daldan erişilemez. Örneğin, daha eski işlemlere işaret eden etiketleriniz varsa, bu işlemlere erişilebilir. Aslında, git git foo'su olan herkes için, eminim bu git itmesinden sonra, GitHub deposundan tüm geçmişi kurtarabileceklerinden emin olabilirsiniz - ve başka şubeleriniz veya etiketleriniz varsa, hatta çok git foo gerekir.
Robert Muil

621

Benim için çalışan (ve alt modüllerin çalışmasını sağlayan) tek çözüm

git checkout --orphan newBranch
git add -A  # Add all files and commit them
git commit
git branch -D master  # Deletes the master branch
git branch -m master  # Rename the current branch to master
git push -f origin master  # Force push master branch to github
git gc --aggressive --prune=all     # remove the old files

Alt .git/modüllerim olduğunda silmek her zaman büyük sorunlara neden olur. Kullanmak bir git rebase --rootşekilde benim için çatışmaya neden olur (ve çok fazla geçmişim olduğundan uzun sürer).


55
bu doğru cevap olmalı! sadece git push -f origin masterson op olarak ekleyin ve güneş tekrar taze repo parlayacak! :)
gru

2
Bu eski taahhütleri ortada tutmuyor mu?
Brad

4
@JonePolvora git getirme; git reset --hard origin / master stackoverflow.com/questions/4785107/…
echo

5
Bunu yaptıktan sonra repo boş alan olacak mı?
Inuart

8
Cevabınızın son satırı olarak @JasonGoemaat'ın önerisini eklemeniz gerektiğine inanıyorum. Olmadan git gc --aggressive --prune allkaybetme tarihin bütün mesele cevapsız olacaktır.
Tuncay Göncüoğlu

93

Bu benim tercih ettiğim yaklaşım:

git branch new_branch_name $(echo "commit message" | git commit-tree HEAD^{tree})

Bu, HEAD'e her şeyi ekleyen bir taahhütle yeni bir şube yaratacaktır. Başka bir şey değiştirmez, bu yüzden tamamen güvenlidir.


3
En iyi yaklaşım! Temizleyin ve işi yapın. Ayrıca, şube "master" dan "local-work" ve "new_branch_name" den "master" bir çok değişiklik ile yeniden adlandırmak. Master'da aşağıdakileri yapın: git -m local-changes git branch -m yerel-değişiklikler git checkout new_branch_name git branch -m master <
Valtoni Boaventura

Bu gerçekten kısa ve şık görünüyor, henüz anlamadığım veya görmediğim tek şey HEAD ^ {tree}, biri açıklayabilir mi? Bunun dışında bunu "___ 'dan verilen
kesin

3
Git referans sözdizimi ile ilgili soruların yanıtlarını arayacağınız kesin yer git-rev-parsedokümanlardır. Burada olan şey git-commit-tree, bir ağaca (repo'nun anlık görüntüsü) bir referans gerektirir, ancak HEADbir düzeltmedir. Bir taahhüt ile ilişkili ağacı bulmak için <rev>^{<type>}formu kullanıyoruz.
dan_waterworth

Güzel cevap. İyi çalışıyor. Sonundagit push --force <remote> new_branch_name:<remote-branch>
Felipe Alvarez

31

Çok fazla taahhüdünüz varsa çok fazla iş olabilecek bir diğer seçenek etkileşimli bir rebase'dir (git sürümünüzün = 1.7.12 olduğu varsayılarak):git rebase --root -i

Editörünüzdeki taahhütlerin bir listesi ile sunulduğunda:

  • İlk işlem için "seç" i "geri sar" olarak değiştir
  • "Seçim" i diğer tüm işlemleri "düzeltme" olarak değiştirin

Kaydet ve kapat. Git yeniden baslamaya başlar.

Sonunda, ondan sonra gelenlerin bir kombinasyonu olan yeni bir kök taahhüdünüz olacaktı.

Avantajı, deponuzu silmek zorunda kalmamanız ve ikinci düşünceleriniz varsa her zaman bir yedeğiniz olur.

Gerçekten geçmişinizi tıklatmak istiyorsanız, master'ı bu işleme sıfırlayın ve diğer tüm dalları silin.


error: failed to push some refs to
Rebase

@Begueradj, yeniden bastığınız dalı zaten ittiyseniz, zorlamayı zorlamanız gerekir git push --force-with-lease. kiralama kuvveti kullanılır - çünkü - kuvvetten daha az yıkıcıdır.
Carl

19

Varyant larsmans 'önerdiği yöntem:

İzlenmeyen dosyalar listenizi kaydedin:

git ls-files --others --exclude-standard > /tmp/my_untracked_files

Git yapılandırmanızı kaydedin:

mv .git/config /tmp/

Sonra larsmans'ın ilk adımlarını uygulayın:

rm -rf .git
git init
git add .

Yapılandırmanızı geri yükleyin:

mv /tmp/config .git/

İzlenmeyen dosyaların izini kaldırın:

cat /tmp/my_untracked_files | xargs -0 git rm --cached

Sonra taahhüt edin:

git commit -m "Initial commit"

Ve son olarak deponuza itin:

git push -u --force origin master

6

Aşağıda @Zeelot'un cevabından uyarlanmış bir script bulunmaktadır. Tarihi sadece ana daldan değil, tüm dallardan kaldırmalıdır:

for BR in $(git branch); do   
  git checkout $BR
  git checkout --orphan ${BR}_temp
  git commit -m "Initial commit"
  git branch -D $BR
  git branch -m $BR
done;
git gc --aggressive --prune=all

Benim amacım için çalıştı (alt modülleri kullanmıyorum).


4
Sanırım itme ustasını prosedürü tamamlamaya zorlamayı unuttunuz.
not2qubit

2
Küçük bir değişiklik yapmak zorunda kaldım. git branchkullanıma alınmış dalınızın yanında bir yıldız işareti içerecek ve bu da globbed olacak ve bu da tüm dallara ya da klasörlere şube adlarıymış gibi çözümlenmesine neden olacak. Bunun yerine, git branch --format="%(refname:lstrip=2)"bana sadece şube isimlerini veren kullandım .
Ben Richards

@ not2qubit: Bunun için teşekkürler. Kesin komut ne olurdu? git push --force origin master, veya git push --force-with-lease? Görünüşe göre ikincisi daha güvenlidir (bkz. Stackoverflow.com/questions/5509543/… )
Shafique Jamal

@BenRichards. İlginç. Bunu test etmek için bir dal adıyla eşleşen bir klasörle bir noktada tekrar deneyeceğim, ardından cevabı güncelleyeceğim. Teşekkürler.
Shafique Jamal


4

git filter-branch majör cerrahi aracıdır.

git filter-branch --parent-filter true -- @^!

--parent-filterebeveynleri stdin'e alır ve yeniden yazılan ebeveynleri stdout'a yazdırmalıdır; unix truebaşarıyla çıkar ve hiçbir şey yazdırmaz, yani: ebeveyn yok. @^!olduğu Git için steno "kafa işlemek ama hiç kimseyi gücünün ebeveynlerin". Sonra diğer tüm referansları silin ve boş zamanlarınızı itin.


3

Sadece Github deposunu silin ve yeni bir tane oluşturun. Şimdiye kadar en hızlı, en kolay ve en güvenli yaklaşım. Sonuçta, tüm istediğiniz komutlar tek bir taahhütle ana dal olduğunda, kabul edilen çözümdeki tüm bu komutları yerine getirmek için ne kazanmanız gerekir?


1
Ana noktalardan biri, nereden çatallandığını görebilmektir.
not2qubit

Bunu sadece yaptım ve iyi
thanos.a

2

Aşağıdaki yöntem tam olarak yeniden üretilebilir, bu nedenle her iki taraf tutarlıysa klon tekrar çalıştırmanıza gerek yoktur, komut dosyasını diğer tarafta da çalıştırın.

git log -n1 --format=%H >.git/info/grafts
git filter-branch -f
rm .git/info/grafts

Daha sonra temizlemek istiyorsanız, şu komut dosyasını deneyin:

http://sam.nipl.net/b/git-gc-all-ferocious

Depodaki her dal için "geçmişi öldüren" bir senaryo yazdım:

http://sam.nipl.net/b/git-kill-history

ayrıca bkz: http://sam.nipl.net/b/confirm


1
Bunun için teşekkürler. Sadece FYI: her dal için geçmişi öldürmek için komut dosyanız bazı güncelleme kullanabilirsiniz - ve şu hataları verir: git-hash: not foundveSupport for <GIT_DIR>/info/grafts is deprecated
Shafique Jamal

1
@ShafiqueJamal, teşekkürler, küçük "git-hash" komut dosyası git log HEAD~${1:-0} -n1 --format=%H, burada, sam.aiki.info/b/git-hash Her şeyi kamu tüketimi için tek bir komut dosyasında koymak daha iyi olurdu. Eğer tekrar kullanırsam, "greftlerin" yerini alan yeni özellik ile nasıl yapılacağını anlayabilirim.
Sam Watkins

2

Ne yapmak istiyorum, yerel Git deposundan TÜM sürüm geçmişini kaldırmak, bu nedenle deponun mevcut içeriği tek taahhüt olarak görünür (ve bu nedenle depo içindeki dosyaların eski sürümleri saklanmaz).

Daha kavramsal bir cevap:

git etiketleri / dalları / referansları göstermezse otomatik olarak çöp eski taahhütleri toplar. Bu nedenle, tüm etiketleri / dalları kaldırmanız ve herhangi bir dalla ilişkili yeni bir yetim taahhüdü oluşturmanız gerekir - kural olarak, şubenin masterbu taahhüdü işaret etmesine izin verirsiniz .

Eski, ulaşılamaz taahhütler düşük seviyeli git komutları ile kazmadığı sürece bir daha asla kimse tarafından görülmez. Bu sizin için yeterliyse, sadece orada durur ve otomatik GC'nin istediği zaman işini yapmasına izin veririm. Onlardan hemen kurtulmak istiyorsanız, git gc(muhtemelen ile --aggressive --prune=all) kullanabilirsiniz. Uzak git deposu için, dosya sistemine kabuk erişiminiz yoksa, bunu zorlamanızın bir yolu yoktur.


@Zeelot'un cevabı bağlamında güzel bir ekleme.
Mogens TrasherDK

Evet, Zeelot'un temelde bunu yapan komutları var (sadece farklı bir şekilde, tamamen baştan başlayarak, OP için iyi olabilir). @MogensTrasherDK
AnoE

0

Hadi bakalım:

#!/bin/bash
#
# By Zibri (2019)
#
# Usage: gitclean username password giturl
#
gitclean () 
{ 
    odir=$PWD;
    if [ "$#" -ne 3 ]; then
        echo "Usage: gitclean username password giturl";
        return 1;
    fi;
    temp=$(mktemp -d 2>/dev/null /dev/shm/git.XXX || mktemp -d 2>/dev/null /tmp/git.XXX);
    cd "$temp";
    url=$(echo "$3" |sed -e "s/[^/]*\/\/\([^@]*@\)\?\.*/\1/");
    git clone "https://$1:$2@$url" && { 
        cd *;
        for BR in "$(git branch|tr " " "\n"|grep -v '*')";
        do
            echo working on branch $BR;
            git checkout $BR;
            git checkout --orphan $(basename "$temp"|tr -d .);
            git add -A;
            git commit -m "Initial Commit" && { 
                git branch -D $BR;
                git branch -m $BR;
                git push -f origin $BR;
                git gc --aggressive --prune=all
            };
        done
    };
    cd $odir;
    rm -rf "$temp"
}

Ayrıca burada barındırılıyor: https://gist.github.com/Zibri/76614988478a076bbe105545a16ee743


Gah! Bana komut satırında gizli, korumasız şifremi vermemi sağla! Ayrıca, git dalının çıktısı genellikle komut dosyası oluşturma için çok uygun değildir. Sıhhi tesisat aletlerine bakmak isteyebilirsiniz.
D. Ben Knoble

-1

.gitKlasörü projemden silerek ve IntelliJ aracılığıyla sürüm kontrolü ile yeniden entegre ederek benzer bir sorunu çözdüm. Not: .gitKlasör gizlidir. İle terminalde görüntüleyebilir ls -ave daha sonra düğmesini kullanarak kaldırabilirsiniz rm -rf .git.


1. adımda yaptığı şey bu: rm -rf .git?
geceler

-1

Bunun için Sığ Klon komutunu kullan git klon - derinlik 1 URL - Deponun yalnızca geçerli HEAD'ini klonlar


-2

Son taahhüdü git'ten kaldırmak için,

git reset --hard HEAD^ 

Birden fazla işi üstünden kaldırıyorsanız,

git reset --hard HEAD~2 

son iki taahhüdü kaldırmak için. Daha fazla işlem yapmak için sayıyı artırabilirsiniz.

Daha fazla bilgi burada.

Git tutoturial burada deponun nasıl temizleneceği konusunda yardım sağlar:

dosyayı geçmişten kaldırmak ve yanlışlıkla yeniden işlenmediğinden emin olmak için .gitignore dosyasına eklemek istiyorsunuz. Örneklerimiz için Rakefile'ı GitHub mücevher deposundan kaldıracağız.

git clone https://github.com/defunkt/github-gem.git

cd github-gem

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

Dosyayı geçmişten sildiğimize göre, yanlışlıkla tekrar tekrar işlemediğimizden emin olalım.

echo "Rakefile" >> .gitignore

git add .gitignore

git commit -m "Add Rakefile to .gitignore"

Deponun durumundan memnunsanız, uzak deponun üzerine yazmak için değişiklikleri zorlamanız gerekir.

git push origin master --force

6
Depodan dosyaları veya taahhütleri kaldırmanın soru ile kesinlikle bir ilişkisi yoktur (bu, geçmişi kaldırmayı ister, tamamen farklı bir şeydir). OP temiz bir tarih istiyor ama deponun mevcut durumunu korumak istiyor.
Victor Schröder

bu soruda sorulan sonucu üretmez. en son tuttuğunuz taahhütten sonra tüm değişiklikleri atarsınız ve o zamandan bu yana tüm değişiklikleri kaybedersiniz, ancak soru mevcut dosyaları ve bırakma geçmişini korumayı ister.
Tuncay Göncüoğlu
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.