GitLab 服务器宕机时的项目代码恢复方法

打印 上一主题 下一主题

主题 1839|帖子 1839|积分 5517

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
重要前提:GitLab 数据挂载盘必须能够正常读取,且 /var/opt/gitlab/git-data/repositories 目录下的数据可以完整拷贝。
当 GitLab 服务器不测宕机且没有备份时,项目代码的恢复变得尤为关键。以下是经过优化的恢复流程,相比传统方法更为简洁高效。
一、数据拷贝与准备


  • 挂载数据盘
    将宕机服务器的数据盘挂载到其他正常运行的主机或服务器上。确保 /var/opt/gitlab/git-data 目录下的所有内容能够完整拷贝到新的主机或服务器中。
    1. sudo mount /dev/sdX /mnt/data  # 示例挂载命令,需根据实际情况调整
    复制代码
  • 拷贝数据
    将 /var/opt/gitlab/git-data 目录下的所有内容完整拷贝到新主机的指定目录,例如 /mnt/recovery。
    1. sudo cp -r /mnt/data/var/opt/gitlab/git-data /mnt/recovery/
    复制代码
二、识别项目数据

GitLab 的项目数据存储在 /var/opt/gitlab/git-data/repositories/@hashed 目录下,文件夹名称经过哈希处置惩罚,无法直接识别项目信息。但每个项目文件夹(如 xxxxx.git)下的 config 文件中存储了项目相关的部分信息,可以提取堆栈所有者及堆栈名称。
留意:xxx.wiki.git 和 xxx.design.git 文件夹通常可以忽略,由于它们不包含重要代码数据,且其 config 文件中也不包含堆栈所有者及堆栈名称。
三、简化恢复方法

传统的恢复方法通常必要搭建新的 GitLab 服务器并进行数据镜像,但这种方法存在以下问题:

  • 必要确保新旧服务器的 GitLab 版本完全一致,否则大概导致数据无法正确镜像。
  • 操作步骤繁琐,耗时且轻易出错。
究竟上,我们可以接纳更简单的方法直接恢复代码,无需搭建新服务器。
以项目文件夹 73/47/73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049.git 为例,以下是具体步骤:

  • 设置安全目录
    由于 GitLab 的项目目录大概被识别为不安全目录,必要通过以下下令将其标记为安全目录:
    1. git config --global --add safe.directory /mnt/recovery/repositories/@hashed/73/47/73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049.git
    复制代码
  • 克隆项目
    在上文中提到,config 文件中存储了完整的堆栈所有者和堆栈名称(例如 author/project_name)。我们可以通过克隆操作将项目恢复到当地目录。假设目标项目路径是 your_clone_dir/author/project_name,那么可以实行以下下令来完成克隆:
    1. git clone /mnt/recovery/repositories/@hashed/73/47/73475cb40a568e8da8a045ced110137e159f890ac4da883b6b17dc651b3a8049.git your_clone_dir/author/project_name
    复制代码
四、自动化恢复脚本

