# repo_script **Repository Path**: GeekKernel/repo_script ## Basic Information - **Project Name**: repo_script - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-11-15 - **Last Updated**: 2025-11-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # repo工作环境打包/恢复工具 本工具用于打包和恢复使用repo工具管理的多仓库工作环境,特别适用于在不同机器间迁移临时开发环境。 ## 功能特性 - **智能基准点检测**: 自动查找远端仓库中存在的基准commit,无需fetch操作 - **本地提交打包**: 自动识别并打包本地新增的commit,生成patch文件 - **工作区修改保存**: 完整保存staged(已暂存)和unstaged(未暂存)的修改 - **未跟踪文件备份**: 完整备份所有untracked文件 - **分支关联记录**: `packrepo` 同时记录本地分支和其跟踪的远程分支,`unpackrepo` 智能比对并恢复对应分支 - **安全恢复**: 基于远端存在的commit创建分支,确保恢复环境的一致性 ## 文件说明 ### 多仓库脚本(repo 管理) - `packrepo`: 打包脚本,用于生成 repo 工作环境快照 - `unpackrepo`: 解包脚本,用于恢复 repo 工作环境 ### 单仓库脚本(单个 git 仓库) - `packgit`: 打包脚本,用于生成单个 git 仓库工作环境快照 - `unpackgit`: 解包脚本,用于恢复单个 git 仓库工作环境 ### 文档 - `README.md`: 本说明文档 - `PACK_DOCUMENTATION.md`: packrepo 详细文档 - `UNPACK_DOCUMENTATION.md`: unpackrepo 详细文档 - `PACK_SINGLE_DOCUMENTATION.md`: packgit 详细文档 - `UNPACK_SINGLE_DOCUMENTATION.md`: unpackgit 详细文档 ## 使用方法 ### 1. 打包工作环境 可以在任意目录下运行,不需要切换到仓库目录: ```bash packrepo [选项] <待打包的仓库路径> [输出的打包文件名] ``` **选项说明:** - `--backup-dotrepo`:打包 .repo 文件夹(默认不打包) - `--help` 或 `-h`:显示帮助信息 **参数说明:** - **待打包的仓库路径**(必需):repo管理的仓库路径 - **输出的打包文件名**(可选): - 如果不指定,自动生成:`snap_<仓库名>_<时间戳>.tar.gz`,保存在当前执行命令的目录 - 如果指定为文件名(如 `my_pack.tar.gz`),保存在当前执行命令的目录 - 如果指定为路径(如 `/tmp/my_pack.tar.gz` 或 `./output/pack.tar.gz`),保存到指定路径 - 如果没有 `.tar.gz` 后缀,会自动添加 **示例:** ```bash # 自动命名,保存在当前目录(默认打包 .repo) packrepo /path/to/repo_test # 指定文件名,保存在当前目录 packrepo /path/to/repo_test my_work_env.tar.gz # 指定完整路径 packrepo /path/to/repo_test /tmp/repo_pack.tar.gz # 指定相对路径 packrepo /path/to/repo_test ./output/pack.tar.gz # 打包 .repo 文件夹 packrepo --backup-dotrepo /path/to/repo_test # 打包 .repo 并指定输出文件名 packrepo --backup-dotrepo /path/to/repo_test my_pack.tar.gz ``` **打包过程:** 1. 扫描所有子仓库 2. 为每个仓库查找远端存在的基准commit(不执行fetch) 3. 生成manifest文件(包含远程信息、本地分支与远程分支) - **重要**:无论仓库是否有修改,都会保存所有仓库的commit信息到manifest - manifest 中会记录远程名称/URL、本地分支以及对应的远程分支(仅存储远程分支名称,远程名称单独存储) - 即使仓库没有任何修改,也能依靠这些信息在解包时准确定位分支/commit 4. 对于有本地新增commit的仓库,生成patch文件 5. 保存工作区修改: - **Staged修改**(Changes to be committed):保存已暂存但未提交的修改 - **Unstaged修改**(Changes not staged for commit):保存已修改但未暂存的文件 6. 备份所有untracked文件 - **支持包含空格的文件名和目录名**:使用 `IFS= read -r` 正确处理 - **支持多级子目录结构**:自动创建所有必要的目录 7. **打包 .repo 文件夹**(可选,默认不打包):将 `.repo` 文件夹复制到打包目录,用于在新路径中初始化repo仓库 - 可以使用 `--backup-dotrepo` 选项打包 .repo 文件夹 8. 压缩为tar.gz文件 **打包输出:** 打包后会生成一个压缩文件(`.tar.gz`),解压后结构如下: ``` repo_pack/ ├── manifest.txt # 仓库清单文件 ├── pack_info.txt # 打包信息 ├── patches/ # patch文件目录(本地提交) │ ├── repo1.patch │ └── repo2.patch ├── worktree_changes/ # 工作区修改目录 │ ├── repo1/ │ │ ├── staged.patch # 已暂存的修改 │ │ └── unstaged.patch # 未暂存的修改 │ └── repo2/ │ └── unstaged.patch └── untracked_files/ # 未跟踪文件目录 ├── repo1/ │ └── new_file.txt └── repo2/ └── heihei.txt .repo/ # repo管理文件夹(可选,使用 --backup-dotrepo 选项时包含,用于在新路径中初始化repo仓库) └── ... ``` ### 2. 恢复工作环境 可以在任意目录下运行,不需要切换到仓库目录: ```bash unpackrepo <待解包的仓库路径> <打包的文件路径> ``` **参数说明:** - **选项**(可选): - `--no-backup` 或 `--skip-backup`:跳过第一步强制备份(默认会执行备份) - `--recover-init`:初始化恢复记录文件,保存repo的初始状态(用于恢复到初始状态,方便repo工具继续维护) - **待解包的仓库路径**(必需):目标repo管理的仓库路径 - 如果路径不存在或为空文件夹,脚本会询问是否初始化新repo仓库 - 如果路径存在且是repo仓库,直接进行恢复 - **打包的文件路径**(使用 `--recover-init` 时不需要):打包生成的tar.gz文件路径 **示例:** ```bash # 从当前目录的打包文件恢复(默认会执行备份) unpackrepo /path/to/repo_test2 ./snap_repo_test_20240101_120000.tar.gz # 从指定路径的打包文件恢复(默认会执行备份) unpackrepo /path/to/repo_test2 /tmp/repo_pack.tar.gz # 跳过第一步强制备份(不推荐,除非确定不需要备份) unpackrepo --no-backup /path/to/repo_test2 /tmp/repo_pack.tar.gz # 初始化恢复记录文件,保存repo的初始状态 unpackrepo --recover-init /path/to/repo_test2 ``` **恢复过程:** 0. **路径检查和初始化**(如果目标路径不存在或为空文件夹): - 检查打包文件中是否包含 `.repo` 文件夹 - 询问用户是否基于当前空文件夹或创建新文件夹初始化repo仓库 - 如果用户确认(输入 Y/y): - 创建文件夹(如果路径不存在) - 解压 `.repo` 文件夹到目标路径 - 执行 `repo sync -c` 同步仓库 - 如果 manifest.txt 中的本地分支存在,执行 `repo start --all ` 切换分支 - 然后继续正常的恢复流程 1. **第一步:强制备份所有仓库的当前状态**(按照packrepo的逻辑,默认执行,可通过 `--no-backup` 选项跳过) - **无论是否有修改,都会保存所有仓库的当前commit信息到manifest**,方便恢复 - 对于有修改的仓库,按照packrepo的流程进行完整备份: - 本地提交的patch - untracked文件 - staged修改(包括二进制文件) - unstaged修改(包括二进制文件) - 备份步骤在恢复操作之前执行,防止忘记备份 - **注意**:使用 `--no-backup` 选项可以跳过备份,但不推荐,因为如果恢复过程中出现问题,可能无法恢复到备份时的状态 2. **第二步:开始恢复流程** - 读取manifest文件 - 检查每个仓库路径以及 `.git` 目录是否存在 - **双重校验**:先检查路径,再检查 `.git`(目录或文件)并通过 `git rev-parse --git-dir` 确认确实是 Git 仓库 - **自动 clone 缺失或无效的仓库**:如果路径不存在、或存在但不是 Git 仓库且 manifest 中有远程 URL,会先把旧目录备份为 `xxx.backup.`,再执行 `git clone` - 如果 clone 成功,继续正常的恢复流程 - 如果 clone 失败或 manifest 中无远程 URL,记录为"跳过"并继续处理其他仓库 - 检查每个仓库的基准commit是否存在 - **按需更新远端引用**:只有在找不到基准commit时,才执行 `git fetch`,并优先仅 fetch manifest 中记录的远程分支,失败时再退回 fetch 全部分支 - fetch 是安全的,只更新远端引用(refs/remotes/*),不会影响工作目录、未提交更改或 untracked 文件 - 如果 fetch 后仍然找不到基准commit,会报错并跳过该仓库 - **清理工作区**:在切换到恢复分支前,清理工作区确保干净状态 - 清理staged和unstaged修改(`git reset --hard HEAD`) - 清理untracked文件和目录(`git clean -fd`) - 这样可以防止工作区修改影响恢复操作 - 如果有未提交的更改,保存到stash - 根据 manifest 中的分支信息切换到对应分支: - **新逻辑**:先查找本地分支是否存在,然后检查其 upstream 是否与记录的远程分支(远程名称 + 远程分支名称)一致 - 如果本地分支存在且与远程分支匹配,直接切换 - 如果本地分支存在但与远程分支不匹配,基于远程分支重新创建或重设跟踪关系 - 如果本地分支不存在但远程分支存在,基于远程分支创建本地分支 - 如果都没有,使用基准commit创建分支(优先使用manifest中的本地分支名称,如果没有则创建 `snap_<基准commit短hash>` 分支) - 应用patch文件(如果有本地提交) - 恢复工作区修改(**恢复顺序很重要**): - **先恢复staged修改**:使用 `git apply` 应用到工作区(不添加到暂存区) - **再恢复unstaged修改**:使用 `git apply` 恢复到工作区,如果失败则使用 `--3way` 选项进行合并 - **最后添加staged文件到暂存区**:将staged涉及的文件添加到暂存区 - **原理**:`staged.patch` 是基于HEAD的差异,`unstaged.patch` 是基于staged后状态的差异,所以必须先应用staged,再应用unstaged,最后将staged文件添加到暂存区 - 这样可以确保同一文件既有staged又有unstaged修改时,两种修改都能正确恢复 - 恢复untracked文件 - **支持包含空格的文件名和目录名**:使用 `IFS= read -r` 和 `find -print0` 正确处理 - **支持多级子目录结构**:自动创建所有必要的目录 - 恢复前使用 `git clean -fd` 先清空当前仓库的 untracked 文件,避免备份文件与现有内容冲突 3. **自动打包压缩所有仓库的备份并保存到当前目录** **备份功能:** - **强制备份**:在恢复前,第一步强制备份所有仓库的当前状态(按照packrepo的逻辑) - **保存内容**: - **所有仓库的commit信息**:无论是否有修改,都会保存所有仓库的当前commit信息到manifest,方便恢复 - **有修改的仓库的完整备份**:对于有修改的仓库,按照packrepo的流程进行完整备份: - 本地提交的patch - staged修改(包括二进制文件) - unstaged修改(包括二进制文件) - untracked文件 - 备份会自动打包为 `backup_<仓库文件夹名>_<时间戳>.tar.gz`,保存在执行unpackrepo的当前目录 - 例如:如果恢复的仓库路径是 `/path/to/repo_test2`,备份文件名会是 `backup_repo_test2_20240101_120000.tar.gz` - 备份文件格式与packrepo生成的打包文件兼容,可以使用unpackrepo再次恢复 - 备份的manifest中包含所有仓库的commit信息,确保可以恢复到备份时的状态 **注意事项:** - **新路径初始化**:如果目标路径不存在或为空文件夹,脚本会询问用户是否初始化新repo仓库。如果用户确认,会解压 `.repo` 文件夹、执行 `repo sync -c`,并根据 manifest 中的本地分支执行 `repo start --all`。这要求打包文件必须包含 `.repo` 文件夹(packrepo 默认不打包,需要使用 `--backup-dotrepo` 选项才会包含)。 - **强制备份**:在恢复前,第一步强制备份所有仓库的当前状态(默认执行),无论是否有修改都会保存commit信息,防止忘记备份 - **跳过备份选项**:可以使用 `--no-backup` 或 `--skip-backup` 选项跳过备份,但不推荐,因为如果恢复过程中出现问题,可能无法恢复到备份时的状态 - **工作区清理**:在备份后、恢复前,会自动清理工作区(reset到HEAD,清理untracked文件),确保切换到恢复分支时是干净状态,防止影响恢复操作 - **自动 clone 功能**:如果打包的 repo 中有更多仓库,而目标 repo 中仓库较少,脚本会自动 clone 缺失的仓库(如果 manifest 中有远程 URL);如果路径存在但不是 Git 仓库,会先备份后重新 clone - **仓库合法性校验**:在执行任何 Git 命令前,会检查 `.git` 目录/文件并通过 `git rev-parse --git-dir` 双重确认,避免误操作非 Git 目录 - **按需执行 git fetch**:脚本不会一开始就执行 fetch,只有在找不到基准 commit 时才会尝试 fetch,并优先只 fetch manifest 中记录的远程分支,失败时再 fetch 全部 - fetch 是安全的,只更新远端引用(refs/remotes/*),不会破坏当前仓库状态(工作目录、未提交更改、untracked 文件等) - 如果基准 commit 仍然不存在(即使 fetch 后),脚本会提示错误并跳过该仓库 - **分支恢复策略**: - 先查找本地分支是否存在,然后检查是否和远程分支匹配 - 如果本地分支存在且与远程分支匹配,直接切换(不会影响现有分支) - 如果本地分支存在但与远程分支不匹配,基于远程分支创建新分支(保持本地分支名称) - 如果本地分支不存在但远程分支存在,基于远程分支创建本地分支 - 如果都没有,使用基准commit创建分支 - 恢复后会创建新分支(如果需要),不会影响现有分支 - 所有仓库的备份会自动保存,可以用于后续恢复 - **工作区修改恢复顺序**:先恢复 staged 修改到工作区,再恢复 unstaged 修改,最后将 staged 文件添加到暂存区 - **同一文件的staged和unstaged修改**:当同一文件既有staged又有unstaged修改时,会先应用staged修改,再应用unstaged修改,确保两种修改都能正确恢复 ## Manifest文件格式 manifest.txt文件格式如下: **新格式(推荐)**: ``` # repo工作环境manifest文件 # 格式: 仓库路径|基准commit|当前commit|是否有本地提交|是否有untracked文件|是否有staged修改|是否有unstaged修改|远程名称|远程URL|本地分支|远程分支 repo1|2bce005abc...|fc2f86fdef...|true|false|true|false|origin|https://gitee.com/...|main|main repo2|b014b72abc...|b014b72abc...|false|true|false|true|origin|https://gitee.com/...|dev_local|dev ``` **旧格式(向后兼容)**: ``` # repo工作环境manifest文件 # 格式: 仓库路径|基准commit|当前commit|是否有本地提交|是否有untracked文件|是否有staged修改|是否有unstaged修改|远程名称|远程URL|当前分支 repo1|2bce005abc...|fc2f86fdef...|true|false|true|false|gitee|https://gitee.com/...|main ``` 字段说明: - **仓库路径**: 相对于repo根目录的路径 - **基准commit**: 远端仓库中存在的commit(用于恢复时的起点) - **当前commit**: 打包时的当前commit - **是否有本地提交**: true/false,表示是否有基于基准commit的本地新提交 - **是否有untracked文件**: true/false,表示是否有未跟踪的文件 - **是否有staged修改**: true/false,表示是否有已暂存但未提交的修改 - **是否有unstaged修改**: true/false,表示是否有已修改但未暂存的文件 - **远程名称**: git remote名称 - **远程URL**: git remote URL - **本地分支**: 打包时的当前本地分支名称(如果不在分支上则为空) - **远程分支**: 本地分支对应的远程分支名称(仅保存分支名,例如 `dev`,远程名称单独记录在“远程名称”字段) **注意**: - 新格式包含本地分支和远程分支两个字段,可以处理本地分支名称和远程分支名称不一致的情况 - unpackrepo支持新旧两种格式,向后兼容旧格式的manifest文件 ## --recover-init 选项说明 `--recover-init` 选项用于恢复到repo的初始状态。这个功能主要用于在repo更新后,即使原始的本地提交和track文件可能已经丢失,也能方便地恢复到初始状态,继续使用repo工具进行维护。 ### 工作原理 1. **第一次 unpackrepo 恢复时**: - 正常恢复流程会自动收集所有仓库的初始状态信息 - 保存到 `.repo/unpacked_recover.txt` 文件(只在第一次恢复时写入) - 同时正常写入 `.repo/unpacked_repos.txt` 文件(记录恢复后的状态) 2. **使用 --recover-init 恢复**: - 读取 `.repo/unpacked_recover.txt` 文件 - 根据文件内容恢复到初始状态 - 如果所有仓库恢复成功,删除 `unpacked_recover.txt` 和 `unpacked_repos.txt` 文件 ### 使用场景 - repo更新后,本地分支可能丢失或处于detached HEAD状态 - 需要恢复到repo的初始状态,方便repo工具继续维护 - 原始的本地提交和track文件可能已经丢失,但.repo仓库还在 ### 使用方法 ```bash # 第一次正常恢复(会自动创建unpacked_recover.txt) unpackrepo /path/to/repo_test2 /path/to/repo_pack.tar.gz # 恢复到初始状态 unpackrepo --recover-init /path/to/repo_test2 ``` ### 功能说明 1. **第一次恢复时自动收集初始状态**: - 在第一次执行 unpackrepo 恢复时,会自动检测并收集所有仓库的初始状态 - 包括:git repo信息、基准commit、当前commit、本地分支、远程分支 - 支持detached HEAD状态("Not currently on any branch") - 保存到 `.repo/unpacked_recover.txt` 文件(只在第一次恢复时写入) 2. **--recover-init 恢复流程**: - 读取 `.repo/unpacked_recover.txt` 文件 - 清理每个仓库的工作区 - 切换到基准commit或对应的分支 - 如果所有仓库恢复成功,删除两个记录文件 3. **重要提示**: - 原始的本地提交和track文件可能已经丢失 - 此功能仅用于方便继续使用repo工具进行维护 - 不会恢复任何本地提交或track文件 ### 记录文件格式 `.repo/unpacked_recover.txt` 文件格式: ``` # unpackrepo恢复记录文件(初始状态) # 格式: 仓库路径|远程URL|本地分支|远程分支|当前commit|基准commit # 生成时间: 2024-01-01 12:00:00 # 说明: 此文件记录repo的初始状态信息,用于恢复到初始状态,方便repo工具继续维护 # 注意: 原始的本地提交和track文件可能已经丢失,此功能仅用于方便继续使用repo工具 repo1|https://example.com/repo1.git|main|main|abc123def|abc123def repo2|https://example.com/repo2.git||dev|789abc123|789abc123 ``` 字段说明: - **仓库路径**: 相对于repo根目录的路径 - **远程URL**: git remote URL(如果存在) - **本地分支**: 当前本地分支名称(如果不在分支上则为空,表示detached HEAD状态) - **远程分支**: 本地分支对应的远程分支名称(仅保存分支名) - **当前commit**: 当前的commit hash - **基准commit**: 基准commit hash(用于恢复时的起点) ### 处理detached HEAD状态 当仓库处于detached HEAD状态("Not currently on any branch")时: - 本地分支字段为空 - 会尝试查找所有远程分支,找到与当前commit匹配的远程分支 - 如果找到匹配的远程分支,会记录该远程分支名称 - 恢复时会直接checkout到基准commit(detached HEAD状态) ## 工作原理 ### 基准commit查找策略 packrepo使用以下策略查找基准commit(**不执行fetch**): 1. 从当前commit向上遍历父commit 2. 检查每个commit是否在本地已有的远端分支引用中存在 3. 找到第一个在远端分支中的commit作为基准点 4. 如果当前commit到基准commit之间有其他commit,这些就是本地新增的提交 **为什么不能fetch?** - fetch会更新本地的远端引用,导致无法区分哪些是本地提交 - 通过只使用本地已有的远端引用,可以准确识别本地新增的提交 ### Patch生成 对于有本地提交的仓库: - 使用 `git format-patch` 生成标准patch文件 - 如果format-patch失败,使用 `git diff` 生成diff文件 ### 工作区修改保存 对于工作区修改: - **Staged修改**:使用 `git diff --cached` 生成patch文件,保存到 `worktree_changes/<仓库路径>/staged.patch` - **Unstaged修改**:使用 `git status --porcelain` 作为主要检测方法,然后使用 `git diff` 生成patch文件,保存到 `worktree_changes/<仓库路径>/unstaged.patch` - **智能检测**:使用 `git status --porcelain` 可以更准确地检测实际的修改,避免二进制文件元数据变化(时间戳、权限等)导致的误判 - 只有当 `git status` 显示有修改时,才会保存unstaged修改 ### 恢复流程 1. **检查仓库路径并自动 clone**(新增功能): - 检查 manifest 中的每个仓库路径是否存在 - 如果仓库路径不存在且 manifest 中有远程 URL,自动使用 `git clone` clone 仓库 - clone 成功后,继续正常的恢复流程 2. **检查commit存在性**: 使用 `git cat-file -e` 检查基准commit是否存在 3. **切换到恢复分支**: - 优先使用 manifest 中的分支信息切换到对应分支 - 如果没有对端远程分支信息,先执行 `git fetch` 更新远端引用 - 如果有对端远程分支或已存在对端远程分支的本地分支,直接切换到对应分支 - 如果没有分支信息或切换失败,使用原来的 snap 分支逻辑(向后兼容) - 切换到分支后,如果当前 commit 不是基准 commit,reset 到基准 commit 4. **应用patch**: - 优先使用 `git am` 应用patch(适用于format-patch生成的patch) - 如果失败,使用 `git apply` 并手动提交 5. **恢复工作区修改**(按顺序执行,顺序很重要): - **先恢复 Staged修改**:使用 `git apply` 应用到工作区(不添加到暂存区),并记录涉及的文件列表 - **再恢复 Unstaged修改**:使用 `git apply` 恢复到工作区,如果失败则使用 `--3way` 选项进行合并 - **最后添加Staged文件到暂存区**:将staged涉及的文件添加到暂存区 - **原理说明**: - `staged.patch` = `git diff --cached` = HEAD vs 暂存区的差异 - `unstaged.patch` = `git diff` = 暂存区 vs 工作区的差异 - unstaged.patch 是基于staged后状态的差异,所以必须先应用staged,再应用unstaged - 这样可以确保同一文件既有staged又有unstaged修改时,两种修改都能正确恢复 6. **恢复文件**: 从备份目录复制untracked文件到原位置 ## 单仓库脚本使用(packgit / unpackgit) 如果你只需要打包/恢复单个 git 仓库(不是 repo 管理的多仓库环境),可以使用单仓库脚本。 ### 打包单个 git 仓库 ```bash packgit <待打包的git仓库路径> [输出的打包文件名] ``` **示例:** ```bash # 自动命名,保存在当前目录 packgit /path/to/my_repo # 指定文件名 packgit /path/to/my_repo my_repo_pack.tar.gz ``` ### 恢复单个 git 仓库 ```bash unpackgit [选项] <待解包的仓库路径> <打包的文件路径> ``` **示例:** ```bash # 从打包文件恢复(默认会执行备份) unpackgit /path/to/my_repo ./snap_my_repo_20240101_120000.tar.gz # 跳过备份 unpackgit --no-backup /path/to/my_repo /tmp/my_repo_pack.tar.gz ``` **详细文档**: - 参见 `PACK_SINGLE_DOCUMENTATION.md` 了解 packgit 的详细说明 - 参见 `UNPACK_SINGLE_DOCUMENTATION.md` 了解 unpackgit 的详细说明 ## 使用场景 ### 场景1: 临时开发环境迁移(多仓库) ```bash # 在开发机器A上(可以在任意目录执行) packrepo /path/to/repo_test dev_env_20240101.tar.gz # 将打包文件传输到机器B scp dev_env_20240101.tar.gz user@machineB:/path/to/ # 在机器B上恢复(可以在任意目录执行) repo sync /path/to/repo_test2 # 确保仓库同步 unpackrepo /path/to/repo_test2 /path/to/dev_env_20240101.tar.gz ``` ### 场景2: 备份当前工作状态(多仓库) ```bash # 定期打包当前工作状态(可以在任意目录执行) packrepo /path/to/repo_test backup_$(date +%Y%m%d).tar.gz ``` ### 场景3: 单个 git 仓库迁移 ```bash # 在开发机器A上打包单个仓库 packgit /path/to/my_repo # 将打包文件传输到机器B scp snap_my_repo_*.tar.gz user@machineB:/path/to/ # 在机器B上恢复 unpackgit /path/to/my_repo /path/to/snap_my_repo_*.tar.gz ``` ### 场景4: 备份单个 git 仓库 ```bash # 定期打包单个仓库的工作状态 packgit /path/to/my_repo backup_$(date +%Y%m%d).tar.gz ``` ## 故障排查 ### 问题1: 基准commit找不到 **现象**: packrepo提示"未找到远端基准commit" **原因**: 当前commit及其所有父commit都不在本地已有的远端引用中 **解决**: - 检查是否有远端分支引用:`git branch -r` - 如果确实没有,可能需要先fetch一次(但注意这会更新远端引用) ### 问题2: 恢复时commit不存在 **现象**: unpackrepo提示"基准commit不存在" **原因**: 目标仓库没有同步到包含该commit的状态 **解决**: ```bash cd <目标仓库> repo sync # 或者手动fetch特定仓库 cd <子仓库路径> git fetch ``` ### 问题3: Patch应用失败 **现象**: unpackrepo提示"Patch应用失败" **原因**: - patch文件格式不正确 - 目标仓库状态与打包时不一致 - 有冲突 **解决**: 1. 检查patch文件内容:`cat ` 2. 手动应用patch:`git apply --check ` 检查 3. 如果有冲突,手动解决后提交 ### 问题4: 恢复分支已存在 **现象**: 创建恢复分支时提示分支已存在 **解决**: 脚本会自动删除旧分支并重新创建,如果仍有问题,手动删除: ```bash git branch -D restore_<仓库名>_<时间戳> ``` ## 限制和注意事项 1. **不执行fetch**: packrepo不会fetch远端更新,只使用本地已有的远端引用 2. **commit必须存在**: 恢复时,基准commit必须在目标仓库中存在 3. **分支命名**: 恢复分支使用固定格式,如果多次恢复可能产生多个分支 4. **文件权限**: 恢复的untracked文件会保持原权限(如果文件系统支持) 5. **大文件**: 对于非常大的untracked文件,打包文件可能会很大 ## 测试建议 在使用前建议先测试: ### 方法1: 使用测试脚本 项目提供了测试脚本 `test_pack_unpackrepo`,可以自动测试 staged 和 unstaged 修改的保存和恢复: ```bash # 运行测试脚本 ./test_pack_unpackrepo ``` 测试脚本会: 1. 创建测试仓库 2. 创建 staged 修改、unstaged 修改和 untracked 文件 3. 执行 packrepo 打包 4. 清理仓库状态 5. 执行 unpackrepo 恢复 6. 验证恢复结果 ### 方法2: 手动测试 ```bash # 1. 准备测试环境:创建 staged 和 unstaged 修改 cd /path/to/repo_test/repo1 echo "unstaged change" >> existing_file.txt echo "new content" > new_file.txt git add new_file.txt # staged 修改 # existing_file.txt 的修改是 unstaged 修改 # 2. 打包repo_test(可以在任意目录执行) packrepo /path/to/repo_test test_pack.tar.gz # 3. 在repo_test2中恢复(可以在任意目录执行) unpackrepo /path/to/repo_test2 /path/to/test_pack.tar.gz # 恢复后会创建分支,例如:snap_2bce005e # 4. 检查恢复结果 cd /path/to/repo_test2/repo1 git status # 检查 staged 和 unstaged 修改 git diff # 查看 unstaged 修改 git diff --cached # 查看 staged 修改 ls -la # 检查 untracked 文件 ``` ## 许可证 本工具脚本可自由使用和修改。