69

I am currently on a debug branch, and would like to switch to the master branch, without modifying the working tree (leave it the way it is in the debug branch), so I can commit some of the changes into the master branch.

Is there a way to do this?

Lii
  • 11,553
  • 8
  • 64
  • 88
static_rtti
  • 53,760
  • 47
  • 136
  • 192
  • The working tree is a tree, not a patch, so if you literally did that you could run roughshod over conflicting changes. (Or indeed *any* changes in master that are not in debug, if I understand you correctly.) – Robin Green May 20 '11 at 09:53
  • 1
    Possible duplicate of [switch git branch without files checkout](http://stackoverflow.com/questions/1282639/switch-git-branch-without-files-checkout) – Ciro Santilli OurBigBook.com Aug 05 '16 at 08:26
  • This doesn't answer the question directly, but I have found that a similar outcome can be achieved by the use of tools. Specifically, with IntelliJ I can use the 'Show Diff from Working Tree' command to highlight the differences by folder and file and then use the 'Get From Branch' to pull required files and folders into the current working tree. This can be easier than cherry picking when the branch I want to pick from has lots of commits. – Ross Attrill Mar 21 '21 at 23:20

5 Answers5

89

You can do the following:

git checkout --detach
git reset --soft master
git checkout master

Explanation:

If you are on the debug branch and would do git reset --soft master you would leave your working tree and index untouched and move to the commit master points to. The problem is, debug will be reset to this commit too. So your commits on debug are "lost" (well, not really, but they are not directly accessible anymore) and you are still on the debug branch.

To prevent git reset from moving debug but still setting your HEAD to the master commit, you first do git checkout --detach to point HEAD directly to your current commit (see man git-checkout, section "DETACHED HEAD"). Then you can do the reset without touching the debugbranch.

Now HEAD is pointing directly to the commit master points to, i.e. it is still detached. You can simply git checkout master to attach to master and are now ready to commit on the master branch.

Note that git checkout (by default and when no path is passed) only updates files that have been changed between the "source" and "target" commit and local modifications to the files in the working tree are kept. As both commits are the same in this case, no files in the working directory are touched.

siegi
  • 5,646
  • 2
  • 30
  • 42
  • 5
    Yup, that's exactly what I was looking for. Nice explanation too. – Aron Ahmadia Nov 12 '13 at 19:18
  • @Jacko I just checked it again on my computer. The only difference is the accepted answer does a "mixed" reset, I do a "soft" reset. If you leave the `--soft` out, it should do the exact same thing as the accepted answer (well, the first part of it, of course). What exactly did not work for you? – siegi Feb 21 '14 at 16:02
  • 4
    This worked for me perfectly and feels much cleaner then the `symbolic-ref` thing, all tough it sucks it has to be 3 commands... – Willem D'Haeseleer Jul 03 '14 at 09:05
  • 1
    @WillemD'haeseleer If you need this often and typing 3 commands is too verbose for you, you could [define an alias](https://git.wiki.kernel.org/index.php/Aliases) for it. – siegi Jul 03 '14 at 16:03
  • I did this and it lead to a confusion the next time I did a git merge master. Lots of files were marked as conflicts when in fact they were identical. – digory doo May 11 '15 at 06:04
  • @digorydoo: The commands in this answer do what the question asks for and I don't think that the result would be different if you used any other (valid) answer. If this leads to a problem later on, that's another issue. If you need assistance with that you are welcome to ask a new question :-) – siegi May 11 '15 at 06:27
  • I was suspicious about this solution because the 3rd command - `git checkout master` - typically updates the working tree. That's not the case here because the target commit is the same. A noteworthy clarification to other users like me is that when `git checkout` is not moving to another commit, then the working tree is indeed untouched. – fernacolo Jun 02 '20 at 00:13
  • 1
    @fernacolo, thank you for your input, I added a note about it to the answer. – siegi Jun 03 '20 at 03:29
