A native Android app for reliably syncing Obsidian vaults to Git repositories.
This app was built as a personal solution after encountering various issues with existing Git sync options on Android. It uses a hybrid clone+SAF approach that provides reliable Git operations with proper Android file system integration.
- Reliable Git Sync - Clone, pull, push with JGit (pure Java, no native dependencies)
- Multiple Auth Methods - Personal Access Tokens (PAT) for GitHub/GitLab
- Bi-directional Sync - Changes sync both from vault to Git and Git to vault
- Background Sync - Configurable intervals (5/15/30/60 minutes) via WorkManager
- Conflict Detection - Identifies and helps resolve merge conflicts
- Existing Vault Migration - Import existing local notes into a cloned repository
- Secure Credential Storage - Encrypted storage for tokens and credentials
- First-time sync may take a few minutes for large vaults due to Android SAF limitations
- Files with special characters (
? " * < > | :) will be renamed on Android as these characters are not supported in Android filenames
Remote Git Repo <--(JGit)--> App Internal Clone <--(SAF Sync)--> Obsidian Vault
The app maintains an internal Git clone for fast operations, then syncs files to/from your Obsidian vault using Android's Storage Access Framework (SAF).
| Component | Technology |
|---|---|
| Language | Kotlin |
| UI | Jetpack Compose + Material 3 |
| Git | JGit 7.1.0 |
| DI | Hilt |
| Database | Room |
| Background Work | WorkManager |
| Security | EncryptedSharedPreferences |
| File Access | SAF (DocumentFile) |
- Android Studio Hedgehog or newer
- JDK 17
- Android SDK 35
./gradlew assembleDebugThe debug APK will be at app/build/outputs/apk/debug/app-debug.apk.
To build a signed release APK, you'll need to set up your own signing configuration:
-
Generate a keystore (skip if you already have one):
keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-key-alias
-
Add signing config to
local.properties(this file is gitignored):KEYSTORE_PATH=../my-release-key.jks KEYSTORE_PASSWORD=your_keystore_password KEY_ALIAS=my-key-alias KEY_PASSWORD=your_key_password
-
Build the release APK:
./gradlew assembleRelease
The signed APK will be at app/build/outputs/apk/release/app-release.apk.
# Unit tests
./gradlew testDebugUnitTest
# Instrumented tests (requires device/emulator)
./gradlew connectedAndroidTestapp/src/main/java/com/obsidiangit/
├── di/ # Hilt dependency injection modules
├── data/
│ ├── git/ # JGit wrapper implementation
│ ├── filesync/ # SAF file access + sync engine
│ ├── credentials/ # Encrypted credential storage, OAuth
│ ├── database/ # Room entities + DAOs
│ └── repository/ # Repository implementations
├── domain/
│ ├── model/ # Domain entities (Vault, SyncResult, etc.)
│ ├── repository/ # Repository interfaces
│ └── usecase/ # Business logic (SyncVault, SetupVault)
├── ui/
│ ├── theme/ # Material 3 theme
│ ├── navigation/ # Navigation graph
│ ├── screens/ # Compose screens
│ └── components/ # Reusable composables
├── worker/ # WorkManager sync workers
└── util/ # Extensions, constants
- Install the app on your Android device
- Grant storage permissions when prompted
- Select your Obsidian vault folder
- Enter your Git repository URL
- Add authentication (PAT or OAuth)
- Clone and start syncing
- GitHub: Settings → Developer settings → Personal access tokens → Fine-grained tokens → Generate new token
- Select the repository, then under "Repository permissions" grant Contents read and write access
- GitLab: Preferences → Access Tokens → Add new token
- Required scopes:
read_repository,write_repository
- Required scopes:
OAuth support for GitHub and GitLab is planned for a future release.
- Periodic check scans for changes at your configured interval
- Local changes detected → Commit + Push
- Remote changes detected → Pull + Merge
- Both changed → Pull first, merge, then push
- Conflicts → User prompted to resolve
MIT License - See LICENSE for details.
Contributions welcome! Please read the CLAUDE.md for development guidelines.