threads / discuss / 22627

Individual file snapshots

Subject: Individual file snapshots

## tl;dr

18 messages between Feb 12, 2010 and Feb 13, 2010.

replies: 17people: 7as markdown or json

Ron Garret· Feb 12, 2010, 12:03 UTC · lore
Before I reinvent the wheel...

I would like to be able to store snapshots of individual files without making a commit on the main branch. The scenario is that I've written some experimental code that I have decided not to keep as part of the main project, but which I might want to refer back to some day. Is there any easy way of doing that in git?

Thanks, rg

Peter Krefting· Feb 12, 2010, 12:18 UTC · re: Ron Garret · lore

Re: Individual file snapshots

Ron Garret:
> I would like to be able to store snapshots of individual files without 
> making a commit on the main branch.
   git hash-object -w filename
will add the file to the object database, outputting it's hash value.
   git tag mytag hashvalue
creates a tag "mytag" pointing to that file.
   git show mytag
will output the file you put in.
-- 
\\// Peter - http://www.softwolves.pp.se/
Ron Garret· Feb 12, 2010, 17:24 UTC · re: Peter Krefting · lore

Re: Individual file snapshots

In article <alpine.DEB.2.00.1002121316260.10583@ds9.cixit.se>,
 Peter Krefting <peter@softwolves.pp.se> wrote:
Show 16 quoted lines
> Ron Garret:
> 
> > I would like to be able to store snapshots of individual files without 
> > making a commit on the main branch.
> 
>    git hash-object -w filename
> 
> will add the file to the object database, outputting it's hash value.
> 
>    git tag mytag hashvalue
> 
> creates a tag "mytag" pointing to that file.
> 
>    git show mytag
> 
> will output the file you put in.

Ah, cool! Thanks. I knew about git hash-object, but I did not know that you could tag the resulting hash. I thought you could only tag commit objects.

rg
René Scharfe· Feb 12, 2010, 20:41 UTC · re: Ron Garret · lore

Re: Individual file snapshots

Am 12.02.2010 13:03, schrieb Ron Garret:
Show 7 quoted lines
> Before I reinvent the wheel...
> 
> I would like to be able to store snapshots of individual files without 
> making a commit on the main branch.  The scenario is that I've written 
> some experimental code that I have decided not to keep as part of the 
> main project, but which I might want to refer back to some day.  Is 
> there any easy way of doing that in git?

You could keep the experimental files (or changes) in a separate, private branch on your local repository.

René
Ron Garret· Feb 12, 2010, 21:25 UTC · re: René Scharfe · lore

Re: Individual file snapshots

In article <4B75BD06.1010802@lsrfire.ath.cx>,
 René Scharfe <rene.scharfe@lsrfire.ath.cx> wrote:
Show 13 quoted lines
> Am 12.02.2010 13:03, schrieb Ron Garret:
> > Before I reinvent the wheel...
> > 
> > I would like to be able to store snapshots of individual files without 
> > making a commit on the main branch.  The scenario is that I've written 
> > some experimental code that I have decided not to keep as part of the 
> > main project, but which I might want to refer back to some day.  Is 
> > there any easy way of doing that in git?
> 
> You could keep the experimental files (or changes) in a separate,
> private branch on your local repository.
> 
> René

Yeah, I considered that. The problem with that is that the actual process turns out to be pretty obtrusive. The scenario is that I've done a bunch of hacking on the main branch and I realize that it's going nowhere. Nothing is working, everything is a horrible mess that's spinning wildly out of control. I want to get rid of everything I've done and start over from an earlier snapshot that I knew was working. But I also want to keep a copy of this current messy state around for reference just in case there's a snippet here and there that might be salvageable later on. I don't know of any easy way to save the messed up file onto another branch. I'd have to save the file somewhere (in the stash maybe?), check out the snapshot branch, retrieve the saved file, do the commit, and then switch back to the main branch.

But maybe the right answer is to just write a script that does all that automatically?

rg
Brian Gernhardt· Feb 12, 2010, 21:37 UTC · re: Ron Garret · lore

Re: Individual file snapshots

