summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorOswald Buddenhagen <oswald.buddenhagen@qt.io>2017-08-25 20:16:13 +0200
committerOswald Buddenhagen <oswald.buddenhagen@gmx.de>2020-02-28 16:53:27 +0000
commit24b9194c656ace9a11d121863f13ca7d9eb0302b (patch)
tree4ef7913467c4da7c35ea3b67246bad70b5f32718 /bin
parent77d2922357ebbbf598a8fa9b3d90fef9d7f78f39 (diff)
downloadqtrepotools-24b9194c656ace9a11d121863f13ca7d9eb0302b.tar.gz
gpush: add target branch validation
complain when attempting to push a Change for a branch it hasn't been pushed for before while it has been pushed for other branches. add the --force-branch option to override the check. we specifically ignore merged Changes, to avoid complaining about cherry-picks. this introduces the risk of creating duplicates for Changes that were temporarily cherry-picked locally and forgotten about. this loophole will be addressed by a different mechanism later. Change-Id: Ifac640a8163ec0f099c53de845fb2c5a372e917a Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Diffstat (limited to 'bin')
-rwxr-xr-xbin/git-gpush60
-rw-r--r--bin/git_gpush.pm18
2 files changed, 72 insertions, 6 deletions
diff --git a/bin/git-gpush b/bin/git-gpush
index 75e0cfd..e38eb28 100755
--- a/bin/git-gpush
+++ b/bin/git-gpush
@@ -57,6 +57,10 @@ Options:
upstream branch for 'ref-from' is used as the target branch.
This setting persists for the series, even when it grows.
+ -fb, --force-branch
+ Push for specified branch despite the pushed Changes having
+ been pushed previously, but only for different branches.
+
-l, --list
Report all Changes that would be pushed, then quit.
This is a purely off-line operation.
@@ -123,6 +127,7 @@ EOM
my $ref_from;
my $ref_to;
+my $force_branch = 0;
my $list_only = 0;
my @reviewers;
@@ -148,6 +153,8 @@ sub parse_arguments(@)
} elsif ($arg eq "-b" || $arg eq "--branch") {
fail("--branch needs an argument.\n") if (!@_ || ($_[0] =~ /^-/));
$ref_to = shift @_;
+ } elsif ($arg eq "-fb" || $arg eq "--force-branch") {
+ $force_branch = 1;
} elsif ($arg eq "-l" || $arg eq "--list") {
$list_only = 1;
} elsif ($arg eq "--aliases") {
@@ -192,7 +199,7 @@ sub parse_arguments(@)
}
my $push_specific =
- @reviewers || @CCs
+ @reviewers || @CCs || $force_branch
|| defined($remote) || defined($ref_to);
if ($list_only) {
@@ -400,6 +407,48 @@ sub get_changes()
return $group;
}
+# Assign each local Change to a matching remote Change if possible,
+# on the way complaining about creating duplicates.
+sub map_remote_changes($)
+{
+ my ($group) = @_;
+
+ my $changes = $$group{changes};
+ my $br = $$group{branch};
+ my (@bad_chg, %bad_br);
+ foreach my $change (@$changes) {
+ my $gis = $gerrit_infos_by_id{$$change{id}};
+ next if (!$gis);
+ my ($good, @bad);
+ foreach my $gi (@$gis) {
+ if ($$gi{branch} eq $br) {
+ $good = $gi;
+ } elsif ($$gi{status} ne "MERGED") {
+ # MERGED Changes are ignored, to avoid false positives for cherry-picks.
+ push @bad, $gi;
+ }
+ }
+ if ($good) {
+ $$change{gerrit} = $good;
+ } elsif (@bad && !$force_branch) {
+ my @bbr = map { $$_{branch} } @bad;
+ $$change{annotation} = ' ['.join(" ", sort @bbr).']';
+ push @bad_chg, $change;
+ $bad_br{$_} = 1 foreach (@bbr);
+ }
+ }
+ if (@bad_chg) {
+ my $reports = report_pushed_changes($group);
+ my $tpfx = (@bad_chg == @$changes) ? "The" : "Some of the";
+ my $tsfx = (keys(%bad_br) == 1) ? "a different branch" : "different branches";
+ report_fixed($reports,
+ "$tpfx Change(s) were previously pushed only for $tsfx.\n",
+ "Please move them server-side, push for a matching branch,\n",
+ "or use --force-branch to continue nonetheless.\n");
+ fail_formatted($reports);
+ }
+}
+
use constant {
NEW => 'NEW',
MODIFIED => 'MODIFIED',
@@ -507,14 +556,13 @@ sub execute_pushing()
my $group = get_changes();
my $pushed_changes = $$group{changes};
if ($online) {
- my @queries;
- foreach my $change (@$pushed_changes) {
- my $commit = $$change{pushed};
- push @queries, "commit:".$commit if (defined($commit));
- }
+ my @queries = map { "change:".$$_{id} } @$pushed_changes;
query_gerrit(\@queries);
}
determine_remote_branch($group);
+ if ($online) {
+ map_remote_changes($group);
+ }
classify_changes_offline($group);
if ($list_only) {
show_changes($group);
diff --git a/bin/git_gpush.pm b/bin/git_gpush.pm
index a9e2553..6c3d69c 100644
--- a/bin/git_gpush.pm
+++ b/bin/git_gpush.pm
@@ -478,6 +478,7 @@ sub changes_from_commits($)
########################
our %gerrit_info_by_key;
+our %gerrit_infos_by_id;
##################
# state handling #
@@ -762,6 +763,8 @@ sub format_reports($)
my $type = $$report{type} // "";
if ($type eq "flowed") {
$output .= wrap("", "", $_)."\n" foreach (@{$$report{texts}});
+ } elsif ($type eq "fixed") {
+ $output .= join("", @{$$report{texts}});
} elsif ($type eq "change") {
_unpack_report($report, my ($id, $subject, $prefix, $suffix, $annot, $fixlen));
my $w = $width - $fixlen;
@@ -802,6 +805,16 @@ sub report_flowed($@)
};
}
+sub report_fixed($@)
+{
+ my ($reports, @texts) = @_;
+
+ push @$reports, {
+ type => "fixed",
+ texts => \@texts
+ };
+}
+
sub report_local_changes($$)
{
my ($reports, $changes) = @_;
@@ -1025,6 +1038,9 @@ sub query_gerrit($;$)
my ($key, $changeid) = ($$review{'number'}, $$review{'id'});
next if (!defined($key) || !defined($changeid));
my $ginfo = \%{$gerrit_info_by_key{$key}};
+ push @{$gerrit_infos_by_id{$changeid}}, $ginfo;
+ my $status = $$review{'status'};
+ defined($status) or fail("Huh?! $changeid has no status?\n");
my $branch = $$review{'branch'};
defined($branch) or fail("Huh?! $changeid has no branch?\n");
my $pss = $$review{'patchSets'};
@@ -1039,6 +1055,8 @@ sub query_gerrit($;$)
);
$revs[$number] = \%rev;
}
+ $$ginfo{id} = $changeid;
+ $$ginfo{status} = $status;
$$ginfo{branch} = $branch;
$$ginfo{revs} = [ grep { $_ } @revs ]; # Drop deleted ones.
push @ginfos, $ginfo;