Everything You'll Need to Know About Git

Resolving Conflicts with Rebase



Everything You'll Need to Know About Git

Check out a free preview of the full Everything You'll Need to Know About Git course

The "Resolving Conflicts with Rebase" Lesson is part of the full, Everything You'll Need to Know About Git course featured in this preview video. Here's what you'd learn in this lesson:

ThePrimeagen creates a conflict in a Git repository and then shows how to resolve the conflict manually using rebase to choose which changes to keep and committing the resolved conflict. The difference between merge and rebase and how conflicts can arise when rebasing is also discussed in this lesson.


Transcript from the "Resolving Conflicts with Rebase" Lesson

>> All right, so to create a conflict, the easiest way to do that is you just have to make two changes that cannot be reconciled. In other words, just change the same line to two different things. So the problem, let's create a conflict in both remote and hello.

To do this, we need to create a commit in both of them. And so we're gonna use trunk for both repos. We're gonna do an A + 1 to hello, and an A + 2 to remote. And this will be in the README. So they both have A at the first line.

So it'll create the same change in both. Let me just make sure that I don't do anything. Okay, good, so let's do that now. So I'm gonna first start off in, hello, I'm gonna go to the README, I'm gonna add A + 1, save that, git, add this git commit.

And then I'll do A + 1 so I can just see the law, I have the same message so we kind of know what we're working with. And then I'll jump over here to remote and do the exact same thing, right? We're gonna go in here, A + 2, save that, git, add this git commit 2.

There we go. We've now created the situation in which we are going to conflict. And now we just need to simply pull hello-git onto remote and it should cause conflict. So git pull, there we go. We now have the official conflict. It's hurting us emotionally, but it's fantastic.

So, what is a conflict? It may not be obvious what is in the conflict. Because often whenever you pull in changes, it's not just one file that's, well, it's unlikely that it's one file that's changed. Usually you pull in a whole bunch of commits from other people, and you get a big list of things that have been changed.

And you have one file in there that's conflicting. And so using just this message right here to find which one, kind of a pain to do, right? That's why git status is a little bit easier cuz you'll get something like this, both modified. And you can see that means it does not know how to resolve it.

It can just tell you, hey, both the one you're on and the one you're pulling into have a change here, and we don't know how to resolve it. Yeah, by the way, you could always abort these type of things by doing a git merge abort, or a git rebase abort, just so you know.

And technically a git revert abort, git cherry pick abort, any of them that you have that availableness. I think with Stash, you can't abort that one, if I'm not mistaken. There's some other technique you're gonna have to do there. I can't remember with Stash, but I'm pretty sure you can't with Stash.

All right, so to resolve a conflict, let's open up the README and let's look at what's inside of it. So here it is for convenience, but for me I like to look at things with Vim. Notice that we have HEAD with a bunch of arrows, the change we made in remote, a bunch of equal signs, the change that's up on hello git, and then of course this with some sort of sha, 7b00.

If I jump over to hello and go git log 1, you'll see 7b00. It's this commit. It's letting you know which commit you're currently resolving against. So when I go here, what this says to me or what it's trying to convey to you is that from this place to the equal sign is your change, and from the equal sign to this is this commit's change.

And so now you need to pick which one you want. Technically, I could delete this, this, and this and have them both if I really wanted to, or I could just have hello. Or I could just have remote, right? You get to choose how you want to resolve it.

That's why if you've ever noticed whenever you're using some sort of visual program, you will get to select one side or the other, and then you have in the middle the ability to edit it. It's because often a conflict isn't as simple as choosing one side or the other.

It's you kind of have to mash them together and make sure things work, right? Anyways, spend and all that. All right, so validate your sha. We already validated the sha. All right, so we are conflicted and we want to resolve it. Use the status message to identify which one, we already did that.

And let's choose our remote-git change, okay? So that means you want to keep the one between head and the equal sign or A + 2. So we're gonna keep the remote-git change. So this is the final state we should be into. Make sure my notes actually agree with me.

Yeah, okay, whew, right? Make sure you pretty much end the file looking like this. I'm not gonna delete the line that I have right here cuz I just don't want to play with history at all. I want everything to keep on going there, but normally I feel the need to delete it.

And just in case you all feel the same need that I need, just letting you know why I'm not deleting it. All right, so there we go. We have resolved the conflict, and if I go git status, it still says the same thing. We need to resolve this conflict still, right?

Even though I've deleted all the conflicting lines, git has not moved forward. It doesn't just move forward automagically when none of those lines are left. It waits for you to tell it, I'm ready for you to move forward. So let's commit the merge now. So all we have to do is do a little quick git add, and then we get to commit it with whatever message we want, right?

So I'll just say, merged in upstream. There we go. We've merged in upstream, we've resolved the conflict. We had to do it manually, had to choose whatever side we wanted, and we happened to choose our remote side not our hello side. Fantastic, just absolutely so happy right now, geez.

All right, I forgot to put the status. That's so sad. If you execute status. So one thing here, we'll look at it right here. If I were to do status before I committed the change, when I added the README, the README went away. Why is that? It's not on the status anymore.

It's quite simple. There's no change, right? We kept our change from remote. Therefore, by resolving this conflict, we're still in a state that needs to be committed, we just have no change. It's just an empty commit, right? There's nothing there. Well, fun fact. Anyway, so we can use a git log to kinda look into it.

