Skip to content

Commit d239e72

Browse files
diffing
1 parent f1f7f33 commit d239e72

File tree

12 files changed

+406
-143
lines changed

12 files changed

+406
-143
lines changed

packages/app/src/app/overmind/effects/api/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
EnvironmentVariable,
88
GitChanges,
99
GitCommit,
10+
GitFileCompare,
1011
GitInfo,
1112
GitPr,
1213
Module,
@@ -329,6 +330,28 @@ export default {
329330
message,
330331
});
331332
},
333+
async compareGit(
334+
sandboxId: string,
335+
username: string,
336+
ref: string,
337+
includeContents = false
338+
): Promise<GitFileCompare[]> {
339+
const response: any = await api.post(
340+
`/sandboxes/${sandboxId}/git/compare`,
341+
{
342+
base: {
343+
username,
344+
ref,
345+
},
346+
includeContents,
347+
}
348+
);
349+
350+
return response.files;
351+
},
352+
getGitPr(sandboxId: string, prNumber: number): Promise<GitPr> {
353+
return api.get(`/sandboxes/${sandboxId}/git/prs/${prNumber}`);
354+
},
332355
createGitPr(sandboxId: string, message: string): Promise<GitPr> {
333356
return api.post(`/sandboxes/${sandboxId}/git/pr`, {
334357
id: sandboxId,

packages/app/src/app/overmind/namespaces/editor/actions.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ export const sandboxChanged: AsyncAction<{ id: string }> = withLoadApp<{
154154
await effects.vscode.closeAllTabs();
155155

156156
state.editor.error = null;
157+
state.git.sourceSandboxId = null;
157158

158159
let newId = id;
159160

@@ -263,7 +264,9 @@ export const sandboxChanged: AsyncAction<{ id: string }> = withLoadApp<{
263264

264265
actions.internal.ensurePackageJSON();
265266

266-
await actions.editor.internal.initializeSandbox(sandbox);
267+
if (!sandbox.git) {
268+
await actions.editor.internal.initializeSandbox(sandbox);
269+
}
267270

268271
// We only recover files at this point if we are not live. When live we recover them
269272
// when the module_state is received
@@ -288,6 +291,12 @@ export const sandboxChanged: AsyncAction<{ id: string }> = withLoadApp<{
288291
actions.comments.getSandboxComments(sandbox.id);
289292
}
290293

294+
state.editor.currentSandbox.prNumber = 1;
295+
296+
if (sandbox.originalGit) {
297+
actions.git.loadGitSource(sandbox.originalGit);
298+
}
299+
291300
state.editor.isLoading = false;
292301
});
293302

@@ -438,6 +447,10 @@ export const codeChanged: Action<{
438447
if (!isServer && state.preferences.settings.livePreviewEnabled) {
439448
actions.editor.internal.updatePreviewCode();
440449
}
450+
451+
if (sandbox.originalGit) {
452+
actions.git.updateGitChanges();
453+
}
441454
};
442455

443456
export const saveClicked: AsyncAction = withOwnedSandbox(
@@ -489,13 +502,6 @@ export const saveClicked: AsyncAction = withOwnedSandbox(
489502
}
490503
});
491504

492-
if (
493-
sandbox.originalGit &&
494-
state.workspace.openedWorkspaceItem === 'github'
495-
) {
496-
actions.git.internal.fetchGitChanges();
497-
}
498-
499505
effects.preview.executeCodeImmediately();
500506
} catch (error) {
501507
actions.internal.handleError({

packages/app/src/app/overmind/namespaces/editor/internalActions.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -155,17 +155,6 @@ export const saveCode: AsyncAction<{
155155
effects.vscode.callCallback(cbID);
156156
}
157157

158-
if (
159-
sandbox.originalGit &&
160-
state.workspace.openedWorkspaceItem === 'github'
161-
) {
162-
state.git.isFetching = true;
163-
state.git.originalGitChanges = await effects.api.getGitChanges(
164-
sandbox.id
165-
);
166-
state.git.isFetching = false;
167-
}
168-
169158
// If the executor is a server we only should send updates if the sandbox has been
170159
// started already
171160
if (

packages/app/src/app/overmind/namespaces/files/actions.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,10 @@ export const moduleMovedToDirectory: AsyncAction<{
262262
error,
263263
});
264264
}
265+
266+
if (sandbox.originalGit) {
267+
actions.git.updateGitChanges();
268+
}
265269
},
266270
async () => {},
267271
'write_code'
@@ -314,6 +318,10 @@ export const directoryMovedToDirectory: AsyncAction<{
314318
error,
315319
});
316320
}
321+
322+
if (sandbox.originalGit) {
323+
actions.git.updateGitChanges();
324+
}
317325
},
318326
async () => {},
319327
'write_code'
@@ -388,6 +396,10 @@ export const directoryDeleted: AsyncAction<{
388396
error,
389397
});
390398
}
399+
400+
if (sandbox.originalGit) {
401+
actions.git.updateGitChanges();
402+
}
391403
},
392404
async () => {},
393405
'write_code'
@@ -487,6 +499,10 @@ export const addedFileToSandbox: AsyncAction<Pick<
487499
});
488500

