Bagaimana cara menghapus riwayat lama dari repositori git?

209

Saya khawatir saya tidak dapat menemukan sesuatu yang persis seperti skenario khusus ini.

Saya memiliki repositori git dengan banyak riwayat: 500+ cabang, 500+ tag, kembali ke pertengahan 2007. Ini mengandung ~ 19.500 komit. Kami ingin menghapus semua riwayat sebelum 1 Januari 2010, untuk membuatnya lebih kecil dan lebih mudah untuk ditangani (kami akan menyimpan salinan lengkap dari sejarah dalam repositori arsip).

Saya tahu komit yang saya inginkan telah menjadi root dari repositori baru. Saya tidak bisa, bagaimanapun, mencari tahu git mojo yang benar untuk memotong repo untuk memulai dengan komit itu. Saya menduga beberapa varian

git filter-branch

akan diperlukan cangkok; itu juga mungkin diperlukan untuk mengobati masing-masing 200 + cabang kami ingin menjaga secara terpisah dan kemudian menambal repo kembali bersama-sama (sesuatu yang saya lakukan tahu bagaimana melakukannya).

Adakah yang pernah melakukan hal seperti ini? Saya mendapat git 1.7.2.3 jika itu penting.

ebneter
sumber

Jawaban:

118

Buat saja cangkok dari induk dari komit root baru Anda ke no induk (atau komit kosong, mis. Komit root asli dari repositori Anda). Misalnyaecho "<NEW-ROOT-SHA1>" > .git/info/grafts

Setelah membuat graft, ia langsung berlaku; Anda harus dapat melihat git logdan melihat bahwa komitmen lama yang tidak diinginkan telah hilang:

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

    Another message

commit 4a46bc886318679d8b15e05aea40b83ff6c3bd47 (grafted)
Author: Your Name <[email protected]>
Date:   Thu May 23 22:27:48 2013 +0200

    Some message

Jika semua tampak seperti yang dimaksudkan, Anda bisa melakukan yang sederhana git filter-branch -- --alluntuk membuatnya permanen.

WASPADALAH: setelah melakukan langkah filter-branch , semua id komit akan berubah, jadi siapa pun yang menggunakan repo lama tidak boleh bergabung dengan siapa pun yang menggunakan repo baru.

apenwarr
sumber
6
Saya harus lakukan git filter-branch --tag-name-filter cat -- --alluntuk memperbarui tag. Tapi saya juga punya tag lama yang menunjuk ke riwayat lama yang ingin saya hapus. Bagaimana saya bisa menyingkirkan semua tag lama itu? Jika saya tidak menghapusnya, maka riwayat lama tidak hilang dan saya masih bisa melihatnya gitk --all.
Craig McQueen
9
"Cukup buat graft dari induk dari komit root baru Anda untuk tidak ada orangtua" perlu penjelasan. Saya mencoba itu dan gagal mengetahui sintaks untuk "no parent". Halaman manual mengklaim ID komit orang tua diperlukan; menggunakan semua nol hanya memberi saya kesalahan.
Marius Gedminas
6
Jika ada orang lain yang bertanya-tanya bagaimana cara kerjanya, itu cukup mudah:echo "<NEW-ROOT-HASH>" > .git/info/grafts
friederbluemle
3
Saya setuju, menjelaskan apa cangkokan itu akan lebih berguna
Charles Martin
4
Dikutip dari halaman wiki tertaut pada graf. "Pada Git 1.6.5, penggantian git yang lebih fleksibel telah ditambahkan, yang memungkinkan Anda mengganti objek apa pun dengan objek lain, dan melacak asosiasi melalui referensi yang dapat didorong dan ditarik di antara repo." Jadi jawaban ini mungkin kedaluwarsa untuk versi git saat ini.
ThorSummoner
130

Mungkin sudah terlambat untuk mengirim balasan, tetapi karena halaman ini adalah hasil Google pertama, mungkin masih bermanfaat.

Jika Anda ingin mengosongkan ruang dalam git repo Anda, tetapi tidak ingin membangun kembali semua komitmen Anda (rebase atau graft), dan masih dapat mendorong / menarik / menggabungkan dari orang-orang yang memiliki repo penuh, Anda dapat menggunakan git clone clone dangkal ( --depth parameter).

; 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

Anda mungkin dapat melakukan repo pada repo yang ada, dengan mengikuti langkah-langkah ini:

; 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

