HomeiOS DevelopmentUnderstanding and resolving merge conflicts – Donny Wals

Understanding and resolving merge conflicts – Donny Wals


Git is nice, and when it really works nicely it may be a breeze to work with. You push , pull, commit, department, merge, however then… you get right into a merge battle, On this publish, we’ll discover merge conflicts. We’ll have a look at why they occur, and what we will do to keep away from operating into merge conflicts within the first place.

Let’s begin by understanding why a merge battle occurs.

Understanding why a merge battle occurs

Git is often fairly good at merging collectively branches or commits. So why does it get confused generally? And what does it imply when a merge battle happens?

Let me begin by saying {that a} merge battle just isn’t your fault. There’s likelihood that you simply couldn’t have averted it, and it’s most definitely not one thing you need to really feel dangerous about.

Merge conflicts occur on a regular basis and they’re at all times fixable.

The rationale merge conflicts occur is that git generally will get conflicting details about adjustments. For instance, possibly your coworker cut up an enormous Swift file into two or extra recordsdata. You’ve made adjustments to elements of the code that was now moved into an extension.

The next two code snippets illustrate the earlier than state of affairs, and two “after” conditions.

// Earlier than
struct MainView: View {
  var physique: some View {
    VStack {
      Textual content("That is an instance")

      Button("Counter ++") {
        // ...
      }
    }
    .padding(16)
  }
}

// After on department foremost
struct MainView: View {
  var physique: some View {
    VStack {
      Textual content("That is one other instance")
      Textual content("It has a number of traces!")

      Button("Counter ++") {
        // ...
      }
    }
    .padding(16)
  }
}

// After on function department
struct MainView: View {
  var physique: some View {
    VStack {
      MyTextView()

      CounterButton()
    }
    .padding(16)
  }
}

When git tries to merge this, it will get confused.

Programmer A has deleted some traces, changing them with new views whereas programmer B has made adjustments to these traces. Git wants some help to inform it what the suitable technique to merge that is. A merge battle like that is no person’s fault as a result of it’s completely affordable for one developer to be refactoring code and for one more developer to be engaged on part of that code.

Normally you’ll attempt to keep away from two builders engaged on the identical recordsdata in a brief timespan, however on the similar time git makes it in order that we can work on the identical file on a number of branches so it’s not widespread for builders to synchronize who works on which recordsdata and when. Doing so could be an enormous waste of time, so we as an alternative we depend on git to get our merges proper generally.

Every time git sees two conflicting adjustments on the identical a part of a file, it asks a human for assist. So let’s transfer on to seeing totally different approaches to resolving merge conflicts.

Resolving merge conflicts

There’s no silver bullet for resolving your merge conflicts. Sometimes you’ll select one among three choices while you’re resolving a battle:

  • Resolve utilizing the incoming change (theirs)
  • Resolve utilizing the present change (mine)
  • Resolve manually

In my expertise you’ll often wish to use a handbook decision when fixing merge conflicts. Earlier than I clarify how that works, let’s take a Fast Take a look at how resolving utilizing “mine” and “theirs” works.

A merge conflicts at all times occurs while you attempt to apply adjustments from one commit onto one other commit. Or, while you attempt to merge one department into one other department.

Generally git can merge elements of a file whereas different elements of the file trigger conflicts. For instance, if my commit adjustments line 2 of a particular file, and the opposite commit removes that line. My commit additionally provides just a few traces of code on the finish of the file, and the opposite commit doesn’t.

Git could be good sufficient to append the brand new traces to the file, however it could’t determine what to do with line 2 of the recordsdata since each commits have made adjustments in a method that git can’t merge.

On this case, we will make a option to both resolve the battle for line 2 utilizing my commit (make a change to line 2) or utilizing the opposite commit (delete the road altogether).

Deciding what must be executed can generally require some work and collaboration.

In case your coworker deleted a particular line, it’s price asking why they did that. Perhaps line 2 declares a variable that’s not wanted or used so your coworker figured they’d delete it. Perhaps you didn’t examine whether or not the variable was nonetheless wanted however you utilized a formatting change to do away with a SwiftLint warning.

