Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/differential/management/PhabricatorDifferentialMigrateHunkWorkflow.php
12256 views
1
<?php
2
3
final class PhabricatorDifferentialMigrateHunkWorkflow
4
extends PhabricatorDifferentialManagementWorkflow {
5
6
protected function didConstruct() {
7
$this
8
->setName('migrate-hunk')
9
->setExamples(
10
"**migrate-hunk** --id __hunk__ --to __storage__\n".
11
"**migrate-hunk** --all")
12
->setSynopsis(pht('Migrate storage engines for a hunk.'))
13
->setArguments(
14
array(
15
array(
16
'name' => 'id',
17
'param' => 'id',
18
'help' => pht('Hunk ID to migrate.'),
19
),
20
array(
21
'name' => 'to',
22
'param' => 'storage',
23
'help' => pht('Storage engine to migrate to.'),
24
),
25
array(
26
'name' => 'all',
27
'help' => pht('Migrate all hunks.'),
28
),
29
array(
30
'name' => 'auto',
31
'help' => pht('Select storage format automatically.'),
32
),
33
array(
34
'name' => 'dry-run',
35
'help' => pht('Show planned writes but do not perform them.'),
36
),
37
));
38
}
39
40
public function execute(PhutilArgumentParser $args) {
41
$is_dry_run = $args->getArg('dry-run');
42
43
$id = $args->getArg('id');
44
$is_all = $args->getArg('all');
45
46
if ($is_all && $id) {
47
throw new PhutilArgumentUsageException(
48
pht(
49
'Options "--all" (to migrate all hunks) and "--id" (to migrate a '.
50
'specific hunk) are mutually exclusive.'));
51
} else if (!$is_all && !$id) {
52
throw new PhutilArgumentUsageException(
53
pht(
54
'Specify a hunk to migrate with "--id", or migrate all hunks '.
55
'with "--all".'));
56
}
57
58
$is_auto = $args->getArg('auto');
59
$storage = $args->getArg('to');
60
if ($is_auto && $storage) {
61
throw new PhutilArgumentUsageException(
62
pht(
63
'Options "--to" (to choose a specific storage format) and "--auto" '.
64
'(to select a storage format automatically) are mutually '.
65
'exclusive.'));
66
} else if (!$is_auto && !$storage) {
67
throw new PhutilArgumentUsageException(
68
pht(
69
'Use "--to" to choose a storage format, or "--auto" to select a '.
70
'format automatically.'));
71
}
72
73
$types = array(
74
DifferentialHunk::DATATYPE_TEXT,
75
DifferentialHunk::DATATYPE_FILE,
76
);
77
$types = array_fuse($types);
78
if (strlen($storage)) {
79
if (!isset($types[$storage])) {
80
throw new PhutilArgumentUsageException(
81
pht(
82
'Storage type "%s" is unknown. Supported types are: %s.',
83
$storage,
84
implode(', ', array_keys($types))));
85
}
86
}
87
88
if ($id) {
89
$hunk = $this->loadHunk($id);
90
$hunks = array($hunk);
91
} else {
92
$hunks = new LiskMigrationIterator(new DifferentialHunk());
93
}
94
95
foreach ($hunks as $hunk) {
96
try {
97
$this->migrateHunk($hunk, $storage, $is_auto, $is_dry_run);
98
} catch (Exception $ex) {
99
// If we're migrating a single hunk, just throw the exception. If
100
// we're migrating multiple hunks, warn but continue.
101
if ($id) {
102
throw $ex;
103
}
104
105
$this->logWarn(
106
pht('WARN'),
107
pht(
108
'Failed to migrate hunk %d: %s',
109
$hunk->getID(),
110
$ex->getMessage()));
111
}
112
}
113
114
return 0;
115
}
116
117
private function loadHunk($id) {
118
$hunk = id(new DifferentialHunk())->load($id);
119
if (!$hunk) {
120
throw new PhutilArgumentUsageException(
121
pht(
122
'No hunk exists with ID "%s".',
123
$id));
124
}
125
126
return $hunk;
127
}
128
129
private function migrateHunk(
130
DifferentialHunk $hunk,
131
$type,
132
$is_auto,
133
$is_dry_run) {
134
135
$old_type = $hunk->getDataType();
136
137
if ($is_auto) {
138
// By default, we're just going to keep hunks in the same storage
139
// engine. In the future, we could perhaps select large hunks stored in
140
// text engine and move them into file storage.
141
$new_type = $old_type;
142
} else {
143
$new_type = $type;
144
}
145
146
// Figure out if the storage format (e.g., plain text vs compressed)
147
// would change if we wrote this hunk anew today.
148
$old_format = $hunk->getDataFormat();
149
$new_format = $hunk->getAutomaticDataFormat();
150
151
$same_type = ($old_type === $new_type);
152
$same_format = ($old_format === $new_format);
153
154
// If we aren't going to change the storage engine and aren't going to
155
// change the storage format, just bail out.
156
if ($same_type && $same_format) {
157
$this->logInfo(
158
pht('SKIP'),
159
pht(
160
'Hunk %d is already stored in the preferred engine ("%s") '.
161
'with the preferred format ("%s").',
162
$hunk->getID(),
163
$new_type,
164
$new_format));
165
return;
166
}
167
168
if ($is_dry_run) {
169
$this->logOkay(
170
pht('DRY RUN'),
171
pht(
172
'Hunk %d would be rewritten (storage: "%s" -> "%s"; '.
173
'format: "%s" -> "%s").',
174
$hunk->getID(),
175
$old_type,
176
$new_type,
177
$old_format,
178
$new_format));
179
return;
180
}
181
182
$old_data = $hunk->getChanges();
183
184
switch ($new_type) {
185
case DifferentialHunk::DATATYPE_TEXT:
186
$hunk->saveAsText();
187
break;
188
case DifferentialHunk::DATATYPE_FILE:
189
$hunk->saveAsFile();
190
break;
191
}
192
193
$this->logOkay(
194
pht('MIGRATE'),
195
pht(
196
'Converted hunk %d to "%s" storage (with format "%s").',
197
$hunk->getID(),
198
$new_type,
199
$hunk->getDataFormat()));
200
201
$hunk = $this->loadHunk($hunk->getID());
202
$new_data = $hunk->getChanges();
203
204
if ($old_data !== $new_data) {
205
throw new Exception(
206
pht(
207
'Integrity check failed: new file data differs from old data!'));
208
}
209
}
210
211
212
}
213
214