The unified diff between revisions [cc2e50fd..] and [4c6270c7..] is displayed below. It can also be downloaded as a raw diff.

#
#
# delete "tests/t_lua_privkey.at"
#
# rename "po/sv_SE.po"
#     to "po/sv.po"
#
# rename "tests/t_diff_outside_working_dir.at"
#     to "tests/t_diff_outside_workspace.at"
#
# rename "tests/t_log_depth.at"
#     to "tests/t_log_last_next.at"
#
# rename "tests/t_log_outside_working_dir.at"
#     to "tests/t_log_outside_workspace.at"
#
# add_file "contrib/ChangeLog.sh"
#  content [43ef52b9ec1e9b09e26ef0de1f5a4d7b156347ef]
#
# add_file "contrib/edit_comment_from_changelog.lua"
#  content [392821013c884731eaef31836acab2c2c5938373]
#
# add_file "tests/t_check_db_format.at"
#  content [4ea08f24426ca4b52f79bb2b9046d822c7548c9f]
#
# add_file "tests/t_no_persist_phrase.at"
#  content [4c25100bf7d7c11892124eaaebeb2b32d671e134]
#
# add_file "tests/t_rename_destdir.at"
#  content [8e16b441f52d44260f34fa9edd99ca92c3b5863a]
#
# patch "AUTHORS"
#  from [789d404e3fdc93f737e1a6338d92c10443adb1ee]
#    to [be2883d201fdd61b63582334f52312a640e012a1]
#
# patch "ChangeLog"
#  from [f92ab5a6ad615b29c1bab7d73dada3301543b899]
#    to [119c14c3c86f103f563ce65dfaab3797e2f44f24]
#
# patch "HACKING"
#  from [2d70798fc515faf275cf6e817cfef4f76e1f4b39]
#    to [dd8654f0de0f726639e8fc988b30e6ac272c173d]
#
# patch "INSTALL"
#  from [54295f34283c38aee1a57864548eceeeae69f4c3]
#    to [e8c5a114eb284a7163393b4721179147dbc165ac]
#
# patch "Makefile.am"
#  from [73bbbf9fadf07f9f85d194f011de6671b8dbbbda]
#    to [1086b6f0ea35d96bdd46455c7d97c5934fb77e5a]
#
# patch "ROADMAP"
#  from [53568f4fbd5d01ef09b991793d62f0fd2a0db58f]
#    to [745d2ad91a86dc235175a570896ae219fb9853ac]
#
# patch "annotate.cc"
#  from [abc86290badfdb97601384d7e8ed9cc5fc012adf]
#    to [da2b9e34836d772f45c7214d268ebeca7044c0c7]
#
# patch "app_state.cc"
#  from [c01ca1020ed025d4a7153030d25fda87d03645f9]
#    to [18045740e735562c70e3a482927003729014844f]
#
# patch "app_state.hh"
#  from [6f33817c2a63c63e5b893cebbc49f863195b2d82]
#    to [7fa51b357c748547e5d71dbc84fb35077c148177]
#
# patch "automate.cc"
#  from [0b608759447d6c0c9f4aed5289f60a89decac403]
#    to [68d579fc833b4a0ec52a0c8e5b130af7517ab7ed]
#
# patch "cert.cc"
#  from [c02a7b0abf62fa3d0485985df5317154041025cf]
#    to [289ab8fc2d5c2622d3f563bd57a37442efbb52b8]
#
# patch "commands.cc"
#  from [1c35354bb631e6dec76f36cfeebfe19be4e8116f]
#    to [19e176916ef418e11386e0b0b76078121a201421]
#
# patch "configure.ac"
#  from [44606bdf31f4bf99cb64d42a5a0e65c1f0c95036]
#    to [3db7a41acaf0a99ca96e19c12c2f96e5eaea45b1]
#
# patch "contrib/mtbrowse.sh"
#  from [f8d866a9eb798aecc974120b90aad5ca40ad4a4b]
#    to [bbcbc8835c2fe6038086c4c15ae86b4301d90cc8]
#
# patch "database.cc"
#  from [7d8fa179c2619f9bab52439e8eff4ede97ef714a]
#    to [2d2107adceb23e96ac42776c4b9e8658224581d0]
#
# patch "database.hh"
#  from [1a013cf26ba78ba1ba658f14ffd727b1d4053c63]
#    to [5d30ef040b7ce46fb8ad65578d68b1693db397d8]
#
# patch "diff_patch.cc"
#  from [622032933fcadaeec86c9fd3c90f157d96b3f6e3]
#    to [62b598d468407c93c6b573a1e7eb97bdd52644c8]
#
# patch "diff_patch.hh"
#  from [094dfdf8f752939ee4dac54366939df07e20898a]
#    to [fb7ab63b1e55fe0278df0c422cd090f555516326]
#
# patch "file_io.hh"
#  from [78b32a51c65a8e43b200d72c1dc8f6871bb59267]
#    to [2082dc3c8480400f32fcbe1c4d881d7512de2204]
#
# patch "keys.cc"
#  from [6a9950c8f9a3d70c82ee7f9519be3f1679afbcdb]
#    to [b61b2cda83c67d33c1a2fb3ff0f7098ecfeb1570]
#
# patch "lua.cc"
#  from [c8c228b16b396eaf8cbc1b41ca6363593e9abfee]
#    to [78722201fdc513bfee248ce6b30b1b1ae593250e]
#
# patch "lua.hh"
#  from [71e2943fa623254b4e3e1340e1f8c4b442271a6c]
#    to [0d1c63ed6c7b36665ad92780ed88afa1edff3194]
#
# patch "merge.hh"
#  from [cd2797de2edf9cc40867385e4a8167be98e7ddfa]
#    to [d8869e646a690f203ff3c85764929d89399f16cd]
#
# patch "merkle_tree.cc"
#  from [7892d63a6614ece8b94d54e8674688eeb9dea008]
#    to [0e353f8460c079b618906210fb3abb23fc550944]
#
# patch "merkle_tree.hh"
#  from [92d813eec1d4aedec4a8f4aba15cb333372032bb]
#    to [1c8305b2f3e3d1e29742113f7f989c61c37487bd]
#
# patch "monotone.1"
#  from [9d6e28ed91576baedfc295f6a51eb6de75527aa5]
#    to [f7f22f2d6e5bebf0d526d15651869113fe9c6ead]
#
# patch "monotone.cc"
#  from [230bf4a63ae962252dd05590b0b6263b9035ff0d]
#    to [92399bda2cd0fba92739636d7b0955c1c43bea23]
#
# patch "monotone.texi"
#  from [cbc87b47e45384cb320b2b59c4ebdf0ae82f2867]
#    to [59eb4911052af4a3a1743caf529acfe14dad092b]
#
# patch "netcmd.cc"
#  from [9ac3f6b7a08c59615bfd86fcad7ca931fb1b8758]
#    to [5a63ccd88ddb02cf5853e13ce3fff6ea20851a53]
#
# patch "netsync.cc"
#  from [81c12af4a0791840ac675220fd06bee263434554]
#    to [1b80ff0d6f984f2723b60cca060f076c7bbb6c57]
#
# patch "options.hh"
#  from [1d2cb0bc377c4b051b2fbd288a5225891bcb8b35]
#    to [6dc48543e2cb20ea82ded241dc5bb6088c3cb09c]
#
# patch "paths.cc"
#  from [a88d2a803ce9e069bd8b0b6d5db4d2c2593de689]
#    to [227025632497d6849fc4946feeded4d7980f7008]
#
# patch "paths.hh"
#  from [f61b1b0f8a6fe9f4b6a55ff1a5513412856cb75c]
#    to [727b9cfba7c9d150dfd7144d0d38744beb8dda7f]
#
# patch "platform.hh"
#  from [f0d95c6add214117b2aa25646a9f62f750764a53]
#    to [0219de39c44ef3898973d9568b5ce26593dbca7c]
#
# patch "po/LINGUAS"
#  from [689ffd44aa62146365ad8b1ba6bdb27bcba72ad0]
#    to [636384d477e9075e67ae689dff5d44ff9fcb4626]
#
# patch "po/pt_BR.po"
#  from [080e73cb4023790b6d88405bb7870950e795e9d9]
#    to [5a3a39f8673a62b0f47c203373fd53d36481d041]
#
# patch "po/sv.po"
#  from [de4cea58cd3ae4662548a5182900ab3ea623cdac]
#    to [e347bfacd741eafd3202feda54aa54e24b3a7bfb]
#
# patch "refiner.cc"
#  from [fc755a9526c0d0abac8c91375e05800b9983112d]
#    to [e2e2a951c54476c717d83d4429647733f46b6aeb]
#
# patch "revision.cc"
#  from [c5a08a3f68665482a0dee20500612884dcc591ca]
#    to [8cda143f1b14ea5d67e40c9dc325b5ec6f2fd1a7]
#
# patch "roster.cc"
#  from [10951343bda88f90980ea365b580d3efb12869d3]
#    to [e1c0e9fe71f80b5bd457801d3cf4eb3240872c22]
#
# patch "sanity.hh"
#  from [d2494fb0f9188882227ca0ce6584efadb48fbb8b]
#    to [ae8401c8dc9afd96ed17ebdb5362fa3cf735d405]
#
# patch "std_hooks.lua"
#  from [efa95818a151a349c981859891931c2ef6533e11]
#    to [1740ad529749fadac6c115b8373ca26e9f932ad6]
#
# patch "tests/t_add_vs_commit.at"
#  from [795e4288501968701031a986173a6d3d4bfce367]
#    to [854fb99182fa82f283d2a26b089e770909461fa5]
#
# patch "tests/t_automate_inventory.at"
#  from [390524783f3f55102d9d20a3606cff4b09ed9722]
#    to [1a9fcca95a722a81cf5fe49a6dd7025900c4e311]
#
# patch "tests/t_commit_message_file.at"
#  from [447a09a7a56815e3298f30a6c84a89882340c029]
#    to [ff5ee2bfe41e4807ee909ee5319db3d7eff38173]
#
# patch "tests/t_cvsimport.at"
#  from [cdb0a29f99f219dd5c2f4ebb2f3da47003ca10d7]
#    to [42e05f340ace04dd4bfdc4e948ab651669f8d2b8]
#
# patch "tests/t_cvsimport_samelog.at"
#  from [ccac785c1ac8421e23723f4cdd9e69ce10be7e93]
#    to [515ea15748990c4be6f61a9a811266b58fb331e7]
#
# patch "tests/t_database_check.at"
#  from [b034de4e58d344e3ac343a04f29bea851aa93717]
#    to [332a5aec62e318573ab12e222b0cd31e56951b97]
#
# patch "tests/t_database_check_normalized.at"
#  from [4a5108a8905b371eb7d723788b294c1dcc31a8ac]
#    to [a226dd38826693885d4b9209528dac5fced42d6f]
#
# patch "tests/t_db_execute.at"
#  from [cad25454a737ae7296cf95712501b7320b4e3898]
#    to [8d4eb06517101eb8b46af0d7e71649beda8c2e4b]
#
# patch "tests/t_diff_outside_workspace.at"
#  from [68e1ab2a3186b905dc91af7814cb0dc10594bb3f]
#    to [28c8bf8ea6fc2088a754308386f6ab936506dc77]
#
# patch "tests/t_drop_missing.at"
#  from [76df22384469606ea77735754609229018a6f6a5]
#    to [b57855a8b89e7f7c8c7cab3ae9fdf660bc4c0111]
#
# patch "tests/t_existsonpath.at"
#  from [4f555bc7a0c0f3d218a445d3fb78cdcf57147345]
#    to [984ebe7ce8dbd63cb065fc1fd5f17c16df3c9fb6]
#
# patch "tests/t_lf_crlf.at"
#  from [8e233d81e10aa6d9bab1d7bb38ba7f183ad151b2]
#    to [19b497fd65b459240b83270df348bab82f09962c]
#
# patch "tests/t_log_last_next.at"
#  from [850fc51a2454f9e2a9a352bed17e9f2e485f13b6]
#    to [b90e1c651ecda4499cd37ce76444701b55c43c09]
#
# patch "tests/t_log_outside_workspace.at"
#  from [1e6f8f708369425dfb8bf1f06f4b9f283e24de01]
#    to [408e3882754be40081c953771285895e36fd2de8]
#
# patch "tests/t_ls_known.at"
#  from [99eb14f8f7144c26b858c836b5fcdc355d0a246d]
#    to [6c871d99df8c34c37190d281ca538aa5c90e37a4]
#
# patch "tests/t_netsync_single.at"
#  from [aad7ba8fa47299d3c9ab357521166b6038364cd3]
#    to [62381331e93d7aca1f92dcbd673144d40a86f1f4]
#
# patch "tests/t_no_rename_overwrite.at"
#  from [1fe86d8eeda1180521602e9b66628aadad9b75ac]
#    to [1f1e1481d40d08fbd33eae39850f79fd4804b4a7]
#
# patch "tests/t_rename.at"
#  from [c237939fa3e27409437b44b2117942e07f05779e]
#    to [b95e23675e65c4c3c9bd2b3490d08ce117b11836]
#
# patch "tests/t_restricted_commands_consistent.at"
#  from [a8a896d3209ec673f8197488d639226d62b18158]
#    to [e11f8aafa55195d6350e35bd53e9fc0e11b0d1d6]
#
# patch "tests/t_setup_existing_path.at"
#  from [0f84f1e69ba45ca565d23fdf122d91154af1d286]
#    to [ce17bd423597f38adb7065e230cc73e226aebfeb]
#
# patch "tests/t_singlecvs.at"
#  from [3ab763cc4821f58ecc8cea7ff0d1b8f62004c8ad]
#    to [d98391bad7a3290996b9a4354e173ccd3277b001]
#
# patch "tests/t_undo_update.at"
#  from [85012f2040dd0f9f6aa13b3c748bf38ea9c10fe7]
#    to [23512408a71b29946fbeb5b1d5058c6a5f9abd87]
#
# patch "tests/t_update_to_revision.at"
#  from [883c66fa494c93d79518368f88752de1821a0951]
#    to [5c67e05438436f0e76bbb4b535481cb4b0f5a27d]
#
# patch "tests/t_update_with_pending_add.at"
#  from [3d046ef7f426d6e5d89b0423c72242fde52142e4]
#    to [af1ac1b21a52bade76fa8fa92cae2c5be1a18321]
#
# patch "tests/t_update_with_pending_drop.at"
#  from [af78561aa074371619cdcdd49ec78084dd0ecf0d]
#    to [04407e4305991ff2358e52325d584674e8f0fcd2]
#
# patch "tests/t_update_with_pending_rename.at"
#  from [c77760a120745388dc4fda95d8d617b7c052ce42]
#    to [2577008031f11dbdeaceeda708d4238b5b52c641]
#
# patch "testsuite.at"
#  from [a1e90a759bbe7ea6d49bdd600cb46c515af9ddc2]
#    to [9e39ba6a09e3b23919675a2ae8adf1275eac5e08]
#
# patch "vocab.cc"
#  from [d8da5a3ac67bb8c2b29a5cb5f3e3704e1375d954]
#    to [084a7a4538c3e0b062b901d0ba891fd6b66cfdfe]
#
# patch "work.cc"
#  from [5f27cea289f3b61f7f645643bf7d6b158ca031ce]
#    to [974c1d8a5295fd7317898e60abc636fdcc82ba65]
#
# patch "work.hh"
#  from [abc46f82b48bd3a9c31beb58446c3ef484880088]
#    to [723c827976adf80b589df8c0e2629ed7fafb907e]
#
#   set "contrib/ChangeLog.sh"
#  attr "mtn:execute"
# value "true"
#
============================================================
--- contrib/ChangeLog.sh	43ef52b9ec1e9b09e26ef0de1f5a4d7b156347ef
+++ contrib/ChangeLog.sh	43ef52b9ec1e9b09e26ef0de1f5a4d7b156347ef
@@ -0,0 +1,72 @@
+#!/bin/sh
+# Usage: ChangeLog [<num>] [-d database] [-r revision]
+# Construct and print a ChangeLog for the last <num> revisions from their
+# date, author, and changelog certs.
+
+# If <num> is not given, it defaults to 15
+
+# If this script is not run from the root of a monotone workspace, both
+# the -d and -r options are required.
+
+NUM=15
+
+while ! [ $# -eq 0 ] ; do
+	case "$1" in
+		-r) shift; REV="$1";;
+		-d) shift; DB="-d $1";;
+		*)  NUM=$(($1 - 1));;
+	esac
+	shift
+done
+
+if ! [ -s MT/revision ]; then
+	if [ "x$REV" = "x" ] || [ "x$DB" = "x" ]; then
+		echo "Both the -d and -r arguments are needed when this" >&2;
+		echo "script is not run from the root of a monotone workspace." >&2;
+		exit 1;
+	fi
+else
+	if [ "x$REV" = "x" ]; then
+		REV=`cat MT/revision`
+	fi
+fi
+
+# Get the contents of a cert
+LOG='/^Name.*changelog$/,/^----/! D; /^Name/ D; /^----/ D'
+DATE='/^Name.*date$/,/^----/! D; /^Name/ D; /^----/ D'
+AUTHOR='/^Name.*author$/,/^----/! D; /^Name/ D; /^----/ D'
+
+# Remove "duplicate" lines (When the date+author line is unneeded because
+# the same info is in the changelog cert (won't match exactly, but should
+# both start with ^${year} ))
+# Keep the line from the changelog, instead of the generated one
+RD=':b; N; /^[[:digit:]]\{4\}.*\n[[:digit:]]\{4\}/ { s/^.*\n//; b b; }; P; D'
+
+get()
+{
+	monotone $DB ls certs "$2" | sed "$1" \
+	| sed 's/^[^\:]\+\: //g'
+}
+
+getrevs()
+{
+	monotone $DB automate ancestors "$1" \
+	| monotone $DB automate toposort -@- \
+	| tail -n "$2" | tac
+}
+
+getlogs()
+{
+	for i in "$REV" `getrevs "$REV" "$NUM"`; do
+		echo `get "$DATE" "$i"` '' `get "$AUTHOR" "$i"`
+		get "$LOG" "$i" | sed 's/^\([^[:digit:]\t]\)/\t\1/g'
+	done
+}
+
+
+if [ ! x$REV = x ]; then
+	getlogs | sed "$RD" | sed '/^$/ d' \
+	| sed 's/^\([[:digit:]]\{4\}.*\)$/\n\1\n/g'
+else
+	echo "MT/revision does not exist!" >&2
+fi
============================================================
--- contrib/edit_comment_from_changelog.lua	392821013c884731eaef31836acab2c2c5938373
+++ contrib/edit_comment_from_changelog.lua	392821013c884731eaef31836acab2c2c5938373
@@ -0,0 +1,27 @@
+std_edit_comment = edit_comment
+function edit_comment(basetext, user_log_message)
+	local tmp, tname = temp_file()
+	if (tmp == nil) then return nil end
+	if (user_log_message == "") then
+		local ChangeLog = io.open("ChangeLog", "r")
+		if ChangeLog == nil then
+			return std_edit_comment(basetext, user_log_message)
+		end
+		local line = ChangeLog:read()
+		local msg = ""
+		local n = 0
+		while(line ~= nil and n < 2) do
+			if (string.find(line, "^[^%s]")) then
+				n = n + 1
+			end
+			if (n < 2 and not string.find(line, "^%s*$"))
+			then
+				msg = msg .. line .. "\n"
+			end
+			line = ChangeLog:read()
+		end
+		user_log_message = msg
+		io.close(ChangeLog)
+	end
+	return std_edit_comment(basetext, user_log_message)
+end
============================================================
--- tests/t_check_db_format.at	4ea08f24426ca4b52f79bb2b9046d822c7548c9f
+++ tests/t_check_db_format.at	4ea08f24426ca4b52f79bb2b9046d822c7548c9f
@@ -0,0 +1,99 @@
+AT_SETUP([db data format checking])
+MONOTONE_SETUP
+NEED_UNGZB64
+
+# A few times in our history we've had to do data migration beyond
+# what the "db migrate" command can handle -- i.e., "db changesetify"
+# and "db rosterify".  We would like for it to be the case, that all
+# other commands give a nice error message if these commands need to
+# be run.  This test makes sure that monotone exits with an error if
+# given a db that appears to be very old.
+
+AT_DATA([changesetify.db.dump.gz.b64], [H4sICA9Nz0MCA2NoYW5nZXNldGlmeS5kYi5kdW1wAO1WW2/aShB+xr9ilZcQlaZcDIRWkWrA
+IlAMCZdcenRkrb3rSwK2YxuI+fWdtcE4tuG0PdV5OpZAsN6Z+Wa++Wa3Lfb6IyQ+dobzaf9e
+/MJ1JqIwE9FMaA9FpLjYUg2ZOrZqeFyhyBUM7BnIsn1krRYLtLLM1xUtof3z8SMKN9gacukS
+m5Zp6Ugz6YJ4yKMOdrFPCVICdPb5jCtE3o95A1/Ptml5aGP6Brhbm55pW7JKXd+7XOPFinKF
+ENjBQeoBDxCB2Etk0LeP1FJtAtFNwhUuUnkSRV5j1+P2lsX4F1hDGnGIUuzZU22HskSx4yxM
+FfsALvyLwFNsbuElTRuH5rAJvdAg3hgmlNm52xhlu98alakYISuFIS7il+nMNHNBZUIXPmb8
+FbiCSfKS8V0bmNpzx4yQals+tXwwU7CXgvaeG7bduwTHtpuMd8kqHf58Z8xYIaamId9mMSD0
+SvWRunJdiIY0F+hiAbnCLk+TlMKFiyxtYeCwLSG445pL7AasqqWfyYrgBLB9IVR76bjU8ygE
+3TfM3iRil3kJkfRHU3EyQ/3RbBwBQffCcC5Oi+eVVrVFWxpfVxrNZlOtVUi50cQtrVap8bhe
+I0q93KB8Uz0vnd/wXl/YPZ8C/3Y7ffsWULUjDKQ1f3t3w9a583TeS2yZGvX8SAwndflrisw2
+R54W9+H3nMdwErznN37oyXYwgGPsMCyqYXvUYghWHnW5Qr4QkobKwla4AtDsYNPNk0wCqrNS
+QJwybI5geaZuYX/lpiOA1WQqfJreCBV02ANFO/uLZfLVJJ8B2N9ncVey1RJivRkCLqEdntLB
+PKdhU5Xat25utqnuxUCrb1DoSqgbNBsMJRz7y9NophYUvuiBrRPkZUX7Z3UbN9AR7f5OAbJy
+/llFH3ykVB3DjJVdbl5R2sDVKgibb/AVTKo1TeNJs6o1sUrUuqpqBN6mlb0dl0fDgeTPN1tp
+Xg6krVqbbZ+qs664kcofyrOuVB11vw9G3fvxdNavSKJXkeath7n4/I2bqltd0Du8W9vcDTfM
+3fV1diRY9M2XXdvzqStbkKRsrZYK6KnIsX/JAicFZS0C+IpqGrCK+obpIR8rC+hougb7dJyE
+oP7s3Mm2QOSFITYJADTB2s2MC3h9jHkQ9A4uYvGWXobgRDIxxYRe8Uq9WSd1GNHVOl9r1aiC
++RZRKXx4orSuKOGrBCi2nr2vjq3Yb5fQZPBf6veIJJR7nelrb9pXat07sS3czQWB7w2Fbqdt
+bL619btOnV/fP7vu65M9025n+sOyMXBWww8vY3Gijqrjqcq1vYfXhqrwD8saMLTc3L8OBsGG
+LWmBPn/aLF8DczK8fZvYnjbsrarVK8EQt1jiB4pwU3F0vdnZrNf3hNNuJo8j4VGU9PFVS28/
+v7xQ9W1o8vNPs0az6w6fXya3grno6A/D76P11RMd91qPwi3VHwK3aYyE1qbfFu+urzPtFl/H
+4AYHCnGDsBmgzGwWZEZrzj0uGjSqYS7y7iNHDXazJQpUQqF9zoB5f1v8zw/I95j/PwrpKYqi
+oQWDClbkvEFwora7gZdg5cTBF23eX1p2podT7xjA04cUK1PRh+EbNVBkcvFvjqO9k5ySJSFD
+wU40Y+rsZOfmrs65F4XfLFnuPeEXrgqZm0J+xrtcj50Qx3PNJeHneEh4OYDqj7riY3b2yXI0
+x8aj7DtUjGbUMR/hgJJZByetw1UEhfknO6Y9OZoTOfaRMsPXzNFYkvqzL9wPFiVsSfMPAAA=
+])
+
+UNGZB64(changesetify.db.dump.gz.b64, changesetify.db.dump)
+AT_CHECK(MONOTONE -d cs-modern.db db load < changesetify.db.dump, [], [ignore], [ignore])
+AT_CHECK(MONOTONE -d cs-modern.db db migrate, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE -d cs-modern.db ls keys, [1], [ignore], [ignore])
+AT_CHECK(MONOTONE -d cs-modern.db serve --bind=127.0.0.1:63219 '*', [1], [ignore], [ignore])
+
+AT_DATA([rosterify.db.dump.gz.b64], [H4sICFVTz0MCA3Jvc3RlcmlmeS5kYi5kdW1wAO1XW4+i2BZ+Ln4F6ZeujjUtcmcmnQwqKoqo
+4P3kpLKBza3kIqCAv342WmVVq1Wna84k5+WYaFD3un3rW2uv1ZS6sopLy5Yy0+W59AfW0iRx
+KuFTsalIuJGA0HQfYRyZbord3WN3LkhdPIwyPNxtNvgu9LY7+IC/vH77DT8eiGw8gQHwQi90
+cNuDGyvFUxiDBGTQwo0S//L7F+zupP09bUiXH3lhiude5iJ1ey/1ovDRhEmWft+DzQ5id0fH
+XhVcvJAGZMGKAtyFxW8wNCMLWfcs7O7bRZyW8bgHSYq9SN6fn5A0CuNs4uGsOTWjGFaBgjje
+eCbIkHPHrzjSdBYPQQAvhY/i6BD+BMvzwWNAVyefD56ifTl6gun+5NnD0cS385+XkdneBj5a
+cJOBKn932J1n3QomSyKUqZfcVUK4GYUZDDMkZoD0wrWfc1MdT78jxVHy1t73Cunj40/CVVYs
+z7bxLKpsINM7M8PNXZIga7idoHRVBrG75zg96+H4w7frtB0NH2mJjMeJF4CkrFB9+JWoLPDG
+sRcgzCiIE5imEBl9IcyLyCm7lZajJ7KqS9oUl9Xp6OQIPheVmaTff7UIQrBMmzQIElKWZUNA
+GITNA8ogKZYWLNYmLZ4x7K8PX3t0KovPr3q5H+utQkPvwRz4Q6Uxl0uzPtQbtqy3GoMMmi1R
+NpY0WK2r89jXSzwCEHo2TLNTkXxYr5+r1GvS3KrRF/MvXDi784YPtwviqCmKAXKuylrli+lG
+KQwrD3YpTLC72wXyVtDYRAZ2h9IfAy+5VUpvXI13BiraR3T45FbqOSHIdsmlBSSl6WJd74kN
+/PUMAu3Lv6pI/vSs35Fj//5yZmv16wNecfbo8AP+7M/Dq/gNIl8g9ULpm9FesBqgtGYuRGxF
+uCESomYFzvpu1e4VFhB9wNdsfZC862L+Z+v5TKB3avrvAHBd5r9a6a86Lqr97Oa54lmGogyh
+gYqaYzjGBGSD51lU9dAELAsMkmUNyyAI/rLiibQYKs2hPqMPui/nuj8f6bONMp1q8vDJO+ht
+bTT0ZXLIdYbDWXrQEymfzuLpTPIHmG4eHNFp7mY7t1DySt2PH9ctIYRF9phEaQaTxxAF+Rju
+AgPV0z1WfXsL8NuCCjcl+jhhWlaIZq6X4hkwNojRcI/kL+28Kah/tu9cU+CkpfLYs5CDHpJO
+rtoF+vu9zKOCfnYXr+wF6VWC3wTz2tQhTxsouRYDaY5kaEqgoAFQNzchetOWIfDQokkLpTj0
+0z/jyIiK74hk6PtQ7lpDkei29G1Xlw2qPZGa4mQminRXEdutppsPms6kxdD7uZ8k21U0tcdT
+ZxGw/Xin1J5Gkmaq5Eg3sWa62LKmQS8CCmUoyOfbfr/Mq5/s0pmt8mBbepoyLrQotZXujiR5
+0ZUOYEj3DbHXiB2Ha+X7/dzC7J62VMWlNHRGvOA0/acnaBaKR8/qU5ZrJ4r/pI1Fb9NyFspa
+3fMrOOoKS3EMnUWZcK4qCrnclCY/flzR7TymockOVUhSHsmAYK56wVVrvTHfnRqN6XqbW3PK
+uwLPveVk6AE/yn+7yuuVd+fsoizRNEkTLEHbAuBggyZNsgFI20a1S3G2wFA0qmkGsvT7Mf9v
+bt2fgfj//Qo/yPsxQ+ekG4JhC9CieIohITRtBgKTtDiW4VEDt1iG5G3epjnzM+R4+HraatCD
+1Z0frG6/XC0YHxyb83VrkAt73Ql6B9NhddtqtKgkow3A2PpYWK2TpdshA9dauPkT16ahPLbG
+hlqnhca8yXO7JICiRxDBfLxgsB7JEtqCtFe1wZoI+u6cag5BnuZa3QC+P17U0p4w0tlDa9R2
+eHfeMEfzp522oAaqZaR52yy38aKZHhKM9nszYkOa5bA2VOzlRM8WUs+a9Maac7pefgFYHuGD
+/OEom4KcbQkES9M8QfEC5HmKNxnSBpYNSP5zwKJ+Dqtu6ou56hH5UCeK0XySj9pRqR6icvQO
+xIvayp0GRad0s3SR9caTpVTbTNKYpUPXVZJVLDadJVs6Ctcm2QY3FoeukGdLfbCy2mOLZkRT
+dbXNvsAEpb1N2ju3JNV6OF1Q8kSk1sqgw8Zj9lDrcPvBHvY5tk+NHAGwoOj3UmK+aUpMH93a
+glSUB2G19TIJO+wEUlpMgj659opaS6vZuejrG08nxV+HGDIWATiBsxkDsCQNCM4ybIIhCIsS
+GJskANo9OFr4HMRgl7lRUpE4iA+TXnO/CgRaCdS9MbkNrjpvgdWuH/W3UkSvDwHjDOuLJi+0
+nJJqD0DOWaOwxhO7QU1eT4raYL+GCGhitI8JtdxFgj9hh72xmmOZERN5Rxab4JCuZoDqFLXa
+gRN5KpM60xk7DRJT0lYG2+ZHnKek/tLvpo0IpGGet9KVmNb8SJ5FsxG21+ptdzsxZ5OWMo1F
+qdw1evlm4409+pfBNQ1ANQjKJAmeEHgAOMpiGxQAps1ztmk2YIMyCUTsz4FruiB04CZy0PM6
+EPa38Bz3onWyNhxxHgqzhdIvOEBqlDA4FKYrqLCxKDRD6ZJJw4i4lau2IpEr5nJHVJd2s1tw
+szxf91Y1FWLqeFlraKlmCTEpNyyzEXDrmI6Ntit4shK4u6VtJHUmqDMyT/ebqrdRdYca7+Q9
+mAl1wqIFg1/PHBWL143BfjXqmc3IWpfjKKOFZf0p4lk1vzFungE9jZxozES/PN4a4z64xJ7H
+1TfX3wdry+nwy8r5LPq6s7zn4McrRnUf3WdodD7d1CeRb//NMvGi5N276ZV9n6DUz8vEYjSb
+bNvDptipU6NBoY2luBP6i5n2pDL7jtPfequtYC9KX+g1fbhvkfU4XpDR/onGVmbbGdtW1lbL
+juJppbEq5E049LTuoFcOlUWRdVe71CMYNjXjBZ37nXqtLRAtb1aAjZOrymFCFf4hxqbj0Xgy
+hqzt5+KolQ5XAbHXtYax4UiZSbKtuu6HTL2ntdjttMaAEeMQXbHJ7FPnvQ3mp5QiQn0wFV1s
+htVW+MzDm2vw36TUzS34E4vw1R58O+LnWN/bf96P9SZJf42nb7S8OiWrbWl5PTs/Pp6m9JF6
+Y66+P03g7+k4ttvHqsLfSp+aMALmP8lVQ+DjaWC9IX8aEY9/V4pGw6E8/QP7C+EOIsPpFgAA
+])
+
+UNGZB64(rosterify.db.dump.gz.b64, rosterify.db.dump)
+AT_CHECK(MONOTONE -d ro-modern.db db load < rosterify.db.dump, [], [ignore], [ignore])
+AT_CHECK(MONOTONE -d ro-modern.db db migrate, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE -d ro-modern.db ls keys, [1], [ignore], [ignore])
+AT_CHECK(MONOTONE -d ro-modern.db serve --bind=127.0.0.1:63219 '*', [1], [ignore], [ignore])
+
+AT_CLEANUP
============================================================
--- tests/t_no_persist_phrase.at	4c25100bf7d7c11892124eaaebeb2b32d671e134
+++ tests/t_no_persist_phrase.at	4c25100bf7d7c11892124eaaebeb2b32d671e134
@@ -0,0 +1,39 @@
+#  -*- Autoconf -*-
+
+AT_SETUP([disallowing persistence of passphrase])
+
+MONOTONE_SETUP
+
+AT_DATA(persist.lua, [
+function persist_phrase_ok()
+	return false
+end
+
+get_passphrase = nil
+])
+
+AT_DATA(input.txt, [version 0 of the file
+])
+
+AT_CHECK(MONOTONE add input.txt, [], [ignore], [ignore])
+
+AT_DATA(input.txt, [version 1 of the file
+])
+
+AT_DATA(passphrase, [tester@test.net
+tester@test.net
+tester@test.net
+tester@test.net
+])
+
+AT_CHECK(MONOTONE --branch=testbranch --rcfile=persist.lua commit --message=blah-blah <passphrase, [], [ignore], [ignore])
+
+TSHA=`BASE_REVISION`
+AT_CHECK(MONOTONE ls certs $TSHA, [], [stdout])
+AT_CHECK(mv stdout certs, [], [ignore])
+AT_CHECK(grep branch certs, [], [ignore])
+AT_CHECK(grep author certs, [], [ignore])
+AT_CHECK(grep date certs, [], [ignore])
+AT_CHECK(grep changelog certs, [], [ignore])
+
+AT_CLEANUP
============================================================
--- tests/t_rename_destdir.at	8e16b441f52d44260f34fa9edd99ca92c3b5863a
+++ tests/t_rename_destdir.at	8e16b441f52d44260f34fa9edd99ca92c3b5863a
@@ -0,0 +1,94 @@
+#  -*- Autoconf -*-
+
+AT_SETUP([rename files into a directory])
+
+MONOTONE_SETUP
+
+ADD_FILE(gnat, [gnat
+])
+ADD_FILE(mosquito, [mosquito
+])
+ADD_FILE(termite, [termite
+])
+ADD_FILE(ant, [ant
+])
+
+# will force foo/, bar/ and foo/gnat/ to be created
+AT_CHECK(mkdir foo bar foo/gnat)
+ADD_FILE(foo/dummy, [... ... ...
+])
+ADD_FILE(bar/dummy, [a b c
+])
+ADD_FILE(foo/gnat/dummmy, [la la la
+])
+
+COMMIT(testbranch)
+
+# checkout in a clean dir and cd there
+m4_define([REN_CHECKOUT], [
+AT_CHECK(rm -fr $_ROOT_DIR/checkout, [], [ignore], [ignore])
+AT_CHECK(MONOTONE checkout -b testbranch $_ROOT_DIR/checkout, [0], [ignore], [ignore])
+])
+
+REN_CHECKOUT
+
+# basics
+AT_CHECK(cd checkout && MONOTONE rename ant foo, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename mosquito termite foo, [0], [ignore], [ignore])
+
+REN_CHECKOUT
+
+# with --execute
+AT_CHECK(cd checkout && MONOTONE --execute rename ant foo, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE --execute rename mosquito termite foo, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && test -e foo/ant -a -e foo/mosquito -a -e foo/termite)
+AT_CHECK(cd checkout && test ! -e ant -a ! -e mosquito -a ! -e termite)
+
+# to root
+AT_CHECK(cd checkout && MONOTONE --execute rename foo/ant ., [0], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename foo/termite ., [0], [ignore], [ignore])
+AT_CHECK(cd checkout && test -e ant -a -e foo/termite -a ! -e foo/ant -a ! -e termite)
+
+REN_CHECKOUT
+
+# conflicts
+AT_CHECK(cd checkout && MONOTONE rename gnat foo, [1], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename gnat termite foo, [1], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename termite foo, [0], [ignore], [ignore])
+
+AT_CHECK(cd checkout && MONOTONE rename mosquito foo/ant, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE rename ant foo, [1], [ignore], [ignore])
+
+REN_CHECKOUT
+
+AT_CHECK(cd checkout && MONOTONE --execute rename gnat foo, [1], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE --execute rename gnat termite foo, [1], [ignore], [ignore])
+AT_CHECK(cd checkout && MONOTONE --execute rename termite foo, [0], [ignore], [ignore])
+AT_CHECK(cd checkout && test -e foo/termite -a ! -e termite -a -e gnat -a -d foo/gnat -a ! -e foo/gnat/gnat)
+
+REN_CHECKOUT
+
+## todo:
+# issues with missing files, should usually be allowed?
+# rename to self
+# rename root node
+
+# rename to non-existing dir path: "foo->blweorih/o4thoihs" (this isn't a destdir case, but needs testing somewhere).
+
+# file0->bar, file0 doesn't exist
+
+# file0->bar, file0 exists, -e
+
+# file0->bar, file0 doesn't exist, -e
+
+# check that nothing happens if any would fail
+# file0->bar file1->bar, file0 exists, file1 doesn't
+
+# file0->bar file1->bar, file0 exists, file1 doesn't, -e
+
+# file0->bar, bar/file0 exists in working, file0 doesn't -e
+
+# file0->bar
+
+
+AT_CLEANUP
============================================================
--- AUTHORS	789d404e3fdc93f737e1a6338d92c10443adb1ee
+++ AUTHORS	be2883d201fdd61b63582334f52312a640e012a1
@@ -71,8 +71,8 @@ contributing authors, including:
   Marcel van der Boom <marcel@hsdev.com>
   Roland McGrath <roland@redhat.com>
   Daniel Carosone <dan@geek.com.au>
