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