Bagaimana cara membuat cadangan repositori Git lokal?

155

Saya menggunakan git pada proyek yang relatif kecil dan saya menemukan bahwa zipping konten direktori .git mungkin cara yang baik untuk membuat cadangan proyek. Tapi ini agak aneh karena, ketika saya mengembalikan, hal pertama yang perlu saya lakukan adalah git reset --hard.

Apakah ada masalah dengan mencadangkan git repo dengan cara ini? Juga, apakah ada cara yang lebih baik untuk melakukannya (misalnya, format git portabel atau yang serupa?)?

Dan Rosenstark
sumber
Mengapa tidak ada yang memberikan jawaban yang jelas menggunakan git bundle ???
gatopeich
@gatopeich mereka melakukannya. Gulir ke bawah.
Dan Rosenstark
Semua jawaban yang diperbarui mengandung dinding teks tentang skrip khusus, bahkan yang mulai menyebutkangit bundle
gatopeich

Jawaban:

23

Saya mulai sedikit meretas skrip Yar dan hasilnya ada di github, termasuk halaman manual dan instal skrip:

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

Instalasi :

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

Menyambut semua saran dan menarik permintaan di 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

sumber
1
@Yar Great bundle script, berdasarkan bundel git yang saya anjurkan dalam jawaban saya di bawah ini. +1.
VonC
1
Saya sudah menginstal aplikasi Anda di repositori telanjang lokal saya .... bagaimana Anda menggunakannya setelah diinstal .... tidak ada info mengenai hal itu pada dokumentasi, Anda harus menyertakan bagian withg contoh tentang cara membuat cadangan
JAF
Hai, maaf Anda tidak membuatnya bekerja. Biasanya Anda menjalankan sudo install.sh, kemudian mengkonfigurasinya (menggunakan sistem konfigurasi git) untuk mengatur direktori tujuan (lihat file readme pada github). Selanjutnya Anda jalankan git backupdi dalam repositori Anda. Sebagai sidenote, ini adalah percobaan dengan bundel git dan respons terhadap pertanyaan ini, tetapi bundel git tidak pernah membuat salinan persis absolut (mis. Jika saya ingat dengan baik, terutama mengenai remote git), maka secara pribadi saya benar-benar menggunakan tar untuk cadangan. direktori git.
144

Cara resmi lainnya akan menggunakan bundel git

Itu akan membuat file yang mendukung git fetchdan git pulluntuk memperbarui repo kedua Anda.
Berguna untuk pencadangan dan pemulihan inkremental.

Tetapi jika Anda perlu mencadangkan semuanya (karena Anda tidak memiliki repo kedua dengan beberapa konten yang lebih lama sudah ada), cadangannya sedikit lebih rumit untuk dilakukan, seperti yang disebutkan dalam jawaban saya yang lain, setelah komentar 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

(Ini adalah operasi atom , yang bertentangan dengan membuat arsip dari .gitfolder, seperti yang dikomentari oleh fantabolous )


Peringatan: Saya tidak akan merekomendasikan Pat Notz 's solusi , yang kloning repo.
Mencadangkan banyak file selalu lebih sulit daripada mencadangkan atau memperbarui ... hanya satu.

Jika Anda melihat sejarah suntingan dari OP Yar jawaban , Anda akan melihat bahwa Yar digunakan pada pertama clone --mirror, ... dengan edit:

Menggunakan ini dengan Dropbox adalah kekacauan total .
Anda akan memiliki kesalahan sinkronisasi, dan Anda TIDAK BISA GULUNG DIREKTORI KEMBALI DI DROPBOX.
Gunakan git bundlejika Anda ingin mencadangkan ke dropbox Anda.

Solusi Yar saat ini menggunakan git bundle.

Saya mengistirahatkan koper saya.

