I have 3 branches: develop
, feature-1
and feature-2
. I was working on feature-1
, then switched to work on feature-2
. I then finished work on feature-2
, committed and pushed to the repo, then created a merge request for that feature. I then saw that feature-2
contained some commits that were supposed to be in feature-1
. Can I move them from feature-2
to feature-1
locally, then have the same moves reflected in the repo?
Undo and Commit to New Branch Use git log to check how many commits you want to roll back. Then undo the commits with git reset HEAD~N where “N” is the number of commits you want to undo. Then create a new branch and check it out in one go and add and commit your changes again.
If we want to move a commit to an existing branch, we can follow a similar process using merge. In step (1) we make sure we are on the branch where we want the commit to end up. We then merge in the source branch in step (2). At this point, our target branch should have the work we want transferred.
technically you do not move the commit, you create a new one with the same content and git remembers this copy when merging.
git cherry-pick <commit-id>
is your friend dear.
commit-id(s)
you want to move from feature-2
to feature-1
Now cherry pick all the commits you have copied in step-2 above.
git cherry-pick <commit-id>
In case you have more than one commits to be moved to feature-1
then put all the commit-ids in sequence of their commit date/time.
For example:
git cherry-pick <commit-id-1> <commit-id-2> . . <commit-id-n>
Your question says that the commits in question belong in feature-1
and not in feature-2
branch. The accepted answer does not accomplish this.
cherry-pick
is an ok way to create a new commit on one branch that duplicates the changes made by a commit on another branch; but it doesn't change the other branch.
So for instance, if you started with
x <--(master)
|\
| A1 -- A2 <--(feature_1)
\
B1 -- A3 -- B2 <--(feature_2)
where A3
was meant for feature_1
, after following a cherry-pick
procedure you will have
x <--(master)
|\
| A1 -- A2 -- A3' <--(feature_1)
\
B1 -- A3 -- B2 <--(feature_2)
So feature_1
may look like you want it now, but feature_2
still has some changes it presumably shouldn't have. It's possible that this will cause some trouble when you merge both features into master
. (By that I mean, there may be merge conflicts that don't make much sense. git
does try to help out here, but it can't be perfect.)
In your question you note that the commits were already pushed, and in response to the cherry-pick
answer, you ask if the changes will push
cleanly; so I assume you've run into (at least some of) the problems with rewriting history on a branch you've pushed.
While it's true that the results of the cherry-pick
will push
cleanly, that's because the cherry-pick
didn't make all the changes you've asked for (as noted above). It's the fix to feature-2
that really causes a problem with push
.
One solution is to use cherry-pick
to fix feature-1
, then use revert
to fix feature-2
. In our example, using feature-2~1
as an expression that identifies the commit to be "moved" (you could generally use the commit ID, or an expression appropriate to your real situation):
git checkout feature-1
git cherry-pick feature-2~1
git checkout feautre-2
git revert feature-2~1
gives you
x <--(master)
|\
| A1 -- A2 -- A3' <--(feature_1)
\
B1 -- A3 -- B2 -- !A3 <--(feature_2)
This will still push
cleanly (because no commit was removed from any branch's history) and both branches will have the right changes now. On the plus side, this makes it less likely that you'd have any merge issues; on the down side, certain rebase
operations could now be problematic. There are steps you might take to mitigate that, but if you're worried about only making changes that push
cleanly, then you won't be doing that sort of rebase
anyway.
If there are many commits you want to "move", then you might want to replace the cherry-pick
with an interactive rebase
, like this:
git checkout feature-2
git branch temp
git rebase -i feature-1 temp
An editor will open, showing a "todo" list for rebase
. Each entry on the list is the instruction for how to handle a commit. Delete the lines for commits you want to "leave behind". (There are other tweaks you could make to how the commits will be represented on feature-1
; but that's a long rabbit hole since all you've asked so far is how to "move" the commits, so just these steps will keep them "as is".)
When you save and exit the editor, the rebase
will commence, rewriting all of the selected commits. Finish up the update to feature-1
by
git checkout feature-1
git merge temp
git branch -d temp
You'll still need to go to feature-2
to revert the commits that shouldn't be there; you might want to do that in a single command with the -n
flag, so that you can create just a single revert commit. (This will have the side-effect of reducing the risk I noted above, of certain rebase
operations acting up after you do the revert
; but again, I expect you won't be doing those types of rebase
anyway given the constraints implied by this question.)
Now some people don't love using revert
for this kind of thing, because the history shows what happened - including making the changes and later removing the changes, which may seem annoying since the change should never have been made.
The only way to avoid that is to "rewrite the history" of feature-2
, and no matter how you do it, that will not push
cleanly. You can get it to push
, but in so doing you'll create work for all the other developers who have a copy of feature-2
- and if they do the wrong thing, it will undo your work. So you have to coordinate with everyone else who uses the repo if you want to do a history rewrite. See the git rebase
docs under "recovering from upstream rebase" for more information about this.
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