Play Git Like A Violin
metacircus.com
metacircus.com
More articles like this, please?
So "g" (without any argument) is "g status", with arguments, g is effectively an alias for git. A micro-optimization, true.
Another git feature I use quite a bit is the history rewrite. "git fuss 5" allows me to fiddle with the last 5 commits. I should write another article to explain git fuss. History rewrite sounds like a dirty thing. But my git workflow involves doing a whole bunch of temporary commits, so I can track small incremental steps with git. History rewrite is pretty legit for this usage pattern.
g() {
if [[ $# == '0' ]]; then
git status
else
case $1 in
fuss)
shift
git rebase -i HEAD~"$1";;
*)
git "$@";;
esac
fi
}well you're only rewriting local history, so you're a-ok. history re-writing becomes a dirty word when you re-write history that someone else may have already seen. i know you already know this.
It seems like git (or some friendlier wrapper) could track this for you. For example, if you've pushed changes to a remote repo, then git could mark those changes as "published" in your local repo and warn you when you later try to rewrite that branch's history.
Automatically correct and execute mistyped commands after waiting for the given number of deciseconds (0.1 sec). If more than one command can be deduced from the entered text, nothing will be executed. If the value of this option is negative, the corrected command will be executed immediately. If the value is 0 - the command will be just shown but not executed. This is the default.
I think the alias system is better though.
alias gs='git status'
alias gc='git commit -m'
alias gca='git commit -a -m'
alias gd='git diff'
I spend most of my time with gs and gd, then usually gca my changes all at once, or add those I want and use gc.
alias co='git checkout'
alias gb='git branch'
alias gd='git diff'
alias gr='git reset --hard'
alias gs='git status'
alias gc='git commit'
alias ga='git add'
But I'm moving away from using git status as much, since adding the following to my $PS1: get_branch () {
GS=$(git status 2>&1)
if [ $? -eq 0 ]
then
STAR='*'
echo $GS | grep 'nothing to commit' > /dev/null 2>&1 && STAR=''
echo -ne "$(echo $GS | grep 'On branch' | cut -d' ' -f4)${STAR}"
else
echo -ne '_'
fi
}
It's a bit slow some days (up to a second to pop up a prompt when the box is busy), but the constant visual reference to my branch name & uncommitted changes is nice.http://git.kernel.org/?p=git/git.git;a=blob_plain;f=contrib/...
It can tell you if your working directory is dirty, if you've got staged changes, what branch/tag you're on, and of course does completion also.
You get to see the diff, piece by piece, and separate the independent fixes.
This is why, I aliased an "uncommit". You can see the details in this article[1] I had written for my friend who were learning git.
usage:
git uncommit 3
effect: add another point in the history where it is said you get back 3 commit in time. Which is better in most case than modifying the history when you want to push it.[1]: http://yannesposito.com/Scratch/en/blog/2009-11-12-Git-for-n...
Maybe this is a git version specific thing.
I can still vividly remember the moment I mistakenly typed in "g co src/" instead of "g ci src/". I immediately changed my checkout alias to "cho".
"git clean", "git reset --hard", "git checkout" all provide ways to irreversibly trash the working tree.
"git clean" has no other function except doing irreversible deletions, so that's OK, IMO.
"git reset --hard" is a pretty useful way to make a branch jump around between places, so having a secondary feature of trashing the working tree is unacceptable, IMO.
"git checkout" is a useful way to take a file from a specific commit. The fact it may also trash uncommitted changes is also a secondary feature. I think without "--force", "checkout" should refuse to irreversibly lose content.
I'm not complaining that these commands mismatch the spec. I'm complaining that the spec for these commands is badly designed.
> They are written explicitly to do that
I agree about git clean, but git reset --hard and git checkout do more than just changing the working tree.
> If you're ever unsure about the result of a given command make a copy of your current working tree before applying it. I like git because it explicitly doesn't hold your hand through things like this. It expects you to know what you're about to do.
You misunderstood my problem. I know exactly what these commands do.
I consider "reverting my changes in the working tree" to be a separate and irreversible thing of whatever "git reset" and "git checkout" do, and want git to ask for approval before accidentally doing irreversible damage.
The main thing protecting me from accidental errors/deletions/etc is my revision control system. But in this case, the irreversible damage is done BY the revision control system.
Here are a few examples of what I mean:
* I jump to the wrong shell. In it, I have an old "git status" that says my working tree is clean, but at the wrong location. I forget that I already moved it, and started working on a feature. I use "git reset --hard <correct-place>" and without warning, I also get my working tree changes irreversibly lost without any question.
* I press <up> a few times in my shell, to reach a previous command, hit <return>. OOPS, that command was not the one I intended. Instead it was a "git reset --hard <someplace>" or perhaps a "git checkout"!
* I may execute a command like "git checkout <refspec> <some_path>" during a merge in order to pick some version of that file. If I accidentally press <return> too early, I may lose all of my working tree changes in the directory.
I don't actually lose my working tree changes with git, because I'm aware of these issues, so I am very careful about which commands I replay.
But it would be nice to know that since I have a revision control backup of my files, I don't need to be as careful, as long as I don't use "clean", "rm" and other commands meant to lose information.
P.S: It would be nice if "checkout" stored a stash or such of any overwritten content so it isn't irreversible unless given a flag not to.
tl;dr: Good UI design dictates that user operations should be reversible whenever possible. Irreversible operations should require explicit confirmation (or at least "--force") and ideally should have no other effect except the specific irreversibility.
The canonical way to use git is by the git syntax. Many (most?) people don't build scripts and aliases around it. It works just fine when used raw.
However, I'm with Peaker in that losing your working copy is enough of a nuisance to deserve an interactive prompt, just like 'rm' prompts by default. Confident users could always disable the prompt with an environment-variable or .gitconfig flag.
Btw, which defaults are, in your opinion, "bad"?
Which can be frustrating as well, at least with git the commands work the first time :)
A better approach (as long as no external pushing/pulling is done) IMO would be to execute the command immediately, but allow undo functionality.
"git reset" does nothing irrevocable unless you add --hard (why should you also need to add --force?)
"git checkout" will only trash uncommitted changes if you ask it to specifically. (e.g. 'git checkout <file to overwrite>').
side note: using 'git reset --hard' to teleport a branch around is bad form, imho.
Have you considered putting your dotfiles on GitHub? That way other people can fork them for their own use, and you can also browse your Fork Queue to see if others have made interesting additions/changes.
Here's mine (mostly forked from a friend's): https://github.com/sumeetjain/dotfiles
As an example, I have one git function (called 'gpa') that shoves everything in the staging area into a single commit and immediately pushes it.
`gpa "the commit message"`
https://github.com/samsonjs/config/blob/master/zsh/zshrc#L23...
Previously on reddit: http://www.reddit.com/r/programming/comments/b0o6z/short_but...