initial commit
This commit is contained in:
194
main.js
Normal file
194
main.js
Normal file
@@ -0,0 +1,194 @@
|
||||
const { app, BrowserWindow, ipcMain, dialog } = require('electron');
|
||||
const { exec } = require('child_process');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const Store = require('electron-store');
|
||||
const simpleGit = require('simple-git');
|
||||
const chokidar = require('chokidar');
|
||||
|
||||
const store = new Store({
|
||||
defaults: {
|
||||
folders: [],
|
||||
selected: null
|
||||
}
|
||||
});
|
||||
|
||||
// Map zum Speichern der Watcher pro Ordner
|
||||
const repoWatchers = new Map();
|
||||
|
||||
/**
|
||||
* Erstellt das BrowserWindow und lädt index.html.
|
||||
* Gibt das Window-Objekt zurück.
|
||||
*/
|
||||
function createWindow() {
|
||||
const win = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 600,
|
||||
title: 'auto-git',
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js'),
|
||||
contextIsolation: true
|
||||
}
|
||||
});
|
||||
win.loadFile('index.html');
|
||||
return win;
|
||||
}
|
||||
|
||||
/**
|
||||
* Startet einen File-Watcher auf .git/refs/heads/master,
|
||||
* sendet bei Änderungen 'repo-updated' an den Renderer.
|
||||
*/
|
||||
function watchRepo(folder, win) {
|
||||
const gitHead = path.join(folder, '.git', 'refs', 'heads', 'master');
|
||||
const watcher = chokidar.watch(gitHead, { ignoreInitial: true });
|
||||
watcher.on('change', () => {
|
||||
win.webContents.send('repo-updated', folder);
|
||||
});
|
||||
repoWatchers.set(folder, watcher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiiert ein Git-Repo in `folder`, falls noch nicht vorhanden,
|
||||
* und erzeugt einen Initial-Commit mit Timestamp.
|
||||
*/
|
||||
async function initGitRepo(folder) {
|
||||
const git = simpleGit(folder);
|
||||
const gitDir = path.join(folder, '.git');
|
||||
if (!fs.existsSync(gitDir)) {
|
||||
await git.init();
|
||||
const message = `Initial commit: ${new Date().toISOString()}`;
|
||||
const readmePath = path.join(folder, 'README.md');
|
||||
fs.writeFileSync(readmePath, `# Projekt in ${path.basename(folder)}\n`);
|
||||
await git.add('./*');
|
||||
await git.commit(message);
|
||||
}
|
||||
}
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const win = createWindow();
|
||||
|
||||
// 1) Beim Start bereits gespeicherte Ordner überwachen
|
||||
const folders = store.get('folders');
|
||||
folders.forEach(folder => {
|
||||
// nur watchen, wenn .git existiert
|
||||
if (fs.existsSync(path.join(folder, '.git', 'refs', 'heads', 'master'))) {
|
||||
watchRepo(folder, win);
|
||||
}
|
||||
});
|
||||
|
||||
// 2) IPC-Handler
|
||||
|
||||
// Liste aller Folders
|
||||
ipcMain.handle('get-folders', () => store.get('folders'));
|
||||
|
||||
// Ordner hinzufügen: Open-Dialog, init, Store-Update, watchen
|
||||
ipcMain.handle('add-folder', async () => {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory']
|
||||
});
|
||||
if (canceled || !filePaths[0]) {
|
||||
return store.get('folders');
|
||||
}
|
||||
const newFolder = filePaths[0];
|
||||
|
||||
// Repo initialisieren
|
||||
await initGitRepo(newFolder);
|
||||
|
||||
// Im Store ablegen
|
||||
const current = store.get('folders');
|
||||
if (!current.includes(newFolder)) {
|
||||
store.set('folders', [...current, newFolder]);
|
||||
}
|
||||
store.set('selected', newFolder);
|
||||
|
||||
// und watchen
|
||||
watchRepo(newFolder, win);
|
||||
|
||||
return store.get('folders');
|
||||
});
|
||||
|
||||
// Ordner entfernen: Watcher schließen, Store-Update
|
||||
ipcMain.handle('remove-folder', (_e, folder) => {
|
||||
const watcher = repoWatchers.get(folder);
|
||||
if (watcher) {
|
||||
watcher.close();
|
||||
repoWatchers.delete(folder);
|
||||
}
|
||||
const updated = store.get('folders').filter(f => f !== folder);
|
||||
store.set('folders', updated);
|
||||
if (store.get('selected') === folder) {
|
||||
store.set('selected', null);
|
||||
}
|
||||
return updated;
|
||||
});
|
||||
|
||||
// Selected
|
||||
ipcMain.handle('get-selected', () => store.get('selected'));
|
||||
ipcMain.handle('set-selected', (_e, folder) => {
|
||||
store.set('selected', folder);
|
||||
return folder;
|
||||
});
|
||||
|
||||
// Commits holen
|
||||
ipcMain.handle('get-commits', async (_e, folder) => {
|
||||
const git = simpleGit(folder);
|
||||
// alle Commits holen
|
||||
const log = await git.log(['--all']);
|
||||
// aktuellen HEAD‐Hash ermitteln
|
||||
const fullHead = (await git.revparse(['--verify', 'HEAD'])).trim();
|
||||
const head = fullHead.substring(0, 7);
|
||||
return {
|
||||
head,
|
||||
commits: log.all.map(c => ({
|
||||
hash: c.hash.substring(0, 7),
|
||||
date: c.date,
|
||||
message: c.message
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
// Diff
|
||||
ipcMain.handle('diff-commit', async (_e, folder, hash) => {
|
||||
const git = simpleGit(folder);
|
||||
return git.diff([`${hash}^!`]);
|
||||
});
|
||||
|
||||
// Revert
|
||||
ipcMain.handle('revert-commit', async (_e, folder, hash) => {
|
||||
const git = simpleGit(folder);
|
||||
await git.revert(hash, ['--no-edit']);
|
||||
});
|
||||
|
||||
/**
|
||||
* Checkt das Arbeitsverzeichnis auf exakt den Zustand von `hash` aus.
|
||||
*/
|
||||
ipcMain.handle('checkout-commit', async (_e, folder, hash) => {
|
||||
const git = simpleGit(folder);
|
||||
// clean mode: alle lokalen Veränderungen verwerfen
|
||||
await git.checkout([hash, '--force']);
|
||||
});
|
||||
|
||||
|
||||
// Snapshot
|
||||
ipcMain.handle('snapshot-commit', async (_e, folder, hash) => {
|
||||
const { canceled, filePaths } = await dialog.showOpenDialog({
|
||||
title: 'Ordner auswählen zum Speichern des Snapshots',
|
||||
properties: ['openDirectory']
|
||||
});
|
||||
if (canceled || !filePaths[0]) return;
|
||||
const outDir = filePaths[0];
|
||||
const baseName = path.basename(folder);
|
||||
const filePath = path.join(outDir, `${baseName}-${hash}.zip`);
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(
|
||||
`git -C "${folder}" archive --format zip --output "${filePath}" ${hash}`,
|
||||
err => err ? reject(err) : resolve(filePath)
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// clean up on exit
|
||||
app.on('window-all-closed', () => {
|
||||
if (process.platform !== 'darwin') app.quit();
|
||||
});
|
||||
Reference in New Issue
Block a user