iOS Development

Understanding and resolving merge conflicts – Donny Wals

Spread the love


Git is nice, and when it really works effectively 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 put up, we’ll discover merge conflicts. We’ll take a look at why they occur, and what we will do to keep away from working 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 will not be your fault. There’s likelihood that you simply couldn’t have averted it, and it’s most actually not one thing it’s best to really feel dangerous about.

Merge conflicts occur on a regular basis and they’re all the time fixable.

The explanation merge conflicts occur is that git generally will get conflicting details about modifications. For instance, possibly your coworker break up an enormous Swift file into two or extra recordsdata. You’ve made modifications 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 essential
struct MainView: View {
  var physique: some View {
    VStack {
      Textual content("That is one other instance")
      Textual content("It has a number of strains!")

      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 strains, changing them with new views whereas programmer B has made modifications to these strains. Git wants some help to inform it what the suitable method 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.

Often 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 frequent 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 usually.

Every time git sees two conflicting modifications 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 certainly one of three choices whenever 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 guide 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 all the time occurs whenever you attempt to apply modifications from one commit onto one other commit. Or, whenever you attempt to merge one department into one other department.

Typically git can merge elements of a file whereas different elements of the file trigger conflicts. For instance, if my commit modifications line 2 of a particular file, and the opposite commit removes that line. My commit additionally provides a number of strains of code on the finish of the file, and the opposite commit doesn’t.

Git could be sensible sufficient to append the brand new strains to the file, however it may’t work out what to do with line 2 of the recordsdata since each commits have made modifications in a means 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 performed 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 now 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 will be eliminated so you’ll be able to inform git that the incoming change is what you need.

In different conditions issues may not be as easy and also you’ll have to do a guide merge.

For instance, let’s say that you simply break up a big file into a number of recordsdata whereas your coworker made some modifications to one of many capabilities that you simply’ve now moved into a unique file.

If so, you’ll be able to’t inform git to make use of one of many commits. As a substitute, you’ll have to manually copy your coworker’s modifications into your new file in order that all the things nonetheless works as supposed. A guide battle decision can generally be comparatively easy and fast to use. Nevertheless, they may also be quite complicated.

For those who’re not 100% positive about one of the best 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 unintentionally discard something your coworker did.

Throughout a merge battle your venture gained’t construct which makes testing your battle decision virtually not possible. When you’ve resolved all the things, be sure you compile and check your app earlier than you commit the battle decision. If issues don’t work and you don’t have any concept 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

It will reset you again to the place you had been earlier than you tried to merge.

For those who method your merge conflicts with warning and also you pay shut consideration to what you’re doing you’ll discover that almost all merge conflicts will be resolved with out an excessive amount of bother.

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

Resolving conflicts whereas rebasing

Once 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 could 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 preserve popping up as a result of every commit may have its personal incompatibilities together with your new base commit.

For instance, contemplate the next diagram as our git historical past:

You possibly can see that our essential department has acquired some commits since we’ve created our function department. Because the essential department has modified, we wish to rebase our function department on essential in order that we all know that our function department is totally updated.

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

We run git rebase essential 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 once we first created function:

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

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

Then, essential 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 strains!")

      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 modifications to this file so once we replay our commits from function on the present model of essential, 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 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’ll be able to think about why this can be a battle. The function department has moved Textual content to a brand new view whereas essential has modified the textual content that’s handed to the Textual content view.

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

Now, the subsequent commit that modified this file will even have a battle to resolve. This time, we have to inform git how one can get from our beforehand mounted decide to this new one. The explanation that is complicated git is that the commit we’re trying to use can now not be utilized in the identical means 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 working git add . adopted by git rebase --continue. It will open your terminal’s textual content editor (typically vim) permitting you to vary your commit message if wanted. Once you’re pleased with the commit message you’ll be able to end your commit by hitting esc after which writing :wq to put in writing your modifications 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 make it possible for every commit builds appropriately on high of the commit that got here earlier than it.

Once you’re coping with a handful of commits that is positive. Nevertheless should you’re resolving conflicts for a dozen of commits this course of will be irritating. If that’s the case, you’ll be able to both select to do a merge as an alternative (and resolve all battle without delay) 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 put up of its personal. So for now, we’ll skip that.

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

Git rebase and your distant server

For those who’ve resolved all of your conflicts utilizing rebasing, you might have barely altered all the commits that had been in your function department. For those who’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 is best to have pulled first earlier than you probably did your rebase.

The explanation you might want to 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 right, 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 it’s best to all the time be tremendous cautious when drive pushing. It is best to solely ever do that after a rebase, and should you’re completely positive that you simply’re not unintentionally discarding commits from the distant by doing this.

Moreover, should you’re engaged on a department with a number of individuals a drive push will be quite irritating and problematic to the native branches of your coworkers.

As a common 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 make it possible for a drive push won’t trigger issues for them.

When doubtful, all the time merge. It’s not the cleanest answer as a result of merge commits it creates, however at the very least you recognize it gained’t trigger points in your teammates.

In Abstract

Merging branching is an everyday a part of your daily work in git. Whether or not it’s since you’re tying to soak up modifications somebody made right into a department of your individual or it’s since you wish to get your individual modifications in to your essential department, understanding totally different merging methods is vital.

No matter how you propose to merge branches, there’s a chance to run right into a merge battle. On this put up, 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 it’s best to all the time resolve these conflicts one after the other. In brief, 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 all the time to maintain your cool, take it straightforward, and work by means of the conflicts one after the other. And when doubtful it’s all the time 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 should you’re working in your department with a number of individuals.

Do you might have any methods you like to make use of whereas resolving conflicts? Let me know on X or Threads!



Leave a Reply

Your email address will not be published. Required fields are marked *