CyberWolf
CyberWolf

Reputation: 91

Git: Make auto-merge files into conflicted ones

I have a github repository that has some code. Another person forked that repository and cloned it. They changed some code. I want to replace all of my code with their code (accept all their changes). I've run

git pull -X theirs https://github.com/epicmanmoo/JavaStudyBuddies.git master

which works fine for conflicted files, it removes my code, leaving theirs. The issue is it is only so for files with ===== >>>>> <<<< stuff in them (conflicted files).

The problem are files like Column.java which are not marked as conflicted.

Column.java:

package javastudybuddies.discordbots.entities;

public enum Column {
    NAME("name", "name", "projects"),
    DESCRIPTION("description", "description",  "projects"),
    STATUS("status", "status", "projects"),
    COMPLETED("completed", "completed", "projects"),
    DIFFICULTY("difficulty", "difficulty", "projects"),
    TYPE("type", "type", "projects"),

    USERNAME("username", "username", "users"), LEVEL("level", "level", "users"),
    COUNTRY("country", "country", "users"), TIMEZONE("timezone", "timezone", "users"),
    AGE("age", "age", "users"), IDSTRING("id_string", "id", "users"), TAGSTRING("tag_string", "tag", "users"),
    GOAL("goal", "goal", "users"), TECH("tech", "tech", "users");

    public final String databaseLabel;
    public final String userLabel;
    public final String table;
...

And in https://github.com/epicmanmoo/JavaStudyBuddies/blob/master/src/main/java/javastudybuddies/discordbots/entities/Column.java

it's

public enum Column {
    NAME("name", "name", "projects"),
    DESCRIPTION("description", "description",  "projects"),
    STATUS("status", "status", "projects"),
    TYPE("type", "type", "projects"),
    COMPLETED("completed", "completed", "projects"),
    DIFFICULTY("difficulty", "difficulty", "projects"),

    USERNAME("username", "username", "users"), LEVEL("level", "level", "users"),
    COUNTRY("country", "country", "users"), TIMEZONE("timezone", "timezone", "users"),
    AGE("age", "age", "users"), IDSTRING("id_string", "id", "users"), TAGSTRING("tag_string", "tag", "users"),
    GOAL("goal", "goal", "users"), TECH("tech", "tech", "users");

    public final String databaseLabel;
    public final String userLabel;
    public final String table;
    ...

See the difference? In my version TYPE is the last, in his version TYPE is in the middle, so that doesn't even register as a merge conflict.

For debugging I've run the usual version of git pull (without -X theirs):

git pull https://github.com/epicmanmoo/JavaStudyBuddies.git master

That version, too, does this:

public enum Column {
    NAME("name", "name", "projects"),
    DESCRIPTION("description", "description",  "projects"),
    STATUS("status", "status", "projects"),
    TYPE("type", "type", "projects"),
    COMPLETED("completed", "completed", "projects"),
    DIFFICULTY("difficulty", "difficulty", "projects"),
    TYPE("type", "type", "projects"),

    USERNAME("username", "username", "users"), LEVEL("level", "level", "users"),
    COUNTRY("country", "country", "users"), TIMEZONE("timezone", "timezone", "users"),
    AGE("age", "age", "users"), IDSTRING("id_string", "id", "users"), TAGSTRING("tag_string", "tag", "users"),
    GOAL("goal", "goal", "users"), TECH("tech", "tech", "users");

    public final String databaseLabel;
    public final String userLabel;
    public final String table;

and doesn't include the file in

git diff --name-only --diff-filter=U (list of conflicted files)

while another file, say, ProjectsBot.java is marked as conflicted and when I open it, I see this

<<<<<<< HEAD
public class ProjectsBot extends ListenerAdapter  {
    private enum Command  {
=======
public class ProjectsBot extends ListenerAdapter {
    private enum Command {
>>>>>>> b858d8ab9c06ee2645a0dda716d9b5e14a6db11d
        CREATE_PROJECT("create project \"name\", \"description\", \"difficulty\""),
        JOIN_PROJECT("join project 'name\"");

        public final String syntax;

<<<<<<< HEAD
        Command(String syntax)  {
=======
        Command(String syntax) {
>>>>>>> b858d8ab9c06ee2645a0dda716d9b5e14a6db11d
            this.syntax = syntax;
        }
    }

So, my problem is, if not for files like Column.java,

git pull -X theirs https://github.com/epicmanmoo/JavaStudyBuddies.git master

would have been the ideal solution - it automatically accepts their changes and I can push them right away. Is there a way to make it work for files like Column.java? Make them look somewhat like this inwardly:

>>>>HEAD
    NAME("name", "name", "projects"),
    DESCRIPTION("description", "description",  "projects"),
    STATUS("status", "status", "projects"),
    COMPLETED("completed", "completed", "projects"),
    DIFFICULTY("difficulty", "difficulty", "projects"),
    TYPE("type", "type", "projects"),
=====
   NAME("name", "name", "projects"),
    DESCRIPTION("description", "description",  "projects"),
    STATUS("status", "status", "projects"),
    TYPE("type", "type", "projects"),
    COMPLETED("completed", "completed", "projects"),
    DIFFICULTY("difficulty", "difficulty", "projects"),
>>>>theircommit

so that the -X theirs option could replace everything above ===== with everything below it?

Upvotes: 2

Views: 244

Answers (1)

torek
torek

Reputation: 488053

The short answer is no, you can't do that.

The long answer is that you can do that, but you must write your own merge strategy: not just a merge driver, and not a strategy option like -X theirs, but a strategy like -s my-new-strategy, which will have Git invoke git-merge-my-new-strategy, which means you have to have a command spelled that way in your $PATH. Writing a strategy is very hard (one way to start would be to clone a copy of Git, and then copy the existing recursive strategy to a new one and write a bunch of new C code in your new strategy, using the recursive strategy as the starting point).

If there are not too many files involved, I would recommend instead using:

git checkout <branch>
git merge --no-commit <options> <commit-specifier>

then resolve each file manually, using whatever technique you find best. The --no-commit means that regardless of whether Git thinks the merge succeeded or not—with -X theirs it will usually think the merge succeeded—the merge will stop as if there had been conflicts, which lets you edit and git add files.

When you're all done—and it's useful to use git diff HEAD or git diff --cached HEAD now and then to compare what you have in the work-tree (first command) or index (second command) to the HEAD commit, to see what will be different in your final merge result—run:

git merge --continue

to commit the merge, or:

git commit

if your Git is too old to support git merge --continue. (The git merge --continue command just runs git commit, but makes there that there is a merge to commit before running git commit, so it acts as a check to make sure nothing else has gone wrong. For instance if you accidentally do a git reset while you're resolving things, you may have removed the merge state. In this case, the final git commit would just make an ordinary non-merge commit, instead of making a merge commit. That's recoverable too, but it's more annoying to fix it than it is to not make the mistake in the first place—so git merge --continue is a good thing.)

Upvotes: 1

Related Questions