monotone

monotone Mtn Source Tree

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