{"thread":{"id":"27164","subject":"[PATCH 00/23] RFC: Introducing git-test, git-atomic, git-base and git-work","startedAt":"2011-04-23T07:22:29Z","lastAt":"2011-04-27T19:11:11Z","messageCount":29,"participants":["Jon Seymour","Peter Baumann","Drew Northup"],"isPatch":true,"patchVersion":1,"patchTotal":23},"messages":[{"id":"166239","messageId":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":null,"subject":"[PATCH 00/23] RFC: Introducing git-test, git-atomic, git-base and git-work","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:29Z","receivedAt":"2011-04-23T07:22:29Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"This series is posted in order to solicit feedback about some commands I'd like to propose for inclusion in git. \n\nThe commands are:\n   git test\n   git atomic\n   git base\n   git work\n\ngit work\n========\n\ngit work depends on the other 3 commands. \n\ngit work is the command I have been using every day myself for the last 8 months. It is the primary means\nI use to manage my working tree.\n\nThe basic idea of git work is to help manage a private working tree using the following principles:\n\n* all dependencies received from others are merged into the 'base' of the working tree\n\n  git work merge some-dependency\n\n     - merges some-dependency into $(git base) producing (1)\n     - rebases $(git base)..HEAD onto (1) producing (2)\n     - resets the current branch to (2)\n     - updates $(git base) to refer to (1)\n\n* all unpublished work is managed as a linear sequence of commits on top of the 'base' of the working tree\n\n  so, \n   git work --as-refs # shows you a symbolic range for your current work\n   git work # shows you a SHA1 range for your current work\n   gitk $(git work) # shows you your unpublished work\n   git diff $(git work) # shows you the diff of your current work\n\n* prior to publishing work, you rebase it onto an appropriate base commit\n\n  so,\n\n  git branch topic upstream/master # choose the base commit for the topic\n  git work update topic HEAD~3     # pull the top 3 commits off the working tree onto the topic\n\n    - rebases HEAD~3..HEAD onto topic to produce (1)\n    - merges topic into $(git base) to produce (2)\n    - rebases $(git base)..HEAD~3 onto (2) to produce (3)\n    - resets the current branch to (3)\n    - resets $(git base) to (2)\n\nThe nice thing about managing your work tree this way is that your working tree remains\nrelatively stable (it always contains everything you have recently been working on)\nand your topic branches remain clean (i.e. they never contain any other cruft from your \nworking tree).\n\ngit base\n========\n\ngit base is a command that is heavily relied on by git work, and is occasionally used by\nthe user to reset the base of their working tree.\n\ngit base tries to automagically maintain the base of the working tree by maintaining\nan invariant that the path between the base and the tip of the current branch should\nnever traverse a merge. If it ever finds the invariant violated, it calls 'git base reset'\nto attempt to restore the invariant. \n\nFor more information about git base, refer to the Documentation/git-base.txt\n\ngit atomic\n==========\ngit atomic is more an experiment than anything else. The idea is to run another git operation \"atomically\".\nThe git operation either succeeds or it fails. If it fails, git branch attempts to restore the\nworking tree and current branch to the state they were in to their original state prior to the comamnd being run.\n\nThe reason I need something like this is that git work performs several operations in sequence some\nof which I can't guarantee will work. I don't want the user to work out what they have to do\nto recover, so I try to restore to the state they were originally in.\n\nNote: the current implementation doesn't handle rebase failures properly and would probably needed\nto be cleaned up a little before being accepted into the mainline.\n\ngit test\n========\nThis is another experiment. The idea is to provide a uniform way to test for various conditions\ninto the working tree, index and repo. For example:\n\n    git test --not-unstaged --branch-exists foobar\n\nwill fail if there are unstaged files in the working tree or the branch foobar does not exist.\n\nAs I say, git atomic and git test are somewhat experimental. I don't really care about those commands\nand if the consensus is that git doesn't need them, I am happy to rework git base and git work\nto not use them.\n\nHowever, I would like to propose git base and git work as being very useful additions to the git toolset.\n\nLet me know if the consensus is that I should proceed with a submission and I will prepare one.\n\nJon Seymour (23):\n  Introduce git-test.sh and git-test-lib.sh\n  Introduce --unstaged.\n  Introduce --staged\n  Introduce --untracked.\n  Introduce --conflicted\n  Introduce --rebasing\n  Introduce --detached\n  Introduce --branch-exists\n  Introduce --tag-exists\n  Introduce --ref-exists\n  Introduce --commit-exists.\n  Introduce --checked-out\n  Introduce --reachable\n  Introduce --tree-same.\n  Introduce --same\n  misc\n  whitespace fix.\n  tests --conflicted.\n  rebasing: add tests\n  test: git test cleanups.\n  Introduce git-atomic.\n  Introduce git base.\n  Introduce support for the git-work command.\n\n .gitignore                   |    7 +\n Documentation/config.txt     |   10 +\n Documentation/git-atomic.txt |   92 ++++++++\n Documentation/git-base.txt   |  216 ++++++++++++++++++\n Documentation/git-test.txt   |  182 +++++++++++++++\n Documentation/git-work.txt   |  163 ++++++++++++++\n Makefile                     |    7 +\n git-atomic-lib.sh            |   58 +++++\n git-atomic.sh                |    5 +\n git-base.sh                  |  378 +++++++++++++++++++++++++++++++\n git-conditions-lib.sh        |  176 +++++++++++++++\n git-test-lib.sh              |  188 ++++++++++++++++\n git-test.sh                  |   11 +\n git-work.sh                  |  323 +++++++++++++++++++++++++++\n t/t1520-test.sh              |  506 ++++++++++++++++++++++++++++++++++++++++++\n t/t3418-base.sh              |  214 ++++++++++++++++++\n t/t3419-atomic.sh            |   59 +++++\n t/t3421-work.sh              |  174 +++++++++++++++\n 18 files changed, 2769 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-atomic.txt\n create mode 100644 Documentation/git-base.txt\n create mode 100644 Documentation/git-test.txt\n create mode 100644 Documentation/git-work.txt\n create mode 100644 git-atomic-lib.sh\n create mode 100755 git-atomic.sh\n create mode 100644 git-base.sh\n create mode 100644 git-conditions-lib.sh\n create mode 100644 git-test-lib.sh\n create mode 100755 git-test.sh\n create mode 100644 git-work.sh\n create mode 100755 t/t1520-test.sh\n create mode 100755 t/t3418-base.sh\n create mode 100755 t/t3419-atomic.sh\n create mode 100755 t/t3421-work.sh\n\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166240","messageId":"1303543372-77843-2-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 01/23] Introduce git-test.sh and git-test-lib.sh","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:30Z","receivedAt":"2011-04-23T07:22:30Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"This command is intended provide a uniform command line interface\nto a suite of assertions that can be made about the state of\nthe working tree, index and repository.\n\nThis commit introduces the core assert infrastructure. Subsequent\ncommits will introduce check functions that extend the infrastructure\nin a modular way with additional tests.\n\nSigned-off-by: Jon Seymour <jon.seymour@gmail.com>\n\nReplace environment with git configuration.\n\ntest_must_fail: core\n---\n .gitignore                 |    3 +\n Documentation/git-test.txt |  154 ++++++++++++++++++++++++++++++++++++\n Makefile                   |    3 +\n git-conditions-lib.sh      |   12 +++\n git-test-lib.sh            |  187 ++++++++++++++++++++++++++++++++++++++++++++\n git-test.sh                |   11 +++\n t/t1520-test.sh            |  185 +++++++++++++++++++++++++++++++++++++++++++\n 7 files changed, 555 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-test.txt\n create mode 100644 git-conditions-lib.sh\n create mode 100644 git-test-lib.sh\n create mode 100755 git-test.sh\n create mode 100755 t/t1520-test.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 2cf3ca5..aa0eb8fb 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -27,6 +27,7 @@\n /git-clone\n /git-commit\n /git-commit-tree\n+/git-conditions-lib\n /git-config\n /git-count-objects\n /git-cvsexportcommit\n@@ -141,6 +142,8 @@\n /git-symbolic-ref\n /git-tag\n /git-tar-tree\n+/git-test\n+/git-test-lib\n /git-unpack-file\n /git-unpack-objects\n /git-update-index\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nnew file mode 100644\nindex 0000000..1792c4e\n--- /dev/null\n+++ b/Documentation/git-test.txt\n@@ -0,0 +1,154 @@\n+git-test(1)\n+===========\n+\n+NAME\n+----\n+git-test - evaluates one or more conditions about the state of the git working tree, index or repository\n+\n+SYNOPSIS\n+--------\n+The git test API is available in the form of a command and also as a shell library. \n+\n+COMMAND SYNOPSIS\n+----------------\n+[verse]\n+'git test' [-q] [--message message] [--<condition> [ arg ... ]] ...\n+\n+LIBRARY SYNOPSIS\n+----------------\n+[verse]\n+'assert' [-q] [--message message] [--<condition> [ arg ... ]] ...\n+'test_condition' [--<condition> [ arg ... ]] ...\n+'require_condition_libs'\n+\n+\n+DESCRIPTION\n+-----------\n+`git test` provides a uniform, extensible API for evaluating various conditions that\n+pertain to the state of a git working tree, index and repository.\n+\n+Specified conditions are evaluated from left to right. If any condition evaluates to false, \n+the command conditionally prints a diagnostic message to stderr and sets a \n+non-zero status code. Otherwise, sets a status code of zero. \n+\n+The message used to report a assertion failure may be overidden by specifying the --message option.\n+\n+Diagnostic output resulting from an assertion failure may be suppressed with the -q option. \n+Note that the -q option does not suppress diagnostic output that results from the failure to \n+successfully parse the arguments that configure the test API.\n+\n+The `assert` and `test_condition` functions differ according to how they handle failing conditions. \n+\n+The `assert` function behaves like the test command but assertion failures will cause \n+the current shell to exit with a non-zero status code. To prevent this occurring, invoke\n+the `assert` function within a subshell or use the `test_condition` function instead.\n+\n+The `test_condition` function will not exit the current shell in the event that an assertion failure\n+is detected nor will it generate diagnostic relating to an assertion failure to stderr. \n+\n+Note, however, that `test_condition` may still exit the current shell with a non-zero status code \n+if it is unable to successfully parse the arguments presented to it. Callers who need to protect \n+against this possibility either guarantee that the required arguments are available or \n+imbed calls to `test_condition` within a subshell.\n+\n+The `require_condition_libs` function may be used to include any condition libs listed\n+in condition.lib variables of the the git configuration.\n+\n+\n+OPTIONS\n+-------\n+'--message'::\n+\tThe message to be used to report a failure if an assertion fails.\n+'-q'::\t\n+        Suppress assertion messages.\n+\n+CONDITIONS\n+----------\n+\n+EXTENDING THE CONDITION LIBRARY\n+-------------------------------\n+The library of conditions that the assert API can process may be extended by \n+adding functions of the form check_\\{dehyphenated_condition_name\\}_N to the \n+shell environment, where \\{dehyphenated_condition_name\\} is the result of \n+replacing any occurrence of \\'-\\' in the condition name with \\'_\\' and \n+N is the number of arguments that need to be passed to the function.\n+\n+For example, suppose you are writing a script, foo.sh, that includes the git test library \n+and that you want to add a new, 1-argument, condition, foo-X to the library of \n+conditions that can be tested by the git testion framework. \n+\n+---------\n+#/bin/sh\n+. $(git --exec-path)/git-test-lib\n+\n+check_foo_X_1()\n+{\n+    if # some test of $1\n+    then\n+\techo \"foo-X is true\"\n+    else\n+\techo \"foo-X is false\"\n+\tfalse\n+    fi\n+}\n+\n+assert --foo-X \"somearg\"\n+--------\n+If its condition holds, the condition function must set a status\n+code of zero and write a non-empty message describing the condition to stdout.\n+If its condition does not hold, the condition function must set a non-zero\n+status code of zero and write a non-empty message describing the condition\n+that does hold to stdout.\n+\n+It is an error, and is reported as such, if a condition function\n+executes without generating any output on stdout. The resulting\n+state will then be interpreted as condition evaluation failure\n+rather than an assertion failure.\n+\n+To make such conditions available to the git test command line, put the \n+function in a file called ~/foo-lib,sh add a reference \n+to the library to the git configuration, like so:\n+\n+---------\n+git config --add condition.lib ~/foo-lib.sh\n+git test --foo-X one-arg                     # now use foo-X from the git test command line\n+---------\n+\n+CONFIGURATION\n+-------------\n+condition.lib::\n+\tSpecifies a shell library that contains definitions of one or more\n+\tcondition check functions that can be used to extend the range\n+\tof conditions that can be used with the git test API.\n+\n+EXAMPLES\n+--------\n+* Reset the working tree to the specified commit, but only the current HEAD is tree-same with that commit\n+! git assert -q --tree-same HEAD upstream/master || git reset --hard upstream/master\n+* Reset the working tree, but only if there are no staged or unstaged changes\n++\n+-----------\n+git test --not-staged --not-unstaged && git reset --hard another-commit\n+-----------\n+* Import the git-test-lib library into another script\n++\n+-----------\n+. $(git --exec-path)/git-test-lib\n+assert --not-staged   \t      \t                              # die if there are any staged files\n+assert --message \"there are staged files\" --not-staged\t      # die with an alternative message \n+                                                              # if there are any staged files\n+test_condition --not-staged || echo \"there are staged files\"  # check whether there are staged files, \n+                                                              # but do not die if there are\n+-----------\n+\n+Author\n+------\n+Written by Jon Seymour <jon.seymour@gmail.com>\n+\n+Documentation\n+-------------\n+Documentation by Jon Seymour\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 92c1c5e..93ff6c6 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -376,11 +376,14 @@ SCRIPT_SH += git-repack.sh\n SCRIPT_SH += git-request-pull.sh\n SCRIPT_SH += git-stash.sh\n SCRIPT_SH += git-submodule.sh\n+SCRIPT_SH += git-test.sh\n SCRIPT_SH += git-web--browse.sh\n \n+SCRIPT_LIB += git-conditions-lib\n SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-parse-remote\n SCRIPT_LIB += git-sh-setup\n+SCRIPT_LIB += git-test-lib\n \n SCRIPT_PERL += git-add--interactive.perl\n SCRIPT_PERL += git-difftool.perl\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nnew file mode 100644\nindex 0000000..f462e22\n--- /dev/null\n+++ b/git-conditions-lib.sh\n@@ -0,0 +1,12 @@\n+#!/bin/sh\n+#\n+# (c) Copyright Jon Seymour 2010\n+#\n+if test -z \"${__GIT_CONDITIONS_LIB_INCLUDED}\"\n+then\n+__GIT_CONDITIONS_LIB_INCLUDED=t\n+#\n+# Provides a function that enables the evaluation of assertions.\n+#\n+\n+fi\ndiff --git a/git-test-lib.sh b/git-test-lib.sh\nnew file mode 100644\nindex 0000000..159cea6\n--- /dev/null\n+++ b/git-test-lib.sh\n@@ -0,0 +1,187 @@\n+#!/bin/sh\n+#\n+# (c) Copyright Jon Seymour 2010\n+#\n+if test -z \"${__GIT_ASSERT_LIB_INCLUDED}\"\n+then\n+__GIT_ASSERT_LIB_INCLUDED=t\n+\n+. $(git --exec-path)/git-conditions-lib\n+\n+is_a_function()\n+{\n+    test \"$(type $1 2>/dev/null | sed -n \"s/is a .*function/is a function/p\")\" == \"$1 is a function\"\n+}\n+\n+if ! is_a_function die\n+then\n+die() {\n+\techo >&2 \"$@\"\n+\texit 1\n+}\n+fi\n+\n+require_lib()\n+{\n+\tif test -f \"$1\"\n+\tthen\n+\t\t. \"$1\" || die \"failed to read: '$1'\"\n+\telse\n+\t\techo \"warning: condition library '$1' does not exist\" 1>&2\n+\tfi\n+}\n+\n+require_condition_libs() {\n+\teval $(\n+\t\tgit config --get-all condition.lib | while read lib\n+\t\tdo \n+\t\t\techo \"require_lib \\\"$lib\\\" \\;\"\n+\t\tdone\t\n+\t)\n+}\n+\n+assertion_failed() {\n+\trc=$1\n+\tshift \n+\tmessage=\"${MESSAGE:-$*}\"\n+\tif ! ${QUIET:-false}\n+\tthen\n+\t\techo \"${message}\" 1>&2\n+\tfi\n+\n+\tif test -z \"${EXIT_ON_FAILURE}\"\n+\tthen\n+\t\treturn $rc\n+\telse\n+\t\texit $rc;\n+\tfi\n+}\n+\n+evaluation_failed() {\n+\trc=$1\n+\tshift\n+\techo \"fatal: condition evaluation failed for $*\" 1>&2\n+\texit $rc\n+}\n+\n+not() {\n+\t! \"$@\"\n+}\n+\n+#\n+# reviewers: is there a more concise way to express this in POSIX?\n+#\n+replace() {\n+\tword=$1\n+\tfrom=$2\n+\tto=$3\n+\t\n+\tprefix=${word%${from}*}\n+\tsuffix=${word#${prefix}${from}}\n+\n+\tif test \"$prefix\" = \"$word\"\n+\tthen\n+\t\techo $word\n+\telse\n+\t\techo \"$(replace ${prefix} '-' '_')${to}${suffix}\"\n+\tfi\n+}\n+\n+impl() {\n+\tMESSAGE=\n+\tQUIET=false\n+\tQUEUE=\"\"\n+\texprs=\"\"\n+\twhile test $# -gt 0\n+\tdo\n+\t\tword=$1\n+\t\tshift\n+\t\tcase $word in\n+\t\t\t--message)\n+\t\t\tMESSAGE=$1\n+\t\t\tshift\n+\t\t\tcontinue\n+\t\t\t;;\n+\t\t\t--include)\n+\t\t\t\ttest $# -gt 0 || die \"can't shift 1 argument for --include option\"\n+\t\t\t\ttest -f \"$1\" || die \"'$1' must be a file\"\n+\t\t\t\trequire_lib \"$1\"\n+\t\t\t\tshift \n+\t\t\t\tcontinue\n+\t\t\t;;\n+\t\t\t-q)\n+\t\t\tQUIET=true\n+\t\t\tcontinue;\n+\t\t\t;;\n+\t\t\t--not-*)\n+\t\t\tnegation='not'\n+\t\t\tcondition=${word#--not-}\n+\t\t\t;;\n+\t\t\t--*)\n+\t\t\tnegation=''\n+\t\t\tcondition=${word#--}\n+\t\t\t;;\n+\t\t\t*)\n+\t\t\tdie \"argument not recognised: $word\"\n+\t\t\t;;\n+\t\tesac\n+\n+\t\tdehyphenated=$(replace \"$condition\" \"-\" \"_\")\n+\n+\t\texpr=\n+\t\targs=\n+\t\ttry=0\n+\n+\t\twhile ! is_a_function check_${dehyphenated}_$try\n+\t\tdo\t\n+\t\t\ttest -n \"$1\" || die \"condition $condition is not supported or insufficient arguments were supplied\"\n+\t\t\ttest \"${1#--}\" = \"$1\" || die \"condition $condition is not supported or insufficient arguments were supplied\"\n+\t\t\targs=\"${args}${args:+ }$(git rev-parse --sq-quote $1)\"\n+\t\t\tshift\n+\t\t\ttry=$((try+1))\n+\t\tdone\n+\n+\t\texprs=\"${exprs}${exprs:+ }${negation}${negation:+ }$word $try check_${dehyphenated}_$try $args\"\n+\n+\tdone \n+\n+\tset -- $exprs\n+\twhile test $# -gt 0\n+\tdo\n+\t\tif test \"$1\" = \"not\"\n+\t\tthen\n+\t\t\tnegation=not\n+\t\t\tshift\n+\t\telse\n+\t\t\tnegation=\n+\t\tfi\n+\t\tword=$1\n+\t\tnargs=$2\n+\t\tshift 2\n+\t\tmessage=$(eval $negation \"$@\") \n+\t\trc=$?\n+\t\tif test $rc -ne 0\n+\t\tthen\n+\t\t\tif test -n \"$message\"\n+\t\t\tthen\n+\t\t\t\tassertion_failed $rc \"$message\"\n+\t\t\telse\n+\t\t\t\tevaluation_failed $rc \"$word $2 ...\"\n+\t\t\tfi\n+\t\t\treturn $rc\n+\t\tfi\n+\t\tshift $((nargs+1))\n+\tdone\n+}\n+\n+assert() {\n+\tEXIT_ON_FAILURE=t\n+\timpl \"$@\"\n+}\n+\n+test_condition() {\n+\tEXIT_ON_FAILURE=\n+\timpl \"$@\" 2>/dev/null\n+}\n+\n+fi\ndiff --git a/git-test.sh b/git-test.sh\nnew file mode 100755\nindex 0000000..4070ae5\n--- /dev/null\n+++ b/git-test.sh\n@@ -0,0 +1,11 @@\n+#!/bin/sh\n+#\n+# (c) Copyright Jon Seymour 2010\n+#\n+SUBDIRECTORY_OK=true\n+. git-sh-setup\n+. git-test-lib\n+\n+require_condition_libs\n+\n+assert \"$@\"\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nnew file mode 100755\nindex 0000000..9e3abe6\n--- /dev/null\n+++ b/t/t1520-test.sh\n@@ -0,0 +1,185 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Jon Seymour\n+#\n+\n+test_description='git-test tests\n+\n+Checks that the git-test conditions are correctly evaluated.\n+'\n+\n+. ./test-lib.sh\n+. $(git --exec-path)/git-test-lib.sh\n+\n+cat >test-assertions-lib.sh <<EOF\n+check_always_fails_0_0()\n+{\n+\techo \"always-fails-0\"\n+\tfalse\n+}\n+\n+check_always_fails_1_1()\n+{\n+\techo \"always-fails-1 \\$1\"\n+\tfalse\n+}\n+\n+check_never_fails_0_0()\n+{\n+\techo \"never-fails-0\"\n+\ttrue\n+}\n+\n+check_never_fails_1_1()\n+{\n+\techo \"never-fails-1 \\$1\"\n+\ttrue\n+}\n+EOF\n+cat >empty-assertions-lib.sh <<EOF\n+EOF\n+\n+git config condition.lib \"$(pwd)/test-assertions-lib.sh\"\n+git config --add condition.lib \"$(pwd)/empty-assertions-lib.sh\"\n+\n+test_expect_assertion_failure()\n+{\n+\ttest=$1\n+\tmessage=$2\n+\tshift \n+\ttest_expect_success $1 \\\n+\"\n+\t! actual_message=$(git test \"$@\" 1>&2) &&\n+\ttest \"$message\" = \"$actual_message\"\n+\"\n+}\n+\n+#        G\n+#       /\n+# base-A---M--C--D--D1--E\n+#     \\   / \\\n+#      -B-   -F\n+\n+test_expect_success 'setup' \\\n+'\n+\tgit add test-assertions-lib.sh empty-assertions-lib.sh && \n+\ttest_commit base &&\n+\ttest_commit A &&\n+\tgit checkout A^1 &&\n+\ttest_commit B && \n+\tgit checkout master &&\n+\ttest_merge M B &&\n+\techo C >> B.t &&\n+\tgit tag STASH_UNSTAGED $(git stash create) &&\n+\tgit add B.t &&\n+\tgit tag STASH_STAGED $(git stash create) &&\n+\ttest_commit C &&\n+\ttest_commit D &&\n+\tgit commit -m \"allow-empty\" --allow-empty &&\n+\tgit tag D1 &&\n+\ttest_commit E &&\n+\tgit checkout M^0 -- &&\n+        echo F >> B.t &&\n+        git add B.t &&\n+\ttest_commit F &&\n+\tgit checkout A^0 -- &&\n+\ttest_commit G &&\n+\tgit checkout master &&\n+\tgit reset --hard D     \n+'\n+\n+test_expect_success 'git test # no arguments' \\\n+'\n+\tgit test &&\n+\ttest -z \"$(git test)\"\n+'\n+\n+test_expect_success 'git test -q # -q only' \\\n+'\n+\tgit test -q &&\n+\ttest -z \"$(git test)\"\n+'\n+\n+test_expect_success 'git test --message msg # with a message' \\\n+'\n+\tgit test --message msg &&\n+\ttest -z \"$(git test)\"\n+'\n+\n+test_expect_success 'git test --message \"\" # with an empty message' \\\n+'\n+\tgit test --message \"\" &&\n+\ttest -z \"$(git test)\"\n+'\n+\n+test_expect_success 'git test --message # should fail' \\\n+'\n+\ttest_must_fail git test --message\n+'\n+\n+test_expect_success 'git test --invalid-condition # should fail' \\\n+'\n+\ttest_must_fail git test --invalid-condition\n+'\n+\n+test_expect_success 'git test --not-invalid-condition # should fail' \\\n+'\n+\ttest_must_fail git test --not-invalid-condition\n+'\n+\n+test_expect_success 'git test --invalid-condition --never-fails-0 # should fail' \\\n+'\n+\ttest_must_fail git test --invalid-condition --never-fails-0\n+'\n+\n+test_expect_success 'git test --invalid-condition one-arg --never-fails-0 #should fail' \\\n+'\n+\ttest_must_fail git test --invalid-condition one-arg --never-fails-0\n+'\n+\n+test_expect_success 'git test --never-fails-0' \\\n+'\n+\tgit test --never-fails-0\n+'\n+\n+test_expect_success 'git test --never-fails-1 # missing argument - should fail' \\\n+'\n+\ttest_must_fail git test --never-fails-1\n+'\n+\n+test_expect_success 'git test --never-fails-1 one-arg' \\\n+'\n+\tgit test --never-fails-1 one-arg\n+'\n+\n+test_expect_success 'git test --not-never-fails-0 # should fail' \\\n+'\n+\ttest_must_fail git test --not-never-fails-0\n+'\n+\n+test_expect_success 'git test --always-fails-0 # should fail' \\\n+'\n+\ttest_must_fail git test --always-fails-0\n+'\n+\n+test_expect_success 'git test --always-fails-1 # should fail' \\\n+'\n+\ttest_must_fail git test --always-fails-1 one-arg\n+'\n+\n+test_expect_success 'git test --not-always-fails-1 one-arg' \\\n+'\n+\tgit test --not-always-fails-1 one-arg\n+'\n+\n+test_expect_success 'git test --not-always-fails-1 # should fail' \\\n+'\n+\ttest_must_fail git test --not-always-fails-1\n+'\n+\n+test_expect_success 'git test --not-always-fails-0' \\\n+'\n+\tgit test --not-always-fails-0\n+'\n+'\n+test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166241","messageId":"1303543372-77843-3-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 02/23] Introduce --unstaged.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:31Z","receivedAt":"2011-04-23T07:22:31Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"test_must_fail: unstaged\n---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   11 +++++++++++\n t/t1520-test.sh            |   35 +++++++++++++++++++++++++++++++++++\n 3 files changed, 48 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 1792c4e..4b2c129 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -64,6 +64,8 @@ OPTIONS\n \n CONDITIONS\n ----------\n+'--unstaged'|'--not-unstaged'::\n+\tTests if there are (not) any unstaged changes in the working tree.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex f462e22..f9ff0b9 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -8,5 +8,16 @@ __GIT_CONDITIONS_LIB_INCLUDED=t\n #\n # Provides a function that enables the evaluation of assertions.\n #\n+check_unstaged_0()\n+{\n+\tif test $(git diff-files --name-only | wc -l) -ne 0\n+\tthen\n+\t\techo \"There are unstaged files.\"\n+\telse\n+\t\techo \"There are no unstaged files.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex 9e3abe6..8543943 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -181,5 +181,40 @@ test_expect_success 'git test --not-always-fails-0' \\\n '\n \tgit test --not-always-fails-0\n '\n+\n+test_expect_success 'git test --unstaged # should fail' \\\n+'\n+\ttest_must_fail git test --unstaged\n+'\n+\n+test_expect_success 'git test --not-unstaged' \\\n+'\n+\tgit test --not-unstaged\n+'\n+\n+test_expect_success 'git test --unstaged # when there are unstaged files' \\\n+'\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\tgit checkout -f M^0 &&\n+\tgit stash apply --index STASH_UNSTAGED &&\n+\tgit test --unstaged\n+'\n+\n+test_expect_success 'git test --not-unstaged # when there are unstaged files - should fail' \\\n+'\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\tgit checkout -f M^0 &&\n+\tgit stash apply --index STASH_UNSTAGED &&\n+\ttest_must_fail git test --not-unstaged\n+'\n+\n+test_expect_success 'git test --unstaged # when there are only staged files' \\\n+'\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\tgit checkout -f M^0 &&\n+\tgit stash apply --index STASH_STAGED &&\n+\tgit test --not-unstaged\n+'\n+\n '\n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166242","messageId":"1303543372-77843-4-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 03/23] Introduce --staged","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:32Z","receivedAt":"2011-04-23T07:22:32Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"test_must_fail: --not-staged\n---\n Documentation/git-test.txt |    3 ++\n git-conditions-lib.sh      |   11 ++++++++++\n t/t1520-test.sh            |   45 ++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 59 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 4b2c129..9264c90 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -66,6 +66,9 @@ CONDITIONS\n ----------\n '--unstaged'|'--not-unstaged'::\n \tTests if there are (not) any unstaged changes in the working tree.\n+'--staged'|'--not-staged'::\n+        Tests if there are (not) any staged changes in the index.\n+\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex f9ff0b9..55b4131 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -19,5 +19,16 @@ check_unstaged_0()\n \tfi\n }\n \n+check_staged_0()\n+{\n+\tif test $(git diff-index --cached --name-only HEAD | wc -l) -ne 0\n+\tthen\n+\t\techo \"There are staged files.\"\n+\telse\n+\t\techo \"There are no staged files.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex 8543943..7438f93 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -216,5 +216,50 @@ test_expect_success 'git test --unstaged # when there are only staged files' \\\n \tgit test --not-unstaged\n '\n \n+test_expect_success 'git test --staged # should fail' \\\n '\n+\ttest_must_fail git test --staged\n+'\n+\n+test_expect_success 'git test --not-staged' \\\n+'\n+\tgit test --not-staged\n+'\n+\n+test_expect_success 'git test --staged # when there are staged files' \\\n+'\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\tgit checkout -f M^0 &&\n+\tgit stash apply --index STASH_STAGED &&\n+\tgit test --staged\n+'\n+\n+test_expect_success 'git test --not-staged # when there are staged files - should fail' \\\n+'\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\tgit checkout -f M^0 &&\n+\tgit stash apply --index STASH_STAGED &&\n+\ttest_must_fail git test --not-staged\n+'\n+\n+test_expect_success 'git test --staged # when there are only unstaged files' \\\n+'\n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\tgit checkout -f M^0 &&\n+\tgit stash apply --index STASH_UNSTAGED &&\n+\tgit test --not-staged\n+'\n+\n+'\n+    test_when_finished \"git reset --hard HEAD && git checkout master\" && \n+    git test --not-staged --not-unstaged && \n+    ! git test --staged && \n+    ! git test --unstaged && \n+    git checkout M^0 &&\n+    git stash apply --index STASH_STAGED &&\n+    git test --not-unstaged --staged &&\n+    ! git test --unstaged &&\n+    ! git test --not-staged \n+'\n+\n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166243","messageId":"1303543372-77843-5-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 04/23] Introduce --untracked.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:33Z","receivedAt":"2011-04-23T07:22:33Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"Add tests for --untracked, --not-untracked.\n---\n Documentation/git-test.txt |    3 ++-\n git-conditions-lib.sh      |   11 +++++++++++\n t/t1520-test.sh            |   24 ++++++++++++++++++++++++\n 3 files changed, 37 insertions(+), 1 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 9264c90..9f6a4c3 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -68,7 +68,8 @@ CONDITIONS\n \tTests if there are (not) any unstaged changes in the working tree.\n '--staged'|'--not-staged'::\n         Tests if there are (not) any staged changes in the index.\n-\n+'--untracked'|'--not-untracked'::\n+\tTests if there are (not) any untracked files in the working tree.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 55b4131..7198e6a 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -30,5 +30,16 @@ check_staged_0()\n \tfi\n }\n \n+check_untracked_0()\n+{\n+\tif test $(git ls-files -o --exclude-standard | wc -l) -ne 0\n+\tthen\n+\t\techo \"There are untracked files.\"\n+\telse\n+\t\techo \"There are no untracked files.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex 7438f93..ebff63c 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -250,6 +250,30 @@ test_expect_success 'git test --staged # when there are only unstaged files' \\\n \tgit test --not-staged\n '\n \n+test_expect_success 'git test --untracked # should fail' \\\n+'\n+\ttest_must_fail git test --untracked\n+'\n+\n+test_expect_success 'git test --not-untracked' \\\n+'\n+\tgit test --not-untracked\n+'\n+\n+test_expect_success 'git test --untracked # when there are untracked files' \\\n+'\n+\ttest_when_finished \"git clean -fd\" && \n+\t:> untracked &&\n+\tgit test --untracked\n+'\n+\n+test_expect_success 'git test --not-untracked # when there are untracked files - should fail' \\\n+'\n+\ttest_when_finished \"git clean -fd\" && \n+\t:> untracked &&\n+\ttest_must_fail git test --not-untracked\n+'\n+\n '\n     test_when_finished \"git reset --hard HEAD && git checkout master\" && \n     git test --not-staged --not-unstaged && \n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166245","messageId":"1303543372-77843-6-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 05/23] Introduce --conflicted","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:34Z","receivedAt":"2011-04-23T07:22:34Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   12 ++++++++++++\n 2 files changed, 14 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 9f6a4c3..4f86265 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -70,6 +70,8 @@ CONDITIONS\n         Tests if there are (not) any staged changes in the index.\n '--untracked'|'--not-untracked'::\n \tTests if there are (not) any untracked files in the working tree.\n+'--conflicted'|'--not-conflicted'::\n+\tTests if there are (not) any merge conflicts in the index.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 7198e6a..7f3724e 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -41,5 +41,17 @@ check_untracked_0()\n \tfi\n }\n \n+check_conflicted_0()\n+{\n+\tif test $(git ls-files --unmerged | wc -l) -ne 0\n+\tthen\n+\t\techo \"There are unmerged files.\"\n+\telse\n+\t\techo \"There are no unmerged files.\"\n+\t\tfalse\n+\tfi\n+\n+}\n+\n \n fi\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166244","messageId":"1303543372-77843-7-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 06/23] Introduce --rebasing","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:35Z","receivedAt":"2011-04-23T07:22:35Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   11 +++++++++++\n 2 files changed, 13 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 4f86265..e872608 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -72,6 +72,8 @@ CONDITIONS\n \tTests if there are (not) any untracked files in the working tree.\n '--conflicted'|'--not-conflicted'::\n \tTests if there are (not) any merge conflicts in the index.\n+'--rebasing'|'--not-rebasing'::\n+        Tests if a rebase is (not) in progress.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 7f3724e..0d6e0a1 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -53,5 +53,16 @@ check_conflicted_0()\n \n }\n \n+check_rebasing_0()\n+{\n+\tif test -d \"$(git rev-parse --git-dir)/rebase-apply\"\n+\tthen\n+\t\techo \"A rebase is in progress.\"\n+\telse\n+\t\techo \"There is no rebase in progress.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166248","messageId":"1303543372-77843-8-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 07/23] Introduce --detached","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:36Z","receivedAt":"2011-04-23T07:22:36Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"detached\n---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   11 +++++++++++\n t/t1520-test.sh            |   30 +++++++++++++++++++++---------\n 3 files changed, 34 insertions(+), 9 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex e872608..501725c 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -74,6 +74,8 @@ CONDITIONS\n \tTests if there are (not) any merge conflicts in the index.\n '--rebasing'|'--not-rebasing'::\n         Tests if a rebase is (not) in progress.\n+'--detached'|'--not-detached'::\n+\tTests if the head is (not) detached.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 0d6e0a1..34de896 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -64,5 +64,16 @@ check_rebasing_0()\n \tfi\n }\n \n+check_detached_0()\n+{\n+\tif ! git symbolic-ref -q HEAD >/dev/null\n+\tthen\n+\t\techo \"HEAD is detached.\"\n+\telse\n+\t\techo \"HEAD is not detached.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex ebff63c..bb39050 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -274,16 +274,28 @@ test_expect_success 'git test --not-untracked # when there are untracked files -\n \ttest_must_fail git test --not-untracked\n '\n \n+test_expect_success 'git test --not-detached' \\\n '\n-    test_when_finished \"git reset --hard HEAD && git checkout master\" && \n-    git test --not-staged --not-unstaged && \n-    ! git test --staged && \n-    ! git test --unstaged && \n-    git checkout M^0 &&\n-    git stash apply --index STASH_STAGED &&\n-    git test --not-unstaged --staged &&\n-    ! git test --unstaged &&\n-    ! git test --not-staged \n+\tgit test --not-detached\n+'\n+\n+test_expect_success 'git test --detached # should fail' \\\n+'\n+\ttest_must_fail git test --detached\n+'\n+\n+test_expect_success 'git test --not-detached # when detached, should fail' \\\n+'\n+\ttest_when_finished \"git checkout -f master\" && \n+\tgit checkout HEAD^0 &&\n+\ttest_must_fail git test --not-detached\n+'\n+\n+test_expect_success 'git test --detached # when detached' \\\n+'\n+\ttest_when_finished \"git checkout -f master\" && \n+\tgit checkout HEAD^0 &&\n+\tgit test --detached\n '\n \n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166246","messageId":"1303543372-77843-9-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 08/23] Introduce --branch-exists","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:37Z","receivedAt":"2011-04-23T07:22:37Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"tweak branch\n\ntweak branch\n---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   12 ++++++++++++\n 2 files changed, 14 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 501725c..ea54d20 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -76,6 +76,8 @@ CONDITIONS\n         Tests if a rebase is (not) in progress.\n '--detached'|'--not-detached'::\n \tTests if the head is (not) detached.\n+'--branch-exists'|'--not-branch-exists branch'::\n+        Tests if the specified branch does (not) exist.\t\t\t\t    \n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 34de896..5d7a7f8 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -75,5 +75,17 @@ check_detached_0()\n \tfi\n }\n \n+check_branch_exists_1()\n+{\n+\tsymbolic=$(git rev-parse --quiet --symbolic-full-name --verify \"$1\")\n+ \tif test \"${symbolic#refs/heads/}\" != \"${symbolic}\"\n+\tthen\n+\t\techo \"Branch '$1' exists.\"\n+\telse\n+\t\techo \"Branch '$1' does not exist.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166249","messageId":"1303543372-77843-10-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 09/23] Introduce --tag-exists","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:38Z","receivedAt":"2011-04-23T07:22:38Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"tweak tag\n\ntweak tag\n---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   12 ++++++++++++\n 2 files changed, 14 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex ea54d20..69c8710 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -78,6 +78,8 @@ CONDITIONS\n \tTests if the head is (not) detached.\n '--branch-exists'|'--not-branch-exists branch'::\n         Tests if the specified branch does (not) exist.\t\t\t\t    \n+'--tag-exists'|'--not-tag-exists tag'::\n+        Tests if the specified tag does (not) exist.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 5d7a7f8..93a916a 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -87,5 +87,17 @@ check_branch_exists_1()\n \tfi\n }\n \n+check_tag_exists_1()\n+{\n+\tsymbolic=$(git rev-parse --quiet --symbolic-full-name --verify \"$1\")\n+ \tif test \"${symbolic#refs/tags/}\" != \"${symbolic}\"\n+\tthen\n+\t\techo \"Tag '$1' exists.\"\n+\telse\n+\t\techo \"Tag '$1' does not exist.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166247","messageId":"1303543372-77843-11-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 10/23] Introduce --ref-exists","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:39Z","receivedAt":"2011-04-23T07:22:39Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"tweak ref\n---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   12 ++++++++++++\n 2 files changed, 14 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 69c8710..259a04c 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -80,6 +80,8 @@ CONDITIONS\n         Tests if the specified branch does (not) exist.\t\t\t\t    \n '--tag-exists'|'--not-tag-exists tag'::\n         Tests if the specified tag does (not) exist.\n+'--ref-exists'|'--not-ref-exists tag'::\n+        Tests if the specified reference does (not) exist.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 93a916a..c21d669 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -99,5 +99,17 @@ check_tag_exists_1()\n \tfi\n }\n \n+check_ref_exists_1()\n+{\n+\tsymbolic=$(git rev-parse --quiet --symbolic-full-name --verify \"$1\")\n+ \tif test \"${symbolic#refs/}\" != \"${symbolic}\"\n+\tthen\n+\t\techo \"Reference '$1' exists.\"\n+\telse\n+\t\techo \"Reference '$1' does not exist.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166250","messageId":"1303543372-77843-12-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 11/23] Introduce --commit-exists.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:40Z","receivedAt":"2011-04-23T07:22:40Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"tweak commit\n---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   11 +++++++++++\n 2 files changed, 13 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 259a04c..9a4e06f 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -82,6 +82,8 @@ CONDITIONS\n         Tests if the specified tag does (not) exist.\n '--ref-exists'|'--not-ref-exists tag'::\n         Tests if the specified reference does (not) exist.\n+'--commit-exists'|'--not-commit-exists commit'::\n+\tTests if the specified commit does (not) exist.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex c21d669..af376d1 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -111,5 +111,16 @@ check_ref_exists_1()\n \tfi\n }\n \n+check_commit_exists_1()\n+{\n+\tif test \"$(git cat-file -t \"$1\")\" = 'commit'\n+\tthen\t\n+\t\techo \"Commit '$1' exists.\"\n+\telse\n+\t\techo \"Commit '$1' does not exist.\"\n+\t\tfalse\n+\tfi\n+}\n+\n \n fi\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166262","messageId":"1303543372-77843-13-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 12/23] Introduce --checked-out","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:41Z","receivedAt":"2011-04-23T07:22:41Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   14 ++++++++++++++\n 2 files changed, 16 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 9a4e06f..f76aa38 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -84,6 +84,8 @@ CONDITIONS\n         Tests if the specified reference does (not) exist.\n '--commit-exists'|'--not-commit-exists commit'::\n \tTests if the specified commit does (not) exist.\n+'--checked-out'|'--not-checked-out branch'::\n+        Tests if the specified branch is (not) checked out.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex af376d1..3eccc19 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -122,5 +122,19 @@ check_commit_exists_1()\n \tfi\n }\n \n+check_checked_out_1()\n+{\n+\tbranch=\"$(git rev-parse --quiet --symbolic-full-name --verify \"$1\")\"\n+\theadref=\"$(git symbolic-ref HEAD)\"\n+\tif test \"${headref}\" = \"${branch}\" -a -n \"${branch}\"\n+\tthen\n+\t\techo \"'$1' is checked out.\"\n+\telse\n+\t\techo \"'$1' is not checked out.\"\n+\t\tfalse\n+\tfi\n+\n+}\n+\n \n fi\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166251","messageId":"1303543372-77843-14-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 13/23] Introduce --reachable","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:42Z","receivedAt":"2011-04-23T07:22:42Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"reachable\n---\n Documentation/git-test.txt |    2 +\n git-conditions-lib.sh      |   14 ++++++\n t/t1520-test.sh            |  101 ++++++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 117 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex f76aa38..e28ed88 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -86,6 +86,8 @@ CONDITIONS\n \tTests if the specified commit does (not) exist.\n '--checked-out'|'--not-checked-out branch'::\n         Tests if the specified branch is (not) checked out.\n+'--reachable'|'--not-reachable' first second::\n+\tTests if the first commit is (not) reachable from the second.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 3eccc19..0223fcd 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -136,5 +136,19 @@ check_checked_out_1()\n \n }\n \n+check_reachable_2()\n+{\n+\tfirst=$(git rev-parse --quiet --verify \"$1\" 2>/dev/null) || die \"'$1' is not a commit\"\n+\tsecond=$(git rev-parse --quiet --verify \"$2\" 2>/dev/null) || die \"'$2' is not a commit\"\n+\tif test \"$first\" = \"$second\" \\\n+                -o -z \"$(git rev-list -n1 \"$first\" ^\"$second\")\" \n+\tthen\n+\t\techo \"'$1' is reachable from '$2'.\"\n+\telse\n+\t\techo \"'$1' is not reachable from '$2'.\"\n+\t\tfalse\n+\tfi     \n+}\n+\n \n fi\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex bb39050..3e33f8a 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -298,4 +298,105 @@ test_expect_success 'git test --detached # when detached' \\\n \tgit test --detached\n '\n \n+\n+test_expect_success 'git test --reachable #should fail' \\\n+'\n+\ttest_must_fail git test --reachable\n+'\n+\n+test_expect_success 'git test --not-reachable #should fail' \\\n+'\n+\ttest_must_fail git test --reachable\n+'\n+\n+test_expect_success 'git test --reachable A #should fail' \\\n+'\n+\ttest_must_fail git test --reachable A\n+'\n+\n+test_expect_success 'git test --not-reachable A #should fail' \\\n+'\n+\ttest_must_fail git test --not-reachable A\n+'\n+\n+test_expect_success 'git test --reachable does-not-exist-1 does-not-exist-2 #should fail' \\\n+'\n+\ttest_must_fail git test --reachable does-not-exist-1 does-not-exist-2\n+'\n+\n+test_expect_success 'git test --not-reachable does-not-exist-1 does-not-exist-2 #should fail' \\\n+'\n+\ttest_must_fail git test --not-reachable does-not-exist-1 does-not-exist-2\n+'\n+\n+test_expect_success 'git test --reachable does-not-exist-1 A #should fail' \\\n+'\n+\ttest_must_fail git test --reachable does-not-exist-1 A\n+'\n+\n+test_expect_success 'git test --not-reachable does-not-exist-1 A #should fail' \\\n+'\n+\ttest_must_fail git test --not-reachable does-not-exist-1 A\n+'\n+\n+test_expect_success 'git test --reachable A does-not-exist-2 #should fail' \\\n+'\n+\ttest_must_fail git test --reachable A does-not-exist-2\n+'\n+\n+test_expect_success 'git test --not-reachable A does-not-exist-2 #should fail' \\\n+'\n+\ttest_must_fail git test --not-reachable A does-not-exist-2\n+'\n+\n+test_expect_success 'git test --reachable A C' \\\n+'\n+       git test --reachable A C\n+'\n+\n+test_expect_success 'git test --not-reachable A C # should fail' \\\n+'\n+       test_must_fail git test --not-reachable A C\n+'\n+\n+test_expect_success 'git test --reachable C A # should fail' \\\n+'\n+       test_must_fail git test --reachable C A\n+'\n+\n+test_expect_success 'git test --not-reachable C A # should fail' \\\n+'\n+       git test --not-reachable C A\n+'\n+\n+test_expect_success 'git test --reachable C C' \\\n+'\n+       git test --reachable C C\n+'\n+\n+test_expect_success 'git test --not-reachable C C' \\\n+'\n+       test_must_fail git test --not-reachable C C\n+'\n+\n+test_expect_success 'git test --reachable C F' \\\n+'\n+       test_must_fail git test --reachable C F\n+'\n+\n+test_expect_success 'git test --not-reachable C F' \\\n+'\n+       git test --not-reachable C F\n+'\n+\n+test_expect_success 'git test --reachable F C' \\\n+'\n+       test_must_fail git test --reachable F C\n+'\n+\n+test_expect_success 'git test --not-reachable F C' \\\n+'\n+       git test --not-reachable F C\n+'\n+\n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166256","messageId":"1303543372-77843-15-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 14/23] Introduce --tree-same.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:43Z","receivedAt":"2011-04-23T07:22:43Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   12 ++++++++++++\n t/t1520-test.sh            |    9 +++++++++\n 3 files changed, 23 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex e28ed88..6709fdd 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -88,6 +88,8 @@ CONDITIONS\n         Tests if the specified branch is (not) checked out.\n '--reachable'|'--not-reachable' first second::\n \tTests if the first commit is (not) reachable from the second.\n+'--tree-same'|'--not-tree-same' first second::\n+        Tests if the first commit is (not) tree-same to the second commit.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 0223fcd..2b7a5d1 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -150,5 +150,17 @@ check_reachable_2()\n \tfi     \n }\n \n+check_tree_same_2()\n+{\n+\tif git diff-tree --quiet \"$1\" \"$2\"\n+\tthen\n+\t\techo \"'$1' has the same tree as '$2'.\"\n+\telse\n+\t\techo \"'$1' does not have the same tree as '$2'.\"\n+\t\tfalse\n+\tfi\n+\n+}\n+\n \n fi\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex 3e33f8a..1fd5122 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -399,4 +399,13 @@ test_expect_success 'git test --not-reachable F C' \\\n        git test --not-reachable F C\n '\n \n+test_expect_success 'tree-same' \\\n+'\n+   git test \\\n+     --tree-same master HEAD \\\n+     --tree-same D D1 \\\n+     --not-tree-same C D\n+\n+'\n+\n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166252","messageId":"1303543372-77843-16-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 15/23] Introduce --same","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:44Z","receivedAt":"2011-04-23T07:22:44Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n Documentation/git-test.txt |    2 ++\n git-conditions-lib.sh      |   10 ++++++++++\n t/t1520-test.sh            |    8 ++++++++\n 3 files changed, 20 insertions(+), 0 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex 6709fdd..b7c3161 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -90,6 +90,8 @@ CONDITIONS\n \tTests if the first commit is (not) reachable from the second.\n '--tree-same'|'--not-tree-same' first second::\n         Tests if the first commit is (not) tree-same to the second commit.\n+'--same'|'--not-same' first second::\n+\tTests if the first object has (does not have) the same SHA1 has as the second object.\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex 2b7a5d1..ec9b516 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -162,5 +162,15 @@ check_tree_same_2()\n \n }\n \n+check_same_2()\n+{\n+\tif test \"$(git rev-parse \"$1\")\" = \"$(git rev-parse \"$2\")\"\n+\tthen\n+\t\techo \"'$1' is the same as '$2'.\"\n+\telse\n+\t\techo \"'$1' is not the same as '$2'.\"\n+\t\tfalse\n+\tfi\n+}\n \n fi\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex 1fd5122..1d0776d 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -408,4 +408,12 @@ test_expect_success 'tree-same' \\\n \n '\n \n+test_expect_success 'same' \\\n+'\n+   git test \\\n+      --same master HEAD \\\n+      --not-same D D1 \\\n+      --not-same C D \n+'\n+\n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166254","messageId":"1303543372-77843-17-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 16/23] misc","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:45Z","receivedAt":"2011-04-23T07:22:45Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n t/t1520-test.sh |   26 ++++++++++++++++++++++++++\n 1 files changed, 26 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex 1d0776d..78f31fe 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -416,4 +416,30 @@ test_expect_success 'same' \\\n       --not-same C D \n '\n \n+test_expect_success 'clean' \\\n+'\n+    git test \\\n+       --not-staged \\\n+       --not-unstaged \\\n+       --not-detached \\\n+       --not-untracked \\\n+       --not-rebasing \\\n+       --not-conflicted \n+'\n+\n+test_expect_success 'existence' \\\n+'\n+    git test \\\n+       --commit-exists M \\\n+       --not-commit-exists N \\\n+       --ref-exists refs/tags/M \\\n+       --not-ref-exists refs/tags/N \\\n+       --branch-exists master \\\n+       --not-branch-exists N \\\n+       --tag-exists D \\\n+       --not-tag-exists N \\\n+       --not-tag-exists master \\\n+       --not-branch-exists A \n+ '\n+\n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166255","messageId":"1303543372-77843-18-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 17/23] whitespace fix.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:46Z","receivedAt":"2011-04-23T07:22:46Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n t/t1520-test.sh |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex 78f31fe..ddca519 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -440,6 +440,6 @@ test_expect_success 'existence' \\\n        --not-tag-exists N \\\n        --not-tag-exists master \\\n        --not-branch-exists A \n- '\n+'\n \n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166253","messageId":"1303543372-77843-19-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 18/23] tests --conflicted.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:47Z","receivedAt":"2011-04-23T07:22:47Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n t/t1520-test.sh |   26 ++++++++++++++++++++++++++\n 1 files changed, 26 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex ddca519..a18c1d7 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -442,4 +442,30 @@ test_expect_success 'existence' \\\n        --not-branch-exists A \n '\n \n+test_expect_success 'git test --conflicted # should fail' \\\n+'\n+\ttest_must_fail git test --conflicted\n+'\n+\n+test_expect_success 'git test --not-conflicted' \\\n+'\n+\tgit test --not-conflicted\n+'\n+\n+test_expect_success 'git test --conflicted' \\\n+'\n+\ttest_when_finished \"git reset --hard HEAD\" && \n+        ! git merge F &&\n+\tgit test --conflicted \n+\t\n+'\n+\n+test_expect_success 'git test --not-conflicted # should fail when there are conflcted files' \\\n+'\n+\ttest_when_finished \"git reset --hard HEAD\" && \n+        ! git merge F &&\n+\ttest_must_fail git test --not-conflicted \n+\t\n+'\n+\n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166257","messageId":"1303543372-77843-20-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 19/23] rebasing: add tests","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:48Z","receivedAt":"2011-04-23T07:22:48Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n t/t1520-test.sh |   35 +++++++++++++++++++++++++++++++++++\n 1 files changed, 35 insertions(+), 0 deletions(-)\n\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex a18c1d7..1db3598 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -468,4 +468,39 @@ test_expect_success 'git test --not-conflicted # should fail when there are conf\n \t\n '\n \n+test_expect_success 'git test --rebasing # should fail' \\\n+'\n+\ttest_must_fail git test --rebasing\n+'\n+\n+test_expect_success 'git test --not-rebasing' \\\n+'\n+\tgit test --not-rebasing\n+'\n+\n+test_expect_success 'git test --rebasing' \\\n+'\n+\ttest_when_finished \"\n+\t\tgit reset --hard HEAD &&\n+\t\tgit checkout -f master && \n+\t\tgit branch -D rebase\n+\t\" && \n+        git branch rebase F &&\n+        ! git rebase --onto D F~1 F\n+\tgit test --rebasing \n+\t\n+'\n+\n+test_expect_success 'git test --not-rebasing' \\\n+'\n+\ttest_when_finished \"\n+\t\tgit reset --hard HEAD &&\n+\t\tgit checkout -f master &&\n+\t\tgit branch -D rebase\n+\t\" && \n+        git branch rebase F &&\n+        ! git rebase --onto D F~1 F\n+\ttest_must_fail git test --not-rebasing\n+'\n+\n test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166258","messageId":"1303543372-77843-21-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 20/23] test: git test cleanups.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:49Z","receivedAt":"2011-04-23T07:22:49Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"---\n Documentation/git-test.txt |   48 +++++++++++++++++++++---------------------\n git-conditions-lib.sh      |    4 +-\n git-test-lib.sh            |   11 +++++----\n t/t1520-test.sh            |   50 ++++++++++++++++++++++----------------------\n 4 files changed, 57 insertions(+), 56 deletions(-)\n\ndiff --git a/Documentation/git-test.txt b/Documentation/git-test.txt\nindex b7c3161..4fa765b 100644\n--- a/Documentation/git-test.txt\n+++ b/Documentation/git-test.txt\n@@ -7,7 +7,7 @@ git-test - evaluates one or more conditions about the state of the git working t\n \n SYNOPSIS\n --------\n-The git test API is available in the form of a command and also as a shell library. \n+The git test API is available in the form of a command and also as a shell library.\n \n COMMAND SYNOPSIS\n ----------------\n@@ -27,28 +27,28 @@ DESCRIPTION\n `git test` provides a uniform, extensible API for evaluating various conditions that\n pertain to the state of a git working tree, index and repository.\n \n-Specified conditions are evaluated from left to right. If any condition evaluates to false, \n-the command conditionally prints a diagnostic message to stderr and sets a \n-non-zero status code. Otherwise, sets a status code of zero. \n+Specified conditions are evaluated from left to right. If any condition evaluates to false,\n+the command conditionally prints a diagnostic message to stderr and sets a\n+non-zero status code. Otherwise, sets a status code of zero.\n \n The message used to report a assertion failure may be overidden by specifying the --message option.\n \n-Diagnostic output resulting from an assertion failure may be suppressed with the -q option. \n-Note that the -q option does not suppress diagnostic output that results from the failure to \n+Diagnostic output resulting from an assertion failure may be suppressed with the -q option.\n+Note that the -q option does not suppress diagnostic output that results from the failure to\n successfully parse the arguments that configure the test API.\n \n-The `assert` and `test_condition` functions differ according to how they handle failing conditions. \n+The `assert` and `test_condition` functions differ according to how they handle failing conditions.\n \n-The `assert` function behaves like the test command but assertion failures will cause \n+The `assert` function behaves like the test command but assertion failures will cause\n the current shell to exit with a non-zero status code. To prevent this occurring, invoke\n the `assert` function within a subshell or use the `test_condition` function instead.\n \n The `test_condition` function will not exit the current shell in the event that an assertion failure\n-is detected nor will it generate diagnostic relating to an assertion failure to stderr. \n+is detected nor will it generate diagnostic relating to an assertion failure to stderr.\n \n-Note, however, that `test_condition` may still exit the current shell with a non-zero status code \n-if it is unable to successfully parse the arguments presented to it. Callers who need to protect \n-against this possibility either guarantee that the required arguments are available or \n+Note, however, that `test_condition` may still exit the current shell with a non-zero status code\n+if it is unable to successfully parse the arguments presented to it. Callers who need to protect\n+against this possibility either guarantee that the required arguments are available or\n imbed calls to `test_condition` within a subshell.\n \n The `require_condition_libs` function may be used to include any condition libs listed\n@@ -77,7 +77,7 @@ CONDITIONS\n '--detached'|'--not-detached'::\n \tTests if the head is (not) detached.\n '--branch-exists'|'--not-branch-exists branch'::\n-        Tests if the specified branch does (not) exist.\t\t\t\t    \n+\tTests if the specified branch does (not) exist.\n '--tag-exists'|'--not-tag-exists tag'::\n         Tests if the specified tag does (not) exist.\n '--ref-exists'|'--not-ref-exists tag'::\n@@ -95,15 +95,15 @@ CONDITIONS\n \n EXTENDING THE CONDITION LIBRARY\n -------------------------------\n-The library of conditions that the assert API can process may be extended by \n-adding functions of the form check_\\{dehyphenated_condition_name\\}_N to the \n-shell environment, where \\{dehyphenated_condition_name\\} is the result of \n-replacing any occurrence of \\'-\\' in the condition name with \\'_\\' and \n+The library of conditions that the assert API can process may be extended by\n+adding functions of the form check_\\{dehyphenated_condition_name\\}_N to the\n+shell environment, where \\{dehyphenated_condition_name\\} is the result of\n+replacing any occurrence of \\'-\\' in the condition name with \\'_\\' and\n N is the number of arguments that need to be passed to the function.\n \n-For example, suppose you are writing a script, foo.sh, that includes the git test library \n-and that you want to add a new, 1-argument, condition, foo-X to the library of \n-conditions that can be tested by the git testion framework. \n+For example, suppose you are writing a script, foo.sh, that includes the git test library\n+and that you want to add a new, 1-argument, condition, foo-X to the library of\n+conditions that can be tested by the git testion framework.\n \n ---------\n #/bin/sh\n@@ -133,8 +133,8 @@ executes without generating any output on stdout. The resulting\n state will then be interpreted as condition evaluation failure\n rather than an assertion failure.\n \n-To make such conditions available to the git test command line, put the \n-function in a file called ~/foo-lib,sh add a reference \n+To make such conditions available to the git test command line, put the\n+function in a file called ~/foo-lib,sh add a reference\n to the library to the git configuration, like so:\n \n ---------\n@@ -163,9 +163,9 @@ git test --not-staged --not-unstaged && git reset --hard another-commit\n -----------\n . $(git --exec-path)/git-test-lib\n assert --not-staged   \t      \t                              # die if there are any staged files\n-assert --message \"there are staged files\" --not-staged\t      # die with an alternative message \n+assert --message \"there are staged files\" --not-staged\t      # die with an alternative message\n                                                               # if there are any staged files\n-test_condition --not-staged || echo \"there are staged files\"  # check whether there are staged files, \n+test_condition --not-staged || echo \"there are staged files\"  # check whether there are staged files,\n                                                               # but do not die if there are\n -----------\n \ndiff --git a/git-conditions-lib.sh b/git-conditions-lib.sh\nindex ec9b516..8933321 100644\n--- a/git-conditions-lib.sh\n+++ b/git-conditions-lib.sh\n@@ -141,13 +141,13 @@ check_reachable_2()\n \tfirst=$(git rev-parse --quiet --verify \"$1\" 2>/dev/null) || die \"'$1' is not a commit\"\n \tsecond=$(git rev-parse --quiet --verify \"$2\" 2>/dev/null) || die \"'$2' is not a commit\"\n \tif test \"$first\" = \"$second\" \\\n-                -o -z \"$(git rev-list -n1 \"$first\" ^\"$second\")\" \n+\t    -o -z \"$(git rev-list -n1 \"$first\" ^\"$second\")\"\n \tthen\n \t\techo \"'$1' is reachable from '$2'.\"\n \telse\n \t\techo \"'$1' is not reachable from '$2'.\"\n \t\tfalse\n-\tfi     \n+\tfi\n }\n \n check_tree_same_2()\ndiff --git a/git-test-lib.sh b/git-test-lib.sh\nindex 159cea6..e0395eb 100644\n--- a/git-test-lib.sh\n+++ b/git-test-lib.sh\n@@ -34,7 +34,7 @@ require_lib()\n require_condition_libs() {\n \teval $(\n \t\tgit config --get-all condition.lib | while read lib\n-\t\tdo \n+\t\tdo\n \t\t\techo \"require_lib \\\"$lib\\\" \\;\"\n \t\tdone\t\n \t)\n@@ -42,7 +42,7 @@ require_condition_libs() {\n \n assertion_failed() {\n \trc=$1\n-\tshift \n+\tshift\n \tmessage=\"${MESSAGE:-$*}\"\n \tif ! ${QUIET:-false}\n \tthen\n@@ -98,6 +98,7 @@ impl() {\n \t\tshift\n \t\tcase $word in\n \t\t\t--message)\n+\t\t\ttest $# -gt 0 || die \"--message requires the following argument to be a message\"\n \t\t\tMESSAGE=$1\n \t\t\tshift\n \t\t\tcontinue\n@@ -106,7 +107,7 @@ impl() {\n \t\t\t\ttest $# -gt 0 || die \"can't shift 1 argument for --include option\"\n \t\t\t\ttest -f \"$1\" || die \"'$1' must be a file\"\n \t\t\t\trequire_lib \"$1\"\n-\t\t\t\tshift \n+\t\t\t\tshift\n \t\t\t\tcontinue\n \t\t\t;;\n \t\t\t-q)\n@@ -143,7 +144,7 @@ impl() {\n \n \t\texprs=\"${exprs}${exprs:+ }${negation}${negation:+ }$word $try check_${dehyphenated}_$try $args\"\n \n-\tdone \n+\tdone\n \n \tset -- $exprs\n \twhile test $# -gt 0\n@@ -158,7 +159,7 @@ impl() {\n \t\tword=$1\n \t\tnargs=$2\n \t\tshift 2\n-\t\tmessage=$(eval $negation \"$@\") \n+\t\tmessage=$(eval $negation \"$@\")\n \t\trc=$?\n \t\tif test $rc -ne 0\n \t\tthen\ndiff --git a/t/t1520-test.sh b/t/t1520-test.sh\nindex 1db3598..3e0571b 100755\n--- a/t/t1520-test.sh\n+++ b/t/t1520-test.sh\n@@ -46,7 +46,7 @@ test_expect_assertion_failure()\n {\n \ttest=$1\n \tmessage=$2\n-\tshift \n+\tshift\n \ttest_expect_success $1 \\\n \"\n \t! actual_message=$(git test \"$@\" 1>&2) &&\n@@ -62,11 +62,11 @@ test_expect_assertion_failure()\n \n test_expect_success 'setup' \\\n '\n-\tgit add test-assertions-lib.sh empty-assertions-lib.sh && \n+\tgit add test-assertions-lib.sh empty-assertions-lib.sh &&\n \ttest_commit base &&\n \ttest_commit A &&\n \tgit checkout A^1 &&\n-\ttest_commit B && \n+\ttest_commit B &&\n \tgit checkout master &&\n \ttest_merge M B &&\n \techo C >> B.t &&\n@@ -85,7 +85,7 @@ test_expect_success 'setup' \\\n \tgit checkout A^0 -- &&\n \ttest_commit G &&\n \tgit checkout master &&\n-\tgit reset --hard D     \n+\tgit reset --hard D\n '\n \n test_expect_success 'git test # no arguments' \\\n@@ -194,7 +194,7 @@ test_expect_success 'git test --not-unstaged' \\\n \n test_expect_success 'git test --unstaged # when there are unstaged files' \\\n '\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" &&\n \tgit checkout -f M^0 &&\n \tgit stash apply --index STASH_UNSTAGED &&\n \tgit test --unstaged\n@@ -202,7 +202,7 @@ test_expect_success 'git test --unstaged # when there are unstaged files' \\\n \n test_expect_success 'git test --not-unstaged # when there are unstaged files - should fail' \\\n '\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" &&\n \tgit checkout -f M^0 &&\n \tgit stash apply --index STASH_UNSTAGED &&\n \ttest_must_fail git test --not-unstaged\n@@ -210,7 +210,7 @@ test_expect_success 'git test --not-unstaged # when there are unstaged files - s\n \n test_expect_success 'git test --unstaged # when there are only staged files' \\\n '\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" &&\n \tgit checkout -f M^0 &&\n \tgit stash apply --index STASH_STAGED &&\n \tgit test --not-unstaged\n@@ -228,7 +228,7 @@ test_expect_success 'git test --not-staged' \\\n \n test_expect_success 'git test --staged # when there are staged files' \\\n '\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" &&\n \tgit checkout -f M^0 &&\n \tgit stash apply --index STASH_STAGED &&\n \tgit test --staged\n@@ -236,7 +236,7 @@ test_expect_success 'git test --staged # when there are staged files' \\\n \n test_expect_success 'git test --not-staged # when there are staged files - should fail' \\\n '\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" &&\n \tgit checkout -f M^0 &&\n \tgit stash apply --index STASH_STAGED &&\n \ttest_must_fail git test --not-staged\n@@ -244,7 +244,7 @@ test_expect_success 'git test --not-staged # when there are staged files - shoul\n \n test_expect_success 'git test --staged # when there are only unstaged files' \\\n '\n-\ttest_when_finished \"git reset --hard HEAD && git checkout master\" && \n+\ttest_when_finished \"git reset --hard HEAD && git checkout master\" &&\n \tgit checkout -f M^0 &&\n \tgit stash apply --index STASH_UNSTAGED &&\n \tgit test --not-staged\n@@ -262,14 +262,14 @@ test_expect_success 'git test --not-untracked' \\\n \n test_expect_success 'git test --untracked # when there are untracked files' \\\n '\n-\ttest_when_finished \"git clean -fd\" && \n+\ttest_when_finished \"git clean -fd\" &&\n \t:> untracked &&\n \tgit test --untracked\n '\n \n test_expect_success 'git test --not-untracked # when there are untracked files - should fail' \\\n '\n-\ttest_when_finished \"git clean -fd\" && \n+\ttest_when_finished \"git clean -fd\" &&\n \t:> untracked &&\n \ttest_must_fail git test --not-untracked\n '\n@@ -286,14 +286,14 @@ test_expect_success 'git test --detached # should fail' \\\n \n test_expect_success 'git test --not-detached # when detached, should fail' \\\n '\n-\ttest_when_finished \"git checkout -f master\" && \n+\ttest_when_finished \"git checkout -f master\" &&\n \tgit checkout HEAD^0 &&\n \ttest_must_fail git test --not-detached\n '\n \n test_expect_success 'git test --detached # when detached' \\\n '\n-\ttest_when_finished \"git checkout -f master\" && \n+\ttest_when_finished \"git checkout -f master\" &&\n \tgit checkout HEAD^0 &&\n \tgit test --detached\n '\n@@ -413,7 +413,7 @@ test_expect_success 'same' \\\n    git test \\\n       --same master HEAD \\\n       --not-same D D1 \\\n-      --not-same C D \n+      --not-same C D\n '\n \n test_expect_success 'clean' \\\n@@ -424,7 +424,7 @@ test_expect_success 'clean' \\\n        --not-detached \\\n        --not-untracked \\\n        --not-rebasing \\\n-       --not-conflicted \n+       --not-conflicted\n '\n \n test_expect_success 'existence' \\\n@@ -439,7 +439,7 @@ test_expect_success 'existence' \\\n        --tag-exists D \\\n        --not-tag-exists N \\\n        --not-tag-exists master \\\n-       --not-branch-exists A \n+       --not-branch-exists A\n '\n \n test_expect_success 'git test --conflicted # should fail' \\\n@@ -454,17 +454,17 @@ test_expect_success 'git test --not-conflicted' \\\n \n test_expect_success 'git test --conflicted' \\\n '\n-\ttest_when_finished \"git reset --hard HEAD\" && \n+\ttest_when_finished \"git reset --hard HEAD\" &&\n         ! git merge F &&\n-\tgit test --conflicted \n+\tgit test --conflicted\n \t\n '\n \n test_expect_success 'git test --not-conflicted # should fail when there are conflcted files' \\\n '\n-\ttest_when_finished \"git reset --hard HEAD\" && \n+\ttest_when_finished \"git reset --hard HEAD\" &&\n         ! git merge F &&\n-\ttest_must_fail git test --not-conflicted \n+\ttest_must_fail git test --not-conflicted\n \t\n '\n \n@@ -482,12 +482,12 @@ test_expect_success 'git test --rebasing' \\\n '\n \ttest_when_finished \"\n \t\tgit reset --hard HEAD &&\n-\t\tgit checkout -f master && \n+\t\tgit checkout -f master &&\n \t\tgit branch -D rebase\n-\t\" && \n+\t\" &&\n         git branch rebase F &&\n         ! git rebase --onto D F~1 F\n-\tgit test --rebasing \n+\tgit test --rebasing\n \t\n '\n \n@@ -497,7 +497,7 @@ test_expect_success 'git test --not-rebasing' \\\n \t\tgit reset --hard HEAD &&\n \t\tgit checkout -f master &&\n \t\tgit branch -D rebase\n-\t\" && \n+\t\" &&\n         git branch rebase F &&\n         ! git rebase --onto D F~1 F\n \ttest_must_fail git test --not-rebasing\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166259","messageId":"1303543372-77843-22-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 21/23] Introduce git-atomic.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:50Z","receivedAt":"2011-04-23T07:22:50Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"git atomic is intended to provide a simple way to for porcelains to\nexecute atomic operations on the git repository.\n\nAn atomic operation either completes successfully or the\nworking tree, index and selected references are returned to their\noriginal state.\n\nSigned-off-by: Jon Seymour <jon.seymour@gmail.com>\n---\n .gitignore                   |    2 +\n Documentation/git-atomic.txt |   92 ++++++++++++++++++++++++++++++++++++++++++\n Makefile                     |    2 +\n git-atomic-lib.sh            |   58 ++++++++++++++++++++++++++\n git-atomic.sh                |    5 ++\n t/t3419-atomic.sh            |   59 +++++++++++++++++++++++++++\n 6 files changed, 218 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-atomic.txt\n create mode 100644 git-atomic-lib.sh\n create mode 100755 git-atomic.sh\n create mode 100755 t/t3419-atomic.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex aa0eb8fb..5efc43c 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -11,6 +11,8 @@\n /git-apply\n /git-archimport\n /git-archive\n+/git-atomic\n+/git-atomic-lib\n /git-bisect\n /git-bisect--helper\n /git-blame\ndiff --git a/Documentation/git-atomic.txt b/Documentation/git-atomic.txt\nnew file mode 100644\nindex 0000000..02bbee6\n--- /dev/null\n+++ b/Documentation/git-atomic.txt\n@@ -0,0 +1,92 @@\n+git-atomic(1)\n+===========\n+\n+NAME\n+----\n+git-atomic - conditionally execute a command and, if it fails, restore the working tree, index and HEAD to their original state.\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git atomic' [options] [--] [cmd args...]\n+. $(git --exec-path)/git-atomic-lib.sh\n+\n+DESCRIPTION\n+-----------\n+The first form conditionally executes a command depending on whether the pre-conditions are satisified. If the command exits with a non-zero exit code, restores the\n+working tree, index and HEAD to their orignal state. If no command is specified, sets the exit code according to the pre-conditions.\n+\n+'git atomic' exits with:\n+\n+0::\n+\tif the pre-conditions are satisified and the command executes successfully\n+128::\n+\tif the command exited with an exit code of 128, 129, 130 or 131.\n+129::\n+\tif git atomic failed during setup or argument parsing\n+130::\n+\tif the pre-conditions were not satisified.\n+131::\n+\tif the command failed and the original state could not be restored.\n+\n+Otherwise, exits with the actual exit code of the command.\n+\n+The second form is used to import the definition of a shell function called atomic that can be called by shell scripts that perform git operations. This is useful when the operations to be given atomic behaviour are themselves shell functions rather than external commands.\n+\n+OPTIONS\n+-------\n+The following options specify pre-condition tests on various lists which are assumed to be empty in the clean state and non-empty in the dirty state.\n+\n+The default required state for each specified pre-condition option is clean. The default required state for each unspecified pre-condition option is any, which\n+means no tests are applied.\n+\n+--unstaged [any|clean|dirty]::\n+\tFail unless the list of unstaged changes is empty (clean) or non-empty (dirty).\t\n+--staged   [any|clean|dirty]::\n+\tFail unless the list of staged changes is empty (clean) or non-empty (dirty).\n+--untracked [any|clean|dirty]::\n+\tFail unless the list of untracked files is empty (clean) or non-empty (dirty).\n+--tracked [any|clean|dirty]::\n+\tFail unless the list of staged and unstaged changes is empty (clean) or non-empty (dirty).\n+--unmerged [any|clean|dirty]::\n+\tFail unless the list of unmerged files is empty (clean) or non-empty (dirty).\n+--rebase [any|clean|dirty]::\n+\tFail unless a rebase is not (clean) or is (dirty) in progress.\n+--all::\n+\tFail unless the list of staged, unstaged changes and untracked files is empty (clean) or non-empty (dirty)\n+\n+EXAMPLES\n+--------\n+* Reset the tree only if there are no staged or unstaged changes and no untracked files.\n++\n+-----------\n+git atomic --all clean git reset --hard upstream/master\n+-----------\n+* Conditionally perform a merge, but rollback and die if it fails.\n++\n+-----------\n+git atomic --all clean git merge topic || die \"unable to merge\"\n+-----------\n+* Import git-atomic-lib.sh into a script and make the execution of the function foo atomic with respect to the state of the git workspace\n++\n+-----------\n+. $(git --exec-path)/git-atomic-lib.sh\n+foo()\n+{\n+    git merge $1\n+}\n+\n+atomic foo $1\n+-----------\n+\n+Author\n+------\n+Written by Jon Seymour <jon.seymour@gmail.com>\n+\n+Documentation\n+-------------\n+Documentation by Jon Seymour\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 93ff6c6..9ed877a 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -360,6 +360,7 @@ TEST_PROGRAMS_NEED_X =\n unexport CDPATH\n \n SCRIPT_SH += git-am.sh\n+SCRIPT_SH += git-atomic.sh\n SCRIPT_SH += git-bisect.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\n@@ -379,6 +380,7 @@ SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-test.sh\n SCRIPT_SH += git-web--browse.sh\n \n+SCRIPT_LIB += git-atomic-lib\n SCRIPT_LIB += git-conditions-lib\n SCRIPT_LIB += git-mergetool--lib\n SCRIPT_LIB += git-parse-remote\ndiff --git a/git-atomic-lib.sh b/git-atomic-lib.sh\nnew file mode 100644\nindex 0000000..db48300\n--- /dev/null\n+++ b/git-atomic-lib.sh\n@@ -0,0 +1,58 @@\n+#!/bin/sh\n+#\n+# Provides a function that provides for robust recovery from\n+#\n+. git-test-lib\n+atomic()\n+{\n+    assert --not-conflicted --message \"cannot perform an atomic operation while there are merge conflicts\"\n+    HEAD=$(git rev-parse --verify HEAD) || setup_failed \"failed to resolve HEAD\"\n+    if REF=$(git symbolic-ref -q HEAD)\n+    then\n+          BRANCH=${REF#refs/heads/}\n+    else\n+        BRANCH=${HEAD}\n+    fi\n+\n+    STASH=$(git stash create) || setup_failed \"failed to stash\"\n+    REF=$(git rev-parse --symbolic-full-name HEAD) || setup_failed \"failed to acquire REF\"\n+    REBASE_COUNT=1\n+    test_condition -q --rebasing || REBASE_COUNT=0\n+\n+    (\n+      \"$@\"\n+    ) || (\n+       RC=$?\n+\n+       command_failed()\n+       {\n+           rc=$1\n+\t   shift\n+           echo \"command failed: $* rc=$rc\" 1>&2\n+           exit 1\n+       }\n+\n+       restore_failed()\n+       {\n+           echo \"restore failed: $*\" 1>&2\n+           exit 2\n+       }\n+\n+       if test $REBASE_COUNT -eq 0 && test -d \"$REBASE_DIR\"\n+       then\n+            git rebase --abort || restore_failed \"failed to abort rebase\"\n+       fi\n+\n+\t{\n+\t\tgit reset --hard HEAD &&\n+\t\tgit checkout -q ${BRANCH}\n+\t} || restore_failed \"failed to checkout ${BRANCH}\"\n+\n+\tif test -n \"$STASH\"\n+\tthen\n+\t\tgit stash apply --index \"$STASH\" || restore_failed \"failed to reapply stash $STASH to $HEAD\"\n+\t\techo \"restored $REF to $(git describe --always --abbrev=6 $HEAD), reapplied stash $(git describe --always --abbrev=6 $STASH)\" 1>&2\n+\tfi\n+\tcommand_failed $RC \"$*\"\n+    )\n+}\ndiff --git a/git-atomic.sh b/git-atomic.sh\nnew file mode 100755\nindex 0000000..aa14e68\n--- /dev/null\n+++ b/git-atomic.sh\n@@ -0,0 +1,5 @@\n+#!/bin/sh\n+SUBDIRECTORY_OK=true\n+. git-sh-setup\n+. git-atomic-lib\n+atomic \"$@\"\ndiff --git a/t/t3419-atomic.sh b/t/t3419-atomic.sh\nnew file mode 100755\nindex 0000000..2b651d2\n--- /dev/null\n+++ b/t/t3419-atomic.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Jon Seymour\n+#\n+\n+test_description='git atomic tests\n+\n+Performs tests on the functions of git atomic\n+'\n+. ./test-lib.sh\n+\n+GIT_AUTHOR_NAME=author@name\n+GIT_AUTHOR_EMAIL=bogus@email@address\n+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL\n+\n+test_expect_success \\\n+    'setup' \\\n+    '\n+    test_commit A &&\n+    test_commit B &&\n+    test_commit C &&\n+    test_commit D &&\n+    git checkout -b branch A &&\n+    test_commit X &&\n+    echo >> B.t &&\n+    git add B.t &&\n+    test_commit Y\n+    true\n+'\n+\n+test_expect_success 'no arguments' '\n+    git atomic\n+'\n+\n+test_expect_success 'successful command' \\\n+'\n+    git atomic true\n+'\n+\n+test_expect_success 'unsuccessful command' \\\n+'\n+    ! git atomic false\n+'\n+\n+test_expect_success 'rebase' \\\n+'\n+     git reset --hard HEAD &&\n+     git checkout master &&\n+     MASTER=$(git rev-parse HEAD) &&\n+     ! git rebase --onto D A Y &&\n+     git test --conflicted &&\n+     git rebase --abort &&\n+     git checkout master &&\n+     ! git atomic git rebase --onto D A Y &&\n+     git test --same HEAD refs/heads/master &&\n+     git test --same HEAD $MASTER\n+'\n+\n+test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166260","messageId":"1303543372-77843-23-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 22/23] Introduce git base.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:51Z","receivedAt":"2011-04-23T07:22:51Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"git base is intended to provide a way to track the 'base'\nof the current branch, where the base is one of several possible\ncommits that satisfies the following invariant.\n\nThe base is reachable from the head the output\ngit rev-list base..head --merges is empty.\n\ngit base is intended to provide a sane way to keep\ntrack of the commit at the base of the current branch\nand thereby simplify the process of rebasing, or more\ngenerally, dependency managment.\n\nFor example of how it is used, see the following git-work\ncommand.\n\nSigned-off-by: Jon Seymour <jon.seymour@gmail.com>\n---\n .gitignore                 |    1 +\n Documentation/config.txt   |   10 ++\n Documentation/git-base.txt |  216 +++++++++++++++++++++++++\n Makefile                   |    1 +\n git-base.sh                |  378 ++++++++++++++++++++++++++++++++++++++++++++\n t/t3418-base.sh            |  214 +++++++++++++++++++++++++\n 6 files changed, 820 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-base.txt\n create mode 100644 git-base.sh\n create mode 100755 t/t3418-base.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 5efc43c..54fa567 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -13,6 +13,7 @@\n /git-archive\n /git-atomic\n /git-atomic-lib\n+/git-base\n /git-bisect\n /git-bisect--helper\n /git-blame\ndiff --git a/Documentation/config.txt b/Documentation/config.txt\nindex 750c86d..52f149b 100644\n--- a/Documentation/config.txt\n+++ b/Documentation/config.txt\n@@ -636,6 +636,16 @@ branch.autosetuprebase::\n \tbranch to track another branch.\n \tThis option defaults to never.\n \n+branch.<name>.baseresetoptions::\n+\tThe options passed to 'git base set' when 'git base' detects\n+\tthat the current base reference is either not set or no longer\n+\tsatisfies the base invariant.\n+\tSee the description of the `init` command in linkgit:git-base[1]\n+\tfor details of how this value is initialized.\n+\tSee the description of the `set` command in linkgit:git-base[1]\n+\tfor details about this value is interpreted.\n+\tDefaults to --fail unless set.\n+\n branch.<name>.remote::\n \tWhen in branch <name>, it tells 'git fetch' and 'git push' which\n \tremote to fetch from/push to.  It defaults to `origin` if no remote is\ndiff --git a/Documentation/git-base.txt b/Documentation/git-base.txt\nnew file mode 100644\nindex 0000000..7a7a710\n--- /dev/null\n+++ b/Documentation/git-base.txt\n@@ -0,0 +1,216 @@\n+git-base(1)\n+===========\n+\n+NAME\n+----\n+git-base - display, check, set, clear and reset the base of a working branch\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git base' [-b branch] [--as-ref]|[<default-commit>]\n+'git base' [-b branch] 'check' <commit>\n+'git base' [-b branch] 'set' [-f] <commit>\n+'git base' [-b branch] 'clear'\n+'git base' [-b branch] 'init' [-d]|[<reset-cmd>]\n+'git base' [-b branch] 'reset'\n+\n+DESCRIPTION\n+-----------\n+In the discussion that follows, the current branch or the branch specified by -b is referred to by the symbol `<branch>` and the commit at the base of `<branch>` is referred to by the symbol `<base>`. `<head>` is used to refer to the commit at the tip of `<branch>` as distinct from the branch reference itself.\n+\n+`git base` helps to track the base of a working branch. For the purposes of the exposition here, the base of the working branch is defined as the\n+last commit in the history that is not part of your current work - it is the basis of your current work, but is not part of the work itself. Furthermore,\n+to be classified as a base, the path from the tip of the branch to the base must not traverse a merge commit. This constraint means that the range\n+`<base>..<bramch>` is always a linear series of commits that can be easily rebased and re-organized as required.\n+\n+COMMANDS\n+--------\n+Without positional arguments, this command checks whether the base reference satisfies the base invariant. If it does, prints the SHA1 hash of the base commit\n+to stdout and exits with a status code of 0.\n+\n+Otherwise, if `<default-commit>` is specified, calls `set <default-commit>`. If this fails to establish the base invariant or if no `<default-commit>`\n+is specified then calls `reset`. If the base reference now satisfies the base invariant, print its SHA1 has to stdout and\n+exits with a status code of 0. Otherwise, prints a warning message to stderr and exits with a non-zero status code.\n+\n+If `--as-ref` is specifed, then the symbolic name of the base reference is printed instead of the SHA1 hash but `reset` is not called, even\n+if the base reference does not satisfy the base invariant.\n+\n+If a `<default-commit>` is specified `set <default-commit>` is called\n+\n+`init [-d]|[<reset-cmd>]`::\n+\n+Initializes the command to be used when `reset` is called. It must be one of `set <commit>`, `clear` or `check`.\n+`<reset-cmd>` defaults to the `set <upstream-branch>` if there is one or to `set <head>`, otherwise.\n+Use a `<reset-cmd>` of `check` to prevent the default `git base` command automatically\n+adjusting an inconsistent base reference. Use a `<reset-cmd>` of `clear` to automatically clear an inconsistent base reference.\n+\n+Use -d to delete the base reference and reset configuration associated with the branch.\n+\n+`clear`::\n+\n+Clears the base reference, if any. The status code is always set to a non-zero value and a warning message is printed to stderr. No output is\n+printed to stdout.\n+\n+`set [-f] <commit>`::\n+\n+Checks that the specified base satisifies the base invariant of `<branch>`, and if so, updates the base reference with the specified commit.\n+Otherwise, updates the base reference to the commit closest to both `<commit>` and `<branch>` which does satisfy the base invariant. The selected\n+commit will always be reachable from `<branch>` but may not be reachable from `<commit>`. In particular, if the merge base of `<commit>` and `<branch>`\n+is hidden by a merge commit, then the selected commit will be (the only) merge commit that satisfies the base invariant of `<branch>`.\n++\n+The status and output are set/generated as per `git base`.\n++\n+`-f` can be used to force the update of the base reference to the specified `<commit>` even if the `<commit>` does not satify the base\n+invariant. Note: however, that unless the effective `reset` command is set to `check`, this value will not stick beyond the next\n+call to `git base`.\n+\n+`check` [<commit>]::\n+\n+If `<commit>` is specified, performs the same function as `set` but does not actually update the base reference. The status code and output are set/generated as they would be if `set` had been called.\n++\n+If `<commit>` is not specified, sets the status code according to whether (0) or not (non-zero) the base reference is well-defined and consistent with the base invariant. The output that is generated is the same output that would be generated if `set` was called with the same arguments.\n++\n+`check` NEVER adjusts the base reference.\n+\n+`reset`::\n+\n+Resets the base reference by calling the git base with the configured `<reset-cmd>` or `clear` if there is no such configuration. The status\n+and output are set/generated as per `git base`.\n+\n+OUTPUT\n+------\n+Unless --as-ref is used, the only output git base generates is the SHA1 hash of a commit that satisfies the base invariant.  If `git base`, `git base set` or `git check` (without arguments) generate output, then the output will be the current base reference at the time the command completes.  If `git check` is called with an\n+argument, then the output is set as if `git set` had been called. If --as-ref is used, the generated output is always the symbolic name of the base reference, whether or not the base reference actually exists or satisfies the base invariant.\n+\n+EXIT CODE\n+---------\n+Except for the `init` command, `git base` exits with status code of zero if command has written the SHA1 hash of a commit that satisfies the base invariant to stdout, and exits with a non-zero status code otherwise.\n+\n+OPTIONS\n+-------\n+-b,--branch branch::\n+\tIf this option is specified and supported, the commands apply to the specified branch. Otherwise, the commands apply to the current branch.\n+-f::\n+\tForce the update of the base reference even if the specified value does not satisfy the base invariant.\n+-q::\n+\tUse this option to suppress data output, information and warning messages.\n+--as-ref::\n+\tPrint the SHA1 hash of the base as a symbolic reference.\n+\n+CONFIGURATION\n+-------------\n+branch.<name>.baseresetcmd::\n+\tConfigures the `git base` subcommand used by `git base reset`. Must be one of `set <commit>`, `check` or `clear`. Defaults to `clear`, if not specified.\n+\n+THE BASE INVARIANT\n+------------------\n+The base invariant applies to all commits that can be described as a base of a branch.\n+\n+The invariant is that the base commit is reachable from the head of the branch and that the path between the head of the branch and the base contains no merge commits other than, possibly, the base commit itself.\n+\n+The head of a branch is also a base commit for the branch. As such, each branch has at least one commit that satisfies the base invariant.\n+For any given branch there is at most one merge commit that satisifies the base invariant.\n+\n+The base invariant is defined as it is because the history between a commit satisifying the base invariant and the head of the branch is, by definition,\n+guaranteed to be linear.\n+\n+BASE REFERENCE\n+--------------\n+The base reference is semi-automatically managed by the git.\n+\n+It is explicitly updated by the `set` and `clear` commands and sometimes by the `reset` command, depending on how it has been configured.\n+It may be implicitly updated by the default command (via a call to `reset`) but only when it is undefined or does not satisfy the base reference.\n+It is never updated by the `check` or `init` commands. Other git tools such as linkgit:git-work[1] use git base to update the base reference as required.\n+\n+Users may use the `set` command with the -f option to modify the base reference to any value but values that fail to match the base invariant will quickly\n+be reset by the automatic operation of `git base` unless the reset operation is configured to be `check`.\n+\n+A non-empty output from `git base`, `git base reset` or `git base set` is guaranteed to be consistent with the base invariant immediately after the command\n+completes.\n+\n+THE STATE MACHINE VIEW\n+----------------------\n+A good way to think about `git base` is as the controls of a 3-state state machine: a machine that has the states: UNDEFINED, INCONSISTENT and CONSISTENT\n+which correspond to an undefined base reference, a base reference that does not satisfy the base invariant and a base reference that does satisfy\n+the base invariant, respectively.\n+\n+The state machine prefers to be in the CONSISTENT state. `git base` without arguments will leave the state of the base reference unchanged if is CONSISTENT\n+or will call `git base reset` otherwise.  `git base reset` uses the `<reset-cmd>` defined with `git base init` to implement the auto-recovery policy\n+of the state machine when it finds itself in the INCONSISTENT or UNDEFINED states.\n+\n+`git base check` tests the state of the base reference, but leaves its state unchanged.\n+\n+`git base clear` unconditionally clears base reference forcing the state to be UNDEFINED.\n+\n+`git base set` without a `-f` option is used to force the machine into a CONSISTENT state.\n+\n+EXAMPLES\n+--------\n+* Initialize the branch so that the base reference is always reset, when required, relative to upstream/master\n++\n+----------\n+git base init set upstream/master\n+----------\n+* Display the current base\n++\n+---------\n+git base\n+---------\n+* Display the current base reference without updating it\n++\n+---------\n+git base check\n+---------\n+* Display the current base as a symbolic reference\n++\n+---------\n+git base --as-ref\n+---------\n+* Set the base of the current branch to the 3rd commit from the tip\n++\n+---------\n+git base set HEAD~3\n+---------\n+* Interactively rebase the current work\n++\n+---------\n+git rebase -i $(git base) HEAD\n+---------\n+* Perform a rebase, but only if the base reference is currently valid\n++\n+---------\n+git base check -q && git rebase -i $(git base) HEAD\n+---------\n+* Set the base relative to an upstream branch\n++\n+---------\n+git base set origin/master\n+---------\n+\n+\n+FILES\n+-----\n+\n+.git/refs/bases/`<branch>`::\n+\tContains the last calculated value of the base for a specific branch. This value is not guaranteed to be correct except immediately after a successful execution of git base. Users that require an accurate value should use the output of git base -b `<branch>`.\n+\n+.git/refs/BASE::\n+\tIf HEAD is a symbolic reference of the form `refs/heads/<branch>`, then BASE will be a symbolic reference of the form `refs/bases/<branch>`. Otherwise,\n+\tit contains the base commit corresponding to the detached HEAD, if one has been specified.\n+\n+SEE ALSO\n+--------\n+linkgit:git-work[1]\n+\n+Author\n+------\n+Written by Jon Seymour <jon.seymour@gmail.com>\n+\n+Documentation\n+-------------\n+Documentation by Jon Seymour\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 9ed877a..6aee4a8 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -361,6 +361,7 @@ unexport CDPATH\n \n SCRIPT_SH += git-am.sh\n SCRIPT_SH += git-atomic.sh\n+SCRIPT_SH += git-base.sh\n SCRIPT_SH += git-bisect.sh\n SCRIPT_SH += git-difftool--helper.sh\n SCRIPT_SH += git-filter-branch.sh\ndiff --git a/git-base.sh b/git-base.sh\nnew file mode 100644\nindex 0000000..14c64c9\n--- /dev/null\n+++ b/git-base.sh\n@@ -0,0 +1,378 @@\n+#!/bin/sh\n+#\n+# (c) Copyright Jon Seymour 2010\n+#\n+USAGE='[help|set|check|reset|init]'\n+LONG_USAGE='\n+git base [--as-ref]\n+        print the SHA1 hash of the base reference or its symbolic name (--as-ref)\n+git base set [-f] <commit>\n+        set the base of the branch to commit nearest <commit> that satisifies the base invariant.\n+git base clear\n+        As per set, but never update the base reference.\n+git base check <commit>\n+        test if the specified commit satisfies the base invariant\n+git base init <reset-cmd>\n+\tInitialise the git base command to be called when git base reset is invoked.\n+git base reset\n+\tInvoke the configured reset command (see git base init) or git base clear, if not such command is configured.\n+git base help\n+        print this help\n+\n+Please use \"git help base\" to get the full man page.'\n+\n+OPTIONS_SPEC=\n+\n+SUBDIRECTORY_OK=true\n+. git-sh-setup\n+require_work_tree\n+\n+warn()\n+{\n+    test -n \"$QUIET\" || echo \"$*\" 1>&2\n+}\n+\n+info()\n+{\n+    test -n \"$QUIET\" || echo \"$*\" 1>&2\n+}\n+\n+data()\n+{\n+\tif test \"$*\" != \"\"\n+\tthen\n+\t\techo \"$*\"\n+\t\ttrue\n+\telse\n+\t\tfalse\n+\tfi\n+}\n+\n+quietly()\n+{\n+\tif test -n \"$QUIET\"\n+\tthen\n+\t\t\"$@\" >/dev/null 2>/dev/null\n+\telse\n+\t\t\"$@\"\n+\tfi\n+}\n+\n+assert_valid_branch()\n+{\n+\ttest -n \"${VALID_BRANCH}\" || die \"${BRANCH} is not a branch\"\n+}\n+\n+invariant_state()\n+{\n+\tcommit=$1\n+\tif test -z \"$commit\"\n+\tthen\n+\t\techo \"UNDEFINED\"\n+\t\tfalse\n+\telif ! git rev-parse --quiet --verify \"$commit\" >/dev/null\n+\tthen\n+\t\techo \"INVALID\"\n+\t\tfalse\n+\telif ! git test -q --reachable $commit $HEAD\n+\tthen\n+\t\techo \"UNREACHABLE\"\n+\t\tfalse\n+\telif ! test -z \"$(last_merge $HEAD $commit)\"\n+\tthen\n+\t\techo \"HIDDEN\"\n+\t\tfalse\n+\telse\n+\t\techo \"CONSISTENT\"\n+\t\ttrue\n+\tfi\n+}\n+\n+closest()\n+{\n+\tcommit=$1\n+\tstate=${2:-$(invariant_state $commit)}\n+\t\n+\tcase $state in\t\n+\t\tUNDEFINED|INVALID)\n+\t\t\t:\n+\t\t;;\n+\t\tUNREACHABLE)\n+\t\t\tclosest $(git merge-base $HEAD $commit)\n+\t\t;;\n+\t\tHIDDEN)\n+\t\t\tlast_merge $HEAD $commit\n+\t\t;;\n+\t\tCONSISTENT)\n+\t\t\techo $commit\n+\t\t;;\n+\tesac\n+}\n+\n+describe()\n+{\n+\tcommit=$1\n+\tstate=$2\n+\t\n+\tcase $state in\n+\t\tUNDEFINED)\n+\t\t\techo \"No commit specified.\"\n+\t\t;;\n+\t\tINVALID)\n+\t\t\techo \"$commit is not a valid reference.\"\n+\t\t;;\n+\t\tHIDDEN)\n+\t\t\techo \"The commit $(short_ref $commit) is not a base of ${BRANCH} because it is hidden by the merge commit $(short_ref $(last_merge $HEAD $commit)).\"\n+\t\t;;\n+\t\tUNREACHABLE)\n+\t\t\techo \"The commit $(short_ref $commit) is not a base of ${BRANCH} because it is unreachable from $(short_ref $HEAD).\"\n+\t\t;;\n+\t\tCONSISTENT)\n+\t\t\techo \"The commit $(short_ref $commit) satisfies the base invariant of $(short_ref $HEAD).\"\n+\t\t;;\n+\tesac\n+}\n+\n+last_merge()\n+{\n+    head=$1\n+    commit=$2\n+    data $(git rev-list --max-count=1 --merges ${head} ^$commit)\n+}\n+\n+short_ref()\n+{\n+    data \"($(git rev-parse --short $1))\"\n+}\n+\n+base_default()\n+{\n+\tassert_valid_branch\n+\n+\trevs=$(git rev-parse --revs-only \"$@\")\n+\n+\tif state=$(invariant_state \"${BASEREF}\")\t\n+\tthen\n+\t\tif test -z \"$ASREF\"\n+\t\tthen\n+\t\t\tgit rev-parse ${BASEREF}\n+\t\telse\n+\t\t\techo \"${BASEREF}\"\n+\t\tfi\n+\telse\n+\t\tif test -z \"$ASREF\"\n+\t\tthen\n+\t\t\tif test -n \"$revs\"\n+\t\t\tthen\n+\t\t\t\tbase_set $revs && return 0\n+\t\t\tfi\n+\t\n+\t\t\tdescribe \"${BASEREF}\" $state 1>&2\n+\t\t\tbase_reset\n+\t\telse\n+\t\t\tdescribe \"${BASEREF}\" $state 1>&2\n+\t\t\techo \"${BASEREF}\"\n+\t\t\tfalse\n+\t\tfi\n+\tfi\n+}\n+\n+base_init()\n+{\n+\tif test -n \"$DELETE\"\n+\tthen\n+\t\tgit update-ref -d ${BASEREF} &&\n+\t\tgit config branch.${BRANCH}.baseresetcmd clear &&\n+\t\tgit config --unset branch.${BRANCH}.baseresetcmd\n+\t\treturn 0\n+\tfi\n+\n+\tassert_valid_branch\n+\n+\tif test $# -eq 0\n+\tthen\n+\t\tset -- $(git config branch.${BRANCH}.merge)\n+\t\tMERGE=$1\n+\t\tset -- $(git config branch.${BRANCH}.remote)\n+\t\tcase \"$1\" in\t\n+\t\t.)\n+\t\t\tOPTION=\"set ${MERGE}\"\n+\t\t;;\n+\t\t\"\")\n+\t\t\tOPTION=\"set $(git rev-parse ${BRANCH})\"\n+\t\t;;\n+\t\t*)\n+\t\t\tOPTION=\"set $1/${MERGE#refs/heads/}\"\n+\t\t;;\n+\t\tesac\n+\t\tset -- ${OPTION}\n+\telse\n+\t\tcase \"$1\" in\t\n+\t\t\tset|clear|check)\n+\t\t\t:\n+\t\t\t;;\n+\t\t*)\n+\t\t\tdie \"$1 is not a valid 'git base' command\"\n+\t\t\t;;\n+\t\tesac\n+\tfi\n+\n+\twarn \"The reset command for ${BRANCH} is now '$*'.\"\n+\tgit config branch.${BRANCH}.baseresetcmd \"$*\" || die \"failed to update branch.${BRANCH}.baseresetcmd\"\n+}\n+\n+base_reset()\n+{\n+\tassert_valid_branch\n+\n+\toptions=$(git config branch.${BRANCH}.baseresetcmd)\n+\toptions=${options:-clear}\n+\n+\twarn \"Resetting the base of ${BRANCH} with 'git base ${options}'.\"\n+\tgit base -b ${BRANCH} ${options}\n+}\n+\n+base_check()\n+{\n+\tassert_valid_branch\n+\n+\tspecified=$1\n+\tcommit=${specified:-${BASEREF}}\n+\n+\tif state=$(invariant_state \"$commit\")\n+\tthen\n+\t\tgit rev-parse $commit\n+\telse\n+\t\tdescribe \"$commit\" $state 1>&2\n+\t\tfalse\n+\tfi\n+}\n+\n+base_set()\n+{\n+\tassert_valid_branch\n+\n+\tUSAGE=\"usage: git base set [-f] <commit>\"\n+\ttest -n \"$1\" || die \"$USAGE\"\n+\n+\tcommit=$1\n+\t\n+\tif state=$(invariant_state \"$commit\")\n+\tthen\n+\t\tgit update-ref \"${BASEREF}\" \"$commit\" || die \"failed to update $BASEREF\"\n+\t\techo $(git rev-parse $commit)\n+\telse\n+\n+\t\tcase $state in\t\n+\t\t\tINVALID)\n+\t\t\t\tdie \"The specified reference $commit is not a valid.\"\n+\t\t\t;;\n+\t\t\tHIDDEN|UNREACHABLE)\n+\t\t\t\tclosest=$(closest $commit)\n+\t\t\t\tdescribe \"$commit\" \"$state\" 1>&2\n+\t\t\t\tif test -z \"$FORCE\"\n+\t\t\t\tthen\n+\t\t\t\t\twarn \"Updating the base of ${BRANCH} to a consistent value $(short_ref $closest).\"\n+\t\t\t\t\tgit update-ref ${BASEREF} $closest || die \"failed to update $BASEREF\"\n+\t\t\t\t\techo $closest\n+\t\t\t\t\ttrue\n+\t\t\t\telse\n+\t\t\t\t\twarn \"Updating the base of ${BRANCH} to an inconsistent value $(short_ref $commit).\"\n+\t\t\t\t\tgit update-ref ${BASEREF} $commit || die \"failed to update $BASEREF\"\n+\t\t\t\t\tfalse\n+\t\t\t\tfi\n+\t\t\t;;\n+\t\t\tCONSISTENT)\n+\t\t\t\tgit update-ref ${BASEREF} $commit || die \"failed to update $BASEREF\"\n+\t\t\t\techo $commit\n+\t\t\t\ttrue\n+\t\t\t;;\n+\t\t\t*)\n+\t\t\t\tdie \"should never happen - invalid state $state\"\n+\t\t\t;;\n+\t\tesac\n+\n+\tfi\n+}\n+\n+base_clear()\n+{\n+   git update-ref -d ${BASEREF} >/dev/null || die \"failed to clear $BASEREF\"\n+   warn \"The base of ${BRANCH} has been cleared.\"\n+   false\n+}\n+\n+base_help()\n+{\n+   git base -h \"$@\"\n+}\n+\n+VALID_BRANCH=\n+QUIET=\n+POSITIONAL=\n+FORCE=\n+DELETE=\n+\n+BRANCHREF=$(git symbolic-ref -q HEAD) || BRANCHREF=HEAD\n+BRANCH=${BRANCHREF#refs/heads/}\n+VALID_BRANCH=t\n+\n+while test $# -gt 0\n+do\n+\targ=$1\n+\tshift\n+\n+\tcase \"$arg\" in\n+\t       -b)\n+\t\t\ttest -n \"$1\" || die \"-b requires a branch to be specified\"\n+\t\t        BRANCH=$1 && shift\n+\t\t\tBRANCHREF=$(git rev-parse --quiet --symbolic-full-name --verify \"${BRANCH}\" --)\n+\t\t\tBRANCH=${BRANCHREF#refs/heads/}\n+\t\t\ttest \"${BRANCH}\" = \"${BRANCHREF}\" || VALID_BRANCH=t\n+\t\t\ttest \"${BRANCH}\" = \"HEAD\" && VALID_BRANCH=t\n+\t\t\ttest -n \"${BRANCHREF}\"  || VALID_BRANCH=\n+\t\t;;\n+\t\t-f)\n+\t\t\tFORCE=-f;\n+\t\t;;\n+                -d)\n+                        DELETE=-d;\n+                ;;\n+\t\t-q)\n+\t\t\tQUIET=-q\n+\t        ;;\n+\t\t--)\n+\t\t\tbreak;\t\t\t\n+\t\t;;\n+\t\t--as-ref)\t\n+\t\t\tASREF=--as-ref;\n+\t\t;;\t\n+\t\tdefault|check|clear|init|reset|set|help)\n+\t\t\tif test -z \"$CMD\"\n+\t\t\tthen\t\n+\t\t\t\tCMD=$arg\n+\t\t\telse\n+\t\t\t\tPOSITIONAL=\"${POSITIONAL}${POSITIONAL:+ }$arg\"\n+\t\t\tfi\n+\t\t;;\n+\t\t*)\n+\t\t\tPOSITIONAL=\"${POSITIONAL}${POSITIONAL:+ }$arg\"\n+\t        ;;\n+\tesac\n+done\n+\n+CMD=${CMD:-default}\n+\n+set -- $POSITIONAL\n+\n+if test \"${BRANCH}\" = \"HEAD\"\n+then\n+\tBASEREF=BASE\n+else\n+\tBASEREF=refs/bases/${BRANCH}\n+fi\n+\n+HEAD=$(git rev-parse --quiet --verify ${BRANCHREF})\n+BASE=$(git rev-parse --quiet --verify ${BASEREF})\n+\n+quietly base_$CMD \"$@\"\ndiff --git a/t/t3418-base.sh b/t/t3418-base.sh\nnew file mode 100755\nindex 0000000..8e2defb\n--- /dev/null\n+++ b/t/t3418-base.sh\n@@ -0,0 +1,214 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Jon Seymour\n+#\n+\n+test_description='git base tests\n+\n+Checks that git base implements its specification.\n+\n+'\n+\n+#\n+#          G\n+#         /\n+# base - A - M - C - D - master - E\n+#     \\     / \\\n+#        B     F\n+#\n+\n+. ./test-lib.sh\n+\n+GIT_AUTHOR_NAME=author@name\n+GIT_AUTHOR_EMAIL=bogus@email@address\n+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL\n+\n+ensure_initial_state()\n+{\n+    git reset --hard HEAD &&\n+    git checkout master &&\n+    git reset --hard D && {\n+        git update-ref -d refs/bases/master || true\n+    } && {\n+        git config --unset branch.master.baseresetcmd || true\n+    } &&\n+    ! git base clear\n+}\n+\n+test_expect_success 'setup' \\\n+'\n+    test_commit base &&\n+    test_commit A &&\n+    git checkout A^1 &&\n+    test_commit B &&\n+    git checkout master &&\n+    test_merge M B &&\n+    test_commit C &&\n+    test_commit D &&\n+    test_commit E &&\n+    git checkout M^0 -- &&\n+    test_commit F &&\n+    git checkout A^0 -- &&\n+    test_commit G &&\n+    git checkout master &&\n+    git reset --hard D\n+'\n+\n+test_expect_success 'empty check when base missing' \\\n+'\n+\tensure_initial_state &&\n+\t! git base clear &&\n+\t! git base check &&\n+\t! git rev-parse --verify refs/bases/master\n+'\n+\n+test_expect_success 'empty check when base hidden (A)' \\\n+'\n+\tensure_initial_state &&\n+\t! git base set -f A &&\n+\t! git base check &&\n+\ttest \"$(git base check)\" = \"\" &&\n+\tgit rev-parse --verify refs/bases/master &&\n+\ttest \"$(git rev-parse refs/bases/master)\" = \"$(git rev-parse A)\"\n+'\n+\n+test_expect_success 'empty check when base unreachable (E)' \\\n+'\n+\tensure_initial_state &&\n+\t! git base set -f E &&\n+\t! git base check &&\n+\ttest \"$(git base check)\" = \"\" &&\n+\tgit rev-parse --verify refs/bases/master &&\n+\ttest \"$(git rev-parse refs/bases/master)\" = \"$(git rev-parse E)\"\n+'\n+\n+test_expect_success 'empty check when base unreachable and hidden (G)' \\\n+'\n+\tensure_initial_state &&\n+\t! git base set -f G &&\n+\t! git base check &&\n+\ttest \"$(git base check)\" = \"\" &&\n+\tgit rev-parse --verify refs/bases/master &&\n+\ttest \"$(git rev-parse refs/bases/master)\" = \"$(git rev-parse G)\"\n+'\n+\n+test_expect_success 'empty check when base reachable and visible (M)' \\\n+'\n+\tensure_initial_state &&\n+\tgit base set -f M &&\n+\tgit base check &&\n+\ttest \"$(git base check)\" = \"$(git rev-parse M)\" &&\n+\tgit rev-parse --verify refs/bases/master &&\n+\ttest \"$(git rev-parse refs/bases/master)\" = \"$(git rev-parse M)\"\n+'\n+\n+test_expect_success 'test default commit when clear' \\\n+'\n+\tensure_initial_state &&\n+        ! git base clear &&\n+\tgit base M &&\n+\ttest \"$(git base check)\" = \"$(git rev-parse M)\"\n+'\n+\n+test_expect_success 'test default commit when set' \\\n+'\n+\tensure_initial_state &&\n+        git base set C &&\n+\tgit base M &&\n+\ttest \"$(git base check)\" = \"$(git rev-parse C)\"\n+'\n+\n+test_expect_success 'git base - base not set initially' \\\n+'\n+    ensure_initial_state &&\n+    ! git base clear &&\n+    ! git rev-parse --verify refs/bases/master &&\n+    ! git base &&\n+    ! git rev-parse --verify refs/bases/master &&\n+    test -z \"$(git base)\"\n+'\n+test_expect_success 'test default when reference stale' \\\n+'\n+    ensure_initial_state &&\n+    ! git base set -f base &&\n+    ! git base &&\n+    ! git rev-parse --verify refs/bases/master &&\n+    test -z \"$(git base)\"\n+'\n+\n+test_expect_success 'test --as-ref does not create or check reference even when empty or stale' \\\n+'\n+    ! git base clear &&\n+    test \"$(git base --as-ref)\" = \"refs/bases/master\" &&\n+    ! git base check &&\n+    ! git base set -f base &&\n+    ! git base --as-ref &&\n+    test \"$(git rev-parse refs/bases/master)\" = \"$(git rev-parse base)\"\n+'\n+\n+test_expect_success 'test set to stale reference ' \\\n+'\n+    ! git base clear &&\n+    git base set A &&\n+    test \"$(git base)\" = $(git rev-parse M) &&\n+    git base set B &&\n+    test $(git base) = $(git rev-parse M) &&\n+    git base set F &&\n+    test $(git base) = $(git rev-parse M) &&\n+    git base set G &&\n+    test $(git base) = $(git rev-parse M) &&\n+    git base set E &&\n+    test $(git base) = $(git rev-parse D)\n+'\n+\n+test_expect_success 'test set to good reference ' \\\n+'\n+    ! git base clear &&\n+    git base set C &&\n+    test $(git base) = $(git rev-parse C) &&\n+    git base set D &&\n+    test $(git base) = $(git rev-parse D)\n+'\n+\n+test_expect_success 'exit codes: ! clear && default' \\\n+'\n+    ! git base clear &&\n+    ! git base\n+'\n+\n+test_expect_success 'exit codes: ! clear && !check && ! default' \\\n+'\n+    ! git base clear &&\n+    ! git base check &&\n+    ! git base\n+'\n+\n+test_expect_success 'exit codes: ! clear && ! set && ! default' \\\n+'\n+    ! git base clear &&\n+    ! git base set &&\n+    ! git base\n+'\n+\n+test_expect_success 'test detached head' \\\n+'\n+    git checkout master^0 &&\n+    test \"$(git base --as-ref)\" = \"BASE\" &&\n+    git base set base &&\n+    git base check &&\n+    git base set M &&\n+    test \"$(git base)\" = \"$(git rev-parse M)\"\n+'\n+\n+test_expect_success 'test -b master' \\\n+'\n+    ! git base -b master clear &&\n+    git checkout master^0 &&\n+    test \"$(git base -b master --as-ref)\" = \"refs/bases/master\" &&\n+    git base -b master set base &&\n+    git base -b master check &&\n+    git base -b master set M &&\n+    test \"$(git base -b master)\" = \"$(git rev-parse M)\"\n+'\n+\n+test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166261","messageId":"1303543372-77843-24-git-send-email-jon.seymour@gmail.com","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"[PATCH 23/23] Introduce support for the git-work command.","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T07:22:52Z","receivedAt":"2011-04-23T07:22:52Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"The git work command uses the implicit base defined by git base\nto help perform dependency management on a working branch.\n\nSee the Documentation for a description of the philosophy\nunderlying git-work.\n\nSigned-off-by: Jon Seymour <jon.seymour@gmail.com>\n---\n .gitignore                 |    1 +\n Documentation/git-work.txt |  163 ++++++++++++++++++++++\n Makefile                   |    1 +\n git-work.sh                |  323 ++++++++++++++++++++++++++++++++++++++++++++\n t/t3421-work.sh            |  174 ++++++++++++++++++++++++\n 5 files changed, 662 insertions(+), 0 deletions(-)\n create mode 100644 Documentation/git-work.txt\n create mode 100644 git-work.sh\n create mode 100755 t/t3421-work.sh\n\ndiff --git a/.gitignore b/.gitignore\nindex 54fa567..5fb3119 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -159,6 +159,7 @@\n /git-verify-tag\n /git-web--browse\n /git-whatchanged\n+/git-work\n /git-write-tree\n /git-core-*/?*\n /gitk-git/gitk-wish\ndiff --git a/Documentation/git-work.txt b/Documentation/git-work.txt\nnew file mode 100644\nindex 0000000..1cae786\n--- /dev/null\n+++ b/Documentation/git-work.txt\n@@ -0,0 +1,163 @@\n+git-work(1)\n+===========\n+\n+NAME\n+----\n+git-work - manage the current branch as a working branch\n+\n+SYNOPSIS\n+--------\n+[verse]\n+'git work'\n+'git work' 'list'   ['commit'|'dependency']\n+'git work' 'merge'  [merge-options] [dependency ...]\n+'git work' 'remerge' [--keep-tree]\n+'git work' 'rebase' ['-i'|[dependency [rebase-options]]]\n+'git work' 'pivot'  [pivot]\n+'git work' 'create' ['--pivot-first'] topic [pivot [on-base]]\n+'git work' 'update' ['--pivot-first'] topic [pivot]\n+\n+DESCRIPTION\n+-----------\n+In the discussion that follows, the current branch is referred to by the symbol \\{branch\\} and the commit at the base of \\{branch\\} is referred to by the symbol \\{base\\}. The symbol \\{head\\} is used to refer to the commit at the tip of \\{branch\\}.\n+\n+COMMANDS\n+--------\n+Without arguments, reports the base and head of the current branch as a range suitable for use with git rev-list. Use --as-refs to print the range with symbolic references.\n+\n+'list'::\n+\tLists the commits in the working branch or the dependencies of the working branch.\n+\n+'merge'::\n+\tMerge the specified dependencies with \\{base\\}, then rebase \\{base\\}..\\{head\\} onto that merge. The head reference is updated with the result of the merge. The base reference is updated with dependency. If not specified, dependency defaults to the tracked branch, if any.\n+\n+'remerge'::\n+\tRebuild the base of the branch using the current dependencies. Unless --keep-tree is specified, perform a piece-wise merge of each dependency. If --keep-tree is specified, then perform an information only merge of the current dependencies and preserve the tree of existing base.\n+\n+'rebase'::\n+\tRebase \\{base\\}..\\{head\\} onto dependency and the base reference is updated to refer to dependency. If the -i option is specified, invokes git rebase -i \\{base\\}..\\{branch\\}. If not specified, dependency defaults to the tracked branch, if any.\n+\n+'pivot'::\n+\tRebase pivot..\\{head\\} onto \\{base\\} and then rebase \\{base\\}..pivot onto the result of the first rebase. The head reference of the branch is updated to refer to the result of the final rebase. The pivot argument must satisfy the base invariant of \\{branch\\}.\n+\n+'create'::\n+\n+\tRebase pivot..\\{branch\\} onto on-base and initialize the topic head reference to the resulting commit; the topic base reference to on-base and the head reference of \\{branch\\} to pivot. Then merge the topic head into the base of \\{branch\\} using git work merge. If specified, pivot must satisfy the base invariant. If not specified, on-base and pivot default to \\{base\\}.\n+\n+'update'::\n+\tPerforms the same operation as create but uses the head of an existing topic as on-base.\n+\n+Unless otherwise specified, if this command completes successfully the base reference will be updated to reflect the current calculated base for \\{branch\\}.\n+\n+OPTIONS\n+-------\n+--as-refs::\n+\tFor the default subcommand, report the SHA1 hashes as symbolic reference names, not as SHA1 hashes.\n+--pivot-first::\n+\tIf this option is specified on a create or update subcommand, a pivot operation is logically performed first, in effect allowing\n+        the commits below, rather than above, the pivot point to be moved from the current topic to the specified topic.\n+\n+ERROR HANDLING\n+--------------\n+Any rebases and merges performed by git work must succeed otherwise the working tree, index and references touched by git base are rolled back to their initial state, which must be clean to begin with.\n+\n+DISCUSSION\n+----------\n+git work is designed to support workflows where a developer's workspace perpetually contains a mixture of work items at different levels of maturity. Examples of such work items might be:\n+\n+* the upstream integration branch\n+* published topics that are yet to be integrated into the integraton branch\n+* completed, but unpublished topics\n+* published topics received from other developers that have not yet been integrated in the integration branch\n+* adhoc patches\n+\n+One way to manage such complexity is to maintain separate topic branches for each work item and then create temporary or throw-away branches to test the combined work. Such a practice has the advantage of keeping the artifacts of each work stream separate but can potentially incur significant process overheads and can be confusing for a developer to manage since at any given point in time a given work item that was recently worked on may, or may not be, integrated into the current work tree.\n+\n+From an individual developer's point of view, it can be more productive to work on a single working branch which accretes recent work and dependencies over time and only use isolated topic branches for the purposes of sharing stabilised work with others.\n+\n+The key to making this work is to ensure that dependencies are always merged into the base of the working branch, new work is done on the head of the branch, and mature work is rebased from the head of the working branch to heads of stable topic branches and then reintegrated back into the base of the working branch.\n+\n+With such a practice, the developer gains the convenience of a single working branch without sacrificing the cleanliness of topic-based development.\n+\n+git work is a porcelain that enables such work practices by providing commands that understand the concept of a branch base, how it relates to the branch head and the importance of merging dependencies into the branch base, rather than into the branch head. In particular, git work knows how to discover, establish and maintain the base of a branch as the maturity of work items merged into the branch evolves over time.\n+\n+In summary, git work allows a developer to keep his working tree stable while keeping his commit history sane.\n+\n+EXAMPLES\n+--------\n+* print the range of commits in the current working branch\n++\n+---------\n+$ git work\n+---------\n+* lists the commits in the current working branch\n++\n+---------\n+$ git work list\n+---------\n+* list the dependencies of the current working branch\n++\n+---------\n+$ git work list dependency\n+---------\n+* start gitk, showing only the current work\n++\n+---------\n+$ git work $(git work)\n+---------\n+* list the commits in the current working branch with a one line description\n++\n+---------\n+$ git rev-list --oneline $(git work)\n+---------\n+* merge the upstream/master into the base of the current branch\n++\n+---------\n+$ git work merge upstream/master\n+---------\n+* rebase the working branch onto the upstream/master\n++\n+---------\n+$ git work rebase upstream/master\n+---------\n+* create a topic branch based on upstream/master from the top 2 commits of the current branch\n++\n+---------\n+$ git work create mytopic HEAD~2 upstream/master\n+---------\n+* create a topic branch using the commits under the top 2 commits of the current branch\n++\n+---------\n+$ git work --pivot-first create mytopic HEAD~2 upstream/master\n+---------\n+* update an existing topic branch with the top commit of the current branch\n++\n+---------\n+$ git work update mytopic HEAD~1\n+---------\n+* clean up the base history by rewriting the base as an octopus merge with the same tree as the current base\n++\n+---------\n+$ git work remerge --keep-tree\n+---------\n+* visualize the merge structure of the dependencies of the current work\n++\n+---------\n+gitk $(git base) --not $(git work list dependency)\n+---------\n+\n+SEE ALSO\n+--------\n+linkgit:git-base[1]\n+\n+Author\n+------\n+Written by Jon Seymour <jon.seymour@gmail.com>\n+\n+Documentation\n+-------------\n+Documentation by Jon Seymour\n+\n+GIT\n+---\n+Part of the linkgit:git[7] suite\ndiff --git a/Makefile b/Makefile\nindex 6aee4a8..42854cb 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -380,6 +380,7 @@ SCRIPT_SH += git-stash.sh\n SCRIPT_SH += git-submodule.sh\n SCRIPT_SH += git-test.sh\n SCRIPT_SH += git-web--browse.sh\n+SCRIPT_SH += git-work.sh\n \n SCRIPT_LIB += git-atomic-lib\n SCRIPT_LIB += git-conditions-lib\ndiff --git a/git-work.sh b/git-work.sh\nnew file mode 100644\nindex 0000000..f6b378d\n--- /dev/null\n+++ b/git-work.sh\n@@ -0,0 +1,323 @@\n+#!/bin/sh\n+USAGE='[help|list|merge|remerge|rebase|pivot|create|update]'\n+LONG_USAGE='\n+git work\n+    Print the range that defines the boundaries of the branch\n+git work list [commit|dependency]\n+    List the commits or dependencies of the current working branch.\n+git work merge [dependency ...]\n+    Merge the specified dependencies into the base of the branch\n+git work remerge [--keep-tree]\n+    Rebuild the base of the current working branch from the dependencies\n+git work pivot [pivot]\n+    Swap two segments of the working branch at the commit specified by the pivot\n+git work rebase [-i]\n+    Invoke an interactive rebase on the branch\n+git work rebase [dependency]\n+    Rebase the current working branch onto the specified dependency.\n+git work create [--pivot-first] topic [pivot] [on-base]\n+    Rebase the top N commits onto new topic based on a specified commit, then merge the result into the base of the current branch\n+git work update [--pivot-first] topic [pivot]\n+    Rebase the top N commits onto an existing topic, then merge the updated topic into the base of the current branch\n+git work help\n+    Print this help\n+\n+Please use \"git help work\" to get the full man page.'\n+\n+SUBDIRECTORY_OK=true\n+OPTIONS_SPEC=\"\n+$LONG_USAGE\n+--\n+pivot-first perform a pivot first, then do..\n+as-refs print the range as references\n+X pass-thru options to merge or rebase\n+i,interactive perform an interactive rebase\n+\"\n+. git-sh-setup\n+. git-atomic-lib\n+\n+warn()\n+{\n+    echo \"warn: $*\" 1>&2\n+}\n+\n+not_implemented()\n+{\n+    die \"not yet implemented\"\n+}\n+\n+work_default()\n+{\n+   if $AS_REFS\n+   then\n+       echo $BASEREF..$BRANCH\n+   else\n+       echo $(git base)..$(git rev-parse HEAD)\n+   fi\n+}\n+\n+work_list_dependency()\n+{\n+    limits=''\n+    while true\n+    do\n+         top=$(git rev-list $BASE $limits --no-merges --max-count=1)\n+\t if test -z \"$top\"\n+         then\n+             break;\n+         else\n+\t     echo $top\n+             limits=\"$limits ^$top\"\n+         fi\n+    done\n+}\n+\n+work_list()\n+{\n+    type=$1\n+    case \"$type\" in\n+       \"\"|commit)\n+\t    git rev-list $(work_default)\n+       ;;\n+       dependency)\n+            work_list_dependency \"$@\"\n+       ;;\n+       *)\n+\t    die \"$type is not a supported type for this command\"\n+       ;;\n+    esac\n+}\n+\n+work_merge()\n+{\n+   DEPENDENCY=$1\n+   shift\n+\n+   test -n \"${DEPENDENCY}\" || die \"usage: git work merge [dependency [merge-options]]\"\n+\n+   assert --not-staged --not-unstaged --commit-exists \"${DEPENDENCY}\"\n+\n+   atomic eval \\\n+\"\n+\tDEPENDENCY=${DEPENDENCY} &&\n+\tBASE=${BASE} &&\n+\tBASEREF=${BASEREF} &&\n+\tBRANCH=${BRANCH} &&\n+\tMERGE_OPTIONS='${MERGE_OPTIONS}'\n+\tgit checkout -q \\${BASE} &&\n+\tgit merge -q \\${DEPENDENCY} \\${MERGE_OPTIONS} &&\n+\tMERGE=\\$(git rev-parse HEAD) &&\n+\tgit rebase --onto HEAD \\${BASE} \\${BRANCH} &&\n+\tgit update-ref \\${BASEREF} \\${MERGE}\n+\"\n+}\n+\n+work_remerge()\n+{\n+   assert --not-staged --not-unstaged\n+   not_yet_implemented\n+}\n+\n+work_rebase()\n+{\n+   git base -q check || die \"use 'git base' to establish a base for this branch\"\n+   if test -n \"${INTERACTIVE}\"\n+   then\n+        PIVOT=${1:-$(git base)}\n+\tgit base -q check ${PIVOT} || die \"${PIVOT} is not a valid pivot commit\"\n+\tgit rebase -i ${PIVOT} HEAD \"$@\"\n+   else\n+\n+      DEPENDENCY=$1\n+\n+      assert --not-staged --not-unstaged --commit-exists \"${DEPENDENCY}\"\n+      atomic eval \"\n+      git rebase --onto \\${DEPENDENCY} \\${BASE} \\${BRANCH} &&\n+      git update-ref \\${BASEREF} \\${DEPENDENCY}\n+\"\n+   fi\n+}\n+\n+work_create()\n+{\n+   TOPIC=$1\n+   PIVOT=$2\n+   DEPENDENCY=$3\n+\n+   test -n \"${DEPENDENCY}\" || die \"usage: git work create topic [pivot [dependency]]\"\n+\n+   PIVOT=$(git rev-parse --verify \"$PIVOT\" 2>/dev/null) || die \"$2 is not a commit\"\n+\n+   git base check \"${PIVOT}\" > /dev/null || die \"$PIVOT does not lie between $BASE and $HEAD\"\n+\n+   assert --commit-exists \"${DEPENDENCY}\" --not-branch-exists \"${TOPIC}\" --not-staged --not-unstaged\n+\n+   if $PIVOT_FIRST\n+   then\n+   atomic eval \"\n+\tBASE=${BASE} &&\n+\tTOPIC=${TOPIC} &&\n+\tPIVOT=${PIVOT} &&\n+\tHEAD=${HEAD} &&\n+\tBRANCHREF=${BRANCHREF} &&\n+\tDEPENDENCY=${DEPENDENCY} &&\n+\tgit rebase -q --onto \\${DEPENDENCY} \\${BASE} \\${PIVOT} &&\n+\tgit branch \\${TOPIC} &&\n+\tgit base -q -b \\${TOPIC} set \\${DEPENDENCY} &&\n+\tgit rebase -q --onto \\${BASE} \\${PIVOT} \\${BRANCH} &&\n+\tgit work merge \\${TOPIC}\n+\"\n+   else\n+   atomic eval \"\n+\tTOPIC=${TOPIC} &&\n+\tPIVOT=${PIVOT} &&\n+\tHEAD=${HEAD} &&\n+\tBRANCHREF=${BRANCHREF} &&\n+\tDEPENDENCY=${DEPENDENCY} &&\n+\tgit rebase -q --onto \\${DEPENDENCY} \\${PIVOT} \\${HEAD} &&\n+\tgit branch \\${TOPIC} &&\n+\tgit base -b \\${TOPIC} set \\${DEPENDENCY} &&\n+\tgit update-ref \\${BRANCHREF} \\${PIVOT} &&\n+\tgit checkout \\${BRANCH} &&\n+\tgit work merge \\${TOPIC}\n+\"\n+   fi\n+\n+}\n+\n+work_update()\n+{\n+   TOPIC=$1\n+   PIVOT=$2\n+\n+   test -n \"${PIVOT}\" || die \"usage: git work update topic [pivot [dependency]]\"\n+\n+   PIVOT=$(git rev-parse --verify \"$PIVOT\" 2>/dev/null) || die \"$2 is not a commit\"\n+\n+   git base check \"${PIVOT}\" > /dev/null || die \"$PIVOT does not lie between $BASE and $HEAD\"\n+\n+   assert --branch-exists \"${TOPIC}\" --not-staged --not-unstaged\n+\n+   if $PIVOT_FIRST\n+   then\n+   atomic eval \"\n+\tTOPIC=${TOPIC} &&\n+\tPIVOT=${PIVOT} &&\n+\tBASE=${BASE} &&\n+\tHEAD=${HEAD} &&\n+\tBRANCHREF=${BRANCHREF} &&\n+\tgit base -q -b \\${TOPIC} \\${TOPIC} &&\n+\tgit rebase -q --onto \\${TOPIC} \\${BASE} \\${PIVOT} &&\n+\tgit branch -f \\${TOPIC} &&\n+\tgit rebase -q --onto \\${BASE} \\${PIVOT} \\${BRANCH} &&\n+\tgit work merge \\${TOPIC}\n+\"\n+   else\n+   atomic eval \"\n+\tTOPIC=${TOPIC} &&\n+\tPIVOT=${PIVOT} &&\n+\tHEAD=${HEAD} &&\n+\tBRANCHREF=${BRANCHREF} &&\n+\tgit base -q -b \\${TOPIC} \\${TOPIC} &&\n+\tgit rebase -q --onto \\${TOPIC} \\${PIVOT} \\${HEAD} &&\n+\tgit branch -f \\${TOPIC} &&\n+\tgit update-ref \\${BRANCHREF} \\${PIVOT} &&\n+\tgit checkout \\${BRANCH} &&\n+\tgit work merge \\${TOPIC}\n+\"\n+   fi\n+\n+}\n+\n+work_pivot()\n+{\n+   PIVOT=$1\n+\n+   test -n \"${PIVOT}\" || die \"usage: git work update topic [pivot [dependency]]\"\n+\n+   PIVOT=$(git rev-parse --verify \"$PIVOT\" 2>/dev/null) || die \"$1 is not a commit\"\n+\n+   git base check \"${PIVOT}\" > /dev/null || die \"$PIVOT does not lie between $BASE and $HEAD\"\n+\n+   assert --not-staged --not-unstaged\n+\n+   atomic eval \"\n+      git rebase -q --onto \\${BASE} \\${PIVOT} \\${HEAD} &&\n+      git rebase -q --onto HEAD \\${BASE} \\${PIVOT} &&\n+      git update-ref \\${BRANCHREF} HEAD &&\n+      git checkout -q \\${BRANCH}\n+\"\n+}\n+\n+INTERACTIVE=\n+CURRENT_BRANCH=$(git branch | grep \"^\\* [^(]\" | cut -c3-)\n+AS_REFS=false\n+PIVOT_FIRST=false\n+if test -n \"$CURRENT_BRANCH\"\n+then\n+   BRANCHREF=refs/heads/${CURRENT_BRANCH}\n+   BASEREF=refs/bases/${CURRENT_BRANCH}\n+   BRANCH=${CURRENT_BRANCH}\n+else\n+   BRANCHREF=HEAD\n+   BASEREF=BASE\n+   BRANCH=HEAD\n+fi\n+\n+while test $# != 0\n+do\n+\tcase $1 in\n+\t\t-b)\n+\t\tshift\n+\t\tBRANCHREF=refs/heads/$1\n+\t\tBASEREF=refs/bases/$1\n+\t\tshift\n+\t\t(git rev-parse --verify $BRANCHREF 1>/dev/null) || die \"$1 is not a valid local branch\"\n+\t\t;;\n+\t\t-i)\n+\t\t\tINTERACTIVE=-i\n+\t\t\tshift\n+\t\t;;\n+\t\t--as-refs)\n+\t\tshift\n+\t\tAS_REFS=true\n+\t\t;;\n+\t\t--pivot-first)\n+\t\tshift\n+\t\tPIVOT_FIRST=true\n+\t\t;;\n+\t\t--)\n+\t\tshift\n+\t\tbreak;\n+\t\t;;\n+\t\t*)\n+\t\tbreak;\n+\t\t;;\n+\tesac\n+done\n+\n+\n+# ensure that a base is established.\n+git base -q || die \"Please use 'git base init' to initialise the reset command for this branch.\"\n+\n+HEAD=$(git rev-parse --verify ${BRANCHREF} 2>/dev/null)\n+BASE=$(git rev-parse --verify ${BASEREF} 2>/dev/null)\n+\n+test -n \"$BASE\" || die \"can't derive BASE from BASEREF=${BASEREF}\"\n+\n+case \"$#\" in\n+0)\n+    work_default \"$@\" ;;\n+*)\n+    cmd=\"$1\"\n+    shift\n+    if test $cmd = 'trace'; then set -x; cmd=$1; shift; fi\n+    case \"$cmd\" in\n+    help)\n+\t    git work -h \"$@\" ;;\n+    list|merge|remerge|rebase|pivot|create|update)\n+\t    work_$cmd \"$@\" ;;\n+    *)\n+\t    usage \"$@\" ;;\n+    esac\n+esac\ndiff --git a/t/t3421-work.sh b/t/t3421-work.sh\nnew file mode 100755\nindex 0000000..9c6ed35\n--- /dev/null\n+++ b/t/t3421-work.sh\n@@ -0,0 +1,174 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2010 Jon Seymour\n+#\n+\n+test_description='git work tests\n+\n+Performs tests on the functions of git work\n+'\n+#\n+#       H-I\n+#      /\n+# A-B-C---M-P-Q\n+#  \\     /\n+#   X-Y-Z\n+#\n+\n+. ./test-lib.sh\n+. $(git --exec-path)/git-test-lib\n+\n+GIT_AUTHOR_NAME=author@name\n+GIT_AUTHOR_EMAIL=bogus@email@address\n+export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL\n+\n+reset_test()\n+{\n+    git reset --hard HEAD &&\n+    git checkout master &&\n+    git base set M && {\n+       test_condition -q --not-branch-exists test ||\n+       git branch -D test\n+    } &&\n+    test_condition -q --not-branch-exists topic || {\n+        git base init -d -b topic &&\n+\tgit branch -D topic\n+    }\n+}\n+\n+test_expect_success 'setup' \\\n+    '\n+    test_commit A &&\n+    test_commit B &&\n+    test_commit C &&\n+    git checkout -b fork-X A &&\n+    test_commit X &&\n+    test_commit Y &&\n+    test_commit Z &&\n+    git checkout master &&\n+    test_merge M Z &&\n+    git base -q set HEAD &&\n+    test_commit P &&\n+    test_commit Q &&\n+    git checkout -b fork-H C &&\n+    test_commit H &&\n+    test_commit I &&\n+    git checkout master &&\n+    true\n+'\n+\n+test_expect_success 'work --as-refs' \\\n+'\n+   test \"$(git work --as-refs)\" = \"refs/bases/master..master\"\n+'\n+\n+test_expect_success 'work' \\\n+'\n+   test \"$(git work)\" = \"$(git rev-parse M)..$(git rev-parse Q)\"\n+'\n+\n+test_expect_success 'work list' \\\n+'\n+   test \"$(git rev-parse Q; git rev-parse P)\" = \"$(git work list)\"\n+'\n+\n+test_expect_success 'git work rebase Z <=> rebase --onto Z M..Q' \\\n+'\n+    reset_test &&\n+    git checkout -b test Q &&\n+    git base -q set M &&\n+    git work rebase Z &&\n+    test_condition --same $(git base) Z &&\n+    test_condition --checked-out test &&\n+    test \"$(git diff M Q | git patch-id)\" = \"$(git diff $(git work) | git patch-id)\"\n+'\n+\n+test_expect_success 'git work merge I <=> rebase --onto I M..Q' \\\n+'\n+    reset_test &&\n+    git checkout -b test Q &&\n+    git base -q set M &&\n+    git work merge I &&\n+    test_condition --not-same I $(git base) &&\n+    test_condition --checked-out test &&\n+    test \"$(git diff M Q | git patch-id)\" = \"$(git diff $(git work) | git patch-id)\"\n+'\n+\n+test_expect_success 'git work create topic I from P..Q' \\\n+'\n+    reset_test &&\n+    git checkout -b test Q &&\n+    git base -q set M &&\n+    git work create topic P I &&\n+    test_condition --same $(git base -b topic) I &&\n+    test_condition --checked-out test &&\n+    test \"$(git diff I topic | git patch-id)\" = \"$(git diff P Q | git patch-id)\" &&\n+    test \"$(git diff $(git base) test | git patch-id)\" = \"$(git diff M P | git patch-id)\"\n+'\n+\n+test_expect_success 'git work create topic --pivot-first I from P..Q' \\\n+'\n+    reset_test &&\n+    git checkout -b test Q &&\n+    git base -q set M &&\n+    git work create --pivot-first topic P I &&\n+    test_condition  --same $(git base -b topic) I &&\n+    test_condition  --checked-out test &&\n+    test \"$(git diff I topic | git patch-id)\" = \"$(git diff M P | git patch-id)\" &&\n+    test \"$(git diff $(git base)  test | git patch-id)\" = \"$(git diff P Q | git patch-id)\"\n+'\n+\n+test_expect_success 'git work update topic I from P..Q' \\\n+'\n+    reset_test &&\n+    git checkout -b test Q &&\n+    git base -q set M &&\n+    git branch topic I &&\n+    git work update topic P &&\n+    test_condition  --same \"$(git base -b topic)\" I &&\n+    test_condition  --checked-out test &&\n+    test \"$(git diff I topic | git patch-id)\" = \"$(git diff P Q | git patch-id)\" &&\n+    test \"$(git diff $(git base)  test | git patch-id)\" = \"$(git diff M P | git patch-id)\"\n+'\n+\n+test_expect_success 'git work update --pivot-first topic I from P..Q' \\\n+'\n+    reset_test &&\n+    git checkout -b test Q &&\n+    git base -q set M &&\n+    git branch topic I &&\n+    git work update --pivot-first topic P &&\n+    test_condition  --same \"$(git base -b topic)\" I &&\n+    test_condition  --checked-out test &&\n+    test \"$(git diff I topic | git patch-id)\" = \"$(git diff M P | git patch-id)\" &&\n+    test \"$(git diff $(git base)  test | git patch-id)\" = \"$(git diff P Q | git patch-id)\"\n+'\n+\n+test_expect_success 'git work update topic I from P..Q - unitialized topic' \\\n+'\n+    git base init -d topic &&\n+    reset_test &&\n+    git checkout -b test Q &&\n+    git base -q set M &&\n+    git branch topic I &&\n+    git work update topic P &&\n+    test_condition  --same \"$(git base -b topic)\" I &&\n+    test_condition  --checked-out test &&\n+    test \"$(git diff I topic | git patch-id)\" = \"$(git diff P Q | git patch-id)\" &&\n+    test \"$(git diff $(git base)  test | git patch-id)\" = \"$(git diff M P | git patch-id)\"\n+'\n+\n+test_expect_success 'git work pivot P' \\\n+'\n+    reset_test &&\n+    git checkout -b test Q &&\n+    git base -q set M &&\n+    git work pivot P &&\n+    test_condition  --same \"$(git base -b test)\" M &&\n+    test_condition  --checked-out test &&\n+    test \"$(git diff M test | git patch-id)\" = \"$(git diff M Q | git patch-id)\" &&\n+    test \"$(git diff M test~1 | git patch-id)\" = \"$(git diff P Q | git patch-id)\" &&\n+    test \"$(git diff test~1 test | git patch-id)\" = \"$(git diff M P | git patch-id)\"\n+'\n+\n+test_done\n-- \n1.7.5.rc1.23.g7f622\n"},{"id":"166263","messageId":"20110423091300.GC9206@m62s10.vlinux.de","threadId":"27164","inReplyTo":"1303543372-77843-1-git-send-email-jon.seymour@gmail.com","subject":"Re: [PATCH 00/23] RFC: Introducing git-test, git-atomic, git-base and git-work","fromName":"Peter Baumann","fromEmail":"waste.manager@gmx.de","sentAt":"2011-04-23T09:13:00Z","receivedAt":"2011-04-23T09:13:00Z","isPatch":true,"sender":{"key":"waste.manager@gmx.de","avatar":null},"body":"On Sat, Apr 23, 2011 at 05:22:29PM +1000, Jon Seymour wrote:\n> This series is posted in order to solicit feedback about some commands I'd like to propose for inclusion in git. \n> \n> The commands are:\n>    git test\n>    git atomic\n>    git base\n>    git work\n> \n> git work\n> ========\n> \n> git work depends on the other 3 commands. \n> \n> git work is the command I have been using every day myself for the last 8 months. It is the primary means\n> I use to manage my working tree.\n> \n> The basic idea of git work is to help manage a private working tree using the following principles:\n> \n> * all dependencies received from others are merged into the 'base' of the working tree\n> \n>   git work merge some-dependency\n> \n>      - merges some-dependency into $(git base) producing (1)\n>      - rebases $(git base)..HEAD onto (1) producing (2)\n>      - resets the current branch to (2)\n>      - updates $(git base) to refer to (1)\n> \n> * all unpublished work is managed as a linear sequence of commits on top of the 'base' of the working tree\n> \n>   so, \n>    git work --as-refs # shows you a symbolic range for your current work\n>    git work # shows you a SHA1 range for your current work\n>    gitk $(git work) # shows you your unpublished work\n>    git diff $(git work) # shows you the diff of your current work\n> \n> * prior to publishing work, you rebase it onto an appropriate base commit\n> \n>   so,\n> \n>   git branch topic upstream/master # choose the base commit for the topic\n>   git work update topic HEAD~3     # pull the top 3 commits off the working tree onto the topic\n> \n>     - rebases HEAD~3..HEAD onto topic to produce (1)\n>     - merges topic into $(git base) to produce (2)\n>     - rebases $(git base)..HEAD~3 onto (2) to produce (3)\n>     - resets the current branch to (3)\n>     - resets $(git base) to (2)\n> \n> The nice thing about managing your work tree this way is that your working tree remains\n> relatively stable (it always contains everything you have recently been working on)\n> and your topic branches remain clean (i.e. they never contain any other cruft from your \n> working tree).\n\nReading this I have to admit I don't fully grok it, but it *sounds* like a solution\nto my problematic workflow. Perhaps you can supply some examples, because reading \nthe unit tests for git work hasn't enlightened me.\n\nOk, let me explain what I am aiming for:\n\nI am stuck with git-svn (our customer is not yet ready to use git) and therefore have\na remote branch remotes/trunk tracking the trunk in SVN. Then there is at least one\ndebug branch which contains several commits only used to debug problems or to speed\nup testing (e.g. avoid popping up error dialogs that some hardware is missing on one\nof our test platforms, or to add more logging statements not suitable for the mainline).\nFurthermore, there are several feature branches, lets call them topic{1, ...N}\n\n\n               . -  q - q     <- topic3\n              /\n           . -s - s - s       <- topic2\n          /\n         /    t - t - t       <- topic1\n        /   / \n       /   /\n   o - o - o - o - o - o - o  <- remotes/trunk, master\n                \\\n                  d  - d      <- debug \n           \n\n\nIf I want to deploy the software onto our target machine, I want to have\nthe code of several branches merged/combined to test them in integration.\nIf I suspect problems in one of the topics, I at least must have the \ntopicX, debug and the upstream branch remotes/trunk.\n \nSo my workflow would have been - if it weren't clumsy - the following\n\n  git checkout master\n  git merge debug topic1 topic2 topic3\n  ... compile ... deploy ... test\n\n               . -  q - q -.       <- topic3\n              /             \\\n           . -s - s - s ---. \\     <- topic2\n          /                 \\|\n         /    t - t - t - - -\\     <- topic1\n        /   /                 m    <- master\n       /   /                / | \n   o - o - o - o - o - o - o  |    <- remotes/trunk\n                \\            /\n                  d  - d - -       <- debug \n \nHaving fond a small error, I can't commit it directly on the merge m, because\nIt is actually a fix a topic branch. Running git checkout topicX before doing\nany change is also not an option, because I often forget to checkout the topic\nbefore commiting. And rebasing afterwards is also not that easy, because there\nis merge m in between.  So this isn't what I actually use, but would be *iff*\nthere where some sort of tool support to help me. \n\nWhat I end up doing is the following:\n\n- o - o   <- remotes/trunk\n       \\\n         d - d   <- debug\n              \\\n                t - t -t  <- topic1\n                        \\\n                          s - s -s  <- topic2\n                                  \\\n                                   q - q   <- topic3, master\n                                        \n                                        \nThe topic branches are just there for illustration, I normally do not track\nthose explicitly with git branches, as master is rebase often on top of trunk,\nthanks to git-svn. Now if I do want to commit a topic branch to SVN, I rebase\nso that my history is now   trunk - topic1 - debug - topic2 - topic3, master\nand then run git checkout topic1; git svn dcommit to make sure only the commits\nin topic1 are commited.\n\nAll in all, this workflow is not perfect, but at least it sort of works.\n\nNow back to my initial question: \nCould your new \"git work\" command help me to adjust my workflow and ease the pain?\n\nGreetings,\nPeter\n\n\n> git base\n> ========\n> \n> git base is a command that is heavily relied on by git work, and is occasionally used by\n> the user to reset the base of their working tree.\n> \n> git base tries to automagically maintain the base of the working tree by maintaining\n> an invariant that the path between the base and the tip of the current branch should\n> never traverse a merge. If it ever finds the invariant violated, it calls 'git base reset'\n> to attempt to restore the invariant. \n> \n> For more information about git base, refer to the Documentation/git-base.txt\n> \n> git atomic\n> ==========\n> git atomic is more an experiment than anything else. The idea is to run another git operation \"atomically\".\n> The git operation either succeeds or it fails. If it fails, git branch attempts to restore the\n> working tree and current branch to the state they were in to their original state prior to the comamnd being run.\n> \n> The reason I need something like this is that git work performs several operations in sequence some\n> of which I can't guarantee will work. I don't want the user to work out what they have to do\n> to recover, so I try to restore to the state they were originally in.\n> \n> Note: the current implementation doesn't handle rebase failures properly and would probably needed\n> to be cleaned up a little before being accepted into the mainline.\n> \n> git test\n> ========\n> This is another experiment. The idea is to provide a uniform way to test for various conditions\n> into the working tree, index and repo. For example:\n> \n>     git test --not-unstaged --branch-exists foobar\n> \n> will fail if there are unstaged files in the working tree or the branch foobar does not exist.\n> \n> As I say, git atomic and git test are somewhat experimental. I don't really care about those commands\n> and if the consensus is that git doesn't need them, I am happy to rework git base and git work\n> to not use them.\n> \n> However, I would like to propose git base and git work as being very useful additions to the git toolset.\n> \n> Let me know if the consensus is that I should proceed with a submission and I will prepare one.\n> \n> Jon Seymour (23):\n>   Introduce git-test.sh and git-test-lib.sh\n>   Introduce --unstaged.\n>   Introduce --staged\n>   Introduce --untracked.\n>   Introduce --conflicted\n>   Introduce --rebasing\n>   Introduce --detached\n>   Introduce --branch-exists\n>   Introduce --tag-exists\n>   Introduce --ref-exists\n>   Introduce --commit-exists.\n>   Introduce --checked-out\n>   Introduce --reachable\n>   Introduce --tree-same.\n>   Introduce --same\n>   misc\n>   whitespace fix.\n>   tests --conflicted.\n>   rebasing: add tests\n>   test: git test cleanups.\n>   Introduce git-atomic.\n>   Introduce git base.\n>   Introduce support for the git-work command.\n> \n>  .gitignore                   |    7 +\n>  Documentation/config.txt     |   10 +\n>  Documentation/git-atomic.txt |   92 ++++++++\n>  Documentation/git-base.txt   |  216 ++++++++++++++++++\n>  Documentation/git-test.txt   |  182 +++++++++++++++\n>  Documentation/git-work.txt   |  163 ++++++++++++++\n>  Makefile                     |    7 +\n>  git-atomic-lib.sh            |   58 +++++\n>  git-atomic.sh                |    5 +\n>  git-base.sh                  |  378 +++++++++++++++++++++++++++++++\n>  git-conditions-lib.sh        |  176 +++++++++++++++\n>  git-test-lib.sh              |  188 ++++++++++++++++\n>  git-test.sh                  |   11 +\n>  git-work.sh                  |  323 +++++++++++++++++++++++++++\n>  t/t1520-test.sh              |  506 ++++++++++++++++++++++++++++++++++++++++++\n>  t/t3418-base.sh              |  214 ++++++++++++++++++\n>  t/t3419-atomic.sh            |   59 +++++\n>  t/t3421-work.sh              |  174 +++++++++++++++\n>  18 files changed, 2769 insertions(+), 0 deletions(-)\n>  create mode 100644 Documentation/git-atomic.txt\n>  create mode 100644 Documentation/git-base.txt\n>  create mode 100644 Documentation/git-test.txt\n>  create mode 100644 Documentation/git-work.txt\n>  create mode 100644 git-atomic-lib.sh\n>  create mode 100755 git-atomic.sh\n>  create mode 100644 git-base.sh\n>  create mode 100644 git-conditions-lib.sh\n>  create mode 100644 git-test-lib.sh\n>  create mode 100755 git-test.sh\n>  create mode 100644 git-work.sh\n>  create mode 100755 t/t1520-test.sh\n>  create mode 100755 t/t3418-base.sh\n>  create mode 100755 t/t3419-atomic.sh\n>  create mode 100755 t/t3421-work.sh\n> \n> -- \n> 1.7.5.rc1.23.g7f622\n> \n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n> \n"},{"id":"166270","messageId":"BANLkTinhjMtNc257NnOCZe6askr2i=4g6Q@mail.gmail.com","threadId":"27164","inReplyTo":"20110423091300.GC9206@m62s10.vlinux.de","subject":"Re: [PATCH 00/23] RFC: Introducing git-test, git-atomic, git-base and git-work","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T17:16:33Z","receivedAt":"2011-04-23T17:16:33Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"On Saturday, April 23, 2011, Peter Baumann <waste.manager@gmx.de> wrote:\n> On Sat, Apr 23, 2011 at 05:22:29PM +1000, Jon Seymour\n>   git checkout master\n>   git merge debug topic1 topic2 topic3\n>   ... compile ... deploy ... test\n>\n>                . -  q - q -.       <- topic3\n>               /             \\\n>            . -s - s - s ---. \\     <- topic2\n>           /                 \\|\n>          /    t - t - t - - -\\     <- topic1\n>         /   /                 m    <- master\n>        /   /                / |\n>    o - o - o - o - o - o - o  |    <- remotes/trunk\n>                 \\            /\n>                   d  - d - -       <- debug\n>\n> Having fond a small error, I can't commit it directly on the merge m, because\n> It is actually a fix a topic branch. Running git checkout topicX before doing\n> any change is also not an option, because I often forget to checkout the topic\n> before commiting. And rebasing afterwards is also not that easy, because there\n> is merge m in between.  So this isn't what I actually use, but would be *iff*\n> there where some sort of tool support to help me.\n>\n> What I end up doing is the following:\n>\n> - o - o   <- remotes/trunk\n>        \\\n>          d - d   <- debug\n>               \\\n>                 t - t -t  <- topic1\n>                         \\\n>                           s - s -s  <- topic2\n>                                   \\\n>                                    q - q   <- topic3, master\n>\n>\n> The topic branches are just there for illustration, I normally do not track\n> those explicitly with git branches, as master is rebase often on top of trunk,\n> thanks to git-svn. Now if I do want to commit a topic branch to SVN, I rebase\n> so that my history is now   trunk - topic1 - debug - topic2 - topic3, master\n> and then run git checkout topic1; git svn dcommit to make sure only the commits\n> in topic1 are commited.\n>\n> All in all, this workflow is not perfect, but at least it sort of works.\n>\n> Now back to my initial question:\n> Could your new \"git work\" command help me to adjust my workflow and ease the pain?\n>\n> > Peter\n>\n\nHi Peter,\n\nYes, git work is good for this use case.\n\nYou would tend to manage you master branch as in your first diagram above.\n\nSuppose you found a problem on topicX. So, you fix the issue inplace\non the top of your master that includes all 5 dependencies. Call this\ncommit m'.\n\nYou have decided that the fix really belongs on topic2. So what you do is:\n\ngit work update topic2 HEAD~1\n\nThis will move that commit onto the tip of topic2 (producing topic2'),\nmerge that into m (producing m''), and reset master to m''.\n\nOne thing to note is that you never share master. It will be a merge\nhell. You only ever share topics when they become stable.\n\nAs the trunk gets updated you do:\n\ngit work merge remotes/trunk\n\nIt will merge remotes/trunk into the base of your unpublished work,\nthen rebase your unpublished work onto that merge.\n\ngit work is also excellent for keeping your debug branch available in\nyour working tree but isolated from your topics.\n\nThings get little tricker when you have a commit that spans multiple\ntopics. However, there is a straight forward methodology for managing\nsuch issues. There are also techniques for dealing with conflicts with\nany dependency, especially remotes/trunk which I can explain in\nanother note if there is interest.\n\nJon.\n"},{"id":"166271","messageId":"BANLkTi=UDYT1hjpFT0LcgL6XvAGtTXMBLw@mail.gmail.com","threadId":"27164","inReplyTo":"BANLkTinhjMtNc257NnOCZe6askr2i=4g6Q@mail.gmail.com","subject":"Re: [PATCH 00/23] RFC: Introducing git-test, git-atomic, git-base and git-work","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-23T18:37:05Z","receivedAt":"2011-04-23T18:37:05Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"BTW anyone interested in trying git work can fetch the tag containing\nthis series from my github fork\n\nWeb link here:\n\nhttps://github.com/jonseymour/git/tree/work-20110423\n\ngit://github.com/jonseymour/git, tag  work-20110423\n\njon.\n"},{"id":"166279","messageId":"BANLkTi=K_Mb-+-5DOE3o6m7pQsQcH0qz-g@mail.gmail.com","threadId":"27164","inReplyTo":"BANLkTi=UDYT1hjpFT0LcgL6XvAGtTXMBLw@mail.gmail.com","subject":"Re: [PATCH 00/23] RFC: Introducing git-test, git-atomic, git-base and git-work","fromName":"Jon Seymour","fromEmail":"jon.seymour@gmail.com","sentAt":"2011-04-24T15:47:28Z","receivedAt":"2011-04-24T15:47:28Z","isPatch":true,"sender":{"key":"jon.seymour@gmail.com","avatar":"https://avatars.githubusercontent.com/u/207131?v=4"},"body":"Also, anyone interested in browsing the HTML documentation for these\ncommands can read:\n\n   https://s3.amazonaws.com/jonseymour/git-work.html\n   https://s3.amazonaws.com/jonseymour/git-base.html\n   https://s3.amazonaws.com/jonseymour/git-atomic.html\n   https://s3.amazonaws.com/jonseymour/git-test.html\n\njon.\n\nOn Sun, Apr 24, 2011 at 4:37 AM, Jon Seymour <jon.seymour@gmail.com> wrote:\n> BTW anyone interested in trying git work can fetch the tag containing\n> this series from my github fork\n>\n> Web link here:\n>\n> https://github.com/jonseymour/git/tree/work-20110423\n>\n> git://github.com/jonseymour/git, tag  work-20110423\n>\n> jon.\n>\n"},{"id":"166543","messageId":"1303931471.25134.54.camel@drew-northup.unet.maine.edu","threadId":"27164","inReplyTo":"1303543372-77843-2-git-send-email-jon.seymour@gmail.com","subject":"Re: [PATCH 01/23] Introduce git-test.sh and git-test-lib.sh","fromName":"Drew Northup","fromEmail":"drew.northup@maine.edu","sentAt":"2011-04-27T19:11:11Z","receivedAt":"2011-04-27T19:11:11Z","isPatch":true,"sender":{"key":"drew.northup@maine.edu","avatar":"https://avatars.githubusercontent.com/u/18331571?v=4"},"body":"On Sat, 2011-04-23 at 17:22 +1000, Jon Seymour wrote:\n> This command is intended provide a uniform command line interface\n> to a suite of assertions that can be made about the state of\n> the working tree, index and repository.\n> \n> This commit introduces the core assert infrastructure. Subsequent\n> commits will introduce check functions that extend the infrastructure\n> in a modular way with additional tests.\n.....\n> +'test_condition' [--<condition> [ arg ... ]] ...\n> +'require_condition_libs'\n> +\n> +\n> +DESCRIPTION\n> +-----------\n> +`git test` provides a uniform, extensible API for evaluating various conditions that\n> +pertain to the state of a git working tree, index and repository.\n> +\n> +Specified conditions are evaluated from left to right. If any condition evaluates to false, \n> +the command conditionally prints a diagnostic message to stderr and sets a \n> +non-zero status code. Otherwise, sets a status code of zero. \n> +\n> +The message used to report a assertion failure may be overidden by specifying the --message option.\n> +\n> +Diagnostic output resulting from an assertion failure may be suppressed with the -q option. \n> +Note that the -q option does not suppress diagnostic output that results from the failure to \n> +successfully parse the arguments that configure the test API.\n.....\nIs this supposed to be a porcelain or plumbing? \n\nThe name could probably be better to avoid confusion with the \"unit\ntesting\" code in t/.\n\nAdditionally, I'd like to know why this shouldn't be implemented as some\nsort of \n'git status --assert=\"XXXX\"' \ncall...\n\n-- \n-Drew Northup\n________________________________________________\n\"As opposed to vegetable or mineral error?\"\n-John Pescatore, SANS NewsBites Vol. 12 Num. 59\n"}]}