Refactor and enhance gitignore handling in auto-git script
This commit is contained in:
145
main.js
145
main.js
@@ -314,81 +314,89 @@ const IGNORED_NAMES = [
|
|||||||
// Maven Wrapper
|
// Maven Wrapper
|
||||||
'mvnw.cmd', 'mvnw', '.mvn/wrapper/maven-wrapper.jar'
|
'mvnw.cmd', 'mvnw', '.mvn/wrapper/maven-wrapper.jar'
|
||||||
];
|
];
|
||||||
|
/**
|
||||||
|
* Liest die .gitignore im Repo (falls vorhanden) und gibt eine "ignore"-Instanz
|
||||||
|
* zurück, die alle aktuellen Einträge kennt. Sollte keine .gitignore existieren,
|
||||||
|
* wird eine leere Instanz zurückgegeben.
|
||||||
|
*/
|
||||||
|
async function loadGitignoreFilter(folderPath) {
|
||||||
|
const gitignorePath = path.join(folderPath, '.gitignore');
|
||||||
|
let raw;
|
||||||
|
try {
|
||||||
|
raw = await fs.readFile(gitignorePath, 'utf8');
|
||||||
|
} catch (e) {
|
||||||
|
// Wenn keine .gitignore existiert, gibt's eine leere Instanz
|
||||||
|
raw = '';
|
||||||
|
}
|
||||||
|
// ignore() versteht Git-Ignore-Syntax (Kommentare, Leerzeilen, Glob-Pattern etc.)
|
||||||
|
const ig = ignore();
|
||||||
|
ig.add(raw.split(/\r?\n/));
|
||||||
|
return ig;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Überprüft, ob ein gegebener Pfad einem Eintrag in IGNORED_NAMES entspricht.
|
* Wie zuvor: Prüft, ob ein relativer Pfad (innerhalb folderPath) mit einem
|
||||||
* Wir testen dabei, ob der Dateiname oder ein Teil des Pfades mit einem der
|
* Eintrag in IGNORED_NAMES übereinstimmt. (Wildcards, Ordnernamen, Dateiendungen etc.)
|
||||||
* Einträge übereinstimmt. Wildcards (z.B. "*.log") werden mit minimiertem Regex-Check abgeglichen.
|
|
||||||
*/
|
*/
|
||||||
function isIgnored(filePathRelative) {
|
function isIgnoredByList(filePathRelative) {
|
||||||
const basename = path.basename(filePathRelative);
|
const basename = path.basename(filePathRelative);
|
||||||
return IGNORED_NAMES.some((pattern) => {
|
return IGNORED_NAMES.some(pattern => {
|
||||||
if (pattern.includes('*')) {
|
if (pattern.includes('*')) {
|
||||||
// Einfacher Umgang mit "*"-Wildcard: z. B. "*.log"
|
// Einfacher Wildcard-Abgleich: "*.log" -> /^.*\.log$/
|
||||||
const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
|
const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
|
||||||
return regex.test(basename);
|
return regex.test(basename);
|
||||||
}
|
}
|
||||||
// genaues Match am Dateinamen oder irgendwo im Pfad
|
|
||||||
return basename === pattern || filePathRelative.split(path.sep).includes(pattern);
|
return basename === pattern || filePathRelative.split(path.sep).includes(pattern);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fügt einen Eintrag in die .gitignore im Zielordner hinzu, falls er noch nicht existiert.
|
* Schreibt einen Eintrag in .gitignore, falls noch nicht vorhanden.
|
||||||
*/
|
*/
|
||||||
async function ensureIgnoredInGitignore(folderPath, entry) {
|
async function ensureIgnoredInGitignore(folderPath, entry) {
|
||||||
const gitignorePath = path.join(folderPath, '.gitignore');
|
const gitignorePath = path.join(folderPath, '.gitignore');
|
||||||
let content = '';
|
let content = '';
|
||||||
try {
|
try {
|
||||||
content = await fs.readFile(gitignorePath, { encoding: 'utf8' });
|
content = await fs.readFile(gitignorePath, 'utf8');
|
||||||
} catch (err) {
|
} catch (e) {
|
||||||
// .gitignore existiert noch nicht => wird beim Schreiben erstellt
|
|
||||||
content = '';
|
content = '';
|
||||||
}
|
}
|
||||||
const lines = content.split(/\r?\n/);
|
const lines = content.split(/\r?\n/).map(l => l.trim());
|
||||||
if (!lines.includes(entry)) {
|
if (!lines.includes(entry)) {
|
||||||
const newContent = content.trim().length > 0
|
const newContent = content.trim().length > 0
|
||||||
? content.trim() + '\n' + entry + '\n'
|
? content.trim() + '\n' + entry + '\n'
|
||||||
: entry + '\n';
|
: entry + '\n';
|
||||||
await fs.writeFile(gitignorePath, newContent, { encoding: 'utf8' });
|
await fs.writeFile(gitignorePath, newContent, 'utf8');
|
||||||
debug(`[IGNORE] Eintrag "${entry}" zu .gitignore hinzugefügt in ${folderPath}`);
|
debug(`[IGNORE] Eintrag "${entry}" zu .gitignore hinzugefügt in ${folderPath}`);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kombinierte Funktion: Initialer Commit und Watcher in einem.
|
* Die kombinierte Funktion für initialen Commit + Watcher.
|
||||||
*/
|
*/
|
||||||
function startMonitoringWatcher(folderPath, win) {
|
async function startMonitoringWatcher(folderPath, win) {
|
||||||
if (monitoringWatchers.has(folderPath)) return;
|
if (monitoringWatchers.has(folderPath)) return;
|
||||||
|
|
||||||
const watcher = chokidar.watch(folderPath, {
|
|
||||||
ignored: /(^|[\/\\])\.git/,
|
|
||||||
ignoreInitial: true,
|
|
||||||
persistent: true,
|
|
||||||
depth: 99,
|
|
||||||
awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 }
|
|
||||||
});
|
|
||||||
|
|
||||||
const git = simpleGit(folderPath);
|
const git = simpleGit(folderPath);
|
||||||
|
|
||||||
|
// 1. Lade oder lege eine .gitignore-Filterinstanz an
|
||||||
|
let igFilter = await loadGitignoreFilter(folderPath);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Führt einen Git-Status-Check durch, filtert ignorierbare Dateien raus und
|
* Führt Git-Status-Check aus, filtert Dateien heraus,
|
||||||
* schreibt sie ggfs. in die .gitignore, bevor die verbleibenden Änderungen committed werden.
|
* die in IGNORED_NAMES stehen, schreibt sie ggf. in .gitignore,
|
||||||
|
* und committet dann verbleibende Änderungen.
|
||||||
*/
|
*/
|
||||||
async function doAutoCommitWithIgnoreCheck() {
|
async function doAutoCommitWithIgnoreCheck() {
|
||||||
const status = await git.status();
|
const status = await git.status();
|
||||||
|
|
||||||
// Dateien, die ignoriert werden sollen (nach IGNORED_NAMES)
|
// 2. Alle not_added/created-Dateien, die in unserer IGNORED_NAMES-Liste stehen:
|
||||||
const toIgnore = [
|
const toIgnore = [ ...status.not_added, ...status.created ]
|
||||||
...status.not_added,
|
.filter(rel => isIgnoredByList(rel));
|
||||||
...status.created
|
|
||||||
].filter(filePathRel => isIgnored(filePathRel));
|
|
||||||
|
|
||||||
// Dateien, die tatsächlich committed werden sollen
|
// 3. Der Rest geht in toCommit:
|
||||||
const toCommit = {
|
const toCommit = {
|
||||||
not_added: status.not_added.filter(p => !toIgnore.includes(p)),
|
not_added: status.not_added.filter(p => !toIgnore.includes(p)),
|
||||||
created: status.created.filter(p => !toIgnore.includes(p)),
|
created: status.created.filter(p => !toIgnore.includes(p)),
|
||||||
@@ -397,26 +405,28 @@ function startMonitoringWatcher(folderPath, win) {
|
|||||||
renamed: status.renamed.slice()
|
renamed: status.renamed.slice()
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. Ignorierbare Dateien in .gitignore schreiben
|
// 4. Zuerst .gitignore aktualisieren, falls nötig
|
||||||
let ignoreWritten = false;
|
let ignoreWritten = false;
|
||||||
for (const fileRel of toIgnore) {
|
for (const relPath of toIgnore) {
|
||||||
// Den Eintrag so in .gitignore setzen, dass er relativ zum Repo-Wurzel passt
|
const parts = relPath.split(path.sep);
|
||||||
// z.B. "node_modules/myLib" => "node_modules/"
|
const entry = parts.length > 1 ? parts[0] + '/' : relPath;
|
||||||
const parts = fileRel.split(path.sep);
|
// "/" anhängen, damit Ordner wie "node_modules/" korrekt ignoriert werden
|
||||||
const entry = parts.length > 1 ? parts[0] : fileRel;
|
|
||||||
const didWrite = await ensureIgnoredInGitignore(folderPath, entry);
|
const didWrite = await ensureIgnoredInGitignore(folderPath, entry);
|
||||||
if (didWrite) ignoreWritten = true;
|
if (didWrite) ignoreWritten = true;
|
||||||
}
|
}
|
||||||
if (ignoreWritten) {
|
if (ignoreWritten) {
|
||||||
|
// .gitignore wurde angepasst → committen
|
||||||
await git.add('.gitignore');
|
await git.add('.gitignore');
|
||||||
await git.commit('auto-git: Aktualisiere .gitignore um ignorierte Dateien');
|
await git.commit('auto-git: Aktualisiere .gitignore um ignorierte Dateien');
|
||||||
win.webContents.send('repo-updated', folderPath);
|
win.webContents.send('repo-updated', folderPath);
|
||||||
debug('[MONITOR] .gitignore Commit ausgeführt');
|
|
||||||
// nach dem Schreiben in .gitignore möchte man nochmal den Status neu holen
|
// Neue .gitignore einladen, damit unser Filter aktuell ist
|
||||||
return await doAutoCommitWithIgnoreCheck();
|
igFilter = await loadGitignoreFilter(folderPath);
|
||||||
|
// Danach Status erneut prüfen (Rekursionsaufruf), weil sich nun ignore-Muster geändert haben
|
||||||
|
return doAutoCommitWithIgnoreCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Commit für die verbleibenden Änderungen (falls vorhanden)
|
// 5. Wenn keine .gitignore-Anpassung nötig war, committen wir alle echten Änderungen:
|
||||||
const hasChanges =
|
const hasChanges =
|
||||||
toCommit.not_added.length > 0 ||
|
toCommit.not_added.length > 0 ||
|
||||||
toCommit.created.length > 0 ||
|
toCommit.created.length > 0 ||
|
||||||
@@ -425,7 +435,6 @@ function startMonitoringWatcher(folderPath, win) {
|
|||||||
toCommit.renamed.length > 0;
|
toCommit.renamed.length > 0;
|
||||||
|
|
||||||
if (hasChanges) {
|
if (hasChanges) {
|
||||||
// Build-Message aus den gefilterten Status-Feldern
|
|
||||||
const fakeStatus = {
|
const fakeStatus = {
|
||||||
not_added: toCommit.not_added,
|
not_added: toCommit.not_added,
|
||||||
created: toCommit.created,
|
created: toCommit.created,
|
||||||
@@ -440,34 +449,60 @@ function startMonitoringWatcher(folderPath, win) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Initialer Commit-Check ---
|
// 6. Chokidar-Watcher startet – nutzt eine Funktion, die sowohl .gitignore-Muster
|
||||||
|
// abfragt als auch das .git-Verzeichnis immer aussperrt.
|
||||||
|
const watcher = chokidar.watch(folderPath, {
|
||||||
|
ignored: (filePath) => {
|
||||||
|
// Immer das .git-Verzeichnis ausschließen
|
||||||
|
const isInDotGit = /(^|[\/\\])\.git(?:[\/\\]|$)/.test(filePath);
|
||||||
|
if (isInDotGit) return true;
|
||||||
|
|
||||||
|
// Relativer Pfad zum Repo
|
||||||
|
const rel = path.relative(folderPath, filePath);
|
||||||
|
// 1) Wenn die Datei in der .gitignore steht → ignorieren
|
||||||
|
if (igFilter.ignores(rel)) return true;
|
||||||
|
// 2) Ansonsten wird die Änderung durch unseren Code ohnehin
|
||||||
|
// beim Commit-Check berücksichtigt (wir ignorieren nur, was auch wirklich in IGNORED_NAMES ist).
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
ignoreInitial: true,
|
||||||
|
persistent: true,
|
||||||
|
depth: 99,
|
||||||
|
awaitWriteFinish: { stabilityThreshold: 300, pollInterval: 100 }
|
||||||
|
});
|
||||||
|
|
||||||
|
// 7. Initialer Commit-Check
|
||||||
(async () => {
|
(async () => {
|
||||||
debug(`[MONITOR] Starte initialen Commit-Check für ${folderPath}`);
|
debug(`[MONITOR] Starte initialen Commit-Check für ${folderPath}`);
|
||||||
await doAutoCommitWithIgnoreCheck();
|
await doAutoCommitWithIgnoreCheck();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// --- Watcher: bei jedem Dateisystem-Event ---
|
// 8. Bei jedem Event: Prüfen, ob Pfad in .gitignore gehört,
|
||||||
|
// sonst normal doAutoCommitWithIgnoreCheck() aufrufen.
|
||||||
watcher.on('all', async (event, filePathAbsolute) => {
|
watcher.on('all', async (event, filePathAbsolute) => {
|
||||||
// Relativer Pfad zum Repository
|
const rel = path.relative(folderPath, filePathAbsolute);
|
||||||
const filePathRel = path.relative(folderPath, filePathAbsolute);
|
|
||||||
|
|
||||||
// Zuerst prüfen, ob die Änderung eine Datei betrifft, die wir ignorieren wollen
|
// a) Prüfe, ob dieser Pfad durch die aktuell geladene .gitignore
|
||||||
if (isIgnored(filePathRel)) {
|
// bereits ausgeschlossen ist (z.B. node_modules/foo.js):
|
||||||
// Sicherstellen, dass der Eintrag in .gitignore steht
|
if (igFilter.ignores(rel)) {
|
||||||
const entry = filePathRel.split(path.sep)[0];
|
// Falls wir bisher noch nicht in .gitignore hatten, dort hinzufügen:
|
||||||
|
// (Aber eigentlich sollte .gitignore aktuell sein, weil doAutoCommitWithIgnoreCheck()
|
||||||
|
// ja erst dafür gesorgt hat.)
|
||||||
|
const parts = rel.split(path.sep);
|
||||||
|
const entry = parts.length > 1 ? parts[0] + '/' : rel;
|
||||||
const didWrite = await ensureIgnoredInGitignore(folderPath, entry);
|
const didWrite = await ensureIgnoredInGitignore(folderPath, entry);
|
||||||
if (didWrite) {
|
if (didWrite) {
|
||||||
// Wenn in .gitignore geschrieben wurde, committen wir das und senden Update
|
// Neue .gitignore-Muster einladen
|
||||||
|
igFilter = await loadGitignoreFilter(folderPath);
|
||||||
await git.add('.gitignore');
|
await git.add('.gitignore');
|
||||||
await git.commit('auto-git: Hinzufügen zu .gitignore');
|
await git.commit('auto-git: Hinzufügen zu .gitignore');
|
||||||
win.webContents.send('repo-updated', folderPath);
|
win.webContents.send('repo-updated', folderPath);
|
||||||
debug(`[WATCHER] "${entry}" zu .gitignore hinzugefügt und committed`);
|
debug(`[WATCHER] "${entry}" zu .gitignore hinzugefügt und committed`);
|
||||||
}
|
}
|
||||||
// Änderung wird nicht weiter committed
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ansonsten ganz normal: Status abfragen und committen
|
// b) Ansonsten ganz normal alle Änderungen committen
|
||||||
await doAutoCommitWithIgnoreCheck();
|
await doAutoCommitWithIgnoreCheck();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user