Skip to content

Commit 9c89658

Browse files
committed
Display three-way-diff content for text files (#896)
1 parent bc13205 commit 9c89658

File tree

7 files changed

+314
-131
lines changed

7 files changed

+314
-131
lines changed

jupyterlab_git/git.py

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,38 +1173,6 @@ async def _get_tag(self, path, commit_sha):
11731173
)
11741174
)
11751175

1176-
async def show_from_object(self, ref, path):
1177-
"""
1178-
Execute git show <ref> command & return the result.
1179-
"""
1180-
command = ["git", "show", ref]
1181-
1182-
code, output, error = await execute(command, cwd=path)
1183-
1184-
error_messages = map(
1185-
lambda n: n.lower(),
1186-
[
1187-
"fatal: Object '{}' exists on disk, but not here".format(ref),
1188-
"fatal: Object '{}' does not exist (neither on disk nor in the index)".format(
1189-
ref
1190-
),
1191-
# TODO fix this
1192-
"fatal: Object '{}' does not exist in 'HEAD'".format(ref),
1193-
"fatal: Invalid object name 'HEAD'",
1194-
],
1195-
)
1196-
lower_error = error.lower()
1197-
if code == 0:
1198-
return output
1199-
elif any([msg in lower_error for msg in error_messages]):
1200-
return ""
1201-
else:
1202-
raise tornado.web.HTTPError(
1203-
log_message="Error [{}] occurred while executing [{}] command to retrieve plaintext diff.".format(
1204-
error, " ".join(command)
1205-
)
1206-
)
1207-
12081176
async def show(self, filename, ref, path):
12091177
"""
12101178
Execute git show <ref:filename> command & return the result.
@@ -1480,32 +1448,3 @@ async def tag_checkout(self, path, tag):
14801448
"command": " ".join(command),
14811449
"message": error,
14821450
}
1483-
1484-
async def get_unmerged_file_contents(self, filename, path):
1485-
"""
1486-
Returns the three-way-diff contents of an unmerged file
1487-
1488-
Execute `git ls-files -uz <filename>` command & return the file contents
1489-
Of the three object refs.
1490-
1491-
Args:
1492-
filename (str): Filename (relative to the git repository)
1493-
path (str): Git repository filepath
1494-
"""
1495-
command = ["git", "ls-files", "-u", "-z", filename]
1496-
1497-
code, output, error = await execute(command, cwd=path)
1498-
if code != 0:
1499-
return {
1500-
"code": code,
1501-
"command": " ".join(command),
1502-
"message": error,
1503-
}
1504-
1505-
result = []
1506-
line_iterable = iter(strip_and_split(output))
1507-
for line in line_iterable:
1508-
ref = line.split(" ")[1]
1509-
content = await self.show_from_object(ref, path)
1510-
result.append(content)
1511-
return {"code": code, "contents": result}

jupyterlab_git/handlers.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -805,22 +805,6 @@ async def post(self, path: str = ""):
805805
self.finish(json.dumps(result))
806806

807807

808-
class GitMergeDiffHandler(GitHandler):
809-
"""
810-
Handler to show a 3-way diff of a file with merge conflicts.
811-
"""
812-
813-
@tornado.web.authenticated
814-
async def post(self, path: str = ""):
815-
data = self.get_json_body()
816-
filename = data["filename"]
817-
response = await self.git.get_unmerged_file_contents(
818-
filename,
819-
self.url2localpath(path),
820-
)
821-
self.finish(json.dumps(response))
822-
823-
824808
def setup_handlers(web_app):
825809
"""
826810
Setups all of the git command handlers.
@@ -858,7 +842,6 @@ def setup_handlers(web_app):
858842
("/tags", GitTagHandler),
859843
("/tag_checkout", GitTagCheckoutHandler),
860844
("/add", GitAddHandler),
861-
("/merge_diff", GitMergeDiffHandler),
862845
]
863846

