1// copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com> 2// all rights reserved. 3// licensed to the public under the terms of the GNU GPL (>= 2) 4// see the file COPYING for details 5 6#include <map> 7#include <cstdio> 8#include <set> 9#include <vector> 10#include <algorithm> 11#include <iterator> 12#include <boost/filesystem/path.hpp> 13#include <boost/filesystem/operations.hpp> 14#include <boost/lexical_cast.hpp> 15 16#include "commands.hh" 17#include "constants.hh" 18 19#include "app_state.hh" 20#include "diff_patch.hh" 21#include "file_io.hh" 22#include "keys.hh" 23#include "manifest.hh" 24#include "network.hh" 25#include "packet.hh" 26#include "patch_set.hh" 27#include "rcs_import.hh" 28#include "sanity.hh" 29#include "cert.hh" 30#include "transforms.hh" 31#include "update.hh" 32#include "vocab.hh" 33#include "work.hh" 34 35// 36// this file defines the task-oriented "top level" commands which can be 37// issued as part of a monotone command line. the command line can only 38// have one such command on it, followed by a vector of strings which are its 39// arguments. all --options will be processed by the main program *before* 40// calling a command 41// 42// we might expose this blunt command interface to scripting someday. but 43// not today. 44 45namespace commands 46{ 47 struct command; 48 bool operator<(command const & self, command const & other); 49}; 50 51namespace std 52{ 53 template <> 54 struct std::greater<commands::command *> 55 { 56 bool operator()(commands::command const * a, commands::command const * b) 57 { 58 return *a < *b; 59 } 60 }; 61}; 62 63namespace commands 64{ 65using namespace std; 66 67struct command; 68 69static map<string,command *> cmds; 70 71struct command 72{ 73 string name; 74 string cmdgroup; 75 string params; 76 string desc; 77 command(string const & n, 78 string const & g, 79 string const & p, 80 string const & d) : name(n), cmdgroup(g), params(p), desc(d) 81 { cmds[n] = this; } 82 virtual ~command() {} 83 virtual void exec(app_state & app, vector<string> const & args) = 0; 84}; 85 86bool operator<(command const & self, command const & other) 87{ 88 return ((self.cmdgroup < other.cmdgroup) 89 || ((self.cmdgroup == other.cmdgroup) && (self.name < other.name))); 90} 91 92 93void explain_usage(string const & cmd, ostream & out) 94{ 95 map<string,command *>::const_iterator i; 96 i = cmds.find(cmd); 97 if (i != cmds.end()) 98 { 99 out << " " << i->second->name << " " << i->second->params << endl 100 << " " << i->second->desc << endl << endl; 101 return; 102 } 103 104 vector<command *> sorted; 105 out << "commands:" << endl; 106 for (i = cmds.begin(); i != cmds.end(); ++i) 107 { 108 sorted.push_back(i->second); 109 } 110 111 sort(sorted.begin(), sorted.end(), std::greater<command *>()); 112 113 string curr_group; 114 size_t col = 0; 115 size_t col2 = 0; 116 for (size_t i = 0; i < sorted.size(); ++i) 117 { 118 col2 = col2 > sorted[i]->cmdgroup.size() ? col2 : sorted[i]->cmdgroup.size(); 119 } 120 121 for (size_t i = 0; i < sorted.size(); ++i) 122 { 123 if (sorted[i]->cmdgroup != curr_group) 124 { 125 curr_group = sorted[i]->cmdgroup; 126 out << endl; 127 out << " " << sorted[i]->cmdgroup; 128 col = sorted[i]->cmdgroup.size() + 2; 129 while (col++ < (col2 + 3)) 130 out << ' '; 131 } 132 out << " " << sorted[i]->name; 133 col += sorted[i]->name.size() + 1; 134 if (col >= 70) 135 { 136 out << endl; 137 col = 0; 138 while (col++ < (col2 + 3)) 139 out << ' '; 140 } 141 } 142 out << endl << endl; 143} 144 145void process(app_state & app, string const & cmd, vector<string> const & args) 146{ 147 if (cmds.find(cmd) != cmds.end()) 148 { 149 L("executing %s command\n", cmd.c_str()); 150 cmds[cmd]->exec(app, args); 151 } 152 else 153 { 154 throw usage(cmd); 155 } 156} 157 158#define CMD(C, group, params, desc) \ 159struct cmd_ ## C : public command \ 160{ \ 161 cmd_ ## C() : command(#C, group, params, desc) \ 162 {} \ 163 virtual void exec(app_state & app, \ 164 vector<string> const & args); \ 165}; \ 166static cmd_ ## C C ## _cmd; \ 167void cmd_ ## C::exec(app_state & app, \ 168 vector<string> const & args) \ 169 170#define ALIAS(C, realcommand, group, params, desc) \ 171CMD(C, group, params, desc) \ 172{ \ 173 process(app, string(#realcommand), args); \ 174} 175 176 177static void ensure_bookdir() 178{ 179 mkdir_p(local_path(book_keeping_dir)); 180} 181 182static void get_manifest_path(local_path & m_path) 183{ 184 m_path = (fs::path(book_keeping_dir) / fs::path(manifest_file_name)).string(); 185 L("manifest path is %s\n", m_path().c_str()); 186} 187 188static void get_work_path(local_path & w_path) 189{ 190 w_path = (fs::path(book_keeping_dir) / fs::path(work_file_name)).string(); 191 L("work path is %s\n", w_path().c_str()); 192} 193 194static void get_manifest_map(manifest_map & m) 195{ 196 ensure_bookdir(); 197 local_path m_path; 198 base64< gzip<data> > m_data; 199 get_manifest_path(m_path); 200 if (file_exists(m_path)) 201 { 202 L("loading manifest file %s\n", m_path().c_str()); 203 read_data(m_path, m_data); 204 read_manifest_map(manifest_data(m_data), m); 205 L("read %d manifest entries\n", m.size()); 206 } 207 else 208 { 209 L("no manifest file %s\n", m_path().c_str()); 210 } 211} 212 213static void put_manifest_map(manifest_map const & m) 214{ 215 ensure_bookdir(); 216 local_path m_path; 217 manifest_data m_data; 218 get_manifest_path(m_path); 219 L("writing manifest file %s\n", m_path().c_str()); 220 write_manifest_map(m, m_data); 221 write_data(m_path, m_data.inner()); 222 L("wrote %d manifest entries\n", m.size()); 223} 224 225static void get_work_set(work_set & w) 226{ 227 ensure_bookdir(); 228 local_path w_path; 229 get_work_path(w_path); 230 if (file_exists(w_path)) 231 { 232 L("checking for un-committed work file %s\n", 233 w_path().c_str()); 234 data w_data; 235 read_data(w_path, w_data); 236 read_work_set(w_data, w); 237 L("read %d dels, %d adds from %s\n", 238 w.dels.size(), w.adds.size(), w_path().c_str()); 239 } 240 else 241 { 242 L("no un-committed work file %s\n", w_path().c_str()); 243 } 244} 245 246static void put_work_set(work_set & w) 247{ 248 local_path w_path; 249 get_work_path(w_path); 250 251 if (w.adds.size() > 0 252 || w.dels.size() > 0) 253 { 254 ensure_bookdir(); 255 data w_data; 256 write_work_set(w_data, w); 257 write_data(w_path, w_data); 258 } 259 else 260 { 261 delete_file(w_path); 262 } 263} 264 265static void calculate_new_manifest_map(manifest_map const & m_old, 266 manifest_map & m_new) 267{ 268 path_set paths; 269 work_set work; 270 extract_path_set(m_old, paths); 271 get_work_set(work); 272 if (work.dels.size() > 0) 273 L("removing %d dead files from manifest\n", 274 work.dels.size()); 275 if (work.adds.size() > 0) 276 L("adding %d files to manifest\n", work.adds.size()); 277 apply_work_set(work, paths); 278 build_manifest_map(paths, m_new); 279} 280 281static string get_stdin() 282{ 283 char buf[bufsz]; 284 string tmp; 285 while(cin) 286 { 287 cin.read(buf, bufsz); 288 tmp.append(buf, cin.gcount()); 289 } 290 return tmp; 291} 292 293static void get_log_message(patch_set const & ps, 294 app_state & app, 295 string & log_message) 296{ 297 string commentary; 298 string summary; 299 stringstream ss; 300 patch_set_to_text_summary(ps, ss); 301 summary = ss.str(); 302 commentary += "----------------------------------------------------------------------\n"; 303 commentary += "Enter Log. Lines beginning with `MT:' are removed automatically\n"; 304 commentary += "\n"; 305 commentary += "Summary of changes:\n"; 306 commentary += "\n"; 307 commentary += summary; 308 commentary += "----------------------------------------------------------------------\n"; 309 N(app.lua.hook_edit_comment(commentary, log_message), 310 "edit of log message failed"); 311} 312 313 314// the goal here is to look back through the ancestry of the provided 315// child, checking to see the least ancestor it has which we received from 316// the given network url/group pair. 317// 318// we use the ancestor as the source manifest when building a patchset to 319// send to that url/group. 320 321static bool find_ancestor_on_netserver (manifest_id const & child, 322 url const & u, 323 group const & g, 324 manifest_id & anc, 325 app_state & app) 326{ 327 set<manifest_id> frontier; 328 cert_name tn(ancestor_cert_name); 329 frontier.insert(child); 330 331 while (!frontier.empty()) 332 { 333 set<manifest_id> next_frontier; 334 for (set<manifest_id>::const_iterator i = frontier.begin(); 335 i != frontier.end(); ++i) 336 { 337 vector< manifest<cert> > tmp; 338 app.db.get_manifest_certs(*i, tn, tmp); 339 340 // we go through this vector backwards because we would prefer to 341 // hit more recently-queued ancestors (such as intermediate nodes 342 // in a multi-node merge) rather than older ancestors. but of 343 // course, any ancestor will do. 344 345 for (vector< manifest<cert> >::reverse_iterator j = tmp.rbegin(); 346 j != tmp.rend(); ++j) 347 { 348 cert_value tv; 349 decode_base64(j->inner().value, tv); 350 manifest_id anc_id (tv()); 351 352 L("looking for parent %s of %s on server\n", 353 i->inner()().c_str(), 354 anc_id.inner()().c_str()); 355 356 if (app.db.manifest_exists_on_netserver (u, g, anc_id)) 357 { 358 L("found parent %s on server\n", anc_id.inner()().c_str()); 359 anc = anc_id; 360 return true; 361 } 362 else 363 next_frontier.insert(anc_id); 364 } 365 } 366 367 frontier = next_frontier; 368 } 369 370 return false; 371} 372 373 374static void queue_edge_for_target_ancestor (pair<url,group> const & targ, 375 manifest_id const & child_id, 376 manifest_map const & child_map, 377 app_state & app) 378{ 379 // now here is an interesting thing: we *might* be sending data to a 380 // depot, or someone with indeterminate pre-existing state (say the first 381 // time we post to netnews), therefore we cannot just "send the edge" we 382 // just constructed in a merge or commit, we need to send an edge from a 383 // parent which we know to be present in the depot (or else an edge from 384 // the empty map -- full contents of all files). what is sent therefore 385 // changes on a depot-by-depot basis. this function calculates the 386 // appropriate thing to send. 387 // 388 // nb: this has no direct relation to what we store in our own 389 // database. we always store the edge from our parent, and we always know 390 // when we have a parent. 391 392 vector< pair<url, group> > one_target; 393 one_target.push_back(targ); 394 queueing_packet_writer qpw(app, one_target); 395 396 manifest_data targ_ancestor_data; 397 manifest_map targ_ancestor_map; 398 manifest_id targ_ancestor_id; 399 400 if (find_ancestor_on_netserver (child_id, 401 targ.first, 402 targ.second, 403 targ_ancestor_id, 404 app)) 405 { 406 app.db.get_manifest_version(targ_ancestor_id, targ_ancestor_data); 407 read_manifest_map(targ_ancestor_data, targ_ancestor_map); 408 } 409 410 patch_set ps; 411 manifests_to_patch_set(targ_ancestor_map, child_map, app, ps); 412 patch_set_to_packets(ps, app, qpw); 413 414 // now that we've queued the data, we can note this new child 415 // node as existing (well .. soon-to-exist) on the server 416 app.db.note_manifest_on_netserver (targ.first, targ.second, child_id); 417 418} 419 420 421// this helper tries to produce merge <- mergeN(left,right), possibly 422// merge3 if it can find an ancestor, otherwise merge2. it also queues the 423// appropriate edges from known ancestors to the new merge node, to be 424// transmitted to each of the targets provided. 425 426static void try_one_merge(manifest_id const & left, 427 manifest_id const & right, 428 manifest_id & merged, 429 app_state & app, 430 vector< pair<url,group> > const & targets) 431{ 432 manifest_data left_data, right_data, ancestor_data, merged_data; 433 manifest_map left_map, right_map, ancestor_map, merged_map; 434 manifest_id ancestor; 435 436 app.db.get_manifest_version(left, left_data); 437 app.db.get_manifest_version(right, right_data); 438 read_manifest_map(left_data, left_map); 439 read_manifest_map(right_data, right_map); 440 441 simple_merge_provider merger(app); 442 443 if(find_common_ancestor(left, right, ancestor, app)) 444 { 445 P("common ancestor %s found, trying merge3\n", ancestor.inner()().c_str()); 446 app.db.get_manifest_version(ancestor, ancestor_data); 447 read_manifest_map(ancestor_data, ancestor_map); 448 N(merge3(ancestor_map, left_map, right_map, 449 app, merger, merged_map), 450 (string("failed to merge manifests ") 451 + left.inner()() + " and " + right.inner()())); 452 } 453 else 454 { 455 P("no common ancestor found, trying merge2\n"); 456 N(merge2(left_map, right_map, app, merger, merged_map), 457 (string("failed to merge manifests ") 458 + left.inner()() + " and " + right.inner()())); 459 } 460 461 write_manifest_map(merged_map, merged_data); 462 calculate_manifest_map_ident(merged_map, merged); 463 464 base64< gzip<delta> > left_edge; 465 diff(left_data.inner(), merged_data.inner(), left_edge); 466 467 // FIXME: we do *not* manufacture or store the second edge to 468 // the merged version, since doing so violates the 469 // assumptions of the db, and the 'right' version already 470 // exists in its entirety, anyways. this is a subtle issue 471 // though and I'm not sure I'm making the right 472 // decision. revisit. if you do not see that it is a subtle 473 // issue I suggest you are not thinking about it long enough. 474 // 475 // base64< gzip<delta> > right_edge; 476 // diff(right_data.inner(), merged_data.inner(), right_edge); 477 // app.db.put_manifest_version(right, merged, right_edge); 478 479 480 // we do of course record the left edge, and ancestry relationship to 481 // both predecessors. 482 483 { 484 packet_db_writer dbw(app); 485 486 dbw.consume_manifest_delta(left, merged, left_edge); 487 cert_manifest_ancestor(left, merged, app, dbw); 488 cert_manifest_ancestor(right, merged, app, dbw); 489 cert_manifest_date_now(merged, app, dbw); 490 cert_manifest_author_default(merged, app, dbw); 491 492 // make sure the appropriate edges get queued for the network. 493 for (vector< pair<url,group> >::const_iterator targ = targets.begin(); 494 targ != targets.end(); ++targ) 495 { 496 queue_edge_for_target_ancestor (*targ, merged, merged_map, app); 497 } 498 499 queueing_packet_writer qpw(app, targets); 500 cert_manifest_ancestor(left, merged, app, qpw); 501 cert_manifest_ancestor(right, merged, app, qpw); 502 cert_manifest_date_now(merged, app, qpw); 503 cert_manifest_author_default(merged, app, qpw); 504 } 505 506} 507 508 509// actual commands follow 510 511CMD(lscerts, "key and cert", "(file|manifest) <id>", 512 "list certs associated with manifest or file") 513{ 514 if (args.size() != 2) 515 throw usage(name); 516 517 vector<cert> certs; 518 519 transaction_guard guard(app.db); 520 521 if (args[0] == "manifest") 522 { 523 manifest_id ident(args[1]); 524 vector< manifest<cert> > ts; 525 app.db.get_manifest_certs(ident, ts); 526 for (size_t i = 0; i < ts.size(); ++i) 527 certs.push_back(ts[i].inner()); 528 } 529 else if (args[0] == "file") 530 { 531 file_id ident(args[1]); 532 vector< file<cert> > ts; 533 app.db.get_file_certs(ident, ts); 534 for (size_t i = 0; i < ts.size(); ++i) 535 certs.push_back(ts[i].inner()); 536 } 537 else 538 throw usage(name); 539 540 for (size_t i = 0; i < certs.size(); ++i) 541 { 542 bool ok = check_cert(app, certs[i]); 543 cert_value tv; 544 decode_base64(certs[i].value, tv); 545 string washed; 546 if (guess_binary(tv())) 547 { 548 washed = "<binary data>"; 549 } 550 else 551 { 552 washed = tv(); 553 } 554 string head = string(ok ? "ok sig from " : "bad sig from ") 555 + "[" + certs[i].key() + "] : " 556 + "[" + certs[i].name() + "] = ["; 557 string pad(head.size(), ' '); 558 vector<string> lines; 559 split_into_lines(washed, lines); 560 I(lines.size() > 0); 561 cout << head << lines[0] ; 562 for (size_t i = 1; i < lines.size(); ++i) 563 cout << endl << pad << lines[i]; 564 cout << "]" << endl; 565 } 566 guard.commit(); 567} 568 569CMD(lskeys, "key and cert", "[partial-id]", "list keys") 570{ 571 vector<rsa_keypair_id> pubkeys; 572 vector<rsa_keypair_id> privkeys; 573 574 transaction_guard guard(app.db); 575 576 if (args.size() == 0) 577 app.db.get_key_ids("", pubkeys, privkeys); 578 else if (args.size() == 1) 579 app.db.get_key_ids(args[0], pubkeys, privkeys); 580 else 581 throw usage(name); 582 583 if (pubkeys.size() > 0) 584 { 585 cout << endl << "[public keys]" << endl; 586 for (size_t i = 0; i < pubkeys.size(); ++i) 587 cout << pubkeys[i]() << endl; 588 cout << endl; 589 } 590 591 if (privkeys.size() > 0) 592 { 593 cout << endl << "[private keys]" << endl; 594 for (size_t i = 0; i < privkeys.size(); ++i) 595 cout << privkeys[i]() << endl; 596 cout << endl; 597 } 598 599 guard.commit(); 600} 601 602CMD(genkey, "key and cert", "<keyid>", "generate an RSA key-pair") 603{ 604 if (args.size() != 1) 605 throw usage(name); 606 607 transaction_guard guard(app.db); 608 rsa_keypair_id ident(args[0]); 609 610 N(! app.db.key_exists(ident), 611 (string("key '") + ident() + "' already exists in database")); 612 613 base64<rsa_pub_key> pub; 614 base64< arc4<rsa_priv_key> > priv; 615 P("generating key-pair '%s'\n", ident().c_str()); 616 generate_key_pair(app.lua, ident, pub, priv); 617 P("storing key-pair '%s' in database\n", ident().c_str()); 618 app.db.put_key_pair(ident, pub, priv); 619 620 guard.commit(); 621} 622 623CMD(cert, "key and cert", "(file|manifest) <id> <certname> [certval]", 624 "create a cert for a file or manifest") 625{ 626 if ((args.size() != 4) && (args.size() != 3)) 627 throw usage(name); 628 629 transaction_guard guard(app.db); 630 631 hexenc<id> ident(args[1]); 632 cert_name name(args[2]); 633 634 rsa_keypair_id key; 635 if (app.signing_key() != "") 636 key = app.signing_key; 637 else 638 N(guess_default_key(key, app), 639 "no unique private key found, and no key specified"); 640 641 cert_value val; 642 if (args.size() == 4) 643 val = cert_value(args[3]); 644 else 645 val = cert_value(get_stdin()); 646 647 base64<cert_value> val_encoded; 648 encode_base64(val, val_encoded); 649 650 cert t(ident, name, val_encoded, key); 651 652 // nb: we want to throw usage on mis-use *before* asking for a 653 // passphrase. 654 655 if (args[0] == "file") 656 { 657 calculate_cert(app, t); 658 app.db.put_file_cert(file<cert>(t)); 659 } 660 else if (args[0] == "manifest") 661 { 662 calculate_cert(app, t); 663 app.db.put_manifest_cert(manifest<cert>(t)); 664 } 665 else 666 throw usage(this->name); 667 668 guard.commit(); 669} 670 671 672CMD(tag, "certificate", "<id> <tagname>", 673 "put a symbolic tag cert on a manifest version") 674{ 675 if (args.size() != 2) 676 throw usage(name); 677 manifest_id m(args[0]); 678 packet_db_writer dbw(app); 679 cert_manifest_tag(m, args[1], app, dbw); 680} 681 682CMD(approve, "certificate", "(file|manifest) <id>", 683 "approve of a manifest or file version") 684{ 685 if (args.size() != 2) 686 throw usage(name); 687 if (args[0] == "manifest") 688 { 689 manifest_id m(args[1]); 690 packet_db_writer dbw(app); 691 cert_manifest_approval(m, true, app, dbw); 692 } 693 else if (args[0] == "file") 694 { 695 packet_db_writer dbw(app); 696 file_id f(args[1]); 697 cert_file_approval(f, true, app, dbw); 698 } 699 else 700 throw usage(name); 701} 702 703CMD(disapprove, "certificate", "(file|manifest) <id>", 704 "disapprove of a manifest or file version") 705{ 706 if (args.size() != 2) 707 throw usage(name); 708 if (args[0] == "manifest") 709 { 710 manifest_id m(args[1]); 711 packet_db_writer dbw(app); 712 cert_manifest_approval(m, false, app, dbw); 713 } 714 else if (args[0] == "file") 715 { 716 file_id f(args[1]); 717 packet_db_writer dbw(app); 718 cert_file_approval(f, false, app, dbw); 719 } 720 else 721 throw usage(name); 722} 723 724 725CMD(comment, "certificate", "(file|manifest) <id> [comment]", 726 "comment on a file or manifest version") 727{ 728 if (args.size() != 2 && args.size() != 3) 729 throw usage(name); 730 731 string comment; 732 if (args.size() == 3) 733 comment = args[2]; 734 else 735 N(app.lua.hook_edit_comment("", comment), "edit comment failed"); 736 737 N(comment.find_first_not_of(" \r\t\n") == string::npos, "empty comment"); 738 739 if (args[0] == "file") 740 { 741 packet_db_writer dbw(app); 742 cert_file_comment(file_id(args[1]), comment, app, dbw); 743 } 744 else if (args[0] == "manifest") 745 { 746 packet_db_writer dbw(app); 747 cert_manifest_comment(manifest_id(args[1]), comment, app, dbw); 748 } 749 else 750 throw usage(name); 751} 752 753 754 755CMD(add, "working copy", "<pathname> [...]", "add files to working copy") 756{ 757 if (args.size() < 1) 758 throw usage(name); 759 760 transaction_guard guard(app.db); 761 762 manifest_map man; 763 work_set work; 764 get_manifest_map(man); 765 get_work_set(work); 766 bool rewrite_work = false; 767 768 for (vector<string>::const_iterator i = args.begin(); i != args.end(); ++i) 769 build_addition(file_path(*i), app, work, man, rewrite_work); 770 771 guard.commit(); 772 773 // small race here 774 if (rewrite_work) 775 put_work_set(work); 776} 777 778CMD(drop, "working copy", "<pathname> [...]", "drop files from working copy") 779{ 780 if (args.size() < 1) 781 throw usage(name); 782 783 manifest_map man; 784 work_set work; 785 get_manifest_map(man); 786 get_work_set(work); 787 bool rewrite_work = false; 788 789 transaction_guard guard(app.db); 790 791 for (vector<string>::const_iterator i = args.begin(); i != args.end(); ++i) 792 build_deletion(file_path(*i), app, work, man, rewrite_work); 793 794 guard.commit(); 795 796 // small race here 797 if (rewrite_work) 798 put_work_set(work); 799} 800 801CMD(commit, "working copy", "[log message]", "commit working copy to database") 802{ 803 string log_message(""); 804 manifest_map m_old, m_new; 805 patch_set ps; 806 807 get_manifest_map(m_old); 808 calculate_new_manifest_map(m_old, m_new); 809 manifest_id old_id, new_id; 810 calculate_manifest_map_ident(m_old, old_id); 811 calculate_manifest_map_ident(m_new, new_id); 812 813 if (args.size() != 0 && args.size() != 1) 814 throw usage(name); 815 816 cert_value branchname; 817 if (app.branch_name != "") 818 { 819 branchname = app.branch_name; 820 } 821 else 822 { 823 vector< manifest<cert> > certs; 824 cert_name branch(branch_cert_name); 825 app.db.get_manifest_certs(old_id, branch, certs); 826 827 N(certs.size() != 0, 828 string("no branch certs found for old manifest ") 829 + old_id.inner()() + ", please provide a branch name"); 830 831 N(certs.size() == 1, 832 string("multiple branch certs found for old manifest ") 833 + old_id.inner()() + ", please provide a branch name"); 834 835 decode_base64(certs[0].inner().value, branchname); 836 } 837 838 L("committing %s to branch %s\n", 839 new_id.inner()().c_str(), branchname().c_str()); 840 app.branch_name = branchname(); 841 842 manifests_to_patch_set(m_old, m_new, app, ps); 843 844 // get log message 845 if (args.size() == 1) 846 log_message = args[0]; 847 else 848 get_log_message(ps, app, log_message); 849 850 N(log_message.find_first_not_of(" \r\t\n") != string::npos, 851 "empty log message"); 852 853 { 854 transaction_guard guard(app.db); 855 856 // process manifest delta or new manifest 857 if (app.db.manifest_version_exists(ps.m_new)) 858 { 859 L("skipping manifest %s, already in database\n", ps.m_new.inner()().c_str()); 860 } 861 else 862 { 863 if (app.db.manifest_version_exists(ps.m_old)) 864 { 865 L("inserting manifest delta %s -> %s\n", 866 ps.m_old.inner()().c_str(), ps.m_new.inner()().c_str()); 867 manifest_data m_old_data, m_new_data; 868 app.db.get_manifest_version(ps.m_old, m_old_data); 869 write_manifest_map(m_new, m_new_data); 870 base64< gzip<delta> > del; 871 diff(m_old_data.inner(), m_new_data.inner(), del); 872 app.db.put_manifest_version(ps.m_old, ps.m_new, manifest_delta(del)); 873 } 874 else 875 { 876 L("inserting full manifest %s\n", 877 ps.m_new.inner()().c_str()); 878 manifest_data m_new_data; 879 write_manifest_map(m_new, m_new_data); 880 app.db.put_manifest(ps.m_new, m_new_data); 881 } 882 } 883 884 // process file deltas 885 for (set<patch_delta>::const_iterator i = ps.f_deltas.begin(); 886 i != ps.f_deltas.end(); ++i) 887 { 888 if (app.db.file_version_exists(i->id_new)) 889 { 890 L("skipping file delta %s, already in database\n", i->id_new.inner()().c_str()); 891 } 892 else 893 { 894 if (app.db.file_version_exists(i->id_old)) 895 { 896 L("inserting delta %s -> %s\n", 897 i->id_old.inner()().c_str(), i->id_new.inner()().c_str()); 898 file_data old_data; 899 base64< gzip<data> > new_data; 900 app.db.get_file_version(i->id_old, old_data); 901 read_data(i->path, new_data); 902 base64< gzip<delta> > del; 903 diff(old_data.inner(), new_data, del); 904 app.db.put_file_version(i->id_old, i->id_new, file_delta(del)); 905 } 906 else 907 { 908 L("inserting full version %s\n", i->id_old.inner()().c_str()); 909 base64< gzip<data> > new_data; 910 read_data(i->path, new_data); 911 // sanity check 912 hexenc<id> tid; 913 calculate_ident(new_data, tid); 914 I(tid == i->id_new.inner()); 915 app.db.put_file(i->id_new, file_data(new_data)); 916 } 917 } 918 } 919 920 // process file adds 921 for (set<patch_addition>::const_iterator i = ps.f_adds.begin(); 922 i != ps.f_adds.end(); ++i) 923 { 924 if (app.db.file_version_exists(i->ident)) 925 { 926 L("skipping file %s %s, already in database\n", 927 i->path().c_str(), i->ident.inner()().c_str()); 928 } 929 else 930 { 931 // it's a new file 932 L("inserting new file %s %s\n", 933 i->path().c_str(), i->ident.inner()().c_str()); 934 base64< gzip<data> > new_data; 935 read_data(i->path, new_data); 936 app.db.put_file(i->ident, new_data); 937 } 938 } 939 940 packet_db_writer dbw(app); 941 942 if (! m_old.empty()) 943 cert_manifest_ancestor(ps.m_old, ps.m_new, app, dbw); 944 945 cert_manifest_in_branch(ps.m_new, branchname, app, dbw); 946 cert_manifest_date_now(ps.m_new, app, dbw); 947 cert_manifest_author_default(ps.m_new, app, dbw); 948 cert_manifest_changelog(ps.m_new, log_message, app, dbw); 949 950 // commit done, now queue diff for sending 951 952 if (app.db.manifest_version_exists(ps.m_new)) 953 { 954 vector< pair<url,group> > targets; 955 app.lua.hook_get_post_targets(branchname, targets); 956 957 // make sure the appropriate edges get queued for the network. 958 for (vector< pair<url,group> >::const_iterator targ = targets.begin(); 959 targ != targets.end(); ++targ) 960 { 961 queue_edge_for_target_ancestor (*targ, ps.m_new, m_new, app); 962 } 963 964 // throw in all available certs for good measure 965 queueing_packet_writer qpw(app, targets); 966 vector< manifest<cert> > certs; 967 app.db.get_manifest_certs(ps.m_new, certs); 968 for(vector< manifest<cert> >::const_iterator i = certs.begin(); 969 i != certs.end(); ++i) 970 qpw.consume_manifest_cert(*i); 971 } 972 973 guard.commit(); 974 } 975 // small race condition here... 976 local_path w_path; 977 get_work_path(w_path); 978 delete_file(w_path); 979 put_manifest_map(m_new); 980 P("committed %s\n", ps.m_new.inner()().c_str()); 981} 982 983CMD(update, "working copy", "[sort keys...]", "update working copy, relative to sorting keys") 984{ 985 986 manifest_data m_chosen_data; 987 manifest_map m_old, m_working, m_chosen, m_new; 988 manifest_id m_old_id, m_chosen_id; 989 990 transaction_guard guard(app.db); 991 992 get_manifest_map(m_old); 993 calculate_manifest_map_ident(m_old, m_old_id); 994 calculate_new_manifest_map(m_old, m_working); 995 996 pick_update_target(m_old_id, args, app, m_chosen_id); 997 P("selected update target %s\n", 998 m_chosen_id.inner()().c_str()); 999 app.db.get_manifest_version(m_chosen_id, m_chosen_data); 1000 read_manifest_map(m_chosen_data, m_chosen); 1001 1002 update_merge_provider merger(app); 1003 N(merge3(m_old, m_chosen, m_working, app, merger, m_new), 1004 string("manifest merge failed, no update performed")); 1005 1006 P("calculating patchset for update\n"); 1007 patch_set ps; 1008 manifests_to_patch_set(m_working, m_new, app, ps); 1009 1010 L("applying %d deletions to files in tree\n", ps.f_dels.size()); 1011 for (set<file_path>::const_iterator i = ps.f_dels.begin(); 1012 i != ps.f_dels.end(); ++i) 1013 { 1014 L("deleting %s\n", (*i)().c_str()); 1015 delete_file(*i); 1016 } 1017 1018 L("applying %d moves to files in tree\n", ps.f_moves.size()); 1019 for (set<patch_move>::const_iterator i = ps.f_moves.begin(); 1020 i != ps.f_moves.end(); ++i) 1021 { 1022 L("moving %s -> %s\n", i->path_old().c_str(), i->path_new().c_str()); 1023 move_file(i->path_old, i->path_new); 1024 } 1025 1026 L("applying %d additions to tree\n", ps.f_adds.size()); 1027 for (set<patch_addition>::const_iterator i = ps.f_adds.begin(); 1028 i != ps.f_adds.end(); ++i) 1029 { 1030 L("adding %s as %s\n", i->ident.inner()().c_str(), i->path().c_str()); 1031 file_data tmp; 1032 if (app.db.file_version_exists(i->ident)) 1033 app.db.get_file_version(i->ident, tmp); 1034 else if (merger.temporary_store.find(i->ident) != merger.temporary_store.end()) 1035 tmp = merger.temporary_store[i->ident]; 1036 else 1037 I(false); // trip assert. this should be impossible. 1038 write_data(i->path, tmp.inner()); 1039 } 1040 1041 L("applying %d deltas to tree\n", ps.f_deltas.size()); 1042 for (set<patch_delta>::const_iterator i = ps.f_deltas.begin(); 1043 i != ps.f_deltas.end(); ++i) 1044 { 1045 P("updating file %s: %s -> %s\n", 1046 i->path().c_str(), 1047 i->id_old.inner()().c_str(), 1048 i->id_new.inner()().c_str()); 1049 1050 // sanity check 1051 { 1052 base64< gzip<data> > dtmp; 1053 hexenc<id> dtmp_id; 1054 read_data(i->path, dtmp); 1055 calculate_ident(dtmp, dtmp_id); 1056 I(dtmp_id == i->id_old.inner()); 1057 } 1058 1059 // ok, replace with new version 1060 { 1061 file_data tmp; 1062 if (app.db.file_version_exists(i->id_new)) 1063 app.db.get_file_version(i->id_new, tmp); 1064 else if (merger.temporary_store.find(i->id_new) != merger.temporary_store.end()) 1065 tmp = merger.temporary_store[i->id_new]; 1066 else 1067 I(false); // trip assert. this should be impossible. 1068 write_data(i->path, tmp.inner()); 1069 } 1070 } 1071 1072 L("update successful\n"); 1073 guard.commit(); 1074 1075 // small race condition here... 1076 // nb: we write out m_chosen, not m_new, because the manifest-on-disk 1077 // is the basis of the working copy, not the working copy itself. 1078 put_manifest_map(m_chosen); 1079 P("updated to base version %s\n", m_chosen_id.inner()().c_str()); 1080} 1081 1082 1083 1084CMD(cat, "tree", "(file|manifest) <id>", "write file or manifest from database to stdout") 1085{ 1086 if (args.size() != 2) 1087 throw usage(name); 1088 1089 transaction_guard guard(app.db); 1090 1091 if (args[0] == "file") 1092 { 1093 file_data dat; 1094 file_id ident(args[1]); 1095 1096 N(app.db.file_version_exists(ident), 1097 (string("no file version ") + ident.inner()() + " found in database")); 1098 1099 L("dumping file %s\n", ident.inner()().c_str()); 1100 app.db.get_file_version(ident, dat); 1101 data unpacked; 1102 unpack(dat.inner(), unpacked); 1103 cout.write(unpacked().data(), unpacked().size()); 1104 1105 } 1106 else if (args[0] == "manifest") 1107 { 1108 manifest_data dat; 1109 manifest_id ident(args[1]); 1110 1111 N(app.db.manifest_version_exists(ident), 1112 (string("no file version ") + ident.inner()() + " found in database")); 1113 1114 L("dumping manifest %s\n", ident.inner()().c_str()); 1115 app.db.get_manifest_version(ident, dat); 1116 data unpacked; 1117 unpack(dat.inner(), unpacked); 1118 cout.write(unpacked().data(), unpacked().size()); 1119 } 1120 else 1121 throw usage(name); 1122 1123 guard.commit(); 1124} 1125 1126 1127CMD(checkout, "tree", "<manifest-id>", "check out tree state from database") 1128{ 1129 if (args.size() != 1) 1130 throw usage(name); 1131 1132 transaction_guard guard(app.db); 1133 1134 file_data data; 1135 manifest_id ident(args[0]); 1136 manifest_map m; 1137 1138 N(app.db.manifest_version_exists(ident), 1139 (string("no manifest version ") + ident.inner()() + " found in database")); 1140 1141 L("exporting manifest %s\n", ident.inner()().c_str()); 1142 manifest_data m_data; 1143 app.db.get_manifest_version(ident, m_data); 1144 read_manifest_map(m_data, m); 1145 put_manifest_map(m); 1146 1147 for (manifest_map::const_iterator i = m.begin(); i != m.end(); ++i) 1148 { 1149 vector<string> args; 1150 path_id_pair pip(*i); 1151 1152 N(app.db.file_version_exists(pip.ident()), 1153 (string("no file version ") 1154 + pip.ident().inner()() 1155 + " found in database for " 1156 + pip.path()().c_str())); 1157 1158 file_data dat; 1159 L("writing file %s to %s\n", 1160 pip.ident().inner()().c_str(), 1161 pip.path()().c_str()); 1162 app.db.get_file_version(pip.ident(), dat); 1163 write_data(pip.path(), dat.inner()); 1164 } 1165 1166 guard.commit(); 1167} 1168 1169ALIAS(co, checkout, "tree", "<manifest-id>", 1170 "check out tree state from database; alias for checkout") 1171 1172CMD(heads, "tree", "", "show unmerged heads of branch") 1173{ 1174 vector<manifest_id> heads; 1175 if (args.size() != 0) 1176 throw usage(name); 1177 1178 if (app.branch_name == "") 1179 { 1180 cout << "please specify a branch, with --branch=<branchname>" << endl; 1181 return; 1182 } 1183 1184 get_branch_heads(app.branch_name, app, heads); 1185 1186 if (heads.size() == 0) 1187 cout << "branch '" << app.branch_name << "' is empty" << endl; 1188 else if (heads.size() == 1) 1189 cout << "branch '" << app.branch_name << "' is currently merged:" << endl; 1190 else 1191 cout << "branch '" << app.branch_name << "' is currently unmerged:" << endl; 1192 1193 for (vector<manifest_id>::const_iterator i = heads.begin(); 1194 i != heads.end(); ++i) 1195 { 1196 cout << i->inner()() << endl; 1197 } 1198} 1199 1200 1201CMD(merge, "tree", "", "merge unmerged heads of branch") 1202{ 1203 1204 vector<manifest_id> heads; 1205 1206 if (args.size() != 0) 1207 throw usage(name); 1208 1209 if (app.branch_name == "") 1210 { 1211 cout << "please specify a branch, with --branch=<branchname>" << endl; 1212 return; 1213 } 1214 1215 get_branch_heads(app.branch_name, app, heads); 1216 1217 if (heads.size() == 0) 1218 { 1219 cout << "branch " << args[0] << "is empty" << endl; 1220 return; 1221 } 1222 else if (heads.size() == 1) 1223 { 1224 cout << "branch " << args[0] << "is merged" << endl; 1225 return; 1226 } 1227 else 1228 { 1229 vector< pair<url,group> > targets; 1230 app.lua.hook_get_post_targets(app.branch_name, targets); 1231 1232 manifest_id left = heads[0]; 1233 manifest_id ancestor; 1234 for (size_t i = 1; i < heads.size(); ++i) 1235 { 1236 manifest_id right = heads[i]; 1237 P("merging with manifest %d / %d: %s <-> %s\n", 1238 i, heads.size(), 1239 left.inner()().c_str(), right.inner()().c_str()); 1240 1241 manifest_id merged; 1242 transaction_guard guard(app.db); 1243 try_one_merge (left, right, merged, app, targets); 1244 1245 // merged 1 edge; now we commit this, update merge source and 1246 // try next one 1247 1248 packet_db_writer dbw(app); 1249 queueing_packet_writer qpw(app, targets); 1250 cert_manifest_in_branch(merged, app.branch_name, app, dbw); 1251 cert_manifest_in_branch(merged, app.branch_name, app, qpw); 1252 1253 string log = "merge of " + left.inner()() + " and " + right.inner()(); 1254 cert_manifest_changelog(merged, log, app, dbw); 1255 cert_manifest_changelog(merged, log, app, qpw); 1256 1257 guard.commit(); 1258 P("[source] %s\n[source] %s\n[merged] %s\n", 1259 left.inner()().c_str(), 1260 right.inner()().c_str(), 1261 merged.inner()().c_str()); 1262 left = merged; 1263 } 1264 } 1265} 1266 1267 1268CMD(propagate, "tree", "<src-branch> <dst-branch>", 1269 "merge from one branch to another asymmetrically") 1270{ 1271 /* 1272 1273 this is a special merge operator, but very useful for people maintaining 1274 "slightly disparate but related" trees. it does a one-way merge; less 1275 powerful than putting things in the same branch and also more flexible. 1276 1277 1. check to see if src and dst branches are merged, if not abort, if so 1278 call heads N1 and N2 respectively. 1279 1280 2. (FIXME: not yet present) run the hook propagate ("src-branch", 1281 "dst-branch", N1, N2) which gives the user a chance to massage N1 into 1282 a state which is likely to "merge nicely" with N2, eg. edit pathnames, 1283 omit optional files of no interest. 1284 1285 3. do a normal 2 or 3-way merge on N1 and N2, depending on the 1286 existence of common ancestors. 1287 1288 4. save the results as the delta (N2,M), the ancestry edges (N1,M) 1289 and (N2,M), and the cert (N2,dst). 1290 1291 5. queue the resulting packets to send to the url for dst-branch, not 1292 src-branch. 1293 1294 */ 1295 1296 vector<manifest_id> src_heads, dst_heads; 1297 1298 if (args.size() != 2) 1299 throw usage(name); 1300 1301 get_branch_heads(args[0], app, src_heads); 1302 get_branch_heads(args[1], app, dst_heads); 1303 1304 if (src_heads.size() == 0) 1305 { 1306 cout << "branch " << args[0] << "is empty" << endl; 1307 return; 1308 } 1309 else if (src_heads.size() != 1) 1310 { 1311 cout << "branch " << args[0] << "is not merged" << endl; 1312 return; 1313 } 1314 else if (dst_heads.size() == 0) 1315 { 1316 cout << "branch " << args[1] << "is empty" << endl; 1317 return; 1318 } 1319 else if (dst_heads.size() != 1) 1320 { 1321 cout << "branch " << args[1] << "is not merged" << endl; 1322 return; 1323 } 1324 else 1325 { 1326 vector< pair<url,group> > targets; 1327 app.lua.hook_get_post_targets(args[1], targets); 1328 1329 manifest_id merged; 1330 transaction_guard guard(app.db); 1331 try_one_merge (src_heads[0], dst_heads[0], merged, app, targets); 1332 1333 queueing_packet_writer qpw(app, targets); 1334 cert_manifest_in_branch(merged, app.branch_name, app, qpw); 1335 cert_manifest_changelog(merged, 1336 "propagate of " 1337 + src_heads[0].inner()() 1338 + " and " 1339 + dst_heads[0].inner()() 1340 + "\n" 1341 + "from branch " 1342 + args[0] + " to " + args[1] + "\n", 1343 app, qpw); 1344 guard.commit(); 1345 } 1346} 1347 1348 1349 1350CMD(diff, "informative", "", "show current diffs on stdout") 1351{ 1352 manifest_map m_old, m_new; 1353 patch_set ps; 1354 1355 transaction_guard guard(app.db); 1356 1357 if (args.size() > 0) 1358 { 1359 manifest_data dat; 1360 manifest_id ident(args[0]); 1361 1362 N(app.db.manifest_version_exists(ident), 1363 (string("no file version ") + ident.inner()() + " found in database")); 1364 1365 L("getting manifest %s\n", ident.inner()().c_str()); 1366 app.db.get_manifest_version(ident, dat); 1367 read_manifest_map(dat, m_old); 1368 } 1369 else 1370 get_manifest_map(m_old); 1371 1372 if (args.size() > 1) 1373 { 1374 manifest_data dat; 1375 manifest_id ident(args[1]); 1376 1377 N(app.db.manifest_version_exists(ident), 1378 (string("no file version ") + ident.inner()() + " found in database")); 1379 1380 L("getting manifest %s\n", ident.inner()().c_str()); 1381 app.db.get_manifest_version(ident, dat); 1382 read_manifest_map(dat, m_new); 1383 } 1384 else 1385 calculate_new_manifest_map(m_old, m_new); 1386 1387 manifests_to_patch_set(m_old, m_new, app, ps); 1388 1389 for (set<patch_delta>::const_iterator i = ps.f_deltas.begin(); 1390 i != ps.f_deltas.end(); ++i) 1391 { 1392 file_data f_old, f_new; 1393 gzip<data> decoded_old, decoded_new; 1394 data decompressed_old, decompressed_new; 1395 vector<string> old_lines, new_lines; 1396 1397 app.db.get_file_version(i->id_old, f_old); 1398 decode_base64(f_old.inner(), decoded_old); 1399 decode_gzip(decoded_old, decompressed_old); 1400 1401 if (args.size() > 1) 1402 { 1403 app.db.get_file_version(i->id_new, f_new); 1404 decode_base64(f_new.inner(), decoded_new); 1405 decode_gzip(decoded_new, decompressed_new); 1406 } 1407 else 1408 read_data(i->path, decompressed_new); 1409 1410 split_into_lines(decompressed_old(), old_lines); 1411 split_into_lines(decompressed_new(), new_lines); 1412 1413 unidiff(i->path(), i->path(), old_lines, new_lines, cout); 1414 } 1415 guard.commit(); 1416} 1417 1418CMD(status, "informative", "", "show status of working copy") 1419{ 1420 manifest_map m_old, m_new; 1421 patch_set ps; 1422 1423 transaction_guard guard(app.db); 1424 get_manifest_map(m_old); 1425 calculate_new_manifest_map(m_old, m_new); 1426 manifests_to_patch_set(m_old, m_new, app, ps); 1427 patch_set_to_text_summary(ps, cout); 1428 guard.commit(); 1429} 1430 1431 1432CMD(mdelta, "packet i/o", "<oldid> <newid>", "write manifest delta packet to stdout") 1433{ 1434 if (args.size() != 2) 1435 throw usage(name); 1436 1437 transaction_guard guard(app.db); 1438 packet_writer pw(cout); 1439 1440 manifest_id m_old_id, m_new_id; 1441 manifest_data m_old_data, m_new_data; 1442 manifest_map m_old, m_new; 1443 patch_set ps; 1444 m_old_id = hexenc<id>(args[0]); 1445 m_new_id = hexenc<id>(args[1]); 1446 app.db.get_manifest_version(m_old_id, m_old_data); 1447 app.db.get_manifest_version(m_new_id, m_new_data); 1448 read_manifest_map(m_old_data, m_old); 1449 read_manifest_map(m_new_data, m_new); 1450 manifests_to_patch_set(m_old, m_new, app, ps); 1451 patch_set_to_packets(ps, app, pw); 1452 guard.commit(); 1453} 1454 1455CMD(fdelta, "packet i/o", "<oldid> <newid>", "write file delta packet to stdout") 1456{ 1457 if (args.size() != 2) 1458 throw usage(name); 1459 1460 transaction_guard guard(app.db); 1461 packet_writer pw(cout); 1462 1463 file_id f_old_id, f_new_id; 1464 file_data f_old_data, f_new_data; 1465 f_old_id = hexenc<id>(args[0]); 1466 f_new_id = hexenc<id>(args[1]); 1467 app.db.get_file_version(f_old_id, f_old_data); 1468 app.db.get_file_version(f_new_id, f_new_data); 1469 base64< gzip<delta> > del; 1470 diff(f_old_data.inner(), f_new_data.inner(), del); 1471 pw.consume_file_delta(f_old_id, f_new_id, file_delta(del)); 1472 guard.commit(); 1473} 1474 1475CMD(mdata, "packet i/o", "<id>", "write manifest data packet to stdout") 1476{ 1477 if (args.size() != 1) 1478 throw usage(name); 1479 1480 transaction_guard guard(app.db); 1481 packet_writer pw(cout); 1482 1483 manifest_id m_id; 1484 manifest_data m_data; 1485 m_id = hexenc<id>(args[0]); 1486 app.db.get_manifest_version(m_id, m_data); 1487 pw.consume_manifest_data(m_id, m_data); 1488 guard.commit(); 1489} 1490 1491 1492CMD(fdata, "packet i/o", "<id>", "write file data packet to stdout") 1493{ 1494 if (args.size() != 1) 1495 throw usage(name); 1496 1497 transaction_guard guard(app.db); 1498 packet_writer pw(cout); 1499 1500 file_id f_id; 1501 file_data f_data; 1502 f_id = hexenc<id>(args[0]); 1503 app.db.get_file_version(f_id, f_data); 1504 pw.consume_file_data(f_id, f_data); 1505 guard.commit(); 1506} 1507 1508CMD(mcerts, "packet i/o", "<id>", "write manifest cert packets to stdout") 1509{ 1510 if (args.size() != 1) 1511 throw usage(name); 1512 1513 transaction_guard guard(app.db); 1514 packet_writer pw(cout); 1515 1516 manifest_id m_id; 1517 vector< manifest<cert> > certs; 1518 1519 m_id = hexenc<id>(args[0]); 1520 app.db.get_manifest_certs(m_id, certs); 1521 for (size_t i = 0; i < certs.size(); ++i) 1522 pw.consume_manifest_cert(certs[i]); 1523 guard.commit(); 1524} 1525 1526CMD(fcerts, "packet i/o", "<id>", "write file cert packets to stdout") 1527{ 1528 if (args.size() != 1) 1529 throw usage(name); 1530 1531 transaction_guard guard(app.db); 1532 packet_writer pw(cout); 1533 1534 file_id f_id; 1535 vector< file<cert> > certs; 1536 1537 f_id = hexenc<id>(args[0]); 1538 app.db.get_file_certs(f_id, certs); 1539 for (size_t i = 0; i < certs.size(); ++i) 1540 pw.consume_file_cert(certs[i]); 1541 guard.commit(); 1542} 1543 1544CMD(pubkey, "packet i/o", "<id>", "write public key packet to stdout") 1545{ 1546 if (args.size() != 1) 1547 throw usage(name); 1548 1549 transaction_guard guard(app.db); 1550 packet_writer pw(cout); 1551 rsa_keypair_id ident(args[0]); 1552 base64< rsa_pub_key > key; 1553 app.db.get_key(ident, key); 1554 pw.consume_public_key(ident, key); 1555 guard.commit(); 1556} 1557 1558CMD(privkey, "packet i/o", "<id>", "write private key packet to stdout") 1559{ 1560 if (args.size() != 1) 1561 throw usage(name); 1562 1563 transaction_guard guard(app.db); 1564 packet_writer pw(cout); 1565 rsa_keypair_id ident(args[0]); 1566 base64< arc4<rsa_priv_key> > key; 1567 app.db.get_key(ident, key); 1568 pw.consume_private_key(ident, key); 1569 guard.commit(); 1570} 1571 1572 1573CMD(read, "packet i/o", "", "read packets from stdin") 1574{ 1575 transaction_guard guard(app.db); 1576 packet_db_writer dbw(app, true); 1577 size_t count = read_packets(cin, dbw); 1578 N(count != 0, "no packets found on stdin"); 1579 if (count == 1) 1580 P("read 1 packet\n"); 1581 else 1582 P("read %d packets\n", count); 1583 guard.commit(); 1584} 1585 1586 1587CMD(agraph, "graph visualization", "", "dump ancestry graph to stdout") 1588{ 1589 vector< manifest<cert> > certs; 1590 transaction_guard guard(app.db); 1591 app.db.get_manifest_certs(ancestor_cert_name, certs); 1592 set<string> nodes; 1593 vector< pair<string, string> > edges; 1594 for(vector< manifest<cert> >::iterator i = certs.begin(); 1595 i != certs.end(); ++i) 1596 { 1597 cert_value tv; 1598 decode_base64(i->inner().value, tv); 1599 nodes.insert(tv()); 1600 nodes.insert(i->inner().ident()); 1601 edges.push_back(make_pair(tv(), i->inner().ident())); 1602 } 1603 cout << "graph: " << endl << "{" << endl; // open graph 1604 for (set<string>::iterator i = nodes.begin(); i != nodes.end(); 1605 ++i) 1606 { 1607 cout << "node: { title : \"" << *i << "\"}" << endl; 1608 } 1609 for (vector< pair<string,string> >::iterator i = edges.begin(); i != edges.end(); 1610 ++i) 1611 { 1612 cout << "edge: { sourcename : \"" << i->first << "\"" << endl 1613 << " targetname : \"" << i->second << "\" }" << endl; 1614 } 1615 cout << "}" << endl << endl; // close graph 1616 guard.commit(); 1617} 1618 1619CMD(fetch, "network", "[URL] [groupname]", "fetch recent changes from network") 1620{ 1621 if (args.size() > 2) 1622 throw usage(name); 1623 1624 vector< pair<url,group> > sources; 1625 1626 if (args.size() == 0) 1627 { 1628 if (app.branch_name == "") 1629 { 1630 P("no branch name provided, fetching from all known URLs\n"); 1631 app.db.get_all_known_sources(sources); 1632 } 1633 else 1634 { 1635 N(app.lua.hook_get_fetch_sources(app.branch_name, sources), 1636 ("no URL / group pairs found for branch " + app.branch_name)); 1637 } 1638 } 1639 else 1640 { 1641 N(args.size() == 2, "need URL and groupname"); 1642 sources.push_back(make_pair(url(args[0]), 1643 group(args[1]))); 1644 } 1645 1646 fetch_queued_blobs_from_network(sources, app); 1647} 1648 1649CMD(post, "network", "[URL] [groupname]", "post queued changes to network") 1650{ 1651 if (args.size() > 2) 1652 throw usage(name); 1653 1654 vector< pair<url,group> > targets; 1655 if (args.size() == 0) 1656 { 1657 if (app.branch_name == "") 1658 { 1659 P("no branch name provided, posting all queued targets\n"); 1660 app.db.get_queued_targets(targets); 1661 } 1662 else 1663 { 1664 N(app.lua.hook_get_post_targets(app.branch_name, targets), 1665 ("no URL / group pairs found for branch " + app.branch_name)); 1666 } 1667 } 1668 else 1669 { 1670 N(args.size() == 2, "need URL and groupname"); 1671 targets.push_back(make_pair(url(args[0]), 1672 group(args[1]))); 1673 } 1674 1675 post_queued_blobs_to_network(targets, app); 1676} 1677 1678 1679CMD(rcs_import, "rcs", "<rcsfile> ...", "import all versions in RCS files") 1680{ 1681 if (args.size() < 1) 1682 throw usage(name); 1683 1684 transaction_guard guard(app.db); 1685 for (vector<string>::const_iterator i = args.begin(); 1686 i != args.end(); ++i) 1687 { 1688 import_rcs_file(fs::path(*i), app.db); 1689 } 1690 guard.commit(); 1691} 1692 1693 1694CMD(cvs_import, "rcs", "<cvsroot>", "import all versions in CVS repository") 1695{ 1696 if (args.size() != 1) 1697 throw usage(name); 1698 1699 import_cvs_repo(fs::path(args.at(0)), app); 1700} 1701 1702 1703}; // namespace commands 1704