[RFC] undo and redo

9 messages, 4 authors, 2016-06-15 · open the first message on its own page

[RFC] undo and redo

From: Carl Baldwin <hidden>
Date: 2016-06-15 22:42:04

Hello,

So, one thing that I liked about GNU Arch when I tried it out was the
ability to undo and redo changes in the local working copy.  I decided
to try to do this with git.  What I have is preliminary.  I'm sure it
could use some work.

So, I started with the assumption that all changes in the working copy
have been updated to the cache.  My scripts check this (with
git-diff-files) and abort if this is not the case.

Undo calls git-write-tree to write the changes to the object store.  It
stores that tree's hash and the current HEAD's tree's hash in a file.
Then it reverts the working copy to HEAD.

Redo grabs these two trees from the file, does git-write-tree to produce
a third tree and merges the three using the old HEAD's tree as the base
of the merge.  This way, new commits can happen and the local copy can
be modified since the undo and it should still work assuming no
conflicts emerge.

Attached are the two scripts.  Comments and criticism are welcome.

Cheers,
Carl

-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Carl Baldwin                        Systems VLSI Laboratory
 Hewlett Packard Company
 MS 88                               work: 970 898-1523
 3404 E. Harmony Rd.                 work: Carl.N.Baldwin@hp.com
 Fort Collins, CO 80525              home: Carl@ecBaldwin.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Re: [RFC] undo and redo

From: Carl Baldwin <hidden>
Date: 2016-06-15 22:42:04

Oops.  I forgot to actually exit from the script if git-diff-files is
non-empty.

Also, looking at it now, I don't think keeping undo information in a
stack is the right thing.  But keeping more than just one would be good.
Oh well, my first shot is never perfect.  ;-)

Carl

On Wed, Aug 24, 2005 at 11:23:39AM -0600, Carl Baldwin wrote:
Hello,

So, one thing that I liked about GNU Arch when I tried it out was the
ability to undo and redo changes in the local working copy.  I decided
to try to do this with git.  What I have is preliminary.  I'm sure it
could use some work.

So, I started with the assumption that all changes in the working copy
have been updated to the cache.  My scripts check this (with
git-diff-files) and abort if this is not the case.

Undo calls git-write-tree to write the changes to the object store.  It
stores that tree's hash and the current HEAD's tree's hash in a file.
Then it reverts the working copy to HEAD.

Redo grabs these two trees from the file, does git-write-tree to produce
a third tree and merges the three using the old HEAD's tree as the base
of the merge.  This way, new commits can happen and the local copy can
be modified since the undo and it should still work assuming no
conflicts emerge.

Attached are the two scripts.  Comments and criticism are welcome.

Cheers,
Carl

-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Carl Baldwin                        Systems VLSI Laboratory
 Hewlett Packard Company
 MS 88                               work: 970 898-1523
 3404 E. Harmony Rd.                 work: Carl.N.Baldwin@hp.com
 Fort Collins, CO 80525              home: Carl@ecBaldwin.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#!/bin/sh

. git-sh-setup-script || die "Not a git archive"

if [ -n "$(git-diff-files)" ]; then
    echo The following files should be updated!
    echo
    git-diff-files | awk '{print $6}'
fi

undostack=$GIT_DIR/undostack

if [ ! -s $undostack ]; then
    echo "No undo information in $undostack"
else
    # Read the top of the stack
    basetree=$(cat $undostack | tail -n 2 | head -n 1)
    redotree=$(cat $undostack | tail -n 1)

    # Pop the stack
    cat $undostack | head -n -2 > $undostack.tmp
    mv $undostack{.tmp,}

    currenttree=$(git-write-tree)

    git-read-tree -u -m $basetree $currenttree $redotree
    git-merge-cache git-merge-one-file-script -a
fi
#!/bin/sh

. git-sh-setup-script || die "Not a git archive"

if [ -n "$(git-diff-files)" ]; then
    echo The following files should be updated!
    echo
    git-diff-files | awk '{print $6}'
fi

undostack=$GIT_DIR/undostack

headtree=$(git-cat-file commit $(cat $GIT_DIR/HEAD) | head -n 1 | sed -e 's/tree //')
undotree=$(git-write-tree)

