Update Gitea-Sektion:
Branch-Auswahl
Dieser Commit ist enthalten in:
117
CHANGELOG.txt
117
CHANGELOG.txt
@ -1,6 +1,123 @@
|
|||||||
TASKMATE - CHANGELOG
|
TASKMATE - CHANGELOG
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
29.12.2025 - Gitea: Branch umbenennen + UI-Aufräumung
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
FEATURE: LOKALEN BRANCH UMBENENNEN
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- Neuer Stift-Button neben dem Branch-Dropdown
|
||||||
|
- Modal zum Umbenennen des aktuellen Branches (z.B. "master" → "main")
|
||||||
|
- Backend-Endpoint: POST /api/git/rename-branch/:projectId
|
||||||
|
|
||||||
|
UI-AUFRÄUMUNG
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- "Ahead/Behind" Anzeige entfernt (war verwirrend)
|
||||||
|
- Branch-Bereich aufgeräumt mit Rename-Button
|
||||||
|
|
||||||
|
ÄNDERUNGEN
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- frontend/index.html:
|
||||||
|
* Branch-Select-Group mit Rename-Button
|
||||||
|
* Neues Rename-Branch-Modal
|
||||||
|
* Ahead/Behind entfernt
|
||||||
|
- frontend/css/gitea.css:
|
||||||
|
* .branch-select-group, .btn-small, .btn-icon Styles
|
||||||
|
* .status-badge.ahead und #git-ahead-behind entfernt
|
||||||
|
- frontend/js/gitea.js:
|
||||||
|
* openRenameBranchModal(), executeRenameBranch() hinzugefügt
|
||||||
|
* Ahead/Behind-Rendering entfernt
|
||||||
|
- frontend/js/api.js: gitRenameBranch() hinzugefügt
|
||||||
|
- backend/services/gitService.js: renameBranch() Funktion
|
||||||
|
- backend/routes/git.js: rename-branch Endpoint
|
||||||
|
- frontend/sw.js: Cache-Version auf 123 erhöht
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
29.12.2025 - Gitea: Force-Push Option
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
FEATURE: FORCE-PUSH UM REMOTE ZU ÜBERSCHREIBEN
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- Problem: Push auf Remote-Branch mit unterschiedlicher Historie schlug fehl
|
||||||
|
(z.B. wenn Gitea eine README erstellt hat und man master→main pushen will)
|
||||||
|
- Lösung: Force-Push Option im Push-Modal hinzugefügt
|
||||||
|
|
||||||
|
ÄNDERUNGEN
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- frontend/index.html: Checkbox "Force Push (Remote überschreiben)" im Push-Modal
|
||||||
|
- frontend/css/gitea.css: .form-hint.warning Style für Warnung
|
||||||
|
- frontend/js/gitea.js: Force-Flag wird an API übergeben
|
||||||
|
- frontend/js/api.js: gitInitPush() akzeptiert force Parameter
|
||||||
|
- backend/services/gitService.js: pushWithUpstream() mit --force Flag
|
||||||
|
- backend/routes/git.js: init-push Endpoint akzeptiert force Parameter
|
||||||
|
- frontend/sw.js: Cache-Version auf 122 erhöht
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
29.12.2025 - Gitea: Bugfix Ziel-Branch Auswahl
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
BUGFIX: PUSH MIT ZIEL-BRANCH FUNKTIONIERTE NICHT
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- Problem: Bei Auswahl eines anderen Ziel-Branches (z.B. "main" statt "master")
|
||||||
|
wurde trotzdem der reguläre Push verwendet, der die Auswahl ignorierte
|
||||||
|
- Ursache: executePush() versuchte immer zuerst den normalen Push, der den
|
||||||
|
bestehenden Upstream verwendet, nicht den ausgewählten Target-Branch
|
||||||
|
|
||||||
|
LÖSUNG
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- frontend/js/gitea.js: executePush() Logik korrigiert
|
||||||
|
* Wenn Ziel-Branch != lokaler Branch → verwendet immer init-push mit Mapping
|
||||||
|
* Nur bei gleichem Branch-Namen → normaler Push mit Fallback
|
||||||
|
* Zeigt Info-Toast mit "Push von X nach Y"
|
||||||
|
- frontend/sw.js: Cache-Version auf 121 erhöht
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
29.12.2025 - Gitea: Ziel-Branch Auswahl beim Push
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
FEATURE: ZIEL-BRANCH AUSWAHL BEIM PUSH
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- Neues Push-Modal mit Branch-Auswahl vor dem Push
|
||||||
|
- Benutzer können jetzt wählen auf welchen Remote-Branch gepusht wird
|
||||||
|
- Z.B. lokaler Branch "master" kann als "main" auf Gitea gepusht werden
|
||||||
|
- Auswahl: "Gleicher Name wie lokal", "main", "master", "develop"
|
||||||
|
|
||||||
|
ÄNDERUNGEN
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- backend/services/gitService.js: pushWithUpstream() unterstützt jetzt targetBranch
|
||||||
|
* Branch-Mapping: `git push -u origin master:main` möglich
|
||||||
|
* Gibt sowohl localBranch als auch remoteBranch im Ergebnis zurück
|
||||||
|
- backend/routes/git.js: init-push Endpoint akzeptiert targetBranch Parameter
|
||||||
|
- frontend/js/api.js: gitInitPush() mit targetBranch Parameter
|
||||||
|
- frontend/js/gitea.js:
|
||||||
|
* Push-Button öffnet jetzt Modal statt direkt zu pushen
|
||||||
|
* Neue Methoden: openPushModal(), executePush()
|
||||||
|
- frontend/index.html: Neues Push-Modal mit Branch-Auswahl
|
||||||
|
- frontend/css/gitea.css: .form-static-value Style hinzugefügt
|
||||||
|
- frontend/sw.js: Cache-Version auf 120 erhöht
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
29.12.2025 - Gitea-Integration Bugfix: Branch-Mismatch
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
BUGFIX: DEFAULT-BRANCH MISMATCH ZWISCHEN GIT UND GITEA
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- Problem: Dateien wurden nach "master" gepusht, aber Gitea zeigte "main"
|
||||||
|
- Benutzer sahen nur die auto-generierte README statt ihrer gepushten Dateien
|
||||||
|
- Ursache: Lokaler Branch "master" != Gitea Default-Branch "main"
|
||||||
|
|
||||||
|
LÖSUNG
|
||||||
|
--------------------------------------------------------------------------------
|
||||||
|
- backend/services/giteaService.js: NEU - updateRepository() Funktion
|
||||||
|
* Erlaubt das Ändern des Default-Branch über Gitea PATCH API
|
||||||
|
* Unterstützt auch Beschreibung und Private-Status Änderungen
|
||||||
|
- backend/services/gitService.js: pushWithUpstream() gibt jetzt Branch-Namen zurück
|
||||||
|
- backend/routes/git.js: init-push Endpoint aktualisiert Default-Branch automatisch
|
||||||
|
* Nach erfolgreichem Push wird Gitea's Default-Branch auf den gepushten Branch gesetzt
|
||||||
|
* Owner/Repo wird automatisch aus der Repository-URL extrahiert
|
||||||
|
- frontend/sw.js: Cache-Version auf 119 erhöht
|
||||||
|
|
||||||
================================================================================
|
================================================================================
|
||||||
28.12.2025 - Gitea-Integration
|
28.12.2025 - Gitea-Integration
|
||||||
================================================================================
|
================================================================================
|
||||||
|
|||||||
179
GITEA_INTEGRATION_STATUS.md
Normale Datei
179
GITEA_INTEGRATION_STATUS.md
Normale Datei
@ -0,0 +1,179 @@
|
|||||||
|
# Gitea-Integration Status - TaskMate
|
||||||
|
|
||||||
|
**Datum:** 29.12.2024
|
||||||
|
**Status:** Vollständig implementiert - Bugfix angewendet
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Übersicht
|
||||||
|
|
||||||
|
Die Gitea-Integration für TaskMate ist vollständig implementiert. Der kritische Branch-Mismatch Bug wurde behoben - nach einem Push wird der Default-Branch in Gitea automatisch auf den gepushten Branch aktualisiert.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Was wurde bereits implementiert
|
||||||
|
|
||||||
|
### Backend (vollständig)
|
||||||
|
|
||||||
|
1. **`backend/server.js`** - Routes registriert:
|
||||||
|
```javascript
|
||||||
|
const gitRoutes = require('./routes/git');
|
||||||
|
const applicationsRoutes = require('./routes/applications');
|
||||||
|
const giteaRoutes = require('./routes/gitea');
|
||||||
|
|
||||||
|
app.use('/api/git', authenticateToken, csrfProtection, gitRoutes);
|
||||||
|
app.use('/api/applications', authenticateToken, csrfProtection, applicationsRoutes);
|
||||||
|
app.use('/api/gitea', authenticateToken, csrfProtection, giteaRoutes);
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **`backend/routes/gitea.js`** - NEU erstellt mit Endpoints:
|
||||||
|
- `GET /test` - Verbindung testen
|
||||||
|
- `GET /repositories` - Alle Repos auflisten
|
||||||
|
- `POST /repositories` - Neues Repo erstellen
|
||||||
|
- `GET /repositories/:owner/:repo` - Repo-Details
|
||||||
|
- `GET /repositories/:owner/:repo/branches` - Branches
|
||||||
|
- `GET /repositories/:owner/:repo/commits` - Commits
|
||||||
|
|
||||||
|
3. **`backend/routes/git.js`** - Erweitert mit:
|
||||||
|
- `POST /set-remote` - Remote setzen
|
||||||
|
- `POST /prepare-for-gitea` - Repository für Gitea vorbereiten
|
||||||
|
|
||||||
|
4. **`backend/services/gitService.js`** - Erweitert mit:
|
||||||
|
- `setRemote()` - Remote-URL setzen
|
||||||
|
- `prepareForGitea()` - Repository für Gitea vorbereiten (remote + initial commit)
|
||||||
|
- `pushWithUpstream()` - Push mit automatischer Branch-Erkennung
|
||||||
|
|
||||||
|
5. **`backend/services/giteaService.js`** - Vorhanden mit:
|
||||||
|
- `listRepositories()` - Organisation-Repositories auflisten
|
||||||
|
- `getRepository()` - Einzelnes Repo abrufen
|
||||||
|
- `createRepository()` - Neues Repo erstellen
|
||||||
|
- `updateRepository()` - Repo-Einstellungen aktualisieren (inkl. default_branch)
|
||||||
|
- `deleteRepository()` - Repo löschen
|
||||||
|
- `testConnection()` - Verbindung testen
|
||||||
|
- `getAuthenticatedCloneUrl()` - Clone-URL mit Token
|
||||||
|
|
||||||
|
6. **`Dockerfile`** - Git-Unterstützung hinzugefügt:
|
||||||
|
```dockerfile
|
||||||
|
RUN apk add --no-cache git
|
||||||
|
RUN git config --system --add safe.directory '*'
|
||||||
|
RUN git config --system user.email "taskmate@local" && \
|
||||||
|
git config --system user.name "TaskMate" && \
|
||||||
|
git config --system init.defaultBranch main
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend (vollständig)
|
||||||
|
|
||||||
|
1. **`frontend/js/api.js`** - Erweitert mit allen Gitea/Git API-Methoden
|
||||||
|
|
||||||
|
2. **`frontend/js/gitea.js`** - NEU erstellt:
|
||||||
|
- Kompletter GiteaManager mit allen Funktionen
|
||||||
|
- Konfigurationsansicht
|
||||||
|
- Status-Anzeige
|
||||||
|
- Git-Operationen (Fetch, Pull, Push, Commit)
|
||||||
|
- Commit-Historie
|
||||||
|
- Branch-Wechsel
|
||||||
|
|
||||||
|
3. **`frontend/css/gitea.css`** - NEU erstellt:
|
||||||
|
- Alle Styles für Gitea-Tab
|
||||||
|
|
||||||
|
4. **`frontend/index.html`** - Erweitert:
|
||||||
|
- Gitea-Tab in Navigation
|
||||||
|
- Gitea-View Container
|
||||||
|
- Commit-Modal
|
||||||
|
- Create-Repo-Modal
|
||||||
|
|
||||||
|
5. **`frontend/js/app.js`** - Integriert:
|
||||||
|
- GiteaManager Import
|
||||||
|
- View-Switching für Gitea-Tab
|
||||||
|
|
||||||
|
6. **`frontend/sw.js`** - Cache-Version erhöht
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Der gelöste Bug: Branch-Mismatch
|
||||||
|
|
||||||
|
### Problem (GELÖST)
|
||||||
|
- 146 Dateien wurden erfolgreich nach Gitea gepusht
|
||||||
|
- Die Dateien landeten im Branch `master`
|
||||||
|
- Gitea zeigte aber `main` als Standard-Branch an
|
||||||
|
- Benutzer sahen nur die auto-generierte README statt ihrer Dateien
|
||||||
|
|
||||||
|
### Ursache
|
||||||
|
1. Gitea erstellt Repositories mit `default_branch: 'main'` (Zeile 179 in giteaService.js)
|
||||||
|
2. Der lokale Git-Branch heißt aber `master`
|
||||||
|
3. Die Dateien wurden nach `master` gepusht
|
||||||
|
4. Gitea zeigte `main` an (leer bis auf README)
|
||||||
|
|
||||||
|
### Implementierte Lösung
|
||||||
|
|
||||||
|
1. **`updateRepository()` Funktion** zu `backend/services/giteaService.js` hinzugefügt
|
||||||
|
- Ändert den Default-Branch über Gitea PATCH API
|
||||||
|
- Unterstützt auch Beschreibung und Private-Status
|
||||||
|
|
||||||
|
2. **`pushWithUpstream()` Funktion** in `backend/services/gitService.js` erweitert
|
||||||
|
- Gibt jetzt den Branch-Namen im Ergebnis zurück
|
||||||
|
|
||||||
|
3. **`init-push` Endpoint** in `backend/routes/git.js` erweitert
|
||||||
|
- Nach erfolgreichem Push wird automatisch der Default-Branch in Gitea aktualisiert
|
||||||
|
- Owner/Repo wird aus der Repository-URL extrahiert
|
||||||
|
|
||||||
|
4. **Cache-Version** auf 119 erhöht in `frontend/sw.js`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Gelöste Probleme (zur Referenz)
|
||||||
|
|
||||||
|
1. **"git: not found"** → Git in Dockerfile installiert
|
||||||
|
2. **"dubious ownership in repository"** → `safe.directory '*'` konfiguriert
|
||||||
|
3. **"No configured push destination"** → `setRemote()` und `prepareForGitea()` hinzugefügt
|
||||||
|
4. **"src refspec main does not match any"** → Branch-Erkennung in `pushWithUpstream()` implementiert
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wichtige Dateien
|
||||||
|
|
||||||
|
| Datei | Status |
|
||||||
|
|-------|--------|
|
||||||
|
| `backend/server.js` | ✅ Fertig |
|
||||||
|
| `backend/routes/gitea.js` | ✅ Fertig |
|
||||||
|
| `backend/routes/git.js` | ✅ Fertig (inkl. auto Default-Branch Update) |
|
||||||
|
| `backend/services/gitService.js` | ✅ Fertig (inkl. Branch-Name Return) |
|
||||||
|
| `backend/services/giteaService.js` | ✅ Fertig (inkl. updateRepository) |
|
||||||
|
| `Dockerfile` | ✅ Fertig |
|
||||||
|
| `frontend/js/api.js` | ✅ Fertig |
|
||||||
|
| `frontend/js/gitea.js` | ✅ Fertig |
|
||||||
|
| `frontend/css/gitea.css` | ✅ Fertig |
|
||||||
|
| `frontend/index.html` | ✅ Fertig |
|
||||||
|
| `frontend/js/app.js` | ✅ Fertig |
|
||||||
|
| `frontend/sw.js` | ✅ Fertig (v119) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Nächste Schritte
|
||||||
|
|
||||||
|
1. ✅ `updateRepository()` zu `backend/services/giteaService.js` hinzugefügt
|
||||||
|
2. ✅ Funktion exportiert
|
||||||
|
3. ✅ Push-Flow erweitert, um Default-Branch nach Push zu aktualisieren
|
||||||
|
4. ✅ Docker-Container neu gebaut
|
||||||
|
5. ⏳ Testen: Projekt mit Gitea verknüpfen → Push → Dateien in Gitea sichtbar
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Plan-Datei
|
||||||
|
|
||||||
|
Der vollständige Implementierungsplan befindet sich in:
|
||||||
|
`C:\Users\hendr\.claude\plans\rosy-stargazing-scroll.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Befehle zum Testen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container neu bauen
|
||||||
|
docker compose down
|
||||||
|
docker compose build --no-cache
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# Browser öffnen
|
||||||
|
start chrome --incognito http://localhost:3000
|
||||||
|
```
|
||||||
@ -414,24 +414,58 @@ router.post('/set-remote/:projectId', (req, res) => {
|
|||||||
/**
|
/**
|
||||||
* POST /api/git/init-push/:projectId
|
* POST /api/git/init-push/:projectId
|
||||||
* Initialen Push mit Upstream-Tracking
|
* Initialen Push mit Upstream-Tracking
|
||||||
|
* Body: { targetBranch?: string, force?: boolean }
|
||||||
|
* - targetBranch: Optional - Ziel-Branch auf Remote (z.B. "main" auch wenn lokal "master")
|
||||||
|
* - force: Optional - Force-Push um Remote zu überschreiben
|
||||||
*/
|
*/
|
||||||
router.post('/init-push/:projectId', (req, res) => {
|
router.post('/init-push/:projectId', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { projectId } = req.params;
|
const { projectId } = req.params;
|
||||||
const { branch } = req.body;
|
const { targetBranch, force } = req.body;
|
||||||
const application = getApplicationForProject(projectId);
|
const application = getApplicationForProject(projectId);
|
||||||
|
|
||||||
if (!application) {
|
if (!application) {
|
||||||
return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' });
|
return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentBranch = branch || 'main';
|
// targetBranch kann null sein - dann wird der lokale Branch-Name verwendet
|
||||||
const result = gitService.pushWithUpstream(application.local_path, currentBranch);
|
// force: boolean - überschreibt Remote-Branch bei Konflikten
|
||||||
|
const result = gitService.pushWithUpstream(application.local_path, targetBranch || null, 'origin', force === true);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// Sync-Zeitpunkt aktualisieren
|
// Sync-Zeitpunkt aktualisieren
|
||||||
const db = getDb();
|
const db = getDb();
|
||||||
db.prepare('UPDATE applications SET last_sync = CURRENT_TIMESTAMP WHERE project_id = ?').run(projectId);
|
db.prepare('UPDATE applications SET last_sync = CURRENT_TIMESTAMP WHERE project_id = ?').run(projectId);
|
||||||
|
|
||||||
|
// Default-Branch in Gitea aktualisieren wenn der gepushte Branch abweicht
|
||||||
|
if (application.gitea_repo_url && result.branch) {
|
||||||
|
try {
|
||||||
|
// Owner/Repo aus URL extrahieren (z.B. https://gitea.../AegisSight/TaskMate.git)
|
||||||
|
const urlMatch = application.gitea_repo_url.match(/\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
|
||||||
|
if (urlMatch) {
|
||||||
|
const owner = urlMatch[1];
|
||||||
|
const repoName = urlMatch[2];
|
||||||
|
const actualBranch = result.branch;
|
||||||
|
|
||||||
|
// Default-Branch in Gitea setzen
|
||||||
|
const updateResult = await giteaService.updateRepository(owner, repoName, {
|
||||||
|
defaultBranch: actualBranch
|
||||||
|
});
|
||||||
|
|
||||||
|
if (updateResult.success) {
|
||||||
|
logger.info(`Default-Branch in Gitea auf '${actualBranch}' gesetzt für ${owner}/${repoName}`);
|
||||||
|
result.giteaUpdated = true;
|
||||||
|
result.defaultBranch = actualBranch;
|
||||||
|
} else {
|
||||||
|
logger.warn(`Konnte Default-Branch in Gitea nicht aktualisieren: ${updateResult.error}`);
|
||||||
|
result.giteaUpdated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (giteaError) {
|
||||||
|
logger.warn('Fehler beim Aktualisieren des Default-Branch in Gitea:', giteaError);
|
||||||
|
result.giteaUpdated = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json(result);
|
res.json(result);
|
||||||
@ -441,4 +475,31 @@ router.post('/init-push/:projectId', (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/git/rename-branch/:projectId
|
||||||
|
* Branch umbenennen
|
||||||
|
* Body: { oldName: string, newName: string }
|
||||||
|
*/
|
||||||
|
router.post('/rename-branch/:projectId', (req, res) => {
|
||||||
|
try {
|
||||||
|
const { projectId } = req.params;
|
||||||
|
const { oldName, newName } = req.body;
|
||||||
|
const application = getApplicationForProject(projectId);
|
||||||
|
|
||||||
|
if (!application) {
|
||||||
|
return res.status(404).json({ error: 'Keine Anwendung für dieses Projekt konfiguriert' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!oldName || !newName) {
|
||||||
|
return res.status(400).json({ error: 'oldName und newName sind erforderlich' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = gitService.renameBranch(application.local_path, oldName, newName);
|
||||||
|
res.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Fehler beim Umbenennen des Branches:', error);
|
||||||
|
res.status(500).json({ error: 'Serverfehler' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
|
|||||||
@ -443,7 +443,7 @@ function hasRemote(localPath, remoteName = 'origin') {
|
|||||||
/**
|
/**
|
||||||
* Initialen Push mit Upstream-Tracking
|
* Initialen Push mit Upstream-Tracking
|
||||||
*/
|
*/
|
||||||
function pushWithUpstream(localPath, branch = null, remoteName = 'origin') {
|
function pushWithUpstream(localPath, targetBranch = null, remoteName = 'origin', force = false) {
|
||||||
if (!isGitRepository(localPath)) {
|
if (!isGitRepository(localPath)) {
|
||||||
return { success: false, error: 'Kein Git-Repository' };
|
return { success: false, error: 'Kein Git-Repository' };
|
||||||
}
|
}
|
||||||
@ -477,11 +477,12 @@ function pushWithUpstream(localPath, branch = null, remoteName = 'origin') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Aktuellen Branch ermitteln NACH dem Commit
|
// Aktuellen (lokalen) Branch ermitteln NACH dem Commit
|
||||||
|
let localBranch = null;
|
||||||
const branchResult = execGitCommand('git branch --show-current', localPath);
|
const branchResult = execGitCommand('git branch --show-current', localPath);
|
||||||
if (branchResult.success && branchResult.output) {
|
if (branchResult.success && branchResult.output) {
|
||||||
branch = branchResult.output;
|
localBranch = branchResult.output;
|
||||||
logger.info(`Aktueller Branch: ${branch}`);
|
logger.info(`Lokaler Branch: ${localBranch}`);
|
||||||
} else {
|
} else {
|
||||||
// Fallback: Branch aus git branch auslesen
|
// Fallback: Branch aus git branch auslesen
|
||||||
const branchListResult = execGitCommand('git branch', localPath);
|
const branchListResult = execGitCommand('git branch', localPath);
|
||||||
@ -491,29 +492,46 @@ function pushWithUpstream(localPath, branch = null, remoteName = 'origin') {
|
|||||||
const lines = branchListResult.output.split('\n');
|
const lines = branchListResult.output.split('\n');
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.includes('*')) {
|
if (line.includes('*')) {
|
||||||
branch = line.replace('*', '').trim();
|
localBranch = line.replace('*', '').trim();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!branch) {
|
if (!localBranch) {
|
||||||
// Letzter Fallback: Versuche den Branch zu erstellen
|
// Letzter Fallback: Versuche den Branch zu erstellen
|
||||||
logger.info('Kein Branch gefunden, erstelle main');
|
logger.info('Kein Branch gefunden, erstelle main');
|
||||||
execGitCommand('git checkout -b main', localPath);
|
execGitCommand('git checkout -b main', localPath);
|
||||||
branch = 'main';
|
localBranch = 'main';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`Push auf Branch: ${branch}`);
|
// Wenn kein Ziel-Branch angegeben, verwende lokalen Branch-Namen
|
||||||
|
const remoteBranch = targetBranch || localBranch;
|
||||||
|
|
||||||
|
logger.info(`Push: lokaler Branch '${localBranch}' → Remote Branch '${remoteBranch}'${force ? ' (FORCE)' : ''}`);
|
||||||
|
|
||||||
// Push mit -u für Upstream-Tracking
|
// Push mit -u für Upstream-Tracking
|
||||||
const pushResult = execGitCommand(`git push -u ${remoteName} ${branch}`, localPath, { timeout: 120000 });
|
// Format: git push -u origin localBranch:remoteBranch
|
||||||
|
const forceFlag = force ? ' --force' : '';
|
||||||
|
let pushCommand;
|
||||||
|
if (localBranch === remoteBranch) {
|
||||||
|
pushCommand = `git push -u${forceFlag} ${remoteName} ${localBranch}`;
|
||||||
|
} else {
|
||||||
|
// Branch-Mapping: lokalen Branch auf anderen Remote-Branch pushen
|
||||||
|
pushCommand = `git push -u${forceFlag} ${remoteName} ${localBranch}:${remoteBranch}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pushResult = execGitCommand(pushCommand, localPath, { timeout: 120000 });
|
||||||
|
|
||||||
if (!pushResult.success) {
|
if (!pushResult.success) {
|
||||||
logger.error(`Push fehlgeschlagen: ${pushResult.error}`);
|
logger.error(`Push fehlgeschlagen: ${pushResult.error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Branch-Namen im Ergebnis inkludieren für Default-Branch Aktualisierung
|
||||||
|
pushResult.localBranch = localBranch;
|
||||||
|
pushResult.branch = remoteBranch; // Remote-Branch für Gitea Default-Branch
|
||||||
|
|
||||||
return pushResult;
|
return pushResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,6 +579,39 @@ function prepareForGitea(localPath, remoteUrl, options = {}) {
|
|||||||
return { success: true, message: 'Repository für Gitea vorbereitet' };
|
return { success: true, message: 'Repository für Gitea vorbereitet' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Branch umbenennen
|
||||||
|
*/
|
||||||
|
function renameBranch(localPath, oldName, newName) {
|
||||||
|
if (!isGitRepository(localPath)) {
|
||||||
|
return { success: false, error: 'Kein Git-Repository' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validiere neuen Branch-Namen
|
||||||
|
if (!newName || !/^[a-zA-Z0-9_\-]+$/.test(newName)) {
|
||||||
|
return { success: false, error: 'Ungültiger Branch-Name. Nur Buchstaben, Zahlen, Unterstriche und Bindestriche erlaubt.' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prüfe ob wir auf dem zu umbenennenden Branch sind
|
||||||
|
const branchResult = execGitCommand('git branch --show-current', localPath);
|
||||||
|
const currentBranch = branchResult.success ? branchResult.output : null;
|
||||||
|
|
||||||
|
if (currentBranch !== oldName) {
|
||||||
|
return { success: false, error: `Bitte wechsle zuerst zum Branch "${oldName}" bevor du ihn umbenennst.` };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Branch umbenennen
|
||||||
|
const renameResult = execGitCommand(`git branch -m ${oldName} ${newName}`, localPath);
|
||||||
|
|
||||||
|
if (!renameResult.success) {
|
||||||
|
logger.error(`Branch umbenennen fehlgeschlagen: ${renameResult.error}`);
|
||||||
|
return renameResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`Branch umbenannt: ${oldName} → ${newName}`);
|
||||||
|
return { success: true, oldName, newName, message: `Branch "${oldName}" zu "${newName}" umbenannt` };
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
windowsToContainerPath,
|
windowsToContainerPath,
|
||||||
containerToWindowsPath,
|
containerToWindowsPath,
|
||||||
@ -581,5 +632,6 @@ module.exports = {
|
|||||||
setRemote,
|
setRemote,
|
||||||
hasRemote,
|
hasRemote,
|
||||||
pushWithUpstream,
|
pushWithUpstream,
|
||||||
prepareForGitea
|
prepareForGitea,
|
||||||
|
renameBranch
|
||||||
};
|
};
|
||||||
|
|||||||
@ -284,12 +284,44 @@ function getSafeCloneUrl(cloneUrl) {
|
|||||||
return cloneUrl.replace(/https:\/\/[^@]+@/, 'https://');
|
return cloneUrl.replace(/https:\/\/[^@]+@/, 'https://');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository-Einstellungen aktualisieren (z.B. default_branch ändern)
|
||||||
|
*/
|
||||||
|
async function updateRepository(owner, repo, options = {}) {
|
||||||
|
try {
|
||||||
|
const updateData = {};
|
||||||
|
if (options.defaultBranch) updateData.default_branch = options.defaultBranch;
|
||||||
|
if (options.description !== undefined) updateData.description = options.description;
|
||||||
|
if (options.private !== undefined) updateData.private = options.private;
|
||||||
|
|
||||||
|
const repoData = await giteaFetch(`/repos/${owner}/${repo}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
body: JSON.stringify(updateData)
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Repository ${owner}/${repo} aktualisiert (default_branch: ${repoData.default_branch})`);
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
repository: {
|
||||||
|
id: repoData.id,
|
||||||
|
name: repoData.name,
|
||||||
|
fullName: repoData.full_name,
|
||||||
|
defaultBranch: repoData.default_branch
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Fehler beim Aktualisieren des Repositories ${owner}/${repo}:`, error);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
listRepositories,
|
listRepositories,
|
||||||
getRepository,
|
getRepository,
|
||||||
getRepositoryBranches,
|
getRepositoryBranches,
|
||||||
getRepositoryCommits,
|
getRepositoryCommits,
|
||||||
createRepository,
|
createRepository,
|
||||||
|
updateRepository,
|
||||||
deleteRepository,
|
deleteRepository,
|
||||||
getCurrentUser,
|
getCurrentUser,
|
||||||
testConnection,
|
testConnection,
|
||||||
|
|||||||
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
0
backups/backup_2025-12-29T18-03-28-634Z.db-wal
Normale Datei
0
backups/backup_2025-12-29T18-03-28-634Z.db-wal
Normale Datei
Binäre Datei nicht angezeigt.
0
backups/backup_2025-12-29T18-30-58-187Z.db-wal
Normale Datei
0
backups/backup_2025-12-29T18-30-58-187Z.db-wal
Normale Datei
Binäre Datei nicht angezeigt.
0
backups/backup_2025-12-29T18-36-12-724Z.db-wal
Normale Datei
0
backups/backup_2025-12-29T18-36-12-724Z.db-wal
Normale Datei
Binäre Datei nicht angezeigt.
0
backups/backup_2025-12-29T18-46-49-826Z.db-wal
Normale Datei
0
backups/backup_2025-12-29T18-46-49-826Z.db-wal
Normale Datei
BIN
backups/backup_2025-12-29T19-01-37-753Z.db
Normale Datei
BIN
backups/backup_2025-12-29T19-01-37-753Z.db
Normale Datei
Binäre Datei nicht angezeigt.
BIN
backups/backup_2025-12-29T19-01-37-753Z.db-wal
Normale Datei
BIN
backups/backup_2025-12-29T19-01-37-753Z.db-wal
Normale Datei
Binäre Datei nicht angezeigt.
BIN
data/taskmate.db
BIN
data/taskmate.db
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
Binäre Datei nicht angezeigt.
@ -225,6 +225,12 @@
|
|||||||
letter-spacing: 0.05em;
|
letter-spacing: 0.05em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.branch-select-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--spacing-2);
|
||||||
|
}
|
||||||
|
|
||||||
.branch-select {
|
.branch-select {
|
||||||
padding: var(--spacing-2) var(--spacing-3);
|
padding: var(--spacing-2) var(--spacing-3);
|
||||||
border-radius: var(--radius-md);
|
border-radius: var(--radius-md);
|
||||||
@ -233,6 +239,7 @@
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.branch-select:focus {
|
.branch-select:focus {
|
||||||
@ -240,6 +247,24 @@
|
|||||||
border-color: var(--primary);
|
border-color: var(--primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.btn-small {
|
||||||
|
padding: var(--spacing-1) var(--spacing-2);
|
||||||
|
font-size: var(--text-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--spacing-1);
|
||||||
|
min-width: 28px;
|
||||||
|
min-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon svg {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.status-badge {
|
.status-badge {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -259,11 +284,6 @@
|
|||||||
color: var(--warning);
|
color: var(--warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-badge.ahead {
|
|
||||||
background: rgba(59, 130, 246, 0.1);
|
|
||||||
color: var(--info);
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-badge.error {
|
.status-badge.error {
|
||||||
background: rgba(239, 68, 68, 0.1);
|
background: rgba(239, 68, 68, 0.1);
|
||||||
color: var(--error);
|
color: var(--error);
|
||||||
@ -275,12 +295,6 @@
|
|||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
#git-ahead-behind {
|
|
||||||
font-size: var(--text-sm);
|
|
||||||
color: var(--text-primary);
|
|
||||||
font-family: monospace;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* =============================================================================
|
/* =============================================================================
|
||||||
OPERATIONS PANEL
|
OPERATIONS PANEL
|
||||||
============================================================================= */
|
============================================================================= */
|
||||||
@ -511,6 +525,20 @@
|
|||||||
color: var(--error);
|
color: var(--error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-hint.warning {
|
||||||
|
color: var(--warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-static-value {
|
||||||
|
padding: var(--spacing-2) var(--spacing-3);
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border-color);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
.checkbox-label {
|
.checkbox-label {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@ -606,9 +606,14 @@
|
|||||||
<div class="status-grid">
|
<div class="status-grid">
|
||||||
<div class="status-item">
|
<div class="status-item">
|
||||||
<span class="status-label">Branch</span>
|
<span class="status-label">Branch</span>
|
||||||
<select id="branch-select" class="branch-select">
|
<div class="branch-select-group">
|
||||||
<!-- Branches dynamisch -->
|
<select id="branch-select" class="branch-select">
|
||||||
</select>
|
<!-- Branches dynamisch -->
|
||||||
|
</select>
|
||||||
|
<button id="btn-rename-branch" class="btn btn-small btn-icon" title="Branch umbenennen">
|
||||||
|
<svg viewBox="0 0 24 24" width="16" height="16"><path d="M17 3a2.85 2.85 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5L17 3z" stroke="currentColor" stroke-width="2" fill="none"/></svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-item">
|
<div class="status-item">
|
||||||
<span class="status-label">Status</span>
|
<span class="status-label">Status</span>
|
||||||
@ -618,10 +623,6 @@
|
|||||||
<span class="status-label">Änderungen</span>
|
<span class="status-label">Änderungen</span>
|
||||||
<span id="git-changes-count" class="changes-count">0</span>
|
<span id="git-changes-count" class="changes-count">0</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="status-item">
|
|
||||||
<span class="status-label">Ahead / Behind</span>
|
|
||||||
<span id="git-ahead-behind">- / -</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -1163,6 +1164,70 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Git Branch Rename Modal -->
|
||||||
|
<div id="git-rename-branch-modal" class="modal modal-small hidden">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>Branch umbenennen</h2>
|
||||||
|
<button class="modal-close" data-close-modal>×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="git-rename-branch-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Aktueller Branch</label>
|
||||||
|
<div id="rename-current-branch" class="form-static-value">master</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="rename-new-branch">Neuer Name *</label>
|
||||||
|
<input type="text" id="rename-new-branch" required
|
||||||
|
placeholder="z.B. main" pattern="[a-zA-Z0-9_\-]+"
|
||||||
|
title="Nur Buchstaben, Zahlen, Unterstriche und Bindestriche">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||||
|
<button type="submit" form="git-rename-branch-form" class="btn btn-primary">Umbenennen</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Git Push Modal -->
|
||||||
|
<div id="git-push-modal" class="modal modal-small hidden">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h2>Push zu Gitea</h2>
|
||||||
|
<button class="modal-close" data-close-modal>×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="git-push-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>Lokaler Branch</label>
|
||||||
|
<div id="push-local-branch" class="form-static-value">wird ermittelt...</div>
|
||||||
|
<span class="form-hint">Der aktuelle lokale Branch wird automatisch erkannt</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="push-target-branch">Ziel-Branch auf Remote</label>
|
||||||
|
<select id="push-target-branch">
|
||||||
|
<option value="">Gleicher Name wie lokal</option>
|
||||||
|
<option value="main">main</option>
|
||||||
|
<option value="master">master</option>
|
||||||
|
<option value="develop">develop</option>
|
||||||
|
</select>
|
||||||
|
<span class="form-hint">Wählen Sie den Branch-Namen auf Gitea</span>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="checkbox-label">
|
||||||
|
<input type="checkbox" id="push-force">
|
||||||
|
Force Push (Remote überschreiben)
|
||||||
|
</label>
|
||||||
|
<span class="form-hint warning">Achtung: Überschreibt alle Änderungen auf dem Remote-Branch!</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-close-modal>Abbrechen</button>
|
||||||
|
<button type="submit" form="git-push-form" class="btn btn-primary">Push starten</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Create Repository Modal -->
|
<!-- Create Repository Modal -->
|
||||||
<div id="create-repo-modal" class="modal modal-small hidden">
|
<div id="create-repo-modal" class="modal modal-small hidden">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
|
|||||||
@ -820,8 +820,12 @@ class ApiClient {
|
|||||||
return this.post(`/git/set-remote/${projectId}`, { repoUrl });
|
return this.post(`/git/set-remote/${projectId}`, { repoUrl });
|
||||||
}
|
}
|
||||||
|
|
||||||
async gitInitPush(projectId, branch = 'main') {
|
async gitInitPush(projectId, targetBranch = null, force = false) {
|
||||||
return this.post(`/git/init-push/${projectId}`, { branch });
|
return this.post(`/git/init-push/${projectId}`, { targetBranch, force });
|
||||||
|
}
|
||||||
|
|
||||||
|
async gitRenameBranch(projectId, oldName, newName) {
|
||||||
|
return this.post(`/git/rename-branch/${projectId}`, { oldName, newName });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -72,6 +72,13 @@ class GiteaManager {
|
|||||||
|
|
||||||
// Commit Modal
|
// Commit Modal
|
||||||
$('#git-commit-form')?.addEventListener('submit', (e) => this.handleCommit(e));
|
$('#git-commit-form')?.addEventListener('submit', (e) => this.handleCommit(e));
|
||||||
|
|
||||||
|
// Push Modal
|
||||||
|
$('#git-push-form')?.addEventListener('submit', (e) => this.executePush(e));
|
||||||
|
|
||||||
|
// Branch umbenennen
|
||||||
|
$('#btn-rename-branch')?.addEventListener('click', () => this.openRenameBranchModal());
|
||||||
|
$('#git-rename-branch-form')?.addEventListener('submit', (e) => this.executeRenameBranch(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribeToStore() {
|
subscribeToStore() {
|
||||||
@ -406,27 +413,76 @@ class GiteaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handlePush() {
|
handlePush() {
|
||||||
|
// Öffne das Push-Modal zur Branch-Auswahl
|
||||||
|
this.openPushModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
openPushModal() {
|
||||||
|
const modal = $('#git-push-modal');
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
// Aktuellen lokalen Branch anzeigen
|
||||||
|
const currentBranch = $('#branch-select')?.value || this.gitStatus?.branch || 'unbekannt';
|
||||||
|
$('#push-local-branch').textContent = currentBranch;
|
||||||
|
|
||||||
|
// Ziel-Branch zurücksetzen (Standard: gleicher Name wie lokal)
|
||||||
|
$('#push-target-branch').value = '';
|
||||||
|
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
modal.classList.add('visible');
|
||||||
|
$('#modal-overlay')?.classList.remove('hidden');
|
||||||
|
|
||||||
|
store.openModal('git-push-modal');
|
||||||
|
}
|
||||||
|
|
||||||
|
async executePush(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
const projectId = store.get('currentProjectId');
|
const projectId = store.get('currentProjectId');
|
||||||
if (!projectId) return;
|
if (!projectId) return;
|
||||||
|
|
||||||
|
// Modal schließen
|
||||||
|
const modal = $('#git-push-modal');
|
||||||
|
modal?.classList.add('hidden');
|
||||||
|
modal?.classList.remove('visible');
|
||||||
|
$('#modal-overlay')?.classList.add('hidden');
|
||||||
|
store.closeModal();
|
||||||
|
|
||||||
this.setOperationLoading('push', true);
|
this.setOperationLoading('push', true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const branch = $('#branch-select')?.value || 'main';
|
const targetBranch = $('#push-target-branch')?.value || null;
|
||||||
let result = await api.gitPush(projectId, branch);
|
const forcePush = $('#push-force')?.checked || false;
|
||||||
|
const localBranch = $('#branch-select')?.value || this.gitStatus?.branch || 'master';
|
||||||
|
|
||||||
// Falls Push wegen fehlendem Upstream/Remote fehlschlägt, versuche init-push
|
let result;
|
||||||
if (!result.success && result.error &&
|
|
||||||
(result.error.includes('No configured push destination') ||
|
// Wenn ein anderer Target-Branch ausgewählt wurde oder Force-Push, verwende init-push
|
||||||
result.error.includes('no upstream') ||
|
if ((targetBranch && targetBranch !== localBranch) || forcePush) {
|
||||||
result.error.includes('Kein Remote'))) {
|
const forceText = forcePush ? ' (Force)' : '';
|
||||||
this.showToast('Kein Upstream konfiguriert, führe initialen Push durch...', 'info');
|
const branchText = targetBranch && targetBranch !== localBranch
|
||||||
result = await api.gitInitPush(projectId, branch);
|
? `Push von "${localBranch}" nach "${targetBranch}"${forceText}...`
|
||||||
|
: `Push nach "${localBranch}"${forceText}...`;
|
||||||
|
this.showToast(branchText, 'info');
|
||||||
|
result = await api.gitInitPush(projectId, targetBranch, forcePush);
|
||||||
|
} else {
|
||||||
|
// Normaler Push (gleicher Branch-Name)
|
||||||
|
result = await api.gitPush(projectId, localBranch);
|
||||||
|
|
||||||
|
// Falls Push wegen fehlendem Upstream/Remote fehlschlägt, versuche init-push
|
||||||
|
if (!result.success && result.error &&
|
||||||
|
(result.error.includes('No configured push destination') ||
|
||||||
|
result.error.includes('no upstream') ||
|
||||||
|
result.error.includes('Kein Remote'))) {
|
||||||
|
this.showToast('Kein Upstream konfiguriert, führe initialen Push durch...', 'info');
|
||||||
|
result = await api.gitInitPush(projectId, targetBranch, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
this.showToast('Push erfolgreich', 'success');
|
const pushedBranch = result.branch || targetBranch || localBranch;
|
||||||
|
this.showToast(`Push erfolgreich nach Branch "${pushedBranch}"`, 'success');
|
||||||
await this.loadGitData();
|
await this.loadGitData();
|
||||||
} else {
|
} else {
|
||||||
this.showToast(result.error || 'Push fehlgeschlagen', 'error');
|
this.showToast(result.error || 'Push fehlgeschlagen', 'error');
|
||||||
@ -438,6 +494,64 @@ class GiteaManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
openRenameBranchModal() {
|
||||||
|
const modal = $('#git-rename-branch-modal');
|
||||||
|
if (!modal) return;
|
||||||
|
|
||||||
|
const currentBranch = $('#branch-select')?.value || this.gitStatus?.branch || 'master';
|
||||||
|
$('#rename-current-branch').textContent = currentBranch;
|
||||||
|
$('#rename-new-branch').value = '';
|
||||||
|
|
||||||
|
modal.classList.remove('hidden');
|
||||||
|
modal.classList.add('visible');
|
||||||
|
$('#modal-overlay')?.classList.remove('hidden');
|
||||||
|
|
||||||
|
store.openModal('git-rename-branch-modal');
|
||||||
|
|
||||||
|
// Focus auf das Eingabefeld
|
||||||
|
setTimeout(() => $('#rename-new-branch')?.focus(), 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
async executeRenameBranch(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const projectId = store.get('currentProjectId');
|
||||||
|
if (!projectId) return;
|
||||||
|
|
||||||
|
const oldName = $('#rename-current-branch')?.textContent;
|
||||||
|
const newName = $('#rename-new-branch')?.value.trim();
|
||||||
|
|
||||||
|
if (!newName) {
|
||||||
|
this.showToast('Bitte geben Sie einen neuen Branch-Namen ein', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldName === newName) {
|
||||||
|
this.showToast('Der neue Name ist identisch mit dem aktuellen', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modal schließen
|
||||||
|
const modal = $('#git-rename-branch-modal');
|
||||||
|
modal?.classList.add('hidden');
|
||||||
|
modal?.classList.remove('visible');
|
||||||
|
$('#modal-overlay')?.classList.add('hidden');
|
||||||
|
store.closeModal();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await api.gitRenameBranch(projectId, oldName, newName);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
this.showToast(`Branch umbenannt: "${oldName}" → "${newName}"`, 'success');
|
||||||
|
await this.loadGitData();
|
||||||
|
} else {
|
||||||
|
this.showToast(result.error || 'Umbenennen fehlgeschlagen', 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showToast(error.message || 'Umbenennen fehlgeschlagen', 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
openCommitModal() {
|
openCommitModal() {
|
||||||
const modal = $('#git-commit-modal');
|
const modal = $('#git-commit-modal');
|
||||||
if (!modal) return;
|
if (!modal) return;
|
||||||
@ -610,13 +724,11 @@ class GiteaManager {
|
|||||||
renderStatus() {
|
renderStatus() {
|
||||||
const statusBadge = $('#git-status-indicator');
|
const statusBadge = $('#git-status-indicator');
|
||||||
const changesCount = $('#git-changes-count');
|
const changesCount = $('#git-changes-count');
|
||||||
const aheadBehind = $('#git-ahead-behind');
|
|
||||||
|
|
||||||
if (!this.gitStatus) {
|
if (!this.gitStatus) {
|
||||||
statusBadge.textContent = 'Fehler';
|
statusBadge.textContent = 'Fehler';
|
||||||
statusBadge.className = 'status-badge error';
|
statusBadge.className = 'status-badge error';
|
||||||
changesCount.textContent = '-';
|
changesCount.textContent = '-';
|
||||||
aheadBehind.textContent = '- / -';
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -630,9 +742,6 @@ class GiteaManager {
|
|||||||
} else if (this.gitStatus.hasChanges) {
|
} else if (this.gitStatus.hasChanges) {
|
||||||
statusBadge.textContent = 'Geändert';
|
statusBadge.textContent = 'Geändert';
|
||||||
statusBadge.className = 'status-badge dirty';
|
statusBadge.className = 'status-badge dirty';
|
||||||
} else if (this.gitStatus.ahead > 0) {
|
|
||||||
statusBadge.textContent = 'Voraus';
|
|
||||||
statusBadge.className = 'status-badge ahead';
|
|
||||||
} else {
|
} else {
|
||||||
statusBadge.textContent = 'OK';
|
statusBadge.textContent = 'OK';
|
||||||
statusBadge.className = 'status-badge clean';
|
statusBadge.className = 'status-badge clean';
|
||||||
@ -641,11 +750,6 @@ class GiteaManager {
|
|||||||
// Änderungen
|
// Änderungen
|
||||||
const changes = this.gitStatus.changes || [];
|
const changes = this.gitStatus.changes || [];
|
||||||
changesCount.textContent = changes.length;
|
changesCount.textContent = changes.length;
|
||||||
|
|
||||||
// Ahead/Behind
|
|
||||||
const ahead = this.gitStatus.ahead || 0;
|
|
||||||
const behind = this.gitStatus.behind || 0;
|
|
||||||
aheadBehind.textContent = `${ahead} / ${behind}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBranches() {
|
renderBranches() {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
* Offline support and caching
|
* Offline support and caching
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const CACHE_VERSION = '118';
|
const CACHE_VERSION = '123';
|
||||||
const CACHE_NAME = 'taskmate-v' + CACHE_VERSION;
|
const CACHE_NAME = 'taskmate-v' + CACHE_VERSION;
|
||||||
const STATIC_CACHE_NAME = 'taskmate-static-v' + CACHE_VERSION;
|
const STATIC_CACHE_NAME = 'taskmate-static-v' + CACHE_VERSION;
|
||||||
const DYNAMIC_CACHE_NAME = 'taskmate-dynamic-v' + CACHE_VERSION;
|
const DYNAMIC_CACHE_NAME = 'taskmate-dynamic-v' + CACHE_VERSION;
|
||||||
|
|||||||
3255
logs/app.log
3255
logs/app.log
Datei-Diff unterdrückt, da er zu groß ist
Diff laden
In neuem Issue referenzieren
Einen Benutzer sperren