monotone

monotone Mtn Source Tree

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