What Changes the Most
jj log --no-graph -r 'ancestors(trunk()) & committer_date(after:"1 year ago")' \
-T 'self.diff().files().map(|f| f.path() ++ "\n").join("")' \
| sort | uniq -c | sort -nr | head -20
Who Built This jj log --no-graph -r 'ancestors(trunk()) & ~merges()' \
-T 'self.author().name() ++ "\n"' \
| sort | uniq -c | sort -nr
Where Do Bugs Cluster jj log --no-graph -r 'ancestors(trunk()) & description(regex:"(?i)fix|bug|broken")' \
-T 'self.diff().files().map(|f| f.path() ++ "\n").join("")' \
| sort | uniq -c | sort -nr | head -20
Is This Project Accelerating or Dying jj log --no-graph -r 'ancestors(trunk())' \
-T 'self.committer().timestamp().format("%Y-%m") ++ "\n"' \
| sort | uniq -c
How Often Is the Team Firefighting jj log --no-graph \
-r 'ancestors(trunk()) & committer_date(after:"1 year ago") & description(regex:"(?i)revert|hotfix|emergency|rollback")'
Much more verbose, closer to programming than shell scripting. But less flags to remember.Not meaning to offend anyone: Nix is cool, but adds complexity. And as a disclaimer: I used jujutsu for a few months and went back to git. Mostly because git is wired in my fingers, and git is everywhere. Those examples of what jujutsu can do and not git sound nice, but in those few months I never remotely had a need for them, so it felt overkill for me.
You can find this pattern again and again. How many redditors say 120fps is essential for gaming or absolutely require a mechanical keyboard?
The most frequent "complex" command I use is to find commits in my name that are unsigned, and then sign them (this is owing to my workflow with agents that commit on my behalf but I'm not going to give agents my private key!)
jj log -r 'mine() & ~signed()'
# or if yolo mode...
jj sign -r 'mine() & ~signed()'
I hadn't even spared a moment to consider the git equivalent but I would humbly expect it to be quite obtuse.Does "own" try to sign working copy snapshot commits too? That would greatly increase the number and frequency of signatures.
> Does "own" try to sign working copy snapshot commits too?
Yes
At this point perhaps a million person-years have been sacrificed to the semantically incoherent shit UX of git. I have loathed git from the beginning but there's effectively no other choice.
That said, the OP's commands are useful, I am copying them (because obviously I won't ever memorize them).
I wrote a cheat sheet in my notes of common commands, until they stuck in my head and I haven't needed it now for a decade or more. I also lean heavily on aliases and "self-documenting" things in my .bashrc file. Curious how others handle it. A search every time I need to do something would be too much friction for me to stand.
Yes! We mostly wouldn’t tolerate the complexity and the terrible UX of a tool we use everyday--but there's enough Stockholm Syndrome out there where most of us are willing to tolerate it.
https://github.com/denisidoro/navi
But for Git, I can't recommend lazygit enough. It's an incredible piece of software:
Or, perhaps better yet, defining your own functions/helpers as you go for things you might care about, which, by virtue of having been named you, are much easier to remember (and still compose nicely).
We can't.
Why do you think the `man` command exists?
Don't feel bad - no one remembers them all, we just remember a few idioms we use...
If you have multiple machines (/must have), just apply your user config to current machine?
I often feel I need to setup bots to make superfluous commits just to make it look like my useful and stable repos are “active”
One example (not mine) a a qr-code generator library. Hasn’t been updated in 10 years. It’s perfect as is. It just provides the size and the bits. You convert those bits to any representation you want. It has no need to be updated
It's not impossible, of course, but if I saw even a qr library that hadn't changed in 10 years I would worry that it wouldn't build on current systems (due to dependencies) and that nobody was actually using it (due to lag of bug reports).
A QR (or barcode) library is exactly the type of thing I’d assume would still work fine, since there’s nothing new to do, the parsing rules don’t change, it’s a static, known, solved problem.
I agree with you - and yet the barcode library I used recently for a variable-data-printing project was last updated 13 hours ago, despite having been around since 2008!
Agreed. Assuming there are no open issues and PRs. When I find a project, if the date of the last commit is old, I next look at the issues and PRs. If there are simple-to-deal-with issues (e.g. a short question or spam) and easy-to-merge PRs (e.g. fixing a typo in the README) which have been left lingering for years, it’s probably abandoned. Looking at the maintainer’s GitHub activity graph should provide more clues.
> I often feel I need to setup bots to make superfluous commits just to make it look like my useful and stable repos are “active”
I have never done it, but a few times thought about making a “maintenance” release to bump the version number and release date, especially since I often use a variant of calendar versioning.
A language that properly maps to the data model, and has readable identifiers is a boon. Git is a database, a database needs a proper query language.
`git log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --`
Which is something I see a lot of people alias in Git for viewing logs.
Disclaimer: I love jq too :)
All joking aside, it really is a chronic problem in the corporate world. Most codebases I encounter just have "changed stuff" or "hope this works now".
It's a small minority of developers (myself included) who consider the git commit log to be important enough to spend time writing something meaningful.
AI generated commit messages helps this a lot, if developers would actually use it (I hope they will).
And I really like that because it leaves room to let the developer do whatever kind of commit messages they want to that makes sense to them. Because nobody's really ever going to read those again after it squashed and merged.
If I joined a company where people committed their code with "stuff" or "made some changes" or "asdfhlfo;ejfo;ae," that would be a red flag that I might have joined the wrong company, and I'd start to wonder what else the developers here do carelessly.
These things never take much time but people dismiss them because of that. Because each commit and each comment in isolation isn't very valuable but they are very helpful in aggregate. I'm not sure why this bias exists though, since the same is true for lines of code. It's also true about a ton of things. All the little things add up. Just because it's little now doesn't mean it's not important
So we told him to commit at least once every day, with a relevant commit message, or else fail his internship.
He worked 21 more days. There were 21 commits: "17:00, time to go home".
In the company I work for, there is a team that has isolated itself to some extent from other teams and works at a furious pace to keep their particular section of the business happy. We're lucky enough that they spun up their own repo to do their work on, so they don't actually impact other teams, but if the quality of the commit messages is anything to go by, I am 100% certain they're going to end up in a huge mess, if they aren't already. The team lead encourages this, and certainly doesn't care about commit messages etc.
Developers who care about other developers tend to write better quality code, because they care what other developers think of them. If you care about other developers, you will most likely write decent quality commit messages too.
I have seen over many years the types of developers who only care about moving their own code into production as fast as possible and getting to the next thing. There is a very high correlation with a mess at the end, which they inevitably won't have to tidy up because they'll be doing the next thing. These types of developers hate owning stuff in production, so they don't do it, so they don't actually care how maintainable their "clever" code is. I am very certain that a number of people reading this will be those types of developers.
As a manager, one of the first things I do is make sure that the PR titles (the PR text becomes the commit messages in squash-merging workflows) at minimum begin with a ticket number. Then we can later read both the intention and the commentary on it.
Personally no, the code is the "truth". If I need more I'm going to open a dialog with the author, not spend time trying to interpret a 7 word commit message, "good" or otherwise.
And for old enough code, the author may not be available, or more likely doesn't remember.
It feels like we're trying really hard to stretch the utility of commit messages here...
I don't feel that is an accurate statement for any complex system.
To the point of other commenters however, I wouldn't lay something this micro at the foot of the CTO in all but the smallest of organizations.
The gold standard is rebased linear unsquashed history with literary commits, but I'll take merged and squashed PR commits with sensible commit messages without complaint.
I understand this isn't inline with traditional git scm, but it's a very powerful workflow if you are OK with some hybridization.
One minor nuisance that can come up with GitHub in particular when using a short reference like #123 is that that link breaks if the repo is forked or merged into something else. For that reason, I try to give full-url references at least when I'm manually inserting them, and I wish GitHub would do the same. Or perhaps add some hidden metadata to the merge commit saying "hey btw that #123 refers to <https://github.com/fancy-org/omg-project/issues/123>"
They might not include anything but the Jira ticket number, if the environment is truly lacking.
I have actually seen proper capitalization and correct conventional-commit types to correlate very well with the author being intentional and the patch being of good quality.
e.g.
- (a) chore: update some_module to include new_func
- (b) feat: Add new_func to handle XYZ case
Where:
(a) is not a chore, as it changes functionality, is uncapitalized and is so low-signal I can probably write a 10 line script to reliably generate similar titles.
(b) is using the correct "feat" commit type, capitalized and describe what this is for. I expect the body to explain "why", as well, and not to reiterate the "how" in natural language.
This is just my experience, but I've seen commit messages where people actually put in some effort to usually come with a good patch, and vice-versa.
Is it really a small minority? I have never worked on a project that didn't have commit messages that at least tried to be descriptive (sometimes people fail at it but its very different to an outright "changed stuff").
I don't remember any friend mentioning to me them encountering a work project where the messages were totally neglected either.
main branch is advanced on PR level, with squashed commits.
So the "." should never make it to main, and have PR description as commit message.
relying on git commit messages assumes they're correct by convention since there is no technical constraint to enforce it. and it assumes no work in progress commits, sometimes it's just necessary to hit the save button real quick or move a workspace from one device to another.
my point is: git is a way of storing and loading files at its core.
git log --oneline and a sprinkle of your personal sauce on .claude goes a long way :)
Random, subjective, or written in a state of mental exhaustion commit messages.
I also love the switcheroo the author made: git not logs. But hey :)
For small personal projects I often write one phrase messages with `-m`, but if you're working with other people you should be writing good commit messages.
> git shortlog -sn --no-merges
Is the most egregious. In one codebase there is a developer's name at the top of the list who outpaced the number 2 by almost 3x the number of commits. That developer no longer works at the company? Crisis? Nope, the opposite. The developer was a net-negative to the team in more ways than one, didn't understand the codebase very well at all, and just happened to commit every time they turned around for some reason.
This leaves developers to commit locally and comment as much or little as they like.
Assuming I'm not ego-mad, I like to think this is because I built the project from the ground up before handing it over to the rest of the team.
These days other people commit more often than I do, but my name is still dominant, and probably will be for some time.
If someone came to me and said "I ran these and I see XXX was the most prolific committer and they left X months ago, what will be do???" I'd have to work hard not to laugh.
Since these snippets are self-described as ways to get familiar with the code/projects I wanted to provide the counter point. Most of those snippets do not at all paint the real picture and for all the repos I tested it on they paint the opposite of reality.
I know these codebases like the back of my hand, the purported purpose of these snippets is to better understand the codebase, I can tell you they don't work for anything I tested them on. Maybe they work for other codebases but the sample size I have access to says they don't work for me.
The most changed file is the one people are afraid of touching?
It shows places that are problematic much better. High churn, low complexity: fine. Its recognized and optimizef that this is worked on a lot (e.g. some mapping file, a dsl, business rules etc). Low churn high complexity: fine too. Its a mess, but no-one has to be there. But both? Thats probably where most bugs originate, where PRs block, where test coverage is poor and where everyone knows time is needed to refactor.
In fact, quite often I found that a teams' call "to rewrite the app from scratch" was really about those few high-churn-high-complexity modules, files or classes.
Complexity is a deep topic, but even simple checks like how nested smt is, or how many statements can do.
I ran this on the repo I have open and after I filtered out the non code files it really can only tell me which features we worked on in the last year. It says more about how we decided to split up the features into increments than anything to do with bugs and “churn”.
Very different from just counting commits - https://vectree.io/c/delta-compression-heuristics-and-packfi...
I’m not sure why HN attracts this need to poke holes in interesting observations to “prove” they aren’t actually interesting.
Nobody is afraid of changing it.
# summary: print a helpful summary of some typical metrics
summary = "!f() { \
printf \"Summary of this branch...\n\"; \
printf \"%s\n\" $(git rev-parse --abbrev-ref HEAD); \
printf \"%s first commit timestamp\n\" $(git log --date-order --format=%cI | tail -1); \
printf \"%s latest commit timestamp\n\" $(git log -1 --date-order --format=%cI); \
printf \"%d commit count\n\" $(git rev-list --count HEAD); \
printf \"%d date count\n\" $(git log --format=oneline --format=\"%ad\" --date=format:\"%Y-%m-%d\" | awk '{a[$0]=1}END{for(i in a){n++;} print n}'); \
printf \"%d tag count\n\" $(git tag | wc -l); \
printf \"%d author count\n\" $(git log --format=oneline --format=\"%aE\" | awk '{a[$0]=1}END{for(i in a){n++;} print n}'); \
printf \"%d committer count\n\" $(git log --format=oneline --format=\"%cE\" | awk '{a[$0]=1}END{for(i in a){n++;} print n}'); \
printf \"%d local branch count\n\" $(git branch | grep -v \" -> \" | wc -l); \
printf \"%d remote branch count\n\" $(git branch -r | grep -v \" -> \" | wc -l); \
printf \"\nSummary of this directory...\n\"; \
printf \"%s\n\" $(pwd); \
printf \"%d file count via git ls-files\n\" $(git ls-files | wc -l); \
printf \"%d file count via find command\n\" $(find . | wc -l); \
printf \"%d disk usage\n\" $(du -s | awk '{print $1}'); \
printf \"\nMost-active authors, with commit count and %%...\n\"; git log-of-count-and-email | head -7; \
printf \"\nMost-active dates, with commit count and %%...\n\"; git log-of-count-and-day | head -7; \
printf \"\nMost-active files, with churn count\n\"; git churn | head -7; \
}; f"
EDIT: props to https://github.com/GitAlias/gitaliasgit log -i -E --grep="\b(fix|fixed|fixes|bug|broken)\b" --name-only --format='' | sort | uniq -c | sort -nr | head -20
I have a project with a large package named "debugger". The presence of "bug" within "debugger" causes the original command to go crazy.
In my experience, when the team doesn't squash, this will reflect the messiest members of the team.
The top committer on the repository I maintain has 8x more commits than the second one. They were fired before I joined and nobody even remembers what they did. Git itself says: not much, just changing the same few files over and over.
Of course if nobody is making a mess in their own commits, this is not an issue. But if they are, squash can be quite more truthful.
If the commit frequency goes down, does it really mean that the project is dying? Maybe it is just becoming stable?
git log --format='%ad' --date=format:'%Y-%m' | sort | uniq -c | awk '{printf $2" "; for (i=1;i<=$1;i++){printf "-";} print ""; }'
If you work with service oriented software, the projects that are "dying" may very well be the most successful if it's a key component. Even from a business perspective having to write less code can also be a sign of success.
I don't know why this was overlooked when the churn metric is right there.
Whenever we initiated a new (internal) SW project, it had to go through an audit. One of the items in the checklist for any dependency was "Must have releases in the last 2 years"
I think the rationale was the risk of security vulnerabilities not being addressed, but still ...
I (largely) wrote a corporate application 8 years ago, with 2 others. There was one change 2 years ago from another dev.
Lots of programs are functionally done in a relatively short amount of time.
"Accelerating or Dying" sounds like private equity's lazy way to describe opportunity, not as a metric to describe software.
I know people win the lottery every week, but I also believe that buying a lottery ticket is essentially the same as losing. It's the same principle.
> How Often Is the Team Firefighting
> git log --oneline --since="1 year ago" | grep -iE 'revert|hotfix|emergency|rollback
> Crisis patterns are easy to read. Either they’re there or they’re not.
I disagree with the last two quoted sentences, and also, they sound like an LLM.
The git blame tip is underrated. People treat it like a gotcha tool but its maybe the fastest way to find the PR/ticket that explains a weird decision.
and it touches in detail what exactly commit standards should be, and even how to automate this on CI level.
And then I also have idea/vision how to connect commits to actual product/technical/infra specs, and how to make it all granular and maintainable, and also IDE support.
I would love to see any feedback on my efforts. If you decide to go through my entire 3 posts I wrote, thank you
Most good projects end up solving a problem permanently and if there is no salary to protect with bogus new features it is then to be considered final?
I abhor squash merging for this and a few other reasons. I literally have to go out of my way to re-check out a branch. Someone who wants to use my current branch cannot do so if I merge my changes a month later, because the squash rewrites history, and now git is very confused. I don't get the obsession with "cleaning up the history" as if we're all always constantly running out of storage over 2 more commits.
And GitHub at least sets the author of the squashed commit as the one who opened the PR, not the one who merged it.
I can definitely see where it wouldn't work well for other workflows but I've had it work well on several teams and it seems easier than trying to get everyone to clean up their commits into nice, clean, well-titled histories before putting up a PR.
That probably isn’t a good sign
Squash-merge workflows are stupid (you lose information without gaining anything in return as it was easily filterable at retrieval anyway) and only useful as a workaround for people not knowing how to use git, but git stores the author and committer names separately, so it doesn't matter who merged, but rather whether the squashed patchset consisted of commits with multiple authors (and even then you could store it with Co-authored-by trailers, but that's harder to use in such oneliners).
With a squash merge one PR is one commit, simple, clean and easy to roll back or cherry-pick to another branch.
1. Open PR page in whatever tool you're using
2. Read title + description to see what's up
3. Swap to diff and start reading through the changes
4. Comment and/or approve
I've never heard anyone bothering to read the previous commit messages for a second, why would they care?
* A method or function that has code you realize needs to be shared...the code may need to be moved and also modified to accommodate its shared purpose. Separating the migration from any substantive modifications allows you to review the migration commit with the assistance of git's diff.colorMoved feature. It becomes easier to understand what changes are due to the migration, and what changes were added for more effective sharing.
* PRs sometimes contain mechanical work that is easy to review in isolation. Added or removed arguments, function renames, etc. No big deal if it's two or three instances, but if it's dozens or hundreds of instances, it's easier for the humans to review all of those consistent changes together, rather than having them mixed in with other things one has to reason about.
* Sometimes a flow of commits can help follow a difficult chain of reasoning. PR developer claims that condition X can never occur, but the code is complex enough that it's difficult to verify. However, by transforming the code in targeted ways that are possible to reason about, the complexity might be reducible to the point where the claim becomes obvious. One frequent example I see of this is of function/method arguments that are actually unnecessary, but it wasn't obvious until after some code transformations.
Having atomic commits lets you actually benefit from having them. Suddenly you don't have to perform weird dances with interconnected PRs with dependencies as "PR too big" is not such a problem anymore as long as commits are digestible; you can have things property bisectable; you can preserve shared authorship; you can range-diff and have a better view on what and how changed between review passes, and so on...
The unit of change is commit, and PRs group commits you want someone to pull. If you don't want or need any of that, you're just sending a patch file in a needlessly elaborate way.
This is in fact what hg does with amending changesets and yes it works far better. Keep PRs small and atomic and you never need to worry about what happens intra-pr. If you need bigger units of work that's what stacking is for.
A PR is a group of commits, just utilize that when you need it.
this forces the reviewer to view the entire diff at once, which can greatly increase the cognitive load vs. being able to view diffs of logical units of work
for tiny PRs it may not matter, but for substantial PRs it can matter a lot
I meant "shared place" as an open review request or a shared branch rather than shared underlying infrastructure. Shared by people's minds.
It just feels wrong to force push, destroying stuff that used to be there.
And I don't have the time or energy to bisect through my shitty PR commits and combine them into something clean looking - I can just squash instead.
Things that aren't referenced by anything anymore will eventually get garbage collected and actually destroyed, but you can just keep a reference somewhere to prevent that from happening if you need. Or even disable garbage collection completely.
Looks like people's fears about git come just from not knowing what it does.
it's hard to say fully, but unless a changeset is quite small or otherwise is basically 0% or 100%, there are usually smaller steps.
like kind of contrived but say you have one function that uses a helper. if there's a bug in the function, and it turns out to fix that it makes a lot more sense to change the return type of the helper, you would make commit 1 to change the return type, then commit 2 fix the bug. would these be separate PRs? probably not to me but I guess it depends on your project workflow. keeping them in separate commits even if they're small lets you bisect more easily later on in case there was some unforseen or untested problem that was introduced, leading you to smaller chunks of code to check for the cause.
I've never considered how an engineer approaches a problem. As long as I can understand the fundamental change and it passes preflights/CI I don't care if it was scryed from a crystal ball.
This does mean it is on the onus of the engineer to explain their change in natural language. In their own words of course.
I might have not stated my position correctly. When I mean "squash on merge", I mean the commit history is fully present in the PR for full scrutiny. Sometimes commits may introduce multiple changes and I can view commit ranges for each set of changes. But it takes the summation of the commits to illustrate the change the engineer is proposing. The summation is an atomic change, thus scrutinizing terms post-merge is meaningless. Squashing preserves the summation but rids of the terms.
Versioned releases on main are tagged by these summations, not their component parts.
Then I must be a bad reviewer. In a past job, I had a colleague who meticulously crafted his commits - his PRs were a joy to review because I could go commit by commit in logical chunks, rather than wading through a single 3k line diff. I tried to do the same for him and hope I succeeded.
I feel like what you're arguing is that you should clean up your commits before anyone else sees them. Fair. But you could also clean it up right before merging to main. It's not that different, except the latter is much less annoying, particularly when going back and forth with people.
I know this is a very github centric workflow, but that's where most engineers work now, and it's nice and easy. This wouldn't work for eg: contributing to linux, but that's not what most of us do.
I think in most teams I've worked with, the majority of developers (> 85%) barely undestand what Git is doing or what things mean inside GitHub, have never seen commit history as a graph, have never run something like "git log --oneline --graph --decorate" or "--format", and have never heard of "git range-diff" which is very useful for following commit/PR/unit changes.
Personally I review using "git" itself, so I see the graph structure either way, and there's little difference between stacked PRs, commit chains in a single PR, or even feature branches, from that point of view. Even force-push branch updatea aren't difficult to review, because of the reflog and "git range-diff". The differences are mainly in what kinds of behaviour the web-based tooling promotes in the rest of the team, which does matter, and depends on the team.
I agree with you if you're using Graphite instead of GitHub. Having a place to give feedback and/or approval on the individual "units" (commits in a PR, or PRs in a stack) is useful, grouping dependent but distinct changes is useful, and diff'd commit evolution within each unit PR in response to back-end-forth review feedback is useful in some collaborative settings. Though, if you know "git range-diff" and reflog, that shows diff'd commit evolution quite well.
In GitHub, people are confused by stacked PRs both conceptually and due to the GitHub UX around them. Most times when I've posted a stacked PR to a GitHub project, other people didn't realise it was stacked, and occasinally someone else has merged the tip of a stack made by me, and been surprised to see all the dependent PRs merged automatically as a side effect. Usually before they get to reviewing those other PRs :-)
People understand commit sequences in a PR, though I've rarely seen people treat the individual commits as units for review when using GitHub, unfortunately. In the Linux kernel world where Git was born, the PR flow is completely different from GitHub: Their system tends to result in feedback on individual commits. It also encourages better quality feedback, with less nitpicking, and better quality commits.
Not often, but given that it costs me nothing to have it all in my tree, I'd rather have it than not.
And here's a slightly smaller one which isn't about "miscellaneous fixes": https://lore.kernel.org/netdev/20260408122027.80303-1-xuanzh...
Some of these commits even get reviewed by different maintainers before being merged, which is common when a patchset touches several subsystems at once.
For other cases, you lose the information about why things are this way. It's too verbose to //comment on every like with how it came to be this way but on (non-rare in total, but rare per line) occasion it's useful to see what the change was that made the line be like this, or even just who to potentially ask for help (when >1 person worked on a feature branch, which I'd say is common)
I use it like that too and yet the reviewers don't get to see these commits. Git has very powerful tools for manipulating the commit graph that many people just don't bother to learn. Imagine if I sent a patchset to the Linux Kernel Mailing List containing such "fix typo", "please work now", "wtf" patches - my shamelessness has its limits!
When I work on something, I commit often and use the commit graph as a undo tool on steroids. I can see what I tried, I can cherry-pick or revert stuff while experimenting, I can leave promising but unfinished stuff to look at later, or I can just commit to have a simple way to send stuff to CI, or a remote backup synced between machines.
Once I'm done working on something, it's time to take a step back, look at the big picture, see how many changes my experiments have actually yielded, separate them, describe and decide whether they go to review together or split in some way, as sometimes working on a single thing requires multiple distinct changes (one PR with multiple commits), but sometimes working in a single session yields fixes for multiple unrelated issues (several PRs). Only then it gets presented to the reviewer.
It just happens that I can do both these distinct jobs with a single tool.
I simply commit something like "WIP: testing xy" and if its working and properly implemented i can squash/rebase/edit the commit message and force push it to my feature branch. Using a Git client like Gitkraken makes this incredibly easy, takes seconds.
This way I can leverage version control without committing bogus states to the final PR.
Part of new feature you had working in an intermediate commit, but broke somewhere along the way and is not working in your last commit when you squashed.
If you catch it early enough, I suppose it's in your reflog, but otherwise you're screwed.
It sounds like a silly example, but I bet most developers have run into this at some point.
With mercurial/jujutsu, you get the best of both worlds: The "argh, let's see if this works" commits are what I call "microcommits", and the squashed versions are the real/public commits. With jujutsu, you get both. Your log shows only the "real" commits (equivalent of squashing all the commits between that and the prior "real" commit). But if you want to drill down into the microcommits, the information is always there.
Let's acknowledge the reality. Many people use git not just for version control, but for backup ("Let me commit this so I don't lose it"). Let's ensure the VC tool supports both and doesn't force you to pick one over the other.
Can you explain to me what an avid squash-merger puts into the commit message of the squashed commit composed of commits "argh, let's see if this works", "crap, the CI is failing again, small fix to see if it works", and "pushing before leaving for vacation" ?
Usually pretty close to what the PR title + description are actually, just without the videos and screenshots.
Example:
feat(ui): Add support for tagging users
* Users can be tagged via the user page * User tags visible in search results (configurable)
etc..
I don't need to spend extra time cleaning up my git commits and force-pushing on the PR branch, losing context for code reviews etc. Nor does anyone have to see my shitty angry commits when I tried to figure out why Playwright tests ran on my machine and failed in the CI for 10 commits.
These are all bad commits IMHO. Aside from the CI one, I understand that message. I have commits like that on personal projects but for professional projects I'd be frustrated if people were committing messages like that.
Personally I'm a "one commit" type of guy, I don't like committing things in a broken state even on a side branch unless I have to (to share the code or test a CI). Occasionally I will make multiple commits at the very end to make review easier or once I have everything working but I want to try something different but I have a bunch of options of saving code that don't involving committing:
- Stash
- Shevle (IDEA)
- Backblaze
- Time Machine
- Local History (IDEA)
The idea of committing WIP before leaving for a vacation just feels so wrong to me.
I once worked for someone who wanted developers to commit code before the end of every day as a safety measure. His reasoning was in case the developer's computer died or similar. I found that silly at the time and still do now. That's what backups are for, I dislike when people use git as a backup like that in a professional setting.
If you update a PR with review feedback, you shouldn’t change existing commits because GitHub’s tools for showing you what has changed since your last review assume you are pushing new commits.
But then you don’t want those multiple commits addressing PR feedback to merge as they’re noise.
So sure, there’s workflows with Git that doesn’t need squashing. But they’re incompatible with GitHub, which is at least where I keep my code today.
Is it perfect? No. But neither is git, and I live in the world I am given.
And to counter some specific points:
* In a github PR, you write the main commit msg and description once per PR, then you tack on as many commits as you want, and everyone knows they're all just pieces of work towards the main goal of the eventually squashed commit
* Forcing a clean up every time you make a new commit is not only annoying extra work, but it also overwrites history that might be important for the review of that PR (but not important for what ends up in main branch).
* When follow up is requested, you can just tack on new commits, and reviewers can easily see what new code was added since their last review. If you had to force overwrite your whole commit chain for the PR, this becomes very annoying and not useful to reviewers.
* In the end, squash merge means you clean up things once, instead of potentially many times
If you want to dig down into the subcommits from a merge, then you still can. This is useful if you are going back and bisecting to find a bug, as those individual commits may hold value.
You can also cherry pick or rollback the single merge commit, as it holds everything under it as a single unit.
This avoids changing history, and importantly, allows stacked PRs to exist cleanly.
I think this is all Github's fault, in the end, but I think we need to get Github to change and until then will keep using squash-merges.
Yeah, I can imagine it being annoying that sqashing in that case wipes the author attribution, when not everybody is doing PRs against the main branch.
However, calling all squash-merge workflows "stupid" without any nuance.. well that's "stupid" :)
* git merge ALWAYS does a merge and git pull ALWAYS does a fast forward.
* git log --first-parent is the default. Have a git log --deep if you want to go down into branches.
If you use a workflow that always merges a PR with a merge commit, then git log --first-parent gives you a very nice linear history. I feel like if this was the default, so many arguments about squashing or rebasing workflows wouldn't be necessary to get our "linear history", everyone would just be doing merges and be happy with it. You get a clean top level history and you can dig down into the individual commits in a merge if you are bisecting for a bug.Also I can understand not squashing if the contribution comes from outside the organization. But in that case, I would expect a cleaned up history. But if every contribution is from members of the team, who can merge their own PR, squash merge is an easy way to get a clean history. Especially when most PR should be a single commit.
The feature is represented by a Story in Jira and a feature branch for that story. Subtasks in jira are created and multiple developers can pick up the different subtasks. There is a personal branch per subtasks, and PRs are put up against the feature branch. Those subtasks are code reviewed, tested, and merged into the feature branch.
In the end, it is the feature branch that is merged (as a single merge commit and complete unit) into main, and may well have had contributions from multiple people.
There can be experiment on the side that warrants your approach, but the amounts of merge going back and forth would make this hard to investigate (especially when blaming) I would prefer to have one single commit with a message that describe every contribution.
For really large PRs, I’m more inclined to agree with you, but those should probably have their own small-PR-and-squash-merge flow that naturally cleans up their git history, anyway.
I categorically disagree that squash-merge is “stupid” but agree there are many ways to skin this cat.
If you've worked on a large team without squashing and without increasing frustration I'd be greatly interested to hear about it.
For the "bus factor", there's one guy and then there's me, but I stopped being a primary contributor to this project nearly two years ago, lol.
git clone --depth 1 --branch $SomeReleaseTag $SomeRepoURL
If you only want to build something, it only downloads what you need to build it. I've probably saved a few terabytes at this point!
This list is also one of many arguments for maintaining good Git discipline.
The takeaway from my experiment is that you can really tell a lot by how / when / what people commit, but conclusions are very hard to generalize.
For example, I've also stumbled upon the "merge vs squash" issue, where squashes compress and mostly hide big chunks of history, so drawing conclusions from a squashed commit is basically just wild guessing.
(The author of course has also flagged this. But I just wanted to add my voice: yeah, careful to generalize.)
¹ Nothing is ever finished.
For most, I added some filters and slightly changed the regex, and it showed the reality of the codebase (I already knew the reality, I just wanted to see if it matched, and it did).
What a weird check and assumption.
I mean, surely most of the "20 most-changed files" will be README and docs, plus language-specific lock-files etc. ?
So if you're not accounting for those in your git/jj syntax you're going to end up with an awful lot of false-positive noise.
You're right about package.json, pnpm-lock etc though, but those are easy to filter out if the project in question uses them.
You're right, perhaps I should have said CHANGELOG etc.
Although some projects e.g. bump version numbers in README or add extra one-liner examples ....
https://gist.github.com/aeimer/8edc0b25f3197c0986d3f2618f036...
|sort |uniq -c |sort -nr |head -20
I use it often.Another one I do, is:
$alias gss='git for-each-ref --sort=-committerdate'
$gss
ce652ca83817e83f6041f7e5cd177f2d023a5489 commit refs/heads/project-feature-development
ce652ca83817e83f6041f7e5cd177f2d023a5489 commit refs/remotes/origin/project-feature-development
1ef272ea1d3552b59c3d22478afa9819d90dfb39 commit refs/remotes/origin/feature/feature-removal-from-good-state
c30b4c67298a5fa944d0b387119c1e5ddaf551f1 commit refs/remotes/origin/feature/feature-removal
eda340eb2c9e75eeb650b5a8850b1879b6b1f704 commit refs/remotes/origin/HEAD
eda340eb2c9e75eeb650b5a8850b1879b6b1f704 commit refs/remotes/origin/main
3f874b24fd49c1011e6866c8ec0f259991a24c94 commit refs/heads/project-bugfix-emergency
...
This way I can see right away which branches are 'ahead' of the pack, what 'the pack' looks like, and what is up and coming for future reference ... in fact I use the 'gss' alias to find out whats going on, regularly, i.e. "git fetch --all && gss" - doing this regularly, and even historically logging it to a file on login, helps see activity in the repo without too much digging. I just watch the hashes.Anyway, I can glean a lot of this information in a few minutes scrolling through and filtering the log in magit, and it doesn't require memorizing a bunch of command line arguments.
I've got my Emacs set up to display next to every file that is versioned the number of commits that file has been modified in (for the curious: using a modified all-the-icons-ivy-rich + custom elisp code + custom Bash scripts I wrote and it's trickier than it seems to do in a way that doesn't slows everything down). For example in the menu to open a file or open a recently visited file etc.: basically in every file list, in addition to its size, owner, permissions, etc. I also add the number of commits if it's a versioned file.
I like the fix/bug/broken search in TFA to see where the bugs gather.
*Mainline linux*
Most changed files: pretty much what I expected for 1 and 2... the "cutting edge" of Linux development over other OSes -- bpf and containers. The bpf verifier and AMD GPU driver might get a boost in this list due to sheer LoCs in those files (26K and 14K respectively). An intel equivalent of amdgpu_dm is #21 in the list (drivers/gpu/drm/i915/display/intel_display.c) and nvidia is nowhere to be seen (presumably due to out-of-tree modules/blobs?).
186 kernel/bpf/verifier.c
174 fs/namespace.c
162 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
161 kernel/sched/ext.c
159 fs/f2fs/f2fs.h
Bus factor: obviously none. The top 4 10399 Christoph Hellwig -> I only know his name because of drama last year regarding rust bindings to DMA subsystem
8481 Mauro Carvalho Chehab -> I also know his name from the classic "Mauro, shut the fuck up!" Linus rant
8413 Takashi Iwai -> Listed as maintainer for sound subsystem, I think he manages ALSA
8072 Al Viro -> His name is all over bunch of filesystem code
Buggy files: Intel comes out on top of GPU drivers this time (twice). Along with KVM for x86(64), the main allocator, and BTRFS. 1477 drivers/gpu/drm/i915/intel_display.c
1406 MAINTAINERS
1390 sound/pci/hda/patch_realtek.c
1102 drivers/gpu/drm/i915/i915_drv.h
943 arch/x86/kvm/x86.c
928 mm/page_alloc.c
871 drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
862 drivers/gpu/drm/i915/i915_reg.h
840 fs/btrfs/inode.c
*GCC*Most changed files: IR autovectorization code, riscv heuristics tables, and C++ template handling (pt.c is "paramaterized types").
152 gcc/tree-vect-stmts.cc
145 gcc/config/riscv/riscv.cc
131 gcc/tree-vect-loop.cc
116 gcc/cp/pt.cc
Buggy files: DWARF debuginfo generation, x86 heuristics tables, RS6000(?!) heuristic tables. I had to look up RS6000, it's an IBM instruction set from the 90s lol. cp-tree.h is an interesting file, it seems be the main C(++) AST datastructures. 1017 gcc/dwarf2out.c
885 gcc/config/i386/i386.c
796 gcc/cp/cp-tree.h
740 gcc/config/rs6000/rs6000.c
720 gcc/cp/pt.c
*xfwm4*
Most changed files: the list is dominated by *.po localizations. I filtered these out. Even after this, I discovered there is very little active development in the last few years. If I extend to 4 years ago, I get:
1. src/client.c - Realizing this project is too "small" to glean much from this. client.c is just the core X client management code. Makes sense.
2. src/placement.c - Other core window management code.This has not told me much other than where most of the functionality of this project lies.
Bus factor: Pretty huge. Not really an issue in this case due to lack of development I guess.
3298 Olivier Fourdan
530 Anonymous
319 Xfce Bot
121 Jasper Huijsmans
Files with bug commits: Very similar distribution to most changed files. Not enough datapoints in this one to draw any big conclusions.I think these massive open projects (excl xfwm) are generally pretty consistent code quality across the heavily trodden areas because of the amount of manpower available to refactor the pain points. I've yet to see an example of "god help you if you have to change that file" in e.g. linux, but I have of course seen that situation many times in large proprietary codebases.
Wtf is happening to this website
git rm -rf .