Hacker News new | comments | ask | show | jobs | submit login
Git 2.19.0 released (lwn.net)
158 points by rbanffy 4 months ago | hide | past | web | favorite | 61 comments

Slightly off topic but just thought I'd say, my favourite git shortcut is: "git checkout -" which toggles between the current and the previous branch just like cd - does with directories.

What is your favourite git shortcut or feature!?

My favorite should be hooks. They're not exactly a shortcut, but they allow you to write logic to prevent yourself from doing dumb things.

Namely, I have two I need to write. One preventing master pushing, as sometimes I forget which branch I'm on if I'm all over the place. And two, preventing committing past a `wip` commit message. Arguably I shouldn't be using wip commit messages, but I prefer being able to have a "stash" per branch, of sorts. If I stashed it, I fear namespace clobbering as well as forgetting I even had a wip.

I do the exact same thing. I find WIP stash-per-branch very useful when I'm context switching often between feature branches. I've got two aliases set up for this -- `git wip` and `git popwip`:

  $ git config --global alias.wip
  !git add --all && git commit -am 'wip'

  $ git config --global alias.popwip
  !git log -1 --pretty=%B | grep -q '^wip$' && git reset HEAD^ || echo 'HEAD is not a wip commit'

To extend this, I might make my CLI show `foo-branch (wip)` if the most recent commit is a wip. Your alias idea is golden though, definitely using this. Thank you!

As long as your work is in a private branch, you can go back and squash those WIP commits into a single descriptive commit.

"git checkout -" is even better if you alias "checkout" to "co" since it just becomes "git co -".

Stash has to be my favorite (especially with "-u" AKA "include untracked").

I'm generally very comfortable doing even advanced stuff in git, but still get paranoid about some things. Especially when I'm trying to troubleshoot something for someone else.

Being able to stash away current changes in a place that is separate from any particular branch is invaluable.

I often show stash to people that make a habit of copying their entire project directory before doing something they aren't sure about (backups aren't inherently a bad idea, but that's why you push early and often).

> "git checkout -" is even better if you alias "checkout" to "co" since it just becomes "git co -".

And even better if you alias `git` to `g`!

Just go all out - alias g-='git checkout -'

Aliasing 'gg' to that would actually be pretty good. Unless you really need git gui.

alias gb='git checkokut -'

gb for go back.

Is there any good way to keep track of stashes? I keep stashing stuff that I forget about and end up rewriting 2 weeks later.

Yes. Use a local branch. They are cheap to create. You can name the branch and, of course, write a descriptive commit message.

Stashes are for quick save and restore. Branches are way more powerful.

Hard agree here. Better to make "git my-stash working-on-foo" alias which makes a separate branch. Stashes have become an anti-pattern for me after having too many fail to reapply or drift out of date.

I have some aliases sl (stash list) and pop/drop for git stash pop/drop.

Also make a habit of saving your stashes with a meaningful label.

But yeah, like others suggest -- use branches.

I've made it a habit to `git stash list` and `git stash show -p stash@{2}` when I want to inspect the stashed changes.

Git worktree changed my life. I can quickly switch branches and work on multiple branches at the same time.

As a developer who often needs to revert other developers' screwups (and sometimes my own): "git show COMMIT_ID -- somefile | git apply -R"

Isn't that the same as `git revert $commit` ?

I’m guessing that it’s different in that it only reverts one file, not the entire commit.

Git noob here, what does this do? I have read the docs on apply but I don't really understand patches. It sounds like you bundle a bunch of commits and allow someone to use them. But in that case, wuldnt you just push a branch and then the person who needs it can pull and merge it or rebase onto it or cherry pick it?

It undoes the change made to `somefile` in `COMMIT_ID`.

Details: `git show <COMMIT_ID>` gives you the patch identified by COMMIT_ID. Appending ` -- somefile` narrows that patch down to only include the specified path.

The `-R` option to `git apply` tells it to apply the patch in reverse.

Thus, "undo the changes in `somefile` from this commit".

Another one: `git worktree add <path> <commit>`, which allows you to checkout another working tree at <commit> under the directory <path>.

Whoa! I didn't know `cd -` did that. Thanks.

