วิธีการสำรองข้อมูลที่เก็บ Git ท้องถิ่น?


155

ฉันใช้คอมไพล์ในโครงการขนาดเล็กและฉันพบว่าการบีบอัดเนื้อหาของไดเรกทอรี. git อาจเป็นวิธีที่ดีในการสำรองข้อมูลโครงการ git reset --hardแต่นี้เป็นชนิดของแปลกเพราะเมื่อฉันเรียกคืนสิ่งแรกที่ผมต้องทำคือ

มีปัญหาในการสำรอง repo คอมไพล์ด้วยวิธีนี้หรือไม่? นอกจากนี้มีวิธีที่ดีกว่าที่จะทำ (เช่นรูปแบบ git พกพาหรือสิ่งที่คล้ายกัน?)


ทำไมไม่มีใครให้คำตอบที่ชัดเจนของการใช้มัด git ???
gatopeich

@gatopeich พวกเขาทำ เลื่อนลง.
Dan Rosenstark

คำตอบที่ upvoted ทั้งหมดมีเนื้อหาของข้อความเกี่ยวกับสคริปต์ที่กำหนดเองแม้แต่คำที่เริ่มพูดถึงgit bundle
gatopeich

คำตอบ:


23

ฉันเริ่มแฮ็คสคริปต์ของ Yar ไปเล็กน้อยและผลลัพธ์ก็คือ github รวมถึง man pages และ script ที่ติดตั้ง:

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ภายในที่เก็บของคุณ ในฐานะ sidenote นี่เป็นการทดลองกับกลุ่ม git และการตอบสนองต่อคำถามนี้ แต่กลุ่ม git ไม่เคยทำสำเนาที่แน่นอน (เช่นถ้าฉันจำได้ดีโดยเฉพาะเกี่ยวกับรีโมต git) ดังนั้นโดยส่วนตัวแล้วฉันใช้ tar เพื่อสำรองข้อมูล ไดเรกทอรีคอมไพล์

144

วิธีทางการอื่น ๆ จะใช้มัด git

ที่จะสร้างไฟล์ที่รองรับgit fetchและgit pullเพื่อปรับปรุง repo ที่สองของคุณ
มีประโยชน์สำหรับการสำรองข้อมูลที่เพิ่มขึ้นและการคืนค่า

แต่ถ้าคุณต้องการสำรองข้อมูลทุกอย่าง (เพราะคุณไม่มี repo ครั้งที่สองที่มีเนื้อหาเก่าอยู่แล้ว) การสำรองข้อมูลนั้นค่อนข้างซับซ้อนกว่าที่ควรทำดังที่ได้กล่าวไว้ในคำตอบอื่นของฉันหลังจากความเห็นของ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

(เป็นการดำเนินการแบบปรมาณูซึ่งตรงข้ามกับการสร้างที่เก็บถาวรจาก.gitโฟลเดอร์ตามที่แสดงความคิดเห็นโดยจินตนาการ )


คำเตือน: ฉันจะไม่แนะนำวิธีแก้ปัญหาของPat Notzซึ่งโคลน repo การสำรองไฟล์หลายไฟล์นั้นยุ่งยากกว่าการสำรองหรืออัปเดต ... เพียงไฟล์เดียว

ถ้าคุณดูที่ประวัติการแก้ไขของOP Yar คำตอบที่คุณจะเห็นว่าหญ้าที่ใช้ในครั้งแรกclone --mirror... กับการแก้ไข:

ใช้นี้กับ Dropbox เป็นระเบียบทั้งหมด
คุณจะมีข้อผิดพลาดในการซิงค์และคุณไม่สามารถย้อนกลับไปยังไดเรกทอรีย่อยใน DROPBOX
ใช้git bundleหากคุณต้องการสำรองข้อมูลไปยังดรอปบ็อกซ์ของคุณ

Yar ของการแก้ปัญหาในปัจจุบันgit bundleการใช้งาน

ฉันพักกรณีของฉัน


ฉันเพิ่งตรวจสอบสิ่งนี้และมันยอดเยี่ยมจริง ๆ ฉันจะต้องลองมัดและ unbundling และ list-heads เพื่อให้มั่นใจ ... แต่ฉันชอบมันค่อนข้างน้อย ขอขอบคุณอีกครั้งโดยเฉพาะอย่างยิ่งสำหรับโน้ตบน - ทั้งหมดสลับ
Dan Rosenstark

