jvcs 1.7.2 → 1.7.3

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.
Files changed (2) hide show
  1. package/controllers/status.js +126 -70
  2. package/package.json +2 -2
@@ -1,17 +1,9 @@
1
- // Untracked files
2
- // Files that exist in your working directory (project folder) but have never been added to staging or committed.
3
-
4
- // Changes to be committed
5
- // Files that are in the staging area (.jvcs/staging) — meaning, you’ve already added them using jvcs add, but haven’t committed yet.
6
-
7
- // Changes not staged for commit
8
- // Files that were already added to staging earlier, but you modified them again in your working directory after staging — i.e., the staged copy and working copy are different.
9
-
10
1
  const fs = require("fs");
11
2
  const path = require("path");
12
3
  const crypto = require("crypto");
13
4
  const chalk = require("chalk");
14
- const { checkGlobalConfig, getGlobalConfig, checkforjvcs } = require("./utility");
5
+ const { getGlobalConfig, checkforjvcs } = require("./utility");
6
+ const { minimatch } = require("minimatch");
15
7
 
16
8
  // normalize relative path to use forward slashes for consistent comparisons
17
9
  function normalizeRel(p) {
@@ -24,10 +16,26 @@ function hashfile(filepath) {
24
16
  return crypto.createHash("sha256").update(data).digest("hex")
25
17
  }
26
18
  catch(error) {
27
- // return null
19
+ return null;
28
20
  }
29
21
  }
30
22
 
23
+ function loadIgnorePatterns() {
24
+ const ignorePath = path.join(process.cwd(), ".jvcsignore");
25
+ if(!fs.existsSync(ignorePath)) return [];
26
+
27
+ const content = fs.readFileSync(ignorePath, "utf-8");
28
+ return content
29
+ .split(/[\n,]+/)
30
+ .map(line => line.trim())
31
+ .filter(line => line.length > 0 && !line.startsWith("#"));
32
+ }
33
+
34
+ function isIgnored(relativePath, ignorePatterns = []) {
35
+ return ignorePatterns.some(pattern =>
36
+ minimatch(relativePath, pattern, { dot: true })
37
+ );
38
+ }
31
39
 
