Skip to content

Git 数据恢复

概述

在使用 Git 时,可能会误删文件、误删分支或提交错误。Git 提供了多种恢复数据的方法。本指南将介绍如何恢复误删文件、误删分支、错误提交,以及数据备份策略。

恢复误删文件

恢复未提交的文件

刚删除,未暂存

bash
# 查看状态
git status

# 恢复文件
git checkout -- file.txt

# 或使用 restore(Git 2.23+)
git restore file.txt

# 恢复所有删除的文件
git checkout -- .
git restore .

已暂存,未提交

bash
# 查看状态
git status

# 取消暂存
git reset HEAD file.txt

# 恢复文件
git checkout -- file.txt

# 或使用 restore
git restore --staged file.txt
git restore file.txt

恢复已提交的文件

从最近提交恢复

bash
# 查看提交历史
git log --oneline

# 恢复文件
git checkout HEAD -- file.txt

# 或使用 restore
git restore --source=HEAD file.txt

从特定提交恢复

bash
# 查找文件存在的提交
git log --all --full-history -- file.txt

# 从特定提交恢复
git checkout <commit-hash> -- file.txt

# 或使用 restore
git restore --source=<commit-hash> file.txt

# 查看文件历史版本
git show <commit-hash>:file.txt

从删除提交恢复

bash
# 查找删除文件的提交
git log --diff-filter=D --summary

# 找到删除文件的提交后
git checkout <commit-hash>^ -- file.txt

# 或使用 revert
git revert <commit-hash>

使用 Git Reflog 恢复

bash
# 查看 reflog
git reflog

# 找到删除文件前的提交
git checkout <commit-hash>

# 恢复文件
git checkout <commit-hash> -- file.txt

# 返回当前分支
git checkout main

恢复误删分支

使用 Reflog 恢复

bash
# 查看 reflog
git reflog

# 找到分支删除前的提交
# 例如: abc1234 HEAD@{5}: checkout: moving from feature to main

# 恢复分支
git checkout -b feature abc1234

# 或使用 branch 命令
git branch feature abc1234

使用 fsck 恢复

bash
# 查找悬挂提交
git fsck --lost-found

# 输出示例:
# dangling commit abc1234...

# 查看提交内容
git show abc1234

# 恢复分支
git checkout -b feature abc1234

从远程恢复

bash
# 如果分支存在于远程
git fetch origin

# 查看远程分支
git branch -r

# 恢复分支
git checkout -b feature origin/feature

恢复已删除的远程分支

bash
# 如果有人删除了远程分支,但本地还有

# 1. 查看本地分支
git branch -a

# 2. 重新推送到远程
git push origin feature

# 或从 reflog 恢复
git reflog
git checkout -b feature <commit-hash>
git push origin feature

恢复错误提交

撤销最近提交

保留变更

bash
# 撤销最近一次提交,保留变更
git reset --soft HEAD~1

# 撤销最近 N 次提交
git reset --soft HEAD~N

# 查看状态
git status

丢弃变更

bash
# 撤销提交并丢弃变更
git reset --hard HEAD~1

# 撤销最近 N 次提交
git reset --hard HEAD~N

# 警告: 这会永久丢失变更!

使用 Revert 撤销

bash
# 撤销特定提交(创建新提交)
git revert <commit-hash>

# 撤销多个提交
git revert <oldest-commit>..<newest-commit>

# 撤销合并提交
git revert -m 1 <merge-commit-hash>

# 不自动提交
git revert --no-commit <commit-hash>

修改最近提交

修改提交信息

bash
# 修改最近提交的信息
git commit --amend -m "新的提交信息"

# 修改提交信息(打开编辑器)
git commit --amend

添加遗漏文件

bash
# 添加遗漏的文件
git add forgotten-file.txt

# 修改提交
git commit --amend --no-edit

# 或同时修改信息
git commit --amend -m "新的提交信息"

修改作者信息

bash
# 修改作者
git commit --amend --author="Name <email@example.com>"