+  Vinzenz Feenstra <evilissimo@c-plusplus.de>

-
 Several people have also contributed to the translation of monotone
 into non-English languages; their work is available in the po/
 subdirectory.  Contributors include:
@@ -80,6 +80,8 @@ subdirectory.  Contributors include:
   Benoît Dejean <benoit@placenet.org>
   Satoru SATOH <ss@gnome.gr.jp>
   Alex Queiroz <asandroq@gmail.com>
+  Richard Levitte <richard@levitte.org>
+  Joel Rosdahl <joel@rosdahl.net>

 supporting files:
 -----------------
============================================================
--- ChangeLog	f92ab5a6ad615b29c1bab7d73dada3301543b899
+++ ChangeLog	119c14c3c86f103f563ce65dfaab3797e2f44f24
@@ -1,3 +1,312 @@
+2006-01-29  Richard Levitte  <richard@levitte.org>
+
+	* po/sv.po: Update a few translations, leave a number of fuzzy
+	ones until we've decided how "workspace" should be translated.
+
+2006-01-27  Matthew Gregan  <kinetik@orcon.net.nz>
+
+	* *: Use the term 'workspace' consistently throughout monotone for
+	the concept we previously described interchangably using the two
+	terms 'working copy' and 'working directory'.  This change has
+	been made everywhere except in historical documentation (NEWS and
+	ChangeLog).
+
+2006-01-27  Richard Levitte  <richard@levitte.org>
+
+	* monotone.texi (Generating Keys): Correct small type, the keys
+	are not stored in /home/jim/.monotone/monotonerc.
+
+2006-01-26  Derek Scherger  <derek@echologic.com>
+
+	* app_state.{cc,hh}:
+	* commands.cc (log):
+	* monotone.cc:
+	* options.hh: allow --next to view upcoming changes
+	* tests/t_log_depth.at: rename to ...
+	* tests/t_log_last_next.at: ... this since log now uses
+	--last/--next and not --depth
+	* testsuite.at: rename t_log_depth.at to t_log_last_next.at
+
+2006-01-27  Matthew Gregan  <kinetik@orcon.net.nz>
+
+	* monotone.texi: Clean up 'serve' syntax in a couple of places.
+
+2006-01-26  Nathaniel Smith  <njs@pobox.com>
+
+	* monotone.texi (Generating Keys, Network Service Revisited):
+	Clean up some key-related stuff -- genkey doesn't need a db, so
+	don't confuse the reader by giving it one, and update the sample
+	output too.
+
+2006-01-27  Stéphane Gimenez <dev@gim.name>
+
+	* sanity.hh: fix scoping issue revealed by gcc-4.1.
+
+2006-01-26  Richard Levitte  <richard@levitte.org>
+
+	* po/sv.po: One more translation.
+
+2006-01-25  Matthew Gregan  <kinetik@orcon.net.nz>
+
+	* netsync.cc (session::rebuild_merkle_trees): Ticker header
+	consistency tweak.
+
+2005-01-25  Matt Johnston  <matt@ucc.asn.au>
+
+	* HACKING: add some notes about compiling - precompiled headers,
+	-O0, and ccache.
+	* INSTALL: mention --enable-pch
+
+2006-01-25  Richard Levitte  <richard@levitte.org>
+
+	* po/sv.po: Fix all fuzzy translations (some needed no fix).
+	Translate the last untranslated ones.
+
+2005-01-24  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	Make a netsync client crash not hang the testsuite.
+	* tests/t_netsync_single.at: use netsync macros
+	* testsuite.at: Make NETSYNC_SETUP set a trap on exit to kill
+	any servers.
+
+2005-01-24  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	* commands.cc (update): Remove fixme comment.
+
+2005-01-24  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	* commands.cc (update): Allow backwards/sideways updates.
+	tests/t_update_to_revision.at: remove XFAIL
+
+2006-01-23  Nathaniel Smith  <njs@pobox.com>
+
+	* database.{cc,hh}: Clean up code formatting a bit, rename some
+	variables, rely more on automatic conversion.
+
+2006-01-24  Vinzenz Feenstra <evilissimo@c-plusplus.de>
+
+	* query_args.hh: Introduced struct query_args and struct
+	query_args_param. Used to typesafe arguments for database::execute
+	and database::fetch
+	* database.cc/.hh: Adjusted database to the new fetch and execute
+	argument style via operator% which is more typesafe.
+
+2006-01-23  Nathaniel Smith  <njs@pobox.com>
+
+	* database.cc (assert_sqlite3_ok): Improve the hint message given
+	for SQLITE_ERROR's.
+
+2006-01-23  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	* tests/t_database_check_normalized.at: update included database to
+	use rosters
+
+2006-01-23  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	New ChangeLog utilities in contrib/ .
+	* contrib/ChangeLog.sh: Script that takes the date, author, and
+	changelog certs from the last n revisions and prints them in standard
+	ChangeLog format.
+	* contrib/edit_comment_from_changelog.lua: an edit_comment hook that
+	takes the initial commit message from the most recent ChangeLog entry
+
+2006-01-23  Henry Nestler  <henry@bigfoot.de>
+
+	* monotone.texi, monotone.1: Mode none for --ticker.
+
+2006-01-23  Nathaniel Smith  <njs@pobox.com>
+
+	* commands.cc (update): Hopefully improve wording of help a bit.
+
+2006-01-21  Nathaniel Smith  <njs@pobox.com>
+
+	* netcmd.cc (read): Expand the error message we issue on protocol
+	version mismatch to be a bit more informative.
+
+2006-01-21  Matthew Gregan  <kinetik@orcon.net.nz>
+
+	* configure.ac: Fix up Windows and IPv6 tests after the change
+	from AC_TRY_RUN to AC_TRY_COMPILE.  Also a couple of other minor
+	cleanups.
+	* Makefile.am: Remove win32/wcwidth.c from WIN32_PLATFORM_SOURCES.
+
+2006-01-21  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	* tests/t_db_execute.at: Play with the files table instead of the
+	revisions table, so we don't trigger the has-revisions-but-no-rosters
+	check.
+
+2006-01-21  Richard Levitte  <richard@levitte.org>
+
+	* po/sv.po: Sort the translation guidelines a little more.
+
+2006-01-21  Joel Rosdahl  <joel@rosdahl.net>
+
+	* po/sv.po: Corrections in translation guidelines.
+
+2006-01-21  Joel Rosdahl  <joel@rosdahl.net>
+
+	* AUTHORS: Added myself as a translation contributor.
+	* po/sv.po: Second round of review of Swedish translation.
+
+2006-01-20  Richard Levitte  <richard@levitte.org>
+
+	* po/sv.po: Added a \n at the end of a msgstr that was missing
+	it.
+
+2006-01-20  Joel Rosdahl  <joel@rosdahl.net>
+
+	* po/sv.po: Review and suggested corrections of Swedish
+	translation.
+
+2006-01-20  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	* netsync.cc: Make our sockets non-blocking.
+
+2006-01-20  Matt Johnston  <matt@ucc.asn.au>
+
+	* commands.cc CMD(setup): default to current dir
+	* monotone.texi: update
+	* t_setup_existing_path.at: add test
+
+2006-01-19  Emile Snyder  <emile@alumni.reed.edu>
+
+	Add a --brief mode to the annotate command which prints more
+	informative annotations rather than just the raw revision ids.
+
+	* commands.cc (CMD(annotate)): Add --brief option to the annotate
+	command and remove obsolete comment lines.
+	* annotate.cc (dump): Pass in app_state to allow access to db for
+	cert lookups on revisions.  Honor new --brief
+	flag by printing <short id>.. by <author> <date>: as the
+	annotation rather than the raw revision id, and only printing the
+	annotation for the first line of each block of lines from the same
+	revision.
+	(cert_string_value): Given a set of certs from a revision, a cert
+	name, and some detail of what part of the value we want, find and
+	return that part of the cert.
+	(build_revisions_to_annotations): After we finish with the
+	annotations pass, build up a mapping of revision id to annotation
+	string.
+	* monotone.texi: Add --brief flag and description to the annotate
+	section of the command reference.
+
+2006-01-19  Matt Johnston  <matt@ucc.asn.au>
+
+	* work.{cc,hh}, commands.cc: add "rename src1 [src2 ...] dst/"
+	syntax.
+	* monotone.texi: update
+	* testsuite.at, tests/t_rename_destdir: new test (is incomplete).
+	* tests/t_no_rename_overwrite.at: syntax should now succeed.
+	* vocab.cc: add hexenc<id> dump() instantiation.
+
+2006-01-19  Matt Johnston  <matt@ucc.asn.au>
+
+	* HACKING: escape the colon in the cino vim modeline option.
+
+2006-01-19  Nathaniel Smith  <njs@pobox.com>
+
+	* database.cc (check_format): Small cleanups to previous change.
+
+2006-01-19  Nathaniel Smith  <njs@pobox.com>
+
+	Fix bug reported by Henry Nestler, where 'serve' did not detect
+	that the user had not run 'db rosterify'.
+	* database.cc (check_rosterified): Rename to...
+	(check_format): ...this.  Detect unchangesetified dbs too.
+	(sql, ensure_open_for_format_changes, get_roster_id_for_revision):
+	* revision.cc (build_roster_style_revs_from_manifest_style_revs)
+	(build_changesets_from_manifest_ancestry): Adjust accordingly.
+	* tests/t_check_db_format.at, testsuite.at: New test.
+
+2005-01-18  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	* tests/t_lua_privkey.at: Remove; this tests a hook that was removed.
+	* testsuite: adjust accordingly
+
+2006-01-19  Matthew Gregan  <kinetik@orcon.net.nz>
+
+	* keys.cc (get_passphrase): Update std::map correctly--remove
+	existing cached passphrase for a given keyid, then store the new
+	one using safe_insert().
+	(make_signature): Fix crash when running monotone with persistent
+	passphrases disallowed--remove shared_ptr that was going out of
+	scope too early, and shadowed and existing shared_ptr in the
+	correct scope.
+	* tests/t_no_persist_phrase.at: New test to check that basic no
+	persistent passphrase functionality works.
+	* testsuite.at: Include new test.
+
+2006-01-18  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	* merkle_tree.{cc,hh}: Add a locate_item() function to find the
+	node and slot in a merkle_table that a given item is in.
+	* refiner.cc (refiner::process_refinement_command): If they have
+	a leaf and we have a subtree then if their leaf is in our subtree,
+	we need to tell them.
+
+2006-01-18  Nathaniel Smith  <njs@pobox.com>
+
+	* configure.ac: Remove some dead comments.
+
+2006-01-18  Nathaniel Smith  <njs@pobox.com>
+
+	* configure.ac: s/TRY_RUN/TRY_COMPILE/ a few places, for general
+	cleanliness.
+
+2006-01-18  Richard Levitte  <richard@levitte.org>
+
+	* po/sv.po: Better translation of "branch".  Inspired from
+	subversion.
+
+2006-01-18  Nathaniel Smith  <njs@pobox.com>
+
+	* database.cc (begin_transaction): Turn an E() into an I().
+
+2006-01-15  Richard Levitte  <richard@levitte.org>
+
+	* po/sv.po: Almost all translations done.  I've left a few for
+	lack of inspiration.  Someone, PLEASE REVIEW!
+
+2006-01-15  Nathaniel Smith  <njs@pobox.com>
+
+	* AUTHORS: Add Richard as a translator.
+
+2006-01-15  Nathaniel Smith  <njs@pobox.com>
+
+	* roster.cc (union_corpses): New function.
+	(unify_roster_oneway, unify_rosters): Remove unused new_ids
+	argument.  Add call to union_corpses.  Add big comment explaining
+	what's going on.
+	(test_unify_rosters_end_to_end_ids)
+	(test_unify_rosters_end_to_end_attr_corpses): Split and improve
+	tests.
+
+2006-01-15  Richard Levitte  <richard@levitte.org>
+
+	* po/sv.po: More translations done.
+
+2006-01-15  Richard Levitte  <richard@levitte.org>
+
+	* LINGUAS: Change sv_SE to just sv.
+	* po/sv_SE.po: Rename ...
+	* po/sv.po: ... to this, as there aren't so many dialects.  Update
+	with more strings to translate.
+
+	* std_hooks.lua: xgettext whines about a missing quote, so add
+	one.
+
+2006-01-15  Nathaniel Smith  <njs@pobox.com>
+
+	* roster.cc (test_unify_rosters_end_to_end): Add failing test for
+	unify_roster's handling of attr corpses.
+
+2006-01-15  Nathaniel Smith  <njs@pobox.com>
+
+	* cert.cc (load_key_pair):
+	* keys.cc (require_password): Use accessor method, so as to
+	actually compile...
+
 2006-01-15  Matt Johnston  <matt@ucc.asn.au>

 	* ui.cc: make tickers saner (203 K vs 0.2 M).
