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