How to backup a local Git repository?

前端 未结 8 1063
伪装坚强ぢ
伪装坚强ぢ 2020-11-22 08:08

I am using git on a relatively small project and I find that zipping the .git directory\'s contents might be a fine way to back up the project. But this is kind of weird bec

相关标签:
8条回答
  • 2020-11-22 08:21

    The way I do this is to create a remote (bare) repository (on a separate drive, USB Key, backup server or even github) and then use push --mirror to make that remote repo look exactly like my local one (except the remote is a bare repository).

    This will push all refs (branches and tags) including non-fast-forward updates. I use this for creating backups of my local repository.

    The man page describes it like this:

    Instead of naming each ref to push, specifies that all refs under $GIT_DIR/refs/ (which includes but is not limited to refs/heads/, refs/remotes/, and refs/tags/) be mirrored to the remote repository. Newly created local refs will be pushed to the remote end, locally updated refs will be force updated on the remote end, and deleted refs will be removed from the remote end. This is the default if the configuration option remote.<remote>.mirror is set.

    I made an alias to do the push:

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

    Then, I just run git bak whenever I want to do a backup.

    0 讨论(0)
  • 2020-11-22 08:24

    [Just leaving this here for my own reference.]

    My bundle script called git-backup looks like this

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

    Sometimes I use git backup and sometimes I use git backup different-name which gives me most of the possibilities I need.

    0 讨论(0)
  • 2020-11-22 08:25

    You can backup the git repo with git-copy . git-copy saved new project as a bare repo, it means minimum storage cost.

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

    Then you can restore your project with git clone

    git clone /backup/project.backup project
    
    0 讨论(0)
  • 2020-11-22 08:32

    I started hacking away a bit on Yar's script and the result is on github, including man pages and install script:

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

    Installation:

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

    Welcoming all suggestions and pull request on 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 讨论(0)
  • 2020-11-22 08:36

    The other offical way would be using git bundle

    That will create a file that support git fetch and git pull in order to update your second repo.
    Useful for incremental backup and restore.

    But if you need to backup everything (because you do not have a second repo with some older content already in place), the backup is a bit more elaborate to do, as mentioned in my other answer, after Kent Fredric's comment:

    $ 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
    

    (It is an atomic operation, as opposed to making an archive from the .git folder, as commented by fantabolous)


    Warning: I wouldn't recommend Pat Notz's solution, which is cloning the repo.
    Backup many files is always more tricky than backing up or updating... just one.

    If you look at the history of edits of the OP Yar answer, you would see that Yar used at first a clone --mirror, ... with the edit:

    Using this with Dropbox is a total mess.
    You will have sync errors, and you CANNOT ROLL A DIRECTORY BACK IN DROPBOX.
    Use git bundle if you want to back up to your dropbox.

    Yar's current solution uses git bundle.

    I rest my case.

    0 讨论(0)
  • 2020-11-22 08:36

    Both answers to this questions are correct, but I was still missing a complete, short solution to backup a Github repository into a local file. The gist is available here, feel free to fork or adapt to your needs.

    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
    
    0 讨论(0)
提交回复
热议问题