I alias the command `git log origin/master.. --oneline`. (You could also do `git log @{upstream}.. --oneline` if you're diligent about configuring upstream branches.) That shows you how many commits you have locally that you haven't pushed yet. I use them compulsively, almost as frequently as I use `git status`.

I find `git show-branch --topics origin/master` (optionally adding branches) to be more useful.

What do you alias it to?

(Also, I tend to use just `git log origin..` rather than spelling out `origin/master`.)

> I tend to use just `git log origin..` rather than spelling out `origin/master`

Doesn't work for me:

  $ git log origin..
  fatal: ambiguous argument 'origin..': unknown revision or path not in the working tree.
  Use '--' to separate paths from revisions, like this:
  'git <command> [<revision>...] -- [<file>...]'

  $ git log origin.. --
  fatal: bad revision 'origin..'

Maybe it will work if you do `git branch --set-upstream-to=origin/master` first? But yeah, this config issue is why I don't use @{upstream} much.

Personally "gout", which I now realize is a disease :p

For me it's "git outgoing".

Even shorter: `git log @{u}..`

In a script called `git-first-push`, on your path:


    git push -u ${1:-origin} HEAD:"$(git symbolic-ref -q HEAD | sed -e 's|^refs/heads/||')"

This means "push the current branch to origin (or the first argument, if specified) to a branch of the same name, when this hasn't been done before. Set origin (or the first arg) as the upstream of the local branch". Formally invoked as `git first-push [remote]`, I just alias it to `git fp`.

EDIT: I've forgotten offhand whether there are actual zsh-isms here. It's probably bash compatible, but I always have zsh installed by the time this script lands on a system.

how is this different from:

    git push -u origin head


Good point; I'd missed that someone taught git a smarter "spelling" for this task. IIRC, `git push -u origin HEAD` didn't used to automatically create a branch of the same name on that remote -- the explicit colon notion was required. I've had this little script around for a long time...

I just do

  git push -u
which does what I want since I set

  git config --global push.default=current

A simple one, but learning that -p works for checkout as well as add.

as well as reset and stash, too.

Have made thousands of commits, TIL. Thanks! I like git smudge and clean for keeping secrets out of files, I created a small utility to automate the process and make it easy to do multiple regex substitutions. Here it is, if it helps anyone: https://www.npmjs.com/package/no-secrets

I had to add this feature with a third party script but I love `git open` for opening the current repo webpage in my browser.

Where can we get this third party script?

the '-C' option to let me do git operations without being in that directory is vastly helpful when I go to script against a variety of git repositories (--git-dir is pretty nice too, I just don't often have a use for it)

"git bisect" is my overall favourite feature, I think it's illustrative of the same widely applicable phenomenon as git itself. Lots of people would know in principle that this idea - binary search for a change introduced at some unknown previous point - is a good way to approach the problem, many of them could implement such a thing, but for whatever particular problem it may never seem like it's quite worth the effort to do so. But, by expending this effort just once (as "git bisect") our culture gets the reap the benefit essentially forever.

But if you use git around me the thing I'll keep talking about is force with lease (as seen in e.g. "git push --force-with-lease"). I believe this is a specific implementation of an idea with much broader possibilities that we should see in a LOT more places.

The central idea of force-with-lease is that when I'm telling a machine that I understand all the circumstances and nevertheless I wish to proceed anyway, I should need to tell the machine what these circumstances are that I'm claiming to understand. For example I know the current state of the tree is rel-4.2.6-20180911 and I'm confident that we want to smash that, and put rel-4.2.6-20180912 there, even though that rewrites history. Then the machine can check automatically that those _are_ the circumstances and refuse to override if not.

In git the reason to have this feature is that it prevents the situation where I think rel-4.2.6-20180911 is in the branch, I'm sure I want rel-4.2.6-20180912 there, so I force that, but unfortunately while I was deciding to do that somebody else landed rel-4.2.7-20180912 and so I've ruined everything by forcefully reverting their change.

The broader idea is very widely applicable. In Puppet for example, somebody can tell the system to stop updating node-04 because "Barry found a problem with XYZ on this node, check with Barry before re-enabling". And if you try to update node-04 it will recite that blurb and refuse. But, you can force enable node-04 without reading the blurb, and worse, if meanwhile another engineer changes it to "Figured out XYZ. Definitely don't re-enable Puppet without talking to me, Sarah" you won't even know that happened, your override will take effect anyway even though you'd no chance to know you should call Sarah.

Even in non-IT systems I think this approach has great merits. Consider a railway signal system. Most of the system isn't the "signals" (which are mostly boringly simple and reliable colored lamps) but the sensors verifying that the state of the system is consistent and safe. Humans are often authorised to overrule this system in small ways when things go wrong. A sensor says it isn't sure if the motorised points at a junction are closed, so the system refuses to move signals from "Danger" for that junction. The human signaller is authorised to tell human train drivers to ignore this Danger signal and proceed, albeit it "at caution, obeying all other signals". But wouldn't it be better if the system required the humans to acknowledge specifically that they've understood the problem is that the sensor says these points aren't locked? Now the driver is thinking about the points, which might not be locked, rather than just vaguely knowing something, somewhere, is busted. Guess who is going to take a proper look at those points before driving over them now! And when the next train arrives, and the signaller tries to let it through, if in fact NOW the problem is that the points are fine but the axle counter says the previous train never left, the signaller would need to diagnose and accept this situation, not just wave through another train assuming it's the same fault because it has the same symptom (signal at danger).

I alias checkout to cd, so it is natural to use 'git cd -'

Wow nice, only yesterday I was thinking I wanted exactly that.

> What is your favourite git shortcut or feature!?

Revsets. Oh wait, git doesn't have revsets it has the ungodly pile of shit that is gitrevisions(7)

Didn't downvote, but this comment would have been better without the attitute. Something like: "what I wish it had were revsets" or "changing the topic a little, I hate that git doesn't have revsets". Anyway, what are revsets?

> Anyway, what are revsets?

Reifying revisions as sets and applying set combinations and predicates to these revision sets: https://www.mercurial-scm.org/repo/hg/help/revsets

It makes for flexible, reliable and readable revisions specifications.

> * The content-transfer-encoding of the message "git send-email" sends out by default was 8bit, which can cause trouble when there is an overlong line to bust RFC 5322/2822 limit. A new option 'auto' to automatically switch to quoted-printable when there is such a line in the payload has been introduced and is made the default.

Nice to see this one land. That was a pretty annoying usability problem for with the send-email command, and a barrier for people less knowledable about git using it.

I thought most people used git-send-email to send emails generated from commits with git-format-patch. Do diffs or commit messages contain lines that exceed 8 kb in length?

Diffs can, sure, so long as the source file has a line like that. I've seen it for example with data URIs on HTML files.

Looking at the new git range-diff command, it looks like they're doing something like:

    diff -u <(git log -p master..fix-typos@{3}) <(git log -p master..fix-typos)
We actually have a program at my job that posts links to diffs like this in Github PRs whenever someone rebases the branch associated with the PR and force pushes it up to the remote.

The algorithm is much more sophisticated than that; it can match up commits that get reordered, and notice commits inserted in the middle of the series.

I think you can some of that by copying the two git log -p outputs to separate files and then running vimdiff on both of them. At least in my experience, one can see commit re-ordering, commit addition, or commit removal. You could also get some of that by using the -y parameter to diff instead of -u.

I'll definitely try it out and see how it compares to what we're doing now.

How effective is this? hg has added extra metadata on top with what we call "changeset evolution", which tracks this renaming information explicitly. git seems to like to track stuff like this implicitly, such as renaming files. How effectively can you track commit rewriting if you don't have explicit metadata to help you?

Quite well, via well-established graph assignment algorithms that look for the best fits/matches between pairs of commits.

What does this do, exactly?

It's more or less equivalent to:

  git log -p master..fix-typos@{3} > tmp1
  git log -p master..fix-typos > tmp2
  diff -u tmp1 tmp2
except that pipes are used instead of temporary files.

Perhaps, yosito meant what the purpose of the whole command was, rather than what <() meant. It looks like a diff of diffs and commit info, but I've never used branch@{n} before. Looking into gitrevisions(7), it gives the nth prior value of branch, so doing a regular commit would be represented as an addition of a commit and its changes and doing a `git reset @~` in that branch would be shown as a deletion. Doing a `git commit --amend` appears as a modification of that commit. It seems useful.

So what does it do?

git log -p start-commit-sha1..end-commit-sha1 will display the commit log including diffs associated with each commit from the commit after start-commit-sha1 up to the end-commit-sha1.

If you make changes to your code and/or commit messages and subsequently rebase the branch, then the end-sha1-value will change (since you now have a different set of commits). If you run the same git log -p command with the new end-commit-sha1 value, then the log output will differ based on the changes you made when you rebased.

So if run a diff between the two git log -p outputs (one with the original end-commit-sha1 and the other with the new end-commit-sha1), you'll be able to see the difference between the two logs which includes the changes you made to the code and/or commit messages).

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact