monotone

monotone Mtn Source Tree

Root/src/cmd_files.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.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 <iostream>
12#include <iterator>
13
14#include "annotate.hh"
15#include "revision.hh"
16#include "cmd.hh"
17#include "diff_output.hh"
18#include "merge_content.hh"
19#include "file_io.hh"
20#include "simplestring_xform.hh"
21#include "transforms.hh"
22#include "app_state.hh"
23#include "project.hh"
24#include "database.hh"
25#include "work.hh"
26#include "roster.hh"
27#include "lexical_cast.hh"
28
29using std::cout;
30using std::ostream_iterator;
31using std::string;
32using std::vector;
33using boost::lexical_cast;
34
35// fload, fmerge, and fdiff are simple commands for debugging the line
36// merger.
37
38CMD(fload, "fload", "", CMD_REF(debug), "",
39 N_("Loads a file's contents into the database"),
40 "",
41 options::opts::none)
42{
43 data dat;
44 read_data_stdin(dat);
45
46 file_id f_id;
47 file_data f_data(dat);
48
49 calculate_ident (f_data, f_id);
50
51 database db(app);
52 transaction_guard guard(db);
53 db.put_file(f_id, f_data);
54 guard.commit();
55}
56
57CMD(fmerge, "fmerge", "", CMD_REF(debug),
58 N_("PARENT_FILEID LEFT_FILEID RIGHT_FILEID"),
59 N_("Merges 3 files and outputs the result"),
60 "",
61 options::opts::none)
62{
63 if (args.size() != 3)
64 throw usage(execid);
65
66 file_id
67 anc_id(decode_hexenc_as<file_id>(idx(args, 0)(), origin::user)),
68 left_id(decode_hexenc_as<file_id>(idx(args, 1)(), origin::user)),
69 right_id(decode_hexenc_as<file_id>(idx(args, 2)(), origin::user));
70
71 file_data anc, left, right;
72
73 database db(app);
74 E(db.file_version_exists (anc_id), origin::user,
75 F("ancestor file id does not exist"));
76
77 E(db.file_version_exists (left_id), origin::user,
78 F("left file id does not exist"));
79
80 E(db.file_version_exists (right_id), origin::user,
81 F("right file id does not exist"));
82
83 db.get_file_version(anc_id, anc);
84 db.get_file_version(left_id, left);
85 db.get_file_version(right_id, right);
86
87 vector<string> anc_lines, left_lines, right_lines, merged_lines;
88
89 split_into_lines(anc.inner()(), anc_lines);
90 split_into_lines(left.inner()(), left_lines);
91 split_into_lines(right.inner()(), right_lines);
92 E(merge3(anc_lines, left_lines, right_lines, merged_lines),
93 origin::user, F("merge failed"));
94 copy(merged_lines.begin(), merged_lines.end(), ostream_iterator<string>(cout, "\n"));
95
96}
97
98CMD(fdiff, "fdiff", "", CMD_REF(debug), N_("SRCNAME DESTNAME SRCID DESTID"),
99 N_("Differences 2 files and outputs the result"),
100 "",
101 options::opts::diff_options)
102{
103 if (args.size() != 4)
104 throw usage(execid);
105
106 string const
107 & src_name = idx(args, 0)(),
108 & dst_name = idx(args, 1)();
109
110 file_id
111 src_id(decode_hexenc_as<file_id>(idx(args, 2)(), origin::user)),
112 dst_id(decode_hexenc_as<file_id>(idx(args, 3)(), origin::user));
113
114 file_data src, dst;
115
116 database db(app);
117 E(db.file_version_exists (src_id), origin::user,
118 F("source file id does not exist"));
119
120 E(db.file_version_exists (dst_id), origin::user,
121 F("destination file id does not exist"));
122
123 db.get_file_version(src_id, src);
124 db.get_file_version(dst_id, dst);
125
126 string pattern("");
127 if (!app.opts.no_show_encloser)
128 app.lua.hook_get_encloser_pattern(file_path_external(utf8(src_name,
129 origin::user)),
130 pattern);
131
132 make_diff(src_name, dst_name,
133 src_id, dst_id,
134 src.inner(), dst.inner(),
135 false, // is_manual_merge
136 cout, app.opts.diff_format, pattern);
137}
138
139CMD(annotate, "annotate", "", CMD_REF(informative), N_("PATH"),
140 N_("Prints an annotated copy of a file"),
141 N_("Calculates and prints an annotated copy of the given file from "
142 "the specified REVISION."),
143 options::opts::revision | options::opts::revs_only)
144{
145 revision_id rid;
146 database db(app);
147 project_t project(db);
148
149 if ((args.size() != 1) || (app.opts.revision.size() > 1))
150 throw usage(execid);
151
152 file_path file = file_path_external(idx(args, 0));
153
154 L(FL("annotate file '%s'") % file);
155
156 roster_t roster;
157 if (app.opts.revision.empty())
158 {
159 // What this _should_ do is calculate the current workspace roster
160 // and/or revision and hand that to do_annotate. This should just
161 // work, no matter how many parents the workspace has. However,
162 // do_annotate currently expects to be given a file_t and revision_id
163 // corresponding to items already in the database. This is a minor
164 // bug in the one-parent case (it means annotate will not show you
165 // changes in the working copy) but is fatal in the two-parent case.
166 // Thus, what we do instead is get the parent rosters, refuse to
167 // proceed if there's more than one, and give do_annotate what it
168 // wants. See tests/two_parent_workspace_annotate.
169 workspace work(app);
170 revision_t rev;
171 work.get_work_rev(rev);
172 E(rev.edges.size() == 1, origin::user,
173 F("with no revision selected, this command can only be used in "
174 "a single-parent workspace"));
175
176 rid = edge_old_revision(rev.edges.begin());
177
178 // this call will change to something else when the above bug is
179 // fixed, and so should not be merged with the identical call in
180 // the else branch.
181 db.get_roster(rid, roster);
182 }
183 else
184 {
185 complete(app.opts, app.lua, project, idx(app.opts.revision, 0)(), rid);
186 db.get_roster(rid, roster);
187 }
188
189 // find the version of the file requested
190 E(roster.has_node(file), origin::user,
191 F("no such file '%s' in revision %s")
192 % file % rid);
193 const_node_t node = roster.get_node(file);
194 E(is_file_t(node), origin::user,
195 F("'%s' in revision %s is not a file")
196 % file % rid);
197
198 const_file_t file_node = downcast_to_file_t(node);
199 L(FL("annotate for file_id %s") % file_node->self);
200 do_annotate(app, project, file_node, rid, app.opts.revs_only);
201}
202
203CMD(identify, "identify", "", CMD_REF(debug), N_("[PATH]"),
204 N_("Calculates the identity of a file or stdin"),
205 N_("If any PATH is given, calculates their identity; otherwise, the "
206 "one from the standard input is calculated."),
207 options::opts::none)
208{
209 if (args.size() > 1)
210 throw usage(execid);
211
212 data dat;
213
214 if (args.empty())
215 read_data_stdin(dat);
216 else
217 read_data_for_command_line(idx(args, 0), dat);
218
219 id ident;
220 calculate_ident(dat, ident);
221 cout << ident << '\n';
222}
223
224// Name: identify
225// Arguments:
226// 1: a file path
227// Added in: 4.2
228// Purpose: Prints the fileid of the given file (aka hash)
229//
230// Output format: a single, 40 byte long hex-encoded id
231//
232// Error conditions: If the file path doesn't point to a valid file prints
233// an error message to stderr and exits with status 1.
234CMD_AUTOMATE(identify, N_("PATH"),
235 N_("Prints the file identifier of a file"),
236 "",
237 options::opts::none)
238{
239 E(args.size() == 1, origin::user,
240 F("wrong argument count"));
241
242 utf8 path = idx(args, 0);
243
244 E(path() != "-", origin::user,
245 F("cannot read from stdin"));
246
247 data dat;
248 read_data_for_command_line(path, dat);
249
250 id ident;
251 calculate_ident(dat, ident);
252 output << ident << '\n';
253}
254
255static void
256dump_file(database & db, std::ostream & output, file_id const & ident)
257{
258 E(db.file_version_exists(ident), origin::user,
259 F("no file version %s found in database") % ident);
260
261 file_data dat;
262 L(FL("dumping file %s") % ident);
263 db.get_file_version(ident, dat);
264 output << dat;
265}
266
267static void
268dump_file(database & db, std::ostream & output, revision_id rid, utf8 filename)
269{
270 E(db.revision_exists(rid), origin::user,
271 F("no revision %s found in database") % rid);
272
273 // Paths are interpreted as standard external ones when we're in a
274 // workspace, but as project-rooted external ones otherwise.
275 file_path fp = file_path_external(filename);
276
277 roster_t roster;
278 marking_map marks;
279 db.get_roster(rid, roster, marks);
280 E(roster.has_node(fp), origin::user,
281 F("no file '%s' found in revision %s") % fp % rid);
282
283 const_node_t node = roster.get_node(fp);
284 E((!null_node(node->self) && is_file_t(node)), origin::user,
285 F("no file '%s' found in revision %s") % fp % rid);
286
287 const_file_t file_node = downcast_to_file_t(node);
288 dump_file(db, output, file_node->content);
289}
290
291CMD(cat, "cat", "", CMD_REF(informative),
292 N_("FILENAME"),
293 N_("Prints a file from the database"),
294 N_("Fetches the given file FILENAME from the database and prints it "
295 "to the standard output."),
296 options::opts::revision)
297{
298 if (args.size() != 1)
299 throw usage(execid);
300
301 database db(app);
302 revision_id rid;
303 if (app.opts.revision.empty())
304 {
305 workspace work(app);
306 parent_map parents;
307 work.get_parent_rosters(db, parents);
308 E(parents.size() == 1, origin::user,
309 F("this command can only be used in a single-parent workspace"));
310 rid = parent_id(parents.begin());
311 }
312 else
313 {
314 project_t project(db);
315 complete(app.opts, app.lua, project, idx(app.opts.revision, 0)(), rid);
316 }
317
318 dump_file(db, cout, rid, idx(args, 0));
319}
320
321// Name: get_file
322// Arguments:
323// 1: a file id
324// Added in: 1.0
325// Purpose: Prints the contents of the specified file.
326//
327// Output format: The file contents are output without modification.
328//
329// Error conditions: If the file id specified is unknown or invalid prints
330// an error message to stderr and exits with status 1.
331CMD_AUTOMATE(get_file, N_("FILEID"),
332 N_("Prints the contents of a file (given an identifier)"),
333 "",
334 options::opts::none)
335{
336 E(args.size() == 1, origin::user,
337 F("wrong argument count"));
338
339 database db(app);
340 hexenc<id> hident(idx(args, 0)(), origin::user);
341 file_id ident(decode_hexenc_as<file_id>(hident(), hident.made_from));
342 dump_file(db, output, ident);
343}
344
345// Name: get_file_size
346// Arguments:
347// 1: a file id
348// Added in: 13.0
349// Purpose: Prints the recorded size of the file in bytes
350//
351// Output format: A integer > 0
352//
353// Error conditions: If the file id specified is unknown or invalid prints
354// an error message to stderr and exits with status 1.
355CMD_AUTOMATE(get_file_size, N_("FILEID"),
356 N_("Prints the size of a file (given an identifier)"),
357 "",
358 options::opts::none)
359{
360 E(args.size() == 1, origin::user,
361 F("wrong argument count"));
362
363 database db(app);
364 hexenc<id> hident(idx(args, 0)(), origin::user);
365 file_id ident(decode_hexenc_as<file_id>(hident(), hident.made_from));
366
367 E(db.file_version_exists(ident), origin::user,
368 F("no file version %s found in database") % ident);
369
370 file_size size;
371 db.get_file_size(ident, size);
372 output << lexical_cast<string>(size) << "\n";
373}
374
375// Name: get_file_of
376// Arguments:
377// 1: a filename
378//
379// Options:
380// r: a revision id
381//
382// Added in: 4.0
383// Purpose: Prints the contents of the specified file.
384//
385// Output format: The file contents are output without modification.
386//
387// Error conditions: If the file id specified is unknown or invalid prints
388// an error message to stderr and exits with status 1.
389CMD_AUTOMATE(get_file_of, N_("FILENAME"),
390 N_("Prints the contents of a file (given a name)"),
391 "",
392 options::opts::revision)
393{
394 E(args.size() == 1, origin::user,
395 F("wrong argument count"));
396
397 database db(app);
398
399 revision_id rid;
400 if (app.opts.revision.empty())
401 {
402 workspace work(app);
403
404 parent_map parents;
405 work.get_parent_rosters(db, parents);
406 E(parents.size() == 1, origin::user,
407 F("this command can only be used in a single-parent workspace"));
408 rid = parent_id(parents.begin());
409 }
410 else
411 {
412 project_t project(db);
413 complete(app.opts, app.lua, project, idx(app.opts.revision, 0)(), rid);
414 }
415
416 dump_file(db, output, rid, idx(args, 0));
417}
418
419// Local Variables:
420// mode: C++
421// fill-column: 76
422// c-file-style: "gnu"
423// indent-tabs-mode: nil
424// End:
425// 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