# 修改日期
git commit --amend --date="2024-01-01 12:00:00"

使用 Interactive Rebase

bash
# 交互式 rebase 最近 N 次提交
git rebase -i HEAD~N

# 在编辑器中:
# pick abc1234 第一个提交
# pick def5678 第二个提交
# pick ghi9012 第三个提交

# 修改为:
# pick abc1234 第一个提交
# squash def5678 第二个提交  # 合并到前一个提交
# drop ghi9012 第三个提交    # 删除这个提交
# edit def5678 第二个提交    # 编辑这个提交
# reword abc1234 第一个提交  # 修改提交信息

恢复已推送的错误提交

bash
# 如果已经推送到远程

# 方案1: 使用 revert(推荐)
git revert <commit-hash>
git push origin main

# 方案2: 使用 reset(需要强制推送)
git reset --hard HEAD~1
git push origin main --force

# 警告: 强制推送会影响其他开发者!

数据备份策略

本地备份

完整克隆

bash
# 克隆仓库作为备份
git clone --mirror https://github.com/user/repo.git backup-repo.git

# 更新备份
cd backup-repo.git
git fetch --all

打包备份

bash
# 创建备份包
git bundle create backup.bundle --all

# 从备份包恢复
git clone backup.bundle recovered-repo

归档备份

bash
# 创建归档文件
git archive --format=tar --output=backup.tar main

# 创建 zip 归档
git archive --format=zip --output=backup.zip main

# 包含子模块
git archive --format=tar --output=backup.tar --prefix=repo/ main
git submodule foreach 'git archive --format=tar --output=$sha1.tar HEAD'

远程备份

多远程仓库

bash
# 添加多个远程仓库
git remote add origin https://github.com/user/repo.git
git remote add backup https://gitlab.com/user/repo.git

# 推送到所有远程
git push origin main
git push backup main

# 同时推送到所有远程
git remote set-url --add --push origin https://github.com/user/repo.git
git remote set-url --add --push origin https://gitlab.com/user/repo.git
git push origin main

定期同步

bash
#!/bin/bash
# backup.sh

# 同步到备份仓库
git fetch origin
git push backup --all
git push backup --tags

echo "Backup completed at $(date)"

使用 cron 定时备份

bash
# 编辑 crontab
crontab -e

# 每天凌晨 2 点备份
0 2 * * * cd /path/to/repo && /path/to/backup.sh >> /var/log/git-backup.log 2>&1

云存储备份

使用 AWS S3

bash
# 安装 AWS CLI
pip install awscli

# 配置 AWS
aws configure

# 上传备份
aws s3 sync .git s3://bucket/git-backup/repo/

# 下载备份
aws s3 sync s3://bucket/git-backup/repo/ .git

使用 Google Cloud Storage

bash
# 安装 gsutil
# https://cloud.google.com/storage/docs/gsutil_install

# 上传备份
gsutil -m rsync -r .git gs://bucket/git-backup/repo/

# 下载备份
gsutil -m rsync -r gs://bucket/git-backup/repo/ .git

备份脚本

bash
#!/bin/bash
# full-backup.sh

REPO_DIR="/path/to/repo"
BACKUP_DIR="/backup/git"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="repo-backup-${DATE}"

# 创建备份目录
mkdir -p ${BACKUP_DIR}

# 进入仓库
cd ${REPO_DIR}

# 1. 完整克隆
git clone --mirror . ${BACKUP_DIR}/${BACKUP_NAME}.git

# 2. 创建 bundle
git bundle create ${BACKUP_DIR}/${BACKUP_NAME}.bundle --all

# 3. 创建归档
git archive --format=tar.gz --output=${BACKUP_DIR}/${BACKUP_NAME}.tar.gz main

# 4. 上传到云存储(可选)
# aws s3 cp ${BACKUP_DIR}/${BACKUP_NAME}.bundle s3://bucket/backups/

# 5. 清理旧备份(保留最近 30 天)
find ${BACKUP_DIR} -name "repo-backup-*.bundle" -mtime +30 -delete

