Como fazer backup de um repositório Git local?

155

Estou usando o git em um projeto relativamente pequeno e acho que compactar o conteúdo do diretório .git pode ser uma boa maneira de fazer backup do projeto. Mas isso é meio estranho, porque, quando eu restauro, a primeira coisa que preciso fazer é git reset --hard.

Há algum problema com o backup de um repositório Git dessa maneira? Além disso, existe alguma maneira melhor de fazê-lo (por exemplo, um formato portátil git ou algo semelhante?)?

Dan Rosenstark
fonte
Por que ninguém deu a resposta óbvia do uso do git bundle ???
gatopeich
@gatopeich eles fizeram. Rolar para baixo.
Dan Rosenstark 06/06
Todas as respostas votadas contêm uma parede de texto sobre scripts personalizados, mesmo o que começa a ser mencionadogit bundle
gatopeich

Respostas:

23

Comecei a cortar um pouco o script de Yar e o resultado está no github, incluindo páginas de manual e script de instalação:

https://github.com/najamelan/git-backup

Instalação :

git clone "https://github.com/najamelan/git-backup.git"
cd git-backup
sudo ./install.sh

Congratulando-se com todas as sugestões e solicitações de recebimento no github.

#!/usr/bin/env ruby
#
# For documentation please sea man git-backup(1)
#
# TODO:
# - make it a class rather than a function
# - check the standard format of git warnings to be conform
# - do better checking for git repo than calling git status
# - if multiple entries found in config file, specify which file
# - make it work with submodules
# - propose to make backup directory if it does not exists
# - depth feature in git config (eg. only keep 3 backups for a repo - like rotate...)
# - TESTING



# allow calling from other scripts
def git_backup


# constants:
git_dir_name    = '.git'          # just to avoid magic "strings"
filename_suffix = ".git.bundle"   # will be added to the filename of the created backup


# Test if we are inside a git repo
`git status 2>&1`

if $?.exitstatus != 0

   puts 'fatal: Not a git repository: .git or at least cannot get zero exit status from "git status"'
   exit 2


else # git status success

   until        File::directory?( Dir.pwd + '/' + git_dir_name )             \
            or  File::directory?( Dir.pwd                      ) == '/'


         Dir.chdir( '..' )
   end


   unless File::directory?( Dir.pwd + '/.git' )

      raise( 'fatal: Directory still not a git repo: ' + Dir.pwd )

   end

end


# git-config --get of version 1.7.10 does:
#
# if the key does not exist git config exits with 1
# if the key exists twice in the same file   with 2
# if the key exists exactly once             with 0
#
# if the key does not exist       , an empty string is send to stdin
# if the key exists multiple times, the last value  is send to stdin
# if exaclty one key is found once, it's value      is send to stdin
#


# get the setting for the backup directory
# ----------------------------------------

directory = `git config --get backup.directory`


# git config adds a newline, so remove it
directory.chomp!


# check exit status of git config
case $?.exitstatus

   when 1 : directory = Dir.pwd[ /(.+)\/[^\/]+/, 1]

            puts 'Warning: Could not find backup.directory in your git config file. Please set it. See "man git config" for more details on git configuration files. Defaulting to the same directroy your git repo is in: ' + directory

   when 2 : puts 'Warning: Multiple entries of backup.directory found in your git config file. Will use the last one: ' + directory

   else     unless $?.exitstatus == 0 then raise( 'fatal: unknown exit status from git-config: ' + $?.exitstatus ) end

end


# verify directory exists
unless File::directory?( directory )

   raise( 'fatal: backup directory does not exists: ' + directory )

end


# The date and time prefix
# ------------------------

prefix           = ''
prefix_date      = Time.now.strftime( '%F'       ) + ' - ' # %F = YYYY-MM-DD
prefix_time      = Time.now.strftime( '%H:%M:%S' ) + ' - '
add_date_default = true
add_time_default = false

prefix += prefix_date if git_config_bool( 'backup.prefix-date', add_date_default )
prefix += prefix_time if git_config_bool( 'backup.prefix-time', add_time_default )



