Recolher o histórico de um repositório git

85

Temos um projeto git que tem uma grande história.

Especificamente, no início do projeto havia muitos arquivos de recursos binários no projeto, eles agora foram removidos porque são recursos externos.

No entanto, o tamanho do nosso repositório é> 200 MB (o checkout total é atualmente de ~ 20 MB) devido a ter esses arquivos previamente confirmados.

O que gostaríamos de fazer é "recolher" o histórico de modo que o repositório pareça ter sido criado a partir de uma revisão posterior. Por exemplo

1-----2-----3-----4-----+---+---+
                   \       /
                    +-----+---+---+
  1. Repositório criado
  2. Grande conjunto de arquivos binários adicionados
  3. Grande conjunto de arquivos binários removidos
  4. Novo 'início' pretendido do repositório

Então, efetivamente, queremos perder o histórico do projeto antes de um certo ponto. Neste ponto, há apenas um branch, então não há complicação em tentar lidar com vários pontos de início, etc. No entanto, não queremos perder todo o histórico e iniciar um novo repositório com a versão atual.

Isso é possível ou estamos condenados a ter um repositório inchado para sempre?

Gareth
fonte

Respostas:

89

Você pode remover o inchaço binário e manter o resto de seu histórico. Git permite que você reordene e 'esmague' commits anteriores, para que você possa combinar apenas os commits que adicionam e removem seus grandes arquivos binários. Se as adições foram todas feitas em um commit e as remoções em outro, isso será muito mais fácil do que lidar com cada arquivo.

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

Pesquise aqui os commits que adicionam e excluem seus arquivos binários e observe seus SHA1s, digamos 2bcdefe 3cdef3.

Então, para editar o histórico do repo, use o rebase -icomando com sua opção interativa, começando com o pai do commit onde você adicionou seus binários. Ele irá lançar seu $ EDITOR e você verá uma lista de commits começando com 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
  .
  .

Insira squash 3cdef3como a segunda linha e remova a linha que diz pick 3cdef3da lista. Você agora tem uma lista de ações para o interativo rebaseque irá combinar os commits que adicionam e deleta seus binários em um commit cujo diff é qualquer outra mudança nesses commits. Em seguida, ele reaplicará todos os commits subsequentes em ordem, quando você disser para concluir:

$ git rebase --continue

Isso levará um ou dois minutos.
Agora você tem um repo que não tem mais os binários entrando ou saindo. Mas eles ainda vão ocupar espaço porque, por padrão, o Git mantém as alterações por 30 dias antes que possam ser coletadas como lixo, para que você possa mudar de ideia. Se você deseja removê-los agora:

$ 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                      

Agora você removeu o inchaço, mas manteve o resto de sua história.

Paulo
fonte
7
Você apenas tem que lembrar que se outros já retiraram desse repositório, reescrever o histórico irá confundir o pull. O manual do git-rebase explica como recuperar esses outros repositórios. kernel.org/pub/software/scm/git/docs/git-rebase.html
Otto
esta é uma ótima resposta para o problema específico do usuário, mas não para a pergunta real! A resposta de Davitenio é uma ótima resposta para a questão real.
Sam Watkins
27

Você pode usar git filter-branchcom enxertos para tornar o commit número 4 o novo commit raiz do seu branch. Basta criar o arquivo .git/info/graftscom apenas uma linha contendo o SHA1 do commit número 4.

Se você fizer um git logou gitkverá que esses comandos exibirão o commit número 4 como a raiz do seu branch. Mas nada terá realmente mudado em seu repositório. Você pode excluir .git/info/graftse a saída de git logou gitkserá como antes. Para realmente tornar o commit número 4 o novo root, você terá que executar git filter-branch, sem argumentos.

Davitenio
fonte
Isso é muito melhor do que um rebase, pois não tem problemas para preservar os commits de mesclagem e não altera os carimbos de data / hora. Mais fácil e rápido do que todos os métodos de rebase também.
mmrobins
Na verdade, existe uma maneira de deletar fisicamente todos os commits que não fazem mais parte desse branch? git gc --prune=0não parece limpá-los.
Verhogen
1
@verhogen git gc --prune=nowlimpa fisicamente todos os commits que não são mais referenciados. Se isso não funcionar para você, então você pode ter algum branch de rastreamento remoto que ainda faz referência à raiz antiga. Listar com git branch -re remover a ramificação remota, por exemplo, com git branch -rd origin/mastere executar git gc --prune=nownovamente.
kayahr
20

Graças à postagem de JesperE eu pesquisei git-filter-branch- pode realmente ser o que você deseja. Parece que você poderia reter seus commits anteriores também, exceto que eles seriam modificados desde que seus Big Files foram removidos. Na página do manual git-filter-branch :

Suponha que você queira remover um arquivo (contendo informações confidenciais ou violação de direitos autorais) de todos os commits:

git filter-branch --tree-filter 'nome do arquivo rm' HEAD

Certifique-se de ler aquela página de manual ... obviamente, você deseja fazer isso em um clone sobressalente de seu repositório para ter certeza de que funciona como esperado.

Pat Notz
fonte
2
Confira o link do github ... tem algumas opções poderosas com o comando git-filter-branch: help.github.com/articles/remove-sensitive-data
ricosrealm
5

É o git-fast-exportque você está procurando?

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).
JesperE
fonte