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