I keep running into the following problem and I'd like to know whether there is an easy solution to it using existing git commands or whether I'll need to write a custom merge driver.
Here's an example to demonstrate my problem: Say the master
branch of my repo has the following file:
$ cat animal.hpp
#include <string>
class Animal
{
public:
virtual std::string say() const = 0;
};
I create a new branch called mybranch
and make the following change:
diff --git a/animal.hpp b/animal.hpp
index e27c4bf..3bb691a 100644
--- a/animal.hpp
+++ b/animal.hpp
@@ -5,3 +5,13 @@ class Animal
public:
virtual std::string say() const = 0;
};
+
+class Dog : public Animal
+{
+public:
+ virtual std::string
+ say() const override
+ {
+ return "woof";
+ }
+};
In the meantime, someone else adds the following, very similar change to master
:
diff --git a/animal.hpp b/animal.hpp
index e27c4bf..310d5a7 100644
--- a/animal.hpp
+++ b/animal.hpp
@@ -5,3 +5,13 @@ class Animal
public:
virtual std::string say() const = 0;
};
+
+class Cat : public Animal
+{
+public:
+ virtual std::string
+ say() const override
+ {
+ return "meow";
+ }
+};
Now when I try to merge mybranch
with master
(or rebase mybranch
onto master
), I get a conflict:
$ cat animal.hpp
#include <string>
class Animal
{
public:
virtual std::string say() const = 0;
};
<<<<<<< HEAD
class Dog : public Animal
=======
class Cat : public Animal
>>>>>>> master
{
public:
virtual std::string
say() const override
{
<<<<<<< HEAD
return "woof";
=======
return "meow";
>>>>>>> master
}
};
The correct resolution to this conflict is:
$ cat animal.hpp
#include <string>
class Animal
{
public:
virtual std::string say() const = 0;
};
class Dog : public Animal
{
public:
virtual std::string
say() const override
{
return "woof";
}
};
class Cat : public Animal
{
public:
virtual std::string
say() const override
{
return "meow";
}
};
I've created the above resolution by manually editing the file, duplicating the code that is common to the two classes, and removing the conflict markers. But I think git should be able to create this resolution automatically, given that it only needs to add all the lines that were added in one branch, followed by all the lines that were added in the other branch, without deduplicating common lines. How can I make git do this for me?
(Bonus points for solutions that allow me to customize the order - Dog
first or Cat
first.)
The only way to "accept both" would be to just re-stage (marking as resolved) the conflicted files without resolving them ( >>> and <<< would be there still), but your result couldnt be compiled or executed. And this is terrible practice, even if you make commits later to resolve.
There is a thing called a union merge:
$ printf 'animal.hpp\tmerge=union\n' > .gitattributes
$ git merge --no-commit mybranch
Auto-merging animal.hpp
Automatic merge went well; stopped before committing as requested
In this particular case, it actually achieves the desired results. However, in many cases it can misfire rather badly, and it never fails (I mean it never considers itself to have failed, even if it produced a nonsense result), so it's not really wise to set it up in .gitattributes
after all (unless you have a really rare case).
Instead, it's better to invoke it after the fact, using git merge-file
:
$ git merge --abort
$ rm .gitattributes
$ git merge mybranch
Auto-merging animal.hpp
CONFLICT (content): Merge conflict in animal.hpp
Automatic merge failed; fix conflicts and then commit the result.
$ git show :1:animal.hpp > animal.hpp.base
$ git show :2:animal.hpp > animal.hpp.ours
$ git show :3:animal.hpp > animal.hpp.theirs
$ mv animal.hpp.ours animal.hpp
$ git merge-file --union animal.hpp animal.hpp.base animal.hpp.theirs
Make sure the result is what you wanted, then clean up a bit and add the final merged version:
$ rm animal.hpp.base animal.hpp.theirs
$ git add animal.hpp
and you are now ready to commit the result. Edit: Here's the result I got (in both cases):
$ cat animal.hpp
#include <string>
class Animal
{
public:
virtual std::string say() const = 0;
};
class Cat : public Animal
{
public:
virtual std::string
say() const override
{
return "meow";
}
};
class Dog : public Animal
{
public:
virtual std::string
say() const override
{
return "woof";
}
};
Git can't handle that kind of merge for you in typical workflows. That's why it's asking you to intervene. It's trying very, very hard not to lose changes from either side, but it doesn't really know how you want to resolve it or how you want to fix it.
You should look into a merge tool to help aid you with this. Alternatively, you can do this manually and remove the Git merge conflict lines, and handle the merge of these two files yourself.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With