Як створити резервну копію локального сховища Git?


155

Я використовую git для відносно невеликого проекту, і я вважаю, що перетягування вмісту каталогу .git може бути прекрасним способом резервного копіювання проекту. Але це щось дивно, бо, коли я відновлююсь, перше, що мені потрібно зробити git reset --hard.

Чи є якісь проблеми із створенням резервного копіювання git repo? Також, чи є кращий спосіб зробити це (наприклад, портативний формат git чи щось подібне?)?


Чому ніхто не дав очевидну відповідь щодо використання git bundle ???
gatopeich

@gatopeich вони зробили. Прокрути вниз.
Дан Розенстарк

Усі відповіді, що обнародують, містять текст тексту про власні сценарії, навіть той, який починається згадуватиgit bundle
gatopeich

Відповіді:


23

Я почав трохи хакнути сценарій Yar, і результат - на github, включаючи man сторінки та встановити скрипт:

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

Установка :

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

Вітаючи всі пропозиції та тягнучи запит на 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 Чудовий сценарій розслаблення, заснований на пакеті git, за який я виступав у своїй відповіді нижче. +1.
VonC

1
Я вже встановив ваш додаток у моєму локальному голосному сховищі .... як ви користуєтесь ним після його встановлення .... немає жодної інформації про це в документації, ви повинні включити розділ, що містить приклад того, як зробити резервну копію
JAF

Привіт, вибач, що ти не працює. Зазвичай ви запускаєте sudo install.sh, а потім налаштовуєте його (він використовує систему git config) для встановлення каталогу призначення (див. Файл readme на github). Далі запустіть git backupвсередині вашого сховища. Як сторонне, це був експеримент із git bundle та відповідь на це питання, але git bundle ніколи не робить абсолютно точну копію (наприклад, якщо я добре пам’ятаю, особливо що стосується віддалених програм git), тому особисто я фактично використовую tar для резервного копіювання. git каталоги.

144

Іншим офіційним способом було б використання git bundle

Це створить файл, який підтримує git fetchі git pullз метою оновлення другого репо.
Корисно для додаткового резервного копіювання та відновлення.

Але якщо вам потрібно створити резервну копію всього (оскільки у вас немає другого репо-файлу з уже встановленим старішим вмістом), резервне копіювання є дещо більш детальним, як це було сказано в моїй іншій відповіді, після коментаря Кента Фредріка :

$ 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

(Це атомарна операція , на відміну від створення архіву з .gitпапки, а коментував по fantabolous )


Попередження: Я б не рекомендував Pat Notz «s рішення , яке клонування репо.
Резервне копіювання багатьох файлів завжди складніше, ніж резервне копіювання чи оновлення ... лише один.

Якщо ви подивіться на історію правок в OP Яру відповідь , ви б побачили , що ЯР використовується спочатку clone --mirror, ... з редагуванням:

Використання цього з Dropbox - це загальний безлад .
У вас виникнуть помилки синхронізації, і ви НЕ МОЖЕТЕ ЗВ'ЯЗАТИ ДИРЕКТОРІЮ НАЗАД В ДРОПБОКСі.
Використовуйте, git bundleякщо ви хочете створити резервну копію до своєї скриньки.

Поточне рішення Yar використовує git bundle.

Я решту своєї справи.


Я просто перевірив це, і це насправді чудово. Мені доведеться спробувати деякі згрупування та роз'єднання та заголовки списків, щоб переконатися ... але мені це дуже подобається. Ще раз дякую, особливо за примітки на --all switch.
Дан Розенстарк

Дещо пов’язане, чи немає нічого поганого в тому, що я просто застебнув місцеве сховище? Мені потрібен один файл резервної копії, копіювання тисяч файлів на зовнішній диск неймовірно повільне. Мені просто цікаво, чи є щось більш ефективне, оскільки zip повинен архівувати стільки файлів у папці .git.

@faB: Єдина відмінність полягає в тому, що ви можете легко робити додаткові резервні копії git bundle. Це неможливо з глобальним поштовим індексом усіх місцевих репо.
VonC

2
Відповідаючи на старий коментар, але інша різниця між розшаруванням та застібненням dir в комплекті є атомною, тому вона не зіпсується, якщо комусь вдасться оновити ваше репо в середині операції.
фантастичний

1
@fantabolous хороший момент. Я включив його у відповідь для більшої наочності.
VonC

62

Я це роблю, щоб створити віддалений (голий) сховище (на окремому накопичувачі, USB-ключі, резервному сервері або навіть github), а потім використати push --mirrorдля того, щоб віддалене репо було схоже на моє місцеве (за винятком віддаленого - голе сховище).

Це підштовхне всі посилання (гілки та теги), включаючи оновлення, не швидкі вперед. Я використовую це для створення резервних копій мого локального сховища.

Сторінка man описує це так:

Замість того , щоб називати кожен реф штовхати, вказує , що все рефов під $GIT_DIR/refs/(яка включає в себе , але не обмежуючись ними refs/heads/, refs/remotes/і refs/tags/) бути дзеркальним в віддаленому сховище. Нещодавно створені локальні рефери будуть висунуті до віддаленого кінця, локально оновлені посилання будуть сильно оновлюватися на віддаленому кінці, а видалені рефлекси будуть видалені з віддаленого кінця. Це за замовчуванням, якщо встановлено параметр конфігурації remote.<remote>.mirror.

Я зробив псевдонім, щоб зробити натиск:

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

