Uma maneira de fazer isso é o inverso - remova tudo, exceto o arquivo que deseja manter.
Basicamente, faça uma cópia do repositório e use git filter-branch
para remover tudo, exceto os arquivos / pastas que deseja manter.
Por exemplo, tenho um projeto do qual desejo extrair o arquivo tvnamer.py
para um novo repositório:
git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD
Isso costuma git filter-branch --tree-filter
passar por cada confirmação, executar o comando e confirmar novamente o conteúdo do diretório resultante. Isso é extremamente destrutivo (então você só deve fazer isso em uma cópia do seu repositório!), E pode demorar um pouco (cerca de 1 minuto em um repositório com 300 commits e cerca de 20 arquivos)
O comando acima apenas executa o seguinte script de shell em cada revisão, que você teria que modificar, é claro (para excluí-lo do seu subdiretório em vez de tvnamer.py
):
for f in *; do
if [ $f != "tvnamer.py" ]; then
rm -rf $f;
fi;
done
O maior problema óbvio é que ele deixa todas as mensagens de commit, mesmo que não estejam relacionadas ao arquivo restante. O script git-remove-empty-commits corrige isso ..
git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'
Você precisa usar o -f
argumento force executado filter-branch
novamente com qualquer coisa em refs/original/
(que basicamente é um backup)
Claro que isso nunca será perfeito, por exemplo, se suas mensagens de commit mencionarem outros arquivos, mas é o mais próximo que um git atual permite (pelo menos pelo que eu sei).
Novamente, apenas execute isso em uma cópia do seu repositório! - mas em resumo, para remover todos os arquivos, exceto "thisismyfilename.txt":
git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD
git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'