Wie sichern Sie ein lokales Git-Repository?

155

Ich verwende git für ein relativ kleines Projekt und finde, dass das Zippen des Inhalts des .git-Verzeichnisses eine gute Möglichkeit ist, das Projekt zu sichern. Aber das ist irgendwie komisch, denn wenn ich wiederherstelle, muss ich als erstes tun git reset --hard.

Gibt es Probleme beim Sichern eines Git-Repos auf diese Weise? Gibt es auch einen besseren Weg, dies zu tun (z. B. ein tragbares Git-Format oder ähnliches?)?

Dan Rosenstark
quelle
Warum hat niemand die offensichtliche Antwort gegeben, ein Git-Bundle zu verwenden ?
Gatopeich
@ Gatopeich haben sie. Runterscrollen.
Dan Rosenstark
Alle positiv bewerteten Antworten enthalten eine Textwand über benutzerdefinierte Skripte, auch die, die zu erwähnen beginntgit bundle
gatopeich

Antworten:

23

Ich habe angefangen, ein bisschen in Yars Skript zu hacken, und das Ergebnis ist auf Github, einschließlich Manpages und Installationsskript:

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

Installation :

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

Begrüßen Sie alle Vorschläge und ziehen Sie Anfrage auf 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

quelle
1
@Yar Tolles Bundle-Skript, basierend auf dem Git-Bundle, für das ich mich in meiner Antwort unten ausgesprochen habe. +1.
VonC
1
Ich habe bereits installiert Ihre Anwendung in meinen lokalen nackten Repository .... wie kann man es benutzen , wenn es installiert ist .... es gibt keine Informationen in Bezug auf den auf der Dokumentation, sollten Sie einen Abschnitt withg ein Beispiel darüber enthalten , wie ein Backup zu machen
JAF
Hallo, tut mir leid, dass du es nicht zum Laufen bringst. Normalerweise führen Sie es aus sudo install.shund konfigurieren es dann (es verwendet das Git-Konfigurationssystem), um das Zielverzeichnis festzulegen (siehe die Readme-Datei auf github). Als nächstes laufen Sie git backupin Ihrem Repository. Als Nebenbemerkung war dies ein Experiment mit dem Git-Bundle und eine Antwort auf diese Frage, aber das Git-Bundle erstellt niemals eine absolut exakte Kopie (z. B. wenn ich mich gut erinnere, insbesondere in Bezug auf Git-Fernbedienungen), also verwende ich persönlich tatsächlich Teer zum Sichern. Git-Verzeichnisse.
144

Der andere offizielle Weg wäre die Verwendung eines Git-Bundles

Dadurch wird eine Datei erstellt, die Ihr zweites Repo unterstützt git fetchund git pullaktualisiert.
Nützlich für inkrementelle Sicherung und Wiederherstellung.

Wenn Sie jedoch alles sichern müssen (da Sie noch kein zweites Repo mit älteren Inhalten haben), ist die Sicherung, wie in meiner anderen Antwort erwähnt, nach Kent Fredrics Kommentar etwas aufwändiger :

$ 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

(Es ist eine atomare Operation , im Gegensatz dazu, ein Archiv aus dem .gitOrdner zu erstellen , wie von fantastabolous kommentiert. )


Warnung: Ich würde Pat Notz ' Lösung , die das Repo klont , nicht empfehlen .
Das Sichern vieler Dateien ist immer schwieriger als das Sichern oder Aktualisieren ... nur eine.

Wenn Sie sich den Bearbeitungsverlauf der OP Yar- Antwort ansehen, werden Sie feststellen, dass Yar zunächst a clone --mirror, ... mit der Bearbeitung verwendet hat:

Die Verwendung mit Dropbox ist ein totales Chaos .
Sie haben Synchronisierungsfehler und können ein Verzeichnis in DROPBOX nicht zurückrollen.
Verwenden git bundleSie diese Option, wenn Sie eine Sicherungskopie Ihrer Dropbox erstellen möchten.

Die aktuelle Lösung von Yar verwendet git bundle.

Ich ruhe meinen Fall aus.