# default bundle name is the name of the repo
bundle_name = Dir.pwd.split('/').last

# set the name of the file to the first command line argument if given
bundle_name = ARGV[0] if( ARGV[0] )


bundle_name = File::join( directory, prefix + bundle_name + filename_suffix )


puts "Backing up to bundle #{bundle_name.inspect}"


# git bundle will print it's own error messages if it fails
`git bundle create #{bundle_name.inspect} --all --remotes`


end # def git_backup



# helper function to call git config to retrieve a boolean setting
def git_config_bool( option, default_value )

   # get the setting for the prefix-time from git config
   config_value = `git config --get #{option.inspect}`

   # check exit status of git config
   case $?.exitstatus

      # when not set take default
      when 1 : return default_value

      when 0 : return true unless config_value =~ /(false|no|0)/i

      when 2 : puts 'Warning: Multiple entries of #{option.inspect} found in your git config file. Will use the last one: ' + config_value
               return true unless config_value =~ /(false|no|0)/i

      else     raise( 'fatal: unknown exit status from git-config: ' + $?.exitstatus )

   end
end

# function needs to be called if we are not included in another script
git_backup if __FILE__ == $0

fonte
1
@Yar Grande script de pacote, com base no pacote git que defendi na minha resposta abaixo. +1.
VonC
1
Eu já instalei seu aplicativo no meu repositório bare local ... como você o usa depois de instalado ... não há informações sobre isso na documentação, você deve incluir uma seção com um exemplo de como fazer um backup
JAF 15/02
Oi, desculpe, você não faz funcionar. Normalmente você executa sudo install.she o configura (ele usa o sistema de configuração do git) para definir o diretório de destino (consulte o arquivo leia-me no github). Em seguida, você executa git backupdentro do seu repositório. Como nota de rodapé, este foi um experimento com o git bundle e uma resposta a essa pergunta, mas o git bundle nunca faz uma cópia exata absoluta (por exemplo, se me lembro bem, especialmente sobre os controles remotos do git), então pessoalmente eu realmente uso o tar para fazer backup. diretórios git.
144

A outra maneira oficial seria usar o pacote git

Isso criará um arquivo compatível git fetche git pullpara atualizar seu segundo repo.
Útil para backup e restauração incrementais.

Mas se você precisar fazer backup de tudo (porque você não possui um segundo repositório com algum conteúdo antigo já existente), o backup é um pouco mais elaborado, como mencionado na minha outra resposta, após o comentário de Kent Fredric :

$ git bundle create /tmp/foo master
$ git bundle create /tmp/foo-all --all
$ git bundle list-heads /tmp/foo
$ git bundle list-heads /tmp/foo-all

(É uma operação atômica , em vez de criar um arquivo da .gitpasta, como comentado por fantabolous )


Aviso: eu não recomendaria a solução de Pat Notz , que está clonando o repositório. Fazer backup de muitos arquivos é sempre mais complicado do que fazer backup ou atualizar ... apenas um.

Se você olhar para o histórico de edições da resposta do OP Yar , verá que Yar usou no início a , ... com a edição:clone --mirror

Usar isso com o Dropbox é uma bagunça total .
Você terá erros de sincronização e NÃO PODE ROLAR UM DIRETÓRIO DE VOLTA NA DROPBOX.
Use git bundlese você deseja fazer backup na sua caixa de depósito.

A solução atual de Yar usa git bundle.

Eu descanso meu caso.

VonC
fonte
Acabei de verificar isso e é realmente ótimo. Vou ter que tentar alguns agrupamentos e desmembramentos e chefes de lista para me convencer ... mas eu gosto bastante. Mais uma vez obrigado, especialmente pelas anotações no botão --all.
Dan Rosenstark
Um pouco relacionado, há algo de errado em apenas compactar meu repositório local? Eu preciso de um único arquivo de backup, copiar milhares de arquivos em uma unidade externa é incrivelmente lento. Só estou me perguntando se há algo mais eficiente, porque o zip precisa arquivar muitos arquivos na pasta .git.
@faB: a única diferença é que você pode facilmente fazer backup incremental com git bundle. Não é possível com um zip global de todo o repositório local.
VonC
2
Respondendo a um comentário antigo, mas outra diferença entre o pacote e compactar o diretório é o pacote atômico, portanto, não será uma bagunça se alguém atualizar seu repositório no meio da operação.
fantabolous
1
@fantabolous good point. Eu o incluí na resposta para obter mais visibilidade.
VonC
62

