Hacker News new | past | comments | ask | show | jobs | submit login
Highlights from Git 2.23 (github.blog)
168 points by digitalnalogika 59 days ago | hide | past | web | favorite | 24 comments

I really need some kind of "modern git" tutorial. A lot of what we're doing day to day stems from the earlier days of the software (and SO posts from that time), and I bet there's plenty of new options that would make something easier or even enable different workflows. I just recently learned about "git worktree", for example.

I'm wary about git restore --staged. It seems backwards to have a destructive (worktree-modifying) command become non-destructive by adding a flag. To me, this appears more footgun-prone than git reset, which defaults to non-destructive behaviour and requires the ominous --hard flag in order to become destructive.

So I will probably avoid both git restore --staged and git reset --hard (and suggest to others to do the same). I hope git status will keep the hint to use git reset to unstage, even after git-restore becomes stable.

Yeah, it's mirroring the behavior of "git checkout -- myfile" which also does destructive changes without warning.

These new commands were hatched in response to a blog post from a couple of years ago pointing out the confusion. https://redfin.engineering/two-commits-that-wrecked-the-user...

A good conversation on HN followed. https://news.ycombinator.com/item?id=14712269

I've long wished git checkout (and now, git restore) would save the contents of blown-away worktree files somewhere (e.g. a throwaway stash) so that they could be retrieved from the reflog or something.

In my mind's eye, the command would be something like "git trash" and it would throw your changes into a trash can, where you could fetch them back out again later, until the GC erased them in ~30 days or so.

> have the contents of blown-away worktree files somewhere

This would be an amazing feature for nearly all git commands that affect the worktree. Many times I've done `git checkout ./src` to blow away "unrelated" files forgetting that the file I was just working on was in that directory. Whenever I teach git to a new-hire I always talk about wanting revision-control for revision-control.

I've always felt like an inotify-based system that made invisible/anonymous commits every time the filesystem changed would be a great extension of the git commit workflow. You could then browse these and choose to make a "real" commit, or jump back to any point.

I think people trip on these potential foot guns in part because a lot of git commands are long and annoying to type, so you end up working more based on your mental model of the state of the working copy and less based on the actual state of it.

I am not so fond of analogies but I would like to offer up an analogy here anyway. I think for a lot of people, the way that they are using git is akin to utilizing a sharp knife with your hands while your hands are obstructed from your view. You have a certain amount of feeling for what is going on but you are bound to cut yourself now and then if your view of what you are doing is obstructed. In this analogy, your reliance on your mental model of the state of the working copy is obstructing your view of the reality of the state that it is in.

Several years ago I defined a few two-letter shell aliases for the git commands that I use the most.

Maintaining my .bashrc across different systems became a drag though, so I simplified my setup even more.

I wrote two-letter “shell scripts” that just wrap the commands, and just put those in my ~/bin/. This makes it very fast and simple to get up and running on a new laptop, desktop or server.


Said two-letter shell scripts can be found in that repo. There’s a README too, which tells you what each of them do.

My git workflow is simple, efficient and ensures that my view of what I am doing is not obstructed.

I can proudly say that the last time I did something unintended with git that resulted in lost work was long ago that I can’t even remember when. And it all boils down to these two-letter aliases.

When there is friction, identify the friction and remove it. That is what I have done here. Hopefully it might be of use to others too.

Here are some consecutive entries from my bash history that are representative of how I use my aliases while working on something.

    mv ~/tmp/index.htm dimensions.htm
    git checkout -- src/
    mv dimensions.htm src/
    mv src/dimensions.htm src/draft.htm
    cm "Draft."
    vim src/draft.htm 
    vim src/draft.htm 
    cm "Mirrored, dark/light."
    vim src/draft.htm 
    vim src/draft.htm 
    cm "Remove unneccessary declaration."
    vim src/draft.htm 
    cm "Organize and restructure classes in draft."
    vim src/draft.htm 
    cm "Increase height of placeholder."
    vim src/draft.htm 
I keep a few terminal windows open when I work on a project, and often use one or two of them for git operations, and smaller edits to some files, while also having my IDE open, web browser, etc.

Notably, these two-letter commands make it very fast to remind myself of what the current state of the repository is whenever I've looked something up on the Internet for example, or after I have run tests in another terminal or made multiple different edits in different files in the IDE window, etc.

And I always check the diff before I commit. It's amazing how often you think you are about to commit just one change but when you look at the diff you realize that there are some other smaller changes that you made as well. By always reviewing your diff before you commit, you ensure that you don't commit something that would be better committed separately, and that your commit messages accurately describe the actual changes that you are committing.

I do something similar, though I use git's built-in aliasing so that I keep everything namespaced under "git". Mine are, for example, "git st" (status), "git ci" (commit), "git co" (checkout), "git dfc" (diff --cached), etc.

That said, my best advice for folks new to git, or folks who find themselves doing destructive actions often, is to embrace WIP commits. I "checkpoint" work somewhat frequently with "git add -A; git ci -m wip". They can be cleaned up later with soft resets, rebases, or similar. Once a commit is created, it's in the reflog, and it's quite rare to lose that set of work, even if a rebase or reset goes "wrong".