============================================================
--- HACKING	2d70798fc515faf275cf6e817cfef4f76e1f4b39
+++ HACKING	dd8654f0de0f726639e8fc988b30e6ac272c173d
@@ -18,7 +18,7 @@ And something close (but not perfect) fo

 And something close (but not perfect) for VIM:

-vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
+vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

 monotone's source includes a number of third party libraries.  These have
 been imported from upstream, and should retain the original coding style of
@@ -208,11 +208,11 @@ will unwind and cause the destructors fo
 thrown that will ultimately cause the monotone process to exit.  Exceptions
 are used (rather than C-style abort() based assertions) so that the stack
 will unwind and cause the destructors for objects allocated on the stack to
-run.  This allows monotone to leave the user's database and working copy in
+run.  This allows monotone to leave the user's database and workspace in
 as logical a state as possible.  Any in-flight uncommitted database
-transactions will be aborted.  Operations occurring on a working copy may
-leave the working copy in an inconsistent state, as we do not have a way to
-atomically update the working copy.
+transactions will be aborted.  Operations occurring on a workspace may
+leave the workspace in an inconsistent state, as we do not have a way to
+atomically update the workspace.


 Patch submission guidelines
@@ -259,3 +259,20 @@ permanent deviation is documented.
 third-party code may not make sense to send upstream.  In this case, make a
 note of this in the file you're changing and in the ChangeLog so that this
 permanent deviation is documented.
