34

Basic question but this happens to me all the time:

  • Make changes in a working-branch
  • Switch to master
  • git merge working-branch
  • git push
  • cap deploy(to staging)
  • make a new cup of tea

then I come back and think of something else and start making some changes...while still on master.

What's an easy way to either:

  1. prevent direct edits on master (warning perhaps)
  2. to move all edits over to working-branch and clear master so I can continue editing on working-branch
  3. to spin edits into an entirely new branch new-working-branch and then discard working-branch?

Took a risk and tried recommendation in the latter part of "Branches" section of this page but that just wiped out ALL my edits!?! perhaps because after git branch dubious-experiment and git checkout master the git status on both branches was identical (not 'clean' on master). So git reset --hard <SHA1sum> wiped out all changes on both!?!

  git branch dubious-experiment

  M---N-----O----P---Q ("master" and "dubious-experiment")

  git checkout master

  # Be careful with this next command: make sure "git status" is
  # clean, you're definitely on "master" and the
  # "dubious-experiment" branch has the commits you were working
  # on first...

  git reset --hard <SHA1sum of commit N>
Esteban Küber
  • 33,970
  • 13
  • 78
  • 96
Meltemi
  • 36,348
  • 48
  • 182
  • 274

6 Answers6

35

From your description, I assume that you did not commit any changes yet – is that correct?

If yes, here’s your answers:

How to prevent direct edits to master

You would need to set that in your editor, but that will probably be difficult. Displaying your current branch in your prompt and your editor helps a lot.

How to move the changes into a new branch new-working-branch and then discard working-branch

git checkout -b new-working-branch
git add …
git commit -m "mycommit" 

As you didn’t commit anything to master yet, you don’t need to change anything on master. You can now discard your working-branch if you feel like it.

How to move the changes over to working-branch

git checkout -b temp-branch
git add …
git commit -m "mycommit" 
git rebase --onto working-branch master
git checkout working-branch
git reset --hard temp-branch
git branch -d temp-branch

If your changes don’t conflict with any changes that are on master, but not in working-branch, this can be done a lot simpler:

git stash
git checkout working-branch
git stash pop
Chronial
  • 55,303
  • 13
  • 76
  • 85
10

If you already committed your changes to master but didn't push to anywhere...

create a new branch for the last changes

git checkout -b newfeat master

replay all the changes (move the commits) on top of your working-branch branch

git rebase --onto working-branch origin/master newfeat

change to master branch and reset it to the state of the last push

git checkout master
git reset --hard origin/master

At this point you have:

  • master pointing to the last pushed commit (origin/master)
  • working-branch never changed
  • a new newfeat branch that contains all the new commits and is ahead of working-branch.
KurzedMetal
  • 11,366
  • 5
  • 35
  • 62
0

I used for similar cases:

git branch -f <branch-name>
git checkout <branch-name>

or

git checkout -B <branch-name>

.

Both variants move the branch branch-name to your current commit with out reseting-hard your tree.

Patrick B.
  • 10,471
  • 7
  • 49
  • 85
0

I generally recommend the following Git setting:

git config push.default nothing

With this, you will at least have to name the branch when you push. It won't stop you from committing to master locally, but when you realize you have, you can move those commits to a branch without affecting anyone else.

Rob Starling
  • 3,760
  • 2
  • 20
  • 38
-1

Get in the habit of typing $ git status before you actually do a git command that will modify something.

Given that, you have probably edited your file but not checked it in, because you would run git status before the commit. In this case, git does the right thing if you just switch branches, then commit.

If you have fired a commit off to master, then just move the file between branches with something like this:

 $ git checkout --patch master <somefile>

You don't really have to reset master if you are just going to merge the same file with it, but since presumably you haven't pushed anything yet you should just reset to your remote tracking branches...

$ git reset master origin/master
$ git reset stage origin/stage # whatever
DigitalRoss
  • 135,013
  • 23
  • 230
  • 316
  • I think I'm missing something conceptual about Git?!? Excuse my ignorance but what do you mean by "git does the right thing if you just switch branches"? – Meltemi Jan 24 '13 at 17:34
  • -1 – just “stealing” a file from master with `git checkout` is dangerous – master might contain changes that are not in the working branch, causing a mess. Running `git reset` without `--hard` will mostly just be very confusing. – Chronial Jan 24 '13 at 23:07
  • @Meltemi, changing a file while the branch is checked out doesn't lock those changes to master. If you checkout another branch you can then just commit your changes to it and master will be unaware of the activity. @Chronial, well, the default `--mixed` will leave his changes available to commit to the correct topic branch. – DigitalRoss Jan 24 '13 at 23:21
-1

1. prevent direct edits on master (warning perhaps)

You're not the only one to want this. The best idea I've come across is to put the git branch directly in your shell prompt. My prompt looks like this:

[user@host directory:git_branch]

I also color the git_branch entry, so it's quite obvious what I'm working on at all times. These two links on Stack Overflow should help with your prompt.

2. to move all edits over to working-branch and clear master so I can continue editing on working-branch

or

3. to spin edits into an entirely new branch new-working-branch and then discard working-branch?

These are really the same question - how to move changes off of master onto a branch, whether it's an old branch or a new branch. And your own answer is correct. Although at second glance, assuming you're on master, you could more simply run:

git branch new_branch
git reset --hard origin/master

I prefer to just reset master to origin/master rather than worry about a specific commit SHA. But your steps were essentially correct. As to why you lost changes, I'd have to think that by mistake there wasn't a branch pointer to Q when you reset master. No other explanation makes sense. Again, having the branch shell prompt will help avoid these mistakes. Further more, I'm a big fan of using gitk or git log --graph to verify where my branches are before I move them around. Since I can't easily use gitk at work, I have an alias in my .gitconfig called "graph," which is essentially a command-line version of it:

[alias]
    graph = log --graph --all --date=short --pretty=format':%C(yellow)%h%Cblue%d%Creset %s %Cgreen %aN, %ad%Creset'

This will show the graph on the far left, the commit SHA in yellow, the branches in blue, the commit message in white, and the author & date in green. Of course this can be modified to your own liking.

[edited to make the above commands simpler]

==============================

In response to the comment below:

Start with

A-B < origin/master
   \
    C-D < master

Now perform git checkout -b new_branch

A-B < origin/master
   \
    C-D < master, new_branch

Now checkout master, git checkout master. Note that git checkout -b new_branch && git checkout master is the same as git branch new_branch if you were already on master. I edited the above answer to reflect this.

Now reset master to origin/master, git reset --hard origin/master

A-B < master, origin/master
   \
    C-D < new_branch

Because you had a branch (new_branch) pointing at D, no changes are lost. If I've made a mistake, please elaborate where.

Community
  • 1
  • 1
Mike Monkiewicz
  • 3,503
  • 1
  • 20
  • 18
  • Absolute nonsense, I just ran this in a test repository and it's fine. (editing main post to prove my point) – Mike Monkiewicz Jan 25 '13 at 04:21
  • 1
    Quote from OP: “`git status` on both branches was identical (not 'clean' on master)” – he obviously didn’t commit his changes -> your `3.` will erase those changes (forever). – Chronial Jan 25 '13 at 04:26
  • Ah, then I stand corrected. That does seem to be the most likely explanation. – Mike Monkiewicz Jan 25 '13 at 04:31