A maneira como faço isso é criar um repositório remoto (vazio) (em uma unidade separada, chave USB, servidor de backup ou mesmo github) e depois usá push --mirror-lo para fazer com que o repositório remoto pareça exatamente com o meu local (exceto que o controle remoto é vazio) repositório).

Isso enviará todas as referências (ramificações e tags), incluindo atualizações de avanço rápido. Eu uso isso para criar backups do meu repositório local.

A página do manual descreve assim:

Em vez de nomear cada árbitro para empurrar, especifica que todos os refs sob $GIT_DIR/refs/(o que inclui, mas não está limitado a refs/heads/, refs/remotes/, e refs/tags/) ser espelhado para o repositório remoto. As refs locais recém-criadas serão enviadas para a extremidade remota, as refs atualizadas localmente serão atualizadas à força na extremidade remota e as refs excluídas serão removidas da extremidade remota. Esse é o padrão se a opção de configuração remote.<remote>.mirrorestiver definida.

Fiz um alias para fazer o push:

git config --add alias.bak "push --mirror github"

Então, eu apenas corro git baksempre que quero fazer um backup.

Pat Notz
fonte
+1. Acordado. O pacote git é bom para mover um backup (um arquivo). Mas com uma unidade que você pode conectar em qualquer lugar, o repositório simples também é bom.
VonC 24/01
+1 em awesme, analisarei isso. Obrigado pelos exemplos também.
Dan Rosenstark
@Pat Notz, no final, eu decidi ir com a sua maneira de fazê-lo, e eu coloquei uma resposta abaixo aqui (pontuação permanentemente mantida em zero :)
Dan Rosenstark
Observe que --mirror, na verdade, não executa nenhum tipo de verificação nos objetos que recebe. Provavelmente você deve executar git fsckem algum momento para evitar corrupção.
docwhat
34

[Apenas deixando isso aqui para minha própria referência.]

Meu script de pacote chamado se git-backupparece com isso

#!/usr/bin/env ruby
if __FILE__ == $0
        bundle_name = ARGV[0] if (ARGV[0])
        bundle_name = `pwd`.split('/').last.chomp if bundle_name.nil? 
        bundle_name += ".git.bundle"
        puts "Backing up to bundle #{bundle_name}"
        `git bundle create /data/Dropbox/backup/git-repos/#{bundle_name} --all`
end

Às vezes eu uso git backupe às vezes uso, o git backup different-nameque me dá a maioria das possibilidades de que preciso.

Dan Rosenstark
fonte
2
+1 Como você não usou a --globalopção, esse alias será visto apenas no seu projeto (está definido no seu .git/configarquivo) - provavelmente é isso que você deseja. Obrigado pela resposta mais detalhada e bem formatada.
Pat Notz
1
@ yar: você sabe como realizar essas tarefas sem a linha de comando e usar apenas o tortoisegit (estou procurando uma solução para meus usuários que não são da linha de comando)?
precisa saber é o seguinte
@pastacool, desculpe, eu não sei sobre o git sem a linha de comando. Talvez verifique um IDE relevante como o RubyMine?
Dan Rosenstark
@ intuited, você pode reverter DIRECTORIES com spideroak, ou apenas arquivos (o que o Dropbox faz e eles oferecem 3 GB de espaço)?
Dan Rosenstark
@ Yar: não sei se entendi .. você quer dizer que se eu excluir um diretório suportado pelo Dropbox, perco todas as revisões anteriores dos arquivos contidos nele? Mais informações sobre as políticas de versão do spideroak estão aqui . TBH Eu realmente não usei muito o SpiderOak e não tenho muita certeza de seus limites. Parece que eles teriam fornecido uma solução para esses problemas, porém, eles colocam muita ênfase na competência técnica. Além disso: o Dropbox ainda tem um limite de 30 dias para reversões de contas gratuitas?
intuited
9

