Git Tag: Initial support for tagging

Branching in git even more useful if using tags. Simulate relase branches,
maintenance branches and so on is therefore really useful in this tool.

Now, rudimentary tagging is implemented, where tags can be attached to the
commits, and referenced by for exmapmle git checkout

Signed-off-by: Max Sikström <msikstrom@op5.com>
This commit is contained in:
Max Sikström 2013-10-08 18:41:00 +02:00
parent fa7b918be9
commit 74342e2028
13 changed files with 2009 additions and 29 deletions

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

1
build/bundle.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -445,7 +445,7 @@
For a much easier time perusing the source, see the individual files at: For a much easier time perusing the source, see the individual files at:
https://github.com/pcottle/learnGitBranching https://github.com/pcottle/learnGitBranching
--> -->
<script src="build/bundle.js"></script> <script src="build/bundle.min.2a1dff9e.js"></script>
<!-- The advantage of github pages: super-easy, simple, slick static hostic. <!-- The advantage of github pages: super-easy, simple, slick static hostic.
The downside? No raw logs to parse for analytics, so I have to include The downside? No raw logs to parse for analytics, so I have to include

View file

@ -557,6 +557,24 @@ var commandConfig = {
destination: tracking destination: tracking
}); });
} }
},
tag: {
regex: /^git +tag($|\s)/,
execute: function(engine, command) {
var generalArgs = command.getGeneralArgs();
if (generalArgs.length === 0) {
var tags = engine.getTags();
engine.printTags(tags);
return;
}
command.twoArgsImpliedHead(generalArgs);
engine.tag(generalArgs[0], generalArgs[1]);
}
} }
}; };

View file

