
Little Things I Like to Do with Git - csswizardry
https://csswizardry.com/2017/05/little-things-i-like-to-do-with-git/
======
TimWolla
Since git 2.9 there is another experimental, opt-in, improvement to git diff
that results in more readable diffs: Heuristics that try to capture the
logical changes better. By default git tries to delay the start of diffs as
long as possible leading to bogus highlighting of e.g. doc comments (slash
star star).

See [https://github.com/blog/2188-git-2-9-has-been-
released](https://github.com/blog/2188-git-2-9-has-been-released) (section
Beautiful diffs) for an example and [http://blog.deveo.com/whats-new-in-
git-2-11/#experimentalheu...](http://blog.deveo.com/whats-new-in-
git-2-11/#experimentalheuristicsgitdiff) for another similar option that I did
not try yet.

~~~
ronjouch
Thanks for the tip!

For readers in a hurry, careful: `compactionHeuristic` ended up deprecated in
Git 2.11 . The experimental heuristic to add to a >=2.11 .gitconfig is now
`indentHeuristic`, see OP's second link :)

~~~
GauntletWizard
I don't see anything about it being deprecated, just that there's a new one.
(It's also somewhat unclear what happens if you enable both)

~~~
avar
The compactionHeuristic was removed entirely in the 2.12.0 release. You can't
enable both since only the indentHeuristic exists now, and serves pretty much
the same purpose.

~~~
GauntletWizard
And here's the release note for that:
[https://github.com/git/git/blob/master/Documentation/RelNote...](https://github.com/git/git/blob/master/Documentation/RelNotes/2.12.0.txt#L157)

Thanks!

------
qznc
Things in my dot repo [0]:

git-overview: Short report about top committers, files with most commits and
most authors. Nice when you checkout a non-trivial repo for the first time.

git-onNotify: Do something (like `make`) whenever a tracked file changes.
Usually used to build LaTeX and static websites.

git-randomline: Chooses a random file and a random line number there. The game
is explain that single line to some fellow. Do this repeatedly to spread
knowledge about a codebase.

git-tarball: Pack the repo into a tar.bz2 file.

[0]
[https://github.com/qznc/dot/tree/master/bin](https://github.com/qznc/dot/tree/master/bin)

~~~
mikerice
Big fan of the git-randomline command. So much that I'm stealing it!

~~~
qznc
I have not used it in a while, but I actually have a good occasion soon. I
just updated it to only return _interesting_ lines, which for now means at
least 10 letters. That filters lines like "}".

------
nicwolff
I have his "git recent" alias, and then in .bashrc

    
    
        alias co='select br in $(git recent); do git co $br; break; done'
    

so when I type "co" at the command prompt I get a numbered menu of branches in
the order I last checked them out, and can just type a branch's number and hit
return to check it out again.

~~~
netcraft
I use a different version of git-recent that Paul Irish wrote
([https://github.com/paulirish/git-recent](https://github.com/paulirish/git-
recent)) but thats pretty slick - thanks for sharing. Might have to switch -
although sometimes its nice to also have the context.

------
qznc
I also have aliases in my shell instead of git aliases.

    
    
        st    git status ...
        gl    git log ...
        gd    git diff ...
        gg    gitg
        gup   git pull --rebase
        gb    git branch
    

The dots mean there are more arguments. The point is, every once in a while I
analyse my shell history and add aliases for the most used commands. Looking
at it now, it seems I should add aliases for `git push` and `vi Makefile`.

I also have the aliases g for git, m for make, and v for vim.

~~~
fredley
Yes, I have similar:

    
    
        gs    git status
        gp    git push
        gac   git commit -am 
        gc    git commit
        gcm   git commit -m
        gA    git add -A
        gC    git checkout
        ga    git add
        gd    git diff
        gm    git merge
        gmc   git merge --continue
        gpu   git pull
        grc   git rebase --continue
    

The only problem is that I _cannot_ use git on anybody else's machine, the
muscle memory is too strongly ingrained now. This is a common message:

    
    
        The program 'gs' is currently not installed. You can install it by typing:
        sudo apt install ghostscript

~~~
danellis
How much time does that actually save? I find that for any given task, I spend
far more time reading and thinking than I do typing, and of the time spent
typing, most of it is spent typing arguments.

~~~
akavel
I use `git diff` and `git log --graph --decorate --all --oneline` and `git
status` _so_ often, that it totally saves heaps of time. Especially the second
one ― I usually type my `gl` somewhat subconsciously already; only afterwards
I realize what I'm looking at, and start reading. That's possibly related to
the fact that I commit _very_ often, use many working branches, and then
rebase -i (usually squashing and composing a good readable commit message)
before publishing.

But anyway, if someone asks for help with "how to fix my mess in git", the
absolutely first thing I do is exactly `git log --graph --decorate --all
--oneline`, to start finding out visually _what_ the mess actually is.

------
ericfrederich
I like how he creates an alias to blame called praise. He's right, blame is a
loaded word. The command I really use to "blame" people is bisect... though
sometimes I use that to find something good too

~~~
dotancohen
That is actually an SVNism. SVN had 'annotate', 'ann', and 'praise' all
aliased to 'blame'.

~~~
csswizardry
OP here.

> That is actually an SVNism.

Yep. From the article:

> Taking the lead from SVN, I alias praise onto blame…

:)

~~~
dotancohen
I actually did read the fine article afterwards and noticed that. Nice job, in
fact this was one of the few HN submissions that drew me to both the comments
and the article!

------
theden
I use this alias to quickly cd to the root directory of a repo. Useful if
you're way deep in a repo and need to back out.

    
    
        alias gitroot='cd $(git rev-parse --show-toplevel) && echo "$_"'

~~~
mythrwy
borrowed and my fingers thank you!

~~~
mort96
You might also be interested in this function from my zshrc:
[https://github.com/mortie/nixConf/blob/f078de8890461d247af45...](https://github.com/mortie/nixConf/blob/f078de8890461d247af459cc966a15449c9515e7/.shrc#L40)

Basically, if you're in the directory
`~/something/src/projectname/foo/com/something/bar/baz`, and want to get back
up to say `~/something/src/projectname/foo`, `to foo` will do that. (Or `to
oo` or `to o`; it matches the end of the name, and goes to the first match;
`to ing` would go to `~/something/src/projectname/foo/com/something`).

------
ericfrederich
I like to run gource: [http://gource.io/](http://gource.io/)

~~~
Pigo
That's pretty slick, and it really does help you visualize who is doing what.
Thanks for that.

------
Vinnl
> You’re not limited to just tags: you can use commit hashes.

Since tags, branches and `HEAD` are simply pointers to commits, it's good to
know that you can interchange them and commits pretty much anywhere where you
can use them (other than creating/deleting tags or branches, of course).

~~~
ithkuil
And IIRC other than cloning, i.e. you cannot git clone at a given sha

------
falcolas
I'll be adding a lot of these; I especially like the "what was I doing..."
ones. To continue with the sharing:

    
    
        up = !git pull --prune $@ && git submodule update --recursive
        cm = !git add -A && git commit
        save = !git add -A && git commit -m 'SAVEPOINT'
        undo = reset HEAD~1 --mixed
        amend = commit -a --amend
        # Removes branches deleted from remote repo
        bclean = "!f() { git branch --merged ${1-master} | grep -v " ${1-master}$" | xargs git branch -d; }; f"
        # Leave current branch & do some cleanup
        bdone = "!f() { git checkout ${1-master} && git up && git bclean ${1-master}; }; f"

~~~
packetized
I'll add a couple of my favorites, as well:

    
    
        glog = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset'
        stash-all = stash save --include-untracked
        update-fork = "!f() { git pull --rebase upstream $1 && git push origin $1; }; f"

------
patrick_haply
Out of curiosity, does anyone here use tig[1]? I've been wondering if it's
worth learning.

[1] [https://jonas.github.io/tig/](https://jonas.github.io/tig/)

~~~
nitemice
I use it occasionally, mainly just when I want to get a better look at the git
graph. I'm sure it has more uses, but that's all I've needed so far.

------
gumby
It's annoying that all the git commands start with 'git' unless you write your
own aliases. It means you can't just type !sta to get the git status much less
'emacs !add:*' to edit the files you just added(!) ; you have to do a ^R
search and edit. !git is almost never what you want unless it's just !!
anyway. RCS had just co and ci and the like[+]

Clearly the guy who came up with this cockamamie scheme was unfamiliar with
Linux. :-)

\+ RCS was pretty bad too; I'm using it just for illustrative purposes

------
macrael
Something I love to do with git: make temporary commits at the tip of a branch
so I can checkout other branches without losing anything. I much prefer this
to stashes, generally. Stashes can still be useful to move changes from one
branch to another, but generally I find changes make sense on the branch they
were being written for.

> git commit -am "TMP COMMIT" > git checkout ... Once you switch back: > git
> reset HEAD^

and you'll undo the temporary commit and be back to where you were.

~~~
dpedu
[https://git-scm.com/docs/git-worktree](https://git-scm.com/docs/git-worktree)

This doesn't exactly fit what you're asking for, but is an alternative
workflow where you'll never need to do that.

~~~
macrael
Interesting! Seems a bit heavyweight for most of what I use temporary commits
for, but could be interesting if there were better tooling

------
atemerev
And the question is... why all these nice features are not enabled by default?
Especially word-level and whitespace-aware diffs.

~~~
TimWolla
Word-level diffs are very situational. In my experience they make the diff
less readable more often than not, because they find the smallest common
parts, which do not necessarily reflect the logical changes.

------
roel_v
So how do largish binaries work in git nowadays? When I last looked (several
years ago), the answer was essentially 'dont do it' or 'use this other tool to
sort of make it work'. Can I store a few gigs of data (alongside my source
code) in git?

~~~
nosefouratyou
Git-annex might be of interest to you.

[https://git-annex.branchable.com/](https://git-annex.branchable.com/)

I don't know how the situation has changed in regards to binaries, or if this
is a good suggestion, I just remember reading about git-annex when it came out
a few years ago.

~~~
drothlis
Or git lfs, which is even supported by / integrated into github and other git
hosting services.

------
nitemice
I actually have that leaderboard alias in my gitconfig already.

I work on a codebase that's over 20 yrs old. A while back, I was interested to
find out who had done the most commits, and what kind impact moving to git,
with it's "commit early, commit often" mentality, had pushed newer players up
the ranks. Suffice to say, it hadn't had as much impact as I was expecting
yet.

------
linkmotif
I've always thought of git blame as a bit of Linus personality infusion into
the git CLI. Aliasing that to "praise" kind of misses that bit of fun.

~~~
zb
Using the name 'blame' for that subcommand is at least as old as CVS, and
probably older.

~~~
krallja
Are you sure? I can't find docs for "cvs blame"; just "cvs annotate."

SVN definitely aliased "blame" and "praise" to annotate, though.

~~~
linkmotif
Interesting! Thanks!

------
herrvogel-
Git-extras[0] is also quite nice.

[0] [https://github.com/tj/git-extras](https://github.com/tj/git-extras)

------
jordigh
There's a great comparison with hg here:

[https://lobste.rs/s/f0t07t/little_things_i_like_do_with_git#...](https://lobste.rs/s/f0t07t/little_things_i_like_do_with_git#c_hbus14)

------
hyperpallium
Instead of --word-diff, I like --color-words.

Though it can be difficult to notice an addition if it's just one or two
chars.

------
soperj
I use meld as my git diff tool. Visual diff is just way easier in my opinion.

------
mmjaa
My personal favourite:

    
    
        git for-each-ref --sort=-committerdate
    

.. shows progress for each branch .. this makes it surprisingly easy to see
which of the developers in our group (with their own branches) is pushing the
codebase further ..

~~~
copperx
That sounds terribly mean spirited. Is that what you meant?

Are you trying to separate the wheat from the chaff from developers that are
already in your team?

~~~
mmjaa
This has nothing to do with developer assessment - its only for finding out,
during the daily review, what branches have had the most activity and what is
ready for the daily review.

------
cryptonector
This is why I wish Fossil were less opinionated and supported rebase and
exposed branches and tags as the light-weight names that they are underneath
the covers (just like git). Fossil uses SQL, which means that all of the
things @csswizardry and much more that no git developer has ever thought of..
can be done with a little bit of SQL.

But no, Fossil's UI is like Mercurial's, and it favors merging over cherry-
picking and rebasing. Their loss!

Fossil does have a cherry-pick operation, and anyways, one could trivially be
constructed. Which means that rebase can also be constructed easily enough.
But my impression is that the devs aren't interested in such contributions.
And the heavy- vs. light-weight branching model in the UI is a big turn off
even if I can deal with it at the SQL level. Fossil's push/pull model
([auto]sync everything) is also not to my liking -- sure, in a corporate
environment pushing every branch is a good idea, but in an open source world
it's not: I may want to push some branches to one upstream, others to another,
and yet others not at all.

This is what I like about git:

\- the index

\- git exposes the Merkle hash tree concept at the lowest layer

\- git branches and tags are just symbolic pointers to commits (see previous
point)

\- support for many remotes

\- git is not opinionated -- if you want to use a merge-based workflow, you
can, and if you want to rebase instead, you can, and if you have to use e-mail
to exchange commits, you can, and so on.

I'm done with the Mercurial "you do what we say" model. A model they keep
half-way reneging on, adding bookmarks (which don't work well), and histedit
and rebase (why not both in one command?! "because we don't like git rebase"
is the answer I imagine) (they really need to be one command!! what if in the
process of rebasing you must drop commits that you know duplicate others in
the new base?!).

I wish Fossil's developers saw this. But they're focused on their needs: VCS
for SQLite3. Since they seem to have few topic branches, they like merging.

Conversely, since Fossil's devs refuse to be non-opinionated, I wish git's
developers saw the power of SQL for VCS. It would save a ton of code C and
shell code, and it would make new extensions trivial. It also would make git
much more power-failure safe: since it could leave that to something like
SQLite3 that does a fantastic job of it (and is very well tested, both in
general and as to power failure safety).

Besides this, I wish git has branch history. That is, a single push can push
multiple commits by different authors, so it would be nice if one could see
who pushed what commits. This would be useful as documentation in and of
itself: if you see N>1 commits pushed together and need to revert one of them,
you might look at whether you need to revert the rest as well, as they might
go together. (Some codebases like to push regression tests first, bug fixes
after. This allows one to see that tests detect the bugs they're testing for
and that corresponding bug fixes fix those bugs. If one has to revert a bug
fix commit, one might have to also revert a corresponding test commit.)

~~~
krupan
Your mercurial experience is out of date. Mercurial now supports a rebase
workflow better than git does (with github and their pull-request workflow
dominating the world, nobody seems to rebase with git anymore). See the evolve
extension, and for even more mindblowingly cool stuff, see mercurial absorb.

Mercurial seems to give merge tools better information when rebasing too. I've
had to resolve some annoying conflicts when rebasing with git, and I've cloned
the repository with hg-git and done the same rebase and had it go flawlessly.

~~~
cryptonector
Cool. Absorb sounds very nice. But even if bookmarks now work flawlessly such
that I need never again deal with Mercurial's awful heavy-weight branching
model, for me it's too late. This is what happens when you don't mind the
store: someone else takes your business. Mercurial people spent years
screaming about the evils of re-writing history only to now aim to provide a
better rebase experience -- fantastic, but too late, the world has moved on.

Also, don't forget the index! I adore the index in git.

~~~
cryptonector
I hope people learn the value of not being opinionated when it comes to
building mind-share. I know, being opinionated works for Apple, and Apple is a
fantastically valuable company. But I don't think it works well for everyone
else. Git's being not-opinionated may be the thing that is best about it.