On Feb 12, 2010, at 4:25 PM, Ron Garret wrote:
Show 12 quoted lines
> Yeah, I considered that.  The problem with that is that the actual 
> process turns out to be pretty obtrusive.  The scenario is that I've 
> done a bunch of hacking on the main branch and I realize that it's going 
> nowhere.  Nothing is working, everything is a horrible mess that's 
> spinning wildly out of control.  I want to get rid of everything I've 
> done and start over from an earlier snapshot that I knew was working.  
> But I also want to keep a copy of this current messy state around for 
> reference just in case there's a snippet here and there that might be 
> salvageable later on.  I don't know of any easy way to save the messed 
> up file onto another branch.  I'd have to save the file somewhere (in 
> the stash maybe?), check out the snapshot branch, retrieve the saved 
> file, do the commit, and then switch back to the main branch.
Have you tried creating a new branch without saving the state?

git checkout -b failed-experiment git commit -a -m "Back to the drawing board" git checkout master

Or if a series of commits seem to have gone bad:

git commit -a -m "Well, that didn't work." git branch failed-experiment git reset --hard origin/master # or other good state

You can of course replace the -a to commit with the needed "git add <file>" commands and leave off the -m to leave real messages about why it went bad using $EDITOR.
~~ Brian
Ron Garret· Feb 12, 2010, 21:57 UTC · re: Brian Gernhardt · lore

Re: Individual file snapshots

In article <12B5BDAB-DD9C-4CED-9489-0773BF577DF3@silverinsanity.com>,
 Brian Gernhardt <benji@silverinsanity.com> wrote:
Show 30 quoted lines
> On Feb 12, 2010, at 4:25 PM, Ron Garret wrote:
> 
> > Yeah, I considered that.  The problem with that is that the actual 
> > process turns out to be pretty obtrusive.  The scenario is that I've 
> > done a bunch of hacking on the main branch and I realize that it's going 
> > nowhere.  Nothing is working, everything is a horrible mess that's 
> > spinning wildly out of control.  I want to get rid of everything I've 
> > done and start over from an earlier snapshot that I knew was working.  
> > But I also want to keep a copy of this current messy state around for 
> > reference just in case there's a snippet here and there that might be 
> > salvageable later on.  I don't know of any easy way to save the messed 
> > up file onto another branch.  I'd have to save the file somewhere (in 
> > the stash maybe?), check out the snapshot branch, retrieve the saved 
> > file, do the commit, and then switch back to the main branch.
> 
> Have you tried creating a new branch without saving the state?
> 
> git checkout -b failed-experiment
> git commit -a -m "Back to the drawing board"
> git checkout master
> 
> Or if a series of commits seem to have gone bad:
> 
> git commit -a -m "Well, that didn't work."
> git branch failed-experiment
> git reset --hard origin/master # or other good state
> 
> You can of course replace the -a to commit with the needed "git add <file>" 
> commands and leave off the -m to leave real messages about why it went bad 
> using $EDITOR.

That would require a separate branch for every snapshot, no? I want this to be lightweight. It's not so much the creation of a zillion branches that bothers me, but having to come up with a new name every time would be a real hassle.

rg
Junio C Hamano· Feb 12, 2010, 22:14 UTC · re: Ron Garret · lore

Re: Individual file snapshots

Ron Garret <ron1@flownet.com> writes:
Show 14 quoted lines
>> Or if a series of commits seem to have gone bad:
>> 
>> git commit -a -m "Well, that didn't work."
>> git branch failed-experiment
>> git reset --hard origin/master # or other good state
>> 
>> You can of course replace the -a to commit with the needed "git add <file>" 
>> commands and leave off the -m to leave real messages about why it went bad 
>> using $EDITOR.
>
> That would require a separate branch for every snapshot, no?  I want 
> this to be lightweight.  It's not so much the creation of a zillion 
> branches that bothers me, but having to come up with a new name every 
> time would be a real hassle.
Perhaps "stash"?

Usually stash is used to store "diff between stash@{N}^1 and stash@{N}", and "stash pop" or "stash apply" are geared toward that usage. But you can use it as a way to keep snapshots. You would be interacting with the stash differently, though. E.g.

	git diff stash@{4}
	git diff origin/master stash@{2}
	git checkout stash -- Makefile
Ron Garret· Feb 12, 2010, 22:41 UTC · re: Junio C Hamano · lore

Re: Individual file snapshots

In article <7v1vgqksoz.fsf@alter.siamese.dyndns.org>,
 Junio C Hamano <gitster@pobox.com> wrote:
