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/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 { ok: false, json: async () => ({ message: e.message || "Network error" }) };
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 paths detected. Committing current state to proceed...",
2298
- commandsToRun: ["git add .", 'git commit -m "Auto-resolve unmerged paths"']
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
  });