มีความเกี่ยวข้องอะไรมีอะไรผิดปกติเพียงแค่ซิปที่เก็บในเครื่องของฉัน ฉันต้องการไฟล์สำรองไฟล์เดียวการคัดลอกไฟล์หลายพันไฟล์บนไดรฟ์ภายนอกนั้นช้ามากอย่างไม่น่าเชื่อ ฉันแค่สงสัยว่ามีบางอย่างที่มีประสิทธิภาพมากกว่านี้หรือไม่เพราะ zip ต้องจัดเก็บไฟล์จำนวนมากในโฟลเดอร์. git

@faB: ความแตกต่างเพียงอย่างเดียวคือที่คุณสามารถทำที่เพิ่มขึ้นgit bundleการสำรองข้อมูลด้วย มันเป็นไปไม่ได้ที่จะมีรหัสไปรษณีย์ทั่วโลกของ repo ท้องถิ่นทั้งหมด
VonC

2
การตอบกลับความคิดเห็นเก่า แต่ความแตกต่างอีกอย่างระหว่างบันเดิลและซิปที่ dir คือบันเดิลก็คืออะตอมดังนั้นมันจะไม่สับสนถ้ามีคนอัปเดต repo ของคุณในระหว่างการดำเนินการ
fantabolous

1
@fantabolous จุดที่ดี ฉันได้รวมไว้ในคำตอบเพื่อให้มองเห็นได้ชัดเจนขึ้น
VonC

62

วิธีที่ผมทำเช่นนี้คือการสร้างระยะไกล (เปลือย) พื้นที่เก็บข้อมูล (บนไดรฟ์แยกต่างหาก, USB คีย์เซิร์ฟเวอร์สำรองหรือแม้กระทั่ง GitHub) แล้วใช้push --mirrorที่จะทำให้มันดู repo ระยะไกลเหมือนหนึ่งในท้องถิ่นของฉัน (ยกเว้นระยะไกลเป็นเปลือยพื้นที่เก็บข้อมูล)

การดำเนินการนี้จะผลักดันการอ้างอิงทั้งหมด (สาขาและแท็ก) รวมถึงการอัปเดตที่ไม่ใช่การส่งต่ออย่างรวดเร็ว ฉันใช้สิ่งนี้เพื่อสร้างการสำรองข้อมูลของที่เก็บในเครื่องของฉัน

หน้าคนอธิบายเช่นนี้

แทนการตั้งชื่อแต่ละเตะที่จะผลักดันระบุว่า refs ทั้งหมดภายใต้$GIT_DIR/refs/(ซึ่งรวมถึง แต่ไม่ จำกัดrefs/heads/, refs/remotes/และrefs/tags/) จะสะท้อนไปยังพื้นที่เก็บข้อมูลระยะไกล การอ้างอิงแบบโลคัลที่สร้างขึ้นใหม่จะถูกผลักไปที่ปลายรีโมตการอ้างอิงแบบโลคัลที่อัปเดตในท้องถิ่นจะถูกบังคับให้อัปเดตบนรีโมตปลายทางและการอ้างอิงที่ถูกลบจะถูกลบออกจากรีโมตปลายทาง นี่เป็นค่าเริ่มต้นหากตั้งค่าตัวเลือกการremote.<remote>.mirrorกำหนดค่า

ฉันสร้างนามแฝงเพื่อทำการผลักดัน:

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

จากนั้นฉันจะเรียกใช้git bakเมื่อใดก็ตามที่ฉันต้องการสำรองข้อมูล


+1 ตกลง มัด git ดีที่จะย้ายการสำรองข้อมูล (หนึ่งไฟล์) แต่ด้วยไดรฟ์คุณสามารถเสียบที่ใดก็ได้ repo เปล่าก็ใช้ได้เช่นกัน
VonC

+1 น่ากลัวฉันจะตรวจสอบเรื่องนี้ ขอบคุณสำหรับตัวอย่างด้วย
Dan Rosenstark

@Pat Notz ในที่สุดผมก็ตัดสินใจที่จะไปกับวิธีที่คุณทำมันและฉันใส่คำตอบด้านล่างนี่ (คะแนนที่จัดขึ้นอย่างถาวรที่ศูนย์ :)
แดน 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ไฟล์ของคุณ) - นั่นอาจเป็นสิ่งที่คุณต้องการ ขอบคุณสำหรับคำตอบที่มีรายละเอียดมากและจัดรูปแบบได้ดี
Pat Notz