As duas respostas para essas perguntas estão corretas, mas ainda estava faltando uma solução completa e curta para fazer backup de um repositório do Github em um arquivo local. A essência está disponível aqui, sinta-se à vontade para usar ou se adaptar às suas necessidades.

backup.sh:

#!/bin/bash
# Backup the repositories indicated in the command line
# Example:
# bin/backup user1/repo1 user1/repo2
set -e
for i in $@; do
  FILENAME=$(echo $i | sed 's/\//-/g')
  echo "== Backing up $i to $FILENAME.bak"
  git clone [email protected]:$i $FILENAME.git --mirror
  cd "$FILENAME.git"
  git bundle create ../$FILENAME.bak --all
  cd ..
  rm -rf $i.git
  echo "== Repository saved as $FILENAME.bak"
done

restore.sh:

#!/bin/bash
# Restore the repository indicated in the command line
# Example:
# bin/restore filename.bak
set -e

FOLDER_NAME=$(echo $1 | sed 's/.bak//')
git clone --bare $1 $FOLDER_NAME.git
Nacho Coloma
fonte
1
Interessante. Mais preciso do que minha resposta. +1
VonC 17/07/2015
Obrigado, isso é útil para o Github. A resposta aceita é a pergunta atual.
Dan Rosenstark
5

Você pode fazer backup do repositório git com git-copy . O git-copy salvou o novo projeto como um repositório simples, significa custo mínimo de armazenamento.

git copy /path/to/project /backup/project.backup

Em seguida, você pode restaurar seu projeto com git clone

git clone /backup/project.backup project
Quanlong
fonte
Argh! essa resposta me fez acreditar que "cópia do git" era um comando oficial do git.
gatopeich
2

Encontre o caminho oficial simples, depois de percorrer as paredes do texto acima, que faria você pensar que não há.

Crie um pacote completo com:

$ git bundle create <filename> --all

Restaure-o com:

$ git clone <filename> <folder>

Esta operação é AFAIK atômica. Verifique os documentos oficiais para obter detalhes detalhados.

Em relação ao "zip": os pacotes configuráveis ​​do git são compactados e surpreendentemente pequenos em comparação com o tamanho da pasta .git.

gatopeich
fonte
Isso não responde a toda a pergunta sobre o zip e também pressupõe que lemos as outras respostas. Corrija-o para que ele seja atômico e lide com toda a pergunta. Fico feliz em torná-la uma resposta aceita (10 anos depois). Obrigado
Dan Rosenstark
0

chegou a esta pergunta via google.

Aqui está o que eu fiz da maneira mais simples.

git checkout branch_to_clone

crie um novo ramo git a partir deste ramo

git checkout -b new_cloned_branch
Switched to branch 'new_cloned_branch'

volte ao ramo original e continue:

git checkout branch_to_clone

Supondo que você tenha estragado tudo e precise restaurar algo do ramo de backup:

git checkout new_cloned_branch -- <filepath>  #notice the space before and after "--"

Melhor parte, se algo estiver errado, você pode simplesmente excluir o ramo de origem e voltar ao ramo de backup !!

NoobEditor
fonte
1
Gosto dessa abordagem - mas não tenho certeza se é uma prática recomendada? Faço ramificações git 'backup' com bastante frequência e, eventualmente, terei muitas ramificações de backup. Não tenho certeza se está tudo bem ou não (com ~ 20 ramificações de backup de datas diferentes). Acho que sempre posso excluir os backups mais antigos eventualmente - mas se eu quiser mantê-los todos - tudo bem? Até agora, está jogando bem - mas seria bom saber se é uma boa ou má prática.
Kyle Vassella
não é algo que seria chamado de melhor prática , presumo que seja mais relacionado aos hábitos individuais de fazer coisas. Geralmente codifico em uma ramificação apenas até que o trabalho seja concluído e mantenho outra ramificação para solicitações ad-hoc . Ambos têm backups, uma vez feito, exclua o ramo principal! :)
NoobEditor