2010-01-24 13 views
137

Ich verwende git in einem relativ kleinen Projekt und finde, dass das Zippen des Inhalts des .git-Verzeichnisses eine gute Möglichkeit ist, das Projekt zu sichern. Aber das ist irgendwie seltsam, denn wenn ich es wiederherstelle, ist das erste, was ich tun muss, git reset --hard.Wie sichert man ein lokales Git Repository?

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

Antwort

22

begann ich ein bisschen auf Yar Skript zu hacken, und das Ergebnis ist auf Github, man-Seiten einschließlich und Installationsskript:

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

Installations:

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

Begrüßung alle Vorschläge und Pull-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 
+0

@Yar Großes Bundle-Skript, basierend auf dem Gitbündel, für das ich in meiner Antwort unten eintrat. +1. – VonC

+1

Ich habe Ihre App bereits in meinem lokalen Repository installiert ... Wie benutze ich sie nach der Installation ... Es gibt keine Informationen darüber in der Dokumentation. Sie sollten einen Abschnitt mit einem Beispiel für die Erstellung einer Sicherungskopie einschließen – JAF

+0

Hallo, tut mir leid, dass du es nicht zur Arbeit bekommst. Normalerweise führt man 'sudo install.sh' aus, dann konfiguriert man es (es benutzt git config system) um das Zielverzeichnis zu setzen (siehe Readme-Datei auf github). Als nächstes führen Sie 'git backup' in Ihrem Repository aus. Als Nebenbemerkung, dies war ein Experiment mit Git-Bundle und eine Antwort auf diese Frage, aber Gitbündel macht nie eine absolut genaue Kopie (zB wenn ich mich gut erinnere, besonders in Bezug auf Git-Fernbedienungen), so persönlich benutze ich tatsächlich tar zu sichern. Git Verzeichnisse. – nus

129

Die andere würde offical Weise verwenden git bundle

, dass eine Datei erstellen wird, die git fetch und git pull unterstützen, um Ihre zweite Repo zu aktualisieren.
Nützlich für inkrementelle Sicherung und Wiederherstellung.

Aber wenn Sie benötigen, um Backup alles (weil Sie nicht ein zweites Repo mit einigen älteren Inhalten, die bereits an der richtigen Stelle), ist die Sicherung ein bisschen aufwendiger zu tun, wie in meiner anderen Antwort erwähnt, nach Kent Fredric ‚s Kommentar:

$ 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 ein atomare Operation, wie auf sie ein Archiv aus dem Ordner .git Gegensatz als commented von fantabolous)


Warnung: Ich würde nicht empfehlen Pat Notz 's solution, die das Repo Klonen ist.
Viele Dateien zu sichern ist immer schwieriger als das Sichern oder Aktualisieren ... nur eines.

Wenn man sich die history of edits der aussehen OP Yaranswer, würden Sie sehen, dass Yar zuerst verwendet eine clone --mirror ... mit dem edit:

Mit diesem mit Dropbox ein totales Durcheinander ist.
Sie werden Synchronisierungsfehler haben, und Sie können ein Verzeichnis nicht zurück in Droppbox rollen.
Verwenden Sie git bundle, wenn Sie Ihre Dropbox sichern möchten.

Yars current solution verwendet git bundle.

Ich ruhe meinen Fall aus.

+0

Ich habe das gerade überprüft und es ist wirklich großartig. Ich muss versuchen, etwas Bündelung und Entbündelung und Listen-Köpfe zu überzeugen ... aber ich mag es ziemlich viel. Nochmals vielen Dank, vor allem für die Notizen auf dem --all Schalter. –

+0

Etwas verwandt, ist es etwas falsch mit nur meinem lokalen Repository zippen? Ich brauche eine einzige Backup-Datei, das Kopieren von Tausenden von Dateien auf einem externen Laufwerk ist unglaublich langsam. Ich frage mich nur, ob es etwas effizienteres gibt, weil zip so viele Dateien im .git-Ordner archivieren muss. –

+0

@faB: Der einzige Unterschied ist, dass Sie einfach * inkrementelle * Backup mit 'git Bundle' machen können. Es ist nicht möglich mit einem globalen Zip des gesamten lokalen Repos. – VonC

58

