gitリポジトリから古い履歴を削除するにはどうすればよいですか?


208

この特定のシナリオのようなものを見つけることができなかったと思います。

2007年半ばまでさかのぼって、500以上のブランチ、500以上のタグを含む、多くの履歴を持つgitリポジトリがあります。約19,500件のコミットが含まれています。2010年1月1日より前にすべての履歴を削除して、処理を小さく簡単にできるようにします(履歴の完全なコピーをアーカイブリポジトリに保存します)。

新しいリポジトリのルートになりたいコミットを知っています。しかし、そのコミットで開始するためにリポジトリを切り詰める正しいgit mojoを理解することはできません。私はいくつかの変種を推測している

git filter-branch

移植を含むことが必要でしょう。また、個別に保持したい200以上のブランチを個別に処理してから、リポジトリにパッチを再度適用する必要がある場合もあります(私が行う方法を知っています)。

誰かがこのようなことをしたことがありますか?それが重要であれば、私はgit 1.7.2.3を持っています。

回答:


118

新しいルートコミットの親を、親なし(または空のコミット、たとえばリポジトリの実際のルートコミット)に移植するだけです。例えばecho "<NEW-ROOT-SHA1>" > .git/info/grafts

グラフトを作成すると、すぐに有効になります。git log不要な古いコミットがなくなっていることを確認できます。

$ 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

すべてが意図したとおりに見える場合は、シンプルgit filter-branch -- --allにするだけで永続化できます。

用心:やった後、フィルタ分岐ステップを、すべてのIDをコミットが変更されていますので、古いレポを使用して誰もが新しいレポを使用して、誰との合併はなりません。


6
私がしなければならなかったgit filter-branch --tag-name-filter cat -- --allタグを更新します。しかし、私は削除したい古い履歴を指す古いタグも持っています。これらの古いタグをすべて削除するにはどうすればよいですか?それらを削除しない場合、古い履歴は消えず、引き続きで表示できgitk --allます。
Craig McQueen

9
「新しいルートコミットの親の移植片を作成して、親が存在しないようにする」には多少の工夫が必要です。私はそれを試しましたが、「親なし」の構文を理解できませんでした。マニュアルページは、親のコミットIDが必要であると主張しています。すべてゼロを使用すると、エラーが発生します。
Marius Gedminas 2013

6
他の誰かがそれがどのように正確に機能するのか疑問に思っていた場合、それは非常に簡単です:echo "<NEW-ROOT-HASH>" > .git/info/grafts
friederbluemle 2013

3
私は同意しますが、移植片が何であるかを説明することは有用ではありません
Charles Martin

4
移植に関するリンクされたwikiページから引用。「Git 1.6.5以降では、より柔軟なgit置換が追加されました。これにより、任意のオブジェクトを他のオブジェクトに置き換えることができ、リポジトリ間でプッシュおよびプルできる参照を介して関連付けを追跡できます。」したがって、この回答現在のバージョンのgitでは古くなっている可能性があります。
ThorSummoner、2015年

129

返信を投稿するのは遅すぎるかもしれませんが、このページはGoogleの最初の結果なので、参考になるかもしれません。

gitリポジトリの一部のスペースを解放したいが、すべてのコミット(リベースまたはグラフト)を再構築したくないが、完全なリポジトリを持つ人々からプッシュ/プル/マージできるようにするには、gitを 使用できますクローン浅いのクローン(--depthパラメータ)。

; 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

次の手順に従って、既存のリポジトリを浅くすることができる場合があります。

; 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

すべてのgitローカルタグを削除するにはどうすればよいですか?

Ps:古いバージョンのgitは、浅いリポジトリからのクローン/プッシュ/プルフロムをサポートしていません。


9
+1これは Gitの新しいバージョンの正解です。(ああ、PPCGに戻って来てください!)
wizzwizz4

6
cd削除したばかりのフォルダに移動するにはどうすればよいですか?ここに欠けている情報があるように感じます。また、これらの変更をリモートリポジトリに適用する方法はありますか?
Trogdor、2016年

4
@Jezそれは他のトップ投票の答えになるでしょう。この答えは、履歴を永久に削除したい場合には適していません。それ巨大な歴史を扱うためのものです。
誰もいない

4
私自身の質問に答えるには:git clone file:///Users/me/Projects/myProject myClonedProject --shallow-since=2016-09-02魅力のように機能します!
Micros 2018

5
@Jezを実行すると、浅いリポジトリを通常のリポジトリに変換できますgit filter-branch -- --all。これにより、すべてのハッシュが変更されますが、その後、それを新しいリポジトリにプッシュすることができます
Ed'ka

60

この方法は理解しやすく、うまく機能します。スクリプトへの引数($1)は、履歴を保持したいコミットの開始(タグ、ハッシュなど)です。

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

