What makes Evil Merges in Git … Evil?
The Git Glossary defines an “Evil merge” as…
a merge that introduces changes that do not appear in any parent.
To me that sounds like any merge with a non-trivial conflict. The sort of conflict that needs hand editing, rather than simply taking yours or theirs for each hunk. That’s pretty common, so why is it evil? To be honest I’m just guessing, but one feature of git that slowed me down considerably when trying to find out who had removed some lines from the file is that git log pretends that merge commits don’t change any files. Try
git log --stat
on a repository with merges. It says merges never change any files, even though they really do. Adding the options
-m to git log suddenly shows merge stats for files which had to change in a merge. When you do a search with git pickaxe the behaviour is the same; the search won’t find anything in merge commits unless you use one of those options.
To me that sounds totally crazy, but perhaps it makes some sense if you’re interested in where changes were first introduced. In that case you’re interested in the initial commit which created the change, not the merge-commit which is a later artifact of the revision control process.
However it hides the contents of Evil Merges by default and to my mind that is Git rather than the Merge being Evil.
I’ve just used Perforce for the first time on a big project, and it was … ok. Feature-wise I’d use Git, Mercurial or Accurev in an instant instead, but I suspect that both the project’s history and scale were the reason behind using Perforce. Unlike all of the above Perforce has per-file versioning, rather than per-repository versioning. Commits to the repository are a sequence of changelists, each of which, presumably, must internally list out the revisions of each file in the repository. It didn’t seem to be widely known how to query Perforce to ask local workspace what the current changelist you have is. A bit of digging reveals that:
p4 changes -m1 /...#have -s submitted
Does the trick.
In Perforce you can update any file to any revision of that file, separate from updating to a changelist. In that case I’m not sure what the command will give back, but clearly a single changelist number can’t fully describe a local workspace where even a single file has been updated separate from a changelist.
After using Git for a while I’m finding the most confusing aspect of it is rebasing. The git-rebase man page says “forward-port local commits to the updated upstream head”. Hmm. Thankfully James Bowes posted a solid explanation of the basic principles.
Which leaves me wondering about corner cases. I have local commits which I’ve submitted as patches upstream. Now I’ll be fetching my changes from upstream, one of which has an updated comment and the other has had minor edits applied. How does Git handle this? In a good way! For commits which are already upstream, with identical content, even with differing comments, rebasing removes the local commit. That’s exactly what you want. For example simply specify the upstream branch to rebase against:
git rebase origin/master
In cases where the commit contents have received minor edits the rebase will pause. Assuming you are happy to drop the local commit tell Git to do exactly that:
git rebase --skip
or if there’s any doubt tell Git to rollback to before the rebase started:
git rebase --abort