Newer Older
783 lines | 19.004kb
renamed gitpub to gitprep
Yuki Kimoto authored on 2012-11-26
1
package Gitprep::Main;
copy gitweblite soruce code
root authored on 2012-11-23
2
use Mojo::Base 'Mojolicious::Controller';
3
use File::Basename 'dirname';
4
use Carp 'croak';
5

            
6
sub blob {
7
  my $self = shift;
8

            
9
  # Parameters
10
  my $project_ns = $self->param('project');
11
  my $project = "/$project_ns";
12
  my $home_ns = dirname $project_ns;
13
  my $home = "/$home_ns";
14
  my $id_file = $self->param('id_file');
15

            
16
  # Id and file
17
  my ($id, $file) = $self->_parse_id_path($project, $id_file);
18

            
19
  # Git
20
  my $git = $self->app->git;
21

            
22
  # Blob content
23
  my $bid = $git->id_by_path($project, $id, $file, 'blob')
24
    or croak 'Cannot find file';
25
  my @cmd = ($git->cmd($project), 'cat-file', 'blob', $bid);
26
  open my $fh, '-|', @cmd
27
    or croak qq/Couldn't cat "$file", "$bid"/;
28
  
29
  # Blob plain
30
  if ($self->stash('plain')) {
31
    # Content type
32
    my $type = $git->blob_contenttype($fh, $file);
33

            
34
    # Convert text/* content type to text/plain
35
    if ($self->config('prevent_xss') &&
36
      ($type =~ m#^text/[a-z]+\b(.*)$# ||
37
      ($type =~ m#^[a-z]+/[a-z]\+xml\b(.*)$# && -T $fh)))
38
    {
39
      my $rest = $1;
40
      $rest = defined $rest ? $rest : '';
41
      $type = "text/plain$rest";
42
    }
43

            
44
    # File name
45
    my $file_name = $id;
46
    if (defined $file) { $file_name = $file }
47
    elsif ($type =~ m/^text\//) { $file_name .= '.txt' }
48
    
49
    # Content
50
    my $content = do { local $/; <$fh> };
51
    my $sandbox = $self->config('prevent_xss') &&
52
      $type !~ m#^(?:text/[a-z]+|image/(?:gif|png|jpeg))(?:[ ;]|$)#;
53
    my $content_disposition = $sandbox ? 'attachment' : 'inline';
54
    $content_disposition .= "; filename=$file_name";
55
    
56
    # Render
57
    $self->res->headers->content_disposition($content_disposition);
58
    $self->res->headers->content_type($type);
59
    $self->render_data($content);
60
  }
61
  
62
  # Blob
63
  else {
64
    # MIME type
65
    my $mimetype = $git->blob_mimetype($fh, $file);
66
    
67
    # Redirect to blob-plain if no display MIME type
68
    if ($mimetype !~ m#^(?:text/|image/(?:gif|png|jpeg)$)# && -B $fh) {
69
      close $fh;
70
      my $url = $self->url_for('blob_plain',
71
        project => $project_ns, id_file => "$id/$file");
72
      
73
      return $self->redirect_to($url);
74
    }
75
    
76
    # Commit
77
    my $commit = $git->parse_commit($project, $id);
78

            
79
    # Parse line
80
    my @lines;
81
    while (my $line = $git->dec(scalar <$fh>)) {
82
      chomp $line;
83
      $line = $git->_tab_to_space($line);
84
      push @lines, $line;
85
    }
86
    
87
    # Render
88
    $self->render(
89
      home => $home,
90
      home_ns => $home_ns,
91
      project => $project,
92
      project_ns => $project_ns,
93
      commit => $commit,
94
      id => $id,
95
      file => $file,
96
      bid => $bid,
97
      lines => \@lines,
98
      mimetype => $mimetype
99
    );
100
  }
101
}
102

            
103
sub blobdiff {
104
  my $self = shift;
105

            
106
  # Parameters
107
  my $project_ns = $self->param('project');
108
  my $project = "/$project_ns";
109
  my $home_ns = dirname $project_ns;
110
  my $home = "/$home_ns";
111
  my $diff = $self->param('diff');
112
  my $file = $self->param('file');
113
  my $from_file = $self->param('from-file');
114
  $from_file = $file unless defined $from_file;
115
  my $plain = $self->param('plain');
116
  my $from_id;
117
  my $id;
118
  if ($diff =~ /\.\./) { ($from_id, $id) = $diff =~ /(.+)\.\.(.+)/ }
119
  else { $id = $diff }
120
  
121
  # Git
122
  my $git = $self->app->git;
123

            
124
  # Get blob diff (command "git diff")
125
  open my $fh, '-|', $git->cmd($project), 'diff', '-r', '-M', '-p',
126
      $from_id, $id, '--', $from_file, $file
127
    or croak "Open git-diff-tree failed";
128
  
129
  # Blob diff plain
130
  if ($plain) {
131
    # Content
132
    my $content = do { local $/; <$fh> };
133
    close $fh;
134
    
135
    # Render
136
    my $content_disposition .= "inline; filename=$file";
137
    $self->res->headers->content_disposition($content_disposition);
138
    $self->res->headers->content_type("text/plain; charset=" . $git->encoding);
139
    $self->render(data => $content);
140
  }
141
  
142
  # Blob diff
143
  else {
144
    # Lines
145
    my @lines = map { $git->dec($_) } <$fh>;
146
    close $fh;
147
    my $lines = $self->_parse_blobdiff_lines(\@lines);
148
    
149
    # Commit
150
    my $commit = $git->parse_commit($project, $id);
151
    
152
    # Render
153
    $self->render(
154
      '/blobdiff',
155
      home => $home,
156
      home_ns => $home_ns,
157
      project => $project,
158
      project_ns => $project_ns,
159
      id => $id,
160
      from_id => $from_id,
161
      file => $file,
162
      from_file => $from_file,
163
      commit => $commit,
164
      lines => $lines
165
    );
166
  }
167
}
168

            
169
sub commit {
170
  my $self = shift;
171

            
172
  # Parameters
173
  my $project_ns = $self->param('project');
174
  my $project = "/$project_ns";
175
  my $home_ns = dirname $project_ns;
176
  my $home = "/$home_ns";
177
  my $id = $self->param('id');
178
  
179
  # Git
180
  my $git = $self->app->git;
181

            
182
  # Commit
183
  my $commit = $git->parse_commit($project, $id);
184
  my $committer_date
185
    = $git->parse_date($commit->{committer_epoch}, $commit->{committer_tz});
186
  my $author_date
187
    = $git->parse_date($commit->{author_epoch}, $commit->{author_tz});
188
  $commit->{author_date} = $git->timestamp($author_date);
189
  $commit->{committer_date} = $git->timestamp($committer_date);
190
  
191
  # References
192
  my $refs = $git->references($project);
193
  
194
  # Diff tree
195
  my $parent = $commit->{parent};
196
  my $parents = $commit->{parents};
197
  my $difftrees = $git->difftree($project, $commit->{id}, $parent, $parents);
198
  
199
  # Render
200
  $self->render(
201
    home => $home,
202
    home_ns => $home_ns,
203
    project => $project,
204
    project_ns => $project_ns,
205
    id => $id,
206
    commit => $commit,
207
    refs => $refs,
208
    difftrees => $difftrees,
209
  );
210
}
211

            
212
sub commitdiff {
213
  my $self = shift;
214
  
215
  # Paramters
216
  my $project_ns = $self->param('project');
217
  my $project = "/$project_ns";
218
  my $home_ns = dirname $project_ns;
219
  my $home = "/$home_ns";
220
  my $diff = $self->param('diff');
221
  my ($from_id, $id) = $diff =~ /(.+)\.\.(.+)/;
222
  $id = $diff unless defined $id;
223
  
224
  # Git
225
  my $git = $self->app->git;
226
  
227
  # Commit
228
  my $commit = $git->parse_commit($project, $id)
229
    or croak 'Unknown commit object';
230
  my $author_date
231
    = $git->parse_date($commit->{author_epoch}, $commit->{author_tz});
232
  my $committer_date
233
    = $git->parse_date($commit->{committer_epoch}, $commit->{committer_tz});
234
  $commit->{author_date} = $git->timestamp($author_date);
235
  $commit->{committer_date} = $git->timestamp($committer_date);
236
  $from_id = $commit->{parent} unless defined $from_id;
237
  
238
  # Plain text
239
  if ($self->param('plain')) {
240
    # Get blob diffs (command "git diff-tree")
241
    my @cmd = ($git->cmd($project), 'diff-tree', '-r', '-M',
242
        '-p', $from_id, $id, '--');
243
    open my $fh, '-|', @cmd
244
      or croak 'Open git-diff-tree failed';
245

            
246
    # Content
247
    my $content = do { local $/; <$fh> };
248
    my $content_disposition .= "inline; filename=$id";
249
    
250
    # Render
251
    $self->res->headers->content_disposition($content_disposition);
252
    $self->res->headers->content_type("text/plain;charset=" . $git->encoding);
253
    $self->render_data($content);
254
  }
255
  
256
  # HTML
257
  else {
258
    
259
    # Diff tree
260
    my $difftrees = $git->difftree($project,
261
      $id, $commit->{parent}, $commit->{parents});
262
    
263
    # Get blob diffs (command "git diff-tree")
264
    my @cmd = ($git->cmd($project), 'diff-tree', '-r', '-M',
265
      '--no-commit-id', '--patch-with-raw', $from_id, $id, '--');
266
    open my $fh, '-|', @cmd
267
      or croak 'Open git-diff-tree failed';
268

            
269
    # Parse output
270
    my @blobdiffs;
271
    while (my $line = $git->dec(scalar <$fh>)) {
272
      
273
      # Parse line
274
      chomp $line;
275
      my $diffinfo = $git->parse_difftree_raw_line($line);
276
      my $from_file = $diffinfo->{from_file};
277
      my $file = $diffinfo->{to_file};
278
      
279
      # Get blobdiff (command "git diff-tree")
280
      my @cmd = ($git->cmd($project), 'diff-tree', '-r', '-M', '-p',
281
        $from_id, $id, '--', (defined $from_file ? $from_file : ()), $file);
282
      open my $fh_blobdiff, '-|', @cmd
283
        or croak 'Open git-diff-tree failed';
284
      my @lines = map { $git->dec($_) } <$fh>;
285
      close $fh_blobdiff;
286
      my $blobdiff = {
287
        file => $file,
288
        from_file => $from_file,
289
        lines => $self->_parse_blobdiff_lines(\@lines)
290
      };
291
      
292
      # Status
293
      for my $difftree (@$difftrees) {
294
        if ($difftree->{to_file} eq $file) {
295
          $blobdiff->{status} = $difftree->{status};
296
          last;
297
        }
298
      }
299
      
300
      push @blobdiffs, $blobdiff;
301
    }
302

            
303
    # References
304
    my $refs = $git->references($project);
305
    
306
    # Render
307
    $self->render(
308
      'commitdiff',
309
      home => $home,
310
      home_ns => $home_ns,
311
      project => $project,
312
      project_ns => $project_ns,
313
      from_id => $from_id,
314
      id => $id,
315
      commit => $commit,
316
      difftrees => $difftrees,
317
      blobdiffs => \@blobdiffs,
318
      refs => $refs
319
    );
320
  }
321
}
322

            
323
sub home {
324
  my $self = shift;
325

            
Dipslay users in home page
Yuki Kimoto authored on 2012-11-23
326
  my $rep_home = '/gitpub';
327
  my @users = qw/kimoto ken/;
copy gitweblite soruce code
root authored on 2012-11-23
328

            
Dipslay users in home page
Yuki Kimoto authored on 2012-11-23
329
  $self->render(users => \@users);
330
}
copy gitweblite soruce code
root authored on 2012-11-23
331

            
332
sub heads {
333
  my $self = shift;
334
  
335
  # Parameters
336
  my $project_ns = $self->param('project');
337
  my $project = "/$project_ns";
338
  my $home_ns = dirname $project_ns;
339
  my $home = "/$home_ns";
340
  
341
  # Git
342
  my $git = $self->app->git;
343
  
344
  # Ref names
345
  my $heads  = $git->heads($project);
346
  
347
  # Render
348
  $self->render(
349
    home => $home,
350
    home_ns => $home_ns,
351
    project => $project,
352
    project_ns => $project_ns,
353
    heads => $heads,
354
  );
355
}
356

            
357
sub log {
358
  my ($self, %opt) = @_;
359

            
360
  # Parameters
361
  my $project_ns = $self->param('project');
362
  my $project = "/$project_ns";
363
  my $home_ns = dirname $project_ns;
364
  my $home = "/$home_ns";
365
  my $id = $self->param('id');
366
  my $page = $self->param('page');
367
  $page = 0 if !defined $page;
368
  my $short = $self->param('short');
369
  
370
  # Git
371
  my $git = $self->app->git;
372
  
373
  # Commit
374
  my $commit = $git->parse_commit($project, $id);
375
  
376
  # Commits
377
  my $page_count = $short ? 50 : 20;
378
  my $commits = $git->parse_commits(
379
    $project, $commit->{id},$page_count, $page_count * $page);
380
  for my $commit (@$commits) {
381
    my $author_date
382
      = $git->parse_date($commit->{author_epoch}, $commit->{author_tz});
383
    $commit->{author_date} = $git->timestamp($author_date);
384
  }
385
  
386
  # References
387
  my $refs = $git->references($project);
388

            
389
  # Render
390
  $self->stash->{action} = 'shortlog' if $short;
391
  $self->render(
392
    home => $home,
393
    home_ns => $home_ns,
394
    project => $project,
395
    project_ns => $project_ns,
396
    id => $id,
397
    commits => $commits,
398
    refs => $refs,
399
    page => $page,
400
    page_count => $page_count
401
  );
402
};
403

            
Fixed repositories page
Yuki Kimoto authored on 2012-11-23
404
has 'root' => '/gitpub';
405

            
improved repository page des...
Yuki Kimoto authored on 2012-11-23
406
sub _root_ns {
407
  my $self = shift;
408
  
409
  my $root = $self->root;
410
  $root =~ s/^\///;
411
  
412
  return $root;
413
}
414

            
415
sub repository {
416
  my $self = shift;
417
  
418
  # Parameters
419
  my $user = $self->param('user');
420
  my $repository = $self->param('repository');
421
  my $root_ns = $self->_root_ns;
422
  
423
  my $project_ns = "$root_ns/$user/$repository.git";
424
  my $project = "/$project_ns";
425
  my $home_ns = dirname $project_ns;
426
  my $home = "/$home_ns";
427
  my $id_dir = $self->param('id_dir') || 'master/';
428

            
429
  # Id and directory
430
  my ($id, $dir) = $self->_parse_id_path($project, $id_dir);
431

            
432
  # Git
433
  my $git = $self->app->git;
434
  
435
  # Tree id
436
  my $tid;
437
  my $commit = $git->parse_commit($project, $id);
438
  unless (defined $tid) {
439
    if (defined $dir && $dir ne '') {
440
      $tid = $git->id_by_path($project, $id, $dir, 'tree');
441
    }
442
    else { $tid = $commit->{tree} }
443
  }
444
  $self->render_not_found unless defined $tid;
445

            
446
  # Get tree (command "git ls-tree")
447
  my @entries = ();
448
  my $show_sizes = 0;
449
  open my $fh, '-|', $git->cmd($project), 'ls-tree', '-z',
450
      ($show_sizes ? '-l' : ()), $tid
451
    or croak 'Open git-ls-tree failed';
452
  local $/ = "\0";
453
  @entries = map { chomp; $git->dec($_) } <$fh>;
454
  close $fh
455
    or croak 404, "Reading tree failed";
456
  
457
  # Parse tree
458
  my @trees;
459
  for my $line (@entries) {
460
    my $tree = $git->parse_ls_tree_line($line, -z => 1, -l => $show_sizes);
461
    $tree->{mode_str} = $git->_mode_str($tree->{mode});
462
    push @trees, $tree;
463
  }
464
  
465
  # References
466
  my $refs = $git->references($project);
467
  
468
  # Render
469
  $self->render(
470
    home => $home,
471
    home_ns => $home_ns,
472
    project => $project,
473
    project_ns => $project_ns,
474
    dir => $dir,
475
    id => $id,
476
    tid => $tid,
477
    commit => $commit,
478
    trees => \@trees,
479
    refs => $refs
480
  );
481
}
482

            
Fixed repositories page
Yuki Kimoto authored on 2012-11-23
483
sub repositories {
484
  my $self = shift;
485

            
486
  my $root = $self->root;
Fixed repositories page
Yuki Kimoto authored on 2012-11-23
487
  my $user = $self->param('user');
Fixed repositories page
Yuki Kimoto authored on 2012-11-23
488
  
Fixed repositories page
Yuki Kimoto authored on 2012-11-23
489
  # Repositories
490
  my $reps = $self->app->git->repositories("/$root/$user");
Fixed repositories page
Yuki Kimoto authored on 2012-11-23
491
  
492
  # Render
Fixed repositories page
Yuki Kimoto authored on 2012-11-23
493
  $self->render(reps => $reps);
Fixed repositories page
Yuki Kimoto authored on 2012-11-23
494
}
495

            
copy gitweblite soruce code
root authored on 2012-11-23
496
sub snapshot {
497
  my $self = shift;
498

            
499
  # Parameter
500
  my $project_ns = $self->param('project');
501
  my $project = "/$project_ns";
502
  my $home_ns = dirname $project_ns;
503
  my $home = "/$home_ns";
504
  my $id = $self->param('id');
505
  
506
  # Git
507
  my $git = $self->app->git;
508

            
509
  # Object type
510
  my $type = $git->object_type($project, "$id^{}");
511
  if (!$type) { croak 404, 'Object does not exist' }
512
  elsif ($type eq 'blob') { croak 400, 'Object is not a tree-ish' }
513
  
514
  my ($name, $prefix) = $git->snapshot_name($project, $id);
515
  my $file = "$name.tar.gz";
516
  my $cmd = $self->_quote_command(
517
    $git->cmd($project), 'archive', "--format=tar", "--prefix=$prefix/", $id
518
  );
519
  $cmd .= ' | ' . $self->_quote_command('gzip', '-n');
520

            
521
  $file =~ s/(["\\])/\\$1/g;
522

            
523
  open my $fh, '-|', $cmd
524
    or croak 'Execute git-archive failed';
525
  
526
  # Write chunk
527
  $self->res->headers->content_type('application/x-tar');
528
  $self->res->headers->content_disposition(qq/attachment; filename="$file"/);
529
  my $cb;
530
  $cb = sub {
531
    my $c = shift;
532
    my $size = 500 * 1024;
533
    my $length = sysread($fh, my $buffer, $size);
534
    unless (defined $length) {
535
      close $fh;
536
      undef $cb;
537
      return;
538
    }
539
    $c->write_chunk($buffer, $cb);
540
  };
541
  $self->$cb;
542
}
543

            
544
sub summary {
545
  my $self = shift;
546
  
547
  # Parameters
548
  my $project_ns = $self->param('project');
549
  my $project = "/$project_ns";
550
  my $home_ns = dirname $project_ns;
551
  my $home = "/$home_ns";
552
  
553
  # Git
554
  my $git = $self->app->git;
555
  
556
  # HEAd commit
557
  my $project_description = $git->project_description($project);
558
  my $project_owner = $git->project_owner($project);
559
  my $head_commit = $git->parse_commit($project, 'HEAD');
560
  my $committer_date
561
    = $git->parse_date($head_commit->{committer_epoch}, $head_commit->{committer_tz});
562
  my $last_change = $git->timestamp($committer_date);
563
  my $head_id = $head_commit->{id};
564
  my $urls = $git->project_urls($project);
565
  
566
  # Commits
567
  my $commit_count = 20;
568
  my $commits = $head_id ? $git->parse_commits($project, $head_id, $commit_count) : ();
569

            
570
  # References
571
  my $refs = $git->references($project);
572
  
573
  # Tags
574
  my $tag_count = 20;
575
  my $tags  = $git->tags($project, $tag_count - 1);
576

            
577
  # Heads
578
  my $head_count = 20;
579
  my $heads = $git->heads($project, $head_count - 1);
580
  
581
  # Render
582
  $self->render(
583
    home => $home,
584
    home_ns => $home_ns,
585
    project => $project,
586
    project_ns => $project_ns,
587
    project_description => $project_description,
588
    project_owner => $project_owner,
589
    last_change => $last_change,
590
    urls => $urls,
591
    commits => $commits,
592
    tags => $tags,
593
    head_id => $head_id,
594
    heads => $heads,
595
    refs => $refs,
596
    commit_count => $commit_count,
597
    tag_count => $tag_count,
598
    head_count => $head_count
599
  );
600
}
601

            
602
sub tag {
603
  my $self = shift;
604
  
605
  # Parameters
606
  my $project_ns = $self->param('project');
607
  my $project = "/$project_ns";
608
  my $home_ns = dirname $project_ns;
609
  my $home = "/$home_ns";
610
  my $id = $self->param('id');
611
  
612
  # Git
613
  my $git = $self->app->git;
614
  
615
  # Ref names
616
  my $tag  = $git->parse_tag($project, $id);
617
  my $author_date
618
    = $git->parse_date($tag->{author_epoch}, $tag->{author_tz});
619
  $tag->{author_date} = $git->timestamp($author_date);
620
  
621
  # Render
622
  $self->render(
623
    home => $home,
624
    home_ns => $home_ns,
625
    project => $project,
626
    project_ns => $project_ns,
627
    tag => $tag,
628
  );
629
}
630

            
631
sub tags {
632
  my $self = shift;
633
  
634
  # Parameters
635
  my $project_ns = $self->param('project');
636
  my $project = "/$project_ns";
637
  my $home_ns = dirname $project_ns;
638
  my $home = "/$home_ns";
639
  
640
  # Git
641
  my $git = $self->app->git;
642
  
643
  # Ref names
644
  my $tags  = $git->tags($project);
645
  
646
  # Render
647
  $self->render(
648
    home => $home,
649
    home_ns => $home_ns,
650
    project => $project,
651
    project_ns => $project_ns,
652
    tags => $tags,
653
  );
654
}
655

            
656
sub tree {
657
  my $self = shift;
658
  
659
  # Parameters
660
  my $project_ns = $self->param('project');
661
  my $project = "/$project_ns";
662
  my $home_ns = dirname $project_ns;
663
  my $home = "/$home_ns";
664
  my $id_dir = $self->param('id_dir');
665

            
666
  # Id and directory
667
  my ($id, $dir) = $self->_parse_id_path($project, $id_dir);
668

            
669
  # Git
670
  my $git = $self->app->git;
671
  
672
  # Tree id
673
  my $tid;
674
  my $commit = $git->parse_commit($project, $id);
675
  unless (defined $tid) {
676
    if (defined $dir && $dir ne '') {
677
      $tid = $git->id_by_path($project, $id, $dir, 'tree');
678
    }
679
    else { $tid = $commit->{tree} }
680
  }
681
  $self->render_not_found unless defined $tid;
682

            
683
  # Get tree (command "git ls-tree")
684
  my @entries = ();
685
  my $show_sizes = 0;
686
  open my $fh, '-|', $git->cmd($project), 'ls-tree', '-z',
687
      ($show_sizes ? '-l' : ()), $tid
688
    or croak 'Open git-ls-tree failed';
689
  local $/ = "\0";
690
  @entries = map { chomp; $git->dec($_) } <$fh>;
691
  close $fh
692
    or croak 404, "Reading tree failed";
693
  
694
  # Parse tree
695
  my @trees;
696
  for my $line (@entries) {
697
    my $tree = $git->parse_ls_tree_line($line, -z => 1, -l => $show_sizes);
698
    $tree->{mode_str} = $git->_mode_str($tree->{mode});
699
    push @trees, $tree;
700
  }
701
  
702
  # References
703
  my $refs = $git->references($project);
704
  
705
  # Render
706
  $self->render(
707
    home => $home,
708
    home_ns => $home_ns,
709
    project => $project,
710
    project_ns => $project_ns,
711
    dir => $dir,
712
    id => $id,
713
    tid => $tid,
714
    commit => $commit,
715
    trees => \@trees,
716
    refs => $refs
717
  );
718
}
719

            
720
sub _parse_blobdiff_lines {
721
  my ($self, $lines_raw) = @_;
722
  
723
  # Git
724
  my $git = $self->app->git;
725
  
726
  # Parse
727
  my @lines;
728
  for my $line (@$lines_raw) {
729
    $line = $git->dec($line);
730
    chomp $line;
731
    my $class;
732
    
733
    if ($line =~ /^diff \-\-git /) { $class = 'diff header' }
734
    elsif ($line =~ /^index /) { $class = 'diff extended_header' }
735
    elsif ($line =~ /^\+/) { $class = 'diff to_file' }
736
    elsif ($line =~ /^\-/) { $class = 'diff from_file' }
737
    elsif ($line =~ /^\@\@/) { $class = 'diff chunk_header' }
738
    elsif ($line =~ /^Binary files/) { $class = 'diff binary_file' }
739
    else { $class = 'diff' }
740
    push @lines, {value => $line, class => $class};
741
  }
742
  
743
  return \@lines;
744
}
745

            
746
sub _parse_id_path {
747
  my ($self, $project, $id_path) = @_;
748
  
749
  # Git
750
  my $git = $self->app->git;
751
  
752
  # Parse id and path
753
  my $refs = $git->references($project);
754
  my $id;
755
  my $path;
756
  for my $rs (values %$refs) {
757
    for my $ref (@$rs) {
758
      $ref =~ s#^heads/##;
759
      $ref =~ s#^tags/##;
760
      if ($id_path =~ s#^\Q$ref(/|$)##) {
761
        $id = $ref;
762
        $path = $id_path;
763
        last;
764
      }      
765
    }
766
  }
767
  unless (defined $id) {
768
    if ($id_path =~ s#(^[^/]+)(/|$)##) {
769
      $id = $1;
770
      $path = $id_path;
771
    }
772
  }
773
  
774
  return ($id, $path);
775
}
776

            
777
sub _quote_command {
778
  my $self = shift;
779
  return join(' ',
780
    map { my $a = $_; $a =~ s/(['!])/'\\$1'/g; "'$a'" } @_ );
781
}
782

            
783
1;