In a state of affairs like this, it’s secure to resolve your battle utilizing “their” change. The road might be eliminated so you may inform git that the incoming change is what you need.

In different conditions issues won’t be as easy and also you’ll must do a handbook merge.

For instance, let’s say that you simply cut up a big file into a number of recordsdata whereas your coworker made some adjustments to one of many features that you simply’ve now moved into a distinct file.

If so, you may’t inform git to make use of one of many commits. As a substitute, you’ll must manually copy your coworker’s adjustments into your new file in order that every part nonetheless works as meant. A handbook battle decision can generally be comparatively easy and fast to use. Nevertheless, they may also be reasonably complicated.

In case you’re not 100% positive about one of the simplest ways to resolve a battle I extremely advocate that you simply ask for a second pair of eyes that will help you out. Ideally the eyes of the writer of the conflicting commit as a result of that can assist be sure you don’t by accident discard something your coworker did.

Throughout a merge battle your venture received’t construct which makes testing your battle decision virtually unimaginable. When you’ve resolved every part, be sure you compile and take a look at your app earlier than you commit the battle decision. If issues don’t work and you haven’t any thought what you’ve missed it may be helpful to only rewind and take a look at once more by aborting your merge. You an do that utilizing the next command:

git merge --abort

This may reset you again to the place you have been earlier than you tried to merge.

In case you strategy your merge conflicts with warning and also you pay shut consideration to what you’re doing you’ll discover that the majority merge conflicts might be resolved with out an excessive amount of bother.

Merge conflicts might be particularly tedious while you attempt to merge branches by rebasing. Within the subsequent part we’ll check out why that’s the case.

Resolving conflicts whereas rebasing

Whenever you’re rebasing your department on a brand new commit (or department), you’re replaying each commit in your department utilizing a brand new commit as the start line.

This may generally result in attention-grabbing issues throughout a rebase the place it feels such as you’re resolving the identical merge conflicts time and again.

In actuality, your conflicts can hold popping up as a result of every commit may have its personal incompatibilities together with your new base commit.

For instance, take into account the next diagram as our git historical past:

You’ll be able to see that our foremost department has acquired some commits since we’ve created our function department. For the reason that foremost department has modified, we wish to rebase our function department on foremost in order that we all know that our function department is absolutely updated.

As a substitute of utilizing a daily merge (which might create a merge commit on function) we select to rebase function on foremost to make our git historical past look as follows:

We run git rebase foremost from the command line and git tells us that there’s a battle in a particular file.

Think about that this file appeared like this after we first created function:

struct MainView: View {
  var physique: some View {
    VStack {
      Textual content("That is an instance")

      Button("Counter ++") {
        // ...
      }
    }
    .padding(16)
  }
}

Then, foremost acquired some new code to make the file appear like this:

struct MainView: View {
  var physique: some View {
    VStack {
      Textual content("That is one other instance")
      Textual content("It has a number of traces!")

      Button("Counter ++") {
        // ...
      }
    }
    .padding(16)
  }
}

However our function department has a model of this file that appears as follows:

struct MainView: View {
  var physique: some View {
    VStack {
      MyTextView()

      CounterButton()
    }
    .padding(16)
  }
}

We didn’t get to this model of the file on function in a single step. We even have a number of commits that made adjustments to this file so after we replay our commits from function on the present model of foremost, every particular person commit might need a number of conflicts with the “earlier” commit.

Let’s take this step-by-step. The primary commit that has a battle appears to be like like this on function:

struct MainView: View {
  var physique: some View {
    VStack {
      MyTextView()

      Button("Counter ++") {
        // ...
      }
    }
    .padding(16)
  }
}

struct MyTextView: View {
  var physique: some View {
    Textual content("That is an instance")
  }
}

I’m positive you may think about why this can be a battle. The function department has moved Textual content to a brand new view whereas foremost has modified the textual content that’s handed to the Textual content view.

We will resolve this battle by grabbing the up to date textual content from foremost, including it to the brand new MyTextView and proceed with our rebase.

