I had created an extra branch for some testing purpose.
Before starting to work, I switched back to master branch and, after having a tea, I started adding files and modifying some other files in master branch.
Only after I committed, I remembered that I was in master branch, while I had to switch to my second-branch before starting the changes.
could you let me know if it is possible to send this commit to the second-branch and remove it from master branch?
thanks
If I understood you right you can do such things:
git checkout master
git log
now check hash commit you want to move - for example: 0123456
git checkout branch
git cherry-pick 0123456
git checkout master
git revert 0123456
that's all.
The answer to the question is "sort of, but maybe you don't have to".
To copy a commit, use git cherry-pick commit
. This essentially tells git: "Go look at the commit I've asked you to cherry-pick. Whatever changes it made, make those same changes again, on the current branch. Then make a new commit1 with the same commit message."
Thus, if you've already made a commit on master
and you want it on newbranch
instead:
$ git checkout newbranch
$ git cherry-pick master
At this point, you'll be on newbranch
and the commit will have been copied. You now need to do something about the old commit on master
.
If you have not published (pushed, or let someone else pull) this commit anywhere, you need only "roll it back":
$ git checkout master
$ git reset --hard HEAD^ # be careful here! I always run "git status" first
Note that git reset --hard
will wipe out any in-progress work.
If you have published the commit, you generally should revert it instead (see user2699113's answer). A "revert" is a lot like a cherry-pick, copying a commit, except that it reverses the changes: whatever was added then is removed now, and whatever was removed, is added back. (And, as with cherry-pick, revert will auto-commit unless you tell it not to.)
(Note that you can specify a commit by SHA-1 ID, but if there's a name—branch or tag name, or other reference name—for it, you can just write the name.)
1Unless you use -n
or --no-commit
to suppress it, or git is not able to make the same changes and tells you that there was a conflict and makes you fix it up yourself.
Here's a thing about git and its commits and branches. Git is different from most other version-control systems, in that while you do a commit "on a branch", the branch name is not actually part of the commit.2You assign branch names (or labels, whatever you want to call these), but the actual branch structures—the "structural branches"—are formed by each commit's history.
When a commit is made, that commit records its parent-commit(s). Branch names are just labels, that give you (and git) a place to start, and then git follows the commit parent IDs. This means we can draw the commit graph (the directed acyclic graph or DAG of commits) by hand:
A <-- B <-- C <-- master
^
\
D <-- branch
Here commit D
is the tip of branch branch
, and it points back to commit C
as its parent. Commit C
points back to B
, which points to A
, which is the initial (root, parent-less) commit.
When you are "on branch master" and make a new branch, that just makes a new label that points to the same place you are now. So suppose you had A
through C
before (I won't draw these with as many arrows this time; just assume arrows generally point leftwards):
A - B - C <-- HEAD=master
This time I added the HEAD=
to show how git keeps track of which branch you're "on". (The HEAD
file in the .git
directory just contains the name of the branch: cat .git/HEAD
and you generally see ref: refs/heads/master
or whatever.) If you now do git checkout -b branch
you get this:
A - B - C <-- master, HEAD=branch
(now .git/HEAD
contains branch
instead of master
).
When you make a new commit (any new commit, including the ones from cherry-pick and revert), git looks at HEAD
to see which branch you're on, and what that branch has for its SHA-1 ID. That's the "tip of the branch". Git then makes a new commit whose parent is the old branch-tip, and writes the new commit ID into the branch file, so that the branch name points to the new commit. (And of course HEAD
still just has the branch name in it.)
So, if you're "on branch branch
" and make a new commit D
, this is what you get:
A - B - C <-- master
\
D <-- HEAD=branch
If you goof it up a bit though, and are "on branch master
", you get this instead:
A - B - C <-- branch
\
D <-- HEAD=master
But wait, that's just what you wanted, except for the branch labels. All you need to do is make the name master
point where branch
does, and branch
point where master
does, and also switch HEAD
so that it says branch
instead of master
.
As it turns out, that's easy ... well, pretty easy. :-) You need a temporary name since git won't let you have two branches with the same name. First, rename the branch branch
to let you change master
. Then rename master
to branch
, and rename branch
's new name to master
:
$ git branch -m branch temp
$ git branch -m master branch
$ git branch -m temp master
The first step changes the label branch
to temp
. The second changes master
to branch
(and you're still on it, so rewrites HEAD
too). The last one changes temp
to master
, giving you the commit DAG and label setup you wanted originally.
As with the other section, you should only attempt this kind of "label swapping" on unpublished (un-pushed) commits. (The reason for this is simple enough: when you publish commits, you do it by name-to-commit-ID mapping. If you shuffle labels around and "re-publish", the commit IDs do not change but the name-to-ID mappings do, so anyone who picked up your previous advertising, that master
meant bb71dde...
for instance, now gets a new mapping that is not "fast-forward related" to the old one. People, and git, expect branch mappings to change, but usually only "fast-forward style".)
2For instance, in Mercurial, the branch name is actually recorded inside the commit-data.
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