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