Now, the subsequent commit that modified this file may also have a battle to resolve. This time, we have to inform git how one can get from our beforehand fastened decide to this new one. The rationale that is complicated git is that the commit we’re trying to use can not be utilized in the identical method that it was earlier than; the bottom for each commit in function has modified so every commit must be rewritten.

We have to resolve this battle in our code editor, after which we will proceed the rebase by operating git add . adopted by git rebase --continue. This may open your terminal’s textual content editor (usually vim) permitting you to alter your commit message if wanted. Whenever you’re proud of the commit message you may end your commit by hitting esc after which writing :wq to write down your adjustments to the commit message.

After that the rebase will proceed and the battle decision course of must be repeated for each commit with a battle to be sure that every commit builds accurately on high of the commit that got here earlier than it.

Whenever you’re coping with a handful of commits that is advantageous. Nevertheless for those who’re resolving conflicts for a dozen of commits this course of might be irritating. If that’s the case, you may both select to do a merge as an alternative (and resolve all battle directly) or to squash (elements of) your function department. Squashing commits utilizing rebase is a subject that’s fairly superior and could possibly be defined in a weblog publish of its personal. So for now, we’ll skip that.

Whenever you’ve determined the way you wish to proceed, you may abandon your rebase by operating git rebase --abort in your terminal to return to the state your department was in earlier than you tried to rebase. After that, you may determine to both do a git merge as an alternative, or you may proceed with squashing commits to make your life a little bit bit simpler.

Git rebase and your distant server

In case you’ve resolved all of your conflicts utilizing rebasing, you’ve gotten barely altered all the commits that have been in your function department. In case you’ve pushed this department to a distant git server, git will let you know that your native repository has n commits that aren’t but on the distant, and that the distant has a (often) equal variety of commits that you don’t but have.

If the distant has extra commits than you do, that’s an issue. It’s best to have pulled first earlier than you probably did your rebase.

The rationale that you must pull first in that situation is since you want to have the ability to rebase all commits on the department earlier than you push the rewritten commits to git since with a view to do a push like that, we have to inform git that the commits we’re pushing are appropriate, and the commits it had remotely needs to be ignored.

We do that by passing the --force flag to our git push command. So for instance git push --force function.

Notice that you need to at all times be tremendous cautious when drive pushing. It’s best to solely ever do that after a rebase, and for those who’re completely positive that you simply’re not by accident discarding commits from the distant by doing this.

Moreover, for those who’re engaged on a department with a number of individuals a drive push might be reasonably irritating and problematic to the native branches of your coworkers.

As a basic rule, I attempt to solely rebase and drive push on branches that I’m engaged on on my own. As quickly as a department is being labored on my others I swap to utilizing git merge, or I solely rebase after checking in with my coworkers to be sure that a drive push won’t trigger issues for them.

When unsure, at all times merge. It’s not the cleanest answer because of the merge commits it creates, however no less than you understand it received’t trigger points on your teammates.

In Abstract

Merging branching is a daily a part of your day after day work in git. Whether or not it’s since you’re tying to soak up adjustments somebody made right into a department of your personal or it’s since you wish to get your personal adjustments in to your foremost department, understanding totally different merging strategies is vital.

No matter how you plan to merge branches, there’s a risk to run right into a merge battle. On this publish, you’ve discovered why merge conflicts can occur, and how one can resolve them.

You’ve additionally study why rebases can run into a number of merge conflicts and why you need to at all times resolve these conflicts one after the other. Briefly, it’s as a result of git replays every commit in your department on high of the “present” commit for the department you’re rebasing on.

The important thing to resolving conflicts is at all times to maintain your cool, take it simple, and work by the conflicts one after the other. And when unsure it’s at all times a good suggestion to ask a coworker to be your second pair of eyes.

You additionally discovered about drive pushing after rebasing and the way that may be problematic for those who’re working in your department with a number of individuals.

Do you’ve gotten any strategies you like to make use of whereas resolving conflicts? Let me know on X or Threads!



RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments