source: subversion/applications/utils/revert/Revert.pm @ 17630

Last change on this file since 17630 was 17630, checked in by ldp, 10 years ago

Allow some handling of duplicate objects in the changeset

File size: 4.9 KB
Line 
1#!/usr/bin/perl
2
3# Revert.pm
4# ---------
5#
6# Implements whole changeset reverts
7#
8# Part of the "osmtools" suite of programs
9# Originally written by Frederik Ramm <frederik@remote.org>; public domain
10
11package Revert;
12
13use strict;
14use warnings;
15
16use OsmApi;
17use Undo;
18
19# downloads a changeset and attempts to undo all changes
20# within that. currently transaction-based, so the revert will
21# fail if it cannot be done cleanly, but see variable $transaction.
22#
23# parameters:
24#   $undo_changeset: the changeset to nuke
25#   $changeset: the changeset in which the undo happens (must be open)
26# return:
27#   success=1 failure=undef
28
29sub revert
30{
31    my ($undo_changeset, $changeset) = @_;
32
33    my $resp = OsmApi::get("changeset/$undo_changeset/download");
34    if (!$resp->is_success)
35    {
36        print STDERR "changeset $undo_changeset cannot be retrieved: ".$resp->status_line."\n";
37        return undef;
38    }
39
40    my $objects = {};
41    my $action;
42
43    foreach (split(/\n/, $resp->content()))
44    { 
45        if (/<(modify|create|delete)/)
46        {
47            $action = $1;
48        }
49        elsif (/<(node|way|relation).*\sid=["'](\d+)["']/)
50        {
51            unshift(@{$objects->{"$action $1"}}, $2);
52        }
53    }
54
55    # first undelete nodes, ways, relations;
56    # then undo changes to nodes, ways, relations;
57    # then undo creations of relations, ways, nodes (note order).
58
59    my $success = [];
60    my $failure = [];
61    # set this to 0 if you want individual API requests rather than a changeset
62    # upload. this will be much slower but may be required if you cannot get all
63    # changes through due to problems.
64    my $transaction = 1;
65    # set this to 1 if you have a large number of object creations. this will
66    # bypass requesting object history for those, and simply try and delete them.
67    # which will fail if the object has been modified since.
68    my $delete_shortcut = 0;
69    my $oscpart;
70
71    foreach my $operation("delete node", "delete way", "delete relation",
72        "modify node", "modify way", "modify relation", 
73        "create relation", "create way", "create node")
74    {
75        my $seen = {};
76
77        foreach my $object(@{$objects->{$operation}})
78        {
79            # Do not process the same object in the same operation twice.
80            # Allows the script to handle cases where a user has modified
81            # an object twice in the same changeset.
82            next if exists $seen->{$object};
83            $seen->{$object} = 1;
84
85            my ($what, $objtype) = split(/ /, $operation);
86            if ($transaction)
87            {
88                # this collects all undos in one osc document.
89                if (($delete_shortcut) && ($what eq "create"))
90                {
91
92                    print STDERR "$objtype $object created; shortcut deletion\n";
93                    $oscpart->{"delete"} .= "<$objtype id=\"$object\" lat=\"0\" lon=\"0\" version=\"1\" changeset=\"$changeset\" />\n";
94                }
95                else
96                {
97                    my ($action, $xml) = Undo::determine_undo_action($objtype, $object, undef, $undo_changeset, $changeset);
98                    return undef unless (defined($action));
99                    $oscpart->{$action} .= $xml;
100                }
101            }
102            else
103            {
104                # this does individual undo operations. currently unused!
105                if (($delete_shortcut) && ($what eq "create"))
106                {
107                    print STDERR "$objtype $object created; shortcut deletion\n";
108                    my $resp = OsmApi::delete("$objtype/$object", "<osm version='0.6'><$objtype id=\"$object\" lat=\"0\" lon=\"0\" version=\"1\" changeset=\"$changeset\" /></osm>");
109                    if (!$resp->is_success)
110                    {
111                        push(@$failure, "$operation $object");
112                    }
113                    else
114                    {
115                        push(@$success, "$operation $object");
116                    }
117                }
118                else
119                {
120                    if (Undo::undo($objtype, $object, undef, $undo_changeset, $changeset))
121                    {
122                        push(@$success, "$operation $object");
123                    }
124                    else
125                    {
126                        push(@$failure, "$operation $object");
127                    }
128                }
129            }
130        }
131    }
132
133    if ($transaction)
134    {
135        my $osc = "<osmChange version='0.6' generator='osmtools'>\n";
136        foreach my $action("modify", "create", "delete")
137        {
138            if (defined($oscpart->{$action}))
139            {
140                $osc .= "<$action>\n".$oscpart->{$action}."</$action>\n";
141            }
142        }
143        $osc .= "</osmChange>\n";
144        my $res = OsmApi::post("changeset/$changeset/upload", $osc);
145        if (!($res->is_success))
146        {
147            print STDERR "changeset upload failed: ".$res->status_line."\n";
148            return undef;
149        }
150    }
151   
152    return 1;
153}
154
1551;
Note: See TracBrowser for help on using the repository browser.