From 9371a5b25a73a8e20a23c4090cd60f5af1af655c Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 3 Dec 2018 11:44:26 -0800 Subject: [PATCH 1/6] Implement handling renames and copies --- README.md | 2 ++ src/diff.pegjs | 43 +++++++++++++++++++++++++++++++++++++++++++ test/diff.test.js | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+) diff --git a/README.md b/README.md index 19a1e03..c8f7942 100644 --- a/README.md +++ b/README.md @@ -53,3 +53,5 @@ parse(diffStr) ] } ``` + +If the diff includes a similarity index line (from a detected copy or rename), the `similarity` property will be set, and will be a number. diff --git a/src/diff.pegjs b/src/diff.pegjs index b54e73f..0838e8f 100644 --- a/src/diff.pegjs +++ b/src/diff.pegjs @@ -43,6 +43,19 @@ patch.binary = !!binary return patch } + + function postProcessSimilarityDiff (renamed_or_copied, similarity_index, old_file, new_file) { + return { + oldPath: old_file, + newPath: new_file, + oldMode: null, + newMode: null, + hunks: [], + status: renamed_or_copied, + similarity: similarity_index, + binary: null + } + } } diffs @@ -50,6 +63,8 @@ diffs diff = binary_merge_conflict_diff + / rename_only_diff + / copy_only_diff / merge_conflict_diff / unmerged_path / binary_diff @@ -67,6 +82,16 @@ regular_diff binary_merge_conflict_diff = path:merge_conflict_header_line index_line binary_declaration { return postProcessMergeConflictDiff(path, undefined, true) } +rename_only_diff + = header:rename_or_copy_diff_header_line similarity:similarity_index old_file:rename_from new_file:rename_to { + return postProcessSimilarityDiff("renamed", similarity, old_file, new_file) + } + +copy_only_diff + = header:rename_or_copy_diff_header_line similarity:similarity_index old_file:copy_from new_file:copy_to { + return postProcessSimilarityDiff("copied", similarity, old_file, new_file) + } + merge_conflict_diff = path:merge_conflict_header_line index_line patch:patch? { return postProcessMergeConflictDiff(path, patch) } @@ -127,9 +152,15 @@ hunk_line diff_header_line = 'diff ' options:TEXT_NO_SPACES ' ' file_name:file_name_str NL { return {file_name} } +rename_or_copy_diff_header_line + = 'diff ' options:TEXT_NO_SPACES ' ' files_unused:TEXT NL + file_name_str = str:TEXT { return str.substr(str.length/2 + 1) } +similarity_index + = 'similarity index ' idx:NUMBER '%' NL { return idx } + file_mode_section = explicit_file_modes:(new_or_deleted_file_mode / changed_file_modes)? index_file_modes:index_line? { return explicit_file_modes || index_file_modes } @@ -142,6 +173,18 @@ new_or_deleted_file_mode changed_file_modes = 'old mode ' old_mode:TEXT NL 'new mode ' new_mode:TEXT NL { return {old_mode, new_mode} } +rename_from + = 'rename from ' file:TEXT NL { return file } + +rename_to + = 'rename to ' file:TEXT NL { return file } + +copy_from + = 'copy from ' file:TEXT NL { return file } + +copy_to + = 'copy to ' file:TEXT NL { return file } + index_line = 'index ' TEXT_NO_SPACES ' ' file_mode:TEXT NL { return {old_mode: file_mode, new_mode: file_mode} } / 'index ' TEXT_NO_SPACES NL diff --git a/test/diff.test.js b/test/diff.test.js index 03f3280..594a35d 100644 --- a/test/diff.test.js +++ b/test/diff.test.js @@ -543,6 +543,44 @@ exports.testNoPatch = function(test) { test.done() } +exports.testRenameCopy = function(test) { + var str = dedent` + diff --git old/file.png new/file.png + similarity index 90% + rename from old/file.png + rename to new/file.png + diff --git copy/file.png copy/file2.png + similarity index 100% + copy from copy/file.png + copy to copy/file2.png + ` + + const output = diff.parse(str) + assert.deepEqual(output, [ + { + oldPath: 'old/file.png', + newPath: 'new/file.png', + oldMode: null, + newMode: null, + status: 'renamed', + similarity: 90, + hunks: [], + binary: null + }, + { + oldPath: 'copy/file.png', + newPath: 'copy/file2.png', + oldMode: null, + newMode: null, + status: 'copied', + similarity: 100, + hunks: [], + binary: null + }, + ]) + test.done() +} + exports.testMergeConflictNoPatch = function(test) { var str = dedent` diff --cc file-0.txt From 0db885513b3831800c604493e17b3d35584122f4 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 3 Dec 2018 11:54:05 -0800 Subject: [PATCH 2/6] Combine most copy/rename rules --- src/diff.pegjs | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/src/diff.pegjs b/src/diff.pegjs index 0838e8f..38f5c10 100644 --- a/src/diff.pegjs +++ b/src/diff.pegjs @@ -44,14 +44,14 @@ return patch } - function postProcessSimilarityDiff (renamed_or_copied, similarity_index, old_file, new_file) { + function postProcessSimilarityDiff (rename_or_copy, similarity_index, old_file, new_file) { return { oldPath: old_file, newPath: new_file, oldMode: null, newMode: null, hunks: [], - status: renamed_or_copied, + status: rename_or_copy === 'copy' ? 'copied' : 'renamed', similarity: similarity_index, binary: null } @@ -63,8 +63,7 @@ diffs diff = binary_merge_conflict_diff - / rename_only_diff - / copy_only_diff + / rename_or_copy_diff / merge_conflict_diff / unmerged_path / binary_diff @@ -82,14 +81,9 @@ regular_diff binary_merge_conflict_diff = path:merge_conflict_header_line index_line binary_declaration { return postProcessMergeConflictDiff(path, undefined, true) } -rename_only_diff - = header:rename_or_copy_diff_header_line similarity:similarity_index old_file:rename_from new_file:rename_to { - return postProcessSimilarityDiff("renamed", similarity, old_file, new_file) - } - -copy_only_diff - = header:rename_or_copy_diff_header_line similarity:similarity_index old_file:copy_from new_file:copy_to { - return postProcessSimilarityDiff("copied", similarity, old_file, new_file) +rename_or_copy_diff + = header:rename_or_copy_diff_header_line similarity:similarity_index old_file:rename_copy_from new_file:rename_copy_to { + return postProcessSimilarityDiff(old_file.operation, similarity, old_file.file, new_file.file) } merge_conflict_diff @@ -173,17 +167,11 @@ new_or_deleted_file_mode changed_file_modes = 'old mode ' old_mode:TEXT NL 'new mode ' new_mode:TEXT NL { return {old_mode, new_mode} } -rename_from - = 'rename from ' file:TEXT NL { return file } - -rename_to - = 'rename to ' file:TEXT NL { return file } - -copy_from - = 'copy from ' file:TEXT NL { return file } +rename_copy_from + = operation:('rename' / 'copy') ' from ' file:TEXT NL { return {operation, file} } -copy_to - = 'copy to ' file:TEXT NL { return file } +rename_copy_to + = operation:('rename' / 'copy') ' to ' file:TEXT NL { return {operation, file} } index_line = 'index ' TEXT_NO_SPACES ' ' file_mode:TEXT NL { return {old_mode: file_mode, new_mode: file_mode} } From e95dad3715189f31b2be98f1a27d14aa4e022102 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Mon, 3 Dec 2018 11:55:20 -0800 Subject: [PATCH 3/6] Rename variables to be more clear --- src/diff.pegjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/diff.pegjs b/src/diff.pegjs index 38f5c10..83ce674 100644 --- a/src/diff.pegjs +++ b/src/diff.pegjs @@ -82,8 +82,8 @@ binary_merge_conflict_diff = path:merge_conflict_header_line index_line binary_declaration { return postProcessMergeConflictDiff(path, undefined, true) } rename_or_copy_diff - = header:rename_or_copy_diff_header_line similarity:similarity_index old_file:rename_copy_from new_file:rename_copy_to { - return postProcessSimilarityDiff(old_file.operation, similarity, old_file.file, new_file.file) + = header:rename_or_copy_diff_header_line similarity:similarity_index copy_from:rename_copy_from copy_to:rename_copy_to { + return postProcessSimilarityDiff(copy_from.operation, similarity, copy_from.file, copy_to.file) } merge_conflict_diff From e5a25300cf70524fea06cd573b9f214b05b50a8b Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 18 Dec 2018 16:52:36 -0800 Subject: [PATCH 4/6] Don't make modes and binary field null for renames We don't get this info from Git for re-names. It felt funky to say that the modes were null when the files actually have modes, we just don't know what they are. Plus, we use a null file mode to indicate that the file does not exist for created/deleted files. Co-authored-by: Michelle Tilley --- src/diff.pegjs | 3 --- test/diff.test.js | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/diff.pegjs b/src/diff.pegjs index 83ce674..50535aa 100644 --- a/src/diff.pegjs +++ b/src/diff.pegjs @@ -48,12 +48,9 @@ return { oldPath: old_file, newPath: new_file, - oldMode: null, - newMode: null, hunks: [], status: rename_or_copy === 'copy' ? 'copied' : 'renamed', similarity: similarity_index, - binary: null } } } diff --git a/test/diff.test.js b/test/diff.test.js index 594a35d..b756ad0 100644 --- a/test/diff.test.js +++ b/test/diff.test.js @@ -560,22 +560,16 @@ exports.testRenameCopy = function(test) { { oldPath: 'old/file.png', newPath: 'new/file.png', - oldMode: null, - newMode: null, status: 'renamed', similarity: 90, hunks: [], - binary: null }, { oldPath: 'copy/file.png', newPath: 'copy/file2.png', - oldMode: null, - newMode: null, status: 'copied', similarity: 100, hunks: [], - binary: null }, ]) test.done() From d36edcba3fbc640e09de3f669c82274ff681cdb9 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 18 Dec 2018 16:53:10 -0800 Subject: [PATCH 5/6] Drop unused `header` variable Co-authored-by: Michelle Tilley --- src/diff.pegjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diff.pegjs b/src/diff.pegjs index 50535aa..6b44221 100644 --- a/src/diff.pegjs +++ b/src/diff.pegjs @@ -79,7 +79,7 @@ binary_merge_conflict_diff = path:merge_conflict_header_line index_line binary_declaration { return postProcessMergeConflictDiff(path, undefined, true) } rename_or_copy_diff - = header:rename_or_copy_diff_header_line similarity:similarity_index copy_from:rename_copy_from copy_to:rename_copy_to { + = rename_or_copy_diff_header_line similarity:similarity_index copy_from:rename_copy_from copy_to:rename_copy_to { return postProcessSimilarityDiff(copy_from.operation, similarity, copy_from.file, copy_to.file) } From 903a5b54f570cfe8d064c3f22e8def157fd9f020 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Tue, 18 Dec 2018 17:44:10 -0800 Subject: [PATCH 6/6] Support case of renamed file with diff and mode change Co-authored-by: Michelle Tilley --- src/diff.pegjs | 17 ++++++++++++----- test/diff.test.js | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 5 deletions(-) diff --git a/src/diff.pegjs b/src/diff.pegjs index 6b44221..e6194ab 100644 --- a/src/diff.pegjs +++ b/src/diff.pegjs @@ -44,14 +44,19 @@ return patch } - function postProcessSimilarityDiff (rename_or_copy, similarity_index, old_file, new_file) { - return { + function postProcessSimilarityDiff (rename_or_copy, similarity_index, old_file, new_file, file_modes, patch) { + const diff = { oldPath: old_file, newPath: new_file, - hunks: [], + hunks: patch ? patch.hunks : [], status: rename_or_copy === 'copy' ? 'copied' : 'renamed', similarity: similarity_index, } + if (file_modes) { + diff.oldMode = file_modes.old_mode + diff.newMode = file_modes.new_mode + } + return diff } } @@ -79,8 +84,10 @@ binary_merge_conflict_diff = path:merge_conflict_header_line index_line binary_declaration { return postProcessMergeConflictDiff(path, undefined, true) } rename_or_copy_diff - = rename_or_copy_diff_header_line similarity:similarity_index copy_from:rename_copy_from copy_to:rename_copy_to { - return postProcessSimilarityDiff(copy_from.operation, similarity, copy_from.file, copy_to.file) + = rename_or_copy_diff_header_line modes:changed_file_modes? similarity:similarity_index copy_from:rename_copy_from copy_to:rename_copy_to + index_modes:index_line? patch:patch? + { + return postProcessSimilarityDiff(copy_from.operation, similarity, copy_from.file, copy_to.file, modes || index_modes, patch) } merge_conflict_diff diff --git a/test/diff.test.js b/test/diff.test.js index b756ad0..a8a847f 100644 --- a/test/diff.test.js +++ b/test/diff.test.js @@ -575,6 +575,47 @@ exports.testRenameCopy = function(test) { test.done() } +exports.testRenameWithChangedLinesAndModeChange = function(test) { + var str = dedent` + diff --git file.txt rename-file.txt + old mode 100644 + new mode 100755 + similarity index 76% + rename from file.txt + rename to rename-file.txt + index 471a7b8..3e32ec2 + --- file.txt + +++ rename-file.txt + @@ -1,4 +1,5 @@ + foo + bar + baz + +qux + + `; + + const output = diff.parse(str) + assert.deepEqual(output, [ + { + oldPath: "file.txt", + newPath: "rename-file.txt", + oldMode: "100644", + newMode: "100755", + status: "renamed", + similarity: 76, + hunks: [{ + oldStartLine: 1, + oldLineCount: 4, + newStartLine: 1, + newLineCount: 5, + heading: '', + lines: [' foo', ' bar', ' baz', '+qux'] + }] + } + ]); + test.done() +} + exports.testMergeConflictNoPatch = function(test) { var str = dedent` diff --cc file-0.txt