Rebase definitely satisfies my OCD and makes everything look pretty, but it's actually worse.
Rebase definitely satisfies my OCD and makes everything look pretty, but it's actually worse.
The concept of "git bisect" is inherently predicated on a simple linear history consisting of commits that were applied on top of each other. We are searching for the bad one which flips the test case from "good" to "bad".
If you throw an ugly graph into it and merges, it's not even comprehensible. Say that instead of git bisect we just go backwards, commit by commit, starting at HEAD. Oops, we encounter a merge commit: which way do we go now to find the breakage? Parent[0], or parent[1]? Or [2] ...? What is the correct traversal order? Do we go down [0] first? At which commit do we backtrack and try going into [1]?
There is absolutely no issue with git bisect over a repository where you have the unmodified upstream commits, and your own local ones rebased on top of them. It nicely finds the bad commit in one or the other.
Now of course the rebase rewrites your code by merging it (in the real sense of the word). If you discovered the bug yesterday (and it's in your own commits), then you rebased this morning, you're now chasing that bug over different commits that have been subject to auto-merge and maybe even manual conflict resolution. Oh well; yesterday was yesterday! Repro the bug with the newly rebased repo and bisect away.
Or, here is an idea: if you're chasing a bug, don't fetch anything and don't rebase! Right? If you've repro-d a bug, you don't want to start changing code, so the last thing you want is to bring in reams of changes from other developers. I've never rebased while chasing a bug with git bisect; I stopped what I was doing until I found it, in the nice linear history as it existed at that point.
If external forces cause you to drop what you're doing and rebase while you have some bug, what you can always do is "git tag bug-hunt" to set a bookmark where you were. Do what you have to do, and then when you pop your stack, return to that tag and debug from there. You can find the original bad commit. If it's in your uncommitted work, you can cross-ref it to the latest rebase. (There is a chance it might not exist any more; e.g. upstream changes made some of your local code obsolete, so you threw them away in the rebase, and the problem was in those discarded changes.)
What you're thinking of as a "simple linear history" is actually not. Notice that when you rebase there are new hashes for each of your commits.
If you're rebasing your branch with master and resolving merge conflicts along the way, you're going to get bit eventually. Even if you're somehow not dealing with merge conflicts, you're losing your ability to look at the git log and figure out when the last time you synced up with master was.
Long running branches are a bad idea. Try not to do that.
Not everything in a long running branch can be merged; selective things will end up cherry-picked (which is a generalization of rebase). Suppose the trunk maintainers like 75% of what was done in the branch. They might go through the commits one by one and take what is relevant.
When you merge a branch, and want to follow a good SCM process, you must treat it as a closed topic; do not commit anything more to that branch. Start a new branch, even for a closely-related continuation of the same work.
The correct way to merge a branch is to rework every single commit onto the new baseline. That is true whether that rework is represented as a merge, or a cherry-pick. Therefore, we can't avoid the issue that every change turns into a new object with a different hash.
What we can do is put tracking information in the commit message to correlate these together. The Gerrit review system uses a Change-Id: line for this. Each commit has random ID which is not a content digest; it stays the same across cherry picks.
If I'm debugging a problem on trunk T, across some commits that came from a branch R, I do not care what those commits looked like in R. Only in a very rare case will I be interested how a change looked like there. I want every commit that came from R to be reworked on T, as if it was originally developed on T. If I find the problem and have a hunch that it was introduced during the merge, then I might out of curiosity find the commit's cousin in the original R branch just to see how it looked like there. Other than that, I'm shipping T, not R, so that's what I'm debugging.
See https://mirrors.edge.kernel.org/pub/software/scm/git/docs/gi... for more info
What, specifically, do you mean by "something goes wrong later"? Like a full outage of some kind? A production bug? A mistake according to the design spec? A failed test in CI? A failed human QA validation? What are we talking about here?
>it's much easier to trace where the problem is.
Furthermore, why would the git commit structure have anything to do with debugging any type of issue, regardless? Isn't debugging mostly comprised of combing through logs, adding random print statements to console output, and using an IDE debugger? Or some kind of more advanced tracing/profiling tools? Are you debugging by hunting in commit histories and Jira tickets or something?
Or are you talking about blaming, not "tracing where the problem is", but "tracing who the problem is"?
That is not realistic because, firstly, a very specific test can (and should) be used for git bisect. You're not just testing for "software fails", but "very specific case fails in a specific way".
If some intermediate issues prevent you from testing the specific test case (you're not able to call "good" or "bad"), you can tell git bisect to skip that commit.
Shit happens! Bug was introduced a month ago, but two weeks ago, a range of commits happened that don't even build in your environment. git bisect will hit that.
The article also glosses over the possibility that git bisect goes back farther than the bad commit. Commits that are not testable can occur earlier or later than the one we are looking for.
Basically the article is just a new version of the old meme "merging is dangerous". except that it doesn't make any sense, since both "git merge" and "git rebase" textually merge code.
One solution is, if you can run your tests with e.g. `make test`, you can run `git rebase --exec "make test"` which will run your test suite against every commit being rebased. I've only done it a handful of times, and because of how long it takes I'd only run it after finishing a rebase that wound up needing resolutions, instead of doing it up front and having to wait for the tests to finish at each commit before potentially resolving another upcoming conflict.
If you are very paranoid about breaking commits being pushed, disallowing rebase isn't enough: any dev could simply write a commit with a breakage. You'd have to set up pre-commit hooks to run your tests, or a pre-push hook to run `git rebase --exec "make test"`. TIL there is also a post-rewrite hook, which sounds like it solves exactly your worry with rebase, but as I mentioned, introduces long delays between rebase commits/resolutions–I'd probably forget what I was doing during each test run, haha.
I generally follow your strategy myself as long as it's something small, but I think there's this meme out there that having merge commits in your branch is somehow shameful, when in reality it should celebrated as an accurate history.
I disagree with git as a bug tracking tool, it's a distributed code repository. It's closer to google docs than it is to an IDE debugger or a system log.
I also disagree with the idea that "tracking down the source of bugs" has anything to do with quality or a "safety net."
Can someone please explain to me where this kind of "commit blame" culture comes from? And what the point is? What is the next step that happens after you identify the "offending commit"? I don't understand why you wouldn't just open a new PR to fix the bug and move the fuck on with your life? What business value is there in this type of exercise to specifically identify which git commit "caused a bug"?
Perhaps someone has a list of criteria that can help you pick between the 2 approaches, or perhaps both merge/rebase are valid at all times, but there is a certain set of rules to evaluate when choosing which to use?
When we rebase a public feature branch and merge it into a mainline, we nicely have two versions of that.
Basically, rebase and merge could be reconciled like this: we could merge a feature branch by iterating over it, and invidiually doing a git merge for every commit. Then that would be a rebase. Except that every commit would have an additional parent pointer which cross-references it to the original branch.
I.e. good rebase:
B0---B1---B2
/
--- A --- B ---- C --- D --- B0' --- B1' --- B2'
good merge (exact same thing, just with parent pointers): ---------------B0-----B1-----B2
/ \ \ \
--- A --- B ---- C --- D --- B0' --- B1' --- B2' ---
typical silly git merge: --B0-----B1-----B2
/ \
--- A --- B ---- C --- D --- B{0+1+2}
still, that cross-referencing parent info in the second example above is not that useful. It's still making a salad out of the git graph. And is not two-way navigable, either. (Say I'm looking at B1 and want to locate B1'.)It's better to have something in the commit messages to track changes that get cherry picked in multiple places. The Gerrit review system has a Change-ID: line with a 160 bit random ID, for instance. All rebases/picks of the same change carry the same ID, which correlates them to the same review item.