Commit | Line | Data |
e1307124 |
1 | package Gitalist::Git::Object::Commit; |
2 | use MooseX::Declare; |
3 | |
0250a92d |
4 | class Gitalist::Git::Object::Commit |
5 | extends Gitalist::Git::Object |
6 | with Gitalist::Git::Object::HasTree { |
7 | use List::MoreUtils qw/any zip/; |
8 | our $SHA1RE = qr/[0-9a-fA-F]{40}/; |
9 | |
10 | has '+_gpp_obj' => ( handles => [ 'comment', |
11 | 'tree_sha1', |
12 | 'committer', |
13 | 'committed_time', |
14 | 'author', |
15 | 'authored_time', |
16 | 'parents', |
17 | 'parent_sha1', |
18 | 'parent_sha1s', |
19 | ], |
e1307124 |
20 | ); |
21 | |
0250a92d |
22 | method diff ( Maybe[Bool] :$patch?, |
23 | Maybe[NonEmptySimpleStr] :$parent?, |
24 | Maybe[NonEmptySimpleStr] :$file? |
25 | ) { |
26 | # method diff (:$patch?, :$parent?, :$file?) { |
27 | # Use parent if specifed, else take the parent from the commit |
28 | # if there is only one, otherwise it was a merge commit. |
29 | $parent = $parent |
30 | ? $parent |
31 | : $self->parents <= 1 |
32 | ? $self->parent_sha1 |
33 | : '-c'; |
34 | my @etc = ( |
35 | ( $file ? ('--', $file) : () ), |
36 | ); |
37 | |
38 | my @out = $self->_raw_diff( |
39 | ( $patch ? '--patch-with-raw' : () ), |
40 | ( $parent ? $parent : () ), |
41 | $self->sha1, @etc, |
42 | ); |
43 | |
44 | # XXX Yes, there is much wrongness having _parse_diff_tree be destructive. |
45 | my @difftree = $self->_parse_diff_tree(\@out); |
46 | |
47 | return \@difftree |
48 | unless $patch; |
49 | |
50 | # The blank line between the tree and the patch. |
51 | shift @out; |
52 | |
53 | # XXX And no I'm not happy about having diff return tree + patch. |
54 | return \@difftree, [$self->_parse_diff(@out)]; |
55 | } |
56 | |
57 | ## Private methods |
58 | # gitweb uses the following sort of command for diffing merges: |
59 | # /home/dbrook/apps/bin/git --git-dir=/home/dbrook/dev/app/.git diff-tree -r -M --no-commit-id --patch-with-raw --full-index --cc 316cf158df3f6207afbae7270bcc5ba0 -- |
60 | # and for regular diffs |
61 | # /home/dbrook/apps/bin/git --git-dir=/home/dbrook/dev/app/.git diff-tree -r -M --no-commit-id --patch-with-raw --full-index 2e3454ca0749641b42f063730b0090e1 316cf158df3f6207afbae7270bcc5ba0 -- |
62 | method _raw_diff (@args) { |
63 | return $self->_run_cmd_list( |
64 | qw(diff-tree -r -M --no-commit-id --full-index), |
65 | @args |
66 | ); |
67 | } |
68 | |
69 | method _parse_diff_tree ($diff) { |
70 | my @keys = qw(modesrc modedst sha1src sha1dst status src dst); |
71 | my @ret; |
72 | while (@$diff and $diff->[0] =~ /^:\d+/) { |
73 | my $line = shift @$diff; |
74 | # see. man git-diff-tree for more info |
75 | # mode src, mode dst, sha1 src, sha1 dst, status, src[, dst] |
76 | my @vals = $line =~ /^:(\d+) (\d+) ($SHA1RE) ($SHA1RE) ([ACDMRTUX]\d*)\t([^\t]+)(?:\t([^\n]+))?$/; |
77 | my %line = zip @keys, @vals; |
78 | # Some convenience keys |
79 | $line{file} = $line{src}; |
80 | $line{sha1} = $line{sha1dst}; |
81 | $line{is_new} = $line{sha1src} =~ /^0+$/ |
82 | if $line{sha1src}; |
83 | @line{qw/status sim/} = $line{status} =~ /(R)(\d+)/ |
84 | if $line{status} =~ /^R/; |
85 | push @ret, \%line; |
86 | } |
87 | |
88 | return @ret; |
89 | } |
90 | |
91 | method _parse_diff (@diff) { |
92 | my @ret; |
93 | for (@diff) { |
94 | # This regex is a little pathological. |
95 | if (m{^diff --git (a/(.*?)) (b/\2)}) { |
96 | push @ret, { |
97 | head => $_, |
98 | a => $1, |
99 | b => $3, |
100 | file => $2, |
101 | diff => '', |
102 | }; |
103 | next; |
104 | } |
105 | |
106 | if (/^index (\w+)\.\.(\w+) (\d+)$/) { |
107 | @{$ret[-1]}{qw(index src dst mode)} = ($_, $1, $2, $3); |
108 | next |
109 | } |
110 | |
111 | # XXX Somewhat hacky. Ahem. |
112 | $ret[@ret ? -1 : 0]{diff} .= "$_\n"; |
113 | } |
114 | |
115 | return @ret; |
116 | } |
117 | |
118 | } |