1package App::Sqitch::Command::checkout;
2
3use 5.010;
4use strict;
5use warnings;
6use utf8;
7use Moo;
8use App::Sqitch::Types qw(Str);
9use Locale::TextDomain qw(App-Sqitch);
10use App::Sqitch::X qw(hurl);
11use App::Sqitch::Plan;
12use Path::Class qw(dir);
13use Try::Tiny;
14use namespace::autoclean;
15
16extends 'App::Sqitch::Command';
17with 'App::Sqitch::Role::RevertDeployCommand';
18
19our $VERSION = '0.9994';
20
21has client => (
22    is       => 'ro',
23    isa      => Str,
24    lazy     => 1,
25    default  => sub {
26        my $sqitch = shift->sqitch;
27        return $sqitch->config->get( key => 'core.vcs.client' )
28            || 'git' . ( $^O eq 'MSWin32' ? '.exe' : '' );
29    },
30);
31
32sub configure { {} }
33
34sub execute {
35    my $self = shift;
36    my ($branch, $targets) = $self->parse_args(
37        target     => $self->target,
38        names      => [undef],
39        args       => \@_,
40        no_changes => 1,
41    );
42
43    # Warn on multiple targets.
44    my $target = shift @{ $targets };
45    $self->warn(__x(
46        'Too many targets specified; connecting to {target}',
47        target => $target->name,
48    )) if @{ $targets };
49
50    # Now get to work.
51    my $sqitch = $self->sqitch;
52    my $git    = $self->client;
53    my $engine = $target->engine;
54    $engine->with_verify( $self->verify );
55    $engine->no_prompt( $self->no_prompt );
56    $engine->prompt_accept( $self->prompt_accept );
57    $engine->log_only( $self->log_only );
58
59    # What branch are we on?
60    my $current_branch = $sqitch->probe($git, qw(rev-parse --abbrev-ref HEAD));
61    hurl {
62        ident   => 'checkout',
63        message => __x('Already on branch {branch}', branch => $branch),
64        exitval => 1,
65    } if $current_branch eq $branch;
66
67    # Instantitate a plan without calling $target->plan.
68    my $from_plan = App::Sqitch::Plan->new(
69        sqitch => $sqitch,
70        target => $target,
71    );
72
73    # Load the branch plan from Git, assuming the same path.
74    my $to_plan = App::Sqitch::Plan->new(
75        sqitch => $sqitch,
76        target => $target,
77      )->parse(
78        # XXX Handle missing file/no contents.
79        scalar $sqitch->capture( $git, 'show', "$branch:" . $target->plan_file)
80    );
81
82    # Find the last change the plans have in common.
83    my $last_common_change;
84    for my $change ($to_plan->changes){
85        last unless $from_plan->get( $change->id );
86        $last_common_change = $change;
87    }
88
89    hurl checkout => __x(
90        'Branch {branch} has no changes in common with current branch {current}',
91        branch  => $branch,
92        current => $current_branch,
93    ) unless $last_common_change;
94
95    $sqitch->info(__x(
96        'Last change before the branches diverged: {last_change}',
97        last_change => $last_common_change->format_name_with_tags,
98    ));
99
100    # Revert to the last common change.
101    if (my %v = %{ $self->revert_variables }) { $engine->set_variables(%v) }
102    $engine->plan( $from_plan );
103    try {
104        $engine->revert( $last_common_change->id );
105    } catch {
106        # Rethrow unknown errors or errors with exitval > 1.
107        die $_ if ! eval { $_->isa('App::Sqitch::X') }
108            || $_->exitval > 1
109            || $_->ident eq 'revert:confirm';
110        # Emite notice of non-fatal errors (e.g., nothign to revert).
111        $self->info($_->message)
112    };
113
114
115    # Check out the new branch.
116    $sqitch->run($git, 'checkout', $branch);
117
118    # Deploy!
119    if (my %v = %{ $self->deploy_variables}) { $engine->set_variables(%v) }
120    $engine->plan( $to_plan );
121    $engine->deploy( undef, $self->mode);
122    return $self;
123}
124
1251;
126
127__END__
128
129=head1 Name
130
131App::Sqitch::Command::checkout - Revert, change checkout a VCS branch, and redeploy
132
133=head1 Synopsis
134
135  my $cmd = App::Sqitch::Command::checkout->new(%params);
136  $cmd->execute;
137
138=head1 Description
139
140If you want to know how to use the C<checkout> command, you probably want to
141be reading C<sqitch-checkout>. But if you really want to know how the
142C<checkout> command works, read on.
143
144=head1 Interface
145
146=head2 Class Methods
147
148=head3 C<options>
149
150  my @opts = App::Sqitch::Command::checkout->options;
151
152Returns a list of L<Getopt::Long> option specifications for the command-line
153options for the C<checkout> command.
154
155=head2 Instance Methods
156
157=head3 C<execute>
158
159  $checkout->execute;
160
161Executes the checkout command.
162
163=head1 See Also
164
165=over
166
167=item L<sqitch-checkout>
168
169Documentation for the C<checkout> command to the Sqitch command-line client.
170
171=item L<sqitch>
172
173The Sqitch command-line client.
174
175=back
176
177=head1 Authors
178
179=over
180
181=item * Ronan Dunklau <ronan@dunklau.fr>
182
183=item * David E. Wheeler <david@justatheory.com>
184
185=back
186
187=head1 License
188
189Copyright (c) 2012-2015 Ronan Dunklau & iovation Inc.
190
191Permission is hereby granted, free of charge, to any person obtaining a copy
192of this software and associated documentation files (the "Software"), to deal
193in the Software without restriction, including without limitation the rights
194to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
195copies of the Software, and to permit persons to whom the Software is
196furnished to do so, subject to the following conditions:
197
198The above copyright notice and this permission notice shall be included in all
199copies or substantial portions of the Software.
200
201THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
202IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
203FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
204AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
205LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
206OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
207SOFTWARE.
208
209=cut
210