697

How can I do the following in Git?

My current branch is branch1 and I have made some local changes. However, I now realize that I actually meant to be applying these changes to branch2. Is there a way to apply/merge these changes so that they become local changes on branch2 without committing them on branch1?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
solsberg
  • 7,875
  • 4
  • 22
  • 12
  • 3
    There is a great Git Tutorial right [here](http://stackoverflow.com/questions/315911/git-for-beginners-the-definitive-practical-guide) on SO. Its kind of a central for all git questions on stack overflow. – Decio Lira Feb 17 '09 at 14:11
  • This link exists in the "linked" set of questions on the right thanks to StackOverflow magic, but I think it deserves a comment link: see also [Move existing, uncommitted work to a new branch in Git](https://stackoverflow.com/q/1394797/1256452). – torek Jan 28 '21 at 01:46

10 Answers10

984

Since your files are not yet committed in branch1:

git stash
git checkout branch2
git stash pop

or

git stash
git checkout branch2
git stash list       # to check the various stash made in different branch
git stash apply x    # to select the right one

Above is the longer more explicit version of rbento's answer:

git stash
git stash branch branch2

It uses:

git stash branch <branchname> [<stash>]

  • Creates and checks out a new branch named <branchname> starting from the commit at which the <stash> was originally created,
  • applies the changes recorded in <stash> to the new working tree and index.

If that succeeds, and <stash> is a reference of the form stash@{<revision>}, it then drops the <stash>.

This is useful if the branch on which you ran git stash push has changed enough that git stash apply fails due to conflicts.
Since the stash entry is applied on top of the commit that was HEAD at the time git stash was run, it restores the originally stashed state with no conflicts.


As commented by benjohn (see git stash man page):

To also stash currently untracked (newly added) files, add the argument -u, so:

git stash -u
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • 2
    You are welcome. More examples of stash usage at http://unethicalblogger.com/posts/2008/11/git_protip_stash_goods_yo_git_stash . – VonC Feb 17 '09 at 14:59
  • 2
    If you are looking for a solution to the same issue but with TFS, the equivalent solution is to shelve your changes then use TFS Power Tools to unshelve to the correct branch using the /migrate switch. – xr280xr Nov 12 '12 at 20:28
  • 1
    This worked for me. However, I also had to create a local branch for the 'stash pop' to work. Checkout out http://stackoverflow.com/questions/1783405/git-checkout-remote-branch if something similar is happening to you. – mimoralea Aug 22 '13 at 15:27
  • This does not work for me. If I make a set of changes accidentally on master, and they are uncommited, but I wanted to apply them to other branch develop, if I try git stash then it will not allow me to checkout develop. – ely Apr 11 '14 at 21:07
  • @EMS What was your error message when trying to `checkout develop` after stashing? – VonC Apr 11 '14 at 21:14
  • I [opened a question on it.](http://stackoverflow.com/questions/23022702/git-move-uncommitted-changes-from-current-branch-to-another-branch-that-confli) – ely Apr 11 '14 at 21:15
  • what if you have a newly added file? how do you apply it to the different branch? – kapitanluffy Oct 02 '14 at 10:58
  • @kapitanluffy if the new file is not already added to the index, it will be stashed and re-applied. If it is added to the index, the checkout to the new branch shouldn't touch it. – VonC Oct 02 '14 at 11:06
  • @VonC added to the index? you mean committed? I tried adding the new file to the stage .stashing it, moving to a different branch and 'git stash pop' there. but it didn't show. – kapitanluffy Oct 03 '14 at 03:11
  • @kapitanluffy no, not commit, just added to the index: http://stackoverflow.com/a/3690796/6309 – VonC Oct 03 '14 at 06:00
  • @VonC well I tried that but when I changed to a different branch and applied the stash the new file didnt appear – kapitanluffy Oct 03 '14 at 15:44
  • [this answer](http://stackoverflow.com/a/6866485/2767755) and yours saved my work.. :) – Arup Rakshit Jun 28 '15 at 12:47
  • 27
    **To also stash currently untracked (newly added) files**, add the argument `-u`, so: `git stash -u`. – Benjohn Jul 24 '15 at 11:12
  • 2
    @Benjohn Good point. I have included your comment in the answer for more visibility. – VonC Jul 24 '15 at 11:14
  • How do you avoid the dreaded merge conflict when you `git stash pop` in the desired branch after switching branches? – Con Antonakos Oct 04 '16 at 15:31
  • @ConAntonakos "avoid"? You don't avoid it if concurrent modification are involved on the same file. But http://stackoverflow.com/a/27382210/6309 is interesting. – VonC Oct 04 '16 at 15:47
  • @VonC Thanks! I think the next answer might actually solve it for me: http://www.theverge.com/tldr/2016/10/4/13160850/new-mac-candle-twelve-south-apple – Con Antonakos Oct 04 '16 at 18:40
89

Stashing, temporary commits and rebasing may all be overkill. If you haven't added the changed files to the index, yet, then you may be able to just checkout the other branch.

git checkout branch2

This will work so long as no files that you are editing are different between branch1 and branch2. It will leave you on branch2 with you working changes preserved. If they are different then you can specify that you want to merge your local changes with the changes introduced by switching branches with the -m option to checkout.

git checkout -m branch2

If you've added changes to the index then you'll want to undo these changes with a reset first. (This will preserve your working copy, it will just remove the staged changes.)

git reset
CB Bailey
  • 755,051
  • 104
  • 632
  • 656
  • 3
    I thought the stash "simpler" somehow to understand, but your approach is better at taking into account the working directory across different branches. +1 – VonC Feb 17 '09 at 21:13
  • 6
    A plain traditional checkout seemed more appropriate to the problem in hand. checkout is lighter weight, it just updates the files that need to change. Perhaps it's easier to understand the stash approach, or it may just be that it's not obvious enough that checkout is 'safe' in this use case. – CB Bailey Feb 17 '09 at 21:41
  • If `checkout -m` isn't "safe" in some situation (maybe it would cause a merge conflict), would stash provide any advantage (e.g. can you unpop a stash pop)? – Craig McQueen Oct 18 '12 at 06:25
  • 1
    @craigMcQueen You can't unpop a popped stash but stash would complain about conflicts when you pop it. You can fix the conflicts and then commit, but the original stash is still on the stack in this case ! :) – Shaun Mar 09 '13 at 00:22
  • In the event of a merge conflict, aren't the files backed up as `.orig`? – jocull Jul 25 '18 at 15:32
16

A shorter alternative to the accepted answer would be:

Temporarily move the changes to a stash.

  1. git stash

Create and switch to a new branch and then pop the stash to it in just one step.

  1. git stash branch new_branch_name

Then just add and commit the changes to this new branch.

rbento
  • 9,919
  • 3
  • 61
  • 61
12

WARNING: Not for git newbies.

This comes up enough in my workflow that I've almost tried to write a new git command for it. The usual git stash flow is the way to go but is a little awkward. I usually make a new commit first since if I have been looking at the changes, all the information is fresh in my mind and it's better to just start git commit-ing what I found (usually a bugfix belonging on master that I discover while working on a feature branch) right away.

It is also helpful—if you run into situations like this a lot—to have another working directory alongside your current one that always have the master branch checked out.

So how I achieve this goes like this:

  1. git commit the changes right away with a good commit message.
  2. git reset HEAD~1 to undo the commit from current branch.
  3. (optional) continue working on the feature.

Sometimes later (asynchronously), or immediately in another terminal window:

  1. cd my-project-master which is another WD sharing the same .git
  2. git reflog to find the bugfix I've just made.
  3. git cherry-pick SHA1 of the commit.

Optionally (still asynchronous) you can then rebase (or merge) your feature branch to get the bugfix, usually when you are about to submit a PR and have cleaned your feature branch and WD already:

  1. cd my-project which is the main WD I'm working on.
  2. git rebase master to get the bugfixes.

This way I can keep working on the feature uninterrupted and not have to worry about git stash-ing anything or having to clean my WD before a git checkout (and then having the check the feature branch backout again.) and still have all my bugfixes goes to master instead of hidden in my feature branch.

IMO git stash and git checkout is a real PIA when you are in the middle of working on some big feature.

chakrit
  • 61,017
  • 25
  • 133
  • 162
  • Interesting and valid alternative to my answer. +1 – VonC May 31 '15 at 06:12
  • Are you coming from mercurial? The `my-project-master` sharing the same `.git` makes it sound like it. Why not `git checkout -b bugfixABC; git commit -a; git reset HEAD^ --hard`, then later (asynchronously) while on `master`, `git cherry-pick – Gauthier Feb 08 '17 at 11:57
  • However, OP sounds like they are not ready to commit the changes, in that case just `checkout -m` is better. – Gauthier Feb 08 '17 at 12:08
  • @chakrit: It annoyed me so much that I took the time to write the script: https://stackoverflow.com/a/75363041/1221661. However, in your case it will likely refuse to checkout `master` in your feature worktree if you have a second `master` worktree lying around somewhere. – Fritz Feb 06 '23 at 15:06
2

If it were about committed changes, you should have a look at git-rebase, but as pointed out in comment by VonC, as you're talking about local changes, git-stash would certainly be the good way to do this.

claf
  • 9,043
  • 17
  • 62
  • 79
  • I do not understand this solution: it would rewrite commit history of branch2 from branch1... why getting all committed changes from branch1 in branch2 when we only want to get local non-committed changes of branch1 in branch2 ?... – VonC Feb 17 '09 at 14:24
  • @VonC : agreed, in this case, the rebase gets all committed changes since last merge between branches into branch1. I didn't get the "non-committed" parameter of this question at first. rebase isn't the good answer. – claf Feb 17 '09 at 14:34
  • @claferri: pfew... I was beginning to have an headache ;) I would have downvoted your answer, but since I had myself published one, there was a "clear conflict of interest". With your updated post, I do not have to downvote at all now. Thanks :) – VonC Feb 17 '09 at 14:56
  • @VonC : next time, feel free to down vote as long as my answer is as wrong as this one ;) – claf Feb 17 '09 at 15:01
1

These are the steps I follow:

  • git clone {link}
  • cd {repo folder}

You can check the status and which branch you are on using:

  • git status
  • git branch
  • git branch -a

Note: Here if you make changes in your local repo before moving to the new branch, the following steps should still work.

If "git branch" shows master, and you want to create+move to another branch:

  • git checkout -b {branch name}

Check branch again using "git branch" It should now show that you are in the new branch.

Now add, commit and push:

  • git add .
  • git commit -m "added new branch"
  • git push origin {branch name}

The above steps work for me in both the situation when I have made changes before moving to the new local branch or making changes after moving to the new branch. I hope it helps people running into similar situations.

Singh
  • 504
  • 4
  • 15
1

You can use git stash. Also, You can learn from this link https://git-scm.com/docs/git-stash.

In my opinion the best easiest way is

git stash
git checkout branch2
git stash pop

please upvote:). that's help me to ask questions from stackoverflow community.

0

If the uncommitted changes are a mix of untracked and tracked changes

What are untracked changes?

When you created e.g a new file. VSCode e.g displays a U next to the file in the file explorer.

What are tracked changes?

When you make changes to files that were committed to the repo earlier (in previous commits).

Quick explanation of procedure

So imagine you are on branch A, but you want to commit only changes to existing files to branch A, while the newly created file (untracked) should be committed to a new branch B. It's possible to use stashing with a few tricks, explained step by step.

Add script to .git/config

Inside the .git folder there is a config file. Open it up and you will see something like this:

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
[remote "origin"]
    url = https://github.com/...
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
    remote = origin
    merge = refs/heads/main

change the config file to:

[core]
    repositoryformatversion = 0
    filemode = true
    bare = false
    logallrefupdates = true
    ignorecase = true
    precomposeunicode = true
[alias]
    stash-untracked = "!f() {    \
                git stash;               \
                git stash -u;            \
                git stash pop stash@{1}; \
            }; f"
[remote "origin"]
    url = https://github.com/...
    fetch = +refs/heads/*:refs/remotes/origin/*
[branch "main"]
    remote = origin
    merge = refs/heads/main

Now you will be able to use the following command while you are on branch A.

git stash-untracked

You will see that the new file disappeared, if you are using a editor like VSCode (it's now stashed)

While still on branch A stage and commit the changes to the existing files:

git add .
git commit -m "committing tracked changes to current branch"

Next step is creating a new branch B (with checkout -b you visit it immediately)

git checkout -b newBranchName

When using stash pop the stashed changes get added to your current branch.

git stash pop

The only thing left is to stage and commit the changes on the new branch B

git add .
git commit -m "created new file"
Yves Boutellier
  • 1,286
  • 9
  • 19
0

I have found this answer useful.

However, as that thread is closed and is unable to make comments I have an issue with that answer.

When I applied git checkout other_branch I got the following error

error: pathspec 'other_branch' did not match any file(s) known to git

So instead of applying the command, I rather use which fixed my problem

git branch other_branch
git checkout other_branch
Ankur Lahiry
  • 2,253
  • 1
  • 15
  • 25
0

WARNING: Not for git newbies.

Similar to chakrit's answer, I run into this situation quite often: Working on a feature branch, I find a bug and want to fix it. But the fix belongs on the main branch, not my-feature. The whole sequence to get the changes into main is 7 or more git commands, which is really quite annoying and easy to get wrong

Since I could not find a script to do it, I wrote it myself. Simply place it somewhere in $PATH (e.g. /usr/local/bin or /$HOME/.local/bin or something), then you can do the following:

Usage

# currently working on branch `my-feature`
$ git add some-file       # use git add -p if you want only some changes
$ git commit-branch main --rebase -m 'Fixed some nasty bug in some-file'

It will then print some progress messages:

Committing your staged changes to branch 'main'.
+ git checkout --quiet HEAD~0
+ git commit --quiet -m 'Fixed some nasty bug in some-file'
++ git rev-parse HEAD
+ commit_hash=82513091473646a09d541893b8bd60a0f98b765d
+ git stash push --quiet
+ git checkout --quiet main
+ git cherry-pick --quiet 82513091473646a09d541893b8bd60a0f98b765d
[main 1c5d96e] Fixed some nasty bug in some-file
 Date: Mon Feb 6 15:04:03 2023 +0100
 1 file changed, 2 insertions(+)
+ git checkout --quiet my-feature
+ git rebase --quiet main
+ git stash pop --quiet
+ set +x
Success.

The script

Here's the source code for the file git-commit-branch. Don't forget to do chmod +x after placing it in $PATH. The script is also on github: https://github.com/fritzw/git-utils. Feel free to suggest improvements.

The way it works is as follows:

  1. Go into detached HEAD state
  2. Create a temporary commit with the staged changes
  3. Stash all other changes (including untracked files)
  4. Switch to target branch
  5. Cherry-pick the temporary commit on the target branch
  6. Switch back to original branch
  7. [optional] Rebase original branch on target branch, in order to include the bugfix in the original branch
  8. Restore all other changes from stash

If any command fails it will simply stop there and print some info to help you recover the situation. If you want more details, look at the comments and the sequence of git commands at the end of the script.

#!/usr/bin/env bash

set -o errexit
set -o nounset

usage() {
    echo "Usage: git commit-branch <target-branch> [--rebase|-r] [ <git-commit-options>... ]"
    echo ""
    echo "Commits your staged changes to <target-branch>, discarding them from your current branch."
    echo "Use --rebase or -r to rebase your current branch on the new commit in <target-branch>,"
    echo "and thus include the changes in your current branch as well."
    echo ""
    echo "Example usage working on branch my-feature:"
    echo "  git add some-file"
    echo "  git commit-branch main --rebase -m 'Fixed a bug in some-file'"
}

if [[ $# -lt 1 ]]; then
    usage
    exit 1
fi
target_branch="$1"; shift # Remove first argument
if ! git rev-parse --verify "$target_branch" >/dev/null; then
    echo "fatal: '$target_branch' is not a branch in this git repository."
    usage
    exit 1
fi

rebase_command=''
if [[ $# -gt 0 ]] && [[ "$1" == "-r" || "$1" == "--rebase" ]]; then
    rebase_command="git rebase --quiet $target_branch"
    shift # Remove -r/--rebase argument
fi

current_branch="$(git branch --show-current)"
if ! [[ "$current_branch" ]]; then
    echo "fatal: Unable to determine current branch. You must be on a branch to use git commit-branch."
    exit 1
fi

commit_hash='not-committed-yet'
print_error_message() {
    set +x
    echo
    echo "Something went wrong in the last command. :-("
    echo "Your unstaged changes and untracked files should be in the last stash."
    echo "Your previously staged changes should be in the following commit: $commit_hash"
    echo "Please check which commands were executed and try to undo them manually."
    echo
}

echo "Committing your staged changes to branch '$target_branch'."
trap 'print_error_message' ERR # Print some hopefully helpful info if something fails
set -x # Print all executed commands
git checkout --quiet 'HEAD~0' # Go into 'detached HEAD' state to avoid changing current branch
git commit --quiet "$@" # Create temporary commit
commit_hash="$(git rev-parse HEAD)" # Note temporary commit ID
git stash push --include-untracked --quiet # Save all other changes from working tree
git checkout --quiet "$target_branch" # Move to target branch
git cherry-pick --quiet "$commit_hash" # Apply changes from temporary commit to target branch
git checkout --quiet "$current_branch" # Switch back to current branch
$rebase_command # Execute git rebase if --rebase flag is present
git stash pop --quiet # Re-apply untracked changes to working tree
set +x # Stop printing executed commands
echo "Success."
if ! [[ "$rebase_command" ]]; then
    echo ""
    echo "If you want to include those changes in your current branch, you can run:"
    echo "  git stash; git rebase $target_branch; git stash pop"
    echo "or"
    echo "  git stash; git merge $target_branch; git stash pop"
    echo ""
fi
Fritz
  • 1,293
  • 15
  • 27