Take the pain out of Git conflict resolution: use diff3 (2017)
blog.nilbus.com
blog.nilbus.com
Their diff tool also has a magic-wand icon, which resolves trivial conflicts automatically. I still wonder why git doesn't do that by default... It's such a pain to resolve conflicts without that feature.
Also, you get syntax highlighting, structural editing, help, definition quick-lookup and similar IDE features available, while working on the resolved version of the code, which is syntactically correct usually, because there are no explicit, textual conflict markers in the resolved text.
I’d be interested to know how they solve it, I suspect they run some language-aware logic to do the merge on a given hunk.
This write-up is deep and super comprehensive:
https://www.eseth.org/2020/mergetools.html
`git config mergetool.hideResolved true` enables the modern git mergetool behavior.
EDIT: derided is a strong word :) just good to learn the command line version before moving to shortcuts
It's not hard to fix that stuff manually, but I think there's just a bit more that IntelliJ could do to make this sort of merge smoother.
I'd rather use KDE software though, since my desktop environment of choice is Plasma. I haven't found the edition-in-place feature in KDiff3. Is it there? Kate also being my code editor of choice, that would be amazing. If it's not there, how do you solve merge conflicts with KDiff3 if you don't have editing in place?
2-way, 3-way and folder diff, in-place editing, it has all I need.
Open source is a helluva drug :)
(Is it just me, or is it not a social norm to treat code reviews like interrupts? I do that; I have Github connected to Slack and treat "I need my code reviewed" at the same level of priority as something like a production outage. It takes 5 minutes and unblocks an entire developer. But that favor seems rarely returned to me. I can usually get a fast review if I whine and nag, but it should just be automatic. And this isn't an artifact of any particular job, it's been like that at every job. I guess we could just stop doing code reviews, but they are pretty valuable for fixing dumb mistakes and sharing knowledge. But I digress...)
Of course that means that we might need to rebase and that could hit conflicts. With diff3 style rebase conflicts are generally [1] easy to understand. When a commit does not apply cleanly the diff3 style output gives you 3 sections:
1. What the code looks like after the previous commit that has already been rebased to the new branch under work
2. What the code looked like in the original branch
3. What it looked like 1 commit later in the original branch.
Now the question the human is: You (or some one else) changed it from 2 to 3 in the past. Now your code is not 2 but 1. How do you introduce an equivalent change from 1 to something that works like 3?
Once I have learned that principle I could no longer understand why diff3 is not the default or how anyone can survive without it.
[1] Of course there can always be tricky changes. If an automatic diff tool choses unlucky alignments the result is not ideal for humans. But diff3 does not make things worse and generally it does not happen that often.
Edit: A couple of iterations needed. Don't try to type somewhat complex comments on your phone...
Hmm. I've never really liked this pattern. We never use fast-forward merges for anything other than bringing a release branch forward to track the "current release" state[1] (the old HEAD gets renamed to a stable branch name with the version number in its name).
Benefits as far as I'm concerned:
- the merge commit can record information about the topic as a whole (e.g., the MR number, `Acked-by` and related reviews, etc.)
- it records the merge as a distinct event
- reverting a topic is just a single revert command of the merge itself (unless there's some magic to divine what sequence of commits is a "topic" in linear history these days?)
- backporting branches is just "branch off of the old release, merge into all relevant branches" instead of individual rebases and tandem merge requests
[1] This is possible because we keep "the primary branch can reach all branches" as a property as well. We do this with `-s ours` "syncup" merges whenever a non-primary branch is merged into. This is, of course, all done at once by merging a topic into all target branches, performing syncup merges and pushing with `--atomic` to avoid race conditions.So we would need to update our process documentation and our CI to allow merge requests without an issue number. And git log —first-parent would become less useful.
I'm a process wonk, but these checkbox items are still the bane of everyone. It'd be far better if such information were more structured. I'd recommend migrating to git trailers if possible (see `git-interpret-trailers(1)`).
While I'm being shameless anyways, features we have in our robot (for merging):
- renaming the source branch in the message (MRs from `master` is not uncommon) - treating the target branch as having a different name (so that we can use `refs/heads/release` for "latest release" while future-proofing its merge commit mentions to be `release-X.Y`) - pulling a block of text from the MR description to add to the merge commit - putting `+1` comments, reactions, and other review information in the merge commit as `Acked-by: …`, `Reviewed-by: …`, etc. - merging to multiple branches at once and synchronization (see my prior comment in this thread)
The primary deployment tool (there's a library for much of the mechanisms): https://gitlab.kitware.com/utils/ghostflow-director
Personally I am mostly skeptical of more tools. Some developers have challenges to understand how the basic stuff works. While automation can reduce manual work and mistakes it adds also complexity and adds new points of failure.
Of course there are always exceptions of truly useful tools you don't want to miss. It just depends where to draw the line.
Of course it also depends on the size of the organization and the teams. We have nobody even remotely dedicated to tools, "ordinary" developers maintain them as "side jobs".
Even this tool is a "side job", but it did have focused development to get it off the ground. We were migrating from a combo of patched Gerrit and gitosis with custom server side commands to implement these things before. In 2015, no forge was even close to having these things, we had to implement it ourselves. Neither Gitlab not GitHub had a good enough API then either, but we could patch and contribute to Gitlab at least. There are other workflow decisions made by Gitlab as well that I prefer too anyways.
In any case, I'd prefer to solve several smaller merge/rebase conflicts rather than one large one.
There is also a command git rerere that records how you resolved a conflict and lets you do it repeatedly the same way. A colleague has tried it and said it worked nicely (not sure whether it was exactly the case yiu describe). I suspect he spent many hours on learning it and I am not convinced it has paid off for him yet. I haven't had the feeling that I urgently want more automation. I just run my git diff --ours (and occasionally also --theirs) to make sure I don't make stupid mistakes during rebasing. And in the end of course git diff @{u}.
But even diff3 is missing information. Lets say the code started as A, you changed it to B. Then you rebase and `master` is C. You'll get basically "It's C now! But you changed it from A to B."
That's not enough information. Why was it changed to C? I don't know how to update my diff without that information. Really you need to see the change that changed it to C too.
There's so much scope for an advanced conflict resolution tool. Git gets it wrong in so many situations where you could do better.
It seems even more complicated to track 3 context points in time rather than 2.
I'm open to changing my mind, but would need reasons. Botched rebases aren't a common issue for me (though merge conflict resolution can be unpleasant).
Of course, if seeing my code and their code works for you, then by all means keep doing what works.
diff3 has another advantage in the general case as well since it allows you to better understand if something outside of the hunk you're applying also needs to be changed when backporting something.
You know from the regular two-part conflict marker that developer A wants the code one way, and B wants it some other way. Often that is quite easy to understand and resolve.
But sometimes it's useful to know the original: what is it that A and B started with, to produce those divergent sections of code?
That may be helpful.
It might not. For one thing, suppose the rest of the code merged. That means that in the rest of the code you don't see anything original.
You could end up with:
<<<<<<<
new_function_x();
========
new_function_y();
||||||||
function_that_no_longer_exists();
>>>>>>>>
OK, the original was a function that no longer exists; and since this is the only conflict, you can't find that function anywhere unless you go digging into history out-of-band.A and B both deleted the function, and that didn't conflict. Nice to know, but the definition of it isn't present to know what it does.
In other kinds of situations, it may be useful.
The reason no-tool is better is that you don't have to read files in whatever order kdiff3 shows them to you -- they're just files, and you open them.
It you merge a file in two commits A and B, it would show version A, version B, the common ancestor, and the resulting merged file in four different views.
xxdiff could do that.
But I fall back to GUI only in nasty cases. Over 90% I feel comfortable with diff3 formatted output by git.
Meld certainly does allow to manually align lines. But sometimes I have struggled when doing several alignments in a single diff. Not sure whether kdiff3 would do better or it's just user error.
Where meld certainly fails is huge files. Its performance can be horrible. But that affects generated files like logs, not source you would have in git.
[as far as I understand this is similar in philosophy to a diff3. True?]
And while searching a bit, it seems that a similar UI is planned for VScode later in May 2022. Cf https://github.com/microsoft/vscode/issues/37350
[Btw, a comment mentions that Sublime Merge already has a UI similar to the one in IntelliJ]
But git diff3 style being textual format in one column it sounds surprising that a GUI would use the same approach.
git pull --rebase can be used to get local changes on top of new remote work.
But just deleting (or renaming the branch) and checking out again is all that is necessary if you end up in a messed up merge.