VONC
sumber
Saya baru saja memeriksa ini dan ini sebenarnya bagus. Saya harus mencoba beberapa bundling dan unbundling dan daftar-kepala untuk diyakinkan ... tapi saya sangat menyukainya. Terima kasih lagi, terutama untuk catatan di sakelar --all.
Dan Rosenstark
Agak terkait, apakah ada yang salah dengan hanya meng-zip repositori lokal saya? Saya memerlukan satu file cadangan, menyalin ribuan file pada drive eksternal sangat lambat. Saya hanya ingin tahu apakah ada sesuatu yang lebih efisien karena zip harus mengarsipkan begitu banyak file di folder .git.
@ FAB: satu-satunya perbedaan adalah Anda dapat dengan mudah melakukan pencadangan tambahangit bundle . Tidak mungkin dengan zip global dari semua repo lokal.
VonC
2
Membalas komentar lama, tetapi perbedaan lain antara bundel dan zipping dir adalah bundle adalah atomik, sehingga tidak akan kacau jika seseorang memperbarui repo Anda di tengah operasi.
fantabolous
1
@Fantabolous bagus. Saya telah memasukkannya dalam jawaban untuk lebih banyak visibilitas.
VonC
62

Cara saya melakukan ini adalah membuat repositori (telanjang) jarak jauh (pada drive terpisah, USB Key, server cadangan atau bahkan github) dan kemudian gunakan push --mirroruntuk membuat repo jarak jauh itu terlihat persis seperti repositori lokal saya (kecuali remote itu kosong gudang).

Ini akan mendorong semua referensi (cabang dan tag) termasuk pembaruan yang tidak maju cepat. Saya menggunakan ini untuk membuat cadangan dari repositori lokal saya.

The halaman manual menggambarkannya seperti ini:

Alih-alih penamaan masing-masing ref untuk push, menetapkan bahwa semua ref bawah $GIT_DIR/refs/(yang termasuk namun tidak terbatas pada refs/heads/, refs/remotes/, dan refs/tags/) dicerminkan ke repositori jauh. Referensi lokal yang baru dibuat akan didorong ke ujung jarak jauh, referensi yang diperbarui secara lokal akan diperbarui secara paksa di ujung jarak jauh, dan referensi yang dihapus akan dihapus dari ujung jarak jauh. Ini adalah default jika opsi konfigurasi remote.<remote>.mirrordiatur.

Saya membuat alias untuk melakukan push:

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

Lalu, saya hanya menjalankan git baksetiap kali saya ingin melakukan backup.

Pat Notz
sumber
+1. Sepakat. bundel git bagus untuk memindahkan cadangan (satu file). Tetapi dengan drive yang dapat Anda pasang di mana saja, repo telanjang juga baik-baik saja.
VonC
+1 awesme, saya akan melihat ini. Terima kasih atas contohnya juga.
Dan Rosenstark
@Pat Notz, pada akhirnya saya memutuskan untuk pergi dengan cara Anda melakukannya, dan saya memberikan jawaban di bawah sini (skor secara permanen diadakan di nol :)
Dan Rosenstark
Perhatikan bahwa --mirrorsebenarnya tidak menjalankan verifikasi apa pun pada objek yang didapatnya. Anda mungkin harus berlari git fsckdi beberapa titik untuk mencegah korupsi.
docwhat
34

[Hanya meninggalkan ini di sini untuk referensi saya sendiri.]

Skrip bundel saya disebut git-backupseperti ini

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

Terkadang saya menggunakan git backupdan terkadang saya menggunakan git backup different-nameyang memberi saya sebagian besar kemungkinan yang saya butuhkan.

Dan Rosenstark
sumber
2
+1 Karena Anda tidak menggunakan --globalopsi alias ini hanya akan terlihat di proyek Anda (ini ditolak di .git/configfile Anda ) - itu mungkin yang Anda inginkan. Terima kasih atas jawaban yang lebih terperinci dan diformat dengan baik.
Pat Notz
1
@yar: apakah Anda tahu bagaimana menyelesaikan tugas-tugas ini tanpa commandline dan sebagai gantinya hanya menggunakan tortoisegit (saya mencari solusi untuk pengguna windoze non-command-line-windoze saya)?
pastacool
@pacacool, maaf saya tidak tahu tentang git tanpa command-line sama sekali. Mungkin melihat IDE yang relevan seperti RubyMine?
Dan Rosenstark
@intuited, Anda dapat memutar kembali DIREKTUR dengan spideroak, atau hanya file (yang dilakukan Dropbox dan memberi Anda ruang 3GB)?
Dan Rosenstark
@ Yar: tidak yakin saya mengerti .. maksud Anda jika saya menghapus direktori yang didukung Dropbox, saya kehilangan semua revisi sebelumnya dari file yang terkandung di dalamnya? Info lebih lanjut tentang kebijakan versi spideroak ada di sini . TBH Saya belum terlalu sering menggunakan SpiderOak, dan saya tidak sepenuhnya yakin akan batasannya. Sepertinya mereka akan memberikan solusi untuk masalah seperti itu, mereka lebih menekankan pada kompetensi teknis. Juga: apakah Dropbox masih memiliki batasan 30 hari untuk pengembalian untuk akun gratis?
intuited
9