if [ $headtree == $undotree ]; then
    echo There are no changes to undo.
else
    {
       echo $headtree
       echo $undotree
    } >> $undostack

    echo Saved current state as tree $undotree.
    echo Reverting to HEAD, $headtree...

    git-checkout-script -f
fi

-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Carl Baldwin                        Systems VLSI Laboratory
 Hewlett Packard Company
 MS 88                               work: 970 898-1523
 3404 E. Harmony Rd.                 work: Carl.N.Baldwin@hp.com
 Fort Collins, CO 80525              home: Carl@ecBaldwin.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Re: [RFC] undo and redo

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:04

Carl Baldwin [off-list ref] writes:
Attached are the two scripts.  Comments and criticism are welcome.
An obligatory non-technical comment.  I would have liked to see
this not in a MIME multipart format, which made commenting on it
a bit harder than necessary.
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=git-undo-script

#!/bin/sh

. git-sh-setup-script || die "Not a git archive"

if [ -n "$(git-diff-files)" ]; then
    echo The following files should be updated!
    echo
    git-diff-files | awk '{print $6}'
fi
There is nothing wrong with the above, but I would have written
it like this (I think you forgot to exit after showing the list
of files):

    git-update-cache --refresh || exit

Also nice to learn here is "git-diff-files --name-only".
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=git-redo-script

#!/bin/sh

. git-sh-setup-script || die "Not a git archive"

if [ -n "$(git-diff-files)" ]; then
    echo The following files should be updated!
    echo
    git-diff-files | awk '{print $6}'
fi
Same here.
    currenttree=$(git-write-tree)
    git-read-tree -u -m $basetree $currenttree $redotree
    git-merge-cache git-merge-one-file-script -a
Interesting.  Very interesting.

Re: [RFC] undo and redo

From: Linus Torvalds <torvalds@osdl.org>
Date: 2016-06-15 22:42:04


On Wed, 24 Aug 2005, Carl Baldwin wrote:
Oops.  I forgot to actually exit from the script if git-diff-files is
non-empty.

Also, looking at it now, I don't think keeping undo information in a
stack is the right thing.  But keeping more than just one would be good.
Oh well, my first shot is never perfect.  ;-)
I would actually argue that

	git checkout -b newbranch <undo-point>

is the perfect undo.

It leaves the old state in the old branch, and creates a new branch (and
checks it out) with the state you want to revert to. The advantage is
exactly that there is no "stack" of undo's: you can have multiple
independent undo's pending, and you can continue development at any of 
them. And merge the results together.

Of course, right now we don't have a "delete branch" command, but it's 
really as simple as

	rm .git/refs/heads/branchname

(and eventually you may want to do a "git prune" to get rid of stale
objects, but that's a separate issue).

		Linus

Re: [RFC] undo and redo

From: Carl Baldwin <hidden>
Date: 2016-06-15 22:42:04

On Wed, Aug 24, 2005 at 11:51:32AM -0700, Linus Torvalds wrote:

On Wed, 24 Aug 2005, Carl Baldwin wrote:
quoted
Oops.  I forgot to actually exit from the script if git-diff-files is
non-empty.

Also, looking at it now, I don't think keeping undo information in a
stack is the right thing.  But keeping more than just one would be good.
Oh well, my first shot is never perfect.  ;-)
I would actually argue that

	git checkout -b newbranch <undo-point>

is the perfect undo.
Yes, this does the job nicely.  I've used it like this effectively.  I
meant for undo/redo to be a lighter weight way of moving (uncommitted)
changes out of the way briefly and then replaying them onto the working
directory later.
It leaves the old state in the old branch, and creates a new branch (and
checks it out) with the state you want to revert to. The advantage is
exactly that there is no "stack" of undo's: you can have multiple
independent undo's pending, and you can continue development at any of 
them. And merge the results together.
The "stack" was the wrong thing to do.  I think I would have undo pick a
name like undo-1, undo-2 etc.  Or something like that.  redo would pick
the most recent unless told to do otherwise.

A possible advantage of undo is having the freedom to stay on the
current branch or switch to another.
Of course, right now we don't have a "delete branch" command, but it's 
really as simple as

	rm .git/refs/heads/branchname

