10 votes

[help] Tips on resolving git conflicts, for the faint hearted

I’ve been using git to keep revisions of my website. Since I work alone I only need three commands:

git add -A
git commit -m “<description>”
git push

Soon I’ll be adding a second person, and I remember from experience that conflicts can happen even with two people. So I have two questions:

  • Is there a way we can avoid that happening outright?
  • Are the commands to resolve fairly standard or does it differ much on a case-by-case basis? I’m hoping to keep the number of commands as small as possible.

42 comments

  1. [2]
    parsley
    Link
    Git is very open ended in how you can use it, so you are going to have to come up with a strategy on how to work together first. Most projects use multiple branches to separate deployable (usually...

    Git is very open ended in how you can use it, so you are going to have to come up with a strategy on how to work together first.

    Most projects use multiple branches to separate deployable (usually main/master) code from work-in-progress code (usually feature/ticket-id or bugfix/ticket-id) from different people. You then merge the work-in-progress branches to the deployable one after some manner of code review (ie via pull requests on github). Check out Trunk based development for a simple system that resolves around this.

    Regarding number of commands, it depends on what system you end up using. If the actual commands are a problem, most IDEs/editors have git plugins that let you automate most of the work. Github/gitlab also allow you to automatically create and merge branches from the UI.

    11 votes
    1. FarraigePlaisteach
      Link Parent
      Very helpful, thank you. The reason I mentioned commands is because I find it more consistent to work with regardless of what machine I’m on. Documentation is easier to read without lots of...

      Very helpful, thank you. The reason I mentioned commands is because I find it more consistent to work with regardless of what machine I’m on. Documentation is easier to read without lots of screenshots too.

      But because I do this work intermittently I have to remind myself of the commands all over again.

      5 votes
  2. [11]
    creesch
    (edited )
    Link
    Some things to consider: Pushing directly to your main/master branch will eventually cause merge conflicts. As @parsley mentioned, a common way to deal with that is by working in a separate...

    Some things to consider:

    • Pushing directly to your main/master branch will eventually cause merge conflicts.
    • As @parsley mentioned, a common way to deal with that is by working in a separate branches. You can then make so-called pull or merge requests (what they are called depends on the platform). If there are conflicts with the current main branch, most platforms (like GitHub, GitLab) will tell you there is one.
    • When you go down this road, you want to adjust the settings of your main/master branch to disallow direct pushes and disable force pushes as well.
    • You also want to keep changes relatively small, as this also keeps the chances of merge conflicts happening low.
    • If you do work on something for longer and other things have been merged, it is generally recommended to merge in main/master from time to time. It does help in avoiding merge conflicts down the line and if you do encounter them they are generally smaller.
    • Another method of avoiding merge conflicts is simply making sure you are not working on the same files/functionality.

    As far as the commands go that are involved with working in branches. You need to know how to check out branches

    • git checkout <branch_name> for an existing branch.
    • git checkout -b <new_branch_name> to create a new branch.
    • git push --set-upstream origin <new_branch_name> when pushing a newly created branch for the first time. After that is just git push.

    Merge conflicts are a bit more tricky and not something you can do by just having commands memorized. I do recommend reading up on it once you do come across the need for it. One thing though, github and similar platforms sometimes have a web interface for resolving merge conflicts. I generally avoid using it, although probably not an issue for you yet I have found it can cause issues with stuff like pipelines.

    What I do recommend is using an IDE of sorts to do the diffing, it does make things a bit easier.

    8 votes
    1. [3]
      FarraigePlaisteach
      Link Parent
      This is fantastic: the bullet points make the detail easy to read (I am not a l33t coder by any stretch). I’ll get branching to start since it seems to be an extension of what I’m already doing....

      This is fantastic: the bullet points make the detail easy to read (I am not a l33t coder by any stretch).

      I’ll get branching to start since it seems to be an extension of what I’m already doing.

      About the IDE: if I generally work from the terminal, can I still switch to an IDE when needed?

      1 vote
      1. [2]
        creesch
        Link Parent
        Absolutely! The nice thing with git is that you can quite easily switch between various tools if you want to. You can also simply do merge conflict resolving in the terminal with your favorite...

        About the IDE: if I generally work from the terminal, can I still switch to an IDE when needed?

        Absolutely! The nice thing with git is that you can quite easily switch between various tools if you want to.
        You can also simply do merge conflict resolving in the terminal with your favorite terminal editor. I just personally do not prefer to do so.

        1 vote
        1. FarraigePlaisteach
          Link Parent
          As much as I like the terminal for basic coding and scripting, things like diffs are hard to conceptualise.

          As much as I like the terminal for basic coding and scripting, things like diffs are hard to conceptualise.

          3 votes
    2. balooga
      Link Parent
      I agree that diffing from the CLI is a real pain, I avoid it whenever I can. Any tool is going to have a learning curve, and the diff tools built into IDEs will vary, but I think any GUI is...

      I agree that diffing from the CLI is a real pain, I avoid it whenever I can. Any tool is going to have a learning curve, and the diff tools built into IDEs will vary, but I think any GUI is probably going to be a better experience on the whole. If you're not coding in an IDE (which you arguably should be, but that's another discussion) it's probably overkill to fire one up just for diffs. I'd recommend a specialized program. My personal favorite is KDiff3, which is kinda fugly but FOSS and full-featured:

      • Git-compatible conflict resolution
      • Auto-merge functionality
      • 3-way diffing
      • Recursive whole-directory contents diffing
      • Toggleable whitespace/comment diff highlighting
      • Inline character-by-character diff rendering
      • Cross-platform binaries available here.

      As utils go it's basically prehistoric but it continues to be maintained and actively developed. You can configure git to use it as difftool and mergetool so it will open instead of the CLI tool by default. It's loosely "themeable" meaning you can change some colors, icons, and fonts, which isn't amazing but does help to file down some of the rough visual edges on Mac in particular.

      1 vote
    3. [6]
      skybrian
      Link Parent
      When contributing to someone else’s repo on GitHub, the workflow I’ve seen is that you create a fork on GitHub, push some changes to it, and then make a pull request. It always struck me as a bit...

      When contributing to someone else’s repo on GitHub, the workflow I’ve seen is that you create a fork on GitHub, push some changes to it, and then make a pull request. It always struck me as a bit unwieldy. Is there a different workflow for team members?

      1. [3]
        creesch
        Link Parent
        The workflow for members is indeed different. Although how much less unwieldy you find it sort of depends on how you used to do it previously. Instead of forking the entire repository (essentially...

        The workflow for members is indeed different. Although how much less unwieldy you find it sort of depends on how you used to do it previously.

        Instead of forking the entire repository (essentially making a copy) you only create a branch within the repository and work based on that. The difference you notice mostly depends on how you have contributed to github projects in the past. Because if you just did one off code contributions, this might not seem all that simpler to you. But if you make multiple contributions this removes the step of having keeping your fork up-to-date and all that.

        3 votes
        1. [2]
          skybrian
          Link Parent
          Do you have to push your temporary branch to GitHub, or can you make a pull request directly?

          Do you have to push your temporary branch to GitHub, or can you make a pull request directly?

          1. creesch
            Link Parent
            You still need to push the branch to the remote, otherwise GitHub/GitLab/Azure/Etc don't know there is a branch to begin with. Though to be honest, from all the things having to push to a remote...

            You still need to push the branch to the remote, otherwise GitHub/GitLab/Azure/Etc don't know there is a branch to begin with.

            Though to be honest, from all the things having to push to a remote isn't what I'd call unwieldy. See it as hitting save on a document you are working on.

            What can get a bit tedious is following all steps to create a pull request. Mostly the step to actually move away from your terminal, navigate to the right project on github (or whatever), etc.

            For tedious tasks like that there also is a variety of tooling available. For example, there is a really nice GitHub extensions for visual studio code (and probably other IDEs as well) that let's you do it right there for the branch you are working on.

            Even if you don't use an IDE there are plenty of options. For example, on my previous work laptop I had a little script set up that is bound to a createPR alias that would push everything to the remote and based on the last commit it also created the pull request.

            With my current job, I haven't set up anything like that. We use GitLab and in response to a push it gives a pre-filled link that when clicked opens up the merge request interface for you with a bunch of details filled in.

            1 vote
      2. Bwerf
        Link Parent
        If you have write access to the repo you would normally create a branch rather than cloning the whole repo. Other than that it's about the same.

        If you have write access to the repo you would normally create a branch rather than cloning the whole repo. Other than that it's about the same.

        1 vote
      3. FarraigePlaisteach
        Link Parent
        Oh that’s a good point. I actually did that once to add an entry to 1mb.club but I blundered through it to such an extent that I wasn’t sure how I did it in the end :) In my case now, I’m hosting...

        Oh that’s a good point. I actually did that once to add an entry to 1mb.club but I blundered through it to such an extent that I wasn’t sure how I did it in the end :)

        In my case now, I’m hosting the repo.

        1 vote
  3. [3]
    RheingoldRiver
    Link
    I'm sure there are a lot of good suggestions out there but i just want to say there is no shame in redownloading your repo, pasting the changes in, and then deleting the old copy (once you are...

    I'm sure there are a lot of good suggestions out there but i just want to say there is no shame in redownloading your repo, pasting the changes in, and then deleting the old copy (once you are certain it's working).

    Obviously this is not what you are "supposed" to do but really what you are supposed to do is write code, and git is a tool to help you write code. Don't let it rule your life.

    4 votes
    1. FarraigePlaisteach
      Link Parent
      Thanks. I did feel a bit of shame doing it that way, but as you say, in life terms it freed up a lot of time, relieved stress and got the job done.

      Thanks. I did feel a bit of shame doing it that way, but as you say, in life terms it freed up a lot of time, relieved stress and got the job done.

      2 votes
    2. parsley
      Link Parent
      It is indeed perfectly fine, but once you learn about a couple commands you really don't need to do that. Some basics here https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things do note that...

      It is indeed perfectly fine, but once you learn about a couple commands you really don't need to do that.

      Some basics here https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things do note that restore is a modern command and is not available in older versions of git.

      1 vote
  4. [10]
    Bwerf
    Link
    Not git specific, but more general common sense. Git is not magic, so the best way to avoid conflicts is to have tasks where you're not working on the same things at the same time. The more your...

    Not git specific, but more general common sense. Git is not magic, so the best way to avoid conflicts is to have tasks where you're not working on the same things at the same time. The more your tasks are in different areas the less conflicts you'll have to resolve. Plan for that.

    3 votes
    1. [9]
      FarraigePlaisteach
      Link Parent
      I think I just assumed that the technology would “handle everything”. I’ll include communication in our process in that case. I’m seriously tempted to work directly on the main branch since they...

      I think I just assumed that the technology would “handle everything”. I’ll include communication in our process in that case. I’m seriously tempted to work directly on the main branch since they will be working nights and I’ll be working days. We could coordinate that.

      1. [6]
        teaearlgraycold
        Link Parent
        What if two people rewrote the same web page from scratch? How would git know what to do? A human being is required for many merge conflicts because you need to have a deep understanding of: What...

        What if two people rewrote the same web page from scratch? How would git know what to do? A human being is required for many merge conflicts because you need to have a deep understanding of:

        • What was the original intention of this code?
        • What did person A intend given the original state?
        • What did person B intend given the original state?

        Which then lets you perform the merge resolution, which can be formulated as: How would I achieve both the goals of A and B given the original state?

        Sometimes A or B is obsoleted by the other. In that case, you can just pick one and move on. Sometimes they're both important fixes and you need to step through the logic of both implementations.

        I would recommend using a very simple workflow where you have a canonical, linear history on main and either commit directly there or rebase feature branches onto main. That way it's always clear what happens first and who needs to fix which feature branch. This will require you to understand the basics of how git commits are defined and structured so that you can visualize the distributed graph as you're performing and planning rebases.

        3 votes
        1. [5]
          FarraigePlaisteach
          Link Parent
          You know, the idea of a linear history is very appealing and something I hadn't considered. We've spoken in the last hour and we could very well end up committing directly to main and making sure...

          You know, the idea of a linear history is very appealing and something I hadn't considered. We've spoken in the last hour and we could very well end up committing directly to main and making sure not to work on code at the same time.

          1. [4]
            teaearlgraycold
            Link Parent
            You'll find that when someone else pushes to main and then you try to push it will reject your change. In this case git pull --rebase will take each commit you've made and, in order, apply them to...

            You'll find that when someone else pushes to main and then you try to push it will reject your change. In this case git pull --rebase will take each commit you've made and, in order, apply them to what's hosted remotely. If there is a conflict with any of your commits it will halt and you'll have to fix the issue.

            You can get a linear history while using feature branches. The linear aspect is almost always a result of retroactive continuity. So when it comes time to add any feature branch to main you'll rebase it onto main just like how you would between your local main and the remote main.

            Merging is, in my opinion, to be avoided. It's a shame it's the default way to combine changes. If you only rebase then the question of what happened, when, and in what order is obvious.

            1 vote
            1. [3]
              FarraigePlaisteach
              Link Parent
              So rebase is like a pull, except it steps you through the process to help you retain your own changes? It sounds like it would be used when the remote has been updated since you did your last pull.

              So rebase is like a pull, except it steps you through the process to help you retain your own changes? It sounds like it would be used when the remote has been updated since you did your last pull.

              1. [2]
                teaearlgraycold
                Link Parent
                Git is overly complex. Rebasing isn't comparable to pulling. Sorry to shake you loose from the small foundation you're starting to build. Use this image as a reference. Let's say the blue sequence...

                Git is overly complex. Rebasing isn't comparable to pulling. Sorry to shake you loose from the small foundation you're starting to build.

                Use this image as a reference. Let's say the blue sequence of dots is main. Each dot is a commit and history goes from left to right. Orange and green are two independent feature branches, each from different people. Orange's base is the left-most blue dot. Green's base is the second blue dot. Re-basing means you redefine the base for a series of commits.

                So if you rebase orange in the reference image onto the right-most blue dot now you have 4 blue dots followed by 2 orange dots. Green in this scenarios is unaffected.

                Confusingly, this does not affect the definition of main, just the definition of the orange branch. In the world of git you would do the following to incorporate orange into main:

                git checkout orange
                git rebase main # Append orange to main
                
                git checkout main
                git reset --hard orange # Set main = orange
                

                Github does offer a "Rebase and merge" option in pull requests which does all of this for you.

                4 votes
      2. [2]
        Pistos
        Link Parent
        Honestly, even when I'm working solo, I still use branches. You can keep features and development tasks separate and flip between them cleanly. You can't predict interruptions. (I guess you could...

        Honestly, even when I'm working solo, I still use branches. You can keep features and development tasks separate and flip between them cleanly. You can't predict interruptions. (I guess you could use git stash in a pinch.)

        1. FarraigePlaisteach
          Link Parent
          Very interesting, thank you! I’d forgotten about the stash option and hadn’t considered using a branch for solo dev. Usually I just edit text on a page or update a price, so those are small jobs....

          Very interesting, thank you! I’d forgotten about the stash option and hadn’t considered using a branch for solo dev.

          Usually I just edit text on a page or update a price, so those are small jobs. But I’ll need to do something more complex later.

  5. [7]
    pete_the_paper_boat
    Link
    I usually git pull --rebase and solve any conflicts that come along. Keeping both your developments in sync regularly also prevents a lot of hassle.

    I usually git pull --rebase and solve any conflicts that come along.

    Keeping both your developments in sync regularly also prevents a lot of hassle.

    2 votes
    1. [6]
      FarraigePlaisteach
      Link Parent
      I actually saw that frequently posted online, but the first conflict I had wasn’t resolved with that for some reason. It’s ridiculous because it was small amounts of changes on just a few files....

      I actually saw that frequently posted online, but the first conflict I had wasn’t resolved with that for some reason. It’s ridiculous because it was small amounts of changes on just a few files.

      In the end I copied the files from my branch into a directory on my local filesystem, nuked the conflicting branch and then checked the code out again to re-add my changes. It took ages.

      1. [5]
        creesch
        Link Parent
        Rebasing will not resolve conflicts. What it does is basically merge in everything from the remote and then puts your commits on top of the tree.

        Rebasing will not resolve conflicts. What it does is basically merge in everything from the remote and then puts your commits on top of the tree.

        4 votes
        1. [4]
          FarraigePlaisteach
          Link Parent
          I’m still figuring out what exactly a remote means. It seems it can be absolutely any repo or branch, on a local machine or a server. Is it just a pointer to any repo/branch I choose? My source:...

          I’m still figuring out what exactly a remote means. It seems it can be absolutely any repo or branch, on a local machine or a server. Is it just a pointer to any repo/branch I choose?

          My source: https://stackoverflow.com/questions/20889346/what-does-git-remote-mean

          1. [3]
            creesch
            Link Parent
            It is in the name. You have your local repository and the remote central one. Often hosted on GitHub, Gitlab or some similar platform. So when you use the pull command, for the branch you...

            It is in the name. You have your local repository and the remote central one. Often hosted on GitHub, Gitlab or some similar platform.

            So when you use the pull command, for the branch you currently are in, you are pulling everything from the remote version of that branch to your local branch. In the same way, if you use the push command, you are pushing your local changes to the remote.

            1 vote
            1. [2]
              FarraigePlaisteach
              Link Parent
              Crystal clear, thank you. Where were you when that SO thread was started 😄

              Crystal clear, thank you. Where were you when that SO thread was started 😄

              1. creesch
                Link Parent
                I should say I simplified it a bit ;) But it does cover the basics for what most people will be dealing with.

                I should say I simplified it a bit ;) But it does cover the basics for what most people will be dealing with.

                1 vote
  6. [2]
    Pistos
    Link
    The actual git commands you use to resolve the conflicts will be pretty mundane. The hard part lies in sorting out the conflicts themselves in your IDE/editor in a way that won't break things or...

    The actual git commands you use to resolve the conflicts will be pretty mundane. The hard part lies in sorting out the conflicts themselves in your IDE/editor in a way that won't break things or inadvertently undo parts of a teammates' work.

    Now, with such a small team size, you probably don't want to bog yourselves down with fancy, enterprise-level project management overhead, but you'll probably at least want to do the following:

    • work in separate topic branches (per task)
    • keep branch scope small
    • keep branches short-lived
    • coordinate with each other to avoid working in the same areas at the same time, when possible

    If it is clear that one teammate's next task depends on the work being done by the other, an option to consider (case by case) is to make a grandchild branch off the work in progress (rather than master/main).

    2 votes
    1. FarraigePlaisteach
      Link Parent
      Another point on communication too. Noted, thank you.

      Another point on communication too. Noted, thank you.

  7. [2]
    waaffsora
    Link
    There's also https://ohshitgit.com/ which helps you fix some common scenarios. I've got it bookmarked on my computer.

    There's also https://ohshitgit.com/ which helps you fix some common scenarios. I've got it bookmarked on my computer.

    2 votes
  8. DeepThought
    Link
    Be really consistent about rebasing. I do it as my first task of the week every Monday on all my work in progress branches.

    Be really consistent about rebasing. I do it as my first task of the week every Monday on all my work in progress branches.

    1 vote
  9. [3]
    skybrian
    Link
    Here are some unconventional alternatives: If you’re working in the same office at the same time, maybe try pair programming? Then you don’t need code reviews since you’re reviewing each others’...

    Here are some unconventional alternatives:

    If you’re working in the same office at the same time, maybe try pair programming? Then you don’t need code reviews since you’re reviewing each others’ work as you do it, and only doing one thing at a time.

    Another approach we used to use was to have a physical token, where when you’re ready to commit, you take the token, and then nobody else can commit while you’re doing it. We would also have a sound effect set up to play so that when someone commits a change, you would notice and pull right away, so you’re always up to date.

    Then you don’t need branches at all. Just work on main. You do need good tests. You don’t need a continuous build if you’re rigorous about running tests before you commit, though it might be nice to avoid “it worked on my machine” problems.

    There are probably ways to do this remotely. Maybe you have an always-on video chat, or something like that, and you get the other person’s attention before committing? As others have said, knowing what the other person is working on and doing something else really helps, too.

    1. [2]
      FarraigePlaisteach
      Link Parent
      Some great ideas there. I've decided not to use branches at first, at least. We're going to phone each other before working on any code. It will be very intermittent anyway, with weeks between any...

      Some great ideas there. I've decided not to use branches at first, at least. We're going to phone each other before working on any code. It will be very intermittent anyway, with weeks between any changes.

      I forgot that the pull is necessary when more than one person is involved. I'll need to explain that to them.

      The thing about tests is that it's HTML we're working on. There are no compilers to give warnings or feedback. So I think the only tests we can have need to be manual and visual.

      1. skybrian
        Link Parent
        When you pull, the merge might fail and then you have to deal with the consequences. Sometimes it’s easier to redo the change than unscramble it. This is a good reason to commit more often when...

        When you pull, the merge might fail and then you have to deal with the consequences. Sometimes it’s easier to redo the change than unscramble it. This is a good reason to commit more often when you can, so there’s less work to redo.

        Yes, it’s mostly visual, but there are HTML checkers that can be useful for detecting bad structure. Some might check that links work, too?

        1 vote
  10. time_and_tildes
    Link
    I realized recently that conflicts are really straightforward. You go "git merge master <your branch>" and a file has conflicts. Open the file in your editor, and it shows you "their" code, and...

    I realized recently that conflicts are really straightforward.

    You go "git merge master <your branch>" and a file has conflicts. Open the file in your editor, and it shows you "their" code, and "your" code.

    All you need to do it remove all the ===== and >>>> from the file, put it into the state you want, and then git add <file that had conflicts>, git commit.

    What happens when the conflicts are super confusing? My approach is to go to my PR in Github, and look at the last clean version I pushed there. What does it look like? What were the changes I was trying to introduce? What changes is the other user trying to introduce?

    My reviewing that "last clean state", I often find the merge conflicts much easier to understand.

    Again, a merge confict is just git saying "I'm not sure what this file is supposed to look like" and then you go "here, lemme clean that up for you."