VonC
quelle
Ich habe das gerade überprüft und es ist wirklich großartig. Ich muss ein paar Bündelungen und Entbündelungen und Listenköpfe ausprobieren, um überzeugt zu sein ... aber ich mag es ziemlich. Nochmals vielen Dank, insbesondere für die Hinweise zum Schalter --all.
Dan Rosenstark
Ist etwas falsch daran, nur mein lokales Repository zu komprimieren? Ich benötige eine einzelne Sicherungsdatei. Das Kopieren von Tausenden von Dateien auf ein externes Laufwerk ist unglaublich langsam. Ich frage mich nur, ob es etwas Effizienteres gibt, weil zip so viele Dateien im .git-Ordner archivieren muss.
@faB: Der einzige Unterschied besteht darin, dass Sie problemlos inkrementelle Sicherungen durchführen können git bundle. Mit einer globalen Zip-Datei des gesamten lokalen Repos ist dies nicht möglich.
VonC
2
Wenn Sie auf einen alten Kommentar antworten, aber ein weiterer Unterschied zwischen Bundle und Zippen des Verzeichnisses besteht darin, dass Bundle atomar ist. Es wird also nicht durcheinander gebracht, wenn jemand Ihr Repo während des Vorgangs aktualisiert.
fantastisch
1
@ Fantabolous guter Punkt. Ich habe es in die Antwort für mehr Sichtbarkeit aufgenommen.
VonC
62

Die Art und Weise, wie ich dies mache, besteht darin, ein Remote-Repository (auf einem separaten Laufwerk, einem USB-Stick, einem Backup-Server oder sogar einem Github) zu erstellen und push --mirrordieses Remote-Repo dann genau so aussehen zu lassen, wie mein lokales (außer dass die Remote ein nacktes Repository ist) Repository).

Dadurch werden alle Verweise (Zweige und Tags) einschließlich nicht schneller Vorlaufaktualisierungen übertragen. Ich verwende dies zum Erstellen von Backups meines lokalen Repositorys.

Die Manpage beschreibt es so:

Anstatt jede ref auf Stoß zu nennen, an , dass alle unter Lit. $GIT_DIR/refs/(das schließt jedoch nicht darauf beschränkt ist refs/heads/, refs/remotes/und refs/tags/) an den entfernten Repository gespiegelt werden. Neu erstellte lokale Refs werden an das Remote-Ende verschoben, lokal aktualisierte Refs werden auf dem Remote-Ende zwangsweise aktualisiert und gelöschte Refs werden vom Remote-Ende entfernt. Dies ist die Standardeinstellung, wenn die Konfigurationsoption festgelegt remote.<remote>.mirrorist.

Ich habe einen Alias ​​für den Push erstellt:

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

Dann git bakstarte ich einfach, wann immer ich ein Backup machen möchte.

Pat Notz
quelle
+1. Einverstanden. Git Bundle ist schön, um ein Backup zu verschieben (eine Datei). Aber mit einem Laufwerk, das Sie überall anschließen können, ist auch das bloße Repo in Ordnung.
VonC
+1 awesme, ich werde das untersuchen. Danke auch für die Beispiele.
Dan Rosenstark
@Pat Notz, am Ende habe ich beschlossen, mit Ihrer Vorgehensweise fortzufahren, und ich habe hier unten eine Antwort angegeben (Punktzahl dauerhaft auf Null gehalten :)
Dan Rosenstark
Beachten Sie, dass --mirrorfür die Objekte, die es erhält, keine Überprüfung durchgeführt wird. Sie sollten wahrscheinlich git fsckirgendwann laufen , um Korruption zu verhindern.
docwhat
34

[Lassen Sie dies hier nur zu meiner eigenen Referenz.]

Mein Bundle-Skript namens git-backupsieht so aus

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

Manchmal benutze ich git backupund manchmal benutze ich, git backup different-namewas mir die meisten Möglichkeiten gibt, die ich brauche.

Dan Rosenstark
quelle
2
+1 Da Sie die --globalOption nicht verwendet haben, wird dieser Alias ​​nur in Ihrem Projekt angezeigt (er ist in Ihrer .git/configDatei definiert) - wahrscheinlich möchten Sie das. Vielen Dank für die detailliertere und schön formatierte Antwort.
Pat Notz
1
@yar: Weißt du, wie man diese Aufgaben ohne die Kommandozeile erledigt und stattdessen nur Schildpatt verwendet (suche nach einer Lösung für meine Nicht-Kommandozeilen-Windoze-Benutzer)?
Pastacool
@pastacool, sorry, ich weiß nichts über Git ohne die Kommandozeile. Vielleicht schauen Sie sich eine relevante IDE wie RubyMine an?
Dan Rosenstark
@intuited, können Sie DIRECTORIES mit Spideroak oder nur Dateien zurücksetzen (was die Dropbox tut und sie Ihnen 3 GB Speicherplatz geben)?
Dan Rosenstark
@Yar: Ich bin mir nicht sicher, ob ich das verstehe. Meinst du damit, dass ich beim Löschen eines von Dropbox unterstützten Verzeichnisses alle vorherigen Revisionen der darin enthaltenen Dateien verliere? Weitere Informationen zu den Versionsrichtlinien von spideroak finden Sie hier . TBH Ich habe SpiderOak nicht wirklich oft benutzt und bin mir seiner Grenzen nicht ganz sicher. Es scheint, als hätten sie eine Lösung für solche Probleme geliefert, aber sie legen großen Wert auf technische Kompetenz. Außerdem: Hat Dropbox noch ein 30-Tage-Limit für Rollbacks für kostenlose Konten?
Intuitiert
9

