Reputation: 17
I'm using GitLab and I'm faced with a situation that I have to change Git Graph. I have to change some parent node from specific commit, but I don't have no idea about it.
Here are some specific situations I'm faced with.
First, I want to change my parent node from this graph
a----b----c----d
\
\--e----f
\
\--g
into below graph.
/-------g
/
a----b----c----d
\
\--e----f
Also, I want to change from this graph (which is merged)
a----b----c----d--\
\ \
\--e----f----g
into below graph
a----b----c----d----g
\ /
\--e----f--/
I want to do these jobs without merge conflict, but all ways I found wasn't work or made confilct.
How do I solve this without conflicts?
Upvotes: 0
Views: 465
Reputation: 45819
Your graphs only show the commit topology. It's probably important to know where you intend branch(es) to be pointing. I think we can infer that from your question and will add the branches I think you're indicating, but it may be incorrect.
I want to change my parent node from this graph
a----b----c----d <--(branch1) \ \--e----f <--(branch2) \ \--g <- (branch3)
into below graph.
/-------g' <- (branch3) / a----b----c----d <- (branch1) \ \--e----f <- (branch2)
Note that I've marked g'
instead of g
in the end-state graph; this is because you cannot change an existing commit, so technically g'
is a new commit that replaces g
.
Related to that point: this will be a history rewrite for branch3
, meaning that once you've done it you would have to force push branch3
to any remotes - and having done so, you will likely cause problems for anyone else that has cloned the repo and has a copy of branch3
. You'll need to coordinate with them to make sure they fix the problem correctly, because if they fix it the wrong way it will undo your work. You can read more about this in the git rebaes documenation under "recovering from upstream rebase". (https://git-scm.com/docs/git-rebase)
This still could represent either of two operations:
You could be rebasing branch3
- indicating that its changes should've been applied to commit b
rather than to commit e
. This is the more common of the things you might want to do. It is possible this will generate conflicts, indicating that changes in g
affected the same parts of the code as changs in e
and so git can't be sure, without further instructions, of what result you want in g'
. If this happens, it is a result of things that are already decided; you can't "avoid" it. (There may be strategies you can apply to simplify the resolution of the conflict, but those are case-specific so I can't really get into which of them might be applicable here.)
git rebase --onto branch1~2 branch2 branch3
The other thing you might intend to do is "re-parent" g
, making g'
match its content. This means that when g'
appears as a child of b
, it will appear to apply the changes from e
as well as those from g
. Because the content is being taken as-is with no changes being re-applied, no conflicts are possible; but you will carry some changes from branch2
into branch3
(making this an essentially cosmetic operation that probably doesn't solve any practical problem that might have led you here).
git checkout branch3
git reset --soft HEAD~2
git add :/:
git commit
I want to change from this graph (which is merged)
a----b----c----d--\ <-- (branch1) \ \ \--e----f----g <-- (branch2)
into below graph
a----b----c----d----g <-- (branch1) \ / \--e----f--/ <-- (branch2)
In this case it's unclear whether you intend g
to be exactly the same as it was before, or whether it's important to change the order of its parent pointers. (The "before" graph suggests you merged branch1
into branch2
. That means that f
- HEAD
at the time of the merge - is the "first parent" of g
; and d
- what you merged in - is the "second parent". This almost never matters, but it does affect commands where you give the --first-parent
option, and it changes how commitish expressions like g^
are evaluated.
Either way, note that you are again rewriting the history of a branch (branch2
, because you are removing g
from its history).
If the parent pointers don't matter, then you can do this just by moving branches:
git checkout branch1
git reset --hard branch2
git checkout branch2
git reset --hard HEAD^
If you do need to swap the parent pointers, then the easiest thing is to redo the merge. (If the merge had conflicts, then this will require you to resolve them again unless you recorded the resoltuion with rerere
. If this would be cumbersome, there are ways around it, but they are a bit involved and should only be used if it's really a big problem to just redo the merge.)
git checkout branch2
git reset --hard HEAD^
git checkout branch1
git merge branch2
Upvotes: 2
Reputation: 114548
In the first case, you can not literally do what you want: git commits are immutable, and the parent node is part of the commit. What you can do though, is to create a new commit, say g'
that does what you want:
git checkout g
git rebase -i HEAD^^
At this stage, select "squash" (or "fixup") for the commit g
(refer here for more info). The result will be a new commit that combines e
and g
.
The second case required no actual work. Git branches are just pointers that move when you run git commit
. Since the parents of g
have not changed, you just need to relabel a couple of things:
g
is in needs to be reset to commit e
f
is in needs to be reset to commit g
Both are done with a simple git reset
, and won't really affect your past history.
Upvotes: 0