我在一个相对较小的项目中使用git,并发现将.git目录的内容压缩成zip文件是一种不错的备份方式。但这有点奇怪,因为在还原时,我需要做的第一件事情是git reset --hard
。
这种备份git仓库的方式是否存在问题?此外,是否有更好的方法来进行备份(例如便携式git格式或类似的东西)?
我在一个相对较小的项目中使用git,并发现将.git目录的内容压缩成zip文件是一种不错的备份方式。但这有点奇怪,因为在还原时,我需要做的第一件事情是git reset --hard
。
这种备份git仓库的方式是否存在问题?此外,是否有更好的方法来进行备份(例如便携式git格式或类似的东西)?
另一种官方的方法是使用git bundle
这将创建一个支持git fetch
和git pull
更新您的第二个仓库的文件。
对于增量备份和恢复非常有用。
但如果您需要备份所有内容(因为您没有已经存在某些旧内容的第二个仓库),备份就需要更加复杂,如我在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
文件夹创建归档不同,如评论所述by fantabolous)
警告:我不建议使用Pat Notz的solution,它是克隆存储库。
备份许多文件总是比备份或更新...只有一个更棘手。
如果您查看OP Yar answer的history of edits,您会发现Yar首先使用了clone --mirror
,...与编辑:
在Dropbox中使用这个会一团糟。
您将遇到同步错误,并且您无法在Dropbox中回滚目录。
如果要备份到Dropbox,请使用git bundle
。
Yar的current solution使用了git bundle
。
我休息我的案子。
push --mirror
将该远程仓库与我的本地仓库完全对应(除了远程仓库是裸仓库)。$GIT_DIR/refs/
下的所有引用(其中包括但不限于refs/heads/
,refs/remotes/
和refs/tags/
)映射到远程仓库。新创建的本地引用将被推送到远程端,本地更新的引用将被强制更新到远程端,并删除的引用将从远程端移除。如果配置选项remote.<remote>.mirror
设置了,则这是默认设置。”git config --add alias.bak "push --mirror github"
那么,每当我想要备份时,我只需运行git bak
。
--mirror
实际上不会对其获取的对象进行任何验证。您应该在某个时候运行 git fsck
以防止损坏。 - docwhat[只是为了自己参考,把这里留下了。]
我的捆绑脚本叫做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
,这样我就可以获得我所需的大部分可能性。--global
选项,所以此别名仅在您的项目中可见(它在您的 .git/config
文件中定义)-- 这可能是您想要的。感谢您提供更详细和格式良好的答案。 - Pat Notz我开始在Yar的脚本上进行一些修改,结果已经上传到Github上,包括手册和安装脚本:
https://github.com/najamelan/git-backup
安装:
git clone "https://github.com/najamelan/git-backup.git"
cd git-backup
sudo ./install.sh
#!/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
这个问题的两个答案都是正确的,但我仍然缺少一个完整而简短的解决方案,将Github存储库备份到本地文件中。 此处提供了gist,随意fork或根据您的需要进行修改。
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
在浏览了上面的大段文字之后,我发现了一个简单的官方方法,让你觉得好像没有这样的方法一样。
创建一个完整的捆绑包,具体步骤如下:
$ git bundle create <filename> --all
使用以下命令进行还原:
$ git clone <filename> <folder>
据我所知,此操作是原子操作。有关详细信息,请查看官方文档。
关于“zip”:Git Bundles 压缩后的大小与 .git 文件夹相比惊人地小。
git copy /path/to/project /backup/project.backup
然后你可以使用git clone
恢复你的项目
git clone /backup/project.backup project
通过谷歌搜索来到这个问题。
以下是我以最简单的方式所做的。
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 "--"
最好的部分是,如果出了什么问题,你可以删除源分支并返回备份分支!