The unified diff between revisions [0010ca1a..] and [8846a6b8..] is displayed below. It can also be downloaded as a raw diff.

#
#
# delete "sqlite/config.h"
#
# delete "sqlite/os_unix.h"
#
# delete "sqlite/os_win.h"
#
# add_file "sqlite/os.c"
#  content [59f05de8c5777c34876607114a2fbe55ae578235]
#
# add_file "tests/t_commit_validate.at"
#  content [2d6e25f801aa61c96d7c063bd1c7e7d1224b55c3]
#
# add_file "tests/t_ls_changed.at"
#  content [1fc5edbd5d67ff60df161f44864efced6c08670a]
#
# patch "AUTHORS"
#  from [be2883d201fdd61b63582334f52312a640e012a1]
#    to [66d90072b355ef6e2b66f56c0dfa00de1565dc98]
#
# patch "ChangeLog"
#  from [259eb3350818f249dada218ce2ffb43a71a4971e]
#    to [cf7af27d617c006696a08efcd68a9bd9fd401a7a]
#
# patch "Makefile.am"
#  from [1086b6f0ea35d96bdd46455c7d97c5934fb77e5a]
#    to [d4ad27fc9fa7d8d2b82c512a67adbd237b7fcca1]
#
# patch "NEWS"
#  from [4109c836eec71571ad7f8cca182bd780771e13a6]
#    to [ef3bd56b18d1386927ffb58c2bb20ef4070712dd]
#
# patch "automate.cc"
#  from [68d579fc833b4a0ec52a0c8e5b130af7517ab7ed]
#    to [d86c347f85ee7ce7a59f699c8e51feaa1fa57bfb]
#
# patch "commands.cc"
#  from [2f64f75b9afb4a86bac84bcc6cacaba86866be2b]
#    to [dbf77e72442ab85b8b4390b8fb1316ca8145d507]
#
# patch "configure.ac"
#  from [3db7a41acaf0a99ca96e19c12c2f96e5eaea45b1]
#    to [0bbe01c42c3d25e72176fda5c606e1e4c3ec3e6d]
#
# patch "cset.cc"
#  from [7596f4bf16c8591f073c6c18df29668474c499c8]
#    to [ac5f2a4848a4c5661496ec4f5af706de1fb6a454]
#
# patch "cset.hh"
#  from [077036ac38849e3b74dcdce595fdb5ee2762f282]
#    to [f2d58803ea89b51b5029a349c30acc3c6c3cf580]
#
# patch "database.cc"
#  from [bd5cc9b0df537b2aab8520160761bbe8605ec6d8]
#    to [4c830b7b46f89cdc384cae08e54bfeac62335eb7]
#
# patch "database.hh"
#  from [dd9695e867200e94e2d10b9e9bd74bd423c0ac10]
#    to [4ea836110ca59b61655724a3eb2dcd1c9a5a34cb]
#
# patch "debian/changelog"
#  from [016bdc7c1c824ca135635243a5b2b8fc98b65428]
#    to [75eaeb6f09dbe2ab13fc811a92a0abfaa2780363]
#
# patch "enumerator.cc"
#  from [31a5efd62fdaaada910d5b15b7fe31648a0ad17a]
#    to [33a06c9ebfb955efb197f5eafb2e8d9beaee8a1d]
#
# patch "enumerator.hh"
#  from [b2662b4dea76a8d832663ed48342920248449f70]
#    to [1880dae7a6147b0ddd8064bfc4679334b520aeab]
#
# patch "lua.cc"
#  from [78722201fdc513bfee248ce6b30b1b1ae593250e]
#    to [8284c5010bee38f15141c3e60eeeeb7c288ee20c]
#
# patch "lua.hh"
#  from [0d1c63ed6c7b36665ad92780ed88afa1edff3194]
#    to [a682ec35ee70ef30fd080616f8f21d8ef2a1a6ea]
#
# patch "monotone.1"
#  from [f7f22f2d6e5bebf0d526d15651869113fe9c6ead]
#    to [6a829e2dda9696f2b3949e266a24ed560c33bf71]
#
# patch "monotone.spec"
#  from [31854929161128485e8ca1cb1e16d2b0d2bab8a1]
#    to [b00e73feb017514c7dd84e700e9dd539585ecf57]
#
# patch "monotone.texi"
#  from [59eb4911052af4a3a1743caf529acfe14dad092b]
#    to [355d07f84caee318dd69f37ff6274842ca2d2c70]
#
# patch "netsync.cc"
#  from [8c4cbdeb2898f87f5f74457e7148a4cc0afde674]
#    to [8d4dc8e530af4fb0a6cca30ca600dd74f905b1aa]
#
# patch "paths.cc"
#  from [227025632497d6849fc4946feeded4d7980f7008]
#    to [c13e9b0c44a37a70e6c7e6d5c8b8869e363ccab9]
#
# patch "paths.hh"
#  from [727b9cfba7c9d150dfd7144d0d38744beb8dda7f]
#    to [054a74b153b42a34140f2776f468bf79c844fa64]
#
# patch "po/sv.po"
#  from [e347bfacd741eafd3202feda54aa54e24b3a7bfb]
#    to [754e90276863a8b1a53cad17c575bb93ffcdd5b4]
#
# patch "revision.cc"
#  from [8cda143f1b14ea5d67e40c9dc325b5ec6f2fd1a7]
#    to [ca74a6db1747f8c8cf213167ec226df0e30c0b84]
#
# patch "revision.hh"
#  from [5e3733994a6c92c4f15f0d78aa26669458443d46]
#    to [ee41c7c61df986d551aebd2445a6232150a9c8b0]
#
# patch "roster.cc"
#  from [e1c0e9fe71f80b5bd457801d3cf4eb3240872c22]
#    to [4547e72b235da17471b9781c3416e7eac2904027]
#
# patch "roster.hh"
#  from [6811b78a4a979e2146ba44e6c3a3023dec0b4c53]
#    to [0f6d33d17d3d1c804be21f2e8025d86e47f087f9]
#
# patch "sanity.cc"
#  from [7b7dd4d379a294fa3d79c0bb0f39dde2ae48d315]
#    to [b31758131617cacbc373a95c25cab49e15ffedc3]
#
# patch "sanity.hh"
#  from [ae8401c8dc9afd96ed17ebdb5362fa3cf735d405]
#    to [3d91a4abd21f2af06ea78724a922b3b52fa0838f]
#
# patch "sqlite/alter.c"
#  from [26d755f2143719dd3f5b8aaf6cbe3c7f95332528]
#    to [faf98b04050d674d06df21bcf23ded5bbff7b5b7]
#
# patch "sqlite/analyze.c"
#  from [21a4cd125bedd3cb15857595c45c2a49c0556d26]
#    to [7d2b7ab9a9c2fd6e55700f69064dfdd3e36d7a8a]
#
# patch "sqlite/attach.c"
#  from [4b21689700a72ae281fa85dbaff06b2a62bd49ee]
#    to [d73a3505de3fb9e373d0a158978116c4212031d0]
#
# patch "sqlite/auth.c"
#  from [31e2304bef67f44d635655f44234387ea7d21454]
#    to [9ae84d2d94eb96195e04515715e08e85963e96c2]
#
# patch "sqlite/btree.c"
#  from [236126155d5607da945d33514218cbec762d887e]
#    to [f45f57e6cbd3b3db947cdd699db64e5215d20b2a]
#
# patch "sqlite/btree.h"
#  from [1ed561263ca0e335bc3e81d761c9d5ff8c22f61e]
#    to [5663c4f43e8521546ccebc8fc95acb013b8f3184]
#
# patch "sqlite/build.c"
#  from [aa9591839c00731370c1ba53a9c0045f70d764c4]
#    to [feaa61e769d7887ffeaa060d746638c7b3e994ef]
#
# patch "sqlite/callback.c"
#  from [9a1162c8f9dae9fad6d548339669aacb5f6cf76b]
#    to [1bf497306c32229114f826707054df7ebe10abf2]
#
# patch "sqlite/complete.c"
#  from [641713ef01657f74037840abb9c0a4552c07b0d0]
#    to [7d1a44be8f37de125fcafd3d3a018690b3799675]
#
# patch "sqlite/date.c"
#  from [7444b0900a28da77e57e3337a636873cff0ae940]
#    to [c70a4f88e495ae2c523f6ef3848c26a021c96de8]
#
# patch "sqlite/delete.c"
#  from [29dac493f4d83b05f91233b116827c133bcdab72]
#    to [56ab34c3a384caa5d5ea06f5739944957e2e4213]
#
# patch "sqlite/expr.c"
#  from [bb2cf5d5b065eaa23d5ae2620f6de0568768147d]
#    to [1149c3380bfce27703f5e9bec7dfb8e51baaf9d9]
#
# patch "sqlite/func.c"
#  from [f63d417248808ff2632a3b576536abffcc21d858]
#    to [96b26601c092b7b43a13e440e3f988b32a385f6a]
#
# patch "sqlite/hash.c"
#  from [2b1b13f7400e179631c83a1be0c664608c8f021f]
#    to [8747cf51d12de46512880dfcf1b68b4e24072863]
#
# patch "sqlite/insert.c"
#  from [1f51566d7cf4b243a2792f5fda37343d6e9377fa]
#    to [7e931b7f06afbcefcbbaab175c02eff8268db33f]
#
# patch "sqlite/keywordhash.h"
#  from [32380a4bfe997d18e3ecd5b85fd948cf7b52aec8]
#    to [53d6b6630b71e2ec402f2c2ff85e3f7e8e88a3cc]
#
# patch "sqlite/legacy.c"
#  from [d58ea507bce885298a2c8c3cbb0f4bff5d47830b]
#    to [86b669707b3cefd570e34154e2f6457547d1df4f]
#
# patch "sqlite/main.c"
#  from [422014201f22aa17b96c76650163178a91a825af]
#    to [2693776249865dc69b97904205638e84a34a059c]
#
# patch "sqlite/opcodes.c"
#  from [9855bca95408a841cfbeb92135596bc18d7c034a]
#    to [1c881baf37c8d67fb9a9a9eb76ba01d98d8a3fd2]
#
# patch "sqlite/opcodes.h"
#  from [9918b6dfce41b2465940c5f6631afd418584dfb3]
#    to [77c3b5ca782504a8d2c92e86839664f74d38fcca]
#
# patch "sqlite/os.h"
#  from [c51f2747f7bd1840447e2c7d26db749604626814]
#    to [93035a0e3b9dd05cdd0aaef32ea28ca28e02fe78]
#
# patch "sqlite/os_common.h"
#  from [1ff88c7e7e6bd3e5f85443106c91cc26a4f8600b]
#    to [061fba8511a656b118551424f64e366ad0d4cb3b]
#
# patch "sqlite/os_unix.c"
#  from [407dd07818d13807c396acf3f7570af81cbb666c]
#    to [73c5e722a661ed98d8919f204911e4e34e51fa41]
#
# patch "sqlite/os_win.c"
#  from [fbccc85e7011174068c27d54256746321a1f0059]
#    to [566bf7b41b72556fd7dca390bceaa2769dc395e9]
#
# patch "sqlite/pager.c"
#  from [b74ebe5aca00ad5f831e19ae17193fe7b83a7ddf]
#    to [b5b380ea7a36f84e50c3adc1a414820a0eb3baa6]
#
# patch "sqlite/pager.h"
#  from [e7b41ce8e7b5f629d456708b7ad9a8c8ede37140]
#    to [e0acb095b3ad0bca48f2ab00c87346665643f64f]
#
# patch "sqlite/parse.c"
#  from [13d6d5853fcaddd632125185f51a4705558950fb]
#    to [a31a0cb5fd9be3781389e4b2784b197d03226dc4]
#
# patch "sqlite/parse.h"
#  from [c3a865a73a9e4988142a3008f99d3cc91fdf77b4]
#    to [20eb1ccb1fa5d4e4493dd0ca916ad74b515eefcf]
#
# patch "sqlite/pragma.c"
#  from [126149668aa7086e86cfa3e32c8523513c19dd63]
#    to [4496cc77dc35824e1c978c3d1413b8a5a4c777d3]
#
# patch "sqlite/prepare.c"
#  from [fc098db25d2a121affb08686cf04833fd50452d4]
#    to [40ae23c8aeb641dc7b9bb271eb5e295b815154a7]
#
# patch "sqlite/printf.c"
#  from [bd421c1ad5e01013c89af63c60eab02852ccd15e]
#    to [c7d6ad9efb71c466305297a448308f467b6e2b6e]
#
# patch "sqlite/random.c"
#  from [90adff4e73a3b249eb4f1fc2a6ff9cf78c7233a4]
#    to [d40f8d356cecbd351ccfab6eaedd7ec1b54f5261]
#
# patch "sqlite/select.c"
#  from [034c7f7447b8711f85bef578a437ea12ca7cac53]
#    to [daee9b20702ba51cf3807fc1b130edd8846e3e48]
#
# patch "sqlite/sqlite3.h"
#  from [44d8c045175afc3da51063660eab529d9ccbd07a]
#    to [6b9ed927e0a9a3c6051a372929c97c2ac87ab314]
#
# patch "sqlite/sqliteInt.h"
#  from [0606b9cc31efabadf5b3d40478c193fa89fa6662]
#    to [0121298397ac14eb468ab1ba9d488ac7ed7d88a1]
#
# patch "sqlite/table.c"
#  from [25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9]
#    to [486dcfce532685b53b5a2b5da8bba0ded6fb2316]
#
# patch "sqlite/tokenize.c"
#  from [e1faf5637f3f4f90933785a0ecf64595f3ac3530]
#    to [9ae9a59238eb97fbc61baea280563b91100518fb]
#
# patch "sqlite/trigger.c"
#  from [f51dec15921629591cb98bf2e350018e268b109a]
#    to [4d3644cbd16959b568c95ae73493402be8021b08]
#
# patch "sqlite/update.c"
#  from [ac506fb7400158f826ec6c3a0dbe65e7ed3928d5]
#    to [14be4ba2f438919b4217085c02feff569e6cf1f2]
#
# patch "sqlite/utf.c"
#  from [bda5eb85039ef16f2d17004c1e18c96e1ab0a80c]
#    to [1199766bbb0157931a83aa6eede6b6381177be64]
#
# patch "sqlite/util.c"
#  from [55caaffbb2716f9928ab452d20f3e9cbbeab872d]
#    to [82ee598519b8193184bdeab06b51a4ffa05ad60b]
#
# patch "sqlite/vacuum.c"
#  from [829d9e1a6d7c094b80e0899686670932eafd768c]
#    to [3865673cc66acd0717ecd517f6b8fdb2a5e7924b]
#
# patch "sqlite/vdbe.c"
#  from [3f1adcf4535dd35e4244d19d332fb6b515491c0d]
#    to [fee677e05236e483d6c75d1d4229955fc1b89193]
#
# patch "sqlite/vdbeInt.h"
#  from [7bedbb9553a10e86b53f75d99e197f3f00a732bf]
#    to [eb3f86ab08ef11635bc78eb88c3ff13f923c233b]
#
# patch "sqlite/vdbeapi.c"
#  from [85bbe1d0243a89655433d60711b4bd71979b59cd]
#    to [dcb2636f49b4807e34960d52a2fc257b3a751140]
#
# patch "sqlite/vdbeaux.c"
#  from [2b728d82cf2095386a90051b66e7faf1a143f27d]
#    to [9bf50cdb6a6c40b8c06ca9a8d87cf90120a16797]
#
# patch "sqlite/vdbemem.c"
#  from [ff426ff6e72aa3f0300a56ec8c7f18099be96b43]
#    to [2034e93b32c14bda6e306bb54e3a8e930b963027]
#
# patch "sqlite/where.c"
#  from [3ed72ca029b3010a76e3a41b7b02ec1bdf849f00]
#    to [8409e00fa2cb5fce873b4c911165cfed097e9c49]
#
# patch "std_hooks.lua"
#  from [1740ad529749fadac6c115b8373ca26e9f932ad6]
#    to [8f01e338e8056c62de500694210fd22dacac6122]
#
# patch "tests/t_add_owndb.at"
#  from [1a63fac0aa5c37fb17934cdb1cd79062e4c284c4]
#    to [74dcb2ff61d7a8cb9c787ddabcbd74aa10ea2eb8]
#
# patch "tests/t_cvsimport.at"
#  from [42e05f340ace04dd4bfdc4e948ab651669f8d2b8]
#    to [72f1079d6cb4ac5810d17e8ecf709e97cec1460b]
#
# patch "tests/t_cvsimport3.at"
#  from [7a5e68124be1da212a3b89c64f098446891952e2]
#    to [f986d11da315b23a88364d9ed55b2e702fc21dc5]
#
# patch "tests/t_cvsimport_deleted_invar.at"
#  from [f6cef14b364d39403eaf99f2a18ad255047f4ca7]
#    to [c01438bad0867f343040ad9c9f76f0f6803957a0]
#
# patch "tests/t_cvsimport_manifest_cycle.at"
#  from [690f76368733c0e909bde2a46f8edea144f7569b]
#    to [367b143477d97f31b5fe948cbc3b3b4960424aac]
#
# patch "tests/t_cvsimport_samelog.at"
#  from [515ea15748990c4be6f61a9a811266b58fb331e7]
#    to [ad82fe334cab4c3cf15409c91498ab99a413cf96]
#
# patch "tests/t_database_check_normalized.at"
#  from [a226dd38826693885d4b9209528dac5fced42d6f]
#    to [903b3a090390261102a713b0339e99322d728f16]
#
# patch "tests/t_diff_outside_workspace.at"
#  from [28c8bf8ea6fc2088a754308386f6ab936506dc77]
#    to [91e929a8e18167fc073b033b782d50c04d0dabad]
#
# patch "tests/t_drop_missing.at"
#  from [b57855a8b89e7f7c8c7cab3ae9fdf660bc4c0111]
#    to [7d7b49bf0e6aee2bb8a7bf96202f464367673274]
#
# patch "tests/t_mt_ignore.at"
#  from [70d452af35a31d73ed28834e8f710d7a2220b140]
#    to [979d32a72c8573f493f549ecf34039562014ca05]
#
# patch "tests/t_netsync_permissions.at"
#  from [a02747cf46bece412d85d63694356704a482c8ff]
#    to [9a8356b76404d50e044f74e5f59d6e055ad64d1e]
#
# patch "tests/t_rename.at"
#  from [b95e23675e65c4c3c9bd2b3490d08ce117b11836]
#    to [f9830fde9e91d2b2c97e3ec7d4c423d3e12d2726]
#
# patch "tests/t_singlecvs.at"
#  from [d98391bad7a3290996b9a4354e173ccd3277b001]
#    to [32bf2cf13d8cb1e5e93e04a979d2202f3172a24a]
#
# patch "testsuite.at"
#  from [2cd96ae551245bab68f4982936338bade0beef18]
#    to [781e96c61a849b6f628e1aa691189b64f4d1bf08]
#
# patch "vocab.cc"
#  from [084a7a4538c3e0b062b901d0ba891fd6b66cfdfe]
#    to [c3824a3441b679e4e8c2c8f29fa412bcf2d2d1c3]
#
# patch "vocab.hh"
#  from [596953ad6326b68846702a85c22977f434e760e9]
#    to [018407f5255129c875a8ccdfaae93c2645d1ac05]
#
# patch "win32/monotone.iss"
#  from [77fa8d3df108c2bad33e0f51d5770f3642b258db]
#    to [48ab358ef0443f95fd1db2d14767914ffb6cbe60]
#
# patch "work.cc"
#  from [974c1d8a5295fd7317898e60abc636fdcc82ba65]
#    to [265e6d058be85a58414c208164fbe9592b1d3e74]
#
============================================================
--- sqlite/os.c	59f05de8c5777c34876607114a2fbe55ae578235
+++ sqlite/os.c	59f05de8c5777c34876607114a2fbe55ae578235
@@ -0,0 +1,92 @@
+/*
+** 2005 November 29
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+******************************************************************************
+**
+** This file contains OS interface code that is common to all
+** architectures.
+*/
+#define _SQLITE_OS_C_ 1
+#include "sqliteInt.h"
+#include "os.h"
+
+/*
+** The following routines are convenience wrappers around methods
+** of the OsFile object.  This is mostly just syntactic sugar.  All
+** of this would be completely automatic if SQLite were coded using
+** C++ instead of plain old C.
+*/
+int sqlite3OsClose(OsFile **pId){
+  OsFile *id;
+  if( pId!=0 && (id = *pId)!=0 ){
+    return id->pMethod->xClose(pId);
+  }else{
+    return SQLITE_OK;
+  }
+}
+int sqlite3OsOpenDirectory(OsFile *id, const char *zName){
+  return id->pMethod->xOpenDirectory(id, zName);
+}
+int sqlite3OsRead(OsFile *id, void *pBuf, int amt){
+  return id->pMethod->xRead(id, pBuf, amt);
+}
+int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
+  return id->pMethod->xWrite(id, pBuf, amt);
+}
+int sqlite3OsSeek(OsFile *id, i64 offset){
+  return id->pMethod->xSeek(id, offset);
+}
+int sqlite3OsTruncate(OsFile *id, i64 size){
+  return id->pMethod->xTruncate(id, size);
+}
+int sqlite3OsSync(OsFile *id, int fullsync){
+  return id->pMethod->xSync(id, fullsync);
+}
+void sqlite3OsSetFullSync(OsFile *id, int value){
+  id->pMethod->xSetFullSync(id, value);
+}
+#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
+/* This method is currently only used while interactively debugging the
+** pager. More specificly, it can only be used when sqlite3DebugPrintf() is
+** included in the build. */
+int sqlite3OsFileHandle(OsFile *id){
+  return id->pMethod->xFileHandle(id);
+}
+#endif
+int sqlite3OsFileSize(OsFile *id, i64 *pSize){
+  return id->pMethod->xFileSize(id, pSize);
+}
+int sqlite3OsLock(OsFile *id, int lockType){
+  return id->pMethod->xLock(id, lockType);
+}
+int sqlite3OsUnlock(OsFile *id, int lockType){
+  return id->pMethod->xUnlock(id, lockType);
+}
+int sqlite3OsLockState(OsFile *id){
+  return id->pMethod->xLockState(id);
+}
+int sqlite3OsCheckReservedLock(OsFile *id){
+  return id->pMethod->xCheckReservedLock(id);
+}
+
+#ifdef SQLITE_ENABLE_REDEF_IO
+/*
+** A function to return a pointer to the virtual function table.
+** This routine really does not accomplish very much since the
+** virtual function table is a global variable and anybody who
+** can call this function can just as easily access the variable
+** for themselves.  Nevertheless, we include this routine for
+** backwards compatibility with an earlier redefinable I/O
+** interface design.
+*/
+struct sqlite3OsVtbl *sqlite3_os_switch(void){
+  return &sqlite3Os;
+}
+#endif
============================================================
--- tests/t_commit_validate.at	2d6e25f801aa61c96d7c063bd1c7e7d1224b55c3
+++ tests/t_commit_validate.at	2d6e25f801aa61c96d7c063bd1c7e7d1224b55c3
@@ -0,0 +1,31 @@
+# -*- Autoconf -*-
+# vim: tw=0
+
+AT_SETUP([commit validation lua hook])
+
+MONOTONE_SETUP
+
+AT_DATA(commit_validate.lua, [
+function validate_commit_message(message, info)
+  if (not string.find(info, "input.txt")) then
+    return false, "Wrong info message"
+  end
+  if (message == "denyme") then
+    return false, "input.txt"
+  end
+
+  return true, ""
+end
+])
+
+AT_DATA(input.txt, [version 0 of the file
+])
+
+AT_CHECK(MONOTONE add input.txt, [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE --branch=testbranch --rcfile=commit_validate.lua commit -m "denyme", 1, [ignore], [monotone: beginning commit on branch 'testbranch'
+monotone: misuse: log message rejected: input.txt
+])
+AT_CHECK(MONOTONE --branch=testbranch --rcfile=commit_validate.lua commit -m "allowme", 0, ignore, ignore)
+
+AT_CLEANUP
============================================================
--- tests/t_ls_changed.at	1fc5edbd5d67ff60df161f44864efced6c08670a
+++ tests/t_ls_changed.at	1fc5edbd5d67ff60df161f44864efced6c08670a
@@ -0,0 +1,44 @@
+#  -*- Autoconf -*-
+
+AT_SETUP([listing changed files])
+
+MONOTONE_SETUP
+
+AT_DATA(foo, [the foo file
+])
+AT_DATA(bar, [the bar file
+])
+AT_DATA(baz, [the baz file
+])
+AT_CHECK(MONOTONE add foo bar baz, [], [ignore], [ignore])
+AT_CHECK(MONOTONE --branch=testbranch commit --message='First commit', [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE ls changed, [], [stdout], [])
+AT_CHECK(cmp stdout /dev/null, [], [ignore])
+
+AT_CHECK(MONOTONE drop foo, [], [ignore], [ignore])
+AT_CHECK(MONOTONE rename --execute bar bartender, [], [ignore], [ignore])
+AT_CHECK(MONOTONE ls changed, [], [stdout], [])
+AT_CHECK(CANONICALISE(stdout))
+AT_DATA(ls_foobar, [bar
+foo
+])
+AT_CHECK(cmp stdout ls_foobar, [], [ignore])
+AT_CHECK(MONOTONE --branch=testbranch commit --message='Second commit', [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE ls changed, [], [stdout], [])
+AT_CHECK(cmp stdout /dev/null, [], [ignore])
+
+AT_DATA(baz, [the baz file, modified
+])
+AT_CHECK(MONOTONE ls changed, [], [stdout], [])
+AT_CHECK(CANONICALISE(stdout))
+AT_DATA(ls_baz, [baz
+])
+AT_CHECK(cmp stdout ls_baz, [], [ignore])
+AT_CHECK(MONOTONE --branch=testbranch commit --message='Third commit', [], [ignore], [ignore])
+
+AT_CHECK(MONOTONE ls changed, [], [stdout], [])
+AT_CHECK(cmp stdout /dev/null, [], [ignore])
+
+AT_CLEANUP
============================================================
--- AUTHORS	be2883d201fdd61b63582334f52312a640e012a1
+++ AUTHORS	66d90072b355ef6e2b66f56c0dfa00de1565dc98
@@ -72,6 +72,7 @@ contributing authors, including:
   Roland McGrath <roland@redhat.com>
   Daniel Carosone <dan@geek.com.au>
   Vinzenz Feenstra <evilissimo@c-plusplus.de>
+  Blake Kaplan <mrbkap@gmail.com>

 Several people have also contributed to the translation of monotone
 into non-English languages; their work is available in the po/
============================================================
--- ChangeLog	259eb3350818f249dada218ce2ffb43a71a4971e
+++ ChangeLog	cf7af27d617c006696a08efcd68a9bd9fd401a7a
@@ -1,3 +1,167 @@
+2006-02-12  Nathaniel Smith  <njs@pobox.com>
+
+	* netsync.cc (serve_connections): Revert garbage that I
+	accidentally checked in last time...
+
+2006-02-12  Nathaniel Smith  <njs@pobox.com>
+
+	* NEWS: Add things done since last time I did this...
+
+2006-02-12  Matthew Gregan  <kinetik@orcon.net.nz>
+
+	* {cset,paths,revision,roster,sanity,vocab}.{cc,hh}: GCC 4.1
+	compile fixes.
+
+2006-02-11  Richard Levitte  <richard@levitte.org>
+
+	* NEWS: Removed my notice about netsync, as I just noticed it
+	was already mentioned under Bugs:.
+
+2006-02-11  Nathaniel Smith  <njs@pobox.com>
+
+	* configure.ac, debian/changelog, monotone.spec:
+	* win32/monotone.iss: Bump version to 0.26pre2.
+
+2006-02-11  Nathaniel Smith  <njs@pobox.com>
+
+	* NEWS: Add mention of validate_commit_message.
+
+2006-02-11  Blake Kaplan <mrbkap@gmail.com>
+
+	* monotone.texi (Hooks): Added new subsection about validation
+	hooks, and describe validate_commit_message in it.
+
+	* std_hooks.lua (validate_commit_message): Change the second
+	argument to match the documentation.
+
+2006-02-11  Matt Johnston  <matt@ucc.asn.au>
+
+	* database.hh: increase checkpoint batch size from 100 to 1000
+
+2006-02-11  Matt Johnston  <matt@ucc.asn.au>
+
+	* NEWS: Fix rename example.
+
+2006-02-11  Richard Levitte  <richard@levitte.org>
+
+	* NEWS: Update with the netsync change.
+
+2006-02-11  Nathaniel Smith  <njs@pobox.com>
+
+	* NEWS: Draft for 0.26pre2.
+
+2006-02-11  Richard Levitte  <richard@levitte.org>
+
+	* netsync.cc (serve_connections): Enclose more or less everything
+	in a try-catch block to catch if using IPv6 failed, and to try
+	with just IPv4 in that case.  This is important for those who
+	copy a IPv6-enabled binary to a system that doesn't use IPv6.
+
+	* po/sv.po: Adapt translation to the newly changed messages.
+
+2006-02-10  Derek Scherger  <derek@echologic.com>
+
+	* tests/t_drop_missing.at:
+	* tests/t_rename.at:
+	* work.cc (visit_file): attempt to improve a couple of messages;
+	remove some unrequired \n's
+
+2006-02-10  Derek Scherger  <derek@echologic.com>
+
+	* netsync.cc (process_anonymous_cmd, process_auth_cmd): don't
+	report misleading permission denied errors for branches that are
+	not being served
+	* paths.cc (find_and_go_to_workspace): delete stale comment
+	* tests/t_netsync_permissions.at: add test pull of branch that is
+	not served
+
+2006-02-11  Timothy Brownawell  <tbrownaw@gmail.com>
+
+	Adding your db is silly and confusing (what should revert do?).
+	So, it's not allowed any more (the db file is ignored, regardless of
+	what the ignore hook says).
+	* tests/t_add_owndb.at: remove XFAIL, use 'ls known' instead of
+	'ls unknown'
+	* testsuite.at: don't put the db in the ignore hook
+	* database.{cc,hh}: is_dbfile(), check if a path is the database file
+	* work.cc: check is_dbfile where we check the ignore hook when
+	walking the filesystem
+	* tests/t_mt_ignore.at: fix for having the db always be ignored
+
+2006-02-10  Richard Levitte  <richard@levitte.org>
+
+	* monotone.texi (Hooks): Change the example for
+	get_revisions_cert_trust to check "branch" certs instead of
+	"ancestor" ones, and thereby match the effect of the "approve"
+	command.
+
+2006-02-10  Matt Johnston  <matt@ucc.asn.au>
+
+	* commands.cc (CMD(checkout)): wrapping in a transaction makes
+	a big difference.
+
+2006-02-09  Nathaniel Smith  <njs@pobox.com>
+
+	* Makefile.am (SQLITE_SOURCES): Remove header files lost in
+	latest SQLite upstream import.
+
+2006-02-09  Graydon Hoare  <graydon@pobox.com>
+
+	* lua.cc (hook_validate_commit_message): make validated the
+	default.
+
+2006-02-09  Richard Levitte  <richard@levitte.org>
+
+	* tests/t_cvsimport.at, tests/t_cvsimport3.at,
+	tests/t_cvsimport_deleted_invar.at,
+	tests/t_cvsimport_manifest_cycle.at, tests/t_cvsimport_samelog.at,
+	tests/t_singlecvs.at: Changed to cope with the strictness of
+	CVSNT.  It doesn't create a CSVROOT/history file automagically,
+	but CVSROOT/modules is created automatically both by the older CVS
+	and by CVSNT.  You can't check out a group of files using revision
+	numbers with CVSNT.  You MUST stand in the work directory for some
+	command with CVSNT.  Finally, with CVSNT, 'cvs init' generates the
+	following message if you're a normal user, at least on Debian:
+
+	cvs init: Unable to register repository.
+	cvs init: Your login may not have sufficient permissions to modify the
+	cvs init: global server settings.
+	cvs init: Repository /home/levitte/cvsfoo initialised
+
+2006-02-09  Matthew Gregan  <kinetik@orcon.net.nz>
+
+	* testsuite.at: Remove duplicate line.
+
+2006-02-08  Matthew Gregan  <kinetik@orcon.net.nz>
+
+	* sqlite/*: Import SQLite 3.3.3.
+	* Makefile.am: Adjust for new and removed files in import.
+	* NEWS: Make a note of the SQLite database format change.
+	* database.cc (database::load): Rewrite so that we don't need any
+	local changes to SQLite.
+
+2006-02-08  Richard Levitte  <richard@levitte.org>
+
+	* testsuite.at, tests/t_ls_changed.at: News test, for "list
+	changed".
+
+	* monotone.texi, monotone.1: Document "list changed".
+
+	* po/sv.po: Correct translations of changed messages, translate
+	new messages.
+
+	* commands.cc (ls_changed, CMD(list)): Add a new command, "list
+	changed", to list changed files, always sorted in lexical order.
+
+2006-02-06  Blake Kaplan  <mrbkap@gmail.com>
+
+	* commands.cc CMD(commit): Call a new lua hook to validate the commit
+	message. Don't ignore -m "" when it's passed on the command line.
+	* lua.cc, lua.hh: Add a new hook that validates a given commit message
+	and passes in the added files, deleted files, and modified files.
+	* std_hooks.lua: Give a default hook to validate commit messages. This
+	currently disallows empty messages, as monotone currently does.
+
 2006-02-05  Benoît Dejean  <benoit@placenet.org>

 	* ui.cc (tick_write_count::write_ticks): Reverted lexical_cast,
============================================================
--- Makefile.am	1086b6f0ea35d96bdd46455c7d97c5934fb77e5a
+++ Makefile.am	d4ad27fc9fa7d8d2b82c512a67adbd237b7fcca1
@@ -172,7 +172,7 @@ SQLITE_SOURCES = \
         sqlite/build.c sqlite/date.c sqlite/delete.c sqlite/expr.c sqlite/func.c \
         sqlite/hash.c sqlite/insert.c \
         sqlite/legacy.c sqlite/main.c sqlite/opcodes.c \
-        sqlite/os_unix.c sqlite/os_win.c \
+        sqlite/os.c sqlite/os_unix.c sqlite/os_win.c \
         sqlite/pager.c sqlite/parse.c sqlite/pragma.c sqlite/printf.c \
         sqlite/random.c sqlite/select.c sqlite/table.c sqlite/tokenize.c \
         sqlite/trigger.c sqlite/update.c sqlite/utf.c \
@@ -183,8 +183,8 @@ SQLITE_SOURCES = \
 	sqlite/analyze.c sqlite/vdbefifo.c \
 	sqlite/complete.c \
 	\
-        sqlite/btree.h sqlite/config.h sqlite/hash.h sqlite/opcodes.h sqlite/os.h \
-        sqlite/os_common.h sqlite/os_unix.h sqlite/os_win.h \
+        sqlite/btree.h sqlite/hash.h sqlite/opcodes.h sqlite/os.h \
+        sqlite/os_common.h \
         sqlite/parse.h sqlite/sqlite3.h sqlite/sqliteInt.h sqlite/vdbe.h sqlite/vdbeInt.h \
 	sqlite/pager.h

============================================================
--- NEWS	4109c836eec71571ad7f8cca182bd780771e13a6
+++ NEWS	ef3bd56b18d1386927ffb58c2bb20ef4070712dd
@@ -1,3 +1,72 @@
+Sat Feb 11 13:32:51 PST 2006
+
+        0.26pre2 release.  Inching towards 0.26.  If you are using
+        0.25 or earlier, then make sure to read the very important
+        notes for 0.26pre1, below.  In particular, like 0.26pre1, this
+        is a pre-release for testing.  Do not package it.  DO NOT USE
+        THIS RELEASE UNLESS YOU WANT TO BE A DAREDEVIL.
+
+        (Though, in fact, in a month of usage, only one bug has been
+        found in the new history code, and it was both minor and
+        harmless.  It has additionally been fixed.)
+
+        Database changes:
+
+        - SQLite 3.3.3 has been imported.  3.3 introduces a new database
+          format that is not backwards compatible with earlier 3.x releases.
+          New databases will be created using this new format.  Existing
+          databases remain compatible, and are not converted automatically.
+          Existing databases can be converted by performing a database
+          vacuum ('monotone db execute vacuum').
+
+        New features:
+
+        - New hook validate_commit_message -- use to verify that all
+          commit messages meet arbitrary user-defined rules.
+
+        UI improvements:
+
+        - rename (and mv) commands now accept a broader range of
+          syntax:
+            monotone rename foo some_dir
+              -> renames foo to some_dir/foo
+            monotone rename foo bar baz some_dir
+              -> moves foo, bar, and baz to some_dir/foo,
+                 some_dir/bar, and some_dir/baz
+        - Print a warning if it looks like a user has made a quoting
+          mistake on push/pull/sync/serve (windows cmd.exe has
+          confusing rules here).
+        - New command "ls changed".
+        - New option "--next" to log, which displays descendents of
+          the start revision.
+        - Updating to an arbitrary revision now works again (as it did
+          in 0.25 and earlier).  This allows one to, for instance,
+          switch a working copy to another head, or back up to an
+          earlier version, while preserving uncommitted changes.
+        - New option --brief to annotate, gives somewhat more friendly
+          output.
+        - Fixed bug that made ticker output from netsync inaccurate.
+        - In 'log', --no-merges is now the default, use --merges to
+          override.
+        - If the database is in the working copy, then it is always
+          ignored.
+
+        Bugs:
+
+        - 'serve' with no --bind should now work on systems where the
+          C library has IPv6 support, but the kernel does not.
+        - Compile fixes for GCC 4.1 pre-releases.
+
+        Other:
+        - Better detection when users have not run "rosterify", and
+          more helpful suggestions on what to do in this case.
+        - Documentation, translation, error message,
+          etc. improvements.
+        - Updates to contrib/mtbrowse.sh, simple shell-based monotone
+          interface.
+        - Updates to many other contrib/ files, mostly to maintain
+          compatibility with monotone changes.
+
 Sun Jan  8 01:08:56 PST 2006

         0.26pre1 release.  Massive rewrites, released for shakedown.
============================================================
--- automate.cc	68d579fc833b4a0ec52a0c8e5b130af7517ab7ed
+++ automate.cc	d86c347f85ee7ce7a59f699c8e51feaa1fa57bfb
@@ -283,7 +283,7 @@ automate_toposort(std::vector<utf8> args
       revs.insert(rid);
     }
   std::vector<revision_id> sorted;
-  toposort(revs, sorted, app);
+  toposort(revs, sorted, app.db);
   for (std::vector<revision_id>::const_iterator i = sorted.begin();
        i != sorted.end(); ++i)
     output << (*i).inner()() << std::endl;
@@ -329,7 +329,7 @@ automate_ancestry_difference(std::vector
   ancestry_difference(a, bs, ancestors, app);

   std::vector<revision_id> sorted;
-  toposort(ancestors, sorted, app);
+  toposort(ancestors, sorted, app.db);
   for (std::vector<revision_id>::const_iterator i = sorted.begin();
        i != sorted.end(); ++i)
     output << (*i).inner()() << std::endl;
============================================================
--- commands.cc	2f64f75b9afb4a86bac84bcc6cacaba86866be2b
+++ commands.cc	dbf77e72442ab85b8b4390b8fb1316ca8145d507
@@ -1,4 +1,5 @@
 // -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
+// 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>
 // all rights reserved.
 // licensed to the public under the terms of the GNU GPL (>= 2)
@@ -1417,6 +1418,8 @@ CMD(checkout, N_("tree"), N_("[DIRECTORY
   // we have a special case for "checkout .", i.e., to current dir
   bool checkout_dot = false;

+  transaction_guard guard(app.db, false);
+
   if (args.size() > 1 || app.revision_selectors.size() > 1)
     throw usage(name);

@@ -1519,6 +1522,7 @@ CMD(checkout, N_("tree"), N_("[DIRECTORY
   remove_work_cset();
   update_any_attrs(app);
   maybe_update_inodeprints(app);
+  guard.commit();
 }

 ALIAS(co, checkout)
@@ -1736,19 +1740,78 @@ ls_missing (app_state & app, vector<utf8
 }


-CMD(list, N_("informative"),
+struct lt_file_path
+{
+  bool operator()(const file_path &fp1, const file_path &fp2) const
+  {
+    return fp1 < fp2;
+  }
+};
+static void
+ls_changed (app_state & app, vector<utf8> const & args)
+{
+  revision_set rs;
+  revision_id rid;
+  roster_t old_roster, new_roster;
+  data tmp;
+  std::set<file_path, lt_file_path> files;
+
+  app.require_workspace();
+  get_working_revision_and_rosters(app, args, rs, old_roster, new_roster);
+
+  I(rs.edges.size() == 1);
+  cset const & cs = edge_changes(rs.edges.begin());
+
+  for (path_set::const_iterator i = cs.nodes_deleted.begin();
+       i != cs.nodes_deleted.end(); ++i)
+    {
+      if (app.restriction_includes(*i))
+        files.insert(file_path(*i));
+    }
+  for (std::map<split_path, split_path>::const_iterator i = cs.nodes_renamed.begin();
+       i != cs.nodes_renamed.end(); ++i)
+    {
+      if (app.restriction_includes(i->first))
+        files.insert(file_path(i->first));
+    }
+  for (path_set::const_iterator i = cs.dirs_added.begin();
+       i != cs.dirs_added.end(); ++i)
+    {
+      if (app.restriction_includes(*i))
+        files.insert(file_path(*i));
+    }
+  for (std::map<split_path, file_id>::const_iterator i = cs.files_added.begin();
+       i != cs.files_added.end(); ++i)
+    {
+      if (app.restriction_includes(i->first))
+        files.insert(file_path(i->first));
+    }
+  for (std::map<split_path, std::pair<file_id, file_id> >::const_iterator
+         i = cs.deltas_applied.begin(); i != cs.deltas_applied.end(); ++i)
+    {
+      if (app.restriction_includes(i->first))
+        files.insert(file_path(i->first));
+    }
+
+  copy(files.begin(), files.end(),
+       ostream_iterator<const file_path>(cout, "\n"));
+}
+
+
+CMD(list, N_("informative"),
     N_("certs ID\n"
-      "keys [PATTERN]\n"
-      "branches\n"
-      "epochs [BRANCH [...]]\n"
-      "tags\n"
-      "vars [DOMAIN]\n"
-      "known\n"
-      "unknown\n"
-      "ignored\n"
-      "missing"),
-    N_("show database objects, or the current workspace manifest,\n"
-      "or unknown, intentionally ignored, or missing state files"),
+       "keys [PATTERN]\n"
+       "branches\n"
+       "epochs [BRANCH [...]]\n"
+       "tags\n"
+       "vars [DOMAIN]\n"
+       "known\n"
+       "unknown\n"
+       "ignored\n"
+       "missing\n"
+       "changed"),
+    N_("show database objects, or the current workspace manifest, or known,\n"
+       "unknown, intentionally ignored, missing, or changed state files"),
     OPT_DEPTH % OPT_EXCLUDE)
 {
   if (args.size() == 0)
@@ -1777,6 +1840,8 @@ CMD(list, N_("informative"),
     ls_unknown_or_ignored(app, true, removed);
   else if (idx(args, 0)() == "missing")
     ls_missing(app, removed);
+  else if (idx(args, 0)() == "changed")
+    ls_changed(app, removed);
   else
     throw usage(name);
 }
@@ -2126,6 +2191,11 @@ CMD(db, N_("database"),
         build_changesets_from_manifest_ancestry(app);
       else if (idx(args, 0)() == "rosterify")
         build_roster_style_revs_from_manifest_style_revs(app);
+      else if (idx(args, 0)() == "flip")
+        {
+          app.db.make_fwd_deltas("files", "file_deltas");
+          app.db.make_fwd_deltas("rosters", "roster_deltas");
+        }
       else
         throw usage(name);
     }
@@ -2258,12 +2328,12 @@ process_commit_message_args(bool & given
   N(app.message().length() == 0 || app.message_file().length() == 0,
     F("--message and --message-file are mutually exclusive"));

-  if (app.message().length() > 0)
+  if (app.is_explicit_option(OPT_MESSAGE))
     {
       log_message = app.message();
       given = true;
     }
-  else if (app.message_file().length() > 0)
+  else if (app.is_explicit_option(OPT_MSGFILE))
     {
       data dat;
       read_data_for_command_line(app.message_file(), dat);
@@ -2274,7 +2344,6 @@ process_commit_message_args(bool & given
     given = false;
 }

-
 CMD(commit, N_("workspace"), N_("[PATH]..."),
     N_("commit workspace to database"),
     OPT_BRANCH_NAME % OPT_MESSAGE % OPT_MSGFILE % OPT_DATE %
@@ -2340,6 +2409,16 @@ CMD(commit, N_("workspace"), N_("[PATH].
       write_user_log(data(log_message));
     }

+  // If the hook doesn't exist, allow the message to be used.
+  bool message_validated;
+  string reason, new_manifest_text;
+
+  dump(rs, new_manifest_text);
+
+  app.lua.hook_validate_commit_message(log_message, new_manifest_text,
+                                       message_validated, reason);
+  N(message_validated, F("log message rejected: %s\n") % reason);
+
   {
     transaction_guard guard(app.db);
     packet_db_writer dbw(app);
============================================================
--- configure.ac	3db7a41acaf0a99ca96e19c12c2f96e5eaea45b1
+++ configure.ac	0bbe01c42c3d25e72176fda5c606e1e4c3ec3e6d
@@ -2,7 +2,7 @@ AC_PREREQ(2.58)
 # Process this file with autoconf to produce a configure script.

 AC_PREREQ(2.58)
-AC_INIT(monotone, 0.26pre1, monotone-devel@nongnu.org)
+AC_INIT(monotone, 0.26pre2, monotone-devel@nongnu.org)
 AM_INIT_AUTOMAKE(1.7.1)
 AC_CONFIG_SRCDIR([app_state.cc])
 AC_CONFIG_TESTDIR([tests])
============================================================
--- cset.cc	7596f4bf16c8591f073c6c18df29668474c499c8
+++ cset.cc	ac5f2a4848a4c5661496ec4f5af706de1fb6a454
@@ -461,7 +461,7 @@ read_cset(data const & dat, cset & cs)
   I(src.lookahead == EOF);
 }

-void
+template <> void
 dump(cset const & cs, std::string & out)
 {
   data dat;
============================================================
--- cset.hh	077036ac38849e3b74dcdce595fdb5ee2762f282
+++ cset.hh	f2d58803ea89b51b5029a349c30acc3c6c3cf580
@@ -123,7 +123,7 @@ read_cset(data const & dat, cset & cs);
 void
 read_cset(data const & dat, cset & cs);

-void
+template <> void
 dump(cset const & cs, std::string & out);


============================================================
--- database.cc	bd5cc9b0df537b2aab8520160761bbe8605ec6d8
+++ database.cc	4c830b7b46f89cdc384cae08e54bfeac62335eb7
@@ -119,6 +119,16 @@ database::database(system_path const & f
   transaction_level(0)
 {}

+bool
+database::is_dbfile(any_path const & file)
+{
+  system_path fn(file);// why is this needed?
+  bool same = (filename.as_internal() == fn.as_internal());
+  if (same)
+    L(FL("'%s' is the database file") % file);
+  return same;
+}
+
 void
 database::check_schema()
 {
@@ -432,8 +442,8 @@ database::load(istream & in)
 void
 database::load(istream & in)
 {
-  char buf[constants::bufsz];
-  string tmp;
+  string line;
+  string sql_stmt;

   check_filename();

@@ -444,20 +454,16 @@ database::load(istream & in)

   while(in)
     {
-      in.read(buf, constants::bufsz);
-      tmp.append(buf, in.gcount());
+      getline(in, line, ';');
+      sql_stmt += line + ';';

-      const char* last_statement = 0;
-      sqlite3_complete_last(tmp.c_str(), &last_statement);
-      if (last_statement == 0)
-        continue;
-      string::size_type len = last_statement + 1 - tmp.c_str();
-      sqlite3_exec(__sql, tmp.substr(0, len).c_str(), NULL, NULL, NULL);
-      tmp.erase(0, len);
+      if (sqlite3_complete(sql_stmt.c_str()))
+        {
+          sqlite3_exec(__sql, sql_stmt.c_str(), NULL, NULL, NULL);
+          sql_stmt.clear();
+        }
     }

-  if (!tmp.empty())
-    sqlite3_exec(__sql, tmp.c_str(), NULL, NULL, NULL);
   assert_sqlite3_ok(__sql);
 }

@@ -788,6 +794,17 @@ database::delta_exists(hexenc<id> const
   return res.size() > 0;
 }

+bool
+database::delta_exists(hexenc<id> const & ident,
+                       hexenc<id> const & base,
+                       string const & table)
+{
+  results res;
+  query q("SELECT id FROM " + table + " WHERE id = ? AND base = ?");
+  fetch(res, one_col, any_rows, q % text(ident()) % text(base()));
+  return res.size() > 0;
+}
+
 unsigned long
 database::count(string const & table)
 {
@@ -1164,23 +1181,10 @@ database::put_version(hexenc<id> const &
                       string const & data_table,
                       string const & delta_table)
 {
+  // TODO: add an invariant or something perhaps

-  data old_data, new_data;
-  delta reverse_delta;
-
-  get_version(old_id, old_data, data_table, delta_table);
-  patch(old_data, del, new_data);
-  diff(new_data, old_data, reverse_delta);
-
   transaction_guard guard(*this);
-  if (exists(old_id, data_table))
-    {
-      // descendent of a head version replaces the head, therefore old head
-      // must be disposed of
-      drop(old_id, data_table);
-    }
-  put(new_id, new_data, data_table);
-  put_delta(old_id, new_id, reverse_delta, delta_table);
+  put_delta(new_id, old_id, del, delta_table);
   guard.commit();
 }

@@ -1361,6 +1365,29 @@ database::get_file_version(file_id const
   dat = tmp;
 }

+void
+database::get_file_delta(file_id const & id,
+                         file_id const & base,
+                         file_delta & del)
+{
+  if (delta_exists(id.inner(), base.inner(), "file_deltas"))
+    {
+      delta d;
+      get_delta(id.inner(), base.inner(), d, "file_deltas");
+      del = file_delta(d);
+    }
+  else
+    {
+      L(FL("get_file_delta failed for '%s'->'%s'") % base % id);
+      file_data dat, base_dat;
+      get_file_version(base, base_dat);
+      get_file_version(id, dat);
+      delta d;
+      diff(base_dat.inner(), dat.inner(), d);
+      del = file_delta(d);
+    }
+}
+
 void
 database::get_manifest_version(manifest_id const & id,
                                manifest_data & dat)
@@ -1467,6 +1494,81 @@ database::get_revision(revision_id const
   dat = rdat;
 }

+void
+database::make_fwd_deltas(string const & data_table, string const & delta_table)
+{
+  transaction_guard guard(*this);
+
+  set< hexenc<id> > del_bases, all_ids;
+  set< pair< hexenc<id>, hexenc<id> > > dels;
+
+  // id, base
+  get_ids(data_table, all_ids);
+    {
+      results res;
+      fetch(res, 2, any_rows, query("SELECT id, base FROM " + delta_table));
+
+      for (size_t i = 0; i < res.size(); ++i)
+        {
+          all_ids.insert(hexenc<id>(res[i][0]));
+          del_bases.insert(hexenc<id>(res[i][1]));
+          dels.insert( make_pair(res[i][0], res[i][1]) );
+        }
+    }
+
+  set< hexenc<id> > new_bases;
+
+  set_difference(all_ids.begin(), all_ids.end(), del_bases.begin(), del_bases.end(),
+                 inserter(new_bases, new_bases.begin()));
+
+  // create some empty temporary tables
+  string tmp_data_table("tmp_" + data_table);
+  string tmp_delta_table("tmp_" + delta_table);
+
+  execute(query("CREATE TABLE " + tmp_data_table + " AS SELECT * FROM " + data_table + " WHERE 1=0"));
+  execute(query("CREATE TABLE " + tmp_delta_table + " AS SELECT * FROM " + delta_table + " WHERE 1=0"));
+
+  ticker full("full", "g", 1);
+  for ( set< hexenc<id> >::const_iterator i = new_bases.begin(); i != new_bases.end(); i++)
+    {
+      MM(*i);
+      data dat;
+      get_version(*i, dat, data_table, delta_table);
+      put(*i, dat, tmp_data_table);
+      ++full;
+    }
+
+  ticker flips("flips", "f", 1);
+
+  for ( set< pair< hexenc<id>, hexenc<id> > >::const_iterator i = dels.begin(); i != dels.end(); i++)
+    {
+      ++flips;
+      hexenc<id> new_id = i->second;
+      hexenc<id> new_base = i->first;
+      MM(new_id);
+      MM(new_base);
+
+      data base, dat;
+      get_version(new_id, dat, data_table, delta_table);
+      get_version(new_base, base, data_table, delta_table);
+      delta del;
+      diff(base, dat, del);
+
+      put_delta(new_id, new_base, del, tmp_delta_table);
+    }
+
+  execute(query("DELETE FROM " + data_table));
+  execute(query("DELETE FROM " + delta_table));
+
+  execute(query("INSERT INTO " + data_table + " SELECT * FROM " + tmp_data_table));
+  execute(query("INSERT INTO " + delta_table + " SELECT * FROM " + tmp_delta_table));
+
+  execute(query("DROP TABLE " + tmp_data_table));
+  execute(query("DROP TABLE " + tmp_delta_table));
+
+  guard.commit();
+}
+
 void
 database::deltify_revision(revision_id const & rid)
 {
@@ -1559,7 +1661,8 @@ database::put_revision(revision_id const
               % text(new_id.inner()()));
     }

-  deltify_revision(new_id);
+  // TODO: some different version?
+  // deltify_revision(new_id);

   // Phase 4: write the roster data and commit
   put_roster(new_id, ros, mm);
@@ -2624,7 +2727,6 @@ database::get_roster(revision_id const &
   read_roster_and_marking(dat, roster, marks);
 }

-
 void
 database::put_roster(revision_id const & rev_id,
                      roster_t & roster,
@@ -2632,7 +2734,6 @@ database::put_roster(revision_id const &
 {
   MM(rev_id);
   data old_data, new_data;
-  delta reverse_delta;
   hexenc<id> old_id, new_id;

   write_roster_and_marking(roster, marks, new_data);
@@ -2661,13 +2762,13 @@ database::put_roster(revision_id const &
   // Else we have a new roster the database hasn't seen yet; our task is to
   // add it, and deltify all the incoming edges (if they aren't already).

-  put(new_id, new_data, data_table);

   std::set<revision_id> parents;
   get_revision_parents(rev_id, parents);

   // Now do what deltify would do if we bothered (we have the
   // roster written now, so might as well do it here).
+  bool delta_written = false;
   for (std::set<revision_id>::const_iterator i = parents.begin();
        i != parents.end(); ++i)
     {
@@ -2675,14 +2776,18 @@ database::put_roster(revision_id const &
         continue;
       revision_id old_rev = *i;
       get_roster_id_for_revision(old_rev, old_id);
-      if (exists(new_id, data_table))
-        {
-          get_version(old_id, old_data, data_table, delta_table);
-          diff(new_data, old_data, reverse_delta);
-          drop(old_id, data_table);
-          put_delta(old_id, new_id, reverse_delta, delta_table);
-        }
+      get_version(old_id, old_data, data_table, delta_table);
+      delta del;
+      diff(old_data, new_data, del);
+      put_delta(new_id, old_id, del, delta_table);
+      delta_written = true;
+      break;
     }
+
+  // in case the parents were all null/didn't exist
+  if (!delta_written)
+    put(new_id, new_data, data_table);
+
   guard.commit();
 }

============================================================
--- database.hh	dd9695e867200e94e2d10b9e9bd74bd423c0ac10
+++ database.hh	4ea836110ca59b61655724a3eb2dcd1c9a5a34cb
@@ -110,6 +110,9 @@ class database
               std::string const & table);
   bool delta_exists(hexenc<id> const & ident,
                     std::string const & table);
+  bool database::delta_exists(hexenc<id> const & ident,
+                              hexenc<id> const & base,
+                              std::string const & table);

   unsigned long count(std::string const & table);
   unsigned long space_usage(std::string const & table,
@@ -205,6 +208,7 @@ public:
   database(system_path const & file);

   void set_filename(system_path const & file);
+  bool is_dbfile(any_path const & file);
   void initialize();
   void debug(std::string const & sql, std::ostream & out);
   void dump(std::ostream &);
@@ -243,6 +247,11 @@ public:
                         file_id const & new_id,
                         file_delta const & del);

+
+  void get_file_delta(file_id const & id,
+                      file_id const & base,
+                      file_delta & del);
+
   // get plain version if it exists, or reconstruct version
   // from deltas (if they exist).
   void get_manifest_version(manifest_id const & id,
@@ -259,6 +268,8 @@ public:
   void get_revision_manifest(revision_id const & cid,
                              manifest_id & mid);

+  void make_fwd_deltas(std::string const & data_table, std::string const & delta_table);
+
   void deltify_revision(revision_id const & rid);

   void get_revision(revision_id const & id,
@@ -475,7 +486,7 @@ public:
   size_t checkpointed_bytes;
 public:
   transaction_guard(database & d, bool exclusive=true,
-                    size_t checkpoint_batch_size=100,
+                    size_t checkpoint_batch_size=1000,
                     size_t checkpoint_batch_bytes=0xfffff);
   ~transaction_guard();
   void do_checkpoint();
============================================================
--- debian/changelog	016bdc7c1c824ca135635243a5b2b8fc98b65428
+++ debian/changelog	75eaeb6f09dbe2ab13fc811a92a0abfaa2780363
@@ -1,3 +1,9 @@
+monotone (0.26pre2-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Nathaniel Smith <njs@pobox.com>  Sat, 11 Feb 2006 13:36:27 -0800
+
 monotone (0.26pre1-0.1) unstable; urgency=low

   * New upstream release.
============================================================
--- enumerator.cc	31a5efd62fdaaada910d5b15b7fe31648a0ad17a
+++ enumerator.cc	33a06c9ebfb955efb197f5eafb2e8d9beaee8a1d
@@ -27,10 +27,14 @@ revision_enumerator::revision_enumerator
                                          set<revision_id> const & terminal)
   : cb(cb), app(app), terminal_nodes(terminal)
 {
+  // TODO: needs sorting out with the toposort stuff.
+  I(false);
+  /*
   for (set<revision_id>::const_iterator i = initial.begin();
        i != initial.end(); ++i)
     revs.push_back(*i);
-  load_graphs();
+    */
+  load_revs();
 }

 revision_enumerator::revision_enumerator(enumerator_callbacks & cb,
@@ -38,147 +42,188 @@ revision_enumerator::revision_enumerator
   : cb(cb), app(app)
 {
   revision_id root;
-  revs.push_back(root);
-  load_graphs();
+  load_revs();
 }

 void
-revision_enumerator::load_graphs()
+revision_enumerator::load_revs()
 {
-  app.db.get_revision_ancestry(graph);
-  for (multimap<revision_id, revision_id>::const_iterator i = graph.begin();
-       i != graph.end(); ++i)
+  // TODO: this totally disrespects the initial and terminal stuff.
+  // It should probably be integrated into the toposort code itself.
+  vector<revision_id> topo_vec;
+  toposort(topo_vec, app.db);
+  for (vector<revision_id>::const_iterator r = topo_vec.begin();
+       r != topo_vec.end(); r++)
     {
-      inverse_graph.insert(make_pair(i->second, i->first));
+      if (null_id(*r))
+        continue;
+      topo_revs.push_back(*r);
     }
 }

-bool
-revision_enumerator::all_parents_enumerated(revision_id const & child)
-{
-  typedef multimap<revision_id, revision_id>::const_iterator ci;
-  pair<ci,ci> range = inverse_graph.equal_range(child);
-  for (ci i = range.first; i != range.second; ++i)
-    {
-      if (i->first == child)
-        {
-          if (enumerated_nodes.find(i->second) == enumerated_nodes.end())
-            return false;
-        }
-    }
-  return true;
-}
-
 bool
 revision_enumerator::done()
 {
-  return revs.empty() && items.empty();
+  return topo_revs.empty() && items.empty();
 }

-void
-revision_enumerator::step()
+void
+revision_enumerator::process_bunch()
 {
-  while (!done())
+  // we build up a set of files and revs to send
+  vector<revision_id> bunch_revs;
+  set<file_id> bunch_files;
+  multimap<file_id, file_id> bunch_file_deltas;
+
+  // files that should be sent first
+  set<file_id> top_files;
+  // files that will be sent either as full files are as the dst of deltas
+  set<file_id> dst_files;
+
+  while (bunch_revs.size() < 100 && !topo_revs.empty())
     {
-      if (items.empty() && !revs.empty())
+      revision_id r = topo_revs.front();
+      topo_revs.pop_front();
+      if (!cb.process_this_rev(r))
+        continue;
+
+      bunch_revs.push_back(r);
+
+      revision_set rs;
+      app.db.get_revision(r, rs);
+      for (edge_map::const_iterator i = rs.edges.begin();
+           i != rs.edges.end(); ++i)
         {
-          revision_id r = revs.front();
-          revs.pop_front();
-
-          // It's possible we've enumerated this node elsewhere since last
-          // time around. Cull rather than reprocess.
-          if (enumerated_nodes.find(r) != enumerated_nodes.end())
-            continue;
-
-          if (!all_parents_enumerated(r))
+          cset const & cs = edge_changes(i);
+
+          // Queue up all the file-adds
+          for (map<split_path, file_id>::const_iterator fa = cs.files_added.begin();
+               fa != cs.files_added.end(); ++fa)
             {
-              revs.push_back(r);
-              continue;
+              if (cb.queue_this_file(fa->second.inner()))
+                {
+                  dst_files.insert(fa->second);
+                  top_files.insert(fa->second);
+                  bunch_files.insert(fa->second);
+                }
             }
-
-          if (terminal_nodes.find(r) == terminal_nodes.end())
+
+          // Queue up all the file-deltas
+          for (map<split_path, std::pair<file_id, file_id> >::const_iterator fd
+                 = cs.deltas_applied.begin();
+               fd != cs.deltas_applied.end(); ++fd)
             {
-              typedef multimap<revision_id, revision_id>::const_iterator ci;
-              pair<ci,ci> range = graph.equal_range(r);
-              for (ci i = range.first; i != range.second; ++i)
+              if (cb.queue_this_file(fd->second.second.inner()))
                 {
-                  if (i->first == r)
-                    if (enumerated_nodes.find(i->first) == enumerated_nodes.end())
-                      revs.push_back(i->second);
+                  if (dst_files.find(fd->second.first) == dst_files.end())
+                    {
+                      top_files.insert(fd->second.first);
+                    }
+
+                  bunch_file_deltas.insert(make_pair(fd->second.first,
+                                                     fd->second.second));
+                  dst_files.insert(fd->second.second);
                 }
             }
+        }
+    }

-          enumerated_nodes.insert(r);
+  // XXX required?
+  set<file_id> sent_files;

-          if (null_id(r))
-            continue;
+  // now we can queue up the file items in order.
+  for (set<file_id>::const_iterator t = top_files.begin();
+       t != top_files.end(); t++)
+    {
+      if (sent_files.find(*t) != sent_files.end())
+        {
+          L(FL("already sent top_file %s") % t->inner()());
+          continue;
+        }

-          if (cb.process_this_rev(r))
-            {
-              L(FL("revision_enumerator::step expanding "
-                  "contents of rev '%d'\n") % r);
+      L(FL("top_file %s") % t->inner()());

-              revision_set rs;
-              app.db.get_revision(r, rs);
-              for (edge_map::const_iterator i = rs.edges.begin();
-                   i != rs.edges.end(); ++i)
+      if (bunch_files.find(*t) != bunch_files.end())
+        {
+          // a full file to send.
+          enumerator_item item;
+          item.tag = enumerator_item::fdata;
+          item.ident_a = t->inner();
+          items.push_back(item);
+          sent_files.insert(*t);
+          L(FL("send full_file %s") % t->inner()());
+        }
+
+      // XXX: doing BFS now, should try DFS too.
+      deque<file_id> frontier;
+      frontier.push_back(*t);
+
+      while (!frontier.empty())
+        {
+          file_id f = frontier.front();
+          frontier.pop_front();
+
+          L(FL("frontier %s") % f.inner()());
+
+          for (multimap<file_id,file_id>::const_iterator
+               d = bunch_file_deltas.lower_bound(f);
+               d != bunch_file_deltas.upper_bound(f);
+               d++)
+            {
+              if (sent_files.find(d->second) != sent_files.end())
                 {
-                  cset const & cs = edge_changes(i);
-
-                  // Queue up all the file-adds
-                  for (map<split_path, file_id>::const_iterator fa = cs.files_added.begin();
-                       fa != cs.files_added.end(); ++fa)
-                    {
-                      if (cb.queue_this_file(fa->second.inner()))
-                        {
-                          enumerator_item item;
-                          item.tag = enumerator_item::fdata;
-                          item.ident_a = fa->second.inner();
-                          items.push_back(item);
-                        }
-                    }
-
-                  // Queue up all the file-deltas
-                  for (map<split_path, std::pair<file_id, file_id> >::const_iterator fd
-                         = cs.deltas_applied.begin();
-                       fd != cs.deltas_applied.end(); ++fd)
-                    {
-                      if (cb.queue_this_file(fd->second.second.inner()))
-                        {
-                          enumerator_item item;
-                          item.tag = enumerator_item::fdelta;
-                          item.ident_a = fd->second.first.inner();
-                          item.ident_b = fd->second.second.inner();
-                          items.push_back(item);
-                        }
-                    }
+                  L(FL("already sent delta %s") % d->second.inner()());
+                  continue;
                 }
-
-              // Queue up the rev itself
-              {
-                enumerator_item item;
-                item.tag = enumerator_item::rev;
-                item.ident_a = r.inner();
-                items.push_back(item);
-              }
+              sent_files.insert(d->second);
+              frontier.push_back(d->second);
+
+              enumerator_item item;
+              item.tag = enumerator_item::fdelta;
+              item.ident_a = d->first.inner();
+              item.ident_b = d->second.inner();
+              items.push_back(item);
+              L(FL("file_delta %s->%s") % d->first.inner()() % d->second.inner()());
             }
-
-          // Queue up some or all of the rev's certs
-          vector<hexenc<id> > hashes;
-          app.db.get_revision_certs(r, hashes);
-          for (vector<hexenc<id> >::const_iterator i = hashes.begin();
-               i != hashes.end(); ++i)
+        }
+    }
+
+  // and the revs
+  for (vector<revision_id>::const_iterator r = bunch_revs.begin();
+       r != bunch_revs.end(); r++)
+    {
+      enumerator_item item;
+      item.tag = enumerator_item::rev;
+      item.ident_a = r->inner();
+      items.push_back(item);
+
+      // Queue up some or all of the rev's certs
+      vector<hexenc<id> > hashes;
+      app.db.get_revision_certs(*r, hashes);
+      for (vector<hexenc<id> >::const_iterator i = hashes.begin();
+           i != hashes.end(); ++i)
+        {
+          if (cb.queue_this_cert(*i))
             {
-              if (cb.queue_this_cert(*i))
-                {
-                  enumerator_item item;
-                  item.tag = enumerator_item::cert;
-                  item.ident_a = *i;
-                  items.push_back(item);
-                }
+              enumerator_item item;
+              item.tag = enumerator_item::cert;
+              item.ident_a = *i;
+              items.push_back(item);
             }
         }
+    }
+}

+void
+revision_enumerator::step()
+{
+  while (!done())
+    {
+      if (items.empty() && !topo_revs.empty())
+        {
+          process_bunch();
+        }
+
       if (!items.empty())
         {
           L(FL("revision_enumerator::step extracting item\n"));
============================================================
--- enumerator.hh	b2662b4dea76a8d832663ed48342920248449f70
+++ enumerator.hh	1880dae7a6147b0ddd8064bfc4679334b520aeab
@@ -50,10 +50,8 @@ revision_enumerator
   app_state & app;
   std::set<revision_id> terminal_nodes;
   std::set<revision_id> enumerated_nodes;
-  std::deque<revision_id> revs;
   std::deque<enumerator_item> items;
-  std::multimap<revision_id, revision_id> graph;
-  std::multimap<revision_id, revision_id> inverse_graph;
+  std::deque<revision_id> topo_revs;

   revision_enumerator(enumerator_callbacks & cb,
                       app_state & app,
@@ -61,8 +59,8 @@ revision_enumerator
                       std::set<revision_id> const & terminal);
   revision_enumerator(enumerator_callbacks & cb,
                       app_state & app);
-  void load_graphs();
-  bool all_parents_enumerated(revision_id const & child);
+  void load_revs();
+  void process_bunch();
   void step();
   bool done();
 };
============================================================
--- lua.cc	78722201fdc513bfee248ce6b30b1b1ae593250e
+++ lua.cc	8284c5010bee38f15141c3e60eeeeb7c288ee20c
@@ -1,4 +1,5 @@
 // -*- mode: C++; c-file-style: "gnu"; indent-tabs-mode: nil -*-
+// 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>
 // all rights reserved.
 // licensed to the public under the terms of the GNU GPL (>= 2)
@@ -1382,6 +1383,25 @@ lua_hooks::hook_get_linesep_conv(file_pa
   return ll.ok();
 }

+bool
+lua_hooks::hook_validate_commit_message(std::string const & message,
+                                        std::string const & new_manifest_text,
+                                        bool & validated,
+                                        std::string & reason)
+{
+  validated = true;
+  return Lua(st)
+    .func("validate_commit_message")
+    .push_str(message)
+    .push_str(new_manifest_text)
+    .call(2, 2)
+    .extract_str(reason)
+    // XXX When validated, the extra returned string is superfluous.
+    .pop()
+    .extract_bool(validated)
+    .ok();
+}
+
 bool
 lua_hooks::hook_note_commit(revision_id const & new_id,
                             revision_data const & rdat,
============================================================
--- lua.hh	0d1c63ed6c7b36665ad92780ed88afa1edff3194
+++ lua.hh	a682ec35ee70ef30fd080616f8f21d8ef2a1a6ea
@@ -106,6 +106,12 @@ public:
   bool hook_get_linesep_conv(file_path const & p,
                              std::string & db, std::string & ext);

+  // validation hooks
+  bool hook_validate_commit_message(std::string const & message,
+                                    std::string const & new_manifest_text,
+                                    bool & validated,
+                                    std::string & reason);
+
   // notification hooks
   bool hook_note_commit(revision_id const & new_id,
                         revision_data const & rdat,
============================================================
--- monotone.1	f7f22f2d6e5bebf0d526d15651869113fe9c6ead
+++ monotone.1	6a829e2dda9696f2b3949e266a24ed560c33bf71
@@ -77,6 +77,10 @@ List files in revision's manifest, but n
 \fBlist missing \fI[<pathname...]\fP
 List files in revision's manifest, but not in workspace.
 .TP
+\fBlist changed \fI[<pathname...]\fP
+List files in workspace that have changed compared to the base
+revision.
+.TP
 \fBfdata\fP \fI<id>\fP
 Write file data packet to stdout.
 .TP
============================================================
--- monotone.spec	31854929161128485e8ca1cb1e16d2b0d2bab8a1
+++ monotone.spec	b00e73feb017514c7dd84e700e9dd539585ecf57
@@ -1,6 +1,6 @@ Name: monotone
 Summary: monotone is a distributed version control tool
 Name: monotone
-Version: 0.26pre1
+Version: 0.26pre2
 Release: 1
 License: GPL
 Group: Development/Tools
@@ -63,6 +63,9 @@ fi


 %changelog
+* Sat Feb 11 2006 nathaniel smith <njs@pobox.com>
+- 0.26pre2 release
+
 * Thu Jan 8 2006 nathaniel smith <njs@pobox.com>
 - 0.26pre1 release

============================================================
--- monotone.texi	59eb4911052af4a3a1743caf529acfe14dad092b
+++ monotone.texi	355d07f84caee318dd69f37ff6274842ca2d2c70
@@ -2708,6 +2708,7 @@ @section Restrictions
 @item @command{list unknown}
 @item @command{list ignored}
 @item @command{list missing}
+@item @command{list changed}
 @end itemize

 Including either the old or new name of a renamed file or directory will
@@ -4406,6 +4407,22 @@ @section Informative
 Specifying only the pathname "." will restrict the search for missing
 files to the current subdirectory of the workspace.

+@item monotone list changed
+@itemx monotone list changed @var{pathname...}
+
+This command lists all files in your workspace that have changed
+compared to the base revision, including files that are dropped, added
+or renamed.
+
+Specifying pathnames to the @command{list changed} command restricts
+the set of paths that are checked for changes. Files not included in the
+specified set of pathnames will not be listed.
+
+From within a subdirectory of the workspace the @command{list
+changed} command will, by default, search the entire workspace.
+Specifying only the pathname "." will restrict the search for known
+files to the current subdirectory of the workspace.
+
 @end ftable


@@ -6350,8 +6367,8 @@ @subsection Trust Evaluation Hooks

    if t == nil then return false end

-   if    (name ~= "ancestor" and table.getn(t) >= 1)
-      or (name == "ancestor" and table.getn(t) >= 2)
+   if    (name ~= "branch" and table.getn(t) >= 1)
+      or (name == "branch" and table.getn(t) >= 2)
    then
       return true
    else
@@ -6363,10 +6380,10 @@ @subsection Trust Evaluation Hooks

 In this example, any revision certificate is trusted if it is signed
 by at least one of three ``trusted'' keys, unless it is an
-@code{ancestor} certificate, in which case it must be signed by
+@code{branch} certificate, in which case it must be signed by
 @emph{two} or more trusted keys. This is one way of requiring that
-ancestry assertions go through an extra ``reviewer'' before they are
-accepted.
+the revision has been approved by an extra ``reviewer'' who used the
+@code{approve} command.

 @item accept_testresult_change (@var{old_results}, @var{new_results})

@@ -6673,6 +6690,25 @@ @subsection Attribute Handling

 @end ftable

+@subsection Validation Hooks
+
+If there is a policy decision to make, Monotone defines certain hooks to
+allow a client to validate or reject certain behaviors.
+
+@ftable @code
+@item validate_commit_message (@var{message}, @var{changeset_text})
+
+This hook is called after the user has entered his/her commit message.
+@var{message} is the commit message that the user has entered and
+@var{changeset_text} is the full text of the changes for this revision,
+which can be parsed with the parse_basic_io function. If the hook finds the
+commit message satisfactory, it can return @code{true, ""}. If it finds
+fault, then it can return @code{false, reason} where @var{reason} is the
+reason the message was rejected. By default, this hook rejects empty log
+messages.
+
+@end ftable
+
 @page
 @node   Additional Lua Functions
 @section Additional Lua Functions
@@ -7547,6 +7583,12 @@ @subsection Commands
 List files in revision's manifest, but not in workspace.
 @comment TROFF INPUT: .TP

+@item @b{list changed} @i{[<pathname>...]}
+@itemx @b{ls changed} @i{[<pathname>...]}
+List files in workspace that have changed compared to the base
+revision.
+@comment TROFF INPUT: .TP
+
 @item @b{fdata} @i{<id>}
 Write file data packet to stdout.
 @comment TROFF INPUT: .TP
============================================================
--- netsync.cc	8c4cbdeb2898f87f5f74457e7148a4cc0afde674
+++ netsync.cc	8d4dc8e530af4fb0a6cca30ca600dd74f905b1aa
@@ -591,15 +591,12 @@ session::note_file_delta(file_id const &
 {
   if (role == sink_role)
     return;
-  file_data fd1, fd2;
-  delta del;
-  id fid1, fid2;
-  decode_hexenc(src.inner(), fid1);
-  decode_hexenc(dst.inner(), fid2);
-  app.db.get_file_version(src, fd1);
-  app.db.get_file_version(dst, fd2);
-  diff(fd1.inner(), fd2.inner(), del);
-  queue_delta_cmd(file_item, fid1, fid2, del);
+  file_delta del;
+  app.db.get_file_delta(dst, src, del);
+  id src_id, dst_id;
+  decode_hexenc(src.inner(), src_id);
+  decode_hexenc(dst.inner(), dst_id);
+  queue_delta_cmd(file_item, src_id, dst_id, del.inner());
   file_items_sent.insert(dst);
 }

@@ -1318,13 +1315,18 @@ session::process_anonymous_cmd(protocol_
       i != branchnames.end(); i++)
     {
       if (their_matcher(*i))
-        if (our_matcher(*i) && app.lua.hook_get_netsync_read_permitted(*i))
-          ok_branches.insert(utf8(*i));
-        else
+        if (!our_matcher(*i))
           {
+            error((F("not serving branch '%s'") % *i).str());
+            return true;
+          }
+        else if (!app.lua.hook_get_netsync_read_permitted(*i))
+          {
             error((F("anonymous access to branch '%s' denied by server") % *i).str());
             return true;
           }
+        else
+          ok_branches.insert(utf8(*i));
     }

   P(F("allowed anonymous read permission for '%s' excluding '%s'\n")
@@ -1410,15 +1412,22 @@ session::process_auth_cmd(protocol_role
     {
       if (their_matcher(*i))
         {
-          if (our_matcher(*i) && app.lua.hook_get_netsync_read_permitted(*i, their_id))
-            ok_branches.insert(utf8(*i));
-          else
+          if (!our_matcher(*i))
             {
+              error((F("not serving branch '%s'") % *i).str());
+              return true;
+
+            }
+          else if (!app.lua.hook_get_netsync_read_permitted(*i, their_id))
+            {
               W(F("denied '%s' read permission for '%s' excluding '%s' because of branch '%s'\n")
                 % their_id % their_include_pattern % their_exclude_pattern % *i);
+
               error((F("access to branch '%s' denied by server") % *i).str());
               return true;
             }
+          else
+            ok_branches.insert(utf8(*i));
         }
     }

@@ -2563,102 +2572,138 @@ serve_connections(protocol_role role,
 #else
   bool use_ipv6=false;
 #endif
-  Netxx::Address addr(use_ipv6);
+  // This will be true when we try to bind while using IPv6.  See comments
+  // further down.
+  bool try_again=false;

-  if (!app.bind_address().empty())
-      addr.add_address(app.bind_address().c_str(), default_port);
-  else
-      addr.add_all_addresses (default_port);
+  do
+    {
+      try
+        {
+          try_again = false;

+          Netxx::Address addr(use_ipv6);

-  Netxx::StreamServer server(addr, timeout);
-  const char *name = addr.get_name();
-  P(F("beginning service on %s : %s\n")
-    % (name != NULL ? name : "all interfaces")
-    % lexical_cast<string>(addr.get_port()));
+          if (!app.bind_address().empty())
+            addr.add_address(app.bind_address().c_str(), default_port);
+          else
+            addr.add_all_addresses (default_port);
+
+          // If se use IPv6 and the initiasation of server fails, we want
+          // to try again with IPv4.  The reason is that someone may have
+          // downloaded a IPv6-enabled monotone on a system that doesn't
+          // have IPv6, and which might fail therefore.
+          // On failure, Netxx::NetworkException is thrown, and we catch
+          // it further down.
+          try_again=use_ipv6;
+
+          Netxx::StreamServer server(addr, timeout);
+
+          // If we came this far, whatever we used (IPv6 or IPv4) was
+          // accepted, so we don't need to try again any more.
+          try_again=false;
+
+          const char *name = addr.get_name();
+          P(F("beginning service on %s : %s\n")
+            % (name != NULL ? name : "all interfaces")
+            % lexical_cast<string>(addr.get_port()));

-  map<Netxx::socket_type, shared_ptr<session> > sessions;
-  set<Netxx::socket_type> armed_sessions;
+          map<Netxx::socket_type, shared_ptr<session> > sessions;
+          set<Netxx::socket_type> armed_sessions;

-  shared_ptr<transaction_guard> guard;
+          shared_ptr<transaction_guard> guard;

-  while (true)
-    {
-      probe.clear();
-      armed_sessions.clear();
+          while (true)
+            {
+              probe.clear();
+              armed_sessions.clear();

-      if (sessions.size() >= session_limit)
-        W(F("session limit %d reached, some connections "
-            "will be refused\n") % session_limit);
-      else
-        probe.add(server);
+              if (sessions.size() >= session_limit)
+                W(F("session limit %d reached, some connections "
+                    "will be refused\n") % session_limit);
+              else
+                probe.add(server);

-      arm_sessions_and_calculate_probe(probe, sessions, armed_sessions);
+              arm_sessions_and_calculate_probe(probe, sessions, armed_sessions);

-      L(FL("i/o probe with %d armed\n") % armed_sessions.size());
-      Netxx::Probe::result_type res = probe.ready(sessions.empty() ? forever
-                                           : (armed_sessions.empty() ? timeout
-                                              : instant));
-      Netxx::Probe::ready_type event = res.second;
-      Netxx::socket_type fd = res.first;
+              L(FL("i/o probe with %d armed\n") % armed_sessions.size());
+              Netxx::Probe::result_type res = probe.ready(sessions.empty() ? forever
+                                                          : (armed_sessions.empty() ? timeout
+                                                             : instant));
+              Netxx::Probe::ready_type event = res.second;
+              Netxx::socket_type fd = res.first;

-      if (!guard)
-        guard = shared_ptr<transaction_guard>(new transaction_guard(app.db));
+              if (!guard)
+                guard = shared_ptr<transaction_guard>(new transaction_guard(app.db));

-      I(guard);
+              I(guard);

-      if (fd == -1)
-        {
-          if (armed_sessions.empty())
-            L(FL("timed out waiting for I/O (listening on %s : %s)\n")
-              % addr.get_name() % lexical_cast<string>(addr.get_port()));
-        }
+              if (fd == -1)
+                {
+                  if (armed_sessions.empty())
+                    L(FL("timed out waiting for I/O (listening on %s : %s)\n")
+                      % addr.get_name() % lexical_cast<string>(addr.get_port()));
+                }

-      // we either got a new connection
-      else if (fd == server)
-        handle_new_connection(addr, server, timeout, role,
-                              include_pattern, exclude_pattern,
-                              sessions, app);
+              // we either got a new connection
+              else if (fd == server)
+                handle_new_connection(addr, server, timeout, role,
+                                      include_pattern, exclude_pattern,
+                                      sessions, app);

-      // or an existing session woke up
-      else
-        {
-          map<Netxx::socket_type, shared_ptr<session> >::iterator i;
-          i = sessions.find(fd);
-          if (i == sessions.end())
-            {
-              L(FL("got woken up for action on unknown fd %d\n") % fd);
-            }
-          else
-            {
-              shared_ptr<session> sess = i->second;
-              bool live_p = true;
+              // or an existing session woke up
+              else
+                {
+                  map<Netxx::socket_type, shared_ptr<session> >::iterator i;
+                  i = sessions.find(fd);
+                  if (i == sessions.end())
+                    {
+                      L(FL("got woken up for action on unknown fd %d\n") % fd);
+                    }
+                  else
+                    {
+                      shared_ptr<session> sess = i->second;
+                      bool live_p = true;

-              if (event & Netxx::Probe::ready_read)
-                handle_read_available(fd, sess, sessions,
-                                      armed_sessions, live_p);
+                      if (event & Netxx::Probe::ready_read)
+                        handle_read_available(fd, sess, sessions,
+                                              armed_sessions, live_p);

-              if (live_p && (event & Netxx::Probe::ready_write))
-                handle_write_available(fd, sess, sessions, live_p);
+                      if (live_p && (event & Netxx::Probe::ready_write))
+                        handle_write_available(fd, sess, sessions, live_p);

-              if (live_p && (event & Netxx::Probe::ready_oobd))
+                      if (live_p && (event & Netxx::Probe::ready_oobd))
+                        {
+                          P(F("got OOB from peer %s, disconnecting\n")
+                            % sess->peer_id);
+                          sessions.erase(i);
+                        }
+                    }
+                }
+              process_armed_sessions(sessions, armed_sessions, *guard);
+              reap_dead_sessions(sessions, timeout_seconds);
+
+              if (sessions.empty())
                 {
-                  P(F("got OOB from peer %s, disconnecting\n")
-                    % sess->peer_id);
-                  sessions.erase(i);
+                  // Let the guard die completely if everything's gone quiet.
+                  guard->commit();
+                  guard.reset();
                 }
             }
         }
-      process_armed_sessions(sessions, armed_sessions, *guard);
-      reap_dead_sessions(sessions, timeout_seconds);
-
-      if (sessions.empty())
+      catch (Netxx::NetworkException &e)
         {
-          // Let the guard die completely if everything's gone quiet.
-          guard->commit();
-          guard.reset();
+          // If we tried with IPv6 and failed, we want to try again using IPv4.
+          if (try_again)
+            {
+              use_ipv6 = false;
+            }
+          // In all other cases, just rethrow the exception.
+          else
+            throw;
         }
     }
+  while(try_again);
 }


============================================================
--- paths.cc	227025632497d6849fc4946feeded4d7980f7008
+++ paths.cc	c13e9b0c44a37a70e6c7e6d5c8b8869e363ccab9
@@ -302,6 +302,7 @@ file_path::split(split_path & sp) const
     }
 }

+template <>
 void dump(split_path const & sp, std::string & out)
 {
   std::ostringstream oss;
@@ -473,7 +474,6 @@ find_and_go_to_workspace(system_path con
 bool
 find_and_go_to_workspace(system_path const & search_root)
 {
-  // unimplemented
   fs::path root(search_root.as_external(), fs::native);
   fs::path bookdir(bookkeeping_root.as_external(), fs::native);
   fs::path current(fs::initial_path());
============================================================
--- paths.hh	727b9cfba7c9d150dfd7144d0d38744beb8dda7f
+++ paths.hh	054a74b153b42a34140f2776f468bf79c844fa64
@@ -115,7 +115,7 @@ null_name(path_component pc)
   return pc == the_null_component;
 }

-void dump(split_path const & sp, std::string & out);
+template <> void dump(split_path const & sp, std::string & out);

 // It's possible this will become a proper virtual interface in the future,
 // but since the implementation is exactly the same in all cases, there isn't
============================================================
--- po/sv.po	e347bfacd741eafd3202feda54aa54e24b3a7bfb
+++ po/sv.po	754e90276863a8b1a53cad17c575bb93ffcdd5b4
@@ -144,8 +144,8 @@ msgstr ""
 msgstr ""
 "Project-Id-Version: monotone 0.26pre1\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2006-01-28 23:06+0100\n"
-"PO-Revision-Date: 2006-01-28 23:59+0100\n"
+"POT-Creation-Date: 2006-02-11 08:32+0100\n"
+"PO-Revision-Date: 2006-02-11 08:43+0100\n"
 "Last-Translator: Joel Rosdahl <joel@rosdahl.net>\n"
 "Language-Team: Richard Levitte <richard@levitte.org>\n"
 "MIME-Version: 1.0\n"
@@ -283,39 +283,39 @@ msgstr "flera grencert funna för revisi
 "multiple branch certs found for revision %s, please provide a branch name"
 msgstr "flera grencert funna för revision %s, var god ange en gren"

-#: commands.cc:159
+#: commands.cc:160
 #, c-format
 msgid "command '%s' has multiple ambiguous expansions:\n"
 msgstr "kommandot '%s' har mer än en betydelse:\n"

-#: commands.cc:202
+#: commands.cc:203
 msgid "commands:"
 msgstr "kommandon:"

-#: commands.cc:253
+#: commands.cc:254
 #, c-format
 msgid "unknown command '%s'\n"
 msgstr "okänt kommando '%s'\n"

-#: commands.cc:300
+#: commands.cc:301
 #, c-format
 msgid "pid file '%s' already exists"
 msgstr "pid-filen '%s' finns redan"

-#: commands.cc:324 commands.cc:1294 commands.cc:1364 commands.cc:1739
-#: commands.cc:2648 commands.cc:3192 commands.cc:3423 commands.cc:3462
+#: commands.cc:325 commands.cc:1295 commands.cc:1365 commands.cc:1801
+#: commands.cc:2722 commands.cc:3266 commands.cc:3497 commands.cc:3536
 msgid "informative"
 msgstr "informativ"

-#: commands.cc:324
+#: commands.cc:325
 msgid "command [ARGS...]"
 msgstr "kommandon [ARGUMENT...]"

-#: commands.cc:324
+#: commands.cc:325
 msgid "display command help"
 msgstr "skriv ut hjälptext"

-#: commands.cc:403
+#: commands.cc:404
 msgid ""
 "Enter a description of this change.\n"
 "Lines beginning with `MT:' are removed automatically.\n"
@@ -323,16 +323,16 @@ msgstr ""
 "Mata in en beskrivning av denna ändring.\n"
 "Rader som börjar med 'MT:' kommer att tas bort automatiskt.\n"

-#: commands.cc:410
+#: commands.cc:411
 #, c-format
 msgid "edit of log message failed"
 msgstr "redigering av loggmeddelandet misslyckades"

-#: commands.cc:419
+#: commands.cc:420
 msgid "note: "
 msgstr "obs: "

-#: commands.cc:420
+#: commands.cc:421
 #, c-format
 msgid ""
 "branch '%s' has multiple heads\n"
@@ -341,59 +341,59 @@ msgstr ""
 "grenen '%s' har mer än ett löv\n"
 "kanske borde du överväga 'monotone merge'"

-#: commands.cc:480 commands.cc:736 commands.cc:1382 commands.cc:1457
-#: commands.cc:1823 commands.cc:2698 commands.cc:2726 commands.cc:2729
-#: commands.cc:2865 commands.cc:3445
+#: commands.cc:481 commands.cc:737 commands.cc:1383 commands.cc:1460
+#: commands.cc:1888 commands.cc:2772 commands.cc:2800 commands.cc:2803
+#: commands.cc:2939 commands.cc:3519
 #, c-format
 msgid "no such revision '%s'"
 msgstr "det finns ingen revision med identiteten '%s'"

-#: commands.cc:487
+#: commands.cc:488
 #, c-format
 msgid "expanding selection '%s'\n"
 msgstr "expanderar valet '%s'\n"

-#: commands.cc:495
+#: commands.cc:496
 #, c-format
 msgid "no match for selection '%s'"
 msgstr "det finns inget som matchar '%s'"

-#: commands.cc:498
+#: commands.cc:499
 #, c-format
 msgid "selection '%s' has multiple ambiguous expansions: \n"
 msgstr "valet '%s' har mer än en möjlig betydelse: \n"

-#: commands.cc:505
+#: commands.cc:506
 #, c-format
 msgid "expanded to '%s'\n"
 msgstr "expanderar till '%s'\n"

-#: commands.cc:516
+#: commands.cc:517
 #, c-format
 msgid "non-hex digits in id"
 msgstr "något tecken i identiteten är inte hexadecimalt"

-#: commands.cc:525
+#: commands.cc:526
 #, c-format
 msgid "partial id '%s' does not have an expansion"
 msgstr "den partiella identiteten '%s' matchar inte befintliga identiteter"

-#: commands.cc:528
+#: commands.cc:529
 #, c-format
 msgid "partial id '%s' has multiple ambiguous expansions:\n"
 msgstr "den partiella identiteten '%s' har mer än en uttydning:\n"

-#: commands.cc:535
+#: commands.cc:536
 #, c-format
 msgid "expanded partial id '%s' to '%s'\n"
 msgstr "expanderade partiell identitet '%s' till '%s'\n"

-#: commands.cc:562 netsync.cc:1709
+#: commands.cc:563 netsync.cc:1722
 #, c-format
 msgid "no public key '%s' found in database"
 msgstr "den publika nyckeln '%s' finns inte i databasen"

-#: commands.cc:572
+#: commands.cc:573
 #, c-format
 msgid ""
 "Key   : %s\n"
@@ -406,87 +406,87 @@ msgstr ""
 "Namn     : %s\n"
 "Värde    : %s\n"

-#: commands.cc:606
+#: commands.cc:607
 msgid "ok"
 msgstr "ok"

-#: commands.cc:609
+#: commands.cc:610
 msgid "bad"
 msgstr "felaktig"

-#: commands.cc:612
+#: commands.cc:613
 msgid "unknown"
 msgstr "okänd"

-#: commands.cc:699
+#: commands.cc:700
 #, c-format
 msgid "(*) - only in %s/"
 msgstr "(*) - enbart i %s/"

-#: commands.cc:722
+#: commands.cc:723
 #, c-format
 msgid "no keys found\n"
 msgstr "hittade inga nycklar\n"

-#: commands.cc:724
+#: commands.cc:725
 #, c-format
 msgid "no keys found matching '%s'\n"
 msgstr "hittade inga nycklar som matchar '%s'\n"

-#: commands.cc:742
+#: commands.cc:743
 #, c-format
 msgid "revision %s already has children. We cannot kill it."
 msgstr "revisionen %s har barn. Vi kan inte ta bort den."

-#: commands.cc:882 commands.cc:906 commands.cc:943 commands.cc:964
-#: commands.cc:1000
+#: commands.cc:883 commands.cc:907 commands.cc:944 commands.cc:965
+#: commands.cc:1001
 msgid "key and cert"
 msgstr "nyckel och cert"

-#: commands.cc:882 commands.cc:906 commands.cc:943
+#: commands.cc:883 commands.cc:907 commands.cc:944
 msgid "KEYID"
 msgstr "NYCKELID"

-#: commands.cc:882
+#: commands.cc:883
 msgid "generate an RSA key-pair"
 msgstr "skapa ett RSA-nyckelpar"

-#: commands.cc:897
+#: commands.cc:898
 #, c-format
 msgid "key '%s' already exists"
 msgstr "nyckeln '%s' finns redan"

-#: commands.cc:900
+#: commands.cc:901
 #, c-format
 msgid "generating key-pair '%s'"
 msgstr "skapar nyckelparet '%s'"

-#: commands.cc:902
+#: commands.cc:903
 #, c-format
 msgid "storing key-pair '%s' in %s/"
 msgstr "lagrar nyckelparet '%s' i %s/"

-#: commands.cc:906
+#: commands.cc:907
 msgid "drop a public and private key"
 msgstr "släng publik och privat nyckel"

-#: commands.cc:920
+#: commands.cc:921
 #, c-format
 msgid "dropping public key '%s' from database\n"
 msgstr "tar bort den publika nyckeln '%s' ur databasen\n"

-#: commands.cc:930
+#: commands.cc:931
 #, c-format
 msgid "dropping key pair '%s' from keystore\n"
 msgstr "tar bort nyckelparet '%s' från nyckellagret\n"

-#: commands.cc:937
+#: commands.cc:938
 #, c-format
 msgid "public or private key '%s' does not exist in keystore or database"
 msgstr ""
 "den publika eller privata nyckeln '%s' finns inte i nyckellager eller databas"

-#: commands.cc:939
+#: commands.cc:940
 #, c-format
 msgid ""
 "public or private key '%s' does not exist in keystore, and no database was "
@@ -495,33 +495,33 @@ msgstr ""
 "den publika eller privata nyckeln '%s' finns inte i nyckellager och ingen "
 "databas angavs"

-#: commands.cc:944
+#: commands.cc:945
 msgid "change passphrase of a private RSA key"
 msgstr "ändra lösen till en privat RSA-nyckel"

-#: commands.cc:954
+#: commands.cc:955
 #, c-format
 msgid "key '%s' does not exist in the keystore"
 msgstr "nyckeln '%s' finns inte i nyckellagret"

-#: commands.cc:961
+#: commands.cc:962
 #, c-format
 msgid "passphrase changed\n"
 msgstr "lösen ändrat\n"

-#: commands.cc:964
+#: commands.cc:965
 msgid "REVISION CERTNAME [CERTVAL]"
 msgstr "REVISION CERTNAMN [CERTVÄRDE]"

-#: commands.cc:965
+#: commands.cc:966
 msgid "create a cert for a revision"
 msgstr "skapa ett cert för en revision"

-#: commands.cc:1000
+#: commands.cc:1001
 msgid "REVISION NAME VALUE SIGNER1 [SIGNER2 [...]]"
 msgstr "REVISION NAMN VÄRDE SIGNERARE1 [SIGNERARE2 [...]]"

-#: commands.cc:1001
+#: commands.cc:1002
 msgid ""
 "test whether a hypothetical cert would be trusted\n"
 "by current settings"
@@ -529,7 +529,7 @@ msgstr ""
 "kontrollera ifall ett hypotetiskt cert skulle vara pålitligt\n"
 "enligt nuvarande inställningar"

-#: commands.cc:1034
+#: commands.cc:1035
 #, c-format
 msgid ""
 "if a cert on: %s\n"
@@ -544,101 +544,101 @@ msgstr ""
 "signerades av: %s\n"
 "skulle det vara: %s\n"

-#: commands.cc:1043
+#: commands.cc:1044
 msgid "trusted"
 msgstr "pålitligt"

-#: commands.cc:1043
+#: commands.cc:1044
 msgid "UNtrusted"
 msgstr "Opålitligt"

-#: commands.cc:1046 commands.cc:1059 commands.cc:1072 commands.cc:1089
-#: commands.cc:1140
+#: commands.cc:1047 commands.cc:1060 commands.cc:1073 commands.cc:1090
+#: commands.cc:1141
 msgid "review"
 msgstr "granskning"

-#: commands.cc:1046
+#: commands.cc:1047
 msgid "REVISION TAGNAME"
 msgstr "REVISION TAGNAMN"

-#: commands.cc:1047
+#: commands.cc:1048
 msgid "put a symbolic tag cert on a revision version"
 msgstr "sätt ett symboliskt tagg-cert på en revision"

-#: commands.cc:1059
+#: commands.cc:1060
 msgid "ID (pass|fail|true|false|yes|no|1|0)"
 msgstr "ID (pass|fail|true|false|yes|no|1|0)"

-#: commands.cc:1060
+#: commands.cc:1061
 msgid "note the results of running a test on a revision"
 msgstr "notera ett testresultat för en revision"

-#: commands.cc:1072 commands.cc:1089
+#: commands.cc:1073 commands.cc:1090
 msgid "REVISION"
 msgstr "REVISION"

-#: commands.cc:1073
+#: commands.cc:1074
 msgid "approve of a particular revision"
 msgstr "godkänn en specifik revision"

-#: commands.cc:1084
+#: commands.cc:1085
 #, c-format
 msgid "need --branch argument for approval"
 msgstr "för godkännande behöver en gren anges med --branch"

-#: commands.cc:1090
+#: commands.cc:1091
 msgid "disapprove of a particular revision"
 msgstr "underkänn en specifik revision"

-#: commands.cc:1103
+#: commands.cc:1104
 #, c-format
 msgid "revision '%s' has %d changesets, cannot invert\n"
 msgstr "revisionen '%s' har %d föräldrar, kan inte invertera\n"

-#: commands.cc:1107
+#: commands.cc:1108
 #, c-format
 msgid "need --branch argument for disapproval"
 msgstr "för underkännande behöver en gren anges med --branch"

-#: commands.cc:1140
+#: commands.cc:1141
 msgid "REVISION [COMMENT]"
 msgstr "REVISION [KOMMENTAR]"

-#: commands.cc:1141
+#: commands.cc:1142
 msgid "comment on a particular revision"
 msgstr "kommentera en specifik revision"

-#: commands.cc:1151
+#: commands.cc:1152
 #, c-format
 msgid "edit comment failed"
 msgstr "misslyckades med att redigera kommentaren"

-#: commands.cc:1154
+#: commands.cc:1155
 #, c-format
 msgid "empty comment"
 msgstr "tom kommentar"

-#: commands.cc:1167 commands.cc:1195 commands.cc:1220 commands.cc:1341
-#: commands.cc:2159 commands.cc:2278 commands.cc:2801 commands.cc:3240
+#: commands.cc:1168 commands.cc:1196 commands.cc:1221 commands.cc:1342
+#: commands.cc:2224 commands.cc:2342 commands.cc:2875 commands.cc:3314
 msgid "workspace"
 msgstr "arbetskopia"

-#: commands.cc:1167 commands.cc:1195 commands.cc:1294 commands.cc:2278
-#: commands.cc:2648 commands.cc:3240
+#: commands.cc:1168 commands.cc:1196 commands.cc:1295 commands.cc:2342
+#: commands.cc:2722 commands.cc:3314
 msgid "[PATH]..."
 msgstr "[SÖKVÄG]..."

-#: commands.cc:1168
+#: commands.cc:1169
 #, fuzzy
 msgid "add files to workspace"
 msgstr "lägg till filer i arbetskopian"

-#: commands.cc:1196
+#: commands.cc:1197
 #, fuzzy
 msgid "drop files from workspace"
 msgstr "överge filer i arbetskopian"

-#: commands.cc:1221
+#: commands.cc:1222
 msgid ""
 "SRC DEST\n"
 "SRC1 [SRC2 [...]] DEST_DIR"
@@ -646,83 +646,83 @@ msgstr ""
 "KÄLLA MÅL\n"
 "KÄLLA1 [KÄLLA2 [...]] MÅLKATALOG"

-#: commands.cc:1223
+#: commands.cc:1224
 #, fuzzy
 msgid "rename entries in the workspace"
 msgstr "byt namn på filer i arbetskopian"

-#: commands.cc:1248 commands.cc:1261 commands.cc:3343 commands.cc:3783
+#: commands.cc:1249 commands.cc:1262 commands.cc:3417 commands.cc:3857
 msgid "debug"
 msgstr "felsökning"

-#: commands.cc:1248
+#: commands.cc:1249
 msgid "load file contents into db"
 msgstr "ladda in filinnehållet i databasen"

-#: commands.cc:1261
+#: commands.cc:1262
 msgid "<parent> <left> <right>"
 msgstr "<förälder> <vänster> <höger>"

-#: commands.cc:1262
+#: commands.cc:1263
 msgid "merge 3 files and output result"
 msgstr "slå ihop 3 filer och skriv ut resultatet"

-#: commands.cc:1272
+#: commands.cc:1273
 #, c-format
 msgid "ancestor file id does not exist"
 msgstr "förälderns filidentitet finns inte"

-#: commands.cc:1275
+#: commands.cc:1276
 #, c-format
 msgid "left file id does not exist"
 msgstr "vänster fils identitet finns inte"

-#: commands.cc:1278
+#: commands.cc:1279
 #, c-format
 msgid "right file id does not exist"
 msgstr "höger fils identitet finns inte"

-#: commands.cc:1289
+#: commands.cc:1290
 #, c-format
 msgid "merge failed"
 msgstr "ihopslagningen misslyckades"

-#: commands.cc:1294
+#: commands.cc:1295
 #, fuzzy
 msgid "show status of workspace"
 msgstr "visa arbetskopians status"

-#: commands.cc:1341
+#: commands.cc:1342
 msgid "[PATH]"
 msgstr "[SÖKVÄG]"

-#: commands.cc:1342
+#: commands.cc:1343
 msgid "calculate identity of PATH or stdin"
 msgstr "räkna ut identiteten för SÖKVÄG eller stdin"

-#: commands.cc:1365
+#: commands.cc:1366
 msgid "FILENAME"
 msgstr "FILNAMN"

-#: commands.cc:1366
+#: commands.cc:1367
 msgid "write file from database to stdout"
 msgstr "skriv ut angiven fil från databasen"

-#: commands.cc:1394 commands.cc:1396 commands.cc:3454
+#: commands.cc:1395 commands.cc:1397 commands.cc:3528
 #, c-format
 msgid "no file '%s' found in revision '%s'\n"
 msgstr "filen '%s' finns inte i revisionen '%s'\n"

-#: commands.cc:1408 commands.cc:1527 commands.cc:3002 commands.cc:3051
-#: commands.cc:3140 commands.cc:3147 commands.cc:3696
+#: commands.cc:1409 commands.cc:1531 commands.cc:3076 commands.cc:3125
+#: commands.cc:3214 commands.cc:3221 commands.cc:3770
 msgid "tree"
 msgstr "träd"

-#: commands.cc:1408
+#: commands.cc:1409
 msgid "[DIRECTORY]\n"
 msgstr "[KATALOG]\n"

-#: commands.cc:1409
+#: commands.cc:1410
 msgid ""
 "check out a revision from database into directory.\n"
 "If a revision is given, that's the one that will be checked out.\n"
@@ -734,62 +734,62 @@ msgstr ""
 "kommer lövet i grenen (implicit eller angiven) att hämtas. Om\n"
 "ingen katalog anges kommer grenens namn att användas som katalognamn."

-#: commands.cc:1426 commands.cc:1445
+#: commands.cc:1429 commands.cc:1448
 #, c-format
 msgid "need --branch argument for branch-based checkout"
 msgstr "--branch behövs för att kunna hämta en revision med given gren"

-#: commands.cc:1439
+#: commands.cc:1442
 #, c-format
 msgid "checkout directory '%s' already exists"
 msgstr "arbetskatalogen '%s' finns redan"

-#: commands.cc:1448 commands.cc:1540 commands.cc:3015 commands.cc:3085
-#: commands.cc:3088
+#: commands.cc:1451 commands.cc:1544 commands.cc:3089 commands.cc:3159
+#: commands.cc:3162
 #, c-format
 msgid "branch '%s' is empty\n"
 msgstr "grenen '%s' är tom\n"

-#: commands.cc:1449
+#: commands.cc:1452
 #, c-format
 msgid "branch %s has multiple heads"
 msgstr "grenen %s har flera löv"

-#: commands.cc:1475
+#: commands.cc:1478
 #, c-format
 msgid "revision %s is not a member of branch %s\n"
 msgstr "revisionen %s är inte med i grenen %s\n"

-#: commands.cc:1509
+#: commands.cc:1512
 #, c-format
 msgid "no file %s found in database for %s"
 msgstr "det finns ingen fil %s för %s i databasen"

-#: commands.cc:1527
+#: commands.cc:1531
 msgid "show unmerged head revisions of branch"
 msgstr "visa ej ihopslagna lövrevisioner i grenen"

-#: commands.cc:1535 commands.cc:3011
+#: commands.cc:1539 commands.cc:3085
 #, c-format
 msgid "please specify a branch, with --branch=BRANCH"
 msgstr "var god ange en gren med --branch=GREN"

-#: commands.cc:1542
+#: commands.cc:1546
 #, c-format
 msgid "branch '%s' is currently merged:\n"
 msgstr "grenen '%s' har för tillfället ett löv:\n"

-#: commands.cc:1544
+#: commands.cc:1548
 #, c-format
 msgid "branch '%s' is currently unmerged:\n"
 msgstr "grenen '%s' har för tillfället mer än ett löv:\n"

-#: commands.cc:1583
+#: commands.cc:1587
 #, c-format
 msgid "no epoch for branch %s\n"
 msgstr "ingen epok i grenen %s\n"

-#: commands.cc:1740
+#: commands.cc:1802
 msgid ""
 "certs ID\n"
 "keys [PATTERN]\n"
@@ -800,7 +800,8 @@ msgid ""
 "known\n"
 "unknown\n"
 "ignored\n"
-"missing"
+"missing\n"
+"changed"
 msgstr ""
 "certs ID\n"
 "keys [MÖNSTER]\n"
@@ -811,162 +812,162 @@ msgstr ""
 "known\n"
 "unknown\n"
 "ignored\n"
-"missing"
+"missing\n"
+"changed"

-#: commands.cc:1750
-#, fuzzy
+#: commands.cc:1813
 msgid ""
-"show database objects, or the current workspace manifest,\n"
-"or unknown, intentionally ignored, or missing state files"
+"show database objects, or the current workspace manifest, or known,\n"
+"unknown, intentionally ignored, missing, or changed state files"
 msgstr ""
-"visa databasobjekten, arbetskopians manifest, okända filer, medvetet\n"
-"ignorerade filer eller saknade tillståndsfiler"
+"visa databasobjekten, arbetskopians manifest, okända filer, kända\n"
+"filer, medvetet ignorerade filer, saknade filer eller ändrade filer"

-#: commands.cc:1786 commands.cc:1810 commands.cc:1829 commands.cc:1848
-#: commands.cc:1866 commands.cc:1894 commands.cc:1912
+#: commands.cc:1851 commands.cc:1875 commands.cc:1894 commands.cc:1913
+#: commands.cc:1931 commands.cc:1959 commands.cc:1977
 msgid "packet i/o"
 msgstr "paket-I/O"

-#: commands.cc:1786
+#: commands.cc:1851
 msgid "OLDID NEWID"
 msgstr "GAMMALT-ID NYTT-ID"

-#: commands.cc:1787
+#: commands.cc:1852
 msgid "write file delta packet to stdout"
 msgstr "skriv ut datapaket för fildelta till stdout"

-#: commands.cc:1801 commands.cc:1803 commands.cc:1842
+#: commands.cc:1866 commands.cc:1868 commands.cc:1907
 #, c-format
 msgid "no such file '%s'"
 msgstr "filen '%s' finns inte"

-#: commands.cc:1810 commands.cc:1829 commands.cc:1848 commands.cc:1866
-#: commands.cc:1894
+#: commands.cc:1875 commands.cc:1894 commands.cc:1913 commands.cc:1931
+#: commands.cc:1959
 msgid "ID"
 msgstr "ID"

-#: commands.cc:1810
+#: commands.cc:1875
 msgid "write revision data packet to stdout"
 msgstr "skriv ut datapaket för revision till stdout"

-#: commands.cc:1829
+#: commands.cc:1894
 msgid "write file data packet to stdout"
 msgstr "skriv ut datapaket för fil till stdout"

-#: commands.cc:1848
+#: commands.cc:1913
 msgid "write cert packets to stdout"
 msgstr "skriv ut datapaket för cert till stdout"

-#: commands.cc:1866
+#: commands.cc:1931
 msgid "write public key packet to stdout"
 msgstr "skriv it datapaket för publik nyckel till stdout"

-#: commands.cc:1888
+#: commands.cc:1953
 #, c-format
 msgid "public key '%s' does not exist"
 msgstr "den publika nyckeln '%s' finns inte"

-#: commands.cc:1894
+#: commands.cc:1959
 msgid "write private key packet to stdout"
 msgstr "skriv it datapaket för privat nyckel till stdout"

-#: commands.cc:1902
+#: commands.cc:1967
 #, c-format
 msgid "public and private key '%s' do not exist in keystore"
 msgstr "den publika och privata nyckeln '%s' finns inte i nyckellagret"

-#: commands.cc:1913
+#: commands.cc:1978
 msgid "read packets from files or stdin"
 msgstr "läs paket från filer eller stdin"

-#: commands.cc:1921
+#: commands.cc:1986
 #, c-format
 msgid "no packets found on stdin"
 msgstr "inga paket hittade i stdin"

-#: commands.cc:1932
+#: commands.cc:1997
 #, c-format
 msgid "no packets found in given file"
 msgid_plural "no packets found in given files"
 msgstr[0] "inga paket funna i den angivna filen"
 msgstr[1] "inga paket funna i de angivna filerna"

-#: commands.cc:1936
+#: commands.cc:2001
 #, c-format
 msgid "read %d packet"
 msgid_plural "read %d packets"
 msgstr[0] "läste %d paket"
 msgstr[1] "läste %d paket"

-#: commands.cc:1965
+#: commands.cc:2030
 #, c-format
 msgid "setting default server to %s\n"
 msgstr "sätter standardserver till %s\n"

-#: commands.cc:1971
+#: commands.cc:2036
 #, c-format
 msgid "no hostname given"
 msgstr "inget värdnamn angivet"

-#: commands.cc:1973
+#: commands.cc:2038
 #, c-format
 msgid "no server given and no default server set"
 msgstr "ingen server angiven och ingen standardserver är satt"

-#: commands.cc:1991
+#: commands.cc:2056
 #, c-format
 msgid "setting default branch include pattern to '%s'\n"
 msgstr "sätter standardmönstret för grenar att ta med till '%s'\n"

-#: commands.cc:1997
+#: commands.cc:2062
 #, c-format
 msgid "setting default branch exclude pattern to '%s'\n"
 msgstr "sätter standardmönstret för grenar att INTE ta med till '%s'\n"

-#: commands.cc:2003
+#: commands.cc:2068
 #, c-format
 msgid "no branch pattern given"
 msgstr "inget grenmönster angivet"

-#: commands.cc:2005
+#: commands.cc:2070
 #, c-format
 msgid "no branch pattern given and no default pattern set"
 msgstr "inget grenmönster angivet och inget standardmönster satt"

-#: commands.cc:2021 commands.cc:2036 commands.cc:2050 commands.cc:2065
+#: commands.cc:2086 commands.cc:2101 commands.cc:2115 commands.cc:2130
 msgid "network"
 msgstr "nätverk"

-#: commands.cc:2021 commands.cc:2036 commands.cc:2050
+#: commands.cc:2086 commands.cc:2101 commands.cc:2115
 msgid "[ADDRESS[:PORTNUMBER] [PATTERN]]"
 msgstr "[ADRESS[:PORTNUMMER] [MÖNSTER]]"

-#: commands.cc:2022
+#: commands.cc:2087
 msgid "push branches matching PATTERN to netsync server at ADDRESS"
 msgstr "skicka grenar som matchar MÖNSTER till netsync-server på ADRESS"

-#: commands.cc:2037
+#: commands.cc:2102
 msgid "pull branches matching PATTERN from netsync server at ADDRESS"
 msgstr "hämta grenar som matchar MÖNSTER till netsync-server på ADRESS"

-#: commands.cc:2044
+#: commands.cc:2109
 #, c-format
 msgid "doing anonymous pull; use -kKEYNAME if you need authentication\n"
 msgstr "hämtar anonymt; använd -kNYCKELNAMN om du behöver autentisera\n"

-#: commands.cc:2051
+#: commands.cc:2116
 msgid "sync branches matching PATTERN with netsync server at ADDRESS"
 msgstr "synkronisera grenar som matchar MÖNSTER till netsync-server på ADRESS"

-#: commands.cc:2065
+#: commands.cc:2130
 msgid "PATTERN ..."
 msgstr "MÖNSTER ..."

-#: commands.cc:2066
+#: commands.cc:2131
 msgid "serve the branches specified by PATTERNs to connecting clients"
 msgstr "servera de grenar som matchar MÖNSTER till anslutande klienter"

-#: commands.cc:2079
+#: commands.cc:2144
 #, c-format
 msgid ""
 "need permission to store persistent passphrase (see hook persist_phrase_ok())"
@@ -974,11 +975,11 @@ msgstr ""
 "behöver tillstånd att använda ett permanent lösenord (se lua-rutinen\n"
 "persist_phrase_ok())"

-#: commands.cc:2091
+#: commands.cc:2156
 msgid "database"
 msgstr "databas"

-#: commands.cc:2092
+#: commands.cc:2157
 msgid ""
 "init\n"
 "info\n"
@@ -1010,11 +1011,11 @@ msgstr ""
 "rosterify\n"
 "set_epoch GREN EPOK\n"

-#: commands.cc:2106
+#: commands.cc:2171
 msgid "manipulate database state"
 msgstr "gör ändringar direkt i databasen"

-#: commands.cc:2159
+#: commands.cc:2224
 msgid ""
 "set PATH ATTR VALUE\n"
 "get PATH [ATTR]\n"
@@ -1024,41 +1025,41 @@ msgstr ""
 "get SÖKVÄG [ATTR]\n"
 "drop SÖKVÄG [ATTR]"

-#: commands.cc:2160
+#: commands.cc:2225
 msgid "set, get or drop file attributes"
 msgstr "sätt, hämta eller ta bort attribut"

-#: commands.cc:2176
+#: commands.cc:2241
 #, c-format
 msgid "Unknown path '%s'"
 msgstr "Okänd sökväg '%s'"

-#: commands.cc:2205
+#: commands.cc:2270
 #, c-format
 msgid "Path '%s' does not have attribute '%s'\n"
 msgstr "Sökvägen '%s' har inget attribut '%s'\n"

-#: commands.cc:2259
+#: commands.cc:2324
 #, c-format
 msgid "--message and --message-file are mutually exclusive"
 msgstr "--message och --message-file får inte anges samtidigt"

-#: commands.cc:2279
+#: commands.cc:2343
 #, fuzzy
 msgid "commit workspace to database"
 msgstr "arkivera arbetskopian i databasen"

-#: commands.cc:2297
+#: commands.cc:2361
 #, c-format
 msgid "no changes to commit\n"
 msgstr "inga ändringar att arkivera\n"

-#: commands.cc:2311
+#: commands.cc:2375
 #, c-format
 msgid "beginning commit on branch '%s'\n"
 msgstr "börjar arkivering av ändringar i grenen '%s'\n"

-#: commands.cc:2320
+#: commands.cc:2384
 #, c-format
 msgid ""
 "MT/log is non-empty and log message was specified on command line\n"
@@ -1069,27 +1070,32 @@ msgstr ""
 "kanske ta bort eller flytta på MT/log\n"
 "eller ta bort --message/--message-file från kommandoraden?"

-#: commands.cc:2332
+#: commands.cc:2396
 #, c-format
 msgid "empty log message; commit canceled"
 msgstr "tomt loggmeddelande; arkivering avbryts"

-#: commands.cc:2349
+#: commands.cc:2415
 #, c-format
+msgid "log message rejected: %s\n"
+msgstr "loggmeddelandet förkastas: %s\n"
+
+#: commands.cc:2423
+#, c-format
 msgid "revision %s already in database\n"
 msgstr "revisionen %s finns redan i databasen\n"

-#: commands.cc:2387 commands.cc:2404 commands.cc:2423
+#: commands.cc:2461 commands.cc:2478 commands.cc:2497
 #, c-format
 msgid "file '%s' modified during commit, aborting"
 msgstr "filen '%s' ändrades under arkivering, avbryter"

-#: commands.cc:2449
+#: commands.cc:2523
 #, c-format
 msgid "committed revision %s\n"
 msgstr "arkiverade revisionen %s\n"

-#: commands.cc:2455
+#: commands.cc:2529
 #, c-format
 msgid ""
 "note: this revision creates divergence\n"
@@ -1098,7 +1104,7 @@ msgstr ""
 "obs: den här revisionen skapade divergens\n"
 "obs: du kan tänkas vilja (eller kanske inte) köra 'monotone merge'"

-#: commands.cc:2649
+#: commands.cc:2723
 #, fuzzy
 msgid ""
 "show current diffs on stdout.\n"
@@ -1111,7 +1117,7 @@ msgstr ""
 "revisionen. Om två revisioner anges visas skillnaden mellan dem. Om\n"
 "inget format anges används unified."

-#: commands.cc:2665
+#: commands.cc:2739
 #, c-format
 msgid ""
 "--diff-args requires --external\n"
@@ -1120,16 +1126,16 @@ msgstr ""
 "--diff-args kräver att även --external anges\n"
 "lägg till --external eller ta bort --diff-args?"

-#: commands.cc:2707
+#: commands.cc:2781
 #, c-format
 msgid "current revision has no ancestor"
 msgstr "denna revision har ingen förälder"

-#: commands.cc:2770
+#: commands.cc:2844
 msgid "no changes"
 msgstr "inga ändringar"

-#: commands.cc:2802
+#: commands.cc:2876
 #, fuzzy
 msgid ""
 "update workspace.\n"
@@ -1144,12 +1150,12 @@ msgstr ""
 "Om en revision har angivits så uppdateras arbetskopian till den,\n"
 "i annat fall uppdateras den till lövet i grenen."

-#: commands.cc:2840
+#: commands.cc:2914
 #, fuzzy, c-format
 msgid "this workspace is a new project; cannot update"
 msgstr "denna arbetskatalog är ett nytt projekt; kan inte uppdatera"

-#: commands.cc:2847
+#: commands.cc:2921
 #, c-format
 msgid ""
 "your request matches no descendents of the current revision\n"
@@ -1160,32 +1166,32 @@ msgstr ""
 "faktum är att den inte ens matchar nuvarande revision\n"
 "du kanske vill ange --revision=<revision i annan gren>"

-#: commands.cc:2852
+#: commands.cc:2926
 #, c-format
 msgid "multiple update candidates:\n"
 msgstr "mer än en uppdateringskandidat:\n"

-#: commands.cc:2856
+#: commands.cc:2930
 #, c-format
 msgid "choose one with 'monotone update -r<id>'\n"
 msgstr "välj en med 'monotone update -r<id>'\n"

-#: commands.cc:2857
+#: commands.cc:2931
 #, c-format
 msgid "multiple candidates remain after selection"
 msgstr "mer än en kandidat efter urval"

-#: commands.cc:2872
+#: commands.cc:2946
 #, c-format
 msgid "already up to date at %s\n"
 msgstr "redan uppdaterad till %s\n"

-#: commands.cc:2880
+#: commands.cc:2954
 #, c-format
 msgid "selected update target %s\n"
 msgstr "valt uppdateringsmål är %s\n"

-#: commands.cc:2892
+#: commands.cc:2966
 #, c-format
 msgid ""
 "revision %s is not a member of branch %s\n"
@@ -1194,140 +1200,140 @@ msgstr ""
 "revisionen %s är inte i grenen %s\n"
 "försök igen med en explicit --branch\n"

-#: commands.cc:2992
+#: commands.cc:3066
 #, c-format
 msgid "updated to base revision %s\n"
 msgstr "uppdaterad till revisionen %s\n"

-#: commands.cc:3002
+#: commands.cc:3076
 msgid "merge unmerged heads of branch"
 msgstr "slå ihop ej ihopslagna löv i grenen"

-#: commands.cc:3016
+#: commands.cc:3090
 #, c-format
 msgid "branch '%s' is merged\n"
 msgstr "löven i grenen '%s' är ihopslagna\n"

-#: commands.cc:3022
+#: commands.cc:3096
 #, c-format
 msgid "starting with revision 1 / %d\n"
 msgstr "börjar med revision 1 av %d\n"

-#: commands.cc:3026
+#: commands.cc:3100
 #, c-format
 msgid "merging with revision %d / %d\n"
 msgstr "slår ihop med revision %d av %d\n"

-#: commands.cc:3027 commands.cc:3028 commands.cc:3095 commands.cc:3170
-#: commands.cc:3171
+#: commands.cc:3101 commands.cc:3102 commands.cc:3169 commands.cc:3244
+#: commands.cc:3245
 #, c-format
 msgid "[source] %s\n"
 msgstr "[källa]      %s\n"

-#: commands.cc:3045 commands.cc:3136 commands.cc:3189
+#: commands.cc:3119 commands.cc:3210 commands.cc:3263
 #, c-format
 msgid "[merged] %s\n"
 msgstr "[ihopslagen] %s\n"

-#: commands.cc:3048
+#: commands.cc:3122
 #, fuzzy, c-format
 msgid "note: your workspaces have not been updated\n"
 msgstr "obs: dina arbetskopior har inte uppdaterats\n"

-#: commands.cc:3051
+#: commands.cc:3125
 msgid "SOURCE-BRANCH DEST-BRANCH"
 msgstr "KÄLLGREN MÅLGREN"

-#: commands.cc:3052
+#: commands.cc:3126
 msgid "merge from one branch to another asymmetrically"
 msgstr "slå ihop asymmetriskt från en gren till en annan"

-#: commands.cc:3086 commands.cc:3089
+#: commands.cc:3160 commands.cc:3163
 #, c-format
 msgid "branch '%s' is not merged\n"
 msgstr "löven i grenen '%s' är inte ihopslagna\n"

-#: commands.cc:3094
+#: commands.cc:3168
 #, c-format
 msgid "propagating %s -> %s\n"
 msgstr "propagerar %s -> %s\n"

-#: commands.cc:3096
+#: commands.cc:3170
 #, c-format
 msgid "[target] %s\n"
 msgstr "[mål]        %s\n"

-#: commands.cc:3101
+#: commands.cc:3175
 #, c-format
 msgid "branch '%s' is up-to-date with respect to branch '%s'\n"
 msgstr "grenen '%s' är uppdaterad med avseende på grenen '%s'\n"

-#: commands.cc:3103
+#: commands.cc:3177
 #, c-format
 msgid "no action taken\n"
 msgstr "inget utfördes\n"

-#: commands.cc:3107
+#: commands.cc:3181
 #, c-format
 msgid "no merge necessary; putting %s in branch '%s'\n"
 msgstr "ingen ihopslagning behövs; sätter %s i grenen '%s'\n"

-#: commands.cc:3140
+#: commands.cc:3214
 msgid "refresh the inodeprint cache"
 msgstr "uppdatera inodeprint-cachen"

-#: commands.cc:3148
+#: commands.cc:3222
 msgid "LEFT-REVISION RIGHT-REVISION DEST-BRANCH"
 msgstr "VÄNSTERREVISION HÖGERREVISION MÅLGREN"

-#: commands.cc:3149
+#: commands.cc:3223
 msgid "merge two explicitly given revisions, placing result in given branch"
 msgstr ""
 "slå ihop två explicit angivna revisioner, placera resultatet i angiven gren"

-#: commands.cc:3163
+#: commands.cc:3237
 #, c-format
 msgid "%s and %s are the same revision, aborting"
 msgstr "%s och %s är samma revision, avbryter"

-#: commands.cc:3165 commands.cc:3167
+#: commands.cc:3239 commands.cc:3241
 #, c-format
 msgid "%s is already an ancestor of %s"
 msgstr "%s är redan förfader till %s"

-#: commands.cc:3192
+#: commands.cc:3266
 msgid "(revision|file|key) PARTIAL-ID"
 msgstr "(revision|file|key) PARTIAL-ID"

-#: commands.cc:3193
+#: commands.cc:3267
 msgid "complete partial id"
 msgstr "utöka den partiella identiteten"

-#: commands.cc:3202
+#: commands.cc:3276
 #, c-format
 msgid "non-hex digits in partial id"
 msgstr "något tecken i den partiella identiteten är inte hexadecimalt"

-#: commands.cc:3241
+#: commands.cc:3315
 #, fuzzy
 msgid "revert file(s), dir(s) or entire workspace (\".\")"
 msgstr "återställ fil(er), katalog(er) eller hela arbetskopian (\".\")"

-#: commands.cc:3318
+#: commands.cc:3392
 #, c-format
 msgid "reverting %s"
 msgstr "återställer %s"

-#: commands.cc:3322
+#: commands.cc:3396
 #, c-format
 msgid "no file version %s found in database for %s"
 msgstr "filversionen %s finns inte i databasen för %s"

-#: commands.cc:3343
+#: commands.cc:3417
 msgid "RCSFILE..."
 msgstr "RCSFIL..."

-#: commands.cc:3344
+#: commands.cc:3418
 msgid ""
 "parse versions in RCS files\n"
 "this command doesn't reconstruct or import revisions.you probably want "
@@ -1337,36 +1343,36 @@ msgstr ""
 "detta kommando importerar inte revisioner; du vill antagligen använda\n"
 "cvs_import"

-#: commands.cc:3360
+#: commands.cc:3434
 msgid "rcs"
 msgstr "rcs"

-#: commands.cc:3360
+#: commands.cc:3434
 msgid "CVSROOT"
 msgstr "CVSROOT"

-#: commands.cc:3360
+#: commands.cc:3434
 msgid "import all versions in CVS repository"
 msgstr "importera alla revisioner i ett CVS-arkiv"

-#: commands.cc:3423
+#: commands.cc:3497
 msgid "PATH"
 msgstr "SÖKVÄG"

-#: commands.cc:3424
+#: commands.cc:3498
 msgid "print annotated copy of the file from REVISION"
 msgstr "skriv ut filen ur REVISION med extra detaljer"

-#: commands.cc:3444
+#: commands.cc:3518
 #, c-format
 msgid "no revision for file '%s' in database"
 msgstr "filen '%s' har ingen revision i databasen"

-#: commands.cc:3462
+#: commands.cc:3536
 msgid "[FILE] ..."
 msgstr "[FIL] ..."

-#: commands.cc:3463
+#: commands.cc:3537
 msgid ""
 "print history in reverse order (filtering by 'FILE'). If one or more\n"
 "revisions are given, use them as a starting point."
@@ -1375,35 +1381,35 @@ msgstr ""
 "arbetskatalogen om inga filer angivits. Om en eller flera revisioner\n"
 "har angivits används de som utgångspunkter."

-#: commands.cc:3510
+#: commands.cc:3584
 #, c-format
 msgid "Unknown file '%s' for log command"
 msgstr "Okänd fil '%s'"

-#: commands.cc:3528
+#: commands.cc:3602
 #, c-format
 msgid "only one of --last/--next allowed"
 msgstr "enbart en av --last eller --next tillåten"

-#: commands.cc:3696
+#: commands.cc:3770
 msgid "[DIRECTORY]"
 msgstr "[KATALOG]"

-#: commands.cc:3696
+#: commands.cc:3770
 #, fuzzy
 msgid "setup a new workspace directory, default to current"
 msgstr "initiera en ny arbetskatalog (nuvarande om katalog ej anges)"

-#: commands.cc:3702
+#: commands.cc:3776
 #, c-format
 msgid "need --branch argument for setup"
 msgstr "du måste ange en gren med --branch till kommandot \"setup\""

-#: commands.cc:3716
+#: commands.cc:3790
 msgid "automation"
 msgstr "automatisering"

-#: commands.cc:3717
+#: commands.cc:3791
 msgid ""
 "interface_version\n"
 "heads [BRANCH]\n"
@@ -1447,40 +1453,40 @@ msgstr ""
 "get_revision [REVID]\n"
 "keys\n"

-#: commands.cc:3737
+#: commands.cc:3811
 msgid "automation interface"
 msgstr "automatiseringsgränssnitt"

-#: commands.cc:3751 commands.cc:3767
+#: commands.cc:3825 commands.cc:3841
 msgid "vars"
 msgstr "variabler"

-#: commands.cc:3751
+#: commands.cc:3825
 msgid "DOMAIN NAME VALUE"
 msgstr "DOMÄN NAMN VÄRDE"

-#: commands.cc:3752
+#: commands.cc:3826
 msgid "set the database variable NAME to VALUE, in domain DOMAIN"
 msgstr "sätt databasvariabeln NAMN till VÄRDE i domänen DOMÄN"

-#: commands.cc:3767
+#: commands.cc:3841
 msgid "DOMAIN NAME"
 msgstr "DOMÄN NAMN"

-#: commands.cc:3768
+#: commands.cc:3842
 msgid "remove the database variable NAME in domain DOMAIN"
 msgstr "ta bort databasvariabeln NAMN från domänen DOMÄN"

-#: commands.cc:3779
+#: commands.cc:3853
 #, c-format
 msgid "no var with name %s in domain %s"
 msgstr "det finns inga variabler med namnet %s i domänen %s"

-#: commands.cc:3783
+#: commands.cc:3857
 msgid "REVID"
 msgstr "REVID"

-#: commands.cc:3784
+#: commands.cc:3858
 msgid "dump the roster associated with the given REVID"
 msgstr "skriv ut den roster som hör ihop med angivet REVID"

@@ -1496,7 +1502,7 @@ msgstr "markeringar"
 msgid "markings"
 msgstr "markeringar"

-#: database_check.cc:319 database_check.cc:507 netsync.cc:2706
+#: database_check.cc:319 database_check.cc:507 netsync.cc:2719
 #: rcs_import.cc:1226
 msgid "revisions"
 msgstr "revisioner"
@@ -1505,7 +1511,7 @@ msgstr "föräldraskap"
 msgid "ancestry"
 msgstr "föräldraskap"

-#: database_check.cc:451 netsync.cc:2710
+#: database_check.cc:451 netsync.cc:2723
 msgid "keys"
 msgstr "nycklar"

@@ -1869,7 +1875,7 @@ msgstr "kan inte skapa %s; den finns red
 msgid "cannot create %s; it already exists"
 msgstr "kan inte skapa %s; den finns redan"

-#: database.cc:515
+#: database.cc:511
 #, c-format
 msgid ""
 "schema version    : %s\n"
@@ -1910,52 +1916,52 @@ msgstr ""
 "  cert                : %u\n"
 "  total               : %u\n"

-#: database.cc:569
+#: database.cc:565
 #, c-format
 msgid "database schema version: %s"
 msgstr "databasens schemaversion: %s"

-#: database.cc:646
+#: database.cc:642
 #, c-format
 msgid "multiple statements in query: %s\n"
 msgstr "multipla kommandon i databasfråga: %s\n"

-#: database.cc:652
+#: database.cc:648
 #, c-format
 msgid "wanted %d columns got %d in query: %s\n"
 msgstr "ville ha %d kolumner, fick %d i databasfråga: %s\n"

-#: database.cc:710
+#: database.cc:706
 #, c-format
 msgid "null result in query: %s\n"
 msgstr "inget resultat med databasfråga: %s\n"

-#: database.cc:728
+#: database.cc:724
 #, c-format
 msgid "wanted %d rows got %s in query: %s\n"
 msgstr "ville ha %d rader, fick %s i databasfråga: %s\n"

-#: database.cc:1775
+#: database.cc:1771
 #, c-format
 msgid "another key with name '%s' already exists"
 msgstr "det finns redan en annan nyckel med namnet '%s'"

-#: database.cc:2819
+#: database.cc:2815
 #, c-format
 msgid "no database specified"
 msgstr "ingen databas angiven"

-#: database.cc:2827
+#: database.cc:2823
 #, c-format
 msgid "database %s does not exist"
 msgstr "databasen %s finns inte"

-#: database.cc:2828
+#: database.cc:2824
 #, c-format
 msgid "%s is a directory, not a database"
 msgstr "%s är en katalog, inte en databas"

-#: database.cc:2853
+#: database.cc:2849
 #, c-format
 msgid "could not open database '%s': %s"
 msgstr "kunde inte öppna databasen '%s': %s"
@@ -2179,37 +2185,37 @@ msgstr "lösen för '%s' stämmer inte"
 msgid "passphrase for '%s' is incorrect"
 msgstr "lösen för '%s' stämmer inte"

-#: lua.cc:538 lua.cc:567 lua.cc:582
+#: lua.cc:539 lua.cc:568 lua.cc:583
 #, c-format
 msgid "%s called with an invalid parameter"
 msgstr "%s anropades med inkorrekt parameter"

-#: lua.cc:585
+#: lua.cc:586
 #, c-format
 msgid "Directory '%s' does not exists"
 msgstr "Katalogen '%s' finns inte"

-#: lua.cc:586 rcs_import.cc:1217
+#: lua.cc:587 rcs_import.cc:1217
 #, c-format
 msgid "'%s' is not a directory"
 msgstr "'%s' är inte en katalog"

-#: lua.cc:605 lua.cc:9