{"thread":{"id":"3887","subject":"[PATCH 0/9] Add a testsuite to stgit (take 3), and more","startedAt":"2006-04-16T10:41:44Z","lastAt":"2006-04-16T10:52:46Z","messageCount":10,"participants":["Yann Dirson"],"isPatch":true,"patchVersion":1,"patchTotal":9},"messages":[{"id":"18727","messageId":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":null,"subject":"[PATCH 0/9] Add a testsuite to stgit (take 3), and more","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:41:44Z","receivedAt":"2006-04-16T10:41:44Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"This new revision of my testsuite patches include:\n\n- cleanup of the repo-initialization stuff in test-lib.sh: now\ntest_create_repo creates an empty commit to start with, so that HEAD\npoints to an existing master branch.  This makes test_stg_init\nuseless, rather let tests can \"stg init\" directly.\n\n- steal the git t/README as well, and adapt it.  Proposes a numbering\nconvention for stgit testcases.\n\n- activation and refining of the testcase which I had left commented\nout in t1000, and a fix for the bug it shows\n\n- fix for a nasty caching issue in stg clone, and associated test\n\n- 2 testcases showing problems with detection of applied patches in\n\"push\" after a pull when our patches were integrated upstream.  No fix\nhere, though, so you'll see t1200 and t1201 failing.\n\n\nAnd not directly related to the testsuite:\n- more bugs listed in TODO\n- support for per-user templates for mail and export, in ~/.stgit/templates\n\n-- \nYann Dirson    <ydirson@altern.org> |\nDebian-related: <dirson@debian.org> |   Support Debian GNU/Linux:\n                                    |  Freedom, Power, Stability, Gratis\n     http://ydirson.free.fr/        | Check <http://www.debian.org/>\n"},{"id":"18729","messageId":"20060416105228.9884.52480.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 1/9] Add a testsuite framework copied from git-core","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:28Z","receivedAt":"2006-04-16T10:52:28Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nSee git's t/README for details on how to use this framework.\n\nThere is no integration yet in the toplevel Makefile, I'll let\npython masters take care of this.  Use \"make -C t\" to run the\ntests for now.\n\nA patch-naming policy should be defined for stgit, since the\ngit one does not apply.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n TODO             |    2 -\n t/Makefile       |   25 ++++++\n t/README         |  210 ++++++++++++++++++++++++++++++++++++++++++++++++++++++\n t/t0000-dummy.sh |   17 ++++\n t/test-lib.sh    |  203 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n 5 files changed, 456 insertions(+), 1 deletions(-)\n\ndiff --git a/TODO b/TODO\nindex e5affe0..d97ffd1 100644\n--- a/TODO\n+++ b/TODO\n@@ -6,7 +6,7 @@ The TODO list until 1.0:\n - debian package support\n - man page\n - code execution allowed from templates\n-- regression tests\n+- more regression tests\n - release 1.0\n \n \ndiff --git a/t/Makefile b/t/Makefile\nnew file mode 100644\nindex 0000000..d5d7b6f\n--- /dev/null\n+++ b/t/Makefile\n@@ -0,0 +1,25 @@\n+# Run tests\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+#\n+\n+#GIT_TEST_OPTS=--verbose --debug\n+SHELL_PATH ?= $(SHELL)\n+TAR ?= $(TAR)\n+\n+# Shell quote;\n+SHELL_PATH_SQ = $(subst ','\\'',$(SHELL_PATH))\n+\n+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)\n+\n+all: $(T) clean\n+\n+$(T):\n+\t@echo \"*** $@ ***\"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)\n+\n+clean:\n+\trm -fr trash\n+\n+.PHONY: $(T) clean\n+.NOPARALLEL:\n+\ndiff --git a/t/README b/t/README\nnew file mode 100644\nindex 0000000..d88bad2\n--- /dev/null\n+++ b/t/README\n@@ -0,0 +1,210 @@\n+Core GIT Tests\n+==============\n+\n+This directory holds many test scripts for core GIT tools.  The\n+first part of this short document describes how to run the tests\n+and read their output.\n+\n+When fixing the tools or adding enhancements, you are strongly\n+encouraged to add tests in this directory to cover what you are\n+trying to fix or enhance.  The later part of this short document\n+describes how your test scripts should be organized.\n+\n+The mechanism that powers this testsuite is directly imported from the\n+Core GIT Tests, in directory t/ of the git repository.  Files are base\n+on Core GIT version 1.3.0.rc4.g5069.\n+\n+\n+Running Tests\n+-------------\n+\n+The easiest way to run tests is to say \"make -C t\".  This runs all\n+the tests.\n+\n+    *** t0000-basic.sh ***\n+    *   ok 1: .git/objects should be empty after git-init-db in an empty repo.\n+    *   ok 2: .git/objects should have 256 subdirectories.\n+    *   ok 3: git-update-index without --add should fail adding.\n+    ...\n+    *   ok 23: no diff after checkout and git-update-index --refresh.\n+    * passed all 23 test(s)\n+    *** t0100-environment-names.sh ***\n+    *   ok 1: using old names should issue warnings.\n+    *   ok 2: using old names but having new names should not issue warnings.\n+    ...\n+\n+Or you can run each test individually from command line, like\n+this:\n+\n+    $ sh ./t3001-ls-files-killed.sh\n+    *   ok 1: git-update-index --add to add various paths.\n+    *   ok 2: git-ls-files -k to show killed files.\n+    *   ok 3: validate git-ls-files -k output.\n+    * passed all 3 test(s)\n+\n+You can pass --verbose (or -v), --debug (or -d), and --immediate\n+(or -i) command line argument to the test.\n+\n+--verbose::\n+\tThis makes the test more verbose.  Specifically, the\n+\tcommand being run and their output if any are also\n+\toutput.\n+\n+--debug::\n+\tThis may help the person who is developing a new test.\n+\tIt causes the command defined with test_debug to run.\n+\n+--immediate::\n+\tThis causes the test to immediately exit upon the first\n+\tfailed test.\n+\n+\n+Naming Tests\n+------------\n+\n+The test files are named as:\n+\n+\ttNNNN-commandname-details.sh\n+\n+where N is a decimal digit.\n+\n+Here is a proposal for numbering, loosely based on the Core GIT\n+numbering conventions.\n+\n+First two digit tells the particular command we are testing:\n+\n+\t00 - stgit itself\n+\t10 - branch\n+\t11 - clone\n+\t12 - push\n+\n+Third and fourth digit (optionally) tells the particular switch or\n+group of switches we are testing.\n+\n+If you create files under t/ directory (i.e. here) that is not\n+the top-level test script, never name the file to match the above\n+pattern.  The Makefile here considers all such files as the\n+top-level test script and tries to run all of them.  A care is\n+especially needed if you are creating a common test library\n+file, similar to test-lib.sh, because such a library file may\n+not be suitable for standalone execution.\n+\n+\n+Writing Tests\n+-------------\n+\n+The test script is written as a shell script.  It should start\n+with the standard \"#!/bin/sh\" with copyright notices, and an\n+assignment to variable 'test_description', like this:\n+\n+\t#!/bin/sh\n+\t#\n+\t# Copyright (c) 2005 Junio C Hamano\n+\t#\n+\n+\ttest_description='xxx test (option --frotz)\n+\n+\tThis test registers the following structure in the cache\n+\tand tries to run git-ls-files with option --frotz.'\n+\n+\n+Source 'test-lib.sh'\n+--------------------\n+\n+After assigning test_description, the test script should source\n+test-lib.sh like this:\n+\n+\t. ./test-lib.sh\n+\n+This test harness library does the following things:\n+\n+ - If the script is invoked with command line argument --help\n+   (or -h), it shows the test_description and exits.\n+\n+ - Creates an empty test directory with an empty .git/objects\n+   database and chdir(2) into it.  This directory is 't/trash'\n+   if you must know, but I do not think you care.\n+\n+ - Defines standard test helper functions for your scripts to\n+   use.  These functions are designed to make all scripts behave\n+   consistently when command line arguments --verbose (or -v),\n+   --debug (or -d), and --immediate (or -i) is given.\n+\n+\n+End with test_done\n+------------------\n+\n+Your script will be a sequence of tests, using helper functions\n+from the test harness library.  At the end of the script, call\n+'test_done'.\n+\n+\n+Test harness library\n+--------------------\n+\n+There are a handful helper functions defined in the test harness\n+library for your script to use.\n+\n+ - test_expect_success <message> <script>\n+\n+   This takes two strings as parameter, and evaluates the\n+   <script>.  If it yields success, test is considered\n+   successful.  <message> should state what it is testing.\n+\n+   Example:\n+\n+\ttest_expect_success \\\n+\t    'git-write-tree should be able to write an empty tree.' \\\n+\t    'tree=$(git-write-tree)'\n+\n+ - test_expect_failure <message> <script>\n+\n+   This is the opposite of test_expect_success.  If <script>\n+   yields success, test is considered a failure.\n+\n+   Example:\n+\n+\ttest_expect_failure \\\n+\t    'git-update-index without --add should fail adding.' \\\n+\t    'git-update-index should-be-empty'\n+\n+ - test_debug <script>\n+\n+   This takes a single argument, <script>, and evaluates it only\n+   when the test script is started with --debug command line\n+   argument.  This is primarily meant for use during the\n+   development of a new test script.\n+\n+ - test_done\n+\n+   Your test script must have test_done at the end.  Its purpose\n+   is to summarize successes and failures in the test script and\n+   exit with an appropriate error code.\n+\n+\n+Tips for Writing Tests\n+----------------------\n+\n+As with any programming projects, existing programs are the best\n+source of the information.  However, do _not_ emulate\n+t0000-basic.sh when writing your tests.  The test is special in\n+that it tries to validate the very core of GIT.  For example, it\n+knows that there will be 256 subdirectories under .git/objects/,\n+and it knows that the object ID of an empty tree is a certain\n+40-byte string.  This is deliberately done so in t0000-basic.sh\n+because the things the very basic core test tries to achieve is\n+to serve as a basis for people who are changing the GIT internal\n+drastically.  For these people, after making certain changes,\n+not seeing failures from the basic test _is_ a failure.  And\n+such drastic changes to the core GIT that even changes these\n+otherwise supposedly stable object IDs should be accompanied by\n+an update to t0000-basic.sh.\n+\n+However, other tests that simply rely on basic parts of the core\n+GIT working properly should not have that level of intimate\n+knowledge of the core GIT internals.  If all the test scripts\n+hardcoded the object IDs like t0000-basic.sh does, that defeats\n+the purpose of t0000-basic.sh, which is to isolate that level of\n+validation in one place.  Your test also ends up needing\n+updating when such a change to the internal happens, so do _not_\n+do it and leave the low level of validation to t0000-basic.sh.\ndiff --git a/t/t0000-dummy.sh b/t/t0000-dummy.sh\nnew file mode 100755\nindex 0000000..1a9bd87\n--- /dev/null\n+++ b/t/t0000-dummy.sh\n@@ -0,0 +1,17 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Yann Dirson\n+#\n+\n+test_description='Dummy test.\n+\n+Only to test the testing environment.\n+'\n+\n+. ./test-lib.sh\n+\n+test_expect_success \\\n+    'check stgit can be run' \\\n+    'stg version'\n+\n+test_done\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nnew file mode 100755\nindex 0000000..6339c54\n--- /dev/null\n+++ b/t/test-lib.sh\n@@ -0,0 +1,203 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2005 Junio C Hamano\n+# Copyright (c) 2006 Yann Dirson - tuning for stgit\n+#\n+\n+# For repeatability, reset the environment to known value.\n+LANG=C\n+LC_ALL=C\n+PAGER=cat\n+TZ=UTC\n+export LANG LC_ALL PAGER TZ\n+unset AUTHOR_DATE\n+unset AUTHOR_EMAIL\n+unset AUTHOR_NAME\n+unset COMMIT_AUTHOR_EMAIL\n+unset COMMIT_AUTHOR_NAME\n+unset GIT_ALTERNATE_OBJECT_DIRECTORIES\n+unset GIT_AUTHOR_DATE\n+GIT_AUTHOR_EMAIL=author@example.com\n+GIT_AUTHOR_NAME='A U Thor'\n+unset GIT_COMMITTER_DATE\n+GIT_COMMITTER_EMAIL=committer@example.com\n+GIT_COMMITTER_NAME='C O Mitter'\n+unset GIT_DIFF_OPTS\n+unset GIT_DIR\n+unset GIT_EXTERNAL_DIFF\n+unset GIT_INDEX_FILE\n+unset GIT_OBJECT_DIRECTORY\n+unset SHA1_FILE_DIRECTORIES\n+unset SHA1_FILE_DIRECTORY\n+export GIT_AUTHOR_EMAIL GIT_AUTHOR_NAME\n+export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME\n+\n+# Each test should start with something like this, after copyright notices:\n+#\n+# test_description='Description of this test...\n+# This test checks if command xyzzy does the right thing...\n+# '\n+# . ./test-lib.sh\n+\n+error () {\n+\techo \"* error: $*\"\n+\ttrap - exit\n+\texit 1\n+}\n+\n+say () {\n+\techo \"* $*\"\n+}\n+\n+test \"${test_description}\" != \"\" ||\n+error \"Test script did not set test_description.\"\n+\n+while test \"$#\" -ne 0\n+do\n+\tcase \"$1\" in\n+\t-d|--d|--de|--deb|--debu|--debug)\n+\t\tdebug=t; shift ;;\n+\t-i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)\n+\t\timmediate=t; shift ;;\n+\t-h|--h|--he|--hel|--help)\n+\t\techo \"$test_description\"\n+\t\texit 0 ;;\n+\t-v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)\n+\t\tverbose=t; shift ;;\n+\t*)\n+\t\tbreak ;;\n+\tesac\n+done\n+\n+exec 5>&1\n+if test \"$verbose\" = \"t\"\n+then\n+\texec 4>&2 3>&1\n+else\n+\texec 4>/dev/null 3>/dev/null\n+fi\n+\n+test_failure=0\n+test_count=0\n+\n+trap 'echo >&5 \"FATAL: Unexpected exit with code $?\"; exit 1' exit\n+\n+\n+# You are not expected to call test_ok_ and test_failure_ directly, use\n+# the text_expect_* functions instead.\n+\n+test_ok_ () {\n+\ttest_count=$(expr \"$test_count\" + 1)\n+\tsay \"  ok $test_count: $@\"\n+}\n+\n+test_failure_ () {\n+\ttest_count=$(expr \"$test_count\" + 1)\n+\ttest_failure=$(expr \"$test_failure\" + 1);\n+\tsay \"FAIL $test_count: $1\"\n+\tshift\n+\techo \"$@\" | sed -e 's/^/\t/'\n+\ttest \"$immediate\" = \"\" || { trap - exit; exit 1; }\n+}\n+\n+\n+test_debug () {\n+\ttest \"$debug\" = \"\" || eval \"$1\"\n+}\n+\n+test_run_ () {\n+\teval >&3 2>&4 \"$1\"\n+\teval_ret=\"$?\"\n+\treturn 0\n+}\n+\n+test_expect_failure () {\n+\ttest \"$#\" = 2 ||\n+\terror \"bug in the test script: not 2 parameters to test-expect-failure\"\n+\tsay >&3 \"expecting failure: $2\"\n+\ttest_run_ \"$2\"\n+\tif [ \"$?\" = 0 -a \"$eval_ret\" != 0 ]\n+\tthen\n+\t\ttest_ok_ \"$1\"\n+\telse\n+\t\ttest_failure_ \"$@\"\n+\tfi\n+}\n+\n+test_expect_success () {\n+\ttest \"$#\" = 2 ||\n+\terror \"bug in the test script: not 2 parameters to test-expect-success\"\n+\tsay >&3 \"expecting success: $2\"\n+\ttest_run_ \"$2\"\n+\tif [ \"$?\" = 0 -a \"$eval_ret\" = 0 ]\n+\tthen\n+\t\ttest_ok_ \"$1\"\n+\telse\n+\t\ttest_failure_ \"$@\"\n+\tfi\n+}\n+\n+test_expect_code () {\n+\ttest \"$#\" = 3 ||\n+\terror \"bug in the test script: not 3 parameters to test-expect-code\"\n+\tsay >&3 \"expecting exit code $1: $3\"\n+\ttest_run_ \"$3\"\n+\tif [ \"$?\" = 0 -a \"$eval_ret\" = \"$1\" ]\n+\tthen\n+\t\ttest_ok_ \"$2\"\n+\telse\n+\t\ttest_failure_ \"$@\"\n+\tfi\n+}\n+\n+# Most tests can use the created repository, but some amy need to create more.\n+# Usage: test_create_repo <directory>\n+test_create_repo () {\n+\ttest \"$#\" = 1 ||\n+\terror \"bug in the test script: not 1 parameter to test-create-repo\"\n+\towd=`pwd`\n+\trepo=\"$1\"\n+\tmkdir \"$repo\"\n+\tcd \"$repo\" || error \"Cannot setup test environment\"\n+\tgit-init-db 2>/dev/null ||\n+\terror \"cannot run git-init-db -- have you installed git-core?\"\n+\tmv .git/hooks .git/hooks-disabled\n+\techo \"empty start\" |\n+\tgit-commit-tree `git-write-tree` >.git/refs/heads/master 2>/dev/null ||\n+\terror \"cannot run git-commit -- is your git-core funtionning?\"\n+\tcd \"$owd\"\n+}\n+\n+test_done () {\n+\ttrap - exit\n+\tcase \"$test_failure\" in\n+\t0)\n+\t\t# We could:\n+\t\t# cd .. && rm -fr trash\n+\t\t# but that means we forbid any tests that use their own\n+\t\t# subdirectory from calling test_done without coming back\n+\t\t# to where they started from.\n+\t\t# The Makefile provided will clean this test area so\n+\t\t# we will leave things as they are.\n+\n+\t\tsay \"passed all $test_count test(s)\"\n+\t\texit 0 ;;\n+\n+\t*)\n+\t\tsay \"failed $test_failure among $test_count test(s)\"\n+\t\texit 1 ;;\n+\n+\tesac\n+}\n+\n+# Test the binaries we have just built.  The tests are kept in\n+# t/ subdirectory and are run in trash subdirectory.\n+PATH=$(pwd)/..:$PATH\n+export PATH\n+\n+\n+# Test repository\n+test=trash\n+rm -fr \"$test\"\n+test_create_repo $test\n+cd \"$test\"\n"},{"id":"18728","messageId":"20060416105230.9884.27600.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 2/9] Add list of bugs to TODO","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:30Z","receivedAt":"2006-04-16T10:52:30Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nSince there is no formal place to register bugs, other than the git\nml, I have added a couple of them, some of them already mentionned on\nthe ml but still around.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n TODO |   15 +++++++++++++++\n 1 files changed, 15 insertions(+), 0 deletions(-)\n\ndiff --git a/TODO b/TODO\nindex d97ffd1..a13e511 100644\n--- a/TODO\n+++ b/TODO\n@@ -7,6 +7,7 @@ The TODO list until 1.0:\n - man page\n - code execution allowed from templates\n - more regression tests\n+- stg help should probably pipe through the $PAGER\n - release 1.0\n \n \n@@ -17,3 +18,17 @@ The future, when time allows or if someo\n   synchronising with other patches (diff format or in other\n   repositories)\n - write bash-completion script for the StGIT commands\n+- support for branches with / in names\n+  (ml: \"Handle branch names with slashes\")\n+- \"pull\" argument should default to a sane value, \"origin\" is wrong in\n+  many cases\n+\n+Bugs:\n+\n+- the following commands break in subdirs:\n+  - refresh (ml: \"Running StGIT in subdirectories\")\n+- \"stg show\" on empty patch shows previous patch\n+- \"stg add\" is accepted when no patch is applied, then any push says\n+  one must refresh first, which is obviously wrong\n+- \"stg add\" on files already added should print a notice, so that the\n+  user can catch a possible typo\n"},{"id":"18736","messageId":"20060416105232.9884.62033.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 3/9] Add a couple of safety checks to series creation","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:32Z","receivedAt":"2006-04-16T10:52:32Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\n- we must check first whether the operation can complete, instead of\n  bombing out halfway.  That means checking that nothing will prevent\n  the creation of stgit data (note that calling is_initialised() is\n  not enough, as it does not catch a bogus file), which is tested by\n  the 3 first couple of testcases, and fixed in stack.py\n\n- creating the git branch unconditionally before knowing whether\n  creation of the stgit stuff can be completed is a problem as well:\n  being atomic would be much much better.  To emulate atomicity (which\n  comeds in the next patch), this patch does a somewhat dirty hack to\n  branch.py: we first attempt to create the stgit stuff, and if that\n  succeeds, we create the git branch: it is much easier to do at first\n  a quick check that the latter will succeed.  Testcase 7/8 ensure\n  that such a safety check has not been forgotten.\n\n- when git already reports a problem with that head we would like to\n  create, we should catch it.  Testcase 9/10 creates such a situation,\n  and the fix to git.py allows to catch the error spit out by\n  git-rev-parse.  I cannot tell why the stderr lines were not included\n  by the Popen3 object.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n stgit/commands/branch.py |    5 ++-\n stgit/git.py             |    4 ++\n stgit/stack.py           |    7 +++-\n t/t1000-branch-create.sh |   82 ++++++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 95 insertions(+), 3 deletions(-)\n\ndiff --git a/stgit/commands/branch.py b/stgit/commands/branch.py\nindex c4b5945..be501a8 100644\n--- a/stgit/commands/branch.py\n+++ b/stgit/commands/branch.py\n@@ -126,8 +126,11 @@ def func(parser, options, args):\n         if len(args) == 2:\n             tree_id = git_id(args[1])\n \n-        git.create_branch(args[0], tree_id)\n+        if git.branch_exists(args[0]):\n+            raise CmdException, 'Branch \"%s\" already exists' % args[0]\n+        \n         stack.Series(args[0]).init()\n+        git.create_branch(args[0], tree_id)\n \n         print 'Branch \"%s\" created.' % args[0]\n         return\ndiff --git a/stgit/git.py b/stgit/git.py\nindex d75b54e..8523455 100644\n--- a/stgit/git.py\n+++ b/stgit/git.py\n@@ -272,9 +272,11 @@ def rev_parse(git_id):\n def branch_exists(branch):\n     \"\"\"Existence check for the named branch\n     \"\"\"\n-    for line in _output_lines(['git-rev-parse', '--symbolic', '--all']):\n+    for line in _output_lines('git-rev-parse --symbolic --all 2>&1'):\n         if line.strip() == branch:\n             return True\n+        if re.compile('[ |/]'+branch+' ').search(line):\n+            raise GitException, 'Bogus branch: %s' % line\n     return False\n \n def create_branch(new_branch, tree_id = None):\ndiff --git a/stgit/stack.py b/stgit/stack.py\nindex f4d7490..c14e029 100644\n--- a/stgit/stack.py\n+++ b/stgit/stack.py\n@@ -431,8 +431,13 @@ class Series:\n         \"\"\"\n         bases_dir = os.path.join(self.__base_dir, 'refs', 'bases')\n \n-        if self.is_initialised():\n+        if os.path.exists(self.__patch_dir):\n             raise StackException, self.__patch_dir + ' already exists'\n+        if os.path.exists(self.__refs_dir):\n+            raise StackException, self.__refs_dir + ' already exists'\n+        if os.path.exists(self.__base_file):\n+            raise StackException, self.__base_file + ' already exists'\n+\n         os.makedirs(self.__patch_dir)\n \n         if not os.path.isdir(bases_dir):\ndiff --git a/t/t1000-branch-create.sh b/t/t1000-branch-create.sh\nnew file mode 100755\nindex 0000000..f0c2367\n--- /dev/null\n+++ b/t/t1000-branch-create.sh\n@@ -0,0 +1,82 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Yann Dirson\n+#\n+\n+test_description='Branch operations.\n+\n+Exercises the \"stg branch\" commands.\n+'\n+\n+. ./test-lib.sh\n+\n+stg init\n+\n+test_expect_failure \\\n+    'Try to create an stgit branch with a spurious refs/patches/ entry' \\\n+    'find .git -name foo | xargs rm -rf &&\n+     touch .git/refs/patches/foo &&\n+     stg branch -c foo\n+'\n+\n+test_expect_success \\\n+    'Check no part of the branch was created' \\\n+    'test \"`find .git -name foo | tee /dev/stderr`\" = \".git/refs/patches/foo\" &&\n+     ( grep foo .git/HEAD; test $? = 1 )\n+'\n+\n+test_expect_failure \\\n+    'Try to create an stgit branch with a spurious patches/ entry' \\\n+    'find .git -name foo | xargs rm -rf &&\n+     touch .git/patches/foo &&\n+     stg branch -c foo\n+'\n+\n+test_expect_success \\\n+    'Check no part of the branch was created' \\\n+    'test \"`find .git -name foo | tee /dev/stderr`\" = \".git/patches/foo\" &&\n+     ( grep foo .git/HEAD; test $? = 1 )\n+'\n+\n+test_expect_failure \\\n+    'Try to create an stgit branch with a spurious refs/bases/ entry' \\\n+    'find .git -name foo | xargs rm -rf &&\n+     touch .git/refs/bases/foo &&\n+     stg branch -c foo\n+'\n+\n+test_expect_success \\\n+    'Check no part of the branch was created' \\\n+    'test \"`find .git -name foo | tee /dev/stderr`\" = \".git/refs/bases/foo\" &&\n+     ( grep foo .git/HEAD; test $? = 1 )\n+'\n+\n+\n+test_expect_failure \\\n+    'Try to create an stgit branch with an existing git branch by that name' \\\n+    'find .git -name foo | xargs rm -rf &&\n+     cp .git/refs/heads/master .git/refs/heads/foo &&\n+     stg branch -c foo\n+'\n+\n+test_expect_success \\\n+    'Check no part of the branch was created' \\\n+    'test \"`find .git -name foo | tee /dev/stderr`\" = \".git/refs/heads/foo\" &&\n+     ( grep foo .git/HEAD; test $? = 1 )\n+'\n+\n+\n+test_expect_failure \\\n+    'Try to create an stgit branch with an invalid refs/heads/ entry' \\\n+    'find .git -name foo | xargs rm -rf &&\n+     touch .git/refs/heads/foo &&\n+     stg branch -c foo\n+'\n+\n+test_expect_success \\\n+    'Check no part of the branch was created' \\\n+    'test \"`find .git -name foo | tee /dev/stderr`\" = \".git/refs/heads/foo\" &&\n+     ( grep foo .git/HEAD; test $? = 1 )\n+'\n+\n+test_done\n"},{"id":"18732","messageId":"20060416105234.9884.38492.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 4/9] Make branch creation atomic","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:35Z","receivedAt":"2006-04-16T10:52:35Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nThis patch adds an optional create_at parameter to Series.init(), to\npass a git.branch_create() parameter if we want it to be called.  This\nparameter can be None, so the test for False has to be explicit.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n stgit/commands/branch.py |    6 +-----\n stgit/stack.py           |    8 +++++---\n 2 files changed, 6 insertions(+), 8 deletions(-)\n\ndiff --git a/stgit/commands/branch.py b/stgit/commands/branch.py\nindex be501a8..c7561a8 100644\n--- a/stgit/commands/branch.py\n+++ b/stgit/commands/branch.py\n@@ -125,12 +125,8 @@ def func(parser, options, args):\n         tree_id = None\n         if len(args) == 2:\n             tree_id = git_id(args[1])\n-\n-        if git.branch_exists(args[0]):\n-            raise CmdException, 'Branch \"%s\" already exists' % args[0]\n         \n-        stack.Series(args[0]).init()\n-        git.create_branch(args[0], tree_id)\n+        stack.Series(args[0]).init(create_at = tree_id)\n \n         print 'Branch \"%s\" created.' % args[0]\n         return\ndiff --git a/stgit/stack.py b/stgit/stack.py\nindex c14e029..19bb62a 100644\n--- a/stgit/stack.py\n+++ b/stgit/stack.py\n@@ -426,7 +426,7 @@ class Series:\n         \"\"\"\n         return os.path.isdir(self.__patch_dir)\n \n-    def init(self):\n+    def init(self, create_at=False):\n         \"\"\"Initialises the stgit series\n         \"\"\"\n         bases_dir = os.path.join(self.__base_dir, 'refs', 'bases')\n@@ -438,6 +438,9 @@ class Series:\n         if os.path.exists(self.__base_file):\n             raise StackException, self.__base_file + ' already exists'\n \n+        if (create_at!=False):\n+            git.create_branch(self.__name, create_at)\n+\n         os.makedirs(self.__patch_dir)\n \n         if not os.path.isdir(bases_dir):\n@@ -509,8 +512,7 @@ class Series:\n         \"\"\"Clones a series\n         \"\"\"\n         base = read_string(self.get_base_file())\n-        git.create_branch(target_series, tree_id = base)\n-        Series(target_series).init()\n+        Series(target_series).init(create_at = base)\n         new_series = Series(target_series)\n \n         # generate an artificial description file\n"},{"id":"18731","messageId":"20060416105237.9884.44865.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 5/9] Correctly handle refs/patches on series rename","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:37Z","receivedAt":"2006-04-16T10:52:37Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nWhen renaming a series, the refs/patches dir was not moved, and by\nchance a new one was created by the repository-upgrade code, but that\nleft the old one behind as cruft (which the safety checks added in a\nformer patch now detects).\n\nAlso adds a regression test to assert that nothing by the old name\nis left behind.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n stgit/stack.py           |    2 ++\n t/t1001-branch-rename.sh |   32 ++++++++++++++++++++++++++++++++\n 2 files changed, 34 insertions(+), 0 deletions(-)\n\ndiff --git a/stgit/stack.py b/stgit/stack.py\nindex 19bb62a..975ac21 100644\n--- a/stgit/stack.py\n+++ b/stgit/stack.py\n@@ -505,6 +505,8 @@ class Series:\n             os.rename(self.__series_dir, to_stack.__series_dir)\n         if os.path.exists(self.__base_file):\n             os.rename(self.__base_file, to_stack.__base_file)\n+        if os.path.exists(self.__refs_dir):\n+            os.rename(self.__refs_dir, to_stack.__refs_dir)\n \n         self.__init__(to_name)\n \ndiff --git a/t/t1001-branch-rename.sh b/t/t1001-branch-rename.sh\nnew file mode 100755\nindex 0000000..28da15c\n--- /dev/null\n+++ b/t/t1001-branch-rename.sh\n@@ -0,0 +1,32 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Yann Dirson\n+#\n+\n+test_description='Branch renames.\n+\n+Exercises branch renaming commands.\n+'\n+\n+. ./test-lib.sh\n+\n+test_expect_success \\\n+    'Create an stgit branch from scratch' \\\n+    'stg init &&\n+     stg branch -c foo &&\n+     stg new p1 -m \"p1\"\n+'\n+\n+test_expect_failure \\\n+    'Rename the current stgit branch' \\\n+    'stg branch -r foo bar\n+'\n+\n+test_expect_success \\\n+    'Rename an stgit branch' \\\n+    'stg branch -c buz &&\n+     stg branch -r foo bar &&\n+     test -z `find .git -name foo | tee /dev/stderr`\n+'\n+\n+test_done\n"},{"id":"18735","messageId":"20060416105239.9884.5212.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 6/9] Fix a seriously bad interaction between .git caching and repo cloning","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:39Z","receivedAt":"2006-04-16T10:52:39Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nTestcase 2 exhibits a problem with caching the location of .git while\ncloning a repository.  Since basedir.get() is called before the clone is\nbuilt, a value may get stored in the cache if we are within a\ngit-controlled tree already.  Then when constructing the object for the\nclone, a bogus .git is used, which can lead, when running tests in t/trash,\nto corruption of the stgit .git repository.\n\nTestcase 1 does not show any problem by chance, because since we have a\n./.git prepared for use by the testsuite, value \".git\" get cached, and it\nhappens that this value will be still valid after chdir'ing into the\nnewborn clone.\n\nClearing the cache at the appropriate place fixes the problem.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n stgit/basedir.py        |    6 ++++++\n stgit/commands/clone.py |    3 +++\n t/t1100-clone-under.sh  |   36 ++++++++++++++++++++++++++++++++++++\n 3 files changed, 45 insertions(+), 0 deletions(-)\n\ndiff --git a/stgit/basedir.py b/stgit/basedir.py\nindex c394572..81f2b40 100644\n--- a/stgit/basedir.py\n+++ b/stgit/basedir.py\n@@ -42,3 +42,9 @@ def get():\n             __base_dir = __output('git-rev-parse --git-dir 2> /dev/null')\n \n     return __base_dir\n+\n+def clear_cache():\n+    \"\"\"Clear the cached location of .git\n+    \"\"\"\n+    global __base_dir\n+    __base_dir = None\ndiff --git a/stgit/commands/clone.py b/stgit/commands/clone.py\nindex 9ad76a6..455dd6e 100644\n--- a/stgit/commands/clone.py\n+++ b/stgit/commands/clone.py\n@@ -51,6 +51,9 @@ def func(parser, options, args):\n     os.chdir(local_dir)\n     git.checkout(tree_id = 'HEAD')\n \n+    # be sure to forget any cached value for .git, since we're going\n+    # to work on a brand new repository\n+    basedir.clear_cache()\n     stack.Series().init()\n \n     print 'done'\ndiff --git a/t/t1100-clone-under.sh b/t/t1100-clone-under.sh\nnew file mode 100755\nindex 0000000..c86ef61\n--- /dev/null\n+++ b/t/t1100-clone-under.sh\n@@ -0,0 +1,36 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Yann Dirson\n+#\n+\n+test_description='Check cloning in a repo subdir\n+\n+Check that \"stg clone\" works in a subdir of a git tree.\n+This ensures (to some point) that a clone within a tree does\n+not corrupt the enclosing repo.\n+\n+This test must be run before any tests making use of clone.\n+'\n+\n+. ./test-lib.sh\n+\n+# Here we are in a repo, we have a ./.git\n+# Do not get rid of it, or a bug may bite out stgit repo hard\n+\n+# Need a repo to clone\n+test_create_repo foo\n+\n+test_expect_success \\\n+    'stg clone right inside a git tree' \\\n+    \"stg clone foo bar\"\n+\n+# now work in a subdir\n+mkdir sub\n+mv foo sub\n+cd sub\n+\n+test_expect_success \\\n+    'stg clone deeper under a git tree' \\\n+    \"stg clone foo bar\"\n+\n+test_done\n"},{"id":"18734","messageId":"20060416105241.9884.16429.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 7/9] Test that pulls a patch creating a file that got modified afterwards","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:41Z","receivedAt":"2006-04-16T10:52:41Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nThis demonstrates an issue wite has bitten me more than once: the stg\nbranch adds a file in one patch, and modifies it in a later patch; then all\npatches get integrated in upstream tree, and at \"stg pull\" time, stgit\nbelieves there is a conflict, even one the patches are exactly the same.\n\n\"stg push\" apparently does not consider patches one by one, or it\nwould have noticed that the patches were \"already applied\".  It reports:\n\n Pushing patch \"p1\"...Error: File \"file2\" added in branches but different\n The merge failed during \"push\". Use \"refresh\" after fixing the conflicts\n stg push: GIT index merging failed (possible conflicts)\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n t/t1200-push-modified.sh |   51 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 51 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t1200-push-modified.sh b/t/t1200-push-modified.sh\nnew file mode 100755\nindex 0000000..18a4df2\n--- /dev/null\n+++ b/t/t1200-push-modified.sh\n@@ -0,0 +1,51 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Yann Dirson\n+#\n+\n+test_description='Exercise pushing patches applied upstream.\n+\n+Especially, consider the case of a patch that adds a file,\n+while a subsequent one modifies it.\n+'\n+\n+. ./test-lib.sh\n+\n+# don't need this repo, but better not drop it, see t1100\n+#rm -rf .git\n+\n+# Need a repo to clone\n+test_create_repo foo\n+\n+test_expect_success \\\n+    'Clone tree and setup changes' \\\n+    \"stg clone foo bar &&\n+     (cd bar && stg new p1 -m p1\n+      printf 'a\\nc\\n' > file && stg add file && stg refresh &&\n+      stg new p2 -m p2\n+      printf 'a\\nb\\nc\\n' > file && stg refresh\n+     )\n+\"\n+\n+test_expect_success \\\n+    'Port those patches to orig tree' \\\n+    \"(cd foo &&\n+      GIT_DIR=../bar/.git git-format-patch --stdout bases/master..HEAD |\n+      git-am -3 -k\n+     )\n+\"\n+\n+test_expect_success \\\n+    'Pull to sync with parent, preparing for the problem' \\\n+    \"(cd bar && stg pop --all &&\n+      stg pull\n+     )\n+\"\n+\n+test_expect_success \\\n+    'Now attempt to push those patches applied upstream' \\\n+    \"(cd bar && stg push --all\n+     )\n+\"\n+\n+test_done\n"},{"id":"18733","messageId":"20060416105244.9884.26316.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 8/9] Exercise \"stg pull\" on patches just appending lines.","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:44Z","receivedAt":"2006-04-16T10:52:44Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nIt indeed reveals a problem in \"push\": appended lines are appended\nagain, as the already-applied patch is not detected.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n t/t1201-pull-trailing.sh |   48 ++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 48 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t1201-pull-trailing.sh b/t/t1201-pull-trailing.sh\nnew file mode 100755\nindex 0000000..142f894\n--- /dev/null\n+++ b/t/t1201-pull-trailing.sh\n@@ -0,0 +1,48 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2006 Yann Dirson\n+#\n+\n+test_description='test\n+\n+'\n+\n+. ./test-lib.sh\n+\n+# don't need this repo, but better not drop it, see t1100\n+#rm -rf .git\n+\n+# Need a repo to clone\n+test_create_repo foo\n+\n+test_expect_success \\\n+    'Setup and clone tree, and setup changes' \\\n+    \"(cd foo &&\n+      printf 'a\\nb\\n' > file && git add file && git commit -m .\n+     ) &&\n+     stg clone foo bar &&\n+     (cd bar && stg new p1 -m p1\n+      printf 'c\\n' >> file && stg refresh\n+     )\n+\"\n+\n+test_expect_success \\\n+    'Port those patches to orig tree' \\\n+    \"(cd foo &&\n+      GIT_DIR=../bar/.git git-format-patch --stdout bases/master..HEAD |\n+      git-am -3 -k\n+     )\n+\"\n+\n+test_expect_success \\\n+    'Pull those patches applied upstream' \\\n+    \"(cd bar && stg pull\n+     )\n+\"\n+\n+test_expect_success \\\n+    'Check that all went well' \\\n+    \"diff -u foo/file bar/file\n+\"\n+\n+test_done\n"},{"id":"18730","messageId":"20060416105246.9884.33207.stgit@gandelf.nowhere.earth","threadId":"3887","inReplyTo":"20060416104144.9884.28167.stgit@gandelf.nowhere.earth","subject":"[PATCH 9/9] Look for templates in ~/.stgit/templates as well","fromName":"Yann Dirson","fromEmail":"ydirson@altern.org","sentAt":"2006-04-16T10:52:46Z","receivedAt":"2006-04-16T10:52:46Z","isPatch":true,"sender":{"key":"ydirson@altern.org","avatar":"https://avatars.githubusercontent.com/u/1190950?v=4"},"body":"\nThis can be quite useful to avoid adding one's sig again and again to\never covermail, and to use a patchmail template that insert\nSigned-off-by lines, for those of us who prefer this to adding them to\ncommits.\n\nAlso make sure to use os.path.join() instead of hardcoded slashes.\n\nSigned-off-by: Yann Dirson <ydirson@altern.org>\n---\n\n stgit/commands/export.py |    9 ++++++---\n stgit/commands/mail.py   |   18 ++++++++++++------\n 2 files changed, 18 insertions(+), 9 deletions(-)\n\ndiff --git a/stgit/commands/export.py b/stgit/commands/export.py\nindex e7de902..fbf5690 100644\n--- a/stgit/commands/export.py\n+++ b/stgit/commands/export.py\n@@ -31,8 +31,9 @@ usage = \"\"\"%prog [options] [<dir>]\n \n Export the applied patches into a given directory (defaults to\n 'patches') in a standard unified GNU diff format. A template file\n-(defaulting to '.git/patchexport.tmpl or\n-/usr/share/stgit/templates/patchexport.tmpl') can be used for the\n+(defaulting to '.git/patchexport.tmpl' or\n+'~/.stgit/templates/patchexport.tmpl' or\n+'/usr/share/stgit/templates/patchexport.tmpl') can be used for the\n patch format. The following variables are supported in the template\n file:\n \n@@ -145,8 +146,10 @@ def func(parser, options, args):\n         patch_tmpl_list = []\n \n     patch_tmpl_list += [os.path.join(basedir.get(), 'patchexport.tmpl'),\n+                        os.path.join(os.path.expanduser('~'), '.stgit', 'templates',\n+                                     'patchexport.tmpl'),\n                         os.path.join(sys.prefix,\n-                                     'share/stgit/templates/patchexport.tmpl')]\n+                                     'share', 'stgit', 'templates', 'patchexport.tmpl')]\n     tmpl = ''\n     for patch_tmpl in patch_tmpl_list:\n         if os.path.isfile(patch_tmpl):\ndiff --git a/stgit/commands/mail.py b/stgit/commands/mail.py\nindex 470cf65..5e01ea1 100644\n--- a/stgit/commands/mail.py\n+++ b/stgit/commands/mail.py\n@@ -30,16 +30,18 @@ usage = \"\"\"%prog [options] [<patch> [<pa\n Send a patch or a range of patches (defaulting to the applied patches)\n by e-mail using the 'smtpserver' configuration option. The From\n address and the e-mail format are generated from the template file\n-passed as argument to '--template' (defaulting to .git/patchmail.tmpl\n-or /usr/share/stgit/templates/patchmail.tmpl). The To/Cc/Bcc addresses\n+passed as argument to '--template' (defaulting to\n+'.git/patchmail.tmpl' or '~/.stgit/templates/patchmail.tmpl' or or\n+'/usr/share/stgit/templates/patchmail.tmpl'). The To/Cc/Bcc addresses\n can either be added to the template file or passed via the\n corresponding command line options.\n \n A preamble e-mail can be sent using the '--cover' and/or '--edit'\n options. The first allows the user to specify a file to be used as a\n template. The latter option will invoke the editor on the specified\n-file (defaulting to .git/covermail.tmpl or\n-/usr/share/stgit/templates/covermail.tmpl).\n+file (defaulting to '.git/covermail.tmpl' or\n+'~/.stgit/templates/covermail.tmpl' or\n+'/usr/share/stgit/templates/covermail.tmpl').\n \n All the subsequent e-mails appear as replies to the first e-mail sent\n (either the preamble or the first patch). E-mails can be seen as\n@@ -444,8 +446,10 @@ def func(parser, options, args):\n             tfile_list = [options.cover]\n         else:\n             tfile_list = [os.path.join(basedir.get(), 'covermail.tmpl'),\n+                          os.path.join(os.path.expanduser('~'), '.stgit', 'templates',\n+                                       'covermail.tmpl'),\n                           os.path.join(sys.prefix,\n-                                       'share/stgit/templates/covermail.tmpl')]\n+                                       'share', 'stgit', 'templates', 'covermail.tmpl')]\n \n         tmpl = None\n         for tfile in tfile_list:\n@@ -476,8 +480,10 @@ def func(parser, options, args):\n         tfile_list = [options.template]\n     else:\n         tfile_list = [os.path.join(basedir.get(), 'patchmail.tmpl'),\n+                      os.path.join(os.path.expanduser('~'), '.stgit', 'templates',\n+                                   'patchmail.tmpl'),\n                       os.path.join(sys.prefix,\n-                                   'share/stgit/templates/patchmail.tmpl')]\n+                                   'share', 'stgit', 'templates', 'patchmail.tmpl')]\n     tmpl = None\n     for tfile in tfile_list:\n         if os.path.isfile(tfile):\n"}]}