Kedua jawaban untuk pertanyaan ini benar, tetapi saya masih kehilangan solusi lengkap dan singkat untuk mencadangkan repositori Github ke file lokal. The Inti tersedia di sini, jangan ragu untuk garpu atau beradaptasi dengan kebutuhan Anda.

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
sumber
1
Menarik. Lebih tepat dari jawaban saya. +1
VonC
Terima kasih, ini berguna untuk Github. Jawaban yang diterima adalah untuk pertanyaan saat ini.
Dan Rosenstark
5

Anda dapat membuat cadangan git repo dengan git-copy . git-copy menyimpan proyek baru sebagai repo kosong, itu berarti biaya penyimpanan minimum.

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

Kemudian Anda dapat memulihkan proyek Anda dengan git clone

git clone /backup/project.backup project
Quanlong
sumber
Argh! jawaban ini membuat saya percaya "git copy" adalah perintah git resmi.
gatopeich
2

Menemukan cara resmi yang sederhana setelah mengarungi dinding teks di atas yang akan membuat Anda berpikir tidak ada.

Buat bundel lengkap dengan:

$ git bundle create <filename> --all

Kembalikan dengan:

$ git clone <filename> <folder>

Operasi ini adalah atom AFAIK. Periksa dokumen resmi untuk rincian berpasir.

Mengenai "zip": bundel git dikompresi dan sangat kecil dibandingkan dengan ukuran folder .git.

Gatopeich
sumber
Ini tidak menjawab seluruh pertanyaan tentang zip dan juga menganggap kami sudah membaca jawaban lainnya. Tolong perbaiki jadi itu atomik dan menangani seluruh pertanyaan dan saya senang membuatnya diterima jawaban (10 tahun kemudian). Terima kasih
Dan Rosenstark
0

sampai pada pertanyaan ini melalui google.

Inilah yang saya lakukan dengan cara paling sederhana.

git checkout branch_to_clone

kemudian buat cabang git baru dari cabang ini

git checkout -b new_cloned_branch
Switched to branch 'new_cloned_branch'

kembali ke cabang asli dan lanjutkan:

git checkout branch_to_clone

Dengan asumsi Anda mengacaukan dan perlu memulihkan sesuatu dari cabang cadangan:

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

Bagian terbaik jika ada yang kacau, Anda bisa menghapus cabang sumber dan kembali ke cabang cadangan !!

NoobEditor
sumber
1
Saya suka pendekatan ini - tetapi saya tidak yakin apakah ini praktik terbaik? Saya sering membuat 'cadangan' cabang git, dan akhirnya saya akan memiliki banyak cabang cadangan. Saya tidak yakin apakah ini baik atau tidak (memiliki ~ 20 cabang cadangan dari tanggal yang berbeda). Saya kira saya selalu bisa menghapus backup yang lebih lama pada akhirnya - tetapi jika saya ingin menyimpan semuanya - apakah itu oke? Sejauh ini bermain dengan baik - tetapi akan menyenangkan untuk mengetahui apakah itu latihan yang baik atau buruk.
Kyle Vassella
itu bukan sesuatu yang akan disebut sebagai praktik terbaik , saya menganggap itu lebih terkait dengan kebiasaan individu melakukan hal-hal. Saya biasanya kode dalam satu cabang hanya sampai pekerjaan selesai dan menyimpan cabang lain untuk permintaan adhoc . Keduanya memiliki cadangan, setelah selesai, hapus cabang utama! :)
NoobEditor