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.empty())
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() > 1)
202 throw usage(execid);
203
204 data dat;
205
206 if (args.empty())
207 read_data_stdin(dat);
208 else
209 read_data_for_command_line(idx(args, 0), dat);
210
211 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 id ident;
243 calculate_ident(dat, ident);
244 output << ident << '\n';
245}
246
247static void
248dump_file(database & db, std::ostream & output, file_id & ident)
249{
250 N(db.file_version_exists(ident),
251 F("no file version %s found in database") % ident);
252
253 file_data dat;
254 L(FL("dumping file %s") % ident);
255 db.get_file_version(ident, dat);
256 output << dat;
257}
258
259static void
260dump_file(database & db, std::ostream & output, revision_id rid, utf8 filename)
261{
262 N(db.revision_exists(rid),
263 F("no such revision '%s'") % rid);
264
265 // Paths are interpreted as standard external ones when we're in a
266 // workspace, but as project-rooted external ones otherwise.
267 file_path fp = file_path_external(filename);
268
269 roster_t roster;
270 marking_map marks;
271 db.get_roster(rid, roster, marks);
272 N(roster.has_node(fp),
273 F("no file '%s' found in revision '%s'") % fp % rid);
274
275 node_t node = roster.get_node(fp);
276 N((!null_node(node->self) && is_file_t(node)),
277 F("no file '%s' found in revision '%s'") % fp % rid);
278
279 file_t file_node = downcast_to_file_t(node);
280 dump_file(db, output, file_node->content);
281}
282
283CMD(cat, "cat", "", CMD_REF(informative),
284 N_("FILENAME"),
285 N_("Prints a file from the database"),
286 N_("Fetches the given file FILENAME from the database and prints it "
287 "to the standard output."),
288 options::opts::revision)
289{
290 if (args.size() != 1)
291 throw usage(execid);
292
293 database db(app);
294 revision_id rid;
295 if (app.opts.revision_selectors.empty())
296 {
297 workspace work(app);
298 parent_map parents;
299 work.get_parent_rosters(db, parents);
300 N(parents.size() == 1,
301 F("this command can only be used in a single-parent workspace"));
302 rid = parent_id(parents.begin());
303 }
304 else
305 {
306 project_t project(db);
307 complete(app.opts, app.lua, project, idx(app.opts.revision_selectors, 0)(), rid);
308 }
309
310 dump_file(db, cout, rid, idx(args, 0));
311}
312
313// Name: get_file
314// Arguments:
315// 1: a file id
316// Added in: 1.0
317// Purpose: Prints the contents of the specified file.
318//
319// Output format: The file contents are output without modification.
320//
321// Error conditions: If the file id specified is unknown or invalid prints
322// an error message to stderr and exits with status 1.
323CMD_AUTOMATE(get_file, N_("FILEID"),
324 N_("Prints the contents of a file (given an identifier)"),
325 "",
326 options::opts::none)
327{
328 N(args.size() == 1,
329 F("wrong argument count"));
330
331 database db(app);
332 hexenc<id> hident(idx(args, 0)());
333 file_id ident(decode_hexenc(hident()));
334 dump_file(db, output, ident);
335}
336
337// Name: get_file_of
338// Arguments:
339// 1: a filename
340//
341// Options:
342// r: a revision id
343//
344// Added in: 4.0
345// Purpose: Prints the contents of the specified file.
346//
347// Output format: The file contents are output without modification.
348//
349// Error conditions: If the file id specified is unknown or invalid prints
350// an error message to stderr and exits with status 1.
351CMD_AUTOMATE(get_file_of, N_("FILENAME"),
352 N_("Prints the contents of a file (given a name)"),
353 "",
354 options::opts::revision)
355{
356 N(args.size() == 1,
357 F("wrong argument count"));
358
359 database db(app);
360
361 revision_id rid;
362 if (app.opts.revision_selectors.empty())
363 {
364 workspace work(app);
365
366 parent_map parents;
367 work.get_parent_rosters(db, parents);
368 N(parents.size() == 1,
369 F("this command can only be used in a single-parent workspace"));
370 rid = parent_id(parents.begin());
371 }
372 else
373 {
374 project_t project(db);
375 complete(app.opts, app.lua, project, idx(app.opts.revision_selectors, 0)(), rid);
376 }
377
378 dump_file(db, output, rid, idx(args, 0));
379}
380
381// Local Variables:
382// mode: C++
383// fill-column: 76
384// c-file-style: "gnu"
385// indent-tabs-mode: nil
386// End:
387// 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