So if you go like this git log, you'll see okay, we see the merge upstream, we actually see where our origin trunk is currently pointing to, we see our change. If we do git, this is where graph gets really cool. If you do it with graph you can actually see.

Okay, now I can see that this happened, I can see this is a merge commit with two parents. You can see which parents are which. There's our upstream one. Here's our last commit down here. So you can kinda look at them. I also think it's kinda neat that there's two 0's right here, and two 0's right here, and two 0's right here.

Feels weird to me, I love those kinda things. I don't know why I just find it very entertaining when shots are close to each other. I don't know, kinda weird guy, but all right. So two conflicts are better than one, right? I agree, but we're not gonna do this quite yet.

So, what I want you to do is I want you to make a change to bar. And I want you to pull in the change into the remote. That's it, very, very simple change. That's it. So I'm just gonna even copy this one, a no conflict change the bar, dang it.

So I'm gonna go back to hello, change bar, git add this git commit a change to bar. Very, very simple, right? I'm gonna do that, remember, in our upstream in hello. For those that are wondering what I'm doing right here, this little maneuver here, when you do echo anything, you are echoing it out to the standard out.

And all kind of programs running, there's a standard in, there's a standard out. I can redirect any output to a file. Two things mean that I not only redirect it to a file, but I append it to the end. So just in case you forgot about that, it's just when you're doing these little exercises, it makes things really, really easy to just do this.

Because I don't need to open the file, and then edit it, and then close the file, and save, and go through all of it. I'll just put the standard out right into file. So now let's pull in the change into the remote. So I'm gonna jump over here and go git pull.

And look at that, we have diverging histories, right? Right? Yeah, cuz I have my change that I merged in. There was a conflict, I changed that conflict. I never told hello git or the upstream about this conflict. I never pushed any change for it. So now, every time I make a change, I have to do a merge commit cuz we have different histories.

So this is where things can quickly get a lot of extra commits in life. Cuz now we just have even more, every time I pull in a change now, if I pull in one change at a time, I will get two commits per pull. So this is where the thing starts to get kind of annoying with merge.

Even though some people really like it, I'm just not a big fan of it. There we go. So this will keep on going over and over again, by the way. So if we look at our graph, by the way, our graph looks so good. One line graph, just so sweet.

Look at that. Look at that little trapezoidal type figure going on right there. But you can see that every time it does that, we're just gonna have to keep on doing these little merge fix. And also it's like Google colors. Strange, okay. All right, takeaways, once you resolve a conflict and you don't merge it back up, you're gonna have problems over and over again.

Anytime you're diverging in history, you're just gonna have merge conflicts or merge commits every single time you pull. So keep yourself in sync as much as possible, it avoids these. Let's do a conflict, but this time let's do it with rebase. Cuz, remember, rebase is fundamentally different than merge.

Merge finds the best common ancestor, replays those things, creates a merge commit, and that's that. Whereas rebase will actually move your changes forward to the tip and then replay a change at a time, which means that different things can happen during a conflict. Again, I wanna make sure everybody remembers, with rebase moves everything forward, plays the commits one at a time.

Very important. Very important, which means it also checks out the target branch or the source branch, then replays it. It's very important to remember that because it's gonna kind of see how this works, all right? So now here's another interesting thing. Let's pretend that C and G had a conflict, we had to resolve it.

What happened if we get another change upstream and then we rebase again? Are we gonna get another conflict? Why should we or why should we not?
>> Do you still have the conflicting change, it's still gonna conflict?
>> Yeah, cuz remember we take our commits, we then check out the branch we're trying to rebase on, we go to the end then we replay our commits one at a time.

Which means if we replay a commit that conflicts it will conflict again, and again, and again, and again. Now there is a resolution around this. In fact, it's such an unheard of option that you can even use it in a sentence to somebody's face and they won't even know you are telling them the solution because of how fun it is, all right?

It's just the best. It's just the best. All right, so let's do this again. It's fun. We're masochists. We're computer scientists, we gotta make sure that we experience this over and over again. So the first thing we wanna do is, let's make sure we first have no more merge commits, any more of that.

So let's get our branch in sync with hello. So I'm gonna push from remote to hello right now. We don't want any more merge commits. So git push. Does anyone know what happened here? You probably got the same error if you've been paying attention. Read the error message, read the friendly manual as they say, read the friendly error.

I'm trying to push to trunk. What am I currently checked out on in hello git? Trunk, do you think it'd be good if you were programming and someone could push to your branch in such a way that would change it out of underneath you? What could possibly go wrong?

We're doing it live. This is like hot reloading, but of your entire code base. So instead, you probably want to go git checkout bar, go back, then push it, which works perfectly fine. Then you can git checkout trunk. There you go. Now, think about that, GitHub has to do the same thing, right?

They can't have trunk checked out in whatever way they're managing your repo up there. It would have the exact same problem. It's kind of like a fun little, don't do that type thing. Anyways, it's just a problem you pretty much never run into cuz normally you don't have two of the exact same repos on your machine that you're pushing to.

Fun little experience right there. All right, awesome. I just wanted to go over that just because it's fun. If you looked at it, you'd see this remote rejected. It's currently checked out. It tells you right there in the error, just kinda hard to see. All right, so we did all that.

We changed branches, fantastic.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now