Come eseguire il backup di un repository Git locale?


155

Sto usando git su un progetto relativamente piccolo e trovo che comprimere i contenuti della directory .git potrebbe essere un ottimo modo per eseguire il backup del progetto. Ma questo è un po 'strano perché, quando ripristino, la prima cosa che devo fare è git reset --hard.

Ci sono problemi con il backup di un repository git in questo modo? Inoltre, c'è un modo migliore per farlo (ad esempio, un formato git portatile o qualcosa di simile?)?


Perché nessuno ha dato l'ovvia risposta dell'utilizzo del bundle git ???
gatopeich,

@gatopeich l'hanno fatto. Scorri verso il basso.
Dan Rosenstark,

Tutte le risposte votate contengono un muro di testo sugli script personalizzati, anche quello che inizia a menzionaregit bundle
gatopeich

Risposte:


23

Ho iniziato a modificare un po 'lo script di Yar e il risultato è su github, incluse le pagine man e lo script di installazione:

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

Installazione :

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

Accogliendo con favore tutti i suggerimenti e richiedi pull su 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

1
@Yar Ottimo script bundle, basato sul bundle git che ho sostenuto nella mia risposta di seguito. +1.
VonC,

1
Ho già installato la tua applicazione in mio repository nudo locale .... come si usa una volta che è installato .... non c'è nessuna info riguardo che sulla documentazione, è necessario includere una sezione withg un esempio su come fare un backup
JAF,

Ciao, scusa se non riesci a farlo funzionare. Normalmente esegui sudo install.sh, quindi configuralo (usa il sistema git config) per impostare la directory di destinazione (vedi il file readme su github). Successivamente si esegue git backupall'interno del repository. Come sidenote, questo è stato un esperimento con git bundle e una risposta a questa domanda, ma git bundle non fa mai una copia esatta assoluta (es. Se ricordo bene, specialmente per quanto riguarda i telecomandi git), quindi personalmente uso effettivamente tar per il backup. directory git.

144

L'altro modo ufficiale sarebbe usare il bundle git

Ciò creerà un file che supporta git fetche git pullper aggiornare il tuo secondo repository.
Utile per il backup e il ripristino incrementali.

Ma se è necessario eseguire il backup di tutto (poiché non si dispone di un secondo repository con alcuni contenuti più vecchi già presenti), il backup è un po 'più elaborato da eseguire, come menzionato nell'altra mia risposta, dopo il commento di 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

