gitxelectron 1.3.7 → 1.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +247 -70
- package/bin/auto-push.js +526 -0
- package/bin/cli.js +41 -7
- package/out/main/index.js +87 -183
- package/out/renderer/assets/index-Bhk4vMRr.css +2989 -0
- package/out/renderer/assets/{index-DyLS9VOZ.js → index-Dtxyy2BB.js} +1043 -261
- package/out/renderer/index.html +2 -2
- package/package.json +3 -1
- package/out/renderer/assets/index-Vre34D_W.css +0 -2913
package/out/main/index.js
CHANGED
|
@@ -408,7 +408,10 @@ async function getCollaborators(sessionId, remoteUrl) {
|
|
|
408
408
|
return await fetch(url, { headers });
|
|
409
409
|
} catch (e) {
|
|
410
410
|
console.error(`Fetch failed for ${url}:`, e.message);
|
|
411
|
-
return {
|
|
411
|
+
return {
|
|
412
|
+
ok: false,
|
|
413
|
+
json: async () => ({ message: e.message || "Network error" })
|
|
414
|
+
};
|
|
412
415
|
}
|
|
413
416
|
};
|
|
414
417
|
const [collabRes, invRes, contribRes] = await Promise.all([
|
|
@@ -609,6 +612,20 @@ function initGit(sessionId, baseDir) {
|
|
|
609
612
|
GIT_ASKPASS: "echo"
|
|
610
613
|
});
|
|
611
614
|
gitSessions.set(sessionId, { git, repoPath: baseDir });
|
|
615
|
+
(async () => {
|
|
616
|
+
try {
|
|
617
|
+
const name = await git.getConfig("user.name");
|
|
618
|
+
if (!name.value) {
|
|
619
|
+
await git.addConfig("user.name", "GitElectron User");
|
|
620
|
+
}
|
|
621
|
+
const email = await git.getConfig("user.email");
|
|
622
|
+
if (!email.value) {
|
|
623
|
+
await git.addConfig("user.email", "auto@gitelectron.local");
|
|
624
|
+
}
|
|
625
|
+
} catch (e) {
|
|
626
|
+
console.warn("Failed to ensure git identity:", e);
|
|
627
|
+
}
|
|
628
|
+
})();
|
|
612
629
|
}
|
|
613
630
|
function createSafeGit(baseDir, sessionId) {
|
|
614
631
|
const options = {
|
|
@@ -1157,6 +1174,49 @@ async function pushToRemote(sessionId, remote, branch) {
|
|
|
1157
1174
|
throw err;
|
|
1158
1175
|
}
|
|
1159
1176
|
}
|
|
1177
|
+
async function masterForcePush(sessionId, remote, branch) {
|
|
1178
|
+
const { git } = getGitSession(sessionId);
|
|
1179
|
+
const remotes = await getRemotes(sessionId);
|
|
1180
|
+
const remoteObj = remotes.find((r) => r.name === remote);
|
|
1181
|
+
await git.raw(["add", "-A", "-f"]);
|
|
1182
|
+
try {
|
|
1183
|
+
const name = await git.getConfig("user.name");
|
|
1184
|
+
if (!name.value) await git.addConfig("user.name", "GitElectron User");
|
|
1185
|
+
const email = await git.getConfig("user.email");
|
|
1186
|
+
if (!email.value) await git.addConfig("user.email", "auto@gitelectron.local");
|
|
1187
|
+
} catch (e) {
|
|
1188
|
+
console.warn("Failed to ensure git identity before master push:", e);
|
|
1189
|
+
}
|
|
1190
|
+
try {
|
|
1191
|
+
await git.commit("Master Force Push - Including all files");
|
|
1192
|
+
} catch (e) {
|
|
1193
|
+
if (!e.message.includes("nothing to commit")) {
|
|
1194
|
+
throw e;
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
const tempGit = simpleGit.simpleGit({
|
|
1198
|
+
baseDir: getGitSession(sessionId).repoPath,
|
|
1199
|
+
unsafe: { allowUnsafeEditor: true, allowUnsafeAskPass: true, allowUnsafeCredentialHelper: true }
|
|
1200
|
+
}).env({
|
|
1201
|
+
...process.env,
|
|
1202
|
+
GCM_INTERACTIVE: "false",
|
|
1203
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1204
|
+
GIT_ASKPASS: "echo"
|
|
1205
|
+
});
|
|
1206
|
+
const pushArgs = [
|
|
1207
|
+
"-c",
|
|
1208
|
+
"credential.helper=",
|
|
1209
|
+
"push",
|
|
1210
|
+
"--progress",
|
|
1211
|
+
"--no-verify",
|
|
1212
|
+
"-f"
|
|
1213
|
+
];
|
|
1214
|
+
if (remoteObj && remoteObj.refs.push) {
|
|
1215
|
+
const authUrl = injectAuthToken(sessionId, remoteObj.refs.push);
|
|
1216
|
+
return await tempGit.raw([...pushArgs, authUrl, branch]);
|
|
1217
|
+
}
|
|
1218
|
+
return await tempGit.raw([...pushArgs, remote, branch]);
|
|
1219
|
+
}
|
|
1160
1220
|
async function pullFromRemote(sessionId, remote, branch) {
|
|
1161
1221
|
const { git, repoPath } = getGitSession(sessionId);
|
|
1162
1222
|
const remotes = await getRemotes(sessionId);
|
|
@@ -1165,6 +1225,14 @@ async function pullFromRemote(sessionId, remote, branch) {
|
|
|
1165
1225
|
}
|
|
1166
1226
|
const remoteObj = (remote ? remotes.find((r) => r.name === remote) : null) || remotes[0];
|
|
1167
1227
|
const targetRemote = remoteObj.name;
|
|
1228
|
+
try {
|
|
1229
|
+
const name = await git.getConfig("user.name");
|
|
1230
|
+
if (!name.value) await git.addConfig("user.name", "GitElectron User");
|
|
1231
|
+
const email = await git.getConfig("user.email");
|
|
1232
|
+
if (!email.value) await git.addConfig("user.email", "auto@gitelectron.local");
|
|
1233
|
+
} catch (e) {
|
|
1234
|
+
console.warn("Failed to ensure git identity before pull:", e);
|
|
1235
|
+
}
|
|
1168
1236
|
let targetBranch = branch;
|
|
1169
1237
|
if (!targetBranch) {
|
|
1170
1238
|
const status = await git.status();
|
|
@@ -1182,11 +1250,19 @@ async function pullFromRemote(sessionId, remote, branch) {
|
|
|
1182
1250
|
let didStash = false;
|
|
1183
1251
|
try {
|
|
1184
1252
|
const status = await git.status();
|
|
1253
|
+
if (status.conflicted && status.conflicted.length > 0) {
|
|
1254
|
+
throw new Error(
|
|
1255
|
+
`Pulling is not possible because you have unmerged files (${status.conflicted.join(", ")}). Please resolve conflicts or abort the merge.`
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1185
1258
|
if (!status.isClean()) {
|
|
1186
1259
|
await git.raw(["stash", "push", "-u", "-m", "gitquickpush-autostash-before-pull"]);
|
|
1187
1260
|
didStash = true;
|
|
1188
1261
|
}
|
|
1189
1262
|
} catch (err) {
|
|
1263
|
+
if (err.message && err.message.includes("unmerged files")) {
|
|
1264
|
+
throw err;
|
|
1265
|
+
}
|
|
1190
1266
|
console.warn("Stash check before pull warning:", err);
|
|
1191
1267
|
}
|
|
1192
1268
|
try {
|
|
@@ -1996,182 +2072,6 @@ async function removeGitLock(sessionId) {
|
|
|
1996
2072
|
return false;
|
|
1997
2073
|
}
|
|
1998
2074
|
}
|
|
1999
|
-
async function masterPush(sessionId, remote, branch) {
|
|
2000
|
-
const { git, repoPath } = getGitSession(sessionId);
|
|
2001
|
-
const remotes = await getRemotes(sessionId);
|
|
2002
|
-
const remoteObj = (remote ? remotes.find((r) => r.name === remote) : null) || remotes[0];
|
|
2003
|
-
if (!remoteObj) throw new Error("No remote repository configured.");
|
|
2004
|
-
const targetRemote = remoteObj.name;
|
|
2005
|
-
const authUrl = remoteObj.refs?.push ? injectAuthToken(sessionId, remoteObj.refs.push) : targetRemote;
|
|
2006
|
-
let originalBranch = branch;
|
|
2007
|
-
try {
|
|
2008
|
-
const branchSummary = await git.branchLocal();
|
|
2009
|
-
originalBranch = branchSummary.current || branch;
|
|
2010
|
-
} catch (e) {
|
|
2011
|
-
}
|
|
2012
|
-
const tempBranch = `__master_push_temp_${Date.now()}`;
|
|
2013
|
-
const hiddenPointers = [];
|
|
2014
|
-
const cleanedAttrsFiles = [];
|
|
2015
|
-
const lfsOffFlags = [
|
|
2016
|
-
"-c",
|
|
2017
|
-
"filter.lfs.clean=cat",
|
|
2018
|
-
"-c",
|
|
2019
|
-
"filter.lfs.smudge=cat",
|
|
2020
|
-
"-c",
|
|
2021
|
-
"filter.lfs.process=",
|
|
2022
|
-
"-c",
|
|
2023
|
-
"filter.lfs.required=false",
|
|
2024
|
-
"-c",
|
|
2025
|
-
"lfs.allowincompletepush=true",
|
|
2026
|
-
"-c",
|
|
2027
|
-
"http.postBuffer=524288000",
|
|
2028
|
-
"-c",
|
|
2029
|
-
"credential.helper="
|
|
2030
|
-
];
|
|
2031
|
-
function runGit(args, allowFail = false) {
|
|
2032
|
-
return new Promise((resolve, reject) => {
|
|
2033
|
-
const child = child_process.spawn("git", [...lfsOffFlags, ...args], {
|
|
2034
|
-
cwd: repoPath,
|
|
2035
|
-
env: {
|
|
2036
|
-
...process.env,
|
|
2037
|
-
GIT_LFS_SKIP_SMUDGE: "1",
|
|
2038
|
-
GIT_LFS_SKIP_PUSH: "1",
|
|
2039
|
-
GCM_INTERACTIVE: "false",
|
|
2040
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
2041
|
-
GIT_ASKPASS: "echo"
|
|
2042
|
-
}
|
|
2043
|
-
});
|
|
2044
|
-
const stdout = [];
|
|
2045
|
-
const stderr = [];
|
|
2046
|
-
child.stdout?.on("data", (d) => stdout.push(d.toString()));
|
|
2047
|
-
child.stderr?.on("data", (d) => stderr.push(d.toString()));
|
|
2048
|
-
child.on("error", (err) => allowFail ? resolve("") : reject(err));
|
|
2049
|
-
child.on("close", (code) => {
|
|
2050
|
-
if (code === 0 || allowFail) {
|
|
2051
|
-
resolve(stdout.join(""));
|
|
2052
|
-
} else {
|
|
2053
|
-
reject(new Error(stderr.join("") || `git exited with code ${code}`));
|
|
2054
|
-
}
|
|
2055
|
-
});
|
|
2056
|
-
});
|
|
2057
|
-
}
|
|
2058
|
-
async function processRepo(dir) {
|
|
2059
|
-
let entries;
|
|
2060
|
-
try {
|
|
2061
|
-
entries = await fs__namespace.readdir(dir, { withFileTypes: true });
|
|
2062
|
-
} catch (e) {
|
|
2063
|
-
return;
|
|
2064
|
-
}
|
|
2065
|
-
for (const entry of entries) {
|
|
2066
|
-
if (entry.name === ".git" || entry.name === "node_modules" || entry.name.endsWith(".__lfs_ptr_bak__")) continue;
|
|
2067
|
-
const fullPath = path__namespace.join(dir, entry.name);
|
|
2068
|
-
if (entry.isDirectory()) {
|
|
2069
|
-
await processRepo(fullPath);
|
|
2070
|
-
continue;
|
|
2071
|
-
}
|
|
2072
|
-
if (!entry.isFile()) continue;
|
|
2073
|
-
try {
|
|
2074
|
-
if (entry.name === ".gitattributes") {
|
|
2075
|
-
const content = await fs__namespace.readFile(fullPath, "utf8");
|
|
2076
|
-
if (content.includes("filter=lfs") || content.includes("lfs")) {
|
|
2077
|
-
cleanedAttrsFiles.push({ filePath: fullPath, original: content });
|
|
2078
|
-
const cleaned = content.split("\n").filter((line) => !line.toLowerCase().includes("lfs")).join("\n");
|
|
2079
|
-
await fs__namespace.writeFile(fullPath, cleaned);
|
|
2080
|
-
}
|
|
2081
|
-
continue;
|
|
2082
|
-
}
|
|
2083
|
-
const stat = await fs__namespace.stat(fullPath);
|
|
2084
|
-
if (stat.size < 4096 && stat.size > 20) {
|
|
2085
|
-
const buf = await fs__namespace.readFile(fullPath);
|
|
2086
|
-
const text = buf.toString("utf8");
|
|
2087
|
-
if (text.includes("version https://git-lfs.github.com/spec/v1") && text.includes("oid sha256:")) {
|
|
2088
|
-
const backupPath = fullPath + ".__lfs_ptr_bak__";
|
|
2089
|
-
await fs__namespace.rename(fullPath, backupPath);
|
|
2090
|
-
hiddenPointers.push({ original: fullPath, backup: backupPath });
|
|
2091
|
-
}
|
|
2092
|
-
}
|
|
2093
|
-
} catch (e) {
|
|
2094
|
-
}
|
|
2095
|
-
}
|
|
2096
|
-
}
|
|
2097
|
-
try {
|
|
2098
|
-
try {
|
|
2099
|
-
await git.raw(["lfs", "uninstall", "--local"]);
|
|
2100
|
-
} catch (e) {
|
|
2101
|
-
}
|
|
2102
|
-
try {
|
|
2103
|
-
await git.addConfig("filter.lfs.clean", "cat");
|
|
2104
|
-
await git.addConfig("filter.lfs.smudge", "cat");
|
|
2105
|
-
await git.addConfig("filter.lfs.process", "");
|
|
2106
|
-
await git.addConfig("filter.lfs.required", "false");
|
|
2107
|
-
} catch (e) {
|
|
2108
|
-
}
|
|
2109
|
-
await processRepo(repoPath);
|
|
2110
|
-
await runGit(["checkout", "--orphan", tempBranch]);
|
|
2111
|
-
await runGit(["add", "."]);
|
|
2112
|
-
try {
|
|
2113
|
-
const grepResult = await runGit(
|
|
2114
|
-
["grep", "--cached", "-l", "version https://git-lfs.github.com/spec/v1"],
|
|
2115
|
-
true
|
|
2116
|
-
);
|
|
2117
|
-
if (grepResult.trim()) {
|
|
2118
|
-
const lfsFiles = grepResult.trim().split("\n").filter(Boolean);
|
|
2119
|
-
for (const f of lfsFiles) {
|
|
2120
|
-
await runGit(["rm", "--cached", "--force", f.trim()], true);
|
|
2121
|
-
}
|
|
2122
|
-
}
|
|
2123
|
-
} catch (e) {
|
|
2124
|
-
}
|
|
2125
|
-
try {
|
|
2126
|
-
await runGit(["commit", "-m", "Master Override Push", "--no-verify"]);
|
|
2127
|
-
} catch (e) {
|
|
2128
|
-
}
|
|
2129
|
-
await runGit(["push", "--force", "--no-verify", authUrl, `${tempBranch}:${branch}`]);
|
|
2130
|
-
} finally {
|
|
2131
|
-
try {
|
|
2132
|
-
await runGit(["checkout", "-f", originalBranch], true);
|
|
2133
|
-
} catch (e) {
|
|
2134
|
-
try {
|
|
2135
|
-
await runGit(["checkout", "-f", branch], true);
|
|
2136
|
-
} catch (e2) {
|
|
2137
|
-
try {
|
|
2138
|
-
await runGit(["checkout", "-b", branch], true);
|
|
2139
|
-
} catch (e3) {
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
}
|
|
2143
|
-
try {
|
|
2144
|
-
const branchList = await runGit(["branch", "--list", "__master_push_temp_*"], true);
|
|
2145
|
-
if (branchList.trim()) {
|
|
2146
|
-
const branches = branchList.trim().split("\n").map((b) => b.trim().replace(/^\*\s*/, "")).filter(Boolean);
|
|
2147
|
-
for (const b of branches) {
|
|
2148
|
-
await runGit(["branch", "-D", b], true);
|
|
2149
|
-
}
|
|
2150
|
-
}
|
|
2151
|
-
} catch (e) {
|
|
2152
|
-
}
|
|
2153
|
-
try {
|
|
2154
|
-
await runGit(["branch", "-D", tempBranch], true);
|
|
2155
|
-
} catch (e) {
|
|
2156
|
-
}
|
|
2157
|
-
for (const { original, backup } of hiddenPointers) {
|
|
2158
|
-
try {
|
|
2159
|
-
await fs__namespace.rename(backup, original);
|
|
2160
|
-
} catch (e) {
|
|
2161
|
-
}
|
|
2162
|
-
}
|
|
2163
|
-
for (const { filePath, original } of cleanedAttrsFiles) {
|
|
2164
|
-
try {
|
|
2165
|
-
await fs__namespace.writeFile(filePath, original);
|
|
2166
|
-
} catch (e) {
|
|
2167
|
-
}
|
|
2168
|
-
}
|
|
2169
|
-
try {
|
|
2170
|
-
await git.raw(["lfs", "install", "--local"]);
|
|
2171
|
-
} catch (e) {
|
|
2172
|
-
}
|
|
2173
|
-
}
|
|
2174
|
-
}
|
|
2175
2075
|
const SYSTEM_PROMPT = `You are an autonomous Git Healer Agent for the "GitElectron" app.
|
|
2176
2076
|
The user has encountered a Git error during a push, pull, or merge operation and you are executing as a background process to silently fix it.
|
|
2177
2077
|
You will receive the Git Status and the Raw Error Message.
|
|
@@ -2292,10 +2192,14 @@ ${errorMsg}
|
|
|
2292
2192
|
]
|
|
2293
2193
|
};
|
|
2294
2194
|
}
|
|
2295
|
-
if (lowerError.includes("unmerged files") || lowerError.includes("you have unmerged paths")) {
|
|
2195
|
+
if (lowerError.includes("unmerged files") || lowerError.includes("you have unmerged paths") || lowerError.includes("unresolved conflict")) {
|
|
2296
2196
|
return {
|
|
2297
|
-
explanation: "Unmerged
|
|
2298
|
-
commandsToRun: [
|
|
2197
|
+
explanation: "Unmerged files detected. Resolving conflicts by keeping local changes...",
|
|
2198
|
+
commandsToRun: [
|
|
2199
|
+
"git checkout --ours .",
|
|
2200
|
+
"git add .",
|
|
2201
|
+
'git commit -m "Auto-resolved unmerged conflicts (kept local changes)"'
|
|
2202
|
+
]
|
|
2299
2203
|
};
|
|
2300
2204
|
}
|
|
2301
2205
|
if (lowerError.includes("please tell me who you are")) {
|
|
@@ -2928,15 +2832,15 @@ electron.app.whenReady().then(async () => {
|
|
|
2928
2832
|
}
|
|
2929
2833
|
return await pushToRemote(event.sender.id, remote, branch);
|
|
2930
2834
|
});
|
|
2835
|
+
electron.ipcMain.handle("master-push", async (event, remote, branch) => {
|
|
2836
|
+
return await masterForcePush(event.sender.id, remote, branch);
|
|
2837
|
+
});
|
|
2931
2838
|
electron.ipcMain.handle("pull-from-remote", async (event, remote, branch) => {
|
|
2932
2839
|
return await pullFromRemote(event.sender.id, remote, branch);
|
|
2933
2840
|
});
|
|
2934
2841
|
electron.ipcMain.handle("check-remote-updates", async (event) => {
|
|
2935
2842
|
return await checkRemoteUpdates(event.sender.id);
|
|
2936
2843
|
});
|
|
2937
|
-
electron.ipcMain.handle("master-push", async (event, remote, branch) => {
|
|
2938
|
-
return await masterPush(event.sender.id, remote, branch);
|
|
2939
|
-
});
|
|
2940
2844
|
electron.ipcMain.handle("reset-to-remote", async (event, remote, branch) => {
|
|
2941
2845
|
return await resetToRemote(event.sender.id, remote, branch);
|
|
2942
2846
|
});
|