git statusgit loggit diffgit show-refgit name-revgit ls-remote$ cd ~/git/My-Project $ git tag v0.6 v0.6.1 v0.6.2 v0.6.3 v0.6.4 v0.6.5
my $git = Git::Repository->new(
    work_tree => "$ENV{HOME}/git/My-Project",
);
my @tags = $git->run( "tag" );
# ("v0.6", "v0.6.1", "v0.6.2", "v0.6.3", 
#  "v0.6.4", "v0.6.5")
$ cd ~/git/My-Project $ git log --oneline -n3 b23123a Release v0.6.12 6762cba do not try to fetch items that don't exist d045083 errors from the server must extend ServerError
my $git = Git::Repository->new(
    work_tree => "$ENV{HOME}/git/My-Project",
);
my %commits = map { split /\s+/, $_, 2 }
              $git->run( log => '--oneline-', '-n3' );
# (
#   b23123a => "Release v0.6.12",
#   6762cba => "do not try to fetch items ...",
#   d045083 => "errors from the server ...",
# )
my $git  = Git::Repository->new( work_tree => '.' );
my @tags = $git->run( 'tag' );
# Make sure each v-tag is in the update.xml file
for my $tag ( grep { /^v/ } @tags ) {
    # Get the information from git
    $git->run( checkout => $tag );
    my @log = $git->run( log => '-n1' );
    my $info = parse_git_log( @log );
    # ... build and add to update.xml
}
# Restore ourselves to master
$git->run( checkout => 'master' );
commit 88ecb78596865b6c1303816dc9051e8c62463993 Author: Doug BellDate: Tue Sep 18 22:28:51 2012 -0500 Release v0.6.5 Changes: * Add: Schema changes to support new item and anim development * Add: Loading images. Scenes can specify an image to show while they are being loaded. A default image can be set in the World dialog. * Fix: Some of the loading problems in the client are fixed 
sub parse_git_log {
    my @lines = @_;
    my $info = { }; my $in_msg = 0;
    for my $line ( @lines ) {
        # First empty line starts the commit message
        if ( $line =~ /^\s*$/ ) {
            $in_msg = 1;
        }
        elsif ( $in_msg ) {
            $info->{description} .= $line . "\n";
        }
        # Extract the date
        elsif ( $line =~ /^Date:\s+(.+)\s*$/ ) {
            my $parser = DateTime::Format::Strptime->new(
                pattern => '%a %b %d %T %Y %z',
            );
            my $dt = $parser->parse_datetime( $1 );
            $info->{date} = $dt->strftime('%F');
        }
    }
    return $info;
}
git submodule add <repo>$ mkdir release && cd release $ git init Initialized empty Git repository in /Users/doug/release/.git/ $ git submodule add ~/storyteller Cloning into 'storyteller' done. $ git submodule add ~/swfconduit Cloning into 'swfconduit' done. $ ls storyteller swfconduit
git submodule lists the submodules and their commitgit ls-remote lists all the branches for a remote$ git submodule 4abfbe96d4e7ba5821eb1adb99bb5856c3e0422e storyteller (v0.6.13) ef95f4294f4dc7554be523bd6cbd573c8c0ae594 swfconduit (v1.0.0)
sub git_submodule {
    my ( $git ) = @_;
    my %submodules;
    for $line ( $git->run( 'submodule' ) ) {
        # <status><SHA1 hash> <submodule> (ref name)
        $line =~ m{^.(\S+)\s(\S+)};
        $submodules{ $2 } = $1;
    }
    return %submodules;
}
$ cd storyteller $ git ls-remote origin b72ea4150cf59957dd9d2a847c3cb7b695b68e7b HEAD 4abfbe96d4e7ba5821eb1adb99bb5856c3e0422e refs/heads/master f9d19573a65a1eb8c4caa6d6aaab00cafc7ce047 refs/heads/prop_iso_view 4abfbe96d4e7ba5821eb1adb99bb5856c3e0422e refs/remotes/origin/HEAD 4abfbe96d4e7ba5821eb1adb99bb5856c3e0422e refs/remotes/origin/master a91fd98facea44769481f0256057dd47a2e2f934 refs/stash efeade8251c5f46b58196624d21b110ac6dc9db0 refs/tags/v0.5.0 b72ea4150cf59957dd9d2a847c3cb7b695b68e7b refs/tags/v0.6 08060acac4dc32b2577be0523b527187681aa851 refs/tags/v0.6.1
sub git_ls_remote {
    my ( $git ) = @_;
    my %refs;
    my @lines = $git->run( 'ls-remote', 'origin' );
    for my $line ( @lines ) {
        # <SHA1 hash> <symbolic ref>
        my ( $ref_id, $ref_name ) = split $line;
        $refs{ $ref_name } = $ref_id;
    }
    return %refs;
}
use feature qw( say );
my $git = Git::Repository->new( work_tree => '.' );
my %submod_refs = git_submodule( $git );
for my $submod ( keys %submod_refs ) {
    my $subgit = Git::Repository->new(
                    work_tree => $submod,
                );
    my %remote = git_ls_remote( $subgit );
    if ( $submod_refs{ $submod } 
        ne $remote{'refs/heads/master'} )
    {
        say "$submod out of date";
    }
}
$ cd storyteller $ git checkout refs/remote/origin/master Note: checking out 'refs/remote/origin/master'. # ... some noise about 'detached HEAD' HEAD is now at 4abfbe9... Release v0.6.13 $ cd .. $ git commit storyteller -m'updated storyteller' [master 8bae1c3] update storyteller 1 file changed, 1 insertion(+)
my $git = Git::Repository->new( work_tree => '.' );
my %submod_refs = git_submodule( $git );
for my $submod ( keys %submod_refs ) {
    my $subgit = Git::Repository->new(
                    work_tree => $submod,
                );
    $subgit->run(
        checkout => 'refs/remotes/origin/master',
    );
}
$git->run( 'commit' => '-m' => 'Update submodules' );
$git->run( 'push', 'origin', 'master' );
my $release_version = 'v1.00';
my $git = Git::Repository->new( work_tree => '.' );
my %submod_refs = git_submodule( $git );
for my $submod ( keys %submod_refs ) {
    my $subgit = Git::Repository->new(
                    work_tree => $submod,
                );
    my %remote = git_ls_remote( $subgit );
    if ( $submod_refs{ $submod } 
        ne $remote{'refs/heads/master'} )
    {
        $subgit->run(
            checkout => 'refs/remotes/origin/master',
        );
    }
    $subgit->run( tag => $release_version );
    $subgit->run( push => '--tags', 'origin' );
}
$git->run(
    'commit' => '-m' => "Release $release_version"
);
$git->run( 'push' => 'origin', 'master' );
# Create release branch
$git->run( 'branch' => $release_version );
$git->run(
    'push' => 'origin', 
    "$release_version:$release_version"
);
Slides are licensed under a CC-BY-SA 3.0 license.
Code is licensed under the Artistic License or GNU GPL v1.0 or later (the same terms as Perl itself).