https://github.com/codybontecou/gitsync.md
Markdown notes synced with Git — a native iOS & iPadOS app that turns any GitHub repository into a synced markdown vault.
https://github.com/codybontecou/gitsync.md
git github ios libgit2 markdown obsidian swift swiftui
Last synced: about 8 hours ago
JSON representation
Markdown notes synced with Git — a native iOS & iPadOS app that turns any GitHub repository into a synced markdown vault.
- Host: GitHub
- URL: https://github.com/codybontecou/gitsync.md
- Owner: CodyBontecou
- License: mit
- Created: 2026-02-08T23:51:07.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-06-09T22:01:13.000Z (14 days ago)
- Last Synced: 2026-06-09T23:19:45.418Z (14 days ago)
- Topics: git, github, ios, libgit2, markdown, obsidian, swift, swiftui
- Language: C
- Size: 138 MB
- Stars: 9
- Watchers: 0
- Forks: 0
- Open Issues: 13
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# GitSync.md
**Markdown notes synced with Git** — a native iOS & iPadOS app that turns any GitHub repository into a synced markdown vault.
## What It Does
GitSync.md clones GitHub repos directly to your iPhone or iPad using [libgit2](https://libgit2.org), giving you a real `.git` directory on the device filesystem. Edit markdown files with any app — [Obsidian](https://obsidian.md), ia Writer, or the built-in Files app — then pull and push changes back to GitHub.
**Key features:**
- **Real git** — Clone, pull, commit, and push via libgit2. No REST API workarounds, no proprietary sync.
- **Multiple repos** — Manage several GitHub repositories at once.
- **Custom save locations** — Store repos anywhere accessible via the Files app.
- **Obsidian integration** — Works with Obsidian vaults via `x-callback-url` for automated sync.
- **GitHub OAuth & PAT** — Sign in with GitHub OAuth or paste a Personal Access Token.
- **Private repo support** — Works with both public and private repositories.
- **iPad support** — Optimized layouts for iPad.
## How It Works
1. **Sign in** with GitHub (OAuth or Personal Access Token)
2. **Pick a repository** from your GitHub account (or add one manually)
3. **Clone** it to your device — files appear in the iOS Files app
4. **Edit** with any markdown editor
5. **Pull** to fetch remote changes, **Push** to commit and upload yours
Files live under `On My iPhone › GitSync.md` by default, or in a custom location you choose.
## Architecture
```
GitSync.md/
├── Sync.md/ # iOS app source
│ ├── Sync_mdApp.swift # App entry point
│ ├── ContentView.swift # Root view router
│ ├── Models/
│ │ ├── AppState.swift # Observable app state (repos, auth, sync)
│ │ ├── RepoConfig.swift # Repository configuration model
│ │ └── GitState.swift # Git state persistence
│ ├── Views/
│ │ ├── SetupView.swift # Onboarding & sign-in
│ │ ├── RepoListView.swift # Home screen — repo cards
│ │ ├── VaultView.swift # Single repo — pull/push/status
│ │ ├── AddRepoView.swift # Add new repository flow
│ │ ├── RepoPickerView.swift # GitHub repo browser
│ │ ├── SettingsView.swift # Per-repo settings
│ │ ├── GitControlSheet.swift # Commit message & push sheet
│ │ └── Theme.swift # Design system (colors, gradients, glass cards)
│ └── Services/
│ ├── LocalGitService.swift # libgit2 wrapper (clone/pull/push/status)
│ ├── GitHubService.swift # GitHub REST API client
│ ├── OAuthService.swift # GitHub OAuth via ASWebAuthenticationSession
│ ├── KeychainService.swift # Secure token storage
│ └── CallbackURLHandler.swift # x-callback-url handler (Obsidian integration)
├── Packages/
│ └── Clibgit2/ # Swift package wrapping the libgit2 C library
├── oauth-server/ # Vercel serverless functions for GitHub OAuth
│ └── api/auth/ # Login & callback endpoints
└── libgit2.xcframework/ # Pre-built libgit2 binary for iOS
```
### Git Implementation
All git operations use **libgit2** directly via C interop — no shelling out, no REST API tree manipulation. The `LocalGitService` wraps libgit2 to provide:
- **Clone** — `git_clone` with HTTPS credential callback
- **Pull** — Fetch + fast-forward merge
- **Commit & Push** — Stage all changes, create commit, push to remote
- **Status** — Uncommitted change detection via `git_status_list`
This produces a standard `.git` directory, making repos compatible with other git tools like the [Obsidian Git](https://github.com/Vinzent03/obsidian-git) plugin.
### x-callback-url API
External apps can trigger sync operations via URL scheme:
```
syncmd://x-callback-url/?repo=&x-success=&x-error=
```
| Action | Description |
|----------|-------------|
| `pull` | Fetch and fast-forward |
| `push` | Stage all, commit, and push |
| `sync` | Pull then push |
| `status` | Return branch, SHA, and change count |
## Building
### Requirements
- **Xcode 16+**
- **iOS 17.0+** deployment target
- macOS with Apple Silicon (or Intel with Rosetta)
### Steps
1. Clone the repo:
```bash
git clone https://github.com/CodyBontecou/GitSync.md.git
cd GitSync.md
```
2. Open in Xcode:
```bash
open Sync.md.xcodeproj
```
3. Select your target device or simulator and build (`⌘B`).
The pre-built `libgit2.xcframework` is included in the repo so no additional dependency setup is needed.
## Testing
Run the unit XCTest gate locally with:
```bash
xcodebuild test \
-project Sync.md.xcodeproj \
-scheme Sync.md \
-destination 'platform=iOS Simulator,name=iPhone 17' \
-only-testing:SyncMDTests \
-parallel-testing-enabled NO
```
If your machine does not have an `iPhone 17` simulator, replace the destination with any available iPhone simulator from `xcrun simctl list devices available`.
The local git tests create isolated temporary repositories via `FileManager.default.temporaryDirectory` and clean them up with `defer`. Fixture setup should use local-only commits (`commitLocalFixtureChanges` in `SyncMDTests`) unless the test is explicitly exercising push behavior; this avoids depending on expected push failures from repositories without an `origin` remote.
The same unit gate runs in GitHub Actions via `.github/workflows/xctest.yml` on pull requests and pushes to `main`.
### OAuth Server (Optional)
The `oauth-server/` directory contains Vercel serverless functions that handle the GitHub OAuth flow. If you want to use OAuth sign-in (instead of a PAT), you'll need to:
1. Create a [GitHub OAuth App](https://github.com/settings/developers)
2. Deploy the oauth-server to Vercel
3. Set `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` as environment variables
4. Update the `serverURL` in `OAuthService.swift`
Using a **Personal Access Token** works without any server setup — just paste a token with `repo` scope.
## Contributing
Contributions are welcome! Feel free to open issues or submit pull requests.
Some areas where help would be appreciated:
- Conflict resolution UI (currently only fast-forward merges)
- Branch switching
- Selective file staging
- Background sync / scheduled pulls
- macOS support
### Editor Setup
If you use a SourceKit-LSP-based editor (Neovim, VS Code + Swift extension, Helix, Zed), generate a `buildServer.json` once so the LSP can resolve cross-file symbols:
```bash
brew install xcode-build-server
xcode-build-server config -project Sync.md.xcodeproj -scheme Sync.md
```
The generated `buildServer.json` is gitignored. Build in Xcode once afterwards so the LSP picks up the compiler index.
## License
[MIT](LICENSE) — Cody Bontecou