forked from TyeMcQueen/whitepages-git-utils
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgit-pullme
executable file
·182 lines (143 loc) · 4.35 KB
/
git-pullme
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
#!/usr/bin/perl
=head1 NAME
git-pullme - Fetch and merge only your current branch
=head1 DESCRIPTION
This will fetch and merge only the branch you're currently in,
avoiding fetches that you aren't interested in and possibly avoiding
"non-fast-forward" messages on pushes
=head1 SYNOPSIS
git pullme [-r | --no-rebase]
-r --no-rebase - Don't rebase when merging
=cut
use 5.006;
use strict;
use warnings;
use Getopt::Long ();
use Pod::Usage ();
use Carp ();
Getopt::Long::Configure( 'bundling_override' );
Main( @ARGV );
exit;
sub Main {
# Command line options
local @ARGV = @_;
my $rebase = 1;
Getopt::Long::GetOptions(
'r|rebase!' => \ $rebase,
help => \ &Pod::Usage::pod2usage,
)
or Pod::Usage::pod2usage();
# Command line argument handling is done
if ( @ARGV ) {
Pod::Usage::pod2usage();
}
# Get the current branch
my $branch = `git symbolic-ref HEAD`;
exit 1 if $?;
chomp $branch;
$branch =~ s{^refs/heads/}{};
if ( ! $branch ) {
die "Not on any branch that I can find!\n";
}
# Get the remote
my $remote = config( "branch.$branch.remote" );
if ( ! $remote ) {
die qq{Your branch is not tracking a remote branch. Consider using "git corb"\n};
}
# Get the tracking branch
my $tracking_branch = config( "branch.$branch.merge" );
$tracking_branch =~ s{^refs/heads/}{};
if ( ! $tracking_branch ) {
die qq{Your branch is not tracking a remote branch. Consider using "git corb"\n};
}
# Fetch the branch from the remote
print "] git fetch $remote $tracking_branch:refs/remotes/$remote/$tracking_branch\n";
system qw( git fetch ), $remote, "$tracking_branch:refs/remotes/$remote/$tracking_branch";
if ( $? ) {
my $exit_code = $? >> 1;
exit $exit_code;
}
# Merge!
my $is_behind = any_commits( "$branch..$remote/$tracking_branch" );
if ( $is_behind ) {
my $is_ahead = any_commits( "$remote/$tracking_branch..$branch" );
if ( $is_ahead ) {
# If we are ahead and behind, we've actually diverged
# If our changes contain no merges, and we are allowed to
# rebase, we will do that, avoiding superfluous merges
my $local_merges = any_merges( "$remote/$tracking_branch..$branch" );
if ( $rebase && ! $local_merges ) {
print "Your local commits do not contain any merges, rebasing on $remote/$tracking_branch\n";
print "] git rebase $remote/$tracking_branch\n";
exec qw( git rebase ), "$remote/$tracking_branch";
}
else {
# Otherwise, we must merge
print "] git merge $remote/$tracking_branch\n";
exec qw( git merge ), "$remote/$tracking_branch";
}
}
else {
# We're only behind; do a merge
print "] git merge $remote/$tracking_branch\n";
exec qw(git merge), "$remote/$tracking_branch";
}
}
else {
print "Already up-to-date\n";
}
return 1;
}
sub any_merges {
my ( $rev_list_spec ) = @_;
local $SIG{CHLD} = 'IGNORE';
my $pid = open my($fh), '-|', qw( git rev-list --parents ), $rev_list_spec;
if ( ! defined $pid ) {
Carp::croak("Can't fork: $!");
}
my $found = 0;
LINE:
while ( my $line = <$fh> ) {
my ( $commit, @parents ) = split ' ', $line;
if ( @parents > 1 ) {
$found = 1;
last LINE;
}
}
close $fh;
return $found;
}
sub any_commits {
my ( $ref_spec ) = @_;
my $rev_list = Run( "git rev-list $ref_spec" );
chomp $rev_list;
return !! $rev_list;
}
sub config {
my ( $name ) = @_;
my $value = `git config $name`;
my $exit_value = $? >> 8;
if ( 0 == $exit_value ) {
chomp $value;
return $value;
}
elsif ( 1 == $exit_value ) {
# git config exits with 1 when the value doesn't exit
return '';
}
else {
Carp::croak(qq{"git config $name" unexpectedly returned exit value $exit_value});
}
}
sub Run {
my $result = `@_`;
if ( $? ) {
my $exit_code = $? >> 8;
Carp::croak(qq{"@_" unexpectedly returned exit value $exit_code});
}
return $result;
}
# Local Variables:
# mode: cperl
# indent-tabs-mode: nil
# End: