File: util.pl

package info (click to toggle)
git-autofixup 0.004007-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 228 kB
  • sloc: perl: 1,876; sh: 58; makefile: 2
file content (182 lines) | stat: -rw-r--r-- 5,252 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
package Util;

use strict;
use warnings FATAL => 'all';

use Carp qw(croak);
use Cwd;
use English qw(-no_match_vars);
use Test::More;

require './t/repo.pl';
require './git-autofixup';

sub check_test_deps {
    if ($OSNAME eq 'MSWin32') {
        plan skip_all => "Windows isn't supported, except with msys or Cygwin";
    } elsif (!git_version_gte('1.7.4')) {
        plan skip_all => 'git version 1.7.4+ required';
    } elsif ($OSNAME eq 'cygwin' && is_git_for_windows()) {
        plan skip_all => "Can't use Git for Windows with a perl for Cygwin";
    }
}

# Return true if at least the given version of git, in X.Y.Z format, is
# available.
sub git_version_gte {
    my $want = shift;
    my @wants = split /\./, $want;
    if (@wants != 3) {
        die "unexpected version format: $want\n";
    }
    my ($want_x, $want_y, $want_z) = @wants;

    my $stdout = qx{git --version};
    return if $? != 0;
    my ($x, $y, $z) = $stdout =~ /(\d+)\.(\d+)(?:\.(\d+))?/;
    defined $x or die "unexpected output from git: $stdout";
    $z = defined $z ? $z : 0;
    my $cmp = $x <=> $want_x || $y <=> $want_y || $z <=> $want_z;
    return $cmp >= 0;
}

sub is_git_for_windows {
    my $version = qx{git --version};
    return $version =~ /\.(?:windows|msysgit)\./i;
}

# Convert a hashref of git config key-value pairs to a hashref of
# GIT_CONFIG_{KEY,VALUE}_<i> pairs suitable for setting as environment
# variables.
#
# For example:
#
#     > git_config_env_vars({'diff.mnemonicPrefix' => 'true'})
#     {
#         GIT_CONFIG_KEY_0 => 'diff.mnemonicPrefix',
#         GIT_CONFIG_VALUE_0 => 'true',
#     }
sub git_config_env_vars {
    my $git_config = shift;
    my %env = ();
    my $i = 0;
    for my $k (sort(keys %$git_config)) {
        $env{"GIT_CONFIG_KEY_$i"} = $k;
        $env{"GIT_CONFIG_VALUE_$i"} = $git_config->{$k};
        $i++;
    }
    return \%env;
}

# Take wanted and actual autofixup exit codes as a hash with keys ('want',
# 'got') and return true if want and got are equal or if want is undefined.
# Print a TAP diagnostic if they aren't ok.
#
# eg: exit_code_got(want => 3, got => 2)
#
# Params are taken as a hash since the order matters and it seems difficult to
# get the order right if the args aren't named.
sub exit_code_ok {
    my %args = @_;
    defined $args{got} or croak "got exit code is undefined";
    if (defined $args{want} && $args{want} != $args{got}) {
        diag("exit_code_want=$args{want},exit_code_got=$args{got}");
        return 0;
    }
    return 1;
}

# Take wanted and actual listrefs of upstream SHAs as a hash with keys ('want',
# 'got') and return true if want and got are equal. Print a TAP diagnostic if
# they aren't ok.
#
# eg: exit_code_got(want => 3, got => 2)
sub upstreams_ok {
    my %args = @_;
    defined $args{want} or croak 'wanted upstream list must be given';
    defined $args{got} or croak 'actual upstream list must be given';
    my @wants = @{$args{want}};
    my @gots = @{$args{got}};
    my $max_len = @wants > @gots ? @wants : @gots;
    my $ok = 1;
    for my $i (0..$max_len - 1) {
        my $want = defined $wants[$i] ? $wants[$i] : '';
        my $got = defined $gots[$i] ? $gots[$i] : '';
        if (!$want || !$got || $want ne $got) {
            diag("upstream mismatch,i=$i,want=$want,got=$got");
            $ok = 0;
        }
    }
    return $ok;
}

# Return whether the repo state is as desired and print TAP diagnostics if not.
#
# Parameters:
# - a Repo object
# - a SHA for the last topic branch commit before any fixups were made
# - a hashref of `git log` outputs, for `fixups`, `staged`, and `unstaged`. If
#   `staged` isn't given, then it's assumed that there shouldn't be any staged
#   changes.
sub repo_state_ok {
    my ($repo, $pre_fixup_rev, $wants) = @_;
    my $ok = 1;

    for my $key (qw(fixup_log staged unstaged)) {
        next if (!defined $wants->{$key});

        my $want = $wants->{$key};

        my $got;
        if ($key eq 'fixup_log') {
            $got = $repo->log_since($pre_fixup_rev);
        } elsif ($key eq 'staged') {
            $got = $repo->diff('--cached');
        } elsif ($key eq 'unstaged') {
            $got = $repo->diff('HEAD');
        }

        if ($got ne $want) {
            diag("${key}_got=<<EOF\n${got}EOF\n${key}_want=<<EOF\n${want}EOF\n");
            $ok = 0;
        }
    }

    if (!defined($wants->{staged})) {
        my $got = $repo->diff('--cached');
        if ($got) {
            diag("staged_got=<<EOF\n${got}EOF\nno staged changes expected\n");
            $ok = 0;
        }
    }

    return $ok;
}

# Run a command for its side effects, and print the command as a TAP
# diagnostic.
sub run {
    print '# ', join(' ', @_), "\n";
    system(@_) == 0 or croak "command " . child_error_desc($?);
}

# Return a description of what $? means.
sub child_error_desc {
    my $err = shift;
    if ($err == -1) {
        return "failed to execute: $!";
    } elsif ($err & 127) {
        return "died with signal " . ($err & 127);
    } else {
        return "exited with " . ($err >> 8);
    }
}

sub write_file {
    my ($filename, $contents) = @_;
    open(my $fh, '>', $filename) or croak "$!";
    print {$fh} $contents or croak "$!";
    close $fh or croak "$!";
}

1;