489501
effects.executor.updateFiles(state.editor.currentSandbox);
502+
503+
if (state.editor.currentSandbox.originalGit) {
504+
actions.git.updateGitChanges();
505+
}
490506
},
491507
async () => {},
492508
'write_code'
@@ -724,6 +740,10 @@ export const moduleCreated: AsyncAction<{
724740
error,
725741
});
726742
}
743+
744+
if (sandbox.originalGit) {
745+
actions.git.updateGitChanges();
746+
}
727747
},
728748
async () => {},
729749
'write_code'
@@ -767,6 +787,10 @@ export const moduleDeleted: AsyncAction<{
767787
state.editor.modulesByPath = effects.vscode.sandboxFsSync.create(sandbox);
768788
actions.internal.handleError({ message: 'Could not delete file', error });
769789
}
790+
791+
if (sandbox.originalGit) {
792+
actions.git.updateGitChanges();
793+
}
770794
},
771795
async () => {},
772796
'write_code'

packages/app/src/app/overmind/namespaces/git/actions.ts

Lines changed: 152 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { Action, AsyncAction } from 'app/overmind';
1+
import { getModulePath } from '@codesandbox/common/lib/sandbox/modules';
2+
import { GitInfo, SandboxGitState } from '@codesandbox/common/lib/types';
3+
import { Action, AsyncAction, Operator } from 'app/overmind';
4+
import { debounce, mutate, pipe } from 'overmind';
25

36
import * as internalActions from './internalActions';
47

@@ -11,6 +14,75 @@ export const repoTitleChanged: Action<{
1114
state.git.error = null;
1215
};
1316