Show 28 quoted lines
> Ron Garret <ron1@flownet.com> writes:
> 
> >> Or if a series of commits seem to have gone bad:
> >> 
> >> git commit -a -m "Well, that didn't work."
> >> git branch failed-experiment
> >> git reset --hard origin/master # or other good state
> >> 
> >> You can of course replace the -a to commit with the needed "git add 
> >> <file>" 
> >> commands and leave off the -m to leave real messages about why it went bad 
> >> using $EDITOR.
> >
> > That would require a separate branch for every snapshot, no?  I want 
> > this to be lightweight.  It's not so much the creation of a zillion 
> > branches that bothers me, but having to come up with a new name every 
> > time would be a real hassle.
> 
> Perhaps "stash"?
> 
> Usually stash is used to store "diff between stash@{N}^1 and stash@{N}",
> and "stash pop" or "stash apply" are geared toward that usage.  But you
> can use it as a way to keep snapshots.  You would be interacting with
> the stash differently, though.  E.g.
> 
> 	git diff stash@{4}
> 	git diff origin/master stash@{2}
> 	git checkout stash -- Makefile

No, I don't really want to usurp the stash for this. But I think a separate disjoint branch (with an empty root) will work. I think I can use git checkout to move the files back and forth between the main branch and the snapshot branch without causing merge problems if the snapshot branch is disjoint.

rg
Brian Gernhardt· Feb 12, 2010, 22:32 UTC · re: Ron Garret · lore

Re: Individual file snapshots

On Feb 12, 2010, at 4:57 PM, Ron Garret wrote:
> That would require a separate branch for every snapshot, no?  I want 
> this to be lightweight.  It's not so much the creation of a zillion 
> branches that bothers me, but having to come up with a new name every 
> time would be a real hassle.
Ahhhh...  You could just use the stash and use some kind of recognizable message: git stash Failed: fooed the bar badly
Or you could make a stash-like snapshot branch?  The following sequence does something like that:
  git add .
  git commit -m "Ooops"
  git branch -f
  git reset --hard HEAD^
After a few failures like that, you can look over all the snapshots with
  git log -g snapshots
By default they will be kept around for 30 days, or the current setting of gc.reflogexpireunreachable.  If you want to keep a snapshot around for more than 30 days, you probably want to give it a real branch anyway.
You could either create a script that uses the above commands or a patch that makes git-stash take a --ref= argument and then set:
  alias.snapshot = stash --ref=refs/snapshots
~~ Brian
Ron Garret· Feb 12, 2010, 22:39 UTC · re: Brian Gernhardt · lore

Re: Individual file snapshots

In article <C022F034-F60E-4E89-A174-DC0F53ADEC19@silverinsanity.com>,
 Brian Gernhardt <benji@silverinsanity.com> wrote:
Show 30 quoted lines
> On Feb 12, 2010, at 4:57 PM, Ron Garret wrote:
> 
> > That would require a separate branch for every snapshot, no?  I want 
> > this to be lightweight.  It's not so much the creation of a zillion 
> > branches that bothers me, but having to come up with a new name every 
> > time would be a real hassle.
> 
> Ahhhh...  You could just use the stash and use some kind of recognizable 
> message: git stash Failed: fooed the bar badly
> 
> Or you could make a stash-like snapshot branch?  The following sequence does 
> something like that:
> 
>   git add .
>   git commit -m "Ooops"
>   git branch -f
>   git reset --hard HEAD^
> 
> After a few failures like that, you can look over all the snapshots with
> 
>   git log -g snapshots
> 
> By default they will be kept around for 30 days, or the current setting of 
> gc.reflogexpireunreachable.  If you want to keep a snapshot around for more 
> than 30 days, you probably want to give it a real branch anyway.
> 
> You could either create a script that uses the above commands or a patch that 
> makes git-stash take a --ref= argument and then set:
> 
>   alias.snapshot = stash --ref=refs/snapshots
Yes, I think the right answer is going to be something like this:

git symbolic-ref HEAD refs/heads/snapshots git rm --cached -r . git commit --allow-empty -m 'Snapshot branch'

Then, to take a snapshot:

git branch snapshot git checkout master -- path git add path git commit -a -m 'Snapshot of [path]' git checkout master

(All of that will be in a script of course.)
And to rollback:
git checkout snapshot-[n] -- path

