tags:

views:

167

answers:

6

Coming from Sourcesafe, I have primarely been using git as a major undo feature. I tend to work in the master branch, code happily away for some time, commit here and there and move on.

While I'm coding my brains out, I sometimes wish to drop what I'm doing right now and start working on another feature. I know I can start another branch right now but let's assume I'd like to keep working on master. I should have branched when I started working on the previous feature but that would have required me to plan ahead (at which I'm terrible btw).

I am pretty confident that what I need is possible with git, but I can't get my head around it. As a picture tells more than a thousand words, schematicly this is what I would like to do:

0--1 master

0--1--2 master

0--1--2--3 master

0--1--2--3 aBranch    
    \-4    master

0--1--2--3 aBranch    
    \-4--5 master

Edit: I should clear up that every number is a commit.

+1  A: 

Check out git-stash.

Edit: If you need to go back further than HEAD, you can always do a git checkout specifying the start point to get a new branch starting from any given commit in master.

ezod
That just helps for a single set of changes that wasn't even committed yet...
Nicolás
A: 

I know little about git, and struggle each time I have to do anything remotely non-trivial. I use Mercurial, where what you want is quite easy:

http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-mercurial/#branching-anonymously

Nicolás
+1 sweet, especialy if you know I've tried mercurial too. I'm still kind of in between between git and mercurial but what the link describes to is exactly what I would need... in git :)
Lieven
It's fairly trivial in git too. You just need `reset` and `branch` which are both in the list of common commands according to `git help`. I don't believe the guide you've linked to is particularly accurate in its comparison with git. With git you can detach your HEAD at any time. You are now on an 'anonymous' branch. Of course it's going to be more difficult to find than a named branch but both the reflog and `git fsck` will let you find old detached heads.
Charles Bailey
+3  A: 

Sure, at this point:

0--1--2--3 master

Run the following commands:

git branch aBranch
git reset --hard 1

The git branch command creates a branch at the current HEAD (so "master" and "aBranch" refer to the same point, but "master" is still the current branch), and the git reset command winds everything back on the current branch ("master") to commit 1. Then you'll be here:

0--1--2--3 aBranch    
    \      master

Committing "4" at this point will give:

0--1--2--3 aBranch    
    \-4    master
Greg Hewgill
+6  A: 

Yes, this is possible, and not hard. The short version: git branch aBranch to create the new branch, git stash to save any uncommitted changes, and git reset --hard 1 to reset master to the commit you would like, and optionally git stash pop to apply those uncommitted changes to your current working copy, if they were intended for the new master branch.

Here's a full explanation, with diagrams so you can follow along. In the following, I will notate the current branch (HEAD) with a *.

0--1 *master

0--1--2 *master

0--1--2--3 *master

Create new branch aBranch pointing at the current commit:

$ git branch aBranch
0--1--2--3 *master aBranch

If you have any changes in the working copy that haven't yet been committed, save them with a stash (this is important, as git reset --hard will wipe away any uncommitted changes you have):

$ git stash
0--1--2--3    *master aBranch
         \- 4 stash

Now reset master back to the revision you want it at. I'm using 1 below as a placeholder; you may want to use the SHA-1, or something like origin/master if you want to reset it to the revision that the upstream master branch was at before you made your commits:

$ git reset --hard 1
0--1--2--3    aBranch
   \     \- 4 stash
    \-        *master

If you had uncommitted changes in your working copy that you stashed away, and you want to apply them to master, you can use git stash apply or git stash pop (apply will leave the stash recorded as a commit; pop will clean up the stash).

$ git stash pop 
0--1--2--3 aBranch
   \-      *master (with stashed changes now back in your working copy)

Now you're on master, and reset to commit 1, exactly as you wanted to be. New commits will go to the master branch.

0--1--2--3 aBranch    
    \-4    *master

0--1--2--3 aBranch    
    \-4--5 *master

Note that if your working copy is clean (nothing reported by git status), you don't need the git stash command. If you wanted to apply your changes to aBranch instead of master, you would just use git stash pop the next time you switched back to aBranch and started working on it again.

One important point to keep in mind is that you should only edit branch history like this in your own private repositories, not public ones that other people could pull from. If you push this to a public repository that people are pulling from, then they'll have to rebase all of their changes, and it's best not to make them do that unless absolutely necessary.

Brian Campbell
Several good answers to choose from. I have taken the liberty to just select the first good answer that came around. Thank you all guys.
Lieven
Brian, I've accidently clicked the downvote io the accepted answer icon. I can't seem to undo this (perhaps a git reset would help). Sorry about that.
Lieven
I've edited it, so you should be able to change your vote now if you wish to (though, it looks to me like you've already done so).
Brian Campbell
now I did. The vote is changed.
Lieven
+3  A: 

Yes, this is easy to do in git.

When you've made commit 3 on master and you realise it should be on another branch you can just create a new branch based on the current position.

git branch aBranch

You can then wind master back to commit 1

git reset --hard <sha1_of_1>

and carry on working, making commits 4 and 5 as you would normally. They fork from commit 1 making a separate branch from aBranch.

Charles Bailey
A: 

You need to get in the habit of branching every time you start on a new feature.

That said, you can do this:

0 -- 1 -- 2 -- 3 master -- 4 -- 5 aBranch

You don't want to mess around with a branch's history - that's going to cause you many headaches.

Always develop on a branch, then merge back to master and commit to SourceSafe from there.

Tycho
you are right offcourse but old habits you know...
Lieven
This isn't correct. You shouldn't ever push changes to a public repository that mess with a branch's history; but one of the big benefits of Git over other systems is that you can easily edit the branch history in your own local repository, which makes it easy to get a patch series really clean and understandable before sending it off for code review.
Brian Campbell