Bagaimana cara menghapus semua tag lokal git?

P: Versi git yang lebih lama tidak mendukung klon / push / pull dari / ke repo dangkal.

Alexandre T.
sumber
9
1 ini adalah yang jawaban yang benar untuk versi yang lebih baru dari Git. (Oh, dan tolong kembali ke PPCG !)
wizzwizz4
6
Bagaimana Anda bisa cdke folder yang baru saja dihapus? Saya merasa ada beberapa informasi yang hilang di sini. Juga, apakah ada cara untuk menerapkan perubahan ini ke repo jarak jauh?
Trogdor
4
@ Jo. Itu akan menjadi jawaban terpilih teratas lainnya. Jawaban ini bukan untuk Anda jika Anda ingin menghilangkan riwayat secara permanen. Ini untuk bekerja dengan sejarah besar.
Tidak ada yang
4
Untuk menjawab pertanyaan saya sendiri: git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02Bekerja seperti pesona!
Micros
5
@ Jo Anda dapat mengubah repo dangkal Anda menjadi yang normal dengan menjalankan git filter-branch -- --all. Ini akan mengubah semua hash di dalamnya tetapi setelah itu Anda akan dapat mendorongnya ke repo baru
Ed'ka
61

Ini metode ini mudah dimengerti dan berfungsi dengan baik. Argumen ke skrip ( $1) adalah referensi (tag, hash, ...) untuk komit yang Anda inginkan untuk menyimpan riwayat Anda.

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

Perhatikan bahwa tag lama akan tetap ada; jadi Anda mungkin harus menghapusnya secara manual

komentar: Saya tahu ini hampir sama dengan @yoyodin, tetapi ada beberapa perintah dan informasi tambahan yang penting di sini. Saya mencoba mengedit jawabannya, tetapi karena ini adalah perubahan besar pada jawaban @ yoyodin, hasil edit saya ditolak, jadi inilah informasinya!

Chris Maes
sumber
Saya menghargai penjelasan yang diberikan untuk git prunedan git gcperintah. Apakah ada penjelasan untuk sisa perintah dalam skrip? Seperti berdiri, tidak jelas argumen apa yang sedang diberikan padanya dan apa yang dilakukan setiap perintah. Terima kasih.
user5359531
2
@ user5359531 terima kasih atas komentar Anda, saya menambahkan beberapa komentar lagi untuk setiap perintah. Semoga ini membantu.
Chris Maes
4
Gabungkan konflik di semua tempat ... tidak terlalu berguna
Warpzit
3
@Warpzit Aku menyingkirkan menggabungkan konflik dengan menambahkan -pke rebaseperintah, seperti yang disarankan dalam jawaban lain
leonbloy
1
Saya mengikuti ini persis, dan semua yang saya dapatkan adalah sejarah yang sama seperti sebelumnya dengan cabang baru mulai dari komit yang ingin saya pangkas dengan semua sejarah yang sama seperti sebelumnya. Tidak ada riwayat yang dihapus.
DrStrangepork
51

Coba metode ini Cara memotong riwayat git :

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

Berikut $1adalah SHA-1 dari komit Anda ingin menyimpan dan script akan membuat cabang baru yang berisi semua komit antara $1dan masterdan semua sejarah yang lebih tua dijatuhkan. Perhatikan bahwa skrip sederhana ini berasumsi bahwa Anda tidak memiliki cabang yang ada dipanggil temp. Perhatikan juga bahwa skrip ini tidak menghapus data git untuk riwayat lama. Jalankan git gc --prune=all && git repack -a -f -F -dsetelah Anda memverifikasi bahwa Anda benar-benar ingin kehilangan semua riwayat. Anda mungkin juga perlu rebase --preserve-mergestetapi diperingatkan bahwa implementasi git dari fitur itu tidak sempurna. Periksa hasilnya secara manual jika Anda menggunakannya.

yoyodyn
sumber
22
Saya mencoba ini, tetapi mendapat konflik gabungan di rebaselangkah. Aneh - Saya tidak berharap bahwa konflik gabungan dapat dimungkinkan dalam situasi ini.
Craig McQueen
2
Gunakan git commit --allow-empty -m "Truncate history"jika komit yang Anda periksa tidak mengandung file apa pun.
friederbluemle
2
Bagaimana cara mendorong ini kembali ke master jarak jauh? Ketika saya melakukan itu, saya berakhir dengan sejarah lama dan baru.
rustyx
1
Apa yang seharusnya 'temp'? Apa yang Anda anggap sebagai argumen untuk ini? Apakah ada contoh bagaimana perintah ini seharusnya terlihat ketika Anda benar-benar menjalankannya? Terima kasih.
user5359531
1
Saya percaya $ 1 adalah hash komit. (Ada lebih banyak detail yang disediakan dalam artikel yang ditautkan).
Chris Nolet
34

