返回文章列表文章索引阅读全文
STARBLOG / 阅读手记
DevOps

code-porter: A Code Workspace Migration Tool

Switching computers and copying dozens of projects by hand? You're either lazy or stupid. I wrote code-porter: auto-scan, smart packing (bundle/zip), dirty repos keep uncommitted changes, one uvx command. Zero dependencies, cross-platform, plug in a USB stick and you're done. Don't ask why I don't use rsync—does it understand your project structure?

曦远 (DealiAxy)约 20 分钟
目录9 个章节

Introduction

Recently I switched from Windows to a MacBook Pro and ran into a practical problem: I had dozens of projects scattered across various directories—Python, TypeScript, Go, C#. Some were proper Git repos; some were experimental code AI had generated for me that didn't even have a .git.

In theory, you'd just push Git repos to a remote and copy the non-Git projects. In practice: some repos had uncommitted changes, some had no remote configured at all, and some were shallow clones that couldn't be bundled directly. Handling them one by one? A nightmare.

I'd tried scp and rsync before, but Windows support for these tools is an absolute disaster—error after error, practically unusable. What about just zipping up the project directories? That doesn't work either—the .venv and node_modules in each project easily run to several GB, and excluding them one by one would drive you insane.

So I wrote code-porter, a purely local code workspace migration tool. It automatically scans project directories, smartly decides the packing strategy for each project, then one-click exports them as git bundles or zip archives. On the import side, you only need a manifest file to restore them in batch.

Why not cloud sync? Because my projects add up to tens of GB, iCloud sync would take forever, and some projects have .env files I don't want to risk uploading to the cloud. Purely local, copy to a USB stick and you're done—lowest hassle.

Tool Overview

code-porter's core idea is simple:

  1. Scan: Walk directories, identify project types and Git status
  2. Export: Pick the optimal packing strategy (bundle / zip) based on project state
  3. Import: Read the manifest and one-click restore to the target machine

Three commands handle everything:

# 扫描,看看有哪些项目
uvx code-porter scan ~/code ~/lab

# 导出,打包成 bundle + zip
uvx code-porter export ~/code ~/lab ./exports/backup

# 导入,在新机器上恢复
uvx code-porter import ./exports/backup/manifest.json ~/code/imported

Zero install: Run it directly via uvx—no need to install a Python environment or any dependencies in advance. uvx is a command that ships with uv, one line and you're done.

Smart Packing Strategy

This is code-porter's core design. It doesn't mindlessly zip everything; it picks the optimal strategy based on each project's actual state:

Project StatePacking StrategyNotes
Clean Git repo (has commits)git bundlePreserves full history, branches, and tags
Dirty Git repo (uncommitted changes)bundle + worktree overlay zipbundle keeps history; overlay zip keeps the working tree
Non-Git projectzipPack directly; read .gitignore to exclude junk files
Empty Git repo (no commits)zipbundle is meaningless; fall back to zip
Shallow clonezipA shallow clone cannot produce a valid bundle; fall back

The killer feature is the overlay zip mechanism: for Git repos with uncommitted changes, the tool exports both a bundle (full Git history) and a worktree overlay zip (current working-tree files). On import it clones the bundle first, then extracts the overlay on top, so your uncommitted changes are fully preserved.

# 导出后的目录结构
exports/backup/
    manifest.json           # 清单文件,记录每个项目的元数据
    artifacts/
        my-app-a1b2c3d4.bundle          # 干净仓库 → bundle
        my-app-a1b2c3d4.worktree.zip    # 脏仓库的 overlay
        experiment-e5f6g7h8.zip         # 非 Git 项目 → zip

Project Detection

The tool identifies projects of different language stacks by looking for marker files:

  • Python: pyproject.toml
  • Node.js: package.json
  • Go: go.mod
  • Rust: Cargo.toml
  • .NET: *.sln

During a scan it fully analyzes each project:

  • Whether it's a Git repo, whether it has a remote, whether the working tree is clean
  • Directory size, whether large directories exist (configurable threshold, default 500MB)
  • Whether it hits default exclude directories (node_modules, .venv, dist, etc.)
  • Whether it's worth exporting, and the recommended packing strategy
# 扫描并输出 JSON 报告,方便进一步分析
uvx code-porter scan ~/code --json-output scan-report.json

Cross-Platform Compatibility

This tool was designed with cross-platform use in mind from the start. My own use case was Windows → macOS, so:

  • manifest path compatibility: Paths in the exported manifest.json use backslashes \ on Windows and forward slashes / on macOS. Import handles this automatically—no manual conversion.
  • CI dual-platform tests: GitHub Actions runs tests on both Ubuntu and Windows to ensure consistent behavior across the two platforms.
  • Purely local: Does not depend on any network service, does not call any cloud API, does not store any credentials. How you transfer is entirely up to you—USB stick, SMB share, iCloud Drive, whatever.

Fault-Tolerant Design

In real use, unexpected situations always come up. code-porter's design principle is one project's failure doesn't affect the overall flow:

  • git bundle create fails? Automatically fall back to zip export
  • Discovers a shallow clone during export? Fall back to zip
  • One project fails to export? Log the error, continue with the next
  • A package is corrupted on import? Skip it, keep importing the rest

Failed projects are marked failed in the results but don't appear in the manifest, so the import side won't try to process them.

Default Exclude List

The following directories are automatically excluded when packing, so you don't copy several GB of dependency packages along:

node_modules    # Node.js 依赖
.venv           # Python 虚拟环境
dist            # 构建产物
build           # 构建产物
target          # Rust 构建产物
.next           # Next.js 缓存
.cache          # 各种缓存
.git            # Git 元数据(bundle 已包含)

For zip exports, it also reads .gitignore at the project root and layers it on top of the default exclude list. In other words, files you exclude in .gitignore are also excluded when packing.

You can append custom excludes with the --exclude flag:

uvx code-porter export ~/code ./exports --exclude .idea --exclude .vscode

Progress Bars

The scan, export, and import commands all have Rich progress bars showing the project currently being processed and elapsed time. When you have a lot of projects, this experience is way better than sitting there waiting.

If you don't need the progress bar (e.g. running in CI), just add --no-progress.

Wrap-up

The core problem code-porter solves: when you have a large number of scattered projects to migrate, doing it by hand is painful and easy to miss things.

It doesn't try to be a heavyweight DevOps tool. It just focuses on doing one thing well: scan → smart packing → manifest-driven import. The whole process is purely local, zero-dependency, and cross-platform.

The tool is still in Alpha, but the core scan, export, and import flow is fully usable. My real-world scenario (Windows → MacBook, migrating dozens of projects) already works end to end.

If you're in a similar situation—switching computers, organizing projects, backing up a workspace—give it a try:

# 一行命令体验
uvx code-porter scan ~/code

The project is open source on GitHub: star-plan/code-porter. Issues and PRs welcome.

PS: Why not rsync or scp? Because they don't know which things are projects, which are junk directories, and which have uncommitted changes. They just copy files; they don't understand the concept of a "project". code-porter's value is that it understands a developer's project structure and makes smart decisions accordingly.