+
+
+Compiling Hints
+---------------
+
+  - monotone's compilation time can be improved significantly by compiling
+  with 'CXXFLAGS=-O0'. Note that disabling optimisation makes the resultant
+  binary significantly slower - don't bother using it for performance
+  profiling.
+
+  - precompiled headers can be enabled by running 'configure' with --enable-pch
+  This should give shorter compile times, given boost's extensive use of
+  templates.  Some versions of gcc have issues with precompiled headers, so if
+  you get strange compilation errors, try disabling them.
+
+  - ccache (http://ccache.samba.org/) is quite effective for speeding up
+  repeated compiles of the same source.
============================================================
--- INSTALL	54295f34283c38aee1a57864548eceeeae69f4c3
+++ INSTALL	e8c5a114eb284a7163393b4721179147dbc165ac
@@ -136,6 +136,12 @@ 2. configuring monotone:
        note that, sometimes, the configure script will be able to guess
        the correct suffix by itself.

+     --enable-pch
+
+       this will enable precompiled headers, which should improve compile
+       time. some versions of gcc have problems with this option, so
+       try disabling it if you run into trouble.
+
 3. building monotone

   * type "make". this should produce a monotone binary in your current
============================================================
--- Makefile.am	73bbbf9fadf07f9f85d194f011de6671b8dbbbda
+++ Makefile.am	1086b6f0ea35d96bdd46455c7d97c5934fb77e5a
@@ -216,7 +216,7 @@ WIN32_PLATFORM_SOURCES = \

 WIN32_PLATFORM_SOURCES = \
 	win32/read_password.cc win32/get_system_flavour.cc win32/process.cc win32/terminal.cc \
-	win32/inodeprint.cc win32/fs.cc win32/wcwidth.c
+	win32/inodeprint.cc win32/fs.cc

 # primaries

============================================================
--- ROADMAP	53568f4fbd5d01ef09b991793d62f0fd2a0db58f
+++ ROADMAP	745d2ad91a86dc235175a570896ae219fb9853ac
@@ -32,7 +32,7 @@ ROADMAP
 - modify database code to use sqlite3 pre-parsed queries, blobs
 - change netsync to globbing branches, not using collections
 - implement improved ACL/permission system for default trust rules
-- implement "merge into working copy" approach to merging
+- implement "merge into workspace" approach to merging
 - emacs integration

   ( probably call it "1.0" or "stable" around here )
============================================================
--- annotate.cc	abc86290badfdb97601384d7e8ed9cc5fc012adf
+++ annotate.cc	da2b9e34836d772f45c7214d268ebeca7044c0c7
@@ -21,6 +21,7 @@
 #include "sanity.hh"
 #include "transforms.hh"
 #include "vocab.hh"
+#include "cert.hh"



@@ -46,11 +47,13 @@ public:
   /// return true if we have no more unassigned lines
   bool is_complete() const;

-  void dump() const;
+  void dump(app_state & app) const;

   std::string get_line(int line_index) const { return file_lines[line_index]; }

 private:
+  void build_revisions_to_annotations(app_state & app, std::map<revision_id, std::string> & revs_to_notations) const;
+
   std::vector<std::string> file_lines;
   std::vector<revision_id> annotations;

@@ -290,22 +293,112 @@ annotate_context::is_complete() const
 }


+std::string cert_string_value (std::vector< revision<cert> > const & certs,
+                               const std::string & name,
+                               bool from_start, bool from_end,
+                               const std::string & sep)
+{
+  for (std::vector < revision < cert > >::const_iterator i = certs.begin ();
+       i != certs.end (); ++i)
+    {
+      if (i->inner ().name () == name)
+        {
+          cert_value tv;
+          decode_base64 (i->inner ().value, tv);
+          std::string::size_type f = 0;
+          std::string::size_type l = std::string::npos;
+          if (from_start)
+            l = tv ().find_first_of (sep);
+          if (from_end)
+            {
+              f = tv ().find_last_of (sep);
+              if (f == std::string::npos)
+                f = 0;
+            }
+          return tv ().substr (f, l);
+        }
+    }
+
+  return "";
+}
+
+
 void
-annotate_context::dump() const
+annotate_context::build_revisions_to_annotations(app_state &app,
+                                                 std::map<revision_id, std::string> &revs_to_notations) const
 {
+  I(annotations.size() == file_lines.size());
+
+  // build set of unique revisions present in annotations
+  std::set<revision_id> seen;
+  for (std::vector<revision_id>::const_iterator i = annotations.begin(); i != annotations.end(); i++)
+    {
+      seen.insert(*i);
+    }
+
+  size_t max_note_length = 0;
+
+  // build revision -> annotation string mapping
+  for (std::set<revision_id>::const_iterator i = seen.begin(); i != seen.end(); i++)
+    {
+      std::vector< revision<cert> > certs;
+      app.db.get_revision_certs(*i, certs);
+      erase_bogus_certs(certs, app);
+
+      std::string author(cert_string_value(certs, author_cert_name, true, false, "@< "));
+      std::string date(cert_string_value(certs, date_cert_name, true, false, "T"));
+
+      std::string result;
+      result.append((*i).inner ()().substr(0, 8));
+      result.append(".. by ");
+      result.append(author);
+      result.append(" ");
+      result.append(date);
+      result.append(": ");
+
+      max_note_length = (result.size() > max_note_length) ? result.size() : max_note_length;
+      revs_to_notations[*i] = result;
+    }
+
+  // justify annotation strings
+  for (std::map<revision_id, std::string>::iterator i = revs_to_notations.begin(); i != revs_to_notations.end(); i++)
+    {
+      size_t l = i->second.size();
+      i->second.insert(std::string::size_type(0), max_note_length - l, ' ');
+    }
+}
+
+
+void
+annotate_context::dump(app_state &app) const
+{
   revision_id nullid;
   I(annotations.size() == file_lines.size());

-  revision_id lastid = nullid;
-  for (size_t i=0; i<file_lines.size(); i++) {
-    //I(! (annotations[i] == nullid) );
-    if (false) //(lastid == annotations[i])
-      std::cout << "                                        : " << file_lines[i] << std::endl;
-    else
-      std::cout << annotations[i] << ": " << file_lines[i] << std::endl;
+  std::map<revision_id, std::string> revs_to_notations;
+  std::string empty_note;
+  if (global_sanity.brief)
+    {
+      build_revisions_to_annotations(app, revs_to_notations);
+      size_t max_note_length = revs_to_notations.begin()->second.size();
+      empty_note.insert(std::string::size_type(0), max_note_length - 2, ' ');
+    }

-    lastid = annotations[i];
-  }
+  revision_id lastid = nullid;
+  for (size_t i=0; i<file_lines.size(); i++)
+    {
+      //I(! (annotations[i] == nullid) );
+      if (global_sanity.brief)
+        {
+          if (lastid == annotations[i])
+            std::cout << empty_note << ": " << file_lines[i] << std::endl;
+          else
+            std::cout << revs_to_notations[annotations[i]] << file_lines[i] << std::endl;
+          lastid = annotations[i];
+        }
+      else
+        std::cout << annotations[i] << ": " << file_lines[i] << std::endl;
+    }
 }


@@ -705,5 +798,5 @@ do_annotate (app_state &app, file_t file
   acp->annotate_equivalent_lines();
   I(acp->is_complete());

-  acp->dump();
+  acp->dump(app);
 }
============================================================
--- app_state.cc	c01ca1020ed025d4a7153030d25fda87d03645f9
+++ app_state.cc	18045740e735562c70e3a482927003729014844f
@@ -33,7 +33,7 @@ app_state::app_state()
     rcfiles(true), diffs(false),
     merges(false), set_default(false), verbose(false), date_set(false),
     search_root("/"),
-    depth(-1), last(-1), diff_format(unified_diff), diff_args_provided(false),
+    depth(-1), last(-1), next(-1), diff_format(unified_diff), diff_args_provided(false),
     use_lca(false), execute(false), bind_address(""), bind_port(""),
     missing(false), unknown(false),
     confdir(get_default_confdir()), have_set_key_dir(false)
@@ -62,12 +62,12 @@ void
 }

 void
-app_state::allow_working_copy()
+app_state::allow_workspace()
 {
   L(FL("initializing from directory %s\n") % fs::initial_path().string());
-  found_working_copy = find_and_go_to_working_copy(search_root);
+  found_workspace = find_and_go_to_workspace(search_root);

-  if (found_working_copy)
+  if (found_workspace)
     {
       read_options();

@@ -95,7 +95,7 @@ app_state::allow_working_copy()
           L(FL("setting dump path to %s\n") % dump_path);
           // the 'true' means that, e.g., if we're running checkout, then it's
           // okay for dumps to go into our starting working dir's MT rather
-          // than the checked-out dir's MT.
+          // than the new workspace dir's MT.
           global_sanity.filename = system_path(dump_path, false);
         }
     }
@@ -103,29 +103,29 @@ void
 }

 void