Sebagai alternatif untuk menulis ulang sejarah, pertimbangkan untuk menggunakan git replaceseperti dalam artikel ini dari Git Pro buku . Contoh yang dibahas melibatkan penggantian orang tua yang berkomitmen untuk mensimulasikan awal pohon, sambil tetap menjaga sejarah penuh sebagai cabang terpisah untuk diamankan.

Jeff Bowman
sumber
Ya, saya pikir Anda mungkin bisa melakukan apa yang kami inginkan dengan itu, jika Anda juga menghapus cabang sejarah penuh yang terpisah. (Kami mencoba mengecilkan repositori.)
ebneter
1
Saya berkecil hati dengan jawabannya karena berada di luar lokasi; tetapi tautan ke situs GitScm dan tutorial yang tertaut ke dalamnya ditulis dengan sangat baik dan tampaknya langsung ke titik pertanyaan OP.
ThorSummoner
@ Tumormoner Maaf tentang itu! Saya akan mengembangkan jawabannya sedikit lebih lengkap di tempat
Jeff Bowman
Sayangnya ini bukan alternatif untuk menulis ulang sejarah. Ada kalimat yang membingungkan di awal artikel yang mungkin memberi kesan ini. Bisakah itu dihapus dari jawaban ini? Anda akan melihat di artikel bahwa penulis menulis ulang sejarah cabang terpotong, tetapi mengusulkan cara pemasangan kembali cabang "sejarah" warisan menggunakan git replace. Saya percaya ini diperbaiki pada pertanyaan lain di mana Anda memposting jawaban ini.
Mitch
1
Diskusi tentang git replaceversus git graftdilakukan di stackoverflow.com/q/6800692/873282
koppor
25

Jika Anda ingin tetap dengan hulu repositori dengan sejarah penuh , tapi checkout kecil lokal, melakukan clone dangkal dengan git clone --depth=1 [repo].

Setelah mendorong komit, Anda bisa melakukannya

  1. git fetch --depth=1untuk memangkas komitmen lama. Ini membuat komitmen lama dan objeknya tidak dapat dijangkau.
  2. git reflog expire --expire-unreachable=now --all. Untuk mengakhiri semua komitmen lama dan objeknya
  3. git gc --aggressive --prune=all untuk menghapus objek lama

Lihat juga Bagaimana menghapus riwayat git lokal setelah komit? .

Perhatikan bahwa Anda tidak dapat mendorong repositori "dangkal" ini ke tempat lain: "pembaruan dangkal tidak diizinkan". Lihat Remote ditolak (pembaruan dangkal tidak diizinkan) setelah mengubah URL remote Git . Jika Anda ingin itu, Anda harus tetap dengan okulasi.

koppor
sumber
1
Poin nomor 1. membuat perbedaan bagi saya. Cheers
clapas
21

Saya perlu membaca beberapa jawaban dan beberapa info lain untuk memahami apa yang saya lakukan.

1. Abaikan segala yang lebih tua dari komit tertentu

File .git/info/graftsdapat menentukan orang tua palsu untuk komit. Baris dengan hanya komit, mengatakan bahwa komit tidak memiliki orang tua. Jika kami ingin mengatakan bahwa kami hanya peduli dengan 2000 komitmen terakhir, kami dapat mengetik:

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

git rev-parse memberi kita id komit dari induk ke-2000 dari komit saat ini. Perintah di atas akan menimpa file cangkok jika ada. Periksa apakah ada di sana dulu.

2. Tulis ulang riwayat Git (opsional)

Jika Anda ingin menjadikan orangtua palsu yang dicangkokkan ini asli, maka jalankan:

git filter-branch -- --all

Itu akan mengubah semua id komit. Setiap salinan repositori ini perlu diperbarui secara paksa.

3. Bersihkan ruang disk

Saya tidak melakukan langkah 2, karena saya ingin salinan saya tetap kompatibel dengan hulu. Saya hanya ingin menghemat ruang disk. Untuk melupakan semua komitmen lama:

