git-drive 0.1.0 → 0.1.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/dist/commands/archive.js +32 -0
- package/dist/commands/init.js +27 -0
- package/dist/commands/link.js +139 -0
- package/dist/commands/list.js +83 -0
- package/dist/commands/push.js +99 -0
- package/dist/commands/restore.js +30 -0
- package/dist/commands/status.js +116 -0
- package/dist/config.js +62 -0
- package/dist/errors.js +30 -0
- package/dist/git.js +60 -0
- package/dist/index.js +89 -0
- package/dist/server.js +474 -0
- package/package.json +46 -14
- package/.github/workflows/ci.yml +0 -77
- package/.planning/codebase/ARCHITECTURE.md +0 -151
- package/.planning/codebase/CONCERNS.md +0 -191
- package/.planning/codebase/CONVENTIONS.md +0 -169
- package/.planning/codebase/INTEGRATIONS.md +0 -94
- package/.planning/codebase/STACK.md +0 -77
- package/.planning/codebase/STRUCTURE.md +0 -157
- package/.planning/codebase/TESTING.md +0 -156
- package/Dockerfile.cli +0 -30
- package/Dockerfile.server +0 -32
- package/README.md +0 -95
- package/docker-compose.yml +0 -48
- package/packages/cli/Dockerfile +0 -26
- package/packages/cli/package.json +0 -57
- package/packages/cli/src/commands/archive.ts +0 -39
- package/packages/cli/src/commands/init.ts +0 -34
- package/packages/cli/src/commands/link.ts +0 -115
- package/packages/cli/src/commands/list.ts +0 -94
- package/packages/cli/src/commands/push.ts +0 -64
- package/packages/cli/src/commands/restore.ts +0 -36
- package/packages/cli/src/commands/status.ts +0 -127
- package/packages/cli/src/config.ts +0 -73
- package/packages/cli/src/errors.ts +0 -23
- package/packages/cli/src/git.ts +0 -55
- package/packages/cli/src/index.ts +0 -97
- package/packages/cli/src/server.ts +0 -514
- package/packages/cli/tsconfig.json +0 -13
- package/packages/git-drive-docker/package.json +0 -15
- package/packages/server/package.json +0 -44
- package/packages/server/src/index.ts +0 -569
- package/packages/server/tsconfig.json +0 -9
- package/packages/ui/README.md +0 -73
- package/packages/ui/eslint.config.js +0 -23
- package/packages/ui/index.html +0 -13
- package/packages/ui/package.json +0 -42
- package/packages/ui/postcss.config.js +0 -6
- package/packages/ui/public/vite.svg +0 -1
- package/packages/ui/src/App.css +0 -23
- package/packages/ui/src/App.tsx +0 -726
- package/packages/ui/src/assets/react.svg +0 -8
- package/packages/ui/src/assets/vite.svg +0 -3
- package/packages/ui/src/index.css +0 -37
- package/packages/ui/src/main.tsx +0 -14
- package/packages/ui/tailwind.config.js +0 -11
- package/packages/ui/tsconfig.app.json +0 -28
- package/packages/ui/tsconfig.json +0 -26
- package/packages/ui/tsconfig.node.json +0 -12
- package/packages/ui/vite.config.ts +0 -7
- package/pnpm-workspace.yaml +0 -4
- package/rewrite_app.js +0 -731
- package/tsconfig.json +0 -14
- /package/{packages/cli/ui → ui}/assets/index-Cc2q1t5k.js +0 -0
- /package/{packages/cli/ui → ui}/assets/index-DrL7ojPA.css +0 -0
- /package/{packages/cli/ui → ui}/index.html +0 -0
- /package/{packages/cli/ui → ui}/vite.svg +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.archive = archive;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const config_js_1 = require("../config.js");
|
|
6
|
+
const git_js_1 = require("../git.js");
|
|
7
|
+
const push_js_1 = require("./push.js");
|
|
8
|
+
const errors_js_1 = require("../errors.js");
|
|
9
|
+
function archive(args) {
|
|
10
|
+
if (!(0, git_js_1.isGitRepo)()) {
|
|
11
|
+
throw new errors_js_1.GitDriveError("Not in a git repository.");
|
|
12
|
+
}
|
|
13
|
+
const force = args.includes("--force");
|
|
14
|
+
// Check for uncommitted changes
|
|
15
|
+
if (!force) {
|
|
16
|
+
const status = (0, git_js_1.git)("status --porcelain");
|
|
17
|
+
if (status) {
|
|
18
|
+
throw new errors_js_1.GitDriveError("Working tree has uncommitted changes.\nCommit first or use --force to archive anyway.");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const config = (0, config_js_1.requireConfig)();
|
|
22
|
+
(0, config_js_1.assertDriveMounted)(config.drivePath);
|
|
23
|
+
const projectName = (0, git_js_1.getProjectName)();
|
|
24
|
+
const repoRoot = (0, git_js_1.getRepoRoot)();
|
|
25
|
+
// Push first
|
|
26
|
+
(0, push_js_1.push)([]);
|
|
27
|
+
// Remove local copy
|
|
28
|
+
process.chdir("..");
|
|
29
|
+
(0, fs_1.rmSync)(repoRoot, { recursive: true, force: true });
|
|
30
|
+
console.log(`Archived: ${projectName}`);
|
|
31
|
+
console.log(`Restore with: git drive restore ${projectName}`);
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.init = init;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const config_js_1 = require("../config.js");
|
|
7
|
+
const errors_js_1 = require("../errors.js");
|
|
8
|
+
function init(args) {
|
|
9
|
+
const rawPath = args[0];
|
|
10
|
+
if (!rawPath) {
|
|
11
|
+
throw new errors_js_1.GitDriveError("Usage: git drive init <path>");
|
|
12
|
+
}
|
|
13
|
+
const drivePath = (0, path_1.resolve)(rawPath);
|
|
14
|
+
if (!(0, fs_1.existsSync)(drivePath)) {
|
|
15
|
+
throw new errors_js_1.GitDriveError(`Path not found: ${drivePath}\nIs the drive mounted?`);
|
|
16
|
+
}
|
|
17
|
+
const stat = (0, fs_1.statSync)(drivePath);
|
|
18
|
+
if (!stat.isDirectory()) {
|
|
19
|
+
throw new errors_js_1.GitDriveError(`Path is not a directory: ${drivePath}`);
|
|
20
|
+
}
|
|
21
|
+
const storePath = (0, config_js_1.getDriveStorePath)(drivePath);
|
|
22
|
+
if (!(0, fs_1.existsSync)(storePath)) {
|
|
23
|
+
(0, fs_1.mkdirSync)(storePath, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
(0, config_js_1.saveConfig)({ drivePath });
|
|
26
|
+
console.log(`Drive configured: ${storePath}`);
|
|
27
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.link = link;
|
|
40
|
+
const errors_js_1 = require("../errors.js");
|
|
41
|
+
const config_js_1 = require("../config.js");
|
|
42
|
+
const git_js_1 = require("../git.js");
|
|
43
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
async function link(args) {
|
|
47
|
+
try {
|
|
48
|
+
const drives = await (0, git_js_1.listDrives)();
|
|
49
|
+
// Only fetch drives that actually have .git-drive configured
|
|
50
|
+
const configuredDrives = drives
|
|
51
|
+
.filter((drive) => drive.mounted && drive.mounted !== "/")
|
|
52
|
+
.filter((drive) => fs.existsSync(path.join(drive.mounted, ".git-drive")));
|
|
53
|
+
if (configuredDrives.length === 0) {
|
|
54
|
+
console.log("No initialized git-drives found. Please initialize a drive first.");
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const { drive } = await (0, prompts_1.default)({
|
|
58
|
+
type: "select",
|
|
59
|
+
name: "drive",
|
|
60
|
+
message: "Select a configured git-drive:",
|
|
61
|
+
choices: configuredDrives.map((d) => ({
|
|
62
|
+
title: `${d.filesystem} (${d.mounted})`,
|
|
63
|
+
value: d,
|
|
64
|
+
})),
|
|
65
|
+
});
|
|
66
|
+
if (!drive)
|
|
67
|
+
return;
|
|
68
|
+
const gitDrivePath = path.join(drive.mounted, ".git-drive");
|
|
69
|
+
const existingRepos = fs.readdirSync(gitDrivePath).filter((entry) => {
|
|
70
|
+
const entryPath = path.join(gitDrivePath, entry);
|
|
71
|
+
return (fs.statSync(entryPath).isDirectory() &&
|
|
72
|
+
(entry.endsWith(".git") || fs.existsSync(path.join(entryPath, "HEAD"))));
|
|
73
|
+
});
|
|
74
|
+
const CREATE_NEW = "__CREATE_NEW__";
|
|
75
|
+
const { selectedRepo } = await (0, prompts_1.default)({
|
|
76
|
+
type: "select",
|
|
77
|
+
name: "selectedRepo",
|
|
78
|
+
message: "Select an existing repository to link, or create a new one:",
|
|
79
|
+
choices: [
|
|
80
|
+
{ title: "✨ Create new repository...", value: CREATE_NEW },
|
|
81
|
+
...existingRepos.map((repo) => ({
|
|
82
|
+
title: `📁 ${repo.replace(/\.git$/, "")}`,
|
|
83
|
+
value: repo,
|
|
84
|
+
})),
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
if (!selectedRepo)
|
|
88
|
+
return;
|
|
89
|
+
let targetRepoName = selectedRepo;
|
|
90
|
+
if (selectedRepo === CREATE_NEW) {
|
|
91
|
+
const defaultName = (0, git_js_1.getProjectName)();
|
|
92
|
+
const { newRepoName } = await (0, prompts_1.default)({
|
|
93
|
+
type: "text",
|
|
94
|
+
name: "newRepoName",
|
|
95
|
+
message: "Enter the new repository name:",
|
|
96
|
+
initial: defaultName,
|
|
97
|
+
});
|
|
98
|
+
if (!newRepoName)
|
|
99
|
+
return;
|
|
100
|
+
targetRepoName = newRepoName.endsWith(".git") ? newRepoName : `${newRepoName}.git`;
|
|
101
|
+
const repoPath = path.join(gitDrivePath, targetRepoName);
|
|
102
|
+
if (fs.existsSync(repoPath)) {
|
|
103
|
+
console.log(`Repository ${targetRepoName} already exists in this drive.`);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
(0, git_js_1.git)(`init --bare "${repoPath}"`);
|
|
107
|
+
console.log(`Created new bare repository: ${targetRepoName}`);
|
|
108
|
+
}
|
|
109
|
+
const repoRoot = (0, git_js_1.getRepoRoot)();
|
|
110
|
+
const finalRepoPath = path.join(gitDrivePath, targetRepoName);
|
|
111
|
+
// Check if remote 'gd' already exists
|
|
112
|
+
let gdExists = false;
|
|
113
|
+
try {
|
|
114
|
+
(0, git_js_1.git)(`remote get-url gd`, repoRoot);
|
|
115
|
+
gdExists = true;
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Remote does not exist
|
|
119
|
+
}
|
|
120
|
+
if (gdExists) {
|
|
121
|
+
console.log("Remote 'gd' already exists. Updating it to point to the new drive.");
|
|
122
|
+
(0, git_js_1.git)(`remote set-url gd "${finalRepoPath}"`, repoRoot);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
(0, git_js_1.git)(`remote add gd "${finalRepoPath}"`, repoRoot);
|
|
126
|
+
}
|
|
127
|
+
// Persist to global git-drive registry for the Web UI
|
|
128
|
+
(0, config_js_1.saveLink)(repoRoot, drive.mounted, targetRepoName);
|
|
129
|
+
console.log(`\n✅ Successfully linked!`);
|
|
130
|
+
console.log(`Repository: ${targetRepoName.replace(/\.git$/, "")}`);
|
|
131
|
+
console.log(`Drive: ${drive.mounted}`);
|
|
132
|
+
console.log(`\nYou can now push to this remote using:`);
|
|
133
|
+
console.log(` git push gd main`);
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
(0, errors_js_1.handleError)(err);
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.list = list;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const os_1 = require("os");
|
|
7
|
+
const node_disk_info_1 = require("node-disk-info");
|
|
8
|
+
function loadLinks() {
|
|
9
|
+
const linksFile = (0, path_1.join)((0, os_1.homedir)(), ".config", "git-drive", "links.json");
|
|
10
|
+
if (!(0, fs_1.existsSync)(linksFile))
|
|
11
|
+
return {};
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse((0, fs_1.readFileSync)(linksFile, "utf-8"));
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return {};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function getGitDrivePath(mountpoint) {
|
|
20
|
+
return (0, path_1.join)(mountpoint, ".git-drive");
|
|
21
|
+
}
|
|
22
|
+
async function list(_args) {
|
|
23
|
+
console.log("Git Drive - Connected Drives\n");
|
|
24
|
+
// Get all connected drives
|
|
25
|
+
let drives = [];
|
|
26
|
+
try {
|
|
27
|
+
drives = await (0, node_disk_info_1.getDiskInfo)();
|
|
28
|
+
}
|
|
29
|
+
catch (err) {
|
|
30
|
+
console.error("Error detecting drives:", err);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Filter to external/removable drives
|
|
34
|
+
const externalDrives = drives.filter((d) => {
|
|
35
|
+
const mp = d.mounted;
|
|
36
|
+
if (!mp)
|
|
37
|
+
return false;
|
|
38
|
+
if (mp === "/" || mp === "100%")
|
|
39
|
+
return false;
|
|
40
|
+
if (process.platform === "darwin") {
|
|
41
|
+
return mp.startsWith("/Volumes/") && !mp.startsWith("/Volumes/Recovery");
|
|
42
|
+
}
|
|
43
|
+
if (mp.startsWith("/sys") || mp.startsWith("/proc") || mp.startsWith("/run") || mp.startsWith("/snap") || mp.startsWith("/boot"))
|
|
44
|
+
return false;
|
|
45
|
+
if (d.filesystem === "tmpfs" || d.filesystem === "devtmpfs" || d.filesystem === "udev" || d.filesystem === "overlay")
|
|
46
|
+
return false;
|
|
47
|
+
return true;
|
|
48
|
+
});
|
|
49
|
+
if (externalDrives.length === 0) {
|
|
50
|
+
console.log("No external drives detected.");
|
|
51
|
+
console.log("\nConnect an external drive and try again.");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Load links to show which drives are registered
|
|
55
|
+
const links = loadLinks();
|
|
56
|
+
const registeredMountpoints = new Set(Object.values(links).map(l => l.mountpoint));
|
|
57
|
+
for (const drive of externalDrives) {
|
|
58
|
+
const mp = drive.mounted;
|
|
59
|
+
const gitDrivePath = getGitDrivePath(mp);
|
|
60
|
+
const hasGitDrive = (0, fs_1.existsSync)(gitDrivePath);
|
|
61
|
+
const isRegistered = registeredMountpoints.has(mp);
|
|
62
|
+
// Count repos on this drive
|
|
63
|
+
let repoCount = 0;
|
|
64
|
+
if (hasGitDrive) {
|
|
65
|
+
try {
|
|
66
|
+
const entries = (0, fs_1.readdirSync)(gitDrivePath).filter(n => n.endsWith(".git") || (0, fs_1.existsSync)((0, path_1.join)(gitDrivePath, n, "HEAD")));
|
|
67
|
+
repoCount = entries.length;
|
|
68
|
+
}
|
|
69
|
+
catch { }
|
|
70
|
+
}
|
|
71
|
+
// Format size
|
|
72
|
+
const sizeGB = drive.blocks ? ((parseInt(drive.blocks) * 1024) / (1024 * 1024 * 1024)).toFixed(1) : "?";
|
|
73
|
+
// Status indicator
|
|
74
|
+
const status = hasGitDrive ? "✓ registered" : "○ not registered";
|
|
75
|
+
console.log(` ${mp}`);
|
|
76
|
+
console.log(` Size: ${sizeGB} GB`);
|
|
77
|
+
console.log(` Status: ${status}`);
|
|
78
|
+
console.log(` Repositories: ${repoCount}`);
|
|
79
|
+
console.log();
|
|
80
|
+
}
|
|
81
|
+
console.log(`\n${externalDrives.length} drive${externalDrives.length === 1 ? "" : "s"} detected.`);
|
|
82
|
+
console.log("\nRun 'git-drive link' to link a repo to a drive.");
|
|
83
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.push = push;
|
|
40
|
+
const git_js_1 = require("../git.js");
|
|
41
|
+
const errors_js_1 = require("../errors.js");
|
|
42
|
+
const prompts_1 = __importDefault(require("prompts"));
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const os = __importStar(require("os"));
|
|
46
|
+
async function push(_args) {
|
|
47
|
+
if (!(0, git_js_1.isGitRepo)()) {
|
|
48
|
+
throw new errors_js_1.GitDriveError("Not in a git repository.");
|
|
49
|
+
}
|
|
50
|
+
const existingUrl = (0, git_js_1.getRemoteUrl)("gd");
|
|
51
|
+
if (!existingUrl) {
|
|
52
|
+
throw new errors_js_1.GitDriveError("No git-drive linked for this project. Please run 'git-drive link' first.");
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
const currentBranch = (0, git_js_1.git)("branch --show-current") || "HEAD";
|
|
56
|
+
const { pushMode } = await (0, prompts_1.default)({
|
|
57
|
+
type: "select",
|
|
58
|
+
name: "pushMode",
|
|
59
|
+
message: `Pushing to ${existingUrl}\nSelect what to branch to push:`,
|
|
60
|
+
choices: [
|
|
61
|
+
{ title: `Current branch only (${currentBranch})`, value: "current" },
|
|
62
|
+
{ title: "All branches & tags", value: "all" }
|
|
63
|
+
]
|
|
64
|
+
});
|
|
65
|
+
if (!pushMode)
|
|
66
|
+
return;
|
|
67
|
+
if (pushMode === "current") {
|
|
68
|
+
console.log(`\nPushing ${currentBranch}...`);
|
|
69
|
+
(0, git_js_1.git)(`push gd ${currentBranch}`);
|
|
70
|
+
console.log(`✅ Successfully pushed ${currentBranch} to git-drive.`);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
console.log("\nPushing all branches and tags...");
|
|
74
|
+
(0, git_js_1.git)("push gd --all");
|
|
75
|
+
(0, git_js_1.git)("push gd --tags");
|
|
76
|
+
console.log(`✅ Successfully pushed all branches and tags to git-drive.`);
|
|
77
|
+
}
|
|
78
|
+
// Write context to a central pushlog safely inside the git-drive repo folder
|
|
79
|
+
try {
|
|
80
|
+
if (fs.existsSync(existingUrl)) {
|
|
81
|
+
const payload = {
|
|
82
|
+
date: new Date().toISOString(),
|
|
83
|
+
computer: os.hostname(),
|
|
84
|
+
user: os.userInfo().username,
|
|
85
|
+
localDir: process.cwd(),
|
|
86
|
+
mode: pushMode,
|
|
87
|
+
};
|
|
88
|
+
const logFile = path.join(existingUrl, "git-drive-pushlog.json");
|
|
89
|
+
fs.appendFileSync(logFile, JSON.stringify(payload) + "\n", "utf-8");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Intentionally swallow telemetry tracking errors
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
throw new errors_js_1.GitDriveError(`Failed to push to drive. Make sure the drive is connected.\n${err.message}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.restore = restore;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const config_js_1 = require("../config.js");
|
|
7
|
+
const git_js_1 = require("../git.js");
|
|
8
|
+
const errors_js_1 = require("../errors.js");
|
|
9
|
+
function restore(args) {
|
|
10
|
+
const projectName = args[0];
|
|
11
|
+
const targetDir = args[1] || projectName;
|
|
12
|
+
if (!projectName) {
|
|
13
|
+
throw new errors_js_1.GitDriveError("Usage: git drive restore <project-name> [target-dir]");
|
|
14
|
+
}
|
|
15
|
+
const config = (0, config_js_1.requireConfig)();
|
|
16
|
+
(0, config_js_1.assertDriveMounted)(config.drivePath);
|
|
17
|
+
const storePath = (0, config_js_1.getDriveStorePath)(config.drivePath);
|
|
18
|
+
const bareRepoPath = (0, path_1.join)(storePath, `${projectName}.git`);
|
|
19
|
+
if (!(0, fs_1.existsSync)(bareRepoPath)) {
|
|
20
|
+
throw new errors_js_1.GitDriveError(`Project '${projectName}' not found on drive.`);
|
|
21
|
+
}
|
|
22
|
+
const targetPath = (0, path_1.resolve)(targetDir);
|
|
23
|
+
if ((0, fs_1.existsSync)(targetPath)) {
|
|
24
|
+
throw new errors_js_1.GitDriveError(`Directory already exists: ${targetPath}`);
|
|
25
|
+
}
|
|
26
|
+
(0, git_js_1.git)(`clone ${bareRepoPath} ${targetPath}`);
|
|
27
|
+
// Rename origin to drive so the remote stays consistent
|
|
28
|
+
(0, git_js_1.git)("remote rename origin drive", targetPath);
|
|
29
|
+
console.log(`Restored ${projectName} into ${targetPath}`);
|
|
30
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.status = status;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const os_1 = require("os");
|
|
7
|
+
const node_disk_info_1 = require("node-disk-info");
|
|
8
|
+
const git_js_1 = require("../git.js");
|
|
9
|
+
function loadLinks() {
|
|
10
|
+
const linksFile = (0, path_1.join)((0, os_1.homedir)(), ".config", "git-drive", "links.json");
|
|
11
|
+
if (!(0, fs_1.existsSync)(linksFile))
|
|
12
|
+
return {};
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse((0, fs_1.readFileSync)(linksFile, "utf-8"));
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function getGitDrivePath(mountpoint) {
|
|
21
|
+
return (0, path_1.join)(mountpoint, ".git-drive");
|
|
22
|
+
}
|
|
23
|
+
async function status(_args) {
|
|
24
|
+
console.log("Git Drive Status\n");
|
|
25
|
+
// Get all connected drives
|
|
26
|
+
let drives = [];
|
|
27
|
+
try {
|
|
28
|
+
drives = await (0, node_disk_info_1.getDiskInfo)();
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
console.error("Error detecting drives:", err);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Filter to external/removable drives
|
|
35
|
+
const externalDrives = drives.filter((d) => {
|
|
36
|
+
const mp = d.mounted;
|
|
37
|
+
if (!mp)
|
|
38
|
+
return false;
|
|
39
|
+
if (mp === "/" || mp === "100%")
|
|
40
|
+
return false;
|
|
41
|
+
if (process.platform === "darwin") {
|
|
42
|
+
return mp.startsWith("/Volumes/") && !mp.startsWith("/Volumes/Recovery");
|
|
43
|
+
}
|
|
44
|
+
if (mp.startsWith("/sys") || mp.startsWith("/proc") || mp.startsWith("/run") || mp.startsWith("/snap") || mp.startsWith("/boot"))
|
|
45
|
+
return false;
|
|
46
|
+
if (d.filesystem === "tmpfs" || d.filesystem === "devtmpfs" || d.filesystem === "udev" || d.filesystem === "overlay")
|
|
47
|
+
return false;
|
|
48
|
+
return true;
|
|
49
|
+
});
|
|
50
|
+
// Load links
|
|
51
|
+
const links = loadLinks();
|
|
52
|
+
const linkEntries = Object.entries(links);
|
|
53
|
+
// Show connected drives with git-drive
|
|
54
|
+
console.log("=== Connected Drives ===\n");
|
|
55
|
+
if (externalDrives.length === 0) {
|
|
56
|
+
console.log("No external drives connected.\n");
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
for (const drive of externalDrives) {
|
|
60
|
+
const mp = drive.mounted;
|
|
61
|
+
const gitDrivePath = getGitDrivePath(mp);
|
|
62
|
+
const hasGitDrive = (0, fs_1.existsSync)(gitDrivePath);
|
|
63
|
+
if (hasGitDrive) {
|
|
64
|
+
const entries = (0, fs_1.readdirSync)(gitDrivePath).filter(n => n.endsWith(".git") || (0, fs_1.existsSync)((0, path_1.join)(gitDrivePath, n, "HEAD")));
|
|
65
|
+
console.log(`✓ ${mp}`);
|
|
66
|
+
console.log(` ${entries.length} repo${entries.length === 1 ? "" : "s"} backed up`);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(`○ ${mp} (not initialized)`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
console.log();
|
|
73
|
+
}
|
|
74
|
+
// Show registered drives (from links)
|
|
75
|
+
console.log("=== Registered Repositories ===\n");
|
|
76
|
+
if (linkEntries.length === 0) {
|
|
77
|
+
console.log("No repositories linked to drives yet.");
|
|
78
|
+
console.log("Run 'git-drive link' to link a repository.\n");
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
for (const [localPath, link] of linkEntries) {
|
|
82
|
+
const stillConnected = (0, fs_1.existsSync)(link.mountpoint);
|
|
83
|
+
const localExists = (0, fs_1.existsSync)(localPath);
|
|
84
|
+
console.log(`${localPath}`);
|
|
85
|
+
console.log(` → ${link.mountpoint} (${link.repoName})`);
|
|
86
|
+
console.log(` Drive: ${stillConnected ? "connected" : "NOT CONNECTED"}`);
|
|
87
|
+
console.log(` Local: ${localExists ? "exists" : "NOT FOUND"}`);
|
|
88
|
+
console.log();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// Show current repo status if in a git repo
|
|
92
|
+
if ((0, git_js_1.isGitRepo)()) {
|
|
93
|
+
console.log("=== Current Repository ===\n");
|
|
94
|
+
const name = (0, git_js_1.getProjectName)();
|
|
95
|
+
const remoteUrl = (0, git_js_1.getRemoteUrl)("gd");
|
|
96
|
+
console.log(`Repository: ${name}`);
|
|
97
|
+
if (remoteUrl) {
|
|
98
|
+
console.log(`Remote 'gd': ${remoteUrl}`);
|
|
99
|
+
// Check if this repo is linked
|
|
100
|
+
const cwd = process.cwd();
|
|
101
|
+
const link = links[cwd];
|
|
102
|
+
if (link) {
|
|
103
|
+
console.log(`Linked to: ${link.mountpoint}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
console.log(`No 'gd' remote configured.`);
|
|
108
|
+
console.log("Run 'git-drive link' to set up backup.");
|
|
109
|
+
}
|
|
110
|
+
console.log();
|
|
111
|
+
}
|
|
112
|
+
// Server status hint
|
|
113
|
+
console.log("=== Server ===\n");
|
|
114
|
+
console.log("Web UI: http://localhost:4483");
|
|
115
|
+
console.log("Run 'git-drive server' to start the web interface.\n");
|
|
116
|
+
}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadConfig = loadConfig;
|
|
4
|
+
exports.saveConfig = saveConfig;
|
|
5
|
+
exports.requireConfig = requireConfig;
|
|
6
|
+
exports.assertDriveMounted = assertDriveMounted;
|
|
7
|
+
exports.getDriveStorePath = getDriveStorePath;
|
|
8
|
+
exports.loadLinks = loadLinks;
|
|
9
|
+
exports.saveLink = saveLink;
|
|
10
|
+
const os_1 = require("os");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const fs_1 = require("fs");
|
|
13
|
+
const errors_js_1 = require("./errors.js");
|
|
14
|
+
const CONFIG_DIR = (0, path_1.join)((0, os_1.homedir)(), ".config", "git-drive");
|
|
15
|
+
const CONFIG_FILE = (0, path_1.join)(CONFIG_DIR, "config.json");
|
|
16
|
+
function loadConfig() {
|
|
17
|
+
if (!(0, fs_1.existsSync)(CONFIG_FILE))
|
|
18
|
+
return null;
|
|
19
|
+
const raw = (0, fs_1.readFileSync)(CONFIG_FILE, "utf-8");
|
|
20
|
+
return JSON.parse(raw);
|
|
21
|
+
}
|
|
22
|
+
function saveConfig(config) {
|
|
23
|
+
(0, fs_1.mkdirSync)(CONFIG_DIR, { recursive: true });
|
|
24
|
+
(0, fs_1.writeFileSync)(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
|
|
25
|
+
}
|
|
26
|
+
function requireConfig() {
|
|
27
|
+
const config = loadConfig();
|
|
28
|
+
if (!config) {
|
|
29
|
+
throw new errors_js_1.GitDriveError("No drive configured. Run: git drive init <path>");
|
|
30
|
+
}
|
|
31
|
+
return config;
|
|
32
|
+
}
|
|
33
|
+
function assertDriveMounted(drivePath) {
|
|
34
|
+
if (!(0, fs_1.existsSync)(drivePath)) {
|
|
35
|
+
throw new errors_js_1.GitDriveError(`Drive not found at ${drivePath}. Is it connected?`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getDriveStorePath(drivePath) {
|
|
39
|
+
return (0, path_1.join)(drivePath, "git-drive");
|
|
40
|
+
}
|
|
41
|
+
const LINKS_FILE = (0, path_1.join)(CONFIG_DIR, "links.json");
|
|
42
|
+
function loadLinks() {
|
|
43
|
+
if (!(0, fs_1.existsSync)(LINKS_FILE))
|
|
44
|
+
return {};
|
|
45
|
+
try {
|
|
46
|
+
const raw = (0, fs_1.readFileSync)(LINKS_FILE, "utf-8");
|
|
47
|
+
return JSON.parse(raw);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function saveLink(localPath, mountpoint, repoName) {
|
|
54
|
+
(0, fs_1.mkdirSync)(CONFIG_DIR, { recursive: true });
|
|
55
|
+
const links = loadLinks();
|
|
56
|
+
links[localPath] = {
|
|
57
|
+
mountpoint,
|
|
58
|
+
repoName,
|
|
59
|
+
linkedAt: new Date().toISOString(),
|
|
60
|
+
};
|
|
61
|
+
(0, fs_1.writeFileSync)(LINKS_FILE, JSON.stringify(links, null, 2) + "\n");
|
|
62
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GitDriveError = void 0;
|
|
4
|
+
exports.handleError = handleError;
|
|
5
|
+
class GitDriveError extends Error {
|
|
6
|
+
constructor(message) {
|
|
7
|
+
super(message);
|
|
8
|
+
this.name = "GitDriveError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
exports.GitDriveError = GitDriveError;
|
|
12
|
+
function handleError(err) {
|
|
13
|
+
if (err instanceof GitDriveError) {
|
|
14
|
+
console.error(`error: ${err.message}`);
|
|
15
|
+
}
|
|
16
|
+
else if (err instanceof Error) {
|
|
17
|
+
const msg = err.message;
|
|
18
|
+
// execSync errors include stderr in the message
|
|
19
|
+
const stderrMatch = msg.match(/stderr:\s*([\s\S]*)/);
|
|
20
|
+
if (stderrMatch) {
|
|
21
|
+
console.error(`error: ${stderrMatch[1].trim()}`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
console.error(`error: ${msg}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.error("An unexpected error occurred.");
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.git = git;
|
|
4
|
+
exports.listDrives = listDrives;
|
|
5
|
+
exports.getRepoRoot = getRepoRoot;
|
|
6
|
+
exports.getProjectName = getProjectName;
|
|
7
|
+
exports.getRemoteUrl = getRemoteUrl;
|
|
8
|
+
exports.isGitRepo = isGitRepo;
|
|
9
|
+
const child_process_1 = require("child_process");
|
|
10
|
+
const path_1 = require("path");
|
|
11
|
+
const node_disk_info_1 = require("node-disk-info");
|
|
12
|
+
function git(args, cwd) {
|
|
13
|
+
return (0, child_process_1.execSync)(`git ${args}`, {
|
|
14
|
+
cwd,
|
|
15
|
+
encoding: "utf-8",
|
|
16
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
17
|
+
}).trim();
|
|
18
|
+
}
|
|
19
|
+
async function listDrives() {
|
|
20
|
+
const drives = await (0, node_disk_info_1.getDiskInfo)();
|
|
21
|
+
return drives.filter((d) => {
|
|
22
|
+
const mp = d.mounted;
|
|
23
|
+
if (!mp)
|
|
24
|
+
return false;
|
|
25
|
+
if (mp === "/" || mp === "100%")
|
|
26
|
+
return false;
|
|
27
|
+
if (process.platform === "darwin") {
|
|
28
|
+
return mp.startsWith("/Volumes/") && !mp.startsWith("/Volumes/Recovery");
|
|
29
|
+
}
|
|
30
|
+
if (mp.startsWith("/sys") || mp.startsWith("/proc") || mp.startsWith("/run") || mp.startsWith("/snap") || mp.startsWith("/boot"))
|
|
31
|
+
return false;
|
|
32
|
+
if (d.filesystem === "tmpfs" || d.filesystem === "devtmpfs" || d.filesystem === "udev" || d.filesystem === "overlay")
|
|
33
|
+
return false;
|
|
34
|
+
return true;
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function getRepoRoot() {
|
|
38
|
+
return git("rev-parse --show-toplevel");
|
|
39
|
+
}
|
|
40
|
+
function getProjectName() {
|
|
41
|
+
const root = getRepoRoot();
|
|
42
|
+
return (0, path_1.basename)(root);
|
|
43
|
+
}
|
|
44
|
+
function getRemoteUrl(remoteName) {
|
|
45
|
+
try {
|
|
46
|
+
return git(`remote get-url ${remoteName}`);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function isGitRepo() {
|
|
53
|
+
try {
|
|
54
|
+
git("rev-parse --is-inside-work-tree");
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|