Skip to content

Commit 033ecb8

Browse files
committed
Add section for merge conflict files (#896)
- [WIP] See TODOs
1 parent 0907a0d commit 033ecb8

File tree

5 files changed

+97
-5
lines changed

5 files changed

+97
-5
lines changed

src/commandsAndMenu.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,16 @@ export function addCommands(
562562
for (const file of files) {
563563
const { context, filePath, isText, status } = file;
564564

565+
// Merge conflict
566+
// TODO: handle
567+
if (status === 'unmerged') {
568+
logger.log({
569+
message: 'File has conflicts!',
570+
level: Level.ERROR
571+
});
572+
continue;
573+
}
574+
565575
// nothing to compare to for untracked files
566576
if (status === 'untracked') {
567577
continue;

src/components/FileItem.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,10 @@ export class FileItem extends React.PureComponent<IFileItemProps> {
173173
render(): JSX.Element {
174174
const { file } = this.props;
175175
const status_code = file.status === 'staged' ? file.x : file.y;
176-
const status = this._getFileChangedLabel(status_code as any);
176+
const status =
177+
file.status === 'unmerged'
178+
? 'Conflicted'
179+
: this._getFileChangedLabel(status_code as any);
177180

178181
return (
179182
<div
@@ -205,7 +208,11 @@ export class FileItem extends React.PureComponent<IFileItemProps> {
205208
/>
206209
{this.props.actions}
207210
<span className={this._getFileChangedLabelClass(this.props.file.y)}>
208-
{this.props.file.y === '?' ? 'U' : status_code}
211+
{this.props.file.status === 'unmerged'
212+
? '!'
213+
: this.props.file.y === '?'
214+
? 'U'
215+
: status_code}
209216
</span>
210217
</div>
211218
);

src/components/FileList.tsx

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ export const CONTEXT_COMMANDS: ContextCommands = {
7979
ContextCommandIDs.gitCommitAmendStaged,
8080
ContextCommandIDs.gitFileHistory
8181
],
82-
unmodified: [ContextCommandIDs.gitFileHistory]
82+
unmodified: [ContextCommandIDs.gitFileHistory],
83+
unmerged: [ContextCommandIDs.gitFileOpen]
8384
};
8485

8586
const SIMPLE_CONTEXT_COMMANDS: ContextCommands = {
@@ -107,7 +108,8 @@ const SIMPLE_CONTEXT_COMMANDS: ContextCommands = {
107108
ContextCommandIDs.gitIgnoreExtension,
108109
ContextCommandIDs.gitFileDelete
109110
],
110-
unmodified: [ContextCommandIDs.gitFileHistory]
111+
unmodified: [ContextCommandIDs.gitFileHistory],
112+
unmerged: [ContextCommandIDs.gitFileOpen]
111113
};
112114

113115
export class FileList extends React.Component<IFileListProps, IFileListState> {
@@ -275,6 +277,7 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
275277
const stagedFiles: Git.IStatusFile[] = [];
276278
const unstagedFiles: Git.IStatusFile[] = [];
277279
const untrackedFiles: Git.IStatusFile[] = [];
280+
const unmergedFiles: Git.IStatusFile[] = [];
278281

279282
this.props.files.forEach(file => {
280283
switch (file.status) {
@@ -297,7 +300,9 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
297300
status: 'unstaged'
298301
});
299302
break;
300-
303+
case 'unmerged':
304+
unmergedFiles.push(file);
305+
break;
301306
default:
302307
break;
303308
}
@@ -311,6 +316,7 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
311316
<AutoSizer disableWidth={true}>
312317
{({ height }) => (
313318
<>
319+
{this._renderUnmerged(unmergedFiles, height)}
314320
{this._renderStaged(stagedFiles, height)}
315321
{this._renderChanged(unstagedFiles, height)}
316322
{this._renderUntracked(untrackedFiles, height)}
@@ -340,6 +346,59 @@ export class FileList extends React.Component<IFileListProps, IFileListState> {
340346
);
341347
}
342348

349+
/**
350+
* Render an unmerged file
351+
*
352+
* Note: This is actually a React.FunctionComponent but defined as
353+
* a private method as it needs access to FileList properties.
354+
*
355+
* @param rowProps Row properties
356+
*/
357+
private _renderUnmergedRow = (
358+
rowProps: ListChildComponentProps
359+
): JSX.Element => {
360+
const { data, index, style } = rowProps;
361+
const file = data[index] as Git.IStatusFile;
362+
const diffButton = this._createDiffButton(file);
363+
return (
364+
<FileItem
365+
trans={this.props.trans}
366+
actions={diffButton}
367+
file={file}
368+
contextMenu={this.openContextMenu}
369+
model={this.props.model}
370+
selected={this._isSelectedFile(file)}
371+
selectFile={this.updateSelectedFile}
372+
onDoubleClick={() => this._openDiffView(file)}
373+
style={style} // TODO probably want to give a different 'danger' style
374+
/>
375+
);
376+
};
377+
378+
private _renderUnmerged(files: Git.IStatusFile[], height: number) {
379+
// Hide section if no merge conflicts are present
380+
return files.length > 0 ? (
381+
<GitStage
382+
actions={
383+
<ActionButton
384+
className={hiddenButtonStyle}
385+
disabled={files.length === 0}
386+
icon={removeIcon}
387+
title={this.props.trans.__('Stage all changes')}
388+
onClick={() => {
389+
// TODO open modal with confirmation to stage with merge conflicts
390+
}}
391+
/>
392+
}
393+
collapsible
394+
files={files}
395+
heading={this.props.trans.__('Unmerged')}
396+
height={height}
397+
rowRenderer={this._renderUnmergedRow}
398+
/>
399+
) : null;
400+
}
401+
343402
/**
344403
* Render a staged file
345404
*

src/tokens.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,7 @@ export namespace Git {
878878
| 'unstaged'
879879
| 'partially-staged'
880880
| 'unmodified'
881+
| 'unmerged'
881882
| null;
882883

883884
export interface ITagResult {

src/utils.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,21 @@ export function extractFilename(path: string): string {
1111
}
1212

1313
export function decodeStage(x: string, y: string): Git.Status {
14+
/**
15+
* All combinations of statuses for merge conflicts
16+
* @see https://git-scm.com/docs/git-status#_short_format
17+
*/
18+
const unmergedCombinations: Record<string, string[]> = {
19+
D: ['D', 'U'],
20+
A: ['U', 'A'],
21+
U: ['D', 'A', 'U']
22+
};
23+
24+
// If the file has a merge conflict
25+
if ((unmergedCombinations[x] ?? []).includes(y)) {
26+
return 'unmerged';
27+
}
28+
1429
// If file is untracked
1530
if (x === '?' && y === '?') {
1631
return 'untracked';

0 commit comments

Comments
 (0)