diff --git a/components/commit/commit.js b/components/commit/commit.js
index 028ce110d..d5c8b0d08 100644
--- a/components/commit/commit.js
+++ b/components/commit/commit.js
@@ -55,8 +55,8 @@ class CommitViewModel {
this.authorDateFromNow(this.authorDate().fromNow());
this.authorName(args.authorName);
this.authorEmail(args.authorEmail);
- this.numberOfAddedLines(args.fileLineDiffs.length > 0 ? args.fileLineDiffs[0][0] : 0);
- this.numberOfRemovedLines(args.fileLineDiffs.length > 0 ? args.fileLineDiffs[0][1] : 0);
+ this.numberOfAddedLines(args.total.additions);
+ this.numberOfRemovedLines(args.total.deletions);
this.fileLineDiffs(args.fileLineDiffs);
this.isInited = true;
this.commitDiff = ko.observable(components.create('commitDiff', {
diff --git a/components/commitdiff/commitdiff.html b/components/commitdiff/commitdiff.html
index 1da08fd46..8cde60b26 100644
--- a/components/commitdiff/commitdiff.html
+++ b/components/commitdiff/commitdiff.html
@@ -19,7 +19,7 @@
-
+
(
diff --git a/components/commitdiff/commitdiff.js b/components/commitdiff/commitdiff.js
index 84ff2c0f9..eabd7bdb2 100644
--- a/components/commitdiff/commitdiff.js
+++ b/components/commitdiff/commitdiff.js
@@ -14,7 +14,6 @@ class CommitDiff {
this.wordWrap = args.wordWrap = args.wordWrap || components.create('textdiff.wordwrap');
this.whiteSpace = args.whiteSpace = args.whiteSpace || components.create('textdiff.whitespace');
- args.fileLineDiffs.shift(); // remove first line that has "total"
this.loadFileLineDiffs(args);
}
diff --git a/components/commitdiff/commitlinediff.js b/components/commitdiff/commitlinediff.js
index eae9d9226..4a3ff374b 100644
--- a/components/commitdiff/commitlinediff.js
+++ b/components/commitdiff/commitlinediff.js
@@ -4,10 +4,12 @@ const programEvents = require('ungit-program-events');
class CommitLineDiff {
constructor(args, fileLineDiff) {
- this.added = ko.observable(fileLineDiff[0]);
- this.removed = ko.observable(fileLineDiff[1]);
- this.fileName = ko.observable(fileLineDiff[2]);
- this.fileType = fileLineDiff[3];
+ this.added = ko.observable(fileLineDiff.additions);
+ this.removed = ko.observable(fileLineDiff.deletions);
+ this.fileName = ko.observable(fileLineDiff.fileName);
+ this.oldFileName = ko.observable(fileLineDiff.oldFileName);
+ this.displayName = ko.observable(fileLineDiff.displayName);
+ this.fileType = fileLineDiff.type;
this.isShowingDiffs = ko.observable(false);
this.repoPath = args.repoPath;
this.server = args.server;
@@ -21,6 +23,7 @@ class CommitLineDiff {
getSpecificDiff() {
return components.create(`${this.fileType}diff`, {
filename: this.fileName(),
+ oldFilename: this.oldFileName(),
repoPath: this.repoPath,
server: this.server,
sha1: this.sha1,
diff --git a/components/imagediff/imagediff.js b/components/imagediff/imagediff.js
index ec13f9e64..7841c8e6e 100644
--- a/components/imagediff/imagediff.js
+++ b/components/imagediff/imagediff.js
@@ -7,6 +7,7 @@ components.register('imagediff', args => new ImageDiffViewModel(args));
class ImageDiffViewModel {
constructor(args) {
this.filename = args.filename;
+ this.oldFilename = args.oldFilename;
this.repoPath = args.repoPath;
this.isNew = ko.observable(false);
this.isRemoved = ko.observable(false);
@@ -16,9 +17,9 @@ class ImageDiffViewModel {
if (this.isRemoved()) return 'removed';
return 'changed';
});
- const gitDiffURL = `${ungit.config.rootPath}/api/diff/image?path=${encodeURIComponent(this.repoPath())}&filename=${this.filename}&version=`;
- this.oldImageSrc = gitDiffURL + (this.sha1 ? this.sha1 + '^' : 'HEAD');
- this.newImageSrc = gitDiffURL + (this.sha1 ? this.sha1 : 'current');
+ const gitDiffURL = `${ungit.config.rootPath}/api/diff/image?path=${encodeURIComponent(this.repoPath())}`;
+ this.oldImageSrc = gitDiffURL + `&filename=${this.oldFilename}&version=${(this.sha1 ? this.sha1 + '^' : 'HEAD')}`;
+ this.newImageSrc = gitDiffURL + `&filename=${this.filename}&version=${(this.sha1 ? this.sha1 : 'current')}`;
this.isShowingDiffs = args.isShowingDiffs;
this.rightArrowIcon = octicons['arrow-right'].toSVG({ 'height': 100 });
this.downArrowIcon = octicons['arrow-down'].toSVG({ 'height': 100 });
diff --git a/components/staging/staging.js b/components/staging/staging.js
index ffb55e582..1d83fd5eb 100644
--- a/components/staging/staging.js
+++ b/components/staging/staging.js
@@ -175,16 +175,16 @@ class StagingViewModel {
setFiles(files) {
const newFiles = [];
- for(const file in files) {
- let fileViewModel = this.filesByPath[file];
+ for(let fileStatus of Object.values(files)) {
+ let fileViewModel = this.filesByPath[fileStatus.fileName];
if (!fileViewModel) {
- this.filesByPath[file] = fileViewModel = new FileViewModel(this, file);
+ this.filesByPath[fileStatus.fileName] = fileViewModel = new FileViewModel(this, fileStatus.fileName, fileStatus.oldFileName, fileStatus.displayName);
} else {
// this is mainly for patching and it may not fire due to the fact that
// '/commit' triggers working-tree-changed which triggers throttled refresh
fileViewModel.diff().invalidateDiff();
}
- fileViewModel.setState(files[file]);
+ fileViewModel.setState(fileStatus);
newFiles.push(fileViewModel);
}
this.files(newFiles);
@@ -324,12 +324,13 @@ class StagingViewModel {
}
class FileViewModel {
- constructor(staging, name) {
+ constructor(staging, name, oldName, displayName) {
this.staging = staging;
this.server = staging.server;
this.editState = ko.observable('staged'); // staged, patched and none
this.name = ko.observable(name);
- this.displayName = ko.observable(name);
+ this.oldName = ko.observable(oldName);
+ this.displayName = ko.observable(displayName);
this.isNew = ko.observable(false);
this.removed = ko.observable(false);
this.conflict = ko.observable(false);
@@ -341,7 +342,7 @@ class FileViewModel {
// only show modfied whe not removed, not conflicted, not new, not renamed
// and length of additions and deletions is 0.
return !this.removed() && !this.conflict() && !this.isNew() &&
- !this.renamed() && this.additions().length === 0 && this.deletions().length === 0;
+ this.additions().length === 0 && this.deletions().length === 0;
});
this.fileType = ko.observable('text');
this.patchLineList = ko.observableArray();
@@ -366,6 +367,8 @@ class FileViewModel {
getSpecificDiff() {
return components.create(!this.name() || `${this.fileType()}diff`, {
filename: this.name(),
+ oldFilename: this.oldName(),
+ displayFilename: this.displayName(),
repoPath: this.staging.repoPath,
server: this.server,
textDiffType: this.staging.textDiffType,
@@ -444,7 +447,6 @@ class FileViewModel {
}
toggleDiffs() {
- if (this.renamed()) return; // do not show diffs for renames
this.isShowingDiffs(!this.isShowingDiffs());
}
diff --git a/components/textdiff/textdiff.js b/components/textdiff/textdiff.js
index 0b6a684f9..980836ae8 100644
--- a/components/textdiff/textdiff.js
+++ b/components/textdiff/textdiff.js
@@ -62,6 +62,7 @@ class WhiteSpace {
class TextDiffViewModel {
constructor(args) {
this.filename = args.filename;
+ this.oldFilename = args.oldFilename
this.repoPath = args.repoPath;
this.server = args.server;
this.sha1 = args.sha1;
@@ -99,6 +100,7 @@ class TextDiffViewModel {
getDiffArguments() {
return {
file: this.filename,
+ oldFile: this.oldFilename,
path: this.repoPath(),
sha1: this.sha1 ? this.sha1 : '',
whiteSpace: this.whiteSpace.value()
diff --git a/source/git-api.js b/source/git-api.js
index 0b6c978e1..5b90761ab 100644
--- a/source/git-api.js
+++ b/source/git-api.js
@@ -260,7 +260,7 @@ exports.registerApi = (env) => {
app.get(`${exports.pathPrefix}/diff`, ensureAuthenticated, ensurePathExists, (req, res) => {
const isIgnoreWhiteSpace = req.query.whiteSpace === "true" ? true : false;
- jsonResultOrFailProm(res, gitPromise.diffFile(req.query.path, req.query.file, req.query.sha1, isIgnoreWhiteSpace));
+ jsonResultOrFailProm(res, gitPromise.diffFile(req.query.path, req.query.file, req.query.oldFile, req.query.sha1, isIgnoreWhiteSpace));
});
app.get(`${exports.pathPrefix}/diff/image`, ensureAuthenticated, ensurePathExists, (req, res) => {
@@ -327,11 +327,11 @@ exports.registerApi = (env) => {
});
app.get(`${exports.pathPrefix}/show`, ensureAuthenticated, (req, res) => {
- jsonResultOrFailProm(res, gitPromise(['show', '--numstat', req.query.sha1], req.query.path).then(gitParser.parseGitLog));
+ jsonResultOrFailProm(res, gitPromise(['show', '--numstat', '-z', req.query.sha1], req.query.path).then(gitParser.parseGitLog));
});
app.get(`${exports.pathPrefix}/head`, ensureAuthenticated, ensurePathExists, (req, res) => {
- const task = gitPromise(['log', '--decorate=full', '--pretty=fuller', '--parents', '--max-count=1'], req.query.path)
+ const task = gitPromise(['log', '--decorate=full', '--pretty=fuller', '-z', '--parents', '--max-count=1'], req.query.path)
.then(gitParser.parseGitLog)
.catch((err) => {
if (err.stderr.indexOf('fatal: bad default revision \'HEAD\'') == 0)
@@ -614,7 +614,7 @@ exports.registerApi = (env) => {
});
app.get(`${exports.pathPrefix}/stashes`, ensureAuthenticated, ensurePathExists, (req, res) => {
- const task = gitPromise(['stash', 'list', '--decorate=full', '--pretty=fuller', '--parents', '--numstat'], req.query.path)
+ const task = gitPromise(['stash', 'list', '--decorate=full', '--pretty=fuller', '-z', '--parents', '--numstat'], req.query.path)
.then(gitParser.parseGitLog);
jsonResultOrFailProm(res, task);
});
diff --git a/source/git-parser.js b/source/git-parser.js
index 627eb16f4..5d1896290 100644
--- a/source/git-parser.js
+++ b/source/git-parser.js
@@ -3,24 +3,36 @@ const fileType = require('./utils/file-type.js');
const _ = require('lodash');
exports.parseGitStatus = (text, args) => {
- const lines = text.split('\n');
+ const lines = text.split('\x00');
const files = {};
// skipping first line...
- lines.slice(1).forEach((line) => {
- if (line == '') return;
+ let lineIterator = lines.slice(1).values();
+
+ for(let line of lineIterator){
+ if (line == '') continue;
const status = line.slice(0, 2);
- const filename = line.slice(3).trim().replace(/^"(.*)"$/, '$1'); // may contain old and renamed file name.
- const finalFilename = status[0] == 'R' ? filename.slice(filename.indexOf('>') + 2) : filename;
- files[finalFilename] = {
- displayName: filename,
+ const newFileName = line.slice(3).trim();
+ let oldFileName;
+ let displayName;
+ if (status[0] == 'R') {
+ oldFileName = lineIterator.next().value
+ displayName = `${oldFileName} → ${newFileName}`;
+ } else {
+ oldFileName = newFileName;
+ displayName = newFileName;
+ }
+ files[newFileName] = {
+ fileName: newFileName,
+ oldFileName: oldFileName,
+ displayName: displayName,
staged: status[0] == 'A' || status[0] == 'M',
removed: status[0] == 'D' || status[1] == 'D',
isNew: (status[0] == '?' || status[0] == 'A') && !(status[0] == 'D' || status[1] == 'D'),
conflict: (status[0] == 'A' && status[1] == 'A') || status[0] == 'U' || status[1] == 'U',
renamed: status[0] == 'R',
- type: fileType(finalFilename)
+ type: fileType(newFileName)
};
- });
+ }
return {
isMoreToLoad: false,
@@ -30,16 +42,19 @@ exports.parseGitStatus = (text, args) => {
};
};
+const fileChangeRegex = /(?[\d-]+)\t(?[\d-]+)\t((?[^\x00]+?)\x00|\x00(?[^\x00]+?)\x00(?[^\x00]+?)\x00)/g;
+
exports.parseGitStatusNumstat = (text) => {
const result = {};
- text.split('\n').forEach((line) => {
- if (line == '') return;
- const parts = line.split('\t');
- result[parts[2]] = {
- additions: parts[0],
- deletions: parts[1]
+ fileChangeRegex.lastIndex = 0;
+ let match = fileChangeRegex.exec(text);
+ while (match !== null) {
+ result[match.groups.fileName || match.groups.newFileName] = {
+ additions: match.groups.additions,
+ deletions: match.groups.deletions
};
- });
+ match = fileChangeRegex.exec(text);
+ }
return result;
};
@@ -141,24 +156,50 @@ exports.parseGitLog = (data) => {
currentCommmit.message += row.trim();
}
const parseFileChanges = (row, index) => {
- if (rows.length === index + 1 || rows[index + 1] && rows[index + 1].indexOf('commit ') === 0) {
- const total = [0, 0, 'Total'];
- for (let n = 0; n < currentCommmit.fileLineDiffs.length; n++) {
- const fileLineDiff = currentCommmit.fileLineDiffs[n];
- if (!isNaN(parseInt(fileLineDiff[0], 10))) {
- total[0] += fileLineDiff[0] = parseInt(fileLineDiff[0], 10);
- }
- if (!isNaN(parseInt(fileLineDiff[1], 10))) {
- total[1] += fileLineDiff[1] = parseInt(fileLineDiff[1], 10);
- }
+ // git log is using -z so all the file changes are on one line
+ // merge commits start the file changes with a null
+ if (row[0] === '\x00'){
+ row = row.slice(1);
+ }
+ fileChangeRegex.lastIndex = 0;
+ while (row[fileChangeRegex.lastIndex] && row[fileChangeRegex.lastIndex] !== '\x00') {
+ let match = fileChangeRegex.exec(row);
+ let fileName = match.groups.fileName || match.groups.newFileName;
+ let oldFileName = match.groups.oldFileName || match.groups.fileName;
+ let displayName;
+ if(match.groups.oldFileName) {
+ displayName = `${match.groups.oldFileName} → ${match.groups.newFileName}`;
+ } else {
+ displayName = fileName;
}
- currentCommmit.fileLineDiffs.splice(0, 0, total);
- parser = parseCommitLine;
- return;
+ currentCommmit.fileLineDiffs.push({
+ additions: match.groups.additions,
+ deletions: match.groups.deletions,
+ fileName: fileName,
+ oldFileName: oldFileName,
+ displayName: displayName,
+ type: fileType(fileName)
+ });
+ }
+ const nextRow = row.slice(fileChangeRegex.lastIndex + 1);
+ const total = {
+ additions: 0,
+ deletions: 0
+ };
+ for (let fileLineDiff of currentCommmit.fileLineDiffs) {
+ if (!isNaN(parseInt(fileLineDiff.additions, 10))) {
+ total.additions += fileLineDiff.additions = parseInt(fileLineDiff.additions, 10);
+ }
+ if (!isNaN(parseInt(fileLineDiff.deletions, 10))) {
+ total.deletions += fileLineDiff.deletions = parseInt(fileLineDiff.deletions, 10);
+ }
+ }
+ currentCommmit.total = total;
+ parser = parseCommitLine;
+ if (nextRow) {
+ parser(nextRow, index);
}
- const splitted = row.split('\t');
- splitted.push(fileType(splitted[2]));
- currentCommmit.fileLineDiffs.push(splitted);
+ return;
}
let parser = parseCommitLine;
const rows = data.split('\n');
diff --git a/source/git-promise.js b/source/git-promise.js
index 1a49fe803..96cf49499 100644
--- a/source/git-promise.js
+++ b/source/git-promise.js
@@ -177,17 +177,17 @@ const getGitError = (args, stderr, stdout) => {
git.status = (repoPath, file) => {
return Bluebird.props({
- numStatsStaged: git([gitOptionalLocks, 'diff', '--no-renames', '--numstat', '--cached', '--', (file || '')], repoPath)
+ numStatsStaged: git([gitOptionalLocks, 'diff', '--numstat', '--cached', '-z', '--', (file || '')], repoPath)
.then(gitParser.parseGitStatusNumstat),
numStatsUnstaged: Bluebird.resolve().then(() => {
if (config.isEnableNumStat) {
- return git([gitOptionalLocks, 'diff', '--no-renames', '--numstat', '--', (file || '')], repoPath)
+ return git([gitOptionalLocks, 'diff', '--numstat', '-z', '--', (file || '')], repoPath)
.then(gitParser.parseGitStatusNumstat);
} else {
return {}
}
}),
- status: git([gitOptionalLocks, 'status', '-s', '-b', '-u', (file || '')], repoPath)
+ status: git([gitOptionalLocks, 'status', '-s', '-b', '-u', '-z', (file || '')], repoPath)
.then(gitParser.parseGitStatus)
.then((status) => {
return Bluebird.props({
@@ -282,11 +282,15 @@ git.binaryFileContent = (repoPath, filename, version, outPipe) => {
return git(['show', `${version}:${filename}`], repoPath, null, outPipe);
}
-git.diffFile = (repoPath, filename, sha1, ignoreWhiteSpace) => {
+git.diffFile = (repoPath, filename, oldFilename, sha1, ignoreWhiteSpace) => {
if (sha1) {
return git(['rev-list', '--max-parents=0', sha1], repoPath).then((initialCommitSha1) => {
let prevSha1 = sha1 == initialCommitSha1.trim() ? gitEmptyReproSha1 : `${sha1}^`;
- return git(['diff', ignoreWhiteSpace ? '-w' : '', prevSha1, sha1, "--", filename.trim()], repoPath);
+ if (oldFilename && oldFilename !== filename) {
+ return git(['diff', ignoreWhiteSpace ? '-w' : '', `${prevSha1}:${oldFilename.trim()}`, `${sha1}:${filename.trim()}`], repoPath);
+ } else {
+ return git(['diff', ignoreWhiteSpace ? '-w' : '', prevSha1, sha1, "--", filename.trim()], repoPath);
+ }
});
}
@@ -304,6 +308,8 @@ git.diffFile = (repoPath, filename, sha1, ignoreWhiteSpace) => {
} else {
if (file && file.isNew) {
return git(['diff', '--no-index', isWindows ? 'NUL' : '/dev/null', filename.trim()], repoPath, true);
+ } else if (file && file.renamed) {
+ return git(['diff', ignoreWhiteSpace ? '-w' : '', `HEAD:${oldFilename}`, filename.trim()], repoPath);
} else {
return git(['diff', ignoreWhiteSpace ? '-w' : '', 'HEAD', '--', filename.trim()], repoPath);
}
@@ -437,7 +443,7 @@ git.revParse = (repoPath) => {
}
git.log = (path, limit, skip, maxActiveBranchSearchIteration) => {
- return git(['log', '--cc', '--decorate=full', '--show-signature', '--date=default', '--pretty=fuller', '--branches', '--tags', '--remotes', '--parents', '--no-notes', '--numstat', '--date-order', `--max-count=${limit}`, `--skip=${skip}`], path)
+ return git(['log', '--cc', '--decorate=full', '--show-signature', '--date=default', '--pretty=fuller', '-z', '--branches', '--tags', '--remotes', '--parents', '--no-notes', '--numstat', '--date-order', `--max-count=${limit}`, `--skip=${skip}`], path)
.then(gitParser.parseGitLog)
.then((log) => {
log = log ? log : [];
diff --git a/test/spec.git-api.branching.js b/test/spec.git-api.branching.js
index 5e497184f..ef6ba6108 100644
--- a/test/spec.git-api.branching.js
+++ b/test/spec.git-api.branching.js
@@ -134,9 +134,12 @@ describe('git-api branching', function () {
it('status should list the changed file', () => {
return common.get(req, '/status', { path: testDir }).then(res => {
+
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile1]).to.eql({
displayName: testFile1,
+ fileName: testFile1,
+ oldFileName: testFile1,
isNew: false,
staged: false,
removed: false,
diff --git a/test/spec.git-api.conflict.js b/test/spec.git-api.conflict.js
index 8215612fc..38fac3e35 100644
--- a/test/spec.git-api.conflict.js
+++ b/test/spec.git-api.conflict.js
@@ -55,6 +55,8 @@ describe('git-api conflict rebase', function () {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile1]).to.eql({
displayName: testFile1,
+ fileName: testFile1,
+ oldFileName: testFile1,
isNew: false,
staged: false,
removed: false,
@@ -121,6 +123,8 @@ describe('git-api conflict checkout', function() {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile1]).to.eql({
displayName: testFile1,
+ fileName: testFile1,
+ oldFileName: testFile1,
isNew: false,
staged: false,
removed: false,
@@ -176,6 +180,8 @@ describe('git-api conflict merge', function () {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile1]).to.eql({
displayName: testFile1,
+ fileName: testFile1,
+ oldFileName: testFile1,
isNew: false,
staged: false,
removed: false,
@@ -204,9 +210,16 @@ describe('git-api conflict merge', function () {
return common.get(req, '/gitlog', { path: testDir }).then((res) => {
expect(res.nodes).to.be.a('array');
expect(res.nodes.length).to.be(4);
- expect(res.nodes[0].fileLineDiffs.length).to.be(2);
- expect(res.nodes[0].fileLineDiffs[0]).to.eql([1, 1, 'Total']);
- expect(res.nodes[0].fileLineDiffs[1]).to.eql([1, 1, testFile1, 'text']);
+ expect(res.nodes[0].total).to.eql({additions: 1, deletions: 1});
+ expect(res.nodes[0].fileLineDiffs.length).to.be(1);
+ expect(res.nodes[0].fileLineDiffs[0]).to.eql({
+ additions: 1,
+ deletions: 1,
+ fileName: testFile1,
+ oldFileName: testFile1,
+ displayName: testFile1,
+ type: 'text'
+ });
});
});
@@ -255,6 +268,8 @@ describe('git-api conflict solve by deleting', function () {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile1]).to.eql({
displayName: testFile1,
+ fileName: testFile1,
+ oldFileName: testFile1,
isNew: false,
staged: false,
removed: false,
diff --git a/test/spec.git-api.diff.js b/test/spec.git-api.diff.js
index d737a4637..82ae75cf0 100644
--- a/test/spec.git-api.diff.js
+++ b/test/spec.git-api.diff.js
@@ -24,6 +24,7 @@ describe('git-api diff', () => {
after(() => common.post(req, '/testing/cleanup', undefined));
const testFile = 'afile.txt';
+ const testFile2 = 'anotherfile.txt';
const testImage = 'icon.png'
it('diff on non existing file should fail', () => {
@@ -56,10 +57,10 @@ describe('git-api diff', () => {
});
it('should be possible to commit a file', () => {
- return common.post(req, '/commit', { path: testDir, message: "Init", files: [{ name: testFile }] });
+ return common.post(req, '/commit', { path: testDir, message: "Init File", files: [{ name: testFile }] });
});
it('should be possible to commit an image file', () => {
- return common.post(req, '/commit', { path: testDir, message: "Init", files: [{ name: testImage }] });
+ return common.post(req, '/commit', { path: testDir, message: "Init Image", files: [{ name: testImage }] });
});
it('diff on first commit should work', () => {
@@ -123,12 +124,35 @@ describe('git-api diff', () => {
.then((res) => expect(res.toString()).to.be('png'));
});
- it('should be possible to commit a file', () => {
- return common.post(req, '/commit', { path: testDir, message: "Init", files: [{ name: testFile }] });
+ it('should be possible to rename a modified file', () => {
+ return common.post(req, '/testing/git', { repo: testDir, command: ['mv', testFile, testFile2] });
+ });
+
+ it('diff on renamed and modified file should work', () => {
+ return common.get(req, '/diff', { path: testDir, file: testFile2, oldFile: testFile}).then((res) => {
+ expect(res.indexOf('diff --git a/afile.txt b/anotherfile.txt')).to.be.above(-1);
+ expect(res.indexOf('+more')).to.be.above(-1);
+ });
+ });
+
+ it('should be possible to commit the renamed and modified file', () => {
+ return common.post(req, '/commit', { path: testDir, message: "Move and Change", files: [{ name: testFile2 }] });
+ });
+
+ it('diff on commit with renamed and modified file should work', () => {
+ return common.get(req, '/gitlog', { path: testDir })
+ .then((res) => {
+ expect(res.nodes.length).to.be(3);
+ return common.get(req, '/diff', { path: testDir, file: testFile2, oldFile:testFile, sha1: res.nodes[0].sha1 });
+ }).then((res) => {
+ for (let i = 0; i < content.length; i++) {
+ expect(res.indexOf(content[i])).to.be.above(-1);
+ }
+ });
});
it('removing a test file should work', () => {
- return common.post(req, '/testing/removefile', { file: path.join(testDir, testFile) });
+ return common.post(req, '/testing/removefile', { file: path.join(testDir, testFile2) });
});
it('should be possible to commit an image file', () => {
@@ -139,7 +163,7 @@ describe('git-api diff', () => {
});
it('diff on removed file should work', () => {
- return common.get(req, '/diff', { path: testDir, file: testFile })
+ return common.get(req, '/diff', { path: testDir, file: testFile2 })
.then((res) => {
expect(res.indexOf('deleted file')).to.be.above(-1);
expect(res.indexOf("@@ -1,6 +0,0 @@")).to.be.above(-1);
@@ -158,7 +182,7 @@ describe('git-api diff', () => {
.then(() => common.get(req, '/gitlog', { path: testDir }))
.then((res) => {
// find a commit which contains the testFile
- const commit = res.nodes.filter((commit) => commit.fileLineDiffs.some((lineDiff) => lineDiff[2] == testFile))[0];
+ const commit = res.nodes.filter((commit) => commit.fileLineDiffs.some((lineDiff) => lineDiff.fileName == testFile))[0];
return common.get(req, '/diff', { path: testDir, sha1: commit.sha1, file: testFile })
});
});
diff --git a/test/spec.git-api.js b/test/spec.git-api.js
index c801f6296..94cfd96b6 100644
--- a/test/spec.git-api.js
+++ b/test/spec.git-api.js
@@ -125,6 +125,8 @@ describe('git-api', () => {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile]).to.eql({
displayName: testFile,
+ fileName: testFile,
+ oldFileName: testFile,
isNew: true,
staged: false,
removed: false,
@@ -184,6 +186,8 @@ describe('git-api', () => {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile]).to.eql({
displayName: testFile,
+ fileName: testFile,
+ oldFileName: testFile,
isNew: false,
staged: false,
removed: false,
@@ -224,6 +228,8 @@ describe('git-api', () => {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile2]).to.eql({
displayName: testFile2,
+ fileName: testFile2,
+ oldFileName: testFile2,
isNew: true,
staged: false,
removed: false,
@@ -267,6 +273,8 @@ describe('git-api', () => {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile3]).to.eql({
displayName: testFile3,
+ fileName: testFile3,
+ oldFileName: testFile3,
isNew: true,
staged: false,
removed: false,
@@ -314,6 +322,8 @@ describe('git-api', () => {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile]).to.eql({
displayName: testFile,
+ fileName: testFile,
+ oldFileName: testFile,
isNew: false,
staged: false,
removed: true,
@@ -348,14 +358,16 @@ describe('git-api', () => {
return common.get(req, '/status', { path: testDir }).then((res) => {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[testFile4]).to.eql({
- displayName: `${testFile3} -> ${testFile4}`,
+ displayName: `${testFile3} → ${testFile4}`,
+ fileName: testFile4,
+ oldFileName: testFile3,
isNew: false,
staged: false,
removed: false,
conflict: false,
renamed: true,
type: 'text',
- additions: '2',
+ additions: '0',
deletions: '0'
});
});
diff --git a/test/spec.git-api.submodule.js b/test/spec.git-api.submodule.js
index 9c6d1caf8..507844970 100644
--- a/test/spec.git-api.submodule.js
+++ b/test/spec.git-api.submodule.js
@@ -83,6 +83,8 @@ describe('git-api submodule', function () {
expect(Object.keys(res.files).length).to.be(1);
expect(res.files[submodulePath]).to.eql({
displayName: submodulePath,
+ fileName: submodulePath,
+ oldFileName: submodulePath,
isNew: false,
staged: false,
removed: false,
diff --git a/test/spec.git-parser.js b/test/spec.git-parser.js
index 4741c7e2c..aea63424f 100644
--- a/test/spec.git-parser.js
+++ b/test/spec.git-parser.js
@@ -184,7 +184,7 @@ describe('git-parser parseGitLog', () => {
})
});
it('parses multiple commits in a row', () => {
- const gitLog = dedent`
+ const gitLog = dedent(`
commit 5867e2766b0a0f81ad59ce9e9895d9b1a3523aa4 37d1154434b70854ed243967e0d7e37aa3564551 (HEAD -> refs/heads/git-parser-specs)
Author: Test ungit
AuthorDate: Fri Jan 4 14:54:06 2019 +0100
@@ -193,10 +193,7 @@ describe('git-parser parseGitLog', () => {
parseGitLog + gix reflox parsing
- 1 1 source/git-parser.js
- 175 0 test/spec.git-parser.js
-
- commit 37d1154434b70854ed243967e0d7e37aa3564551 d58c8e117fc257520d90b099fd2c6acd7c1e8861
+ 1 1 source/git-parser.js\x00175 0 test/spec.git-parser.js\x00\x00commit 37d1154434b70854ed243967e0d7e37aa3564551 d58c8e117fc257520d90b099fd2c6acd7c1e8861
Author: Test ungit
AuthorDate: Fri Jan 4 14:03:56 2019 +0100
Commit: Test ungit
@@ -204,8 +201,7 @@ describe('git-parser parseGitLog', () => {
submodules parser
- 32 0 test/spec.git-parser.js\n
- `
+ 32 0 test/spec.git-parser.js\x00\x00`)
const res = gitParser.parseGitLog(gitLog)
expect(res[0]).to.eql({
@@ -215,10 +211,27 @@ describe('git-parser parseGitLog', () => {
commitDate: "Fri Jan 4 14:54:06 2019 +0100",
committerEmail: "test@example.com",
committerName: "Test ungit",
+ total: {
+ "additions": 176,
+ "deletions": 1
+ },
fileLineDiffs: [
- [176, 1, "Total"],
- [1, 1, "source/git-parser.js", "text"],
- [175, 0, "test/spec.git-parser.js", "text"],
+ {
+ "additions": 1,
+ "deletions": 1,
+ "displayName": "source/git-parser.js",
+ "fileName": "source/git-parser.js",
+ "oldFileName": "source/git-parser.js",
+ "type": "text"
+ },
+ {
+ "additions": 175,
+ "deletions": 0,
+ "displayName": "test/spec.git-parser.js",
+ "fileName": "test/spec.git-parser.js",
+ "oldFileName": "test/spec.git-parser.js",
+ "type": "text"
+ }
],
isHead: true,
message: "parseGitLog + gix reflox parsing",
@@ -238,9 +251,19 @@ describe('git-parser parseGitLog', () => {
commitDate: "Fri Jan 4 14:03:56 2019 +0100",
committerEmail: "test@example.com",
committerName: "Test ungit",
+ total: {
+ "additions": 32,
+ "deletions": 0
+ },
fileLineDiffs: [
- [32, 0, "Total"],
- [32, 0, "test/spec.git-parser.js", "text"],
+ {
+ "additions": 32,
+ "deletions": 0,
+ "displayName": "test/spec.git-parser.js",
+ "fileName": "test/spec.git-parser.js",
+ "oldFileName": "test/spec.git-parser.js",
+ "type": "text"
+ }
],
isHead: false,
message: "submodules parser",
@@ -252,7 +275,7 @@ describe('git-parser parseGitLog', () => {
})
});
it('parses reflog commits without email', () => {
- const gitLog = dedent`
+ const gitLog = dedent(`
commit 37d11544 d58c8e11 (HEAD -> refs/heads/git-parser-specs)
Reflog: git-parser-specs@{Fri Jan 4 14:03:56 2019 +0100} (Test ungit)
Reflog message: commit: submodules parser
@@ -263,8 +286,7 @@ describe('git-parser parseGitLog', () => {
submodules parser
- 32 0 test/spec.git-parser.js\n
- `
+ 32 0 test/spec.git-parser.js\x00\x00`)
expect(gitParser.parseGitLog(gitLog)[0]).to.eql({
authorDate: "Fri Jan 4 14:03:56 2019 +0100",
@@ -273,9 +295,19 @@ describe('git-parser parseGitLog', () => {
commitDate: "Fri Jan 4 14:03:56 2019 +0100",
committerEmail: "test@example.com",
committerName: "Test ungit",
+ total: {
+ "additions": 32,
+ "deletions": 0
+ },
fileLineDiffs: [
- [32, 0, "Total"],
- [32, 0, "test/spec.git-parser.js", "text"]
+ {
+ "additions": 32,
+ "deletions": 0,
+ "displayName": "test/spec.git-parser.js",
+ "fileName": "test/spec.git-parser.js",
+ "oldFileName": "test/spec.git-parser.js",
+ "type": "text"
+ }
],
isHead: true,
message: "submodules parser",
@@ -293,7 +325,7 @@ describe('git-parser parseGitLog', () => {
})
});
it('parses reflog commits', () => {
- const gitLog = dedent`
+ const gitLog = dedent(`
commit 37d11544 d58c8e11 (HEAD -> refs/heads/git-parser-specs)
Reflog: git-parser-specs@{Fri Jan 4 14:03:56 2019 +0100} (Test ungit )
Reflog message: commit: submodules parser
@@ -304,8 +336,7 @@ describe('git-parser parseGitLog', () => {
submodules parser
- 32 0 test/spec.git-parser.js\n
- `
+ 32 0 test/spec.git-parser.js\x00\x00`)
expect(gitParser.parseGitLog(gitLog)[0]).to.eql({
authorDate: "Fri Jan 4 14:03:56 2019 +0100",
@@ -314,9 +345,19 @@ describe('git-parser parseGitLog', () => {
commitDate: "Fri Jan 4 14:03:56 2019 +0100",
committerEmail: "test@example.com",
committerName: "Test ungit",
+ total: {
+ "additions": 32,
+ "deletions": 0
+ },
fileLineDiffs: [
- [32, 0, "Total"],
- [32, 0, "test/spec.git-parser.js", "text"]
+ {
+ "additions": 32,
+ "deletions": 0,
+ "displayName": "test/spec.git-parser.js",
+ "fileName": "test/spec.git-parser.js",
+ "oldFileName": "test/spec.git-parser.js",
+ "type": "text"
+ }
],
isHead: true,
message: "submodules parser",
@@ -389,7 +430,7 @@ describe('git-parser parseGitLog', () => {
});
});
it('parses the git log', () => {
- const gitLog = dedent`
+ const gitLog = dedent(`
commit 37d1154434b70854ed243967e0d7e37aa3564551 d58c8e117fc257520d90b099fd2c6acd7c1e8861 (HEAD -> refs/heads/git-parser-specs)
Author: Test ungit
AuthorDate: Fri Jan 4 14:03:56 2019 +0100
@@ -398,15 +439,24 @@ describe('git-parser parseGitLog', () => {
submodules parser
- 32 0 test/spec.git-parser.js\n
- `
+ 32 0 test/spec.git-parser.js\x00\x00`)
expect(gitParser.parseGitLog(gitLog)[0]).to.eql(
{
refs: [ 'HEAD', 'refs/heads/git-parser-specs' ],
+ total: {
+ "additions": 32,
+ "deletions": 0
+ },
fileLineDiffs: [
- [32, 0, "Total"],
- [32, 0, "test/spec.git-parser.js", "text"]
+ {
+ "additions": 32,
+ "deletions": 0,
+ "displayName": "test/spec.git-parser.js",
+ "fileName": "test/spec.git-parser.js",
+ "oldFileName": "test/spec.git-parser.js",
+ "type": "text"
+ }
],
sha1: '37d1154434b70854ed243967e0d7e37aa3564551',
parents: [ 'd58c8e117fc257520d90b099fd2c6acd7c1e8861' ],
@@ -628,11 +678,7 @@ describe('parseGitLsRemote', () => {
describe('parseGitStatusNumstat', () => {
it('parses the git status numstat', () => {
- const gitStatusNumstat = dedent`
- 1459 202 package-lock.json
- 2 1 package.json
- 13 0 test/spec.git-parser.js
- `
+ const gitStatusNumstat = `1459 202 package-lock.json\x002 1 package.json\x0013 0 test/spec.git-parser.js\x00`
expect(gitParser.parseGitStatusNumstat(gitStatusNumstat)).to.eql({
"package-lock.json": { additions: "1459", deletions: "202" },
@@ -641,13 +687,12 @@ describe('parseGitStatusNumstat', () => {
});
})
it('skips empty lines', () => {
- const gitStatusNumstat = dedent`
- 1459 202 package-lock.json
+ const gitStatusNumstat = dedent(`
+ 1459 202 package-lock.json\x00
- 2 1 package.json
- 13 0 test/spec.git-parser.js
- `
+ 2 1 package.json\x0013 0 test/spec.git-parser.js\x00
+ `)
expect(gitParser.parseGitStatusNumstat(gitStatusNumstat)).to.eql({
"package-lock.json": { additions: "1459", deletions: "202" },
@@ -659,69 +704,66 @@ describe('parseGitStatusNumstat', () => {
describe('parseGitStatus', () => {
it('parses git status', () => {
- const gitStatus = dedent`
- ## git-parser-specs
- A file1.js
- M file2.js
- D file3.js
- D file4.js
- U file5.js
- U file6.js
- AA file7.js
- ? file8.js
- A file9.js
- ?D file10.js
- AD file11.js
- M file12.js
- ?? file13.js
-
- R ../source/sysinfo.js -> ../source/sys.js
- `
+ const gitStatus = `## git-parser-specs\x00` +
+ `A file1.js\x00` +
+ `M file2.js\x00` +
+ `D file3.js\x00` +
+ ` D file4.js\x00` +
+ ` U file5.js\x00` +
+ `U file6.js\x00` +
+ `AA file7.js\x00` +
+ `? file8.js\x00` +
+ `A file9.js\x00` +
+ `?D file10.js\x00` +
+ `AD file11.js\x00` +
+ ` M file12.js\x00` +
+ `?? file13.js\x00` +
+ `R ../source/sys.js\x00../source/sysinfo.js\x00`
expect(gitParser.parseGitStatus(gitStatus)).to.eql({
branch: "git-parser-specs",
files: {
"../source/sys.js": {
- conflict: false, displayName: "../source/sysinfo.js -> ../source/sys.js", isNew: false, removed: false, renamed: true, staged: false, type: "text"
+ conflict: false, displayName: "../source/sysinfo.js → ../source/sys.js", fileName: "../source/sys.js", oldFileName: "../source/sysinfo.js", isNew: false, removed: false, renamed: true, staged: false, type: "text"
},
"file1.js": {
- conflict: false, displayName: "file1.js", isNew: true, removed: false, renamed: false, staged: true, type: "text"
+ conflict: false, displayName: "file1.js", fileName: "file1.js", oldFileName: "file1.js", isNew: true, removed: false, renamed: false, staged: true, type: "text"
},
"file2.js": {
- conflict: false, displayName: "file2.js", isNew: false, removed: false, renamed: false, staged: true, type: "text"
+ conflict: false, displayName: "file2.js", fileName: "file2.js", oldFileName: "file2.js", isNew: false, removed: false, renamed: false, staged: true, type: "text"
},
"file3.js": {
- conflict: false, displayName: "file3.js", isNew: false, removed: true, renamed: false, staged: false, type: "text"
+ conflict: false, displayName: "file3.js", fileName: "file3.js", oldFileName: "file3.js", isNew: false, removed: true, renamed: false, staged: false, type: "text"
},
"file4.js": {
- conflict: false, displayName: "file4.js", isNew: false, removed: true, renamed: false, staged: false, type: "text"
+ conflict: false, displayName: "file4.js", fileName: "file4.js", oldFileName: "file4.js", isNew: false, removed: true, renamed: false, staged: false, type: "text"
},
"file5.js": {
- conflict: true, displayName: "file5.js", isNew: false, removed: false, renamed: false, staged: false, type: "text"
+ conflict: true, displayName: "file5.js", fileName: "file5.js", oldFileName: "file5.js", isNew: false, removed: false, renamed: false, staged: false, type: "text"
},
"file6.js": {
- conflict: true, displayName: "file6.js", isNew: false, removed: false, renamed: false, staged: false, type: "text"
+ conflict: true, displayName: "file6.js", fileName: "file6.js", oldFileName: "file6.js", isNew: false, removed: false, renamed: false, staged: false, type: "text"
},
"file7.js": {
- conflict: true, displayName: "file7.js", isNew: true, removed: false, renamed: false, staged: true, type: "text"
+ conflict: true, displayName: "file7.js", fileName: "file7.js", oldFileName: "file7.js", isNew: true, removed: false, renamed: false, staged: true, type: "text"
},
"file8.js": {
- conflict: false, displayName: "file8.js", isNew: true, removed: false, renamed: false, staged: false, type: "text"
+ conflict: false, displayName: "file8.js", fileName: "file8.js", oldFileName: "file8.js", isNew: true, removed: false, renamed: false, staged: false, type: "text"
},
"file9.js": {
- conflict: false, displayName: "file9.js", isNew: true, removed: false, renamed: false, staged: true, type: "text"
+ conflict: false, displayName: "file9.js", fileName: "file9.js", oldFileName: "file9.js", isNew: true, removed: false, renamed: false, staged: true, type: "text"
},
"file10.js": {
- conflict: false, displayName: "file10.js", isNew: false, removed: true, renamed: false, staged: false, type: "text"
+ conflict: false, displayName: "file10.js", fileName: "file10.js", oldFileName: "file10.js", isNew: false, removed: true, renamed: false, staged: false, type: "text"
},
"file11.js": {
- conflict: false, displayName: "file11.js", isNew: false, removed: true, renamed: false, staged: true, type: "text"
+ conflict: false, displayName: "file11.js", fileName: "file11.js", oldFileName: "file11.js", isNew: false, removed: true, renamed: false, staged: true, type: "text"
},
"file12.js": {
- conflict: false, displayName: "file12.js", isNew: false, removed: false, renamed: false, staged: false, type: "text"
+ conflict: false, displayName: "file12.js", fileName: "file12.js", oldFileName: "file12.js", isNew: false, removed: false, renamed: false, staged: false, type: "text"
},
"file13.js": {
- conflict: false, displayName: "file13.js", isNew: true, removed: false, renamed: false, staged: false, type: "text"
+ conflict: false, displayName: "file13.js", fileName: "file13.js", oldFileName: "file13.js", isNew: true, removed: false, renamed: false, staged: false, type: "text"
}
},
inited: true,