Showing 4 changed files with 194 additions and 28 deletions
+4 -1
lib/Gitprep.pm
... ...
@@ -192,9 +192,12 @@ sub startup {
192 192
         
193 193
         # Blob
194 194
         $r->get('/blob/*rev_file' => template '/blob');
195
-        
195
+
196 196
         # Raw
197 197
         $r->get('/raw/*rev_file' => template '/raw');
198
+
199
+        # Blame
200
+        $r->get('/blame/*rev_file' => template '/blame');
198 201
         
199 202
         # Archive
200 203
         $r->get('/archive/(*rev).tar.gz' => template '/archive')->to(archive_type => 'tar');
+86 -26
lib/Gitprep/Git.pm
... ...
@@ -155,6 +155,92 @@ sub authors {
155 155
   return [sort keys %$authors];
156 156
 }
157 157
 
158
+sub blame {
159
+  my ($self, $user, $project, $rev, $file) = @_;
160
+  
161
+  # Blob
162
+  my $hash = $self->path_to_hash($user, $project, $rev, $file, 'blob')
163
+    or croak 'Cannot find file';
164
+  
165
+  # Git blame
166
+  my @cmd = $self->cmd(
167
+    $user,
168
+    $project,
169
+    'blame',
170
+    '--line-porcelain',
171
+    $rev,
172
+    '--',
173
+    $file
174
+  );
175
+  open my $fh, '-|', @cmd
176
+    or croak "Can't git blame --line-porcelain";
177
+  
178
+  # Format lines
179
+  my $blame_lines = [];
180
+  my $blame_line;
181
+  while (my $line = $self->_dec(scalar <$fh>)) {
182
+    warn $line;
183
+    chomp $line;
184
+    
185
+    if ($blame_line) {
186
+      if ($line =~ /^author +(.+)/) {
187
+        $blame_line->{author} = $1;
188
+      }
189
+      elsif ($line =~ /^author-mail +(.+)/) {
190
+        $blame_line->{author_mail} = $1;
191
+      }
192
+      elsif ($line =~ /^summary +(.+)/) {
193
+        $blame_line->{summary} = $1;
194
+      }
195
+      elsif ($line =~ /^\t(.+)?/) {
196
+        my $content = $1;
197
+        $content = '' unless defined $content;
198
+        $blame_line->{content} = $content;
199
+        push @$blame_lines, $blame_line;
200
+        $blame_line = undef;
201
+      }
202
+    }
203
+    elsif ($line =~ /^([a-fA-F0-9]{40}) +\d+ +(\d+)/) {
204
+      $blame_line = {};
205
+      $blame_line->{commit} = $1;
206
+      $blame_line->{line} = $2;
207
+      if ($blame_lines->[-1]
208
+        && $blame_lines->[-1]{commit} eq $blame_line->{commit})
209
+      {
210
+        $blame_line->{before_same_commit} = 1;
211
+      }
212
+    }
213
+  }
214
+  
215
+  return $blame_lines;
216
+}
217
+
218
+sub blob {
219
+  my ($self, $user, $project, $rev, $file) = @_;
220
+  
221
+  # Blob
222
+  my $hash = $self->path_to_hash($user, $project, $rev, $file, 'blob')
223
+    or croak 'Cannot find file';
224
+  my @cmd = $self->cmd(
225
+    $user,
226
+    $project,
227
+    'cat-file',
228
+    'blob',
229
+    $hash
230
+  );
231
+  open my $fh, '-|', @cmd
232
+    or croak "Can't cat $file, $hash";
233
+  
234
+  # Format lines
235
+  my $lines =[];
236
+  while (my $line = $self->_dec(scalar <$fh>)) {
237
+    chomp $line;
238
+    push @$lines, $line;
239
+  }
240
+  
241
+  return $lines;
242
+}
243
+
158 244
 sub blob_diffs {
159 245
   my ($self, $user, $project, $rev1, $rev2, $diff_trees) = @_;
160 246
   
... ...
@@ -240,32 +326,6 @@ sub blob_diffs {
240 326
   return $blob_diffs;
241 327
 }
242 328
 
243
-sub blob {
244
-  my ($self, $user, $project, $rev, $file) = @_;
245
-  
246
-  # Blob
247
-  my $hash = $self->path_to_hash($user, $project, $rev, $file, 'blob')
248
-    or croak 'Cannot find file';
249
-  my @cmd = $self->cmd(
250
-    $user,
251
-    $project,
252
-    'cat-file',
253
-    'blob',
254
-    $hash
255
-  );
256
-  open my $fh, '-|', @cmd
257
-    or croak "Can't cat $file, $hash";
258
-  
259
-  # Format lines
260
-  my $lines =[];
261
-  while (my $line = $self->_dec(scalar <$fh>)) {
262
-    chomp $line;
263
-    push @$lines, $line;
264
-  }
265
-  
266
-  return $lines;
267
-}
268
-
269 329
 sub blob_is_image {
270 330
   my ($self, $user, $project, $rev, $file) = @_;
271 331
   
+101
templates/blame.html.ep
... ...
@@ -0,0 +1,101 @@
1
+<%
2
+  # API
3
+  my $api = gitprep_api;
4
+
5
+  # Git
6
+  my $git = $self->app->git;
7
+  
8
+  # Parameters
9
+  my $user = param('user');
10
+  my $project = param('project');
11
+  my $rev_file = param('rev_file');
12
+  my ($rev, $file) = $git->parse_rev_path($user, $project, $rev_file);
13
+
14
+  # Commit
15
+  my $commit = $git->last_change_commit($user, $project, $rev, $file);
16
+  
17
+  # Authors
18
+  my $authors = $git->authors($user, $project, $rev, $file);
19
+  
20
+  # File size
21
+  my $file_size = $git->blob_size($user, $project, $rev, $file);
22
+
23
+  # File mode
24
+  my $mode = $git->blob_mode($user, $project, $rev, $file);
25
+  my $file_type = $git->file_type_long($mode);
26
+
27
+  # MIME type
28
+  my $mime_type = $git->blob_mime_type($user, $project, $rev, $file);
29
+
30
+  # Blame
31
+  my $lines = $git->blame($user, $project, $rev, $file);
32
+
33
+  # Variables for included template
34
+  stash(id => $rev, project => $project, rev => $rev);
35
+%>
36
+
37
+% layout 'common' , title => "$project/$file at $rev \x{30fb} $user/$project";
38
+
39
+%
40
+  %= include '/include/header';
41
+
42
+  <div class="container">
43
+    %= include '/include/project_header';
44
+    %= include '/include/code_menu', display => 'files';
45
+    %= include '/include/page_path', type => 'blob', Path => $file;
46
+        
47
+    <div class="border-gray" style="margin-bottom:20px">
48
+      <div class="bk-blue-light" style="padding:5px">
49
+        <a style="color:#333;font-weight:bold" href="#" title="<%= $commit->{author_email} %>"><%= $commit->{author_name} %></a>
50
+        <span class="muted" title="<%= $commit->{age_string_datetime} %>"><%= $commit->{age_string} %></span>
51
+        <a style="color:#666" href="<%= url_for("/$user/$project/commit/$rev") %>">
52
+          <%= $commit->{title} %>
53
+        </a>
54
+      </div>
55
+      <div style="padding:5px">
56
+        <b><%= @$authors %></b> <span class="muted">contributor</span>
57
+      </div>
58
+    </div>
59
+
60
+    <div class="border-gray bk-gray-light" style="padding:5px">
61
+      <div class="row">
62
+        <div class="span7" style="padding-top:5px">
63
+          <i class="icon-file icon-white"></i>
64
+          <%= $file_type %>
65
+          <span class="muted">|</span>
66
+          <%= @$lines %> lines
67
+          <span class="muted">|</span>
68
+          <%= $file_size %>kb
69
+        </div>
70
+        <div class="text-right">
71
+          <a class="btn" href="<%= url_for("/$user/$project/raw/$rev/$file") %>">Raw</a>
72
+          <a class="btn" href="<%= url_for("/$user/$project/blame/$rev/$file") %>">Normal View</a>
73
+          <a class="btn" href="<%= url_for("/$user/$project/commits/$rev/$file") %>">History</a>
74
+        </div>
75
+      </div>
76
+    </div>
77
+    % if ($mime_type =~ m#^image/#) {
78
+      <div style="background:#ddd;text-align:center;padding-top:30px;padding-bottom:30px;margin-bottom:30px">
79
+        <img type="<%= $mime_type %>
80
+          % if (defined $file) {
81
+            alt="<%= $file %>" title="<%= $file %>"
82
+          % }
83
+          src="<%= url_for("/$user/$project/raw/$rev/$file") %>"
84
+        />
85
+      </div>
86
+    % } elsif ($mime_type =~ m#^text/#) {
87
+      <pre class="prettyprint linenums"><% for my $line (@$lines) { %><%= "$line\n" %><% } %></pre>
88
+    % } else {
89
+      <div style="font-size:16px;background:#ddd;text-align:center;padding-top:30px;padding-bottom:30px;margin-bottom:30px">
90
+        <a href="<%= url_for("/$user/$project/raw/$rev/$file") %>">View raw</a>
91
+      </div>
92
+    % }
93
+  </div>
94
+  
95
+  %= javascript '/js/google-code-prettify/prettify.js';
96
+  %= javascript begin
97
+    // Google prety print
98
+    prettyPrint();
99
+  % end
100
+  
101
+  %= include '/include/footer';
+3 -1
templates/blob.html.ep
... ...
@@ -69,7 +69,9 @@
69 69
           <%= $file_size %>kb
70 70
         </div>
71 71
         <div class="text-right">
72
-          <a class="btn" href="<%= url_for("/$user/$project/raw/$rev/$file") %>">Raw</a><a class="btn" href="<%= url_for("/$user/$project/commits/$rev/$file") %>">History</a>
72
+          <a class="btn" href="<%= url_for("/$user/$project/raw/$rev/$file") %>">Raw</a>
73
+          <a class="btn" href="<%= url_for("/$user/$project/blame/$rev/$file") %>">Blame</a>
74
+          <a class="btn" href="<%= url_for("/$user/$project/commits/$rev/$file") %>">History</a>
73 75
         </div>
74 76
       </div>
75 77
     </div>