Here are a few important shortcuts that help me be more productive throughout the day at work:
Creating aliases for commands.
Using pbcopy.
Using...
For further actions, you may consider blocking this person and/or reporting abuse
I hate this one with a passion. People who have aliases like this are almost always the same people who only ever write one-liner commit messages, no matter how simple or complex the actual commit is.
But what they lose by writing one-liners, they often make up with line length, often going way over the best practice character limit.
I really wish committing with
-m
was just locked for anyone who hasn't been using git for at least 5 years and knows what they're doing.Why would you need multi line commit messages? A commit message should be a name and that's it, leave the details for the PR that will eventually squash all commits into a final frozen commit (which will include all the details)
When browsing the git logs I don't want to have to find the merge commit to get details about what one commit changed. A commit should, when necessary, explain the changes and maybe the reasoning for it. It's painful when I want to find out why a change was made but the commit doesn't explain anything.
Squashing all commits into one is not good practice in my opinion. If you are going to do squashing, do it in a controlled manner with rebase. Taking 10 commits and squashing into 1 loses a ton of information if the commits were well written.
PRs are tied to GitHub. Commit messages are agnostic to which git forge you're using. When I want to see why a change was made, I
git blame
the file, and look at the commit message for that change.Squashing commits should generally be avoided because you lose a lot of information from individual commits.
I think you're a bit too fixated on your own non-standard git workflow there.
I also sometimes use a sort of throw-away git history where some commits are just "fix", that I later intend to clean up with a rebase. If you want to make that your main workflow for every branch, then sure, you can be a lot less thorough with your individual commit messages.
But the "intended", and afaik most common way of using git, is to keep commits atomic, and have their messages describe the change that was made. If the reason for a change isn't obvious from just looking at the code and maybe some extra context from previous commits (or from a later merge commit), then that should be explained in the commit message.
Putting too much information in a single commit really just makes it hard to reason about the individual parts that make up a larger change, like introducing a new feature. If all you see in your git history is "add feature X", then that's hardly better than a normal change log, and you lose all the important step-by-step information of how feature X was actually added, what preparations were necessary, etc. as well as the ability to undo individual changes if necessary.
Yeah perhaps my workflow is uncommon. My commits are still atomic however, just at the master branch, not my working branch. And usually extra context lives in the task definition that originated the changes, which is usually linked in the PR which ultimately forma part of the master commit description.
The problem with that approach is that it doesn't work for everyone, because both github PRs and obviously anything linked from them as well exist outside of the repository. So anyone aiming for having as much context as possible contained in the repository itself will have to avoid those.
As for atomic, please note that entire features aren't usually atomic. The point of atomic commits is that they make changes that ideally don't break the project, but also cannot be split into smaller non-breaking changes. The way I most commonly see git used, is that commits generally represent atomic changes, while higher-level concepts like "features" tend to be represented by merges.
I agree that leaving a detailed commit message is really important. It saves so much time for both the creator and anyone working in that codebase in the future. But most commit messages can be within the 50-72 character count without the need for multi-line messages
If you're adhering to best practices, atomic commits should also be another tenet to follow. Small, succinct changes need to be committed as soon as they're completed, rather than big changes that would necessitate multi-line messages
Yes, it is correct that the majority of commits can do just fine with a single line of description. My problem is with how using
-m
(or an alias) often makes people write one-liners even if a longer commit message would be preferable, because now that would be a deviation from their usual workflow.Even worse: Noticing while writing the message that it is a bit more complicated, now the user has to delete what they already typed, commit without
-m
and write it all again in the editor (or at best copy-paste it), which is more resistance that more often than not makes people just cut their messages shorter than they should be.A good engineer should understand best practices in 5 minutes. Then needs maybe 5 more days to make them a habit. If anyone needs 5 years for this, something else goes wrong.
The problem is that complicated commits that need multi-line messages are generally a rarity, so "building habits" around those cases is difficult.
Using the editor to write commit messages means there is no barrier to decide halfway through the commit line that you need to add some additional context, and just write it down.
Being used to using
-m
, on the other hand, puts a lot of resistance in the way of doing this, so in the worst case, people simply skip the extra context or attempt to shove it all into an incomprehensible commit line, and at best requires some discipline to do things differently.IMO there is nothing wrong using commit message in one line. But when you submit a PR, I'd expect the purpose of the PR, what gets a fix and what not. :)
Yea, most of the time a one-liner is enough and you shouldn't write a novel in a commit message; that's why an alias like
gc
is very useful in principle.I'm specifically worried that it creates a habit, and in the rare cases where you do need more information, that habit tempts people to just skip the important stuff because writing a commit message in the editor now falls outside of their routine.
About this, I really like this article about how to write a really useful commit message : chris.beams.io/posts/git-commit/
I'm sorry but I've to ask, if you have pull request changes, and have to make multiple commits, what kind of commit message do you give in that case?
I don't think I fully understand your question. Can you give an example of what you mean?
Suppose you submit a Pull Request, and the reviewer suggested some changes, which you've to make, for that there are two options, you can edit the commit or you can add a new commit, if you go with the latter approach, what kind of commit message do you prefer, the whole explanation for the feature for which you are working or just the minor changes you've made like, "added title".
Why would you explain the whole feature again? That's clearly wrong, you already have a commit that does that.
But please don't keep it as short as "added title". That looks horrible when browsing the logs, just a bunch of "fixed bug", "added text", etc. Provide more details like "Added title to help text for [feature name]" or "Fix X bug in [feature name]" (with details on what X was in the commit body, it's annoying to see a bugfix that doesn't explain why it was needed because the change doesn't always make sense to someone else).
@cappe987 When you say "logs", do you mean the overall commit logs of the repository, or the commit logs of a given PR? The comment thread here has implied that the commits of a PR will be squashed into one commit. Maybe you aren't making that same assumption.
In the case of squashing the commits per-PR, each commit on the main branch will be tidy, and have the whole context of that PR. For individual commits in a PR, I'm not sure if we need to include the feature name in the commit message, especially if the PR is scoped to one feature.
Imo, every commit should go through a PR, and should not be committed to the main branch. The only time I bypass a PR in my own projects is if I want to make a small change to the README. And for that, I will be descriptive with my commit message on what I'm changing in the README.
I mean the
git log
.I was talking under the assumption that no squashing is done (or controlled squashing with rebase). I mentioned in another comment here that sqashing a full PR is not good practice in my opinion. You lose a lot of information if the commits are well written. Of course it depends on how the PR looks. If it's just one commit with a bunch of small adjustments afterwards the feel free to squash. If the original PR is made up of, say, 5 carefully written commits that together add a feature then please don't squash.
Yes, one squashed commit looks tidy. But you know what's better? 5 commits that explain the different steps in adding that feature, instead of one giant commit.
Having detailed git logs is incredibly useful. I don't want to locate the original PR to get the full context.
I'm not disputing using PRs, I'm only against indiscriminate squashing. And if a commit is gonna be in the final git logs then it should be well written.
Having the commits be one per-PR allows us to compare branches, and see the most concise summary of the differences of the branches. "This branch has 3 PRs/features/fixes merged on top of the other", whereas if a given PR is merged and spread out to multiple commits, it's unclear exactly which PRs were merged on each one, and which PR that commits are related to. This is important for me to be able to pull individual PRs into a separate branch to release a patch release for something.
Having the commits squashed also makes it easier to revert the entire PR by
git revert
ing the squashed commit. There are tradeoffs to this, as one could favor the benefit of being able to revert only one of the commits related to that PR. Though I prefer the squashed commit scenario, since you can view each PR as its own unit of work for development and QA, that can be reverted if needed.If you structure the squash commit message to summarize the PRs commits, you can still achieve this by viewing this message in the
git log
output. The commit history of the PR is automatically populated in the squash commit message box while you are merging the PR, so you can edit from there if you want or keep it just the original commit messages.I've gotten accustomed to a workflow that works with squashing commits, so I'm biased, but I would personally never go back to not squashing commits because of the benefits I've described here.
...everything you just described is exactly what merge commits are for though; they preserve the individual commits of both branches but group together groups of atomic changes that represent a higher level change.
Not to say either approach is inherently correct, but you make it sound a bit like your approach is the only one that gets you those benefits, when you can easily get those in a different way, just, well, a bit different.
The behavior of git log and ease of reverting a PR is not the same in the two scenarios though right? I'm legitimately not sure about the specifics of merge commits in this regard. I prefer git log to show one commit per PR on the main branch.
So I got curious and decided to just experiment a bit. Created a simple dummy repository that looks like this:
with a single "feature" branch that has three individual commits. The second one has some dummy "extra information" that I would want to preserve in a real-world scenario:
After digging through the git log manpage for a bit, I found
--merges
, but that has the disadvantage of only displaying merge commits, so any potential hotfixes on main could get lost (maybe this is what you want, but I wasn't entirely satisfied). But just under that there's also the--first-parent
flag, which will only follow the first parent on merge requests, which in practice is what you merged into, aka. the main branch. This gives me the following log:Meanwhile the feature branch can be safely removed now, as the commits live within the
main
branch and can easily be displayed:So overall, I would say that yea, a merge-commit based workflow can offer the same information as one based on commit squashing, and it's not even all that difficult.
And of course, git aliases can make things even easier:
Thanks for digging into this so deeply. The
--merges
and--first-parent
flags look very useful here. Really insightful conclusion overall.🫣 instead of aliasing directories, use zoxide. After the first
cd /this/is/batman
then you can just doz batman
.🎉😎 See CLI tools you cant live without
bash aliases are great, but for git sub commands I personally prefer (and recommend) to use
[alias]
section in.gitconfig
.For a good Linux alternative to
pbcopy
andpbpaste
, try outxclip
.And if you're using Termux on Android,
termux-clipboard-get
andtermux-clipboard-set
give you access to the Android clipboard.I have these ones in my zshrc, near others for Linux
alias G=’| grep --color=auto
gitcommit(){
# All element except the last one
git add "${@: 1:-1}"
# Last element
git commit -m "${@: -1:1}"
}
alias ggc=gitcommit
Adding all alias in the plugin git in zsh :)
Some problems here:
aliases
This doesn't do what you think it does:
The
$1
isn't expanded to anything in your alias. It works because your message goes at the end of the line. You can see this if you try a little test:If you want to use substitution then you should be using a function or a script, not an alias.
pbcopy
You go on to say
But you don't provide any guides for Linux. If you want similar functionality to
pbcopy
, you're probably looking forxsel
orxclip
but ymmv.reverse search
This isn't a command at all! It's a feature of the input library. I think from memory this is
readline
most of the time. It gives you a bunch of nice features like the reverse search you mentioned, and it will work that way in any application which uses that library. This means that you can be using something like themysql
client, or a programming language REPL and there's a reasonable chance you have the same super powers there too.Instead of the
cd
alias, on ZSH there is a named directory feature:With ZSH you can put all of your custom aliases in a custom file. Default location is ~/.oh-my-zsh/custom.
I use ~/.oh-my-zsh/custom/zsh-aliases
I preface my aliases with my initials and a - , mw- in my case.
Then I can type mw- [tab] to see all of my aliases.
I also created an alias that opens the custom alias file.
As mentioned above ZSH includes a lot of aliases and you can use the "aliases" plug in to display all installed ZSH aliases
This is what my ZSH plugins looks like:
plugins=(
git
zsh-completions
zsh-autosuggestions
zsh-syntax-highlighting
history-substring-search
colored-man-pages
aliases
zsh-docker-aliases
)
github.com/ohmyzsh/ohmyzsh/tree/ma...
You are aware that OMZ is not required? Nothing OMZ does can't be done without it, it's all ZSH scripts. Though, yes you can source any file you want in to your
.zshrc
(or any shell script).Use OhMyZsh
Wow 😲 these alias commands are awesome 🔥🔥🔥
Thank you so much!
Thank you for "cal" =)
I have a bunch of useful git shortcuts defined in my .zshrc
Cool Stuff!!
What about
fzf
? Do you think it could be a good addition?Cool article - one thing I'd add is to be careful with the encryption within vim. This was removed in neovim for good reason, it's very buggy, unmaintained and doesn't do what it says it is.
Those bonus ones are amazing 👍
Thanks, really helpful!
Thx, helpful ! I didn't know pbcopy. Regarding aliases, the Ohmyzsh plugin already provide a lot of my usual git commands.
I've been coding for over 10 years now and never thought to alias my projects 😅
Time to alias all my active ones 😈
Nice tips, for git aliases it is better to put them inside of .gitconfig instead of bash aliases to me and you could just create on bash g as git to make it accessible, have you tried that?
I use zshrc, and it comes with a lot of default git shortcuts, didn't have to do that.