(È un'operazione atomica , al contrario di fare un archivio dalla .gitcartella, come commentato da fantabolous )


Avvertenza: non consiglierei la soluzione di Pat Notz , che sta clonando il repository. Il backup di molti file è sempre più complicato del backup o dell'aggiornamento ... solo uno.

Se guardi la cronologia delle modifiche della risposta OP Yar , vedresti che Yar ha usato inizialmente un , ... con la modifica:clone --mirror

Usarlo con Dropbox è un casino totale .
Avrai errori di sincronizzazione e NON PUOI ROTARE UNA DIRECTORY INDIETRO IN DROPBOX.
Utilizzare git bundlese si desidera eseguire il backup nella casella personale.

La soluzione attuale di Yar utilizza git bundle.

Non ho niente da aggiungere.


Ho appena controllato questo ed è davvero fantastico. Dovrò provare un po 'di raggruppamento, disaggregazione e testate di lista per essere convinto ... ma mi piace parecchio. Grazie ancora, soprattutto per le note sull'interruttore --all.
Dan Rosenstark,

In qualche modo correlato, c'è qualcosa di sbagliato nel comprimere il mio repository locale? Ho bisogno di un singolo file di backup, copiare migliaia di file su un'unità esterna è incredibilmente lento. Mi chiedo solo se c'è qualcosa di più efficiente perché zip deve archiviare così tanti file nella cartella .git.

@faB: l'unica differenza è che puoi facilmente fare un backup incrementale con git bundle. Non è possibile con una zip globale di tutto il repository locale.
VonC

2
Rispondere a un vecchio commento, ma un'altra differenza tra bundle e zippare la directory è bundle è atomico, quindi non verrà incasinato se qualcuno capita di aggiornare il tuo repository nel mezzo dell'operazione.
fantabolous,

1
@fantabolous buon punto. L'ho incluso nella risposta per una maggiore visibilità.
VonC,

62

Il modo in cui lo faccio è creare un repository remoto (vuoto) (su un'unità separata, chiave USB, server di backup o persino github) e quindi utilizzarlo push --mirrorper far sembrare quel repository remoto esattamente come quello locale (tranne per il fatto che il telecomando è nudo repository).

Ciò spingerà tutti i riferimenti (rami e tag) inclusi gli aggiornamenti non a avanzamento rapido. Lo uso per creare backup del mio repository locale.

La pagina man la descrive così:

Invece di nominare ogni ref a spinta, specifica che tutti arbitri inferiore $GIT_DIR/refs/(che include ma non è limitato a refs/heads/, refs/remotes/e refs/tags/) essere mirroring repository remoto. I riferimenti locali appena creati verranno spinti all'estremità remota, i riferimenti aggiornati localmente verranno forzati sull'estremità remota e i riferimenti eliminati verranno rimossi dall'estremità remota. Questo è il valore predefinito se l'opzione di configurazione remote.<remote>.mirrorè impostata.

Ho fatto un alias per fare la spinta:

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

Quindi, corro git bakogni volta che voglio fare un backup.


+1. Concordato. Il bundle git è utile per spostare un backup (un file). Ma con un'unità che puoi collegare ovunque, anche il repository nudo va bene.
VonC,

+1 stupendo, esaminerò questo. Grazie anche per gli esempi.
Dan Rosenstark,

@Pat Notz, alla fine ho deciso di seguire il tuo modo di farlo, e ho inserito una risposta qui sotto (punteggio permanentemente tenuto a zero :)
Dan Rosenstark,

Nota che in --mirrorrealtà non viene eseguito alcun tipo di verifica sugli oggetti che ottiene. Probabilmente dovresti correre git fsckad un certo punto per prevenire la corruzione.
docwhat il

34

[Sto solo lasciando questo qui per mio riferimento.]

Il mio script bundle chiamato git-backupè simile al seguente

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

A volte uso git backupea volte uso ciò git backup different-nameche mi dà la maggior parte delle possibilità di cui ho bisogno.


2
+1 Poiché non hai utilizzato l' --globalopzione questo alias verrà visualizzato solo nel tuo progetto (è definito nel tuo .git/configfile) - probabilmente è quello che vuoi. Grazie per la risposta più dettagliata e ben formattata.
Pat Notz,

1
@yar: sai come eseguire queste attività senza la riga di comando e invece usi solo tortoisegit (sto cercando una soluzione per i miei utenti non-command-windoze)?
pastacool

@pastacool, scusami non conosco affatto Git senza la riga di comando. Magari dai un'occhiata a un IDE rilevante come RubyMine?
Dan Rosenstark,

@intuited, puoi eseguire il rollback di DIRECTORIES con spideroak o solo dei file (cosa che fa Dropbox e ti danno 3GB di spazio)?
Dan Rosenstark,

@Yar: non sono sicuro di aver capito .. vuoi dire che se cancello una directory supportata da Dropbox, perdo tutte le revisioni precedenti dei file in essa contenuti? Maggiori informazioni sulle politiche di versioning di spideroak sono disponibili qui . TBH Non ho usato molto SpiderOak e non sono completamente sicuro dei suoi limiti. Sembra che avrebbero fornito una soluzione a tali problemi, ma pongono molta enfasi sulla competenza tecnica. Inoltre: Dropbox ha ancora un limite di 30 giorni sui rollback per gli account gratuiti?
intuito il

9

Entrambe le risposte a queste domande sono corrette, ma mancava ancora una soluzione completa e breve per il backup di un repository Github in un file locale. L' essenza è disponibile qui, sentiti libero di fare fork o adattarti alle tue esigenze.

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 git@github.com:$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

1
Interessante. Più preciso della mia risposta. +1
VonC,

Grazie, questo è utile per Github. La risposta accettata è alla domanda attuale.
Dan Rosenstark,

5

È possibile eseguire il backup del repository git con git-copy . git-copy ha salvato il nuovo progetto come repository nudo, significa un costo di archiviazione minimo.

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

Quindi puoi ripristinare il tuo progetto con git clone

git clone /backup/project.backup project

Argh! questa risposta mi ha fatto credere che "git copy" fosse un comando git ufficiale.
gatopeich,

2

Trovato il semplice modo ufficiale dopo aver attraversato i muri di testo sopra che ti farebbe pensare che non ce ne sia.

Crea un pacchetto completo con:

$ git bundle create <filename> --all

Ripristina con:

$ git clone <filename> <folder>

Questa operazione è atomica AFAIK. Controlla i documenti ufficiali per i dettagli grintosi.

Riguardo a "zip": i bundle git sono compressi e sorprendentemente piccoli rispetto alle dimensioni della cartella .git.


Questo non risponde all'intera domanda su zip e presuppone anche che abbiamo letto le altre risposte. Correggilo in modo che sia atomico e gestisca l'intera domanda e sono contento di averlo accettato come risposta (10 anni dopo). Grazie
Dan Rosenstark il

0

è arrivato a questa domanda tramite google.

Ecco cosa ho fatto nel modo più semplice.

git checkout branch_to_clone

quindi creare un nuovo ramo git da questo ramo

git checkout -b new_cloned_branch
Switched to branch 'new_cloned_branch'

tornare alla filiale originale e continuare:

git checkout branch_to_clone

Supponendo di aver sbagliato e di dover ripristinare qualcosa dal ramo di backup:

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

La parte migliore se qualcosa è rovinato, puoi semplicemente eliminare il ramo di origine e tornare al ramo di backup !!


1
Mi piace questo approccio, ma non sono sicuro che sia la migliore pratica? Faccio i rami git di 'backup' abbastanza spesso e alla fine avrò molti rami di backup. Non sono sicuro che vada bene o meno (con ~ 20 filiali di backup di date diverse). Immagino che alla fine potrei sempre eliminare i backup più vecchi, ma se voglio mantenerli tutti, va bene? Finora sta giocando bene, ma sarebbe bello sapere se è una buona o cattiva pratica.
Kyle Vassella,

non è qualcosa che sarebbe definito come la migliore pratica , presumo sia più legato a quei singoli habbit di fare cose. Generalmente codice in un ramo solo fino a quando il lavoro non viene svolto e conservo un altro ramo per richieste ad hoc . Entrambi hanno i backup, una volta fatto, cancella il ramo principale! :)
NoobEditor il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.