为了进一步简化操作,以下是一个 Python 脚本,可以快速实行上述操作,只需提供哈希化堆栈的源目录和克隆堆栈的目标目录。
  1. #!/usr/bin/env python
  2. # -*-coding:utf-8 -*-
  3. # ==============================================================================
  4. # Copyright (c) 2025 laugh12321 Authors. All Rights Reserved.
  5. #
  6. # Licensed under the GNU General Public License v3.0 (the "License");
  7. # you may not use this file except in compliance with the License.
  8. # You may obtain a copy of the License at
  9. #
  10. #     https://www.gnu.org/licenses/gpl-3.0.html  
  11. #
  12. # Unless required by applicable law or agreed to in writing, software
  13. # distributed under the License is distributed on an "AS IS" BASIS,
  14. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. # See the License for the specific language governing permissions and
  16. # limitations under the License.
  17. # ==============================================================================
  18. # File    :   hashed_repo_cloner.py
  19. # Version :   1.0
  20. # Author  :   laugh12321
  21. # Contact :   laugh12321@vip.qq.com
  22. # Date    :   2025/03/31 14:51:38
  23. # Desc    :   None
  24. # ==============================================================================
  25. from pathlib import Path
  26. import configparser
  27. import subprocess
  28. import argparse
  29. from typing import Optional
  30. from rich.progress import track
  31. import sys
  32. def extract_repo_name_from_config(config_path: Path) -> str:
  33.     """
  34.     从Git配置文件中提取仓库完整路径
  35.     :param config_path: Git配置文件路径
  36.     :return: 仓库完整路径
  37.     :raises ValueError: 如果配置缺少gitlab段或fullpath键
  38.     :raises FileNotFoundError: 如果配置文件不存在
  39.     """
  40.     if not config_path.is_file():
  41.         raise FileNotFoundError(f"Git config file not found: {config_path}")
  42.     config = configparser.ConfigParser()
  43.     config.read(config_path)
  44.     if 'gitlab' not in config or 'fullpath' not in config['gitlab']:
  45.         raise ValueError(f"Config file missing required gitlab section or fullpath key: {config_path}")
  46.     return config.get('gitlab', 'fullpath')
  47. def add_safe_directory(git_dir: Path) -> None:
  48.     """
  49.     将Git目录添加到安全目录列表
  50.     :param git_dir: Git仓库路径
  51.     """
  52.     subprocess.run(
  53.         ["git", "config", "--global", "--add", "safe.directory", str(git_dir)],
  54.         check=True,
  55.         stdout=subprocess.DEVNULL,  # 将标准输出重定向到 /dev/null
  56.         stderr=subprocess.DEVNULL   # 将标准错误重定向到 /dev/null
  57.     )
  58. def clone_repository(source_dir: Path, target_dir: Path, repo_name: str) -> None:
  59.     """
  60.     克隆仓库到目标目录
  61.     :param source_dir: 源Git仓库路径
  62.     :param target_dir: 目标目录路径
  63.     :param repo_name: 仓库名称
  64.     """
  65.     target_path = target_dir / repo_name
  66.     subprocess.run(
  67.         ["git", "clone", str(source_dir), str(target_path)],
  68.         check=True,
  69.         stdout=subprocess.DEVNULL,  # 将标准输出重定向到 /dev/null
  70.         stderr=subprocess.DEVNULL   # 将标准错误重定向到 /dev/null
  71.     )
  72. def process_git_repositories(hashed_repos_dir: Path, output_dir: Path) -> None:
  73.     """
  74.     处理所有哈希化的Git仓库并将其克隆到输出目录
  75.     :param hashed_repos_dir: 包含哈希化仓库的目录
  76.     :param output_dir: 输出目录
  77.     """
  78.     # 预过滤.git目录,排除wiki和design仓库
  79.     git_folders = [
  80.         folder for folder in hashed_repos_dir.rglob("*.git")
  81.         if not folder.name.endswith((".wiki.git", ".design.git"))
  82.     ]
  83.     if not git_folders:
  84.         print("No valid Git repositories found to process.")
  85.         return
  86.     for git_folder in track(git_folders, description="Processing repositories"):
  87.         config_path = git_folder / "config"
  88.         try:
  89.             repo_name = extract_repo_name_from_config(config_path)
  90.             add_safe_directory(git_folder)
  91.             clone_repository(git_folder, output_dir, repo_name)
  92.         except Exception as e:
  93.             print(f"Error processing {git_folder.name}: {e}")
  94.             sys.exit()  # 终止程序
  95. def validate_directory(path: Optional[str]) -> Path:
  96.     """
  97.     验证并将路径字符串转换为Path对象
  98.     :param path: 路径字符串
  99.     :return: Path对象
  100.     :raises ValueError: 如果路径不存在或不是目录
  101.     """
  102.     if path is None:
  103.         raise ValueError("Path cannot be None")
  104.     path_obj = Path(path)
  105.     if not path_obj.exists():
  106.         raise ValueError(f"Path does not exist: {path}")
  107.     if not path_obj.is_dir():
  108.         raise ValueError(f"Path is not a directory: {path}")
  109.     return path_obj
  110. def parse_arguments():
  111.     """
  112.     解析命令行参数
  113.     :return: 包含参数的命名空间
  114.     """
  115.     parser = argparse.ArgumentParser(
  116.         description="将GitLab哈希化仓库克隆到目标目录",
  117.         formatter_class=argparse.ArgumentDefaultsHelpFormatter
  118.     )
  119.     parser.add_argument(
  120.         "--source",
  121.         type=str,
  122.         required=True,
  123.         help="包含哈希化仓库的源目录(必须)"
  124.     )
  125.     parser.add_argument(
  126.         "--output",
  127.         type=str,
  128.         required=True,
  129.         help="克隆仓库的目标目录(必须)"
  130.     )
  131.     return parser.parse_args()
  132. def main():
  133.     args = parse_arguments()
  134.     try:
  135.         source_dir = validate_directory(args.source)
  136.         output_dir = Path(args.output)
  137.         process_git_repositories(source_dir, output_dir)
  138.     except ValueError as e:
  139.         print(f"Argument error: {e}")
  140.         return 1
  141.     return 0
  142. if __name__ == "__main__":
  143.     exit(main())
复制代码
利用方法

运行以下下令即可启动脚本:
  1. python hashed_repo_cloner.py --source gitlab_hashed_dir --output project_out_dir
复制代码
五、后续操作


  • 验证恢复效果
    进入克隆后的项目目录,检查代码完整性,确保所有分支和提交记录都已正确恢复。
    1. cd project_out_dir/author/project_name
    2. git log  # 查看提交记录
    3. git branch -a  # 查看所有分支
    复制代码
  • 重新托管到 GitLab 或其他平台
    假如必要将恢复的代码重新托管到 GitLab 或其他代码托管平台,可以按照以下步骤操作:

    • 在目标平台创建新的堆栈。
    • 将当地克隆的项目推送到新堆栈:
      1. git remote add origin <新仓库的URL>
      2. git push -u origin --all
      3. git push -u origin --tags
      复制代码

通过上述方法,我们无需搭建新服务器,也无需担心版本兼容问题,能够快速高效地恢复 GitLab 项目代码。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王海鱼

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表