32
40
  function getAllFiles(dir, rootDir=dir, collected=[]) {
33
41
 
@@ -39,11 +47,12 @@ function getAllFiles(dir, rootDir=dir, collected=[]) {
39
47
  const fullPath = path.join(dir,entry.name)
40
48
  const rel = normalizeRel(path.relative(rootDir,fullPath))
41
49
 
42
- if(entry.isDirectory() && (entry.name === ".jvcs" || entry.name === "node_modules"))
43
- continue;
50
+ if(entry.isDirectory() && (entry.name === ".jvcs" || entry.name === ".git"))
51
+ continue;
44
52
 
45
- if(entry.isFile() && (entry.name === "meta.json" || entry.name === "jvcs_hashcode.json"))
46
- continue;
53
+ // Skip internal files from being hashed/tracked in cwd maps
54
+ if(entry.isFile() && (entry.name === "meta.json" || entry.name === "jvcs_hashcode.json" || entry.name === ".jvcsignore"))
55
+ continue;
47
56
 
48
57
  if(entry.isFile()) {
49
58
  collected.push(rel);
@@ -51,7 +60,6 @@ function getAllFiles(dir, rootDir=dir, collected=[]) {
51
60
  else if(entry.isDirectory()) {
52
61
  getAllFiles(fullPath, rootDir, collected);
53
62
  }
54
-
55
63
  }
56
64
 
57
65
  return collected
@@ -59,21 +67,16 @@ function getAllFiles(dir, rootDir=dir, collected=[]) {
59
67
 
60
68
  async function statusCmd() {
61
69
 
62
- if(!checkGlobalConfig()) {
63
- console.log(chalk.red("No existing session found. Please login or signup."));
64
- console.log(chalk.green("jvcs --help for help"));
65
- return;
66
- }
67
-
68
70
  const configData = getGlobalConfig();
69
71
  if(!configData) {
70
72
  console.log(chalk.red("No existing session found. Please login or signup."));
71
- return;
73
+ console.log(chalk.green("jvcs --help for help"));
74
+ process.exit(1);
72
75
  }
73
76
 
74
77
  if(!checkforjvcs()) {
75
- console.log(chalk.red("Repository is not initialized or is deleted."));
76
- return;
78
+ console.log(chalk.red("Repository is not initialized or is deleted. Please create it."));
79
+ process.exit(1);
77
80
  }
78
81
 
79
82
  const cwd = process.cwd()
@@ -84,77 +87,130 @@ async function statusCmd() {
84
87
 
85
88
  if(!fs.existsSync(jvcsDir)) {
86
89
  console.log(chalk.red("No repository exists. Please create one using 'jvcs init'"))
87
- return
90
+ process.exit(1);
88
91
  }
89
92
 
90
- // collect files (all relative to cwd, normalized)
93
+ const ignorePatterns = loadIgnorePatterns();
94
+
95
+ // 1. Collect hashes for CWD
91
96
  const cwdFiles = getAllFiles(cwd, cwd, []);
92
- const stagedFiles = fs.existsSync(stagingDir) ? getAllFiles(stagingDir, stagingDir, []).map(f => normalizeRel(f)) : [];
93
- let commitedFiles = []
94
- if(fs.existsSync(commitDir)) {
95
- const commits = fs.readdirSync(commitDir)
96
- if(commits.length > 0) {
97
- const lastCommit = `${fs.readFileSync(headFile, "utf-8").trim()}`;
98
- const commitPath = path.join(commitDir, lastCommit);
99
- commitedFiles = getAllFiles(commitPath, commitPath, []).map(f => normalizeRel(f))
97
+ const cwdMap = new Map();
98
+ for(const f of cwdFiles) {
99
+ if (!isIgnored(f, ignorePatterns)) {
100
+ const h = hashfile(path.join(cwd, f));
101
+ if (h) cwdMap.set(f, h);
102
+ }
103
+ }
104
+
105
+ // 2. Collect hashes for HEAD
106
+ const headMap = new Map();
107
+ if (fs.existsSync(commitDir) && fs.existsSync(headFile)) {
108
+ const headFileContent = fs.readFileSync(headFile, "utf-8").trim();
109
+ if (headFileContent) {
110
+ const commitPath = path.join(commitDir, headFileContent);
111
+ if (fs.existsSync(commitPath)) {
112
+ const commitFiles = getAllFiles(commitPath, commitPath, []);
113
+ for (const f of commitFiles) {
114
+ const h = hashfile(path.join(commitPath, f));
115
+ if (h) headMap.set(f, h);
116
+ }
117
+ }
100
118
  }
101
119
  }
102
120
 
103
- // use Sets for fast looku
104
- const committedSet = new Set(commitedFiles);
105
- const stagedSet = new Set(stagedFiles);
121
+ // 3. Collect hashes for Staging (Index)
122
+ const stagingMap = new Map();
123
+ const hashPath = path.join(stagingDir, "jvcs_hashcode.json");
124
+ if (fs.existsSync(hashPath)) {
125
+ try {
126
+ const data = JSON.parse(fs.readFileSync(hashPath, "utf-8"));
127
+ for (const [key, val] of Object.entries(data)) {
128
+ stagingMap.set(normalizeRel(key), val.hash);
129
+ }
130
+ } catch(e) {}
131
+ } else {
132
+ // Fallback
133
+ if (fs.existsSync(stagingDir)) {
134
+ const stFiles = getAllFiles(stagingDir, stagingDir, []);
135
+ for(const f of stFiles) {
136
+ const h = hashfile(path.join(stagingDir, f));
137
+ if (h) stagingMap.set(f, h);
138
+ }
139
+ }
140
+ }
106
141
 
107
- // Untracked: present in cwd but not in staging or commits
108
- const untracked = cwdFiles.filter(f => !stagedSet.has(f) && !committedSet.has(f));
142
+ // --- CALCULATE DIFFS ---
109
143
 
110
- // Changes to be committed: present in staging but not in (any) commits
111
- const toBeCommitted = stagedFiles.filter(f => !committedSet.has(f));
144
+ // Untracked files
145
+ const untracked = [];
146
+ for (const f of cwdMap.keys()) {
147
+ if (!stagingMap.has(f)) {
148
+ untracked.push(f);
149
+ }
150
+ }
112
151
 
113
- // Changes not staged for commit: present in staging and in cwd, but changed in cwd compared to staged copy
114
- const modified = stagedFiles.filter((file)=> {
115
- const cwdFilePath = path.join(process.cwd(),file)
116
- const stagedFilePath = path.join(stagingDir,file)
117
-
118
- if(!fs.existsSync(cwdFilePath) || !fs.existsSync(stagedFilePath))
119
- return false
152
+ // Staged changes (Diff between Staging and HEAD)
153
+ const stagedAdded = [];
154
+ const stagedModified = [];
155
+ const stagedDeleted = [];
120
156
 
121
- const cwdHash = hashfile(cwdFilePath)
122
- const stagingHash = hashfile(stagedFilePath)
157
+ for (const [f, sHash] of stagingMap.entries()) {
158
+ if (!headMap.has(f)) {
159
+ stagedAdded.push(f);
160
+ } else if (headMap.get(f) !== sHash) {
161
+ stagedModified.push(f);
162
+ }
163
+ }
164
+ for (const f of headMap.keys()) {
165
+ if (!stagingMap.has(f)) {
166
+ stagedDeleted.push(f);
167
+ }
168
+ }
123
169
 
124
- return cwdHash !== stagingHash
125
- })
170
+ // Unstaged changes (Diff between CWD and Staging)
171
+ const unstagedModified = [];
172
+ const unstagedDeleted = [];
126
173
 
174
+ for (const [f, sHash] of stagingMap.entries()) {
175
+ if (!cwdMap.has(f)) {
176
+ unstagedDeleted.push(f);
177
+ } else if (cwdMap.get(f) !== sHash) {
178
+ unstagedModified.push(f);
179
+ }
180
+ }
127
181
 
128
- // output
182
+ // --- OUTPUT ---
129
183
  console.log(chalk.bold.blue(`\nOn branch: main (default)`));
130
184
 
131
- console.log(chalk.bold.green("\nChanges to be committed (files that are staged but not commited):"));
132
- if(toBeCommitted.length > 0) {
133
- toBeCommitted.forEach(f => console.log(chalk.green(`\t${f}`)));
134
- }
135
- else {
185
+ const hasStaged = stagedAdded.length > 0 || stagedModified.length > 0 || stagedDeleted.length > 0;
186
+ console.log(chalk.bold.green("\nChanges to be committed:"));
187
+ if (hasStaged) {
188
+ stagedAdded.forEach(f => console.log(chalk.green(`\tnew file: ${f}`)));
189
+ stagedModified.forEach(f => console.log(chalk.green(`\tmodified: ${f}`)));
190
+ stagedDeleted.forEach(f => console.log(chalk.green(`\tdeleted: ${f}`)));
191
+ } else {
136
192
  console.log(chalk.gray("\tNo changes added to commit"));
137
193
  }
138
194
 
139
- console.log(chalk.bold.yellow("\nChanges not staged for commit (files that are modified after adding to staging area):"));
140
- if(modified.length > 0) {
141
- modified.forEach(f => console.log(chalk.yellow(`\t${f}`)));
142
- }
143
- else {
195
+ const hasUnstaged = unstagedModified.length > 0 || unstagedDeleted.length > 0;
196
+ console.log(chalk.bold.yellow("\nChanges not staged for commit:"));
197
+ if (hasUnstaged) {
198
+ unstagedModified.forEach(f => console.log(chalk.yellow(`\tmodified: ${f}`)));
199
+ unstagedDeleted.forEach(f => console.log(chalk.yellow(`\tdeleted: ${f}`)));
200
+ } else {
144
201
  console.log(chalk.gray("\tNo modified files detected"));
145
202
  }
146
203
 
147
- console.log(chalk.bold.red("\nUntracked files (files that are not staged or commited):"));
148
- if(untracked.length > 0) {
204
+ console.log(chalk.bold.red("\nUntracked files:"));
205
+ if (untracked.length > 0) {
149
206
  untracked.forEach(f => console.log(chalk.red(`\t${f}`)));
150
- }
151
- else {
207
+ } else {
152
208
  console.log(chalk.gray("\tNo untracked files"));
153
209
  }
154
210
 
155
211
  console.log(chalk.gray("\n(use 'jvcs add <file>' to stage changes)"));
156
212
  console.log(chalk.gray("(use 'jvcs commit -m \"message\"' to commit changes)"));
157
- console.log(chalk.gray("(use 'jvcs unstage <file>/<folder> to unstage a file/folder')"))
213
+ console.log(chalk.gray("(use 'jvcs unstage <file>/<folder>' to unstage a file/folder)"));
158
214
  }
159
215
 
160
- module.exports = statusCmd
216
+ module.exports = statusCmd;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jvcs",
3
- "version": "1.7.2",
3
+ "version": "1.7.3",
4
4
  "bin": {
5
5
  "jvcs": "index.js"
6
6
  },
@@ -23,4 +23,4 @@
23
23
  "validator": "^13.15.20",
24
24
  "yargs": "^18.0.0"
25
25
  }
26
- }
26
+ }