I can currently do it by following these three steps
git reset HEAD <file>...
git checkout [-b] <branchname>
git add <file>...
Is there a better way of moving the staged changes?
Edmundo's answer is the right one, but it could use some details about why it's the right one.
There are two cases of interest here:
Making a new branch that starts from the commit where you are now is trivially easy:
$ git checkout -b newbranch
... continue working; git add and/or git commit as usual ...
The git checkout -b
step never fails as long as the new branch itself is OK (has a valid name that does not collide with an existing name).
Moving to some other, already-existing branch may be trivially easy:
$ git checkout otherbranch
... continue working ...
If that fails, you will want/need to make at least one commit.
You can make this commit as an ordinary commit, that you then copy to the new branch and remove from the current branch; or you can use git stash
, which actually makes two commits.1 It's just that these two commits are not on any branch, which makes it more obvious that git stash apply
can re-apply those commits anywhere. (Using git stash pop
is just saying git stash apply && git stash drop
, i.e., apply and then, if Git thinks that went OK—whether or not it really did go well—drop the stash. I recommend splitting the two so that you can check whether it went well, though usually it does and in the end this is usually quite minor.)
1These two commits are to save (a) the index/staging area, and (b) corresponding files (i.e., all tracked files) in the work-tree. If you use git stash save --untracked
or git stash save --all
, it actually makes three commits. You probably don't want the three-commit form, which is substantially trickier.
When you have made changes, these changes are stored in files. These files appear in one or both of two places:
The index or staging area is where you build the next commit you will make. Normally, it already has in it—it starts out with—all the files from your current commit, in the same form they have in the current commit (which, incidentally, is always known as HEAD
). That is, you run:
git checkout <branch-name-or-commit-hash-or-similar>
and both your work tree and your index/staging-area get filled in so that they match the commit you just checked out, which now known as HEAD
. (If you check out a branch name, the commit you're checking out is the latest, or tip, commit on that branch.) Let's say, for instance, that you did git checkout master
.
If you don't touch the index and work tree at this point, you can, obviously, git checkout
any other commit, e.g., git checkout develop
. It's fine for Git to throw away the versions in your index and work-tree, because they match the commit you had checked out. If it tosses out a README
file, or even your entire working set of files, so what? They're still in that other commit, the tip commit of master
. You can just git checkout master
again when you want them back.
But that's not the case you care about here. Here, you have modified some files. You may even have run git add
to stage them for the next commit. But all git add README
does is to copy the file from the work-tree into the index. This means that the index and the work-tree versions of README
match each other. They both don't match the HEAD
(current) commit.
Let's say that HEAD
is the tip of develop
and you should have been on feature
instead. You'd like to git checkout feature
. But what will happen to README
? You made a change. You maybe even staged it, copying it into the index.
Well, the fact is, Git is lazy. If you now run git checkout feature
to move the HEAD
commit to the tip of branch feature
, Git will have to do some work. That might include removing the current README
file and replacing it with the one from the tip of feature
.
But maybe—just maybe—the tip commit of feature
has the same README
file as the tip commit of develop
. If so, Git can be lazy! Git can simply not bother ripping out the existing README
from the index/staging-area and/or work-tree. It can leave the modified README
in whichever places it is now. And that's precisely what Git does.
If Git can't be lazy about the files, it checks. If you the files you have in your index and/or work-tree don't match the ones in the HEAD
commit you are trying to move off of, then git checkout feature
fails. (See the git read-tree
documentation and its list of—yikes!—21 possible cases for a "two tree merge", to see exactly which ones succeed and which ones fail. Mostly, though, you don't have to care about all of this: if it succeeds, you're good, if it fails, you have to commit or stash.)
This is why git checkout -b newbranch
always succeeds: it creates the new branch with that branch-name pointing to the current (HEAD
) commit. Then it switches from the HEAD
commit to ... well, itself. There's nothing to switch. The laziness step applies to every file: no file must be changed, so no file is changed, so git checkout -b
just works.
And, this is why git checkout -b otherbranch
sometimes succeeds: it needs to switch from the HEAD
commit to the tip of otherbranch
. If that commit has some files that are different, Git will have to rip out the ones that are in the index/staging-area and work-tree right now, and replace them with the versions from the tip of otherbranch
. This is only allowed if the versions that are in the index/staging-area and work-tree right now match the versions in the HEAD
commit.
Just checking out where you want to move them should be enough. Git performs a check to see if the staged changes can be applied on the point you are checking out without conflicts. Another easy technique you can use is to stash the changes, then checkout the branch and then stash pop.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With