Seungjae Yoo
Seungjae Yoo

Reputation: 17

How to change parent node in git?

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

Answers (2)

Mark Adelsberger
Mark Adelsberger

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:

  1. 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
    
  2. 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

Mad Physicist
Mad Physicist

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:

  1. The branch that g is in needs to be reset to commit e
  2. The branch that 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

Related Questions