古いタグは引き続き存在することに注意してください。手動で削除する必要があるかもしれません

備考:私はこれが@yoyodinとほとんど同じであることを知っていますが、ここにはいくつかの重要な追加コマンドと情報があります。解答を編集してみましたが、@ yoyodinの解答が大幅に変更されたため、編集が拒否されましたので、こちらをご覧ください。


git prunegit gcコマンドの説明に感謝します。スクリプト内の残りのコマンドの説明はありますか?現状では、どの引数が渡されているのか、各コマンドが何をしているのかは明らかではありません。ありがとう。
user5359531 2016年

2
@ user5359531発言ありがとうございます。コマンドごとにコメントを追加しました。お役に立てれば。
Chris Maes

4
マージはあらゆる場所で競合します...あまり
役に立ち

3
@Warpzit 他の回答で提案されている-pように、rebaseコマンドに追加することでマージの競合を解消しました
leonbloy

1
私はこれを正確にたどりました。私が得たのは以前と同じ履歴であり、新しいブランチをコミットから開始して、以前と同じ履歴でプルーニングしたいです。履歴は削除されませんでした。
DrStrangepork

51

この方法を試してくださいgitの履歴を切り捨てる方法:

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

以下$1は、保持したいコミットのSHA-1です。スクリプトは$1masterとの間のすべてのコミットを含む新しいブランチを作成し、古い履歴はすべて削除されます。この単純なスクリプトは、という既存のブランチがないことを前提としていますtemp。また、このスクリプトは古い履歴のgitデータをクリアしないことに注意してください。git gc --prune=all && git repack -a -f -F -dすべての履歴を本当に削除したいことを確認した後に実行します。またrebase --preserve-merges、その機能のgit実装が完全ではないことも必要になるかもしれませんが、警告されます。使用する場合は、結果を手動で検査してください。


22
私はこれを試しましたが、rebaseステップでマージの競合が発生しました。奇妙なことに、これらの状況でマージの競合が発生する可能性があるとは思っていませんでした。
Craig McQueen 2013年

2
使用すると、git commit --allow-empty -m "Truncate history"コミット場合は、すべてのファイルが含まれていませんチェックアウト。
friederbluemle 2013年

2
これをリモートマスターにプッシュする方法を教えてください。そうするとき、私は古い歴史と新しい歴史の両方に終わります。
rustyx 14

1
「temp」とは何ですか?これに対する引数として何を渡すことになっていますか?実際に実行したときに、これらのコマンドがどのように表示されるかの例はありますか?ありがとう。
user5359531 2016年

1
$ 1はコミットハッシュだと思います。(リンク先の記事に詳細が記載されています)。
Chris Nolet、2016年

34

履歴を書き換える代わりにPro Gitブックのこの記事のgit replaceように使用することを検討してください。説明した例には、ツリーの開始をシミュレートするために親コミットを置き換える一方で、完全な履歴を安全のために別のブランチとして保持することが含まれます。


はい、別の完全な履歴ブランチも使用していれば、おそらくそれで私たちがやりたいことができると思います。(リポジトリを縮小しようとしていました。)
ebneter '26 / 10/26

1
答えがオフサイトにあることに私は落胆しました。しかし、それはGitScmサイトにリンクしており、リンク先のチュートリアルは非常によく書かれており、OPの質問の要点に直接思われます。
ThorSummoner、2015年

@ThorSummoner申し訳ありません!現場でもう少し詳しく答えを作成します
ジェフボーマン、

残念ながら、これは履歴を書き換える代わりにはなりません。記事の冒頭には、おそらくこの印象を与えた混乱した文があります。それはこの回答から削除できますか?記事で、著者が切り捨てられたブランチの履歴を書き換えているが、を使用してレガシー「履歴」ブランチを再接続する方法を提案していることがわかりますgit replace。これは、あなたがこの回答を投稿した別の質問で修正されたと思います。
ミッチ

1
git replace対の議論はgit graftstackoverflow.com
q / 6800692/873282

25

アップストリームリポジトリを完全な履歴保持したいが、ローカルの小さなチェックアウトを保持したい場合は、で浅いクローンを作成します。git clone --depth=1 [repo]

コミットをプッシュすると、次のことができます

  1. git fetch --depth=1古いコミットを削除します。これにより、古いコミットとそのオブジェクトに到達できなくなります。
  2. git reflog expire --expire-unreachable=now --all。すべての古いコミットとそのオブジェクトを期限切れにするには
  3. git gc --aggressive --prune=all 古いオブジェクトを削除するには

コミット後にローカルのgit履歴を削除する方法もご覧ください

この「浅い」リポジトリを他の場所にプッシュできないことに注意してください:「浅い更新は許可されていません」。GitリモートURLの変更後にリモートが拒否されました(浅い更新は許可されません)を参照してください。あなたがそれにしたいなら、あなたは接ぎ木に固執しなければなりません。


