monotone

monotone Mtn Source Tree

Root/work_migration.cc

1// Copyright (C) 2006 Zack Weinberg <zackw@panix.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include "sanity.hh"
12#include "ui.hh"
13#include "cset.hh"
14#include "simplestring_xform.hh"
15#include "revision.hh"
16#include "file_io.hh"
17#include "work.hh"
18#include "transforms.hh"
19
20#include "lexical_cast.hh"
21#include <exception>
22
23using std::string;
24using std::exception;
25using boost::lexical_cast;
26
27// This file's primary entry point is workspace::migrate_ws_format. It is
28// responsible for migrating workspace directories from metadata formats
29// used by older versions of monotone. This file also defines the other
30// workspace:: functions related to metadata format. Whenever a new
31// workspace format is added, this file must be updated and a test must be
32// added to tests/workspace_migration/, following the instructions in that
33// file.
34
35// Workspace metadata formats have a revision number, which is a simple
36// nonnegative integer. Any given version of monotone supports normal use
37// of exactly one format, the "current" format; it also supports 'migrating'
38// from all previous formats. The current metadata format is recorded in
39// this constant:
40static const unsigned int current_workspace_format = 2;
41
42// This is the oldest released version of monotone that supports the current
43// format.
44static const char first_version_supporting_current_format[] = "0.30";
45
46// In a workspace, the metadata format's revision number is, notionally,
47// stored in the file _MTN/format. However, this file only appears in
48// metadata formats 2 and later. Format 1 is indicated by the _absence_
49// of _MTN/format. Format 0 is even older, and is indicated by the
50// metadata directory being named "MT", not "_MTN". All these little
51// details are handled by the following two functions. Note that
52// write_ws_format is a public interface, but get_ws_format is not
53// (the corresponding public interface is check_ws_format, below).
54
55static unsigned int
56get_ws_format()
57{
58 unsigned int format;
59 bookkeeping_path f_path = bookkeeping_root / "format";
60 if (!file_exists(f_path))
61 {
62 if (directory_exists(bookkeeping_root))
63 format = 1;
64 else if (directory_exists(file_path() / old_bookkeeping_root_component))
65 format = 0;
66 else
67 N(false, F("workspace required but not found"));
68 }
69 else
70 {
71 data f_dat;
72 try
73 {
74 read_data(f_path, f_dat);
75 format = lexical_cast<unsigned int>(remove_ws(f_dat()));
76 }
77 catch (exception & e)
78 {
79 E(false, F("workspace is corrupt: %s is invalid")
80 % f_path);
81 }
82 if (format == 1)
83 {
84 W(F("_MTN/format should not exist in a format 1 workspace; corrected"));
85 delete_file(f_path);
86 }
87 }
88 return format;
89}
90
91void
92workspace::write_ws_format()
93{
94 bookkeeping_path f_path = bookkeeping_root / "format";
95 // one or other side of this conditional will always be dead code, but
96 // both sides should be preserved, to document all historical formats.
97 // N.B. this will _not_ do the right thing for format 0. Which is fine.
98 if (current_workspace_format <= 1)
99 {
100 if (file_exists(f_path))
101 delete_file(f_path);
102 }
103 else
104 {
105 data f_dat(lexical_cast<string>(current_workspace_format) + "\n");
106 write_data(f_path, f_dat);
107 }
108}
109
110// This function is the public face of get_ws_format. It produces
111// suitable error messages if the workspace's format number is not
112// equal to current_workspace_format.
113
114void
115workspace::check_ws_format()
116{
117 if (!workspace::found)
118 return;
119
120 unsigned int format = get_ws_format();
121
122 // Don't give user false expectations about format 0.
123 E(format > 0,
124 F("this workspace's metadata is in format 0. to use this workspace\n"
125 "with this version of monotone, you must delete it and check it\n"
126 "out again (migration from format 0 is not possible).\n"
127 "once you have done this, you will not be able to use the workspace\n"
128 "with versions of monotone older than %s.\n"
129 "we apologize for the inconvenience.")
130 % first_version_supporting_current_format);
131
132 E(format >= current_workspace_format,
133 F("to use this workspace with this version of monotone, its metadata\n"
134 "must be migrated from format %d to format %d, using the command\n"
135 "'%s migrate_workspace'.\n"
136 "once you have done this, you will not be able to use the workspace\n"
137 "with versions of monotone older than %s.")
138 % format % current_workspace_format % ui.prog_name
139 % first_version_supporting_current_format);
140
141 // keep this message in sync with the copy in migrate_ws_format
142 E(format <= current_workspace_format,
143 F("this version of monotone only understands workspace metadata\n"
144 "in formats 0 through %d. your workspace is in format %d.\n"
145 "you need a newer version of monotone to use this workspace.")
146 % current_workspace_format % format);
147}
148
149
150// Workspace migration is done incrementally. The functions defined below
151// each perform one step. Note that they must access bookkeeping directory
152// files directly, not via work.cc APIs, as those APIs expect a workspace in
153// the current format. Also, note that these functions do not have access
154// to the database, lua hooks, or keys; this is because we want the
155// migration command to work without options, but work.cc may not know how
156// to read options from an old workspace.
157
158static void
159migrate_0_to_1()
160{
161 // Notionally, converting a format 0 workspace to a format 1 workspace is
162 // done by renaming the bookkeeping directory from "MT" to "_MTN" and the
163 // ignore file from ".mt-ignore" to ".mtn-ignore". However, there is no
164 // point in implementing this, because the first version of monotone that
165 // supported workspace format 1 (0.26) also brought a database flag day
166 // that invalidates the revision number cached in the bookkeeping
167 // directory. There is no programmatic way to find the new revision
168 // number corresponding to what was cached. Thus, even if we did convert
169 // the workspace, it would still be unusable.
170
171 E(false,
172 F("it is not possible to migrate from workspace format 0 to any\n"
173 "later format. you must delete this workspace and check it out\n"
174 "again. we apologize for the inconvenience."));
175}
176
177static void
178migrate_1_to_2()
179{
180 // In format 1, the parent revision ID of the checkout is stored bare in a
181 // file named _MTN/revision, and any directory tree operations are in cset
182 // format in _MTN/work, which does not exist if that cset is empty (no
183 // changes or only content changes). In format 2, _MTN/revision contains
184 // a serialized revision (qua revision.hh), carrying both pieces of
185 // information, and _MTN/work does not exist; also, there may be more than
186 // one parent revision, but we do not have to worry about that here.
187
188 bookkeeping_path rev_path = bookkeeping_root / "revision";
189 data base_rev_data; MM(base_rev_data);
190 try
191 {
192 read_data(rev_path, base_rev_data);
193 }
194 catch (exception & e)
195 {
196 E(false, F("workspace is corrupt: reading %s: %s")
197 % rev_path % e.what());
198 }
199 revision_id base_rid(decode_hexenc(remove_ws(base_rev_data())));
200 MM(base_rid);
201
202 cset workcs;
203 MM(workcs);
204 bookkeeping_path workcs_path = bookkeeping_root / "work";
205 bool delete_workcs = false;
206 if (file_exists(workcs_path))
207 {
208 delete_workcs = true;
209 data workcs_data; MM(workcs_data);
210 try
211 {
212 read_data(workcs_path, workcs_data);
213 }
214 catch (exception & e)
215 {
216 E(false, F("workspace is corrupt: reading %s: %s")
217 % workcs_path % e.what());
218 }
219
220 read_cset(workcs_data, workcs);
221 }
222 else
223 require_path_is_nonexistent(workcs_path,
224 F("workspace is corrupt: "
225 "%s exists but is not a regular file")
226 % workcs_path);
227
228 revision_t rev;
229 MM(rev);
230 make_revision_for_workspace(base_rid, workcs, rev);
231 data rev_data;
232 write_revision(rev, rev_data);
233 write_data(rev_path, rev_data);
234 if (delete_workcs)
235 delete_file(workcs_path);
236}
237
238// This function is the public face of the migrate_X_to_X+1 functions.
239
240void
241workspace::migrate_ws_format()
242{
243 unsigned int format = get_ws_format();
244
245 // When adding new migrations, note the organization of the first block of
246 // case entries in this switch statement. There are entries each of the
247 // numbers 0 ... C-1 (where C is current_workspace_format); each calls the
248 // migrate_<n>_to_<n+1> function, AND DROPS THROUGH. Thus, when we
249 // encounter a workspace in format K < C, the migrate_K_to_K+1,
250 // migrate_K+1_to_K+2, ..., migrate_C-1_to_C functions will all be called.
251 // The last entry drops through to the write_ws_format() line.
252
253 switch (format)
254 {
255 case 0: migrate_0_to_1();
256 case 1: migrate_1_to_2();
257
258 // We are now in the current format.
259 write_ws_format();
260 break;
261
262 case current_workspace_format:
263 P(F("this workspace is in the current format, "
264 "no migration is necessary."));
265 break;
266
267 default:
268 I(format > current_workspace_format);
269 // keep this message in sync with the copy in check_ws_format
270 E(false,
271 F("this version of monotone only understands workspace metadata\n"
272 "in formats 0 through %d. your workspace is in format %d.\n"
273 "you need a newer version of monotone to use this workspace.")
274 % current_workspace_format % format);
275 }
276}
277
278// Local Variables:
279// mode: C++
280// fill-column: 76
281// c-file-style: "gnu"
282// indent-tabs-mode: nil
283// End:
284// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status