17+
export const loadGitSource: AsyncAction<GitInfo> = async (
18+
{ state, actions, effects },
19+
info
20+
) => {
21+
const sandbox = state.editor.currentSandbox;
22+
const sourceSandbox = await effects.api.getSandbox(
23+
`github/${info.username}/${info.repo}/tree/${info.branch}`
24+
);
25+
26+
state.editor.sandboxes[sourceSandbox.id] = sourceSandbox;
27+
state.git.sourceSandboxId = sourceSandbox.id;
28+
29+
state.git.sourceModulesByPath = sourceSandbox.modules.reduce(
30+
(aggr, module) => {
31+
const path = getModulePath(
32+
sourceSandbox.modules,
33+
sourceSandbox.directories,
34+
module.id
35+
);
36+
module.path = path;
37+
if (path) {
38+
aggr[path] = module;
39+
}
40+
41+
return aggr;
42+
},
43+
{}
44+
);
45+
46+
if (sandbox.prNumber) {
47+
state.git.pr = await effects.api.getGitPr(sandbox.id, sandbox.prNumber);
48+
49+
const changes = await effects.api.compareGit(
50+
sandbox.id,
51+
state.git.pr.username,
52+
state.git.pr.baseCommitSha
53+
);
54+
55+
state.git.originalGitChanges = changes.reduce((aggr, file) => {
56+
aggr['/' + file.filename] = file;
57+
58+
return aggr;
59+
}, {});
60+
61+
actions.git._setGitChanges();
62+
63+
if (sandbox.originalGitCommitSha === state.git.pr.commitSha) {
64+
state.git.gitState = SandboxGitState.SYNCED;
65+
} else {
66+
const outOfSyncChanges = await effects.api.compareGit(
67+
sandbox.id,
68+
state.git.pr.username,
69+
sandbox.originalGitCommitSha,
70+
true
71+
);
72+
state.git.outOfSyncChanges = outOfSyncChanges.reduce((aggr, file) => {
73+
aggr['/' + file.filename] = file;
74+
75+
return aggr;
76+
}, {});
77+
state.git.gitState = SandboxGitState.OUT_OF_SYNC_PR;
78+
// Will move to "CONFLICT" if there is a conflict
79+
actions.git._setGitChanges();
80+
}
81+
} else {
82+
state.git.gitState = SandboxGitState.DETACHED;
83+
}
84+
};
85+
1486
export const createRepoClicked: AsyncAction = async ({ state, effects }) => {
1587
const { repoTitle } = state.git;
1688
const modulesNotSaved = !state.editor.isAllModulesSynced;
@@ -59,13 +131,6 @@ export const createRepoClicked: AsyncAction = async ({ state, effects }) => {
59131
effects.router.updateSandboxUrl({ git });
60132
};
61133

62-
export const gitMounted: AsyncAction = async ({ actions, state }) => {
63-
const currentSandbox = state.editor.currentSandbox;
64-
if (currentSandbox && currentSandbox.originalGit) {
65-
await actions.git.internal.fetchGitChanges();
66-
}
67-
};
68-
69134
export const createCommitClicked: AsyncAction = async ({ state, effects }) => {
70135
const { git } = state;
71136
const sandbox = state.editor.currentSandbox;
@@ -93,7 +158,7 @@ export const createCommitClicked: AsyncAction = async ({ state, effects }) => {
93158
}
94159
state.git.subject = '';
95160
state.git.description = '';
96-
state.git.originalGitChanges = null;
161+
state.git.gitChanges = null;
97162
};
98163

99164
export const subjectChanged: Action<{
@@ -146,7 +211,84 @@ export const createPrClicked: AsyncAction = async ({ state, effects }) => {
146211

147212
state.git.subject = '';
148213
state.git.description = '';
149-
state.git.originalGitChanges = null;
214+
state.git.gitChanges = null;
150215

151216
effects.router.updateSandboxUrl({ git: pr.git });
152217
};
218+
219+
export const updateGitChanges: Operator = pipe(
220+
debounce(500),
221+
mutate(({ actions }) => actions.git._setGitChanges())
222+
);
223+
224+
export const _setGitChanges: Action = ({ state }) => {
225+
const changes = {
226+
added: [],
227+
deleted: [],
228+
modified: [],
229+
rights: '',
230+
};
231+
let hasConflict = false;
232+
state.editor.currentSandbox.modules.forEach(module => {
233+
if (
234+
state.git.outOfSyncChanges[module.path] &&
235+
state.git.outOfSyncChanges[module.path].content !== module.code
236+
) {
237+
hasConflict = true;
238+
}
239+
240+
// We check if the file is not in source and not on PR
241+
if (
242+
!state.git.sourceModulesByPath[module.path] &&
243+
!state.git.originalGitChanges[module.path]
244+
) {
245+
changes.added.push(module.path);
246+
} else if (
247+
// We check if the file has been changed related to PR
248+
state.git.originalGitChanges[module.path] &&
249+
module.code !== state.git.originalGitChanges[module.path].content
250+
) {
251+
changes.modified.push(module.path);
252+
} else if (
253+
// We check if the file has been changed related to source
254+
state.git.sourceModulesByPath[module.path].code !== module.code
255+
) {
256+
changes.modified.push(module.path);
257+
}
258+
});
259+
state.editor.sandboxes[state.git.sourceSandboxId].modules.forEach(module => {
260+
if (!state.editor.modulesByPath[module.path]) {
261+
changes.deleted.push(module.path);
262+
}
263+
});
264+
state.git.gitChanges = changes;
265+
266+
if (state.git.gitState === SandboxGitState.CONFLICT_PR && !hasConflict) {
267+
state.git.gitState = SandboxGitState.OUT_OF_SYNC_PR;
268+
} else if (
269+
state.git.gitState === SandboxGitState.OUT_OF_SYNC_PR &&
270+
hasConflict
271+
) {
272+
state.git.gitState = SandboxGitState.CONFLICT_PR;
273+
}
274+
};
275+
276+
export const resolveOutOfSyncPR: Action = ({ state, effects }) => {
277+
const sandbox = state.editor.currentSandbox;
278+
state.git.gitState = SandboxGitState.RESOLVING;
279+
280+
sandbox.modules.forEach(module => {
281+
if (
282+
state.git.outOfSyncChanges[module.path] &&
283+
state.git.outOfSyncChanges[module.path].content !== module.code
284+
) {
285+
effects.vscode.openDiff(
286+
sandbox.id,
287+
module,
288+
state.git.outOfSyncChanges[module.path].content
289+
);
290+
}
291+
});
292+
};
293+
294+
export const updateOutOfSyncPR: Action = ({ state, effects }) => {};

packages/app/src/app/overmind/namespaces/git/internalActions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export const fetchGitChanges: AsyncAction = async ({ state, effects }) => {
88
const { id } = state.editor.currentSandbox;
99

1010
state.git.isFetching = true;
11-
state.git.originalGitChanges = await effects.api.getGitChanges(id);
11+
state.git.gitChanges = await effects.api.getGitChanges(id);
1212
state.git.isFetching = false;
1313
};

0 commit comments

Comments
 (0)