Beide Antworten auf diese Fragen sind richtig, aber mir fehlte immer noch eine vollständige, kurze Lösung zum Sichern eines Github-Repositorys in einer lokalen Datei. Das Wesentliche finden Sie hier. Sie können sich gerne an Ihre Bedürfnisse anpassen.

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
quelle
1
Interessant. Genauer als meine Antwort. +1
VonC
Danke, das ist nützlich für Github. Die akzeptierte Antwort ist auf die aktuelle Frage.
Dan Rosenstark
5

Sie können das Git-Repo mit Git-Copy sichern . git-copy hat ein neues Projekt als reines Repo gespeichert, was minimale Speicherkosten bedeutet.

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

Dann können Sie Ihr Projekt mit wiederherstellen git clone

git clone /backup/project.backup project
Quanlong
quelle
Argh! Diese Antwort ließ mich glauben, dass "Git-Kopie" ein offizieller Git-Befehl war.
Gatopeich
2

Habe den einfachen offiziellen Weg gefunden, nachdem du durch die Wände des Textes oben gewatet bist, der dich denken lässt, dass es keinen gibt.

Erstellen Sie ein komplettes Bundle mit:

$ git bundle create <filename> --all

Stellen Sie es wieder her mit:

$ git clone <filename> <folder>

Diese Operation ist atomare AFAIK. Überprüfen Sie die offiziellen Dokumente auf die wichtigsten Details.

In Bezug auf "zip": Git-Bundles sind komprimiert und im Vergleich zur Größe des .git-Ordners überraschend klein.

gatopeich
quelle
Dies beantwortet nicht die gesamte Frage zu zip und setzt auch voraus, dass wir die anderen Antworten gelesen haben. Bitte beheben Sie es so, dass es atomar ist und die gesamte Frage bearbeitet, und ich bin froh, dass es akzeptiert wird (10 Jahre später). Danke
Dan Rosenstark
0

kam zu dieser Frage über Google.

Hier ist, was ich auf einfachste Weise getan habe.

git checkout branch_to_clone

Erstellen Sie dann einen neuen Git-Zweig aus diesem Zweig

git checkout -b new_cloned_branch
Switched to branch 'new_cloned_branch'

Kommen Sie zurück zum ursprünglichen Zweig und fahren Sie fort:

git checkout branch_to_clone

Angenommen, Sie haben es vermasselt und müssen etwas aus dem Sicherungszweig wiederherstellen:

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

Das Beste daran, wenn etwas schief geht, können Sie einfach den Quellzweig löschen und zum Sicherungszweig zurückkehren !!

NoobEditor
quelle
1
Ich mag diesen Ansatz - bin mir aber nicht sicher, ob es sich um eine bewährte Methode handelt? Ich mache ziemlich oft 'Backup'-Git-Zweige, und irgendwann werde ich viele Backup-Zweige haben. Ich bin mir nicht sicher, ob dies in Ordnung ist oder nicht (mit ~ 20 Sicherungszweigen von verschiedenen Daten). Ich denke, ich könnte die älteren Backups irgendwann immer löschen - aber wenn ich sie alle behalten möchte - ist das in Ordnung? Bisher spielt es gut - aber es wäre schön zu wissen, ob es eine gute oder eine schlechte Übung ist.
Kyle Vassella
Es ist nicht etwas, das als Best Practice bezeichnet werden würde , ich nehme an, es hängt eher mit den individuellen Gewohnheiten zusammen, Dinge zu tun. Ich codiere im Allgemeinen nur in einem Zweig, bis die Arbeit erledigt ist, und behalte einen anderen Zweig für Ad-hoc- Anfragen. Beide haben Backups, sobald sie fertig sind, löschen Sie den Hauptzweig! :)
NoobEditor