web-dev-qa-db-ja.com

Gitリポジトリの履歴を折りたたむ

かなり大きな歴史を持つgitプロジェクトがあります。

具体的には、プロジェクトの初期段階でプロジェクトにはかなり多くのバイナリリソースファイルがありましたが、これらは事実上外部リソースであるため削除されました。

ただし、これらのファイルが以前にコミットされているため、リポジトリのサイズは200MBを超えています(現在、合計チェックアウトは約20MBです)。

私たちがやりたいのは、履歴を「崩壊」させ、リポジトリがそれよりも新しいリビジョンから作成されたように見えるようにすることです。例えば

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. 作成されたリポジトリ
  2. 追加された大量のバイナリファイル
  3. 大量のバイナリファイルが削除されました
  4. リポジトリの新しい意図した「開始」

事実上、ある時点の前にプロジェクトの履歴を失いたいのです。この時点ではブランチは1つしかないため、複数の開始点などに対処しようとすることで問題はありません。ただし、すべての履歴を失い、現在のバージョンで新しいリポジトリを開始する必要はありません。

これは可能ですか、それとも永遠に肥大化したリポジトリを持つ運命にありますか?

80
Gareth

バイナリの膨張を削除して、残りの履歴を保持できます。 Gitでは、以前のコミットを並べ替えて「スカッシュ」することができるため、大きなバイナリファイルを追加および削除するコミットだけを組み合わせることができます。追加がすべて1つのコミットで行われ、削除が別のコミットで行われた場合、これは各ファイルを処理するよりもはるかに簡単になります。

$ git log --stat       # list all commits and commit messages 

バイナリファイルを追加および削除するコミットを検索し、2bcdefおよび3cdef3などのSHA1を書き留めます。

次に、リポジトリの履歴を編集するには、バイナリを追加したコミットの親から始めて、rebase -iコマンドとそのインタラクティブオプションを使用します。 $ EDITORが起動し、2bcdefで始まるコミットのリストが表示されます。

$ git rebase -i 2bcdef^    # generate a pick list of all commits starting with 2bcdef
# Rebasing zzzzzz onto yyyyyyy 
# 
# Commands: 
#  pick = use commit 
#  edit = use commit, but stop for amending 
#  squash = use commit, but meld into previous commit 
# 
# If you remove a line here THAT COMMIT WILL BE LOST.
#
pick 2bcdef   Add binary files and other edits
pick xxxxxx   Another change
  .
  .
pick 3cdef3   Remove binary files; link to them as external resources
  .
  .

2行目としてsquash 3cdef3を挿入し、リストからpick 3cdef3と書かれている行を削除します。これで、インタラクティブrebaseのアクションのリストができました。これは、バイナリを追加および削除するコミットを1つのコミットに結合します。次に、完了するように指示すると、後続のすべてのコミットが順番に再適用されます。

$ git rebase --continue

これには1〜2分かかります。
これでバイナリが出入りしないリポジトリができました。ただし、デフォルトでは、Gitは変更を30日間保持してからガベージコレクションが行われるため、スペースを占有します。今すぐ削除したい場合:

$ git reflog expire --expire=1.minute refs/heads/master
      #all deletions up to 1 minute  ago available to be garbage-collected
$ git fsck --unreachable      # lists all the blobs(files) that will be garbage-collected
$ git Prune
$ git gc                      

これで肥大化は解消されましたが、残りの履歴は保持されます。

88
Paul

git filter-branchをグラフトで使用して、コミット番号4をブランチの新しいルートコミットにすることができます。コミット番号4のSHA1を含む1行だけのファイル.git/info/graftsを作成します。

git logまたはgitkを実行すると、これらのコマンドがブランチのルートとしてコミット番号4を表示することがわかります。しかし、実際にはリポジトリ内で何も変更されていません。 .git/info/graftsを削除すると、git logまたはgitkの出力は以前と同じになります。実際にコミット番号4を新しいルートにするには、git filter-branchを引数なしで実行する必要があります。

26
davitenio

JesperEの投稿のおかげで、私はgit-filter-branch-それは実際にあなたが望むものかもしれません。 Big Filesが削除されてから変更される場合を除き、以前のコミットも保持できるようです。 git-filter-branch manページ から:

すべてのコミットからファイル(機密情報または著作権侵害を含む)を削除するとします。

git filter-branch --tree-filter 'rm filename' HEAD

必ずそのマニュアルページを読んでください...明らかに、期待どおりに動作することを確認するために、リポジトリの予備のクローンでこれを行いたいでしょう。

20
Pat Notz

git-fast-exportお探しのものは?

NAME
   git-fast-export - Git data exporter

SYNOPSIS
   git-fast-export [options] | git-fast-import

DESCRIPTION
   This program dumps the given revisions in a form suitable to be piped into git-fast-
   import(1).

   You can use it as a human readable bundle replacement (see git-bundle(1)), or as a kind
   of an interactive git-filter-branch(1).
5
JesperE