echo "Backup completed: ${BACKUP_NAME}"

恢复脚本

完整恢复脚本

bash
#!/bin/bash
# restore.sh

BACKUP_FILE=$1
RESTORE_DIR=${2:-"restored-repo"}

if [ -z "$BACKUP_FILE" ]; then
  echo "用法: ./restore.sh <backup-file> [restore-dir]"
  exit 1
fi

# 检查备份文件
if [ ! -f "$BACKUP_FILE" ]; then
  echo "错误: 备份文件不存在: $BACKUP_FILE"
  exit 1
fi

# 从 bundle 恢复
if [[ $BACKUP_FILE == *.bundle ]]; then
  echo "从 bundle 恢复..."
  git clone $BACKUP_FILE $RESTORE_DIR
  
# 从镜像仓库恢复
elif [[ $BACKUP_FILE == *.git ]]; then
  echo "从镜像仓库恢复..."
  git clone $BACKUP_FILE $RESTORE_DIR
  
# 从归档恢复
elif [[ $BACKUP_FILE == *.tar.gz ]]; then
  echo "从归档恢复..."
  mkdir -p $RESTORE_DIR
  tar -xzf $BACKUP_FILE -C $RESTORE_DIR
  
else
  echo "错误: 不支持的备份格式"
  exit 1
fi

echo "恢复完成: $RESTORE_DIR"

选择性恢复脚本

bash
#!/bin/bash
# selective-restore.sh

BACKUP_REPO=$1
FILE_PATTERN=$2

if [ -z "$BACKUP_REPO" ] || [ -z "$FILE_PATTERN" ]; then
  echo "用法: ./selective-restore.sh <backup-repo> <file-pattern>"
  exit 1
fi

# 添加备份仓库为远程
git remote add backup $BACKUP_REPO

# 获取备份
git fetch backup

# 查找文件
echo "查找匹配的文件:"
git ls-tree -r backup/main --name-only | grep $FILE_PATTERN

# 恢复文件
read -p "恢复这些文件吗? (y/n) " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
  git checkout backup/main -- $(git ls-tree -r backup/main --name-only | grep $FILE_PATTERN)
  echo "文件已恢复"
fi

# 清理
git remote remove backup

恢复最佳实践

定期备份

markdown
## 备份频率建议

- **重要仓库**: 每天备份
- **活跃仓库**: 每周备份
- **归档仓库**: 每月备份

## 备份保留策略

- 每日备份: 保留 7 天
- 每周备份: 保留 4 周
- 每月备份: 保留 12 个月

验证备份

bash
#!/bin/bash
# verify-backup.sh

BACKUP_FILE=$1

# 验证 bundle
if [[ $BACKUP_FILE == *.bundle ]]; then
  echo "验证 bundle..."
  git bundle verify $BACKUP_FILE
  
# 验证仓库
elif [[ $BACKUP_FILE == *.git ]]; then
  echo "验证仓库..."
  cd $BACKUP_FILE
  git fsck --full
  
else
  echo "不支持的备份格式"
  exit 1
fi

echo "验证完成"

文档记录

markdown
## 恢复文档模板

### 备份信息
- 备份时间: YYYY-MM-DD HH:MM:SS
- 备份文件: backup-file-name
- 仓库大小: XX MB
- 提交数量: XXX

### 恢复步骤
1. 下载备份文件
2. 运行恢复脚本
3. 验证恢复结果

### 恢复验证
- [ ] 文件完整性
- [ ] 提交历史
- [ ] 分支信息
- [ ] 标签信息

总结

数据恢复的关键:

预防措施:

  • 定期备份
  • 验证备份
  • 多重备份

恢复方法:

  • Git 内置命令
  • Reflog 和 fsck
  • 备份恢复

最佳实践:

  • 及时响应
  • 记录过程
  • 验证结果

通过合理的备份策略和恢复方法,可以最大限度地减少数据丢失的风险。