-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathfileStatus.ts
172 lines (148 loc) · 4.63 KB
/
fileStatus.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import path from 'path';
import hashObject from './commands/hashObject';
import { RELATIVE_PATH_TO_INDEX_FILE } from './constants';
import { FileStatusCode } from './enums';
import IndexParser from './indexParser';
import { decodeCommit } from './objects/commit';
import { Tree, decodeTree } from './objects/tree';
import { getBranchHeadReference, getFileStats } from './utils';
import fs from 'fs';
import { DiffEntry, FileStatus } from './types';
export function diffCommitWithStaging(
gitRoot: string,
branch: string
): DiffEntry[] {
const index = new IndexParser(gitRoot).parse();
const files: DiffEntry[] = [];
// Retrieve the hash of the latest commit for this branch.
const commitHash = getBranchHeadReference(gitRoot, branch);
let tree: Tree | undefined = undefined;
if (commitHash) {
const commitObject = decodeCommit(gitRoot, commitHash);
tree = decodeTree(gitRoot, commitObject.treeHash);
}
// No previous commit present.
// Status of all files => 'ADDED'
if (tree === undefined) {
index.entries.forEach((e) => {
files.push({ name: e.name, status: FileStatusCode.ADDED });
});
return files;
}
// Sort files w.r.t name
const treeFiles = [...tree.map.entries()].sort((a, b) =>
a[0].localeCompare(b[0])
);
treeFiles.forEach(([name, node]) => {
const indexEntry = index.getEntry(name);
// File present in tree and index
if (indexEntry) {
files.push({
name,
status:
indexEntry.hash === node.hash
? FileStatusCode.UNMODIFIED
: FileStatusCode.MODIFIED
});
} else {
// File present in tree but not in index => 'DELETED'
files.push({
name,
status: FileStatusCode.DELETED
});
}
// Remove the file form index entries.
// Make sure not to save the index to disk.
// This is required to process the 'ADDED' files.
index.remove(name);
});
// File present in index but not in tree => 'ADDED'
index.entries.forEach((e) => {
files.push({ name: e.name, status: FileStatusCode.ADDED });
});
return files;
}
export function diffStagingWithWorktree(gitRoot: string): DiffEntry[] {
const files: DiffEntry[] = [];
const index = new IndexParser(gitRoot).parse();
const fileStats = getFileStats(gitRoot);
index.entries.forEach((e) => {
const stat = fileStats.get(e.name);
// File is present in index and Worktree
if (stat) {
const hash = hashObject({ gitRoot, write: false, file: e.name });
files.push({
name: e.name,
status:
e.hash === hash ? FileStatusCode.UNMODIFIED : FileStatusCode.MODIFIED
});
} else {
// File present in index but not in Worktree => 'DELETED'
files.push({ name: e.name, status: FileStatusCode.DELETED });
}
fileStats.delete(e.name);
});
// File present in Worktree but not in index => 'UNTRACKED'
fileStats.forEach((value) => {
files.push({
name: value.pathFromGitRoot,
status: FileStatusCode.UNTRACKED
});
});
return files;
}
/**
* This function finds the status of the files present in Staging and Worktree.
* It returns a Map where:
* - key: the path to the file, and
* - value: FileStatus object.
*
* @export
* @param {string} gitRoot
* @returns {Map<string, FileStatus>}
*/
export function getFileStatus(
gitRoot: string,
branch: string
): Map<string, FileStatus> {
const files = new Map<string, FileStatus>();
// No index file is present. All the files will be set as untracked.
if (!fs.existsSync(path.join(gitRoot, RELATIVE_PATH_TO_INDEX_FILE))) {
const fileStats = getFileStats(gitRoot);
fileStats.forEach((file) => {
files.set(file.pathFromGitRoot, {
name: file.pathFromGitRoot,
staging: FileStatusCode.UNTRACKED,
worktree: FileStatusCode.UNTRACKED
});
});
return files;
}
const diff1 = diffCommitWithStaging(gitRoot, branch);
// Assuming files present in Staging area have UNMODIFIED worktree status.
// The worktree status might be updated later on.
diff1.forEach((value) => {
const file: FileStatus = {
name: value.name,
staging: value.status,
worktree: FileStatusCode.UNMODIFIED
};
files.set(value.name, file);
});
const diff2 = diffStagingWithWorktree(gitRoot);
diff2.forEach((value) => {
let file = files.get(value.name);
// file not present in commit or index
if (file === undefined) {
file = {
name: value.name,
staging: FileStatusCode.UNTRACKED,
worktree: FileStatusCode.UNTRACKED
};
} else {
file.worktree = value.status;
}
files.set(value.name, file);
});
return files;
}