15

Here's the situation. I'm working on the master branch. I create file1 and commit. I create file2 and commit. Whoops. I may want to use file2, someday, but it's definitely not something that should be put in the master branch. So that I don't lose file2 I use

git checkout head~1 git branch new-branch git checkout new-branch 

so that I can continue developing. I add file3 to new-branch. If you've been paying attention, I've got two branches, master that contains "file1" and "file2" and new-branch that contains "file1" and "file3".

Now is the time to get the changes I've made back into the master branch. What's the best way to do this? I definitely want the head of the master branch to point at the files as they appear in new-branch, but I also don't want to lose the work I've done in file2 by doing a reset, in case I want to use it.

Keep in mind this is a simplification. Instead of just three files, I've got a dozen files with tens of lines of code being changed all over the place all with multiple commits. I certainly hope the solution isn't to do a file-by-file merge/checkout, because that would be a huge pain.

Any ideas?

2
  • You said "This looks like just what I need", but did you try switching branch as I indicate in my answer? Commented Jan 8, 2010 at 13:43
  • I did. Randal's answer was even closer to my solution. It turns out the key is git reset --hard HEAD~1. I didn't realize you could reset a string of commits with one command. All I need to do is branch off the current master branch, then reset back to where I started to go awry. Commented Jan 8, 2010 at 15:59

3 Answers 3

3

I'm working on the master branch. I create file1 and commit.

date >file1 git add file1 git commit -m 'added file1' 

I create file2 and commit.

date >file2 git add file2 git commit -m 'added file2' 

Whoops. I may want to use file2, someday, but it's definitely not something that should be put in the master branch.

Oops. Very simple. Make a new branch from where you are:

git checkout -b savingfile2 

This will make the file2 change the commit for savingfile2. Now go back and unwind one step on master

git checkout master git reset --hard HEAD~1 

At this point, the commits leading up to master will reflect the addition of file1, and the additional commit between master and savingfile2 will be the addition of file2 to that.

If you make more changes to master, and then want to bring file2 back eventually, you'll want to rebase that side-branch onto the new master:

date >file3 git add file3 git commit -m 'adding file3' date >file4 git add file4 git commit -m 'adding file4' 

And now we finally want file2:

git checkout savingfile2 git rebase master # might need to fix conflicts here git checkout master git merge savingfile2 # will be a fast-forward git branch -d savingfile2 # no need any more 

That should do it.

Sign up to request clarification or add additional context in comments.

2 Comments

"git merge savingfile2 # will be a fast-forward" <- What about adding --ff-only to make sure of it?
@weakish - that option is newer than the answer. :)
1

What you should do is what you should have done when you noticed your mistake of commiting file2: undo the commit (instead of creating a new branch):

git checkout master git reset HEAD^ 

This leaves file2 untracked and unharmed and possible modifications uncommited. Then you should (have) stash(ed) the uncommited modifications in case you want to use them later:

git stash save "modifications that should not be in the master branch" 

Stashing gets rid of any local changes, which allows master to be made point to new-branch:

git merge new-branch 

The goal here was to eliminate the divergence between the two branches, i.e. make master an ancestor of new-branch. This way no actual merging would have to occur, and the last command would just fast-forward the master branch (provided there are no local changes).

2 Comments

How would this work if I've made multiple commits on my master branch before noticing? git reset is just going to rewind the HEAD, not let me move those improper commits, right?
You can use (interactive) rebase to drop arbitrary commits from along the commit history (as opposed to git reset) as long as those commits haven't been pushed anywhere.
1

Since you didn't follow the optimal workflow described by Tomi Kyöstilä, but also since you didn' publish (push) anything yet, why not switch the two branches?
(provided everything is committed)

master and new-branch are just some pointers to some SHA1:

$ git checkout master #start from master $ git branch tmp # tmp points on master HEAD $ git checkout new-branch # switch to new-branch $ git branch -f master new_branch # master points at new-branch HEAD $ git checkout tmp # switch back to *former* master branch $ git branch -f new_branch tmp # new-branch points at former master HEAD $ git checkout master # go to new master $ git branch -D tmp # remove tmp pointer 

... and you're done.
(disclaimer: not tested yet, so try it with caution ;) )

See:

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.