Тоді я просто запускаю, git bakколи хочу зробити резервну копію.


+1. Домовились. git bundle приємно переміщувати резервну копію (один файл). Але з приводом, який ви можете підключити де завгодно, голе репо теж чудово.
VonC

+1 awesme, я вивчу це. Дякуємо також за приклади.
Дан Розенстарк

@Pat Notz, врешті-решт я вирішив піти з вашим способом зробити це, і я поставив тут відповідь (оцінка постійно тримається на нулі :)
Dan Rosenstark

Зауважте, що --mirrorнасправді не виконується будь-яка перевірка об'єктів, які він отримує. Вам, мабуть, варто запуститись git fsckу якийсь момент, щоб запобігти корупції.
docwhat

34

[Просто залишаю це для моєї власної довідки.]

Мій скрипт, який називається, git-backupвиглядає приблизно так

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

Іноді я використовую, git backupа іноді використовую, git backup different-nameщо дає мені більшість потрібних мені можливостей.


2
+1 Оскільки ви не використовували --globalопцію, цей псевдонім буде бачити лише у вашому проекті (він визначений у вашому .git/configфайлі) - це, мабуть, те, що вам потрібно. Дякую за більш детальну та добре відформатовану відповідь.
Пат Нотц

1
@yar: чи знаєте ви, як виконати ці завдання без командного рядка, і замість цього використовуєте лише tortoisegit (шукаю рішення для моїх користувачів, які не командують команду-windoze)?
макаронні вироби

@pastacool, вибачте, я взагалі не знаю про git без командного рядка. Можливо, перевірте відповідний IDE на зразок RubyMine?
Дан Розенстарк

@intuited, ви можете відкатати ДИРЕКТОРІЇ за допомогою spideroak або просто файлів (що робить Dropbox, і вони дають вам 3 Гб місця)?
Дан Розенстарк

@Yar: не впевнений, що я розумію .. ти маєш на увазі, що якщо я видалю каталог, підтримуваний Dropbox, я втрачу всі попередні версії файлів, що містяться в ньому? Більше інформації про політику щодо версій spideroak можна знайти тут . TBH Я не дуже багато використовував SpiderOak, і не зовсім впевнений у його межах. Схоже, вони могли б вирішити такі проблеми, хоча вони роблять великий акцент на технічній компетентності. Також: чи все ще в Dropbox діє обмеження на 30 днів для зворотних зворотів для безкоштовних акаунтів?
інтуїтоване

9

Обидві відповіді на це питання правильні, але мені все одно не вистачало повного, короткого рішення щодо резервного копіювання сховища Github у локальний файл. Тут можна ознайомитись із суттю, сміливо роздвоювати або адаптуватися до ваших потреб.

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

Resto.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
Цікаво. Точніше, ніж моя відповідь. +1
VonC

Дякую, це корисно для Github. Прийнята відповідь - на поточне запитання.
Dan Rosenstark

5

Можна створити резервну копію git repo за допомогою git-copy . git-copy зберегла новий проект як голосне репо, це означає мінімальну вартість зберігання.

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

Тоді ви можете відновити свій проект за допомогою git clone

git clone /backup/project.backup project

Арг! ця відповідь змусила мене повірити, що "git copy" є офіційною командою git.
gatopeich

2

Знайшовши простий офіційний спосіб, пробравшись крізь стіни тексту вище, який би змусив вас думати, що його немає.

Створіть повний пакет із:

$ git bundle create <filename> --all

Відновіть його за допомогою:

$ git clone <filename> <folder>

Ця операція є атомною AFAIK. Перегляньте офіційні документи на предмет дрібних деталей.

Щодо "zip": пучки git стиснуті і на диво невеликі порівняно з розміром папки .git.


Це не відповідає на все запитання про zip, а також передбачає, що ми прочитали інші відповіді. Будь ласка, виправте це, щоб воно було атомним і вирішувало все запитання, і я радий прийняти відповідь (через 10 років). Спасибі
Ден Розенстарк

0

прийшов до цього питання через google.

Ось що я зробив найпростішим способом.

git checkout branch_to_clone

потім створіть нову гіт-гіт із цієї гілки

git checkout -b new_cloned_branch
Switched to branch 'new_cloned_branch'

поверніться до початкової філії та продовжуйте:

git checkout branch_to_clone

Якщо припустити, що ви перекрутили і вам потрібно відновити щось із гілки резервного копіювання:

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

Найкраще, якщо щось накручено, ви можете просто видалити вихідну гілку та повернутися до гілки резервного копіювання !!


1
Мені подобається такий підхід - але я не впевнений, чи це найкраща практика? Я роблю «резервні» гіти гіта досить часто, і врешті-решт у мене буде багато резервних гілок. Я не впевнений, це нормально чи ні (маючи ~ 20 резервних гілок з різних дат). Я думаю, я завжди міг би видалити старіші резервні копії з часом, але якщо я хочу зберегти їх усі - це добре? Поки вона грає добре - але було б добре знати, чи це хороша чи погана практика.
Кайл Васселла

це не те, що можна було б назвати найкращою практикою , я вважаю, що це більше пов'язане з тими чи іншими звичками робити речі. Я зазвичай кодую в одній гілці лише до тих пір, поки робота не буде виконана, а інша гілка зберігається для adhoc- запитів. В обох є резервне копіювання, після завершення видалення головної гілки! :)
NoobEditor
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.