Git Balance Changes
I have 3 branches: master, feature, bugfix ... And the commits look like this:
4-5-6(feature)
|
1-2-3(master)
|
7(bugfix)
I did a "git redirect fix function" to test my function with bugfix
1-2-3(master) | 7(bugfix)-4-5-6(feature)
Now I need to rebase creating a pull request for my feature branch without patching, so I did a "git master rebase" function and expected:
1-2-3(master)-4-5-6(feature) | 7(bugfix)
Instead, he says the feature has been updated with a wizard. It's true, but I don't want to merge commit 7. I could reload interactively and remove that commit, but I would like to know if there is a better way to do this. I thought that rebase would only carry commits in one branch to another, but it looks like it doesn't.
source to share
Something to understand is that rebase does not rewrite history or transfer commits, a commit in Git cannot be changed. Instead, he creates a new story and says that everything was like this. For example, when you start with:
4-5-6(feature)
|
1-2-3(master)
|
7(bugfix)
And then git rebase bugfix feature
what really happens:
4-5-6
|
1-2-3(master)
|
7(bugfix)-4A-5A-6A(feature)
Three new commits are made, 4A, 5A, and 6A. The original commits are still there, but nothing points to them. They will eventually be cleaned out, but they will stay there for a few days.
This means you can undo the rebase, which is what you are trying to do. You need to find where you feature
were before reinstalling. This can be done with a help git reflog
that tracks every time it moves HEAD
. This happens to be checkout
, commit
, reset
and rebase
. git reflog
maybe something like:
65e93ca (HEAD -> feature) HEAD@{0}: rebase finished: returning to refs/heads/feature
65e93ca (HEAD -> feature) HEAD@{1}: rebase: 3 feature
6d539a3 HEAD@{2}: rebase: 2 feature
3cd634f HEAD@{3}: rebase: 1 feature
b84924b (bugfix) HEAD@{4}: rebase: checkout bugfix
a9fd2f1 HEAD@{5}: commit: 3 feature
29136bc HEAD@{6}: commit: 2 feature
60543b0 HEAD@{7}: commit: 1 feature
c487530 (master) HEAD@{8}: checkout: moving from master to feature
This tells me that a9fd2f1 was the last function commit before it was reinstalled. Instead of re-doing the rebase, I can just move the function back.
git checkout feature
git reset --hard a9fd2f1
In the future, this kind of thing becomes much easier if you git tag
initial position the function before doing the rebase. Then you can git reset
go back to that tag without searching in the reflog.
As for your specific problem, the problem is that after reinstalling, your repository now looks like this:
6A [feature]
|
5A
|
4A
|
7 [bugfix]
|
3 [master]
|
2
|
1
When you ask git rebase master feature
Git, notes that the master is already the ancestor of the function and does nothing. It doesn't matter what bugfix is in between.
Instead, you need to tell Git that you only want to reinstall 4A, 5A, and 6A and ignore 7. This is done using syntax --onto
.
git rebase --onto master bugfix feature
This indicates the need to reinstall, but does not include fixes for the function on master.
I would recommend using git reset
rebase instead of trying to redo. There is no guarantee that the second rebase will be the same, especially if there were conflicts. If with using git reset
you explicitly revert to the old state of the repository.
source to share
I thought rebase would only push commits in one branch to another, but it looks like it doesn't.
This is the key: your commit 7
on your diagram is on a branch feature
. It's also in the branch bugfix
. Commits 1-2-3
are found in all three branches.
Git is very different from most other version control systems. The branch "contains" the commit only because of the "reach" ability that makes the commit pointed to by the branch name. Branch names such as master
, bugfix
and feature
, simply point to one particular commit that Git calls the tip of the branch. He himself does, forming a chain, through each fixation "back" to his predecessor.
It git rebase
actually copies commits because of this : you went from:
4--5--6 <-- feature / 1--2--3 <-- master \ 7 <-- bugfix
in
4--5--6 [abandoned - used to be feature] / 1--2--3 <-- master \ 7 <-- bugfix \ D--E--F <-- feature
where D
is a copy of the original 4
, E
is a copy 5
, and F
is a copy 6
(I used the 4th, 5th and 6th letters here so we can copy 7 onto G
if we want, but this technique is about to end.)
You can still get what you want. You just need to copy over again D-E-F
, or - it's probably better for this particular case - go back to the abandoned, original 4-5-6
.
When you use git rebase
to copy fixes, the originals are glued. There are two names by which you can find them: ORIG_HEAD
and reflog names. The name is ORIG_HEAD
overwritten by various other commands, but you can check if it all points to a commit 6
:
git log ORIG_HEAD
and you will probably recognize your originals.
Reflog names are of the form name@{number}
, for example feature@{1}
. The chunk number
grows every time you change the commit that the chunk points to name
, since Git just stores the current value name
in the reflog and the rest is slice.
Hence:
git log feature@{1}
should show you the same as it git log ORIG_HEAD
, except that it feature@{1}
works longer (maybe becomes feature@{2}
, feature@{3}
etc. over time). By default, the previous values for each name are retained for at least 30 days, so it will take enough time to return.
To get it back, use git reflog feature
to find out which number goes in the part @{...}
, and then, while on feature
( git checkout feature
), run:
git reset --hard feature@{1}
or whatever the number (although checking again with help git log
first is a good idea).
(This assumes you have nothing to log anything, i.e., which git status
says everything is clean because it git reset --hard
destroys unchecked indexes and work tree changes.)
source to share