864847
handlers = [

src/commandsAndMenu.tsx

Lines changed: 48 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import { PromiseDelegate } from '@lumino/coreutils';
2121
import { Message } from '@lumino/messaging';
2222
import { ContextMenu, Menu, Panel } from '@lumino/widgets';
2323
import * as React from 'react';
24-
import { DiffModel } from './components/diff/model';
24+
import { DiffModel, MergeDiffModel } from './components/diff/model';
2525
import { createPlainTextDiff } from './components/diff/PlainTextDiff';
26+
import { createPlainTextMergeDiff } from './components/diff/PlainTextMergeDiff';
2627
import { CONTEXT_COMMANDS } from './components/FileList';
2728
import { AUTH_ERROR_MESSAGES, requestAPI } from './git';
2829
import { logger } from './logger';
@@ -421,6 +422,7 @@ export function addCommands(
421422
*
422423
* @params model {Git.Diff.IModel<string>}: The diff model to display
423424
* @params isText {boolean}: Optional, whether the content is a plain text
425+
* @params isMerge {boolean}: Optional, whether the diff is a merge conflict
424426
* @returns the main area widget or null
425427
*/
426428
commands.addCommand(CommandIDs.gitShowDiff, {
@@ -433,7 +435,8 @@ export function addCommands(
433435
};
434436

435437
const buildDiffWidget =
436-
getDiffProvider(model.filename) ?? (isText && createPlainTextDiff);
438+
getDiffProvider(model.filename) ??
439+
(isText && model.base ? createPlainTextMergeDiff : createPlainTextDiff);
437440

438441
if (buildDiffWidget) {
439442
const id = `diff-${model.filename}-${model.reference.label}-${model.challenger.label}`;
@@ -569,33 +572,29 @@ export function addCommands(
569572

570573
const repositoryPath = gitModel.getRelativeFilePath();
571574
const filename = PathExt.join(repositoryPath, filePath);
572-
573-
// Merge conflict
574-
if (status === 'unmerged') {
575-
const t = await gitModel.getMergeDiff(filename);
576-
// TODO: handle
577-
console.log(t);
578-
continue;
579-
}
580-
581-
let diffContext = context;
582-
if (!diffContext) {
583-
const specialRef =
584-
status === 'staged'
585-
? Git.Diff.SpecialRef.INDEX
586-
: Git.Diff.SpecialRef.WORKING;
587-
diffContext = {
588-
currentRef: specialRef,
589-
previousRef: 'HEAD'
590-
};
591-
}
575+
const specialRef =
576+
status === 'staged'
577+
? Git.Diff.SpecialRef.INDEX
578+
: Git.Diff.SpecialRef.WORKING;
579+
580+
const diffContext: Git.Diff.IContext =
581+
status === 'unmerged'
582+
? {
583+
currentRef: 'MERGE_HEAD',
584+
previousRef: 'HEAD',
585+
baseRef: 'ORIG_HEAD'
586+
}
587+
: context ?? {
588+
currentRef: specialRef,
589+
previousRef: 'HEAD'
590+
};
592591

593592
const challengerRef = Git.Diff.SpecialRef[diffContext.currentRef as any]
594593
? { special: Git.Diff.SpecialRef[diffContext.currentRef as any] }
595594
: { git: diffContext.currentRef };
596595

597-
// Create the diff widget
598-
const model = new DiffModel<string>({
596+
// Base props used for Diff Model
597+
const props = {
599598
challenger: {
600599
content: async () => {
601600
return requestAPI<Git.IDiffContent>(
@@ -630,8 +629,32 @@ export function addCommands(
630629
diffContext.previousRef,
631630
source: diffContext.previousRef,
632631
updateAt: Date.now()
632+
},
633+
// Only add base when diff-ing merge conflicts
634+
base: diffContext.baseRef && {
635+
content: async () => {
636+
return requestAPI<Git.IDiffContent>(
637+
URLExt.join(repositoryPath, 'content'),
638+
'POST',
639+
{
640+
filename: filePath,
641+
reference: { git: diffContext.baseRef }
642+
}
643+
).then(data => data.content);
644+
},
645+
label:
646+
(Git.Diff.SpecialRef[diffContext.baseRef as any] as any) ||
647+
diffContext.baseRef,
648+
source: diffContext.baseRef,
649+
updateAt: Date.now()
633650
}
634-
});
651+
};
652+
653+
// Create the diff widget
654+
const model =
655+
status === 'unmerged'
656+
? new MergeDiffModel<string>(props)
657+
: new DiffModel<string>(props);
635658

636659
const widget = await commands.execute(CommandIDs.gitShowDiff, {
637660
model,

0 commit comments

Comments
 (0)