I use prezto's 2-3 letter aliases. But that doesn't help when I run `git reset --hard` (or gwR) and erase changes I don't know exist.

I wish I could move HEAD to a different commit (git reset), and apply (new HEAD - old HEAD) to my working directory (--hard), but abort if working directory != old HEAD.

In fact, this sounds like `git reset --keep`, but unfortunately prezto doesn't have an alias for that.

I don't like prezto's git-reset aliases. gwr is "move HEAD", gir is "move HEAD and reset index", and gwR is "move head, reset index, and reset working directory". I wish gwr was renamed to ghr, and maybe gar (git apply reset) for --keep.

As a comparison, mercurial seems to do this; if you revert an uncommitted change, it makes a "*.orig" file. (Mercurial generally is more obsessed with never losing anything than git, such as its refusal to delete commits)

Mercurial generally is more obsessed with never losing anything

Which is silly, because people often commit sensitive data such as private keys or customer's personal information. Sometimes you really do need to delete a commit (or worse, a file that lives through a long history of commits).

> I've long wished git checkout (and now, git restore) would save the contents of blown-away worktree files somewhere (e.g. a throwaway stash) so that they could be retrieved from the reflog or something.

Magit can do this. See https://magit.vc/manual/magit.html#Wip-Modes

I agree, and it would be great if git adopted the general philosophy that it would never do a destructive change without stashing it in "trash" first. This could even go as far as adding a "git undo" command.

This is still far from ideal. "git switch" and "git checkout" should not be able to create a new branch. It clearly should be the "git branch" subcommand's responsibility, because "You want to do something with a branch". It would be two commands to create and checkout a new branch, but less confusion. You can use your git alias anyway. But a shortcut for "git branch -c" would be better if you want to keep the "create and switch" shortcut IMO.

I disagree. I found git checkout -b very convenient an intuitive way to say "create and checkout this branch".

For git branch -c one could say that the "branch" command should not do checkout-business.

I should be a separate cmd. It's such a common operation, and it's a huge noob trap.

The reason it's a noob trap isn't that the two operations should be separate.

The issue is that "-b" (mnemonic for "branch") in no way suggests that it creates a new branch. Since checkout does so many different things, it would be reasonable to guess that -b limits the command to perform the "branch" action (i.e. switch to a branch). Git-switch doesn't have this issue.

But I want to switch to my new branch. One of these commands will have to do double duty.

Either switch/checkout will create or branch will switch.

I don’t see why choosing one for double duty is inherently worse than the other.

But I do consider your proposal of `git branch —create` as a poor command for create and switch branch.

FWIW in their proposal `git branch -c` would be `git branch --checkout` not `git branch --create`, which is what `git branch <name>` does.

The `git checkout -b` and `git switch -c` docs specifically note that they're shortcuts for going a git branch then a git checkout/switch.

> It clearly should be the "git branch" subcommand's responsibility, because "You want to do something with a branch".

The git switch subcommand's description is "Switch branches", so clearly it, too, is intended to handle a subset of the cases where you want to do something with a branch. Switching to a branch that doesn't already exist is a special case of git-switch's main purpose, and you have to be explicit about it. Further, when you create a new branch, you just about always want to switch to it (because while you can do some things with a branch that's not checked out, most commands are designed to work on the currently-checked-out branch), so what's the point in forcibly separating the commands?

> But a shortcut for "git branch -c" would be better if you want to keep the "create and switch" shortcut IMO.

Strongly disagree. In Git, creating a branch is a much smaller action than changing the state of your checkout. It makes much more sense to have the branch creation as the side effect than the other way around.

I'm sure it's not what they were intending, but 'switch' used to be a pretty common term for a light, flexible branch.

And all of git's branches are pretty light and flexible, at least compared against the branches of the previous generations of version control systems..

But yeah. That's definitely not what they were intending.

It's nice to hear that there appears to be progress being made in making git's tooling nicer and more consistent. Git's model itself is pretty simple, but the command line tools for working with it aren't and I feel that this fuels most of the "Git is hard" complaints.

On the one hand I'm happy on the new "switch" and "restore" commands. On the other hand I wonder if they truly add any value other than the semantic distinction of functions otherwise present in "checkout".

I tend to prefer smaller vocabularies tools: less to remember if you know what you are doing.

I would argue that it's not really increasing the vocabulary. The fact that one word has (say) seven meanings doesn't mean the vocabulary you must learn is one word. It's still seven words, which happen to be spelt and pronounced the same way.

Similar to GUI apps that work on top of git binary, does anyone know a user friendlier CLI app?

It can have new commands:

- `gitlite create` vs `git init`

- `gitlite branch create feature-dom` vs `git checkout -b feature-dom`

- `gitlite time-travel cfae736` vs `git checkout cfae736`.

- `gitlite undo cfae736` vs `git reset cfae736 --hard`

- gitlite release create v4.2` vs `git tag v4.2`.

Each command will call the translated `git` command, and perhaps improve upon the output from the command.

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