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.
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.
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 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 "Mirrored, dark/light."
cm "Remove unneccessary declaration."
cm "Organize and restructure classes in draft."
cm "Increase height of placeholder."
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.
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 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.
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).
Magit can do this. See https://magit.vc/manual/magit.html#Wip-Modes
For git branch -c one could say that the "branch" command should not do checkout-business.
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.
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.
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.
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.
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.
I tend to prefer smaller vocabularies tools: less to remember if you know what you are doing.
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.