Skip to content

Commit

Permalink
tg: add wayback machine
Browse files Browse the repository at this point in the history
Building on the "tg tag" + "tg revert" functionality, allow any
operation to be performed in the context of a "wayback" state.

Signed-off-by: Kyle J. McKay <mackyle@gmail.com>
  • Loading branch information
mackyle committed Jun 26, 2017
1 parent f63b69b commit 1dc3d2b
Show file tree
Hide file tree
Showing 6 changed files with 1,150 additions and 67 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Expand Up @@ -90,6 +90,9 @@
/tg-revert
/tg-revert.txt
/tg-revert.html
/tg-shell
/tg-shell.txt
/tg-shell.html
/tg-status.txt
/tg-status.html
/tg-summary
Expand Down
167 changes: 154 additions & 13 deletions README
Expand Up @@ -15,17 +15,18 @@ patch and providing some tools to maintain the branches.

See also:

:REQUIREMENTS_: Installation requirements
:SYNOPSIS_: Command line example session
:USAGE_: Command line details
:`NO UNDO`_: Where's the undo!!!
:CONVENTIONS_: Suggestions for organizing your TopGit branches
:`EXTRA SETTINGS`_: Various possible "topgit.*" config settings
:ALIASES_: Git-like TopGit command aliases
:NAVIGATION_: Getting around with "next" and "prev"
:GLOSSARY_: All the TopGit vocabulary in one place
:TECHNICAL_: How it works behind the scenes
:`TESTING TOPGIT`_: How to run the TopGit test suite
:REQUIREMENTS_: Installation requirements
:SYNOPSIS_: Command line example session
:USAGE_: Command line details
:`NO UNDO`_: Where's the undo!!!
:CONVENTIONS_: Suggestions for organizing your TopGit branches
:`EXTRA SETTINGS`_: Various possible "topgit.*" config settings
:ALIASES_: Git-like TopGit command aliases
:NAVIGATION_: Getting around with "next" and "prev"
:`WAYBACK MACHINE`_: Turn back the clock and then come back
:GLOSSARY_: All the TopGit vocabulary in one place
:TECHNICAL_: How it works behind the scenes
:`TESTING TOPGIT`_: How to run the TopGit test suite


REQUIREMENTS
Expand Down Expand Up @@ -361,12 +362,58 @@ command unless an explicit ``--stash`` option is given.
If you are likely to ever want to undo a ``tg update``, setting
``topgit.autostash`` to ``false`` is highly discouraged!

