[
  {
    "path": ".gitignore",
    "content": "*.gem\n.bundle\nGemfile.lock\npkg/*\n*.swp\n.rvmrc\ngit_hug/*\n.profile.yml"
  },
  {
    "path": ".rspec",
    "content": "--color\n--order default\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: ruby\ndist: focal\nrvm:\n  - 2.4.10\n  - 2.5.9\n  - 2.6.8\n  - 2.7.4\n  - 3.0.1\n  - ruby-head\n\nmatrix:\n  allow_failures:\n    - rvm: ruby-head\n\nbefore_install:\n  - gem update --system\n  - git config --global user.email 'user@example.com'\n  - git config --global user.name 'Test User'\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM ruby:slim\n\nLABEL org.opencontainers.image.authors=\"diraneyya@ip.rwth-aachen.de\"\n\n# This is in order to have the man pages during exercises\nRUN sed -i '/path-exclude \\/usr\\/share\\/man/d' /etc/dpkg/dpkg.cfg.d/docker\nRUN sed -i '/path-exclude \\/usr\\/share\\/groff/d' /etc/dpkg/dpkg.cfg.d/docker\nRUN apt update && apt install -y man git && apt install --reinstall coreutils\n\n# The DATA_PATH is used for the deliverables, or the submissible\n# content for the classroom activity.\nENV DATA_PATH=\"/data\"\n# The REPO_PATH is where the original or the teacher's forked repo\n# resides inside the container.\nENV REPO_PATH=\"/root/githug\"\n# The LEVEL_PATH is where the current challenge resides and where\n# students should navigate prior to attemping to use the `githug`\n# commands.\nENV LEVEL_PATH=\"/git_hug\"\nENV GITHUG_GITCONF=\"/root/.gitconfig\"\nENV GITHUG_PROFILE=\"$LEVEL_PATH/.profile.yml\"\nENV GITHUG_HISTORY_OUTPUT=\"$DATA_PATH/history.txt\"\nENV GITHUG_PROFILE_OUTPUT=\"$DATA_PATH/profile.yml\"\nENV GITHUG_GITCONF_OUTPUT=\"$DATA_PATH/gitconfig\"\n\nRUN mkdir -p $DATA_PATH\nADD . $REPO_PATH\nWORKDIR $REPO_PATH\nRUN gem build\nRUN gem install *.gem\n\nWORKDIR /\nRUN echo \"y\" | $GEM_HOME/bin/githug\nRUN cp $GITHUG_PROFILE $GITHUG_PROFILE_OUTPUT\n\nWORKDIR $LEVEL_PATH\n\n# The bash login script in below clears the history and restores\n# progress using the contents of the $GITHUG_PROFILE_OUTPUT file.\nRUN printf \"history -c\\nHISTSIZE= \\nHISTFILESIZE= \\n\\\necho '--- NEW SESSION ---' >> $GITHUG_HISTORY_OUTPUT \\n\\\nif ! [ -e $GITHUG_PROFILE_OUTPUT ]; then \\n\\\n    echo 'ERROR: Corrupt level progress data. Exiting.' \\n\\\n    echo '>>> CORRUPT DATA <<<' >> $GITHUG_HISTORY_OUTPUT \\n\\\n    exit 1; fi \\n\\\ncp $GITHUG_GITCONF_OUTPUT $GITHUG_GITCONF \\n\\\nmkdir -p $LEVEL_PATH && cp $GITHUG_PROFILE_OUTPUT $GITHUG_PROFILE \\n\\\nexport PATH=\\\"\\$GEM_HOME/bin:\\$PATH\\\" \\n\\\ncd $LEVEL_PATH && githug reset \\n\\\necho -e '\\nIMPORTANT: everything you type in this container is \\\nrecorded to assist in the grading process.' \\n\" >> ~/.bash_profile\n\nRUN printf \"history -a\\ncat \\$HISTFILE >> $GITHUG_HISTORY_OUTPUT \\n\\\ncp $GITHUG_PROFILE $GITHUG_PROFILE_OUTPUT \\n\\\ncp $GITHUG_GITCONF $GITHUG_GITCONF_OUTPUT 2>/dev/null \\n\\\ncp $GITHUG_PROFILE $GITHUG_PROFILE_OUTPUT \\n\" >> ~/.bash_logout \n\nENTRYPOINT [\"/bin/bash\", \"--login\"]\nVOLUME $DATA_PATH\n"
  },
  {
    "path": "Gemfile",
    "content": "source \"http://rubygems.org\"\n\n# Specify your gem's dependencies in githug.gemspec\ngemspec\n"
  },
  {
    "path": "LICENCE.txt",
    "content": "Copyright (c) 2013  Gary 'Gazler' Rennie\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# Githug\nGit Your Game On \n\n[![Build Status](https://travis-ci.org/Gazler/githug.svg?branch=master)](https://travis-ci.org/Gazler/githug) [![Code Climate](https://codeclimate.com/github/Gazler/githug.svg)](https://codeclimate.com/github/Gazler/githug)\n\n## About\nGithug is designed to give you a practical way of learning git.  It has a series of levels, each requiring you to use git commands to arrive at a correct answer.\n\n## Playing Githug\n\nGithug should work on Linux, OS X and Windows.\n\n### Prerequisites\n\nGithug requires Ruby 1.8.7 or higher.\n\nYou can check which version of Ruby is installed with the following command:\n\n```\nruby --version\n```\n\nIf ruby is not installed, follow the installation instructions on [ruby-lang.org](https://www.ruby-lang.org/en/documentation/installation/).\n\n### Installation\n\nTo install Githug, run\n\n    gem install githug\n\nIf you get a complaint about permissions, you can rerun the command with `sudo`:\n\n    sudo gem install githug\n    \n#### Usage with Docker\n\nAn unofficial _Docker_ image for this project by [@odiraneyya](https://github.com/odiraneyya) is available on ([Docker Hub](https://hub.docker.com/r/orwa84/githug)).\n\n### Starting the Game\n\nAfter the gem is installed change directory to the location where you want the game-related assets to be stored.\nThen run `githug`:\n\n    githug\n\nYou will be prompted to create a directory.\n\n    No githug directory found, do you wish to create one? [yn]\n\nType `y` (yes) to continue, `n` (no) to cancel and quit Githug.\n\n### Commands\n\nGithug has 4 game commands:\n\n * play - The default command, checks your solution for the current level\n * hint - Gives you a hint (if available) for the current level\n * reset - Reset the current level or reset the level to a given name or path\n * levels - List all the levels\n\n## Change Log\n\nThe change log is available on the wiki.  [Change log](https://github.com/Gazler/githug/wiki/Change-Log)\n\n## Contributing\n\nTo suggest a level or create a level that has been suggested, check out [the wiki](https://github.com/Gazler/githug/wiki).\n\n Get yourself on the [contributors list](https://github.com/Gazler/githug/contributors) by doing the following:\n\n * Fork the repository\n * Make a level in the levels directory (covered below)\n * Add your level to the LEVELS array inside `lib/githug/level.rb` in a position that makes sense (the \"commit\" level after the \"add\" and \"init\" levels for example)\n * Make sure your level works (covered below)\n * Submit a pull request\n\n## Todo List\n\n * A follow-up to the level, more information on a specific command, etc.\n * More levels!\n\n## Writing Levels\n\nGithug has a DSL for writing levels. Here is an example:\n\n```ruby\ndifficulty 1\ndescription \"There is a file in your folder called README, you should add it to your staging area\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"README\")\nend\n\nsolution do\n  return false unless repo.status.files.keys.include?(\"README\")\n  return false if repo.status.files[\"README\"].untracked\n\n  true\nend\n\nhint do\n  puts \"You can type `git` in your shell to get a list of available git commands\"\nend\n```\n\n `difficulty`, `description` and `solution` are required.\n\nYou can include multiple hints like this:\n\n```ruby\nhints [\n  \"You can type `git` in your shell to get a list of available git commands\",\n  \"Check the man for `git add`\"]\n```\n\n By default, `setup` will remove all files from the game folder.  You do not need to include a setup method if you don't want an initial git repository (if you are testing `git init` or only checking an answer.)\n\n You can call `repo.init` to initialize an empty repository.\n\n All methods called on `repo` are sent to the [grit gem](https://github.com/mojombo/grit) if the method does not exist, and you can use that for most git related commands (`repo.add`, `repo.commit`, etc.).\n\nAnother method exists called `init_from_level` and it is used like so:\n\n```ruby\nsetup do\n  init_from_level\nend\n```\n\nThis will copy the contents of a repository specified in the levels folder for your level.  For example, if your level is called \"merge\" then it will copy the contents of the \"merge\" folder.  It is recommended that you perform the following steps:\n\n * mkdir \"yourlevel\"\n * cd \"yourlevel\"\n * git init\n * some git stuff\n * **important** rename \".git\" to \".githug\" so that it isn't treated as a submodule\n * cd \"../\"\n * git add \"yourlevel\"\n\nAfter doing this, your level should be able to copy the contents from that git repository and use those for your level.  See the \"blame\" level for an example of this.\n\n## Testing Levels\n\nThe easiest way to test a level is:\n\n * Change into your git_hug repository\n * Run `githug reset PATH_TO_YOUR_LEVEL`\n * Solve the level\n * Run `githug test PATH_TO_YOUR_LEVEL`\n\nPlease note that the `githug test` command can be run as `githug test --errors` to get an error stack trace from your solve method.\n\nIt would be ideal if you add an integration test for your level.  These tests live in `spec/githug_spec` and **must** be run in order.  If you add a level but do not add a test, please add a simple `skip_level` test case similar to the `contribute` level.\n\n## FAQs\n\n1. Answers are not being checked properly\n\n   *This is a common issue we are facing and we are actively working to fix it.*\n\n   For now, run the following commands to change the default branch name to master. This should fix most of the issues you may face.\n   ```\n   $ git config --global init.defaultBranch master\n   $ githug reset\n   ```\n\n   From the current level forward, the default branch will be `master`.\n\n2. `githug` command doesn't work\n\n    Githug currently isn't supported on ruby versions ^3.0.0. Use any ruby version below 3.0.0 (preferrably 2.7.1).\n\n    If you use rvm, execute the below commands\n\n    ```\n    $ rvm install 2.7.1\n    $ rvm use 2.7.1\n    ```\n\n    If you use rbenv, execute the below commands\n\n    ```\n    $ rbenv install 2.7.1\n    $ rbenv global 2.7.1\n    ```\n\n"
  },
  {
    "path": "Rakefile",
    "content": "require \"bundler/gem_tasks\"\nrequire 'rspec/core/rake_task'\n\nRSpec::Core::RakeTask.new(:spec)\n\ntask :default => [:spec]\n"
  },
  {
    "path": "bin/githug",
    "content": "#!/usr/bin/env ruby\nrequire 'githug/cli'\n\nGithug::CLI.start\n"
  },
  {
    "path": "githug.gemspec",
    "content": "# -*- encoding: utf-8 -*-\n$:.push File.expand_path(\"../lib\", __FILE__)\nrequire \"githug/version\"\n\nGem::Specification.new do |s|\n  s.name        = \"githug\"\n  s.version     = Githug::VERSION\n  s.authors     = [\"Gary Rennie\"]\n  s.email       = [\"gazler@gmail.com\"]\n  s.homepage    = \"https://github.com/Gazler/githug\"\n  s.summary     = %q{An interactive way to learn git.}\n  s.description = %q{An interactive way to learn git.}\n\n  s.rubyforge_project = \"githug\"\n\n  s.files         = `git ls-files`.split(\"\\n\")\n  s.test_files    = `git ls-files -- {test,spec,features}/*`.split(\"\\n\")\n  s.executables   = `git ls-files -- bin/*`.split(\"\\n\").map{ |f| File.basename(f) }\n  s.require_paths = [\"lib\"]\n\n  # specify any dependencies here; for example:\n  s.add_development_dependency \"rspec\", \"~>2.8.0\"\n\n  s.add_dependency \"grit\", \"~>2.3.0\"\n  s.add_dependency \"thor\", \"~>0.14.6\"\n  s.add_dependency \"rake\", \"<11\"\nend\n"
  },
  {
    "path": "levels/add.rb",
    "content": "difficulty 1\ndescription \"There is a file in your folder called `README`; add it to your staging area.\nNote: Each level starts with a new repo. Don't look for files of the previous one.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"README\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false unless repo.status.files.keys.include?(\"README\")\n  return false if repo.status.files[\"README\"].untracked\n  true\nend\n\nhint do\n  puts \"You can type `git` in your shell to get a list of available git commands.\"\nend\n"
  },
  {
    "path": "levels/bisect/.githug/COMMIT_EDITMSG",
    "content": "Another Commit\n"
  },
  {
    "path": "levels/bisect/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/bisect/.githug/ORIG_HEAD",
    "content": "f351ca63a759f56bb26924fd566294eb23455c71\n"
  },
  {
    "path": "levels/bisect/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n[pack]\n\tdeltaCacheSize = 1\n"
  },
  {
    "path": "levels/bisect/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/bisect/.githug/gitk.cache",
    "content": "1 1\nf351ca63a759f56bb26924fd566294eb23455c71 f608824888b83bbedc1f658be7496ffea467a8fb {7f8406e742c5281ec2c9bc3c6cc69dc6ba5311fd 36da01fb0571360b0e1170f0cb46e74f72927cda c1b80f5ed4995cda8e19027f0776351af6b76703 4145057e11ab90109c95882b1f40d560da408bfa 94e162b505bc2290fb67764357a625192f8a4e8a bfb16eec1081387b586dc8009ef422cfff60b622 c0a1cdff8dd63948a5fc41f5871681c5b133c053 e8dd08d5aabb62b56c80320672cd8d42e2b8954d 89d27c5f4680cd21028947be75c33a68511decb9 98d617d3bdf0b6c59d6177233acb4b9fd54b7aac 0b194f30d6867522d1666590087e6c0b5e20dc93 d406b1b8bc269c9aef9127299e83947220e7a9b0 f608824888b83bbedc1f658be7496ffea467a8fb}\n1\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/post-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script that is called after a successful\n# commit is made.\n#\n# To enable this hook, rename this file to \"post-commit\".\n\n: Nothing\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/post-receive.sample",
    "content": "#!/bin/sh\n#\n# An example hook script for the \"post-receive\" event.\n#\n# The \"post-receive\" script is run after receive-pack has accepted a pack\n# and the repository has been updated.  It is passed arguments in through\n# stdin in the form\n#  <oldrev> <newrev> <refname>\n# For example:\n#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master\n#\n# see contrib/hooks/ for a sample, or uncomment the next line and\n# rename the file to \"post-receive\".\n\n#. /usr/share/doc/git-core/contrib/hooks/post-receive-email\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest \"$(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0')\"\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n<<\\DOC_END\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n\nDOC_END\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/bisect/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/bisect/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/bisect/.githug/info/refs",
    "content": "94e162b505bc2290fb67764357a625192f8a4e8a\trefs/bisect/bad\nbfb16eec1081387b586dc8009ef422cfff60b622\trefs/bisect/good-bfb16eec1081387b586dc8009ef422cfff60b622\nc0a1cdff8dd63948a5fc41f5871681c5b133c053\trefs/bisect/good-c0a1cdff8dd63948a5fc41f5871681c5b133c053\nf608824888b83bbedc1f658be7496ffea467a8fb\trefs/bisect/good-f608824888b83bbedc1f658be7496ffea467a8fb\n12628f463f4c722695bf0e9d603c9411287885db\trefs/heads/master\n32ae79196bd49a3416264970b0cd873e26bd1824\trefs/tags/graetsch\n89d27c5f4680cd21028947be75c33a68511decb9\trefs/tags/graetsch^{}\n2a5197909a0a1575145e0a40bb13dd54f5d6a9f7\trefs/tags/orig_head\nf351ca63a759f56bb26924fd566294eb23455c71\trefs/tags/orig_head^{}\n"
  },
  {
    "path": "levels/bisect/.githug/logs/HEAD",
    "content": "f351ca63a759f56bb26924fd566294eb23455c71 f608824888b83bbedc1f658be7496ffea467a8fb Florian Sesser <fs@it-agenten.com> 1348697815 +0200\treset: moving to f608824888b83bbedc1f658be7496ffea467a8fb\nf608824888b83bbedc1f658be7496ffea467a8fb f608824888b83bbedc1f658be7496ffea467a8fb Florian Sesser <fs@it-agenten.com> 1348697842 +0200\tcheckout: moving from master to hacklschorsch\nf608824888b83bbedc1f658be7496ffea467a8fb 80a9b3d94237f982b6c9052e6d56b930f18a4ef5 Florian Sesser <fs@it-agenten.com> 1348697952 +0200\tcherry-pick: Another Commit\n80a9b3d94237f982b6c9052e6d56b930f18a4ef5 8c992afff5e16c97f4ef82d58671a3403d734086 Florian Sesser <fs@it-agenten.com> 1348697967 +0200\tcherry-pick: Another Commit\n8c992afff5e16c97f4ef82d58671a3403d734086 49774ea84ae3723cc4fac75521435cc04d56b657 Florian Sesser <fs@it-agenten.com> 1348698291 +0200\tcherry-pick: Another Commit\n49774ea84ae3723cc4fac75521435cc04d56b657 e060c0d789288fda946f91254672295230b2de9d Florian Sesser <fs@it-agenten.com> 1348698316 +0200\tcherry-pick: Another Commit\ne060c0d789288fda946f91254672295230b2de9d ffb097e3edfa828afa565eeceee6b506b3f2a131 Florian Sesser <fs@it-agenten.com> 1348698720 +0200\tcommit: Another Commit\nffb097e3edfa828afa565eeceee6b506b3f2a131 2e1735d5bef6db0f3e325051a179af280f05573a Florian Sesser <fs@it-agenten.com> 1348698824 +0200\tcommit: Another Commit\n2e1735d5bef6db0f3e325051a179af280f05573a ccddb96f824a0e929f5fecf55c0f4479552246f3 Florian Sesser <fs@it-agenten.com> 1348698884 +0200\tcommit: Another Commit\nccddb96f824a0e929f5fecf55c0f4479552246f3 a530e7ed25173d0800cfe33cc8915e5929209b8e Florian Sesser <fs@it-agenten.com> 1348698911 +0200\tcommit: Another Commit\na530e7ed25173d0800cfe33cc8915e5929209b8e fdbfc0d403e5ac0b2659cbfa2cbb061fcca0dc2a Florian Sesser <fs@it-agenten.com> 1348699468 +0200\tcommit: Another Commit\nfdbfc0d403e5ac0b2659cbfa2cbb061fcca0dc2a 5d1eb75377072c5c6e5a1b0ac4159181ecc4edff Florian Sesser <fs@it-agenten.com> 1348699592 +0200\tcommit: Another Commit\n5d1eb75377072c5c6e5a1b0ac4159181ecc4edff 9f54462abbb991b167532929b34118113aa6c52e Florian Sesser <fs@it-agenten.com> 1348699604 +0200\tcherry-pick: Another Commit\n9f54462abbb991b167532929b34118113aa6c52e 7c03a99ba384572c216769f0273b5baf3ba83694 Florian Sesser <fs@it-agenten.com> 1348699614 +0200\tcherry-pick: Another Commit\n7c03a99ba384572c216769f0273b5baf3ba83694 5db7a7cb90e745e2c9dbdd84810ccc7d91d92e72 Florian Sesser <fs@it-agenten.com> 1348699625 +0200\tcherry-pick: Another Commit\n5db7a7cb90e745e2c9dbdd84810ccc7d91d92e72 18ed2ac1522a014412d4303ce7c8db39becab076 Florian Sesser <fs@it-agenten.com> 1348699635 +0200\tcherry-pick: Another Commit\n18ed2ac1522a014412d4303ce7c8db39becab076 bb736ddd9b83d6296d23444a2ab3b0d2fa6dfb81 Florian Sesser <fs@it-agenten.com> 1348699648 +0200\tcherry-pick: Another Commit\nbb736ddd9b83d6296d23444a2ab3b0d2fa6dfb81 888386c77c957dc52f3113f2483663e3132559d4 Florian Sesser <fs@it-agenten.com> 1348699657 +0200\tcherry-pick: Another Commit\n888386c77c957dc52f3113f2483663e3132559d4 028763b396121e035f672ef5af75d2dcb1cc8146 Florian Sesser <fs@it-agenten.com> 1348699668 +0200\tcherry-pick: Another Commit\n028763b396121e035f672ef5af75d2dcb1cc8146 979576184c5ec9667cf7593cf550c420378e960f Florian Sesser <fs@it-agenten.com> 1348699679 +0200\tcherry-pick: Another Commit\n979576184c5ec9667cf7593cf550c420378e960f 12628f463f4c722695bf0e9d603c9411287885db Florian Sesser <fs@it-agenten.com> 1348699693 +0200\tcherry-pick: Another Commit\n"
  },
  {
    "path": "levels/bisect/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 f608824888b83bbedc1f658be7496ffea467a8fb Florian Sesser <fs@it-agenten.com> 1348697842 +0200\tbranch: Created from HEAD\nf608824888b83bbedc1f658be7496ffea467a8fb 80a9b3d94237f982b6c9052e6d56b930f18a4ef5 Florian Sesser <fs@it-agenten.com> 1348697952 +0200\tcherry-pick: Another Commit\n80a9b3d94237f982b6c9052e6d56b930f18a4ef5 8c992afff5e16c97f4ef82d58671a3403d734086 Florian Sesser <fs@it-agenten.com> 1348697967 +0200\tcherry-pick: Another Commit\n8c992afff5e16c97f4ef82d58671a3403d734086 49774ea84ae3723cc4fac75521435cc04d56b657 Florian Sesser <fs@it-agenten.com> 1348698291 +0200\tcherry-pick: Another Commit\n49774ea84ae3723cc4fac75521435cc04d56b657 e060c0d789288fda946f91254672295230b2de9d Florian Sesser <fs@it-agenten.com> 1348698316 +0200\tcherry-pick: Another Commit\ne060c0d789288fda946f91254672295230b2de9d ffb097e3edfa828afa565eeceee6b506b3f2a131 Florian Sesser <fs@it-agenten.com> 1348698720 +0200\tcommit: Another Commit\nffb097e3edfa828afa565eeceee6b506b3f2a131 2e1735d5bef6db0f3e325051a179af280f05573a Florian Sesser <fs@it-agenten.com> 1348698824 +0200\tcommit: Another Commit\n2e1735d5bef6db0f3e325051a179af280f05573a ccddb96f824a0e929f5fecf55c0f4479552246f3 Florian Sesser <fs@it-agenten.com> 1348698884 +0200\tcommit: Another Commit\nccddb96f824a0e929f5fecf55c0f4479552246f3 a530e7ed25173d0800cfe33cc8915e5929209b8e Florian Sesser <fs@it-agenten.com> 1348698911 +0200\tcommit: Another Commit\na530e7ed25173d0800cfe33cc8915e5929209b8e fdbfc0d403e5ac0b2659cbfa2cbb061fcca0dc2a Florian Sesser <fs@it-agenten.com> 1348699468 +0200\tcommit: Another Commit\nfdbfc0d403e5ac0b2659cbfa2cbb061fcca0dc2a 5d1eb75377072c5c6e5a1b0ac4159181ecc4edff Florian Sesser <fs@it-agenten.com> 1348699592 +0200\tcommit: Another Commit\n5d1eb75377072c5c6e5a1b0ac4159181ecc4edff 9f54462abbb991b167532929b34118113aa6c52e Florian Sesser <fs@it-agenten.com> 1348699604 +0200\tcherry-pick: Another Commit\n9f54462abbb991b167532929b34118113aa6c52e 7c03a99ba384572c216769f0273b5baf3ba83694 Florian Sesser <fs@it-agenten.com> 1348699614 +0200\tcherry-pick: Another Commit\n7c03a99ba384572c216769f0273b5baf3ba83694 5db7a7cb90e745e2c9dbdd84810ccc7d91d92e72 Florian Sesser <fs@it-agenten.com> 1348699625 +0200\tcherry-pick: Another Commit\n5db7a7cb90e745e2c9dbdd84810ccc7d91d92e72 18ed2ac1522a014412d4303ce7c8db39becab076 Florian Sesser <fs@it-agenten.com> 1348699635 +0200\tcherry-pick: Another Commit\n18ed2ac1522a014412d4303ce7c8db39becab076 bb736ddd9b83d6296d23444a2ab3b0d2fa6dfb81 Florian Sesser <fs@it-agenten.com> 1348699648 +0200\tcherry-pick: Another Commit\nbb736ddd9b83d6296d23444a2ab3b0d2fa6dfb81 888386c77c957dc52f3113f2483663e3132559d4 Florian Sesser <fs@it-agenten.com> 1348699657 +0200\tcherry-pick: Another Commit\n888386c77c957dc52f3113f2483663e3132559d4 028763b396121e035f672ef5af75d2dcb1cc8146 Florian Sesser <fs@it-agenten.com> 1348699668 +0200\tcherry-pick: Another Commit\n028763b396121e035f672ef5af75d2dcb1cc8146 979576184c5ec9667cf7593cf550c420378e960f Florian Sesser <fs@it-agenten.com> 1348699679 +0200\tcherry-pick: Another Commit\n979576184c5ec9667cf7593cf550c420378e960f 12628f463f4c722695bf0e9d603c9411287885db Florian Sesser <fs@it-agenten.com> 1348699693 +0200\tcherry-pick: Another Commit\n12628f463f4c722695bf0e9d603c9411287885db 12628f463f4c722695bf0e9d603c9411287885db Florian Sesser <fs@it-agenten.com> 1348701064 +0200\tBranch: renamed refs/heads/hacklschorsch to refs/heads/master\n"
  },
  {
    "path": "levels/bisect/.githug/objects/info/packs",
    "content": "P pack-59fab357f3158a9640633de6a3326ed79a2b4fe6.pack\n\n"
  },
  {
    "path": "levels/bisect/.githug/packed-refs",
    "content": "# pack-refs with: peeled \n94e162b505bc2290fb67764357a625192f8a4e8a refs/bisect/bad\nbfb16eec1081387b586dc8009ef422cfff60b622 refs/bisect/good-bfb16eec1081387b586dc8009ef422cfff60b622\nc0a1cdff8dd63948a5fc41f5871681c5b133c053 refs/bisect/good-c0a1cdff8dd63948a5fc41f5871681c5b133c053\nf608824888b83bbedc1f658be7496ffea467a8fb refs/bisect/good-f608824888b83bbedc1f658be7496ffea467a8fb\n12628f463f4c722695bf0e9d603c9411287885db refs/heads/master\n32ae79196bd49a3416264970b0cd873e26bd1824 refs/tags/graetsch\n^89d27c5f4680cd21028947be75c33a68511decb9\n2a5197909a0a1575145e0a40bb13dd54f5d6a9f7 refs/tags/orig_head\n^f351ca63a759f56bb26924fd566294eb23455c71\n"
  },
  {
    "path": "levels/bisect/makefile",
    "content": "test:\n\truby prog.rb 5 | ruby test.rb\n"
  },
  {
    "path": "levels/bisect/prog.rb",
    "content": "#!/usr/bin/env ruby\n\nputs Integer((Integer(ARGV[0])-3)*9)-1\n"
  },
  {
    "path": "levels/bisect/test.rb",
    "content": "#!/usr/bin/env ruby\n\nline = gets\nexit 1 if line != \"15\\n\"\n"
  },
  {
    "path": "levels/bisect.rb",
    "content": "difficulty 3\ndescription \"A bug was introduced somewhere along the way. You know that running `ruby prog.rb 5` should output 15. You can also run `make test`. What are the first 7 chars of the hash of the commit (the abbreviated hash) that introduced the bug?\"\n\nsetup do\n  init_from_level\n  repo.init\n  system \"git branch -m master\"\nend\n\nsolution do\n  \"18ed2ac\" == request(\"What are the first 7 characters of the hash of the commit that introduced the bug?\")[0..6]\nend\n\nhint do\n  puts [\"The fastest way to find the bug is with bisect.\", \"Don't forget to start bisect first, identify a good or bad commit, then run `git bisect run make test`.\"]\nend\n"
  },
  {
    "path": "levels/blame/.githug/COMMIT_EDITMSG",
    "content": "added more options (no really)\n"
  },
  {
    "path": "levels/blame/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/blame/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n"
  },
  {
    "path": "levels/blame/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/blame/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/blame/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/blame/.githug/hooks/post-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script that is called after a successful\n# commit is made.\n#\n# To enable this hook, rename this file to \"post-commit\".\n\n: Nothing\n"
  },
  {
    "path": "levels/blame/.githug/hooks/post-receive.sample",
    "content": "#!/bin/sh\n#\n# An example hook script for the \"post-receive\" event.\n#\n# The \"post-receive\" script is run after receive-pack has accepted a pack\n# and the repository has been updated.  It is passed arguments in through\n# stdin in the form\n#  <oldrev> <newrev> <refname>\n# For example:\n#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master\n#\n# see contrib/hooks/ for a sample, or uncomment the next line and\n# rename the file to \"post-receive\".\n\n#. /usr/share/doc/git-core/contrib/hooks/post-receive-email\n"
  },
  {
    "path": "levels/blame/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/blame/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/blame/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest \"$(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0')\"\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/blame/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n"
  },
  {
    "path": "levels/blame/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/blame/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/blame/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/blame/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 5e8863df752e3b7f2150df7c78f12bef6f1ff00e Gary Rennie <webmaster@gazler.com> 1331247924 +0000\tcommit (initial): added config with name\n5e8863df752e3b7f2150df7c78f12bef6f1ff00e 094094808dc6dc336c93c8602190a9e5f7bd6a11 Spider Man <spidey-sense@tingling.com> 1331247978 +0000\tcommit: added options\n094094808dc6dc336c93c8602190a9e5f7bd6a11 70d00535a3a25b0ac1736dd3d306d6271e5427ed Bruce Banner <hulk@smash.com> 1331248061 +0000\tcommit: added password\n70d00535a3a25b0ac1736dd3d306d6271e5427ed 97bdd0cccf9f4b8730f78cb53a81a74f205dbcc2 Spider Man <spidey-sense@tingling.com> 1331248095 +0000\tcommit: added more options\n97bdd0cccf9f4b8730f78cb53a81a74f205dbcc2 ffd39c2dbfd94bdbca06d48686e0cbda642f3de7 Gary Rennie <webmaster@gazler.com> 1331248138 +0000\tcommit: added more options (no really)\n"
  },
  {
    "path": "levels/blame/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 5e8863df752e3b7f2150df7c78f12bef6f1ff00e Gary Rennie <webmaster@gazler.com> 1331247924 +0000\tcommit (initial): added config with name\n5e8863df752e3b7f2150df7c78f12bef6f1ff00e 094094808dc6dc336c93c8602190a9e5f7bd6a11 Spider Man <spidey-sense@tingling.com> 1331247978 +0000\tcommit: added options\n094094808dc6dc336c93c8602190a9e5f7bd6a11 70d00535a3a25b0ac1736dd3d306d6271e5427ed Bruce Banner <hulk@smash.com> 1331248061 +0000\tcommit: added password\n70d00535a3a25b0ac1736dd3d306d6271e5427ed 97bdd0cccf9f4b8730f78cb53a81a74f205dbcc2 Spider Man <spidey-sense@tingling.com> 1331248095 +0000\tcommit: added more options\n97bdd0cccf9f4b8730f78cb53a81a74f205dbcc2 ffd39c2dbfd94bdbca06d48686e0cbda642f3de7 Gary Rennie <webmaster@gazler.com> 1331248138 +0000\tcommit: added more options (no really)\n"
  },
  {
    "path": "levels/blame/.githug/objects/09/4094808dc6dc336c93c8602190a9e5f7bd6a11",
    "content": "x\u0001NK\u00021\u0010tS^\u0001\u0011/\u0013d\u0002N2$qW.*m\u0003\u0010a\u0012jjK6褧\u000eH\u001aDа\f0UVGR\u0018>]t\\,\t\"Α\r\u001e{N\u001e\n\\WO\u001dK9yuPJH\u0016'L1_m`!%LPk\u0003dM"
  },
  {
    "path": "levels/blame/.githug/objects/31/11dda1f5b08d50ac44b99acabfa54f1e6e72b0",
    "content": "x\u00015\n\u00021\fD=\u0015MAD/\u001e\u0016\u0004\u0010l7@MdS\u0010\u0014ݶ\u001e&Cz\u001cEH䎋i\u0012;6Szr\u0003GJ\u0016Ju[+oSJ\u0006V@\u0012\u001b쭁o-\u0016\u001ae\u001d[\u0007#3"
  },
  {
    "path": "levels/blame/.githug/objects/97/bdd0cccf9f4b8730f78cb53a81a74f205dbcc2",
    "content": "x\u0001Kj\u00031\u0010DS$t`L.UN\u0003\u001ei(\u000b>\u0015((-u\u000elͩ7U`\"'!Fo\u0011\u0013{1p4uL?\t)\u0006Uܴt\b(θl2\u0019Bx\u0011#\u0006x\u000er\u0018\u001f6\u0002_\u001fCZϡϥnW cm<@p\u001ac߇)VBZ1\u0001nK"
  },
  {
    "path": "levels/blame/.githug/objects/dd/df1d8ebd60eec169c15a5b23cb49a58d2ed5a0",
    "content": "x\u0001eA\n0\u0010E]\u0014cW\nŅ.Ģ x\f\u0011IT\u0006LI\u0005w7I(]$T+X7\u0013isp`\u0001\bYHc\u000b%.l\u0002ӲU\u0001P\u0006$(\f>\u0007\u001ev@h\n#\u000bk\u001e$}\u0004c\u001d>qcAە\u0013.RqKR8}JgbH)դxkZIэ\r?X"
  },
  {
    "path": "levels/blame/.githug/objects/ff/d39c2dbfd94bdbca06d48686e0cbda642f3de7",
    "content": "x\u0001MJD1\u0010]\u0014t\u0010$t`\u0018ܹ\u0006NG\u001f$C&\"\u0019\u0007Ž}i\u000e\u0011x\u000b\u0001|ڠ!搲ٸ1Ґ6!.53X|t\tis/Vo91[E?\u000fqOim\u00178Jt2޿ʽ^8gG\u0010^Zf\u000fيr\f\u000f~{o7xn\u001dqO\u001fdbO["
  },
  {
    "path": "levels/blame/.githug/refs/heads/master",
    "content": "ffd39c2dbfd94bdbca06d48686e0cbda642f3de7\n"
  },
  {
    "path": "levels/blame/config.rb",
    "content": "class Config\n  attr_accessor :name, :password\n  def initialize(name, password = nil, options = {})\n    @name = name\n    @password = password || \"i<3evil\"\n\n    if options[:downcase]\n      @name.downcase!\n    end\n\n    if options[:upcase]\n      @name.upcase!\n    end\n\n  end\nend\n"
  },
  {
    "path": "levels/blame.rb",
    "content": "difficulty 2\ndescription \"Identify who put a password inside the file `config.rb`.\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  offender = repo.commit(\"97bdd0cccf9f4b8730f78cb53a81a74f205dbcc2\").author.name\n  request(\"Who made the commit with the password?\").downcase.strip == offender.downcase\nend\n\nhint do\n  puts \"You want to research the `git blame` command.\"\nend\n"
  },
  {
    "path": "levels/branch.rb",
    "content": "difficulty 1\ndescription \"To work on a piece of code that has the potential to break things, create the branch test_code.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"README\")\n  repo.add \"README\"\n  repo.commit_all(\"Initial commit\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  repo.branches.map(&:name).include?(\"test_code\")\nend\n\nhint do\n  puts \"`git branch` is what you want to investigate.\"\nend\n\n"
  },
  {
    "path": "levels/branch_at.rb",
    "content": "difficulty 3\ndescription \"You forgot to branch at the previous commit and made a commit on top of it. Create the branch test_branch at the commit before the last.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"file1\")\n  repo.add(\"file1\")\n  repo.commit_all(\"Adding file1\")\n  File.open(\"file1\", 'w') { |f| f.write(\"content\") }\n  repo.add(\"file1\")\n  repo.commit_all(\"Updating file1\")\n  File.open(\"file1\", 'a') { |f| f.write(\"\\nAdding some more text\") }\n  repo.add(\"file1\")\n  repo.commit_all(\"Updating file1 again\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false unless repo.branches.map(&:name).include?(\"test_branch\")\n  repo.commits(\"test_branch\").each { |commit| return false if commit.message == \"Updating file1 again\" }\n  true\nend\n\nhint do\n  puts \"Just like creating a branch, but you have to pass an extra argument.\"\nend\n"
  },
  {
    "path": "levels/checkout.rb",
    "content": "difficulty 2\ndescription \"Create and switch to a new branch called my_branch. You will need to create a branch like you did in the previous level.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"README\")\n  repo.add(\"README\")\n  repo.commit_all(\"initial commit\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false unless repo.head.name == \"my_branch\"\n  true\nend\n\nhint do\n  puts \"Try looking up `git checkout` and `git branch`.\"\nend\n"
  },
  {
    "path": "levels/checkout_file.rb",
    "content": "difficulty 3\n\ndescription \"A file has been modified, but you don't want to keep the modification. Checkout the `config.rb` file from the last commit.\"\n\nsetup do\n  repo.init\n  File.open(\"config.rb\", \"w\") do |file|\n    file.puts(\"This is the initial config file\")\n  end\n\n  repo.add(\"config.rb\")\n  repo.commit_all(\"Added initial config file\")\n\n  File.open(\"config.rb\", \"a\") do |file|\n    file.puts(\"These are changed you don't want to keep!\")\n  end\n  system \"git branch -m master\"\nend\n\nsolution do\n  repo.status.files[\"config.rb\"].type != \"M\" && repo.commits.length == 1\nend\n\nhint do\n  puts \"You will need to do some research on the checkout command for this one.\"\nend\n"
  },
  {
    "path": "levels/checkout_tag.rb",
    "content": "difficulty 2\n\ndescription \"You need to fix a bug in the version 1.2 of your app. Checkout the tag `v1.2`.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"app.rb\")\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Initial commit\")\n\n  `echo \"Some code\" >> app.rb`\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Some changes\")\n  repo.git.tag( { 'f' => true }, \"v1.0\" )\n\n  `echo \"Buggy code\" >> app.rb`\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Some more changes\")\n  repo.git.tag( { 'f' => true }, \"v1.2\" )\n\n  `echo \"More code\" >> app.rb`\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Yet more changes\")\n\n  `echo \"Some more code\" >> app.rb`\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Changes galore\")\n  repo.git.tag( { 'f' => true }, \"v1.5\" )\n\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false unless repo.commits.length == 5\n  return false unless `git show HEAD --format=%s` =~ /Some more changes/\n  true\nend\n\nhint do\n  puts \"There's no big difference between checking out a branch and checking out a tag.\"\nend\n"
  },
  {
    "path": "levels/checkout_tag_over_branch.rb",
    "content": "difficulty 2\n\ndescription \"You need to fix a bug in the version 1.2 of your app. Checkout the tag `v1.2` (Note: There is also a branch named `v1.2`).\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"app.rb\")\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Initial commit\")\n  system \"git branch -m master\"\n\n  `echo \"Some code\" >> app.rb`\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Some changes\")\n  repo.git.tag( { 'f' => true }, \"v1.0\" )\n\n  `echo \"Buggy code\" >> app.rb`\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Some more changes\")\n  repo.git.tag( { 'f' => true }, \"v1.2\" )\n\n  `echo \"More code\" >> app.rb`\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Yet more changes\")\n\n  `echo \"Some more code\" >> app.rb`\n  repo.add(\"app.rb\")\n  repo.commit_all(\"Changes galore\")\n  repo.git.tag( { 'f' => true }, \"v1.5\" )\n\n  repo.git.native :checkout, {\"b\" => true}, 'v1.2'\n  File.open(\"file3\", 'w') { |f| f << \"some feature\\n\" }\n  repo.add \"file3\"\n  repo.commit_all \"Developing new features\"\n\n  repo.git.native :checkout, {}, 'master'\nend\n\nsolution do\n  return false unless repo.commits.length == 5\n  return false unless `git show HEAD --format=%s` =~ /Some more changes/\n  true\nend\n\nhint do\n  puts \"You should think about specifying you're after the tag named `v1.2` (think `tags/`).\"\nend\n"
  },
  {
    "path": "levels/cherry-pick/.githug/COMMIT_EDITMSG",
    "content": "some small fixes\n"
  },
  {
    "path": "levels/cherry-pick/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/cherry-pick/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\tignorecase = true\n"
  },
  {
    "path": "levels/cherry-pick/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/cherry-pick/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/cherry-pick/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/cherry-pick/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/cherry-pick/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/cherry-pick/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Redirect output to stderr.\nexec 1>&2\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest $(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0' | wc -c) != 0\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\n# If there are whitespace errors, print the offending file names and fail.\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/cherry-pick/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/opt/local/bin/perl5.12 -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n"
  },
  {
    "path": "levels/cherry-pick/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /opt/local/bin/perl5.12 -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /opt/local/bin/perl5.12 -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/cherry-pick/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/cherry-pick/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/cherry-pick/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 ea3dbcc5e2d2359698c3606b0ec44af9f76def54 Andrey <aslushnikov@gmail.com> 1332886832 +0400\tcommit (initial): Initial commit\nea3dbcc5e2d2359698c3606b0ec44af9f76def54 b30c6a965415df6aef5f2903f9892fa5481152fc Andrey <aslushnikov@gmail.com> 1332886882 +0400\tcommit: Added a hardcore math module\nb30c6a965415df6aef5f2903f9892fa5481152fc 232d266a78d5ef7196f1ede14972ccf7ee19e587 Andrey <aslushnikov@gmail.com> 1332886949 +0400\tcommit: Renamed project.js -> herdcore-math.js\n232d266a78d5ef7196f1ede14972ccf7ee19e587 6edea632d9540e060bca97dda0897df2b7da0ec0 Andrey <aslushnikov@gmail.com> 1332887008 +0400\tcommit: Added fancy branded output\n6edea632d9540e060bca97dda0897df2b7da0ec0 ea3dbcc5e2d2359698c3606b0ec44af9f76def54 Andrey <aslushnikov@gmail.com> 1332887015 +0400\tcheckout: moving from master to ea3dbcc\nea3dbcc5e2d2359698c3606b0ec44af9f76def54 ea3dbcc5e2d2359698c3606b0ec44af9f76def54 Andrey <aslushnikov@gmail.com> 1332887030 +0400\tcheckout: moving from ea3dbcc5e2d2359698c3606b0ec44af9f76def54 to new-feature\nea3dbcc5e2d2359698c3606b0ec44af9f76def54 58a8c8edcfdd00c6d8cce9aada8f987a1677571f Andrey <aslushnikov@gmail.com> 1332887081 +0400\tcommit: Added a stub for the feature\n58a8c8edcfdd00c6d8cce9aada8f987a1677571f cfd8ce38c22c5fe83cc04e23f94646464f20d990 Andrey <aslushnikov@gmail.com> 1332887151 +0400\tcommit: README.md\ncfd8ce38c22c5fe83cc04e23f94646464f20d990 ca32a6dac7b6f97975edbe19a4296c2ee7682f68 Andrey <aslushnikov@gmail.com> 1332887177 +0400\tcommit (amend): Filled in README.md with proper input\nca32a6dac7b6f97975edbe19a4296c2ee7682f68 4a1961bce62840eaef9c4392fe5cc799e38c9b7b Andrey <aslushnikov@gmail.com> 1332887238 +0400\tcommit: Fixed feature\n4a1961bce62840eaef9c4392fe5cc799e38c9b7b ea2a47c19b85fc321e2737ddc49db3deeba3a1b5 Andrey <aslushnikov@gmail.com> 1332887315 +0400\tcommit: some small fixes\nea2a47c19b85fc321e2737ddc49db3deeba3a1b5 6edea632d9540e060bca97dda0897df2b7da0ec0 Andrey <aslushnikov@gmail.com> 1332887329 +0400\tcheckout: moving from new-feature to master\n6edea632d9540e060bca97dda0897df2b7da0ec0 ea2a47c19b85fc321e2737ddc49db3deeba3a1b5 Andrey <aslushnikov@gmail.com> 1332887695 +0400\tcheckout: moving from master to new-feature\nea2a47c19b85fc321e2737ddc49db3deeba3a1b5 6edea632d9540e060bca97dda0897df2b7da0ec0 Andrey <aslushnikov@gmail.com> 1332887737 +0400\tcheckout: moving from new-feature to master\n"
  },
  {
    "path": "levels/cherry-pick/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 ea3dbcc5e2d2359698c3606b0ec44af9f76def54 Andrey <aslushnikov@gmail.com> 1332886832 +0400\tcommit (initial): Initial commit\nea3dbcc5e2d2359698c3606b0ec44af9f76def54 b30c6a965415df6aef5f2903f9892fa5481152fc Andrey <aslushnikov@gmail.com> 1332886882 +0400\tcommit: Added a hardcore math module\nb30c6a965415df6aef5f2903f9892fa5481152fc 232d266a78d5ef7196f1ede14972ccf7ee19e587 Andrey <aslushnikov@gmail.com> 1332886949 +0400\tcommit: Renamed project.js -> herdcore-math.js\n232d266a78d5ef7196f1ede14972ccf7ee19e587 6edea632d9540e060bca97dda0897df2b7da0ec0 Andrey <aslushnikov@gmail.com> 1332887008 +0400\tcommit: Added fancy branded output\n"
  },
  {
    "path": "levels/cherry-pick/.githug/logs/refs/heads/new-feature",
    "content": "0000000000000000000000000000000000000000 ea3dbcc5e2d2359698c3606b0ec44af9f76def54 Andrey <aslushnikov@gmail.com> 1332887030 +0400\tbranch: Created from HEAD\nea3dbcc5e2d2359698c3606b0ec44af9f76def54 58a8c8edcfdd00c6d8cce9aada8f987a1677571f Andrey <aslushnikov@gmail.com> 1332887081 +0400\tcommit: Added a stub for the feature\n58a8c8edcfdd00c6d8cce9aada8f987a1677571f cfd8ce38c22c5fe83cc04e23f94646464f20d990 Andrey <aslushnikov@gmail.com> 1332887151 +0400\tcommit: README.md\ncfd8ce38c22c5fe83cc04e23f94646464f20d990 ca32a6dac7b6f97975edbe19a4296c2ee7682f68 Andrey <aslushnikov@gmail.com> 1332887177 +0400\tcommit (amend): Filled in README.md with proper input\nca32a6dac7b6f97975edbe19a4296c2ee7682f68 4a1961bce62840eaef9c4392fe5cc799e38c9b7b Andrey <aslushnikov@gmail.com> 1332887238 +0400\tcommit: Fixed feature\n4a1961bce62840eaef9c4392fe5cc799e38c9b7b ea2a47c19b85fc321e2737ddc49db3deeba3a1b5 Andrey <aslushnikov@gmail.com> 1332887315 +0400\tcommit: some small fixes\n"
  },
  {
    "path": "levels/cherry-pick/.githug/objects/6e/dea632d9540e060bca97dda0897df2b7da0ec0",
    "content": "x\u0001A\u000e \u0010E]s\n&\u0018{\u0014\fтAj\u001ey/,ܤ̩Uf\t\u0007\u0006쉌Kj`-ʙ^C|\u0001\u001d\u0019N\b&`bnƄ8\u0014amSrTyy)?0.,7\t]C<^)_K1\u00101\u0014rXC>VY{m\u0007[K("
  },
  {
    "path": "levels/cherry-pick/.githug/refs/heads/master",
    "content": "6edea632d9540e060bca97dda0897df2b7da0ec0\n"
  },
  {
    "path": "levels/cherry-pick/.githug/refs/heads/new-feature",
    "content": "ea2a47c19b85fc321e2737ddc49db3deeba3a1b5\n"
  },
  {
    "path": "levels/cherry-pick/README.md",
    "content": "I'll fill in the file some time later..\n\n"
  },
  {
    "path": "levels/cherry-pick/hardcore-math.js",
    "content": "for(var i = 0; i < 10; i++) {\n    console.log(42 * i);\n}\n"
  },
  {
    "path": "levels/cherry-pick/nokia.js",
    "content": "console.log(\"[NOKIA] Connecting people\");\n"
  },
  {
    "path": "levels/cherry-pick.rb",
    "content": "difficulty 3\ndescription \"Your new feature isn't worth the time and you're going to delete it. But it has one commit that fills in `README` file, and you want this commit to be on the master as well.\"\n\nsetup do\n  init_from_level\n  `git stash` # fix for README.md being in githug root an the level\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false unless repo.commits[1].message == \"Added fancy branded output\"\n  return false unless repo.commits[0].message == \"Filled in README.md with proper input\"\n  true\nend\n\nhint do\n  puts \"Sneak a peek at the `git help cherry-pick` command.\"\nend\n"
  },
  {
    "path": "levels/clone.rb",
    "content": "difficulty 1\ndescription \"Clone the repository at https://github.com/Gazler/cloneme.\"\n\nsolution do\n  repo(\"cloneme\").commit(\"157b2b61f29ab9df45f31c7cd9cb5d8ff06ecde4\")\nend\n\nhint do\n  puts \"You should have a look at this site: https://github.com/Gazler/cloneme.\"\nend\n"
  },
  {
    "path": "levels/clone_to_folder.rb",
    "content": "difficulty 1\ndescription \"Clone the repository at https://github.com/Gazler/cloneme into the folder `my_cloned_repo`.\"\n\nsolution do\n  repo(\"my_cloned_repo\").commit(\"157b2b61f29ab9df45f31c7cd9cb5d8ff06ecde4\")\nend\n\nhint do\n  puts \"This is like the last level, `git clone` has an optional argument.\"\nend\n"
  },
  {
    "path": "levels/commit.rb",
    "content": "difficulty 1\ndescription \"The `README` file has been added to your staging area, now commit it.\"\n\nsetup do\n  repo.init\n  system \"git branch -m master\"\n  FileUtils.touch(\"README\")\n  repo.add(\"README\")\nend\n\nsolution do\n  return false if repo.commits.empty?\n  true\nend\n\nhint do\n  puts \"You must include a message when you commit.\"\nend\n"
  },
  {
    "path": "levels/commit_amend.rb",
    "content": "difficulty 2\ndescription \"The `README` file has been committed, but it looks like the file `forgotten_file.rb` was missing from the commit. Add the file and amend your previous commit to include it.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"README\")\n  repo.add(\"README\")\n  repo.commit_all(\"Initial commit\")\n  FileUtils.touch(\"forgotten_file.rb\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  # Reset config - see issue #74\n  file = File.open(\".git/config\", \"w\") do |file|\n    file.puts(\"[format]\")\n    file.puts(\" pretty = medium\")\n  end\n  repo.commits.length == 1 && Grit::CommitStats.find_all(repo, repo.commits.first.sha).first[1].files.length == 2\nend\n\nhint do\n  puts \"Running `git commit --help` will display the man page and possible flags.\"\nend\n"
  },
  {
    "path": "levels/commit_in_future.rb",
    "content": "require 'time'\n\ndifficulty 2\ndescription \"Commit your changes with the future date (e.g. tomorrow).\"\n\nsetup do\n  repo.init\n\n  FileUtils.touch(\"README\")\n  repo.add(\"README\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  repo.commits.length == 1 && repo.commits.first.authored_date > Time.now\nend\n\nhint do\n  puts \"Build a time format, and commit your code using --date parameter for \\\"future\\\".\"\nend\n"
  },
  {
    "path": "levels/config.rb",
    "content": "difficulty 1\ndescription \"Set up your git name and email; this is important so that your commits can be identified.\"\n\nsetup do\n  repo.init\nend\n\nsolution do\n  valid = false\n\n  name = request(\"What is your name?\")\n  email = request(\"What is your email?\")\n  config_name = repo.config[\"user.name\"]\n  config_email = repo.config[\"user.email\"]\n\n  if name.respond_to?(:force_encoding)\n    config_name = config_name.force_encoding(\"UTF-8\")\n    config_email = config_email.force_encoding(\"UTF-8\")\n  end\n\n  if name == config_name && email == config_email\n    valid = true\n  end\n\n  puts \"Your config has the following name: #{config_name}\"\n  puts \"Your config has the following email: #{config_email}\"\n\n  valid\nend\n\nhint do\n  puts \"These settings are config settings. You should run `git help config` if you are stuck.\"\nend\n"
  },
  {
    "path": "levels/conflict/.githug/COMMIT_EDITMSG",
    "content": "Updated the poem\n\n# Please enter the commit message for your changes. Lines starting\n# with '#' will be ignored, and an empty message aborts the commit.\n#\n# Author:    Thameera Senanayaka <thameera123@gmail.com>\n# Committer: Alex <snooz@localhost.localdomain>\n#\n# On branch master\n# Changes to be committed:\n#   (use \"git reset HEAD^1 <file>...\" to unstage)\n#\n#\tmodified:   poem.txt\n#\n"
  },
  {
    "path": "levels/conflict/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/conflict/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n"
  },
  {
    "path": "levels/conflict/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/conflict/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/conflict/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/conflict/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/conflict/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/conflict/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Redirect output to stderr.\nexec 1>&2\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest $(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0' | wc -c) != 0\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\n# If there are whitespace errors, print the offending file names and fail.\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/conflict/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\n<<\\DOC_END\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n\nDOC_END\n"
  },
  {
    "path": "levels/conflict/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/conflict/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/conflict/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/conflict/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 306868e3258be1f35ae43db71e3a6d7edf42ffe7 Thameera Senanayaka <thameera123@gmail.com> 1352041121 +0530\tcommit (initial): Initial commit\n306868e3258be1f35ae43db71e3a6d7edf42ffe7 40e20a455ac2731ad25c297b03aa543d7eedf6ab Thameera Senanayaka <thameera123@gmail.com> 1352041153 +0530\tcommit: Added two lines\n40e20a455ac2731ad25c297b03aa543d7eedf6ab 306868e3258be1f35ae43db71e3a6d7edf42ffe7 Thameera Senanayaka <thameera123@gmail.com> 1352041162 +0530\tcheckout: moving from master to mybranch\n306868e3258be1f35ae43db71e3a6d7edf42ffe7 50a127cb066eb903a6fa59d71802c10cb442fb3b Thameera Senanayaka <thameera123@gmail.com> 1352041213 +0530\tcommit: Added lines\n50a127cb066eb903a6fa59d71802c10cb442fb3b 75179304f4fab00613f08a9412b6cb0965bfa564 Thameera Senanayaka <thameera123@gmail.com> 1352041234 +0530\tcommit: Added comment\n75179304f4fab00613f08a9412b6cb0965bfa564 40e20a455ac2731ad25c297b03aa543d7eedf6ab Thameera Senanayaka <thameera123@gmail.com> 1352041240 +0530\tcheckout: moving from mybranch to master\n40e20a455ac2731ad25c297b03aa543d7eedf6ab 75179304f4fab00613f08a9412b6cb0965bfa564 Alex <snooz@localhost.localdomain> 1382868220 -0700\tcheckout: moving from master to mybranch\n75179304f4fab00613f08a9412b6cb0965bfa564 bdc7bec8acae9b3eabf0a15b223a48211b7a89a1 Alex <snooz@localhost.localdomain> 1382868333 -0700\tcommit: Changed the poem\nbdc7bec8acae9b3eabf0a15b223a48211b7a89a1 3d7aec017559be2b61cab850dafdcb2b6212f1c3 Alex <snooz@localhost.localdomain> 1382868450 -0700\tcommit (amend): Changed the poem\n3d7aec017559be2b61cab850dafdcb2b6212f1c3 40e20a455ac2731ad25c297b03aa543d7eedf6ab Alex <snooz@localhost.localdomain> 1382868483 -0700\tcheckout: moving from mybranch to master\n40e20a455ac2731ad25c297b03aa543d7eedf6ab 30cc28e66966109bb5bfbe96d6c817c367d2050a Alex <snooz@localhost.localdomain> 1382868518 -0700\tcommit: Updated the poem\n30cc28e66966109bb5bfbe96d6c817c367d2050a c797f979cf24ba148bf10d5e26f5d7402dd5f2e1 Alex <snooz@localhost.localdomain> 1382868533 -0700\tcommit (amend): Updated the poem\n"
  },
  {
    "path": "levels/conflict/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 306868e3258be1f35ae43db71e3a6d7edf42ffe7 Thameera Senanayaka <thameera123@gmail.com> 1352041121 +0530\tcommit (initial): Initial commit\n306868e3258be1f35ae43db71e3a6d7edf42ffe7 40e20a455ac2731ad25c297b03aa543d7eedf6ab Thameera Senanayaka <thameera123@gmail.com> 1352041153 +0530\tcommit: Added two lines\n40e20a455ac2731ad25c297b03aa543d7eedf6ab 30cc28e66966109bb5bfbe96d6c817c367d2050a Alex <snooz@localhost.localdomain> 1382868518 -0700\tcommit: Updated the poem\n30cc28e66966109bb5bfbe96d6c817c367d2050a c797f979cf24ba148bf10d5e26f5d7402dd5f2e1 Alex <snooz@localhost.localdomain> 1382868533 -0700\tcommit (amend): Updated the poem\n"
  },
  {
    "path": "levels/conflict/.githug/logs/refs/heads/mybranch",
    "content": "0000000000000000000000000000000000000000 306868e3258be1f35ae43db71e3a6d7edf42ffe7 Thameera Senanayaka <thameera123@gmail.com> 1352041133 +0530\tbranch: Created from master\n306868e3258be1f35ae43db71e3a6d7edf42ffe7 50a127cb066eb903a6fa59d71802c10cb442fb3b Thameera Senanayaka <thameera123@gmail.com> 1352041213 +0530\tcommit: Added lines\n50a127cb066eb903a6fa59d71802c10cb442fb3b 75179304f4fab00613f08a9412b6cb0965bfa564 Thameera Senanayaka <thameera123@gmail.com> 1352041234 +0530\tcommit: Added comment\n75179304f4fab00613f08a9412b6cb0965bfa564 bdc7bec8acae9b3eabf0a15b223a48211b7a89a1 Alex <snooz@localhost.localdomain> 1382868333 -0700\tcommit: Changed the poem\nbdc7bec8acae9b3eabf0a15b223a48211b7a89a1 3d7aec017559be2b61cab850dafdcb2b6212f1c3 Alex <snooz@localhost.localdomain> 1382868450 -0700\tcommit (amend): Changed the poem\n"
  },
  {
    "path": "levels/conflict/.githug/objects/2d/0d90051e320215f54f357e746c9838490557e7",
    "content": "x\u0001+)JMU06c040031Q(O+(apKLyVi.G1Z\u0013\u0001>\u000f"
  },
  {
    "path": "levels/conflict/.githug/objects/3d/7aec017559be2b61cab850dafdcb2b6212f1c3",
    "content": "x\u0001=[JC1\u0014E(\u0004,'\u0007\u00148\u0004ISz4:z/R,XZ/\u0013QOs0C\"bQ!rD9\u0005es(=5b{qm[\u000b\u001aM6\":`v\u0018\u00123-\b\u000f(T\u0007;7jMW|@\\\u001d^_@jZkxF(v<u/8[?~^hYaB\tfau\u0017M\u0004"
  },
  {
    "path": "levels/conflict/.githug/objects/6b/0c0b32bdca3af9beb831744cb755d6fdc7c7c0",
    "content": "x\u0001U\t0\u0010EQשU \n \u0016`\u0003\u00133~`\t\bvop'\\cE-acyྙE\u0001\u0004\"\u000b\u0006Gb*ةY+ET\u0005QكoN\u0006F>0Л\u0017&"
  },
  {
    "path": "levels/conflict/.githug/objects/75/179304f4fab00613f08a9412b6cb0965bfa564",
    "content": "x\u0001Kn@\f@Y)@瓤\u0010=\u0003\\q j'AtI^b\u0016O֩~V3=\u00058GRQ\"b\rI\u0007&L\u001d\u0012U4 \u0010wY1%\u0001QP:3a\u0010xTN~mYrj\nmY\u001e% ?]L߇#a\u00010zt\u001bbqX_۶yV8D\u0004b"
  },
  {
    "path": "levels/conflict/.githug/objects/88/e0473c9da347c6311f5f8eca8d256bf25402b6",
    "content": "x\u0001+)JMU06c040031Q(O+(ap1kzY)x\u0002׌\n\u001f\u0001/\u000e"
  },
  {
    "path": "levels/conflict/.githug/objects/bd/c7bec8acae9b3eabf0a15b223a48211b7a89a1",
    "content": "x\u0001A\n0\u0010@Q9\\@dd\u0002\"'4\u0013[h\u001b\u0011\u000b=y,S\u0003Gth*d\u0011%F٫\u001c!eR\b1l 6D®tE\u0012T%v%?$OEz\u0019ynp\u0003Z:A汾i\\\u0017\u000bXbǞ\b\u0018\u0010Ͱ?6O(C3QYu1?6lJ"
  },
  {
    "path": "levels/conflict/.githug/refs/heads/master",
    "content": "c797f979cf24ba148bf10d5e26f5d7402dd5f2e1\n"
  },
  {
    "path": "levels/conflict/.githug/refs/heads/mybranch",
    "content": "3d7aec017559be2b61cab850dafdcb2b6212f1c3\n"
  },
  {
    "path": "levels/conflict/poem.txt",
    "content": "Humpty dumpty\nCategorized shoes by color\nHumpty dumpty\nHad a great fall\n\n"
  },
  {
    "path": "levels/conflict.rb",
    "content": "difficulty 4\ndescription \"You need to merge mybranch into the current branch (master). But there may be some incorrect changes in mybranch which may cause conflicts. Solve any merge-conflicts you come across and finish the merge.\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  solved = true\n\n  solved = false unless repo.head.name == \"master\"\n  solved = false unless repo.commits(\"master\")[0].parents.length == 2\n\n  txt = File.read(\"poem.txt\")\n  solved = false if txt =~ /[<>=|]/\n  solved = false unless txt =~ /Sat on a wall/\n\n  solved\nend\n\nhint do\n  puts [\"First you have to do a merge. Then resolve any conflicts and finish the merge\", \"Take a look at the sections on merge conflicts in 'git merge'.\", \"Remove the unnecessary lines in `poem.txt`, so only the correct poem remains.\"]\nend\n"
  },
  {
    "path": "levels/contribute.rb",
    "content": "difficulty 3\ndescription \"This is the final level, the goal is to contribute to this repository by making a pull request on GitHub. Please note that this level is designed to encourage you to add a valid contribution to Githug, not testing your ability to create a pull request. Contributions that are likely to be accepted are levels, bug fixes and improved documentation.\"\n\nsolution do\n  location = \"/tmp/githug\"\n  FileUtils.rm_rf(location)\n  puts \"Cloning repository to #{location}\"\n  `git clone https://github.com/Gazler/githug #{location}`\n\n  contributor = false\n\n  repo = Grit::Repo.new(location)\n  repo.commits('master', false).each do |commit|\n    if commit.author.name == repo.config[\"user.name\"]\n      if commit.author.email == repo.config[\"user.email\"]\n        contributor = true\n      end\n    end\n  end\n  contributor\nend\n\nhint do\n  puts \"Forking the repository would be a good start!\"\nend\n"
  },
  {
    "path": "levels/delete_branch/.githug/COMMIT_EDITMSG",
    "content": "delete_me branch\n"
  },
  {
    "path": "levels/delete_branch/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/delete_branch/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\tignorecase = true\n\tprecomposeunicode = false\n"
  },
  {
    "path": "levels/delete_branch/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ASCII filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Redirect output to stderr.\nexec 1>&2\n\n# Cross platform projects tend to avoid non-ASCII filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest $(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0' | wc -c) != 0\nthen\n\tcat <<\\EOF\nError: Attempt to add a non-ASCII file name.\n\nThis can cause problems if you want to work with people on other platforms.\n\nTo be portable it is advisable to rename the file.\n\nIf you know what you are doing you can disable this check using:\n\n  git config hooks.allownonascii true\nEOF\n\texit 1\nfi\n\n# If there are whitespace errors, print the offending file names and fail.\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/pre-push.sample",
    "content": "#!/bin/sh\n\n# An example hook script to verify what is about to be pushed.  Called by \"git\n# push\" after it has checked the remote status, but before anything has been\n# pushed.  If this script exits with a non-zero status nothing will be pushed.\n#\n# This hook is called with the following parameters:\n#\n# $1 -- Name of the remote to which the push is being done\n# $2 -- URL to which the push is being done\n#\n# If pushing without using a named remote those arguments will be equal.\n#\n# Information about the commits which are being pushed is supplied as lines to\n# the standard input in the form:\n#\n#   <local ref> <local sha1> <remote ref> <remote sha1>\n#\n# This sample shows how to prevent push of commits where the log message starts\n# with \"WIP\" (work in progress).\n\nremote=\"$1\"\nurl=\"$2\"\n\nz40=0000000000000000000000000000000000000000\n\nIFS=' '\nwhile read local_ref local_sha remote_ref remote_sha\ndo\n\tif [ \"$local_sha\" = $z40 ]\n\tthen\n\t\t# Handle delete\n\telse\n\t\tif [ \"$remote_sha\" = $z40 ]\n\t\tthen\n\t\t\t# New branch, examine all commits\n\t\t\trange=\"$local_sha\"\n\t\telse\n\t\t\t# Update to existing branch, examine new commits\n\t\t\trange=\"$remote_sha..$local_sha\"\n\t\tfi\n\n\t\t# Check for WIP commit\n\t\tcommit=`git rev-list -n 1 --grep '^WIP' \"$range\"`\n\t\tif [ -n \"$commit\" ]\n\t\tthen\n\t\t\techo \"Found WIP commit in $local_ref, not pushing\"\n\t\t\texit 1\n\t\tfi\n\tfi\ndone\n\nexit 0\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/delete_branch/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/delete_branch/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/delete_branch/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 b60afe294eb3c200d646995c9e0234470157c1b0 Daniel Smilansky <dsmilansky@gmail.com> 1386225561 -0800\tcommit (initial): first commit\nb60afe294eb3c200d646995c9e0234470157c1b0 b60afe294eb3c200d646995c9e0234470157c1b0 Daniel Smilansky <dsmilansky@gmail.com> 1386225573 -0800\tcheckout: moving from master to master\nb60afe294eb3c200d646995c9e0234470157c1b0 b60afe294eb3c200d646995c9e0234470157c1b0 Daniel Smilansky <dsmilansky@gmail.com> 1386225580 -0800\tcheckout: moving from master to delete_me\nb60afe294eb3c200d646995c9e0234470157c1b0 b60afe294eb3c200d646995c9e0234470157c1b0 Daniel Smilansky <dsmilansky@gmail.com> 1386225584 -0800\tcheckout: moving from delete_me to master\n"
  },
  {
    "path": "levels/delete_branch/.githug/logs/refs/heads/delete_me",
    "content": "0000000000000000000000000000000000000000 b60afe294eb3c200d646995c9e0234470157c1b0 Daniel Smilansky <dsmilansky@gmail.com> 1386225568 -0800\tbranch: Created from master\n"
  },
  {
    "path": "levels/delete_branch/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 b60afe294eb3c200d646995c9e0234470157c1b0 Daniel Smilansky <dsmilansky@gmail.com> 1386225561 -0800\tcommit (initial): first commit\n"
  },
  {
    "path": "levels/delete_branch/.githug/objects/b6/0afe294eb3c200d646995c9e0234470157c1b0",
    "content": "x\u0001\r\u0002!\u0010aT1\rh\u0018'\u0018\u000fv`\u0005\u0013\u0018\bK\u0002x{\u0018+=<_\u0001q7\u001a3\u0014'OBc#\u001bœwFh6zz\u0016\u001cLk\f2%>\n|\b\u001c\u0001ǘgNJx\u001f\u0007|%\u00061?Q"
  },
  {
    "path": "levels/delete_branch/.githug/refs/heads/delete_me",
    "content": "b60afe294eb3c200d646995c9e0234470157c1b0\n"
  },
  {
    "path": "levels/delete_branch/.githug/refs/heads/master",
    "content": "b60afe294eb3c200d646995c9e0234470157c1b0\n"
  },
  {
    "path": "levels/delete_branch/readme",
    "content": ""
  },
  {
    "path": "levels/delete_branch.rb",
    "content": "difficulty 2\n\ndescription \"You have created too many branches for your project. There is an old branch in your repo called 'delete_me', you should delete it.\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  return true unless repo.branches.map(&:name).include?('delete_me')\nend\n\nhint do\n  puts \"Running 'git --help branch' will give you a list of branch commands.\"\nend\n"
  },
  {
    "path": "levels/diff/.githug/COMMIT_EDITMSG",
    "content": "added app.rb\n"
  },
  {
    "path": "levels/diff/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/diff/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n"
  },
  {
    "path": "levels/diff/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/diff/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/diff/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/diff/.githug/hooks/post-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script that is called after a successful\n# commit is made.\n#\n# To enable this hook, rename this file to \"post-commit\".\n\n: Nothing\n"
  },
  {
    "path": "levels/diff/.githug/hooks/post-receive.sample",
    "content": "#!/bin/sh\n#\n# An example hook script for the \"post-receive\" event.\n#\n# The \"post-receive\" script is run after receive-pack has accepted a pack\n# and the repository has been updated.  It is passed arguments in through\n# stdin in the form\n#  <oldrev> <newrev> <refname>\n# For example:\n#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master\n#\n# see contrib/hooks/ for a sample, or uncomment the next line and\n# rename the file to \"post-receive\".\n\n#. /usr/share/doc/git-core/contrib/hooks/post-receive-email\n"
  },
  {
    "path": "levels/diff/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/diff/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/diff/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest \"$(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0')\"\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/diff/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n"
  },
  {
    "path": "levels/diff/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/diff/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/diff/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/diff/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 dcaa55e97af34402e84d5336da37abcccc23cba6 Gary Rennie <webmaster@gazler.com> 1331585213 +0000\tcommit (initial): added app.rb\n"
  },
  {
    "path": "levels/diff/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 dcaa55e97af34402e84d5336da37abcccc23cba6 Gary Rennie <webmaster@gazler.com> 1331585213 +0000\tcommit (initial): added app.rb\n"
  },
  {
    "path": "levels/diff/.githug/objects/4f/703ca9bd25781b6758eeb3c42ed5348610ba6d",
    "content": "x\u0001Sn0\u00143_a\u0007\u0012\u0002jZiQtKfj\u0012uZU!o\u001b6tڿ\u001aeos=Dǯ$<\\\u0002q\u0014ϩԱ~W\u0004-u2\u0015\u001e\u001d\u000br\u001a@\u0002\u0005Jq+bpGC-B3\u0015\u0004M͡\u001a\u001a\u001fs|\u001f.UUMDS)QUe+~;M'/\u0019~]YǫjujʎxCo\u00035;b'Z\u0017\"i\"\u000e}߷Y=hb{f\u0018O6a\u0002uJ`8w;容\u000fc\u0012?CXt\u0010\u001f\u001b\u001c\u0018<TF4:-\u0015c*b\u000b9$D堠f&0:nq>KnB\u001d\u0004}[݆\u0018G~\fz\u0011{U64\u001dc`\u0001\u000e #tm9x5*ǲaƒ\n\b\f\u001cF5u\u0004\r\u0006h\u0016Xa*\u0003ko\u0015T!\u0001i֠\u0013\u001a?fGMh@\u001fb\u0003X-/]\u0018Ntty+\u001by O{F]\\\u001fZ\u0004kE6W:D\u0017bXN4^\u0005ej\\G\u0010\u0016\u001eY㛜]EP~\u0002W"
  },
  {
    "path": "levels/diff/.githug/objects/dc/aa55e97af34402e84d5336da37abcccc23cba6",
    "content": "x\u0001M\n0\u0010F]\u0014\u0017J&?M\nEܹ\u0006d\u0005Ӕ\u0018\u0011=9\u001e\u001f\u0006*3`\u001cWz4B.ZS$8$f\u0011^^*\\Bme\u001csx6[>\u000eT\tPk*p}ٵj\u0011R\u0004a߇\u001a\u000f+:"
  },
  {
    "path": "levels/diff/.githug/refs/heads/master",
    "content": "dcaa55e97af34402e84d5336da37abcccc23cba6\n"
  },
  {
    "path": "levels/diff/app.rb",
    "content": "require 'sinatra'\nrequire 'oauth2'\nrequire 'json'\nenable :sessions\n\ndef client\n  OAuth2::Client.new(\"mTeZFqkCmzc8JnjKXaSww95bFFxhUpp1wwmSi8vG\", \"a9OMyEdW7JvWThHmmvFcShR9P2dyad3EGuA2ULDh\", :site => \"http://localhost:3000\")\nend\n\nget \"/auth/test\" do\n  redirect client.auth_code.authorize_url(:redirect_uri => redirect_uri)\nend\n\nget '/auth/test/callback' do\n  access_token = client.auth_code.get_token(params[:code], :redirect_uri => redirect_uri)\n  session[:access_token] = access_token.token\n  @message = \"Successfully authenticated with the server\"\n  erb :success\nend\n\nget '/yet_another' do\n  @message = get_response('data.json')\n  erb :success\nend\nget '/another_page' do\n  @message = get_response('server.json')\n  erb :another\nend\n\ndef get_response(url)\n  access_token = OAuth2::AccessToken.new(client, session[:access_token])\n  JSON.parse(access_token.get(\"/api/v1/#{url}\").body)\nend\n\n\ndef redirect_uri\n  uri = URI.parse(request.url)\n  uri.path = '/auth/test/callback'\n  uri.query = nil\n  uri.to_s\nend\n\n"
  },
  {
    "path": "levels/diff.rb",
    "content": "difficulty 2\ndescription \"Since your last commit, file `app.rb` was modified. Find out which line has changed.\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  line = request \"What is the number of the line which has changed?\"\n  return false unless line == \"26\"\n  true\nend\n\nhint do\n  puts \"You are looking for the difference since your last commit. Don't forget that running `git` on its own will list the possible commands.\"\nend\n"
  },
  {
    "path": "levels/fetch.rb",
    "content": "difficulty 2\ndescription \"Looks like a new branch was pushed into our remote repository. Get the changes without merging them with the local repository \"\n\nsetup do\n  # remember the working directory so we can come back to it later\n  cwd = Dir.pwd\n  # initialize another git repo to be used as a \"remote\"\n  tmpdir = Dir.mktmpdir\n\n  # local repo\n  repo.init\n  system \"git branch -m master\"\n\n  # adds a file to origin/master\n  FileUtils.touch \"master_file\"\n  repo.add        \"master_file\"\n  repo.commit_all 'Commits master_file'\n\n  # remote repo\n  Dir.chdir tmpdir\n  repo.init\n\n  # adds a file to origin/master\n  FileUtils.touch \"master_file\"\n  repo.add        \"master_file\"\n  repo.commit_all 'Commits master_file'\n\n  # adds remote repo\n  Dir.chdir cwd\n  `git remote add origin #{tmpdir}/.git`\n  `git fetch origin --quiet`\n  `git branch -u origin/master master 2> /dev/null`\n\n  Dir.chdir tmpdir\n  # create a new branch in the remote repo\n  `git checkout -b new_branch --quiet`\n\n  # adds a file into the new branch.  Should not be pulled into the local\n  FileUtils.touch \"file1\"\n  repo.add        \"file1\"\n  repo.commit_all 'Commits file 1'\n\nend\n\nsolution do\n  repo.init\n  result = true\n\n  # counts the number of local branches. Should equal 1\n  local_branches = repo.branches.size\n\n  # after a git fetch command, each branch will be stored in in the .git/FETCH_HEAD file. Each branch is on its own line\n  # This command will count the number of lines, which will give the number of branches\n  if File.file?('.git/FETCH_HEAD') # checks for file existence\n    num_remote = File.read(\".git/FETCH_HEAD\").split(\"\\n\").count\n  else\n    num_remote = 0\n  end\n\n  # there should be 1 local branch and 2 remote branches for a success condition\n  if local_branches == 1 and num_remote == 2\n    result = true\n  else\n    result = false\n  end\nend\n\nhint do\n  puts \"Look up the 'git fetch' command\"\nend\n"
  },
  {
    "path": "levels/find_old_branch/.githug/COMMIT_EDITMSG",
    "content": "commit another todo\n"
  },
  {
    "path": "levels/find_old_branch/.githug/HEAD",
    "content": "ref: refs/heads/kill_the_batman\n"
  },
  {
    "path": "levels/find_old_branch/.githug/ORIG_HEAD",
    "content": "05e9c01bd3c9264761dd0cde477400a2c3104642\n"
  },
  {
    "path": "levels/find_old_branch/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\tignorecase = true\n"
  },
  {
    "path": "levels/find_old_branch/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/post-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script that is called after a successful\n# commit is made.\n#\n# To enable this hook, rename this file to \"post-commit\".\n\n: Nothing\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/post-receive.sample",
    "content": "#!/bin/sh\n#\n# An example hook script for the \"post-receive\" event.\n#\n# The \"post-receive\" script is run after receive-pack has accepted a pack\n# and the repository has been updated.  It is passed arguments in through\n# stdin in the form\n#  <oldrev> <newrev> <refname>\n# For example:\n#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master\n#\n# see contrib/hooks/ for a sample, or uncomment the next line and\n# rename the file to \"post-receive\".\n\n#. /usr/share/doc/git-core/contrib/hooks/post-receive-email\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Redirect output to stderr.\nexec 1>&2\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest $(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0' | wc -c) != 0\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\n# If there are whitespace errors, print the offending file names and fail.\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/find_old_branch/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/find_old_branch/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n.DS_Store\n"
  },
  {
    "path": "levels/find_old_branch/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283192 -0600\tcommit (initial): initial commit\n6876e5b41fb693190df76b1baef6ef98623b4f1a 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283224 -0600\tcheckout: moving from cure_common_cold to kill_the_batman\n6876e5b41fb693190df76b1baef6ef98623b4f1a 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283240 -0600\tcheckout: moving from kill_the_batman to blowup_sun_for_ransom\n6876e5b41fb693190df76b1baef6ef98623b4f1a 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283272 -0600\tcheckout: moving from blowup_sun_for_ransom to solve_world_hunger\n6876e5b41fb693190df76b1baef6ef98623b4f1a 324336a8401afc8ca384eaafe6615c84d552dd2c mcramm <GMCramm@gmail.com> 1332283302 -0600\tcommit: commit todo\n324336a8401afc8ca384eaafe6615c84d552dd2c 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283316 -0600\tcheckout: moving from solve_world_hunger to kill_the_batman\n6876e5b41fb693190df76b1baef6ef98623b4f1a 894a16d6f1a48224e9006b4a6f0fe3846da19bec mcramm <GMCramm@gmail.com> 1332283505 -0600\tcommit: commit another todo\n"
  },
  {
    "path": "levels/find_old_branch/.githug/logs/refs/heads/blowup_sun_for_ransom",
    "content": "0000000000000000000000000000000000000000 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283240 -0600\tbranch: Created from HEAD\n"
  },
  {
    "path": "levels/find_old_branch/.githug/logs/refs/heads/cure_common_cold",
    "content": "0000000000000000000000000000000000000000 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283192 -0600\tcommit (initial): initial commit\n6876e5b41fb693190df76b1baef6ef98623b4f1a 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283208 -0600\tBranch: renamed refs/heads/master to refs/heads/cure_common_cold\n"
  },
  {
    "path": "levels/find_old_branch/.githug/logs/refs/heads/kill_the_batman",
    "content": "0000000000000000000000000000000000000000 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283224 -0600\tbranch: Created from HEAD\n6876e5b41fb693190df76b1baef6ef98623b4f1a 05e9c01bd3c9264761dd0cde477400a2c3104642 mcramm <GMCramm@gmail.com> 1332283348 -0600\tcommit: commit todo\n05e9c01bd3c9264761dd0cde477400a2c3104642 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283385 -0600\treset: moving to HEAD~\n6876e5b41fb693190df76b1baef6ef98623b4f1a 894a16d6f1a48224e9006b4a6f0fe3846da19bec mcramm <GMCramm@gmail.com> 1332283505 -0600\tcommit: commit another todo\n"
  },
  {
    "path": "levels/find_old_branch/.githug/logs/refs/heads/solve_world_hunger",
    "content": "0000000000000000000000000000000000000000 6876e5b41fb693190df76b1baef6ef98623b4f1a mcramm <GMCramm@gmail.com> 1332283272 -0600\tbranch: Created from HEAD\n6876e5b41fb693190df76b1baef6ef98623b4f1a 324336a8401afc8ca384eaafe6615c84d552dd2c mcramm <GMCramm@gmail.com> 1332283302 -0600\tcommit: commit todo\n"
  },
  {
    "path": "levels/find_old_branch/.githug/refs/heads/blowup_sun_for_ransom",
    "content": "6876e5b41fb693190df76b1baef6ef98623b4f1a\n"
  },
  {
    "path": "levels/find_old_branch/.githug/refs/heads/cure_common_cold",
    "content": "6876e5b41fb693190df76b1baef6ef98623b4f1a\n"
  },
  {
    "path": "levels/find_old_branch/.githug/refs/heads/kill_the_batman",
    "content": "894a16d6f1a48224e9006b4a6f0fe3846da19bec\n"
  },
  {
    "path": "levels/find_old_branch/.githug/refs/heads/solve_world_hunger",
    "content": "324336a8401afc8ca384eaafe6615c84d552dd2c\n"
  },
  {
    "path": "levels/find_old_branch/TODO",
    "content": "FIND THE JOKER\n"
  },
  {
    "path": "levels/find_old_branch/myfile.txt",
    "content": "THIS TEXT DOESN'T MATTER\n"
  },
  {
    "path": "levels/find_old_branch.rb",
    "content": "difficulty 4\ndescription \"You have been working on a branch but got distracted by a major issue. Switch back to that branch even though you forgot the name of it.\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false unless repo.head.name == \"solve_world_hunger\"\n  true\nend\n\nhint do\n  puts \"Ever played with the `git reflog` command?\"\nend\n"
  },
  {
    "path": "levels/grep/.githug/COMMIT_EDITMSG",
    "content": "Add application files.\n"
  },
  {
    "path": "levels/grep/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/grep/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\tignorecase = true\n"
  },
  {
    "path": "levels/grep/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/grep/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/grep/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/grep/.githug/hooks/post-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script that is called after a successful\n# commit is made.\n#\n# To enable this hook, rename this file to \"post-commit\".\n\n: Nothing\n"
  },
  {
    "path": "levels/grep/.githug/hooks/post-receive.sample",
    "content": "#!/bin/sh\n#\n# An example hook script for the \"post-receive\" event.\n#\n# The \"post-receive\" script is run after receive-pack has accepted a pack\n# and the repository has been updated.  It is passed arguments in through\n# stdin in the form\n#  <oldrev> <newrev> <refname>\n# For example:\n#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master\n#\n# see contrib/hooks/ for a sample, or uncomment the next line and\n# rename the file to \"post-receive\".\n\n#. /usr/share/doc/git-core/contrib/hooks/post-receive-email\n"
  },
  {
    "path": "levels/grep/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/grep/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/grep/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest \"$(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0')\"\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/grep/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n"
  },
  {
    "path": "levels/grep/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/grep/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/grep/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/grep/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 12c702f8b25b6b528cf904670b854dba3eba0f45 Thibaud Colas <thibaudcolas@gmail.com> 1384229258 +0100\tcommit (initial): Add application files.\n"
  },
  {
    "path": "levels/grep/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 12c702f8b25b6b528cf904670b854dba3eba0f45 Thibaud Colas <thibaudcolas@gmail.com> 1384229258 +0100\tcommit (initial): Add application files.\n"
  },
  {
    "path": "levels/grep/.githug/objects/12/c702f8b25b6b528cf904670b854dba3eba0f45",
    "content": "x\u0001K\n0\u0010@]\u0014\u0017JЀ\u0015dj\u0003)mz+{՚;\u0018O}\u0015\u0001f\u0012[1:Dޙ,D)rd\u0015}n+<L'x\u001b\\\u000fKW\\\u0006n\nƍh\bgmV=]/{JR2c\rS.\r\u0003DB\""
  },
  {
    "path": "levels/grep/.githug/objects/6f/45753f4a16b69f5b2215f2dbe8245f073353cc",
    "content": "x\u0001e1k0\u0010;W:K\u000b!CC\u0016@]n%\u0004E>\u0003Eg,ن8!d\u0010B}FXQ]lŧ\u0019o\u000eJkrN\u001aVi\u000eL/M\u0019*e\u000fc\u000bf\u0001=u\u001a$`\u0017X+\u0005s|\u0015\u001f\u0005>#\\u/PiQq\u0006*6\u001cG\\\u0018\u0006dYQ&%４jh\u0016)}N)QlGm\u000fh"
  },
  {
    "path": "levels/grep/.githug/refs/heads/master",
    "content": "12c702f8b25b6b528cf904670b854dba3eba0f45\n"
  },
  {
    "path": "levels/grep/app.rb",
    "content": "require 'sinatra'\nrequire 'oauth2'\nrequire 'json'\nenable :sessions\n\n# TODO Make site url variable.\ndef client\n  OAuth2::Client.new(\"mTeZFqkCmzc8JnjKXaSww95bFFxhUpp1wwmSi8vG\", \"a9OMyEdW7JvWThHmmvFcShR9P2dyad3EGuA2ULDh\", :site => \"http://localhost:3000\")\nend\n\nget \"/auth/test\" do\n  redirect client.auth_code.authorize_url(:redirect_uri => redirect_uri)\nend\n\nget '/auth/test/callback' do\n  access_token = client.auth_code.get_token(params[:code], :redirect_uri => redirect_uri)\n  session[:access_token] = access_token.token\n  @message = \"Successfully authenticated with the server\"\n  erb :success\nend\n\nget '/yet_another' do\n  @message = get_response('data.json')\n  erb :success\nend\n\nget '/another_page' do\n  @message = get_response('server.json')\n  erb :another\nend\n\n# TODO Make API version variable.\ndef get_response(url)\n  access_token = OAuth2::AccessToken.new(client, session[:access_token])\n  JSON.parse(access_token.get(\"/api/v1/#{url}\").body)\nend\n\n# TODO Redirecting queries could be useful.\ndef redirect_uri\n  uri = URI.parse(request.url)\n  uri.path = '/auth/test/callback'\n  uri.query = nil\n  uri.to_s\nend\n\n"
  },
  {
    "path": "levels/grep/config.rb",
    "content": "class Config\n  attr_accessor :name, :password\n  def initialize(name, password = nil, options = {})\n    @name = name\n    # TODO Move password to a configuration file.\n    @password = password || \"i<3evil\"\n\n    if options[:downcase]\n      @name.downcase!\n    end\n\n    if options[:upcase]\n      @name.upcase!\n    end\n\n  end\nend\n"
  },
  {
    "path": "levels/grep.rb",
    "content": "difficulty 2\ndescription \"Your project's deadline approaches, you should evaluate how many TODOs are left in your code\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  request(\"How many items are there in your todolist?\") == \"4\"\nend\n\nhint do\n  puts \"You want to research the `git grep` command.\"\nend\n"
  },
  {
    "path": "levels/ignore.rb",
    "content": "difficulty 2\ndescription \"The text editor 'vim' creates files ending in `.swp` (swap files) for all files that are currently open. We don't want them creeping into the repository. Make this repository ignore those swap files which are ending in `.swp`.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"README.swp\")\n  system \"git branch -m master\"\n  file = File.open(\".git/config\", \"w\") do |file|\n    file.puts \"[core]\\nexcludesfile=\"\n  end\nend\n\nsolution do\n\n  valid = false\n\n\n  File.open(\".gitignore\", \"r\") do |file|\n    while line = file.gets\n      if line.chomp == \"*.swp\"\n        valid = true\n      end\n    end\n  end\n\n  valid\nend\n\nhint do\n  puts \"You may have noticed there is a file named `.gitignore` in the repository.\"\nend\n"
  },
  {
    "path": "levels/include.rb",
    "content": "difficulty 2\ndescription \"Notice a few files with the '.a' extension. We want git to ignore all the files except the 'lib.a' file.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"first.a\")\n  FileUtils.touch(\"second.a\")\n  FileUtils.touch(\"lib.a\")\n  system \"git branch -m master\"\n  file = File.open(\".git/config\", \"w\") do |file|\n    file.puts \"[core]\\nexcludesfile=\"\n  end\nend\n\nsolution do\n  entries = File.readlines('.gitignore').map(&:chomp)\n  entries.include? '*.a' and entries.include? '!lib.a'\nend\n\nhint do\n  puts \"Using `git help ignore`, read about the optional prefix to negate a pattern.\"\nend\n"
  },
  {
    "path": "levels/init.rb",
    "content": "difficulty 1\ndescription \"A new directory, `git_hug`, has been created; initialize an empty repository in it.\"\n\nsolution do\n  repo.valid?\nend\n\nhint do\n  puts \"You can type `git --help` or  `git` in your shell to get a list of available git commands.\"\nend\n"
  },
  {
    "path": "levels/log.rb",
    "content": "difficulty 2\n\ndescription \"Identify the hash of the latest commit.\"\n\nsetup do\n  repo.init\n  file = File.new(\"newfile.rb\", \"w\")\n  repo.add(\"newfile.rb\")\n  repo.commit_all(\"THIS IS THE COMMIT YOU ARE LOOKING FOR!\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  repo.commits.last.id_abbrev == request(\"What is the hash of the most recent commit?\")[0..6]\nend\n\nhint do\n  puts \"You need to investigate the logs. There is probably a command for doing that!\"\nend\n"
  },
  {
    "path": "levels/merge/.githug/COMMIT_EDITMSG",
    "content": "added file2\n"
  },
  {
    "path": "levels/merge/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/merge/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\tignorecase = true\n"
  },
  {
    "path": "levels/merge/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/merge/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/merge/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/merge/.githug/hooks/post-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script that is called after a successful\n# commit is made.\n#\n# To enable this hook, rename this file to \"post-commit\".\n\n: Nothing\n"
  },
  {
    "path": "levels/merge/.githug/hooks/post-receive.sample",
    "content": "#!/bin/sh\n#\n# An example hook script for the \"post-receive\" event.\n#\n# The \"post-receive\" script is run after receive-pack has accepted a pack\n# and the repository has been updated.  It is passed arguments in through\n# stdin in the form\n#  <oldrev> <newrev> <refname>\n# For example:\n#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master\n#\n# see contrib/hooks/ for a sample, or uncomment the next line and\n# rename the file to \"post-receive\".\n\n#. /usr/share/doc/git-core/contrib/hooks/post-receive-email\n"
  },
  {
    "path": "levels/merge/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/merge/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/merge/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest \"$(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0')\"\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/merge/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/opt/local/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n"
  },
  {
    "path": "levels/merge/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /opt/local/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /opt/local/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/merge/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/merge/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/merge/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 e12277fe88657a072f1c4eb7d9320e4e6a74ba95 Dustin Rodrigues <dust.rod@gmail.com> 1331884867 -0700\tcommit (initial): added file1\ne12277fe88657a072f1c4eb7d9320e4e6a74ba95 e12277fe88657a072f1c4eb7d9320e4e6a74ba95 Dustin Rodrigues <dust.rod@gmail.com> 1331884877 -0700\tcheckout: moving from master to feature\ne12277fe88657a072f1c4eb7d9320e4e6a74ba95 cc8ea5a233df119d025eb240b9470e1ca76a151c Dustin Rodrigues <dust.rod@gmail.com> 1331884901 -0700\tcommit: added file2\ncc8ea5a233df119d025eb240b9470e1ca76a151c e12277fe88657a072f1c4eb7d9320e4e6a74ba95 Dustin Rodrigues <dust.rod@gmail.com> 1331884911 -0700\tcheckout: moving from feature to master\n"
  },
  {
    "path": "levels/merge/.githug/logs/refs/heads/feature",
    "content": "0000000000000000000000000000000000000000 e12277fe88657a072f1c4eb7d9320e4e6a74ba95 Dustin Rodrigues <dust.rod@gmail.com> 1331884877 -0700\tbranch: Created from HEAD\ne12277fe88657a072f1c4eb7d9320e4e6a74ba95 cc8ea5a233df119d025eb240b9470e1ca76a151c Dustin Rodrigues <dust.rod@gmail.com> 1331884901 -0700\tcommit: added file2\n"
  },
  {
    "path": "levels/merge/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 e12277fe88657a072f1c4eb7d9320e4e6a74ba95 Dustin Rodrigues <dust.rod@gmail.com> 1331884867 -0700\tcommit (initial): added file1\n"
  },
  {
    "path": "levels/merge/.githug/refs/heads/feature",
    "content": "cc8ea5a233df119d025eb240b9470e1ca76a151c\n"
  },
  {
    "path": "levels/merge/.githug/refs/heads/master",
    "content": "e12277fe88657a072f1c4eb7d9320e4e6a74ba95\n"
  },
  {
    "path": "levels/merge/file1",
    "content": ""
  },
  {
    "path": "levels/merge.rb",
    "content": "difficulty 2\ndescription \"We have a file in the branch 'feature'. Let's merge it with the master branch.\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  File.exists?(\"file1\")\t&& File.exists?(\"file2\")\nend\n\nhint do\n  puts \"You want to research the `git merge` command.\"\nend\n"
  },
  {
    "path": "levels/merge_squash.rb",
    "content": "difficulty 3\ndescription \"Merge all commits from the long-feature-branch as a single commit.\"\n\nsetup do\n  repo.init\n\n  FileUtils.touch \"file1\"\n  repo.add        \"file1\"\n  repo.commit_all \"First commit\"\n  system \"git branch -m master\"\n\n  repo.git.native :checkout, {\"b\" => true}, 'long-feature-branch'\n  File.open(\"file3\", 'w') { |f| f << \"some feature\\n\" }\n  repo.add        \"file3\"\n  repo.commit_all \"Developing new features\"\n\n  File.open(\"file3\", 'a') { |f| f << \"getting awesomer\\n\" }\n  repo.add        \"file3\"\n  repo.commit_all \"Takes\"\n\n  File.open(\"file3\", 'a') { |f| f << \"and awesomer!\\n\" }\n  repo.add        \"file3\"\n  repo.commit_all \"Time\"\n\n  repo.git.native :checkout, {}, 'master'\n\n  FileUtils.touch \"file2\"\n  repo.add        \"file2\"\n  repo.commit_all \"Second commit\"\nend\n\nsolution do\n  result = true\n\n  # Check the number of commits in the repo (should be 4 - including initial .gitignore).\n  result = false unless repo.commits.size == 3\n\n  # Check if changes from all the commits from long-feature-branch are included.\n  file = File.open('file3')\n  result = false unless file.readline =~ /some feature/\n  result = false unless file.readline =~ /getting awesomer/\n  result = false unless file.readline =~ /and awesomer!/\n  file.close\n\n  result\nend\n\nhint do\n  puts \"Take a look at the `--squash` option of the merge command. Don't forget to commit the merge!\"\nend\n"
  },
  {
    "path": "levels/number_of_files_committed.rb",
    "content": "difficulty 1\ndescription \"There are some files in this repository; how many of them are staged for a commit?\"\n\nsetup do\n  repo.init\n\n  # Modified files\n  %w{rubyfile4.rb rubyfile5.rb}.each do |file|\n    FileUtils.touch(file)\n    repo.add(file)\n    system \"git branch -m master\"\n  end\n  repo.commit_all \"Commit\"\n\n  # Staged file\n  File.open(\"rubyfile4.rb\", 'w') { |f| f << \"#Changes\" }\n  repo.add(\"rubyfile4.rb\")\n\n  # Not staged file\n  File.open(\"rubyfile5.rb\", 'w') { |f| f << \"#Changes\" }\n\n  # Changes to be committed\n  %w{rubyfile1.rb}.each do |file|\n    FileUtils.touch(file)\n    repo.add(file)\n  end\n\n  # Untracked files\n  %w{rubyfile6.rb rubyfile7.rb}.each do |file|\n    FileUtils.touch(file)\n  end\nend\n\nsolution do\n  numberOfFilesThereWillBeCommit = request(\"How many files are going to be committed?\")\n\n  isInteger = !!(numberOfFilesThereWillBeCommit =~ /^[-+]?[0-9]+$/)\n\n  if !isInteger\n    return false\n  end\n\n  if numberOfFilesThereWillBeCommit.to_i == 2\n    return true\n  end\n\n  return false\nend\n\nhint do\n  puts \"You are looking for a command to identify the status of the repository (resembles a Linux command).\"\nend\n"
  },
  {
    "path": "levels/pull.rb",
    "content": "difficulty 2\n\ndescription \"You need to pull changes from your origin repository.\"\n\nsetup do\n  repo.init\n  repo.remote_add(\"origin\", \"https://github.com/pull-this/thing-to-pull\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  repo.commits.last.id_abbrev == \"1797a7c\"\nend\n\nhint do\n  puts \"Check out the remote repositories and research `git pull`.\"\nend\n"
  },
  {
    "path": "levels/push.rb",
    "content": "difficulty 3\ndescription \"Your local master branch has diverged from \" +\n  \"the remote origin/master branch. Rebase your branch onto \" +\n  \"origin/master and push it to remote.\"\n\nsetup do\n  # remember the working directory so we can come back to it later\n  cwd = Dir.pwd\n  # initialize another git repo to be used as a \"remote\"\n  tmpdir = Dir.mktmpdir\n\n  # local repo\n  repo.init\n\n  FileUtils.touch \"file1\"\n  repo.add        \"file1\"\n  repo.commit_all \"First commit\"\n\n  FileUtils.touch \"file2\"\n  repo.add        \"file2\"\n  repo.commit_all \"Second commit\"\n\n  # copy the repo to remote\n  FileUtils.cp \"file1\", tmpdir\n  FileUtils.cp \"file2\", tmpdir\n\n  # add another file\n  FileUtils.touch \"file3\"\n  repo.add        \"file3\"\n  repo.commit_all \"Third commit\"\n  system \"git branch -m master\"\n\n  # remote repo\n  Dir.chdir tmpdir\n  repo.init\n  # make a 'non-bare' repo accept pushes\n  `git config receive.denyCurrentBranch ignore`\n\n  # add a different file and commit so remote and local would diverge\n  FileUtils.touch \"file4\"\n  repo.add        \"file4\"\n  repo.commit_all \"Fourth commit\"\n  system \"git branch -m master\"  # tentative addition\n\n  # change back to original repo to set up a remote\n  Dir.chdir cwd\n  `git remote add origin #{tmpdir}/.git`\n  `git fetch origin`\n  `git branch -u origin/master master 2> /dev/null`\nend\n\nsolution do\n  repo.init\n  result = true\n\n  # Check the commits of the local branch and the branch are the same.\n  local_commits = repo.commits(\"master\")\n  remote_commits = repo.commits(\"origin/master\")\n  result = false unless local_commits.size == 4\n  local_commits.each_with_index do |commit, idx|\n    result &&= (commit.id == remote_commits[idx].id)\n  end\n\n  result\nend\n\nhint do\n  puts \"Take a look at `git fetch`, `git pull`, and `git push`.\"\nend\n"
  },
  {
    "path": "levels/push_branch.rb",
    "content": "difficulty 2\ndescription \"You've made some changes to a local branch and want to share it, but aren't yet ready to merge it with the 'master' branch. Push only 'test_branch' to the remote repository\"\n\nsetup do\n\n  # remember the working directory so we can come back to it later\n  cwd = Dir.pwd\n  # initialize another git repo to be used as a \"remote\"\n  tmpdir = Dir.mktmpdir\n\n  # local repo\n  repo.init\n\n  FileUtils.touch \"file1\"\n  repo.add        \"file1\"\n  repo.commit_all \"committed changes on master\"\n  system \"git branch -m master\"\n\n  # copy the repo to remote\n  FileUtils.cp_r \".\", tmpdir\n\n  # add another file. If successful this file won't be pushed to the remote repository\n  FileUtils.touch \"file2\"\n  repo.add        \"file2\"\n  repo.commit_all \"If this commit gets pushed to repo, then you have lost the level :( \"\n\n  # This branch should not be pushed to to the remote repository\n  `git checkout -b other_branch --quiet`\n  # add another file\n  FileUtils.touch \"file3\"\n  repo.add        \"file3\"\n  repo.commit_all \"If this commit gets pushed to repo, then you have lost the level :( \"\n\n  `git checkout -b test_branch --quiet`\n\n  # This file should get pushed if the level is successful\n  FileUtils.touch \"file4\"\n  repo.add        \"file4\"\n  repo.commit_all \"committed change on test_branch\"\n\n  # remote repo\n  Dir.chdir tmpdir\n\n  repo.init\n\n  # make a 'non-bare' repo accept pushes\n  `git config receive.denyCurrentBranch ignore`\n\n  # change back to original repo to set up a remote\n  Dir.chdir cwd\n  `git remote add origin #{tmpdir}/.git`\n  `git fetch --quiet origin`\n  `git branch -u origin/master master 2> /dev/null`\n\n  `git checkout master --quiet` # return to master branch\nend\n\nsolution do\n  repo.init\n  result = false\n\n  # each branch consists of one line, `wc -l` counts the number of lines in order to get the number of remote branches\n  # At the moment Grit doesn't support remote branch references but is on the ToDo list. This should be revisited when Grit implements the change\n  num_remote_branches = `git branch -r`.split(\"\\n\").count\n\n  # counts the number of commits in the remote master branch'\n  remote_master_commits = repo.commits('origin/master').count\n  remote_test_branch_commits = repo.commits('origin/test_branch').count # if returns 0 indicates that the remote test_branch doesn't exist\n\n  # Level will be successful if the remote master branch remains at 1 commit, the remote test_branch and only 2 remote branches exists\n  if remote_master_commits == 1 and remote_test_branch_commits > 0 and num_remote_branches == 2\n    result = true\n\n  # User pushed up too many branches, level failed\n  elsif num_remote_branches > 2\n    puts \"*** It looks like you pushed up too many branches. You need to make sure only 'test_branch' gets pushed. Please try again! ***\"\n\n  # User pushed up the master branch, level failed\n  elsif remote_master_commits > 1\n    puts \"*** It looks like you pushed up new master branch changes. You need to make sure only 'test_branch' gets pushed. Please try again! ***\"\n  end\n\n  result\nend\n\nhint do\n  puts \"Investigate the options in `git push` using `git push --help`\"\nend\n"
  },
  {
    "path": "levels/push_tags.rb",
    "content": "difficulty 2\ndescription \"A tag in the local repository isn't pushed into remote repository. Push it now.\"\n\nsetup do\n  # remember the working directory so we can come back to it later\n  cwd = Dir.pwd\n  # initialize another git repo to be used as a \"remote\"\n  tmpdir = Dir.mktmpdir\n\n  # local repo\n  repo.init\n\n  FileUtils.touch \"file1\"\n  repo.add        \"file1\"\n  repo.commit_all \"First commit\"\n  repo.git.tag({'f' => true}, \"tag_to_be_pushed\")\n  system \"git branch -m master\"\n\n  FileUtils.touch \"file2\"\n  repo.add        \"file2\"\n  repo.commit_all \"Second commit\"\n\n  # copy the repo to remote\n  FileUtils.cp_r \".\", tmpdir\n\n  # remote repo\n  Dir.chdir tmpdir\n  repo.init\n  # make a 'non-bare' repo accept pushes\n  `git config receive.denyCurrentBranch ignore`\n\n  # change back to original repo to set up a remote\n  Dir.chdir cwd\n  `git remote add origin #{tmpdir}/.git`\n  `git fetch origin`\n\n  # delete tags from remote\n  Dir.chdir tmpdir\n  repo.git.tag({'d' => true}, \"tag_to_be_pushed\")\n\n  # change back to local repo\n  Dir.chdir cwd\nend\n\nsolution do\n  solved = false\n\n  # a bit hacky solution to get tags from remote\n  remote_tags=\n    repo.git.raw_git_call(\"git ls-remote --tags .\", repo.git.git_file_index).\n      first.\n      split(\"\\n\")\n\n  # see if we have the correct tag in the remote\n  remote_tags.each do |t|\n    solved=true if t.include?(\"refs/tags/tag_to_be_pushed\")\n  end\n\n  solved\nend\n\nhint do\n  puts \"Take a look at `--tags` flag of `git push`\"\nend\n"
  },
  {
    "path": "levels/rebase/.githug/COMMIT_EDITMSG",
    "content": "add content\n"
  },
  {
    "path": "levels/rebase/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/rebase/.githug/ORIG_HEAD",
    "content": "4419b972c0cd1b346ac90332aa7c5cc949589f78\n"
  },
  {
    "path": "levels/rebase/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\tignorecase = true\n\tprecomposeunicode = true\n"
  },
  {
    "path": "levels/rebase/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 a78bcab6232e9382a86436cdfcb2ed0391b1f0ac ipmsteven <steven.lyl147@gmail.com> 1418373185 -0800\tcommit (initial): init commit\na78bcab6232e9382a86436cdfcb2ed0391b1f0ac a78bcab6232e9382a86436cdfcb2ed0391b1f0ac ipmsteven <steven.lyl147@gmail.com> 1418373194 -0800\tcheckout: moving from master to feature\na78bcab6232e9382a86436cdfcb2ed0391b1f0ac ed0fdcf366b21b8984fb37ea34106978a2e5c5ba ipmsteven <steven.lyl147@gmail.com> 1418373239 -0800\tcommit: add feature\ned0fdcf366b21b8984fb37ea34106978a2e5c5ba a78bcab6232e9382a86436cdfcb2ed0391b1f0ac ipmsteven <steven.lyl147@gmail.com> 1418373246 -0800\tcheckout: moving from feature to master\na78bcab6232e9382a86436cdfcb2ed0391b1f0ac 98205e9faf10cf33d2ef7c0f66e402540c62613a ipmsteven <steven.lyl147@gmail.com> 1418373270 -0800\tcommit: add content\n98205e9faf10cf33d2ef7c0f66e402540c62613a ed0fdcf366b21b8984fb37ea34106978a2e5c5ba ipmsteven <steven.lyl147@gmail.com> 1418373282 -0800\tcheckout: moving from master to feature\ned0fdcf366b21b8984fb37ea34106978a2e5c5ba 98205e9faf10cf33d2ef7c0f66e402540c62613a ipmsteven <steven.lyl147@gmail.com> 1418373314 -0800\trebase: checkout master\n98205e9faf10cf33d2ef7c0f66e402540c62613a 4419b972c0cd1b346ac90332aa7c5cc949589f78 ipmsteven <steven.lyl147@gmail.com> 1418373314 -0800\trebase: add feature\n4419b972c0cd1b346ac90332aa7c5cc949589f78 4419b972c0cd1b346ac90332aa7c5cc949589f78 ipmsteven <steven.lyl147@gmail.com> 1418373314 -0800\trebase finished: returning to refs/heads/feature\n4419b972c0cd1b346ac90332aa7c5cc949589f78 98205e9faf10cf33d2ef7c0f66e402540c62613a ipmsteven <steven.lyl147@gmail.com> 1418373423 -0800\tcheckout: moving from feature to master\n98205e9faf10cf33d2ef7c0f66e402540c62613a 4419b972c0cd1b346ac90332aa7c5cc949589f78 ipmsteven <steven.lyl147@gmail.com> 1418373435 -0800\tcheckout: moving from master to feature\n4419b972c0cd1b346ac90332aa7c5cc949589f78 ed0fdcf366b21b8984fb37ea34106978a2e5c5ba ipmsteven <steven.lyl147@gmail.com> 1418373492 -0800\treset: moving to ed0fdcf\ned0fdcf366b21b8984fb37ea34106978a2e5c5ba 98205e9faf10cf33d2ef7c0f66e402540c62613a ipmsteven <steven.lyl147@gmail.com> 1418373509 -0800\tcheckout: moving from feature to master\n"
  },
  {
    "path": "levels/rebase/.githug/logs/refs/heads/feature",
    "content": "0000000000000000000000000000000000000000 a78bcab6232e9382a86436cdfcb2ed0391b1f0ac ipmsteven <steven.lyl147@gmail.com> 1418373194 -0800\tbranch: Created from HEAD\na78bcab6232e9382a86436cdfcb2ed0391b1f0ac ed0fdcf366b21b8984fb37ea34106978a2e5c5ba ipmsteven <steven.lyl147@gmail.com> 1418373239 -0800\tcommit: add feature\ned0fdcf366b21b8984fb37ea34106978a2e5c5ba 4419b972c0cd1b346ac90332aa7c5cc949589f78 ipmsteven <steven.lyl147@gmail.com> 1418373314 -0800\trebase finished: refs/heads/feature onto 98205e9faf10cf33d2ef7c0f66e402540c62613a\n4419b972c0cd1b346ac90332aa7c5cc949589f78 ed0fdcf366b21b8984fb37ea34106978a2e5c5ba ipmsteven <steven.lyl147@gmail.com> 1418373492 -0800\treset: moving to ed0fdcf\n"
  },
  {
    "path": "levels/rebase/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 a78bcab6232e9382a86436cdfcb2ed0391b1f0ac ipmsteven <steven.lyl147@gmail.com> 1418373185 -0800\tcommit (initial): init commit\na78bcab6232e9382a86436cdfcb2ed0391b1f0ac 98205e9faf10cf33d2ef7c0f66e402540c62613a ipmsteven <steven.lyl147@gmail.com> 1418373270 -0800\tcommit: add content\n"
  },
  {
    "path": "levels/rebase/.githug/objects/98/205e9faf10cf33d2ef7c0f66e402540c62613a",
    "content": "x\u0001Q\n\u0002!\u0010@\u0014^P\u001c2:c-Y[\u0006}=x4\u0003s\u0018Έ`1\u000b\u0010LbvH\u0002cea<Zieh\u000bI(\u0006\u000fp-\t\u001bH6j(zG琷,ihOF@phDcn!折u؇\u0017}IY"
  },
  {
    "path": "levels/rebase/.githug/objects/a7/8bcab6232e9382a86436cdfcb2ed0391b1f0ac",
    "content": "x\u0001Q\n\u0002!\u0014Ev\u0015o\u0003\r>\n\u0011mE}R1Y\u0013f\u0007}]pV\u0006S\fFS1GNkdtT\ni7`,\u0013\b;g{\u001b\\\\귢{\u000b.+FG\u00198K'픎'.6/\u001f#\u0007;"
  },
  {
    "path": "levels/rebase/.githug/refs/heads/feature",
    "content": "ed0fdcf366b21b8984fb37ea34106978a2e5c5ba\n"
  },
  {
    "path": "levels/rebase/.githug/refs/heads/master",
    "content": "98205e9faf10cf33d2ef7c0f66e402540c62613a\n"
  },
  {
    "path": "levels/rebase/README",
    "content": "readme\n"
  },
  {
    "path": "levels/rebase.rb",
    "content": "difficulty 2\n\ndescription \"We are using a git rebase workflow and the feature branch is ready to go into master. Let's rebase the feature branch onto our master branch.\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  return repo.commits('feature').last.id_abbrev != \"ed0fdcf\" &&\n    repo.commits(\"feature\").map(&:message) == ['add feature','add content','init commit']\nend\n\nhint do\n  puts \"You want to research the `git rebase` command\"\nend\n"
  },
  {
    "path": "levels/rebase_onto.rb",
    "content": "difficulty 2\n\ndescription \"You have created your branch from `wrong_branch` and already made some commits, \\\nand you realise that you needed to create your branch from `master`. \\\nRebase your commits onto `master` branch so that you don't have `wrong_branch` commits.\"\n\nsetup do\n  readme_file  = \"README.md\"\n  authors_file = \"authors.md\"\n\n  repo.init\n  FileUtils.touch(authors_file)\n  File.open(authors_file, \"w\") { |f| f << \"https://github.com/janis-vitols\\n\" }\n  repo.add(authors_file)\n  repo.commit_all(\"Create authors file\")\n  system \"git branch -m master\"\n\n  repo.git.native :checkout, { \"b\" => true }, \"wrong_branch\"\n  File.open(authors_file, \"w\") { |f| f << \"None\\n\" }\n  repo.add(authors_file)\n  repo.commit_all(\"Wrong changes\")\n\n  repo.git.native :checkout, { \"b\" => true }, \"readme-update\"\n  FileUtils.touch(readme_file)\n  File.open(readme_file, \"a\") { |f| f << \"# SuperApp\\n\" }\n  repo.add(readme_file)\n  repo.commit_all(\"Add app name in readme\")\n  File.open(readme_file, \"a\") { |f| f << \"## About\\n\" }\n  repo.add(readme_file)\n  repo.commit_all(\"Add `About` header in readme\")\n  File.open(readme_file, \"a\") { |f| f << \"## Install\\n\" }\n  repo.add(readme_file)\n  repo.commit_all(\"Add `Install` header in readme\")\nend\n\nsolution do\n  repo.commits(\"readme-update\").each { |commit| return false if commit.message ==  \"Wrong changes\" }\n  return false unless repo.commits(\"readme-update\").length == 4\n  return false unless File.readlines(\"authors.md\").include?(\"https://github.com/janis-vitols\\n\")\n\n  true\nend\n\nhint do\n  puts \"You want to research the `git rebase` commands `--onto` argument\"\nend\n"
  },
  {
    "path": "levels/remote.rb",
    "content": "difficulty 2\n\ndescription \"This project has a remote repository. Identify it.\"\n\nsetup do\n  repo.init\n  repo.remote_add(\"my_remote_repo\", \"https://github.com/Gazler/githug\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  \"my_remote_repo\" == request(\"What is the name of the remote repository?\")\nend\n\nhint do\n  puts \"You are looking for a remote. You can run `git` for a list of commands.\"\nend\n"
  },
  {
    "path": "levels/remote_add.rb",
    "content": "difficulty 2\n\ndescription \"Add a remote repository called `origin` with the url https://github.com/githug/githug\"\n\nsetup do\n  repo.init\n  system \"git branch -m master\"\nend\n\nsolution do\n  result = `git remote -v`\n  result.include?(\"https://github.com/githug/githug\")\nend\n\nhint do\n  puts \"You can run `git remote --help` for the man pages.\"\nend\n"
  },
  {
    "path": "levels/remote_url.rb",
    "content": "difficulty 2\n\ndescription \"The remote repositories have a url associated to them. Please enter the url of remote_location.\"\n\nsetup do\n  repo.init\n  repo.remote_add(\"my_remote_repo\", \"https://github.com/Gazler/githug\")\n  repo.remote_add(\"remote_location\", \"https://github.com/githug/not_a_repo\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  !!(request(\"What is the url of the remote repository?\") =~ /https:\\/\\/github.com\\/githug\\/not_a_repo\\/?/)\nend\n\nhint do\n  puts \"You can run `git remote --help` for the man pages.\"\nend\n"
  },
  {
    "path": "levels/rename.rb",
    "content": "difficulty 3\n\ndescription \"We have a file called `oldfile.txt`. We want to rename it to `newfile.txt` and stage this change.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"oldfile.txt\")\n  repo.add(\"oldfile.txt\")\n  repo.commit_all(\"Commited oldfile.txt\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  repo.status[\"oldfile.txt\"].type == \"D\" && repo.status[\"newfile.txt\"].type == \"A\" && repo.status[\"oldfile.txt\"].stage.nil?\nend\n\nhint do\n  puts \"Take a look at `git mv`.\"\nend\n"
  },
  {
    "path": "levels/rename_commit.rb",
    "content": "difficulty 3\ndescription \"Correct the typo in the message of your first (non-root) commit.\"\n\nsetup do\n  repo.init\n  FileUtils.touch \"README\"\n  repo.add        \"README\"\n  repo.commit_all \"Initial commit\"\n  system \"git branch -m master\"\n\n  FileUtils.touch \"file1\"\n  repo.add        \"file1\"\n  repo.commit_all \"First coommit\"\n\n  FileUtils.touch \"file2\"\n  repo.add        \"file2\"\n  repo.commit_all \"Second commit\"\nend\n\nsolution do\n  repo.commits.first.parents[0].message == \"First commit\"\nend\n\nhint do\n  puts \"Take a look the `-i` flag of the rebase command.\"\nend\n"
  },
  {
    "path": "levels/reorder.rb",
    "content": "difficulty 4\ndescription \"You have committed several times but in the wrong order. Please reorder your commits.\"\n\nsetup do\n  repo.init\n\n  FileUtils.touch \"README\"\n  repo.add        \"README\"\n  repo.commit_all \"Initial Setup\"\n  system \"git branch -m master\"\n\n  FileUtils.touch \"file1\"\n  repo.add        \"file1\"\n  repo.commit_all \"First commit\"\n\n  FileUtils.touch \"file3\"\n  repo.add        \"file3\"\n  repo.commit_all \"Third commit\"\n\n  FileUtils.touch \"file2\"\n  repo.add        \"file2\"\n  repo.commit_all \"Second commit\"\nend\n\nsolution do\n    `git log --format=\"%s\"`.split.join(\"\").match /Third.*Second.*First.*Initial/\nend\n\nhint do\n  puts \"Take a look the `-i` flag of the rebase command.\"\nend\n"
  },
  {
    "path": "levels/repack.rb",
    "content": "difficulty 2\ndescription \"Optimise how your repository is packaged ensuring that redundant packs are removed.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"foo\")\n  repo.add(\"foo\")\n  repo.commit_all(\"Added foo\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  result = `git count-objects -v`\n  required = [\"count: 0\", \"prune-packable: 0\"];\n  required.all? { |r| result.include?(r) }\nend\n\nhint do\n  puts \"You want to research the `git repack` command.\"\nend\n"
  },
  {
    "path": "levels/reset.rb",
    "content": "difficulty 2\ndescription \"There are two files to be committed. The goal was to add each file as a separate commit, however both were added by accident. Unstage the file `to_commit_second.rb` using the reset command (don't commit anything).\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"README\")\n  repo.add(\"README\")\n  repo.commit_all(\"Initial commit\")\n  FileUtils.touch(\"to_commit_first.rb\")\n  FileUtils.touch(\"to_commit_second.rb\")\n  repo.add(\".\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false unless (repo.status.files[\"to_commit_second.rb\"].nil? || repo.status.files[\"to_commit_second.rb\"].stage.nil?) && File.exists?(\"to_commit_second.rb\")\n  return false if (repo.status.files[\"to_commit_first.rb\"].nil? || repo.status.files[\"to_commit_first.rb\"].stage.nil?)\n  true\nend\n\nhint do\n  puts \"You can get some useful information for git status, it will tell you the command you need to run.\"\nend\n"
  },
  {
    "path": "levels/reset_soft.rb",
    "content": "difficulty 2\ndescription \"You committed too soon. Now you want to undo the last commit, while keeping the index.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"README\")\n  repo.add(\"README\")\n  repo.commit_all(\"Initial commit\")\n  FileUtils.touch(\"newfile.rb\")\n  repo.add(\"newfile.rb\")\n  repo.commit_all(\"Premature commit\")\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false unless File.exists?(\"newfile.rb\") && repo.status.files.keys.include?(\"newfile.rb\")\n  return false if repo.status.files[\"newfile.rb\"].untracked || repo.commit_count > 1\n  true\nend\n\nhint do\n  puts \"What are some options you can use with `git reset`?\"\nend\n"
  },
  {
    "path": "levels/restore.rb",
    "content": "difficulty 4\ndescription \"You decided to delete your latest commit by running `git reset --hard HEAD^` (not a smart thing to do). Now you changed your mind and want that commit back. Restore the deleted commit.\"\n\nsetup do\n  repo.init\n  FileUtils.touch 'file1'\n  repo.add        'file1'\n  repo.commit_all 'Initial commit'\n  system \"git branch -m master\"\n\n  FileUtils.touch 'file2'\n  repo.add        'file2'\n  repo.commit_all 'First commit'\n\n  FileUtils.touch 'file3'\n  repo.add        'file3'\n  repo.commit_all 'Restore this commit'\n\n  repo.git.native :reset, { \"hard\" => true }, 'HEAD^'\nend\n\nsolution do\n  return false unless File.exists?('file3')\n  true\nend\n\nhint do\n  puts \"The commit is still floating around somewhere. Have you checked out `git reflog`?\"\nend\n"
  },
  {
    "path": "levels/restructure.rb",
    "content": "difficulty 3\n\ndescription \"You added some files to your repository, but now realize that your project needs to be restructured. Make a new folder named `src` and use Git move all of the .html files into this folder.\"\n\nsetup do\n  repo.init\n\n  FileUtils.touch(\"about.html\")\n  FileUtils.touch(\"contact.html\")\n  FileUtils.touch(\"index.html\")\n\n  repo.add(\"about.html\")\n  repo.add(\"contact.html\")\n  repo.add(\"index.html\")\n\n  system \"git branch -m master\"\n\n  repo.commit_all(\"adding web content.\")\nend\n\nsolution do\n  index =\n    repo.status[\"index.html\"].type == \"D\" &&\n    repo.status[\"index.html\"].stage.nil? &&\n    repo.status[\"src/index.html\"].type == \"A\"\n\n  about =\n    repo.status[\"about.html\"].type == \"D\" &&\n    repo.status[\"about.html\"].stage.nil? &&\n    repo.status[\"src/about.html\"].type == \"A\"\n\n  contact =\n    repo.status[\"contact.html\"].type == \"D\" &&\n    repo.status[\"contact.html\"].stage.nil? &&\n    repo.status[\"src/contact.html\"].type == \"A\"\n\n  index && about && contact\nend\n\nhint do\n  puts \"You'll have to use mkdir, and `git mv`.\"\nend\n"
  },
  {
    "path": "levels/revert.rb",
    "content": "difficulty 4\ndescription \"You have committed several times but want to undo the middle commit. All commits have been pushed, so you can't change existing history.\"\n\nsetup do\n  repo.init\n\n  FileUtils.touch \"file1\"\n  repo.add        \"file1\"\n  repo.commit_all \"First commit\"\n  system \"git branch -m master\"\n\n  FileUtils.touch \"file3\"\n  repo.add        \"file3\"\n  repo.commit_all \"Bad commit\"\n\n  FileUtils.touch \"file2\"\n  repo.add        \"file2\"\n  repo.commit_all \"Second commit\"\nend\n\nsolution do\n  valid = false\n  commit_messages = repo.commits.map(&:message)\n  valid = true if repo.commits.length > 3 &&\n    commit_messages.any? { |e| e =~ /(Revert )?\"Bad commit\"/ }\n  valid\nend\n\nhint do\n  puts \"Try the revert command.\"\nend\n"
  },
  {
    "path": "levels/rm.rb",
    "content": "difficulty 2\n\ndescription \"A file has been removed from the working tree, but not from the repository. Identify this file and remove it.\"\n\nsetup do\n  repo.init\n  file = File.new(\"deleteme.rb\", \"w\")\n  file.close\n  system \"git branch -m master\"\n  repo.add(\"deleteme.rb\")\n  repo.commit_all(\"Added a temp file\")\n  File.delete(\"deleteme.rb\")\nend\n\nsolution do\n  repo.status.files[\"deleteme.rb\"].nil? || repo.status.files[\"deleteme.rb\"].stage.nil?\nend\n\nhint do\n  puts [\"You may need to use more than one command to complete this.\",\n    \"You have checked your staging area in a previous level.\",\n    \"Don't forget to run `git` for a list of commands.\"]\nend\n"
  },
  {
    "path": "levels/rm_cached.rb",
    "content": "difficulty 2\n\ndescription \"A file has accidentally been added to your staging area. Identify and remove it from the staging area. *NOTE* Do not remove the file from the file system, only from git.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\"deleteme.rb\")\n  system \"git branch -m master\"\n  repo.add(\".gitignore\")\n  repo.add(\"deleteme.rb\")\nend\n\nsolution do\n  (repo.status.files[\"deleteme.rb\"].nil? || repo.status.files[\"deleteme.rb\"].stage.nil?) && File.exists?(\"deleteme.rb\")\nend\n\nhint do\n  puts \"You may need to use more than one command to complete this. You have checked your staging area in a previous level. Don't forget to run `git` for a list of commands.\"\nend\n"
  },
  {
    "path": "levels/squash.rb",
    "content": "difficulty 4\ndescription \"You have committed several times but would like all those changes to be one commit.\"\n\nsetup do\n  repo.init\n  FileUtils.touch(\".hidden\")\n  repo.add(\".hidden\")\n  repo.commit_all(\"Initial Commit\")\n  system \"git branch -m master\"\n  FileUtils.touch(\"README\")\n  repo.add(\"README\")\n  repo.commit_all(\"Adding README\")\n  File.open(\"README\", 'w') { |f| f.write(\"hey there\") }\n  repo.add(\"README\")\n  repo.commit_all(\"Updating README (squash this commit into Adding README)\")\n  File.open(\"README\", 'a') { |f| f.write(\"\\nAdding some more text\") }\n  repo.add(\"README\")\n  repo.commit_all(\"Updating README (squash this commit into Adding README)\")\n  File.open(\"README\", 'a') { |f| f.write(\"\\neven more text\") }\n  repo.add(\"README\")\n  repo.commit_all(\"Updating README (squash this commit into Adding README)\")\nend\n\nsolution do\n  repo.commits.length == 2\nend\n\nhint do\n  puts \"Take a look at the `-i` flag of the rebase command.\"\nend\n"
  },
  {
    "path": "levels/stage_lines.rb",
    "content": "difficulty 4\n\ndescription \"You've made changes within a single file that belong to two different features, but neither of the changes are yet staged. Stage only the changes belonging to the first feature.\"\n\nsetup do\n  repo.init\n  File.open(\"feature.rb\", \"w\") do |file|\n    file.puts(\"this is the class of my feature\")\n  end\n  system \"git branch -m master\"\n\n  repo.add(\"feature.rb\")\n  repo.commit_all(\"Added initial feature file\")\n\n  File.open(\"feature.rb\", \"a\") do |file|\n    file.puts(\"This change belongs to the first feature\")\n  end\n\n  File.open(\"feature.rb\", \"a\") do |file|\n    file.puts(\"This change belongs to the second feature\")\n  end\nend\n\nsolution do\n  `git diff --no-ext-diff --no-color --staged` =~ /\\+This change belongs to the first feature/ && `git diff --no-ext-diff --no-color` =~ /\\+This change belongs to the second feature/\nend\n\nhint do\n  puts \"You might want to try to manipulate the hunks of the diff to choose which lines of the diff get staged. Read about the flags which can be passed to the `add` command; `git --help add`.\"\nend\n"
  },
  {
    "path": "levels/stash/.githug/COMMIT_EDITMSG",
    "content": "Add some lyrics\n"
  },
  {
    "path": "levels/stash/.githug/HEAD",
    "content": "ref: refs/heads/master\n"
  },
  {
    "path": "levels/stash/.githug/config",
    "content": "[core]\n\trepositoryformatversion = 0\n\tfilemode = true\n\tbare = false\n\tlogallrefupdates = true\n\tignorecase = true\n\tprecomposeunicode = false\n"
  },
  {
    "path": "levels/stash/.githug/description",
    "content": "Unnamed repository; edit this file 'description' to name the repository.\n"
  },
  {
    "path": "levels/stash/.githug/hooks/applypatch-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message taken by\n# applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.  The hook is\n# allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"applypatch-msg\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/commit-msg\" &&\n\texec \"$GIT_DIR/hooks/commit-msg\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/stash/.githug/hooks/commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to check the commit log message.\n# Called by \"git commit\" with one argument, the name of the file\n# that has the commit message.  The hook should exit with non-zero\n# status after issuing an appropriate message if it wants to stop the\n# commit.  The hook is allowed to edit the commit message file.\n#\n# To enable this hook, rename this file to \"commit-msg\".\n\n# Uncomment the below to add a Signed-off-by line to the message.\n# Doing this in a hook is a bad idea in general, but the prepare-commit-msg\n# hook is more suited to it.\n#\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n\n# This example catches duplicate Signed-off-by lines.\n\ntest \"\" = \"$(grep '^Signed-off-by: ' \"$1\" |\n\t sort | uniq -c | sed -e '/^[ \t]*1[ \t]/d')\" || {\n\techo >&2 Duplicate Signed-off-by lines.\n\texit 1\n}\n"
  },
  {
    "path": "levels/stash/.githug/hooks/post-update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare a packed repository for use over\n# dumb transports.\n#\n# To enable this hook, rename this file to \"post-update\".\n\nexec git update-server-info\n"
  },
  {
    "path": "levels/stash/.githug/hooks/pre-applypatch.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed\n# by applypatch from an e-mail message.\n#\n# The hook should exit with non-zero status after issuing an\n# appropriate message if it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-applypatch\".\n\n. git-sh-setup\ntest -x \"$GIT_DIR/hooks/pre-commit\" &&\n\texec \"$GIT_DIR/hooks/pre-commit\" ${1+\"$@\"}\n:\n"
  },
  {
    "path": "levels/stash/.githug/hooks/pre-commit.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to verify what is about to be committed.\n# Called by \"git commit\" with no arguments.  The hook should\n# exit with non-zero status after issuing an appropriate message if\n# it wants to stop the commit.\n#\n# To enable this hook, rename this file to \"pre-commit\".\n\nif git rev-parse --verify HEAD >/dev/null 2>&1\nthen\n\tagainst=HEAD\nelse\n\t# Initial commit: diff against an empty tree object\n\tagainst=4b825dc642cb6eb9a060e54bf8d69288fbee4904\nfi\n\n# If you want to allow non-ascii filenames set this variable to true.\nallownonascii=$(git config hooks.allownonascii)\n\n# Redirect output to stderr.\nexec 1>&2\n\n# Cross platform projects tend to avoid non-ascii filenames; prevent\n# them from being added to the repository. We exploit the fact that the\n# printable range starts at the space character and ends with tilde.\nif [ \"$allownonascii\" != \"true\" ] &&\n\t# Note that the use of brackets around a tr range is ok here, (it's\n\t# even required, for portability to Solaris 10's /usr/bin/tr), since\n\t# the square bracket bytes happen to fall in the designated range.\n\ttest $(git diff --cached --name-only --diff-filter=A -z $against |\n\t  LC_ALL=C tr -d '[ -~]\\0' | wc -c) != 0\nthen\n\techo \"Error: Attempt to add a non-ascii file name.\"\n\techo\n\techo \"This can cause problems if you want to work\"\n\techo \"with people on other platforms.\"\n\techo\n\techo \"To be portable it is advisable to rename the file ...\"\n\techo\n\techo \"If you know what you are doing you can disable this\"\n\techo \"check using:\"\n\techo\n\techo \"  git config hooks.allownonascii true\"\n\techo\n\texit 1\nfi\n\n# If there are whitespace errors, print the offending file names and fail.\nexec git diff-index --check --cached $against --\n"
  },
  {
    "path": "levels/stash/.githug/hooks/pre-rebase.sample",
    "content": "#!/bin/sh\n#\n# Copyright (c) 2006, 2008 Junio C Hamano\n#\n# The \"pre-rebase\" hook is run just before \"git rebase\" starts doing\n# its job, and can prevent the command from running by exiting with\n# non-zero status.\n#\n# The hook is called with the following parameters:\n#\n# $1 -- the upstream the series was forked from.\n# $2 -- the branch being rebased (or empty when rebasing the current branch).\n#\n# This sample shows how to prevent topic branches that are already\n# merged to 'next' branch from getting rebased, because allowing it\n# would result in rebasing already published history.\n\npublish=next\nbasebranch=\"$1\"\nif test \"$#\" = 2\nthen\n\ttopic=\"refs/heads/$2\"\nelse\n\ttopic=`git symbolic-ref HEAD` ||\n\texit 0 ;# we do not interrupt rebasing detached HEAD\nfi\n\ncase \"$topic\" in\nrefs/heads/??/*)\n\t;;\n*)\n\texit 0 ;# we do not interrupt others.\n\t;;\nesac\n\n# Now we are dealing with a topic branch being rebased\n# on top of master.  Is it OK to rebase it?\n\n# Does the topic really exist?\ngit show-ref -q \"$topic\" || {\n\techo >&2 \"No such branch $topic\"\n\texit 1\n}\n\n# Is topic fully merged to master?\nnot_in_master=`git rev-list --pretty=oneline ^master \"$topic\"`\nif test -z \"$not_in_master\"\nthen\n\techo >&2 \"$topic is fully merged to master; better remove it.\"\n\texit 1 ;# we could allow it, but there is no point.\nfi\n\n# Is topic ever merged to next?  If so you should not be rebasing it.\nonly_next_1=`git rev-list ^master \"^$topic\" ${publish} | sort`\nonly_next_2=`git rev-list ^master           ${publish} | sort`\nif test \"$only_next_1\" = \"$only_next_2\"\nthen\n\tnot_in_topic=`git rev-list \"^$topic\" master`\n\tif test -z \"$not_in_topic\"\n\tthen\n\t\techo >&2 \"$topic is already up-to-date with master\"\n\t\texit 1 ;# we could allow it, but there is no point.\n\telse\n\t\texit 0\n\tfi\nelse\n\tnot_in_next=`git rev-list --pretty=oneline ^${publish} \"$topic\"`\n\t/usr/bin/perl -e '\n\t\tmy $topic = $ARGV[0];\n\t\tmy $msg = \"* $topic has commits already merged to public branch:\\n\";\n\t\tmy (%not_in_next) = map {\n\t\t\t/^([0-9a-f]+) /;\n\t\t\t($1 => 1);\n\t\t} split(/\\n/, $ARGV[1]);\n\t\tfor my $elem (map {\n\t\t\t\t/^([0-9a-f]+) (.*)$/;\n\t\t\t\t[$1 => $2];\n\t\t\t} split(/\\n/, $ARGV[2])) {\n\t\t\tif (!exists $not_in_next{$elem->[0]}) {\n\t\t\t\tif ($msg) {\n\t\t\t\t\tprint STDERR $msg;\n\t\t\t\t\tundef $msg;\n\t\t\t\t}\n\t\t\t\tprint STDERR \" $elem->[1]\\n\";\n\t\t\t}\n\t\t}\n\t' \"$topic\" \"$not_in_next\" \"$not_in_master\"\n\texit 1\nfi\n\nexit 0\n\n################################################################\n\nThis sample hook safeguards topic branches that have been\npublished from being rewound.\n\nThe workflow assumed here is:\n\n * Once a topic branch forks from \"master\", \"master\" is never\n   merged into it again (either directly or indirectly).\n\n * Once a topic branch is fully cooked and merged into \"master\",\n   it is deleted.  If you need to build on top of it to correct\n   earlier mistakes, a new topic branch is created by forking at\n   the tip of the \"master\".  This is not strictly necessary, but\n   it makes it easier to keep your history simple.\n\n * Whenever you need to test or publish your changes to topic\n   branches, merge them into \"next\" branch.\n\nThe script, being an example, hardcodes the publish branch name\nto be \"next\", but it is trivial to make it configurable via\n$GIT_DIR/config mechanism.\n\nWith this workflow, you would want to know:\n\n(1) ... if a topic branch has ever been merged to \"next\".  Young\n    topic branches can have stupid mistakes you would rather\n    clean up before publishing, and things that have not been\n    merged into other branches can be easily rebased without\n    affecting other people.  But once it is published, you would\n    not want to rewind it.\n\n(2) ... if a topic branch has been fully merged to \"master\".\n    Then you can delete it.  More importantly, you should not\n    build on top of it -- other people may already want to\n    change things related to the topic as patches against your\n    \"master\", so if you need further changes, it is better to\n    fork the topic (perhaps with the same name) afresh from the\n    tip of \"master\".\n\nLet's look at this example:\n\n\t\t   o---o---o---o---o---o---o---o---o---o \"next\"\n\t\t  /       /           /           /\n\t\t /   a---a---b A     /           /\n\t\t/   /               /           /\n\t       /   /   c---c---c---c B         /\n\t      /   /   /             \\         /\n\t     /   /   /   b---b C     \\       /\n\t    /   /   /   /             \\     /\n    ---o---o---o---o---o---o---o---o---o---o---o \"master\"\n\n\nA, B and C are topic branches.\n\n * A has one fix since it was merged up to \"next\".\n\n * B has finished.  It has been fully merged up to \"master\" and \"next\",\n   and is ready to be deleted.\n\n * C has not merged to \"next\" at all.\n\nWe would want to allow C to be rebased, refuse A, and encourage\nB to be deleted.\n\nTo compute (1):\n\n\tgit rev-list ^master ^topic next\n\tgit rev-list ^master        next\n\n\tif these match, topic has not merged in next at all.\n\nTo compute (2):\n\n\tgit rev-list master..topic\n\n\tif this is empty, it is fully merged to \"master\".\n"
  },
  {
    "path": "levels/stash/.githug/hooks/prepare-commit-msg.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to prepare the commit log message.\n# Called by \"git commit\" with the name of the file that has the\n# commit message, followed by the description of the commit\n# message's source.  The hook's purpose is to edit the commit\n# message file.  If the hook fails with a non-zero status,\n# the commit is aborted.\n#\n# To enable this hook, rename this file to \"prepare-commit-msg\".\n\n# This hook includes three examples.  The first comments out the\n# \"Conflicts:\" part of a merge commit.\n#\n# The second includes the output of \"git diff --name-status -r\"\n# into the message, just before the \"git status\" output.  It is\n# commented because it doesn't cope with --amend or with squashed\n# commits.\n#\n# The third example adds a Signed-off-by line to the message, that can\n# still be edited.  This is rarely a good idea.\n\ncase \"$2,$3\" in\n  merge,)\n    /usr/bin/perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' \"$1\" ;;\n\n# ,|template,)\n#   /usr/bin/perl -i.bak -pe '\n#      print \"\\n\" . `git diff --cached --name-status -r`\n#\t if /^#/ && $first++ == 0' \"$1\" ;;\n\n  *) ;;\nesac\n\n# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\\(.*>\\).*$/Signed-off-by: \\1/p')\n# grep -qs \"^$SOB\" \"$1\" || echo \"$SOB\" >> \"$1\"\n"
  },
  {
    "path": "levels/stash/.githug/hooks/update.sample",
    "content": "#!/bin/sh\n#\n# An example hook script to blocks unannotated tags from entering.\n# Called by \"git receive-pack\" with arguments: refname sha1-old sha1-new\n#\n# To enable this hook, rename this file to \"update\".\n#\n# Config\n# ------\n# hooks.allowunannotated\n#   This boolean sets whether unannotated tags will be allowed into the\n#   repository.  By default they won't be.\n# hooks.allowdeletetag\n#   This boolean sets whether deleting tags will be allowed in the\n#   repository.  By default they won't be.\n# hooks.allowmodifytag\n#   This boolean sets whether a tag may be modified after creation. By default\n#   it won't be.\n# hooks.allowdeletebranch\n#   This boolean sets whether deleting branches will be allowed in the\n#   repository.  By default they won't be.\n# hooks.denycreatebranch\n#   This boolean sets whether remotely creating branches will be denied\n#   in the repository.  By default this is allowed.\n#\n\n# --- Command line\nrefname=\"$1\"\noldrev=\"$2\"\nnewrev=\"$3\"\n\n# --- Safety check\nif [ -z \"$GIT_DIR\" ]; then\n\techo \"Don't run this script from the command line.\" >&2\n\techo \" (if you want, you could supply GIT_DIR then run\" >&2\n\techo \"  $0 <ref> <oldrev> <newrev>)\" >&2\n\texit 1\nfi\n\nif [ -z \"$refname\" -o -z \"$oldrev\" -o -z \"$newrev\" ]; then\n\techo \"Usage: $0 <ref> <oldrev> <newrev>\" >&2\n\texit 1\nfi\n\n# --- Config\nallowunannotated=$(git config --bool hooks.allowunannotated)\nallowdeletebranch=$(git config --bool hooks.allowdeletebranch)\ndenycreatebranch=$(git config --bool hooks.denycreatebranch)\nallowdeletetag=$(git config --bool hooks.allowdeletetag)\nallowmodifytag=$(git config --bool hooks.allowmodifytag)\n\n# check for no description\nprojectdesc=$(sed -e '1q' \"$GIT_DIR/description\")\ncase \"$projectdesc\" in\n\"Unnamed repository\"* | \"\")\n\techo \"*** Project description file hasn't been set\" >&2\n\texit 1\n\t;;\nesac\n\n# --- Check types\n# if $newrev is 0000...0000, it's a commit to delete a ref.\nzero=\"0000000000000000000000000000000000000000\"\nif [ \"$newrev\" = \"$zero\" ]; then\n\tnewrev_type=delete\nelse\n\tnewrev_type=$(git cat-file -t $newrev)\nfi\n\ncase \"$refname\",\"$newrev_type\" in\n\trefs/tags/*,commit)\n\t\t# un-annotated tag\n\t\tshort_refname=${refname##refs/tags/}\n\t\tif [ \"$allowunannotated\" != \"true\" ]; then\n\t\t\techo \"*** The un-annotated tag, $short_refname, is not allowed in this repository\" >&2\n\t\t\techo \"*** Use 'git tag [ -a | -s ]' for tags you want to propagate.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,delete)\n\t\t# delete tag\n\t\tif [ \"$allowdeletetag\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tag is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/tags/*,tag)\n\t\t# annotated tag\n\t\tif [ \"$allowmodifytag\" != \"true\" ] && git rev-parse $refname > /dev/null 2>&1\n\t\tthen\n\t\t\techo \"*** Tag '$refname' already exists.\" >&2\n\t\t\techo \"*** Modifying a tag is not allowed in this repository.\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,commit)\n\t\t# branch\n\t\tif [ \"$oldrev\" = \"$zero\" -a \"$denycreatebranch\" = \"true\" ]; then\n\t\t\techo \"*** Creating a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/heads/*,delete)\n\t\t# delete branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\trefs/remotes/*,commit)\n\t\t# tracking branch\n\t\t;;\n\trefs/remotes/*,delete)\n\t\t# delete tracking branch\n\t\tif [ \"$allowdeletebranch\" != \"true\" ]; then\n\t\t\techo \"*** Deleting a tracking branch is not allowed in this repository\" >&2\n\t\t\texit 1\n\t\tfi\n\t\t;;\n\t*)\n\t\t# Anything else (is there anything else?)\n\t\techo \"*** Update hook: unknown type of update to ref $refname of type $newrev_type\" >&2\n\t\texit 1\n\t\t;;\nesac\n\n# --- Finished\nexit 0\n"
  },
  {
    "path": "levels/stash/.githug/info/exclude",
    "content": "# git ls-files --others --exclude-from=.git/info/exclude\n# Lines that start with '#' are comments.\n# For a project mostly in C, the following would be a good set of\n# exclude patterns (uncomment them if you want to use them):\n# *.[oa]\n# *~\n"
  },
  {
    "path": "levels/stash/.githug/logs/HEAD",
    "content": "0000000000000000000000000000000000000000 02060592b31c9e12ffe1b282addf9537c5ef8e1f Anton Vasin <magnumsmail@gmail.com> 1354518787 +0400\tcommit (initial): Add some lyrics\n"
  },
  {
    "path": "levels/stash/.githug/logs/refs/heads/master",
    "content": "0000000000000000000000000000000000000000 02060592b31c9e12ffe1b282addf9537c5ef8e1f Anton Vasin <magnumsmail@gmail.com> 1354518787 +0400\tcommit (initial): Add some lyrics\n"
  },
  {
    "path": "levels/stash/.githug/refs/heads/master",
    "content": "02060592b31c9e12ffe1b282addf9537c5ef8e1f\n"
  },
  {
    "path": "levels/stash/lyrics.txt",
    "content": "Down in Louisiana in that sunny clime,\nThey play a class of music that is super fine,\nAnd it makes no difference if its rain or shine,\nYou can hear that that jazz band music playing all the time.\nIt sounds so peculiar cause the music's queer.\nHow its sweet vibration seem to fill the air.\nThen to you the whole world seems to be in rhyme.\nYou want nothing else but blues-band music all the time.\n\nEv'ry one that's nigh\nNever seems to sigh,\nHear them loudly cry:\nHey!\n"
  },
  {
    "path": "levels/stash.rb",
    "content": "difficulty 2\ndescription \"You've made some changes and want to work on them later. You should save them, but don't commit them.\"\n\nsetup do\n  init_from_level\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false if `git stash list` !~ /stash@\\{0\\}/\n  return false if repo.status.changed.to_a.flatten.include? \"lyrics.txt\"\n  true\nend\n\nhint do\n  puts \"It's like stashing. Try finding an appropriate git command.\"\nend\n"
  },
  {
    "path": "levels/status.rb",
    "content": "difficulty 1\ndescription \"Among the files in this repository, which of them is untracked?\"\n\nsetup do\n  repo.init\n  %w{config.rb README setup.rb deploy.rb Guardfile}.each do |file|\n    FileUtils.touch(file)\n    system \"git branch -m master\"\n    repo.add(file)\n  end\n  FileUtils.touch(\"database.yml\")\nend\n\nsolution do\n\n  name = request(\"What is the full file name of the untracked file?\")\n\n  if name != \"database.yml\"\n    return false\n  end\n\n  true\nend\n\nhint do\n  puts \"You are looking for a command to identify the status of the repository.\"\nend\n"
  },
  {
    "path": "levels/submodule.rb",
    "content": "difficulty 2\ndescription \"You want to include the files from the following repo: `https://github.com/jackmaney/githug-include-me` into the folder `./githug-include-me`. Do this without manually cloning the repo or copying the files from the repo into this repo.\"\n\nsetup do\n  repo.init\n  system \"git branch -m master\"\nend\n\nsolution do\n  return false if not File.directory?(\"./githug-include-me\")\n  return false if not File.exist?(\"./githug-include-me/README.md\")\n  return false if not File.exist?(\"./githug-include-me/.git\")\n  return false if File.directory?(\"./githug-include-me/.git\")\n  return false if not File.exist?(\".gitmodules\")\n\n  return true\nend\n\nhint do\n  puts \"Take a look at `git submodule`.\"\nend\n"
  },
  {
    "path": "levels/tag.rb",
    "content": "difficulty 2\n\ndescription \"We have a git repo and we want to tag the current commit with `new_tag`.\"\n\nsetup do\n    repo.init\n    FileUtils.touch(\"somefile.txt\")\n    repo.add(\"somefile.txt\")\n    repo.commit_all(\"Added some file to the repo\")\n    system \"git branch -m master\"\nend\n\nsolution do\n    repo.tags.first.name == \"new_tag\"\nend\n\nhint do\n    puts \"Take a look at `git tag`.\"\nend\n"
  },
  {
    "path": "lib/githug/cli.rb",
    "content": "require 'thor'\nrequire 'githug'\nmodule Githug\n  class CLI < Thor\n\n\n    default_task :play\n\n    desc :play, \"Initialize the game\"\n\n    def play\n      UI.word_box(\"Githug\")\n      make_directory!\n      Game.new.play_level\n    end\n\n    desc :test, \"Test a level from a file path\"\n    method_option :errors, :type => :boolean, :default => false\n\n    def test(path)\n      UI.word_box(\"Githug\")\n      make_directory!\n      level = Level.load_from_file(path)\n      Game.new.test_level(level, options[:errors])\n    end\n\n    desc :hint, \"Get a hint for the current level\"\n\n    def hint\n      if level = load_level\n        level.show_hint\n      end\n    end\n\n    desc :reset, \"Reset the current level or select specific level\"\n    long_desc <<-LONGDESC\n      `githug reset` will reset the current level. You can optionally specify a\n      LEVEL parameter which will reset the game to a specific level. For\n      example:\n\n      > $ githug reset merge_squash   # or $ githug reset 47\n\n      Will reset githug to level '#47: merge_squash'\n    LONGDESC\n    def reset(path = nil)\n      level = load_level(path)\n      UI.word_box(\"Githug\")\n      if level\n        UI.puts(\"resetting level\")\n        level.setup_level\n        level.full_description\n      else\n        UI.error(\"Level does not exist\")\n      end\n    end\n\n    desc :levels, \"List all of the levels\"\n\n    def levels\n      list_with_numbers = Level.list.each_with_index.map do |name, index|\n        \"##{index + 1}: #{name}\"\n      end\n      UI.puts(list_with_numbers)\n    end\n\n    no_tasks do\n\n      def load_level(path = nil)\n        return load_level_from_profile unless path\n        return load_level_from_number(path.to_i) if path.to_i.to_s == path\n        return load_level_from_name(path) if Level.list.include?(path)\n        Level.load_from_file(path)\n      end\n\n      def load_level_from_number(number)\n        level_name = number >= 1 ? Level.list[number - 1] : nil\n        return load_level_from_name(level_name)\n      end\n\n      def load_level_from_name(name)\n        profile = Profile.load\n        profile.set_level(name)\n        Level.load(name)\n      end\n\n      def load_level_from_profile\n        profile = Profile.load\n        Level.load(profile.level)\n      end\n\n\n      def make_directory!\n        return if File.basename(Dir.pwd) == \"git_hug\"\n        check_githug_directory!\n        prompt_githug_directory!\n        make_githug_directory!\n      end\n\n      def check_githug_directory!\n        if File.exists?(\"./git_hug\")\n          UI.puts \"Please change into the git_hug directory\"\n          exit\n        end\n      end\n\n      def prompt_githug_directory!\n        unless UI.ask(\"No githug directory found, do you wish to create one?\")\n          UI.puts(\"Exiting\")\n          exit\n        end\n      end\n\n      def make_githug_directory!\n        Dir.mkdir(\"./git_hug\")\n        Dir.chdir(\"git_hug\")\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/githug/extensions/grit/ruby1.9.rb",
    "content": "class String\n  if self.method_defined?(:ord)\n    def getord(offset); self[offset].ord; end\n  else\n    alias :getord :[]\n  end\n\n  unless self.method_defined?(:b)\n    if self.method_defined?(:force_encoding)\n      def b; self.dup.force_encoding(Encoding::ASCII_8BIT); end\n    else\n      def b; self.dup; end\n    end\n  end\nend\n\nif Object.const_defined?(:PACK_IDX_SIGNATURE)\n  Object.send(:remove_const, :PACK_IDX_SIGNATURE)\nend\n\nPACK_IDX_SIGNATURE = \"\\377tOc\".b\n"
  },
  {
    "path": "lib/githug/game.rb",
    "content": "module Githug\n  class Game\n\n    attr_accessor :profile\n\n    def initialize\n      @profile = Profile.load\n    end\n\n    def play_level\n      solve = true\n      if profile.level.nil?\n        UI.puts(\"Welcome to Githug!\")\n        solve = false\n        level_bump\n      else\n        level = Level.load(profile.level)\n        if solve && level\n          if level.solve\n            UI.success \"Congratulations, you have solved the level!\"\n            level_bump\n          else\n            UI.error \"Sorry, this solution is not quite right!\"\n            profile.current_attempts += 1\n            profile.save\n\n            if (profile.current_attempts > 2 && profile.current_attempts % 3 == 0)\n              UI.error \"Don't forget you can type `githug hint` for a hint and `githug reset` to reset the current level.\"\n            end\n\n            UI.puts level.full_description\n          end\n        end\n      end\n    end\n\n    def test_level(level, errors = nil)\n      UI.puts level.full_description\n      method = :solve\n      method = :test if errors\n      if level.send(method)\n        UI.success \"Valid solution\"\n      else\n        UI.error \"Invalid solution\"\n      end\n    end\n\n    def level_bump\n      profile.level_bump\n      if level = Level.load(profile.level)\n        UI.puts(level.full_description)\n        level.setup_level\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/githug/level.rb",
    "content": "module Githug\n  class Level\n    include UI\n\n    LEVELS = [nil, \"init\", \"config\", \"add\", \"commit\", \"clone\",\n              \"clone_to_folder\", \"ignore\", \"include\", \"status\",\n              \"number_of_files_committed\", \"rm\", \"rm_cached\", \"stash\", \"rename\",\n              \"restructure\", \"log\", \"tag\", \"push_tags\", \"commit_amend\",\n              \"commit_in_future\", \"reset\", \"reset_soft\", \"checkout_file\", \"remote\",\n              \"remote_url\", \"pull\", \"remote_add\", \"push\", \"diff\", \"blame\", \"branch\",\n              \"checkout\", \"checkout_tag\", \"checkout_tag_over_branch\", \"branch_at\",\n              \"delete_branch\", \"push_branch\", \"merge\", \"fetch\", \"rebase\", \"rebase_onto\", \"repack\", \"cherry-pick\",\n              \"grep\", \"rename_commit\", \"squash\", \"merge_squash\", \"reorder\", \"bisect\",\n              \"stage_lines\", \"find_old_branch\", \"revert\", \"restore\", \"conflict\",\n              \"submodule\",\"contribute\"]\n\n    attr_accessor :level_no, :level_path, :level_name\n\n    class << self\n\n      def load(level_name)\n        path = \"#{File.dirname(__FILE__)}/../../levels/#{level_name}.rb\"\n        setup(path)\n      end\n\n      def load_from_file(path)\n        setup(path)\n      end\n\n      def list\n        return LEVELS - [nil]\n      end\n\n      def setup(path)\n        level_path = path.chomp(File.extname(path))\n        level = self.new\n\n        return false unless File.exists?(path)\n\n        level.instance_eval(File.read(path))\n        level.level_name = File.basename(path, File.extname(path))\n        level.level_no = LEVELS.index(level.level_name) || 1\n        level.level_path = level_path\n        level\n      end\n\n    end\n\n    def init_from_level\n      FileUtils.cp_r(\"#{level_path}/.\", \".\")\n      FileUtils.mv(\".githug\", \".git\")\n    end\n\n    def difficulty(num)\n      @difficulty = num\n    end\n\n    def description(description)\n      @description = description\n    end\n\n    def solution(&block)\n      singleton = class << self; self end\n      singleton.send :define_method, :_solution, &block\n    end\n\n    def setup(&block)\n      @setup = block\n    end\n\n    def hint(&hint)\n      @hint = hint\n    end\n\n    def hints(hints)\n      @hints = hints\n    end\n\n    def full_description\n      UI.puts\n      UI.puts \"Name: #{level_name}\"\n      UI.puts \"Level: #{level_no}\"\n      UI.puts \"Difficulty: #{\"*\"*@difficulty}\"\n      UI.puts\n      UI.puts @description\n      UI.puts\n    end\n\n    def setup_level\n      repo.reset\n      @setup.call if @setup\n    end\n\n    def repo(location = \"\")\n      @repo ||= Repository.new(location)\n    end\n\n    def solve\n      _solution\n    rescue\n      false\n    end\n\n    def test\n      _solution\n    end\n\n    def show_hint\n      UI.word_box(\"Githug\")\n      profile = Profile.load\n      current_hint_index = profile.current_hint_index\n      if @hints\n        puts @hints[current_hint_index]\n        if current_hint_index < @hints.size - 1\n          profile.current_hint_index += 1\n          profile.save\n        else\n          profile.current_hint_index = 0\n          profile.save\n        end\n      elsif @hint\n        @hint.call\n      else\n        UI.puts(\"No hints available for this level.\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "lib/githug/profile.rb",
    "content": "require 'yaml'\nmodule Githug\n  class Profile\n    PROFILE_FILE = \".profile.yml\"\n\n    attr_accessor :settings\n\n    class << self\n      def load\n        self.new(settings)\n      end\n\n      private\n\n      def settings\n        return defaults unless File.exists?(PROFILE_FILE)\n        defaults.merge(YAML::load(File.open(PROFILE_FILE)))\n      end\n\n      def defaults\n        {\n          :level => nil,\n          :current_attempts => 0,\n          :current_hint_index => 0,\n          :current_levels => [],\n          :completed_levels => []\n        }\n      end\n\n    end\n\n\n    def method_missing(method, *args, &block)\n      if method.to_s.end_with?(\"=\")\n        method = method.to_s.chop.to_sym\n        return settings[method] = args[0] if settings.include?(method)\n      end\n      return(settings[method]) if settings.include?(method)\n      super\n    end\n\n    def initialize(settings)\n      @settings = settings\n    end\n\n    def save\n      File.open(PROFILE_FILE, 'w') do |out|\n        YAML.dump(settings, out)\n      end\n    end\n\n    def set_level(name)\n      settings[:level] = name\n      reset!\n      save\n    end\n\n    def level_bump\n      settings[:completed_levels] << level\n      settings[:current_levels] = levels\n      set_level(next_level)\n    end\n\n    private\n\n    def levels\n      Level::LEVELS\n    end\n\n    def next_level\n      (levels - settings[:completed_levels]).first || levels.last\n    end\n\n    def reset!\n      settings[:current_attempts] = 0\n      settings[:current_hint_index] = 0\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/githug/repository.rb",
    "content": "module Githug\n  class Repository\n\n    attr_accessor :grit\n\n    def initialize(location = \".\")\n      @grit = Grit::Repo.new(location)\n    rescue Grit::InvalidGitRepositoryError\n      @grit = nil\n    end\n\n    def reset\n      dont_delete = [\"..\", \".\", \".profile.yml\"]\n      if File.basename(Dir.pwd) == \"git_hug\"\n        Dir.entries(Dir.pwd).each do |file|\n          FileUtils.rm_rf(file) unless dont_delete.include?(file)\n        end\n      end\n      create_gitignore\n    end\n\n    def create_gitignore\n      Dir.chdir(\"git_hug\") if File.exists?(\"./git_hug\")\n      File.open(\".gitignore\", \"w\") do |file|\n        file.puts(\".profile.yml\")\n        file.puts(\".gitignore\")\n      end\n    end\n\n    def valid?\n      !@grit.nil?\n    end\n\n    # Initialize a Git repo. If the repo already exists, do nothing.\n    def init(location = \".\")\n      @grit = Grit::Repo.init(location)\n    end\n\n    def method_missing(method, *args, &block)\n      if @grit && @grit.respond_to?(method)\n        return @grit.send(method, *args, &block)\n      end\n      super\n    end\n\n\n  end\nend\n"
  },
  {
    "path": "lib/githug/ui.rb",
    "content": "module Githug\n  module UI\n\n    class << self\n\n      attr_accessor :out_stream, :in_stream\n\n      @out_stream = STDOUT\n      @in_stream = STDIN\n\n      def puts(string = \"\")\n        out_stream.puts(string)\n      end\n\n      def print(string)\n        out_stream.print(string)\n      end\n\n      def gets\n        in_stream.gets\n      end\n\n      def word_box(string,width=80,char='*')\n        puts char*width\n        puts \"#{char}#{string.center(width-2)}#{char}\"\n        puts char*width\n      end\n\n      def request(msg)\n        print(\"#{msg} \")\n        gets.chomp\n      end\n\n      def ask(msg)\n        request(\"#{msg} [yn] \") == 'y'\n      end\n\n      def colorize(text, color_code)\n        return puts text if ENV['OS'] && ENV['OS'].downcase.include?(\"windows\")\n        puts \"#{color_code}#{text}\\033[0m\"\n      end\n\n      def error(text)\n        colorize(text, \"\\033[31m\")\n      end\n\n      def success(text)\n        colorize(text, \"\\033[32m\")\n      end\n\n    end\n\n    def method_missing(method, *args, &block)\n      return UI.send(method, *args) if UI.methods(false).include?(method.to_s) || UI.methods(false).include?(method)\n      super\n    end\n\n  end\nend\n"
  },
  {
    "path": "lib/githug/version.rb",
    "content": "module Githug\n  VERSION = \"0.5.1\"\nend\n"
  },
  {
    "path": "lib/githug.rb",
    "content": "require 'grit'\n\nrequire \"githug/extensions/grit/ruby1.9\"\n\n\nrequire \"githug/version\"\n\nrequire 'githug/ui'\nrequire 'githug/game'\nrequire 'githug/profile'\nrequire 'githug/level'\nrequire 'githug/repository'\n\nGithug::UI.in_stream = STDIN\nGithug::UI.out_stream = STDOUT\nSTDIN.sync = true\n"
  },
  {
    "path": "spec/githug/cli_spec.rb",
    "content": "require 'spec_helper'\nrequire 'githug/cli'\n\ndescribe Githug::CLI do\n\n  before(:each) do\n    game = mock.as_null_object\n    Githug::Game.stub(:new).and_return(game)\n  end\n\n  it \"prints the logo\" do\n    Githug::UI.should_receive(:word_box).with(\"Githug\")\n    subject.stub(:make_directory!)\n    subject.play\n  end\n\n  it \"creates a directory if one does not exist\" do\n    Githug::UI.stub(:ask).and_return(true)\n    Dir.should_receive(:mkdir).with(\"./git_hug\")\n    Dir.should_receive(:chdir).with(\"git_hug\")\n    subject.make_directory!\n  end\n\n  it \"does not create a directory if you are in the game directory\" do\n    Dir.stub(:pwd).and_return(\"/home/git_hug\")\n    Githug::UI.should_not_receive(:ask)\n    subject.make_directory!\n  end\n\n  it \"exits if the user selects no\" do\n    Githug::UI.stub(:ask).and_return(false)\n    lambda {subject.prompt_githug_directory!}.should raise_error(SystemExit)\n  end\n\n  it \"prompts to change into the directory if it exists\" do\n    File.stub(:exists?).and_return(true)\n    Githug::UI.should_receive(:puts).with(\"Please change into the git_hug directory\")\n    lambda {subject.check_githug_directory!}.should raise_error(SystemExit)\n  end\n\n  describe \"#test\" do\n    it \"performs a test run of the level\" do\n      level = mock\n      game = mock\n      subject.stub(:make_directory!)\n      Githug::Level.should_receive(:load_from_file).with(\"/foo/bar/test/level.rb\").and_return(level)\n      Githug::Game.stub(:new).and_return(game)\n      game.should_receive(:test_level).with(level, anything)\n      subject.test(\"/foo/bar/test/level.rb\")\n    end\n  end\n\n  describe \"level methods\" do\n\n    let(:level) { mock }\n    let(:profile) { mock }\n\n    before(:each) do\n      profile.stub(:level).and_return(1)\n      Githug::Profile.stub(:load).and_return(profile)\n      Githug::Level.stub(:load).and_return(level)\n      Githug::Level.stub(:load_from_file).with(\"/foo/bar/level.rb\").and_return(level)\n    end\n\n    it \"calls the hint method on the level\" do\n      level.should_receive(:show_hint)\n      subject.hint\n    end\n\n    describe \"#reset\" do\n\n\n      it \"resets the current level\" do\n        level.should_receive(:setup_level)\n        level.should_receive(:full_description)\n        Githug::UI.should_receive(:word_box).with(\"Githug\")\n        Githug::UI.should_receive(:puts).with(\"resetting level\")\n        subject.reset\n      end\n\n      it \"does not reset if the level cannot be loaded\" do\n        Githug::Level.stub(:load).and_return(false)\n        level.should_not_receive(:setup_level)\n        level.should_not_receive(:full_description)\n        Githug::UI.should_receive(:error).with(\"Level does not exist\")\n        subject.reset\n      end\n\n      it \"resets the level with a level name\" do\n        level.should_receive(:setup_level)\n        level.should_receive(:full_description)\n        profile = mock\n        Githug::Profile.stub(:load).and_return(profile)\n        profile.should_receive(:set_level).with(\"add\")\n        Githug::Level.should_receive(:load).with(\"add\").and_return(level)\n        Githug::UI.should_receive(:word_box).with(\"Githug\")\n        Githug::UI.should_receive(:puts).with(\"resetting level\")\n        subject.reset(\"add\")\n      end\n\n      it \"resets the level with a level number\" do\n        level.should_receive(:setup_level)\n        level.should_receive(:full_description)\n        profile = mock\n        Githug::Profile.stub(:load).and_return(profile)\n        profile.should_receive(:set_level).with(\"rename_commit\")\n        Githug::Level.should_receive(:load).with(\"rename_commit\").and_return(level)\n        Githug::UI.should_receive(:word_box).with(\"Githug\")\n        Githug::UI.should_receive(:puts).with(\"resetting level\")\n        subject.reset(\"45\")\n      end\n\n      it \"resets the level with a path\" do\n        level.should_receive(:setup_level)\n        level.should_receive(:full_description)\n        Githug::UI.should_receive(:word_box).with(\"Githug\")\n        Githug::UI.should_receive(:puts).with(\"resetting level\")\n        subject.reset(\"/foo/bar/level.rb\")\n      end\n    end\n\n  end\n\n  describe \"#levels\" do\n\n    it \"prints the levels and their numbers\" do\n      Githug::Level.stub(:list).and_return([\"commit\", \"add\"])\n      Githug::UI.should_receive(:puts).with([\"#1: commit\", \"#2: add\"])\n      subject.levels\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/githug/game_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Githug::Game do\n\n  let(:profile) { mock(:level => 1,\n                       :current_attempts => 0).as_null_object }\n  let(:game) { Githug::Game.new }\n  let(:level) { mock(:full_description => nil, :setup_level => nil) }\n\n  before(:each) do\n    Githug::Profile.stub(:new).and_return(profile)\n    profile.stub(:save)\n    profile.stub(:level_bump)\n\n    Githug::UI.stub(:puts)\n\n    Githug::Level.stub(:load).and_return(level)\n  end\n\n  it \"has a profile\" do\n    game.profile.should eql(profile)\n  end\n\n  it \"shows a description if the level is 0\" do\n    level.should_not_receive(:solve)\n    profile.stub(:level).and_return(nil)\n    profile.should_receive(:level_bump)\n    Githug::UI.should_receive(:puts).with(\"Welcome to Githug!\")\n    game.play_level\n  end\n\n  describe \"play_level\" do\n\n    it \"outputs congratulations if the level is solved\" do\n      level.stub(:solve).and_return(true)\n      profile.should_receive(:level_bump)\n      Githug::UI.should_receive(:success).with(\"Congratulations, you have solved the level!\")\n      game.play_level\n    end\n\n    it \"outputs a message if the solution is not right\" do\n      level.stub(:solve).and_return(false)\n      Githug::UI.should_receive(:error).with(\"Sorry, this solution is not quite right!\")\n      game.play_level\n    end\n\n    it \"increments the number of failed attempts\" do\n      level.stub(:solve).and_return(false)\n      profile.should_receive(:current_attempts=).with(1)\n      profile.should_receive(:save)\n      game.play_level\n    end\n\n    it \"prompts for a hint if the user has failed 3 times.\" do\n      profile.stub(:current_attempts).and_return(3)\n      level.stub(:solve).and_return(false)\n      Githug::UI.should_receive(:error).with(\"Sorry, this solution is not quite right!\")\n      Githug::UI.should_receive(:error).with(\"Don't forget you can type `githug hint` for a hint and `githug reset` to reset the current level.\")\n      game.play_level\n    end\n\n  end\n\n\n  describe \"test_level\" do\n    it \"outputs Valid solution if the solution is valid\" do\n      level.stub(:solve).and_return(true)\n      Githug::UI.should_receive(:success).with(\"Valid solution\")\n      game.test_level(level)\n    end\n\n    it \"outputs Invalid solution if the solution is invalid\" do\n      level.stub(:solve).and_return(false)\n      Githug::UI.should_receive(:error).with(\"Invalid solution\")\n      game.test_level(level)\n    end\n\n    it \"calls test when errors is true\" do\n      level.should_receive(:test)\n      game.test_level(level, true)\n    end\n  end\n\n  it \"outputs the description of the next level\" do\n    level.should_receive(:full_description)\n    profile.stub(:level=)\n    game.level_bump\n  end\n\n  it \"calls setup_level for the next level\" do\n    level.should_receive(:setup_level)\n    profile.stub(:level=)\n    game.level_bump\n  end\n\nend\n"
  },
  {
    "path": "spec/githug/level_spec.rb",
    "content": "require 'spec_helper'\nrequire 'grit'\n\ndescribe Githug::Level do\n\n  let(:subject) { Githug::Level.load_from_file(File.expand_path(\"spec/support/files/test_level.rb\")) }\n  let(:repo) { mock(:reset) }\n\n  before(:each) do\n    Githug::Repository.stub(:new).and_return(repo)\n    Githug::UI.stub(:puts)\n    Githug::UI.stub(:print)\n  end\n\n  it \"should mixin UI\" do\n    Githug::Level.ancestors.should include(Githug::UI)\n  end\n\n  describe \".load\" do\n\n    it \"loads the level\" do\n      File.stub(:dirname).and_return(\"\")\n      Githug::Level.should_receive(:setup).with(\"/../../levels/init.rb\")\n      Githug::Level.load(\"init\")\n    end\n\n    it \"returns false if the level does not exist\" do\n      File.stub(:exists?).and_return(false)\n      Githug::Level.load(1).should eql(false)\n    end\n\n  end\n\n  describe \".list\" do\n    it \"lists the levels without nil\" do\n      Githug::Level.list.should eql(Githug::Level::LEVELS - [nil])\n    end\n  end\n\n  describe \".load_from_file\" do\n    it \"loads the level\" do\n      subject.instance_variable_get(\"@difficulty\").should eql(1)\n      subject.instance_variable_get(\"@description\").should eql(\"A test description\")\n    end\n\n    it \"return false if the level does not exist\" do\n      File.stub(:exists?).and_return(false)\n      Githug::Level.load_from_file(\"/foo/bar/test/level.rb\").should eql(false)\n    end\n  end\n\n  describe \".setup\" do\n\n    it \"returns false if the level does not exist\" do\n      File.stub(:exists?).and_return(false)\n      Githug::Level.setup(\"/foo/bar/test/level.rb\").should eql(false)\n    end\n\n  end\n\n\n  describe \"#solve\" do\n\n    it \"returns false if the level requirements have not been met\" do\n      subject.solve.should eql(false)\n    end\n\n    it \"returns true if the level requirements have been met\" do\n      Grit::Repo.stub(:new).and_return(true)\n      subject.solve.should eql(true)\n    end\n\n  end\n\n  describe \"#test\" do\n    it \"calls solve\" do\n      subject.should_receive(:_solution)\n      subject.test\n    end\n  end\n\n\n  describe \"#full_description\" do\n\n    it \"displays a full description\" do\n      Githug::UI.stub(:puts)\n      Githug::UI.should_receive(:puts).with(\"Level: 1\")\n      Githug::UI.should_receive(:puts).with(\"Difficulty: *\")\n      Githug::UI.should_receive(:puts).with(\"A test description\")\n      subject.full_description\n    end\n\n  end\n\n  describe \"#setup\" do\n\n    it \"calls setup\" do\n      repo.should_receive(:reset)\n      subject.setup_level.should eql(\"test\")\n    end\n\n    it \"does not call the setup if none exists\" do\n      subject.instance_variable_set(\"@setup\", nil)\n      lambda {subject.setup_level}.should_not raise_error(NoMethodError)\n    end\n\n  end\n\n\n  describe \"#repo\" do\n\n    it \"initializes a repository when repo is called\" do\n      subject.repo.should equal(repo)\n      Githug::Repository.should_not_receive(:new)\n      subject.repo.should equal(repo)\n    end\n\n  end\n\n  describe \"#hint\" do\n\n    let(:profile) { mock.as_null_object }\n\n    before(:each) do\n      profile.stub(:current_hint_index).and_return(0,0,1,0)\n      Githug::Profile.stub(:load).and_return(profile)\n    end\n\n    it \"returns sequential hint if there are multiple\" do\n      subject.should_receive(:puts).ordered.with(\"this is hint 1\")\n      subject.show_hint\n\n      subject.should_receive(:puts).ordered.with(\"this is hint 2\")\n      subject.show_hint\n\n      subject.should_receive(:puts).ordered.with(\"this is hint 1\")\n      subject.show_hint\n    end\n\n    it \"displays a hint if there are not multiple\" do\n      subject.instance_variable_set(\"@hints\", nil)\n      subject.should_receive(:puts).with(\"this is a hint\")\n      subject.show_hint\n    end\n\n    it \"does not call the hint if none exist\" do\n      subject.instance_variable_set(\"@hint\", nil)\n      lambda {subject.show_hint}.should_not raise_error(NoMethodError)\n    end\n  end\n\n  describe \"#init_from_level\" do\n    it \"copies the files from the level folder\" do\n      FileUtils.should_receive(:cp_r).with(\"#{subject.level_path}/.\", \".\")\n      FileUtils.should_receive(:mv).with(\".githug\", \".git\")\n      subject.init_from_level\n    end\n  end\nend\n"
  },
  {
    "path": "spec/githug/profile_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Githug::Profile do\n\n  describe \".load\" do\n    it \"loads the profile\" do\n      settings = {:level => 1, :current_attempts => 0, :current_hint_index => 0, :current_levels => [], :completed_levels => []}\n      File.should_receive(:exists?).with(Githug::Profile::PROFILE_FILE).and_return(true)\n      File.should_receive(:open).with(Githug::Profile::PROFILE_FILE).and_return(\"settings\")\n      YAML.should_receive(:load).with(\"settings\").and_return(settings)\n      Githug::Profile.should_receive(:new).with(settings)\n      Githug::Profile.load\n    end\n\n    it \"loads the defaults if the file does not exist\" do\n      defaults = {:level => nil, :current_attempts => 0, :current_hint_index => 0, :current_levels => [], :completed_levels => []}\n      File.should_receive(:exists?).with(Githug::Profile::PROFILE_FILE).and_return(false)\n      Githug::Profile.should_receive(:new).with(defaults)\n      Githug::Profile.load\n    end\n  end\n\n  it \"allows method access to getters and setters\" do\n    profile = Githug::Profile.load\n    profile.level.should eql(nil)\n    profile.level = 1\n    profile.level.should eql(1)\n  end\n\n  describe \".save\" do\n\n    it \"saves the file\" do\n      profile = Githug::Profile.load\n      File.should_receive(:open).with(Githug::Profile::PROFILE_FILE, \"w\")\n      profile.save\n    end\n\n  end\n\n  describe \"level methods\" do\n\n    let(:profile) { Githug::Profile.load }\n\n    before(:each) do\n      profile.stub(:save)\n      @levels = Githug::Level::LEVELS\n      Githug::Level::LEVELS = [\"init\", \"add\", \"rm\", \"rm_cached\", \"diff\"]\n      profile.level = \"init\"\n    end\n\n    after(:each) do\n      Githug::Level::LEVELS = @levels\n    end\n\n    describe \"#level_bump\" do\n\n      it \"bumps the level\" do\n        profile.should_receive(:set_level).with(\"add\")\n        profile.level_bump\n      end\n\n      it \"resets the current_attempts\" do\n        profile.current_attempts = 1\n        profile.level_bump\n        profile.current_attempts.should eql(0)\n      end\n\n      it \"sets the level to the first incomplete level\" do\n        profile.level = \"rm_cached\"\n        profile.completed_levels = [\"init\", \"add\"]\n        profile.level_bump\n        profile.level.should eql(\"rm\")\n      end\n    end\n\n    describe \"#set_level\" do\n\n      it \"sets the level\" do\n        profile.should_receive(:save)\n        profile.should_receive(:reset!)\n        profile.set_level(\"rm\")\n        profile.settings[:level].should eql(\"rm\")\n      end\n\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "spec/githug/repository_spec.rb",
    "content": "require 'spec_helper'\n\ndescribe Githug::Repository do\n\n  let(:grit) { mock }\n\n  before(:each) do\n    Grit::Repo.stub(:new).and_return(grit)\n    subject.stub(:create_gitignore)\n  end\n\n  describe \"#initialize\" do\n\n    it \"calls grit on initialize\" do\n      Grit::Repo.should_receive(:new).with(\".\").and_return(grit)\n      repo = Githug::Repository.new\n      repo.grit.should equal(grit)\n    end\n\n    it \"contains a nil grit if the repo is invalid\" do\n      Grit::Repo.should_receive(:new).and_raise(Grit::InvalidGitRepositoryError)\n      repo = Githug::Repository.new\n      repo.grit.should equal(nil)\n    end\n\n    it \"initializes with a location\" do\n      Grit::Repo.should_receive(:new).with(\"test\").and_return(grit)\n      repo = Githug::Repository.new(\"test\")\n    end\n\n  end\n\n  describe \"#reset\" do\n\n    before(:each) do\n      FileUtils.stub(:rm_rf)\n    end\n\n    it \"does nothing if the current directory isn't git_hug\" do\n      Dir.stub(:pwd).and_return(\"/tmp/foo\")\n      FileUtils.should_not_receive(:rm_rf)\n      subject.reset\n    end\n\n    it \"removes all the files except .gitignore and .profile.yml\" do\n      Dir.stub(:pwd).and_return(\"/tmp/git_hug\")\n      Dir.stub(:entries).and_return([\".profile.yml\", \".gitignore\", \"..\", \".\", \"README\", \".git\"])\n      FileUtils.should_receive(:rm_rf).with(\"README\")\n      FileUtils.should_receive(:rm_rf).with(\".git\")\n      subject.reset\n    end\n  end\n\n\n  describe \"#create_gitignore\" do\n    it \"creates a gitignore\" do\n      subject.unstub(:create_gitignore)\n      File.stub(:exists?).and_return(true)\n      Dir.should_receive(:chdir).with(\"git_hug\")\n      File.should_receive(:open).with(\".gitignore\", \"w\")\n      subject.create_gitignore\n    end\n  end\n\n  describe \"#valid?\" do\n    it \"is valid if grit exists\" do\n      subject.should be_valid\n    end\n\n    it \"is not valid if grit does not exist\" do\n      subject.instance_variable_set(\"@grit\", nil)\n      subject.should_not be_valid\n    end\n  end\n\n  describe \"#init\" do\n    it \"does not add and commit gitignore if prompted\" do\n      Grit::Repo.should_receive(:init).with(\".\")\n      subject.init\n    end\n  end\n\n  describe \"#method_missing\" do\n    it \"deletegates to grit if the method exists\" do\n      grit.should_receive(:respond_to?).with(:valid_method).and_return(true)\n      grit.should_receive(:valid_method)\n      subject.valid_method\n    end\n\n    it \"should not deletegate to grit if the method does not exist\" do\n      grit.should_receive(:respond_to?).with(:invalid_method).and_return(false)\n      lambda { subject.invalid_method }.should raise_error(NoMethodError)\n    end\n  end\n\n\nend\n"
  },
  {
    "path": "spec/githug/ui_spec.rb",
    "content": "require 'spec_helper'\n\nrequire 'spec_helper'\n\ndescribe Githug::UI do\n\n  let(:ui_out) { StringIO.new }\n  let(:ui_in) { StringIO.new }\n\n  before(:each) do\n    subject.out_stream = ui_out\n    subject.in_stream = ui_in\n  end\n\n  it \"puts to the stream\" do\n    subject.puts(\"hello\")\n    ui_out.string.should eql(\"hello\\n\")\n  end\n\n  it \"prints an empty line with no arguments\" do\n    subject.puts\n    ui_out.string.should eql(\"\\n\")\n  end\n\n  it \"prints without a new line\" do\n    subject.print(\"hello\")\n    ui_out.string.should eql(\"hello\")\n  end\n\n  it \"fetches gets from input stream\" do\n    ui_in.puts \"bar\"\n    ui_in.rewind\n    subject.gets.should == \"bar\\n\"\n  end\n\n  it \"makes a wordbox\" do\n    word_box = <<-eof\n********************************************************************************\n*                                    Githug                                    *\n********************************************************************************\n    eof\n    subject.word_box(\"Githug\")\n    ui_out.string.should eql(word_box)\n  end\n\n  it \"prints a correct wordbox for uneven msg length\" do\n    subject.word_box(\"odd\",80)\n    printed = ui_out.string.lines\n    first_size = printed.first.chomp.length\n\n    printed.map{ |line| line.chomp.length.should eq(first_size) }\n  end\n\n  it \"requests text input\" do\n    ui_in.puts \"bar\"\n    ui_in.rewind\n    subject.request(\"foo\").should == \"bar\"\n    ui_out.string.should == \"foo \"\n  end\n\n  it \"asks for yes/no and return true when yes\" do\n    subject.should_receive(:request).with('foo? [yn] ').and_return('y')\n    subject.ask(\"foo?\").should be_true\n  end\n\n  it \"asks for yes/no and return false when no\" do\n    subject.stub(:request).and_return('n')\n    subject.ask(\"foo?\").should be_false\n  end\n\n  it \"asks for yes/no and return false for any input\" do\n    subject.stub(:request).and_return('aklhasdf')\n    subject.ask(\"foo?\").should be_false\n  end\n\n  describe \"Non Windows Platform\" do\n    before(:each) do\n      ENV.stub(:[]).with(\"OS\").and_return(nil)\n    end\n\n    it \"prints out a success message in green\" do\n      subject.success(\"success\")\n      ui_out.string.should eql(\"\\033[32msuccess\\033[0m\\n\")\n    end\n\n    it \"prints out a error message in red\" do\n      subject.error(\"error\")\n      ui_out.string.should eql(\"\\033[31merror\\033[0m\\n\")\n    end\n\n  end\n\n  describe \"Windows Platform\" do\n\n    before(:each) do\n      ENV.stub(:[]).with(\"OS\").and_return(\"Windows_NT\")\n    end\n\n    it \"prints out a success message in white\" do\n      subject.success(\"success\")\n      ui_out.string.should eql(\"success\\n\")\n    end\n\n    it \"prints out a error message in white\" do\n      subject.error(\"error\")\n      ui_out.string.should eql(\"error\\n\")\n    end\n\n  end\n\n\nend\n"
  },
  {
    "path": "spec/githug_spec.rb",
    "content": "require 'spec_helper'\n\nRSpec::Matchers.define :be_solved do\n  match do |actual|\n    !actual.match(\"Congratulations, you have solved the level!\").nil?\n  end\nend\n\ndef skip_level\n  Githug::Profile.load.level_bump\n  `githug reset`\nend\n\n\ndescribe \"The Game\" do\n\n\n  before(:all) do\n    @dir = Dir.pwd\n    `rake build`\n    `gem install pkg/githug-#{Githug::VERSION}.gem`\n    FileUtils.rm_rf(\"/tmp/git_hug\")\n    Dir.chdir(\"/tmp\")\n    `echo \"y\" | githug`\n    Dir.chdir(\"/tmp/git_hug\")\n  end\n\n  after(:all) do\n    Dir.chdir(@dir)\n  end\n\n  it \"solves the init level\" do\n    `git init`\n    `githug`.should be_solved\n  end\n\n  it \"solves the config level\" do\n    skip_level #The CI server does not have git config set\n    #full_name = `git config --get user.name`.chomp\n    #email = `git config --get user.email`.chomp\n    #f = IO::popen('githug', 'w')\n    #f.puts(full_name)\n    #f.puts(email)\n    #f.close\n  end\n\n  it \"solves the add level\" do\n    `git add README`\n    `githug`.should be_solved\n  end\n\n  it \"solves the commit level\" do\n    `git commit -m \"test message\"`\n    `githug`.should be_solved\n  end\n\n  it \"solves the clone level\" do\n    `git clone https://github.com/Gazler/cloneme`\n    `githug`.should be_solved\n  end\n\n  it \"solves the clone_to_folder level\" do\n    `git clone https://github.com/Gazler/cloneme my_cloned_repo`\n    `githug`.should be_solved\n  end\n\n  it \"solves the ignore level\" do\n    `echo \"*.swp\" >> .gitignore`\n    `githug`.should be_solved\n  end\n\n  it \"solves the include level\" do\n    `echo \"*.a\\n!lib.a\" >> .gitignore`\n    `githug`.should be_solved\n  end\n\n  it \"solves the status level\" do\n    `git ls-files --other --exclude-standard | githug`.should be_solved\n  end\n\n  it \"solves the number of files committed level\" do\n    `git diff --name-only --cached | wc -l | githug`.should be_solved\n  end\n\n  it \"solves the rm level\" do\n    file_name = `git status | grep deleted | cut -d \" \" -f 5`\n    `git rm #{file_name}`\n    `githug`.should be_solved\n  end\n\n  it \"solves the rm cached level\" do\n    file_name = `git status | grep \"new file\" | cut -d \" \" -f 5`\n    `git rm --cached #{file_name}`\n    `githug`.should be_solved\n  end\n\n  it \"solves the stash level\" do\n    `git stash save`\n    `githug`.should be_solved\n  end\n\n  it \"solves the rename level\" do\n    `git mv oldfile.txt newfile.txt`\n    `githug`.should be_solved\n  end\n\n  it \"solves the restructure level\" do\n    `mkdir src`\n    `git mv *.html src`\n    `githug`.should be_solved\n  end\n\n  it \"solves the log level\" do\n    `git log --pretty=short | grep commit | cut -c 8-14 | githug`.should be_solved\n  end\n\n  it \"solves the tag level\" do\n    `git tag new_tag`\n    `githug`.should be_solved\n  end\n\n  it \"solves the push_tags level\" do\n    `git push origin master --tags`\n    `githug`.should be_solved\n  end\n\n  it \"solves the commit_amend level\" do\n    `git add forgotten_file.rb`\n    `git commit --amend -C HEAD`\n    `githug`.should be_solved\n  end\n\n  it \"solves the commit_in_future level\" do\n    authored_date = Time.now + 14\n    authored_date = authored_date.rfc2822\n\n    `git commit -m \"Test of future date\" --date=\"#{authored_date}\"`\n    `githug`.should be_solved\n  end\n\n  it \"solves the reset level\" do\n    `git reset HEAD to_commit_second.rb`\n    `githug`.should be_solved\n  end\n\n  it \"solves the reset_soft level\" do\n    `git reset --soft HEAD^`\n    `githug`.should be_solved\n  end\n\n  it \"solves the checkout_file level\" do\n    `git checkout -- config.rb`\n    `githug`.should be_solved\n  end\n\n  it \"solves the remove level\" do\n    `git remote | githug`.should be_solved\n  end\n\n  it \"solves the remote_url level\" do\n    `git remote -v | tail -2 | head -1 | cut -c 17-52 | githug`.should be_solved\n  end\n\n  it \"solves the pull level\" do\n    `git pull origin master`\n    `githug`.should be_solved\n  end\n\n  it \"solves the remote_add level\" do\n    `git remote add origin https://github.com/githug/githug`\n    `githug`.should be_solved\n  end\n\n  it \"solves the push level\" do\n    `git rebase origin/master`\n    `git push origin`\n    `githug`.should be_solved\n  end\n\n  it \"solves the diff level\" do\n    `echo \"26\" | githug`.should be_solved\n  end\n\n  it \"solves the blame level\" do\n    `echo \"spider man\" | githug`.should be_solved\n  end\n\n  it \"solves the branch level\" do\n    `git branch test_code`\n    `githug`.should be_solved\n  end\n\n  it \"solves the checkout level\" do\n    `git checkout -b my_branch`\n    `githug`.should be_solved\n  end\n\n  it \"solves the checkout_tag level\" do\n    `git checkout v1.2`\n    `githug`.should be_solved\n  end\n\n  it \"solves the checkout_tag_over_branch level\" do\n    `git checkout tags/v1.2`\n    `githug`.should be_solved\n  end\n\n  it \"solves the branch_at level\" do\n    commit = `git log HEAD~1 --pretty=short | head -1 | cut -d \" \" -f 2`\n    `git branch test_branch #{commit}`\n    `githug`.should be_solved\n  end\n\n  it \"solves the delete_branch level\" do\n    `git branch -d delete_me`\n    `githug`.should be_solved\n  end\n\n  it \"solves the push_branch level\" do\n    `git push origin test_branch`\n    `githug`.should be_solved\n  end\n\n  it \"should commit the merge level\" do\n    `git merge feature`\n    `githug`.should be_solved\n  end\n\n  it \"solves the fetch level\" do\n    `git fetch`\n    `githug`.should be_solved\n  end\n\n  it \"solves the rebase level\" do\n    `git checkout feature`\n    `git rebase master`\n    `githug`.should be_solved\n  end\n\n  it \"solves the rebase_onto level\" do\n    `git checkout readme-update`\n    `git rebase --onto master wrong_branch readme-update`\n    `githug`.should be_solved\n  end\n\n  it \"solves the repack level\" do\n    `git repack -d`\n    `githug`.should be_solved\n  end\n\n  it \"solves the cherry-pick level\" do\n    commit = `git log new-feature --oneline  -n 3 | tail -1 | cut -d \" \" -f 1`\n    `git cherry-pick #{commit}`\n    `githug`.should be_solved\n  end\n\n  it \"solves the grep level\" do\n    `echo \"4\" | githug`.should be_solved\n  end\n\n  it \"solves the rename_commit level\" do\n    skip_level\n  end\n\n  it \"solves the squash level\" do\n    skip_level\n  end\n\n  it \"solves the merge squash level\" do\n    `git merge --squash long-feature-branch`\n    `git commit -m \"Merged Long Feature Branch\"`\n    `githug`.should be_solved\n  end\n\n  it \"solves the reorder level\" do\n    skip_level\n  end\n\n  it \"solves the bisect level\" do\n    `echo \"18ed2ac\" | githug`.should be_solved\n  end\n\n  it \"solves the stage_lines level\" do\n    skip_level\n  end\n\n  it \"solves the find_old_branch level\" do\n    `git checkout solve_world_hunger`\n    `githug`.should be_solved\n  end\n\n  it \"solves the revert level\" do\n    sleep 1\n    `git revert HEAD~1 --no-edit`\n    `githug`.should be_solved\n  end\n\n  it \"solves the restore level\" do\n    `git reflog | grep \"Restore this commit\" | awk '{print $1}' | xargs git checkout`\n    `githug`.should be_solved\n  end\n\n  it \"solves the conflict level\" do\n    skip_level\n  end\n\n  it \"solves the submodule level\" do\n      `git submodule add https://github.com/jackmaney/githug-include-me`\n      `githug`.should be_solved\n  end\n\n  it \"solves the contribute level\" do\n    skip_level\n  end\n\nend\n"
  },
  {
    "path": "spec/spec_helper.rb",
    "content": "require './lib/githug.rb'\n"
  },
  {
    "path": "spec/support/files/test_level.rb",
    "content": "difficulty 1\ndescription \"A test description\"\nsetup do\n  \"test\"\nend\nsolution do\n  Grit::Repo.new(\"githug/notadir\")\nend\n\nhints [\n  \"this is hint 1\",\n  \"this is hint 2\"]\n\nhint do\n  puts \"this is a hint\"\nend\n"
  }
]