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