Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13,449 changes: 13,449 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

22 changes: 22 additions & 0 deletions simple-git/add-commit.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { writeFileSync, mkdtempSync } from 'fs';
import { tmpdir } from 'os';
import { join } from 'path';
import { simpleGit } from '../../src';

describe('add and commit', () => {
it('should add and commit a file', async () => {
const dir = mkdtempSync(join(tmpdir(), 'git-add-'));
const git = simpleGit(dir);

await git.init();
const filePath = join(dir, 'test.txt');
writeFileSync(filePath, 'hello world');

await git.add('test.txt');
await git.commit('Add test.txt');

const log = await git.log();
expect(log.total).toBeGreaterThan(0);
expect(log.latest?.message).toBe('Add test.txt');
});
});
103 changes: 102 additions & 1 deletion simple-git/src/git.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,15 @@ const {
const { addAnnotatedTagTask, addTagTask, tagListTask } = require('./lib/tasks/tag');
const { straightThroughBufferTask, straightThroughStringTask } = require('./lib/tasks/task');

// In simple-git/src/git.js

function Git(options, plugins) {
this._plugins = plugins;
this._executor = new GitExecutor(
options.baseDir,
new Scheduler(options.maxConcurrentProcesses),
plugins
plugins,
options.env
);

this._trimmed = options.trimmed;
Expand Down Expand Up @@ -321,13 +324,18 @@ Git.prototype.branchLocal = function (then) {
return this._runTask(branchLocalTask(), trailingFunctionArgument(arguments));
};


/**
* Executes any command against the git binary.
* Enhanced to handle --show-signature flag
*/
Git.prototype.raw = function (commands) {
const createRestCommands = !Array.isArray(commands);
const command = [].slice.call(createRestCommands ? arguments : commands, 0);

// NEU: Prüfe ob --show-signature verwendet wird
const showSignature = command.includes('--show-signature');

for (let i = 0; i < command.length && createRestCommands; i++) {
if (!filterPrimitives(command[i])) {
command.splice(i, command.length - i);
Expand All @@ -346,9 +354,102 @@ Git.prototype.raw = function (commands) {
);
}

// NEU: Signature-Parsing für --show-signature
if (showSignature) {
const task = straightThroughStringTask(command, this._trimmed);
const originalParser = task.parser;

task.parser = function(text) {
const result = originalParser ? originalParser(text) : text;

// Parse signature information
const signature = parseSignature(text);
if (signature && Object.keys(signature).length > 0) {
// Wenn result ein String ist, in Objekt umwandeln
if (typeof result === 'string') {
return {
output: result,
signature: signature
};
}
// Wenn result ein Objekt ist, signature hinzufügen
result.signature = signature;
}

return result;
};

return this._runTask(task, next);
}

return this._runTask(straightThroughStringTask(command, this._trimmed), next);
};

/**
* Parse Git signature output from --show-signature
*/
function parseSignature(text) {
if (!text || typeof text !== 'string') {
return null;
}

const lines = text.split('\n');
const signature = {
verified: false,
status: 'NONE',
signer: null,
keyId: null,
timestamp: null,
};

let hasSignature = false;

for (const line of lines) {
// Beispiel: "gpg: Signature made Mon Jul 20 2026"
const madeMatch = line.match(/gpg: Signature made (.+)/);
if (madeMatch) {
signature.timestamp = madeMatch[1].trim();
hasSignature = true;
}

// Beispiel: "gpg: Good signature from John Doe <john@example.com>"
const goodMatch = line.match(/gpg: Good signature from (.+)/);
if (goodMatch) {
signature.verified = true;
signature.status = 'GOOD';
signature.signer = goodMatch[1].trim();
hasSignature = true;
}

// Beispiel: "gpg: BAD signature from John Doe <john@example.com>"
const badMatch = line.match(/gpg: BAD signature from (.+)/);
if (badMatch) {
signature.verified = false;
signature.status = 'BAD';
signature.signer = badMatch[1].trim();
hasSignature = true;
}

// Beispiel: "gpg: ERROR: ..."
const errorMatch = line.match(/gpg: ERROR: (.+)/);
if (errorMatch) {
signature.verified = false;
signature.status = 'ERROR';
signature.signer = errorMatch[1].trim();
hasSignature = true;
}

// Beispiel: "gpg: Signature key 1234567890ABCDEF"
const keyMatch = line.match(/gpg: Signature key ([A-F0-9]+)/);
if (keyMatch) {
signature.keyId = keyMatch[1];
hasSignature = true;
}
}

return hasSignature ? signature : null;
}

Git.prototype.submoduleAdd = function (repo, path, then) {
return this._runTask(addSubModuleTask(repo, path), trailingFunctionArgument(arguments));
};
Expand Down
3 changes: 2 additions & 1 deletion simple-git/src/lib/git-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,5 +71,6 @@ export function gitInstanceFactory(

customBinaryPlugin(plugins, config.binary, config.unsafe?.allowUnsafeCustomBinary);

// NEU: Erstelle den Git-Executor mit env
return new Git(config, plugins);
}
}
67 changes: 65 additions & 2 deletions simple-git/src/lib/parsers/parse-commit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,45 @@ const parsers: LineParser<CommitResult>[] = [
),
];

/**
* Extrahiert Git Trailer aus der Commit-Nachricht
* @param body - Vollständige Commit-Nachricht
* @returns Key-Value Objekt aller Trailer
*/
export function parseTrailers(body: string): Record<string, string> {
if (!body) return {};

const trailers: Record<string, string> = {};
const lines = body.split('\n');
let inTrailerSection = false;

// Von hinten durchgehen
for (let i = lines.length - 1; i >= 0; i--) {
const line = lines[i];

// Wenn leere Zeile oder ---, dann sind wir im Trailer-Bereich
if (line.trim() === '' || line.trim() === '---') {
inTrailerSection = true;
continue;
}

if (inTrailerSection) {
// Prüfe auf "Key: Value" Format
const match = line.match(/^([^:]+):\s*(.+)$/);
if (match) {
const key = match[1].trim();
const value = match[2].trim();
trailers[key] = value;
} else {
// Wenn kein Trailer-Format, sind wir fertig
break;
}
}
}

return trailers;
}

export function parseCommitResult(stdOut: string): CommitResult {
const result: CommitResult = {
author: null,
Expand All @@ -54,5 +93,29 @@ export function parseCommitResult(stdOut: string): CommitResult {
deletions: 0,
},
};
return parseStringResponse(result, parsers, stdOut);
}

// Parse die Standard-Felder
const parsedResult = parseStringResponse(result, parsers, stdOut);

// Extrahiere die Commit-Nachricht
const lines = stdOut.split('\n');
let body = '';
let inBody = false;

for (const line of lines) {
if (line.startsWith(' ')) {
inBody = true;
body += line.trim() + '\n';
} else if (inBody && line.trim() === '') {
break;
}
}

// Füge trailers zum Ergebnis hinzu
const trailers = parseTrailers(body.trim());
if (Object.keys(trailers).length > 0) {
parsedResult.trailers = trailers;
}

return parsedResult;
}
18 changes: 17 additions & 1 deletion simple-git/src/lib/responses/StatusSummary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export class StatusSummary implements StatusResult {
public current = null;
public tracking = null;
public detached = false;
public rebasing = false; // <-- NEU
public cherryPicking = false; // <-- NEU

public isClean = () => {
return !this.files.length;
Expand Down Expand Up @@ -137,6 +139,11 @@ const parsers: Map<string, StatusLineParser> = new Map([
const currentReg = /^(.+?(?=(?:\.{3}|\s|$)))/;
const trackingReg = /\.{3}(\S*)/;
const onEmptyBranchReg = /\son\s([\S]+)$/;

// NEU: Rebase und Cherry-pick Erkennung
const rebaseReg = /rebase in progress|Rebasing/;
const cherryPickReg = /cherry-pick in progress|Cherry-picking/;

let regexResult;

regexResult = aheadReg.exec(line);
Expand All @@ -155,6 +162,15 @@ const parsers: Map<string, StatusLineParser> = new Map([
result.current = (regexResult && regexResult[1]) || result.current;

result.detached = /\(no branch\)/.test(line);

// NEU: Prüfe auf Rebase und Cherry-pick
if (rebaseReg.test(line)) {
result.rebasing = true;
}

if (cherryPickReg.test(line)) {
result.cherryPicking = true;
}
},
],
]);
Expand Down Expand Up @@ -203,4 +219,4 @@ function splitLine(result: StatusResult, lineStr: string) {
result.files.push(new FileStatusSummary(path, index, workingDir));
}
}
}
}
Loading