1
ポイント1が違いを生みました。乾杯
clapas

21

私は何をしているのかを理解するために、いくつかの回答やその他の情報を読む必要がありました。

1.特定のコミットより古いものはすべて無視する

ファイル.git/info/graftsはコミットの偽の親を定義できます。コミットIDのみの行は、コミットに親がないことを示しています。直近の2000件のコミットのみに関心があると言いたい場合は、次のように入力できます。

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

git rev-parseは、現在のコミットの2000番目の親のコミットIDを提供します。上記のコマンドは、存在する場合、graftsファイルを上書きします。最初にあるかどうかを確認します。

2. Git履歴を書き換える(オプション)

この移植された偽の親を本物にしたい場合は、次を実行:

git filter-branch -- --all

すべてのコミットIDを変更します。このリポジトリのすべてのコピーは、強制的に更新する必要があります。

3.ディスク領域をクリーンアップします

私は自分のコピーがアップストリームとの互換性を維持することを望んだため、ステップ2を実行しませんでした。ディスク容量を節約したかっただけです。古いコミットをすべて忘れるために:

git prune
git gc

代替:浅いコピー

別のリポジトリの浅いコピーがあり、ディスク容量を節約したいだけの場合は、を更新できます.git/shallow。ただし、以前のコミットを指しているものは何もないことに注意してください。したがって、次のように実行できます。

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

浅いエントリは移植片のように機能します。ただし、移植片と浅い部分を同時に使用しないように注意してください。少なくとも、そこに同じエントリがないと失敗します。

古いコミットを指す古い参照(タグ、ブランチ、リモートヘッド)がまだある場合、それらはクリーンアップされず、ディスク領域を節約できません。


<GIT_DIR> / info / graftsのサポートは非​​推奨であり、将来のGitバージョンでは削除される予定です。
ダニー

git replace代わりに使用することを検討してください。stackoverflow.com/questions/6800692/…を
Joel AZEMAR

3

ときリベースまたはプッシュヘッド/マスターこのエラーが発生したことがあり

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ダッシュボードでこの問題を解決するには、「保護されたブランチ」からマスターブランチを削除する必要があります

ここに画像の説明を入力してください

次に、このコマンドを実行できます

git push -f origin master

または

git rebase --onto temp $1 master

0

最新ではない回答が多すぎて、結果を完全に説明できないものもあります。最新のgit 2.26を使用して履歴を削減するために私が働いたことは次のとおりです。

最初にダミーのコミットを作成します。このコミットは、切り詰められたリポジトリの最初のコミットとして表示されます。このコミットは、保持している履歴のすべての基本ファイルを保持するため、これが必要です。SHAは、保持するコミットの前のコミットの IDです(この例では8365366)。文字列「Initial」は、最初のコミットのコミットメッセージとして表示されます。Windowsを使用している場合は、Git Bashコマンドプロンプトから以下のコマンドを入力します。

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

上記のコマンドは、たとえば、SHAを出力しd10f7503bc1ec9d367da15b540887730db862023ます。

次のように入力してください:

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

これにより、最初にすべてのファイルがas-of-commit 8365366でダミーcommitに配置されd10f750ます。次に 8365366 以降のすべてのコミットをの上で再生しd10f750ます。最後に、masterブランチポインタが最後に再生されたコミットに更新されます。

これらの切り捨てられたレポをプッシュしたい場合は、ただ実行してくださいgit push -f

覚えておくべきことはいくつかあります(これらは他の方法にも当てはまります):タグは転送されません。コミットIDとタイムスタンプは保持されますが、GitHubにはこれらのコミットがのようにまとめて表示されますCommits on XY date

幸いにも、切り捨てられた履歴を「アーカイブ」として保持することが可能であり、後でアーカイブリポジトリを使用して、トリミングされたリポジトリを結合することができます。これを行うには、このガイドを参照してください


-3

下記のjar [ダウンロード]とコマンドを使用して、ディレクトリ、ファイル、およびディレクトリまたはファイルに関連するすべての履歴を削除できます

bfg.jarファイル:https ://rtyley.github.io/bfg-repo-cleaner/

git clone --bare repo-url cd repo_dir java -jar bfg.jar --delete-folders folder_name git reflog expire --expire = now --all && git gc --prune = now --aggressive git push --mirror repo_url


-10
  1. gitデータを削除、rm .git
  2. git init
  3. gitリモートを追加する
  4. 力押し

6
それはすべての履歴を削除するように機能しますが、彼が尋ねたものではありません:2010年1月以降の履歴を保持
Chris Maes

1
これは質問に対する正しい答えではないかもしれませんが、私のシナリオで私を助けたので、感謝を伝えたかっただけです
apnerve
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.