(and eventually you may want to do a "git prune" to get rid of stale
objects, but that's a separate issue).

		Linus
This brings up a good point (indirectly).  "git prune" would destroy the
undo objects.  I had thought of this but decided to ignore it for the
time being.

Carl

-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Carl Baldwin                        Systems VLSI Laboratory
 Hewlett Packard Company
 MS 88                               work: 970 898-1523
 3404 E. Harmony Rd.                 work: Carl.N.Baldwin@hp.com
 Fort Collins, CO 80525              home: Carl@ecBaldwin.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Re: [RFC] undo and redo

From: Carl Baldwin <hidden>
Date: 2016-06-15 22:42:04

On Wed, Aug 24, 2005 at 11:18:42AM -0700, Junio C Hamano wrote:
Carl Baldwin [off-list ref] writes:
quoted
Attached are the two scripts.  Comments and criticism are welcome.
An obligatory non-technical comment.  I would have liked to see
this not in a MIME multipart format, which made commenting on it
a bit harder than necessary.
quoted
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=git-undo-script

#!/bin/sh

. git-sh-setup-script || die "Not a git archive"

if [ -n "$(git-diff-files)" ]; then
    echo The following files should be updated!
    echo
    git-diff-files | awk '{print $6}'
fi
There is nothing wrong with the above, but I would have written
it like this (I think you forgot to exit after showing the list
of files):

    git-update-cache --refresh || exit
I'll take this.  This is what I was going for but being new to git I
didn't know all that was available.  A good reason to request comments
:-)
Also nice to learn here is "git-diff-files --name-only".
Also good to know, thanks.

Carl

-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Carl Baldwin                        Systems VLSI Laboratory
 Hewlett Packard Company
 MS 88                               work: 970 898-1523
 3404 E. Harmony Rd.                 work: Carl.N.Baldwin@hp.com
 Fort Collins, CO 80525              home: Carl@ecBaldwin.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Re: [RFC] undo and redo

From: Daniel Barkalow <hidden>
Date: 2016-06-15 22:42:04

On Wed, 24 Aug 2005, Carl Baldwin wrote:
This brings up a good point (indirectly).  "git prune" would destroy the
undo objects.  I had thought of this but decided to ignore it for the
time being.
If you made undo store the tree under refs somewhere, git prune would
preserve it.

	-Daniel
*This .sig left intentionally blank*

Re: [RFC] undo and redo

From: Carl Baldwin <hidden>
Date: 2016-06-15 22:42:04

This is interesting.  Can a ref be to a tree rather than a commit?  And
it still works?  I guess it would.  I hadn't thought about that.

Will prune preserve any tree mentioned in any file in refs?  How does
this work exactly?

Cheers,
Carl

On Wed, Aug 24, 2005 at 04:44:48PM -0400, Daniel Barkalow wrote:
On Wed, 24 Aug 2005, Carl Baldwin wrote:
quoted
This brings up a good point (indirectly).  "git prune" would destroy the
undo objects.  I had thought of this but decided to ignore it for the
time being.
If you made undo store the tree under refs somewhere, git prune would
preserve it.

	-Daniel
*This .sig left intentionally blank*
-- 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
 Carl Baldwin                        Systems VLSI Laboratory
 Hewlett Packard Company
 MS 88                               work: 970 898-1523
 3404 E. Harmony Rd.                 work: Carl.N.Baldwin@hp.com
 Fort Collins, CO 80525              home: Carl@ecBaldwin.net
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Re: [RFC] undo and redo

From: Daniel Barkalow <hidden>
Date: 2016-06-15 22:42:04

On Wed, 24 Aug 2005, Carl Baldwin wrote:
This is interesting.  Can a ref be to a tree rather than a commit?  And
it still works?  I guess it would.  I hadn't thought about that.
Generally, each subdirectory of refs/ has refs to objects of the same
type, and heads/ is commits, but other directories are other things. tags/
is all tag objects, and you could have undo/ be trees.
Will prune preserve any tree mentioned in any file in refs?  How does
this work exactly?
It keeps any object reachable from an object that there's a ref to in
refs.

	-Daniel
*This .sig left intentionally blank*
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help