Note that if you have foolishly disabled the autostash functionality and
suddenly find yourself in an emergency "WHERE'S THE UNDO???" situation you
*may* be able to use the special ``TG_STASH`` ref. But only if you're quick.
It's only set if you've foolishly disabled autostash and it always overwrites
the previous ``TG_STASH`` value if there was one (there's no reflog for it)
and it will most likely *not* survive a ``git gc`` (even an automatic one) no
matter what gc expiration values are used.

Note that the tags saved by ``tg tag --stash`` are stored in the
``refs/tgstash`` ref and its reflog. Unfortunately, while Git is happy to
maintain the reflog (once it's been enabled which ``tg tag`` guarantees for
``refs/tgstash``), Git is unable to view an annotated/signed tag's reflog!
Instead Git dereferences the tag and shows the wrong thing. Use the
``tg tag -g`` command to view the ``refs/tgstash`` reflog instead.
Instead Git dereferences the tag and shows the wrong thing.

Use the ``tg tag -g`` command to view the ``refs/tgstash`` reflog instead.


WAYBACK MACHINE
---------------

After reading about `NO UNDO`_ and the `tg tag`_ command used to provide a
semblance of undo in some cases, you have the foundation to understand the
wayback machine.

The "wayback machine" provides a way to go back to a previous ref state as
stored in a TopGit tag created by `tg tag`_. It actually normally returns to a
hybrid state as it does not prune (unless you prefix the wayback tag with
a ``:``). In other words, any refs that have been newly created since the
target tag was made will continue to exist in the "wayback" view of things
(unless you used a pruning wayback tag -- one prefixed with a ``:``).

Any operations that are read-only and do not require working tree files (e.g.
the ``-i`` or ``-w`` options of `tg patch`_) are allowed using the wayback
machine. Simply add a global ``-w <tgtag>`` option to the command.

This functionality can be extremely useful for quickly examing/querying a
previous state recorded some time ago with a `tg tag`_.

As the wayback machine uses a separate caching area, except initial operations
to be less speedy, but repeated wayback operations on the same wayback tag
should happen at normal speed.

One new command exists expressly for use with the wayback machine.

The `tg shell`_ command will spawn an interactive shell or run a specific shell
command in a temporary writable and non-bare repository that has its ref
namespace set to the (possibly pruned if it's a pruning wayback tag) wayback
tag's view of the world. This pretty much lifts all wayback restrictions, but
read the description for `tg shell`_ for more details. There is an option
available to specify the location where this "temporary" directory is created
thereby allowing it to persist, but the same warnings then apply as using the
``git clone --shared`` command.


EXTRA SETTINGS
Expand Down Expand Up @@ -797,6 +844,7 @@ Global options:
-r <remote> Pretend ``topgit.remote`` is set to <remote>
-u Pretend ``topgit.remote`` is not set
-c <name=val> Pass config option to git, may be repeated
-w <tgtag> Activate `wayback machine`_ using the `tg tag`_ <tgtag>
--no-pager Disable use of any pager (by both TopGit and Git)
--pager Enable use of a pager (aka ``-p``)
--top-bases Show full ``top-bases`` ref prefix and exit
Expand Down Expand Up @@ -827,6 +875,7 @@ The ``tg`` tool has several subcommands:
:`tg rebase`_: Auto continue git rebase if rerere resolves conflicts
:`tg remote`_: Set up remote for fetching/pushing TopGit branches
:`tg revert`_: Revert ref(s) to a state stored in a ``tg tag``
:`tg shell`_: Extended `wayback machine`_ mode
:`tg status`_: Show current TopGit status (e.g. in-progress update)
:`tg summary`_: Show various information about TopGit branches
:`tg tag`_: Create tag that records current TopGit branch state
Expand Down Expand Up @@ -2060,6 +2109,98 @@ tg revert
tree will always be left completely untouched (and the reflog for
the pointed-to ref can always be used to find the previous value).

tg shell
~~~~~~~~
Enter extended `wayback machine`_ mode.

The global ``-w <tgtag>`` option must be specified (but as a special
case for the ``shell`` subcommand a <tgtag> destination of ``:`` may be
used to get a shell with no wayback ref changes).

The "<tgtag>" value must be the name of a tag created by (or known to)
`tg tag`_. However, it may also have a ``:`` prefixed to it to
indicate that it should prune (making it into a "pruning wayback tag").
Use of a "pruning wayback tag" results in a repository that contains
excludsively those refs listed in the specified tag. Otherwise the
wayback repository will just revert those refs while keeping the others
untouched (the default behavior).

The `wayback machine`_ activates as normal for the specified
destination but then a new ``${SHELL:-/bin/sh}`` is spawned in a
temporary non-bare repository directory that shares all the same
objects from the repository but has its own copy of the ref namespace
where the refs specified in the wayback destination have all been
changed to have their wayback values.

If any arguments are given a POSIX shell will be spawned instead
concatenating all the arguments together with a space and passing
them to it via a ``-c`` option. If ``-q`` (or ``--quote``) is given
then each argument will first be separately "quoted" to protect it from
the shell allowing something like this::

tg -w <tgtag> shell -q git for-each-ref --format="%(refname)"

to work without needing to manually add the extra level of quoting that
would otherwise be required due to the parentheses.

Most of the repository configuration will be inherited, but some
will be overridden for safety and for convenience. All "gc" activity
within the wayback repository will be suppressed to avoid accidents
(i.e. no auto gc will run and "gc" commands will complain and not run).

Override and/or bypass this safety protection at your own peril!
Especially *do not run* the ``git prune`` plumbing command in the
wayback repository! If you do so (or bypass any of the other safties)
be prepared for corruption and loss of data in the repository.
Just *don't do that* in the first place!

Using `git wayback-tag` will show the tag used to enter the wayback
machine. Using `git wayback-updates` will show ref changes that have
occurred since the wayback tag was created (it will not show refs that
have since been created unless a pruning wayback tag was used).
Finally, ``git wayback-repository`` will show the home repository but
so will ``git remote -v`` in the output displayed for the ``wayback``
remote.

The special ``wayback`` remote refers to the original repository and
can be used to push ref changes back to it. Note, however, that all
default push refspecs are disabled for safety and an explicit refspec
will need to be used to do so.

Unlike the normal `wayback machine`_ mode, ``HEAD`` will be detached
to a new commit with an empty tree that contains the message and author
from the wayback tag used. This prevents ugly status displays while
avoiding the need to checkout any files into the temporary working
tree. The parent of this commit will, however, be set to the wayback
tag's commit making it easy to access if desired.

Also unlike the normal `wayback machine`_ mode, there are no
limitations on what can be done in the temporary repository.
And since it will be non-bare and writable, commands that may not have
been allowed in the original repository will work too.

When the shell spawned by this subcommand exits, the temporary wayback
repository and all newly created objects and ref changes made in it, if
any, *will be lost*. If work has been done in it that needs to be
saved, it must be pushed somewhere (even if only back to the original
repository using the special ``wayback`` remote).

Lastly there's the ``--directory`` option. If the ``--directory``
option is used the temporary "wayback repository" will be created at
the specified location (which must either not exist or must be an empty
directory -- no force option available this time as too many things
could easily go wrong in that case). If the ``--directory`` option is
used then the "wayback repository" *will persist* after ``tg shell``
completes allowing it to continue to be used! Be warned though, all
the same warnings that apply to ``git clone --shared`` apply to such
a repository. If it's created using a ``tgstash`` tag those warnings
are especially salient. Use a single argument of either ``:`` (to
just create with no output) or ``pwd`` (to show the full absolute path
to the new "wayback repository") when using the ``--directory`` option
if the sole purpose is just to create the wayback repository for use.
Note that the ``--directory`` option *must* be listed as the first
option after the ``shell`` subcommand name if used.

tg prev
~~~~~~~
Output the "previous" branch(es) in the patch series containing the
Expand Down
19 changes: 10 additions & 9 deletions t/t2000-hook-installation.sh
Expand Up @@ -90,6 +90,7 @@ push
rebase
remote
revert
shell
status
summary
tag
Expand All @@ -100,7 +101,7 @@ version
tg_cmd_will_setup() {
case "$1" in
--version|--status|--hooks-path|--exec-path|--awk-path|--top-bases| \
base|contains|files|info|log|mail|next|patch|prev|rebase|revert|status|st|summary|tag|version)
base|contains|files|info|log|mail|next|patch|prev|rebase|revert|shell|status|st|summary|tag|version)
return 1
esac
return 0
Expand All @@ -115,8 +116,8 @@ test_expect_success 'no setup happens for help' '
test_might_fail tg --bogus && has_no_tg_setup &&
for cmd in $TG_CMDS; do
say "# checking tg $cmd help variations" &&
test_might_fail tg $cmd -h && has_no_tg_setup &&
test_might_fail tg $cmd --help && has_no_tg_setup
test_might_fail </dev/null tg $cmd -h && has_no_tg_setup &&
test_might_fail </dev/null tg $cmd --help && has_no_tg_setup
done &&
has_no_tg_setup &&
cd .. && rm -rf r3
Expand All @@ -127,8 +128,8 @@ test_expect_success 'no setup happens for exempted commands' '
for cmd in $TG_CMDS; do
if ! tg_cmd_will_setup "$cmd"; then
say "# checking tg $cmd does not do setup"
test_might_fail tg $cmd && has_no_tg_setup &&
test_might_fail tg $cmd --bogus-option-here && has_no_tg_setup
test_might_fail </dev/null tg $cmd && has_no_tg_setup &&
test_might_fail </dev/null tg $cmd --bogus-option-here && has_no_tg_setup
fi
done &&
has_no_tg_setup &&
Expand All @@ -140,8 +141,8 @@ for cmd in $TG_CMDS; do
test_expect_success "setup happens for tg $cmd" '
rm -rf rt && test_create_repo rt && cd rt &&
has_no_tg_setup &&
test_might_fail tg $cmd &&
test_might_fail tg $cmd --bogus-option-here &&
test_might_fail </dev/null tg $cmd &&
test_might_fail </dev/null tg $cmd --bogus-option-here &&
has_tg_setup &&
cd .. && rm -rf rt
'
Expand All @@ -152,8 +153,8 @@ test_expect_success 'no setup happens in bare repository' '
git init --bare r5 && cd r5 &&
for cmd in $TG_CMDS; do
say "# checking tg $cmd does not do setup in bare repo"
test_might_fail tg $cmd && has_no_tg_setup_bare &&
test_might_fail tg $cmd --bogus-option-here && has_no_tg_setup_bare
test_might_fail </dev/null tg $cmd && has_no_tg_setup_bare &&
test_might_fail </dev/null tg $cmd --bogus-option-here && has_no_tg_setup_bare
done &&
has_no_tg_setup_bare &&
cd .. && rm -rf r5
Expand Down

0 comments on commit 1dc3d2b

Please sign in to comment.