@ -35,6 +35,7 @@ function GitEngine(options) {
this.localRepo = null; this.localRepo = null;
this.branchCollection = options.branches; this.branchCollection = options.branches;
this.tagCollection = options.tags;
this.commitCollection = options.collection; this.commitCollection = options.collection;
this.gitVisuals = options.gitVisuals; this.gitVisuals = options.gitVisuals;
@ -207,6 +208,7 @@ GitEngine.prototype.exportTree = function() {
_.each(this.branchCollection.toJSON(), function(branch) { _.each(this.branchCollection.toJSON(), function(branch) {
branch.target = branch.target.get('id'); branch.target = branch.target.get('id');
branch.visBranch = undefined; branch.visBranch = undefined;
branch.visTag = undefined;
totalExport.branches[branch.id] = branch; totalExport.branches[branch.id] = branch;
}); });
@ -228,8 +230,7 @@ GitEngine.prototype.exportTree = function() {
}, this); }, this);
var HEAD = this.HEAD.toJSON(); var HEAD = this.HEAD.toJSON();
HEAD.visBranch = undefined; HEAD.lastTarget = HEAD.lastLastTarget = HEAD.visBranch = HEAD.visTag =undefined;
HEAD.lastTarget = HEAD.lastLastTarget = HEAD.visBranch = undefined;
HEAD.target = HEAD.target.get('id'); HEAD.target = HEAD.target.get('id');
totalExport.HEAD = HEAD; totalExport.HEAD = HEAD;
@ -291,6 +292,12 @@ GitEngine.prototype.instantiateFromTree = function(tree) {
this.branchCollection.add(branch, {silent: true}); this.branchCollection.add(branch, {silent: true});
}, this); }, this);
_.each(tree.tags, function(tagJSON) {
var tag = this.getOrMakeRecursive(tree, createdSoFar, tagJSON.id);
this.tagCollection.add(tag, {silent: true});
}, this);
var HEAD = this.getOrMakeRecursive(tree, createdSoFar, tree.HEAD.id); var HEAD = this.getOrMakeRecursive(tree, createdSoFar, tree.HEAD.id);
this.HEAD = HEAD; this.HEAD = HEAD;
@ -304,6 +311,9 @@ GitEngine.prototype.instantiateFromTree = function(tree) {
this.branchCollection.each(function(branch) { this.branchCollection.each(function(branch) {
this.gitVisuals.addBranch(branch); this.gitVisuals.addBranch(branch);
}, this); }, this);
this.tagCollection.each(function(tag) {
this.gitVisuals.addTag(tag);
}, this);
if (tree.originTree) { if (tree.originTree) {
var treeString = JSON.stringify(tree.originTree); var treeString = JSON.stringify(tree.originTree);
@ -438,6 +448,19 @@ GitEngine.prototype.getOrMakeRecursive = function(tree, createdSoFar, objID) {
return branch; return branch;
} }
if (type == 'tag') {
var tagJSON = tree.tags[objID];
var tag = new Tag(_.extend(
tree.tags[objID],
{
target: this.getOrMakeRecursive(tree, createdSoFar, tagJSON.target)
}
));
createdSoFar[objID] = tag;
return tag;
}
if (type == 'commit') { if (type == 'commit') {
// for commits, we need to grab all the parents // for commits, we need to grab all the parents
var commitJSON = tree.commits[objID]; var commitJSON = tree.commits[objID];
@ -485,6 +508,7 @@ GitEngine.prototype.reloadGraphics = function() {
GitEngine.prototype.removeAll = function() { GitEngine.prototype.removeAll = function() {
this.branchCollection.reset(); this.branchCollection.reset();
this.tagCollection.reset();
this.commitCollection.reset(); this.commitCollection.reset();
this.refs = {}; this.refs = {};
this.HEAD = null; this.HEAD = null;
@ -551,6 +575,20 @@ GitEngine.prototype.validateAndMakeBranch = function(id, target) {
this.makeBranch(id, target); this.makeBranch(id, target);
}; };
GitEngine.prototype.validateAndMakeTag = function(id, target) {
id = this.validateBranchName(id);
if (this.refs[id]) {
throw new GitError({
msg: intl.str(
'bad-tag-name',
{ tag: name }
)
});
}
this.makeTag(id, target);
};
GitEngine.prototype.makeBranch = function(id, target) { GitEngine.prototype.makeBranch = function(id, target) {
if (this.refs[id]) { if (this.refs[id]) {
throw new Error('woah already have that'); throw new Error('woah already have that');
@ -565,10 +603,37 @@ GitEngine.prototype.makeBranch = function(id, target) {
return branch; return branch;
}; };
GitEngine.prototype.makeTag = function(id, target) {
if (this.refs[id]) {
throw new Error('woah already have that');
}
var tag = new Tag({
target: target,
id: id
});
this.tagCollection.add(tag);
this.refs[tag.get('id')] = tag;
return tag;
};
GitEngine.prototype.getHead = function() { GitEngine.prototype.getHead = function() {
return _.clone(this.HEAD); return _.clone(this.HEAD);
}; };
GitEngine.prototype.getTags = function() {
var toReturn = [];
this.tagCollection.each(function(tag) {
toReturn.push({
id: tag.get('id'),
target: tag.get('target'),
remote: tag.getIsRemote(),
obj: tag
});
}, this);
return toReturn;
};
GitEngine.prototype.getBranches = function() { GitEngine.prototype.getBranches = function() {
var toReturn = []; var toReturn = [];
this.branchCollection.each(function(branch) { this.branchCollection.each(function(branch) {
@ -619,6 +684,17 @@ GitEngine.prototype.printBranches = function(branches) {
}); });
}; };
GitEngine.prototype.printTags = function(tags) {
var result = '';
_.each(tags, function(tag) {
console.log(tag);
result += tag.id + '\n';
});
throw new CommandResult({
msg: result
});
};
GitEngine.prototype.printRemotes = function(options) { GitEngine.prototype.printRemotes = function(options) {
var result = ''; var result = '';
if (options.verbose) { if (options.verbose) {
@ -2063,11 +2139,14 @@ GitEngine.prototype.checkout = function(idOrTarget) {
target = this.getCommitFromRef(target.get('id')); target = this.getCommitFromRef(target.get('id'));
} }
if (type !== 'branch' && type !== 'commit') { if (type !== 'branch' && type !== 'tag' && type !== 'commit') {
throw new GitError({ throw new GitError({
msg: intl.str('git-error-options') msg: intl.str('git-error-options')
}); });
} }
if (type === 'tag') {
target = target.get('target');
}
this.HEAD.set('target', target); this.HEAD.set('target', target);
}; };
@ -2102,6 +2181,11 @@ GitEngine.prototype.branch = function(name, ref) {
this.validateAndMakeBranch(name, target); this.validateAndMakeBranch(name, target);
}; };
GitEngine.prototype.tag = function(name, ref) {
var target = this.getCommitFromRef(ref);
this.validateAndMakeTag(name, target);
};
GitEngine.prototype.deleteBranch = function(name) { GitEngine.prototype.deleteBranch = function(name) {
// trying to delete, lets check our refs // trying to delete, lets check our refs
var target = this.resolveID(name); var target = this.resolveID(name);
@ -2603,8 +2687,20 @@ var Commit = Backbone.Model.extend({
} }
}); });
var Tag = Ref.extend({
defaults: {
visTag: null
},
initialize: function() {
Ref.prototype.initialize.call(this);
this.set('type', 'tag');
}
});
exports.GitEngine = GitEngine; exports.GitEngine = GitEngine;
exports.Commit = Commit; exports.Commit = Commit;
exports.Branch = Branch; exports.Branch = Branch;
exports.Tag = Tag;
exports.Ref = Ref; exports.Ref = Ref;

View file

@ -5,6 +5,7 @@ var Backbone = (!require('../util').isBrowser()) ? Backbone = require('backbone'
var Commit = require('../git').Commit; var Commit = require('../git').Commit;
var Branch = require('../git').Branch; var Branch = require('../git').Branch;
var Tag = require('../git').Tag;
var Command = require('../models/commandModel').Command; var Command = require('../models/commandModel').Command;
var CommandEntry = require('../models/commandModel').CommandEntry; var CommandEntry = require('../models/commandModel').CommandEntry;
@ -22,6 +23,10 @@ var BranchCollection = Backbone.Collection.extend({
model: Branch model: Branch
}); });
var TagCollection = Backbone.Collection.extend({
model: Tag
});
var CommandEntryCollection = Backbone.Collection.extend({ var CommandEntryCollection = Backbone.Collection.extend({
model: CommandEntry, model: CommandEntry,
localStorage: (Backbone.LocalStorage) ? new Backbone.LocalStorage('CommandEntries') : null localStorage: (Backbone.LocalStorage) ? new Backbone.LocalStorage('CommandEntries') : null
@ -125,6 +130,7 @@ var CommandBuffer = Backbone.Model.extend({
exports.CommitCollection = CommitCollection; exports.CommitCollection = CommitCollection;
exports.CommandCollection = CommandCollection; exports.CommandCollection = CommandCollection;
exports.BranchCollection = BranchCollection; exports.BranchCollection = BranchCollection;
exports.TagCollection = TagCollection;
exports.CommandEntryCollection = CommandEntryCollection; exports.CommandEntryCollection = CommandEntryCollection;
exports.CommandBuffer = CommandBuffer; exports.CommandBuffer = CommandBuffer;

View file

@ -33,6 +33,7 @@ var GRAPHICS = {
originDash: '- ', originDash: '- ',
multiBranchY: 20, multiBranchY: 20,
multiTagY: 15,
upstreamHeadOpacity: 0.5, upstreamHeadOpacity: 0.5,
upstreamNoneOpacity: 0.2, upstreamNoneOpacity: 0.2,
edgeUpstreamHeadOpacity: 0.4, edgeUpstreamHeadOpacity: 0.4,
@ -45,6 +46,10 @@ var GRAPHICS = {
defaultNodeStrokeWidth: 2, defaultNodeStrokeWidth: 2,
defaultNodeStroke: '#FFF', defaultNodeStroke: '#FFF',
tagFill: 'hsb(0,0,0.9)',
tagStroke: '#FFF',
tagStrokeWidth: '2',
orphanNodeFill: 'hsb(0.5,0.8,0.7)' orphanNodeFill: 'hsb(0.5,0.8,0.7)'
}; };

View file

@ -8,10 +8,13 @@ var GLOBAL = require('../util/constants').GLOBAL;
var Collections = require('../models/collections'); var Collections = require('../models/collections');
var CommitCollection = Collections.CommitCollection; var CommitCollection = Collections.CommitCollection;
var BranchCollection = Collections.BranchCollection; var BranchCollection = Collections.BranchCollection;
var TagCollection = Collections.TagCollection;
var VisNode = require('../visuals/visNode').VisNode; var VisNode = require('../visuals/visNode').VisNode;
var VisBranch = require('../visuals/visBranch').VisBranch; var VisBranch = require('../visuals/visBranch').VisBranch;
var VisBranchCollection = require('../visuals/visBranch').VisBranchCollection; var VisBranchCollection = require('../visuals/visBranch').VisBranchCollection;
var VisTag = require('../visuals/visTag').VisTag;
var VisTagCollection = require('../visuals/visTag').VisTagCollection;
var VisEdge = require('../visuals/visEdge').VisEdge; var VisEdge = require('../visuals/visEdge').VisEdge;
var VisEdgeCollection = require('../visuals/visEdge').VisEdgeCollection; var VisEdgeCollection = require('../visuals/visEdge').VisEdgeCollection;
@ -21,14 +24,17 @@ function GitVisuals(options) {
this.visualization = options.visualization; this.visualization = options.visualization;
this.commitCollection = options.commitCollection; this.commitCollection = options.commitCollection;
this.branchCollection = options.branchCollection; this.branchCollection = options.branchCollection;
this.tagCollection = options.tagCollection;
this.visNodeMap = {}; this.visNodeMap = {};
this.visEdgeCollection = new VisEdgeCollection(); this.visEdgeCollection = new VisEdgeCollection();
this.visBranchCollection = new VisBranchCollection(); this.visBranchCollection = new VisBranchCollection();
this.visTagCollection = new VisTagCollection();
this.commitMap = {}; this.commitMap = {};
this.rootCommit = null; this.rootCommit = null;
this.branchStackMap = null; this.branchStackMap = null;
this.tagStackMap = null;
this.upstreamBranchSet = null; this.upstreamBranchSet = null;
this.upstreamHeadSet = null; this.upstreamHeadSet = null;
@ -37,6 +43,10 @@ function GitVisuals(options) {
this.branchCollection.on('add', this.addBranchFromEvent, this); this.branchCollection.on('add', this.addBranchFromEvent, this);
this.branchCollection.on('remove', this.removeBranch, this); this.branchCollection.on('remove', this.removeBranch, this);
this.tagCollection.on('add', this.addTagFromEvent, this);
this.tagCollection.on('remove', this.removeTag, this);
this.deferred = []; this.deferred = [];
this.flipFraction = 0.65; this.flipFraction = 0.65;
@ -69,12 +79,18 @@ GitVisuals.prototype.resetAll = function() {
visBranch.remove(); visBranch.remove();
}, this); }, this);
var tags = this.visTagCollection.toArray();
_.each(tags, function(visTag) {
visTag.remove();
}, this);
_.each(this.visNodeMap, function(visNode) { _.each(this.visNodeMap, function(visNode) {
visNode.remove(); visNode.remove();
}, this); }, this);
this.visEdgeCollection.reset(); this.visEdgeCollection.reset();
this.visBranchCollection.reset(); this.visBranchCollection.reset();
this.visTagsCollection.reset();
this.visNodeMap = {}; this.visNodeMap = {};
this.rootCommit = null; this.rootCommit = null;
@ -178,6 +194,7 @@ GitVisuals.prototype.animateAllAttrKeys = function(keys, attr, speed, easing) {
this.visBranchCollection.each(animate); this.visBranchCollection.each(animate);
this.visEdgeCollection.each(animate); this.visEdgeCollection.each(animate);
this.visTagCollection.each(animate);
_.each(this.visNodeMap, animate); _.each(this.visNodeMap, animate);
var time = (speed !== undefined) ? speed : GRAPHICS.defaultAnimationTime; var time = (speed !== undefined) ? speed : GRAPHICS.defaultAnimationTime;
@ -334,6 +351,7 @@ GitVisuals.prototype.animateAllFromAttrToAttr = function(fromSnapshot, toSnapsho
this.visBranchCollection.each(animate); this.visBranchCollection.each(animate);
this.visEdgeCollection.each(animate); this.visEdgeCollection.each(animate);
this.visTagCollection.each(animate);
_.each(this.visNodeMap, animate); _.each(this.visNodeMap, animate);
}; };
@ -370,6 +388,10 @@ GitVisuals.prototype.genSnapshot = function() {
snapshot[visEdge.getID()] = visEdge.getAttributes(); snapshot[visEdge.getID()] = visEdge.getAttributes();
}, this); }, this);
this.visTagCollection.each(function(visTag) {
snapshot[visTag.getID()] = visTag.getAttributes();
}, this);
return snapshot; return snapshot;
}; };
@ -411,6 +433,7 @@ GitVisuals.prototype.calcTreeCoords = function() {
this.calcUpstreamSets(); this.calcUpstreamSets();
this.calcBranchStacks(); this.calcBranchStacks();
this.calcTagStacks();
this.calcDepth(); this.calcDepth();
this.calcWidth(); this.calcWidth();
@ -420,6 +443,9 @@ GitVisuals.prototype.calcGraphicsCoords = function() {
this.visBranchCollection.each(function(visBranch) { this.visBranchCollection.each(function(visBranch) {
visBranch.updateName(); visBranch.updateName();
}); });
this.visTagCollection.each(function(visTag) {
visTag.updateName();
});
}; };
GitVisuals.prototype.calcUpstreamSets = function() { GitVisuals.prototype.calcUpstreamSets = function() {
@ -496,6 +522,23 @@ GitVisuals.prototype.calcBranchStacks = function() {
this.branchStackMap = map; this.branchStackMap = map;
}; };
GitVisuals.prototype.calcTagStacks = function() {
var tags = this.gitEngine.getTags();
var map = {};
_.each(tags, function(tag) {
var thisId = tag.target.get('id');
map[thisId] = map[thisId] || [];
map[thisId].push(tag);
map[thisId].sort(function(a, b) {
var aId = a.obj.get('id');
var bId = b.obj.get('id');
return aId.localeCompare(bId);
});
});
this.tagStackMap = map;
};
GitVisuals.prototype.calcWidth = function() { GitVisuals.prototype.calcWidth = function() {
this.maxWidthRecursive(this.rootCommit); this.maxWidthRecursive(this.rootCommit);
@ -626,10 +669,44 @@ GitVisuals.prototype.addBranch = function(branch) {
} }
}; };
GitVisuals.prototype.addTagFromEvent = function(tag, collection, index) {
var action = _.bind(function() {
this.addTag(tag);
}, this);
if (!this.gitEngine || !this.gitReady) {
this.defer(action);
} else {
action();
}
};
GitVisuals.prototype.addTag = function(tag) {
var visTag = new VisTag({
tag: tag,
gitVisuals: this,
gitEngine: this.gitEngine
});
this.visTagCollection.add(visTag);
if (this.gitReady) {
visTag.genGraphics(this.paper);
} else {
this.defer(_.bind(function() {
visTag.genGraphics(this.paper);
}, this));
}
};
GitVisuals.prototype.removeVisBranch = function(visBranch) { GitVisuals.prototype.removeVisBranch = function(visBranch) {
this.visBranchCollection.remove(visBranch); this.visBranchCollection.remove(visBranch);
}; };
GitVisuals.prototype.removeVisTag = function(visTag) {
this.visTagCollection.remove(visTag);
};
GitVisuals.prototype.removeVisNode = function(visNode) { GitVisuals.prototype.removeVisNode = function(visNode) {
delete this.visNodeMap[visNode.getID()]; delete this.visNodeMap[visNode.getID()];
}; };
@ -642,6 +719,9 @@ GitVisuals.prototype.animateRefs = function(speed) {
this.visBranchCollection.each(function(visBranch) { this.visBranchCollection.each(function(visBranch) {
visBranch.animateUpdatedPos(speed); visBranch.animateUpdatedPos(speed);
}, this); }, this);
this.visTagCollection.each(function(visTag) {
visTag.animateUpdatedPos(speed);
}, this);
}; };
GitVisuals.prototype.animateEdges = function(speed) { GitVisuals.prototype.animateEdges = function(speed) {
@ -756,6 +836,7 @@ GitVisuals.prototype.addEdge = function(idTail, idHead) {
GitVisuals.prototype.zIndexReflow = function() { GitVisuals.prototype.zIndexReflow = function() {
this.visNodesFront(); this.visNodesFront();
this.visBranchesFront(); this.visBranchesFront();
this.visTagsFront();
}; };
GitVisuals.prototype.visNodesFront = function() { GitVisuals.prototype.visNodesFront = function() {
@ -775,6 +856,17 @@ GitVisuals.prototype.visBranchesFront = function() {
}); });
}; };
GitVisuals.prototype.visTagsFront = function() {
this.visTagCollection.each(function(vTag) {
vTag.nonTextToFront();
vTag.textToFront();
});
this.visTagCollection.each(function(vTag) {
vTag.textToFrontIfInStack();
});
};
GitVisuals.prototype.drawTreeFromReload = function() { GitVisuals.prototype.drawTreeFromReload = function() {
this.gitReady = true; this.gitReady = true;
// gen all the graphics we need // gen all the graphics we need
@ -799,6 +891,10 @@ GitVisuals.prototype.drawTreeFirstTime = function() {
visBranch.genGraphics(this.paper); visBranch.genGraphics(this.paper);
}, this); }, this);
this.visTagCollection.each(function(visTag) {
visTag.genGraphics(this.paper);
}, this);
this.zIndexReflow(); this.zIndexReflow();
}; };

View file

@ -130,6 +130,7 @@ var VisEdge = VisBase.extend({
var stat = this.gitVisuals.getCommitUpstreamStatus(this.get('tail')); var stat = this.gitVisuals.getCommitUpstreamStatus(this.get('tail'));
var map = { var map = {
'branch': 1, 'branch': 1,
'tag': 1,
'head': GRAPHICS.edgeUpstreamHeadOpacity, 'head': GRAPHICS.edgeUpstreamHeadOpacity,
'none': GRAPHICS.edgeUpstreamNoneOpacity 'none': GRAPHICS.edgeUpstreamNoneOpacity
}; };

View file

@ -74,6 +74,7 @@ var VisNode = VisBase.extend({
var stat = this.gitVisuals.getCommitUpstreamStatus(this.get('commit')); var stat = this.gitVisuals.getCommitUpstreamStatus(this.get('commit'));
var map = { var map = {
branch: 1, branch: 1,
tag: 1,
head: 0.3, head: 0.3,
none: 0.1 none: 0.1
}; };
@ -89,6 +90,7 @@ var VisNode = VisBase.extend({
getOpacity: function() { getOpacity: function() {
var map = { var map = {
'branch': 1, 'branch': 1,
'tag' : 1,
'head': GRAPHICS.upstreamHeadOpacity, 'head': GRAPHICS.upstreamHeadOpacity,
'none': GRAPHICS.upstreamNoneOpacity 'none': GRAPHICS.upstreamNoneOpacity
}; };

429
src/js/visuals/visTag.js Normal file
View file

@ -0,0 +1,429 @@
var _ = require('underscore');
var Backbone = require('backbone');
var GRAPHICS = require('../util/constants').GRAPHICS;
var VisBase = require('../visuals/visBase').VisBase;
var TreeCompare = require('../git/treeCompare').TreeCompare;
var randomHueString = function() {
var hue = Math.random();
var str = 'hsb(' + String(hue) + ',0.7,1)';
return str;
};
var VisTag = VisBase.extend({
defaults: {
pos: null,
text: null,
rect: null,
isHead: false,
fill: GRAPHICS.tagFill,
stroke: GRAPHICS.tagStroke,
'stroke-width': GRAPHICS.tagStrokeWidth,
offsetX: GRAPHICS.nodeRadius,
offsetY: GRAPHICS.nodeRadius,
vPad: 2,
hPad: 2,
animationSpeed: GRAPHICS.defaultAnimationTime,
animationEasing: GRAPHICS.defaultEasing
},
validateAtInit: function() {
if (!this.get('tag')) {
throw new Error('need a Tag!');
}
},
getID: function() {
return this.get('tag').get('id');
},
initialize: function() {
this.validateAtInit();
// shorthand notation for the main objects
this.gitVisuals = this.get('gitVisuals');
this.gitEngine = this.get('gitEngine');
if (!this.gitEngine) {
throw new Error('asd wtf');
}
this.get('tag').set('visTag', this);
},
getCommitPosition: function() {
var commit = this.gitEngine.getCommitFromRef(this.get('tag'));
var visNode = commit.get('visNode');
return visNode.getScreenCoords();
},
getDashArray: function() {
if (!this.get('gitVisuals').getIsGoalVis()) {
return '';
}
return (this.getIsLevelTagCompared()) ? '' : '--';
},
getIsGoalAndNotCompared: function() {
if (!this.get('gitVisuals').getIsGoalVis()) {
return false;
}
return !this.getIsLevelTagCompared();
},
/**
* returns true if we are a Tag that is not being
* compared in the goal (used in a goal visualization context
*/
getIsLevelTagCompared: function() {
if (this.getIsMaster()) {
return true; // master always compared
}
// we are not master, so return true if its not just master being compared
var levelBlob = this.get('gitVisuals').getLevelBlob();
return !TreeCompare.onlyMasterCompared(levelBlob);
},
getIsMaster: function() {
return this.get('tag').get('id') == 'master';
},
getArrowTransform: function() {
return 't2,20R-35';
},
getTagStackIndex: function() {
if (this.get('isHead')) {
// head is never stacked with other Tages
return 0;
}
var myArray = this.getTagStackArray();
var index = -1;
_.each(myArray, function(Tag, i) {
if (Tag.obj == this.get('tag')) {
index = i;
}
}, this);
return index;
},
getTagStackLength: function() {
if (this.get('isHead')) {
// head is always by itself
return 1;
}
return this.getTagStackArray().length;
},
isTagStackEmpty: function() {
// useful function for head when computing flip logic
var arr = this.gitVisuals.tagStackMap[this.getCommitID()];
return (arr) ?
arr.length === 0 :
true;
},
getCommitID: function() {
var target = this.get('tag').get('target');
if (target.get('type') === 'tag') {
// for HEAD
target = target.get('target');
}
return target.get('id');
},
getTagStackArray: function() {
var arr = this.gitVisuals.tagStackMap[this.getCommitID()];
if (arr === undefined) {
// this only occurs when we are generating graphics inside of
// a new Tag instantiation, so we need to force the update
this.gitVisuals.calcTagStacks();
return this.getTagStackArray();
}
return arr;
},
getTextPosition: function() {
var pos = this.getCommitPosition();
// then order yourself accordingly. we use alphabetical sorting
// so everything is independent
var myPos = this.getTagStackIndex();
return {
x: pos.x + this.get('offsetX'),
y: pos.y + myPos * GRAPHICS.multiTagY + this.get('offsetY')
};
},
getRectPosition: function() {
var pos = this.getTextPosition();
// first get text width and height
var textSize = this.getTextSize();
return {
x: pos.x - this.get('hPad'),
y: pos.y - 0.5 * textSize.h - this.get('vPad')
};
},
getTextSize: function() {
var getTextWidth = function(visTag) {
var textNode = (visTag.get('text')) ? visTag.get('text').node : null;
return (textNode === null) ? 0 : textNode.clientWidth;
};
var firefoxFix = function(obj) {
if (!obj.w) { obj.w = 75; }
if (!obj.h) { obj.h = 20; }
return obj;
};
var textNode = this.get('text').node;
var maxWidth = 0;
_.each(this.getTagStackArray(), function(Tag) {
maxWidth = Math.max(maxWidth, getTextWidth(
Tag.obj.get('visTag')
));
});
return firefoxFix({
w: maxWidth,
h: textNode.clientHeight
});
},
getSingleRectSize: function() {
var textSize = this.getTextSize();
var vPad = this.get('vPad');
var hPad = this.get('hPad');
return {
w: textSize.w + vPad * 2,
h: textSize.h + hPad * 2
};
},
getRectSize: function() {
var textSize = this.getTextSize();
// enforce padding
var vPad = this.get('vPad');
var hPad = this.get('hPad');
// number of other Tag names we are housing
var totalNum = this.getTagStackLength();
return {
w: textSize.w + vPad * 2,
h: textSize.h * totalNum + hPad * 2
};
},
getIsRemote: function() {
return this.get('tag').getIsRemote();
},
getName: function() {
var name = this.get('tag').getName();
var selected = this.get('tag') === this.gitEngine.HEAD.get('target');
var isRemote = this.getIsRemote();
var isHg = this.gitEngine.getIsHg();
if (name === 'HEAD' && isHg) {
name = '.';
}
var after = (selected && !this.getIsInOrigin() && !isRemote) ? '*' : '';
return name + after;
},
nonTextToFront: function() {
this.get('rect').toFront();
},
textToFront: function() {
this.get('text').toFront();
},
textToFrontIfInStack: function() {
if (this.getTagStackIndex() !== 0) {
this.get('text').toFront();
}
},
remove: function() {
this.removeKeys(['text', 'rect']);
// also need to remove from this.gitVisuals
this.gitVisuals.removeVisTag(this);
},
handleModeChange: function() {
},
genGraphics: function(paper) {
var textPos = this.getTextPosition();
var name = this.getName();
// when from a reload, we dont need to generate the text
var text = paper.text(textPos.x, textPos.y, String(name));
text.attr({
'font-size': 14,
'font-family': 'Monaco, Courier, font-monospace',
opacity: this.getTextOpacity(),
'text-anchor': 'start'
});
this.set('text', text);
var attr = this.getAttributes();
var rectPos = this.getRectPosition();
var sizeOfRect = this.getRectSize();
var rect = paper
.rect(rectPos.x, rectPos.y, sizeOfRect.w, sizeOfRect.h, 8)
.attr(attr.rect);
this.set('rect', rect);
// set CSS
var keys = ['text', 'rect'];
_.each(keys, function(key) {
$(this.get(key).node).css(attr.css);
}, this);
this.attachClickHandlers();
rect.toFront();
text.toFront();
},
attachClickHandlers: function() {
if (this.get('gitVisuals').options.noClick) {
return;
}
var objs = [
this.get('rect'),
this.get('text')
];
_.each(objs, function(rObj) {
rObj.click(_.bind(this.onClick ,this));
}, this);
},
shouldDisableClick: function() {
return this.get('isHead') && !this.gitEngine.getDetachedHead();
},
onClick: function() {
if (this.shouldDisableClick()) {
return;
}
var commandStr = 'git checkout ' + this.get('tag').get('id');
var Main = require('../app');
Main.getEventBaton().trigger('commandSubmitted', commandStr);
},
updateName: function() {
this.get('text').attr({
text: this.getName()
});
},
getNonTextOpacity: function() {
if (this.get('isHead')) {
return this.gitEngine.getDetachedHead() ? 1 : 0;
}
if (this.getTagStackIndex() !== 0) {
return 0.0;
}
return 1;
},
getTextOpacity: function() {
if (this.get('isHead')) {
return this.gitEngine.getDetachedHead() ? 1 : 0;
}
if (this.getIsGoalAndNotCompared()) {
return (this.getTagStackIndex() === 0) ? 0.7 : 0.3;
}
return 1;
},
getStrokeWidth: function() {
if (this.getIsGoalAndNotCompared()) {
return this.get('stroke-width') / 5.0;
}
return this.get('stroke-width');
},
getAttributes: function() {
var textOpacity = this.getTextOpacity();
this.updateName();
var textPos = this.getTextPosition();
var rectPos = this.getRectPosition();
var rectSize = this.getRectSize();
var dashArray = this.getDashArray();
var cursorStyle = (this.shouldDisableClick()) ?
'auto' :
'pointer';
return {
css: {
cursor: cursorStyle
},
text: {
x: textPos.x,
y: textPos.y,
opacity: textOpacity
},
rect: {
x: rectPos.x,
y: rectPos.y,
width: rectSize.w,
height: rectSize.h,
opacity: this.getNonTextOpacity(),
fill: this.get('fill'),
stroke: this.get('stroke'),
'stroke-dasharray': dashArray,
'stroke-width': this.getStrokeWidth()
}
};
},
animateUpdatedPos: function(speed, easing) {
var attr = this.getAttributes();
this.animateToAttr(attr, speed, easing);
},
animateFromAttrToAttr: function(fromAttr, toAttr, speed, easing) {
// an animation of 0 is essentially setting the attribute directly
this.animateToAttr(fromAttr, 0);
this.animateToAttr(toAttr, speed, easing);
},
setAttr: function(attr, instant, speed, easing) {
var keys = ['text', 'rect'];
this.setAttrBase(keys, attr, instant, speed, easing);
}
});
var VisTagCollection = Backbone.Collection.extend({
model: VisTag
});
exports.VisTagCollection = VisTagCollection;
exports.VisTag = VisTag;
exports.randomHueString = randomHueString;

View file

@ -5,6 +5,7 @@ var Backbone = (!require('../util').isBrowser()) ? Backbone = require('backbone'
var Collections = require('../models/collections'); var Collections = require('../models/collections');
var CommitCollection = Collections.CommitCollection; var CommitCollection = Collections.CommitCollection;
var BranchCollection = Collections.BranchCollection; var BranchCollection = Collections.BranchCollection;
var TagCollection = Collections.TagCollection;
var EventBaton = require('../util/eventBaton').EventBaton; var EventBaton = require('../util/eventBaton').EventBaton;
var GitVisuals = require('../visuals').GitVisuals; var GitVisuals = require('../visuals').GitVisuals;
@ -43,10 +44,12 @@ var Visualization = Backbone.View.extend({
this.commitCollection = new CommitCollection(); this.commitCollection = new CommitCollection();
this.branchCollection = new BranchCollection(); this.branchCollection = new BranchCollection();
this.tagCollection = new TagCollection();
this.gitVisuals = new GitVisuals({ this.gitVisuals = new GitVisuals({
commitCollection: this.commitCollection, commitCollection: this.commitCollection,
branchCollection: this.branchCollection, branchCollection: this.branchCollection,
tagCollection: this.tagCollection,
paper: this.paper, paper: this.paper,
noClick: this.options.noClick, noClick: this.options.noClick,
isGoalVis: this.options.isGoalVis, isGoalVis: this.options.isGoalVis,
@ -58,6 +61,7 @@ var Visualization = Backbone.View.extend({
this.gitEngine = new GitEngine({ this.gitEngine = new GitEngine({
collection: this.commitCollection, collection: this.commitCollection,
branches: this.branchCollection, branches: this.branchCollection,
tags: this.tagCollection,
gitVisuals: this.gitVisuals, gitVisuals: this.gitVisuals,
eventBaton: this.eventBaton eventBaton: this.eventBaton
}); });