I kind of like rebasing
rednafi.com
rednafi.com
I, too, much prefer a rebase-heavy workflow. It allows me to both have a dirty "internal" history and clean up for publication.
As a side-effect, it also makes me comfortable having a mostly linear-history for what I publish, as opposed to a many-branched, merge-heavy one, which I dislike, and makes history confusing.
I reject the argument that a no-rebase, merge-only history "preserves the true history of how commits were created", because I believe that is irrelevant. What is relevant is what the tree looks like once the merge (or rebase) lands.
Should a merge conflict arise, in a rebase workflow the conflict resolution is folded into the rebased commit, so it looks like it was fine all along. In a merge workflow, the fix is in the separate merge commit. In both cases you still have to handle the merge conflict. And in my opinion it is not significant for the merge conflict resolution to be separate from the original commit itself because, again: what's important is the final state of the repo.
I recommend reading this 2008 exchange between top Linux developers learning to use git, and Torvalds's... characteristic language when talking about rebasing a "public" repo:
https://www.yarchive.net/comp/linux/git_rebase.html
(I'm putting "private" and "public" in quotes because they're really on a spectrum)
Just mark it draft or otherwise communicate when it’s no longer a moving target.
But if you work on a feature branch, you can publish that branch as long as everybody else understands that it is yours and nobody should push to it. Then you can safely git push --force to the feature branch so that others can (a) see what you're up to (b) test the results. When you're done, you can do a final (or only) rebase against the target branch then merge to that branch.
What I hate about this workflow is it teaches you to git push --force.
Then one day, you're in the wrong window and you accidentally git push --force on master.
Much prefer a workflow where you never git push --force
The ability to rewrite history on feature branches is powerful in a good way and slots right into the way Git is philosophically designed. I would probably not be interested in removing that feature to prevent the rare case that someone footguns themselves or someone else
git push —-force-with-lease repo branch
Most PRs happen against the main branch, maybe after a private feature branch, maybe not.
Meanwhile, major developments happen in a feature branch, and these are almost always owned by a single person who is responsible for rebasing against the main branch (and eventually merging back there).
But maybe our development team and pace are not good tests for this model.
That still seems very annoying. People have unplanned time-off all the time, live in different timezones, or perhaps are l just busy ATM and can't do the rebase.
- Unplanned time off means the feature just doesn’t make it in
- Living in different time zones means that you just wait and work slows down
In either event, there is rebase and squash auto merge from major git providers like GitHub which helps with this a ton. With that enabled unless the repo is super high traffic usually this is an afterthought unless there is merge conflicts which is semi rare.
That being said, that’s just the reality of what I’ve seen play out in various organizations. I personally like to work off other people branches a lot more than my peers because I work in platform engineering and very often developers come to me with feature branches that need some CI/infra/other change or optimization and very often it’s easiest for me to just drop a commit into their existing branch. The change falls under their ownership and it gets mainlined along with their feature.
...then we got used to it. A few workflow changes were necessary:
- configure pull.rebase=true. This is kinda just nice in general, but critical if someone might have rebased the branch you're working on overnight. - get used to pushing up your changes regularly - at very least before you quit for the day. - get used to pulling remote changes regularly - at very least before you start work on a branch each day. Rebase after pulling - especially important if you've branched off another feature branch (which may have been rebased). This way you avoid doing a bunch of work on an out of date base. - *Talk to each other.* If it isn't already obvious what a collaborator is doing... Ask them! And err on the side of over communicating what you're doing.
It turns out, all of these behaviors are kinda just generally useful, and after a bit you forget about rebase being the motivation and just enjoy having a bit less friction when working together.
If the problem is that you urgently need some small fix in an otherwise broken branch, you can always just cherry-pick that across to your own fresh pr.
Then if someone changed your remote branch, git will always let you know :)
That's great to have learned, thanks!
This is false. Well, it can be false. Git sucks and makes it harder then it needs to be.
It’s relatively easy to have multiple people working on a feature branch that is continuously rebased.
Meta’s Mercurial-like VCS system automagically stores 100% of commits in the cloud. Feature branches are anonymous rather than named. It’s all just a tree of commit hashes.
Having multiple people work on one “branch” requires some basic communication as to what is considered “tip”. But since it’s all anonymous there’s no issues with different people incrementing a named branch to different commits.
Honestly it’s pretty simple and easy. Git makes it harder than it actually is.
This seems to be very similar to what you say with “requires some basic communication as to what is considered ‘tip’”
Requiring coordination (or a constrained model of allowed changes) when modifying things (such as what the tip/branch points to) is a general principle unrelated to git and applies to every workflow.
That said, I do understand that Meta’s centralized tool is more useful for your usecase.
One of my chief Git complaints is that 99.9% of projects are de facto centralized on GitHub. Genuinely decentralized projects are vanishingly rare.
The way Git auto-advances branch tags causes a lot of pain. Having lots of people commit to a shared feature branch that has lots of rebasing is quite easy when the “tip” is infrequently updated. Each person thinking they can declare the new tip with every commit is the source of pain.
But even when using GitHub as the hosting service, every time you work on a local branch (this includes main/master) you are working on something purely local that will not conflict with what anyone else is doing. It’s only by pushing/pulling that tracking branches are automatically updated, which seems to correspond to the operation that you would refer to as updating tip.
As the rest of the discussion is this topic seems to indication, people do work on these “private” branches a lot, and do rebase/modify them freely to create a history as they like before publishing that history (at which point, you would require coordination with everyone who saw that to change it again).
As far as I can tell, what the Meta tooling seems to offer is that you can see the “kinda private” commits of other people, if I understand correctly. That does sound indeed useful, but also out of scope for Git – rather part of some further UI that displays such information conveniently.
===
In other words, you really shouldn't rebase stuff that has been exposed anywhere outside of your own private tree. But within your own private tree, and within the commits that have never seen the light of day, rebasing is fine.
(And yes, there are exceptions. If it's a clear "throw-away tree" all the rules go out the window, of course, as long as everybody involved knows it's a throw-away tree, and know that if they pull it they have to synchronise 100% with you - so within a very tight-knit case or within a very specific small detail that is actively being worked on, those rebases with cleanups make tons of sense).
===
That's kind of the point though: being reasonably sure that a commit contains a tree that the committer had seen at some point, instead of making up history with commits that contain trees that the committer never saw at any point at all.
When someone rebases `n` commits, experience has taught me I can't trust any commits other than `HEAD`; chances are any commit printed by `git log "HEAD~${n}..HEAD^"` was never checked out by anyone, much less tested at all.
CI pipelines also usually run only against HEAD at the moment of push, so if someone pushes `n` commits, then `n-1` are usually ignored by CI pipeline.
Modifying options for compiler or linter or formatter checker; adding a new dependency or updating an existing dependency's version; changing default options for the project. Stuff like that might make those commits useless, and if someone notices a problem in HEAD after rebase, and decides to fix it, even if the fix is moved to the earliest possible point, nobody would bother re-testing all those n-1 commits after the fix was added, leaving broken commits useless for git bisect.
So I agree that rebase is nice. How most people use it, though, not so nice.
1. Never rebase a shared branch 2. Never break rule 1
If you squash you have a history where every commit was tested and works (bugs notwithstanding) which to me is way more useful.
For some types of work it is easy, N=M: you were able to do high quality value adding atomic commits for the whole PR without rework.
For other types N >> M. This can happen when trying different approaches to a hard problem. I suppose research type work could always be considered a POC and the actual implementation could be a kind of cleanroom re-implementation of the POC but there isn't always time for such things and (again) the PR is far more important than the commits that built up to it - particularly if the resultant code is of equal quality. Note that I am not advocating for long running branches here - trunk based development is generally better provided it doesn't over incentivize teams to avoid hard problems (but that is a topic for another day).
This is why I think git should include the PR as a first class concept. For simple N=M type work, 1 PR should generally be 1 commit. Why not, after all, make the PR small and easy to review when you can? For harder N >> M type work, you get one PR with many commits that one can dig into if necessary.
This is the reason. I've been on a maintenance team for years where almost everything we handle was written by people no longer at the company, and often enough I've seen bugs get introduced during the original work, where the fix ends up being obvious because I can see the original commits and how the code got into its current state. A squash of any sort would've hidden the refactor and made it much more difficult.
My favorite are ones where "linting" and code formatter commits introduce bugs. Keep those separate from your actual work, please.
I like rebasing but it's ultimately up to the author. Even tools like Fossil, that don't have official history rewriting tools, don't ensure that history has never been rewritten because people can use external tools to do the rewriting (and I've done this).
Two months from now I'm quite likely to say something like "oh yeah, I remember I encountered a bug related to that, I was trying to fix it before lunch". The "wip" and "before lunch" commits are just as likely to be relevant in the future as any other.
It's nice to assume that all commits will compile and pass the tests, but it's sometimes useful to have a snapshot of that weird compiler error you encountered. So much for our nice assumption.
This is why I say it's all up to the author, and if the author likes rebasing, I don't think anyone should have a problem with that. (Don't rewrite public branches, of course.)
If you make a commit "wip" or "before lunch" because you want a backup of your work or want to continue on a different computer, then it's not a meaningful unit either. It's OK to throw away.
Most people prefer less granular commits but not to the point of having 1 commit per issue/PR. For example after inheriting someone else's code written in a hurry and not tested, I often end up dividing my work into several commits - first there's a cleanup of all the things that need renaming for consistency, adding docs/tests, removing redundant/unused code, etc. sometimes this ends up being more commits as i reveal more tech debt. Then, when i am confident i actually understand code and it's up to my standards, I make the actual change. This can be again multiple commits. The first and second group are often mixed.
And it's important when it later turns out i broke something - i can focus on the commits that make functional changes as the issue is usually there and not in the cleanup commits which can be 10x larger.
BTW what git is really missing is a way to mark multiple commits as one unit of work so the granularity stays there but is hidden by default and can be expanded.
Is that not just a non-FF'd, non-squashed merge of a branch?
Isn't this something that git makes simple?
It works really well with things like git bisect. It also means history is actually useful.
Every Jetbrains IDE does this, and VSCode has it's own equivalent feature. They don't use git, but same thing really. It's one of the most useful features ever IMO.
I think people should just use what works for them, if that’s debase who cares? The important part is being able to commit “asd” 9 billion times. If you can’t do that it will tax your developers with needlessly having to come up with reasons why they committed before lunch… that meeting… going to the toilet and so on.
I'm not against rebase, and even use it myself. But having a repo where every 3rd commit is a dice roll for git bisect just because straight line pretty, is just as annoying as people shipping their reflog.
A rebase of one commit is harmless. A squash is harmless. A rebase of multiple commits where every commit is deliberate (verifying all rebased commits, etc) is harmless.
A rebase that ignores the fact that any commit whose hash changed can now fail, is irresponsible. Shipping `wip` commit messages is irresponsible. A merge commit with the default message is irresponsible (it's no different from a `wip`-style commit). Having a branch with merge commits that could have been cherry-picks[3].
Also, to me the lie is not some aesthetic thing like commit order or some easily forgeable timestamp; the lie is having a commit that (for example) assumes the `p4tcc` driver is being used[1], and you read the diff and indeed it has assumptions that imply that driver is being used[2], but when you actually checkout that commit and see if that driver exists it turns out no it fucking doesn't, and hours were wasted chasing ghosts. Only because when that commit was created, the p4tcc driver was being used, but when you checked out weeks later now that commit magically uses the `est` driver instead.
If you're going to keep straight line, then test every change; if you don't do it, don't complain about broken middle commits.
If you're going to do merge commits, then keep each commit clean[4], even the merge commit[5]; if you don't don't complain about a history that is polluted with weird commits and looks like the timeline of a time-travelling show.
[1]: Because it did when that commit was created.
[2]: Because, again, it did when that commit was created.
[3]: This assumes the branch will later be integrated into main with a merge commit.
[4]: Squash is harmless. It's just omission. If anyone complains about purity, then just keep them happy with `git reset $COMMIT ; git add --all ; git commit -m "This is a new commit from scratch"`
[5]: Write something that helps those who use `git log --first-parent`. If you're on GitHub, at least use PR title and description as default (can be overriden on a case-by-case basis). If not, then even just "${JIRA_ID}: ${JIRA_TITLE}" is more useful than the default merge commit message while still letting you be lazy.
There is no “real history” in git, and it’s kind of a fictitious idea, even in Fossil or other VCSes that don’t offer rebase. Think about it: commit order of “WIP” ideas in a branch is already arbitrary, and commits only capture what you committed, not what you typed, nor when you ran the build, nor what bugs you fixed before committing, nor what you had for lunch, nor anything you didn’t choose to commit. Taking away rebase only adds extra pressure to plan your commits and be careful before committing, which means that people will do more editing that is not captured by the commit “history” before committing! Having rebase allows you to commit willy-nilly messes as you go and know that nobody has to see it. It seems like rebase might very well be safer in general because it encourages use of the safety net rather than discouraging frequent messes… and we’re all making frequent messes regardless of VCS, all we’re talking about is whether we force the rest of the team to have to be subjected to our messes.
Git provides change dependencies, and does not offer “history” in the sense you’re implying. People overload the word “history”, and git’s sense of history is to show the chain of state dependencies known as commits, and those have editable metadata on them. In other words, git’s “history” is a side-effect, a view of the dependencies. Git’s “history” does usually have loose association with an order of events, but nothing is or ever was guaranteed. It is by design that you can edit them (meaning build a new set of dependencies with rewritten metadata… the old one is still there until garbage collection), therefore there is no “real history”, that’s not a real thing.
I don't really understand why this would be important. If I'm the one committing, I can rebase however I want to rewrite history before merging, so if I'm super adamant that a commit that looks a certain way exists, I can just make that commit and then put commits around it as needed to ensure that it can be merged with by fast-forward to preserve it. If I'm not the one committing, why should I care about what intermediate states that the person who committed them don't even care about enough to preserve?
To me, the issue seems more that the UX for doing this sort of thing is not intuitive to most people, so the amount of effort needed to get the history rebased to what I described above often ends up being higher than people are willing to spend. This isn't a particulary compelling argument to me in favor of merging workflows though because it doesn't end up making the history better; it just removes most of the friction of merging by giving up any semblance of sane commit history.
(edited to add the below)
> When someone rebases `n` commits, experience has taught me I can't trust any commits other than `HEAD`; chances are any commit printed by `git log "HEAD~${n}..HEAD^"` was never checked out by anyone, much less tested at all.
I definitely agree that generating broken commits during a rebase is not a good thing for anyone, and I'd be super frustrated if I had teammates doing that. At least personally, I make sure to compile and run unit tests before continuing after each step of a rebase after I've fixed conflicts; there's even the `x` option in an interactive rebase to execute a command on each commit (which will halt and drop into commit and allow you to amend before continuing if it fails), which is unfortunately not super well known.
It is important because not everyone does this:
> [...] and then put commits around it as needed to ensure that it can be merged with by fast-forward to preserve it.
Good quality rebases like those are more likely to happen on patch-based workflows (not necessarily email), compared to PR-based workflows, because there's more focus on the individual commits themselves being meaningful, with straight line history being mostly a nice side-effect. With "more likely" I mean literally that, more likely; I'm not saying it only happens there.
In PR-based workflows on the other hand, people tend to care only about HEAD. PR color is green? LGTM ship it :rocketemoji:. Most just read blog post by git shaman saying straight line pretty and then go to GitHub and enable the setting for that without thinking more than that; or learn that you can reorder commits to tell pretty story and do it without thinking more than that.
Though it's also true that some repository owners only care about the tagged commits; all untagged commits could be broken and they don't care because "it's supposed to be in progress" and "as long as the most recent commit works, it's fine". They've never needed to checkout any specific commit on any repository (understandable if they never contribute to others' repositories).
---
Also, you probably noticed already because of your edit, but re:
> If I'm not the one committing, why should I care about what intermediate states that the person who committed them don't even care about enough to preserve?
With "intermediate states" I don't mean what other people committed; I mean all your own commits that you just rebased (all your own commits whose hash changed) that are not the most recent one.
You are in the minority that fixes those; most people I've met would be like:
* All commits are tested and work fine.
* Create PR.
* See CI fails because branch is outdated.
* Rebase PR onto most recent commit of main branch.
* See CI fails because, idk, let's say it's something easy to fix like a more strict linter config.
* Make a new commit that fixes the linter errors.
* CI passes.
* Everyone LGTM's the PR and it gets fast-forwarded.
* The PR had `n` commits, but now `n-1` of those fail the linter because they contain the new config for the linter, but the committer never bothered to look at those commits, they only cared about HEAD. Those `n-1` commits "contain trees that the committer never saw at any point at all" (copy-pasting that quote from my message). And it doesn't matter that those commits are broken because for those people having pretty straight line is way more important than a working commit.
The recent FreeBSD/Netflix thingy[1] had a successful bisect only because when people rebase stuff in there, they don't YOLO those `n-1` rebased commits. If that had been any of my previous workplaces, or anyone who only rebase because "straight line pretty" without thinking anything more than that, then that whole bisect could have gone way worse.
Using merges lets you commit as you go, without needing to go back to repeat a test on a previous commit, and only worry about conflicts at the end of your development. Write code, test, commit. Write more code, test, commit. Cherry-pick, test, commit. Merge into main, fix conflicts, finish merge. There's never a need to go back and re-test, like with rebase, because the commits that were already tested are still there. But they require discipline to not pollute history, and being open to squashing commits that don't add any useful information (you want to avoid having "WIP"-style commits).
Using rebases lets you rewrite commits to take advantage of the most recent changes from the main branch, instead of waiting until you finish with your feature. But they require discipline to go back and repeat tests to ensure that any commit that changed still works as expected (and it's needed because the commits changed, hence their different hash, so they are no longer the commit hashes that were tested), and being open to having some merge commits (you want to avoid rebasing a 10 commit migration of your telemetry library because if 3 months later you find out your costs in production were way higher than what they told you they would be, reverting a single merge commit is more dumbproof compared to reverting a manually provided range of commits).
So yes, choosing one or the other is a social problem. Both are good solutions with good discipline, and both are bad solutions with bad discipline. One of those makes it less likely for people in my bubble to make a mess out of that repo. It might be the same as for your bubble, or it might be different.
But on a good project it doesn't really matter which one is done.
> But on a good project it doesn't really matter which one is done.
I appreciate your explanations! I think I understand your point of view now, and I do actually agree with it. In particular, I hadn't fully considered that the problem ultimately being social means that the "best" choice will be mostly dependent on what consensus a group is able to come to.
Thinking about this more, it almost seems like having a preference could become self-reinforcing; it's hard to be a member of a group that reaches a consensus on using merges as someone who prefers rebases (and likewise for the reverse), which over time manifests as more and more anecdotal evidence in favor of the preference working better than the alternative. It's no wonder that debates about this sort of thing become so contentious over time...
I don't see how this follows. Merge-heavy histories in my experience tend to be far less bisectable. They have all sorts of "oops, fixup" nonsense going on, precisely because the author did not take the time to get things right the first time.
Any workflow that happens on a number of patches greater than 1 accepts poor bisectability as a risk. But the only real solution there is Giant Monolithic Commits, which we all agree is even worse, right?
But if "merge-heavy" means "use merges when it makes sense, use rebase when it makes sense", then you can get a nice history with `git log --first-parent` that groups related commits together, and also a nice history with `git log --cherry` that shows what the "always-rebase-never-merge" dogmatic people want.
If for this particular project it just so happens that merge doesn't make sense because of the specific needs of the project, then so be it, nothing wrong with that. Same with rebases.
Unfortunately this topic is another holy war where the ship-the-reflog dogma fights against the always-rebase-never-merge dogma.
No balance.
> I don't see how this follows. Merge-heavy histories in my experience tend to be far less bisectable. They have all sorts of "oops, fixup" nonsense going on, precisely because the author did not take the time to get things right the first time.
That sounds more like merge-only (a.k.a. "ship the reflog"). Doesn't have to be that way.
Evaluate trade-offs and choose based on that evaluation.
Does adding a new commit have any actual advantage (e.g. easily reverting one or the other) compared to just amending/squashing it, or is it just some developer's own subjective sense of purity?
Does re-ordering the commits have any actual advantage (e.g. change has a smaller context and can be more easily reverted that way) compared to just leaving those commits in that order, or is it just some developer's own subjective sense of aesthetics?
Does using merge commits bring any actual advantage (e.g. the project benefits from being able to bisect on PRs or features as a whole) compared to rebasing (not fast-forwarding), or is it just some developer's own subjective sense of purity?
Does rebasing bring any actual advantage (e.g. each commit is already atomic, fully self-contained, and well tested against the new base, so "grouping" them with a merge commit doesn't make sense) compared to doing a merge-commit, or is it just some developer's own subjective sense of aesthetics?
> Any workflow that happens on a number of patches greater than 1 accepts poor bisectability as a risk.
Poor bisectability or developers putting actual effort into ensuring commits are atomic and test them.
Bisectability is nice with good rebased commits. Bisectability is nice with good merge commits.
Bisectability is bad when developers don't care about keeping bisectability good.
> But the only real solution there is Giant Monolithic Commits, which we all agree is even worse, right?
It depends.
Those commits might not be easy to understand, but they sure as hell are easy to revert (more likely than not) if something goes wrong, because they tend to correspond almost 1:1 to GitHub issues (or Jira tickets, or whatever equivalent). Keyword "almost" because sometimes you can get 2 of those for the same issue/ticket/whatever.
But those 2 unproperly split commits (therefore huge) are still easier to revert compared to a spray of 10 unproperly rebased tiny commits where 9 of them are broken (because of what I mention in other comments where people only test HEAD).
Where the straightforward "get your stuff rebased into a linear tree" tends to work pretty well in practice. The rules are simpler and easier to audit and enforce.
I do.
Honestly all of this is just a function of Git’s tooling being pretty bad.
There’s no reason that a merge based history can’t be presented as a linear history. That’s purely a matter of how the information is displayed!
Similarly there’s absolutely no reason that git bisect should try to operate on every single commit. We live in a world where CI systems need to land a queue commits at a time. No one can afford to run every test on every commit. Git should have support for tagging commits that had varying levels of tests and then running bisect against only those commits. Easy peasy.
It's assuming that that merge conflicts will never be too difficult, and people won't make mistakes in resolving them. If so, too bad, the original commits are lost by the rebase process. (They might or might not be kept in any given clone. No guarantees, good luck trawling through the reflog.)
It's corrupting your database because it makes the UI for exploring it nicer.
This is the opposite of what we should be doing! If the truth is messy we should build better UIs for exploring it. Add additional information on top of the true history, allow people to group and describe commits. Add more information to get the UI that you want, rather than destroying information because it makes things look cluttered.
That is the role of the CI system.
(Unless you think "works on my machine" is good enough testing. Sometimes it is.)
Only true if the commit author doesn't do so, and it is trivial to do so, either during or after a rebase using the rebase exec command. So given this is a discipline issue no different from a developer authoring a change without testing it, I fail to see how this is "rebase"'s fault.
>it makes the UI for exploring it nicer
Not to imply I accept the "corrupt database" opinion, but I think it's worth saying that aside from the collaborative element of VCS, commits exist for the purpose of exploring past code changes. A practice which improves that seems sound to me.
>we should build better UIs for exploring it
Go right ahead :)
"Undisciplined enough to use rebase, disciplined enough to put in extra effort to mitigate some of the harms of rebase" is an imaginary intersection.
> Go right ahead :)
git log --first-parentYou can't use your preferred answer to the debate as justification for dismissing your opponent's arguments.
> Not to imply I accept the "corrupt database" opinion, but I think it's worth saying that aside from the collaborative element of VCS, commits exist for the purpose of exploring past code changes. A practice which improves that seems sound to me.
I dispute that rebasing improves exploring history. It makes the history linear at the cost of making intermediate commits untrustworthy.
Both techniques require some manual discipline, and I'd feel differently if in practice people generally would lint and test each commit every time they rebased. If you do, then I've got no quarrel with ya.
I take it that you've never reviewed a PR where the commits were a series of "wip" commits that don't even type check, much less pass the tests?
Undisciplined developers will be undisciplined. Forcing a rebase-free workflow mostly makes it less likely that these developers will lose work, it doesn't magically give you a clean commit history from them.
I certainly have used commit messages and seen others do the same. Perhaps this is more an indictment of the quality of the commit messages than anything else.
In my experience, rebase and squash makes it easier to collect work into meaningful groups and thus write more helpful/informative commit messages.
I can think of a few times off the top of my head when I referred back to a detailed commit message in a repo to understand why a change was made.
I don't know what splitting up a branch means.
Merge commits 'lock' you in to the commits you've made so far.
I don't doubt that Linus has good reasons for all this. I just don't know what they are. And I don't know if they're applicable to other repos.
If I used rebase like this regularly it would become difficult to determine if a given commit is an ancestor of HEAD. Sometimes I like to do that.
I wouldn't do so if the history were a mess
Looking through my work project git log, it's a sea of random ticket numbers followed by absolutely nothing helpful or descriptive, usually just the title of the ticket if you're lucky.
Was the bug introduced by the rebase, or was that code always broken?
If I'm trying to fix the PR, the distinction is often a critical starting point for finding the root problem.
A workflow like what OP is describing (code doesn't exist until it's merged) typically also assumes that your PR is one atomic change, not a whole feature. You'd use feature flags or something similar to decide when to actually release a feature, not the PR process.
Who said anything about bisecting?
But yes, sometimes changes are big and I'd rather land them as one cohesive unit that demonstrates how it actually solves the root issue than get stuck down a wrong path because "this is what we already landed the infra for".
> A workflow like what OP is describing (code doesn't exist until it's merged)
That's not a workflow, it's a misunderstanding of reality.
While rebasing works most of the time, the problem arises when actually collaborating on feature branches - especially when your collaborator is not confident enough with git to realize when a rebase conflict might lose data. Merging works better in these situations. So while standardizing on rebasing is great for productivity across the org, you also have to watch out for this and make sure developers don't lose the ability to collaborate on branches.
In my mind the only real benefit of rebasing is that you split your work into a couple of "nice" commits, but if you squash them anyway...
Ok, I've seen people doing this to ease the review process and I guess it makes sense sometimes, but to me it's still a largely "not worth it" effort.
It's just that squashing takes 2 seconds. I just shift-select the commits in my Git client and click 'squash'. To me it's just like cleaning up after cooking. And if I want to re-order my commits, I just like drag and drop the order of them.
If I told you right now to squash or re-order your last 5 commits and you think it'd take you longer than 2 seconds, you are honestly using a bad tool.
Yet hundreds of people have spent many human hours writing these long blog posts about how you have to do things a certain way yadda yadda yadda when the real problem is that they use bad tools, squashing is such a pain, they don't bother to clean up their "fix 1" "fix 2" "try again" commits, and everyone has to deal with Git gymnastics at the end.
* the rebase command, as a means of adjusting commits
* the practice of squashing all commits into a single commit (typically, but not always, via rebase)
* the "rewriting of history", whether on public or private branches
* rebasing on merge vs. creating a merge commit
This article starts by saying they "like rebasing" only to then say
>Git rebase allows me to squash my disordered commits into a neat little one
Rebase allows this practice but you could just as easily `git reset @~` a few times and then `git add .; git commit -m "My big commit"`. Alternatively the author could just `git add .; git commit --amend --no-edit` throughout the task and end up with the same result. To be fair to the author, they later add that they don't always squash to a single commit, but here I'm talking about "rebasing" vs. commit practices. Rebase is just a tool which allows various practices and it's tiresome seeing the same arguments against this nebulous "rebase", when the arguments are actually directed at practices.
I personally use rebase similar to the author, to squash all my noise down to my final intention. But this could be achieved in other ways, just the same.
I am 100% onboard with devs squashing their "lint fix", "test fix", "whatever minor fix to get CI working", "generally meaningless commit to cross the finish line". Also, if devs are working on something they check in a "WIP" commit. It is great if you smash your WIPs into a single meaningful commit. These manual squash strategies require some discipline to clean things up.
I think the "squash branch to a single commit" merge strategy defeats the purpose of atomic commits. Of course devs will be bad at atomic commits if the commits will inevitably smashed to a single commit. IMO squashing branches on merge is a bad version control strategy. I love it when commits are intentional.
One rule I have for any rebasing is, when there may be more than one person using the branch, no more rebasing that branch.
The most valuable “feature” of git for me is the “single commit with 20+ files changes”. I explain: I have landed in a new codebase, and now I need to add a new feature that would include perhaps a migration and adding a “usecase” and perhaps a “controller” and the corresponding tests. As usual, things are never clear in new codebases (depending on the library/framework used, one might need to add routes to a main routing file, or perhaps touch some configuration yaml file to whitelist the newly introduced endpoint, or perhaps a changelog needs to be kept updated whenever a migration is introduced, etc.). My point is: if I can git blame a line of code of the codebase that’s doing already more or less what I want to introduce, I want to see ALL the files that were touched as part of that change. It’s a lifesaver.
Professionally, 21 years. Seems like you're implying that someone with an opinion that is different than your own is not experienced.
So if you know a commit sha and you'd like to see all of the merged branch changes associated with that commit as a single patch, I cooked up a script that takes a commit sha and finds the merge commit and show all the changes in a consolidated patch.
#!/bin/sh
merge_commit=$(git log -n1 --merges --pretty=format:"%H" $(git rev-list --ancestry-path $1..HEAD --merges -n1))
git show -m -p --stat --format="" $merge_commit
I frequently get halfway through building something, but the. A more urgent project comes up and i need to commit my half-done non-functional work so I can hop onto a different branch
For some reason it feels ephemeral and I worry that I’ll lose the stashed changes like when you accidentally overwrote the copy/paste clipboard
That said for me the stash is usually used either for temporary stuff (eg taking my current work to another branch) or for things that might be useful to reference later but I will rewrite differently anyways; stuff I want to keep goes into a WIP commit.
I know stash feels easier to a lot of people, and that’s a valid reason, but it’s really no more typing or thinking to use branches instead, it just might require changing habits.
As a team leader, I prefer to avoid "a lot of" WIP branches, but I just expect developers to rebase their WIP onto dev, etc.
Oh, and I really really dislike "merging" develop into the WIP branch. This accomplishes the same thing as "rebasing" the WIP branch onto develop, but it leaves a horrible mess behind.
Frankly, I don' give a hoot about some "history" of work. In the end, I care about the unit of work encapsulated in that WIP branch, and that unit must always add on top of develop. Rebase just makes that super clear.
It's so frustrating to see 7 commits for one MR and it's a 10 line change.
The tooling for that is called a branch with multiple commits and it works fine. The unit for something that makes sense as a single commit and what makes sense to review together is not the same and forcing the latter to be as small as the former will only lead to merging dead ends that are fine on their own but don't actually lead to the optimal goal.
> One rule I have for any rebasing is, when there may be more than one person using the branch, no more rebasing that branch.
That is the one rule to obey for rebasing to be useable at all.
No commit should introduce a non-working state but it often still makes sense to review all commits that form a new feature together. After all, you are not just reviewing if every commit does what it says and doesn't break anything etc. but also that the commit is the correct approach for the final goal - and that often requires knowledge about how later parts are implemented.
Is it just me, or is it genuinely funny that whenever someone does this I think of Deep Thoughts by Jack Handey: “When you're going up the stairs and you take a step, kick the other leg up high behind you to keep people from following too close.”
Question: how does Magit compare to GitUp? The latter is my benchmark for complex rebases, but I'd love to know if I'm stuck in a local optima.
This statement really confuses me. SVN was so much easier to use than git. It was incredibly straightforward: make changes, commit, done.
--reintegrate was introduced later, and even with that it was easy to get wrong.
Otherwise I tend to prefer rebase.
- I can rebase all I want in my own private repo
- Rebasing is fine if no one else thinks they can depend on your branch's history
- You obviously don't rebase master (!) or a branch that you'll collaborate on (not without proper warning at least).
I've seen this attitude more than a few times, and I think it's fear born of ignorance. I just don't get the inability to consider a different perspective.
Edit: formatting.
If rebasing works for you, I'm not going to try to sell you something. But there are other ways.
Or maybe you're suggesting it as a way to unravel a screwed up rebase. I don't know how to do that. But luckily I do know to abort, reset, and merge.
And this literally cannot happen in the standard rebase workflow where you have one development branch and then rebase your feature branches on top of that development branch before (fast-forward) merging.
- Creating a false history that will be harder for you to understand in the future
- Creating a history that tools will have a harder time dealing with in the future
- Doing so for no actual benefit (in 99% of cases)
You are factually incorrect about rebase making things harder for people or tools to understand. The primary use case for rebase is making things easy to understand, in private branches after liberal use of the ‘commit early; commit often’ rule. If you take rebase away from git, and ask people to be okay with commits becoming immediately permanent, you increase the chances that people will make mistakes or lose work before they commit, and you remove some flexibility of being able to work on multiple separate topics in a single branch. Like any and all tools, rebase can be used incorrectly, but it is not used incorrectly most of the time, and basing your argument on the rare accidents means the argument is straw man.
I've come from a merge workflow into a rebase (git + gerrit) workflow in a huge software project with hundreds of people. Rebase workflows are fine. Merge workflows can be fine.
Personal preferences + the way you're used to doing things.
There's some advantages to being able to see how conflicts were resolved when you're looking for something that went wrong but if you have code reviews and tests then the probability of needing that in a rebase workflow is smaller. In a large team "how we got to a change" is IMO less interesting than "this is the change".
Have seen occasional breaks from things that were incorrectly rebased (generally from auto-rebase situations), but those are rare enough and easy to address. Same things can happen with merges.
An IMO much larger question is branching strategy. Normally if you're following a rebase workflow you're doing trunk based development. Many teams that use a merge workflow use feature branches. If you use feature branches you're pushed towards merge workflows. That's a bigger debate maybe (and I'd vote for trunk based).
There is an historical reason for this. It is a hyperbolic marketing point of view invented by Richard Hipp, the author of SQLite, and of Fossil, in order to try to sell Fossil as being superior to Git. “Rebase is a lie” has been a meme ever since he published an article titled “Rebase Considered Harmful”, which has been posted to HN many times. Because he has contributed a lot to open source software development and because SQLite is so widely used, a lot of people respect everything he says and so this unfortunate idea has spread and gets repeated, even by otherwise very smart people.
Dr. Hipp has made appearances on HN arguing that rebase is bad, and even told me it should be likened to criminal activity. (https://news.ycombinator.com/item?id=29133188) The Fossil documentation still has remnants of it, but it has been softened over the years.
Luckily it seems to be dying, and I think the Fossil team might be coming around to the realization that this negative attack smear hyperbole is ultimately not helping Fossil grow. This is the primary sticking point for me and the reason I won’t use Fossil. I’m actually quite interested in trying it, but not until the (ironically) dishonest claims about Git and it’s goals are taken down.
Yikes. To give a charitable interpretation, that's quite an extreme view. Financial ledgers and VCS histories are not equal and they serve different purposes; the primary purpose of VCS history being to aid in the understanding of the current state of the codebase. In some cases, the "true" activity may provide that understanding, and in others, it may hinder it. Squashing a PR-requested change like "rename new variable based on PR feedback" seems sensible as it reduces noise in the history - definitely not what I'd call fraudulent. To me commits are like documentation and I see no reason why I shouldn't be able to refine that documentation if I think I can improve it for a future reader.
git checkout main
git checkout -b squash-branch
git merge --squash [branch-to-rebase]
At this point I usually git diff the two branches as a sanity check before merging back into main: git diff [branch-to-rebase]
git checkout main
git merge squash-branch
I am normally able to squash rebase 99% of the time using git rebase -i main, but doing a git merge --squash into a temp branch has saved me a lot of hassle over the years.If you actually care about organizing you disordered commits, you can do it in a sane way and use git rebase -i master. That's basically how I work all the time, I don't even try to write meaningful commits first, I even commit stuff I know I must drop later and I commit every little incremental change file-by-file (to make re-ordering commits easier). Then I'll rebase them interactively to review what I did and make several (most of the time) clean atomic commits.
Then, rebase is simply superior to making merge commits all over the place, because it... well, "does nothing" as opposed to "makes your commit tree into a complete mess". There can be reasons why you would rather not bother rebasing on a particular project, but if you don't have them (i.e. you just don't have a strong justifiable opinion on that matter), just set [pull] rebase = true, and use git merge --ff whenever possible. Most of the time it doesn't even feel any different and just works.
Furthermore, since pushes must necessarily overwrite (--force), you actually risk loosing work.
This is all fine and doable but you have to be "on the ball" / paying close attention all the time thus introducing higher cognitive load.
The bottom line is getting work done and making history look good are competing objectives to some extent. There are all kinds of reasons to commit code (I got something working, I want to share something, I need to try this on another machine, etc.). Thus, the only way for commits to appear logical / atomic is to review the current state and refactor it to a logical looking but artificial series of commits. I can't imagine such efforts being useful enough to actually do in most cases. Perhaps even a task better suited to AI.
In reality, the PR is the only commit that anyone cares about. Everything else is mostly just noise. Therefore it would be best if the PR was a first class concept in git vs something only available outside of it (i.e. github).
``` git pull --rebase main ```
And then push the changes with `git push origin HEAD`. Pushing with `--force-with-rebase` won't save me from this.
I usually never rebase a shared branch.
1. Never rebase a shared branch 2. Never break rule 1
In this case, you can easily perform rebase and run `git push origin HEAD --force-with-lease` without causing any headache for anyone else.
So would I, but I have also have pull set to rebase.
I find merging to be extremely tough to work with -- I don't think I actually got good at git until I learned to rebase entirely.
Also, cherry-picking (effectively an atomic rebase-type) is underrated.
Not entirely, since git can automatically garbage collect commits that aren't reachable.
> Also, cherry-picking (effectively an atomic rebase-type) is underrated.
This this this, all of this.
I never really "got git" until I finally committed to learning how rebase works.
+1 to cherry-picking. Also patch-add (`add -p`).
Squash merge.. zero downsides.. hard to take you seriously.
My preferred workflow on a team project is to have well-crafted commits with merge commits. The merge commits signify the wider intent and link back to the PR in GitHub, while the interactively rebased commits tell the story of the steps to get from point A to point B.
Git blame is less useful when what were individual commits associated with lines are instead rolled up into a massive commit, such that each affected line is now described by a more general commit.
It can encourage lazy developers to submit shit commit history in PRs knowing it's going to be squashed anyway, making PR harder.
https://github.com/libexpat/libexpat/pull/789/commits is a PR of 16 related commits. Many of them make sense individually, but are depended on by subsequent commits.
Reviewing them separately makes sense. It's easier on the reviewer (and future readers) to handle multiple smaller changes, especially since each is more tightly coupled to a rationale in the commit message. Unfortunately, github's PR-focused UI doesn't really make this per-commit review as convenient as Gerrit does.
Additionally, the last two commits are authored by another contributor. This metadata would be lost in a squash.
Of course, each intermediate commit must build and pass tests. WIP and cleanup commits are squashed locally before final review/merge.
You could argue that all of these could be separate PRs, but I think there's value in grouping them up with that final merge commit, showing what one was trying to do at a larger scale.
No one on our team had used rebasing extensively before this, but it's hard to go back. Similar to the article, the benefits we see are:
- Fast iteration with `gt m` (`git commit --amend`) and `gt s` (`git push --force`) - Using `gt restack` (`git rebase` on each parent commit) helps make merge conflicts more transparent in what happened - Commit histories are much more legible
I highly recommend giving it a go.
Keeping disorganized records is the only thing you can say in favor of merge commits, and I'm unconvinced that's a positive thing to begin with.
And you can eventually have github squash for you anyway.
If you're trying to figure out where a bug came from it can be helpful to bisect through all the commits. If you bisect down to 40 file change that's going to be a pain to continue bisecting. However, if you can bisect down to say 5 changes where your bisection fails and 4 of them are because the app fails to build and the 5th is because that's when the bug was introduced that can be very useful.
- Namely there would be review comments on the first PRs that then cause a cascade of merge conflicts in the follow-on PRs
- Somehow reviewers never seem to like the stack of PRs, my experience is they always react with disdain and/or confusion.
There is the counter-question too, why not stack commits cleanly?
A third reason against, not a good reason, but for many developers Git is super difficult (IMO largely a skill issue, not taking the time to learn basic tools that they need everyday; otherwise I have no clue why software developers do not learn their IDEs and VCS tools very well). Stacking PRs requires some Git skills, a simple feature-branch workflow can be a challenge for many..
Ultimately, I think the solution to stacked PRs is to change review policy to "ship/show/ask": https://martinfowler.com/articles/ship-show-ask.html
In other words, if someone is skilled enough to do a set of stacked PRs, the team likely benefits by letting that person merge the stack on their own when each bit is ready and do a post-merge review instead of pre-merge.
(Side-note, my unsolicited perspective: I'm personally convinced that the benefits of linear history is a magnitude more important than all the other peeves & nits combined between merge vs rebase.)
Are people sending multiple branches of the stack for review at once? It should only ever be the "bottom" branch out-for-review at any time.
> - Namely there would be review comments on the first PRs that then cause a cascade of merge conflicts in the follow-on PRs
This can still happen in the model above of course as you need to make changes to the bottom branch in response to review comments/requests.
However, as I noted elsewhere, `--update-refs` is an absolute god-send in those situations: https://andrewlock.net/working-with-stacked-branches-in-git-...
It reduces a ton of manual work (scaled by how many branches you have stacked!) to one operation.
I can think of quite a few additional concerns. Overall I think it comes down to how the team wants to handle code reviews.
Personally, I do think if the team is at the level to coordinate and execute on a stack list of PRs, there is little need to incur extra round-trip times for "reviewing" precursor changes and instead focus review time where it is explicitly wanted.
Though, I do indeed like stacked PRs over commit-list because there is more incremental progress, but it does come with some costs. For example, perhaps the last reviewer does not like the overall direction that the cumulative work has led to.
My experience is that at that rate, it's best to let teams decide how they want to operate, formalize somewhat how things are shipped, and bias towards shipping. On the other end of the spectrum, a person quickly glancing at refactoring updates, not having good context on how a given PR fits in - it can almost put into question whether CR itself is entirely a best practice. Hence, I'm a fan of "ship/show/ask". I think it mostly does away with the need for stacking PRs with very little downside (and upside of greater efficiency, CR is spent time reviewing more important code, things that benefit from CR and use the reviewers time well, and makes for a better flow for the author since they can readily merge).
And if you have atomic commits, then merge commits generally aren't helpful. As others have pointed out, what matters is when the code was added and what it does, which becomes very clear if you have a linear history without merge commits (but atomic commits as much as possible).
If during the development process your commits aren't atomic, that is fine, but then you should make them atomic with squashing before they actually get rebased into main.
So for example if I'm in a personal dev branch and I have "messy fix feat A", "fix feat A but better", "even better feat A", and "add feat B", I should just squash the first three into "Improve feat A" and leave the fourth alone as "Add feat B". Then after a PR review or tests or whatever you just rebase into main. Now there is still a clear delineation between my fixes to A and my addition of B, without forcing me to make separate PRs for highly related features. I end up with:
- clean history
- atomic commits
- limited mental overhead when doing quick and dirty commits
- not forcing atomic PRs which can be overwhelming
I sometimes wish I could require squash merging to main so that history on main is fully linear -- however the fatal flaw with doing that is that it becomes impossible to know from git history whether a given branch has actually been merged to main or whether it was abandoned and left to dangle and rot forever. The inability to observe merge state for a given commit increases the effort required to know whether some given piece of work was merged (or at least requires using mechanisms that are not native to git to make such a determination) and complicates cleaning up old local branches after they are merged. If you use a graphical git client then seeing a brunch of old local branches that are already merged everywhere is noisy and distracting. When real merges are done then can easily write a script to remove all the branches that are already merged to main which helps to reduce noise and maintain better focus and faster navigation.
https://github.com/mystor/git-revise
Its inability to change your worktree (operates in memory instead) is a big speed and safety feature: It won't invalidate your build, and you can't screw up the end state.
It also has features that regular rebase lacks, like splitting a commit and editing all commits at once. I'm more than a big fan of it.
Before I started regularly looking up that ‘blame’, I also didn’t care too much about got history. Now, I put some effort into making my own history useful to everyone (including future me).
"Hey something went wrong with X service"
Find commit like "add y to x service'
It's a time saver to me. Plus if the bug is extra tricky, you can ~easily~ revert or pick it out.
Rebasing is excellent. It's a whole suite of tools, unlike merge, which is just merge, for managing history. Rebasing is how I build a sensible history.
I should see if there's some way to set --committer-date-is-author-date by default. Also haven't found a way to make git pull --rebase do c-d-i-a-d.
Gerrit solves the referencing issue by using git notes and maintaining its own ID for changesets across rebases. Without such a system, some seeming benefits of the rebase workflow, like atomically reviewable stacked commits, become fairly awkward. IE, if we want to review commits themselves, but their references get clobbered due to a rebase, that's no good (this was the case a few years ago with GH's rebasing support, maybe improved since).
IMO, that we have to make the "rebase or merge" tradeoff at all, or accept the deep limitations of pure commit-based history, is fairly sub-optimal. It'd be nice if more tooling/workflows were built more around notes. I envision someone/some bot going back and annotatinb a range of commits with a note that associates them into some coherent code documentation system, or amends some faulty assertion, links artifacts, etc. That way blame could take us to salient docs or surface behavior snapshot gifs or w/e. From directly within IDEs
This would free up developers to spend more time solving problems for the business instead of tidying up code.
Could also delay doing this to when somebody is trying to understand the code (during review or later) or doing a bisect.
Then you run `git rebase -i --autosquash origin/main` instead and the commits are already in the right order.
Explain?
Keeping things neat in the repo has gained me far more than I ever theoretically lost by some conceptual "loss" of history.
I'm sure there's even more clever ways to do this, as it always seems like there's more when it comes to git. This is just the most intuitive way I've seen so far, and so it sticks in my mind.
- easier to read/analyze
- have better documentation (more isolated git metadata/history)
- are easier to debug (and possible to bisect; it's not possible to bisect a squashed commit).
The problem is that it requires a significant amount of discipline (of course, 100% rate of atomic commits is not possible, but high rate is).
In practice, if bisecting indicates a commit caused the problem you can narrow it down further by checking out that branch and investigating further within that branch (which ideally isn't too large). Also, if you have a highly structured PR then you may need to be careful to ensure that each individual commit passes CI.
At that point you might as well ship each commit via a separate PR. (While in development you can temporarily set the branch for Part 1 as the base for Part 2.)
A PR for each commit seems overkill. Usually a PR is for a feature, but code-wise, a feature might require several atomic changes. For example: prepare configuration, move files around, add tests, add new code, delete old code, refactor. Each of those could be a separate commit, bringing you to a polished feature with test coverage.
I think splitting PRs into multiple commits can make sense when there are only 2-3 commits but you're right that it doesn't when there are more than that.
Absolutely. Indeed, this (independent commit) is a very positive side effect (typical example: separating refactoring commits).
Also is a lot faster to write and easier to communicate compared to messing around with moving code across commits, ensuring tests pass across them, etc.
Basically I think people care more about what was built and how it works rather than how to split it up step by step (although if the latter is important I can add a comment for that on the PR - although ideally it would have been separate PRs.)
That vanishes when you shift forges
> The squash branch on merge strategy is just lazy
"Lazy" is the negative way of saying "easy" or even "more efficient". "Lazy" implies that the other way is better. Is it? Maybe sometimes.
Am I "lazy" if I walk on my feet instead of my hands? It really is a lot easier.
My biggest issue is that if you have to delay a feature then the sheer amount of conflicts you have to resolve (many of which you had nothing to do with) becomes prohibitive.
I have no problems with rebasing master or a published branch - thats the release managers problem to deal with everything on but I really dont know where this "rebase everything" comes from
The rare exception is when you have multiple people contributing to a branch before it is merged into master, but for obvious reasons you should avoid that when at all possible.
A bold claim.
> but for obvious reasons you should avoid that when at all possible.
Obvious? I do this somewhat regularly and haven't had a problem. I don't even know what the problem is supposed to be.
Also, I never rebase. Maybe that's why I don't have a problem? When you rebase, commit A becomes commit A'. This becomes a problem when you need to determine whether a particular commit is an ancestor or not. A and A' have different commit hashes, so the identity of the commit becomes somewhat ambiguous.
My workflow is do all development using merge only. When the code review is complete, squash to master. I have found no down sides to this. But I'm not trying to sell it to you either.
I appreciate that anyone saying this is probably already on the same wavelength as me, but I don't find this to be true for myself. Many times, complex application features end up represented as a series of related, but atomic/meaningful changesets. I want the pull request to make sense as a whole, but expect that code review or audits are easier to accomplish diff-by-diff.
I see it like building a recipe. I want to hide all of the false starts, sloppy mistakes, do-overs, and checkpoints, because they are useless from a historical standpoint. But I still may want to publish a sequence of changes that accomplishes something.
I tend to checkout branches to do code reviews, when changes are complex a diff is not enough, I want an IDE to help me reason about the code (I sometimes even refactor and try different things just so I can understand the code better, so I can make good suggestions).
When I do this, and comment, and the other dev rebases, it's an extra step for me on the next CR cycle.
Also, we tend to avoid having multiple people work on the same branch.. so, that's also a thing.
If there was a multi-person development effort, then each of those people would have to have a sub-branch of a main feature, and then they would be rebasing their work onto the 'main' feature branch.. which would ultimately be rebased on to dev.. etc.
The issue is another: git does next to nothing to handle forks. We can cherry pick some commits, but there is no easy way to state "we have a project that a certain point in time diverge, keeping some common base, that need to be kept in sync, ignoring the fork specific changes". This led to long and manual three-way merges. No extra help.
Usually I just look at the file history, find the work item from the commit message and then understand _why_ the code is the way it is. That is the functionality I need to have in mind when changing the file. Who cares how it came to be?
Git workflows imposed by those that don't understand git well enough is one of the most annoying things.
I understand rebase vs merge invokes strong feelings (including in myself), which is exactly why imposing them on others is extremely annoying. Perhaps we just do as we like, but don't presume to know better than everyone else?
You can also use `git log main..` to show the commits in HEAD but not in main. I prefer the order of the arguments in the OLD..NEW syntax for the commit range (OLD, NEW] over the reversed order with separate arguments.
Squash merge to main always.
Problem solved.
Diffs are what matter--not commits.
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup [-C | -c] <commit> = like "squash" but keep only the previous
...the CLI git text-document rebase UI is awful. Better command-line rebase UIs are available, or in Fork, I just select a bunch of tweets, right-click, and squash into parent (or use the https://git-fork.com/images/interactiveRebase.jpg where necessary).If someone complains that CLI git is somehow more pure than using a decent GUI, ask them to rename a stash and time them.
On another note, is it possible not to lose the commit description when trying to split a commit? I'm talking about 1) marking commit as `e` to edit it 2) git reset HEAD~ to move back 3) split the change however you want 4) commit again whenever I do it I always lose the original commit message. I'm not sure if it's me being stupid or if there is just no simple way to do it.
There's 2 cases: 1) extract changes into a commit that comes before the original commit, and 2) extract changes into a commit that comes after the original commit.
In both, instead of editing the commit you want to split, `break` before that commit, and then use `git checkout <hash_of_commit_to_edit> <path_to_files_of_interest>` to pull the changes of interest out into an new, earlier commit. `git checkout -p` is worth a look here. Alternatively if the changes are simple enough, you could use exec instead of break before the target commit.
For 1) you can commit those extracted changes with a new message and then `git rebase --continue`. The original commit will then lack the extracted changes, and have the original commit message. If you did want to adjust it, reword that commit.
e.g.
pick c62dfe67 1
pick 63dcd748 2
exec git checkout eea68bb8 three.txt && git commit -m "3"
reword eea68bb8 3+4
For 2) reference the target commit's message as the new, earlier commit's message. Keep in mind that the git invocation in this exec here still supports git aliases so if this was something you do often, you could create an alias for retrieving the next commit message and that last part of the exec could just be `.. && git getnextcommitmessage`e.g.
pick c62dfe67 1
pick 63dcd748 2
exec git checkout eea68bb8 four.txt && git commit -m "$(git log -1 eea68bb8 --format="%B")"
reword eea68bb8 3+4~/.gitconfig
[sequence]
presentation-order-head-on-top = true
Branch: https://github.com/anordal/git-revise/commits/rebasehappy-in...