Die Art, wie ich dies tue, ist ein Remote (blank) Repository (auf einem separaten Laufwerk, USB Key, Backup-Server oder sogar GitHub) und dann push --mirror, damit diese Remote-Repo genauso aussehen wie meine lokale (außer die Fernbedienung ist ein bare Repository).

Dadurch werden alle Refs (Verzweigungen und Tags) einschließlich Nicht-Fast-Forward-Aktualisierungen übertragen. Ich benutze dies zum Erstellen von Backups meines lokalen Repository.

Die man page beschreibt es wie folgt:

Anstatt jede ref der Namensgebung zu drücken, gibt an, dass alle refs unter $GIT_DIR/refs/ (das aber auf refs/heads/ ist nicht beschränkt enthält, refs/remotes/ und refs/tags/) zu dem gespiegelten werden Remote-Repository. Neu erstellte lokale Refs werden an das Remote-Ende gesendet, lokal aktualisierte Refs werden am Remote-Ende aktualisiert, und gelöschte Refs werden vom Remote-Ende entfernt. Dies ist der Standardwert, wenn die Konfigurationsoption remote.<remote>.mirror festgelegt ist.

machte ich einen Alias ​​den Push-to-do:

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

Dann habe ich git bak nur ausgeführt, wenn ich ein Backup machen wollen.

+0

+1. Einverstanden. git bundle ist schön um ein Backup zu verschieben (eine Datei). Aber mit einem Laufwerk können Sie überall anschließen, das bloße Repo ist auch in Ordnung. – VonC

+0

+1 awesme, ich werde das untersuchen. Danke auch für die Beispiele. –

+0

@Pat Notz, am Ende habe ich beschlossen, mit Ihrer Art und Weise zu tun, und ich legte eine Antwort hier unten (Score dauerhaft auf Null gehalten :) –

33

[Nur das hier für meine eigene Referenz zu verlassen.]

Mein Bündel Skript sieht genannt git-backup wie diese

#!/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 backup und manchmal benutze ich git backup different-name was mich am meisten von den Möglichkeiten gibt Ich brauche.

+2

+1 Da Sie die Option '--global' nicht verwendet haben, wird dieser Alias ​​nur in Ihrem Projekt angezeigt (er ist in Ihrer' .git/config' Datei definiert) - das ist wahrscheinlich das, was Sie wollen. Danke für die ausführlichere und schön formatierte Antwort. –

+0

Sicher Sache @Pat Notz danke für die Inspiration und Hilfe. –

+1

@yar: Weißt du, wie man diese Aufgaben ohne die Befehlszeile ausführt und stattdessen nur tortoisegit verwendet (suche nach Lösung für meine nicht-Kommandozeilen-windoze Benutzer)? – pastacool

2

Sie können den Git Repo mit git-copy sichern. git-copy gespeichert neues projekt als bare repo, bedeutet es minimale speicherkosten.

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

Dann können Sie Ihr Projekt mit git clone

git clone /backup/project.backup project 
6

Beiden Antworten richtig auf diese Fragen sind wieder herstellen, aber ich war immer noch eine vollständige, kurze Lösung zum Sichern einer Github-Repository in eine lokale Datei fehlt. Die gist ist hier verfügbar, fühlen Sie sich frei zu Gabel oder passen Sie sich Ihren Bedürfnissen an.

backup.sh:

#!/bin/bash 
# Backup the repositories indicated in the command line 
# Example: 
# bin/backup user1/repo1 user1/repo2 
set -e 
for i in [email protected]; 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 
+1

Interessant. Präziser als meine Antwort. +1 – VonC

+0

Danke, das ist nützlich für Github. Die angenommene Antwort ist auf die aktuelle Frage. –

0

kam über Google auf diese Frage.

Hier ist, was ich auf die einfachste Weise getan habe.

git checkout branch_to_clone 

erstellen Sie einen neuen git Zweig aus diesem Zweig

git checkout -b new_cloned_branch 
Switched to branch 'new_cloned_branch' 

kommen zurück zur ursprünglichen Zweig und weiter:

git checkout branch_to_clone 

Sie etwas von Backup-Zweig wiederherstellen müssen Unter der Annahme, zerknüllt und :

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

Der beste Teil, wenn etwas vermasselt ist, können Sie einfach den Quellzweig löschen und zurück zum Backup-Zweig bewegen !!

Verwandte Themen