gitxelectron 1.2.9 → 1.3.1
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 +282 -32
- package/out/preload/index.js +1 -0
- package/out/renderer/assets/index-C1EI1txt.css +1365 -0
- package/out/renderer/assets/{index-xMfzgem_.js → index-Ctq_B0UN.js} +72 -15
- package/out/renderer/index.html +2 -2
- package/package.json +1 -1
- package/out/renderer/assets/index-BbL4wUJk.css +0 -1365
package/out/main/index.js
CHANGED
|
@@ -577,7 +577,12 @@ function initGit(sessionId, baseDir) {
|
|
|
577
577
|
baseDir,
|
|
578
578
|
binary: "git",
|
|
579
579
|
maxConcurrentProcesses: 6,
|
|
580
|
-
unsafe: {
|
|
580
|
+
unsafe: {
|
|
581
|
+
allowUnsafeEditor: true,
|
|
582
|
+
allowUnsafeAskPass: true,
|
|
583
|
+
allowUnsafeCredentialHelper: true,
|
|
584
|
+
allowUnsafeFilter: true
|
|
585
|
+
},
|
|
581
586
|
progress: ({ method, stage, progress }) => {
|
|
582
587
|
try {
|
|
583
588
|
const wc = electron.webContents.fromId(sessionId);
|
|
@@ -598,7 +603,12 @@ function createSafeGit(baseDir, sessionId) {
|
|
|
598
603
|
const options = {
|
|
599
604
|
binary: "git",
|
|
600
605
|
maxConcurrentProcesses: 6,
|
|
601
|
-
unsafe: {
|
|
606
|
+
unsafe: {
|
|
607
|
+
allowUnsafeEditor: true,
|
|
608
|
+
allowUnsafeAskPass: true,
|
|
609
|
+
allowUnsafeCredentialHelper: true,
|
|
610
|
+
allowUnsafeFilter: true
|
|
611
|
+
},
|
|
602
612
|
progress: ({ method, stage, progress }) => {
|
|
603
613
|
if (!sessionId) return;
|
|
604
614
|
try {
|
|
@@ -1017,15 +1027,148 @@ async function pushToRemote(sessionId, remote, branch) {
|
|
|
1017
1027
|
}
|
|
1018
1028
|
}
|
|
1019
1029
|
async function pullFromRemote(sessionId, remote, branch) {
|
|
1020
|
-
const { git } = getGitSession(sessionId);
|
|
1030
|
+
const { git, repoPath } = getGitSession(sessionId);
|
|
1021
1031
|
const remotes = await getRemotes(sessionId);
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1032
|
+
if (!remotes || remotes.length === 0) {
|
|
1033
|
+
throw new Error("No remote repository configured to pull from.");
|
|
1034
|
+
}
|
|
1035
|
+
const remoteObj = (remote ? remotes.find((r) => r.name === remote) : null) || remotes[0];
|
|
1036
|
+
const targetRemote = remoteObj.name;
|
|
1037
|
+
let targetBranch = branch;
|
|
1038
|
+
if (!targetBranch) {
|
|
1039
|
+
const status = await git.status();
|
|
1040
|
+
targetBranch = status.current || "main";
|
|
1041
|
+
}
|
|
1042
|
+
const authUrl = remoteObj.refs?.fetch ? injectAuthToken(sessionId, remoteObj.refs.fetch) : targetRemote;
|
|
1043
|
+
let didStash = false;
|
|
1044
|
+
try {
|
|
1045
|
+
const status = await git.status();
|
|
1046
|
+
if (!status.isClean()) {
|
|
1047
|
+
await git.raw(["stash", "push", "-u", "-m", "gitquickpush-autostash-before-pull"]);
|
|
1048
|
+
didStash = true;
|
|
1049
|
+
}
|
|
1050
|
+
} catch (err) {
|
|
1051
|
+
console.warn("Stash check before pull warning:", err);
|
|
1052
|
+
}
|
|
1053
|
+
try {
|
|
1054
|
+
const pullArgs = [
|
|
1055
|
+
"-c",
|
|
1056
|
+
"credential.helper=",
|
|
1057
|
+
"-c",
|
|
1058
|
+
"core.longpaths=true",
|
|
1059
|
+
"-c",
|
|
1060
|
+
"filter.lfs.required=false",
|
|
1061
|
+
"-c",
|
|
1062
|
+
"filter.lfs.smudge=",
|
|
1063
|
+
"-c",
|
|
1064
|
+
"http.postBuffer=524288000",
|
|
1065
|
+
"pull",
|
|
1066
|
+
"--no-rebase",
|
|
1067
|
+
"--allow-unrelated-histories",
|
|
1068
|
+
authUrl,
|
|
1069
|
+
targetBranch
|
|
1070
|
+
];
|
|
1071
|
+
const pullOutput = await new Promise((resolve, reject) => {
|
|
1072
|
+
const child = child_process.spawn("git", pullArgs, {
|
|
1073
|
+
cwd: repoPath,
|
|
1074
|
+
env: {
|
|
1075
|
+
...process.env,
|
|
1076
|
+
GIT_LFS_SKIP_SMUDGE: "1",
|
|
1077
|
+
GCM_INTERACTIVE: "false",
|
|
1078
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1079
|
+
GIT_ASKPASS: "echo"
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
let out = "";
|
|
1083
|
+
child.stdout.on("data", (d) => {
|
|
1084
|
+
out += d.toString();
|
|
1085
|
+
});
|
|
1086
|
+
child.stderr.on("data", (d) => {
|
|
1087
|
+
out += d.toString();
|
|
1088
|
+
});
|
|
1089
|
+
child.on("close", (code) => {
|
|
1090
|
+
if (code === 0) {
|
|
1091
|
+
resolve(out);
|
|
1092
|
+
} else {
|
|
1093
|
+
reject(new Error(out || `Git pull exited with code ${code}`));
|
|
1094
|
+
}
|
|
1095
|
+
});
|
|
1096
|
+
child.on("error", (err) => reject(err));
|
|
1097
|
+
});
|
|
1098
|
+
if (didStash) {
|
|
1099
|
+
try {
|
|
1100
|
+
await git.raw(["stash", "pop"]);
|
|
1101
|
+
} catch (popErr) {
|
|
1102
|
+
console.warn("Auto-stash pop had merge notice:", popErr?.message);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
const alreadyUpToDate = pullOutput.includes("Already up to date.") || pullOutput.includes("Already up-to-date.");
|
|
1106
|
+
const lines = pullOutput.split(/[\r\n]+/).map((l) => l.trim());
|
|
1107
|
+
const changedFiles2 = [];
|
|
1108
|
+
for (const l of lines) {
|
|
1109
|
+
if (l.includes("|") && (l.includes("+") || l.includes("-"))) {
|
|
1110
|
+
const filePart = l.split("|")[0].trim();
|
|
1111
|
+
if (filePart) changedFiles2.push(filePart);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
const message = alreadyUpToDate ? "Already up to date with remote." : changedFiles2.length > 0 ? `Successfully pulled ${changedFiles2.length} file(s) from team!` : "Successfully pulled latest changes from team!";
|
|
1115
|
+
return {
|
|
1116
|
+
success: true,
|
|
1117
|
+
alreadyUpToDate,
|
|
1118
|
+
message,
|
|
1119
|
+
filesChanged: changedFiles2,
|
|
1120
|
+
raw: pullOutput
|
|
1121
|
+
};
|
|
1122
|
+
} catch (err) {
|
|
1123
|
+
if (didStash) {
|
|
1124
|
+
try {
|
|
1125
|
+
await git.raw(["stash", "pop"]);
|
|
1126
|
+
} catch {
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
const token = getToken(sessionId);
|
|
1130
|
+
let cleanMsg = err?.message || String(err);
|
|
1131
|
+
if (token) {
|
|
1132
|
+
cleanMsg = cleanMsg.split(token).join("***");
|
|
1133
|
+
}
|
|
1134
|
+
cleanMsg = cleanMsg.replace(/https:\/\/[^@]+@github\.com/g, "https://github.com");
|
|
1135
|
+
throw new Error(cleanMsg);
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1138
|
+
async function checkRemoteUpdates(sessionId) {
|
|
1139
|
+
const { git, repoPath } = getGitSession(sessionId);
|
|
1140
|
+
try {
|
|
1141
|
+
const remotes = await getRemotes(sessionId);
|
|
1142
|
+
if (!remotes || remotes.length === 0) return { behind: 0, ahead: 0, current: "" };
|
|
1143
|
+
const remoteObj = remotes[0];
|
|
1144
|
+
const authUrl = remoteObj.refs?.fetch ? injectAuthToken(sessionId, remoteObj.refs.fetch) : remoteObj.name;
|
|
1145
|
+
await new Promise((resolve) => {
|
|
1146
|
+
const child = child_process.spawn(
|
|
1147
|
+
"git",
|
|
1148
|
+
["-c", "credential.helper=", "-c", "core.longpaths=true", "fetch", "--prune", authUrl],
|
|
1149
|
+
{
|
|
1150
|
+
cwd: repoPath,
|
|
1151
|
+
env: {
|
|
1152
|
+
...process.env,
|
|
1153
|
+
GIT_LFS_SKIP_SMUDGE: "1",
|
|
1154
|
+
GCM_INTERACTIVE: "false",
|
|
1155
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1156
|
+
GIT_ASKPASS: "echo"
|
|
1157
|
+
}
|
|
1158
|
+
}
|
|
1159
|
+
);
|
|
1160
|
+
child.on("close", () => resolve());
|
|
1161
|
+
child.on("error", () => resolve());
|
|
1162
|
+
});
|
|
1163
|
+
const status = await git.status();
|
|
1164
|
+
return {
|
|
1165
|
+
behind: status.behind,
|
|
1166
|
+
ahead: status.ahead,
|
|
1167
|
+
current: status.current || "main"
|
|
1168
|
+
};
|
|
1169
|
+
} catch {
|
|
1170
|
+
return { behind: 0, ahead: 0, current: "" };
|
|
1027
1171
|
}
|
|
1028
|
-
return await git.pull(remote, branch, options);
|
|
1029
1172
|
}
|
|
1030
1173
|
async function resetToRemote(sessionId, remote, branch) {
|
|
1031
1174
|
const { git } = getGitSession(sessionId);
|
|
@@ -1405,39 +1548,122 @@ async function cloneRepository(sessionId, remoteUrl, destPath) {
|
|
|
1405
1548
|
await existingGit.raw(["remote", "add", "origin", authUrl]);
|
|
1406
1549
|
}
|
|
1407
1550
|
await existingGit.raw(["-c", "credential.helper=", "fetch", "--progress", "origin"]);
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1551
|
+
await new Promise((resolve) => {
|
|
1552
|
+
const child = child_process.spawn(
|
|
1553
|
+
"git",
|
|
1554
|
+
["-c", "core.longpaths=true", "-c", "filter.lfs.required=false", "-c", "filter.lfs.smudge=", "checkout", "-f"],
|
|
1555
|
+
{
|
|
1556
|
+
cwd: destPath,
|
|
1557
|
+
env: { ...process.env, GIT_LFS_SKIP_SMUDGE: "1" }
|
|
1558
|
+
}
|
|
1559
|
+
);
|
|
1560
|
+
child.on("close", () => resolve());
|
|
1561
|
+
child.on("error", () => resolve());
|
|
1562
|
+
});
|
|
1414
1563
|
return destPath;
|
|
1415
1564
|
} catch (err) {
|
|
1416
1565
|
console.warn("Re-using existing repo failed, falling through to clone attempt:", err);
|
|
1417
1566
|
}
|
|
1418
1567
|
}
|
|
1419
|
-
if (destExists && destIsEmpty) {
|
|
1420
|
-
const emptyDirGit = createSafeGit(destPath, sessionId);
|
|
1421
|
-
await emptyDirGit.raw(["-c", "credential.helper=", "clone", "--progress", authUrl, "."]);
|
|
1422
|
-
try {
|
|
1423
|
-
await createSafeGit(void 0, sessionId).raw(["config", "--global", "--add", "safe.directory", destPath]);
|
|
1424
|
-
} catch (e) {
|
|
1425
|
-
}
|
|
1426
|
-
return destPath;
|
|
1427
|
-
}
|
|
1428
1568
|
if (destExists && !destIsEmpty && !destHasGit) {
|
|
1429
1569
|
throw new Error(
|
|
1430
1570
|
`The folder '${path__namespace.basename(destPath)}' already contains files. Please select an empty folder or a different location to clone.`
|
|
1431
1571
|
);
|
|
1432
1572
|
}
|
|
1433
|
-
const
|
|
1434
|
-
const
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1573
|
+
const wc = electron.webContents.fromId(sessionId);
|
|
1574
|
+
const cloneArgs = [
|
|
1575
|
+
"-c",
|
|
1576
|
+
"credential.helper=",
|
|
1577
|
+
"-c",
|
|
1578
|
+
"core.longpaths=true",
|
|
1579
|
+
"-c",
|
|
1580
|
+
"filter.lfs.required=false",
|
|
1581
|
+
"-c",
|
|
1582
|
+
"filter.lfs.smudge=",
|
|
1583
|
+
"-c",
|
|
1584
|
+
"http.postBuffer=524288000",
|
|
1585
|
+
"-c",
|
|
1586
|
+
"core.compression=0",
|
|
1587
|
+
"-c",
|
|
1588
|
+
"pack.windowMemory=256m",
|
|
1589
|
+
"clone",
|
|
1590
|
+
"--progress",
|
|
1591
|
+
authUrl
|
|
1592
|
+
];
|
|
1593
|
+
let cwd;
|
|
1594
|
+
if (destExists && destIsEmpty) {
|
|
1595
|
+
cloneArgs.push(".");
|
|
1596
|
+
cwd = destPath;
|
|
1597
|
+
} else {
|
|
1598
|
+
cloneArgs.push(path__namespace.basename(destPath));
|
|
1599
|
+
cwd = path__namespace.dirname(destPath);
|
|
1440
1600
|
}
|
|
1601
|
+
await new Promise((resolve, reject) => {
|
|
1602
|
+
const child = child_process.spawn("git", cloneArgs, {
|
|
1603
|
+
cwd,
|
|
1604
|
+
env: {
|
|
1605
|
+
...process.env,
|
|
1606
|
+
GIT_LFS_SKIP_SMUDGE: "1",
|
|
1607
|
+
GCM_INTERACTIVE: "false",
|
|
1608
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
1609
|
+
GIT_ASKPASS: "echo"
|
|
1610
|
+
}
|
|
1611
|
+
});
|
|
1612
|
+
const errorLines = [];
|
|
1613
|
+
child.stderr.on("data", (data) => {
|
|
1614
|
+
const text = data.toString();
|
|
1615
|
+
const match = text.match(
|
|
1616
|
+
/(Receiving objects|Resolving deltas|Counting objects|Compressing objects):\s+(\d+)%/i
|
|
1617
|
+
);
|
|
1618
|
+
if (match && wc) {
|
|
1619
|
+
const stage = match[1];
|
|
1620
|
+
const progress = parseInt(match[2], 10);
|
|
1621
|
+
try {
|
|
1622
|
+
wc.send("git-progress", { method: "clone", stage, progress });
|
|
1623
|
+
} catch {
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
const lines = text.split(/[\r\n]+/).map((l) => l.trim()).filter(Boolean);
|
|
1627
|
+
for (const line of lines) {
|
|
1628
|
+
if (!line.startsWith("Receiving objects:") && !line.startsWith("Resolving deltas:") && !line.startsWith("Counting objects:") && !line.startsWith("Compressing objects:") && !line.startsWith("Cloning into") && !line.startsWith("remote:")) {
|
|
1629
|
+
errorLines.push(line);
|
|
1630
|
+
if (errorLines.length > 10) errorLines.shift();
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
child.on("error", (err) => reject(err));
|
|
1635
|
+
child.on("close", async (code) => {
|
|
1636
|
+
if (code === 0) {
|
|
1637
|
+
try {
|
|
1638
|
+
await createSafeGit(void 0, sessionId).raw([
|
|
1639
|
+
"config",
|
|
1640
|
+
"--global",
|
|
1641
|
+
"--add",
|
|
1642
|
+
"safe.directory",
|
|
1643
|
+
destPath
|
|
1644
|
+
]);
|
|
1645
|
+
} catch {
|
|
1646
|
+
}
|
|
1647
|
+
resolve();
|
|
1648
|
+
} else {
|
|
1649
|
+
const token = getToken(sessionId);
|
|
1650
|
+
let errorMsg = errorLines.join("\n") || `Git clone failed (exit code ${code})`;
|
|
1651
|
+
if (token) {
|
|
1652
|
+
errorMsg = errorMsg.split(token).join("***");
|
|
1653
|
+
}
|
|
1654
|
+
errorMsg = errorMsg.replace(/https:\/\/[^@]+@github\.com/g, "https://github.com");
|
|
1655
|
+
if (errorMsg.includes("terminal prompts disabled") || errorMsg.includes("Authentication failed") || errorMsg.includes("could not read Username")) {
|
|
1656
|
+
reject(
|
|
1657
|
+
new Error(
|
|
1658
|
+
"Authentication failed for GitHub repository. Please verify your account is logged in and has access to this repository."
|
|
1659
|
+
)
|
|
1660
|
+
);
|
|
1661
|
+
} else {
|
|
1662
|
+
reject(new Error(errorMsg));
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
});
|
|
1666
|
+
});
|
|
1441
1667
|
return destPath;
|
|
1442
1668
|
} catch (err) {
|
|
1443
1669
|
const token = getToken(sessionId);
|
|
@@ -1802,6 +2028,7 @@ const getLastWorkspace = async () => {
|
|
|
1802
2028
|
};
|
|
1803
2029
|
let currentWatcher = null;
|
|
1804
2030
|
let syncTimeout = null;
|
|
2031
|
+
let remotePollInterval = null;
|
|
1805
2032
|
let isAutoSyncEnabled = false;
|
|
1806
2033
|
let isResolving = false;
|
|
1807
2034
|
let globalOnLog = null;
|
|
@@ -1822,13 +2049,33 @@ function toggleAutoSync(sessionId, repoPath, enabled, onLog, onErrorApproval, on
|
|
|
1822
2049
|
}
|
|
1823
2050
|
if (syncTimeout) {
|
|
1824
2051
|
clearTimeout(syncTimeout);
|
|
2052
|
+
syncTimeout = null;
|
|
2053
|
+
}
|
|
2054
|
+
if (remotePollInterval) {
|
|
2055
|
+
clearInterval(remotePollInterval);
|
|
2056
|
+
remotePollInterval = null;
|
|
1825
2057
|
}
|
|
1826
2058
|
changedFiles.clear();
|
|
1827
2059
|
if (!enabled) {
|
|
1828
2060
|
onLog("Auto-Sync disabled.");
|
|
1829
2061
|
return;
|
|
1830
2062
|
}
|
|
1831
|
-
onLog("⚡ Auto-Sync enabled
|
|
2063
|
+
onLog("⚡ Auto-Sync enabled (Watching local edits & syncing team changes from GitHub)...");
|
|
2064
|
+
remotePollInterval = setInterval(async () => {
|
|
2065
|
+
if (!isAutoSyncEnabled || isResolving) return;
|
|
2066
|
+
try {
|
|
2067
|
+
const updates = await checkRemoteUpdates(sessionId);
|
|
2068
|
+
if (updates.behind > 0 && !isResolving) {
|
|
2069
|
+
onLog(`📥 Found ${updates.behind} new commit(s) from team members on GitHub! Syncing to your computer...`);
|
|
2070
|
+
const result = await pullFromRemote(sessionId);
|
|
2071
|
+
onLog(`✅ ${result.message}`);
|
|
2072
|
+
if (globalOnResolveComplete) {
|
|
2073
|
+
globalOnResolveComplete();
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2076
|
+
} catch {
|
|
2077
|
+
}
|
|
2078
|
+
}, 25e3);
|
|
1832
2079
|
currentWatcher = chokidar.watch(repoPath, {
|
|
1833
2080
|
ignored: [
|
|
1834
2081
|
/(^|[\/\\])\../,
|
|
@@ -2195,6 +2442,9 @@ electron.app.whenReady().then(async () => {
|
|
|
2195
2442
|
electron.ipcMain.handle("pull-from-remote", async (event, remote, branch) => {
|
|
2196
2443
|
return await pullFromRemote(event.sender.id, remote, branch);
|
|
2197
2444
|
});
|
|
2445
|
+
electron.ipcMain.handle("check-remote-updates", async (event) => {
|
|
2446
|
+
return await checkRemoteUpdates(event.sender.id);
|
|
2447
|
+
});
|
|
2198
2448
|
electron.ipcMain.handle("reset-to-remote", async (event, remote, branch) => {
|
|
2199
2449
|
return await resetToRemote(event.sender.id, remote, branch);
|
|
2200
2450
|
});
|
package/out/preload/index.js
CHANGED
|
@@ -20,6 +20,7 @@ const api = {
|
|
|
20
20
|
commitChanges: (message) => electron.ipcRenderer.invoke("commit-changes", message),
|
|
21
21
|
pushToRemote: (remote, branch) => electron.ipcRenderer.invoke("push-to-remote", remote, branch),
|
|
22
22
|
pullFromRemote: (remote, branch) => electron.ipcRenderer.invoke("pull-from-remote", remote, branch),
|
|
23
|
+
checkRemoteUpdates: () => electron.ipcRenderer.invoke("check-remote-updates"),
|
|
23
24
|
resetToRemote: (remote, branch) => electron.ipcRenderer.invoke("reset-to-remote", remote, branch),
|
|
24
25
|
fetchRemote: () => electron.ipcRenderer.invoke("fetch-remote"),
|
|
25
26
|
getBranches: () => electron.ipcRenderer.invoke("get-branches"),
|