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 { |
9cd610f4 |
7 | use MooseX::Types::Moose qw/Str Int Bool Maybe ArrayRef/; |
8 | use MooseX::Types::Common::String qw/NonEmptySimpleStr/; |
61ba8635 |
9 | use Moose::Autobox; |
0250a92d |
10 | use List::MoreUtils qw/any zip/; |
11 | our $SHA1RE = qr/[0-9a-fA-F]{40}/; |
12 | |
98390bf6 |
13 | has '+type' => ( default => 'commit' ); |
0250a92d |
14 | has '+_gpp_obj' => ( handles => [ 'comment', |
15 | 'tree_sha1', |
16 | 'committer', |
17 | 'committed_time', |
18 | 'author', |
19 | 'authored_time', |
20 | 'parents', |
21 | 'parent_sha1', |
22 | 'parent_sha1s', |
23 | ], |
e1307124 |
24 | ); |
25 | |
f707d264 |
26 | method get_patch ( Maybe[NonEmptySimpleStr] $parent?, |
27 | Int $count?) { |
61ba8635 |
28 | $count ||= 1; |
29 | # Assemble the git command. |
30 | # common args: |
31 | my @cmd_args = qw/format-patch --encoding=utf8 --stdout/; |
32 | # single patch, or patch set? |
33 | if ($count > 1) { |
34 | push @cmd_args, "-$count", "-n"; |
35 | } else { |
36 | push @cmd_args, "-1"; |
37 | } |
f707d264 |
38 | # ref spec: |
39 | # if a parent is specified: hp..h |
40 | # if not, but a merge commit: --cc h |
41 | # otherwise: --root h |
377bf360 |
42 | if (defined $parent) { |
61ba8635 |
43 | push @cmd_args, "$parent.." . $self->sha1; |
44 | } else { |
45 | push @cmd_args, $self->parents->length > 1 |
46 | ? '--cc' : '--root'; |
47 | push @cmd_args, $self->sha1; |
377bf360 |
48 | } |
61ba8635 |
49 | my $out = $self->_run_cmd( @cmd_args ); |
377bf360 |
50 | return $out; |
51 | } |
52 | |
53 | method diff ( Maybe[Bool] :$patch?, |
0250a92d |
54 | Maybe[NonEmptySimpleStr] :$parent?, |
55 | Maybe[NonEmptySimpleStr] :$file? |
56 | ) { |
0250a92d |
57 | $parent = $parent |
58 | ? $parent |
59 | : $self->parents <= 1 |
60 | ? $self->parent_sha1 |
61 | : '-c'; |
62 | my @etc = ( |
63 | ( $file ? ('--', $file) : () ), |
64 | ); |
65 | |
66 | my @out = $self->_raw_diff( |
67 | ( $patch ? '--patch-with-raw' : () ), |
68 | ( $parent ? $parent : () ), |
69 | $self->sha1, @etc, |
70 | ); |
71 | |
72 | # XXX Yes, there is much wrongness having _parse_diff_tree be destructive. |
73 | my @difftree = $self->_parse_diff_tree(\@out); |
74 | |
75 | return \@difftree |
76 | unless $patch; |
77 | |
78 | # The blank line between the tree and the patch. |
79 | shift @out; |
80 | |
81 | # XXX And no I'm not happy about having diff return tree + patch. |
82 | return \@difftree, [$self->_parse_diff(@out)]; |
83 | } |
84 | |
85 | ## Private methods |
86 | # gitweb uses the following sort of command for diffing merges: |
87 | # /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 -- |
88 | # and for regular diffs |
89 | # /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 -- |
90 | method _raw_diff (@args) { |
91 | return $self->_run_cmd_list( |
92 | qw(diff-tree -r -M --no-commit-id --full-index), |
93 | @args |
94 | ); |
95 | } |
96 | |
97 | method _parse_diff_tree ($diff) { |
98 | my @keys = qw(modesrc modedst sha1src sha1dst status src dst); |
99 | my @ret; |
100 | while (@$diff and $diff->[0] =~ /^:\d+/) { |
101 | my $line = shift @$diff; |
102 | # see. man git-diff-tree for more info |
103 | # mode src, mode dst, sha1 src, sha1 dst, status, src[, dst] |
104 | my @vals = $line =~ /^:(\d+) (\d+) ($SHA1RE) ($SHA1RE) ([ACDMRTUX]\d*)\t([^\t]+)(?:\t([^\n]+))?$/; |
105 | my %line = zip @keys, @vals; |
106 | # Some convenience keys |
107 | $line{file} = $line{src}; |
108 | $line{sha1} = $line{sha1dst}; |
109 | $line{is_new} = $line{sha1src} =~ /^0+$/ |
110 | if $line{sha1src}; |
111 | @line{qw/status sim/} = $line{status} =~ /(R)(\d+)/ |
112 | if $line{status} =~ /^R/; |
113 | push @ret, \%line; |
114 | } |
115 | |
116 | return @ret; |
117 | } |
118 | |
119 | method _parse_diff (@diff) { |
120 | my @ret; |
121 | for (@diff) { |
122 | # This regex is a little pathological. |
123 | if (m{^diff --git (a/(.*?)) (b/\2)}) { |
124 | push @ret, { |
125 | head => $_, |
126 | a => $1, |
127 | b => $3, |
128 | file => $2, |
129 | diff => '', |
130 | }; |
131 | next; |
132 | } |
133 | |
134 | if (/^index (\w+)\.\.(\w+) (\d+)$/) { |
135 | @{$ret[-1]}{qw(index src dst mode)} = ($_, $1, $2, $3); |
136 | next |
137 | } |
138 | |
139 | # XXX Somewhat hacky. Ahem. |
140 | $ret[@ret ? -1 : 0]{diff} .= "$_\n"; |
141 | } |
142 | |
143 | return @ret; |
144 | } |
145 | |
146 | } |