After playing with two pre-prototypes and discussing a wide variety of ideas on this list, I have given detailed thought to a v1 checkpointing design with the following properties:

  * local (not server)
  * built on shelving v1 (patches)
* main commands are 'checkpoint' to save a new version and 'rollback' to revert to version N
  * not intended for making a series of commits
  * designed to integrate with 'changelists' later

This aims to streamline the manual method, which I use myself, of using 'svn diff' and 'svn patch' to create and apply patches named 'feature-v1.patch', 'feature-v2.patch', etc.

I plan to go ahead and implement this, in order to have something useful. At the same time I will keep an eye on further developments and the wider picture.


== Shelving and Checkpointing Commands ==

unshelve X [N]
  assert(not X.applied)
  if (any patch-get-paths(X) is modified):
    warn?
  patch-apply X [N]
  X.applied := true

shelve X [PATH...]
  # X may or may not be previously 'applied'
  assert(any PATH... is modified)
  patch-save(X, PATH...)
  patch-unapply X
  X.applied := false

checkpoint X [PATH...]
  # X may or may not be previously 'applied'
  assert(any PATH... is modified)
  patch-save(X, PATH...)
  X.applied := true

rollback X [N]
  assert(X.applied)
  revert(union( patch-get-files(X, X.current),
                patch-get-files(X, N) ))
  patch-apply(X, N)
  patch-set-current(X, N)

drop/remove/delete X
  # current syntax "svn shelve --delete X"
  assert(not X.applied)
  patch-delete-all X


== Interaction with existing commands ==

Existing ways to remove a change from the WC are 'revert' and 'commit'.

commit X
  # possible syntax "svn commit --changelist X"
  assert(X.applied)
  commit(patch-get-files(X))
  X.applied := false

revert X
  # possible syntax "svn revert --changelist X"
  assert(X.applied)
  revert(patch-get-files(X))
  X.applied := false

revert PATH...
  revert(PATH...)
  if PATH... includes patch-get-files(X):
    X.active := false


== Low-level Interface ==

patch-save(X, PATH...)
  save diff(PATH...) as 'X-<latest+1>.patch'

patch-apply(X, N=latest)
  apply 'X-N.patch' to the WC

patch-unapply(X, N=latest)
  reverse-apply 'X-N.patch' to the WC

patch-delete-all(X)
  delete all versions of X

patch-get-current(X)
  return N = the maximum '*' in 'X-*.patch'

path-set-current(X, N)
  mark the current applied version of X as being N
  (options: prune all versions greater than N, or set X.current=N,
  or even perhaps 'symlink' 'X-<latest+1>.patch' to 'X-N.patch')

patch-get-files(X, N=latest)
  return the list of (file) paths in X version N


=== Remembering the Applied/Shelved state ===

For each change-set name X, the WC will remember whether X is considered to be currently 'applied' to the WC, and if so, at which version N. This is needed so that 'rollback' knows (roughly) what to revert, and to a lesser degree so that commands like 'shelve' and 'unshelve' can say 'hold on, you already have [or do not have] that change-set applied'.

The Shelving v1 implementation by default deleted a patch (or rather renamed it to '.bak') when unshelving, and when shelving it objected if the given name was already shelved. Now in this design we are going to keep all versions of X (and 'shelve' will always add a new version) so we will store a separate 'applied' flag instead.

Multiple independent change-sets can be active in the WC at the same time, like with 'changelists'. Clashes (overlaps) in the list of paths affected must be managed manually. (Potential enhancement: add some warnings, e.g. if 'rollback' is going to revert a file that is part of both the specified change-set and another one.)


=== Rollback and Revert ===

Perhaps one of the trickiest parts is which files will 'rollback' revert. Suggestion is union(files in current applied version, files in requested version).

May want to check and warn if those files overlap with any other currently applied change-set.

Integration with changelists should clarify this.


=== Path spec [PATH...] ===

* is applicable to 'save' commands (shelve, checkpoint)
* is not applicable to other commands
* default is "." like in most svn commands
* is restrictive (restricts operation to PATH...)
* is not tracked or managed
* clashes (same path in more than one applied patch) are not managed

Future considerations:

* when checkpointing, warn if PATH... excludes any paths that were in the previous version?
* consider allowing restricting paths on 'apply' commands.


=== Changelists and Terminology ===

Already in this design we see a need to specify a 'change set' which is not necessarily in a 'shelved' state. The existing 'changelist' fits this concept quite closely. It makes sense to extend it to do so. I even suggest 'svn revert|commit --changelist=X' as a possible syntax although that won't quite fit the v1 implementation if we haven't integrated with changelists yet.

One implication is we need a consistent name to use for the change-sets that we are dealing with. 'change-set' is pretty good; 'changelist' has the advantage of already existing although I don't like it so much. 'Shelved change' and 'patch' no longer work when talking about modifications in the WC.

I was already finding terminology very awkward (and I have been inconsistent in using it) because of the difference between 'shelf' (noun) and 'shelve' (verb) and whether a single change-set contains 'a change' or 'some changes'. I am glad to have a reason to change it.


=== Roll Forward ===

As a starting point, rollback be destructive, deleting versions newer than the target version. As an enhancement, it could be made to keep the newer versions and allow rolling forward to them. It could operate like the 'undo stack' model commonly found in editing applications, where roll-forward (often named 'redo') is possible up until a different change is saved to the stack, at which time the possibility is lost.

Auto checkpoint before rollback would then be possible too.


=== Commit Log Messages ===

Each new version should keep any previously specified log message by default, and allow it to be replaced or edited.

In 'rollback': keep the latest version of the log message; maybe do not even store older versions of it.

This mental model -- unversioned log message, versioned files -- is simple and fits well with existing svn concepts.


- Julian

Reply via email to