Maple es un cliente de Git nativo, gratis y rápido para macOS hecho en SwiftUI. Sin webviews, sin Electron, sin suscripciones. Habla directo con
giten tu máquina y no pretende esconder el modelo real de Git detrás de abstracciones raras.Repo: https://github.com/poolcamacho/Maple · Licencia: MIT · 🇺🇸 Read in English
Por qué otro cliente de Git
En macOS ya hay GUIs de Git muy decentes — Tower y Fork son las que más me gustan. Empecé Maple por algo más específico: quería una app que fuera gratis, open source, 100% SwiftUI nativo, y sin miedo a exponer el modelo real de Git para power users, y a la vez quería aprender SwiftUI moderno construyendo algo que yo mismo usara todos los días.
Maple arrancó con un brief bien acotado:
- 100% SwiftUI nativo — sin webviews, sin Electron, abre tan rápido como Terminal.
- Gratis y MIT — nunca suscripción.
- Muestra Git como es — commit graph con topología real, branches y merges visibles, vista de conflictos de verdad. Cero wizards que escondan lo que está pasando.
- Respeta tu flujo — shortcuts y acciones para lo que un power user realmente usa: interactive staging, stash, rebase, merge, cherry-pick.
No pretende reemplazar a Tower para todo el mundo. Es el cliente que yo quería que existiera.
Lo que ya hace
Esto es un post de avance, no un lanzamiento. Las capturas las subiré en el próximo post. Pero esto ya funciona de punta a punta en repos reales (llevo varios días dogfooding con la misma app para commitear su propio código):
- Abrir cualquier repo local con el folder picker y validación de
.git - Sidebar con lista de repos, branches locales y remotos
- Toolbar con Pull, Push, Fetch, Stash, Branch, Merge, Rebase
- Cuatro tabs: Changes, History, Branches, Stashes
- Diff viewer con hunks coloreados, números de línea, y un toggle de Blame
- Commit graph con topología real — algoritmo de lanes, edges curvos por cada parent, merges dibujados con círculos anillados
-
Merge y rebase con manejo de conflictos — detecta
UU/AA/DD, banner de operación con Abort / Continue / Skip, y resolución por archivo conUse Ours/Use Theirs - Auto-refresh vía FSEvents sobre
.git/ - Layout adaptativo de desktops anchos a pantallas compactas
La arquitectura en una pantalla
Models/ — Data pura y Sendable (AppState, GitModels, StashModels)
Services/ — GitService (actor, ejecuta el CLI)
GitCoordinator (@MainActor, orquestación)
Extensiones por comando (GitCommands, GitBranchOps,
GitStashOps, GitMergeRebase)
CommitGraphBuilder, ConflictParser, FileWatcher
Views/ — Un archivo por vista, cero lógica de negocio
Utils/ — FolderPicker, DateExtensions
Tres decisiones que vale la pena llamar aparte:
GitService es un actor, así que todas las llamadas al CLI se serializan por un solo "portero". GitCoordinator es @MainActor y hace de pegamento entre la UI y el actor — las vistas solo llaman state.coordinator.* y nunca tocan un Process directamente. Resultado: lógica de negocio cero en las vistas, fácil de testear y refactorizar.
Cada invocación de git abre un /dev/null fresco para stdin y cierra sus pipes explícitamente. Sin eso me salía NSPOSIXErrorDomain code=9 / EBADF después de comandos largos como push, porque FileHandle.nullDevice es un singleton compartido que termina en estados raros después de muchos posix_spawn. Abrir /dev/null por llamada con closeOnDealloc: true lo arregla de tajo.
El commit graph no es el "punto + línea vertical" de toda la vida. Construye un layout real de lanes: para cada commit, reclama el lane que lo estaba esperando (el vínculo child→parent); si no hay, reusa un lane libre. Los primeros parents se quedan en el mismo lane para que la línea principal quede recta; los parents adicionales de merges abren lanes laterales con edges curvos. Los edges se resuelven en una segunda pasada porque un parent puede aparecer muchas filas más abajo.
El UX de conflictos es por archivo, no por hunk — a propósito para v1. Cuando cae un merge con conflicto, el tab Changes marca cada archivo conflictivo con un ! morado, y cada uno tiene tres botones: Use Ours, Use Theirs, o editas los markers tú mismo en tu editor favorito. Arriba aparece un banner persistente que dice Merging X con Abort / Continue / Skip. Sin modales, sin secuestrarte el flujo.
Lo que ya está armado alrededor del código
Porque la idea es que esto crezca como proyecto OSS serio, no como experimento de fin de semana:
-
GitHub Actions CI — build +
xcodebuild analyze+ SwiftLint strict en cada push y PR - CodeQL — análisis semanal de Swift más en cada PR que toque código Swift
-
Workflow de release — taggeas
v*y sale un.appsin firmar zipeado automáticamente -
Branch protection en
master— status checks requeridos, no force push, no delete, conversations resueltas - Dependabot para GitHub Actions, así las versiones no se quedan oxidadas
-
SECURITY.mdcon private vulnerability reporting activado - Issue forms y PR template que fuerza la regla de "ni una sola línea de lógica de negocio en las vistas"
Nada de esto es heroico. Es el andamiaje aburrido que separa un hobby project de un repo donde la gente realmente puede contribuir.
Lo que sigue
El roadmap que estoy atacando en orden:
- [ ] Interactive staging — stage de hunks o líneas individuales, no solo archivos enteros
- [ ] Tag management — crear, listar, borrar tags desde la UI
- [ ] Search / filtering — filtrar commits y archivos con fuzzy match
- [ ] Clone from URL — ahora solo abres repos existentes, clonar es el paso obvio
- [ ] Remote management — agregar, quitar, configurar remotes sin CLI
- [ ] Keyboard shortcuts —
Cmd+Sstage,Cmd+Entercommit,Cmd+Kcommand palette - [ ] Persistir repos abiertos entre sesiones
- [ ] Settings & preferences
- [ ] Releases firmados + notarizados cuando la app ya esté lista para usuarios no-devs
Pruébalo y ayúdame a darle forma
MIT, open source, todo en público:
→ github.com/poolcamacho/Maple
Si tienes macOS 14+ y Xcode 16+, git clone + Cmd+R y lo tienes corriendo en menos de un minuto. Abre issues si te topas con un flujo de Git que la UI vuelve torpe — prefiero arreglar eso a adivinar qué necesitan los power users.
Si quieres patrocinar el proyecto para que se mantenga gratis y activo, hay botón Sponsor arriba del repo, o directo en github.com/sponsors/poolcamacho. El patrocinio mantiene Maple libre para todos y me ayuda a cubrir el presupuesto de firma + notarización que voy a necesitar para shipear a usuarios no-devs.
El próximo post probablemente sea sobre interactive staging — es el problema de diseño más interesante de la lista. Nos leemos.



Top comments (0)