Gitweb with 'side-by-side' diff  

Update: With git v1.7.9.5, this hack below is not needed anymore. Gitweb comes with sidebyside diff.

Bonus: Simultaneously scroll left and right files with overflow.

Dependencies: jQuery

Usage: Simply include jQuery in gitweb/gitweb.cgi (within the head tag).

Future work: Going by this thread on kerneltrap.org, ‘side-by-side’ diff appears to have been on the team’s TODO list and never gotton around to being implemented. So, except for the simultaneous scrolling, I intend to port this to Perl and hope to get it integrated into Gitweb core. Simultaneous scrolling needs to be done in Javascript and the fact that external dependencies are frowned upon,

Note that we frown upon introducing extra dependencies for gitweb, unless they are optional, and best detected automatically.

scrolling needs to be handled separately in gitweb.js (the currently implementation depends on jQuery).

Screenshot: Gitweb side-by-side diff

Gist: https://gist.github.com/1274726

// See http://saicharan.in/blog/2011/10/09/gitweb-with-sidebyside-diff/
$(document).ready(function() {
// Assumption: rem for left file, add for right file.
// => This follows from the assumption that additions
// are possible only for the right file.
$(".patch").each( function( name ) {
$.divp = $('<div/>', { class: "patch", id: $(this).attr('id') }); // div patch
$.diva = $('<div/>', { class: "patch a", id: $(this).attr('id')+'-a' }); // div for left file
$.divb = $('<div/>', { class: "patch b", id: $(this).attr('id')+'-b' }); // div for right file
$.divn = $('<div/>', { class: "no-exist" }).html('&#160;'); // div for non-existent lines
// Insert &nbsp; This seems to be the only way this works
$.diva_count = 0;
$.divb_count = 0;
$.diff_prev = 'none';
$.adjustDiff = function() {
if( $.diff_prev.match(/^add$/) || $.diff_prev.match(/^rem$/) ) {
if( $.diva_count - $.divb_count < 0 ) {
for(i=0; i<$.divb_count-$.diva_count; i++) {
$.diva.append($.divn.clone());
}
} else if( $.diva_count - $.divb_count > 0 ) {
for(i=0; i<$.diva_count-$.divb_count; i++) {
$.divb.append($.divn.clone());
}
}
$.diva_count = 0;
}
}
// If div class is 'diff rem', append to left
// If div class is 'diff add', append to right
// if div class is simply 'diff', append to both.
// Hunks have - & + lines.
$(this).children().each( function(name, value){
if( $(this).attr("class").match(/^diff$/) ) { // Plain block: Add to both sides
$.adjustDiff();
$.divb_count = 0;
$.diva.append($(this));
$.divb.append($(this).clone());
$.diff_prev = 'none';
} else if( $(this).attr("class").match(/^diff chunk_header$/) ) { // chunk header
$.diva.append($(this));
$.divb.append($(this).clone());
$.diff_prev = 'none';
} else if( $(this).attr("class").match(/^diff rem$/) ) { // Remove block
if( $.diff_prev.match(/^add$/) ){
$.adjustDiff();
}
$.diva.append($(this));
$.diva_count++;
$.diff_prev = 'rem';
} else if( $(this).attr("class").match(/^diff add$/) ) { // Add block
$.divb.append($(this));
if( $.diff_prev.match(/^rem$/) ) {
$.divb_count = 0;
}
$.divb_count++;
$.diff_prev = 'add';
} else { // Not part of diff, but of preamble.
$.divp.append($(this));
$.diff_prev = 'none';
}
});
$.adjustDiff(); // Cleanup, when necessary
$.divp.append( $.diva ); // Append diva, divb to divp
$.divp.append( $.divb );
$(this).replaceWith($.divp); // Replace this with divp
// Iterating thru' all .patch divs again. Can be refactored.
$(".patch").filter(function() { // Enable simultaneous scroll
return this.id.match(/patch[1-9]+-./);
}).each( function() {
var ids = this.id.match(/patch([1-9]+)-(.)/);
var otherdiv = ( ids[2].match(/^a$/) ? "b" : "a" );
var scrollid = "#patch" + ids[1] + "-" + otherdiv;
$(this).scroll( function() {
$(scrollid).scrollLeft( $(this).scrollLeft() );
});
});
});
});
view raw diff.js hosted with ❤ by GitHub
/* Append this to gitweb-HOME/static/gitweb.css, or insert it using JS */
div.patch.a {
width: 50%;
overflow-y: hidden;
overflow-x: auto;
padding-bottom: 10px;
position: relative;
float: left;
clear: both;
}
div.patch.b {
width: 50%;
overflow-y: hidden;
overflow-x: auto;
padding-bottom: 10px;
position: relative;
float: right;
clear: right;
}
div.patch {
clear: both;
}
div.page_footer {
clear: both;
}
div.diff.add {
background-color: #F6F9ED;
}
div.diff.rem {
background-color: #FEF1E9;
}
div.no-exist {
background-color: #EBECE4;
width: 100%;
}
view raw gitweb.css hosted with ❤ by GitHub