1
@yar: คุณรู้วิธีการทำงานเหล่านี้โดยไม่ commandline และเพียงแค่ใช้ tortoisegit (กำลังค้นหาวิธีแก้ปัญหาสำหรับผู้ใช้ที่ไม่ใช่ command-line-windoze)?
pastacool

@pastacool ขอโทษฉันไม่รู้เกี่ยวกับคอมไพล์โดยไม่มีบรรทัดคำสั่งเลย บางทีลองดู IDE ที่เกี่ยวข้องเช่น RubyMine?
Dan Rosenstark

@ โดยเริ่มต้นคุณสามารถย้อนกลับ DIRECTORIES ด้วย spideroak หรือเพียงแค่ไฟล์ (ซึ่ง Dropbox ทำอะไรและพวกเขาให้พื้นที่ 3GB แก่คุณ)
Dan Rosenstark

@ ใช่: ไม่แน่ใจว่าฉันเข้าใจ .. คุณหมายถึงว่าถ้าฉันลบไดเรกทอรีที่สำรองข้อมูล Dropbox ฉันจะสูญเสียการแก้ไขก่อนหน้าทั้งหมดของไฟล์ที่อยู่ในนั้นหรือไม่ ข้อมูลเพิ่มเติมเกี่ยวกับนโยบายเวอร์ชัน SpiderOak เป็นที่นี่ TBH ฉันไม่ได้ใช้ SpiderOak มากนักและไม่แน่ใจในข้อ จำกัด ทั้งหมด ดูเหมือนว่าพวกเขาจะให้แนวทางแก้ไขปัญหาดังกล่าว แต่พวกเขาให้ความสำคัญกับความสามารถทางเทคนิคมาก นอกจากนี้: Dropbox ยัง จำกัด วงเงินย้อนกลับ 30 วันสำหรับบัญชีฟรีหรือไม่
intuited

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

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
น่าสนใจ แม่นยำกว่าคำตอบของฉัน +1
VonC

ขอบคุณสิ่งนี้มีประโยชน์สำหรับ Github คำตอบที่ยอมรับคือคำถามปัจจุบัน
Dan Rosenstark

5

คุณสามารถสำรองข้อมูล repo คอมไพล์กับคอมไพล์สำเนา git-copyบันทึกโครงการใหม่เป็น repo เปล่ามันหมายถึงต้นทุนการจัดเก็บขั้นต่ำ

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 ปีต่อมา) ขอบคุณ
Dan Rosenstark

0

มาถึงคำถามนี้ผ่านทาง google

นี่คือสิ่งที่ฉันทำในวิธีที่ง่ายที่สุด

git checkout branch_to_clone

จากนั้นสร้างสาขา git ใหม่จากสาขานี้

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
ฉันชอบวิธีการนี้ - แต่ฉันไม่แน่ใจว่าเป็นวิธีปฏิบัติที่ดีที่สุด ฉันทำสาขา git 'backup' บ่อยครั้งและในที่สุดฉันก็จะมี branch สำรองมากมาย ฉันไม่แน่ใจว่าจะเป็นเช่นนั้นหรือไม่ (มีสาขาสำรองมากกว่า 20 สาขาจากวันที่แตกต่างกัน) ฉันเดาว่าฉันสามารถลบข้อมูลสำรองที่เก่ากว่าได้ในที่สุด - แต่ถ้าฉันต้องการเก็บข้อมูลทั้งหมด - ไม่เป็นไรใช่ไหม จนถึงตอนนี้มันเล่นได้ดี - แต่ก็ดีที่จะรู้ว่ามันเป็นการปฏิบัติที่ดีหรือไม่ดี
Kyle Vassella

ไม่ใช่สิ่งที่จะถูกเรียกว่าเป็นแนวปฏิบัติที่ดีที่สุดฉันถือว่ามันเกี่ยวข้องกับบุคคลที่มีนิสัยชอบทำสิ่งของ ฉันมักจะรหัสในสาขาหนึ่งเท่านั้นจนกว่างานจะทำและเก็บสาขาอื่นสำหรับการร้องขอadhoc ทั้งคู่มีการสำรองข้อมูลเสร็จแล้วลบสาขาหลัก! :)
NoobEditor
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.