-app_state::require_working_copy(std::string const & explanation)
+app_state::require_workspace(std::string const & explanation)
 {
-  N(found_working_copy,
-    F("working copy directory required but not found%s%s")
+  N(found_workspace,
+    F("workspace required but not found%s%s")
     % (explanation.empty() ? "" : "\n") % explanation);
   write_options();
 }

 void
-app_state::create_working_copy(system_path const & new_dir)
+app_state::create_workspace(system_path const & new_dir)
 {
   N(!new_dir.empty(), F("invalid directory ''"));

-  L(FL("creating working copy in %s\n") % new_dir);
+  L(FL("creating workspace in %s\n") % new_dir);

   mkdir_p(new_dir);
-  go_to_working_copy(new_dir);
+  go_to_workspace(new_dir);

   N(!directory_exists(bookkeeping_root),
     F("monotone bookkeeping directory '%s' already exists in '%s'\n")
     % bookkeeping_root % new_dir);

-  L(FL("creating bookkeeping directory '%s' for working copy in '%s'\n")
+  L(FL("creating bookkeeping directory '%s' for workspace in '%s'\n")
     % bookkeeping_root % new_dir);

   mkdir_p(bookkeeping_root);
@@ -329,11 +329,11 @@ app_state::make_branch_sticky()
 app_state::make_branch_sticky()
 {
   options[branch_option] = branch_name();
-  if (found_working_copy)
+  if (found_workspace)
     {
-      // already have a working copy, can (must) write options directly,
+      // already have a workspace, can (must) write options directly,
       // because no-one else will do so
-      // if we don't have a working copy yet, then require_working_copy (for
+      // if we don't have a workspace yet, then require_workspace (for
       // instance) will call write_options when it finds one.
       write_options();
     }
@@ -422,6 +422,14 @@ void
 }

 void
+app_state::set_next(long l)
+{
+  N(l > 0,
+    F("negative or zero next not allowed\n"));
+  next = l;
+}
+
+void
 app_state::set_pidfile(system_path const & p)
 {
   pidfile = p;
@@ -490,8 +498,8 @@ app_state::get_confdir()
   return confdir;
 }

-// rc files are loaded after we've changed to the working copy directory so
-// that MT/monotonerc can be loaded between ~/.monotone/monotonerc and other
+// rc files are loaded after we've changed to the workspace so that
+// MT/monotonerc can be loaded between ~/.monotone/monotonerc and other
 // rcfiles

 void
@@ -508,11 +516,11 @@ app_state::load_rcfiles()
   if (rcfiles)
     {
       system_path default_rcfile;
-      bookkeeping_path working_copy_rcfile;
+      bookkeeping_path workspace_rcfile;
       lua.default_rcfilename(default_rcfile);
-      lua.working_copy_rcfilename(working_copy_rcfile);
+      lua.workspace_rcfilename(workspace_rcfile);
       lua.load_rcfile(default_rcfile, false);
-      lua.load_rcfile(working_copy_rcfile, false);
+      lua.load_rcfile(workspace_rcfile, false);
     }

   // command-line rcfiles override even that
============================================================
--- app_state.hh	6f33817c2a63c63e5b893cebbc49f863195b2d82
+++ app_state.hh	7fa51b357c748547e5d71dbc84fb35077c148177
@@ -55,9 +55,10 @@ public:
   std::vector<utf8> extra_rcfiles;
   path_set restrictions;
   path_set excludes;
-  bool found_working_copy;
+  bool found_workspace;
   long depth;
   long last;
+  long next;
   system_path pidfile;
   diff_type diff_format;
   bool diff_args_provided;
@@ -87,9 +88,9 @@ public:
     std::pair<boost::shared_ptr<Botan::PK_Verifier>,
         boost::shared_ptr<Botan::RSA_PublicKey> > > verifiers;

-  void allow_working_copy();
-  void require_working_copy(std::string const & explanation = "");
-  void create_working_copy(system_path const & dir);
+  void allow_workspace();
+  void require_workspace(std::string const & explanation = "");
+  void create_workspace(system_path const & dir);

   void set_restriction(path_set const & valid_paths,
                        std::vector<utf8> const & paths);
@@ -97,11 +98,11 @@ public:
   bool restriction_includes(split_path const & path);

   // Set the branch name.  If you only invoke set_branch, the branch
-  // name is not sticky (and won't be written to the working copy and
+  // name is not sticky (and won't be written to the workspace and
   // reused by subsequent monotone invocations).  Commands which
   // switch the working to a different branch should invoke
-  // make_branch_sticky (before require_working_copy because this
-  // function updates the working copy).
+  // make_branch_sticky (before require_workspace because this
+  // function updates the workspace).
   void set_branch(utf8 const & name);
   void make_branch_sticky();

@@ -115,6 +116,7 @@ public:
   void set_author(utf8 const & author);
   void set_depth(long depth);
   void set_last(long last);
+  void set_next(long next);
   void set_pidfile(system_path const & pidfile);
   void add_revision(utf8 const & selector);
   void add_exclude(utf8 const & exclude_pattern);
============================================================
--- automate.cc	0b608759447d6c0c9f4aed5289f60a89decac403
+++ automate.cc	68d579fc833b4a0ec52a0c8e5b130af7517ab7ed
@@ -638,7 +638,7 @@ extract_added_file_paths(addition_map co
 // Name: inventory
 // Arguments: none
 // Added in: 1.0
-// Purpose: Prints a summary of every file found in the working copy or its
+// Purpose: Prints a summary of every file found in the workspace or its
 //   associated base manifest. Each unique path is listed on a line prefixed by
 //   three status characters and two numeric values used for identifying
 //   renames. The three status characters are as follows.
@@ -665,7 +665,7 @@ extract_added_file_paths(addition_map co
 //   includes the rest of the line. Directory paths are identified as ending with
 //   the "/" character, file paths do not end in this character.
 //
-// Error conditions: If no working copy book keeping MT directory is found,
+// Error conditions: If no workspace book keeping MT directory is found,
 //   prints an error message to stderr, and exits with status 1.

 static void
@@ -677,7 +677,7 @@ automate_inventory(std::vector<utf8> arg
   if (args.size() != 0)
     throw usage(help_name);

-  app.require_working_copy();
+  app.require_workspace();

   temp_node_id_source nis;
   roster_t base, curr;
@@ -890,7 +890,7 @@ automate_certs(std::vector<utf8> args,

 // Name: get_revision
 // Arguments:
-//   1: a revision id (optional, determined from working directory if non-existant)
+//   1: a revision id (optional, determined from the workspace if non-existant)
 // Added in: 1.0
 // Purpose: Prints changeset information for the specified revision id.
 //
@@ -942,7 +942,7 @@ automate_get_revision(std::vector<utf8>
       revision_set rev;
       roster_t old_roster, new_roster;

-      app.require_working_copy();
+      app.require_workspace();
       get_unrestricted_working_revision_and_rosters(app, rev,
                                                     old_roster,
                                                     new_roster);
@@ -963,7 +963,7 @@ automate_get_revision(std::vector<utf8>

 // Name: get_manifest_of
 // Arguments:
-//   1: a revision id (optional, determined from working directory if non-existant)
+//   1: a revision id (optional, determined from the workspace if non-existant)
 // Added in: 2.0
 // Purpose: Prints the contents of the manifest associated with the given revision ID.
 //
@@ -987,7 +987,7 @@ automate_get_manifest_of(std::vector<utf
   if (args.size() == 0)
     {
       revision_set rs;
-      app.require_working_copy();
+      app.require_workspace();
       get_unrestricted_working_revision_and_rosters(app, rs, old_roster, new_roster);
     }
   else
============================================================
--- cert.cc	c02a7b0abf62fa3d0485985df5317154041025cf
+++ cert.cc	289ab8fc2d5c2622d3f563bd57a37442efbb52b8
@@ -344,7 +344,8 @@ load_key_pair(app_state & app,
   else
     {
       N(app.keys.key_pair_exists(id),
-        F("no key pair '%s' found in key store '%s'") % id % app.keys.key_dir);
+        F("no key pair '%s' found in key store '%s'")
+        % id % app.keys.get_key_dir());
       app.keys.get_key_pair(id, kp);
       if (persist_ok)
         keys.insert(make_pair(id, kp));
============================================================
--- commands.cc	1c35354bb631e6dec76f36cfeebfe19be4e8116f
+++ commands.cc	19e176916ef418e11386e0b0b76078121a201421
@@ -1164,13 +1164,13 @@ static void find_unknown_and_ignored (ap
                                       path_set & unknown, path_set & ignored);


-CMD(add, N_("working copy"), N_("[PATH]..."),
-    N_("add files to working copy"), OPT_UNKNOWN)
+CMD(add, N_("workspace"), N_("[PATH]..."),
+    N_("add files to workspace"), OPT_UNKNOWN)
 {
   if (!app.unknown && (args.size() < 1))
     throw usage(name);

-  app.require_working_copy();
+  app.require_workspace();

   path_set paths;
   if (app.unknown)
@@ -1192,13 +1192,13 @@ static void find_missing (app_state & ap
 static void find_missing (app_state & app,
                           vector<utf8> const & args, path_set & missing);

-CMD(drop, N_("working copy"), N_("[PATH]..."),
-    N_("drop files from working copy"), OPT_EXECUTE % OPT_MISSING)
+CMD(drop, N_("workspace"), N_("[PATH]..."),
+    N_("drop files from workspace"), OPT_EXECUTE % OPT_MISSING)
 {
   if (!app.missing && (args.size() < 1))
     throw usage(name);

-  app.require_working_copy();
+  app.require_workspace();

   path_set paths;
   if (app.missing)
@@ -1217,19 +1217,26 @@ ALIAS(rm, drop);
 ALIAS(rm, drop);


-CMD(rename, N_("working copy"), N_("SRC DST"),
-    N_("rename entries in the working copy"),
+CMD(rename, N_("workspace"),
+    N_("SRC DEST\n"
+       "SRC1 [SRC2 [...]] DEST_DIR"),
+    N_("rename entries in the workspace"),
     OPT_EXECUTE)
 {
-  if (args.size() != 2)
+  if (args.size() < 2)
     throw usage(name);

-  app.require_working_copy();
+  app.require_workspace();

-  file_path src_path = file_path_external(idx(args, 0));
-  file_path dst_path = file_path_external(idx(args, 1));
+  file_path dst_path = file_path_external(args.back());

-  perform_rename(src_path, dst_path, app);
+  set<file_path> src_paths;
+  for (size_t i = 0; i < args.size()-1; i++)
+    {
+      file_path s = file_path_external(idx(args, i));
+      src_paths.insert(s);
+    }
+  perform_rename(src_paths, dst_path, app);
 }

 ALIAS(mv, rename)
@@ -1284,14 +1291,14 @@ CMD(fmerge, N_("debug"), N_("<parent> <l

 }

-CMD(status, N_("informative"), N_("[PATH]..."), N_("show status of working copy"),
+CMD(status, N_("informative"), N_("[PATH]..."), N_("show status of workspace"),
     OPT_DEPTH % OPT_EXCLUDE % OPT_BRIEF)
 {
   revision_set rs;
   roster_t old_roster, new_roster;
   data tmp;

-  app.require_working_copy();
+  app.require_workspace();
   get_working_revision_and_rosters(app, args, rs, old_roster, new_roster);

   if (global_sanity.brief)
@@ -1331,7 +1338,7 @@ CMD(status, N_("informative"), N_("[PATH
 }


-CMD(identify, N_("working copy"), N_("[PATH]"),
+CMD(identify, N_("workspace"), N_("[PATH]"),
     N_("calculate identity of PATH or stdin"),
     OPT_NONE)
 {
@@ -1363,7 +1370,7 @@ CMD(cat, N_("informative"),
     throw usage(name);

   if (app.revision_selectors.size() == 0)
-    app.require_working_copy();
+    app.require_workspace();

   transaction_guard guard(app.db, false);

@@ -1375,7 +1382,7 @@ CMD(cat, N_("informative"),
   N(app.db.revision_exists(rid), F("no such revision '%s'") % rid);

   // paths are interpreted as standard external ones when we're in a
-  // working copy, but as project-rooted external ones otherwise
+  // workspace, but as project-rooted external ones otherwise
   file_path fp;
   split_path sp;
   fp = file_path_external(idx(args, 0));
@@ -1469,7 +1476,7 @@ CMD(checkout, N_("tree"), N_("[DIRECTORY
         % ident % app.branch_name);
     }

-  app.create_working_copy(dir);
+  app.create_workspace(dir);

   file_data data;
   roster_t ros;
@@ -1642,7 +1649,7 @@ ls_known (app_state & app, vector<utf8>
   roster_t old_roster, new_roster;
   data tmp;

-  app.require_working_copy();
+  app.require_workspace();

   path_set paths;
   get_working_revision_and_rosters(app, args, rs, old_roster, new_roster);
@@ -1675,7 +1682,7 @@ ls_unknown_or_ignored (app_state & app,
 static void
 ls_unknown_or_ignored (app_state & app, bool want_ignored, vector<utf8> const & args)
 {
-  app.require_working_copy();
+  app.require_workspace();

   path_set unknown, ignored;
   find_unknown_and_ignored(app, want_ignored, args, unknown, ignored);
@@ -1697,7 +1704,7 @@ find_missing (app_state & app, vector<ut
   cset included_work, excluded_work;
   path_set old_paths, new_paths;

-  app.require_working_copy();
+  app.require_workspace();
   get_base_roster_and_working_cset(app, args, base_rid, base_roster,
                                    old_paths, new_paths,
                                    included_work, excluded_work);
@@ -1740,7 +1747,7 @@ CMD(list, N_("informative"),
       "unknown\n"
       "ignored\n"
       "missing"),
-    N_("show database objects, or the current working copy manifest,\n"
+    N_("show database objects, or the current workspace manifest,\n"
       "or unknown, intentionally ignored, or missing state files"),
     OPT_DEPTH % OPT_EXCLUDE)
 {
@@ -2154,7 +2161,7 @@ CMD(db, N_("database"),
     throw usage(name);
 }

-CMD(attr, N_("working copy"), N_("set PATH ATTR VALUE\nget PATH [ATTR]\ndrop PATH [ATTR]"),
+CMD(attr, N_("workspace"), N_("set PATH ATTR VALUE\nget PATH [ATTR]\ndrop PATH [ATTR]"),
     N_("set, get or drop file attributes"),
     OPT_NONE)
 {
@@ -2164,7 +2171,7 @@ CMD(attr, N_("working copy"), N_("set PA
   revision_set rs;
   roster_t old_roster, new_roster;

-  app.require_working_copy();
+  app.require_workspace();
   get_unrestricted_working_revision_and_rosters(app, rs, old_roster, new_roster);

   file_path path = file_path_external(idx(args,1));
@@ -2273,8 +2280,8 @@ process_commit_message_args(bool & given
 }


-CMD(commit, N_("working copy"), N_("[PATH]..."),
-    N_("commit working copy to database"),
+CMD(commit, N_("workspace"), N_("[PATH]..."),
+    N_("commit workspace to database"),
     OPT_BRANCH_NAME % OPT_MESSAGE % OPT_MSGFILE % OPT_DATE %
     OPT_AUTHOR % OPT_DEPTH % OPT_EXCLUDE)
 {
@@ -2285,7 +2292,7 @@ CMD(commit, N_("working copy"), N_("[PAT
   roster_t old_roster, new_roster;

   app.make_branch_sticky();
-  app.require_working_copy();
+  app.require_workspace();

   // preserve excluded work for future commmits
   cset excluded_work;
@@ -2645,7 +2652,7 @@ CMD(diff, N_("informative"), N_("[PATH].

 CMD(diff, N_("informative"), N_("[PATH]..."),
     N_("show current diffs on stdout.\n"
-    "If one revision is given, the diff between the working directory and\n"
+    "If one revision is given, the diff between the workspace and\n"
     "that revision is shown.  If two revisions are given, the diff between\n"
     "them is given.  If no format is specified, unified is used by default."),
     OPT_REVISION % OPT_DEPTH % OPT_EXCLUDE %
@@ -2669,9 +2676,9 @@ CMD(diff, N_("informative"), N_("[PATH].
   // initialize before transaction so we have a database to work with

   if (app.revision_selectors.size() == 0)
-    app.require_working_copy();
+    app.require_workspace();
   else if (app.revision_selectors.size() == 1)
-    app.require_working_copy();
+    app.require_workspace();

   if (app.revision_selectors.size() == 0)
     {
@@ -2733,7 +2740,7 @@ CMD(diff, N_("informative"), N_("[PATH].
         // Calculate a cset from old->new, then re-restrict it.
         // FIXME: this is *possibly* a UI bug, insofar as we
         // look at the restriction name(s) you provided on the command
-        // line in the context of new and old, *not* the working copy.
+        // line in the context of new and old, *not* the workspace.
         // One way of "fixing" this is to map the filenames on the command
         // line to node_ids, and then restrict based on those. This
         // might be more intuitive; on the other hand it would make it
@@ -2796,17 +2803,19 @@ struct update_source
   }
 };

-CMD(update, N_("working copy"), "",
-    N_("update working copy.\n"
-    "If a revision is given, base the update on that revision.  If not,\n"
-    "base the update on the head of the branch (given or implicit)."),
+CMD(update, N_("workspace"), "",
+    N_("update workspace.\n"
+       "This command modifies your workspace to be based off of a\n"
+       "different revision, preserving uncommitted changes as it does so.\n"
+       "If a revision is given, update the workspace to that revision.\n"
+       "If not, update the workspace to the head of the branch."),
     OPT_BRANCH_NAME % OPT_REVISION)
 {
   revision_set r_old, r_working, r_new;
-  roster_t working_roster, chosen_roster;
+  roster_t working_roster, chosen_roster, target_roster;
   boost::shared_ptr<roster_t> old_roster = boost::shared_ptr<roster_t>(new roster_t());
-  marking_map working_mm, chosen_mm, merged_mm;
-  revision_id r_old_id, r_working_id, r_chosen_id;
+  marking_map working_mm, chosen_mm, merged_mm, target_mm;
+  revision_id r_old_id, r_working_id, r_chosen_id, r_target_id;

   if (args.size() > 0)
     throw usage(name);
@@ -2814,7 +2823,7 @@ CMD(update, N_("working copy"), "",
   if (app.revision_selectors.size() > 1)
     throw usage(name);

-  app.require_working_copy();
+  app.require_workspace();

   // FIXME: the next few lines are a little bit expensive insofar as they
   // load the base roster twice. The API could use some factoring or
@@ -2833,7 +2842,7 @@ CMD(update, N_("working copy"), "",
                                  working_roster, working_mm, app);

   N(!null_id(r_old_id),
-    F("this working directory is a new project; cannot update"));
+    F("this workspace is a new project; cannot update"));

   if (app.revision_selectors.size() == 0)
     {
@@ -2866,7 +2875,7 @@ CMD(update, N_("working copy"), "",
   if (r_old_id == r_chosen_id)
     {
       P(F("already up to date at %s\n") % r_old_id);
-      // do still switch the working copy branch, in case they have used
+      // do still switch the workspace branch, in case they have used
       // update to switch branches.
       if (!app.branch_name().empty())
         app.make_branch_sticky();
@@ -2890,37 +2899,36 @@ CMD(update, N_("working copy"), "",
         % r_chosen_id % app.branch_name);
     }

-  // FIXME_ROSTERS: In the old (pre-roster) code, we supported updating to
-  // any revision in the graph, anywhere; if there was a path to get there,
-  // we'd synthesize a changeset to get there. This was a little easier to
-  // work with in pre-roster monotone because we merged changesets, not
-  // rosters.
-  //
-  // To do this using rosters' default "mark-merge", we'd need to
-  // synthesize a marking map using a fake ancestry graph. It's
-  // conceivable, but it's a fair amount of work, and it's not clear that
-  // many people used it, nor that mark-merge is ideal for the task. It
-  // might make more sense to make a different merger for this
-  // case.
-  //
-  // Anyways, for the time being we're only implementing "forwards
-  // updates". That is, you can only "update" to new base revisions which
-  // are descendents of the base revision you have in your working copy.
-
-  N(is_ancestor(r_old_id, r_chosen_id, app),
-    F("Update target is not a descendent of working copy base revision\n"));
-
   app.db.get_roster(r_chosen_id, chosen_roster, chosen_mm);

   std::set<revision_id>
     working_uncommon_ancestors,
     chosen_uncommon_ancestors;

-  app.db.get_uncommon_ancestors(r_old_id, r_chosen_id,
-                                working_uncommon_ancestors,
-                                chosen_uncommon_ancestors);
+  if (is_ancestor(r_old_id, r_chosen_id, app))
+    {
+      target_roster = chosen_roster;
+      target_mm = chosen_mm;
+      r_target_id = r_chosen_id;
+      app.db.get_uncommon_ancestors(r_old_id, r_chosen_id,
+                                    working_uncommon_ancestors,
+                                    chosen_uncommon_ancestors);
+    }
+  else
+    {
+      cset transplant;
+      make_cset (*old_roster, chosen_roster, transplant);
+      // just pick something, all that's important is that it not
+      // match the work revision or any ancestors of the base revision.
+      r_target_id = revision_id(hexenc<id>("5432100000000000000000000500000000000000"));
+      make_roster_for_base_plus_cset(r_old_id,
+                                     transplant,
+                                     r_target_id,
+                                     target_roster, target_mm, app);
+      chosen_uncommon_ancestors.insert(r_target_id);
+    }

-  // Note that under the definition of mark-merge, the working copy is an
+  // Note that under the definition of mark-merge, the workspace is an
   // "uncommon ancestor" if itself too, even though it was not present in
   // the database (hence not returned by the query above).

@@ -2930,19 +2938,20 @@ CMD(update, N_("working copy"), "",

   roster_merge_result result;
   roster_merge(working_roster, working_mm, working_uncommon_ancestors,
-               chosen_roster, chosen_mm, chosen_uncommon_ancestors,
+               target_roster, target_mm, chosen_uncommon_ancestors,
                result);

   roster_t & merged_roster = result.roster;

-  content_merge_working_copy_adaptor wca(app, old_roster);
-  resolve_merge_conflicts (r_old_id, r_chosen_id,
-                           working_roster, chosen_roster,
-                           working_mm, chosen_mm,
+  content_merge_workspace_adaptor wca(app, old_roster);
+  resolve_merge_conflicts (r_old_id, r_target_id,
+                           working_roster, target_roster,
+                           working_mm, target_mm,
                            result, wca, app);

   I(result.is_clean());
-  merged_roster.check_sane();
+  // temporary node ids may appear if updating to a non-ancestor
+  merged_roster.check_sane(true);

   // we have the following
   //
@@ -2952,23 +2961,23 @@ CMD(update, N_("working copy"), "",
   //  chosen --> merged
   //
   // - old is the revision specified in MT/revision
-  // - working is based on old and includes the working copy's changes
+  // - working is based on old and includes the workspace's changes
   // - chosen is the revision we're updating to and will end up in MT/revision
   // - merged is the merge of working and chosen
   //
-  // we apply the working to merged cset to the working copy
+  // we apply the working to merged cset to the workspace
   // and write the cset from chosen to merged changeset in MT/work

   cset update, remaining;
   make_cset (working_roster, merged_roster, update);
-  make_cset (chosen_roster, merged_roster, remaining);
+  make_cset (target_roster, merged_roster, remaining);

   //   {
   //     data t1, t2, t3;
   //     write_cset(update, t1);
   //     write_cset(remaining, t2);
   //     write_manifest_of_roster(merged_roster, t3);
-  //     P(F("updating working copy with [[[\n%s\n]]]\n") % t1);
+  //     P(F("updating workspace with [[[\n%s\n]]]\n") % t1);
   //     P(F("leaving residual work [[[\n%s\n]]]\n") % t2);
   //     P(F("merged roster [[[\n%s\n]]]\n") % t3);
   //   }
@@ -2979,7 +2988,7 @@ CMD(update, N_("working copy"), "",

   // small race condition here...
   // nb: we write out r_chosen, not r_new, because the revision-on-disk
-  // is the basis of the working copy, not the working copy itself.
+  // is the basis of the workspace, not the workspace itself.
   put_revision_id(r_chosen_id);
   if (!app.branch_name().empty())
     {
@@ -3041,7 +3050,7 @@ CMD(merge, N_("tree"), "", N_("merge unm
       P(F("[merged] %s\n") % merged);
       left = merged;
     }
-  P(F("note: your working copies have not been updated\n"));
+  P(F("note: your workspaces have not been updated\n"));
 }

 CMD(propagate, N_("tree"), N_("SOURCE-BRANCH DEST-BRANCH"),
@@ -3233,8 +3242,8 @@ CMD(complete, N_("informative"), N_("(re
     throw usage(name);
 }

-CMD(revert, N_("working copy"), N_("[PATH]..."),
-    N_("revert file(s), dir(s) or entire working copy (\".\")"),
+CMD(revert, N_("workspace"), N_("[PATH]..."),
+    N_("revert file(s), dir(s) or entire workspace (\".\")"),
     OPT_DEPTH % OPT_EXCLUDE % OPT_MISSING)
 {
   roster_t old_roster;
@@ -3245,7 +3254,7 @@ CMD(revert, N_("working copy"), N_("[PAT
   if (args.size() < 1 && !app.missing)
       throw usage(name);

-  app.require_working_copy();
+  app.require_workspace();

   get_base_revision(app, old_revision_id, old_roster);

@@ -3418,14 +3427,12 @@ CMD(annotate, N_("informative"), N_("PAT

 CMD(annotate, N_("informative"), N_("PATH"),
     N_("print annotated copy of the file from REVISION"),
-    OPT_REVISION)
+    OPT_REVISION % OPT_BRIEF)
 {
-  // this function compiles, but the do_annotate() stuff in annotate.cc is
-  // still in flux, so disabling command here.
   revision_id rid;

   if (app.revision_selectors.size() == 0)
-    app.require_working_copy();
+    app.require_workspace();

   if ((args.size() != 1) || (app.revision_selectors.size() > 1))
     throw usage(name);
@@ -3460,10 +3467,10 @@ CMD(log, N_("informative"), N_("[FILE] .
 CMD(log, N_("informative"), N_("[FILE] ..."),
     N_("print history in reverse order (filtering by 'FILE'). If one or more\n"
     "revisions are given, use them as a starting point."),
-    OPT_LAST % OPT_REVISION % OPT_BRIEF % OPT_DIFFS % OPT_MERGES)
+    OPT_LAST % OPT_NEXT % OPT_REVISION % OPT_BRIEF % OPT_DIFFS % OPT_MERGES)
 {
   if (app.revision_selectors.size() == 0)
-    app.require_working_copy("try passing a --revision to start at");
+    app.require_workspace("try passing a --revision to start at");

   set<node_id> nodes;

@@ -3520,9 +3527,13 @@ CMD(log, N_("informative"), N_("[FILE] .

   set<revision_id> seen;
   long last = app.last;
+  long next = app.next;

+  N(last == -1 || next == -1,
+    F("only one of --last/--next allowed"));
+
   revision_set rev;
-  while(! frontier.empty() && (last == -1 || last > 0))
+  while(! frontier.empty() && (last == -1 || last > 0) && (next == -1 || next > 0))
     {
       set<revision_id> next_frontier;

@@ -3585,10 +3596,24 @@ CMD(log, N_("informative"), N_("[FILE] .
                e != rev.edges.end(); ++e)
             {
               ancestors.insert(edge_old_revision(e));
-              next_frontier.insert(edge_old_revision(e));
               csum.add_change_set(edge_changes(e));
             }

+          if (next > 0)
+            {
+              set<revision_id> children;
+              app.db.get_revision_children(rid, children);
+              copy(children.begin(), children.end(),
+                   inserter(next_frontier, next_frontier.end()));
+            }
+          else // work backwards by default
+            {
+              set<revision_id> parents;
+              app.db.get_revision_parents(rid, parents);
+              copy(parents.begin(), parents.end(),
+                   inserter(next_frontier, next_frontier.end()));
+            }
+
           if (!app.merges && rev.is_merge_node())
             print_this = false;

@@ -3653,10 +3678,15 @@ CMD(log, N_("informative"), N_("[FILE] .
                   }
               }

-            if (last > 0)
+            if (next > 0)
               {
+                next--;
+              }
+            else if (last > 0)
+              {
                 last--;
               }
+
           }
         // when we had a restriction and run out of nodes, stop.
         if (!nodes.empty() && next_nodes.empty())
@@ -3668,17 +3698,22 @@ CMD(log, N_("informative"), N_("[FILE] .
     }
 }

-CMD(setup, N_("tree"), N_("DIRECTORY"), N_("setup a new working copy directory"),
+CMD(setup, N_("tree"), N_("[DIRECTORY]"), N_("setup a new workspace directory, default to current"),
     OPT_BRANCH_NAME)
 {
-  if (args.size() != 1)
+  if (args.size() > 1)
     throw usage(name);

   N(!app.branch_name().empty(), F("need --branch argument for setup"));
   app.db.ensure_open();

-  string dir = idx(args,0)();
-  app.create_working_copy(dir);
+  string dir;
+  if (args.size() == 1)
+    dir = idx(args,0)();
+  else
+    dir = ".";
+
+  app.create_workspace(dir);
   revision_id null;
   put_revision_id(null);
 }
============================================================
--- configure.ac	44606bdf31f4bf99cb64d42a5a0e65c1f0c95036
+++ configure.ac	3db7a41acaf0a99ca96e19c12c2f96e5eaea45b1
@@ -8,16 +8,6 @@ AM_CONFIG_HEADER([config.h])
 AC_CONFIG_TESTDIR([tests])
 AM_CONFIG_HEADER([config.h])

-# we have some sse2 code in cryptopp
-# currently gcc does some surprising things here, so we disable it
-# completely.
-# AC_CHECK_HEADERS(emmintrin.h, [CXXFLAGS_MSSE2="-msse2"])
-
-# autoconf turns on -g by default unless a user overrides.  this is
-# nice for smallish programs but it makes build times insane for
-# spirit-based programs. we turn it off here unless the user turned it
-# on themselves.
-
 # Checks for programs.
 AC_PROG_CXX
 AC_PROG_CC
@@ -287,20 +277,11 @@ AC_CACHE_CHECK([if this is Windows],

 AC_CACHE_CHECK([if this is Windows],
        ac_win32, [
-       AC_TRY_RUN([
-#ifdef WIN32
-#include <windows.h>
-int main(void)
-{
-  return 0;
-}
-#else
-__wont_compile_
-#endif
-],
-        ac_win32=yes,
-        ac_win32=no,
-        ac_win32=no)])
+       AC_TRY_COMPILE([#include <windows.h>],
+                      [HANDLE h; DWORD d;],
+                      ac_win32=yes,
+                      ac_win32=no)
+])
 if test "$ac_win32" = "yes"; then
    AM_CONDITIONAL(WIN32_PLATFORM, true)
 else
@@ -319,21 +300,16 @@ AC_CACHE_CHECK([presence of IPv6],
       ac_inet6=no
       if test x"${enable_ipv6}" = x"auto" || test x"${enable_ipv6}" = x"yes"
       then
-         AC_TRY_RUN([
-#if defined(WIN32)
-# include <winsock2.h>
-#else
-# include <sys/types.h>
-# include <sys/socket.h>
-# include <netinet/in.h>
-# include <arpa/inet.h>
-#endif
-int main(void)
-{
-  sockaddr_in6 sa_in6;
-}
-],
-            ac_inet6=yes
+         AC_TRY_COMPILE([#ifdef WIN32
+                         #include <winsock2.h>
+                         #else
+                         #include <sys/types.h>
+                         #include <sys/socket.h>
+                         #include <netinet/in.h>
+                         #include <arpa/inet.h>
+                         #endif],
+                        [sockaddr_in6 sa_in6;],
+                        ac_inet6=yes
          )

          if test x"${enable_ipv6}" = x"yes" && test x"${ac_inet6}" = x"no"
@@ -429,7 +405,7 @@ AC_DEFUN([TYPE_SOCKLEN_T],
   AC_TRY_COMPILE(
   [#include <sys/types.h>
    #include <sys/socket.h>],
-  [socklen_t len = 42; return 0;],
+  [socklen_t len = 42;],
   ac_cv_type_socklen_t=yes,
   ac_cv_type_socklen_t=no)
 ])
@@ -444,7 +420,7 @@ AC_DEFUN([TYPE_PID_T],
 [
   AC_TRY_COMPILE(
   [#include <sys/types.h>],
-  [pid_t pid = 42; return 0;],
+  [pid_t pid = 42; ],
   ac_cv_type_pid_t=yes,
   ac_cv_type_pid_t=no)
 ])
============================================================
--- contrib/mtbrowse.sh	f8d866a9eb798aecc974120b90aad5ca40ad4a4b
+++ contrib/mtbrowse.sh	bbcbc8835c2fe6038086c4c15ae86b4301d90cc8
@@ -22,11 +22,11 @@
 #  - Browse in branches, revisions, diff files, view logs ...
 #
 # Needed tools:
-#  monotone 0.19 or compatible
+#  monotone 0.19, 0.23, 0.26 or compatible
 #  dialog (tested Version 0.9b)
-#  bash, sh, ash, dash
+#  bash, sh, dash
 #  less, vi or vim (use $VISUAL or $PAGER)
-#  cat, cut, echo, eval, head, sort, tail, wc, xargs ...
+#  cat, cut, echo, eval, head, sort, tail, tr, wc, xargs ...
 #
 # History:
 # 2005/5/5 Version 0.1.1 Henry@BigFoot.de
@@ -111,11 +111,25 @@
 # Temp files without tailing dot.
 # New option: Sort by Date/Time with up or down.
 # Fix: Parent of merge, if certs list from cache (ncolor).
+#
+# 2006-01-17 Version 0.1.14 Henry@BigFoot.de
+# One call for getting monotone version.
+# Warn before using "automate ancestor" on big database.
+#
+# 2006-01-21 Version 0.1.15 Henry@BigFoot.de
+# Fix: Author colors >8 wrong handled.
+# Faster brief selection menu, without changelog (do_log_brief_ancestors).
+# Skip merges from more as one person in brief selection.
+# Remove forbitten chars from changelog with tr. added backslash.
+#
+# 2006-01-23 Version 0.1.16 Henry@BigFoot.de
+# More posix shell syntax for dash.
+# do_head_sel: Prints error message from mt (it's first call with db file).

 # Known Bugs / ToDo-List:
 # * better make "sed -n -e '1p'" for merge two different branches.

-VERSION="0.1.13"
+VERSION="0.1.16"

 # Save users settings
 # Default values, can overwrite on .mtbrowserc
@@ -162,7 +176,7 @@ FORMAT_COLOR="\\Z7\\Zb"
 FORMAT_COLOR="\\Z7\\Zb"

 # How get automate ancestors: I=interal function, A=automate ancestors,
-# L=Log brief
+# L=log brief with changelog, B=brief log
 ANCESTORS="L"

 # read saved settings
@@ -243,7 +257,7 @@ do_clear_on_exit()
 {
     rm -f $TEMPFILE.branches $TEMPFILE.ancestors $TEMPFILE.toposort \
       $TEMPFILE.action-select $TEMPFILE.menu $TEMPFILE.input \
-      $TEMPFILE.ncolor $TEMPFILE.tfc
+      $TEMPFILE.ncolor $TEMPFILE.tfc $TEMPFILE.error

     if [ "$CACHE" != "1" ]
     then
@@ -288,7 +302,7 @@ fill_date_key()
 {
     local in_file=$1
     local out_file=$2
-    local short_hash dat bra aut log lineno color count tfc
+    local hash short_hash dat bra aut log lineno color tfc

     line_count=`wc -l < $in_file`
     if [ "$line_count" -eq 0 ]
@@ -306,16 +320,16 @@ fill_date_key()
     do
 	if [ -n "$line_count" ]
 	then
-	    let lineno++
-	    echo "$(( 100*$lineno/line_count ))"
+	    lineno=$(( $lineno+1 ))
+	    echo "$(( 100*$lineno/$line_count ))"
 	else
 	    echo -n "." 1>&2
 	fi

 	short_hash=`echo $hash | cut -c 1-$HASH_TRIM`

-	# get certs of revision, cache it
-	monotone --db=$DB list certs $hash > $tfc
+	# get certs of revision, remove special chars, cache it
+	monotone --db=$DB list certs $hash | tr '\\\t\042' '/ \047' > $tfc

 	# Date format
 	case $FORMAT_DATE in
@@ -376,15 +390,15 @@ fill_date_key()

 	# Changelog format
 	case $FORMAT_LOG in
-	    F) # full     TAB here ----v
-		log=`sed -n -r -e "y/\"	/' /" -e \
+	    F) # full
+		log=`sed -n -r -e \
 		    '/^Name  : changelog/,+1s/Value : (.+)$/ \1/p' \
-		    < $tfc`
+		    < $tfc | sed -n -e '1p'`
 		;;
 	    S) # short
-		log=`sed -n -r -e "y/\"	/' /" -e \
+		log=`sed -n -r -e \
 		    '/^Name  : changelog/,+1s/Value : (.{1,20}).*$/ \1/p' \
-		    < $tfc`
+		    < $tfc | sed -n -e '1p'`
 		;;
 	esac

@@ -410,7 +424,8 @@ fill_date_key()
 		then
 		    color="\\Zb\\Z$color"
 		else
-		    color="\\Z$color"
+		    color8=$(( $color - 8 ))
+		    color="\\Z$color8"
 		fi
 	     else
 		color="$FORMAT_COLOR"
@@ -426,7 +441,118 @@ fill_date_key()
     rm $tfc
 }

+do_log_brief_ancestors()
+{
+    local out_file=$1
+    local hash short_hash dat bra aut lineno color

+    # Brief output
+    # e51dc90425c6371a176e87df294b47fcdba3f0bb Full Name <henry@bigfoot.de> 2005-11-20T20:31:34 mtbrowse
+
+    lineno=0
+    monotone log --brief --last=$CERTS_MAX --revision=$HEAD --db=$DB |\
+    while read line
+    do
+	lineno=$(( $lineno+1 ))
+	# TODO: Why MT give more than "--last"?
+	if [ $lineno -le $CERTS_MAX ]
+	then
+	    echo "$(( 100*$lineno/$CERTS_MAX ))"
+	fi
+
+	set -- `echo "$line" | sed -n -r -e 's/^([^ ]+) (.+) ([0-9\-]{10})T([0-9:]{8}) (.+)$/\1 \3 \4 \5/p'`
+	author=`echo "$line" | sed -n -r -e 's/^([^ ]+) (.+) ([0-9\-]{10})T([0-9:]{8}) (.+)$/\2/p'`
+	hash=$1
+	date=$2
+	time=$3
+	branch="$4"
+
+	# Skip wrong formats, special case
+	# 37416b924fc25a48bba11ed8b2cd62e9dab7e637 First Name  <abc@gmail.com>,geecko@geek.com.au,third@domain.org 2006-01-20T08:21:37,2006-01-20T08:35:46,2006-01-20T08:35:33 net.venge.monotone
+
+	if [ -n "$hash" -a -n "$date" -a -n "$time" -a -n "$author" ]
+	then
+
+	    short_hash=`echo $hash | cut -c 1-$HASH_TRIM`
+
+	    # Date format
+	    case $FORMAT_DATE in
+	    F) # 2005-12-31T23:59:59
+		dat="${date}T$time "
+		;;
+	    L) # 2005-12-31 23:59
+		dat="`echo "$date $time" | cut -c 1-16` "
+		;;
+	    D) # 2005-12-31
+		dat=$date
+		;;
+	    S) # 12-31 23:59
+		dat=`echo "$date $time" | sed -n -r -e \
+		    '.{4}-(.+) (.{5}).+/\1 \2 /p'`
+		;;
+	    T) # 23:59:59
+		dat=$time
+		;;
+	    esac
+
+	    # Branch format
+	    case $FORMAT_BRANCH in
+	    F) # full
+		bra=$branch
+		;;
+	    S) # short
+		bra=`echo "$branch" | sed -n -r -e \
+		    '/.*\.([^\.]+)$/\1 /p' `
+		;;
+	    esac
+
+	    # Author format
+	    case $FORMAT_AUTHOR in
+	    F) # full
+		aut="$author"
+		;;
+	    S) # short
+		aut=`echo "$author " | sed -n -r -e \
+		    's/(.{1,10}).*[@ ].+$/\1/p'`
+		;;
+	    esac
+
+	    # TODO: Copied from fill_date_key
+	    # Author coloring?
+	    if [ -n "$FORMAT_COLOR" -a "$FORMAT_AUTHOR" != "N" ]
+	    then
+		if [ "$last_aut" != "$aut" ]
+		then
+		    # Automatic color by author?
+		    if [ "$FORMAT_COLOR" = "A" ]
+		    then
+			color=`grep -n "$aut" $TEMPFILE.ncolor | cut -d ':' -f 1`
+			if [ -z "$color" ]
+			then
+			    color=$(( `wc -l < $TEMPFILE.ncolor` % 16 + 1 ))
+			    echo "$aut" >> $TEMPFILE.ncolor
+			fi
+
+			if [ $color -le 8 ]
+			then
+			    color="\\Zb\\Z$color"
+			else
+			    color8=$(( $color - 8 ))
+			    color="\\Z$color8"
+			fi
+		    else
+			color="$FORMAT_COLOR"
+		    fi
+		    last_aut="$aut"
+		fi
+		echo "$short_hash \"$dat$bra\\ZR$color$aut\"" >> $out_file
+	    else
+		echo "$short_hash \"$dat$bra\\ZR$aut\"" >> $out_file
+	    fi
+	fi
+    done | dialog --gauge "$CERTS_MAX certs reading" 6 60
+}
+
 # Select a branch
 # Is parameter given: No user select, if branch known.
 do_branch_sel()
@@ -506,7 +632,12 @@ do_head_sel()
 	return
     fi

-    monotone --db=$DB automate heads $BRANCH > $TEMPFILE.heads 2>/dev/null
+    if ! monotone --db=$DB automate heads $BRANCH > $TEMPFILE.heads 2>$TEMPFILE.error
+    then
+	cat $TEMPFILE.error
+	exit -1
+    fi
+
     # Only one head ?
     if [ `wc -l < $TEMPFILE.heads` -eq 1 -a -n "$1" ]
     then
@@ -677,7 +808,7 @@ do_automate_ancestors_depth()
 		return 0
 	fi

-	let depth++
+	depth=$(( $depth+1 ))
 	monotone --db=$DB automate parents $head |\
 	while read rev
 	do
@@ -687,7 +818,7 @@ do_automate_ancestors_depth()
 		do_automate_ancestors_depth $depth $rev || return $?
 	    fi
 	done
-	let depth--
+	depth=$(( $depth-1 ))

 	return 0
 }
@@ -713,6 +844,10 @@ do_revision_sel()
 	echo "Reading ancestors ($HEAD)"

 	case $ANCESTORS in
+	    B)	# TODO:
+		# Get ancestors from log, brief format
+		do_log_brief_ancestors $TEMPFILE.certs3tmp
+	    ;;
 	    L)
 		# Get ancestors from log
 		monotone log --brief --last=$CERTS_MAX --revision=$HEAD \
@@ -733,9 +868,11 @@ do_revision_sel()
 	    ;;
 	esac

-	# Must toposort, if enabled by user, or before tailing
-	if [ "$TOPOSORT" = "T" -o "$CERTS_MAX" -gt 0 -a "$ANCESTORS" != 'L' ]
+	if [ "$ANCESTORS" != "B" ]
 	then
+	    # Must toposort, if enabled by user, or before tailing
+	    if [ "$TOPOSORT" = "T" -o "$CERTS_MAX" -gt 0 -a "$ANCESTORS" != "L" ]
+	    then
 		echo "Toposort..."
 		monotone --db=$DB automate toposort `cat $TEMPFILE.ancestors` \
 		  > $TEMPFILE.toposort || exit 200
@@ -747,12 +884,13 @@ do_revision_sel()
 			  > $TEMPFILE.toposort2
 			mv $TEMPFILE.toposort2 $TEMPFILE.toposort
 		fi
-	else
+	    else
 		mv $TEMPFILE.ancestors $TEMPFILE.toposort
-	fi
+	    fi

-	# Reading revisions and fill with date, names and changelog
-	fill_date_key $TEMPFILE.toposort $TEMPFILE.certs3tmp
+	    # Reading revisions and fill with date, names and changelog
+	    fill_date_key $TEMPFILE.toposort $TEMPFILE.certs3tmp
+	fi

 	case $TOPOSORT in
 	    D)
@@ -875,7 +1013,7 @@ do_config()
 	    if dialog --default-item "$TOPOSORT" \
 		--menu "Sort revisions by" 0 0 0 \
 		"T" "Toposort, oldest top (from Monotone)" \
-		"D" "Date/Time, oldest top" \
+		"D" "Date/Time, oldest top (internal function)" \
 		"R" "Reverse Date/Time (reverse toposort)" \
 		"N" "None. Simple get from list" \
 		2> $TEMPFILE.input
@@ -977,12 +1115,23 @@ do_config()
 	    # How to get ancestors
 	    if dialog --default-item "$ANCESTORS" \
 		--menu "Get ancestors by using" 0 0 0 \
-		"L" "Monotone \"log --brief\" (fastest)" \
+		"B" "Brief log, without changelog (fastest)" \
+		"L" "Monotone \"log --brief\" with changelog" \
 		"I" "Internal function with depth limit (faster)" \
 		"A" "Monotone \"automate ancestor\" (save mode, very slow)" \
 		2> $TEMPFILE.input
 	    then
 		ANCESTORS=`cat $TEMPFILE.input`
+
+		if [ "$ANCESTORS" = "A" ]
+		then
+		    # functions don't work with big database
+		    dialog --title " Info " --msgbox \
+"\"automate ancestor\":
+
+Big database can overflow command line.
+It's saver to use internal function (I)." 0 0
+		fi
 	    fi
 	    ;;
 	  C)
@@ -1039,7 +1188,8 @@ Automatic corrected" 0 0
 	    # functions don't work without limit
 	    dialog --title " Info " --msgbox \
 "Certs limit in Select-List:
-negative or zero last not allowed
+
+Negative or zero limit not allowed
 Automatic corrected" 0 0
 	    CERTS_MAX=20
 	fi
@@ -1066,8 +1216,9 @@ fi
 fi

 # Get monotone version
-MT_MAJOR=`monotone --version | sed -r -e 's/^.+ ([0-9]{1,2})\.([0-9]{1,2}) .+$/\1/'`
-MT_MINOR=`monotone --version | sed -r -e 's/^.+ ([0-9]{1,2})\.([0-9]{1,2}) .+$/\2/'`
+set -- `monotone --version | sed -r -e 's/^.+ ([0-9]{1,2})\.([0-9]{1,2}) .+$/\1 \2/'`
+MT_MAJOR=$1
+MT_MINOR=$2

 if [ -z "$MT_MAJOR" -o -z "$MT_MINOR" ]
 then
============================================================
--- database.cc	7d8fa179c2619f9bab52439e8eff4ede97ef714a
+++ database.cc	2d2107adceb23e96ac42776c4b9e8658224581d0
@@ -1,9 +1,12 @@
-// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
+// -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
 // copyright (C) 2002, 2003 graydon hoare <graydon@pobox.com>
+// copyright (C) 2006 vinzenz feenstra <evilissimo@c-plusplus.de>
 // all rights reserved.
 // licensed to the public under the terms of the GNU GPL (>= 2)
 // see the file COPYING for details

+
 #include <algorithm>
 #include <deque>
 #include <fstream>
@@ -40,7 +43,6 @@

 // defined in views.sql, converted to header:
 #include "views.h"
-
 // this file defines a public, typed interface to the database.
 // the database class encapsulates all knowledge about sqlite,
 // the schema, and all SQL statements used to access the schema.
@@ -56,12 +58,55 @@ int const any_cols = -1;
 int const any_rows = -1;
 int const any_cols = -1;

-namespace
+namespace
 {
+  struct query_param
+  {
+    enum arg_type { text, blob };
+    arg_type type;
+    std::string data;
+  };
+
+  query_param
+  text(std::string const & txt)
+  {
+    query_param q = {
+      query_param::text,
+      txt,
+    };
+    return q;
+  }
+
+  query_param
+  blob(std::string const & blb)
+  {
+    query_param q = {
+      query_param::blob,
+      blb,
+    };
+    return q;
+  }
+
   // track all open databases for close_all_databases() handler
   set<sqlite3*> sql_contexts;
 }

+struct query
+{
+  query(std::string const & cmd)
+    : sql_cmd(cmd)
+  {}
+
+  query & operator %(query_param const & qp)
+  {
+    args.push_back(qp);
+    return *this;
+  }
+
+  std::vector<query_param> args;
+  std::string sql_cmd;
+};
+
 extern "C" {
 // some wrappers to ease migration
   const char *sqlite3_value_text_s(sqlite3_value *v);
@@ -94,26 +139,46 @@ void
 }

 void
-database::check_rosterified()
+database::check_format()
 {
-  results res;
+  results res_revisions;
+  string manifests_query = "SELECT 1 FROM manifests LIMIT 1";
+  string revisions_query = "SELECT 1 FROM revisions LIMIT 1";
   string rosters_query = "SELECT 1 FROM rosters LIMIT 1";
-  string revisions_query = "SELECT 1 FROM revisions LIMIT 1";

-  fetch(res, one_col, any_rows, revisions_query.c_str());
-  if (res.size() > 0)
+  fetch(res_revisions, one_col, any_rows, query(revisions_query));
+
+  if (res_revisions.size() > 0)
     {
-      fetch(res, one_col, any_rows, rosters_query.c_str());
-      N (res.size() != 0,
-         F("database %s contains revisions but no rosters\n"
-           "if you are a project leader or doing local testing:\n"
-           "  see the file UPGRADE for instructions on upgrading.\n"
-           "if you are not a project leader:\n"
-           "  wait for a leader to migrate project data, and then\n"
-           "  pull into a fresh database.\n"
-           "sorry about the inconvenience.")
-         % filename);
+      // they have revisions, so they can't be _ancient_, but they still might
+      // not have rosters
+      results res_rosters;
+      fetch(res_rosters, one_col, any_rows, query(rosters_query));
+      N(res_rosters.size() != 0,
+        F("database %s contains revisions but no rosters\n"
+          "if you are a project leader or doing local testing:\n"
+          "  see the file UPGRADE for instructions on upgrading.\n"
+          "if you are not a project leader:\n"
+          "  wait for a leader to migrate project data, and then\n"
+          "  pull into a fresh database.\n"
+          "sorry about the inconvenience.")
+        % filename);
     }
+  else
+    {
+      // they have no revisions, so they shouldn't have any manifests either.
+      // if they do, their db is probably ancient.  (though I guess you could
+      // trigger this check by taking a pre-roster monotone, doing "db
+      // init; commit; db kill_rev_locally", and then upgrading to a
+      // rosterified monotone.)
+      results res_manifests;
+      fetch(res_manifests, one_col, any_rows, query(manifests_query));
+      N(res_manifests.size() == 0,
+        F("database %s contains manifests but no revisions\n"
+          "this is a very old database; it needs to be upgraded\n"
+          "please see README.changesets for details")
+        % filename);
+    }
 }

 // sqlite3_value_text gives a const unsigned char * but most of the time
@@ -204,7 +269,8 @@ assert_sqlite3_ok(sqlite3 *s)
   std::string auxiliary_message = "";
   if (errcode == SQLITE_ERROR)
     {
-      auxiliary_message += _("make sure database and containing directory are writeable");
+      auxiliary_message += _("make sure database and containing directory are writeable\n"
+                             "and you have not run out of disk space");
     }
   // if the last message is empty, the \n will be stripped off too
   E(errcode == SQLITE_OK,
@@ -213,7 +279,7 @@ struct sqlite3 *
 }

 struct sqlite3 *
-database::sql(bool init)
+database::sql(bool init, bool migrating_format)
 {
   if (! __sql)
     {
@@ -236,7 +302,15 @@ database::sql(bool init)
       check_schema();
       install_functions(__app);
       install_views();
+
+      if (!migrating_format)
+        check_format();
     }
+  else
+    {
+      I(!init);
+      I(!migrating_format);
+    }
   return __sql;
 }

@@ -402,7 +476,7 @@ database::debug(string const & sql, ostr
 database::debug(string const & sql, ostream & out)
 {
   results res;
-  fetch(res, any_cols, any_rows, sql.c_str());
+  fetch(res, any_cols, any_rows, query(sql));
   out << "'" << sql << "' -> " << res.size() << " rows\n" << endl;
   for (size_t i = 0; i < res.size(); ++i)
     {
@@ -514,6 +588,13 @@ database::ensure_open()
   I(s != NULL);
 }

+void
+database::ensure_open_for_format_changes()
+{
+  sqlite3 *s = sql(false, true);
+  I(s != NULL);
+}
+
 database::~database()
 {
   L(FL("statement cache statistics\n"));
@@ -529,34 +610,18 @@ void
 }

 void
-database::execute(char const * query, ...)
+database::execute(query const & query)
 {
   results res;
-  va_list args;
-  va_start(args, query);
-  fetch(res, 0, 0, query, args);
-  va_end(args);
+  fetch(res, 0, 0, query );
 }

 void
 database::fetch(results & res,
                 int const want_cols,
                 int const want_rows,
-                char const * query, ...)
+                query const & query)
 {
-  va_list args;
-  va_start(args, query);
-  fetch(res, want_cols, want_rows, query, args);
-  va_end(args);
-}
-
-void
-database::fetch(results & res,
-                int const want_cols,
-                int const want_rows,
-                char const * query,
-                va_list args)
-{
   int nrow;
   int ncol;
   int rescode;
@@ -564,44 +629,45 @@ database::fetch(results & res,
   res.clear();
   res.resize(0);

-  map<string, statement>::iterator i = statement_cache.find(query);
+  map<string, statement>::iterator i = statement_cache.find(query.sql_cmd);
   if (i == statement_cache.end())
     {
-      statement_cache.insert(make_pair(query, statement()));
-      i = statement_cache.find(query);
+      statement_cache.insert(make_pair(query.sql_cmd, statement()));
+      i = statement_cache.find(query.sql_cmd);
       I(i != statement_cache.end());

       const char * tail;
-      sqlite3_prepare(sql(), query, -1, i->second.stmt.paddr(), &tail);
+      sqlite3_prepare(sql(), query.sql_cmd.c_str(), -1, i->second.stmt.paddr(), &tail);
       assert_sqlite3_ok(sql());
-      L(FL("prepared statement %s\n") % query);
+      L(FL("prepared statement %s\n") % query.sql_cmd);

       // no support for multiple statements here
       E(*tail == 0,
-        F("multiple statements in query: %s\n") % query);
+        F("multiple statements in query: %s\n") % query.sql_cmd);
     }

   ncol = sqlite3_column_count(i->second.stmt());

   E(want_cols == any_cols || want_cols == ncol,
-    F("wanted %d columns got %d in query: %s\n") % want_cols % ncol % query);
+    F("wanted %d columns got %d in query: %s\n") % want_cols % ncol % query.sql_cmd);

   // bind parameters for this execution

   int params = sqlite3_bind_parameter_count(i->second.stmt());

+  // Ensure that exactly the right number of parameters were given
+  I(params == int(query.args.size()));
+
   // profiling finds this logging to be quite expensive
   if (global_sanity.debug)
-    L(FL("binding %d parameters for %s\n") % params % query);
+    L(FL("binding %d parameters for %s\n") % params % query.sql_cmd);

   for (int param = 1; param <= params; param++)
     {
-      char *value = va_arg(args, char *);
-
       // profiling finds this logging to be quite expensive
       if (global_sanity.debug)
         {
-          string log = string(value);
+          string log = query.args[param-1].data;

           if (log.size() > constants::log_line_sz)
             log = log.substr(0, constants::log_line_sz);
@@ -609,7 +675,25 @@ database::fetch(results & res,
           L(FL("binding %d with value '%s'\n") % param % log);
         }

-      sqlite3_bind_text(i->second.stmt(), param, value, -1, SQLITE_TRANSIENT);
+      switch (idx(query.args, param - 1).type)
+        {
+        case query_param::text:
+          sqlite3_bind_text(i->second.stmt(), param,
+                            idx(query.args, param - 1).data.c_str(), -1,
+                            SQLITE_STATIC);
+          break;
+        case query_param::blob:
+          {
+            std::string const & data = idx(query.args, param - 1).data;
+            sqlite3_bind_blob(i->second.stmt(), param,
+                              data.data(), data.size(),
+                              SQLITE_STATIC);
+          }
+          break;
+        default:
+          I(false);
+        }
+
       assert_sqlite3_ok(sql());
     }

@@ -623,7 +707,7 @@ database::fetch(results & res,
       for (int col = 0; col < ncol; col++)
         {
           const char * value = sqlite3_column_text_s(i->second.stmt(), col);
-          E(value, F("null result in query: %s\n") % query);
+          E(value, F("null result in query: %s\n") % query.sql_cmd);
           row.push_back(value);
           //L(FL("row %d col %d value='%s'\n") % nrow % col % value);
         }
@@ -641,7 +725,7 @@ database::fetch(results & res,
   i->second.count++;

   E(want_rows == any_rows || want_rows == nrow,
-    F("wanted %d rows got %s in query: %s\n") % want_rows % nrow % query);
+    F("wanted %d rows got %s in query: %s\n") % want_rows % nrow % query.sql_cmd);
 }

 // general application-level logic
@@ -659,14 +743,16 @@ database::begin_transaction(bool exclusi
   if (transaction_level == 0)
     {
       if (exclusive)
-        execute("BEGIN EXCLUSIVE");
+        execute(query("BEGIN EXCLUSIVE"));
       else
-        execute("BEGIN DEFERRED");
+        execute(query("BEGIN DEFERRED"));
       transaction_exclusive = exclusive;
     }
   else
     {
-      E(!exclusive || transaction_exclusive, F("Attempt to start exclusive transaction within non-exclusive transaction."));
+      // You can't start an exclusive transaction within a non-exclusive
+      // transaction
+      I(!exclusive || transaction_exclusive);
     }
   transaction_level++;
 }
@@ -675,7 +761,7 @@ database::commit_transaction()
 database::commit_transaction()
 {
   if (transaction_level == 1)
-    execute("COMMIT");
+    execute(query("COMMIT"));
   transaction_level--;
 }

@@ -683,7 +769,7 @@ database::rollback_transaction()
 database::rollback_transaction()
 {
   if (transaction_level == 1)
-    execute("ROLLBACK");
+    execute(query("ROLLBACK"));
   transaction_level--;
 }

@@ -693,8 +779,8 @@ database::exists(hexenc<id> const & iden
                       string const & table)
 {
   results res;
-  string query = "SELECT id FROM " + table + " WHERE id = ?";
-  fetch(res, one_col, any_rows, query.c_str(), ident().c_str());
+  query q("SELECT id FROM " + table + " WHERE id = ?");
+  fetch(res, one_col, any_rows, q % text(ident()));
   I((res.size() == 1) || (res.size() == 0));
   return res.size() == 1;
 }
@@ -705,8 +791,8 @@ database::delta_exists(hexenc<id> const
                        string const & table)
 {
   results res;
-  string query = "SELECT id FROM " + table + " WHERE id = ?";
-  fetch(res, one_col, any_rows, query.c_str(), ident().c_str());
+  query q("SELECT id FROM " + table + " WHERE id = ?");
+  fetch(res, one_col, any_rows, q % text(ident()));
   return res.size() > 0;
 }

@@ -714,8 +800,8 @@ database::count(string const & table)
 database::count(string const & table)
 {
   results res;
-  string query = "SELECT COUNT(*) FROM " + table;
-  fetch(res, one_col, one_row, query.c_str());
+  query q("SELECT COUNT(*) FROM " + table);
+  fetch(res, one_col, one_row, q);
   return lexical_cast<unsigned long>(res[0][0]);
 }

@@ -725,8 +811,8 @@ database::space_usage(string const & tab
   results res;
   // COALESCE is required since SUM({empty set}) is NULL.
   // the sqlite docs for SUM suggest this as a workaround
-  string query = "SELECT COALESCE(SUM(LENGTH(" + concatenated_columns + ")), 0) FROM " + table;
-  fetch(res, one_col, one_row, query.c_str());
+  query q("SELECT COALESCE(SUM(LENGTH(" + concatenated_columns + ")), 0) FROM " + table);
+  fetch(res, one_col, one_row, q);
   return lexical_cast<unsigned long>(res[0][0]);
 }

@@ -734,8 +820,8 @@ database::get_ids(string const & table,
 database::get_ids(string const & table, set< hexenc<id> > & ids)
 {
   results res;
-  string query = "SELECT id FROM " + table;
-  fetch(res, one_col, any_rows, query.c_str());
+  query q("SELECT id FROM " + table);
+  fetch(res, one_col, any_rows, q);

   for (size_t i = 0; i < res.size(); ++i)
     {
@@ -749,8 +835,8 @@ database::get(hexenc<id> const & ident,
               string const & table)
 {
   results res;
-  string query = "SELECT data FROM " + table + " WHERE id = ?";
-  fetch(res, one_col, one_row, query.c_str(), ident().c_str());
+  query q("SELECT data FROM " + table + " WHERE id = ?");
+  fetch(res, one_col, one_row, q % text(ident()));

   // consistency check
   base64<gzip<data> > rdata(res[0][0]);
@@ -773,9 +859,8 @@ database::get_delta(hexenc<id> const & i
   I(ident() != "");
   I(base() != "");
   results res;
-  string query = "SELECT delta FROM " + table + " WHERE id = ? AND base = ?";
-  fetch(res, one_col, one_row, query.c_str(),
-        ident().c_str(), base().c_str());
+  query q("SELECT delta FROM " + table + " WHERE id = ? AND base = ?");
+  fetch(res, one_col, one_row, q % text(ident()) % text(base()));

   base64<gzip<delta> > del_packed = res[0][0];
   unpack(del_packed, del);
@@ -798,7 +883,9 @@ database::put(hexenc<id> const & ident,
   pack(dat, dat_packed);

   string insert = "INSERT INTO " + table + " VALUES(?, ?)";
-  execute(insert.c_str(),ident().c_str(), dat_packed().c_str());
+  execute(query(insert)
+          % text(ident())
+          % text(dat_packed()));
 }
 void
 database::put_delta(hexenc<id> const & ident,
@@ -814,7 +901,10 @@ database::put_delta(hexenc<id> const & i
   pack(del, del_packed);

   string insert = "INSERT INTO "+table+" VALUES(?, ?, ?)";
-  execute(insert.c_str(), ident().c_str(), base().c_str(), del_packed().c_str());
+  execute(query(insert)
+          % text(ident())
+          % text(base())
+          % text(del_packed()));
 }

 // static ticker cache_hits("vcache hits", "h", 1);
@@ -987,7 +1077,8 @@ database::get_version(hexenc<id> const &
                   // This tip is not a root, so extend the path.
                   results res;
                   fetch(res, one_col, any_rows,
-                        delta_query.c_str(), tip().c_str());
+                        query(delta_query)
+                        % text(tip()));

                   I(res.size() != 0);

@@ -1071,7 +1162,7 @@ database::drop(hexenc<id> const & ident,
                string const & table)
 {
   string drop = "DELETE FROM " + table + " WHERE id = ?";
-  execute(drop.c_str(), ident().c_str());
+  execute(query(drop) % text(ident()));
 }

 void
@@ -1113,9 +1204,8 @@ database::remove_version(hexenc<id> cons

   {
     results res;
-    string query = "SELECT id FROM " + delta_table + " WHERE base = ?";
-    fetch(res, one_col, any_rows,
-          query.c_str(), target_id().c_str());
+    query q("SELECT id FROM " + delta_table + " WHERE base = ?");
+    fetch(res, one_col, any_rows, q % text(target_id()));
     for (size_t i = 0; i < res.size(); ++i)
       {
         hexenc<id> old_id(res[i][0]);
@@ -1136,9 +1226,8 @@ database::remove_version(hexenc<id> cons
           hexenc<id> newer_id;
           data newer_data;
           results res;
-          string query = "SELECT base FROM " + delta_table + " WHERE id = ?";
-          fetch(res, one_col, any_rows,
-                query.c_str(), target_id().c_str());
+          query q("SELECT base FROM " + delta_table + " WHERE id = ?");
+          fetch(res, one_col, any_rows, q % text(target_id()));
           I(res.size() > 0);
           newer_id = hexenc<id>(res[0][0]);
           get_version(newer_id, newer_data, data_table, delta_table);
@@ -1150,8 +1239,8 @@ database::remove_version(hexenc<id> cons
               put_delta(i->first, newer_id, bypass_delta, delta_table);
             }
         }
-      string query = "DELETE from " + delta_table + " WHERE id = ?";
-      execute(query.c_str(), target_id().c_str());
+      execute(query("DELETE from " + delta_table + " WHERE id = ?")
+              % text(target_id()));
     }
   else
     {
@@ -1160,8 +1249,8 @@ database::remove_version(hexenc<id> cons
       for (map<hexenc<id>, data>::const_iterator i = older.begin();
            i != older.end(); ++i)
         put(i->first, i->second, data_table);
-      string query = "DELETE from " + data_table + " WHERE id = ?";
-      execute(query.c_str(), target_id().c_str());
+      execute(query("DELETE from " + data_table + " WHERE id = ?")
+              % text(target_id()));
     }

   guard.commit();
@@ -1198,9 +1287,9 @@ database::roster_link_exists_for_revisio
 database::roster_link_exists_for_revision(revision_id const & rev_id)
 {
   results res;
-  string query = ("SELECT roster_id FROM revision_roster WHERE rev_id = ? ");
-  fetch(res, one_col, any_rows, query.c_str(),
-        rev_id.inner()().c_str());
+  fetch(res, one_col, any_rows,
+        query("SELECT roster_id FROM revision_roster WHERE rev_id = ? ")
+        % text(rev_id.inner()()));
   I((res.size() == 1) || (res.size() == 0));
   return res.size() == 1;
 }
@@ -1209,9 +1298,9 @@ database::roster_exists_for_revision(rev
 database::roster_exists_for_revision(revision_id const & rev_id)
 {
   results res;
-  string query = ("SELECT roster_id FROM revision_roster WHERE rev_id = ? ");
-  fetch(res, one_col, any_rows, query.c_str(),
-        rev_id.inner()().c_str());
+  fetch(res, one_col, any_rows,
+        query("SELECT roster_id FROM revision_roster WHERE rev_id = ? ")
+        % text(rev_id.inner()()));
   I((res.size() == 1) || (res.size() == 0));
   return (res.size() == 1) && roster_version_exists(hexenc<id>(res[0][0]));
 }
@@ -1221,8 +1310,7 @@ database::get_roster_links(std::map<revi
 {
   links.clear();
   results res;
-  string query = ("SELECT rev_id, roster_id FROM revision_roster");
-  fetch(res, 2, any_rows, query.c_str());
+  fetch(res, 2, any_rows, query("SELECT rev_id, roster_id FROM revision_roster"));
   for (size_t i = 0; i < res.size(); ++i)
     {
       links.insert(make_pair(revision_id(res[i][0]),
@@ -1299,7 +1387,7 @@ database::get_revision_ancestry(std::mul
   results res;
   graph.clear();
   fetch(res, 2, any_rows,
-        "SELECT parent,child FROM revision_ancestry");
+        query("SELECT parent,child FROM revision_ancestry"));
   for (size_t i = 0; i < res.size(); ++i)
     graph.insert(std::make_pair(revision_id(res[i][0]),
                                 revision_id(res[i][1])));
@@ -1313,8 +1401,8 @@ database::get_revision_parents(revision_
   results res;
   parents.clear();
   fetch(res, one_col, any_rows,
-        "SELECT parent FROM revision_ancestry WHERE child = ?",
-        id.inner()().c_str());
+        query("SELECT parent FROM revision_ancestry WHERE child = ?")
+        % text(id.inner()()));
   for (size_t i = 0; i < res.size(); ++i)
     parents.insert(revision_id(res[i][0]));
 }
@@ -1326,8 +1414,8 @@ database::get_revision_children(revision
   results res;
   children.clear();
   fetch(res, one_col, any_rows,
-        "SELECT child FROM revision_ancestry WHERE parent = ?",
-        id.inner()().c_str());
+        query("SELECT child FROM revision_ancestry WHERE parent = ?")
+        % text(id.inner()()));
   for (size_t i = 0; i < res.size(); ++i)
     children.insert(revision_id(res[i][0]));
 }
@@ -1357,8 +1445,8 @@ database::get_revision(revision_id const
   I(!null_id(id));
   results res;
   fetch(res, one_col, one_row,
-        "SELECT data FROM revisions WHERE id = ?",
-        id.inner()().c_str());
+        query("SELECT data FROM revisions WHERE id = ?")
+        % text(id.inner()()));

   base64<gzip<data> > rdat_packed;
   rdat_packed = base64<gzip<data> >(res[0][0]);
@@ -1541,16 +1629,16 @@ database::put_revision(revision_id const
   base64<gzip<data> > d_packed;
   pack(d.inner(), d_packed);

-  execute("INSERT INTO revisions VALUES(?, ?)",
-          new_id.inner()().c_str(),
-          d_packed().c_str());
+  execute(query("INSERT INTO revisions VALUES(?, ?)")
+          % text(new_id.inner()())
+          % text(d_packed()));

   for (edge_map::const_iterator e = rev.edges.begin();
        e != rev.edges.end(); ++e)
     {
-      execute("INSERT INTO revision_ancestry VALUES(?, ?)",
-              edge_old_revision(e).inner()().c_str(),
-              new_id.inner()().c_str());
+      execute(query("INSERT INTO revision_ancestry VALUES(?, ?)")
+              % text(edge_old_revision(e).inner()())
+              % text(new_id.inner()()));
     }

   // TODO: some different version?
@@ -1576,16 +1664,16 @@ database::delete_existing_revs_and_certs
 void
 database::delete_existing_revs_and_certs()
 {
-  execute("DELETE FROM revisions");
-  execute("DELETE FROM revision_ancestry");
-  execute("DELETE FROM revision_certs");
+  execute(query("DELETE FROM revisions"));
+  execute(query("DELETE FROM revision_ancestry"));
+  execute(query("DELETE FROM revision_certs"));
 }

 void
 database::delete_existing_manifests()
 {
-  execute("DELETE FROM manifests");
-  execute("DELETE FROM manifest_deltas");
+  execute(query("DELETE FROM manifests"));
+  execute(query("DELETE FROM manifest_deltas"));
 }

 /// Deletes one revision from the local database.
@@ -1605,9 +1693,14 @@ database::delete_existing_rev_and_certs(
   L(FL("Killing revision %s locally\n") % rid);

   // Kill the certs, ancestry, and rev itself.
-  execute("DELETE from revision_certs WHERE id = ?",rid.inner()().c_str());
-  execute("DELETE from revision_ancestry WHERE child = ?", rid.inner()().c_str());
-  execute("DELETE from revisions WHERE id = ?",rid.inner()().c_str());
+  execute(query("DELETE from revision_certs WHERE id = ?")
+          % text(rid.inner()()));
+
+  execute(query("DELETE from revision_ancestry WHERE child = ?")
+          % text(rid.inner()()));
+
+  execute(query("DELETE from revisions WHERE id = ?")
+          % text(rid.inner()()));

   // Find the associated roster and count the number of links to it
   hexenc<id> roster_id;
@@ -1615,15 +1708,16 @@ database::delete_existing_rev_and_certs(
   get_roster_id_for_revision(rid, roster_id);
   {
     results res;
-    string query = ("SELECT rev_id, roster_id FROM revision_roster "
-                    "WHERE roster_id = ?");
-    fetch(res, 2, any_rows, query.c_str(), roster_id().c_str());
+    fetch(res, 2, any_rows,
+          query("SELECT rev_id, roster_id FROM revision_roster "
+                "WHERE roster_id = ?") % text(roster_id()));
     I(res.size() > 0);
     link_count = res.size();
   }

   // Delete our link.
-  execute("DELETE from revision_roster WHERE rev_id = ?", rid.inner()().c_str());
+  execute(query("DELETE from revision_roster WHERE rev_id = ?")
+          % text(rid.inner()()));

   // If that was the last link to the roster, kill the roster too.
   if (link_count == 1)
@@ -1639,10 +1733,10 @@ database::delete_branch_named(cert_value
   base64<cert_value> encoded;
   encode_base64(branch, encoded);
   L(FL("Deleting all references to branch %s\n") % branch);
-  execute("DELETE FROM revision_certs WHERE name='branch' AND value =?",
-          encoded().c_str());
-  execute("DELETE FROM branch_epochs WHERE branch=?",
-          encoded().c_str());
+  execute(query("DELETE FROM revision_certs WHERE name='branch' AND value =?")
+          % text(encoded()));
+  execute(query("DELETE FROM branch_epochs WHERE branch=?")
+          % text(encoded()));
 }

 /// Deletes all certs referring to a particular tag.
@@ -1652,8 +1746,8 @@ database::delete_tag_named(cert_value co
   base64<cert_value> encoded;
   encode_base64(tag, encoded);
   L(FL("Deleting all references to tag %s\n") % tag);
-  execute("DELETE FROM revision_certs WHERE name='tag' AND value =?",
-          encoded().c_str());
+  execute(query("DELETE FROM revision_certs WHERE name='tag' AND value =?")
+          % text(encoded()));
 }

 // crypto key management
@@ -1667,11 +1761,11 @@ database::get_key_ids(string const & pat

   if (pattern != "")
     fetch(res, one_col, any_rows,
-          "SELECT id FROM public_keys WHERE id GLOB ?",
-          pattern.c_str());
+          query("SELECT id FROM public_keys WHERE id GLOB ?")
+          % text(pattern));
   else
     fetch(res, one_col, any_rows,
-          "SELECT id FROM public_keys");
+          query("SELECT id FROM public_keys"));

   for (size_t i = 0; i < res.size(); ++i)
     pubkeys.push_back(res[i][0]);
@@ -1682,8 +1776,7 @@ database::get_keys(string const & table,
 {
   keys.clear();
   results res;
-  string query = "SELECT id FROM " + table;
-  fetch(res, one_col, any_rows, query.c_str());
+  fetch(res, one_col, any_rows, query("SELECT id FROM " + table));
   for (size_t i = 0; i < res.size(); ++i)
     keys.push_back(res[i][0]);
 }
@@ -1699,8 +1792,8 @@ database::public_key_exists(hexenc<id> c
 {
   results res;
   fetch(res, one_col, any_rows,
-        "SELECT id FROM public_keys WHERE hash = ?",
-        hash().c_str());
+        query("SELECT id FROM public_keys WHERE hash = ?")
+        % text(hash()));
   I((res.size() == 1) || (res.size() == 0));
   if (res.size() == 1)
     return true;
@@ -1712,8 +1805,8 @@ database::public_key_exists(rsa_keypair_
 {
   results res;
   fetch(res, one_col, any_rows,
-        "SELECT id FROM public_keys WHERE id = ?",
-        id().c_str());
+        query("SELECT id FROM public_keys WHERE id = ?")
+        % text(id()));
   I((res.size() == 1) || (res.size() == 0));
   if (res.size() == 1)
     return true;
@@ -1727,8 +1820,8 @@ database::get_pubkey(hexenc<id> const &
 {
   results res;
   fetch(res, 2, one_row,
-        "SELECT id, keydata FROM public_keys WHERE hash = ?",
-        hash().c_str());
+        query("SELECT id, keydata FROM public_keys WHERE hash = ?")
+        % text(hash()));
   id = res[0][0];
   pub_encoded = res[0][1];
 }
@@ -1739,8 +1832,8 @@ database::get_key(rsa_keypair_id const &
 {
   results res;
   fetch(res, one_col, one_row,
-        "SELECT keydata FROM public_keys WHERE id = ?",
-        pub_id().c_str());
+        query("SELECT keydata FROM public_keys WHERE id = ?")
+        % text(pub_id()));
   pub_encoded = res[0][0];
 }

@@ -1753,15 +1846,17 @@ database::put_key(rsa_keypair_id const &
   I(!public_key_exists(thash));
   E(!public_key_exists(pub_id),
     F("another key with name '%s' already exists") % pub_id);
-  execute("INSERT INTO public_keys VALUES(?, ?, ?)",
-          thash().c_str(), pub_id().c_str(), pub_encoded().c_str());
+  execute(query("INSERT INTO public_keys VALUES(?, ?, ?)")
+          % text(thash())
+          % text(pub_id())
+          % text(pub_encoded()));
 }

 void
 database::delete_public_key(rsa_keypair_id const & pub_id)
 {
-  execute("DELETE FROM public_keys WHERE id = ?",
-          pub_id().c_str());
+  execute(query("DELETE FROM public_keys WHERE id = ?")
+          % text(pub_id()));
 }

 // cert management
@@ -1771,19 +1866,19 @@ database::cert_exists(cert const & t,
                       string const & table)
 {
   results res;
-  string query =
-    "SELECT id FROM " + table + " WHERE id = ? "
-    "AND name = ? "
-    "AND value = ? "
-    "AND keypair = ? "
-    "AND signature = ?";
+  query q = query("SELECT id FROM " + table + " WHERE id = ? "
+                  "AND name = ? "
+                  "AND value = ? "
+                  "AND keypair = ? "
+                  "AND signature = ?")
+    % text(t.ident())
+    % text(t.name())
+    % text(t.value())
+    % text(t.key())
+    % text(t.sig());

-  fetch(res, 1, any_rows, query.c_str(),
-        t.ident().c_str(),
-        t.name().c_str(),
-        t.value().c_str(),
-        t.key().c_str(),
-        t.sig().c_str());
+  fetch(res, 1, any_rows, q);
+
   I(res.size() == 0 || res.size() == 1);
   return res.size() == 1;
 }
@@ -1797,13 +1892,13 @@ database::put_cert(cert const & t,

   string insert = "INSERT INTO " + table + " VALUES(?, ?, ?, ?, ?, ?)";

-  execute(insert.c_str(),
-          thash().c_s