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