diff --git a/.hgext/hg-prompt/.hg/00changelog.i b/.hgext/hg-prompt/.hg/00changelog.i new file mode 100644 index 00000000..d3a83110 Binary files /dev/null and b/.hgext/hg-prompt/.hg/00changelog.i differ diff --git a/.hgext/hg-prompt/.hg/branch b/.hgext/hg-prompt/.hg/branch new file mode 100644 index 00000000..4ad96d51 --- /dev/null +++ b/.hgext/hg-prompt/.hg/branch @@ -0,0 +1 @@ +default diff --git a/.hgext/hg-prompt/.hg/branchheads.cache b/.hgext/hg-prompt/.hg/branchheads.cache new file mode 100644 index 00000000..4514c7ab --- /dev/null +++ b/.hgext/hg-prompt/.hg/branchheads.cache @@ -0,0 +1,2 @@ +2817cac5f5d77ff70cb2649911ac1647149fcc43 110 +2817cac5f5d77ff70cb2649911ac1647149fcc43 default diff --git a/.hgext/hg-prompt/.hg/dirstate b/.hgext/hg-prompt/.hg/dirstate new file mode 100644 index 00000000..cc75ae51 Binary files /dev/null and b/.hgext/hg-prompt/.hg/dirstate differ diff --git a/.hgext/hg-prompt/.hg/hgrc b/.hgext/hg-prompt/.hg/hgrc new file mode 100644 index 00000000..404705b0 --- /dev/null +++ b/.hgext/hg-prompt/.hg/hgrc @@ -0,0 +1,2 @@ +[paths] +default = ssh://hg@bitbucket.org/sjl/hg-prompt diff --git a/.hgext/hg-prompt/.hg/requires b/.hgext/hg-prompt/.hg/requires new file mode 100644 index 00000000..5175383b --- /dev/null +++ b/.hgext/hg-prompt/.hg/requires @@ -0,0 +1,3 @@ +revlogv1 +store +fncache diff --git a/.hgext/hg-prompt/.hg/store/00changelog.i b/.hgext/hg-prompt/.hg/store/00changelog.i new file mode 100644 index 00000000..b8afdaac Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/00changelog.i differ diff --git a/.hgext/hg-prompt/.hg/store/00manifest.i b/.hgext/hg-prompt/.hg/store/00manifest.i new file mode 100644 index 00000000..92725e17 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/00manifest.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/.hgignore.i b/.hgext/hg-prompt/.hg/store/data/.hgignore.i new file mode 100644 index 00000000..ced024ca Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/.hgignore.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/.hgtags.i b/.hgext/hg-prompt/.hg/store/data/.hgtags.i new file mode 100644 index 00000000..88161f5c Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/.hgtags.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/_r_e_a_d_m_e.i b/.hgext/hg-prompt/.hg/store/data/_r_e_a_d_m_e.i new file mode 100644 index 00000000..149b4b10 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/_r_e_a_d_m_e.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/404.html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/404.html.i new file mode 100644 index 00000000..c92b8086 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/404.html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/base.html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/base.html.i new file mode 100644 index 00000000..84208249 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/base.html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/document.html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/document.html.i new file mode 100644 index 00000000..fd942cab Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/document.html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/listing.html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/listing.html.i new file mode 100644 index 00000000..b05fc48a Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/listing.html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/macros/html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/macros/html.i new file mode 100644 index 00000000..8602d707 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/macros/html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/404.html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/404.html.i new file mode 100644 index 00000000..33ab52b8 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/404.html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/base.html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/base.html.i new file mode 100644 index 00000000..c89d05cc Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/base.html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/document.html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/document.html.i new file mode 100644 index 00000000..e67c18a1 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/document.html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/listing.html.i b/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/listing.html.i new file mode 100644 index 00000000..024c1c45 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.templates/markdoc-default/listing.html.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/.venv.i b/.hgext/hg-prompt/.hg/store/data/docs/.venv.i new file mode 100644 index 00000000..8e193220 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/.venv.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/markdoc.yaml.i b/.hgext/hg-prompt/.hg/store/data/docs/markdoc.yaml.i new file mode 100644 index 00000000..f3b23ddf Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/markdoc.yaml.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/publish.sh.i b/.hgext/hg-prompt/.hg/store/data/docs/publish.sh.i new file mode 100644 index 00000000..bcfbc342 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/publish.sh.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/layout.css.i b/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/layout.css.i new file mode 100644 index 00000000..90879106 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/layout.css.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/pygments.css.i b/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/pygments.css.i new file mode 100644 index 00000000..bc06fb99 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/pygments.css.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/reset.css.i b/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/reset.css.i new file mode 100644 index 00000000..fedd3639 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/reset.css.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/typography.css.i b/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/typography.css.i new file mode 100644 index 00000000..f386bef1 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/static/media/css/typography.css.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/index.mdown.i b/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/index.mdown.i new file mode 100644 index 00000000..4c335483 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/index.mdown.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/keywords/index.mdown.i b/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/keywords/index.mdown.i new file mode 100644 index 00000000..75be9494 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/keywords/index.mdown.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/samples/index.mdown.i b/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/samples/index.mdown.i new file mode 100644 index 00000000..d5e14b8d Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/samples/index.mdown.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/usage/index.mdown.i b/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/usage/index.mdown.i new file mode 100644 index 00000000..56310d22 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/wiki/documentation/usage/index.mdown.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/wiki/index.mdown.i b/.hgext/hg-prompt/.hg/store/data/docs/wiki/index.mdown.i new file mode 100644 index 00000000..6487e996 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/wiki/index.mdown.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/wiki/installation/index.mdown.i b/.hgext/hg-prompt/.hg/store/data/docs/wiki/installation/index.mdown.i new file mode 100644 index 00000000..f2277efa Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/wiki/installation/index.mdown.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/docs/wiki/quickstart/index.mdown.i b/.hgext/hg-prompt/.hg/store/data/docs/wiki/quickstart/index.mdown.i new file mode 100644 index 00000000..203c6ec7 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/docs/wiki/quickstart/index.mdown.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/prompt.py.i b/.hgext/hg-prompt/.hg/store/data/prompt.py.i new file mode 100644 index 00000000..4cc8fd8a Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/prompt.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/tests/____init____.py.i b/.hgext/hg-prompt/.hg/store/data/tests/____init____.py.i new file mode 100644 index 00000000..d9f64520 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/tests/____init____.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/tests/test__branch.py.i b/.hgext/hg-prompt/.hg/store/data/tests/test__branch.py.i new file mode 100644 index 00000000..6904d3df Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/tests/test__branch.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/tests/test__node.py.i b/.hgext/hg-prompt/.hg/store/data/tests/test__node.py.i new file mode 100644 index 00000000..293dc1d8 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/tests/test__node.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/tests/test__none.py.i b/.hgext/hg-prompt/.hg/store/data/tests/test__none.py.i new file mode 100644 index 00000000..68f7ddbb Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/tests/test__none.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/tests/test__rev.py.i b/.hgext/hg-prompt/.hg/store/data/tests/test__rev.py.i new file mode 100644 index 00000000..1b863640 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/tests/test__rev.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/tests/test__tip.py.i b/.hgext/hg-prompt/.hg/store/data/tests/test__tip.py.i new file mode 100644 index 00000000..737717f3 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/tests/test__tip.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/tests/test__update.py.i b/.hgext/hg-prompt/.hg/store/data/tests/test__update.py.i new file mode 100644 index 00000000..cc6347db Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/tests/test__update.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/data/tests/util.py.i b/.hgext/hg-prompt/.hg/store/data/tests/util.py.i new file mode 100644 index 00000000..11047294 Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/data/tests/util.py.i differ diff --git a/.hgext/hg-prompt/.hg/store/fncache b/.hgext/hg-prompt/.hg/store/fncache new file mode 100644 index 00000000..b34ecc0e --- /dev/null +++ b/.hgext/hg-prompt/.hg/store/fncache @@ -0,0 +1,35 @@ +data/.hgignore.i +data/.hgtags.i +data/README.i +data/docs/.templates/404.html.i +data/docs/.templates/base.html.i +data/docs/.templates/document.html.i +data/docs/.templates/listing.html.i +data/docs/.templates/macros/html.i +data/docs/.templates/markdoc-default/404.html.i +data/docs/.templates/markdoc-default/base.html.i +data/docs/.templates/markdoc-default/document.html.i +data/docs/.templates/markdoc-default/listing.html.i +data/docs/.venv.i +data/docs/markdoc.yaml.i +data/docs/publish.sh.i +data/docs/static/media/css/layout.css.i +data/docs/static/media/css/pygments.css.i +data/docs/static/media/css/reset.css.i +data/docs/static/media/css/typography.css.i +data/docs/wiki/documentation/index.mdown.i +data/docs/wiki/documentation/keywords/index.mdown.i +data/docs/wiki/documentation/samples/index.mdown.i +data/docs/wiki/documentation/usage/index.mdown.i +data/docs/wiki/index.mdown.i +data/docs/wiki/installation/index.mdown.i +data/docs/wiki/quickstart/index.mdown.i +data/prompt.py.i +data/tests/__init__.py.i +data/tests/test_branch.py.i +data/tests/test_node.py.i +data/tests/test_none.py.i +data/tests/test_rev.py.i +data/tests/test_tip.py.i +data/tests/test_update.py.i +data/tests/util.py.i diff --git a/.hgext/hg-prompt/.hg/store/undo b/.hgext/hg-prompt/.hg/store/undo new file mode 100644 index 00000000..18e9744a Binary files /dev/null and b/.hgext/hg-prompt/.hg/store/undo differ diff --git a/.hgext/hg-prompt/.hg/tags.cache b/.hgext/hg-prompt/.hg/tags.cache new file mode 100644 index 00000000..85a721c2 --- /dev/null +++ b/.hgext/hg-prompt/.hg/tags.cache @@ -0,0 +1,4 @@ +110 2817cac5f5d77ff70cb2649911ac1647149fcc43 90ce2c56ee1ffaf9e509b56a80fbd13525ecb12c + +d390b5e2719169d39b522f5dcf0320ce81a08d57 0.1 +a6ec48f03985b7c2bb6b7fbe16c6bbec2d650783 0.2 diff --git a/.hgext/hg-prompt/.hg/undo.branch b/.hgext/hg-prompt/.hg/undo.branch new file mode 100644 index 00000000..331d858c --- /dev/null +++ b/.hgext/hg-prompt/.hg/undo.branch @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/.hgext/hg-prompt/.hg/undo.desc b/.hgext/hg-prompt/.hg/undo.desc new file mode 100644 index 00000000..3f5f0e00 --- /dev/null +++ b/.hgext/hg-prompt/.hg/undo.desc @@ -0,0 +1,3 @@ +0 +pull +ssh://hg@bitbucket.org/sjl/hg-prompt diff --git a/.hgext/hg-prompt/.hg/undo.dirstate b/.hgext/hg-prompt/.hg/undo.dirstate new file mode 100644 index 00000000..e69de29b diff --git a/.hgext/hg-prompt/.hgignore b/.hgext/hg-prompt/.hgignore new file mode 100644 index 00000000..e5ac930b --- /dev/null +++ b/.hgext/hg-prompt/.hgignore @@ -0,0 +1,6 @@ +syntax: glob + +.DS_Store +*.pyc +docs/.html +docs/.tmp diff --git a/.hgext/hg-prompt/.hgtags b/.hgext/hg-prompt/.hgtags new file mode 100644 index 00000000..320fdeb5 --- /dev/null +++ b/.hgext/hg-prompt/.hgtags @@ -0,0 +1,2 @@ +d390b5e2719169d39b522f5dcf0320ce81a08d57 0.1 +a6ec48f03985b7c2bb6b7fbe16c6bbec2d650783 0.2 diff --git a/.hgext/hg-prompt/LOL b/.hgext/hg-prompt/LOL new file mode 100644 index 00000000..e69de29b diff --git a/.hgext/hg-prompt/README b/.hgext/hg-prompt/README new file mode 100644 index 00000000..cdfd8c26 --- /dev/null +++ b/.hgext/hg-prompt/README @@ -0,0 +1,46 @@ +-*- markdown -*- + +hg-prompt +========= + +hg-prompt adds an 'hg prompt' command to Mercurial for viewing repository information. It's designed to be used in a shell prompt. + + +Requirements +------------ + +hg-prompt requires Python 2.5+ and (obviously) Mercurial. + + +Installing +---------- + +Clone the repository: + + $ hg clone http://bitbucket.org/sjl/hg-prompt/ + +Edit the `[extensions]` section in your `~/.hgrc` file: + + [extensions] + prompt = (path to)/hg-prompt/prompt.py + + +Documentation +------------- + +The documentation for hg-prompt is at the [project page][project]. There's a [Quick Start guide][quickstart], [Full Usage Guide][usage], [Keyword List][keywords], and a few other things there. + +The documentation is stored in the `docs/` directory of the repository if you prefer to read it offline. + +[project]: http://sjl.bitbucket.org/hg-prompt/ +[quickstart]: http://sjl.bitbucket.org/hg-prompt/quickstart/ +[usage]: http://sjl.bitbucket.org/hg-prompt/documentation/usage/ +[keywords]: http://sjl.bitbucket.org/hg-prompt/documentation/keywords/ + + +Questions, Comments, Suggestions +-------------------------------- + +The code was kind of thrown together in a few nights after I got tired of chaining three or four hg runs together to get what I wanted. I'm sure it's not perfect, so if you've got a way to improve it please add an issue and let me know. + +Patches are also welcome! \ No newline at end of file diff --git a/.hgext/hg-prompt/docs/.venv b/.hgext/hg-prompt/docs/.venv new file mode 100644 index 00000000..4542e4f8 --- /dev/null +++ b/.hgext/hg-prompt/docs/.venv @@ -0,0 +1 @@ +markdoc diff --git a/.hgext/hg-prompt/docs/markdoc.yaml b/.hgext/hg-prompt/docs/markdoc.yaml new file mode 100644 index 00000000..3a134af2 --- /dev/null +++ b/.hgext/hg-prompt/docs/markdoc.yaml @@ -0,0 +1,21 @@ +wiki-name: "hg-prompt" + + +static-dir: "static" + +hide-prefix: "." +document-extensions: [.md, .mdown, .markdown] +generate-listing: always +listing-filename: "list.html" + +use-default-static: false + +markdown: + safe_mode: false + output_format: xhtml1 + extensions: [codehilite, def_list, toc] + +server: + bind: '127.0.0.1' + port: 8010 + server_name: 'sjl.bitbucket.org' \ No newline at end of file diff --git a/.hgext/hg-prompt/docs/publish.sh b/.hgext/hg-prompt/docs/publish.sh new file mode 100755 index 00000000..388e2150 --- /dev/null +++ b/.hgext/hg-prompt/docs/publish.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +markdoc build +rsync --delete -az .html/ ~/src/sjl.bitbucket.org/hg-prompt +hg -R ~/src/sjl.bitbucket.org commit -Am 'hg-prompt: Update documentation.' +hg -R ~/src/sjl.bitbucket.org push diff --git a/.hgext/hg-prompt/docs/static/media/css/layout.css b/.hgext/hg-prompt/docs/static/media/css/layout.css new file mode 100644 index 00000000..f44f2ebc --- /dev/null +++ b/.hgext/hg-prompt/docs/static/media/css/layout.css @@ -0,0 +1,182 @@ +/* @override http://localhost:8008/media/css/layout.css */ +body, html { + background-color: #226F89; + margin: 0; + padding: 0; +} + +div#breadcrumbs { + border-bottom: 4px dashed #265565; + border-right: 1px solid #265565; + padding: 0em 1.5em; + background-color: #fff; + width: 55em; + margin: 0em 0em 0em -1.5em; +} +div#breadcrumbs p { + margin: 0.5em auto; +} +div#content { + background-color: #fff; + height: 100%; + margin: 0 auto 3em; + overflow: hidden; + padding: 0em 1.5em 0em 1.5em; + width: 55em; + border-left: 1px solid #265565; + border-right: 1px solid #265565; +} + +h1 { + margin: 0.3em 0; + padding: 0.1em 0; +} +h2, h3, h4, h5, h6 { + margin-top: 1em; + margin-bottom: 0.4em; +} +p, ul, blockquote { + line-height: 1.8em; + margin-top: 0; + margin-bottom: 1em; +} +a { + color: #133C4C; +} +a:hover { + color: #F14800; +} + +p#footer { + background-color: #fff; + border-top: 4px dashed #265565; + width: 55em; + padding: 0.75em 1.5em; + text-align: center; + color: #666; + margin-left: -1.5em; + margin-bottom: 0em; +} + +ul { + padding-left: 1.5em; +} +ul li { + list-style-type: disc; + margin-left: 1.5em; + margin-top: 0.4em; +} + +div.toc ul { + padding: 0; +} +div.toc ul li { + list-style-type: none; + margin: 0; +} +div.toc > ul > li { + list-style-type: none; + margin: 0; +} +div.toc > ul ul { + margin: 0 3em; +} + +code { + background-color: #f6f6f6; + border: 1px solid #999; + padding: 2px; + white-space: nowrap; +} +pre code { + border: none; + padding: 0; + background: none; + white-space: pre-wrap; +} +a code { + background: none; + border: none; + margin: 0; + padding: 0; + text-decoration: inherit; +} +a.code { + background-color: #3f3f3f; +} + +dt { + font-weight: bold; + margin-top: 1em; +} +dd { + line-height: 1.8em; + margin-left: 1.5em; +} +blockquote p { + margin: 0; +} + +img { + background-color: #f6f6f6; + border: 1px solid #999; + padding: 1em; + display: block; + margin: 0 auto; +} + +table { + margin: 1em; +} +table thead { + background-color: #f6f6f6; +} +table thead th { + border: 1px solid #999; + padding: 0.5em 1em; +} +table tbody tr td { + border: 1px solid #999; + padding: 0.5em 1em; +} + +pre, blockquote, table.codehilitetable { + background-color: #f6f6f6; + border: 1px solid #999; + display: block; + line-height: 1.5em; + margin-left: 1.5em; + margin-right: 1.5em; + padding: 0.5em; + padding-left: 1em; +} +pre, table.codehilitetable { + margin-bottom: 1em; +} +table.codehilitetable div.linenodiv { + border-right: 1px solid #ccc; + margin-right: 1em; + padding-right: 1em; +} +table.codehilitetable pre { + background: none; + border: none; + margin: 0; + padding: 0; +} + +table#pages tr, table#subdirs tr, table#files tr { + border-top: 1px solid #999; + border-bottom: 1px solid #999; +} +table tr td.name a { + display: block; + padding: 0.5em 1em; +} +table tr td.name a:hover { + background-color: #f6f6f6; +} +table tr td.size { + padding: 0.5em 1em; + width: 40px; +} diff --git a/.hgext/hg-prompt/docs/static/media/css/pygments.css b/.hgext/hg-prompt/docs/static/media/css/pygments.css new file mode 100644 index 00000000..b0bdc4d9 --- /dev/null +++ b/.hgext/hg-prompt/docs/static/media/css/pygments.css @@ -0,0 +1,61 @@ +.hll { background-color: #ffffcc } +.c { color: #808080 } /* Comment */ +.err { color: #F00000; background-color: #F0A0A0 } /* Error */ +.k { color: #008000; font-weight: bold } /* Keyword */ +.o { color: #303030 } /* Operator */ +.cm { color: #808080 } /* Comment.Multiline */ +.cp { color: #507090 } /* Comment.Preproc */ +.c1 { color: #808080 } /* Comment.Single */ +.cs { color: #cc0000; font-weight: bold } /* Comment.Special */ +.gd { color: #A00000 } /* Generic.Deleted */ +.ge { font-style: italic } /* Generic.Emph */ +.gr { color: #FF0000 } /* Generic.Error */ +.gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.gi { color: #00A000 } /* Generic.Inserted */ +.go { color: #808080 } /* Generic.Output */ +.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */ +.gs { font-weight: bold } /* Generic.Strong */ +.gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.gt { color: #0040D0 } /* Generic.Traceback */ +.kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.kp { color: #003080; font-weight: bold } /* Keyword.Pseudo */ +.kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.kt { color: #303090; font-weight: bold } /* Keyword.Type */ +.m { color: #6000E0; font-weight: bold } /* Literal.Number */ +.s { background-color: #fff0f0 } /* Literal.String */ +.na { color: #0000C0 } /* Name.Attribute */ +.nb { color: #007020 } /* Name.Builtin */ +.nc { color: #B00060; font-weight: bold } /* Name.Class */ +.no { color: #003060; font-weight: bold } /* Name.Constant */ +.nd { color: #505050; font-weight: bold } /* Name.Decorator */ +.ni { color: #800000; font-weight: bold } /* Name.Entity */ +.ne { color: #F00000; font-weight: bold } /* Name.Exception */ +.nf { color: #0060B0; font-weight: bold } /* Name.Function */ +.nl { color: #907000; font-weight: bold } /* Name.Label */ +.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */ +.nt { color: #007000 } /* Name.Tag */ +.nv { color: #906030 } /* Name.Variable */ +.ow { color: #000000; font-weight: bold } /* Operator.Word */ +.w { color: #bbbbbb } /* Text.Whitespace */ +.mf { color: #6000E0; font-weight: bold } /* Literal.Number.Float */ +.mh { color: #005080; font-weight: bold } /* Literal.Number.Hex */ +.mi { color: #0000D0; font-weight: bold } /* Literal.Number.Integer */ +.mo { color: #4000E0; font-weight: bold } /* Literal.Number.Oct */ +.sb { background-color: #fff0f0 } /* Literal.String.Backtick */ +.sc { color: #0040D0 } /* Literal.String.Char */ +.sd { color: #D04020 } /* Literal.String.Doc */ +.s2 { background-color: #fff0f0 } /* Literal.String.Double */ +.se { color: #606060; font-weight: bold; background-color: #fff0f0 } /* Literal.String.Escape */ +.sh { background-color: #fff0f0 } /* Literal.String.Heredoc */ +.si { background-color: #e0e0e0 } /* Literal.String.Interpol */ +.sx { color: #D02000; background-color: #fff0f0 } /* Literal.String.Other */ +.sr { color: #000000; background-color: #fff0ff } /* Literal.String.Regex */ +.s1 { background-color: #fff0f0 } /* Literal.String.Single */ +.ss { color: #A06000 } /* Literal.String.Symbol */ +.bp { color: #007020 } /* Name.Builtin.Pseudo */ +.vc { color: #306090 } /* Name.Variable.Class */ +.vg { color: #d07000; font-weight: bold } /* Name.Variable.Global */ +.vi { color: #3030B0 } /* Name.Variable.Instance */ +.il { color: #0000D0; font-weight: bold } /* Literal.Number.Integer.Long */ diff --git a/.hgext/hg-prompt/docs/static/media/css/reset.css b/.hgext/hg-prompt/docs/static/media/css/reset.css new file mode 100644 index 00000000..4b1978c3 --- /dev/null +++ b/.hgext/hg-prompt/docs/static/media/css/reset.css @@ -0,0 +1,7 @@ +/* +Copyright (c) 2009, Yahoo! Inc. All rights reserved. +Code licensed under the BSD License: +http://developer.yahoo.net/yui/license.txt +version: 2.7.0 +*/ +html{color:#000;background:#FFF;}body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td{margin:0;padding:0;}table{border-collapse:collapse;border-spacing:0;}fieldset,img{border:0;}address,caption,cite,code,dfn,em,strong,th,var,optgroup{font-style:inherit;font-weight:inherit;}del,ins{text-decoration:none;}li{list-style:none;}caption,th{text-align:left;}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:normal;}q:before,q:after{content:'';}abbr,acronym{border:0;font-variant:normal;}sup{vertical-align:baseline;}sub{vertical-align:baseline;}legend{color:#000;}input,button,textarea,select,optgroup,option{font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;}input,button,textarea,select{*font-size:100%;} \ No newline at end of file diff --git a/.hgext/hg-prompt/docs/static/media/css/typography.css b/.hgext/hg-prompt/docs/static/media/css/typography.css new file mode 100644 index 00000000..cc212f4f --- /dev/null +++ b/.hgext/hg-prompt/docs/static/media/css/typography.css @@ -0,0 +1,86 @@ +html { + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; + font-size: 10pt; +} + +h1, h2, h3, h4, h5, h6 { + font-weight: bold; +} + +h1 { + font-size: 2em; +} + +h2 { + font-size: 1.6em; +} + +h3 { + font-size: 1.3em; +} + +h4 { + font-size: 1.1em; + font-weight: bold; +} + +em { + font-style: italic; +} + +strong { + font-weight: bold; +} + +a { + font-weight: bold; + text-decoration: none; +} + +table#files a:hover, table#subdirs a:hover, table#pages a:hover { + color: #a00000; +} + +p#footer a { + text-decoration: none; +} + +p code, ul code, ol code, dl code, blockquote code, tbody code, thead code { + font-size: 8pt; +} + +pre, code, tt, table#subdirs tr td.name, table#files tr td.name, table tr td.size { + font-family: Monaco, "DejaVu Sans Mono", "Bitstream Vera Sans Mono", Menlo, Inconsolata, Consolas, "Courier New", Courier; +} + +table#subdirs code, table#files code { + font-size: 10pt; +} + +table.codehilitetable div.linenodiv { + color: #777; +} + +blockquote { + color: #222; + font-style: italic; +} + +pre { + overflow-x: auto; /* Use horizontal scroller if needed; for Firefox 2, not needed in Firefox 3 */ + white-space: pre-wrap; /* css-3 */ + white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: -o-pre-wrap; /* Opera 7 */ + /* width: 99%; */ + word-wrap: break-word; /* Internet Explorer 5.5+ */ +} + +table thead th { + font-weight: bold; +} + +.list-crumb { + color: #777; + font-variant: italic; +} \ No newline at end of file diff --git a/.hgext/hg-prompt/docs/wiki/documentation/index.mdown b/.hgext/hg-prompt/docs/wiki/documentation/index.mdown new file mode 100644 index 00000000..2f5d2045 --- /dev/null +++ b/.hgext/hg-prompt/docs/wiki/documentation/index.mdown @@ -0,0 +1,14 @@ +Documentation +============= + +##[Usage](/documentation/usage/)## + +How the `hg prompt` command can be used. + +##[Keywords](/documentation/keywords/)## + +Keywords available to use with the command. + +##[Sample Prompts](/documentation/samples/)## + +Some sample prompts to get you started. \ No newline at end of file diff --git a/.hgext/hg-prompt/docs/wiki/documentation/keywords/index.mdown b/.hgext/hg-prompt/docs/wiki/documentation/keywords/index.mdown new file mode 100644 index 00000000..58768c98 --- /dev/null +++ b/.hgext/hg-prompt/docs/wiki/documentation/keywords/index.mdown @@ -0,0 +1,183 @@ +Keywords +======== + +There a number of keywords available. If you have any suggestions for more +please [let me know][issues]. + +[issues]: http://bitbucket.org/sjl/issues + +Some of the keywords support filters. These filters can be combined when it +makes sense. If in doubt, try it! + +[TOC] + +##bookmark## +: Display the current bookmark (requires the [bookmarks][] extension). + +##branch## +: Display the current branch. + + |quiet + : Display the current branch only if it is not the default branch. + +##closed## +: Display `X` if working on a closed branch (i.e. if committing now would + reopen the branch). + +##count## +: Display the number of revisions in the given revset (the revset `all()` + will be used if none is given). + + See `hg help revsets` for more information. + + |REVSET + : The revset to count. + +##incoming## +: Display nothing, but if the default path contains incoming changesets the + extra text will be expanded. + + For example: `{incoming changes{incoming}}` will expand to + `incoming changes` if there are changes, otherwise nothing. + + Checking for incoming changesets is an expensive operation, so `hg-prompt` + will cache the results in `.hg/prompt/cache/` and refresh them every 15 + minutes. + + |count + : Display the number of incoming changesets (if greater than 0). + +##node## +: Display the (full) changeset hash of the current parent. + + |short + : Display the hash as the short, 12-character form. + + |merge + : Display the hash of the changeset you're merging with. + +##outgoing## +: Display nothing, but if the current repository contains outgoing + changesets (to default) the extra text will be expanded. + + For example: `{outgoing changes{outgoing}}` will expand to + `outgoing changes` if there are changes, otherwise nothing. + + Checking for outgoing changesets is an expensive operation, so `hg-prompt` + will cache the results in `.hg/prompt/cache/` and refresh them every 15 + minutes. + + |count + : Display the number of outgoing changesets (if greater than 0). + +##patch## +: Display the topmost currently-applied patch (requires the [mq][] + extension). + + |count + : Display the number of patches in the queue. + + |applied + : Display the number of currently applied patches in the queue. + + |unapplied + : Display the number of currently unapplied patches in the queue. + + |quiet + : Display a number only if there are any patches in the queue. + +##patches## +: Display a list of the current patches in the queue. It will look like + this: + + :::console + $ hg prompt '{patches}' + bottom-patch -> middle-patch -> top-patch + + |reverse + : Display the patches in reverse order (i.e. topmost first). + + |hide_applied + : Do not display applied patches. + + |hide_unapplied + : Do not display unapplied patches. + + |join(SEP) + : Display SEP between each patch, instead of the default ` -> `. + + |pre_applied(STRING) + : Display STRING immediately before each applied patch. Useful for + adding color codes. + + |post_applied(STRING) + : Display STRING immediately after each applied patch. Useful for + resetting color codes. + + |pre_unapplied(STRING) + : Display STRING immediately before each unapplied patch. Useful for + adding color codes. + + |post_unapplied(STRING) + : Display STRING immediately after each unapplied patch. Useful for + resetting color codes. + +##queue## +: Display the name of the current MQ queue. + +##rev## +: Display the repository-local changeset number of the current parent. + + |merge + : Display the repository-local changeset number of the changeset you're + merging with. + +##root## +: Display the full path to the root of the current repository, without a + trailing slash. + + |basename + : Display the directory name of the root of the current repository. For + example, if the repository is in `/home/u/myrepo` then this keyword + would expand to `myrepo`. + +##status## +: Display `!` if the repository has any changed/added/removed files, + otherwise `?` if it has any untracked (but not ignored) files, otherwise + nothing. + + |modified + : Display `!` if the current repository contains files that have been + modified, added, removed, or deleted, otherwise nothing. + + |unknown + : Display `?` if the current repository contains untracked files, + otherwise nothing. + +##tags## +: Display the tags of the current parent, separated by a space. + + |SEP + : Display the tags of the current parent, separated by `SEP`. + +##task## +: Display the current task (requires the [tasks][] extension). + +##tip## +: Display the repository-local changeset number of the current tip. + + |node + : Display the (full) changeset hash of the current tip. + + |short + : Display a short form of the changeset hash of the current tip (must be + used with the **|node** filter) + +##update## +: Display `^` if the current parent is not the tip of the current branch, + otherwise nothing. In effect, this lets you see if running `hg update` + would do something. + +[bookmarks]: http://mercurial.selenic.com/wiki/BookmarksExtension +[tasks]: http://bitbucket.org/alu/hgtasks/wiki/Home +[mq]: http://mercurial.selenic.com/wiki/MqExtension diff --git a/.hgext/hg-prompt/docs/wiki/documentation/samples/index.mdown b/.hgext/hg-prompt/docs/wiki/documentation/samples/index.mdown new file mode 100644 index 00000000..a3011f6a --- /dev/null +++ b/.hgext/hg-prompt/docs/wiki/documentation/samples/index.mdown @@ -0,0 +1,63 @@ +Sample Prompts +============== + +`hg-prompt` supports many keywords, but you probably don't want to use them +all at once. Which keywords you'll find useful depends on the workflow(s) you +commonly use. + +Here are some example prompts to get you started. + +A Basic Prompt +-------------- + +A very simple prompt could tell you: + +* Which named branch you're currently working on. +* If there are any uncommitted changes in the working directory. +* If you're at a revision that's not a branch tip (i.e. if running `hg update` + would do something). + +To get a prompt like this you could add this to your `~/.bashrc` file: + + :::bash + export PS1='\u in \w`hg prompt "{on {branch}}{status}{update}" 2>/dev/null` $' + +The result would look something like this: + + :::text + username in ~/src $ cd project + username in ~/src/project on feature-branch $ touch sample + username in ~/src/project on feature-branch? $ hg add sample + username in ~/src/project on feature-branch! $ hg commit -m 'Add a file.' + username in ~/src/project on feature-branch $ hg update default + username in ~/src/project on default $ hg update 0 + username in ~/src/project on default^ $ + +The `2>/dev/null` part of the prompt command prevents errors from showing when +you're not currently in a Mercurial repository. + +The keywords (`{branch}`, `{status}` and `{update}`) display the relevant +information. + +The extra text in the `{branch}` keyword will only display if a branch exists, +so you won't see the word "on" if you're not in a repository. + +A More Compact Basic Prompt +--------------------------- + +Some people prefer a smaller, less obtrusive prompt. To get that kind of +prompt you can omit some of the less important text: + + :::bash + export PS1='\w`hg prompt "[{branch}{status}{update}]" 2>/dev/null` $' + +That will give you something like this: + + :::text + ~/src $ cd project + ~/src/project[feature-branch] $ touch sample + ~/src/project[feature-branch?] $ hg add sample + ~/src/project[feature-branch!] $ hg commit -m 'Add a file.' + ~/src/project[feature-branch] $ hg update default + ~/src/project[default] $ hg update 0 + ~/src/project[default^] $ diff --git a/.hgext/hg-prompt/docs/wiki/documentation/usage/index.mdown b/.hgext/hg-prompt/docs/wiki/documentation/usage/index.mdown new file mode 100644 index 00000000..bcdb886a --- /dev/null +++ b/.hgext/hg-prompt/docs/wiki/documentation/usage/index.mdown @@ -0,0 +1,53 @@ +Usage +===== + +The `hg prompt` command takes a single string as an argument and outputs it. +Here's a simple (and useless) example: + + :::console + $ hg prompt "test" + test + +Keywords in curly braces can be used to output repository information: + + :::console + $ hg prompt "currently on {branch}" + currently on default + +Keywords also have an extended form: + + :::text + {optional text{branch}more optional text} + +This form will output the text and the expanded keyword **only** if the +keyword successfully expands. This can be useful for displaying extra text +only if it's applicable: + + :::console + $ hg prompt "currently on {branch} and at {bookmark}" + currently on branch default and at + + $ hg prompt "currently on {branch} {and at {bookmark}}" + currently on branch default + + $ hg bookmark my-book + + $ hg prompt "currently on {branch} {and at {bookmark}}" + currently on branch default and at my-book + +You can give the `--angle-brackets` option to use angle brackets for keywords +instead of curly brackets. This can come in handy when combining a simple +prompt string with more complicated shell functionality (like color +variables): + + :::console + $ hg prompt "{currently on {branch}}" + currently on default + + $ hg prompt --angle-brackets ">" + currently on default + +Take a look at the [keywords][] documentation to see all the keywords +`hg-prompt` supports. + +[keywords]: /documentation/keywords/ \ No newline at end of file diff --git a/.hgext/hg-prompt/docs/wiki/index.mdown b/.hgext/hg-prompt/docs/wiki/index.mdown new file mode 100644 index 00000000..ff27616e --- /dev/null +++ b/.hgext/hg-prompt/docs/wiki/index.mdown @@ -0,0 +1,28 @@ +hg-prompt +========= + +`hg-prompt` is a Mercurial extension that adds an 'hg prompt' command to +Mercurial for viewing repository information. It's designed to be used in a +shell prompt. + +[Installation][] +---------------- + +[Quick Start][] +--------------- + +[Full Documentation][] +---------------------- + +[Code][] +-------- + +[Installation]: /installation/ +[Quick Start]: /quickstart/ +[Full Documentation]: /documentation/ +[Code]: http://bitbucket.org/sjl + +Screenshot +---------- + +![My bash prompt while using hg-prompt.](http://stevelosh.com/media/images/projects/hg-prompt/prompt.png "My bash prompt while using hg-prompt.") diff --git a/.hgext/hg-prompt/docs/wiki/installation/index.mdown b/.hgext/hg-prompt/docs/wiki/installation/index.mdown new file mode 100644 index 00000000..f974431f --- /dev/null +++ b/.hgext/hg-prompt/docs/wiki/installation/index.mdown @@ -0,0 +1,28 @@ +Installation +============ + +Installing `hg-prompt` requires [Python][] 2.5+ and (obviously) Mercurial. + +[Python]: http://python.org/ + +First, clone the repository: + + :::console + $ hg clone http://bitbucket.org/sjl/hg-prompt/ + +Edit the `[extensions]` section in your `~/.hgrc` file: + + :::cfg + [extensions] + prompt = (path to)/prompt.py + +Make sure everything is working: + + :::console + $ hg prompt 'test' + test + +Take a look at the [Quick Start][] guide to learn how to put some useful +information into your shell prompt. + +[Quick Start]: /quickstart/ \ No newline at end of file diff --git a/.hgext/hg-prompt/docs/wiki/quickstart/index.mdown b/.hgext/hg-prompt/docs/wiki/quickstart/index.mdown new file mode 100644 index 00000000..e9dee412 --- /dev/null +++ b/.hgext/hg-prompt/docs/wiki/quickstart/index.mdown @@ -0,0 +1,56 @@ +Quick Start +=========== + +This guide will get you up and running so you can put some useful information +into your shell prompt. + +If you haven't already [installed][install] it, do that now. + +[install]: /installation/ + +A Simple (But Useful) Prompt +---------------------------- + +Edit your `~/.bashrc` file to include something like this: + + :::bash + hg_ps1() { + hg prompt "{ on {branch}}{ at {bookmark}}{status}" 2> /dev/null + } + + export PS1='\u at \h in \w$(hg_ps1)\n$ ' + +`source ~/.bashrc` after to test it out. Make sure you're in a Mercurial +repository or you won't see anything. This little prompt will give you +something like this: + + :::console + steve at myhost in ~/src/hg-prompt on default at feature-bookmark? + $ + +An Advanced Prompt +------------------ + +How about something a little more interesting? + + :::bash + hg_ps1() { + hg prompt "{[+{incoming|count}]-->}{root|basename}{/{branch}}{-->[+{outgoing|count}]}{ at {bookmark}}{status}" 2> /dev/null + } + + export PS1='$(hg_ps1)\n\u at \h in \w\n$ ' + +And the result (this example assumes one incoming changeset and two outgoing): + + :::console + [+1]-->hg-prompt/default-->[+2] at feature-bookmark + steve at myhost in ~/src/hg-prompt + $ + +Learn More +---------- + +From here you can take a look at the [full documentation][] to see all the +interesting things `hg-prompt` can do. + +[full documentation]: /documentation/ \ No newline at end of file diff --git a/.hgext/hg-prompt/prompt.py b/.hgext/hg-prompt/prompt.py new file mode 100644 index 00000000..6258b2cb --- /dev/null +++ b/.hgext/hg-prompt/prompt.py @@ -0,0 +1,600 @@ +#!/usr/bin/env python + +from __future__ import with_statement + +'''get repository information for use in a shell prompt + +Take a string, parse any special variables inside, and output the result. + +Useful mostly for putting information about the current repository into +a shell prompt. +''' + +import re +import os +import subprocess +from datetime import datetime, timedelta +from os import path +from mercurial import extensions, commands, cmdutil, help +from mercurial.node import hex, short + +CACHE_PATH = ".hg/prompt/cache" +CACHE_TIMEOUT = timedelta(minutes=15) + +FILTER_ARG = re.compile(r'\|.+\((.*)\)') + +def _cache_remote(repo, kind): + cache = path.join(repo.root, CACHE_PATH, kind) + c_tmp = cache + '.temp' + + # This is kind of a hack and I feel a little bit dirty for doing it. + IGNORE = open('NUL:','w') if subprocess.mswindows else open('/dev/null','w') + + subprocess.call(['hg', kind, '--quiet'], stdout=file(c_tmp, 'w'), stderr=IGNORE) + os.rename(c_tmp, cache) + return + +def _with_groups(groups, out): + out_groups = [groups[0]] + [groups[-1]] + + if any(out_groups) and not all(out_groups): + print 'Error parsing prompt string. Mismatched braces?' + + out = out.replace('%', '%%') + return ("%s" + out + "%s") % (out_groups[0][:-1] if out_groups[0] else '', + out_groups[1][1:] if out_groups[1] else '') + +def _get_filter(name, g): + '''Return the filter with the given name, or None if it was not used.''' + matching_filters = filter(lambda s: s and s.startswith('|%s' % name), g) + if not matching_filters: + return None + + # Later filters will override earlier ones, for now. + f = matching_filters[-1] + + return f + +def _get_filter_arg(f): + if not f: + return None + + args = FILTER_ARG.match(f).groups() + if args: + return args[0] + else: + return None + +def prompt(ui, repo, fs='', **opts): + '''get repository information for use in a shell prompt + + Take a string and output it for use in a shell prompt. You can use + keywords in curly braces:: + + $ hg prompt "currently on {branch}" + currently on default + + You can also use an extended form of any keyword:: + + {optional text here{keyword}more optional text} + + This will expand the inner {keyword} and output it along with the extra + text only if the {keyword} expands successfully. This is useful if you + have a keyword that may not always apply to the current state and you + have some text that you would like to see only if it is appropriate:: + + $ hg prompt "currently at {bookmark}" + currently at + $ hg prompt "{currently at {bookmark}}" + $ hg bookmark my-bookmark + $ hg prompt "{currently at {bookmark}}" + currently at my-bookmark + + See 'hg help prompt-keywords' for a list of available keywords. + ''' + + def _basename(m): + return _with_groups(m.groups(), path.basename(repo.root)) if repo.root else '' + + def _bookmark(m): + try: + book = extensions.find('bookmarks').current(repo) + except AttributeError: + book = getattr(repo, '_bookmarkcurrent', None) + return _with_groups(m.groups(), book) if book else '' + + def _branch(m): + g = m.groups() + + branch = repo.dirstate.branch() + quiet = _get_filter('quiet', g) + + out = branch if (not quiet) or (branch != 'default') else '' + + return _with_groups(g, out) if out else '' + + def _closed(m): + g = m.groups() + + quiet = _get_filter('quiet', g) + + p = repo[None].parents()[0] + pn = p.node() + branch = repo.dirstate.branch() + closed = (p.extra().get('close') + and pn in repo.branchheads(branch, closed=True)) + out = 'X' if (not quiet) and closed else '' + + return _with_groups(g, out) if out else '' + + def _count(m): + g = m.groups() + query = [g[1][1:]] if g[1] else ['all()'] + return _with_groups(g, str(len(cmdutil.revrange(repo, query)))) + + def _node(m): + g = m.groups() + + parents = repo[None].parents() + p = 0 if '|merge' not in g else 1 + p = p if len(parents) > p else None + + format = short if '|short' in g else hex + + node = format(parents[p].node()) if p is not None else None + return _with_groups(g, str(node)) if node else '' + + def _patch(m): + g = m.groups() + + try: + extensions.find('mq') + except KeyError: + return '' + + q = repo.mq + + if _get_filter('quiet', g) and not len(q.series): + return '' + + if _get_filter('applied', g): + out = str(len(q.applied)) + elif _get_filter('unapplied', g): + out = str(len(q.unapplied(repo))) + elif _get_filter('count', g): + out = str(len(q.series)) + else: + out = q.applied[-1].name if q.applied else '' + + return _with_groups(g, out) if out else '' + + def _patches(m): + g = m.groups() + + try: + extensions.find('mq') + except KeyError: + return '' + + join_filter = _get_filter('join', g) + join_filter_arg = _get_filter_arg(join_filter) + sep = join_filter_arg if join_filter else ' -> ' + + patches = repo.mq.series + applied = [p.name for p in repo.mq.applied] + unapplied = filter(lambda p: p not in applied, patches) + + if _get_filter('hide_applied', g): + patches = filter(lambda p: p not in applied, patches) + if _get_filter('hide_unapplied', g): + patches = filter(lambda p: p not in unapplied, patches) + + if _get_filter('reverse', g): + patches = reversed(patches) + + pre_applied_filter = _get_filter('pre_applied', g) + pre_applied_filter_arg = _get_filter_arg(pre_applied_filter) + post_applied_filter = _get_filter('post_applied', g) + post_applied_filter_arg = _get_filter_arg(post_applied_filter) + + pre_unapplied_filter = _get_filter('pre_unapplied', g) + pre_unapplied_filter_arg = _get_filter_arg(pre_unapplied_filter) + post_unapplied_filter = _get_filter('post_unapplied', g) + post_unapplied_filter_arg = _get_filter_arg(post_unapplied_filter) + + for n, patch in enumerate(patches): + if patch in applied: + if pre_applied_filter: + patches[n] = pre_applied_filter_arg + patches[n] + if post_applied_filter: + patches[n] = patches[n] + post_applied_filter_arg + elif patch in unapplied: + if pre_unapplied_filter: + patches[n] = pre_unapplied_filter_arg + patches[n] + if post_unapplied_filter: + patches[n] = patches[n] + post_unapplied_filter_arg + + return _with_groups(g, sep.join(patches)) if patches else '' + + def _queue(m): + g = m.groups() + + try: + extensions.find('mq') + except KeyError: + return '' + + q = repo.mq + + out = os.path.basename(q.path) + if out == 'patches' and not os.path.isdir(q.path): + out = '' + elif out.startswith('patches-'): + out = out[8:] + + return _with_groups(g, out) if out else '' + + def _remote(kind): + def _r(m): + g = m.groups() + + cache_dir = path.join(repo.root, CACHE_PATH) + cache = path.join(cache_dir, kind) + if not path.isdir(cache_dir): + os.makedirs(cache_dir) + + cache_exists = path.isfile(cache) + + cache_time = (datetime.fromtimestamp(os.stat(cache).st_mtime) + if cache_exists else None) + if not cache_exists or cache_time < datetime.now() - CACHE_TIMEOUT: + if not cache_exists: + open(cache, 'w').close() + subprocess.Popen(['hg', 'prompt', '--cache-%s' % kind]) + + if cache_exists: + with open(cache) as c: + count = len(c.readlines()) + if g[1]: + return _with_groups(g, str(count)) if count else '' + else: + return _with_groups(g, '') if count else '' + else: + return '' + return _r + + def _rev(m): + g = m.groups() + + parents = repo[None].parents() + parent = 0 if '|merge' not in g else 1 + parent = parent if len(parents) > parent else None + + rev = parents[parent].rev() if parent is not None else -1 + return _with_groups(g, str(rev)) if rev >= 0 else '' + + def _root(m): + return _with_groups(m.groups(), repo.root) if repo.root else '' + + def _status(m): + g = m.groups() + + st = repo.status(unknown=True)[:5] + modified = any(st[:4]) + unknown = len(st[-1]) > 0 + + flag = '' + if '|modified' not in g and '|unknown' not in g: + flag = '!' if modified else '?' if unknown else '' + else: + if '|modified' in g: + flag += '!' if modified else '' + if '|unknown' in g: + flag += '?' if unknown else '' + + return _with_groups(g, flag) if flag else '' + + def _tags(m): + g = m.groups() + + sep = g[1][1:] if g[1] else ' ' + tags = repo[None].tags() + + return _with_groups(g, sep.join(tags)) if tags else '' + + def _task(m): + try: + task = extensions.find('tasks').current(repo) + return _with_groups(m.groups(), task) if task else '' + except KeyError: + return '' + + def _tip(m): + g = m.groups() + + format = short if '|short' in g else hex + + tip = repo[len(repo) - 1] + rev = tip.rev() + tip = format(tip.node()) if '|node' in g else tip.rev() + + return _with_groups(g, str(tip)) if rev >= 0 else '' + + def _update(m): + if not repo.branchtags(): + # We are in an empty repository. + return '' + + current_rev = repo[None].parents()[0] + to = repo[repo.branchtags()[current_rev.branch()]] + return _with_groups(m.groups(), '^') if current_rev != to else '' + + + if opts.get("angle_brackets"): + tag_start = r'\<([^><]*?\<)?' + tag_end = r'(\>[^><]*?)?>' + brackets = '<>' + else: + tag_start = r'\{([^{}]*?\{)?' + tag_end = r'(\}[^{}]*?)?\}' + brackets = '{}' + + patterns = { + 'bookmark': _bookmark, + 'branch(\|quiet)?': _branch, + 'closed(\|quiet)?': _closed, + 'count(\|[^%s]*?)?' % brackets[-1]: _count, + 'node(?:' + '(\|short)' + '|(\|merge)' + ')*': _node, + 'patch(?:' + '(\|applied)' + '|(\|unapplied)' + '|(\|count)' + '|(\|quiet)' + ')*': _patch, + 'patches(?:' + + '(\|join\([^%s]*?\))' % brackets[-1] + + '|(\|reverse)' + + '|(\|hide_applied)' + + '|(\|hide_unapplied)' + + '|(\|pre_applied\([^%s]*?\))' % brackets[-1] + + '|(\|post_applied\([^%s]*?\))' % brackets[-1] + + '|(\|pre_unapplied\([^%s]*?\))' % brackets[-1] + + '|(\|post_unapplied\([^%s]*?\))' % brackets[-1] + + ')*': _patches, + 'queue': _queue, + 'rev(\|merge)?': _rev, + 'root': _root, + 'root\|basename': _basename, + 'status(?:' + '(\|modified)' + '|(\|unknown)' + ')*': _status, + 'tags(\|[^%s]*?)?' % brackets[-1]: _tags, + 'task': _task, + 'tip(?:' + '(\|node)' + '|(\|short)' + ')*': _tip, + 'update': _update, + + 'incoming(\|count)?': _remote('incoming'), + 'outgoing(\|count)?': _remote('outgoing'), + } + + if opts.get("cache_incoming"): + _cache_remote(repo, 'incoming') + + if opts.get("cache_outgoing"): + _cache_remote(repo, 'outgoing') + + for tag, repl in patterns.items(): + fs = re.sub(tag_start + tag + tag_end, repl, fs) + ui.status(fs) + +def _pull_with_cache(orig, ui, repo, *args, **opts): + """Wrap the pull command to delete the incoming cache as well.""" + res = orig(ui, repo, *args, **opts) + cache = path.join(repo.root, CACHE_PATH, 'incoming') + if path.isfile(cache): + os.remove(cache) + return res + +def _push_with_cache(orig, ui, repo, *args, **opts): + """Wrap the push command to delete the outgoing cache as well.""" + res = orig(ui, repo, *args, **opts) + cache = path.join(repo.root, CACHE_PATH, 'outgoing') + if path.isfile(cache): + os.remove(cache) + return res + +def uisetup(ui): + extensions.wrapcommand(commands.table, 'pull', _pull_with_cache) + extensions.wrapcommand(commands.table, 'push', _push_with_cache) + +cmdtable = { + "prompt": + (prompt, [ + ('', 'angle-brackets', None, 'use angle brackets (<>) for keywords'), + ('', 'cache-incoming', None, 'used internally by hg-prompt'), + ('', 'cache-outgoing', None, 'used internally by hg-prompt'), + ], + 'hg prompt STRING') +} +help.helptable += ( + (['prompt-keywords', 'prompt-keywords'], ('Keywords supported by hg-prompt'), + (r'''hg-prompt currently supports a number of keywords. + +Some keywords support filters. Filters can be chained when it makes +sense to do so. When in doubt, try it! + +bookmark + Display the current bookmark (requires the bookmarks extension). + +branch + Display the current branch. + + |quiet + Display the current branch only if it is not the default branch. + +closed + Display `X` if working on a closed branch (i.e. committing now would reopen + the branch). + +count + Display the number of revisions in the given revset (the revset `all()` + will be used if none is given). + + See `hg help revsets` for more information. + + |REVSET + The revset to count. + +incoming + Display nothing, but if the default path contains incoming changesets the + extra text will be expanded. + + For example: `{incoming changes{incoming}}` will expand to + `incoming changes` if there are changes, otherwise nothing. + + Checking for incoming changesets is an expensive operation, so `hg-prompt` + will cache the results in `.hg/prompt/cache/` and refresh them every 15 + minutes. + + |count + Display the number of incoming changesets (if greater than 0). + +node + Display the (full) changeset hash of the current parent. + + |short + Display the hash as the short, 12-character form. + + |merge + Display the hash of the changeset you're merging with. + +outgoing + Display nothing, but if the current repository contains outgoing + changesets (to default) the extra text will be expanded. + + For example: `{outgoing changes{outgoing}}` will expand to + `outgoing changes` if there are changes, otherwise nothing. + + Checking for outgoing changesets is an expensive operation, so `hg-prompt` + will cache the results in `.hg/prompt/cache/` and refresh them every 15 + minutes. + + |count + Display the number of outgoing changesets (if greater than 0). + +patch + Display the topmost currently-applied patch (requires the mq + extension). + + |count + Display the number of patches in the queue. + + |applied + Display the number of currently applied patches in the queue. + + |unapplied + Display the number of currently unapplied patches in the queue. + + |quiet + Display a number only if there are any patches in the queue. + +patches + Display a list of the current patches in the queue. It will look like + this: + + :::console + $ hg prompt '{patches}' + bottom-patch -> middle-patch -> top-patch + + |reverse + Display the patches in reverse order (i.e. topmost first). + + |hide_applied + Do not display applied patches. + + |hide_unapplied + Do not display unapplied patches. + + |join(SEP) + Display SEP between each patch, instead of the default ` -> `. + + |pre_applied(STRING) + Display STRING immediately before each applied patch. Useful for + adding color codes. + + |post_applied(STRING) + Display STRING immediately after each applied patch. Useful for + resetting color codes. + + |pre_unapplied(STRING) + Display STRING immediately before each unapplied patch. Useful for + adding color codes. + + |post_unapplied(STRING) + Display STRING immediately after each unapplied patch. Useful for + resetting color codes. + +queue + Display the name of the current MQ queue. + +rev + Display the repository-local changeset number of the current parent. + + |merge + Display the repository-local changeset number of the changeset you're + merging with. + +root + Display the full path to the root of the current repository, without a + trailing slash. + + |basename + Display the directory name of the root of the current repository. For + example, if the repository is in `/home/u/myrepo` then this keyword + would expand to `myrepo`. + +status + Display `!` if the repository has any changed/added/removed files, + otherwise `?` if it has any untracked (but not ignored) files, otherwise + nothing. + + |modified + Display `!` if the current repository contains files that have been + modified, added, removed, or deleted, otherwise nothing. + + |unknown + Display `?` if the current repository contains untracked files, + otherwise nothing. + +tags + Display the tags of the current parent, separated by a space. + + |SEP + Display the tags of the current parent, separated by `SEP`. + +task + Display the current task (requires the tasks extension). + +tip + Display the repository-local changeset number of the current tip. + + |node + Display the (full) changeset hash of the current tip. + + |short + Display a short form of the changeset hash of the current tip (must be + used with the **|node** filter) + +update + Display `^` if the current parent is not the tip of the current branch, + otherwise nothing. In effect, this lets you see if running `hg update` + would do something. +''')), +) diff --git a/.hgext/hg-prompt/prompt.pyc b/.hgext/hg-prompt/prompt.pyc new file mode 100644 index 00000000..9d168f8e Binary files /dev/null and b/.hgext/hg-prompt/prompt.pyc differ diff --git a/.hgext/hg-prompt/tests/__init__.py b/.hgext/hg-prompt/tests/__init__.py new file mode 100644 index 00000000..ab06bf6f --- /dev/null +++ b/.hgext/hg-prompt/tests/__init__.py @@ -0,0 +1,5 @@ +"""Unit tests for hg-prompt. + +The tests require nose: pip install nose && nosetests --with-doctest -v + +""" \ No newline at end of file diff --git a/.hgext/hg-prompt/tests/test_branch.py b/.hgext/hg-prompt/tests/test_branch.py new file mode 100644 index 00000000..d49905f7 --- /dev/null +++ b/.hgext/hg-prompt/tests/test_branch.py @@ -0,0 +1,41 @@ +'''Test output of {branch}.''' + +from nose import * +from util import * + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_default_branch(): + output = prompt(fs='{branch}') + assert output == 'default' + + output = prompt(fs='{on {branch}}') + assert output == 'on default' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_non_default_branch(): + hg_branch('test') + + output = prompt(fs='{branch}') + assert output == 'test' + + output = prompt(fs='{on the {branch} branch}') + assert output == 'on the test branch' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_quiet_filter(): + output = prompt(fs='{branch|quiet}') + assert output == '' + + output = prompt(fs='{on {branch|quiet}}') + assert output == '' + + hg_branch('test') + + output = prompt(fs='{branch|quiet}') + assert output == 'test' + + output = prompt(fs='{on the {branch|quiet} branch}') + assert output == 'on the test branch' diff --git a/.hgext/hg-prompt/tests/test_node.py b/.hgext/hg-prompt/tests/test_node.py new file mode 100644 index 00000000..6a8f0510 --- /dev/null +++ b/.hgext/hg-prompt/tests/test_node.py @@ -0,0 +1,73 @@ +'''Test output of {node}.''' + +from nose import * +from util import * + + +def _parent_node(): + opts = { 'template': '{node}', 'rev': '.', 'date': None, 'user': None } + + _ui = get_sandbox_ui() + _ui.pushbuffer() + commands.log(_ui, get_sandbox_repo(), **opts) + + return _ui.popbuffer() + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_node(): + output = prompt(fs='{node}') + assert output == '0000000000000000000000000000000000000000' + + output = prompt(fs='{ at node {node}}') + assert output == ' at node 0000000000000000000000000000000000000000' + + hg_commit() + output = prompt(fs='{node}') + assert output == _parent_node() + + hg_commit() + output = prompt(fs='{node}') + assert output == _parent_node() + + hg_update(0) + output = prompt(fs='{node}') + assert output == _parent_node() + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_short_filter(): + output = prompt(fs='{node|short}') + assert output == '0000000000000000000000000000000000000000'[:12] + + output = prompt(fs='{ at node {node|short}}') + assert output == ' at node ' + '0000000000000000000000000000000000000000'[:12] + + hg_commit() + output = prompt(fs='{node|short}') + assert output == _parent_node()[:12] + + hg_commit() + output = prompt(fs='{node|short}') + assert output == _parent_node()[:12] + + hg_update(0) + output = prompt(fs='{node|short}') + assert output == _parent_node()[:12] + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_merge_filter(): + hg_commit('one.txt') + hg_commit('one.txt') + node_to_merge = _parent_node() + + hg_update(0) + hg_commit('two.txt') + hg_merge(1) + + output = prompt(fs='{node|merge}') + assert output == node_to_merge + + output = prompt(fs='{node|merge|short}') + assert output == node_to_merge[:12] diff --git a/.hgext/hg-prompt/tests/test_none.py b/.hgext/hg-prompt/tests/test_none.py new file mode 100644 index 00000000..5d2ece71 --- /dev/null +++ b/.hgext/hg-prompt/tests/test_none.py @@ -0,0 +1,22 @@ +'''Test output without keywords.''' + +from nose import * +from util import * + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_blank(): + output = prompt(fs='') + assert output == '' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_text(): + output = prompt(fs='test one two three') + assert output == 'test one two three' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_invalid_keyword(): + output = prompt(fs='{invalidkeyword}') + assert output == '{invalidkeyword}' diff --git a/.hgext/hg-prompt/tests/test_rev.py b/.hgext/hg-prompt/tests/test_rev.py new file mode 100644 index 00000000..fcb7fbbd --- /dev/null +++ b/.hgext/hg-prompt/tests/test_rev.py @@ -0,0 +1,55 @@ +'''Test output of {node}.''' + +from nose import * +from util import * + + +def _parent_rev(): + opts = { 'template': '{rev}', 'rev': '.', 'date': None, 'user': None } + + _ui = get_sandbox_ui() + _ui.pushbuffer() + commands.log(_ui, get_sandbox_repo(), **opts) + + return _ui.popbuffer() + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_nullrev(): + output = prompt(fs='{rev}') + assert output == '' + + output = prompt(fs='{ at revision {rev}}') + assert output == '' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_rev(): + hg_commit() + + output = prompt(fs='{rev}') + assert output == _parent_rev() + + output = prompt(fs='{ at revision {rev}}') + assert output == ' at revision %s' % _parent_rev() + + hg_commit() + output = prompt(fs='{rev}') + assert output == _parent_rev() + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_merge_filter(): + hg_commit('one.txt') + hg_commit('one.txt') + rev_to_merge = _parent_rev() + + hg_update(0) + hg_commit('two.txt') + hg_merge(1) + + output = prompt(fs='{rev|merge}') + assert output == rev_to_merge + + output = prompt(fs='{ merging with {rev|merge}}') + assert output == ' merging with %s' % rev_to_merge diff --git a/.hgext/hg-prompt/tests/test_tip.py b/.hgext/hg-prompt/tests/test_tip.py new file mode 100644 index 00000000..45d79374 --- /dev/null +++ b/.hgext/hg-prompt/tests/test_tip.py @@ -0,0 +1,77 @@ +'''Test output of {tip}.''' + +from nose import * +from util import * + + +def _tip_rev(): + opts = { 'template': '{rev}', } + + _ui = get_sandbox_ui() + _ui.pushbuffer() + commands.tip(_ui, get_sandbox_repo(), **opts) + + return _ui.popbuffer() + +def _tip_node(): + opts = { 'template': '{node}', } + + _ui = get_sandbox_ui() + _ui.pushbuffer() + commands.tip(_ui, get_sandbox_repo(), **opts) + + return _ui.popbuffer() + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_empty_repo(): + output = prompt(fs='{tip}') + assert output == '' + + output = prompt(fs='{ the tip is at {tip}}') + assert output == '' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_tip(): + hg_commit() + + output = prompt(fs='{tip}') + assert output == _tip_rev() + + output = prompt(fs='{ the tip is {tip}}') + assert output == ' the tip is %s' % _tip_rev() + + hg_commit() + output = prompt(fs='{tip}') + assert output == _tip_rev() + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_node_filter(): + hg_commit() + + output = prompt(fs='{tip|node}') + assert output == _tip_node() + + output = prompt(fs='{ the tip is {tip|node}}') + assert output == ' the tip is %s' % _tip_node() + + hg_commit() + output = prompt(fs='{tip|node}') + assert output == _tip_node() + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_short_filter(): + hg_commit() + + output = prompt(fs='{tip|node|short}') + assert output == _tip_node()[:12] + + output = prompt(fs='{ the tip is {tip|node|short}}') + assert output == ' the tip is %s' % _tip_node()[:12] + + hg_commit() + output = prompt(fs='{tip|node|short}') + assert output == _tip_node()[:12] diff --git a/.hgext/hg-prompt/tests/test_update.py b/.hgext/hg-prompt/tests/test_update.py new file mode 100644 index 00000000..8741b5f4 --- /dev/null +++ b/.hgext/hg-prompt/tests/test_update.py @@ -0,0 +1,132 @@ +'''Test output of {update}.''' + +from nose import * +from util import * + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_empty_repo(): + output = prompt(fs='{update}') + assert output == '' + + output = prompt(fs='{ need to update? {update}}') + assert output == '' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_single_branch(): + hg_commit() + + output = prompt(fs='{update}') + assert output == '' + + output = prompt(fs='{ need to update? {update}}') + assert output == '' + + hg_commit() + + output = prompt(fs='{update}') + assert output == '' + + output = prompt(fs='{ need to update? {update}}') + assert output == '' + + hg_update(0) + + output = prompt(fs='{update}') + assert output == '^' + + output = prompt(fs='{ need to update? {update}}') + assert output == ' need to update? ^' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_multiple_branches(): + hg_commit() + hg_commit() + hg_commit() + + hg_update(0) + hg_commit('two.txt') + hg_commit('two.txt') + + # @ 4 + # | + # o 3 + # | + # | o 2 + # | | + # | o 1 + # |/ + # | + # o 0 + + hg_log() + + hg_update(4) + output = prompt(fs='{update}') + assert output == '' + + hg_update(3) + output = prompt(fs='{update}') + assert output == '^' + + # This test case matches the behavior of Mercurial, but it seems a bit + # unintuitive to me. + hg_update(2) + output = prompt(fs='{update}') + assert output == '^' + + hg_update(1) + output = prompt(fs='{update}') + assert output == '^' + + hg_update(0) + output = prompt(fs='{update}') + assert output == '^' + + +@with_setup(setup_sandbox, teardown_sandbox) +def test_multiple_named_branches(): + hg_commit() + hg_commit() + hg_commit() + + hg_update(0) + hg_branch('test') + hg_commit('two.txt') + hg_commit('two.txt') + + # @ 4 (test) + # | + # o 3 (test) + # | + # | o 2 (default) + # | | + # | o 1 (default) + # |/ + # | + # o 0 (default) + + hg_log() + + hg_update(4) + output = prompt(fs='{update}') + assert output == '' + + hg_update(3) + output = prompt(fs='{update}') + assert output == '^' + + hg_update(2) + output = prompt(fs='{update}') + assert output == '' + + hg_update(1) + output = prompt(fs='{update}') + assert output == '^' + + hg_update(0) + output = prompt(fs='{update}') + assert output == '^' + diff --git a/.hgext/hg-prompt/tests/util.py b/.hgext/hg-prompt/tests/util.py new file mode 100644 index 00000000..f1ba84e1 --- /dev/null +++ b/.hgext/hg-prompt/tests/util.py @@ -0,0 +1,68 @@ +"""Utilities for writing unit tests for hg-prompt.""" + +import os, shutil, sys +from mercurial import cmdutil, commands, hg, ui + +pkg_path = os.path.realpath(__file__) +sys.path =[os.path.split(os.path.split(pkg_path)[0])[0]] + sys.path +from prompt import prompt as _prompt + +_ui = ui.ui() +def prompt(fs=''): + _ui.pushbuffer() + _prompt(_ui, get_sandbox_repo(), fs=fs) + output = _ui.popbuffer() + + print output + return output + + +sandbox_path = os.path.join(os.path.realpath('.'), 'sandbox') + +def setup_sandbox(): + os.mkdir(sandbox_path) + os.chdir(sandbox_path) + + commands.init(_ui) + +def teardown_sandbox(): + os.chdir(os.path.realpath(os.path.join(sandbox_path, os.pardir))) + shutil.rmtree(sandbox_path) + +def get_sandbox_repo(): + return hg.repository(_ui, sandbox_path) + +def get_sandbox_ui(): + return _ui + + +# Mercurial command wrappers + +def hg_branch(branch='test'): + commands.branch(_ui, get_sandbox_repo(), branch) + +def hg_update(rev): + opts = { 'rev': str(rev), } + commands.update(_ui, get_sandbox_repo(), **opts) + +def hg_merge(rev): + opts = { 'rev': str(rev) } + commands.merge(_ui, get_sandbox_repo(), **opts) + +def hg_commit(filename='text.txt'): + with open(os.path.join(sandbox_path, filename), 'a') as test_file: + test_file.writelines(['test', '.']) + + opts = { 'addremove': True, 'date': None, 'user': 'Prompt Tester', + 'logfile': None, 'message': "Sandbox commit." } + commands.commit(get_sandbox_ui(), get_sandbox_repo(), **opts) + +def hg_log(): + opts = { 'template': '{rev} {desc}\n', 'rev': None, 'date': None, 'user': None } + + _ui.pushbuffer() + commands.log(_ui, get_sandbox_repo(), **opts) + output = _ui.popbuffer() + + return output + diff --git a/.hgext/hgshelve/.hg/00changelog.i b/.hgext/hgshelve/.hg/00changelog.i new file mode 100644 index 00000000..d3a83110 Binary files /dev/null and b/.hgext/hgshelve/.hg/00changelog.i differ diff --git a/.hgext/hgshelve/.hg/branch b/.hgext/hgshelve/.hg/branch new file mode 100644 index 00000000..4ad96d51 --- /dev/null +++ b/.hgext/hgshelve/.hg/branch @@ -0,0 +1 @@ +default diff --git a/.hgext/hgshelve/.hg/branchheads.cache b/.hgext/hgshelve/.hg/branchheads.cache new file mode 100644 index 00000000..0933e37b --- /dev/null +++ b/.hgext/hgshelve/.hg/branchheads.cache @@ -0,0 +1,2 @@ +3d7a2a3ae9a5e8ba4b5a333acb9d5c2a12cc8793 36 +3d7a2a3ae9a5e8ba4b5a333acb9d5c2a12cc8793 default diff --git a/.hgext/hgshelve/.hg/dirstate b/.hgext/hgshelve/.hg/dirstate new file mode 100644 index 00000000..5986c1de Binary files /dev/null and b/.hgext/hgshelve/.hg/dirstate differ diff --git a/.hgext/hgshelve/.hg/hgrc b/.hgext/hgshelve/.hg/hgrc new file mode 100644 index 00000000..0eed1b71 --- /dev/null +++ b/.hgext/hgshelve/.hg/hgrc @@ -0,0 +1,2 @@ +[paths] +default = ssh://hg@bitbucket.org/tksoh/hgshelve diff --git a/.hgext/hgshelve/.hg/requires b/.hgext/hgshelve/.hg/requires new file mode 100644 index 00000000..5175383b --- /dev/null +++ b/.hgext/hgshelve/.hg/requires @@ -0,0 +1,3 @@ +revlogv1 +store +fncache diff --git a/.hgext/hgshelve/.hg/store/00changelog.i b/.hgext/hgshelve/.hg/store/00changelog.i new file mode 100644 index 00000000..9cbbc95d Binary files /dev/null and b/.hgext/hgshelve/.hg/store/00changelog.i differ diff --git a/.hgext/hgshelve/.hg/store/00manifest.i b/.hgext/hgshelve/.hg/store/00manifest.i new file mode 100644 index 00000000..9f79ed1b Binary files /dev/null and b/.hgext/hgshelve/.hg/store/00manifest.i differ diff --git a/.hgext/hgshelve/.hg/store/data/hgshelve.py.i b/.hgext/hgshelve/.hg/store/data/hgshelve.py.i new file mode 100644 index 00000000..3182fb03 Binary files /dev/null and b/.hgext/hgshelve/.hg/store/data/hgshelve.py.i differ diff --git a/.hgext/hgshelve/.hg/store/data/shelve.py.i b/.hgext/hgshelve/.hg/store/data/shelve.py.i new file mode 100644 index 00000000..0eaf6429 Binary files /dev/null and b/.hgext/hgshelve/.hg/store/data/shelve.py.i differ diff --git a/.hgext/hgshelve/.hg/store/data/test-shelve-color.i b/.hgext/hgshelve/.hg/store/data/test-shelve-color.i new file mode 100644 index 00000000..c0bd5d37 Binary files /dev/null and b/.hgext/hgshelve/.hg/store/data/test-shelve-color.i differ diff --git a/.hgext/hgshelve/.hg/store/data/test-shelve-color.out.i b/.hgext/hgshelve/.hg/store/data/test-shelve-color.out.i new file mode 100644 index 00000000..b8d898e9 Binary files /dev/null and b/.hgext/hgshelve/.hg/store/data/test-shelve-color.out.i differ diff --git a/.hgext/hgshelve/.hg/store/data/test-shelve.i b/.hgext/hgshelve/.hg/store/data/test-shelve.i new file mode 100644 index 00000000..5e39bfc2 Binary files /dev/null and b/.hgext/hgshelve/.hg/store/data/test-shelve.i differ diff --git a/.hgext/hgshelve/.hg/store/data/test-shelve.out.i b/.hgext/hgshelve/.hg/store/data/test-shelve.out.i new file mode 100644 index 00000000..1edf1d1c Binary files /dev/null and b/.hgext/hgshelve/.hg/store/data/test-shelve.out.i differ diff --git a/.hgext/hgshelve/.hg/store/fncache b/.hgext/hgshelve/.hg/store/fncache new file mode 100644 index 00000000..bb0ab817 --- /dev/null +++ b/.hgext/hgshelve/.hg/store/fncache @@ -0,0 +1,6 @@ +data/hgshelve.py.i +data/shelve.py.i +data/test-shelve.i +data/test-shelve-color.i +data/test-shelve-color.out.i +data/test-shelve.out.i diff --git a/.hgext/hgshelve/.hg/store/undo b/.hgext/hgshelve/.hg/store/undo new file mode 100644 index 00000000..51812439 Binary files /dev/null and b/.hgext/hgshelve/.hg/store/undo differ diff --git a/.hgext/hgshelve/.hg/tags.cache b/.hgext/hgshelve/.hg/tags.cache new file mode 100644 index 00000000..c0c81422 --- /dev/null +++ b/.hgext/hgshelve/.hg/tags.cache @@ -0,0 +1,2 @@ +36 3d7a2a3ae9a5e8ba4b5a333acb9d5c2a12cc8793 + diff --git a/.hgext/hgshelve/.hg/undo.branch b/.hgext/hgshelve/.hg/undo.branch new file mode 100644 index 00000000..331d858c --- /dev/null +++ b/.hgext/hgshelve/.hg/undo.branch @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/.hgext/hgshelve/.hg/undo.desc b/.hgext/hgshelve/.hg/undo.desc new file mode 100644 index 00000000..1b44b3e0 --- /dev/null +++ b/.hgext/hgshelve/.hg/undo.desc @@ -0,0 +1,3 @@ +0 +pull +ssh://hg@bitbucket.org/tksoh/hgshelve diff --git a/.hgext/hgshelve/.hg/undo.dirstate b/.hgext/hgshelve/.hg/undo.dirstate new file mode 100644 index 00000000..e69de29b diff --git a/.hgext/hgshelve/hgshelve.py b/.hgext/hgshelve/hgshelve.py new file mode 100644 index 00000000..7be5c1a1 --- /dev/null +++ b/.hgext/hgshelve/hgshelve.py @@ -0,0 +1,661 @@ +# shelve.py +# +# Copyright 2007 Bryan O'Sullivan +# Copyright 2007 TK Soh +# +# This software may be used and distributed according to the terms of +# the GNU General Public License, incorporated herein by reference. + +'''interactive change selection to set aside that may be restored later''' + +from mercurial.i18n import _ +from mercurial import cmdutil, commands, cmdutil, hg, mdiff, patch, revlog +from mercurial import util, fancyopts, extensions +import copy, cStringIO, errno, operator, os, re, shutil, tempfile + +lines_re = re.compile(r'@@ -(\d+),(\d+) \+(\d+),(\d+) @@\s*(.*)') + +def internalpatch(patchobj, ui, strip, cwd, reverse=False, files={}): + """use builtin patch to apply to the working directory. + returns whether patch was applied with fuzz factor. + + Adapted from patch.internalpatch() to support reverse patching. + """ + + eolmode = ui.config('patch', 'eol', 'strict') + + if eolmode.lower() not in patch.eolmodes: + raise util.Abort(_('Unsupported line endings type: %s') % eolmode) + + try: + fp = file(patchobj, 'rb') + except TypeError: + fp = patchobj + if cwd: + curdir = os.getcwd() + os.chdir(cwd) + try: + ret = patch.applydiff(ui, fp, files, strip=strip, eolmode=eolmode) + finally: + if cwd: + os.chdir(curdir) + if ret < 0: + raise PatchError + return ret > 0 + +def scanpatch(fp): + lr = patch.linereader(fp) + + def scanwhile(first, p): + lines = [first] + while True: + line = lr.readline() + if not line: + break + if p(line): + lines.append(line) + else: + lr.push(line) + break + return lines + + while True: + line = lr.readline() + if not line: + break + if line.startswith('diff --git a/'): + def notheader(line): + s = line.split(None, 1) + return not s or s[0] not in ('---', 'diff') + header = scanwhile(line, notheader) + fromfile = lr.readline() + if fromfile.startswith('---'): + tofile = lr.readline() + header += [fromfile, tofile] + else: + lr.push(fromfile) + yield 'file', header + elif line[0] == ' ': + yield 'context', scanwhile(line, lambda l: l[0] in ' \\') + elif line[0] in '-+': + yield 'hunk', scanwhile(line, lambda l: l[0] in '-+\\') + else: + m = lines_re.match(line) + if m: + yield 'range', m.groups() + else: + raise patch.PatchError('unknown patch content: %r' % line) + +class header(object): + diff_re = re.compile('diff --git a/(.*) b/(.*)$') + allhunks_re = re.compile('(?:index|new file|deleted file) ') + pretty_re = re.compile('(?:new file|deleted file) ') + special_re = re.compile('(?:index|new|deleted|copy|rename) ') + + def __init__(self, header): + self.header = header + self.hunks = [] + + def binary(self): + for h in self.header: + if h.startswith('index '): + return True + + def pretty(self, fp): + for h in self.header: + if h.startswith('index '): + fp.write(_('this modifies a binary file (all or nothing)\n')) + break + if self.pretty_re.match(h): + fp.write(h) + if self.binary(): + fp.write(_('this is a binary file\n')) + break + if h.startswith('---'): + fp.write(_('%d hunks, %d lines changed\n') % + (len(self.hunks), + sum([h.added + h.removed for h in self.hunks]))) + break + fp.write(h) + + def write(self, fp): + fp.write(''.join(self.header)) + + def allhunks(self): + for h in self.header: + if self.allhunks_re.match(h): + return True + + def files(self): + fromfile, tofile = self.diff_re.match(self.header[0]).groups() + if fromfile == tofile: + return [fromfile] + return [fromfile, tofile] + + def filename(self): + return self.files()[-1] + + def __repr__(self): + return '
' % (' '.join(map(repr, self.files()))) + + def special(self): + for h in self.header: + if self.special_re.match(h): + return True + +def countchanges(hunk): + add = len([h for h in hunk if h[0] == '+']) + rem = len([h for h in hunk if h[0] == '-']) + return add, rem + +class hunk(object): + maxcontext = 3 + + def __init__(self, header, fromline, toline, proc, before, hunk, after): + def trimcontext(number, lines): + delta = len(lines) - self.maxcontext + if False and delta > 0: + return number + delta, lines[:self.maxcontext] + return number, lines + + self.header = header + self.fromline, self.before = trimcontext(fromline, before) + self.toline, self.after = trimcontext(toline, after) + self.proc = proc + self.hunk = hunk + self.added, self.removed = countchanges(self.hunk) + + def __cmp__(self, rhs): + # since the hunk().toline needs to be adjusted when hunks are + # removed/added, we can't take it into account when we cmp + attrs = ['header', 'fromline', 'proc', 'hunk', 'added', 'removed'] + for attr in attrs: + selfattr = getattr(self, attr, None) + rhsattr = getattr(rhs, attr, None) + + if selfattr is None or rhsattr is None: + raise util.Abort(_('non-existant attribute %s') % attr) + + rv = cmp(selfattr, rhsattr) + if rv != 0: + return rv + return rv + + + def write(self, fp): + delta = len(self.before) + len(self.after) + if self.after and self.after[-1] == '\\ No newline at end of file\n': + delta -= 1 + fromlen = delta + self.removed + tolen = delta + self.added + fp.write('@@ -%d,%d +%d,%d @@%s\n' % + (self.fromline, fromlen, self.toline, tolen, + self.proc and (' ' + self.proc))) + fp.write(''.join(self.before + self.hunk + self.after)) + + pretty = write + + def filename(self): + return self.header.filename() + + def __repr__(self): + return '' % (self.filename(), self.fromline) + +def parsepatch(fp): + class parser(object): + def __init__(self): + self.fromline = 0 + self.toline = 0 + self.proc = '' + self.header = None + self.context = [] + self.before = [] + self.hunk = [] + self.stream = [] + + def addrange(self, (fromstart, fromend, tostart, toend, proc)): + self.fromline = int(fromstart) + self.toline = int(tostart) + self.proc = proc + + def addcontext(self, context): + if self.hunk: + h = hunk(self.header, self.fromline, self.toline, self.proc, + self.before, self.hunk, context) + self.header.hunks.append(h) + self.stream.append(h) + self.fromline += len(self.before) + h.removed + self.toline += len(self.before) + h.added + self.before = [] + self.hunk = [] + self.proc = '' + self.context = context + + def addhunk(self, hunk): + if self.context: + self.before = self.context + self.context = [] + self.hunk = hunk + + def newfile(self, hdr): + self.addcontext([]) + h = header(hdr) + self.stream.append(h) + self.header = h + + def finished(self): + self.addcontext([]) + return self.stream + + transitions = { + 'file': {'context': addcontext, + 'file': newfile, + 'hunk': addhunk, + 'range': addrange}, + 'context': {'file': newfile, + 'hunk': addhunk, + 'range': addrange}, + 'hunk': {'context': addcontext, + 'file': newfile, + 'range': addrange}, + 'range': {'context': addcontext, + 'hunk': addhunk}, + } + + p = parser() + + state = 'context' + for newstate, data in scanpatch(fp): + try: + p.transitions[state][newstate](p, data) + except KeyError: + raise patch.PatchError('unhandled transition: %s -> %s' % + (state, newstate)) + state = newstate + return p.finished() + +def filterpatch(ui, chunks, shouldprompt=True): + chunks = list(chunks) + chunks.reverse() + seen = {} + def consumefile(): + consumed = [] + while chunks: + if isinstance(chunks[-1], header): + break + else: + consumed.append(chunks.pop()) + return consumed + + resp_all = [None] + + """ If we're not to prompt (i.e. they specified the --all flag) + we pre-emptively set the 'all' flag """ + if shouldprompt == False: + resp_all = ['y'] + + resp_file = [None] + applied = {} + def prompt(query): + if resp_all[0] is not None: + return resp_all[0] + if resp_file[0] is not None: + return resp_file[0] + while True: + resps = _('[Ynsfdaq?]') + choices = (_('&Yes, shelve this change'), + _('&No, skip this change'), + _('&Skip remaining changes to this file'), + _('Shelve remaining changes to this &file'), + _('&Done, skip remaining changes and files'), + _('Shelve &all changes to all remaining files'), + _('&Quit, shelving no changes'), + _('&?')) + r = ui.promptchoice("%s %s " % (query, resps), choices) + if r == 7: + c = shelve.__doc__.find('y - shelve this change') + for l in shelve.__doc__[c:].splitlines(): + if l: ui.write(_(l.strip()) + '\n') + continue + elif r == 0: # yes + ret = 'y' + elif r == 1: # no + ret = 'n' + elif r == 2: # Skip + ret = resp_file[0] = 'n' + elif r == 3: # file (shelve remaining) + ret = resp_file[0] = 'y' + elif r == 4: # done, skip remaining + ret = resp_all[0] = 'n' + elif r == 5: # all + ret = resp_all[0] = 'y' + elif r == 6: # quit + raise util.Abort(_('user quit')) + return ret + while chunks: + chunk = chunks.pop() + if isinstance(chunk, header): + resp_file = [None] + fixoffset = 0 + hdr = ''.join(chunk.header) + if hdr in seen: + consumefile() + continue + seen[hdr] = True + if resp_all[0] is None: + chunk.pretty(ui) + if shouldprompt == True: + r = prompt(_('shelve changes to %s?') % + _(' and ').join(map(repr, chunk.files()))) + else: + r = 'y' + + if r == 'y': + applied[chunk.filename()] = [chunk] + if chunk.allhunks(): + applied[chunk.filename()] += consumefile() + else: + consumefile() + else: + if resp_file[0] is None and resp_all[0] is None: + chunk.pretty(ui) + r = prompt(_('shelve this change to %r?') % + chunk.filename()) + if r == 'y': + if fixoffset: + chunk = copy.copy(chunk) + chunk.toline += fixoffset + applied[chunk.filename()].append(chunk) + else: + fixoffset += chunk.removed - chunk.added + return reduce(operator.add, [h for h in applied.itervalues() + if h[0].special() or len(h) > 1], []) + +def refilterpatch(allchunk, selected): + ''' return unshelved chunks of files to be shelved ''' + l = [] + fil = [] + for c in allchunk: + if isinstance(c, header): + if len(l) > 1 and l[0] in selected: + fil += l + l = [c] + elif c not in selected: + l.append(c) + if len(l) > 1 and l[0] in selected: + fil += l + return fil + +def makebackup(ui, repo, dir, files): + try: + os.mkdir(dir) + except OSError, err: + if err.errno != errno.EEXIST: + raise + + backups = {} + for f in files: + fd, tmpname = tempfile.mkstemp(prefix=f.replace('/', '_')+'.', + dir=dir) + os.close(fd) + ui.debug('backup %r as %r\n' % (f, tmpname)) + util.copyfile(repo.wjoin(f), tmpname) + backups[f] = tmpname + + return backups + +def getshelfpath(repo, name): + if name: + shelfpath = "shelves/" + name + else: + # Check if a shelf from an older version exists + if os.path.isfile(repo.join('shelve')): + shelfpath = 'shelve' + else: + shelfpath = "shelves/default" + + return shelfpath + +def shelve(ui, repo, *pats, **opts): + '''interactively select changes to set aside + + If a list of files is omitted, all changes reported by "hg status" + will be candidates for shelving. + + You will be prompted for whether to shelve changes to each + modified file, and for files with multiple changes, for each + change to use. + + The shelve command works with the Color extension to display + diffs in color. + + On each prompt, the following responses are possible:: + + y - shelve this change + n - skip this change + + s - skip remaining changes to this file + f - shelve remaining changes to this file + + d - done, skip remaining changes and files + a - shelve all changes to all remaining files + q - quit, shelving no changes + + ? - display help''' + + if not ui.interactive: + raise util.Abort(_('shelve can only be run interactively')) + + # List all the active shelves by name and return ' + if opts['list']: + listshelves(ui,repo) + return + + forced = opts['force'] or opts['append'] + + # Shelf name and path + shelfname = opts.get('name') + shelfpath = getshelfpath(repo, shelfname) + + if os.path.exists(repo.join(shelfpath)) and not forced: + raise util.Abort(_('shelve data already exists')) + + def shelvefunc(ui, repo, message, match, opts): + changes = repo.status(match=match)[:5] + modified, added, removed = changes[:3] + files = modified + added + removed + diffopts = mdiff.diffopts(git=True, nodates=True) + patch_diff = ''.join(patch.diff(repo, repo.dirstate.parents()[0], + match=match, changes=changes, opts=diffopts)) + + fp = cStringIO.StringIO(patch_diff) + ac = parsepatch(fp) + fp.close() + + chunks = filterpatch(ui, ac, not opts['all']) + rc = refilterpatch(ac, chunks) + + contenders = {} + for h in chunks: + try: contenders.update(dict.fromkeys(h.files())) + except AttributeError: pass + + newfiles = [f for f in files if f in contenders] + + if not newfiles: + ui.status(_('no changes to shelve\n')) + return 0 + + modified = dict.fromkeys(changes[0]) + + backupdir = repo.join('shelve-backups') + + try: + bkfiles = [f for f in newfiles if f in modified] + backups = makebackup(ui, repo, backupdir, bkfiles) + + # patch to shelve + sp = cStringIO.StringIO() + for c in chunks: + if c.filename() in backups: + c.write(sp) + doshelve = sp.tell() + sp.seek(0) + + # patch to apply to shelved files + fp = cStringIO.StringIO() + for c in rc: + if c.filename() in backups: + c.write(fp) + dopatch = fp.tell() + fp.seek(0) + + try: + # 3a. apply filtered patch to clean repo (clean) + if backups: + hg.revert(repo, repo.dirstate.parents()[0], backups.has_key) + + # 3b. apply filtered patch to clean repo (apply) + if dopatch: + ui.debug('applying patch\n') + ui.debug(fp.getvalue()) + patch.internalpatch(fp, ui, 1, repo.root) + del fp + + # 3c. apply filtered patch to clean repo (shelve) + if doshelve: + ui.debug("saving patch to shelve\n") + if opts['append']: + f = repo.opener(shelfpath, "a") + else: + f = repo.opener(shelfpath, "w") + f.write(sp.getvalue()) + del f + del sp + except: + try: + for realname, tmpname in backups.iteritems(): + ui.debug('restoring %r to %r\n' % (tmpname, realname)) + util.copyfile(tmpname, repo.wjoin(realname)) + ui.debug('removing shelve file\n') + os.unlink(repo.join(shelfpath)) + except OSError: + pass + + return 0 + finally: + try: + for realname, tmpname in backups.iteritems(): + ui.debug('removing backup for %r : %r\n' % (realname, tmpname)) + os.unlink(tmpname) + os.rmdir(backupdir) + except OSError: + pass + fancyopts.fancyopts([], commands.commitopts, opts) + + # wrap ui.write so diff output can be labeled/colorized + def wrapwrite(orig, *args, **kw): + label = kw.pop('label', '') + for chunk, l in patch.difflabel(lambda: args): + orig(chunk, label=label + l) + oldwrite = ui.write + extensions.wrapfunction(ui, 'write', wrapwrite) + try: + return cmdutil.commit(ui, repo, shelvefunc, pats, opts) + finally: + ui.write = oldwrite + +def listshelves(ui, repo): + # Check for shelve file at old location first + if os.path.isfile(repo.join('shelve')): + ui.status('default\n') + + # Now go through all the files in the shelves folder and list them out + dirname = repo.join('shelves') + if os.path.isdir(dirname): + for filename in os.listdir(repo.join('shelves')): + ui.status(filename + '\n') + +def unshelve(ui, repo, **opts): + '''restore shelved changes''' + + # Shelf name and path + shelfname = opts.get('name') + shelfpath = getshelfpath(repo, shelfname) + + # List all the active shelves by name and return ' + if opts['list']: + listshelves(ui,repo) + return + + try: + patch_diff = repo.opener(shelfpath).read() + fp = cStringIO.StringIO(patch_diff) + if opts['inspect']: + ui.status(fp.getvalue()) + else: + files = [] + ac = parsepatch(fp) + for chunk in ac: + if isinstance(chunk, header): + files += chunk.files() + backupdir = repo.join('shelve-backups') + backups = makebackup(ui, repo, backupdir, set(files)) + + ui.debug('applying shelved patch\n') + patchdone = 0 + try: + try: + fp.seek(0) + internalpatch(fp, ui, 1, repo.root) + patchdone = 1 + except: + if opts['force']: + patchdone = 1 + else: + ui.status('restoring backup files\n') + for realname, tmpname in backups.iteritems(): + ui.debug('restoring %r to %r\n' % + (tmpname, realname)) + util.copyfile(tmpname, repo.wjoin(realname)) + finally: + try: + ui.debug('removing backup files\n') + shutil.rmtree(backupdir, True) + except OSError: + pass + + if patchdone: + ui.debug("removing shelved patches\n") + os.unlink(repo.join(shelfpath)) + ui.status("unshelve completed\n") + except IOError: + ui.warn('nothing to unshelve\n') + +cmdtable = { + "shelve": + (shelve, + [('A', 'addremove', None, + _('mark new/missing files as added/removed before shelving')), + ('f', 'force', None, + _('overwrite existing shelve data')), + ('a', 'append', None, + _('append to existing shelve data')), + ('', 'all', None, + _('shelve all changes')), + ('n', 'name', '', + _('shelve changes to specified shelf name')), + ('l', 'list', None, _('list active shelves')), + ] + commands.walkopts, + _('hg shelve [OPTION]... [FILE]...')), + "unshelve": + (unshelve, + [('i', 'inspect', None, _('inspect shelved changes only')), + ('f', 'force', None, + _('proceed even if patches do not unshelve cleanly')), + ('n', 'name', '', + _('unshelve changes from specified shelf name')), + ('l', 'list', None, _('list active shelves')), + ], + _('hg unshelve [OPTION]...')), +} diff --git a/.hgext/hgshelve/hgshelve.pyc b/.hgext/hgshelve/hgshelve.pyc new file mode 100644 index 00000000..c09fc67b Binary files /dev/null and b/.hgext/hgshelve/hgshelve.pyc differ diff --git a/.hgext/hgshelve/test-shelve b/.hgext/hgshelve/test-shelve new file mode 100755 index 00000000..0d9f3a57 --- /dev/null +++ b/.hgext/hgshelve/test-shelve @@ -0,0 +1,73 @@ +#!/bin/sh + +HGRCPATH=$HGTMP/.hgrc +export HGRCPATH +echo "[ui]" >> $HGRCPATH +echo "interactive=true" >> $HGRCPATH +echo "[extensions]" >> $HGRCPATH +echo "hgshelve=" >> $HGRCPATH + +hg init a +cd a + +for i in 1 2 3 4; do + echo $i >> file1.txt +done +cp file1.txt file2.txt +hg add file1.txt file2.txt +hg commit -d '1 0' -m 'first file' file1.txt +hg commit -d '2 0' -m 'second file' file2.txt +hg bundle --base -2 tip.bundle +hg add tip.bundle +hg commit -d '3 0' -m 'binary file' tip.bundle + +rm file1.txt +for i in a 1 2 3 4 b; do + echo $i >> file1.txt +done +cp file1.txt file2.txt +hg bundle --base -2 tip.bundle +hg commit -d '4 0' -m 'more changes' + +echo % create changes for shelving +hg revert --rev 2 --all +hg diff --nodates + +echo % do selective shelve +hg shelve<> $HGRCPATH +echo "color=" >> $HGRCPATH + +hg init repo +cd repo +cat > a < a <> $HGRCPATH +echo "interactive=true" >> $HGRCPATH +echo "[extensions]" >> $HGRCPATH +echo "hgshelve=" >> $HGRCPATH +echo "[diff]" >> $HGRCPATH +echo "git=True" >> $HGRCPATH + +echo % shelve +chmod 0755 a +hg shelve --color=always a <x$T4*^jL0KkKStX{nX#fBX|NrgVUcpx-fA>nDw?IGjzt|Zn3=N$Aw|KPWX!r1qxN +z^$Fr58kz=%Mg+nGH3ox5h7d3&k5fjP82~jVplFx@3<;)9G{#6607em$6DC2lXdu%9 +zWWtIPid_|~ry%P%q~tW$!Qw;eFEkK<6vCK+fG?6(b&ooFr>96-LNf@JgmnR>9F0i> +ze{=>YwaT|6O3VY*<7PDEZNDG$>}T!HpVbAF4V<{0^jlDd5^jXR;#7pJv}%>Mf#_26KF$$ +z01Qg)6*km`Z;!B^0@j~Kfn)~2GXWMpfFcFFL^RD?et!VbV9$*?K&7}2Bv2(pTRHo> +qar#atNvM%#;0jJt&??Xnqyaz5Pzsh+06idB-Y(>daG@YeO>ELh`lWmT + +diff --git a/file2.txt b/file2.txt +--- a/file2.txt ++++ b/file2.txt +@@ -1,5 +1,4 @@ +-a + 1 + 2 + 3 + 4 +diff --git a/file1.txt b/file1.txt +--- a/file1.txt ++++ b/file1.txt +@@ -1,4 +1,3 @@ +-a + 1 + 2 + 3 +diff --git a/file2.txt b/file2.txt +--- a/file2.txt ++++ b/file2.txt +@@ -3,4 +3,3 @@ + 2 + 3 + 4 +-b +% unshelve and verify +unshelve completed +% check shelf names +M tip.bundle +file2 +file1 +file2 +file1 +unshelve completed +unshelve completed +M file1.txt +M file2.txt +M tip.bundle +nothing to unshelve diff --git a/.hgrc b/.hgrc index c004d46d..087ae29c 100644 --- a/.hgrc +++ b/.hgrc @@ -8,8 +8,8 @@ editor = vim # Shelve: http://mercurial.selenic.com/wiki/ShelveExtension # Bookmarks: http://mercurial.selenic.com/wiki/BookmarksExtension [extensions] -prompt = ~/Projects/python/hg-prompt/prompt.py -hgshelve = ~/Projects/python/hgshelve/hgshelve.py +prompt = ~/.hgext/hg-prompt/prompt.py +hgshelve = ~/.hgext/hgshelve/hgshelve.py bookmarks = hgext.extdiff =