59

This answer uses low-level "plumbing" commands. Be careful. If you prefer "porcelain" commands, go with this answer which produces the same results.

You can reset your head to point at master without changing the index or working tree with:

git symbolic-ref HEAD refs/heads/master

You should probably reset the index so that you can selectively apply your working tree changes, otherwise you may end up committing all the differences between master and the debug branch, which is probably a bad thing.

git reset

Once you've made the commit that you want to make you can return to your debug branch with:

git symbolic-ref HEAD refs/heads/debug-branch
git reset
Inigo
  • 12,186
  • 5
  • 41
  • 70
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • This answer helped me... Switching like this can really help with splitting one commit in to smaller pieces. – PeterSW Dec 13 '13 at 16:03
  • Does this also work if you want to checkout a commit rather than a branch? – Cameron Martin Aug 31 '17 at 12:23
  • @CameronMartin: from `man git-symbolic-refs`, "A symbolic ref is a regular file that stores a string that begins with `ref: refs/`. For example, your `.git/HEAD` is a regular file whose contents is `ref: refs/heads/master`." so no, you would want to `git branch ` to create a branch with that commit as its head, then update your `.git/HEAD` to point at that commit with the above. – CR Drost Jan 15 '18 at 23:31
9

You can stash (git stash) your changes, switch branches, unstash (git stash pop) your changes, add and commit the changes.

If you want the exact state of debug, then simply merge debug into master (or reset master to debug).

Lii
  • 11,553
  • 8
  • 64
  • 88
knittl
  • 246,190
  • 53
  • 318
  • 364
  • 2
    resetting master isn't always sound, nor safe, advise. This could complicate matters if other branches had relations to master – sehe May 20 '11 at 10:05
  • 2
    Merging `debug` into `master` usually won't make `master`'s tree the same as `debug`'s, unless (e.g.) there's been no new development on `master` since their common ancestor. – Mark Longair May 20 '11 at 10:16
  • @sehe: it's hard to tell which outcome is expected by the OP, i mentioned some options to choose from. @mark: yes, correct, but almost ;) (plus/minus the changes from `debug`) – knittl May 20 '11 at 10:35
  • 1
    In my opinion, this solution (together with the remarks from the comments) is way cleaner than messing with symbolic-refs, as the accepted answer does. – Ricardo Sanchez-Saez May 26 '11 at 17:20
3

Here is a raw workflow

 git stash
 git checkout otherbranch
 git stash apply
 git reset
 git add # interactively? just add the hunks/changes you want to commit
 git commit

And to go back

 git reset --hard # watch it here! make sure you haven't added more changes that you wanted to keep
 git checkout debug
 git stash pop

Alternatively, you might just commit the relevant changes 'here' and push / cherry-pick onto the master branch.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks! What is the reset --soft needed for? – static_rtti May 20 '11 at 09:52
  • 1
    `git reset --soft` doesn't touch the index or the working tree - I think you mean just `git reset`. – Mark Longair May 20 '11 at 09:59
  • @Mark: Good call. AAMOF i do `git reset` -- just reckoned `--soft` was the default and wanted to make that explicit. Thanks for correcting that – sehe May 20 '11 at 10:02
  • @static_rtti: It is possible that the stash contains already staged changes. `git reset [--mixed]` unstages them so you will be able to select from scratch the changes to commit on master – sehe May 20 '11 at 10:19
-1

As far as I know, you can't. Branch switching means checking out into the working copy a version of the code that sits in the HEAD of that branch.

You want to merge your branches. Do

git checkout master
git merge devel

The branches will now be synchronized. If you want to merge a subset of changes, you can specify a commit or a range of commits. Also take a look at cherry-pick For example:

git checkout master
git cherry-pick devel

Will merge the last commit in devel back into master.

If you need to merge two branches that sit on different hosts, have a look at git pull and git push.

VladFr
  • 816
  • 1
  • 10
  • 18