From 10057de9b3b8eaa5cd719fb66024ce71a9873108 Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Tue, 21 Nov 2017 23:31:41 +0000 Subject: [PATCH] A big projects update Includes: - change local/remote branches - basic support for username/password handling --- editor/js/main.js | 4 +- editor/js/ui/common/stack.js | 8 + editor/js/ui/diff.js | 238 +++++- editor/js/ui/notifications.js | 2 +- editor/js/ui/projectSettings.js | 247 +++++- editor/js/ui/projects.js | 333 ++++++-- editor/js/ui/tab-versionControl.js | 771 +++++++++++++++--- editor/sass/diff.scss | 25 +- editor/sass/editor.scss | 4 + editor/sass/mixins.scss | 3 + editor/sass/projects.scss | 209 ++++- editor/sass/userSettings.scss | 2 + red/api/editor/projects/index.js | 169 +++- .../localfilesystem/projects/Project.js | 150 +++- .../projects/git/authServer.js | 86 ++ .../projects/git/authWriter.js | 24 + .../localfilesystem/projects/git/index.js | 483 ++++++++--- .../projects/git/node-red-ask-pass.sh | 1 + .../storage/localfilesystem/projects/index.js | 44 +- 19 files changed, 2412 insertions(+), 391 deletions(-) create mode 100644 red/runtime/storage/localfilesystem/projects/git/authServer.js create mode 100644 red/runtime/storage/localfilesystem/projects/git/authWriter.js create mode 100755 red/runtime/storage/localfilesystem/projects/git/node-red-ask-pass.sh diff --git a/editor/js/main.js b/editor/js/main.js index 4c061083a..a9afd5bc1 100644 --- a/editor/js/main.js +++ b/editor/js/main.js @@ -43,7 +43,9 @@ $(".palette-scroll").removeClass("hide"); $("#palette-search").removeClass("hide"); loadFlows(function() { - RED.projects.refresh(); + RED.projects.refresh(function() { + RED.sidebar.info.refresh() + }); var persistentNotifications = {}; RED.comms.subscribe("notification/#",function(topic,msg) { diff --git a/editor/js/ui/common/stack.js b/editor/js/ui/common/stack.js index 5b28eccd9..eca30bd75 100644 --- a/editor/js/ui/common/stack.js +++ b/editor/js/ui/common/stack.js @@ -101,6 +101,14 @@ RED.stack = (function() { if (entry.onexpand) { entry.onexpand.call(entry); } + if (options.singleExpanded) { + entries.forEach(function(e) { + if (e !== entry) { + e.collapse(); + } + }) + } + icon.addClass("expanded"); entry.container.addClass("palette-category-expanded"); entry.contentWrap.slideDown(200); diff --git a/editor/js/ui/diff.js b/editor/js/ui/diff.js index 171db8637..8e9fdbbd3 100644 --- a/editor/js/ui/diff.js +++ b/editor/js/ui/diff.js @@ -1581,10 +1581,10 @@ RED.diff = (function() { if (Adiff.type === 2) { cellNo.addClass('blank'); cellLine.addClass('blank'); - } else if (Adiff.type === 1) { + } else if (Adiff.type === 4) { cellNo.addClass('added'); cellLine.addClass('added'); - } else if (Adiff.type === 4) { + } else if (Adiff.type === 1) { cellNo.addClass('removed'); cellLine.addClass('removed'); } @@ -1593,10 +1593,10 @@ RED.diff = (function() { if (Bdiff.type === 2) { cellNo.addClass('blank'); cellLine.addClass('blank'); - } else if (Bdiff.type === 1) { + } else if (Bdiff.type === 4) { cellNo.addClass('added'); cellLine.addClass('added'); - } else if (Bdiff.type === 4) { + } else if (Bdiff.type === 1) { cellNo.addClass('removed'); cellLine.addClass('removed'); } @@ -1694,9 +1694,14 @@ RED.diff = (function() { var isCollapsed = diffFileRow.hasClass("collapsed"); diffFileRow.nextUntil(".node-text-diff-file-header").toggle(!isCollapsed); }) - var label = $('').text(file.file).appendTo(content); + var label = $('').text(file.file).appendTo(content); - if (commitOptions.project.files && commitOptions.project.files.flow === file.file) { + var conflictHeader; + var unresolvedConflicts = 0; + var resolvedConflicts = 0; + var conflictResolutions = {}; + + if (!commitOptions.unmerged && commitOptions.project.files && commitOptions.project.files.flow === file.file) { var tools = $('').appendTo(content); $('').appendTo(tools).click(function(e) { e.preventDefault(); @@ -1740,47 +1745,143 @@ RED.diff = (function() { } - for (var i=0;i').appendTo(codeBody); var content = $('').appendTo(diffRow); - var label = $('').text(hunks[i].header).appendTo(content); + var label = $('').text(hunk.header).appendTo(content); + var isConflict = hunk.conflict; + var localLine = hunk.localStartLine; + var remoteLine = hunk.remoteStartLine; + if (isConflict) { + unresolvedConflicts++; + } - var localLine = hunks[i].localStartLine; - var remoteLine = hunks[i].remoteStartLine; - - - for (var j=0;j').appendTo(codeBody); + + var actualLineNumber = hunk.diffStart + lineNumber; + var isMergeHeader = isConflict && /^..(<<<<<<<|=======$|>>>>>>>)/.test(lineText); + var diffRow = $('').appendTo(codeBody); var localLineNo = $('').appendTo(diffRow); - var remoteLineNo = $('').appendTo(diffRow); - var line = $('').appendTo(diffRow); - $('').text(lineText[0]).appendTo(line); - $('').text(lineText.substring(1)).appendTo(line); - if (lineText[0] === '+') { - localLineNo.addClass("added"); - remoteLineNo.addClass("added"); - line.addClass("added"); - remoteLineNo.text(remoteLine++); - } else if (lineText[0] === '-') { - localLineNo.addClass("removed"); - remoteLineNo.addClass("removed"); - line.addClass("removed"); - localLineNo.text(localLine++); + var remoteLineNo; + if (!isMergeHeader) { + remoteLineNo = $('').appendTo(diffRow); } else { - line.addClass("unchanged"); - if (localLine > 0 && lineText[0] !== '\\' && lineText !== "") { - localLineNo.text(localLine++); + localLineNo.attr('colspan',2); + } + var line = $('').appendTo(diffRow); + var prefixStart = 0; + var prefixEnd = 1; + if (isConflict) { + prefixEnd = 2; + } + if (!isMergeHeader) { + var changeMarker = lineText[0]; + if (isConflict && !commitOptions.unmerged && changeMarker === ' ') { + changeMarker = lineText[1]; } - if (remoteLine > 0 && lineText[0] !== '\\' && lineText !== "") { - remoteLineNo.text(remoteLine++); + $('').text(changeMarker).appendTo(line); + var handledlLine = false; + if (isConflict && commitOptions.unmerged) { + $('').text(lineText[1]).appendTo(line); + if (lineText[0] === '+') { + localLineNo.text(localLine++); + handledlLine = true; + } + if (lineText[1] === '+') { + remoteLineNo.text(remoteLine++); + handledlLine = true; + } + } else { + if (lineText[0] === '+' || (isConflict && lineText[1] === '+')) { + localLineNo.addClass("added"); + remoteLineNo.addClass("added"); + line.addClass("added"); + remoteLineNo.text(remoteLine++); + handledlLine = true; + } else if (lineText[0] === '-' || (isConflict && lineText[1] === '-')) { + localLineNo.addClass("removed"); + remoteLineNo.addClass("removed"); + line.addClass("removed"); + localLineNo.text(localLine++); + handledlLine = true; + } + } + if (!handledlLine) { + line.addClass("unchanged"); + if (localLine > 0 && lineText[0] !== '\\' && lineText !== "") { + localLineNo.text(localLine++); + } + if (remoteLine > 0 && lineText[0] !== '\\' && lineText !== "") { + remoteLineNo.text(remoteLine++); + } + } + $('').text(lineText.substring(prefixEnd)).appendTo(line); + } else { + diffRow.addClass("mergeHeader"); + var isSeparator = /^..(=======$)/.test(lineText); + if (!isSeparator) { + var isOurs = /^..<<<<<<').text("<<<<<<< Local Changes").appendTo(line); + hunk.localChangeStart = actualLineNumber; + } else { + hunk.remoteChangeEnd = actualLineNumber; + $('').text(">>>>>>> Remote Changes").appendTo(line); + + } + diffRow.addClass("mergeHeader-"+(isOurs?"ours":"theirs")); + $('') + .appendTo(line) + .click(function(evt) { + evt.preventDefault(); + resolvedConflicts++; + var addedRows; + var midRow; + if (isOurs) { + addedRows = diffRow.nextUntil(".mergeHeader-separator"); + midRow = addedRows.last().next(); + midRow.nextUntil(".mergeHeader").remove(); + midRow.next().remove(); + } else { + addedRows = diffRow.prevUntil(".mergeHeader-separator"); + midRow = addedRows.last().prev(); + midRow.prevUntil(".mergeHeader").remove(); + midRow.prev().remove(); + } + midRow.remove(); + diffRow.remove(); + addedRows.find(".linetext").addClass('added'); + conflictHeader.empty(); + $(''+resolvedConflicts+' of '+unresolvedConflicts+' conflicts resolved').appendTo(conflictHeader); + + conflictResolutions[file.file] = conflictResolutions[file.file] || {}; + conflictResolutions[file.file][hunk.localChangeStart] = { + changeStart: hunk.localChangeStart, + separator: hunk.changeSeparator, + changeEnd: hunk.remoteChangeEnd, + selection: isOurs?"A":"B" + } + if (commitOptions.resolveConflict) { + commitOptions.resolveConflict({ + conflicts: unresolvedConflicts, + resolved: resolvedConflicts, + resolutions: conflictResolutions + }); + } + }) + } else { + hunk.changeSeparator = actualLineNumber; + diffRow.addClass("mergeHeader-separator"); } } - } + }); + }); + if (commitOptions.unmerged) { + conflictHeader = $(''+resolvedConflicts+' of '+unresolvedConflicts+' conflicts resolved').appendTo(content); } }); return diffPanel; @@ -1820,8 +1921,9 @@ RED.diff = (function() { $('
').text("Commit "+commit.sha).appendTo(summary); $('
').text((commit.authorName||commit.author)+" - "+options.date).appendTo(summary); - - createUnifiedDiffTable(commit.files,options).appendTo(diffPanel); + if (commit.files) { + createUnifiedDiffTable(commit.files,options).appendTo(diffPanel); + } }, @@ -1837,16 +1939,30 @@ RED.diff = (function() { function showUnifiedDiff(options) { var diff = options.diff; var title = options.title; - var files = parseUnifiedDiff(diff); + + var currentResolution; + if (options.unmerged) { + options.resolveConflict = function(results) { + currentResolution = results; + if (results.conflicts === results.resolved) { + $("#node-diff-view-resolve-diff").removeClass('disabled'); + } + } + } + + var trayOptions = { title: title||"Compare Changes", //TODO: nls width: Infinity, overlay: true, buttons: [ { - text: RED._("common.label.close"), + text: RED._((options.unmerged)?"common.label.cancel":"common.label.close"), click: function() { + if (options.oncancel) { + options.oncancel(); + } RED.tray.close(); } } @@ -1858,8 +1974,6 @@ RED.diff = (function() { var trayBody = tray.find('.editor-tray-body'); var diffPanel = $('
').appendTo(trayBody); createUnifiedDiffTable(files,options).appendTo(diffPanel); - - }, close: function() { diffVisible = false; @@ -1868,6 +1982,23 @@ RED.diff = (function() { } } + if (options.unmerged) { + trayOptions.buttons.push( + { + id: "node-diff-view-resolve-diff", + text: "Save conflict resolution", + class: "primary disabled", + click: function() { + if (!$("#node-diff-view-resolve-diff").hasClass('disabled')) { + if (options.onresolve) { + options.onresolve(currentResolution); + } + RED.tray.close(); + } + } + } + ); + } RED.tray.show(trayOptions); } @@ -1912,6 +2043,7 @@ RED.diff = (function() { } var fileHeader = /^\+\+\+ b\/(.*)\t?/; var hunkHeader = /^@@ -((\d+)(,(\d+))?) \+((\d+)(,(\d+))?) @@ ?(.*)$/; + var conflictHunkHeader = /^@+ -((\d+)(,(\d+))?) -((\d+)(,(\d+))?) \+((\d+)(,(\d+))?) @+/; var files = []; var currentFile; var hunks = []; @@ -1944,9 +2076,29 @@ RED.diff = (function() { localLength: hunkLine[4]||1, remoteStartLine: hunkLine[6], remoteLength: hunkLine[8]||1, - lines: [] + lines: [], + conflict: false } - } else if (currentHunk) { + continue; + } + hunkLine = conflictHunkHeader.exec(line); + if (hunkLine) { + if (currentHunk) { + currentFile.hunks.push(currentHunk); + } + currentHunk = { + header: line, + localStartLine: hunkLine[2], + localLength: hunkLine[4]||1, + remoteStartLine: hunkLine[6], + remoteLength: hunkLine[8]||1, + diffStart: parseInt(hunkLine[10]), + lines: [], + conflict: true + } + continue; + } + if (currentHunk) { currentHunk.lines.push(line); } } diff --git a/editor/js/ui/notifications.js b/editor/js/ui/notifications.js index 7c17741ea..5c4dfd26c 100644 --- a/editor/js/ui/notifications.js +++ b/editor/js/ui/notifications.js @@ -78,7 +78,7 @@ RED.notify = (function() { window.clearTimeout(nn.timeoutid); }; })()); - n.timeoutid = window.setTimeout(n.close,timeout||3000); + n.timeoutid = window.setTimeout(n.close,timeout||5000); } currentNotifications.push(n); c+=1; diff --git a/editor/js/ui/projectSettings.js b/editor/js/ui/projectSettings.js index 55c6486e6..ea12a112e 100644 --- a/editor/js/ui/projectSettings.js +++ b/editor/js/ui/projectSettings.js @@ -110,17 +110,13 @@ RED.projects.settings = (function() { RED.tray.show(trayOptions); } - function addSpinnerOverlay(container) { - var spinner = $('
').appendTo(container); - return spinner; - } function editDescription(activeProject, container) { RED.editor.editMarkdown({ title: RED._('sidebar.project.editDescription'), value: activeProject.description, complete: function(v) { container.empty(); - var spinner = addSpinnerOverlay(container); + var spinner = utils.addSpinnerOverlay(container); var done = function(err,res) { if (err) { return editDescription(activeProject, container); @@ -176,7 +172,7 @@ RED.projects.settings = (function() { evt.preventDefault(); var v = input.val(); updateProjectSummary(v, container); - var spinner = addSpinnerOverlay(container); + var spinner = utils.addSpinnerOverlay(container); var done = function(err,res) { if (err) { spinner.remove(); @@ -307,7 +303,7 @@ RED.projects.settings = (function() { complete: function(v) { try { var parsed = JSON.parse(v); - var spinner = addSpinnerOverlay(container); + var spinner = utils.addSpinnerOverlay(container); var done = function(err,res) { if (err) { @@ -427,9 +423,8 @@ RED.projects.settings = (function() { var dialogBody; var filesList; var selected; - var container = $('
',{style:"position: relative; min-height: 175px; height: 175px;"}).appendTo(row); - var spinner = addSpinnerOverlay(container); - + var container = $('
',{style:"position: relative; min-height: 175px; height: 175px;"}).hide().appendTo(row); + var spinner = utils.addSpinnerOverlay(container); $.getJSON("/projects/"+activeProject.name+"/files",function(result) { var fileNames = Object.keys(result); var files = {}; @@ -467,7 +462,9 @@ RED.projects.settings = (function() { createFileSubList(container,files.children,current,done,"height: 175px"); spinner.remove(); }); + return container; } + function createFileSubList(container, files, current, onselect, style) { style = style || ""; var list = $('
    ',{class:"projects-dialog-file-list", style:style}).appendTo(container).editableList({ @@ -575,7 +572,7 @@ RED.projects.settings = (function() { // evt.preventDefault(); // var newFlowFile = flowFileInput.val(); // var newCredsFile = credentialsFileInput.val(); - // var spinner = addSpinnerOverlay(container); + // var spinner = utils.addSpinnerOverlay(container); // var done = function(err,res) { // if (err) { // spinner.remove(); @@ -617,12 +614,12 @@ RED.projects.settings = (function() { function createFilesSection(activeProject,pane) { var title = $('

    ').text("Files").appendTo(pane); var filesContainer = $('').appendTo(pane); - var editButton = $('') + var editFilesButton = $('') .appendTo(title) .click(function(evt) { evt.preventDefault(); formButtons.show(); - editButton.hide(); + editFilesButton.hide(); flowFileLabelText.hide(); flowFileInput.show(); flowFileInputSearch.show(); @@ -646,21 +643,21 @@ RED.projects.settings = (function() { var flowFileLabelText = $('').text(activeProject.files.flow).appendTo(flowFileLabel); var flowFileInput = $('').val(activeProject.files.flow).hide().appendTo(flowFileLabel); - var flowFileInputSearch = $('') + var flowFileInputSearch = $('') .hide() .appendTo(flowFileLabel) .click(function(e) { if ($(this).hasClass('selected')) { $(this).removeClass('selected'); - flowFileLabel.find('.project-file-listing-container').remove(); - flowFileLabel.css('height',''); + flowFileLabel.find('.project-file-listing-container').slideUp(200,function() { + $(this).remove(); + flowFileLabel.css('height',''); + }); flowFileLabel.css('color',''); - } else { $(this).addClass('selected'); - flowFileLabel.css('height','auto'); flowFileLabel.css('color','inherit'); - showProjectFileListing(flowFileLabel,activeProject,flowFileInput.val(),function(result,isDblClick) { + var fileList = showProjectFileListing(flowFileLabel,activeProject,flowFileInput.val(),function(result,isDblClick) { if (result) { flowFileInput.val(result); } @@ -668,7 +665,12 @@ RED.projects.settings = (function() { $(flowFileInputSearch).click(); } checkFiles(); - }) + }); + flowFileLabel.css('height','auto'); + setTimeout(function() { + fileList.slideDown(200); + },50); + } }) @@ -748,7 +750,7 @@ RED.projects.settings = (function() { } checkFiles(); }); - var credentialSecretEditButton = $('') + var credentialSecretEditButton = $('') .appendTo(credentialSecretButtons) .click(function(e) { e.preventDefault(); @@ -810,7 +812,7 @@ RED.projects.settings = (function() { var hideEditForm = function() { - editButton.show(); + editFilesButton.show(); formButtons.hide(); flowFileLabelText.show(); flowFileInput.hide(); @@ -821,6 +823,11 @@ RED.projects.settings = (function() { credentialStateLabel.removeClass("uneditable-input"); credentialStateLabel.css('height',''); + flowFileInputSearch.removeClass('selected'); + flowFileLabel.find('.project-file-listing-container').remove(); + flowFileLabel.css('height',''); + flowFileLabel.css('color',''); + $(".user-settings-row-credentials").hide(); credentialFormRows.hide(); credentialSecretButtons.hide(); @@ -831,7 +838,7 @@ RED.projects.settings = (function() { } var formButtons = $('').hide().appendTo(filesContainer); - var cancelButton = $('') + $('') .appendTo(formButtons) .click(function(evt) { evt.preventDefault(); @@ -841,7 +848,7 @@ RED.projects.settings = (function() { .appendTo(formButtons) .click(function(evt) { evt.preventDefault(); - var spinner = addSpinnerOverlay(filesContainer); + var spinner = utils.addSpinnerOverlay(filesContainer); var done = function(err) { spinner.remove(); if (err) { @@ -927,9 +934,201 @@ RED.projects.settings = (function() { checkFiles(); updateForm(); } + + function createLocalRepositorySection(activeProject,pane) { + var title = $('

    ').text("Local Repository").appendTo(pane); + var repoContainer = $('').appendTo(pane); + var editRepoButton = $('') + .appendTo(title) + .click(function(evt) { + editRepoButton.hide(); + localRepoSearch.show(); + formButtons.show(); + }); + + var row = $('').appendTo(repoContainer); + $('').text('Branch').appendTo(row); + var localRepoLabel = $('
    ').appendTo(row); + + var hideLocalRepoBranchList = function() { + localRepoSearch.removeClass('selected'); + localRepoLabel.css('height',''); + localRepoBranchListRow.slideUp(100); + } + var localRepoText = $('').text(activeProject.branches.local).appendTo(localRepoLabel); + var localRepoSearch = $('') + .hide() + .appendTo(localRepoLabel) + .click(function(e) { + e.preventDefault(); + if ($(this).hasClass('selected')) { + hideLocalRepoBranchList(); + } else { + $(this).addClass('selected'); + localRepoLabel.css('height','auto'); + localRepoBranchListRow.slideDown(100); + localRepoBranchList.refresh("/projects/"+activeProject.name+"/branches"); + localRepoBranchList.focus(); + } + + }); + + var localRepoBranchListRow = $('
    ').hide().appendTo(localRepoLabel); + var localRepoBranchList = utils.createBranchList({ + current: function() { + return activeProject.branches.local + }, + placeholder: "Find or create a branch", + container: localRepoBranchListRow, + onselect: function(body) { + localRepoText.text(body.name); + hideLocalRepoBranchList(); + } + }) + + var hideEditForm = function() { + editRepoButton.show(); + localRepoSearch.hide(); + formButtons.hide(); + localRepoBranchListRow.slideUp(100); + localRepoSearch.removeClass('selected'); + + } + + var formButtons = $('').hide().appendTo(repoContainer); + $('') + .appendTo(formButtons) + .click(function(evt) { + evt.preventDefault(); + hideEditForm(); + }); + var saveButton = $('') + .appendTo(formButtons) + .click(function(evt) { + evt.preventDefault(); + hideEditForm(); + }); + var updateForm = function() { + // if (activeProject.settings.credentialSecretInvalid) { + // credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-warning"); + // credentialStateLabel.find(".user-settings-credentials-state").text("Invalid encryption key"); + // } else if (activeProject.settings.credentialsEncrypted) { + // credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-lock"); + // credentialStateLabel.find(".user-settings-credentials-state").text("Encryption enabled"); + // } else { + // credentialStateLabel.find(".user-settings-credentials-state-icon").removeClass().addClass("user-settings-credentials-state-icon fa fa-unlock"); + // credentialStateLabel.find(".user-settings-credentials-state").text("Encryption disabled"); + // } + // credentialSecretResetButton.toggleClass('disabled',!activeProject.settings.credentialsEncrypted); + // credentialSecretResetButton.prop('disabled',!activeProject.settings.credentialsEncrypted); + } + } + + function createRemoteRepositorySection(activeProject,pane) { + var title = $('

    ').text("Git Remotes").appendTo(pane); + var repoContainer = $('').appendTo(pane); + var editRepoButton = $('') + .appendTo(title) + .click(function(evt) { + editRepoButton.hide(); + formButtons.show(); + }); + + var row = $('').appendTo(repoContainer); + + + var remotesList = $("
      ",{style:"height: 320px"}).appendTo(row); + remotesList.editableList({ + addButton: "remote", + addItem: function(outer,index,entry) { + var row = $('').appendTo(outer); + $('').text('Name').appendTo(row); + $('
      ').text(entry.name).appendTo(row); + row = $('').appendTo(outer); + $('').text('Fetch URL').appendTo(row); + $('
      ').text(entry.urls.fetch).appendTo(row); + row = $('').appendTo(outer); + $('').text('Push URL').appendTo(row); + $('
      ').text(entry.urls.push).appendTo(row); + } + }); + if (activeProject.hasOwnProperty('remotes')) { + for (var name in activeProject.remotes) { + if (activeProject.remotes.hasOwnProperty(name)) { + remotesList.editableList('addItem',{name:name,urls:activeProject.remotes[name]}); + } + } + } + + + + // row = $('').appendTo(repoContainer); + + // if (activeProject.hasOwnProperty('remotes')) { + // $('').text('URL').appendTo(row); + // for (var name in activeProject.remotes) { + // if (activeProject.remotes.hasOwnProperty(name)) { + // var repos = activeProject.remotes[name]; + // if (repos.fetch === repos.push) { + // $('
      ').text(repos.fetch).appendTo(row); + // $('
      ').appendTo(row).find('small').text(name+" fetch/push"); + // } else { + // $('
      ').text(repos.fetch).appendTo(row); + // $('
      ').appendTo(row).find('small').text(name+" fetch"); + // $('').appendTo(row); + // $('
      ').text(repos.push).appendTo(row); + // $('
      ').appendTo(row).find('small').text(name+" push"); + // // $('').text(repos.fetch+" (fetch)").appendTo(repoRow); + // // $('').text(repos.push+" (push)").appendTo(repoRow); + // } + // } + // } + // if (activeProject.branches.hasOwnProperty('remote')) { + // row = $('').appendTo(repoContainer); + // $('').appendTo(row); + // $('
      ').text(activeProject.branches.remote).appendTo(row); + // + // row = $('').appendTo(repoContainer); + // $('').appendTo(row); + // $('
      ').appendTo(row); + // + // row = $('').appendTo(repoContainer); + // $('').appendTo(row); + // $('
      ').html("• • • • • • • •").appendTo(row); + // } + // } else { + // + // + // } + + var hideEditForm = function() { + editRepoButton.show(); + formButtons.hide(); + } + + var formButtons = $('').hide().appendTo(repoContainer); + $('') + .appendTo(formButtons) + .click(function(evt) { + evt.preventDefault(); + hideEditForm(); + }); + var saveButton = $('') + .appendTo(formButtons) + .click(function(evt) { + evt.preventDefault(); + hideEditForm(); + }); + var updateForm = function() { } + } + + + function createSettingsPane(activeProject) { var pane = $('
      '); createFilesSection(activeProject,pane); + // createLocalRepositorySection(activeProject,pane); + createRemoteRepositorySection(activeProject,pane); return pane; } diff --git a/editor/js/ui/projects.js b/editor/js/ui/projects.js index 3a8a3d3f0..ad2200d68 100644 --- a/editor/js/ui/projects.js +++ b/editor/js/ui/projects.js @@ -51,24 +51,61 @@ RED.projects = (function() { var copyProject; var projectRepoInput; var emptyProjectCredentialInput; + var projectRepoUserInput; + var projectRepoPasswordInput; + var projectNameSublabel; + var projectRepoPassphrase; + var projectRepoRemoteName + var projectRepoBranch; return { title: "Create a new project", // TODO: NLS content: function() { + var projectList = null; + var pendingFormValidation = false; + $.getJSON("projects", function(data) { + projectList = {}; + data.projects.forEach(function(p) { + projectList[p] = true; + if (pendingFormValidation) { + pendingFormValidation = false; + validateForm(); + } + }) + }); + var container = $('
      '); var row; var validateForm = function() { var projectName = projectNameInput.val(); var valid = true; - if (!/^[a-zA-Z0-9\-_]+$/.test(projectName)) { - if (projectNameInputChanged) { - projectNameInput.addClass("input-error"); + if (projectNameInputChanged) { + if (projectList === null) { + pendingFormValidation = true; + return; } - valid = false; - } else { - projectNameInput.removeClass("input-error"); + projectNameStatus.empty(); + if (!/^[a-zA-Z0-9\-_]+$/.test(projectName) || projectList[projectName]) { + projectNameInput.addClass("input-error"); + $('').appendTo(projectNameStatus); + projectNameValid = false; + valid = false; + if (projectList[projectName]) { + projectNameSublabel.text("Project already exists"); + } else { + projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + } + } else { + projectNameInput.removeClass("input-error"); + $('').appendTo(projectNameStatus); + projectNameSublabel.text("Must contain only A-Z 0-9 _ -"); + projectNameValid = true; + } + projectNameLastChecked = projectName; } + valid = projectNameValid; + var projectType = $(".projects-dialog-screen-create-type.selected").data('type'); if (projectType === 'copy') { if (!copyProject) { @@ -76,19 +113,43 @@ RED.projects = (function() { } } else if (projectType === 'clone') { var repo = projectRepoInput.val(); - if (repo.trim() === '') { - // TODO: could do more url regex checking... + + var validRepo = /^(?:git|ssh|https?|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?[\w\.@:\/~_-]+\.git(?:\/?|\#[\d\w\.\-_]+?)$/.test(repo); + if (!validRepo) { if (projectRepoChanged) { projectRepoInput.addClass("input-error"); } valid = false; } else { projectRepoInput.removeClass("input-error"); - } + if (/^(?:ssh|[\d\w\.\-_]+@[\w\.]+):(?:\/\/)?/.test(repo)) { + $(".projects-dialog-screen-create-row-creds").hide(); + $(".projects-dialog-screen-create-row-passphrase").show(); + } else if (/^https?:\/\//.test(repo)) { + $(".projects-dialog-screen-create-row-creds").show(); + $(".projects-dialog-screen-create-row-passphrase").hide(); + } else { + $(".projects-dialog-screen-create-row-creds").show(); + $(".projects-dialog-screen-create-row-passphrase").hide(); + } + + } else if (projectType === 'empty') { - projectFlowFileInput.toggleClass("input-error",projectFlowFileInput.val()==='') - valid = valid && projectFlowFileInput.val()!==''; + var flowFile = projectFlowFileInput.val(); + if (flowFile === "" || !/\.json$/.test(flowFile)) { + valid = false; + if (!projectFlowFileInput.hasClass("input-error")) { + projectFlowFileInput.addClass("input-error"); + projectFlowFileInput.next().empty().append(''); + } + } else { + if (projectFlowFileInput.hasClass("input-error")) { + projectFlowFileInput.removeClass("input-error"); + projectFlowFileInput.next().empty(); + } + } + var encryptionState = $("input[name=projects-encryption-type]:checked").val(); if (encryptionState === 'enabled') { var encryptionKeyType = $("input[name=projects-encryption-key]:checked").val(); @@ -103,7 +164,7 @@ RED.projects = (function() { row = $('
      ').appendTo(container); var createAsEmpty = $('').appendTo(row); - var createAsCopy = $('').appendTo(row); + // var createAsCopy = $('').appendTo(row); var createAsClone = $('').appendTo(row); row.find(".projects-dialog-screen-create-type").click(function(evt) { evt.preventDefault(); @@ -116,24 +177,51 @@ RED.projects = (function() { row = $('
      ').appendTo(container); - $('').appendTo(row); + $('').appendTo(row); + + var subrow = $('
      ').appendTo(row); + projectNameInput = $('').appendTo(subrow); + var projectNameStatus = $('
      ').appendTo(subrow); - projectNameInput = $('').appendTo(row); var projectNameInputChanged = false; - projectNameInput.on("change keyup paste",function() { projectNameInputChanged = true; validateForm(); }); - $('').appendTo(row); + var projectNameLastChecked = ""; + var projectNameValid; + var checkProjectName; + var autoInsertedName = ""; + + + projectNameInput.on("change keyup paste",function() { + projectNameInputChanged = (projectNameInput.val() !== projectNameLastChecked); + if (checkProjectName) { + clearTimeout(checkProjectName); + } else if (projectNameInputChanged) { + projectNameStatus.empty(); + $('').appendTo(projectNameStatus); + if (projectNameInput.val() === '') { + validateForm(); + return; + } + } + checkProjectName = setTimeout(function() { + validateForm(); + checkProjectName = null; + },300) + }); + projectNameSublabel = $('').appendTo(row).find("small"); // Empty Project row = $('
      ').appendTo(container); - $('').appendTo(row); - projectSummaryInput = $('').appendTo(row); + $('').appendTo(row); + projectSummaryInput = $('').appendTo(row); $('').appendTo(row); row = $('
      ').appendTo(container); - $('').appendTo(row); - projectFlowFileInput = $('').val("flow.json") + $('').appendTo(row); + subrow = $('
      ').appendTo(row); + projectFlowFileInput = $('').val("flow.json") .on("change keyup paste",validateForm) - .appendTo(row); + .appendTo(subrow); + $('
      ').appendTo(subrow); $('').appendTo(row); row = $('
      ').appendTo(container); @@ -185,7 +273,7 @@ RED.projects = (function() { emptyProjectCredentialInput.on("change keyup paste", validateForm); row = $('
      ').hide().appendTo(credentialsRightBox); - $('
      The credentials file will not be encrypted and its contents easily read
      ').appendTo(row); + $('
      The credentials file will not be encrypted and its contents easily read
      ').appendTo(row); credentialsRightBox.find("input[name=projects-encryption-key]").click(function() { var val = $(this).val(); @@ -195,29 +283,31 @@ RED.projects = (function() { // Copy Project - row = $('
      ').appendTo(container); - $('').appendTo(row); - var autoInsertedName = ""; - createProjectList({ - height: "250px", - small: true, - select: function(project) { - copyProject = project; - var projectName = projectNameInput.val(); - if (projectName === "" || projectName === autoInsertedName) { - autoInsertedName = project.name+"-copy"; - projectNameInput.val(autoInsertedName); - } - validateForm(); - } - }).appendTo(row); + // row = $('
      ').appendTo(container); + // $('').appendTo(row); + // createProjectList({ + // height: "250px", + // small: true, + // select: function(project) { + // copyProject = project; + // var projectName = projectNameInput.val(); + // if (projectName === "" || projectName === autoInsertedName) { + // autoInsertedName = project.name+"-copy"; + // projectNameInput.val(autoInsertedName); + // } + // validateForm(); + // } + // }).appendTo(row); // Clone Project row = $('
      ').appendTo(container); - $('').appendTo(row); - projectRepoInput = $('').appendTo(row); + $('').appendTo(row); + projectRepoInput = $('').appendTo(row); + $('').appendTo(row); + var projectRepoChanged = false; projectRepoInput.on("change keyup paste",function() { + projectRepoChanged = true; var repo = $(this).val(); var m = /\/([^/]+)\.git/.exec(repo); if (m) { @@ -230,10 +320,35 @@ RED.projects = (function() { validateForm(); }); - // Secret - clone - row = $('
      ').appendTo(container); - $('').appendTo(row); - projectSecretInput = $('').appendTo(row); + row = $('
      ').hide().appendTo(container); + + var subrow = $('
      ').appendTo(row); + $('').appendTo(subrow); + projectRepoUserInput = $('').appendTo(subrow); + + subrow = $('
      ').appendTo(row); + $('').appendTo(subrow); + projectRepoPasswordInput = $('').appendTo(subrow); + + row = $('
      ').hide().appendTo(container); + $('').appendTo(row); + projectRepoPassphrase = $('').appendTo(row); + + // row = $('
      ').hide().appendTo(container); + // $('').appendTo(row); + // projectRepoRemoteName = $('').val("origin").appendTo(row); + // + // row = $('
      ').hide().appendTo(container); + // $('').appendTo(row); + // projectRepoBranch = $('').val('master').appendTo(row); + + + + + // // Secret - clone + // row = $('
      ').appendTo(container); + // $('').appendTo(row); + // projectSecretInput = $('').appendTo(row); createAsEmpty.click(); @@ -280,9 +395,13 @@ RED.projects = (function() { } else if (projectType === 'copy') { projectData.copy = copyProject.name; } else if (projectType === 'clone') { - projectData.credentialSecret = projectSecretInput.val(); + // projectData.credentialSecret = projectSecretInput.val(); projectData.remote = { - url: projectRepoInput.val() + // name: projectRepoRemoteName.val()||'origin', + // branch: projectRepoBranch.val()||'master', + url: projectRepoInput.val(), + username: projectRepoUserInput.val(), + password: projectRepoPasswordInput.val() } } @@ -304,6 +423,8 @@ RED.projects = (function() { console.log("git error",error); }, 'git_auth_failed': function(error) { + projectRepoUserInput.addClass("input-error"); + projectRepoPasswordInput.addClass("input-error"); // getRepoAuthDetails(req); console.log("git auth error",error); }, @@ -551,6 +672,115 @@ RED.projects = (function() { }); } + function createBranchList(options) { + var branchFilterTerm = ""; + var branchFilterCreateItem; + var branches = []; + var currentBranch; + var branchPrefix = ""; + var container = $('
      ').appendTo(options.container); + + var branchFilter = $('').attr('placeholder',options.placeholder).appendTo(container).searchBox({ + delay: 200, + change: function() { + branchFilterTerm = $(this).val(); + if (/(\.\.|\/\.|[?*[~^: \\]|\/\/|\/.$|\/$)/.test(branchFilterTerm)) { + if (!branchFilterCreateItem.hasClass("input-error")) { + branchFilterCreateItem.addClass("input-error"); + branchFilterCreateItem.find("i").addClass("fa-warning").removeClass("fa-code-fork"); + } + branchFilterCreateItem.find("span").text("Invalid branch: "+branchPrefix+branchFilterTerm); + } else { + if (branchFilterCreateItem.hasClass("input-error")) { + branchFilterCreateItem.removeClass("input-error"); + branchFilterCreateItem.find("i").removeClass("fa-warning").addClass("fa-code-fork"); + } + branchFilterCreateItem.find(".sidebar-version-control-branch-list-entry-create-name").text(branchPrefix+branchFilterTerm); + } + branchList.editableList("filter"); + } + }); + var branchList = $("
        ",{style:"height: 130px;"}).appendTo(container); + branchList.editableList({ + addButton: false, + scrollOnAdd: false, + addItem: function(row,index,entry) { + var container = $('
        ').appendTo(row); + if (typeof entry !== "string") { + branchFilterCreateItem = container; + $('').appendTo(container); + $('').text("Create branch:").appendTo(container); + $('
        ').text(entry.name).appendTo(container); + } else { + $('').appendTo(container); + $('').text(entry).appendTo(container); + if (currentBranch === entry) { + container.addClass("selected"); + $('').text(options.currentLabel||"current").appendTo(container); + } + } + container.click(function(evt) { + evt.preventDefault(); + if ($(this).hasClass('input-error')) { + return; + } + var body = {}; + if (typeof entry !== "string") { + body.name = branchFilter.val(); + body.create = true; + if (options.remote) { + body.name = options.remote()+"/"+body.name; + } + } else { + if ($(this).hasClass('selected')) { + body.current = true; + } + body.name = entry; + } + if (options.onselect) { + options.onselect(body); + } + }); + }, + filter: function(data) { + var isCreateEntry = (typeof data !=="string"); + return (isCreateEntry && (branchFilterTerm !== "" && branches.indexOf(branchPrefix+branchFilterTerm) === -1) ) || (!isCreateEntry && data.indexOf(branchPrefix+branchFilterTerm) !== -1); + } + }); + return { + refresh: function(url) { + branchFilter.searchBox("value",""); + branchList.editableList('empty'); + var start = Date.now(); + var spinner = addSpinnerOverlay(container).addClass("projects-dialog-spinner-contain"); + currentBranch = options.current(); + if (options.remote) { + branchPrefix = options.remote()+"/"; + } else { + branchPrefix = ""; + } + $.getJSON(url,function(result) { + branches = result.branches; + result.branches.forEach(function(b) { + branchList.editableList('addItem',b); + }); + branchList.editableList('addItem',{}); + setTimeout(function() { + spinner.remove(); + },Math.max(300-(Date.now() - start),0)); + }); + }, + addItem: function(data) { branchList.editableList('addItem',data) }, + filter: function() { branchList.editableList('filter') }, + focus: function() { branchFilter.focus() } + } + } + + function addSpinnerOverlay(container) { + var spinner = $('
        ').appendTo(container); + return spinner; + } + function init() { dialog = $('
        ') .appendTo("body") @@ -577,9 +807,13 @@ RED.projects = (function() { RED.actions.add("core:new-project",RED.projects.newProject); RED.actions.add("core:open-project",RED.projects.selectProject); - - RED.projects.settings.init({sendRequest:sendRequest}); - RED.sidebar.versionControl.init({sendRequest:sendRequest}); + var projectsAPI = { + sendRequest:sendRequest, + createBranchList:createBranchList, + addSpinnerOverlay:addSpinnerOverlay + }; + RED.projects.settings.init(projectsAPI); + RED.sidebar.versionControl.init(projectsAPI); initScreens(); // initSidebar(); } @@ -589,6 +823,7 @@ RED.projects = (function() { $.getJSON("projects",function(data) { if (data.active) { $.getJSON("projects/"+data.active, function(project) { + console.log(project.branches); activeProject = project; // updateProjectSummary(); // updateProjectDescription(); diff --git a/editor/js/ui/tab-versionControl.js b/editor/js/ui/tab-versionControl.js index cf26e470d..25f9c1275 100644 --- a/editor/js/ui/tab-versionControl.js +++ b/editor/js/ui/tab-versionControl.js @@ -15,7 +15,7 @@ **/ RED.sidebar.versionControl = (function() { - var content; + var sidebarContent; var sections; var allChanges = {}; @@ -27,17 +27,21 @@ RED.sidebar.versionControl = (function() { var unstagedChanges; var stagedChanges; var bulkChangeSpinner; + var unmergedContent; + var unmergedChangesList; var commitButton; + var mergeConflictNotification; + var localChanges; var localCommitList; + var localCommitListShade; + // var remoteCommitList; + var isMerging; // TODO: DRY projectSummary.js - function addSpinnerOverlay(container) { - var spinner = $('
        ').appendTo(container); - return spinner; - } - function createChangeEntry(row, entry, status, unstaged) { + + function createChangeEntry(row, entry, status, state) { row.addClass("sidebar-version-control-change-entry"); var container = $('
        ').appendTo(row); if (entry.label) { @@ -51,13 +55,14 @@ RED.sidebar.versionControl = (function() { var label = $('').appendTo(container); var bg = $('
        ').appendTo(row); - var viewDiffButton = $('') + var viewDiffButton = $('') .appendTo(bg) .click(function(evt) { evt.preventDefault(); var activeProject = RED.projects.getActiveProject(); + var diffTarget = (state === 'staged')?"index":"tree"; utils.sendRequest({ - url: "projects/"+activeProject.name+"/diff/"+(unstaged?"tree":"index")+"/"+encodeURIComponent(entry.file), + url: "projects/"+activeProject.name+"/diff/"+diffTarget+"/"+encodeURIComponent(entry.file), type: "GET", responses: { 0: function(error) { @@ -65,15 +70,58 @@ RED.sidebar.versionControl = (function() { // done(error,null); }, 200: function(data) { + if (mergeConflictNotification) { + mergeConflictNotification.close(); + mergeConflictNotification = null; + } + var title; + if (state === 'unstaged') { + title = 'Unstaged changes : '+entry.file + } else if (state === 'staged') { + title = 'Staged changes : '+entry.file + } else { + title = 'Resolve conflicts : '+entry.file + } var options = { diff: data.diff, - title: (unstaged?"Unstaged":"Staged")+" changes : "+entry.file, - oldRevTitle: unstaged?(entry.indexStatus === " "?"HEAD":"Staged"):"HEAD", - newRevTitle: unstaged?"Unstaged":"Staged", - oldRev: unstaged?(entry.indexStatus === " "?"@":":0"):"@", - newRev: unstaged?"_":":0", + title: title, + unmerged: state === 'unmerged', project: activeProject } + if (state == 'unstaged') { + options.oldRevTitle = entry.indexStatus === " "?"HEAD":"Staged"; + options.newRevTitle = "Unstaged"; + options.oldRev = entry.indexStatus === " "?"@":":0"; + options.newRev = "_"; + } else if (state === 'staged') { + options.oldRevTitle = "HEAD"; + options.newRevTitle = "Staged"; + options.oldRev = "@"; + options.newRev = ":0"; + } else { + options.onresolve = function(resolution) { + utils.sendRequest({ + url: "projects/"+activeProject.name+"/resolve/"+encodeURIComponent(entry.file), + type: "POST", + responses: { + 0: function(error) { + console.log(error); + // done(error,null); + }, + 200: function(data) { + refresh(true); + }, + 400: { + 'unexpected_error': function(error) { + console.log(error); + // done(error,null); + } + }, + } + },{resolutions:resolution.resolutions[entry.file]}); + } + } + options.oncancel = showMergeConflictNotification; RED.diff.showUnifiedDiff(options); // console.log(data.diff); }, @@ -82,38 +130,39 @@ RED.sidebar.versionControl = (function() { console.log(error); // done(error,null); } - }, + } } }) - }) - $('') - .appendTo(bg) - .click(function(evt) { - evt.preventDefault(); - var activeProject = RED.projects.getActiveProject(); - entry.spinner = addSpinnerOverlay(row).addClass('projects-version-control-spinner-sidebar'); - utils.sendRequest({ - url: "projects/"+activeProject.name+"/stage/"+encodeURIComponent(entry.file), - type: unstaged?"POST":"DELETE", - responses: { - 0: function(error) { - console.log(error); - // done(error,null); - }, - 200: function(data) { - refreshFiles(data); - }, - 400: { - 'unexpected_error': function(error) { + if (state !== 'unmerged') { + $('') + .appendTo(bg) + .click(function(evt) { + evt.preventDefault(); + var activeProject = RED.projects.getActiveProject(); + entry.spinner = utils.addSpinnerOverlay(row).addClass('projects-version-control-spinner-sidebar'); + utils.sendRequest({ + url: "projects/"+activeProject.name+"/stage/"+encodeURIComponent(entry.file), + type: (state==='unstaged')?"POST":"DELETE", + responses: { + 0: function(error) { console.log(error); // done(error,null); - } - }, - } - },{}); - }); - entry["update"+(unstaged?"Unstaged":"Staged")] = function(entry,status) { + }, + 200: function(data) { + refreshFiles(data); + }, + 400: { + 'unexpected_error': function(error) { + console.log(error); + // done(error,null); + } + }, + } + },{}); + }); + } + entry["update"+((state==='unstaged')?"Unstaged":"Staged")] = function(entry,status) { container.removeClass(); var iconClass = ""; if (status === 'A') { @@ -131,6 +180,9 @@ RED.sidebar.versionControl = (function() { } else if (status === 'R') { container.addClass("node-diff-changed"); iconClass = "fa-toggle-right"; + } else if (status === 'U') { + container.addClass("node-diff-conflicted"); + iconClass = "fa-exclamation-triangle"; } else { iconClass = "fa-exclamation-triangle" } @@ -157,7 +209,7 @@ RED.sidebar.versionControl = (function() { .toggleClass('fa-eye-slash',(status === 'D' || status === '?')) } - entry["update"+(unstaged?"Unstaged":"Staged")](entry, status); + entry["update"+((state==='unstaged')?"Unstaged":"Staged")](entry, status); } var utils; function init(_utils) { @@ -165,15 +217,15 @@ RED.sidebar.versionControl = (function() { RED.actions.add("core:show-version-control-tab",show); - content = $('
        ', {class:"sidebar-version-control"}); - var stackContainer = $("
        ",{class:"sidebar-version-control-stack"}).appendTo(content); + sidebarContent = $('
        ', {class:"sidebar-version-control"}); + var stackContainer = $("
        ",{class:"sidebar-version-control-stack"}).appendTo(sidebarContent); sections = RED.stack.create({ container: stackContainer, fill: true, singleExpanded: true }); - var localChanges = sections.add({ + localChanges = sections.add({ title: "Local Changes", collapsible: true }); @@ -190,7 +242,7 @@ RED.sidebar.versionControl = (function() { var unstagedContent = $('').appendTo(localChanges.content); - var header = $('').appendTo(unstagedContent); + var header = $('').appendTo(unstagedContent); stageAllButton = $('') .appendTo(header) .click(function(evt) { @@ -206,7 +258,7 @@ RED.sidebar.versionControl = (function() { addButton: false, scrollOnAdd: false, addItem: function(row,index,entry) { - createChangeEntry(row,entry,entry.treeStatus,true); + createChangeEntry(row,entry,entry.treeStatus,'unstaged'); }, sort: function(A,B) { if (A.treeStatus === '?' && B.treeStatus !== '?') { @@ -219,9 +271,58 @@ RED.sidebar.versionControl = (function() { }) + unmergedContent = $('').appendTo(localChanges.content); + + header = $('').appendTo(unmergedContent); + bg = $('
        ').appendTo(header); + var abortMergeButton = $('') + .appendTo(bg) + .click(function(evt) { + evt.preventDefault(); + evt.stopPropagation(); + var spinner =u(unmergedContent); + var activeProject = RED.projects.getActiveProject(); + utils.sendRequest({ + url: "projects/"+activeProject.name+"/merge", + type: "DELETE", + responses: { + 0: function(error) { + console.log(error); + }, + 200: function(data) { + spinner.remove(); + refresh(true); + }, + 400: { + 'unexpected_error': function(error) { + console.log(error); + } + }, + } + }); + }); + unmergedChangesList = $("
          ",{style:"position: absolute; top: 30px; bottom: 0; right:0; left:0;"}).appendTo(unmergedContent); + unmergedChangesList.editableList({ + addButton: false, + scrollOnAdd: false, + addItem: function(row,index,entry) { + createChangeEntry(row,entry,entry.treeStatus,'unmerged'); + }, + sort: function(A,B) { + if (A.treeStatus === '?' && B.treeStatus !== '?') { + return 1; + } else if (A.treeStatus !== '?' && B.treeStatus === '?') { + return -1; + } + return A.file.localeCompare(B.file); + } + + }) + + var stagedContent = $('').appendTo(localChanges.content); - var header = $('').appendTo(stagedContent); + header = $('').appendTo(stagedContent); bg = $('
          ').appendTo(header); commitButton = $('') @@ -232,9 +333,16 @@ RED.sidebar.versionControl = (function() { commitMessage.val(""); submitCommitButton.attr("disabled",true); unstagedContent.css("height","30px"); - stagedContent.css("height","calc(100% - 30px - 175px)"); + if (unmergedContent.is(":visible")) { + unmergedContent.css("height","30px"); + stagedContent.css("height","calc(100% - 60px - 175px)"); + } else { + stagedContent.css("height","calc(100% - 30px - 175px)"); + } commitBox.show(); - commitBox.css("height","175px"); + setTimeout(function() { + commitBox.css("height","175px"); + },10); stageAllButton.attr("disabled",true); unstageAllButton.attr("disabled",true); commitButton.attr("disabled",true); @@ -258,21 +366,21 @@ RED.sidebar.versionControl = (function() { addButton: false, scrollOnAdd: false, addItem: function(row,index,entry) { - createChangeEntry(row,entry,entry.indexStatus,false); + createChangeEntry(row,entry,entry.indexStatus,'staged'); }, sort: function(A,B) { return A.file.localeCompare(B.file); } }) - commitBox = $('').hide().appendTo(localChanges.content); + commitBox = $('').hide().appendTo(localChanges.content); var commitMessage = $('