Convert a directory of timestamped snapshot directories into a proper Git repository using git fast-import — the method described in Pro Git §9.2 – Custom Importer.
bash≥ 3.2git≥ 2.0 (2.28+ recommended for--initial-branchsupport)- Standard POSIX tools:
find,sort,wc,grep,date
Each subdirectory of your source directory becomes one Git commit.
Supported naming patterns:
- Date-based snapshots: subdirectory name contains
YYYY-MM-DD(prefix, suffix, or embedded) - Release-style snapshots: subdirectory name matches
Vx.y.z(for exampleV5.6.0), similar to zip-integration release folders
Date-based example:
/backups/myproject/
├── 2024-01-02/
├── backup-2024-01-14/
├── my-project-2024-02-03-hotfix/
└── current/ ← receives today's timestamp
Release-style example:
/backups/myproject/
├── V5.6.0/
├── V5.7.0/
└── V5.10.0/
Ordering rules:
- Date-based snapshots are ordered chronologically by extracted date.
- Release-style snapshots are ordered by semantic version.
- A directory named exactly
currentreceives today's date. - Subdirectories matching neither pattern are skipped with a warning.
./dir2git.sh --source /backups/myproject \
--author "Jane Doe" \
--email jane@corp.comThis creates ./target_repo/ containing the full imported history.
Usage: dir2git.sh [OPTIONS]
OPTIONS
--source DIR Directory containing snapshot subdirectories [required]
--target DIR Output path for the new git repo [default: ./target_repo]
--author NAME Committer name
--email EMAIL Committer email
--timezone TZ UTC offset, e.g. +0200 or -0500 [default: +0000]
--branch NAME Target branch name [default: main]
--config FILE Config file path [default: dir2git.conf next to script]
--dry-run Print the fast-import stream to stdout; do not import
-h, --help Show help
Copy dir2git.conf and edit it — useful when migrating multiple projects or automating runs. CLI arguments always override config values.
./dir2git.sh --config /path/to/myproject.conf| Variable | Description |
|---|---|
SOURCE_DIR |
Path to directory containing snapshots |
TARGET_DIR |
Output path for the new git repo |
AUTHOR_NAME |
Committer name |
AUTHOR_EMAIL |
Committer email |
TIMEZONE |
UTC offset applied to all commits (e.g. +0200) |
BRANCH |
Target branch name |
COMMIT_MSG_TEMPLATE |
Message template; {dir} and {date} are substituted |
DATE_REGEX |
ERE used to extract YYYY-MM-DD from directory names |
RELEASE_REGEX |
ERE used to match release-style directories (default Vx.y.z) |
GITIGNORE_PATTERNS |
Newline-separated patterns to write into .gitignore |
SOURCE_DIR="/backups/myproject"
TARGET_DIR="/repos/myproject"
AUTHOR_NAME="Jane Doe"
AUTHOR_EMAIL="jane@corp.com"
TIMEZONE="+0200"
BRANCH="main"
COMMIT_MSG_TEMPLATE="snapshot: {dir} ({date})"
RELEASE_REGEX="^[Vv][0-9]+\.[0-9]+\.[0-9]+$"
GITIGNORE_PATTERNS=$'*.log\n*.tmp\n.DS_Store'| Feature | Notes |
|---|---|
| Chronological ordering | Dates extracted from directory names |
| Configurable author & timezone | Per-project config file or CLI |
| Custom commit message | Template with {dir} and {date} |
| Executable file detection | Files with execute permission → mode 100755 |
| Symbolic link preservation | Mode 120000, stores link target |
.gitignore generation |
Optional post-import commit |
| Dry-run mode | Inspect the stream before importing |
| Dual source-import pattern | Supports both date-based and Vx.y.z release directories |
| Safe partial-import cleanup | Target directory removed on failure |
| Paths with spaces | C-style quoting applied automatically |
Preview the fast-import stream without creating a repository:
./dir2git.sh --source /backups/myproject --dry-runValidate the stream syntax without writing objects (if your Git build supports it):
./dir2git.sh --source /backups/myproject --dry-run \
| git fast-import --dry-runIf git fast-import --dry-run is unavailable in your environment, you can still sanity-check stream generation with:
./dir2git.sh --source /backups/myproject --dry-run > /tmp/dir2git.stream# View history
git -C target_repo log --oneline
# Push to a remote
git -C target_repo remote add origin git@github.com:you/myproject.git
git -C target_repo push origin --all- Binary files: Very large binary files will be fully buffered in the fast-import stream. For repositories with many large assets, consider Git LFS after import.
- Filenames with newlines: Unusual but possible on some filesystems; such paths are not handled correctly.
- Incremental imports: Each snapshot is imported as a complete file-set snapshot (
deleteallstrategy). This matches the book's approach and is simpler than tracking deltas.