Or something like that. I haven't actually tried this yet, but it seems like it ought to work.

Am I right that 'git checkout branch' changes branches, but 'git checkout branch -- path' doesn't? That's very confusing. Useful, but confusing :)

rg
Mark Lodato· Feb 13, 2010, 00:33 UTC · re: Ron Garret · lore

Re: Individual file snapshots

Ron, I also could use a feature like this. Sometimes I just have some code that I don't want to just throw away, but it really doesn't go in the history. I usually just create a file called "junk" and add it to .git/info/excludes, but I'd rather have it stored in git.

On Fri, Feb 12, 2010 at 5:39 PM, Ron Garret <ron1@flownet.com> wrote:
Show 22 quoted lines
> Yes, I think the right answer is going to be something like this:
>
> git symbolic-ref HEAD refs/heads/snapshots
> git rm --cached -r .
> git commit --allow-empty -m 'Snapshot branch'
>
> Then, to take a snapshot:
>
> git branch snapshot
> git checkout master -- path
> git add path
> git commit -a -m 'Snapshot of [path]'
> git checkout master
>
> (All of that will be in a script of course.)
>
> And to rollback:
>
> git checkout snapshot-[n] -- path
>
> Or something like that.  I haven't actually tried this yet, but it seems
> like it ought to work.

I took your idea and ran with it. In my version, I don't bother switching branches and instead use plumbing commands to get the job done. (This prevents issues if there's a conflict from the old commit.) Also, I record HEAD as an additional parent of the commit, so I can see where the commit came from.

I uploaded it as a Github Gist with a bunch of comments. (It's much nicer to read it with syntax highlighting.) You may be able to modify this to suit your needs. In particular, if you'd prefer it not to be a real branch, you could change $BRANCH to /refs/snapshots/foo or something like that to hide it.

http://gist.github.com/303142
The basic steps are:

backup .git/index git add -a git write-tree git commit-tree restore .git/index

I don't know much about git's plumbing commands, so I'd be interested in hearing from git experts to see if what I'm doing is a good idea.

> Am I right that 'git checkout branch' changes branches, but 'git
> checkout branch -- path' doesn't?  That's very confusing.  Useful, but
> confusing :)
Yes on both counts :)

-- Mark

Junio C Hamano· Feb 13, 2010, 00:47 UTC · re: Mark Lodato · lore

Re: Individual file snapshots

Mark Lodato <lodatom@gmail.com> writes:
Show 7 quoted lines
> The basic steps are:
>
> backup .git/index
> git add -a
> git write-tree
> git commit-tree
> restore .git/index
Instead of doing that, you might want to consider
	GIT_INDEX_FILE=...some-temporary-file...
        export GIT_INDEX_FILE

and run everything as if that is the index throughout your script. That way, if your script ever failed in the middle, you won't have to worry about a broken index file.

Mark Lodato· Feb 13, 2010, 15:13 UTC · re: Junio C Hamano · lore

Re: Individual file snapshots

On Fri, Feb 12, 2010 at 7:47 PM, Junio C Hamano <gitster@pobox.com> wrote:
Show 18 quoted lines
> Mark Lodato <lodatom@gmail.com> writes:
>
>> The basic steps are:
>>
>> backup .git/index
>> git add -a
>> git write-tree
>> git commit-tree
>> restore .git/index
>
> Instead of doing that, you might want to consider
>
>        GIT_INDEX_FILE=...some-temporary-file...
>        export GIT_INDEX_FILE
>
> and run everything as if that is the index throughout your script.  That
> way, if your script ever failed in the middle, you won't have to worry
> about a broken index file.

In the actual script, I copied the index file back if any error occured. Still, your idea sounds better, so I just updated it to use this variable instead. I still remove the temporary file on error, but if something goes wrong, at least the index won't be hosed.

On Sat, Feb 13, 2010 at 2:01 AM, Ron Garret <ron1@flownet.com> wrote:
> Moving the index around seems kinda hacky.  You probably want git-mktree.

git-mktree is way too complicated. I would have to parse the index and call git-mktree for each directory. There is already a program to do this: git-write-tree :) Besides, using GIT_INDEX_FILE as suggested by Junio allows me to leave the original index file intact.

-- Mark

Ron Garret· Feb 13, 2010, 18:41 UTC · re: Mark Lodato · lore