git prune
git gc

Alternatif: salinan dangkal

Jika Anda memiliki salinan repositori lain yang dangkal dan hanya ingin menghemat ruang disk, Anda dapat memperbarui .git/shallow. Tapi hati-hati bahwa tidak ada yang menunjuk pada komitmen dari sebelumnya. Jadi Anda dapat menjalankan sesuatu seperti ini:

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

Entri dalam dangkal berfungsi seperti graft. Tapi hati-hati jangan sampai menggunakan cangkokan dan dangkal pada saat bersamaan. Paling tidak, tidak memiliki entri yang sama di sana, itu akan gagal.

Jika Anda masih memiliki beberapa referensi lama (tag, cabang, kepala jarak jauh) yang menunjuk ke komit yang lebih lama, mereka tidak akan dibersihkan dan Anda tidak akan menghemat lebih banyak ruang disk.

Maikel
sumber
Dukungan untuk <GIT_DIR> / info / cangkok sudah usang dan akan dihapus di versi Git yang akan datang.
danny
Silakan pertimbangkan untuk menggunakan git replace. Lihat stackoverflow.com/questions/6800692/…
Joel AZEMAR
3

Ketika rebase atau push to head / master kesalahan ini dapat terjadi

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'

Untuk mengatasi masalah ini di dashboard git harus menghapus cabang master dari "Cabang yang dilindungi"

masukkan deskripsi gambar di sini

maka Anda dapat menjalankan perintah ini

git push -f origin master

atau

git rebase --onto temp $1 master
HMagdy
sumber
0

Ada terlalu banyak jawaban di sini yang tidak terkini dan beberapa tidak sepenuhnya menjelaskan konsekuensinya. Inilah yang berhasil bagi saya untuk memangkas sejarah menggunakan git 2.26 terbaru:

Pertama buat komit dummy. Komit ini akan muncul sebagai komit pertama di repo terpotong Anda. Anda memerlukan ini karena komit ini akan menampung semua file dasar untuk riwayat yang Anda simpan. SHA adalah ID dari komit sebelumnya dari komit yang ingin Anda pertahankan (dalam contoh ini, 8365366). String 'Initial' akan muncul sebagai pesan komit dari komit pertama. Jika Anda menggunakan Windows, ketikkan perintah di bawah ini dari command prompt Git Bash.

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

Perintah di atas akan mencetak SHA, misalnya d10f7503bc1ec9d367da15b540887730db862023,.

Sekarang ketikkan saja:

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

Ini pertama-tama akan meletakkan semua file sebagai-komit 8365366ke komit dummy d10f750. Kemudian akan memainkan semua komit setelah 8365366 di atas d10f750. Akhirnya masterpointer cabang akan diperbarui untuk komit terakhir diputar.

Sekarang jika Anda ingin mendorong repo terpotong ini, lakukan saja git push -f .

Beberapa hal yang perlu diingat (ini berlaku untuk metode lain dan juga yang ini): Tag tidak ditransfer. Sementara ID komit dan stempel waktu dipertahankan, Anda akan melihat GitHub menunjukkan komit ini dalam judul lumpsum seperti Commits on XY date.

Untungnya dimungkinkan untuk menjaga riwayat terpotong sebagai "arsip" dan kemudian Anda dapat bergabung kembali repo dipangkas dengan arsip repo. Untuk melakukan ini, lihat panduan ini .

Shital Shah
sumber
-3

Anda dapat menghapus direktori, file dan juga seluruh riwayat yang terkait dengan dir atau file menggunakan jar yang disebutkan di bawah ini [unduh] dan perintahnya

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

git clone --besar repo-url cd repo_dir java -jar bfg.jar --delete-folder folder_name git reflog kedaluwarsa - expire = sekarang --semua & & git gc --prune = sekarang - dorongan git agresif --mirror repo_url

Rahul Mohan Kolakandy
sumber
-10
  1. hapus data git, rm .git
  2. git init
  3. tambahkan remote git
  4. dorong paksa
Brad Reid
sumber
6
yang akan bekerja untuk menghapus SEMUA sejarah, tetapi tidak untuk apa yang dia minta: jaga sejarah sejak Januari 2010
Chris Maes
1
Hanya ingin mengucapkan terima kasih karena itu membantu saya dalam skenario saya meskipun ini mungkin bukan jawaban yang tepat untuk pertanyaan
apnerve