#!/usr/bin/perl

use strict;
use warnings;

#    Copyright 2012 Grant Street Group, All Rights Reserved.
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.

# PODNAME: gitc-sync
# ABSTRACT: Sync up all local branches with the remote
our $VERSION = '0.60'; # VERSION

use App::Gitc::Util qw(
    git
    confirm current_branch
    guarantee_a_clean_working_directory
);

my $stash = guarantee_a_clean_working_directory();
git "remote update -p origin";

warn "Updating local branches...\n";
my @needs_rebase = update_local_branches();

warn "Packing tags...\n";
git "pack-refs";
my $git_dir = git "rev-parse --git-dir";
if ( -d "$git_dir/refs/tags" ) {
    system "find $git_dir/refs/tags -depth -type d -empty -exec rmdir {} \\;";
}

if ( @needs_rebase > 1 ) {
    my $branches = join ' and ', @needs_rebase;
    warn  "\n"
        . "$branches have local changes which aren't in the\n"
        . "central repository.  To bring them up to date, you need to\n"
        . "rebase each one with something like this:\n"
        . "\n"
        . " git checkout master\n"
        . " git rebase origin/master\n"
        . "\n"
        . "Because you've committed directly to multiple branches, gitc\n"
        . "can't start the rebase for you automatically\n"
        ;
}
elsif( @needs_rebase ) {
    my $branch = $needs_rebase[0];
    warn "\n$branch had local changes and may be outdated\n";
    if($stash) {
        warn "Since you stashed changes, you'll have to rebase manually\n";
    }
    elsif ( confirm('Shall I start a rebase for you?') ) {
        git "checkout $branch";
        exec "git rebase origin/$branch";
    }
}

# reinstate any changes present when we started
git "stash apply $stash" if $stash;

exit;

sub branch_exists {
    my ($branch) = @_;
    our $branches;
    if ( not $branches ) {
        my %all = map { substr( $_, 2 ) => 1 } git "branch";
        $branches = \%all;
    }

    return $branches->{$branch};
}

# make sure that local branches match upstream branches.
# returns a list of local branches with local changes
sub update_local_branches {
    my $current_branch = current_branch;
    for my $branch (qw( master test stage prod )) {
        my $branch_exists = branch_exists($branch);
        my $has_local_commits
            = $branch_exists ? git "rev-list -n 1 $branch ^origin/$branch" : '';
        if($has_local_commits) {
            warn "You've committed directly to $branch. I'll leave it alone.\n";
            push @needs_rebase, $branch;
        }
        elsif ( $branch eq $current_branch ) {
            git "reset --hard origin/$branch";
        }
        elsif ($branch_exists) {
            git "update-ref -m 'gitc sync' refs/heads/$branch origin/$branch";
        }
        else {
            git "branch $branch origin/$branch";
        }
    }
    return @needs_rebase;
}

__END__

=pod

=head1 NAME

gitc-sync - Sync up all local branches with the remote

=head1 VERSION

version 0.60

=head1 AUTHOR

Grant Street Group <developers@grantstreet.com>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2013 by Grant Street Group.

This is free software, licensed under:

  The GNU Affero General Public License, Version 3, November 2007

=cut