Re: Individual file snapshots

In article 
<ca433831002130713i5b015686k9f53911954858845@mail.gmail.com>,
 Mark Lodato <lodatom@gmail.com> wrote:
Show 5 quoted lines
> On Sat, Feb 13, 2010 at 2:01 AM, Ron Garret <ron1@flownet.com> wrote:
> > Moving the index around seems kinda hacky.  You probably want git-mktree.
> 
> git-mktree is way too complicated.  I would have to parse the index
> and call git-mktree for each directory.
Yeah, but think of how studly you'll feel when you do that! ;-)
> Besides, using GIT_INDEX_FILE as suggested
> by Junio allows me to leave the original index file intact.
Yeah, that might actually be a better plan.
rg
Junio C Hamano· Feb 13, 2010, 18:58 UTC · re: Mark Lodato · lore

Re: Individual file snapshots

Mark Lodato <lodatom@gmail.com> writes:
> ...  Besides, using GIT_INDEX_FILE as suggested
> by Junio allows me to leave the original index file intact.

Heh, that is what the Professionals use ;-) It is used by git-stash.sh for example.

Aspiring Porcelain authors would benefit greatly by learning from some of the few remaining scripted Porcelain commands. With the recent rewriting to many of the originally scripted ones in C, it may have become somewhat harder to learn the plumbing mechanisms and techniques that way, but we still keep some older ones in contrib/examples directory for that purpose.

Ron Garret· Feb 13, 2010, 07:01 UTC · re: Mark Lodato · lore

Re: Individual file snapshots

In article 
<ca433831002121633j5b96049bs71e539c96397aff4@mail.gmail.com>,
 Mark Lodato <lodatom@gmail.com> wrote:
Show 53 quoted lines
> Ron, I also could use a feature like this.  Sometimes I just have some
> code that I don't want to just throw away, but it really doesn't go in
> the history.  I usually just create a file called "junk" and add it to
> .git/info/excludes, but I'd rather have it stored in git.
> 
> On Fri, Feb 12, 2010 at 5:39 PM, Ron Garret <ron1@flownet.com> wrote:
> > Yes, I think the right answer is going to be something like this:
> >
> > git symbolic-ref HEAD refs/heads/snapshots
> > git rm --cached -r .
> > git commit --allow-empty -m 'Snapshot branch'
> >
> > Then, to take a snapshot:
> >
> > git branch snapshot
> > git checkout master -- path
> > git add path
> > git commit -a -m 'Snapshot of [path]'
> > git checkout master
> >
> > (All of that will be in a script of course.)
> >
> > And to rollback:
> >
> > git checkout snapshot-[n] -- path
> >
> > Or something like that.  I haven't actually tried this yet, but it seems
> > like it ought to work.
> 
> I took your idea and ran with it.  In my version, I don't bother
> switching branches and instead use plumbing commands to get the job
> done.  (This prevents issues if there's a conflict from the old
> commit.)  Also, I record HEAD as an additional parent of the commit,
> so I can see where the commit came from.
> 
> I uploaded it as a Github Gist with a bunch of comments.  (It's much
> nicer to read it with syntax highlighting.)  You may be able to modify
> this to suit your needs.  In particular, if you'd prefer it not to be
> a real branch, you could change $BRANCH to /refs/snapshots/foo or
> something like that to hide it.
> 
> http://gist.github.com/303142
> 
> The basic steps are:
> 
> backup .git/index
> git add -a
> git write-tree
> git commit-tree
> restore .git/index
> 
> I don't know much about git's plumbing commands, so I'd be interested
> in hearing from git experts to see if what I'm doing is a good idea.
Moving the index around seems kinda hacky.  You probably want git-mktree.
rg
Larry D'Anna· Feb 13, 2010, 03:00 UTC · re: Ron Garret · lore

Re: Individual file snapshots

* Ron Garret (ron1@flownet.com) [100212 16:58]:
> That would require a separate branch for every snapshot, no?  I want 
> this to be lightweight.  It's not so much the creation of a zillion 
> branches that bothers me, but having to come up with a new name every 
> time would be a real hassle.

I like to keep a branch called 'archive' for crap that's not being actively used anymore, but that I don't to throw away. Like failed experiments, or top-git branches that have been merged. I just merge -s ours all of it into archive and forget about it.

       --larry

← back to recent threads