diff --git a/build/bundle.js b/build/bundle.js
index 646a2b8b..443b9be3 100644
--- a/build/bundle.js
+++ b/build/bundle.js
@@ -402,7 +402,7 @@ require.define("/animationFactory.js",function(require,module,exports,__dirname,
*/
// essentially a static class
-var AnimationFactory = function() {
+function AnimationFactory() {
}
@@ -645,2819 +645,10 @@ AnimationFactory.prototype.genFromToSnapshotAnimation = function(
exports.AnimationFactory = AnimationFactory;
-});
-
-require.define("/collections.js",function(require,module,exports,__dirname,__filename,process,global){var Commit = require('./git').Commit;
-var Branch = require('./git').Branch;
-
-var CommitCollection = Backbone.Collection.extend({
- model: Commit
-});
-
-var CommandCollection = Backbone.Collection.extend({
- model: Command,
-});
-
-var BranchCollection = Backbone.Collection.extend({
- model: Branch
-});
-
-var CommandEntryCollection = Backbone.Collection.extend({
- model: CommandEntry,
- localStorage: new Backbone.LocalStorage('CommandEntries')
-});
-
-var CommandBuffer = Backbone.Model.extend({
- defaults: {
- collection: null,
- },
-
- initialize: function(options) {
- events.on('gitCommandReady', _.bind(
- this.addCommand, this
- ));
-
- options.collection.bind('add', this.addCommand, this);
-
- this.buffer = [];
- this.timeout = null;
- },
-
- addCommand: function(command) {
- this.buffer.push(command);
- this.touchBuffer();
- },
-
- touchBuffer: function() {
- // touch buffer just essentially means we just check if our buffer is being
- // processed. if it's not, we immediately process the first item
- // and then set the timeout.
- if (this.timeout) {
- // timeout existence implies its being processed
- return;
- }
- this.setTimeout();
- },
-
-
- setTimeout: function() {
- this.timeout = setTimeout(_.bind(function() {
- this.sipFromBuffer();
- }, this), TIME.betweenCommandsDelay);
- },
-
- popAndProcess: function() {
- var popped = this.buffer.shift(0);
- var callback = _.bind(function() {
- this.setTimeout();
- }, this);
-
- // find a command with no error
- while (popped.get('error') && this.buffer.length) {
- popped = this.buffer.pop();
- }
- if (!popped.get('error')) {
- // pass in a callback, so when this command is "done" we will process the next.
- events.trigger('processCommand', popped, callback);
- } else {
- this.clear();
- }
- },
-
- clear: function() {
- clearTimeout(this.timeout);
- this.timeout = null;
- },
-
- sipFromBuffer: function() {
- if (!this.buffer.length) {
- this.clear();
- return;
- }
-
- this.popAndProcess();
- },
-});
-
-exports.CommitCollection = CommitCollection;
-exports.CommandCollection = CommandCollection;
-exports.BranchCollection = BranchCollection;
-exports.CommandEntryCollection = CommandEntryCollection;
-exports.CommandBuffer = CommandBuffer;
-
-
-});
-
-require.define("/git.js",function(require,module,exports,__dirname,__filename,process,global){var animationFactory = new require('./animationFactory').AnimationFactory();
-
-// backbone or something uses _.uniqueId, so we make our own here
-var uniqueId = (function() {
- var n = 0;
- return function(prepend) {
- return prepend? prepend + n++ : n++;
- };
-})();
-
-function GitEngine(options) {
- this.rootCommit = null;
- this.refs = {};
- this.HEAD = null;
-
- this.branchCollection = options.branches;
- this.commitCollection = options.collection;
- this.gitVisuals = options.gitVisuals;
-
- // global variable to keep track of the options given
- // along with the command call.
- this.commandOptions = {};
- this.generalArgs = [];
-
- events.on('processCommand', _.bind(this.dispatch, this));
-}
-
-GitEngine.prototype.defaultInit = function() {
- var defaultTree = JSON.parse(unescape("%7B%22branches%22%3A%7B%22master%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22master%22%2C%22type%22%3A%22branch%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22type%22%3A%22commit%22%2C%22parents%22%3A%5B%5D%2C%22author%22%3A%22Peter%20Cottle%22%2C%22createTime%22%3A%22Mon%20Nov%2005%202012%2000%3A56%3A47%20GMT-0800%20%28PST%29%22%2C%22commitMessage%22%3A%22Quick%20Commit.%20Go%20Bears%21%22%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22type%22%3A%22commit%22%2C%22parents%22%3A%5B%22C0%22%5D%2C%22author%22%3A%22Peter%20Cottle%22%2C%22createTime%22%3A%22Mon%20Nov%2005%202012%2000%3A56%3A47%20GMT-0800%20%28PST%29%22%2C%22commitMessage%22%3A%22Quick%20Commit.%20Go%20Bears%21%22%2C%22id%22%3A%22C1%22%7D%7D%2C%22HEAD%22%3A%7B%22id%22%3A%22HEAD%22%2C%22target%22%3A%22master%22%2C%22type%22%3A%22general%20ref%22%7D%7D"));
- this.loadTree(defaultTree);
-};
-
-GitEngine.prototype.init = function() {
- // make an initial commit and a master branch
- this.rootCommit = this.makeCommit(null, null, {rootCommit: true});
- this.commitCollection.add(this.rootCommit);
-
- var master = this.makeBranch('master', this.rootCommit);
- this.HEAD = new Ref({
- id: 'HEAD',
- target: master
- });
- this.refs[this.HEAD.get('id')] = this.HEAD;
-
- // commit once to get things going
- this.commit();
-};
-
-GitEngine.prototype.exportTree = function() {
- // need to export all commits, their connectivity / messages, branches, and state of head.
- // this would be simple if didn't have circular structures.... :P
- // thus, we need to loop through and "flatten" our graph of objects referencing one another
- var totalExport = {
- branches: {},
- commits: {},
- HEAD: null
- };
-
- _.each(this.branchCollection.toJSON(), function(branch) {
- branch.target = branch.target.get('id');
- branch.visBranch = undefined;
-
- totalExport.branches[branch.id] = branch;
- });
-
- _.each(this.commitCollection.toJSON(), function(commit) {
- // clear out the fields that reference objects and create circular structure
- _.each(Commit.prototype.constants.circularFields, function(field) {
- commit[field] = undefined;
- }, this);
-
- // convert parents
- var parents = [];
- _.each(commit.parents, function(par) {
- parents.push(par.get('id'));
- });
- commit.parents = parents;
-
- totalExport.commits[commit.id] = commit;
- }, this);
-
- var HEAD = this.HEAD.toJSON();
- HEAD.visBranch = undefined;
- HEAD.lastTarget = HEAD.lastLastTarget = HEAD.visBranch = undefined;
- HEAD.target = HEAD.target.get('id');
- totalExport.HEAD = HEAD;
-
- return totalExport;
-};
-
-GitEngine.prototype.printTree = function() {
- var str = escape(JSON.stringify(this.exportTree()));
- return str;
-};
-
-GitEngine.prototype.printAndCopyTree = function() {
- window.prompt('Copy the tree string below', this.printTree());
-};
-
-GitEngine.prototype.loadTree = function(tree) {
- // deep copy in case we use it a bunch
- tree = $.extend(true, {}, tree);
-
- // first clear everything
- this.removeAll();
-
- this.instantiateFromTree(tree);
-
- this.reloadGraphics();
-};
-
-GitEngine.prototype.loadTreeFromString = function(treeString) {
- this.loadTree(JSON.parse(unescape(treeString)));
-};
-
-GitEngine.prototype.instantiateFromTree = function(tree) {
- // now we do the loading part
- var createdSoFar = {};
-
- _.each(tree.commits, function(commitJSON) {
- var commit = this.getOrMakeRecursive(tree, createdSoFar, commitJSON.id);
- this.commitCollection.add(commit);
- }, this);
-
- _.each(tree.branches, function(branchJSON) {
- var branch = this.getOrMakeRecursive(tree, createdSoFar, branchJSON.id);
-
- this.branchCollection.add(branch, {silent: true});
- }, this);
-
- var HEAD = this.getOrMakeRecursive(tree, createdSoFar, tree.HEAD.id);
- this.HEAD = HEAD;
-
- this.rootCommit = createdSoFar['C0'];
- if (!this.rootCommit) {
- throw new Error('Need root commit of C0 for calculations');
- }
- this.refs = createdSoFar;
-
- this.branchCollection.each(function(branch) {
- this.gitVisuals.addBranch(branch);
- }, this);
-};
-
-GitEngine.prototype.reloadGraphics = function() {
- // get the root commit, no better way to do it
- var rootCommit = null;
- this.commitCollection.each(function(commit) {
- if (commit.get('id') == 'C0') {
- rootCommit = commit;
- }
- });
- this.gitVisuals.rootCommit = rootCommit;
-
- // this just basically makes the HEAD branch. the head branch really should have been
- // a member of a collection and not this annoying edge case stuff... one day
- this.gitVisuals.initHeadBranch();
-
- // when the paper is ready
- this.gitVisuals.drawTreeFromReload();
-
- this.gitVisuals.refreshTreeHarsh();
-};
-
-GitEngine.prototype.getOrMakeRecursive = function(tree, createdSoFar, objID) {
- if (createdSoFar[objID]) {
- // base case
- return createdSoFar[objID];
- }
-
- var getType = function(tree, id) {
- if (tree.commits[id]) {
- return 'commit';
- } else if (tree.branches[id]) {
- return 'branch';
- } else if (id == 'HEAD') {
- return 'HEAD';
- }
- throw new Error("bad type for " + id);
- };
-
- // figure out what type
- var type = getType(tree, objID);
-
- if (type == 'HEAD') {
- var headJSON = tree.HEAD;
- var HEAD = new Ref(_.extend(
- tree.HEAD,
- {
- target: this.getOrMakeRecursive(tree, createdSoFar, headJSON.target)
- }
- ));
- createdSoFar[objID] = HEAD;
- return HEAD;
- }
-
- if (type == 'branch') {
- var branchJSON = tree.branches[objID];
-
- var branch = new Branch(_.extend(
- tree.branches[objID],
- {
- target: this.getOrMakeRecursive(tree, createdSoFar, branchJSON.target)
- }
- ));
- createdSoFar[objID] = branch;
- return branch;
- }
-
- if (type == 'commit') {
- // for commits, we need to grab all the parents
- var commitJSON = tree.commits[objID];
-
- var parentObjs = [];
- _.each(commitJSON.parents, function(parentID) {
- parentObjs.push(this.getOrMakeRecursive(tree, createdSoFar, parentID));
- }, this);
-
- var commit = new Commit(_.extend(
- commitJSON,
- {
- parents: parentObjs,
- gitVisuals: this.gitVisuals
- }
- ));
- createdSoFar[objID] = commit;
- return commit;
- }
-
- throw new Error('ruh rho!! unsupported tyep for ' + objID);
-};
-
-GitEngine.prototype.removeAll = function() {
- this.branchCollection.reset();
- this.commitCollection.reset();
- this.refs = {};
- this.HEAD = null;
- this.rootCommit = null;
-
- this.gitVisuals.resetAll();
-};
-
-GitEngine.prototype.getDetachedHead = function() {
- // detached head is if HEAD points to a commit instead of a branch...
- var target = this.HEAD.get('target');
- var targetType = target.get('type');
- return targetType !== 'branch';
-};
-
-GitEngine.prototype.validateBranchName = function(name) {
- name = name.replace(/\s/g, '');
- if (!/^[a-zA-Z0-9]+$/.test(name)) {
- throw new GitError({
- msg: 'woah bad branch name!! This is not ok: ' + name
- });
- }
- if (/[hH][eE][aA][dD]/.test(name)) {
- throw new GitError({
- msg: 'branch name of "head" is ambiguous, dont name it that'
- });
- }
- if (name.length > 9) {
- name = name.slice(0, 9);
- this.command.addWarning(
- 'Sorry, we need to keep branch names short for the visuals. Your branch ' +
- 'name was truncated to 9 characters, resulting in ' + name
- );
- }
- return name;
-};
-
-GitEngine.prototype.makeBranch = function(id, target) {
- id = this.validateBranchName(id);
- if (this.refs[id]) {
- throw new GitError({
- msg: 'that branch id either matches a commit hash or already exists!'
- });
- }
-
- var branch = new Branch({
- target: target,
- id: id
- });
- this.branchCollection.add(branch);
- this.refs[branch.get('id')] = branch;
- return branch;
-};
-
-GitEngine.prototype.getHead = function() {
- return _.clone(this.HEAD);
-};
-
-GitEngine.prototype.getBranches = function() {
- var toReturn = [];
- this.branchCollection.each(function(branch) {
- toReturn.push({
- id: branch.get('id'),
- selected: this.HEAD.get('target') === branch,
- target: branch.get('target'),
- obj: branch
- });
- }, this);
- return toReturn;
-};
-
-GitEngine.prototype.printBranchesWithout = function(without) {
- var commitToBranches = this.getUpstreamBranchSet();
- var commitID = this.getCommitFromRef(without).get('id');
-
- var toPrint = [];
- _.each(commitToBranches[commitID], function(branchJSON) {
- branchJSON.selected = this.HEAD.get('target').get('id') == branchJSON.id;
- toPrint.push(branchJSON);
- }, this);
- this.printBranches(toPrint);
-};
-
-GitEngine.prototype.printBranches = function(branches) {
- var result = '';
- _.each(branches, function(branch) {
- result += (branch.selected ? '* ' : '') + branch.id + '\n';
- });
- throw new CommandResult({
- msg: result
- });
-};
-
-GitEngine.prototype.makeCommit = function(parents, id, options) {
- // ok we need to actually manually create commit IDs now because
- // people like nikita (thanks for finding this!) could
- // make branches named C2 before creating the commit C2
- if (!id) {
- id = uniqueId('C');
- while (this.refs[id]) {
- id = uniqueId('C');
- }
- }
-
- var commit = new Commit(_.extend({
- parents: parents,
- id: id,
- gitVisuals: this.gitVisuals
- },
- options || {}
- ));
-
- this.refs[commit.get('id')] = commit;
- this.commitCollection.add(commit);
- return commit;
-};
-
-GitEngine.prototype.acceptNoGeneralArgs = function() {
- if (this.generalArgs.length) {
- throw new GitError({
- msg: "That command accepts no general arguments"
- });
- }
-};
-
-GitEngine.prototype.validateArgBounds = function(args, lower, upper, option) {
- // this is a little utility class to help arg validation that happens over and over again
- var what = (option === undefined) ?
- 'git ' + this.command.get('method') :
- this.command.get('method') + ' ' + option + ' ';
- what = 'with ' + what;
-
- if (args.length < lower) {
- throw new GitError({
- msg: 'I expect at least ' + String(lower) + ' argument(s) ' + what
- });
- }
- if (args.length > upper) {
- throw new GitError({
- msg: 'I expect at most ' + String(upper) + ' argument(s) ' + what
- });
- }
-};
-
-GitEngine.prototype.oneArgImpliedHead = function(args, option) {
- // for log, show, etc
- this.validateArgBounds(args, 0, 1, option);
- if (args.length == 0) {
- args.push('HEAD');
- }
-};
-
-GitEngine.prototype.twoArgsImpliedHead = function(args, option) {
- // our args we expect to be between 1 and 2
- this.validateArgBounds(args, 1, 2, option);
- // and if it's one, add a HEAD to the back
- if (args.length == 1) {
- args.push('HEAD');
- }
-};
-
-GitEngine.prototype.revertStarter = function() {
- this.validateArgBounds(this.generalArgs, 1, NaN);
-
- var response = this.revert(this.generalArgs);
-
- if (response) {
- animationFactory.rebaseAnimation(this.animationQueue, response, this, this.gitVisuals);
- }
-};
-
-GitEngine.prototype.revert = function(whichCommits) {
- // for each commit, we want to revert it
- var toRebase = [];
- _.each(whichCommits, function(stringRef) {
- toRebase.push(this.getCommitFromRef(stringRef));
- }, this);
-
- // we animate reverts now!! we use the rebase animation though so that's
- // why the terminology is like it is
- var animationResponse = {};
- animationResponse.destinationBranch = this.resolveID(toRebase[0]);
- animationResponse.toRebaseArray = toRebase.slice(0);
- animationResponse.rebaseSteps = [];
-
- beforeSnapshot = this.gitVisuals.genSnapshot();
- var afterSnapshot;
-
- // now make a bunch of commits on top of where we are
- var base = this.getCommitFromRef('HEAD');
- _.each(toRebase, function(oldCommit) {
- var newId = this.rebaseAltID(oldCommit.get('id'));
-
- var newCommit = this.makeCommit([base], newId, {
- commitMessage: 'Reverting ' + this.resolveName(oldCommit) +
- ': "' + oldCommit.get('commitMessage') + '"'
- });
-
- base = newCommit;
-
- // animation stuff
- afterSnapshot = this.gitVisuals.genSnapshot();
- animationResponse.rebaseSteps.push({
- oldCommit: oldCommit,
- newCommit: newCommit,
- beforeSnapshot: beforeSnapshot,
- afterSnapshot: afterSnapshot
- });
- beforeSnapshot = afterSnapshot;
- }, this);
- // done! update our location
- this.setTargetLocation('HEAD', base);
-
- // animation
- return animationResponse;
-};
-
-GitEngine.prototype.resetStarter = function() {
- if (this.commandOptions['--soft']) {
- throw new GitError({
- msg: "You can't use --soft because there is no concept of stashing" +
- " changes or staging files, so you will lose your progress." +
- " Try using interactive rebasing (or just rebasing) to move commits."
- });
- }
- if (this.commandOptions['--hard']) {
- this.command.addWarning(
- 'Nice! You are using --hard. The default behavior is a hard reset in ' +
- "this demo, so don't worry about specifying the option explicity"
- );
- // dont absorb the arg off of --hard
- this.generalArgs = this.generalArgs.concat(this.commandOptions['--hard']);
- }
-
- this.validateArgBounds(this.generalArgs, 1, 1);
-
- if (this.getDetachedHead()) {
- throw new GitError({
- msg: "Cant reset in detached head! Use checkout if you want to move"
- });
- }
-
- this.reset(this.generalArgs[0]);
-};
-
-GitEngine.prototype.reset = function(target) {
- this.setTargetLocation('HEAD', this.getCommitFromRef(target));
-};
-
-GitEngine.prototype.cherrypickStarter = function() {
- this.validateArgBounds(this.generalArgs, 1, 1);
- var newCommit = this.cherrypick(this.generalArgs[0]);
-
- animationFactory.genCommitBirthAnimation(this.animationQueue, newCommit, this.gitVisuals);
-};
-
-GitEngine.prototype.cherrypick = function(ref) {
- var commit = this.getCommitFromRef(ref);
- // check if we already have that
- var set = this.getUpstreamSet('HEAD');
- if (set[commit.get('id')]) {
- throw new GitError({
- msg: "We already have that commit in our changes history! You can't cherry-pick it " +
- "if it shows up in git log."
- });
- }
-
- // alter the ID slightly
- var id = this.rebaseAltID(commit.get('id'));
-
- // now commit with that id onto HEAD
- var newCommit = this.makeCommit([this.getCommitFromRef('HEAD')], id);
- this.setTargetLocation(this.HEAD, newCommit);
- return newCommit;
-};
-
-GitEngine.prototype.commitStarter = function() {
- this.acceptNoGeneralArgs();
- if (this.commandOptions['-am'] && (
- this.commandOptions['-a'] || this.commandOptions['-m'])) {
- throw new GitError({
- msg: "You can't have -am with another -m or -a!"
- });
- }
-
- var msg = null;
- if (this.commandOptions['-a']) {
- this.command.addWarning('No need to add files in this demo');
- }
-
- if (this.commandOptions['-am']) {
- var args = this.commandOptions['-am'];
- this.validateArgBounds(args, 1, 1, '-am');
-
- this.command.addWarning("Don't worry about adding files in this demo. I'll take " +
- "down your commit message anyways, but you can commit without a message " +
- "in this demo as well");
- msg = args[0];
- }
-
- if (this.commandOptions['-m']) {
- var args = this.commandOptions['-m'];
- this.validateArgBounds(args, 1, 1, '-m');
- msg = args[0];
- }
-
- var newCommit = this.commit();
- if (msg) {
- msg = msg
- .replace(/"/g, '"')
- .replace(/^"/g, '')
- .replace(/"$/g, '');
-
- newCommit.set('commitMessage', msg);
- }
- animationFactory.genCommitBirthAnimation(this.animationQueue, newCommit, this.gitVisuals);
-};
-
-GitEngine.prototype.commit = function() {
- var targetCommit = this.getCommitFromRef(this.HEAD);
- var id = undefined;
-
- // if we want to ammend, go one above
- if (this.commandOptions['--amend']) {
- targetCommit = this.resolveID('HEAD~1');
- id = this.rebaseAltID(this.getCommitFromRef('HEAD').get('id'));
- }
-
- var newCommit = this.makeCommit([targetCommit], id);
- if (this.getDetachedHead()) {
- this.command.addWarning('Warning!! Detached HEAD state');
- }
-
- this.setTargetLocation(this.HEAD, newCommit);
- return newCommit;
-};
-
-GitEngine.prototype.resolveName = function(someRef) {
- // first get the obj
- var obj = this.resolveID(someRef);
- if (obj.get('type') == 'commit') {
- return 'commit ' + obj.get('id');
- }
- if (obj.get('type') == 'branch') {
- return 'branch "' + obj.get('id') + '"';
- }
- // we are dealing with HEAD
- return this.resolveName(obj.get('target'));
-};
-
-GitEngine.prototype.resolveID = function(idOrTarget) {
- if (idOrTarget === null || idOrTarget === undefined) {
- throw new Error('Dont call this with null / undefined');
- }
-
- if (typeof idOrTarget !== 'string') {
- return idOrTarget;
- }
- return this.resolveStringRef(idOrTarget);
-};
-
-GitEngine.prototype.resolveStringRef = function(ref) {
- if (this.refs[ref]) {
- return this.refs[ref];
- }
-
- // may be something like HEAD~2 or master^^
- var relativeRefs = [
- [/^([a-zA-Z0-9]+)~(\d+)\s*$/, function(matches) {
- return parseInt(matches[2]);
- }],
- [/^([a-zA-Z0-9]+)(\^+)\s*$/, function(matches) {
- return matches[2].length;
- }]
- ];
-
- var startRef = null;
- var numBack = null;
- _.each(relativeRefs, function(config) {
- var regex = config[0];
- var parse = config[1];
- if (regex.test(ref)) {
- var matches = regex.exec(ref);
- numBack = parse(matches);
- startRef = matches[1];
- }
- }, this);
-
- if (!startRef) {
- throw new GitError({
- msg: 'unknown ref ' + ref
- });
- }
- if (!this.refs[startRef]) {
- throw new GitError({
- msg: 'the ref ' + startRef +' does not exist.'
- });
- }
- var commit = this.getCommitFromRef(startRef);
-
- return this.numBackFrom(commit, numBack);
-};
-
-GitEngine.prototype.getCommitFromRef = function(ref) {
- var start = this.resolveID(ref);
-
- // works for both HEAD and just a single layer. aka branch
- while (start.get('type') !== 'commit') {
- start = start.get('target');
- }
- return start;
-};
-
-GitEngine.prototype.getType = function(ref) {
- return this.resolveID(ref).get('type');
-};
-
-GitEngine.prototype.setTargetLocation = function(ref, target) {
- if (this.getType(ref) == 'commit') {
- // nothing to do
- return;
- }
-
- // sets whatever ref is (branch, HEAD, etc) to a target. so if
- // you pass in HEAD, and HEAD is pointing to a branch, it will update
- // the branch to that commit, not the HEAD
- var ref = this.getOneBeforeCommit(ref);
- ref.set('target', target);
-};
-
-GitEngine.prototype.getUpstreamBranchSet = function() {
- // this is expensive!! so only call once in a while
- var commitToSet = {};
-
- var inArray = function(arr, id) {
- var found = false;
- _.each(arr, function(wrapper) {
- if (wrapper.id == id) {
- found = true;
- }
- });
-
- return found;
- };
-
- var bfsSearch = function(commit) {
- var set = [];
- var pQueue = [commit];
- while (pQueue.length) {
- var popped = pQueue.pop();
- set.push(popped.get('id'));
-
- if (popped.get('parents') && popped.get('parents').length) {
- pQueue = pQueue.concat(popped.get('parents'));
- }
- }
- return set;
- };
-
- this.branchCollection.each(function(branch) {
- var set = bfsSearch(branch.get('target'));
- _.each(set, function(id) {
- commitToSet[id] = commitToSet[id] || [];
-
- // only add it if it's not there, so hue blending is ok
- if (!inArray(commitToSet[id], branch.get('id'))) {
- commitToSet[id].push({
- obj: branch,
- id: branch.get('id')
- });
- }
- });
- });
-
- return commitToSet;
-};
-
-GitEngine.prototype.getUpstreamHeadSet = function() {
- var set = this.getUpstreamSet('HEAD');
- var including = this.getCommitFromRef('HEAD').get('id');
-
- set[including] = true;
- return set;
-};
-
-GitEngine.prototype.getOneBeforeCommit = function(ref) {
- // you can call this command on HEAD in detached, HEAD, or on a branch
- // and it will return the ref that is one above a commit. aka
- // it resolves HEAD to something that we can move the ref with
- var start = this.resolveID(ref);
- if (start === this.HEAD && !this.getDetachedHead()) {
- start = start.get('target');
- }
- return start;
-};
-
-GitEngine.prototype.numBackFrom = function(commit, numBack) {
- // going back '3' from a given ref is not trivial, for you might have
- // a bunch of merge commits and such. like this situation:
- //
- // * merge master into new
- // |\
- // | \* commit here
- // |* \ commit there
- // | |* commit here
- // \ /
- // | * root
- //
- //
- // hence we need to do a BFS search, with the commit date being the
- // value to sort off of (rather than just purely the level)
- if (numBack == 0) {
- return commit;
- }
-
- // we use a special sorting function here that
- // prefers the later commits over the earlier ones
- var sortQueue = _.bind(function(queue) {
- queue.sort(this.idSortFunc);
- queue.reverse();
- }, this);
-
- var pQueue = [].concat(commit.get('parents') || []);
- sortQueue(pQueue);
- numBack--;
-
- while (pQueue.length && numBack !== 0) {
- var popped = pQueue.shift(0);
- var parents = popped.get('parents');
-
- if (parents && parents.length) {
- pQueue = pQueue.concat(parents);
- }
-
- sortQueue(pQueue);
- numBack--;
- }
-
- if (numBack !== 0 || pQueue.length == 0) {
- throw new GitError({
- msg: "Sorry, I can't go that many commits back"
- });
- }
- return pQueue.shift(0);
-};
-
-GitEngine.prototype.scrapeBaseID = function(id) {
- var results = /^C(\d+)/.exec(id);
-
- if (!results) {
- throw new Error('regex failed on ' + id);
- }
-
- return 'C' + results[1];
-};
-
-GitEngine.prototype.rebaseAltID = function(id) {
- // this function alters an ID to add a quote to the end,
- // indicating that it was rebased. it also checks existence
- var regexMap = [
- [/^C(\d+)[']{0,2}$/, function(bits) {
- // this id can use another quote, so just add it
- return bits[0] + "'";
- }],
- [/^C(\d+)[']{3}$/, function(bits) {
- // here we switch from C''' to C'^4
- return bits[0].slice(0, -3) + "'^4";
- }],
- [/^C(\d+)['][^](\d+)$/, function(bits) {
- return 'C' + String(bits[1]) + "'^" + String(Number(bits[2]) + 1);
- }]
- ];
-
- for (var i = 0; i < regexMap.length; i++) {
- var regex = regexMap[i][0];
- var func = regexMap[i][1];
- var results = regex.exec(id);
- if (results) {
- var newId = func(results);
- // if this id exists, continue down the rabbit hole
- if (this.refs[newId]) {
- return this.rebaseAltID(newId);
- } else {
- return newId;
- }
- }
- }
- throw new Error('could not modify the id ' + id);
-};
-
-GitEngine.prototype.idSortFunc = function(cA, cB) {
- // commit IDs can come in many forms:
- // C4
- // C4' (from a rebase)
- // C4'' (from multiple rebases)
- // C4'^3 (from a BUNCH of rebases)
-
- var scale = 1000;
-
- var regexMap = [
- [/^C(\d+)$/, function(bits) {
- // return the 4 from C4
- return scale * bits[1];
- }],
- [/^C(\d+)([']+)$/, function(bits) {
- // return the 4 from C4, plus the length of the quotes
- return scale * bits[1] + bits[2].length;
- }],
- [/^C(\d+)['][^](\d+)$/, function(bits) {
- return scale * bits[1] + Number(bits[2]);
- }]
- ];
-
- var getNumToSort = function(id) {
- for (var i = 0; i < regexMap.length; i++) {
- var regex = regexMap[i][0];
- var func = regexMap[i][1];
- var results = regex.exec(id);
- if (results) {
- return func(results);
- }
- }
- throw new Error('Could not parse commit ID ' + id);
- }
-
- return getNumToSort(cA.get('id')) - getNumToSort(cB.get('id'));
-};
-
-GitEngine.prototype.rebaseInteractiveStarter = function() {
- var args = this.commandOptions['-i'];
- this.twoArgsImpliedHead(args, ' -i');
-
- this.rebaseInteractive(args[0], args[1]);
-};
-
-GitEngine.prototype.rebaseStarter = function() {
- if (this.commandOptions['-i']) {
- this.rebaseInteractiveStarter();
- return;
- }
-
- this.twoArgsImpliedHead(this.generalArgs);
-
- var response = this.rebase(this.generalArgs[0], this.generalArgs[1]);
-
- if (response === undefined) {
- // was a fastforward or already up to date. returning now
- // will trigger the refresh animation by not adding anything to
- // the animation queue
- return;
- }
-
- animationFactory.rebaseAnimation(this.animationQueue, response, this, this.gitVisuals);
-};
-
-GitEngine.prototype.rebase = function(targetSource, currentLocation) {
- // first some conditions
- if (this.isUpstreamOf(targetSource, currentLocation)) {
- this.command.setResult('Branch already up-to-date');
-
- // git for some reason always checks out the branch you are rebasing,
- // no matter the result of the rebase
- this.checkout(currentLocation);
-
- // returning instead of throwing makes a tree refresh
- return;
- }
-
- if (this.isUpstreamOf(currentLocation, targetSource)) {
- // just set the target of this current location to the source
- this.setTargetLocation(currentLocation, this.getCommitFromRef(targetSource));
- // we need the refresh tree animation to happen, so set the result directly
- // instead of throwing
- this.command.setResult('Fast-forwarding...');
-
- this.checkout(currentLocation);
- return;
- }
-
- // now the part of actually rebasing.
- // We need to get the downstream set of targetSource first.
- // then we BFS from currentLocation, using the downstream set as our stopping point.
- // we need to BFS because we need to include all commits below
- // pop these commits on top of targetSource and modify their ids with quotes
- var stopSet = this.getUpstreamSet(targetSource)
-
- // now BFS from here on out
- var toRebaseRough = [];
- var pQueue = [this.getCommitFromRef(currentLocation)];
-
- while (pQueue.length) {
- var popped = pQueue.pop();
-
- // if its in the set, dont add it
- if (stopSet[popped.get('id')]) {
- continue;
- }
-
- // it's not in the set, so we need to rebase this commit
- toRebaseRough.push(popped);
- toRebaseRough.sort(this.idSortFunc);
- toRebaseRough.reverse();
- // keep searching
- pQueue = pQueue.concat(popped.get('parents'));
- }
-
- return this.rebaseFinish(toRebaseRough, stopSet, targetSource, currentLocation);
-};
-
-GitEngine.prototype.rebaseInteractive = function(targetSource, currentLocation) {
- // there are a reduced set of checks now, so we can't exactly use parts of the rebase function
- // but it will look similar.
-
- // first if we are upstream of the target
- if (this.isUpstreamOf(currentLocation, targetSource)) {
- throw new GitError({
- msg: 'Nothing to do... (git throws a "noop" status here); ' +
- 'Your source is upstream of your rebase target'
- });
- }
-
- // now get the stop set
- var stopSet = this.getUpstreamSet(targetSource);
-
- var toRebaseRough = [];
- // standard BFS
- var pQueue = [this.getCommitFromRef(currentLocation)];
-
- while (pQueue.length) {
- var popped = pQueue.pop();
-
- if (stopSet[popped.get('id')]) {
- continue;
- }
-
- toRebaseRough.push(popped);
- pQueue = pQueue.concat(popped.get('parents'));
- pQueue.sort(this.idSortFunc);
- }
-
- // throw our merge's real fast and see if we have anything to do
- var toRebase = [];
- _.each(toRebaseRough, function(commit) {
- if (commit.get('parents').length == 1) {
- toRebase.push(commit);
- }
- });
-
- if (!toRebase.length) {
- throw new GitError({
- msg: 'No commits to rebase! Everything is a merge commit'
- });
- }
-
- // now do stuff :D since all our validation checks have passed, we are going to defer animation
- // and actually launch the dialog
- this.animationQueue.set('defer', true);
-
- var callback = _.bind(function(userSpecifiedRebase) {
- // first, they might have dropped everything (annoying)
- if (!userSpecifiedRebase.length) {
- this.command.setResult('Nothing to do...');
- this.animationQueue.start();
- return;
- }
-
- // finish the rebase crap and animate!
- var animationData = this.rebaseFinish(userSpecifiedRebase, {}, targetSource, currentLocation);
- animationFactory.rebaseAnimation(this.animationQueue, animationData, this, this.gitVisuals);
- this.animationQueue.start();
- }, this);
-
- new InteractiveRebaseView({
- callback: callback,
- toRebase: toRebase,
- el: $('#dialogHolder')
- });
-};
-
-GitEngine.prototype.rebaseFinish = function(toRebaseRough, stopSet, targetSource, currentLocation) {
- // now we have the all the commits between currentLocation and the set of target to rebase.
- var animationResponse = {};
- animationResponse.destinationBranch = this.resolveID(targetSource);
-
- // we need to throw out merge commits
- var toRebase = [];
- _.each(toRebaseRough, function(commit) {
- if (commit.get('parents').length == 1) {
- toRebase.push(commit);
- }
- });
-
- // we ALSO need to throw out commits that will do the same changes. like
- // if the upstream set has a commit C4 and we have C4', we dont rebase the C4' again.
- // get this by doing ID scraping
- var changesAlreadyMade = {};
- _.each(stopSet, function(val, key) {
- changesAlreadyMade[this.scrapeBaseID(key)] = val; // val == true
- }, this);
-
- // now get rid of the commits that will redo same changes
- toRebaseRough = toRebase;
- toRebase = [];
- _.each(toRebaseRough, function(commit) {
- var baseID = this.scrapeBaseID(commit.get('id'));
- if (!changesAlreadyMade[baseID]) {
- toRebase.push(commit);
- }
- }, this);
-
- if (!toRebase.length) {
- throw new GitError({
- msg: 'No Commits to Rebase! Everything else is merge commits or changes already have been applied'
- });
- }
-
- // now reverse it once more to get it in the right order
- toRebase.reverse();
- animationResponse.toRebaseArray = toRebase.slice(0);
-
- // now pop all of these commits onto targetLocation
- var base = this.getCommitFromRef(targetSource);
-
- // do the rebase, and also maintain all our animation info during this
- animationResponse.rebaseSteps = [];
- var beforeSnapshot = this.gitVisuals.genSnapshot();
- var afterSnapshot;
- _.each(toRebase, function(old) {
- var newId = this.rebaseAltID(old.get('id'));
-
- var newCommit = this.makeCommit([base], newId);
- base = newCommit;
-
- // animation info
- afterSnapshot = this.gitVisuals.genSnapshot();
- animationResponse.rebaseSteps.push({
- oldCommit: old,
- newCommit: newCommit,
- beforeSnapshot: beforeSnapshot,
- afterSnapshot: afterSnapshot
- });
- beforeSnapshot = afterSnapshot;
- }, this);
-
- if (this.resolveID(currentLocation).get('type') == 'commit') {
- // we referenced a commit like git rebase C2 C1, so we have
- // to manually check out C1'
-
- var steps = animationResponse.rebaseSteps;
- var newestCommit = steps[steps.length - 1].newCommit;
-
- this.checkout(newestCommit);
- } else {
- // now we just need to update the rebased branch is
- this.setTargetLocation(currentLocation, base);
- this.checkout(currentLocation);
- }
-
- // for animation
- return animationResponse;
-};
-
-GitEngine.prototype.mergeStarter = function() {
- this.twoArgsImpliedHead(this.generalArgs);
-
- var newCommit = this.merge(this.generalArgs[0], this.generalArgs[1]);
-
- if (newCommit === undefined) {
- // its just a fast forwrard
- animationFactory.refreshTree(this.animationQueue, this.gitVisuals);
- return;
- }
-
- animationFactory.genCommitBirthAnimation(this.animationQueue, newCommit, this.gitVisuals);
-};
-
-GitEngine.prototype.merge = function(targetSource, currentLocation) {
- // first some conditions
- if (this.isUpstreamOf(targetSource, currentLocation) ||
- this.getCommitFromRef(targetSource) === this.getCommitFromRef(currentLocation)) {
- throw new CommandResult({
- msg: 'Branch already up-to-date'
- });
- }
-
- if (this.isUpstreamOf(currentLocation, targetSource)) {
- // just set the target of this current location to the source
- this.setTargetLocation(currentLocation, this.getCommitFromRef(targetSource));
- // get fresh animation to happen
- this.command.setResult('Fast-forwarding...');
- return;
- }
-
- // now the part of making a merge commit
- var parent1 = this.getCommitFromRef(currentLocation);
- var parent2 = this.getCommitFromRef(targetSource);
-
- // we need a fancy commit message
- var msg = 'Merge ' + this.resolveName(targetSource) +
- ' into ' + this.resolveName(currentLocation);
-
- // since we specify parent 1 as the first parent, it is the "main" parent
- // and the node will be displayed below that branch / commit / whatever
- var mergeCommit = this.makeCommit(
- [parent1, parent2],
- null,
- {
- commitMessage: msg
- }
- );
-
- this.setTargetLocation(currentLocation, mergeCommit)
- return mergeCommit;
-};
-
-GitEngine.prototype.checkoutStarter = function() {
- if (this.commandOptions['-b']) {
- // the user is really trying to just make a branch and then switch to it. so first:
- var args = this.commandOptions['-b'];
- this.twoArgsImpliedHead(args, '-b');
-
- var validId = this.validateBranchName(args[0]);
- this.branch(validId, args[1]);
- this.checkout(validId);
- return;
- }
-
- if (this.commandOptions['-']) {
- // get the heads last location
- var lastPlace = this.HEAD.get('lastLastTarget');
- if (!lastPlace) {
- throw new GitError({
- msg: 'Need a previous location to do - switching'
- });
- }
- this.HEAD.set('target', lastPlace);
- return;
- }
-
- if (this.commandOptions['-B']) {
- var args = this.commandOptions['-B'];
- this.twoArgsImpliedHead(args, '-B');
-
- this.forceBranch(args[0], args[1]);
- this.checkout(args[0]);
- return;
- }
-
- this.validateArgBounds(this.generalArgs, 1, 1);
-
- this.checkout(this.unescapeQuotes(this.generalArgs[0]));
-};
-
-GitEngine.prototype.checkout = function(idOrTarget) {
- var target = this.resolveID(idOrTarget);
- if (target.get('id') === 'HEAD') {
- // git checkout HEAD is a
- // meaningless command but i used to do this back in the day
- return;
- }
-
- var type = target.get('type');
- if (type !== 'branch' && type !== 'commit') {
- throw new GitError({
- msg: 'can only checkout branches and commits!'
- });
- }
-
- this.HEAD.set('target', target);
-};
-
-GitEngine.prototype.branchStarter = function() {
- // handle deletion first
- if (this.commandOptions['-d'] || this.commandOptions['-D']) {
- var names = this.commandOptions['-d'] || this.commandOptions['-D'];
- this.validateArgBounds(names, 1, NaN, '-d');
-
- _.each(names, function(name) {
- this.deleteBranch(name);
- }, this);
- return;
- }
-
- if (this.commandOptions['--contains']) {
- var args = this.commandOptions['--contains'];
- this.validateArgBounds(args, 1, 1, '--contains');
- this.printBranchesWithout(args[0]);
- return;
- }
-
- if (this.commandOptions['-f']) {
- var args = this.commandOptions['-f'];
- this.twoArgsImpliedHead(args, '-f');
-
- // we want to force a branch somewhere
- this.forceBranch(args[0], args[1]);
- return;
- }
-
-
- if (this.generalArgs.length == 0) {
- this.printBranches(this.getBranches());
- return;
- }
-
- this.twoArgsImpliedHead(this.generalArgs);
- this.branch(this.generalArgs[0], this.generalArgs[1]);
-};
-
-GitEngine.prototype.forceBranch = function(branchName, where) {
- // if branchname doesn't exist...
- if (!this.refs[branchName]) {
- this.branch(branchName, where);
- }
-
- var branch = this.resolveID(branchName);
- if (branch.get('type') !== 'branch') {
- throw new GitError({
- msg: "Can't force move anything but a branch!!"
- });
- }
-
- var whereCommit = this.getCommitFromRef(where);
-
- this.setTargetLocation(branch, whereCommit);
-};
-
-GitEngine.prototype.branch = function(name, ref) {
- var target = this.getCommitFromRef(ref);
- this.makeBranch(name, target);
-};
-
-GitEngine.prototype.deleteBranch = function(name) {
- // trying to delete, lets check our refs
- var target = this.resolveID(name);
- if (target.get('type') !== 'branch') {
- throw new GitError({
- msg: "You can't delete things that arent branches with branch command"
- });
- }
- if (target.get('id') == 'master') {
- throw new GitError({
- msg: "You can't delete the master branch!"
- });
- }
- if (this.HEAD.get('target') === target) {
- throw new GitError({
- msg: "Cannot delete the branch you are currently on"
- });
- }
-
- // now we know it's a branch
- var branch = target;
-
- this.branchCollection.remove(branch);
- this.refs[branch.get('id')] = undefined;
- delete this.refs[branch.get('id')];
-
- if (branch.get('visBranch')) {
- branch.get('visBranch').remove();
- }
-};
-
-GitEngine.prototype.unescapeQuotes = function(str) {
- return str.replace(/'/g, "'");
-}
-
-GitEngine.prototype.dispatch = function(command, callback) {
- // current command, options, and args are stored in the gitEngine
- // for easy reference during processing.
- this.command = command;
- this.commandOptions = command.get('supportedMap');
- this.generalArgs = command.get('generalArgs');
-
- // set up the animation queue
- var whenDone = _.bind(function() {
- command.set('status', 'finished');
- callback();
- }, this);
- this.animationQueue = new AnimationQueue({
- callback: whenDone
- });
-
- command.set('status', 'processing');
- try {
- var methodName = command.get('method').replace(/-/g, '') + 'Starter';
- this[methodName]();
- } catch (err) {
- if (err instanceof GitError ||
- err instanceof CommandResult) {
- // short circuit animation by just setting error and returning
- command.set('error', err);
- callback();
- return;
- } else {
- throw err;
- }
- }
-
- // only add the refresh if we didn't do manual animations
- if (!this.animationQueue.get('animations').length && !this.animationQueue.get('defer')) {
- animationFactory.refreshTree(this.animationQueue, this.gitVisuals);
- }
-
- // animation queue will call the callback when its done
- if (!this.animationQueue.get('defer')) {
- this.animationQueue.start();
- }
-};
-
-GitEngine.prototype.showStarter = function() {
- this.oneArgImpliedHead(this.generalArgs);
-
- this.show(this.generalArgs[0]);
-};
-
-GitEngine.prototype.show = function(ref) {
- var commit = this.getCommitFromRef(ref);
-
- throw new CommandResult({
- msg: commit.getShowEntry()
- });
-};
-
-GitEngine.prototype.statusStarter = function() {
- var lines = [];
- if (this.getDetachedHead()) {
- lines.push('Detached Head!');
- } else {
- var branchName = this.HEAD.get('target').get('id');
- lines.push('On branch ' + branchName);
- }
- lines.push('Changes to be committed:');
- lines.push('');
- lines.push(' modified: cal/OskiCostume.stl');
- lines.push('');
- lines.push('Ready to commit! (as always in this demo)');
-
- var msg = '';
- _.each(lines, function(line) {
- msg += '# ' + line + '\n';
- });
-
- throw new CommandResult({
- msg: msg
- });
-};
-
-GitEngine.prototype.logStarter = function() {
- if (this.generalArgs.length == 2) {
- // do fancy git log branchA ^branchB
- if (this.generalArgs[1][0] == '^') {
- this.logWithout(this.generalArgs[0], this.generalArgs[1]);
- } else {
- throw new GitError({
- msg: 'I need a not branch (^branchName) when getting two arguments!'
- });
- }
- }
-
- this.oneArgImpliedHead(this.generalArgs);
- this.log(this.generalArgs[0]);
-};
-
-GitEngine.prototype.logWithout = function(ref, omitBranch) {
- // slice off the ^branch
- omitBranch = omitBranch.slice(1);
- this.log(ref, this.getUpstreamSet(omitBranch));
-};
-
-GitEngine.prototype.log = function(ref, omitSet) {
- // omit set is for doing stuff like git log branchA ^branchB
- omitSet = omitSet || {};
- // first get the commit we referenced
- var commit = this.getCommitFromRef(ref);
-
- // then get as many far back as we can from here, order by commit date
- var toDump = [];
- var pQueue = [commit];
-
- var seen = {};
-
- while (pQueue.length) {
- var popped = pQueue.shift(0);
- if (seen[popped.get('id')] || omitSet[popped.get('id')]) {
- continue;
- }
- seen[popped.get('id')] = true;
-
- toDump.push(popped);
-
- if (popped.get('parents') && popped.get('parents').length) {
- pQueue = pQueue.concat(popped.get('parents'));
- }
- }
-
- // now go through and collect logs
- var bigLogStr = '';
- _.each(toDump, function(c) {
- bigLogStr += c.getLogEntry();
- }, this);
-
- throw new CommandResult({
- msg: bigLogStr
- });
-};
-
-GitEngine.prototype.addStarter = function() {
- throw new CommandResult({
- msg: "This demo is meant to demonstrate git branching, so don't worry about " +
- "adding / staging files. Just go ahead and commit away!"
- });
-};
-
-GitEngine.prototype.getCommonAncestor = function(ancestor, cousin) {
- if (this.isUpstreamOf(cousin, ancestor)) {
- throw new Error('Dont use common ancestor if we are upstream!');
- }
-
- var upstreamSet = this.getUpstreamSet(ancestor);
- // now BFS off of cousin until you find something
-
- var queue = [this.getCommitFromRef(cousin)];
- while (queue.length) {
- var here = queue.pop();
- if (upstreamSet[here.get('id')]) {
- return here;
- }
- queue = queue.concat(here.get('parents'));
- }
- throw new Error('something has gone very wrong... two nodes arent connected!');
-};
-
-GitEngine.prototype.isUpstreamOf = function(child, ancestor) {
- child = this.getCommitFromRef(child);
-
- // basically just do a completely BFS search on ancestor to the root, then
- // check for membership of child in that set of explored nodes
- var upstream = this.getUpstreamSet(ancestor);
- return upstream[child.get('id')] !== undefined;
-};
-
-GitEngine.prototype.getUpstreamSet = function(ancestor) {
- var commit = this.getCommitFromRef(ancestor);
- var ancestorID = commit.get('id');
- var queue = [commit];
-
- var exploredSet = {};
- exploredSet[ancestorID] = true;
- while (queue.length) {
- var here = queue.pop();
- var rents = here.get('parents');
-
- _.each(rents, function(rent) {
- exploredSet[rent.get('id')] = true;
- queue.push(rent);
- });
- }
- return exploredSet;
-};
-
-
-var Ref = Backbone.Model.extend({
- initialize: function() {
- if (!this.get('target')) {
- throw new Error('must be initialized with target');
- }
- if (!this.get('id')) {
- throw new Error('must be given an id');
- }
- this.set('type', 'general ref');
-
- if (this.get('id') == 'HEAD') {
- this.set('lastLastTarget', null);
- this.set('lastTarget', this.get('target'));
- // have HEAD remember where it is for checkout -
- this.on('change:target', this.targetChanged, this);
- }
- },
-
- targetChanged: function(model, targetValue, ev) {
- // push our little 3 stack back. we need to do this because
- // backbone doesn't give you what the value WAS, only what it was changed
- // TO
- this.set('lastLastTarget', this.get('lastTarget'));
- this.set('lastTarget', targetValue);
- },
-
- toString: function() {
- return 'a ' + this.get('type') + 'pointing to ' + String(this.get('target'));
- }
-});
-
-var Branch = Ref.extend({
- defaults: {
- visBranch: null,
- },
-
- initialize: function() {
- Ref.prototype.initialize.call(this);
- this.set('type', 'branch');
- }
-});
-
-var Commit = Backbone.Model.extend({
- defaults: {
- type: 'commit',
- children: null,
- parents: null,
- author: 'Peter Cottle',
- createTime: null,
- commitMessage: null,
- visNode: null,
- gitVisuals: null
- },
-
- constants: {
- circularFields: ['gitVisuals', 'visNode', 'children']
- },
-
- getLogEntry: function() {
- // for now we are just joining all these things with newlines which
- // will get placed by paragraph tags. Not really a fan of this, but
- // it's better than making an entire template and all that jazz
- return [
- 'Author: ' + this.get('author'),
- 'Date: ' + this.get('createTime'),
- '
',
- this.get('commitMessage'),
- '
',
- 'Commit: ' + this.get('id')
- ].join('\n' ) + '\n';
- },
-
- getShowEntry: function() {
- // same deal as above, show log entry and some fake changes
- return [
- this.getLogEntry(),
- 'diff --git a/bigGameResults.html b/bigGameResults.html',
- '--- bigGameResults.html',
- '+++ bigGameResults.html',
- '@@ 13,27 @@ Winner, Score',
- '- Stanfurd, 14-7',
- '+ Cal, 21-14',
- ].join('\n') + '\n';
- },
-
- validateAtInit: function() {
- if (!this.get('id')) {
- throw new Error('Need ID!!');
- }
-
- if (!this.get('createTime')) {
- this.set('createTime', new Date().toString());
- }
- if (!this.get('commitMessage')) {
- this.set('commitMessage', 'Quick Commit. Go Bears!');
- }
-
- this.set('children', []);
-
- // root commits have no parents
- if (!this.get('rootCommit')) {
- if (!this.get('parents') || !this.get('parents').length) {
- throw new Error('needs parents');
- }
- }
- },
-
- addNodeToVisuals: function() {
- var visNode = this.get('gitVisuals').addNode(this.get('id'), this);
- this.set('visNode', visNode);
- },
-
- addEdgeToVisuals: function(parent) {
- this.get('gitVisuals').addEdge(this.get('id'), parent.get('id'));
- },
-
- isMainParent: function(parent) {
- var index = this.get('parents').indexOf(parent);
- return index === 0;
- },
-
- initialize: function(options) {
- this.validateAtInit();
- this.addNodeToVisuals();
-
- _.each(this.get('parents'), function(parent) {
- parent.get('children').push(this);
- this.addEdgeToVisuals(parent);
- }, this);
- }
-});
-
-exports.GitEngine = GitEngine;
-exports.Commit = Commit;
-exports.Branch = Branch;
-exports.Ref = Ref;
-
-
-});
-
-require.define("/commandViews.js",function(require,module,exports,__dirname,__filename,process,global){var CommandEntryCollection = require('./collections').CommandEntryCollection;
-
-var CommandPromptView = Backbone.View.extend({
- initialize: function(options) {
- this.collection = options.collection;
-
- // uses local storage
- this.commands = new CommandEntryCollection();
- this.commands.fetch({
- success: _.bind(function() {
- // reverse the commands. this is ugly but needs to be done...
- var commands = [];
- this.commands.each(function(c) {
- commands.push(c);
- });
-
- commands.reverse();
- this.commands.reset();
-
- _.each(commands, function(c) {
- this.commands.add(c);
- }, this);
- }, this)
- });
-
- this.index = -1;
-
- this.commandSpan = this.$('#prompt span.command')[0];
- this.commandCursor = this.$('#prompt span.cursor')[0];
-
- // this is evil, but we will refer to HTML outside the document
- // and attach a click event listener so we can focus / unfocus
- $(document).delegate('#commandLineHistory', 'click', _.bind(function() {
- this.focus();
- }, this));
-
-
- $(document).delegate('#commandTextField', 'blur', _.bind(function() {
- this.blur();
- }, this));
-
- events.on('processCommandFromEvent', this.addToCollection, this);
- events.on('submitCommandValueFromEvent', this.submitValue, this);
- events.on('rollupCommands', this.rollupCommands, this);
-
- // hacky timeout focus
- setTimeout(_.bind(function() {
- this.focus();
- }, this), 100);
- },
-
- events: {
- 'keydown #commandTextField': 'onKey',
- 'keyup #commandTextField': 'onKeyUp',
- 'blur #commandTextField': 'hideCursor',
- 'focus #commandTextField': 'showCursor'
- },
-
- blur: function() {
- $(this.commandCursor).toggleClass('shown', false);
- },
-
- focus: function() {
- this.$('#commandTextField').focus();
- this.showCursor();
- },
-
- hideCursor: function() {
- this.toggleCursor(false);
- },
-
- showCursor: function() {
- this.toggleCursor(true);
- },
-
- toggleCursor: function(state) {
- $(this.commandCursor).toggleClass('shown', state);
- },
-
- onKey: function(e) {
- var el = e.srcElement;
- this.updatePrompt(el)
- },
-
- onKeyUp: function(e) {
- this.onKey(e);
-
- // we need to capture some of these events.
- // WARNING: this key map is not internationalized :(
- var keyMap = {
- // enter
- 13: _.bind(function() {
- this.submit();
- }, this),
- // up
- 38: _.bind(function() {
- this.commandSelectChange(1);
- }, this),
- // down
- 40: _.bind(function() {
- this.commandSelectChange(-1);
- }, this)
- };
-
- if (keyMap[e.which] !== undefined) {
- e.preventDefault();
- keyMap[e.which]();
- this.onKey(e);
- }
- },
-
- badHtmlEncode: function(text) {
- return text.replace(/&/g,'&')
- .replace(/= this.commands.length || this.index < 0) {
- this.clear();
- this.index = -1;
- return;
- }
-
- // yay! we actually can display something
- var commandEntry = this.commands.toArray()[this.index].get('text');
- this.setTextField(commandEntry);
- },
-
- clearLocalStorage: function() {
- this.commands.each(function(c) {
- Backbone.sync('delete', c, function() { });
- }, this);
- localStorage.setItem('CommandEntries', '');
- },
-
- setTextField: function(value) {
- this.$('#commandTextField').val(value);
- },
-
- clear: function() {
- this.setTextField('');
- },
-
- submit: function() {
- var value = this.$('#commandTextField').val().replace('\n', '');
- this.clear();
- this.submitValue(value);
- },
-
- rollupCommands: function(numBack) {
- var which = this.commands.toArray().slice(1, Number(numBack) + 1);
- which.reverse();
-
- var str = '';
- _.each(which, function(commandEntry) {
- str += commandEntry.get('text') + ';';
- }, this);
-
- console.log('the str', str);
-
- var rolled = new CommandEntry({text: str});
- this.commands.unshift(rolled);
- Backbone.sync('create', rolled, function() { });
- },
-
- submitValue: function(value) {
- // we should add if it's not a blank line and this is a new command...
- // or if we edited the command
- var shouldAdd = (value.length && this.index == -1) ||
- ((value.length && this.index !== -1 &&
- this.commands.toArray()[this.index].get('text') !== value));
-
- if (shouldAdd) {
- var commandEntry = new CommandEntry({text: value});
- this.commands.unshift(commandEntry);
-
- // store to local storage
- Backbone.sync('create', commandEntry, function() { });
-
- // if our length is too egregious, reset
- if (this.commands.length > 100) {
- this.clearLocalStorage();
- }
- }
- this.index = -1;
-
- // split commands on semicolon
- _.each(value.split(';'), _.bind(function(command, index) {
- command = _.escape(command);
-
- command = command
- .replace(/^(\s+)/, '')
- .replace(/(\s+)$/, '')
- .replace(/"/g, '"')
- .replace(/'/g, "'");
-
- if (index > 0 && !command.length) {
- return;
- }
-
- this.addToCollection(command);
- }, this));
- },
-
- addToCollection: function(value) {
- var command = new Command({
- rawStr: value
- });
- this.collection.add(command);
- }
-});
-
-
-// This is the view for all commands -- it will represent
-// their status (inqueue, processing, finished, error),
-// their value ("git commit --amend"),
-// and the result (either errors or warnings or whatever)
-var CommandView = Backbone.View.extend({
- tagName: 'div',
- model: Command,
- template: _.template($('#command-template').html()),
-
- events: {
- 'click': 'clicked'
- },
-
- clicked: function(e) {
- },
-
- initialize: function() {
- this.model.bind('change', this.wasChanged, this);
- this.model.bind('destroy', this.remove, this);
- },
-
- wasChanged: function(model, changeEvent) {
- // for changes that are just comestic, we actually only want to toggle classes
- // with jquery rather than brutally delete a html of HTML
- var changes = changeEvent.changes;
- var changeKeys = _.keys(changes);
- if (_.difference(changeKeys, ['status']) == 0) {
- this.updateStatus();
- } else if (_.difference(changeKeys, ['error']) == 0) {
- // the above will
- this.render();
- } else {
- this.render();
- }
- },
-
- updateStatus: function() {
- var statuses = ['inqueue', 'processing', 'finished'];
- var toggleMap = {};
- _.each(statuses, function(status) {
- toggleMap[status] = false;
- });
- toggleMap[this.model.get('status')] = true;
-
- var query = this.$('p.commandLine');
-
- _.each(toggleMap, function(value, key) {
- query.toggleClass(key, value);
- });
- },
-
- render: function() {
- var json = _.extend(
- {
- resultType: '',
- result: '',
- formattedWarnings: this.model.getFormattedWarnings()
- },
- this.model.toJSON()
- );
-
- this.$el.html(this.template(json));
- return this;
- },
-
- remove: function() {
- $(this.el).hide();
- }
-});
-
-
-var CommandLineHistoryView = Backbone.View.extend({
- initialize: function(options) {
- this.collection = options.collection;
-
- this.collection.on('add', this.addOne, this);
- this.collection.on('reset', this.addAll, this);
- this.collection.on('all', this.render, this);
-
- this.collection.on('change', this.scrollDown, this);
-
- events.on('issueWarning', this.addWarning, this);
- events.on('commandScrollDown', this.scrollDown, this);
- },
-
- addWarning: function(msg) {
- var err = new Warning({
- msg: msg
- });
-
- var command = new Command({
- error: err,
- rawStr: 'Warning:'
- });
-
- this.collection.add(command);
- },
-
- scrollDown: function() {
- // if commandDisplay is ever bigger than #terminal, we need to
- // add overflow-y to terminal and scroll down
- var cD = $('#commandDisplay')[0];
- var t = $('#terminal')[0];
-
- if ($(t).hasClass('scrolling')) {
- t.scrollTop = t.scrollHeight;
- return;
- }
- if (cD.clientHeight > t.clientHeight) {
- $(t).css('overflow-y', 'scroll');
- $(t).css('overflow-x', 'hidden');
- $(t).addClass('scrolling');
- t.scrollTop = t.scrollHeight;
- }
- },
-
- addOne: function(command) {
- var view = new CommandView({
- model: command
- });
- this.$('#commandDisplay').append(view.render().el);
- this.scrollDown();
- },
-
- addAll: function() {
- this.collection.each(this.addOne);
- }
-});
-
-exports.CommandPromptView = CommandPromptView;
-exports.CommandLineHistoryView = CommandLineHistoryView;
-
-
-});
-
-require.define("/visuals.js",function(require,module,exports,__dirname,__filename,process,global){var CommitCollection = require('./collections').CommitCollection;
-var BranchCollection = require('./collections').BranchCollection;
-var GitEngine = require('./git').GitEngine;
-
-var Visualization = Backbone.View.extend({
- initialize: function(options) {
- var _this = this;
- Raphael(10, 10, 200, 200, function() {
-
- // for some reason raphael calls this function with a predefined
- // context...
- // so switch it
- _this.paperInitialize(this);
- });
- },
-
- paperInitialize: function(paper, options) {
- this.paper = paper;
-
- this.commitCollection = new CommitCollection();
- this.branchCollection = new BranchCollection();
-
- this.gitVisuals = new GitVisuals({
- commitCollection: this.commitCollection,
- branchCollection: this.branchCollection,
- paper: this.paper
- });
-
- this.gitEngine = new GitEngine({
- collection: this.commitCollection,
- branches: this.branchCollection,
- gitVisuals: this.gitVisuals
- });
- this.gitEngine.init();
- this.gitVisuals.assignGitEngine(this.gitEngine);
-
- this.myResize();
- $(window).on('resize', _.bind(this.myResize, this));
- this.gitVisuals.drawTreeFirstTime();
- },
-
- myResize: function() {
- var smaller = 1;
- var el = this.el;
-
- var left = el.offsetLeft;
- var top = el.offsetTop;
- var width = el.clientWidth - smaller;
- var height = el.clientHeight - smaller;
-
- $(this.paper.canvas).css({
- left: left + 'px',
- top: top + 'px'
- });
- this.paper.setSize(width, height);
- this.gitVisuals.canvasResize(width, height);
- }
-
-});
-
-function GitVisuals(options) {
- this.commitCollection = options.commitCollection;
- this.branchCollection = options.branchCollection;
- this.visNodeMap = {};
-
- this.visEdgeCollection = new VisEdgeCollection();
- this.visBranchCollection = new VisBranchCollection();
- this.commitMap = {};
-
- this.rootCommit = null;
- this.branchStackMap = null;
- this.upstreamBranchSet = null;
- this.upstreamHeadSet = null;
-
- this.paper = options.paper;
- this.gitReady = false;
-
- this.branchCollection.on('add', this.addBranchFromEvent, this);
- this.branchCollection.on('remove', this.removeBranch, this);
- this.deferred = [];
-
- events.on('refreshTree', _.bind(
- this.refreshTree, this
- ));
-}
-
-GitVisuals.prototype.defer = function(action) {
- this.deferred.push(action);
-};
-
-GitVisuals.prototype.deferFlush = function() {
- _.each(this.deferred, function(action) {
- action();
- }, this);
- this.deferred = [];
-};
-
-GitVisuals.prototype.resetAll = function() {
- // make sure to copy these collections because we remove
- // items in place and underscore is too dumb to detect length change
- var edges = this.visEdgeCollection.toArray();
- _.each(edges, function(visEdge) {
- visEdge.remove();
- }, this);
-
- var branches = this.visBranchCollection.toArray();
- _.each(branches, function(visBranch) {
- visBranch.remove();
- }, this);
-
- _.each(this.visNodeMap, function(visNode) {
- visNode.remove();
- }, this);
-
- this.visEdgeCollection.reset();
- this.visBranchCollection.reset();
-
- this.visNodeMap = {};
- this.rootCommit = null;
- this.commitMap = {};
-};
-
-GitVisuals.prototype.assignGitEngine = function(gitEngine) {
- this.gitEngine = gitEngine;
- this.initHeadBranch();
- this.deferFlush();
-};
-
-GitVisuals.prototype.initHeadBranch = function() {
- // it's unfortaunte we have to do this, but the head branch
- // is an edge case because it's not part of a collection so
- // we can't use events to load or unload it. thus we have to call
- // this ugly method which will be deleted one day
-
- // seed this with the HEAD pseudo-branch
- this.addBranchFromEvent(this.gitEngine.HEAD);
-};
-
-GitVisuals.prototype.getScreenBounds = function() {
- // for now we return the node radius subtracted from the walls
- return {
- widthPadding: GRAPHICS.nodeRadius * 1.5,
- heightPadding: GRAPHICS.nodeRadius * 1.5
- };
-};
-
-GitVisuals.prototype.toScreenCoords = function(pos) {
- if (!this.paper.width) {
- throw new Error('being called too early for screen coords');
- }
- var bounds = this.getScreenBounds();
-
- var shrink = function(frac, total, padding) {
- return padding + frac * (total - padding * 2);
- };
-
- return {
- x: shrink(pos.x, this.paper.width, bounds.widthPadding),
- y: shrink(pos.y, this.paper.height, bounds.heightPadding)
- };
-};
-
-GitVisuals.prototype.animateAllFromAttrToAttr = function(fromSnapshot, toSnapshot, idsToOmit) {
- var animate = function(obj) {
- var id = obj.getID();
- if (_.include(idsToOmit, id)) {
- return;
- }
-
- if (!fromSnapshot[id] || !toSnapshot[id]) {
- // its actually ok it doesnt exist yet
- return;
- }
- obj.animateFromAttrToAttr(fromSnapshot[id], toSnapshot[id]);
- };
-
- this.visBranchCollection.each(function(visBranch) {
- animate(visBranch);
- });
- this.visEdgeCollection.each(function(visEdge) {
- animate(visEdge);
- });
- _.each(this.visNodeMap, function(visNode) {
- animate(visNode);
- });
-};
-
-/***************************************
- == BEGIN Tree Calculation Parts ==
- _ __ __ _
- \\/ / \ \//_
- \ \ / __| __
- \ \___/ /_____/ /
- | _______ \
- \ ( ) / \_\
- \ /
- | |
- | |
- ____+-_=+-^ ^+-=_=__________
-
-^^ I drew that :D
-
- **************************************/
-
-GitVisuals.prototype.genSnapshot = function() {
- this.fullCalc();
-
- var snapshot = {};
- _.each(this.visNodeMap, function(visNode) {
- snapshot[visNode.get('id')] = visNode.getAttributes();
- }, this);
-
- this.visBranchCollection.each(function(visBranch) {
- snapshot[visBranch.getID()] = visBranch.getAttributes();
- }, this);
-
- this.visEdgeCollection.each(function(visEdge) {
- snapshot[visEdge.getID()] = visEdge.getAttributes();
- }, this);
-
- return snapshot;
-};
-
-GitVisuals.prototype.refreshTree = function(speed) {
- if (!this.gitReady) {
- return;
- }
-
- // this method can only be called after graphics are rendered
- this.fullCalc();
-
- this.animateAll(speed);
-};
-
-GitVisuals.prototype.refreshTreeHarsh = function() {
- this.fullCalc();
-
- this.animateAll(0);
-};
-
-GitVisuals.prototype.animateAll = function(speed) {
- this.zIndexReflow();
-
- this.animateEdges(speed);
- this.animateNodePositions(speed);
- this.animateRefs(speed);
-};
-
-GitVisuals.prototype.fullCalc = function() {
- this.calcTreeCoords();
- this.calcGraphicsCoords();
-};
-
-GitVisuals.prototype.calcTreeCoords = function() {
- // this method can only contain things that dont rely on graphics
- if (!this.rootCommit) {
- throw new Error('grr, no root commit!');
- }
-
- this.calcUpstreamSets();
- this.calcBranchStacks();
-
- this.calcDepth();
- this.calcWidth();
-};
-
-GitVisuals.prototype.calcGraphicsCoords = function() {
- this.visBranchCollection.each(function(visBranch) {
- visBranch.updateName();
- });
-};
-
-GitVisuals.prototype.calcUpstreamSets = function() {
- this.upstreamBranchSet = this.gitEngine.getUpstreamBranchSet();
- this.upstreamHeadSet = this.gitEngine.getUpstreamHeadSet();
-};
-
-GitVisuals.prototype.getCommitUpstreamBranches = function(commit) {
- return this.branchStackMap[commit.get('id')];
-};
-
-GitVisuals.prototype.getBlendedHuesForCommit = function(commit) {
- var branches = this.upstreamBranchSet[commit.get('id')];
- if (!branches) {
- throw new Error('that commit doesnt have upstream branches!');
- }
-
- return this.blendHuesFromBranchStack(branches);
-};
-
-GitVisuals.prototype.blendHuesFromBranchStack = function(branchStackArray) {
- var hueStrings = [];
- _.each(branchStackArray, function(branchWrapper) {
- var fill = branchWrapper.obj.get('visBranch').get('fill');
-
- if (fill.slice(0,3) !== 'hsb') {
- // crap! convert
- var color = Raphael.color(fill);
- fill = 'hsb(' + String(color.h) + ',' + String(color.l);
- fill = fill + ',' + String(color.s) + ')';
- }
-
- hueStrings.push(fill);
- });
-
- return blendHueStrings(hueStrings);
-};
-
-GitVisuals.prototype.getCommitUpstreamStatus = function(commit) {
- if (!this.upstreamBranchSet) {
- throw new Error("Can't calculate this yet!");
- }
-
- var id = commit.get('id');
- var branch = this.upstreamBranchSet;
- var head = this.upstreamHeadSet;
-
- if (branch[id]) {
- return 'branch';
- } else if (head[id]) {
- return 'head';
- } else {
- return 'none';
- }
-};
-
-GitVisuals.prototype.calcBranchStacks = function() {
- var branches = this.gitEngine.getBranches();
- var map = {};
- _.each(branches, function(branch) {
- var thisId = branch.target.get('id');
-
- map[thisId] = map[thisId] || [];
- map[thisId].push(branch);
- map[thisId].sort(function(a, b) {
- var aId = a.obj.get('id');
- var bId = b.obj.get('id');
- if (aId == 'master' || bId == 'master') {
- return aId == 'master' ? -1 : 1;
- }
- return aId.localeCompare(bId);
- });
- });
- this.branchStackMap = map;
-};
-
-GitVisuals.prototype.calcWidth = function() {
- this.maxWidthRecursive(this.rootCommit);
-
- this.assignBoundsRecursive(this.rootCommit, 0, 1);
-};
-
-GitVisuals.prototype.maxWidthRecursive = function(commit) {
- var childrenTotalWidth = 0;
- _.each(commit.get('children'), function(child) {
- // only include this if we are the "main" parent of
- // this child
- if (child.isMainParent(commit)) {
- var childWidth = this.maxWidthRecursive(child);
- childrenTotalWidth += childWidth;
- }
- }, this);
-
- var maxWidth = Math.max(1, childrenTotalWidth);
- commit.get('visNode').set('maxWidth', maxWidth);
- return maxWidth;
-};
-
-GitVisuals.prototype.assignBoundsRecursive = function(commit, min, max) {
- // I always center myself within my bounds
- var myWidthPos = (min + max) / 2.0;
- commit.get('visNode').get('pos').x = myWidthPos;
-
- if (commit.get('children').length == 0) {
- return;
- }
-
- // i have a certain length to divide up
- var myLength = max - min;
- // I will divide up that length based on my children's max width in a
- // basic box-flex model
- var totalFlex = 0;
- var children = commit.get('children');
- _.each(children, function(child) {
- if (child.isMainParent(commit)) {
- totalFlex += child.get('visNode').getMaxWidthScaled();
- }
- }, this);
-
- var prevBound = min;
-
- // now go through and do everything
- // TODO: order so the max width children are in the middle!!
- _.each(children, function(child) {
- if (!child.isMainParent(commit)) {
- return;
- }
-
- var flex = child.get('visNode').getMaxWidthScaled();
- var portion = (flex / totalFlex) * myLength;
- var childMin = prevBound;
- var childMax = childMin + portion;
- this.assignBoundsRecursive(child, childMin, childMax);
- prevBound = childMax;
- }, this);
-};
-
-GitVisuals.prototype.calcDepth = function() {
- var maxDepth = this.calcDepthRecursive(this.rootCommit, 0);
- if (maxDepth > 15) {
- // issue warning
- console.warn('graphics are degrading from too many layers');
- }
-
- var depthIncrement = this.getDepthIncrement(maxDepth);
- _.each(this.visNodeMap, function(visNode) {
- visNode.setDepthBasedOn(depthIncrement);
- }, this);
-};
-
-/***************************************
- == END Tree Calculation ==
- _ __ __ _
- \\/ / \ \//_
- \ \ / __| __
- \ \___/ /_____/ /
- | _______ \
- \ ( ) / \_\
- \ /
- | |
- | |
- ____+-_=+-^ ^+-=_=__________
-
-^^ I drew that :D
-
- **************************************/
-
-GitVisuals.prototype.animateNodePositions = function(speed) {
- _.each(this.visNodeMap, function(visNode) {
- visNode.animateUpdatedPosition(speed);
- }, this);
-};
-
-GitVisuals.prototype.turnOnPaper = function() {
- this.gitReady = false;
-};
-
-// does making an accessor method make it any less hacky? that is the true question
-GitVisuals.prototype.turnOffPaper = function() {
- this.gitReady = true;
-};
-
-GitVisuals.prototype.addBranchFromEvent = function(branch, collection, index) {
- var action = _.bind(function() {
- this.addBranch(branch);
- }, this);
-
- if (!this.gitEngine || !this.gitReady) {
- this.defer(action);
- } else {
- action();
- }
-};
-
-GitVisuals.prototype.addBranch = function(branch) {
- var visBranch = new VisBranch({
- branch: branch,
- gitVisuals: this,
- gitEngine: this.gitEngine
- });
-
- this.visBranchCollection.add(visBranch);
- if (this.gitReady) {
- visBranch.genGraphics(this.paper);
- }
-};
-
-GitVisuals.prototype.removeVisBranch = function(visBranch) {
- this.visBranchCollection.remove(visBranch);
-};
-
-GitVisuals.prototype.removeVisNode = function(visNode) {
- this.visNodeMap[visNode.getID()] = undefined;
-};
-
-GitVisuals.prototype.removeVisEdge = function(visEdge) {
- this.visEdgeCollection.remove(visEdge);
-};
-
-GitVisuals.prototype.animateRefs = function(speed) {
- this.visBranchCollection.each(function(visBranch) {
- visBranch.animateUpdatedPos(speed);
- }, this);
-};
-
-GitVisuals.prototype.animateEdges = function(speed) {
- this.visEdgeCollection.each(function(edge) {
- edge.animateUpdatedPath(speed);
- }, this);
-};
-
-GitVisuals.prototype.getDepthIncrement = function(maxDepth) {
- // assume there are at least 7 layers until later
- maxDepth = Math.max(maxDepth, 7);
- var increment = 1.0 / maxDepth;
- return increment;
-};
-
-GitVisuals.prototype.calcDepthRecursive = function(commit, depth) {
- commit.get('visNode').setDepth(depth);
-
- var children = commit.get('children');
- var maxDepth = depth;
- _.each(children, function(child) {
- var d = this.calcDepthRecursive(child, depth + 1);
- maxDepth = Math.max(d, maxDepth);
- }, this);
-
- return maxDepth;
-};
-
-GitVisuals.prototype.canvasResize = function(width, height) {
- // refresh when we are ready
- if (GLOBAL.isAnimating) {
- events.trigger('processCommandFromEvent', 'refresh');
- } else {
- this.refreshTree();
- }
-};
-
-GitVisuals.prototype.addCommit = function(commit) {
- // TODO
-};
-
-GitVisuals.prototype.addNode = function(id, commit) {
- this.commitMap[id] = commit;
- if (commit.get('rootCommit')) {
- this.rootCommit = commit;
- }
-
- var visNode = new VisNode({
- id: id,
- commit: commit,
- gitVisuals: this,
- gitEngine: this.gitEngine
- });
- this.visNodeMap[id] = visNode;
-
- if (this.gitReady) {
- visNode.genGraphics(this.paper);
- }
- return visNode;
-};
-
-GitVisuals.prototype.addEdge = function(idTail, idHead) {
- var visNodeTail = this.visNodeMap[idTail];
- var visNodeHead = this.visNodeMap[idHead];
-
- if (!visNodeTail || !visNodeHead) {
- throw new Error('one of the ids in (' + idTail +
- ', ' + idHead + ') does not exist');
- }
-
- var edge = new VisEdge({
- tail: visNodeTail,
- head: visNodeHead,
- gitVisuals: this,
- gitEngine: this.gitEngine
- });
- this.visEdgeCollection.add(edge);
-
- if (this.gitReady) {
- edge.genGraphics(this.paper);
- }
-};
-
-GitVisuals.prototype.collectionChanged = function() {
- // TODO ?
-};
-
-GitVisuals.prototype.zIndexReflow = function() {
- this.visNodesFront();
- this.visBranchesFront();
-};
-
-GitVisuals.prototype.visNodesFront = function() {
- _.each(this.visNodeMap, function(visNode) {
- visNode.toFront();
- });
-};
-
-GitVisuals.prototype.visBranchesFront = function() {
- this.visBranchCollection.each(function(vBranch) {
- vBranch.nonTextToFront();
- });
-
- this.visBranchCollection.each(function(vBranch) {
- vBranch.textToFront();
- });
-};
-
-GitVisuals.prototype.drawTreeFromReload = function() {
- this.gitReady = true;
- // gen all the graphics we need
- this.deferFlush();
-
- this.calcTreeCoords();
-};
-
-GitVisuals.prototype.drawTreeFirstTime = function() {
- this.gitReady = true;
- this.calcTreeCoords();
-
- _.each(this.visNodeMap, function(visNode) {
- visNode.genGraphics(this.paper);
- }, this);
-
- this.visEdgeCollection.each(function(edge) {
- edge.genGraphics(this.paper);
- }, this);
-
- this.visBranchCollection.each(function(visBranch) {
- visBranch.genGraphics(this.paper);
- }, this);
-
- this.zIndexReflow();
-};
-
-
-/************************
- * Random util functions, some from liquidGraph
- ***********************/
-function blendHueStrings(hueStrings) {
- // assumes a sat of 0.7 and brightness of 1
-
- var x = 0;
- var y = 0;
- var totalSat = 0;
- var totalBright = 0;
- var length = hueStrings.length;
-
- _.each(hueStrings, function(hueString) {
- var exploded = hueString.split('(')[1];
- exploded = exploded.split(')')[0];
- exploded = exploded.split(',');
-
- totalSat += parseFloat(exploded[1]);
- totalBright += parseFloat(exploded[2]);
- var hue = parseFloat(exploded[0]);
-
- var angle = hue * Math.PI * 2;
- x += Math.cos(angle);
- y += Math.sin(angle);
- });
-
- x = x / length;
- y = y / length;
- totalSat = totalSat / length;
- totalBright = totalBright / length;
-
- var hue = Math.atan2(y, x) / (Math.PI * 2); // could fail on 0's
- if (hue < 0) {
- hue = hue + 1;
- }
- return 'hsb(' + String(hue) + ',' + String(totalSat) + ',' + String(totalBright) + ')';
-}
-
-function randomHueString() {
- var hue = Math.random();
- var str = 'hsb(' + String(hue) + ',0.7,1)';
- return str;
-};
-
-exports.Visualization = Visualization;
-
});
require.define("/main.js",function(require,module,exports,__dirname,__filename,process,global){var AnimationFactory = require('./animationFactory').AnimationFactory;
-var CommandCollection = require('./collections').CommandCollection;
-var CommandBuffer = require('./collections').CommandBuffer;
-var CommandPromptView = require('./commandViews').CommandPromptView;
-var CommandLineHistoryView = require('./commandViews').CommandLineHistoryView;
-var Visualization = require('./visuals').Visualization;
/**
* Globals
@@ -3525,7 +716,7 @@ require.define("/animationFactory.js",function(require,module,exports,__dirname,
*/
// essentially a static class
-var AnimationFactory = function() {
+function AnimationFactory() {
}
@@ -3768,2815 +959,7 @@ AnimationFactory.prototype.genFromToSnapshotAnimation = function(
exports.AnimationFactory = AnimationFactory;
+
});
require("/animationFactory.js");
-
-require.define("/git.js",function(require,module,exports,__dirname,__filename,process,global){var animationFactory = new require('./animationFactory').AnimationFactory();
-
-// backbone or something uses _.uniqueId, so we make our own here
-var uniqueId = (function() {
- var n = 0;
- return function(prepend) {
- return prepend? prepend + n++ : n++;
- };
-})();
-
-function GitEngine(options) {
- this.rootCommit = null;
- this.refs = {};
- this.HEAD = null;
-
- this.branchCollection = options.branches;
- this.commitCollection = options.collection;
- this.gitVisuals = options.gitVisuals;
-
- // global variable to keep track of the options given
- // along with the command call.
- this.commandOptions = {};
- this.generalArgs = [];
-
- events.on('processCommand', _.bind(this.dispatch, this));
-}
-
-GitEngine.prototype.defaultInit = function() {
- var defaultTree = JSON.parse(unescape("%7B%22branches%22%3A%7B%22master%22%3A%7B%22target%22%3A%22C1%22%2C%22id%22%3A%22master%22%2C%22type%22%3A%22branch%22%7D%7D%2C%22commits%22%3A%7B%22C0%22%3A%7B%22type%22%3A%22commit%22%2C%22parents%22%3A%5B%5D%2C%22author%22%3A%22Peter%20Cottle%22%2C%22createTime%22%3A%22Mon%20Nov%2005%202012%2000%3A56%3A47%20GMT-0800%20%28PST%29%22%2C%22commitMessage%22%3A%22Quick%20Commit.%20Go%20Bears%21%22%2C%22id%22%3A%22C0%22%2C%22rootCommit%22%3Atrue%7D%2C%22C1%22%3A%7B%22type%22%3A%22commit%22%2C%22parents%22%3A%5B%22C0%22%5D%2C%22author%22%3A%22Peter%20Cottle%22%2C%22createTime%22%3A%22Mon%20Nov%2005%202012%2000%3A56%3A47%20GMT-0800%20%28PST%29%22%2C%22commitMessage%22%3A%22Quick%20Commit.%20Go%20Bears%21%22%2C%22id%22%3A%22C1%22%7D%7D%2C%22HEAD%22%3A%7B%22id%22%3A%22HEAD%22%2C%22target%22%3A%22master%22%2C%22type%22%3A%22general%20ref%22%7D%7D"));
- this.loadTree(defaultTree);
-};
-
-GitEngine.prototype.init = function() {
- // make an initial commit and a master branch
- this.rootCommit = this.makeCommit(null, null, {rootCommit: true});
- this.commitCollection.add(this.rootCommit);
-
- var master = this.makeBranch('master', this.rootCommit);
- this.HEAD = new Ref({
- id: 'HEAD',
- target: master
- });
- this.refs[this.HEAD.get('id')] = this.HEAD;
-
- // commit once to get things going
- this.commit();
-};
-
-GitEngine.prototype.exportTree = function() {
- // need to export all commits, their connectivity / messages, branches, and state of head.
- // this would be simple if didn't have circular structures.... :P
- // thus, we need to loop through and "flatten" our graph of objects referencing one another
- var totalExport = {
- branches: {},
- commits: {},
- HEAD: null
- };
-
- _.each(this.branchCollection.toJSON(), function(branch) {
- branch.target = branch.target.get('id');
- branch.visBranch = undefined;
-
- totalExport.branches[branch.id] = branch;
- });
-
- _.each(this.commitCollection.toJSON(), function(commit) {
- // clear out the fields that reference objects and create circular structure
- _.each(Commit.prototype.constants.circularFields, function(field) {
- commit[field] = undefined;
- }, this);
-
- // convert parents
- var parents = [];
- _.each(commit.parents, function(par) {
- parents.push(par.get('id'));
- });
- commit.parents = parents;
-
- totalExport.commits[commit.id] = commit;
- }, this);
-
- var HEAD = this.HEAD.toJSON();
- HEAD.visBranch = undefined;
- HEAD.lastTarget = HEAD.lastLastTarget = HEAD.visBranch = undefined;
- HEAD.target = HEAD.target.get('id');
- totalExport.HEAD = HEAD;
-
- return totalExport;
-};
-
-GitEngine.prototype.printTree = function() {
- var str = escape(JSON.stringify(this.exportTree()));
- return str;
-};
-
-GitEngine.prototype.printAndCopyTree = function() {
- window.prompt('Copy the tree string below', this.printTree());
-};
-
-GitEngine.prototype.loadTree = function(tree) {
- // deep copy in case we use it a bunch
- tree = $.extend(true, {}, tree);
-
- // first clear everything
- this.removeAll();
-
- this.instantiateFromTree(tree);
-
- this.reloadGraphics();
-};
-
-GitEngine.prototype.loadTreeFromString = function(treeString) {
- this.loadTree(JSON.parse(unescape(treeString)));
-};
-
-GitEngine.prototype.instantiateFromTree = function(tree) {
- // now we do the loading part
- var createdSoFar = {};
-
- _.each(tree.commits, function(commitJSON) {
- var commit = this.getOrMakeRecursive(tree, createdSoFar, commitJSON.id);
- this.commitCollection.add(commit);
- }, this);
-
- _.each(tree.branches, function(branchJSON) {
- var branch = this.getOrMakeRecursive(tree, createdSoFar, branchJSON.id);
-
- this.branchCollection.add(branch, {silent: true});
- }, this);
-
- var HEAD = this.getOrMakeRecursive(tree, createdSoFar, tree.HEAD.id);
- this.HEAD = HEAD;
-
- this.rootCommit = createdSoFar['C0'];
- if (!this.rootCommit) {
- throw new Error('Need root commit of C0 for calculations');
- }
- this.refs = createdSoFar;
-
- this.branchCollection.each(function(branch) {
- this.gitVisuals.addBranch(branch);
- }, this);
-};
-
-GitEngine.prototype.reloadGraphics = function() {
- // get the root commit, no better way to do it
- var rootCommit = null;
- this.commitCollection.each(function(commit) {
- if (commit.get('id') == 'C0') {
- rootCommit = commit;
- }
- });
- this.gitVisuals.rootCommit = rootCommit;
-
- // this just basically makes the HEAD branch. the head branch really should have been
- // a member of a collection and not this annoying edge case stuff... one day
- this.gitVisuals.initHeadBranch();
-
- // when the paper is ready
- this.gitVisuals.drawTreeFromReload();
-
- this.gitVisuals.refreshTreeHarsh();
-};
-
-GitEngine.prototype.getOrMakeRecursive = function(tree, createdSoFar, objID) {
- if (createdSoFar[objID]) {
- // base case
- return createdSoFar[objID];
- }
-
- var getType = function(tree, id) {
- if (tree.commits[id]) {
- return 'commit';
- } else if (tree.branches[id]) {
- return 'branch';
- } else if (id == 'HEAD') {
- return 'HEAD';
- }
- throw new Error("bad type for " + id);
- };
-
- // figure out what type
- var type = getType(tree, objID);
-
- if (type == 'HEAD') {
- var headJSON = tree.HEAD;
- var HEAD = new Ref(_.extend(
- tree.HEAD,
- {
- target: this.getOrMakeRecursive(tree, createdSoFar, headJSON.target)
- }
- ));
- createdSoFar[objID] = HEAD;
- return HEAD;
- }
-
- if (type == 'branch') {
- var branchJSON = tree.branches[objID];
-
- var branch = new Branch(_.extend(
- tree.branches[objID],
- {
- target: this.getOrMakeRecursive(tree, createdSoFar, branchJSON.target)
- }
- ));
- createdSoFar[objID] = branch;
- return branch;
- }
-
- if (type == 'commit') {
- // for commits, we need to grab all the parents
- var commitJSON = tree.commits[objID];
-
- var parentObjs = [];
- _.each(commitJSON.parents, function(parentID) {
- parentObjs.push(this.getOrMakeRecursive(tree, createdSoFar, parentID));
- }, this);
-
- var commit = new Commit(_.extend(
- commitJSON,
- {
- parents: parentObjs,
- gitVisuals: this.gitVisuals
- }
- ));
- createdSoFar[objID] = commit;
- return commit;
- }
-
- throw new Error('ruh rho!! unsupported tyep for ' + objID);
-};
-
-GitEngine.prototype.removeAll = function() {
- this.branchCollection.reset();
- this.commitCollection.reset();
- this.refs = {};
- this.HEAD = null;
- this.rootCommit = null;
-
- this.gitVisuals.resetAll();
-};
-
-GitEngine.prototype.getDetachedHead = function() {
- // detached head is if HEAD points to a commit instead of a branch...
- var target = this.HEAD.get('target');
- var targetType = target.get('type');
- return targetType !== 'branch';
-};
-
-GitEngine.prototype.validateBranchName = function(name) {
- name = name.replace(/\s/g, '');
- if (!/^[a-zA-Z0-9]+$/.test(name)) {
- throw new GitError({
- msg: 'woah bad branch name!! This is not ok: ' + name
- });
- }
- if (/[hH][eE][aA][dD]/.test(name)) {
- throw new GitError({
- msg: 'branch name of "head" is ambiguous, dont name it that'
- });
- }
- if (name.length > 9) {
- name = name.slice(0, 9);
- this.command.addWarning(
- 'Sorry, we need to keep branch names short for the visuals. Your branch ' +
- 'name was truncated to 9 characters, resulting in ' + name
- );
- }
- return name;
-};
-
-GitEngine.prototype.makeBranch = function(id, target) {
- id = this.validateBranchName(id);
- if (this.refs[id]) {
- throw new GitError({
- msg: 'that branch id either matches a commit hash or already exists!'
- });
- }
-
- var branch = new Branch({
- target: target,
- id: id
- });
- this.branchCollection.add(branch);
- this.refs[branch.get('id')] = branch;
- return branch;
-};
-
-GitEngine.prototype.getHead = function() {
- return _.clone(this.HEAD);
-};
-
-GitEngine.prototype.getBranches = function() {
- var toReturn = [];
- this.branchCollection.each(function(branch) {
- toReturn.push({
- id: branch.get('id'),
- selected: this.HEAD.get('target') === branch,
- target: branch.get('target'),
- obj: branch
- });
- }, this);
- return toReturn;
-};
-
-GitEngine.prototype.printBranchesWithout = function(without) {
- var commitToBranches = this.getUpstreamBranchSet();
- var commitID = this.getCommitFromRef(without).get('id');
-
- var toPrint = [];
- _.each(commitToBranches[commitID], function(branchJSON) {
- branchJSON.selected = this.HEAD.get('target').get('id') == branchJSON.id;
- toPrint.push(branchJSON);
- }, this);
- this.printBranches(toPrint);
-};
-
-GitEngine.prototype.printBranches = function(branches) {
- var result = '';
- _.each(branches, function(branch) {
- result += (branch.selected ? '* ' : '') + branch.id + '\n';
- });
- throw new CommandResult({
- msg: result
- });
-};
-
-GitEngine.prototype.makeCommit = function(parents, id, options) {
- // ok we need to actually manually create commit IDs now because
- // people like nikita (thanks for finding this!) could
- // make branches named C2 before creating the commit C2
- if (!id) {
- id = uniqueId('C');
- while (this.refs[id]) {
- id = uniqueId('C');
- }
- }
-
- var commit = new Commit(_.extend({
- parents: parents,
- id: id,
- gitVisuals: this.gitVisuals
- },
- options || {}
- ));
-
- this.refs[commit.get('id')] = commit;
- this.commitCollection.add(commit);
- return commit;
-};
-
-GitEngine.prototype.acceptNoGeneralArgs = function() {
- if (this.generalArgs.length) {
- throw new GitError({
- msg: "That command accepts no general arguments"
- });
- }
-};
-
-GitEngine.prototype.validateArgBounds = function(args, lower, upper, option) {
- // this is a little utility class to help arg validation that happens over and over again
- var what = (option === undefined) ?
- 'git ' + this.command.get('method') :
- this.command.get('method') + ' ' + option + ' ';
- what = 'with ' + what;
-
- if (args.length < lower) {
- throw new GitError({
- msg: 'I expect at least ' + String(lower) + ' argument(s) ' + what
- });
- }
- if (args.length > upper) {
- throw new GitError({
- msg: 'I expect at most ' + String(upper) + ' argument(s) ' + what
- });
- }
-};
-
-GitEngine.prototype.oneArgImpliedHead = function(args, option) {
- // for log, show, etc
- this.validateArgBounds(args, 0, 1, option);
- if (args.length == 0) {
- args.push('HEAD');
- }
-};
-
-GitEngine.prototype.twoArgsImpliedHead = function(args, option) {
- // our args we expect to be between 1 and 2
- this.validateArgBounds(args, 1, 2, option);
- // and if it's one, add a HEAD to the back
- if (args.length == 1) {
- args.push('HEAD');
- }
-};
-
-GitEngine.prototype.revertStarter = function() {
- this.validateArgBounds(this.generalArgs, 1, NaN);
-
- var response = this.revert(this.generalArgs);
-
- if (response) {
- animationFactory.rebaseAnimation(this.animationQueue, response, this, this.gitVisuals);
- }
-};
-
-GitEngine.prototype.revert = function(whichCommits) {
- // for each commit, we want to revert it
- var toRebase = [];
- _.each(whichCommits, function(stringRef) {
- toRebase.push(this.getCommitFromRef(stringRef));
- }, this);
-
- // we animate reverts now!! we use the rebase animation though so that's
- // why the terminology is like it is
- var animationResponse = {};
- animationResponse.destinationBranch = this.resolveID(toRebase[0]);
- animationResponse.toRebaseArray = toRebase.slice(0);
- animationResponse.rebaseSteps = [];
-
- beforeSnapshot = this.gitVisuals.genSnapshot();
- var afterSnapshot;
-
- // now make a bunch of commits on top of where we are
- var base = this.getCommitFromRef('HEAD');
- _.each(toRebase, function(oldCommit) {
- var newId = this.rebaseAltID(oldCommit.get('id'));
-
- var newCommit = this.makeCommit([base], newId, {
- commitMessage: 'Reverting ' + this.resolveName(oldCommit) +
- ': "' + oldCommit.get('commitMessage') + '"'
- });
-
- base = newCommit;
-
- // animation stuff
- afterSnapshot = this.gitVisuals.genSnapshot();
- animationResponse.rebaseSteps.push({
- oldCommit: oldCommit,
- newCommit: newCommit,
- beforeSnapshot: beforeSnapshot,
- afterSnapshot: afterSnapshot
- });
- beforeSnapshot = afterSnapshot;
- }, this);
- // done! update our location
- this.setTargetLocation('HEAD', base);
-
- // animation
- return animationResponse;
-};
-
-GitEngine.prototype.resetStarter = function() {
- if (this.commandOptions['--soft']) {
- throw new GitError({
- msg: "You can't use --soft because there is no concept of stashing" +
- " changes or staging files, so you will lose your progress." +
- " Try using interactive rebasing (or just rebasing) to move commits."
- });
- }
- if (this.commandOptions['--hard']) {
- this.command.addWarning(
- 'Nice! You are using --hard. The default behavior is a hard reset in ' +
- "this demo, so don't worry about specifying the option explicity"
- );
- // dont absorb the arg off of --hard
- this.generalArgs = this.generalArgs.concat(this.commandOptions['--hard']);
- }
-
- this.validateArgBounds(this.generalArgs, 1, 1);
-
- if (this.getDetachedHead()) {
- throw new GitError({
- msg: "Cant reset in detached head! Use checkout if you want to move"
- });
- }
-
- this.reset(this.generalArgs[0]);
-};
-
-GitEngine.prototype.reset = function(target) {
- this.setTargetLocation('HEAD', this.getCommitFromRef(target));
-};
-
-GitEngine.prototype.cherrypickStarter = function() {
- this.validateArgBounds(this.generalArgs, 1, 1);
- var newCommit = this.cherrypick(this.generalArgs[0]);
-
- animationFactory.genCommitBirthAnimation(this.animationQueue, newCommit, this.gitVisuals);
-};
-
-GitEngine.prototype.cherrypick = function(ref) {
- var commit = this.getCommitFromRef(ref);
- // check if we already have that
- var set = this.getUpstreamSet('HEAD');
- if (set[commit.get('id')]) {
- throw new GitError({
- msg: "We already have that commit in our changes history! You can't cherry-pick it " +
- "if it shows up in git log."
- });
- }
-
- // alter the ID slightly
- var id = this.rebaseAltID(commit.get('id'));
-
- // now commit with that id onto HEAD
- var newCommit = this.makeCommit([this.getCommitFromRef('HEAD')], id);
- this.setTargetLocation(this.HEAD, newCommit);
- return newCommit;
-};
-
-GitEngine.prototype.commitStarter = function() {
- this.acceptNoGeneralArgs();
- if (this.commandOptions['-am'] && (
- this.commandOptions['-a'] || this.commandOptions['-m'])) {
- throw new GitError({
- msg: "You can't have -am with another -m or -a!"
- });
- }
-
- var msg = null;
- if (this.commandOptions['-a']) {
- this.command.addWarning('No need to add files in this demo');
- }
-
- if (this.commandOptions['-am']) {
- var args = this.commandOptions['-am'];
- this.validateArgBounds(args, 1, 1, '-am');
-
- this.command.addWarning("Don't worry about adding files in this demo. I'll take " +
- "down your commit message anyways, but you can commit without a message " +
- "in this demo as well");
- msg = args[0];
- }
-
- if (this.commandOptions['-m']) {
- var args = this.commandOptions['-m'];
- this.validateArgBounds(args, 1, 1, '-m');
- msg = args[0];
- }
-
- var newCommit = this.commit();
- if (msg) {
- msg = msg
- .replace(/"/g, '"')
- .replace(/^"/g, '')
- .replace(/"$/g, '');
-
- newCommit.set('commitMessage', msg);
- }
- animationFactory.genCommitBirthAnimation(this.animationQueue, newCommit, this.gitVisuals);
-};
-
-GitEngine.prototype.commit = function() {
- var targetCommit = this.getCommitFromRef(this.HEAD);
- var id = undefined;
-
- // if we want to ammend, go one above
- if (this.commandOptions['--amend']) {
- targetCommit = this.resolveID('HEAD~1');
- id = this.rebaseAltID(this.getCommitFromRef('HEAD').get('id'));
- }
-
- var newCommit = this.makeCommit([targetCommit], id);
- if (this.getDetachedHead()) {
- this.command.addWarning('Warning!! Detached HEAD state');
- }
-
- this.setTargetLocation(this.HEAD, newCommit);
- return newCommit;
-};
-
-GitEngine.prototype.resolveName = function(someRef) {
- // first get the obj
- var obj = this.resolveID(someRef);
- if (obj.get('type') == 'commit') {
- return 'commit ' + obj.get('id');
- }
- if (obj.get('type') == 'branch') {
- return 'branch "' + obj.get('id') + '"';
- }
- // we are dealing with HEAD
- return this.resolveName(obj.get('target'));
-};
-
-GitEngine.prototype.resolveID = function(idOrTarget) {
- if (idOrTarget === null || idOrTarget === undefined) {
- throw new Error('Dont call this with null / undefined');
- }
-
- if (typeof idOrTarget !== 'string') {
- return idOrTarget;
- }
- return this.resolveStringRef(idOrTarget);
-};
-
-GitEngine.prototype.resolveStringRef = function(ref) {
- if (this.refs[ref]) {
- return this.refs[ref];
- }
-
- // may be something like HEAD~2 or master^^
- var relativeRefs = [
- [/^([a-zA-Z0-9]+)~(\d+)\s*$/, function(matches) {
- return parseInt(matches[2]);
- }],
- [/^([a-zA-Z0-9]+)(\^+)\s*$/, function(matches) {
- return matches[2].length;
- }]
- ];
-
- var startRef = null;
- var numBack = null;
- _.each(relativeRefs, function(config) {
- var regex = config[0];
- var parse = config[1];
- if (regex.test(ref)) {
- var matches = regex.exec(ref);
- numBack = parse(matches);
- startRef = matches[1];
- }
- }, this);
-
- if (!startRef) {
- throw new GitError({
- msg: 'unknown ref ' + ref
- });
- }
- if (!this.refs[startRef]) {
- throw new GitError({
- msg: 'the ref ' + startRef +' does not exist.'
- });
- }
- var commit = this.getCommitFromRef(startRef);
-
- return this.numBackFrom(commit, numBack);
-};
-
-GitEngine.prototype.getCommitFromRef = function(ref) {
- var start = this.resolveID(ref);
-
- // works for both HEAD and just a single layer. aka branch
- while (start.get('type') !== 'commit') {
- start = start.get('target');
- }
- return start;
-};
-
-GitEngine.prototype.getType = function(ref) {
- return this.resolveID(ref).get('type');
-};
-
-GitEngine.prototype.setTargetLocation = function(ref, target) {
- if (this.getType(ref) == 'commit') {
- // nothing to do
- return;
- }
-
- // sets whatever ref is (branch, HEAD, etc) to a target. so if
- // you pass in HEAD, and HEAD is pointing to a branch, it will update
- // the branch to that commit, not the HEAD
- var ref = this.getOneBeforeCommit(ref);
- ref.set('target', target);
-};
-
-GitEngine.prototype.getUpstreamBranchSet = function() {
- // this is expensive!! so only call once in a while
- var commitToSet = {};
-
- var inArray = function(arr, id) {
- var found = false;
- _.each(arr, function(wrapper) {
- if (wrapper.id == id) {
- found = true;
- }
- });
-
- return found;
- };
-
- var bfsSearch = function(commit) {
- var set = [];
- var pQueue = [commit];
- while (pQueue.length) {
- var popped = pQueue.pop();
- set.push(popped.get('id'));
-
- if (popped.get('parents') && popped.get('parents').length) {
- pQueue = pQueue.concat(popped.get('parents'));
- }
- }
- return set;
- };
-
- this.branchCollection.each(function(branch) {
- var set = bfsSearch(branch.get('target'));
- _.each(set, function(id) {
- commitToSet[id] = commitToSet[id] || [];
-
- // only add it if it's not there, so hue blending is ok
- if (!inArray(commitToSet[id], branch.get('id'))) {
- commitToSet[id].push({
- obj: branch,
- id: branch.get('id')
- });
- }
- });
- });
-
- return commitToSet;
-};
-
-GitEngine.prototype.getUpstreamHeadSet = function() {
- var set = this.getUpstreamSet('HEAD');
- var including = this.getCommitFromRef('HEAD').get('id');
-
- set[including] = true;
- return set;
-};
-
-GitEngine.prototype.getOneBeforeCommit = function(ref) {
- // you can call this command on HEAD in detached, HEAD, or on a branch
- // and it will return the ref that is one above a commit. aka
- // it resolves HEAD to something that we can move the ref with
- var start = this.resolveID(ref);
- if (start === this.HEAD && !this.getDetachedHead()) {
- start = start.get('target');
- }
- return start;
-};
-
-GitEngine.prototype.numBackFrom = function(commit, numBack) {
- // going back '3' from a given ref is not trivial, for you might have
- // a bunch of merge commits and such. like this situation:
- //
- // * merge master into new
- // |\
- // | \* commit here
- // |* \ commit there
- // | |* commit here
- // \ /
- // | * root
- //
- //
- // hence we need to do a BFS search, with the commit date being the
- // value to sort off of (rather than just purely the level)
- if (numBack == 0) {
- return commit;
- }
-
- // we use a special sorting function here that
- // prefers the later commits over the earlier ones
- var sortQueue = _.bind(function(queue) {
- queue.sort(this.idSortFunc);
- queue.reverse();
- }, this);
-
- var pQueue = [].concat(commit.get('parents') || []);
- sortQueue(pQueue);
- numBack--;
-
- while (pQueue.length && numBack !== 0) {
- var popped = pQueue.shift(0);
- var parents = popped.get('parents');
-
- if (parents && parents.length) {
- pQueue = pQueue.concat(parents);
- }
-
- sortQueue(pQueue);
- numBack--;
- }
-
- if (numBack !== 0 || pQueue.length == 0) {
- throw new GitError({
- msg: "Sorry, I can't go that many commits back"
- });
- }
- return pQueue.shift(0);
-};
-
-GitEngine.prototype.scrapeBaseID = function(id) {
- var results = /^C(\d+)/.exec(id);
-
- if (!results) {
- throw new Error('regex failed on ' + id);
- }
-
- return 'C' + results[1];
-};
-
-GitEngine.prototype.rebaseAltID = function(id) {
- // this function alters an ID to add a quote to the end,
- // indicating that it was rebased. it also checks existence
- var regexMap = [
- [/^C(\d+)[']{0,2}$/, function(bits) {
- // this id can use another quote, so just add it
- return bits[0] + "'";
- }],
- [/^C(\d+)[']{3}$/, function(bits) {
- // here we switch from C''' to C'^4
- return bits[0].slice(0, -3) + "'^4";
- }],
- [/^C(\d+)['][^](\d+)$/, function(bits) {
- return 'C' + String(bits[1]) + "'^" + String(Number(bits[2]) + 1);
- }]
- ];
-
- for (var i = 0; i < regexMap.length; i++) {
- var regex = regexMap[i][0];
- var func = regexMap[i][1];
- var results = regex.exec(id);
- if (results) {
- var newId = func(results);
- // if this id exists, continue down the rabbit hole
- if (this.refs[newId]) {
- return this.rebaseAltID(newId);
- } else {
- return newId;
- }
- }
- }
- throw new Error('could not modify the id ' + id);
-};
-
-GitEngine.prototype.idSortFunc = function(cA, cB) {
- // commit IDs can come in many forms:
- // C4
- // C4' (from a rebase)
- // C4'' (from multiple rebases)
- // C4'^3 (from a BUNCH of rebases)
-
- var scale = 1000;
-
- var regexMap = [
- [/^C(\d+)$/, function(bits) {
- // return the 4 from C4
- return scale * bits[1];
- }],
- [/^C(\d+)([']+)$/, function(bits) {
- // return the 4 from C4, plus the length of the quotes
- return scale * bits[1] + bits[2].length;
- }],
- [/^C(\d+)['][^](\d+)$/, function(bits) {
- return scale * bits[1] + Number(bits[2]);
- }]
- ];
-
- var getNumToSort = function(id) {
- for (var i = 0; i < regexMap.length; i++) {
- var regex = regexMap[i][0];
- var func = regexMap[i][1];
- var results = regex.exec(id);
- if (results) {
- return func(results);
- }
- }
- throw new Error('Could not parse commit ID ' + id);
- }
-
- return getNumToSort(cA.get('id')) - getNumToSort(cB.get('id'));
-};
-
-GitEngine.prototype.rebaseInteractiveStarter = function() {
- var args = this.commandOptions['-i'];
- this.twoArgsImpliedHead(args, ' -i');
-
- this.rebaseInteractive(args[0], args[1]);
-};
-
-GitEngine.prototype.rebaseStarter = function() {
- if (this.commandOptions['-i']) {
- this.rebaseInteractiveStarter();
- return;
- }
-
- this.twoArgsImpliedHead(this.generalArgs);
-
- var response = this.rebase(this.generalArgs[0], this.generalArgs[1]);
-
- if (response === undefined) {
- // was a fastforward or already up to date. returning now
- // will trigger the refresh animation by not adding anything to
- // the animation queue
- return;
- }
-
- animationFactory.rebaseAnimation(this.animationQueue, response, this, this.gitVisuals);
-};
-
-GitEngine.prototype.rebase = function(targetSource, currentLocation) {
- // first some conditions
- if (this.isUpstreamOf(targetSource, currentLocation)) {
- this.command.setResult('Branch already up-to-date');
-
- // git for some reason always checks out the branch you are rebasing,
- // no matter the result of the rebase
- this.checkout(currentLocation);
-
- // returning instead of throwing makes a tree refresh
- return;
- }
-
- if (this.isUpstreamOf(currentLocation, targetSource)) {
- // just set the target of this current location to the source
- this.setTargetLocation(currentLocation, this.getCommitFromRef(targetSource));
- // we need the refresh tree animation to happen, so set the result directly
- // instead of throwing
- this.command.setResult('Fast-forwarding...');
-
- this.checkout(currentLocation);
- return;
- }
-
- // now the part of actually rebasing.
- // We need to get the downstream set of targetSource first.
- // then we BFS from currentLocation, using the downstream set as our stopping point.
- // we need to BFS because we need to include all commits below
- // pop these commits on top of targetSource and modify their ids with quotes
- var stopSet = this.getUpstreamSet(targetSource)
-
- // now BFS from here on out
- var toRebaseRough = [];
- var pQueue = [this.getCommitFromRef(currentLocation)];
-
- while (pQueue.length) {
- var popped = pQueue.pop();
-
- // if its in the set, dont add it
- if (stopSet[popped.get('id')]) {
- continue;
- }
-
- // it's not in the set, so we need to rebase this commit
- toRebaseRough.push(popped);
- toRebaseRough.sort(this.idSortFunc);
- toRebaseRough.reverse();
- // keep searching
- pQueue = pQueue.concat(popped.get('parents'));
- }
-
- return this.rebaseFinish(toRebaseRough, stopSet, targetSource, currentLocation);
-};
-
-GitEngine.prototype.rebaseInteractive = function(targetSource, currentLocation) {
- // there are a reduced set of checks now, so we can't exactly use parts of the rebase function
- // but it will look similar.
-
- // first if we are upstream of the target
- if (this.isUpstreamOf(currentLocation, targetSource)) {
- throw new GitError({
- msg: 'Nothing to do... (git throws a "noop" status here); ' +
- 'Your source is upstream of your rebase target'
- });
- }
-
- // now get the stop set
- var stopSet = this.getUpstreamSet(targetSource);
-
- var toRebaseRough = [];
- // standard BFS
- var pQueue = [this.getCommitFromRef(currentLocation)];
-
- while (pQueue.length) {
- var popped = pQueue.pop();
-
- if (stopSet[popped.get('id')]) {
- continue;
- }
-
- toRebaseRough.push(popped);
- pQueue = pQueue.concat(popped.get('parents'));
- pQueue.sort(this.idSortFunc);
- }
-
- // throw our merge's real fast and see if we have anything to do
- var toRebase = [];
- _.each(toRebaseRough, function(commit) {
- if (commit.get('parents').length == 1) {
- toRebase.push(commit);
- }
- });
-
- if (!toRebase.length) {
- throw new GitError({
- msg: 'No commits to rebase! Everything is a merge commit'
- });
- }
-
- // now do stuff :D since all our validation checks have passed, we are going to defer animation
- // and actually launch the dialog
- this.animationQueue.set('defer', true);
-
- var callback = _.bind(function(userSpecifiedRebase) {
- // first, they might have dropped everything (annoying)
- if (!userSpecifiedRebase.length) {
- this.command.setResult('Nothing to do...');
- this.animationQueue.start();
- return;
- }
-
- // finish the rebase crap and animate!
- var animationData = this.rebaseFinish(userSpecifiedRebase, {}, targetSource, currentLocation);
- animationFactory.rebaseAnimation(this.animationQueue, animationData, this, this.gitVisuals);
- this.animationQueue.start();
- }, this);
-
- new InteractiveRebaseView({
- callback: callback,
- toRebase: toRebase,
- el: $('#dialogHolder')
- });
-};
-
-GitEngine.prototype.rebaseFinish = function(toRebaseRough, stopSet, targetSource, currentLocation) {
- // now we have the all the commits between currentLocation and the set of target to rebase.
- var animationResponse = {};
- animationResponse.destinationBranch = this.resolveID(targetSource);
-
- // we need to throw out merge commits
- var toRebase = [];
- _.each(toRebaseRough, function(commit) {
- if (commit.get('parents').length == 1) {
- toRebase.push(commit);
- }
- });
-
- // we ALSO need to throw out commits that will do the same changes. like
- // if the upstream set has a commit C4 and we have C4', we dont rebase the C4' again.
- // get this by doing ID scraping
- var changesAlreadyMade = {};
- _.each(stopSet, function(val, key) {
- changesAlreadyMade[this.scrapeBaseID(key)] = val; // val == true
- }, this);
-
- // now get rid of the commits that will redo same changes
- toRebaseRough = toRebase;
- toRebase = [];
- _.each(toRebaseRough, function(commit) {
- var baseID = this.scrapeBaseID(commit.get('id'));
- if (!changesAlreadyMade[baseID]) {
- toRebase.push(commit);
- }
- }, this);
-
- if (!toRebase.length) {
- throw new GitError({
- msg: 'No Commits to Rebase! Everything else is merge commits or changes already have been applied'
- });
- }
-
- // now reverse it once more to get it in the right order
- toRebase.reverse();
- animationResponse.toRebaseArray = toRebase.slice(0);
-
- // now pop all of these commits onto targetLocation
- var base = this.getCommitFromRef(targetSource);
-
- // do the rebase, and also maintain all our animation info during this
- animationResponse.rebaseSteps = [];
- var beforeSnapshot = this.gitVisuals.genSnapshot();
- var afterSnapshot;
- _.each(toRebase, function(old) {
- var newId = this.rebaseAltID(old.get('id'));
-
- var newCommit = this.makeCommit([base], newId);
- base = newCommit;
-
- // animation info
- afterSnapshot = this.gitVisuals.genSnapshot();
- animationResponse.rebaseSteps.push({
- oldCommit: old,
- newCommit: newCommit,
- beforeSnapshot: beforeSnapshot,
- afterSnapshot: afterSnapshot
- });
- beforeSnapshot = afterSnapshot;
- }, this);
-
- if (this.resolveID(currentLocation).get('type') == 'commit') {
- // we referenced a commit like git rebase C2 C1, so we have
- // to manually check out C1'
-
- var steps = animationResponse.rebaseSteps;
- var newestCommit = steps[steps.length - 1].newCommit;
-
- this.checkout(newestCommit);
- } else {
- // now we just need to update the rebased branch is
- this.setTargetLocation(currentLocation, base);
- this.checkout(currentLocation);
- }
-
- // for animation
- return animationResponse;
-};
-
-GitEngine.prototype.mergeStarter = function() {
- this.twoArgsImpliedHead(this.generalArgs);
-
- var newCommit = this.merge(this.generalArgs[0], this.generalArgs[1]);
-
- if (newCommit === undefined) {
- // its just a fast forwrard
- animationFactory.refreshTree(this.animationQueue, this.gitVisuals);
- return;
- }
-
- animationFactory.genCommitBirthAnimation(this.animationQueue, newCommit, this.gitVisuals);
-};
-
-GitEngine.prototype.merge = function(targetSource, currentLocation) {
- // first some conditions
- if (this.isUpstreamOf(targetSource, currentLocation) ||
- this.getCommitFromRef(targetSource) === this.getCommitFromRef(currentLocation)) {
- throw new CommandResult({
- msg: 'Branch already up-to-date'
- });
- }
-
- if (this.isUpstreamOf(currentLocation, targetSource)) {
- // just set the target of this current location to the source
- this.setTargetLocation(currentLocation, this.getCommitFromRef(targetSource));
- // get fresh animation to happen
- this.command.setResult('Fast-forwarding...');
- return;
- }
-
- // now the part of making a merge commit
- var parent1 = this.getCommitFromRef(currentLocation);
- var parent2 = this.getCommitFromRef(targetSource);
-
- // we need a fancy commit message
- var msg = 'Merge ' + this.resolveName(targetSource) +
- ' into ' + this.resolveName(currentLocation);
-
- // since we specify parent 1 as the first parent, it is the "main" parent
- // and the node will be displayed below that branch / commit / whatever
- var mergeCommit = this.makeCommit(
- [parent1, parent2],
- null,
- {
- commitMessage: msg
- }
- );
-
- this.setTargetLocation(currentLocation, mergeCommit)
- return mergeCommit;
-};
-
-GitEngine.prototype.checkoutStarter = function() {
- if (this.commandOptions['-b']) {
- // the user is really trying to just make a branch and then switch to it. so first:
- var args = this.commandOptions['-b'];
- this.twoArgsImpliedHead(args, '-b');
-
- var validId = this.validateBranchName(args[0]);
- this.branch(validId, args[1]);
- this.checkout(validId);
- return;
- }
-
- if (this.commandOptions['-']) {
- // get the heads last location
- var lastPlace = this.HEAD.get('lastLastTarget');
- if (!lastPlace) {
- throw new GitError({
- msg: 'Need a previous location to do - switching'
- });
- }
- this.HEAD.set('target', lastPlace);
- return;
- }
-
- if (this.commandOptions['-B']) {
- var args = this.commandOptions['-B'];
- this.twoArgsImpliedHead(args, '-B');
-
- this.forceBranch(args[0], args[1]);
- this.checkout(args[0]);
- return;
- }
-
- this.validateArgBounds(this.generalArgs, 1, 1);
-
- this.checkout(this.unescapeQuotes(this.generalArgs[0]));
-};
-
-GitEngine.prototype.checkout = function(idOrTarget) {
- var target = this.resolveID(idOrTarget);
- if (target.get('id') === 'HEAD') {
- // git checkout HEAD is a
- // meaningless command but i used to do this back in the day
- return;
- }
-
- var type = target.get('type');
- if (type !== 'branch' && type !== 'commit') {
- throw new GitError({
- msg: 'can only checkout branches and commits!'
- });
- }
-
- this.HEAD.set('target', target);
-};
-
-GitEngine.prototype.branchStarter = function() {
- // handle deletion first
- if (this.commandOptions['-d'] || this.commandOptions['-D']) {
- var names = this.commandOptions['-d'] || this.commandOptions['-D'];
- this.validateArgBounds(names, 1, NaN, '-d');
-
- _.each(names, function(name) {
- this.deleteBranch(name);
- }, this);
- return;
- }
-
- if (this.commandOptions['--contains']) {
- var args = this.commandOptions['--contains'];
- this.validateArgBounds(args, 1, 1, '--contains');
- this.printBranchesWithout(args[0]);
- return;
- }
-
- if (this.commandOptions['-f']) {
- var args = this.commandOptions['-f'];
- this.twoArgsImpliedHead(args, '-f');
-
- // we want to force a branch somewhere
- this.forceBranch(args[0], args[1]);
- return;
- }
-
-
- if (this.generalArgs.length == 0) {
- this.printBranches(this.getBranches());
- return;
- }
-
- this.twoArgsImpliedHead(this.generalArgs);
- this.branch(this.generalArgs[0], this.generalArgs[1]);
-};
-
-GitEngine.prototype.forceBranch = function(branchName, where) {
- // if branchname doesn't exist...
- if (!this.refs[branchName]) {
- this.branch(branchName, where);
- }
-
- var branch = this.resolveID(branchName);
- if (branch.get('type') !== 'branch') {
- throw new GitError({
- msg: "Can't force move anything but a branch!!"
- });
- }
-
- var whereCommit = this.getCommitFromRef(where);
-
- this.setTargetLocation(branch, whereCommit);
-};
-
-GitEngine.prototype.branch = function(name, ref) {
- var target = this.getCommitFromRef(ref);
- this.makeBranch(name, target);
-};
-
-GitEngine.prototype.deleteBranch = function(name) {
- // trying to delete, lets check our refs
- var target = this.resolveID(name);
- if (target.get('type') !== 'branch') {
- throw new GitError({
- msg: "You can't delete things that arent branches with branch command"
- });
- }
- if (target.get('id') == 'master') {
- throw new GitError({
- msg: "You can't delete the master branch!"
- });
- }
- if (this.HEAD.get('target') === target) {
- throw new GitError({
- msg: "Cannot delete the branch you are currently on"
- });
- }
-
- // now we know it's a branch
- var branch = target;
-
- this.branchCollection.remove(branch);
- this.refs[branch.get('id')] = undefined;
- delete this.refs[branch.get('id')];
-
- if (branch.get('visBranch')) {
- branch.get('visBranch').remove();
- }
-};
-
-GitEngine.prototype.unescapeQuotes = function(str) {
- return str.replace(/'/g, "'");
-}
-
-GitEngine.prototype.dispatch = function(command, callback) {
- // current command, options, and args are stored in the gitEngine
- // for easy reference during processing.
- this.command = command;
- this.commandOptions = command.get('supportedMap');
- this.generalArgs = command.get('generalArgs');
-
- // set up the animation queue
- var whenDone = _.bind(function() {
- command.set('status', 'finished');
- callback();
- }, this);
- this.animationQueue = new AnimationQueue({
- callback: whenDone
- });
-
- command.set('status', 'processing');
- try {
- var methodName = command.get('method').replace(/-/g, '') + 'Starter';
- this[methodName]();
- } catch (err) {
- if (err instanceof GitError ||
- err instanceof CommandResult) {
- // short circuit animation by just setting error and returning
- command.set('error', err);
- callback();
- return;
- } else {
- throw err;
- }
- }
-
- // only add the refresh if we didn't do manual animations
- if (!this.animationQueue.get('animations').length && !this.animationQueue.get('defer')) {
- animationFactory.refreshTree(this.animationQueue, this.gitVisuals);
- }
-
- // animation queue will call the callback when its done
- if (!this.animationQueue.get('defer')) {
- this.animationQueue.start();
- }
-};
-
-GitEngine.prototype.showStarter = function() {
- this.oneArgImpliedHead(this.generalArgs);
-
- this.show(this.generalArgs[0]);
-};
-
-GitEngine.prototype.show = function(ref) {
- var commit = this.getCommitFromRef(ref);
-
- throw new CommandResult({
- msg: commit.getShowEntry()
- });
-};
-
-GitEngine.prototype.statusStarter = function() {
- var lines = [];
- if (this.getDetachedHead()) {
- lines.push('Detached Head!');
- } else {
- var branchName = this.HEAD.get('target').get('id');
- lines.push('On branch ' + branchName);
- }
- lines.push('Changes to be committed:');
- lines.push('');
- lines.push(' modified: cal/OskiCostume.stl');
- lines.push('');
- lines.push('Ready to commit! (as always in this demo)');
-
- var msg = '';
- _.each(lines, function(line) {
- msg += '# ' + line + '\n';
- });
-
- throw new CommandResult({
- msg: msg
- });
-};
-
-GitEngine.prototype.logStarter = function() {
- if (this.generalArgs.length == 2) {
- // do fancy git log branchA ^branchB
- if (this.generalArgs[1][0] == '^') {
- this.logWithout(this.generalArgs[0], this.generalArgs[1]);
- } else {
- throw new GitError({
- msg: 'I need a not branch (^branchName) when getting two arguments!'
- });
- }
- }
-
- this.oneArgImpliedHead(this.generalArgs);
- this.log(this.generalArgs[0]);
-};
-
-GitEngine.prototype.logWithout = function(ref, omitBranch) {
- // slice off the ^branch
- omitBranch = omitBranch.slice(1);
- this.log(ref, this.getUpstreamSet(omitBranch));
-};
-
-GitEngine.prototype.log = function(ref, omitSet) {
- // omit set is for doing stuff like git log branchA ^branchB
- omitSet = omitSet || {};
- // first get the commit we referenced
- var commit = this.getCommitFromRef(ref);
-
- // then get as many far back as we can from here, order by commit date
- var toDump = [];
- var pQueue = [commit];
-
- var seen = {};
-
- while (pQueue.length) {
- var popped = pQueue.shift(0);
- if (seen[popped.get('id')] || omitSet[popped.get('id')]) {
- continue;
- }
- seen[popped.get('id')] = true;
-
- toDump.push(popped);
-
- if (popped.get('parents') && popped.get('parents').length) {
- pQueue = pQueue.concat(popped.get('parents'));
- }
- }
-
- // now go through and collect logs
- var bigLogStr = '';
- _.each(toDump, function(c) {
- bigLogStr += c.getLogEntry();
- }, this);
-
- throw new CommandResult({
- msg: bigLogStr
- });
-};
-
-GitEngine.prototype.addStarter = function() {
- throw new CommandResult({
- msg: "This demo is meant to demonstrate git branching, so don't worry about " +
- "adding / staging files. Just go ahead and commit away!"
- });
-};
-
-GitEngine.prototype.getCommonAncestor = function(ancestor, cousin) {
- if (this.isUpstreamOf(cousin, ancestor)) {
- throw new Error('Dont use common ancestor if we are upstream!');
- }
-
- var upstreamSet = this.getUpstreamSet(ancestor);
- // now BFS off of cousin until you find something
-
- var queue = [this.getCommitFromRef(cousin)];
- while (queue.length) {
- var here = queue.pop();
- if (upstreamSet[here.get('id')]) {
- return here;
- }
- queue = queue.concat(here.get('parents'));
- }
- throw new Error('something has gone very wrong... two nodes arent connected!');
-};
-
-GitEngine.prototype.isUpstreamOf = function(child, ancestor) {
- child = this.getCommitFromRef(child);
-
- // basically just do a completely BFS search on ancestor to the root, then
- // check for membership of child in that set of explored nodes
- var upstream = this.getUpstreamSet(ancestor);
- return upstream[child.get('id')] !== undefined;
-};
-
-GitEngine.prototype.getUpstreamSet = function(ancestor) {
- var commit = this.getCommitFromRef(ancestor);
- var ancestorID = commit.get('id');
- var queue = [commit];
-
- var exploredSet = {};
- exploredSet[ancestorID] = true;
- while (queue.length) {
- var here = queue.pop();
- var rents = here.get('parents');
-
- _.each(rents, function(rent) {
- exploredSet[rent.get('id')] = true;
- queue.push(rent);
- });
- }
- return exploredSet;
-};
-
-
-var Ref = Backbone.Model.extend({
- initialize: function() {
- if (!this.get('target')) {
- throw new Error('must be initialized with target');
- }
- if (!this.get('id')) {
- throw new Error('must be given an id');
- }
- this.set('type', 'general ref');
-
- if (this.get('id') == 'HEAD') {
- this.set('lastLastTarget', null);
- this.set('lastTarget', this.get('target'));
- // have HEAD remember where it is for checkout -
- this.on('change:target', this.targetChanged, this);
- }
- },
-
- targetChanged: function(model, targetValue, ev) {
- // push our little 3 stack back. we need to do this because
- // backbone doesn't give you what the value WAS, only what it was changed
- // TO
- this.set('lastLastTarget', this.get('lastTarget'));
- this.set('lastTarget', targetValue);
- },
-
- toString: function() {
- return 'a ' + this.get('type') + 'pointing to ' + String(this.get('target'));
- }
-});
-
-var Branch = Ref.extend({
- defaults: {
- visBranch: null,
- },
-
- initialize: function() {
- Ref.prototype.initialize.call(this);
- this.set('type', 'branch');
- }
-});
-
-var Commit = Backbone.Model.extend({
- defaults: {
- type: 'commit',
- children: null,
- parents: null,
- author: 'Peter Cottle',
- createTime: null,
- commitMessage: null,
- visNode: null,
- gitVisuals: null
- },
-
- constants: {
- circularFields: ['gitVisuals', 'visNode', 'children']
- },
-
- getLogEntry: function() {
- // for now we are just joining all these things with newlines which
- // will get placed by paragraph tags. Not really a fan of this, but
- // it's better than making an entire template and all that jazz
- return [
- 'Author: ' + this.get('author'),
- 'Date: ' + this.get('createTime'),
- '
',
- this.get('commitMessage'),
- '
',
- 'Commit: ' + this.get('id')
- ].join('\n' ) + '\n';
- },
-
- getShowEntry: function() {
- // same deal as above, show log entry and some fake changes
- return [
- this.getLogEntry(),
- 'diff --git a/bigGameResults.html b/bigGameResults.html',
- '--- bigGameResults.html',
- '+++ bigGameResults.html',
- '@@ 13,27 @@ Winner, Score',
- '- Stanfurd, 14-7',
- '+ Cal, 21-14',
- ].join('\n') + '\n';
- },
-
- validateAtInit: function() {
- if (!this.get('id')) {
- throw new Error('Need ID!!');
- }
-
- if (!this.get('createTime')) {
- this.set('createTime', new Date().toString());
- }
- if (!this.get('commitMessage')) {
- this.set('commitMessage', 'Quick Commit. Go Bears!');
- }
-
- this.set('children', []);
-
- // root commits have no parents
- if (!this.get('rootCommit')) {
- if (!this.get('parents') || !this.get('parents').length) {
- throw new Error('needs parents');
- }
- }
- },
-
- addNodeToVisuals: function() {
- var visNode = this.get('gitVisuals').addNode(this.get('id'), this);
- this.set('visNode', visNode);
- },
-
- addEdgeToVisuals: function(parent) {
- this.get('gitVisuals').addEdge(this.get('id'), parent.get('id'));
- },
-
- isMainParent: function(parent) {
- var index = this.get('parents').indexOf(parent);
- return index === 0;
- },
-
- initialize: function(options) {
- this.validateAtInit();
- this.addNodeToVisuals();
-
- _.each(this.get('parents'), function(parent) {
- parent.get('children').push(this);
- this.addEdgeToVisuals(parent);
- }, this);
- }
-});
-
-exports.GitEngine = GitEngine;
-exports.Commit = Commit;
-exports.Branch = Branch;
-exports.Ref = Ref;
-
-
-});
-require("/git.js");
-
-require.define("/collections.js",function(require,module,exports,__dirname,__filename,process,global){var Commit = require('./git').Commit;
-var Branch = require('./git').Branch;
-
-var CommitCollection = Backbone.Collection.extend({
- model: Commit
-});
-
-var CommandCollection = Backbone.Collection.extend({
- model: Command,
-});
-
-var BranchCollection = Backbone.Collection.extend({
- model: Branch
-});
-
-var CommandEntryCollection = Backbone.Collection.extend({
- model: CommandEntry,
- localStorage: new Backbone.LocalStorage('CommandEntries')
-});
-
-var CommandBuffer = Backbone.Model.extend({
- defaults: {
- collection: null,
- },
-
- initialize: function(options) {
- events.on('gitCommandReady', _.bind(
- this.addCommand, this
- ));
-
- options.collection.bind('add', this.addCommand, this);
-
- this.buffer = [];
- this.timeout = null;
- },
-
- addCommand: function(command) {
- this.buffer.push(command);
- this.touchBuffer();
- },
-
- touchBuffer: function() {
- // touch buffer just essentially means we just check if our buffer is being
- // processed. if it's not, we immediately process the first item
- // and then set the timeout.
- if (this.timeout) {
- // timeout existence implies its being processed
- return;
- }
- this.setTimeout();
- },
-
-
- setTimeout: function() {
- this.timeout = setTimeout(_.bind(function() {
- this.sipFromBuffer();
- }, this), TIME.betweenCommandsDelay);
- },
-
- popAndProcess: function() {
- var popped = this.buffer.shift(0);
- var callback = _.bind(function() {
- this.setTimeout();
- }, this);
-
- // find a command with no error
- while (popped.get('error') && this.buffer.length) {
- popped = this.buffer.pop();
- }
- if (!popped.get('error')) {
- // pass in a callback, so when this command is "done" we will process the next.
- events.trigger('processCommand', popped, callback);
- } else {
- this.clear();
- }
- },
-
- clear: function() {
- clearTimeout(this.timeout);
- this.timeout = null;
- },
-
- sipFromBuffer: function() {
- if (!this.buffer.length) {
- this.clear();
- return;
- }
-
- this.popAndProcess();
- },
-});
-
-exports.CommitCollection = CommitCollection;
-exports.CommandCollection = CommandCollection;
-exports.BranchCollection = BranchCollection;
-exports.CommandEntryCollection = CommandEntryCollection;
-exports.CommandBuffer = CommandBuffer;
-
-
-});
-require("/collections.js");
-
-require.define("/commandViews.js",function(require,module,exports,__dirname,__filename,process,global){var CommandEntryCollection = require('./collections').CommandEntryCollection;
-
-var CommandPromptView = Backbone.View.extend({
- initialize: function(options) {
- this.collection = options.collection;
-
- // uses local storage
- this.commands = new CommandEntryCollection();
- this.commands.fetch({
- success: _.bind(function() {
- // reverse the commands. this is ugly but needs to be done...
- var commands = [];
- this.commands.each(function(c) {
- commands.push(c);
- });
-
- commands.reverse();
- this.commands.reset();
-
- _.each(commands, function(c) {
- this.commands.add(c);
- }, this);
- }, this)
- });
-
- this.index = -1;
-
- this.commandSpan = this.$('#prompt span.command')[0];
- this.commandCursor = this.$('#prompt span.cursor')[0];
-
- // this is evil, but we will refer to HTML outside the document
- // and attach a click event listener so we can focus / unfocus
- $(document).delegate('#commandLineHistory', 'click', _.bind(function() {
- this.focus();
- }, this));
-
-
- $(document).delegate('#commandTextField', 'blur', _.bind(function() {
- this.blur();
- }, this));
-
- events.on('processCommandFromEvent', this.addToCollection, this);
- events.on('submitCommandValueFromEvent', this.submitValue, this);
- events.on('rollupCommands', this.rollupCommands, this);
-
- // hacky timeout focus
- setTimeout(_.bind(function() {
- this.focus();
- }, this), 100);
- },
-
- events: {
- 'keydown #commandTextField': 'onKey',
- 'keyup #commandTextField': 'onKeyUp',
- 'blur #commandTextField': 'hideCursor',
- 'focus #commandTextField': 'showCursor'
- },
-
- blur: function() {
- $(this.commandCursor).toggleClass('shown', false);
- },
-
- focus: function() {
- this.$('#commandTextField').focus();
- this.showCursor();
- },
-
- hideCursor: function() {
- this.toggleCursor(false);
- },
-
- showCursor: function() {
- this.toggleCursor(true);
- },
-
- toggleCursor: function(state) {
- $(this.commandCursor).toggleClass('shown', state);
- },
-
- onKey: function(e) {
- var el = e.srcElement;
- this.updatePrompt(el)
- },
-
- onKeyUp: function(e) {
- this.onKey(e);
-
- // we need to capture some of these events.
- // WARNING: this key map is not internationalized :(
- var keyMap = {
- // enter
- 13: _.bind(function() {
- this.submit();
- }, this),
- // up
- 38: _.bind(function() {
- this.commandSelectChange(1);
- }, this),
- // down
- 40: _.bind(function() {
- this.commandSelectChange(-1);
- }, this)
- };
-
- if (keyMap[e.which] !== undefined) {
- e.preventDefault();
- keyMap[e.which]();
- this.onKey(e);
- }
- },
-
- badHtmlEncode: function(text) {
- return text.replace(/&/g,'&')
- .replace(/= this.commands.length || this.index < 0) {
- this.clear();
- this.index = -1;
- return;
- }
-
- // yay! we actually can display something
- var commandEntry = this.commands.toArray()[this.index].get('text');
- this.setTextField(commandEntry);
- },
-
- clearLocalStorage: function() {
- this.commands.each(function(c) {
- Backbone.sync('delete', c, function() { });
- }, this);
- localStorage.setItem('CommandEntries', '');
- },
-
- setTextField: function(value) {
- this.$('#commandTextField').val(value);
- },
-
- clear: function() {
- this.setTextField('');
- },
-
- submit: function() {
- var value = this.$('#commandTextField').val().replace('\n', '');
- this.clear();
- this.submitValue(value);
- },
-
- rollupCommands: function(numBack) {
- var which = this.commands.toArray().slice(1, Number(numBack) + 1);
- which.reverse();
-
- var str = '';
- _.each(which, function(commandEntry) {
- str += commandEntry.get('text') + ';';
- }, this);
-
- console.log('the str', str);
-
- var rolled = new CommandEntry({text: str});
- this.commands.unshift(rolled);
- Backbone.sync('create', rolled, function() { });
- },
-
- submitValue: function(value) {
- // we should add if it's not a blank line and this is a new command...
- // or if we edited the command
- var shouldAdd = (value.length && this.index == -1) ||
- ((value.length && this.index !== -1 &&
- this.commands.toArray()[this.index].get('text') !== value));
-
- if (shouldAdd) {
- var commandEntry = new CommandEntry({text: value});
- this.commands.unshift(commandEntry);
-
- // store to local storage
- Backbone.sync('create', commandEntry, function() { });
-
- // if our length is too egregious, reset
- if (this.commands.length > 100) {
- this.clearLocalStorage();
- }
- }
- this.index = -1;
-
- // split commands on semicolon
- _.each(value.split(';'), _.bind(function(command, index) {
- command = _.escape(command);
-
- command = command
- .replace(/^(\s+)/, '')
- .replace(/(\s+)$/, '')
- .replace(/"/g, '"')
- .replace(/'/g, "'");
-
- if (index > 0 && !command.length) {
- return;
- }
-
- this.addToCollection(command);
- }, this));
- },
-
- addToCollection: function(value) {
- var command = new Command({
- rawStr: value
- });
- this.collection.add(command);
- }
-});
-
-
-// This is the view for all commands -- it will represent
-// their status (inqueue, processing, finished, error),
-// their value ("git commit --amend"),
-// and the result (either errors or warnings or whatever)
-var CommandView = Backbone.View.extend({
- tagName: 'div',
- model: Command,
- template: _.template($('#command-template').html()),
-
- events: {
- 'click': 'clicked'
- },
-
- clicked: function(e) {
- },
-
- initialize: function() {
- this.model.bind('change', this.wasChanged, this);
- this.model.bind('destroy', this.remove, this);
- },
-
- wasChanged: function(model, changeEvent) {
- // for changes that are just comestic, we actually only want to toggle classes
- // with jquery rather than brutally delete a html of HTML
- var changes = changeEvent.changes;
- var changeKeys = _.keys(changes);
- if (_.difference(changeKeys, ['status']) == 0) {
- this.updateStatus();
- } else if (_.difference(changeKeys, ['error']) == 0) {
- // the above will
- this.render();
- } else {
- this.render();
- }
- },
-
- updateStatus: function() {
- var statuses = ['inqueue', 'processing', 'finished'];
- var toggleMap = {};
- _.each(statuses, function(status) {
- toggleMap[status] = false;
- });
- toggleMap[this.model.get('status')] = true;
-
- var query = this.$('p.commandLine');
-
- _.each(toggleMap, function(value, key) {
- query.toggleClass(key, value);
- });
- },
-
- render: function() {
- var json = _.extend(
- {
- resultType: '',
- result: '',
- formattedWarnings: this.model.getFormattedWarnings()
- },
- this.model.toJSON()
- );
-
- this.$el.html(this.template(json));
- return this;
- },
-
- remove: function() {
- $(this.el).hide();
- }
-});
-
-
-var CommandLineHistoryView = Backbone.View.extend({
- initialize: function(options) {
- this.collection = options.collection;
-
- this.collection.on('add', this.addOne, this);
- this.collection.on('reset', this.addAll, this);
- this.collection.on('all', this.render, this);
-
- this.collection.on('change', this.scrollDown, this);
-
- events.on('issueWarning', this.addWarning, this);
- events.on('commandScrollDown', this.scrollDown, this);
- },
-
- addWarning: function(msg) {
- var err = new Warning({
- msg: msg
- });
-
- var command = new Command({
- error: err,
- rawStr: 'Warning:'
- });
-
- this.collection.add(command);
- },
-
- scrollDown: function() {
- // if commandDisplay is ever bigger than #terminal, we need to
- // add overflow-y to terminal and scroll down
- var cD = $('#commandDisplay')[0];
- var t = $('#terminal')[0];
-
- if ($(t).hasClass('scrolling')) {
- t.scrollTop = t.scrollHeight;
- return;
- }
- if (cD.clientHeight > t.clientHeight) {
- $(t).css('overflow-y', 'scroll');
- $(t).css('overflow-x', 'hidden');
- $(t).addClass('scrolling');
- t.scrollTop = t.scrollHeight;
- }
- },
-
- addOne: function(command) {
- var view = new CommandView({
- model: command
- });
- this.$('#commandDisplay').append(view.render().el);
- this.scrollDown();
- },
-
- addAll: function() {
- this.collection.each(this.addOne);
- }
-});
-
-exports.CommandPromptView = CommandPromptView;
-exports.CommandLineHistoryView = CommandLineHistoryView;
-
-
-});
-require("/commandViews.js");
-
-require.define("/visuals.js",function(require,module,exports,__dirname,__filename,process,global){var CommitCollection = require('./collections').CommitCollection;
-var BranchCollection = require('./collections').BranchCollection;
-var GitEngine = require('./git').GitEngine;
-
-var Visualization = Backbone.View.extend({
- initialize: function(options) {
- var _this = this;
- Raphael(10, 10, 200, 200, function() {
-
- // for some reason raphael calls this function with a predefined
- // context...
- // so switch it
- _this.paperInitialize(this);
- });
- },
-
- paperInitialize: function(paper, options) {
- this.paper = paper;
-
- this.commitCollection = new CommitCollection();
- this.branchCollection = new BranchCollection();
-
- this.gitVisuals = new GitVisuals({
- commitCollection: this.commitCollection,
- branchCollection: this.branchCollection,
- paper: this.paper
- });
-
- this.gitEngine = new GitEngine({
- collection: this.commitCollection,
- branches: this.branchCollection,
- gitVisuals: this.gitVisuals
- });
- this.gitEngine.init();
- this.gitVisuals.assignGitEngine(this.gitEngine);
-
- this.myResize();
- $(window).on('resize', _.bind(this.myResize, this));
- this.gitVisuals.drawTreeFirstTime();
- },
-
- myResize: function() {
- var smaller = 1;
- var el = this.el;
-
- var left = el.offsetLeft;
- var top = el.offsetTop;
- var width = el.clientWidth - smaller;
- var height = el.clientHeight - smaller;
-
- $(this.paper.canvas).css({
- left: left + 'px',
- top: top + 'px'
- });
- this.paper.setSize(width, height);
- this.gitVisuals.canvasResize(width, height);
- }
-
-});
-
-function GitVisuals(options) {
- this.commitCollection = options.commitCollection;
- this.branchCollection = options.branchCollection;
- this.visNodeMap = {};
-
- this.visEdgeCollection = new VisEdgeCollection();
- this.visBranchCollection = new VisBranchCollection();
- this.commitMap = {};
-
- this.rootCommit = null;
- this.branchStackMap = null;
- this.upstreamBranchSet = null;
- this.upstreamHeadSet = null;
-
- this.paper = options.paper;
- this.gitReady = false;
-
- this.branchCollection.on('add', this.addBranchFromEvent, this);
- this.branchCollection.on('remove', this.removeBranch, this);
- this.deferred = [];
-
- events.on('refreshTree', _.bind(
- this.refreshTree, this
- ));
-}
-
-GitVisuals.prototype.defer = function(action) {
- this.deferred.push(action);
-};
-
-GitVisuals.prototype.deferFlush = function() {
- _.each(this.deferred, function(action) {
- action();
- }, this);
- this.deferred = [];
-};
-
-GitVisuals.prototype.resetAll = function() {
- // make sure to copy these collections because we remove
- // items in place and underscore is too dumb to detect length change
- var edges = this.visEdgeCollection.toArray();
- _.each(edges, function(visEdge) {
- visEdge.remove();
- }, this);
-
- var branches = this.visBranchCollection.toArray();
- _.each(branches, function(visBranch) {
- visBranch.remove();
- }, this);
-
- _.each(this.visNodeMap, function(visNode) {
- visNode.remove();
- }, this);
-
- this.visEdgeCollection.reset();
- this.visBranchCollection.reset();
-
- this.visNodeMap = {};
- this.rootCommit = null;
- this.commitMap = {};
-};
-
-GitVisuals.prototype.assignGitEngine = function(gitEngine) {
- this.gitEngine = gitEngine;
- this.initHeadBranch();
- this.deferFlush();
-};
-
-GitVisuals.prototype.initHeadBranch = function() {
- // it's unfortaunte we have to do this, but the head branch
- // is an edge case because it's not part of a collection so
- // we can't use events to load or unload it. thus we have to call
- // this ugly method which will be deleted one day
-
- // seed this with the HEAD pseudo-branch
- this.addBranchFromEvent(this.gitEngine.HEAD);
-};
-
-GitVisuals.prototype.getScreenBounds = function() {
- // for now we return the node radius subtracted from the walls
- return {
- widthPadding: GRAPHICS.nodeRadius * 1.5,
- heightPadding: GRAPHICS.nodeRadius * 1.5
- };
-};
-
-GitVisuals.prototype.toScreenCoords = function(pos) {
- if (!this.paper.width) {
- throw new Error('being called too early for screen coords');
- }
- var bounds = this.getScreenBounds();
-
- var shrink = function(frac, total, padding) {
- return padding + frac * (total - padding * 2);
- };
-
- return {
- x: shrink(pos.x, this.paper.width, bounds.widthPadding),
- y: shrink(pos.y, this.paper.height, bounds.heightPadding)
- };
-};
-
-GitVisuals.prototype.animateAllFromAttrToAttr = function(fromSnapshot, toSnapshot, idsToOmit) {
- var animate = function(obj) {
- var id = obj.getID();
- if (_.include(idsToOmit, id)) {
- return;
- }
-
- if (!fromSnapshot[id] || !toSnapshot[id]) {
- // its actually ok it doesnt exist yet
- return;
- }
- obj.animateFromAttrToAttr(fromSnapshot[id], toSnapshot[id]);
- };
-
- this.visBranchCollection.each(function(visBranch) {
- animate(visBranch);
- });
- this.visEdgeCollection.each(function(visEdge) {
- animate(visEdge);
- });
- _.each(this.visNodeMap, function(visNode) {
- animate(visNode);
- });
-};
-
-/***************************************
- == BEGIN Tree Calculation Parts ==
- _ __ __ _
- \\/ / \ \//_
- \ \ / __| __
- \ \___/ /_____/ /
- | _______ \
- \ ( ) / \_\
- \ /
- | |
- | |
- ____+-_=+-^ ^+-=_=__________
-
-^^ I drew that :D
-
- **************************************/
-
-GitVisuals.prototype.genSnapshot = function() {
- this.fullCalc();
-
- var snapshot = {};
- _.each(this.visNodeMap, function(visNode) {
- snapshot[visNode.get('id')] = visNode.getAttributes();
- }, this);
-
- this.visBranchCollection.each(function(visBranch) {
- snapshot[visBranch.getID()] = visBranch.getAttributes();
- }, this);
-
- this.visEdgeCollection.each(function(visEdge) {
- snapshot[visEdge.getID()] = visEdge.getAttributes();
- }, this);
-
- return snapshot;
-};
-
-GitVisuals.prototype.refreshTree = function(speed) {
- if (!this.gitReady) {
- return;
- }
-
- // this method can only be called after graphics are rendered
- this.fullCalc();
-
- this.animateAll(speed);
-};
-
-GitVisuals.prototype.refreshTreeHarsh = function() {
- this.fullCalc();
-
- this.animateAll(0);
-};
-
-GitVisuals.prototype.animateAll = function(speed) {
- this.zIndexReflow();
-
- this.animateEdges(speed);
- this.animateNodePositions(speed);
- this.animateRefs(speed);
-};
-
-GitVisuals.prototype.fullCalc = function() {
- this.calcTreeCoords();
- this.calcGraphicsCoords();
-};
-
-GitVisuals.prototype.calcTreeCoords = function() {
- // this method can only contain things that dont rely on graphics
- if (!this.rootCommit) {
- throw new Error('grr, no root commit!');
- }
-
- this.calcUpstreamSets();
- this.calcBranchStacks();
-
- this.calcDepth();
- this.calcWidth();
-};
-
-GitVisuals.prototype.calcGraphicsCoords = function() {
- this.visBranchCollection.each(function(visBranch) {
- visBranch.updateName();
- });
-};
-
-GitVisuals.prototype.calcUpstreamSets = function() {
- this.upstreamBranchSet = this.gitEngine.getUpstreamBranchSet();
- this.upstreamHeadSet = this.gitEngine.getUpstreamHeadSet();
-};
-
-GitVisuals.prototype.getCommitUpstreamBranches = function(commit) {
- return this.branchStackMap[commit.get('id')];
-};
-
-GitVisuals.prototype.getBlendedHuesForCommit = function(commit) {
- var branches = this.upstreamBranchSet[commit.get('id')];
- if (!branches) {
- throw new Error('that commit doesnt have upstream branches!');
- }
-
- return this.blendHuesFromBranchStack(branches);
-};
-
-GitVisuals.prototype.blendHuesFromBranchStack = function(branchStackArray) {
- var hueStrings = [];
- _.each(branchStackArray, function(branchWrapper) {
- var fill = branchWrapper.obj.get('visBranch').get('fill');
-
- if (fill.slice(0,3) !== 'hsb') {
- // crap! convert
- var color = Raphael.color(fill);
- fill = 'hsb(' + String(color.h) + ',' + String(color.l);
- fill = fill + ',' + String(color.s) + ')';
- }
-
- hueStrings.push(fill);
- });
-
- return blendHueStrings(hueStrings);
-};
-
-GitVisuals.prototype.getCommitUpstreamStatus = function(commit) {
- if (!this.upstreamBranchSet) {
- throw new Error("Can't calculate this yet!");
- }
-
- var id = commit.get('id');
- var branch = this.upstreamBranchSet;
- var head = this.upstreamHeadSet;
-
- if (branch[id]) {
- return 'branch';
- } else if (head[id]) {
- return 'head';
- } else {
- return 'none';
- }
-};
-
-GitVisuals.prototype.calcBranchStacks = function() {
- var branches = this.gitEngine.getBranches();
- var map = {};
- _.each(branches, function(branch) {
- var thisId = branch.target.get('id');
-
- map[thisId] = map[thisId] || [];
- map[thisId].push(branch);
- map[thisId].sort(function(a, b) {
- var aId = a.obj.get('id');
- var bId = b.obj.get('id');
- if (aId == 'master' || bId == 'master') {
- return aId == 'master' ? -1 : 1;
- }
- return aId.localeCompare(bId);
- });
- });
- this.branchStackMap = map;
-};
-
-GitVisuals.prototype.calcWidth = function() {
- this.maxWidthRecursive(this.rootCommit);
-
- this.assignBoundsRecursive(this.rootCommit, 0, 1);
-};
-
-GitVisuals.prototype.maxWidthRecursive = function(commit) {
- var childrenTotalWidth = 0;
- _.each(commit.get('children'), function(child) {
- // only include this if we are the "main" parent of
- // this child
- if (child.isMainParent(commit)) {
- var childWidth = this.maxWidthRecursive(child);
- childrenTotalWidth += childWidth;
- }
- }, this);
-
- var maxWidth = Math.max(1, childrenTotalWidth);
- commit.get('visNode').set('maxWidth', maxWidth);
- return maxWidth;
-};
-
-GitVisuals.prototype.assignBoundsRecursive = function(commit, min, max) {
- // I always center myself within my bounds
- var myWidthPos = (min + max) / 2.0;
- commit.get('visNode').get('pos').x = myWidthPos;
-
- if (commit.get('children').length == 0) {
- return;
- }
-
- // i have a certain length to divide up
- var myLength = max - min;
- // I will divide up that length based on my children's max width in a
- // basic box-flex model
- var totalFlex = 0;
- var children = commit.get('children');
- _.each(children, function(child) {
- if (child.isMainParent(commit)) {
- totalFlex += child.get('visNode').getMaxWidthScaled();
- }
- }, this);
-
- var prevBound = min;
-
- // now go through and do everything
- // TODO: order so the max width children are in the middle!!
- _.each(children, function(child) {
- if (!child.isMainParent(commit)) {
- return;
- }
-
- var flex = child.get('visNode').getMaxWidthScaled();
- var portion = (flex / totalFlex) * myLength;
- var childMin = prevBound;
- var childMax = childMin + portion;
- this.assignBoundsRecursive(child, childMin, childMax);
- prevBound = childMax;
- }, this);
-};
-
-GitVisuals.prototype.calcDepth = function() {
- var maxDepth = this.calcDepthRecursive(this.rootCommit, 0);
- if (maxDepth > 15) {
- // issue warning
- console.warn('graphics are degrading from too many layers');
- }
-
- var depthIncrement = this.getDepthIncrement(maxDepth);
- _.each(this.visNodeMap, function(visNode) {
- visNode.setDepthBasedOn(depthIncrement);
- }, this);
-};
-
-/***************************************
- == END Tree Calculation ==
- _ __ __ _
- \\/ / \ \//_
- \ \ / __| __
- \ \___/ /_____/ /
- | _______ \
- \ ( ) / \_\
- \ /
- | |
- | |
- ____+-_=+-^ ^+-=_=__________
-
-^^ I drew that :D
-
- **************************************/
-
-GitVisuals.prototype.animateNodePositions = function(speed) {
- _.each(this.visNodeMap, function(visNode) {
- visNode.animateUpdatedPosition(speed);
- }, this);
-};
-
-GitVisuals.prototype.turnOnPaper = function() {
- this.gitReady = false;
-};
-
-// does making an accessor method make it any less hacky? that is the true question
-GitVisuals.prototype.turnOffPaper = function() {
- this.gitReady = true;
-};
-
-GitVisuals.prototype.addBranchFromEvent = function(branch, collection, index) {
- var action = _.bind(function() {
- this.addBranch(branch);
- }, this);
-
- if (!this.gitEngine || !this.gitReady) {
- this.defer(action);
- } else {
- action();
- }
-};
-
-GitVisuals.prototype.addBranch = function(branch) {
- var visBranch = new VisBranch({
- branch: branch,
- gitVisuals: this,
- gitEngine: this.gitEngine
- });
-
- this.visBranchCollection.add(visBranch);
- if (this.gitReady) {
- visBranch.genGraphics(this.paper);
- }
-};
-
-GitVisuals.prototype.removeVisBranch = function(visBranch) {
- this.visBranchCollection.remove(visBranch);
-};
-
-GitVisuals.prototype.removeVisNode = function(visNode) {
- this.visNodeMap[visNode.getID()] = undefined;
-};
-
-GitVisuals.prototype.removeVisEdge = function(visEdge) {
- this.visEdgeCollection.remove(visEdge);
-};
-
-GitVisuals.prototype.animateRefs = function(speed) {
- this.visBranchCollection.each(function(visBranch) {
- visBranch.animateUpdatedPos(speed);
- }, this);
-};
-
-GitVisuals.prototype.animateEdges = function(speed) {
- this.visEdgeCollection.each(function(edge) {
- edge.animateUpdatedPath(speed);
- }, this);
-};
-
-GitVisuals.prototype.getDepthIncrement = function(maxDepth) {
- // assume there are at least 7 layers until later
- maxDepth = Math.max(maxDepth, 7);
- var increment = 1.0 / maxDepth;
- return increment;
-};
-
-GitVisuals.prototype.calcDepthRecursive = function(commit, depth) {
- commit.get('visNode').setDepth(depth);
-
- var children = commit.get('children');
- var maxDepth = depth;
- _.each(children, function(child) {
- var d = this.calcDepthRecursive(child, depth + 1);
- maxDepth = Math.max(d, maxDepth);
- }, this);
-
- return maxDepth;
-};
-
-GitVisuals.prototype.canvasResize = function(width, height) {
- // refresh when we are ready
- if (GLOBAL.isAnimating) {
- events.trigger('processCommandFromEvent', 'refresh');
- } else {
- this.refreshTree();
- }
-};
-
-GitVisuals.prototype.addCommit = function(commit) {
- // TODO
-};
-
-GitVisuals.prototype.addNode = function(id, commit) {
- this.commitMap[id] = commit;
- if (commit.get('rootCommit')) {
- this.rootCommit = commit;
- }
-
- var visNode = new VisNode({
- id: id,
- commit: commit,
- gitVisuals: this,
- gitEngine: this.gitEngine
- });
- this.visNodeMap[id] = visNode;
-
- if (this.gitReady) {
- visNode.genGraphics(this.paper);
- }
- return visNode;
-};
-
-GitVisuals.prototype.addEdge = function(idTail, idHead) {
- var visNodeTail = this.visNodeMap[idTail];
- var visNodeHead = this.visNodeMap[idHead];
-
- if (!visNodeTail || !visNodeHead) {
- throw new Error('one of the ids in (' + idTail +
- ', ' + idHead + ') does not exist');
- }
-
- var edge = new VisEdge({
- tail: visNodeTail,
- head: visNodeHead,
- gitVisuals: this,
- gitEngine: this.gitEngine
- });
- this.visEdgeCollection.add(edge);
-
- if (this.gitReady) {
- edge.genGraphics(this.paper);
- }
-};
-
-GitVisuals.prototype.collectionChanged = function() {
- // TODO ?
-};
-
-GitVisuals.prototype.zIndexReflow = function() {
- this.visNodesFront();
- this.visBranchesFront();
-};
-
-GitVisuals.prototype.visNodesFront = function() {
- _.each(this.visNodeMap, function(visNode) {
- visNode.toFront();
- });
-};
-
-GitVisuals.prototype.visBranchesFront = function() {
- this.visBranchCollection.each(function(vBranch) {
- vBranch.nonTextToFront();
- });
-
- this.visBranchCollection.each(function(vBranch) {
- vBranch.textToFront();
- });
-};
-
-GitVisuals.prototype.drawTreeFromReload = function() {
- this.gitReady = true;
- // gen all the graphics we need
- this.deferFlush();
-
- this.calcTreeCoords();
-};
-
-GitVisuals.prototype.drawTreeFirstTime = function() {
- this.gitReady = true;
- this.calcTreeCoords();
-
- _.each(this.visNodeMap, function(visNode) {
- visNode.genGraphics(this.paper);
- }, this);
-
- this.visEdgeCollection.each(function(edge) {
- edge.genGraphics(this.paper);
- }, this);
-
- this.visBranchCollection.each(function(visBranch) {
- visBranch.genGraphics(this.paper);
- }, this);
-
- this.zIndexReflow();
-};
-
-
-/************************
- * Random util functions, some from liquidGraph
- ***********************/
-function blendHueStrings(hueStrings) {
- // assumes a sat of 0.7 and brightness of 1
-
- var x = 0;
- var y = 0;
- var totalSat = 0;
- var totalBright = 0;
- var length = hueStrings.length;
-
- _.each(hueStrings, function(hueString) {
- var exploded = hueString.split('(')[1];
- exploded = exploded.split(')')[0];
- exploded = exploded.split(',');
-
- totalSat += parseFloat(exploded[1]);
- totalBright += parseFloat(exploded[2]);
- var hue = parseFloat(exploded[0]);
-
- var angle = hue * Math.PI * 2;
- x += Math.cos(angle);
- y += Math.sin(angle);
- });
-
- x = x / length;
- y = y / length;
- totalSat = totalSat / length;
- totalBright = totalBright / length;
-
- var hue = Math.atan2(y, x) / (Math.PI * 2); // could fail on 0's
- if (hue < 0) {
- hue = hue + 1;
- }
- return 'hsb(' + String(hue) + ',' + String(totalSat) + ',' + String(totalBright) + ')';
-}
-
-function randomHueString() {
- var hue = Math.random();
- var str = 'hsb(' + String(hue) + ',0.7,1)';
- return str;
-};
-
-exports.Visualization = Visualization;
-
-
-});
-require("/visuals.js");
})();
diff --git a/src/animationFactory.js b/src/animationFactory.js
index e9d9bcdb..bc6156c1 100644
--- a/src/animationFactory.js
+++ b/src/animationFactory.js
@@ -9,7 +9,7 @@
*/
// essentially a static class
-var AnimationFactory = function() {
+function AnimationFactory() {
}
@@ -251,3 +251,4 @@ AnimationFactory.prototype.genFromToSnapshotAnimation = function(
exports.AnimationFactory = AnimationFactory;
+
diff --git a/src/collections.js b/src/collections.js
index 39007a55..4044115c 100644
--- a/src/collections.js
+++ b/src/collections.js
@@ -1,6 +1,3 @@
-var Commit = require('./git').Commit;
-var Branch = require('./git').Branch;
-
var CommitCollection = Backbone.Collection.extend({
model: Commit
});
@@ -90,9 +87,3 @@ var CommandBuffer = Backbone.Model.extend({
},
});
-exports.CommitCollection = CommitCollection;
-exports.CommandCollection = CommandCollection;
-exports.BranchCollection = BranchCollection;
-exports.CommandEntryCollection = CommandEntryCollection;
-exports.CommandBuffer = CommandBuffer;
-
diff --git a/src/commandViews.js b/src/commandViews.js
index 69bd36e7..d3cddd28 100644
--- a/src/commandViews.js
+++ b/src/commandViews.js
@@ -1,5 +1,3 @@
-var CommandEntryCollection = require('./collections').CommandEntryCollection;
-
var CommandPromptView = Backbone.View.extend({
initialize: function(options) {
this.collection = options.collection;
@@ -381,7 +379,3 @@ var CommandLineHistoryView = Backbone.View.extend({
this.collection.each(this.addOne);
}
});
-
-exports.CommandPromptView = CommandPromptView;
-exports.CommandLineHistoryView = CommandLineHistoryView;
-
diff --git a/src/git.js b/src/git.js
index 95cd4afc..f6059def 100644
--- a/src/git.js
+++ b/src/git.js
@@ -1,6 +1,3 @@
-var animationFactory = new require('./animationFactory').AnimationFactory();
-console.log('this is what animatioinf actory is', require('./animationFactory'));
-
// backbone or something uses _.uniqueId, so we make our own here
var uniqueId = (function() {
var n = 0;
@@ -1627,8 +1624,3 @@ var Commit = Backbone.Model.extend({
}
});
-exports.GitEngine = GitEngine;
-exports.Commit = Commit;
-exports.Branch = Branch;
-exports.Ref = Ref;
-
diff --git a/src/index.html b/src/index.html
index 08de639d..fb67e28a 100644
--- a/src/index.html
+++ b/src/index.html
@@ -135,15 +135,15 @@
-
+
-
+
-
-
+
+
diff --git a/src/main.js b/src/main.js
index 978d4867..0f22ab9f 100644
--- a/src/main.js
+++ b/src/main.js
@@ -1,9 +1,4 @@
var AnimationFactory = require('./animationFactory').AnimationFactory;
-var CommandCollection = require('./collections').CommandCollection;
-var CommandBuffer = require('./collections').CommandBuffer;
-var CommandPromptView = require('./commandViews').CommandPromptView;
-var CommandLineHistoryView = require('./commandViews').CommandLineHistoryView;
-var Visualization = require('./visuals').Visualization;
/**
* Globals
diff --git a/src/visuals.js b/src/visuals.js
index 17dc2ddd..64a1f12b 100644
--- a/src/visuals.js
+++ b/src/visuals.js
@@ -1,7 +1,3 @@
-var CommitCollection = require('./collections').CommitCollection;
-var BranchCollection = require('./collections').BranchCollection;
-var GitEngine = require('./git').GitEngine;
-
var Visualization = Backbone.View.extend({
initialize: function(options) {
var _this = this;
@@ -671,5 +667,3 @@ function randomHueString() {
return str;
};
-exports.Visualization = Visualization;
-