
Lesser known Git commands - guomanmin
https://hackernoon.com/lesser-known-git-commands-151a1918a60#.uff1lmouf
======
dahart
I've seen more stash accidents than any other kind with git. Stashing is more
dangerous than committing or branching, and to me it doesn't seem to provide
any advantages... do people actually find stashing easier than branching? Is
it just because when you branch you have to name it, and that causes friction?
I stay away from stash.

Right from the man page: "If you mistakenly drop or clear stashes, they cannot
be recovered through the normal safety mechanisms."

[https://git-scm.com/docs/git-stash](https://git-scm.com/docs/git-stash)

\--

I do like shorty. My version:

    
    
      alias gits='git status -sb'
    

\--

My next fave is rebase the current branch against it's upstream branch point:

    
    
      [alias]
        rearrange = "!git rebase -i $(git merge-base HEAD @{u})"
    

git rearrange ftw.

~~~
ryanSrich
I use stash when I forget to branch. So I might be working and then realize I
never branched. So I stash my changes, create a new branch, and then pop my
changes on the new branch.

~~~
ashark
My #1 use of stash is as the quickest way to express "get rid of all this
crap". I rarely intend to ever retrieve it.

~~~
loeg
'git reset'.

~~~
ashark
Doesn't do the same thing, without some more options. Stash is shorter.

------
masklinn
Warning: not actually git commands, rather TFAA's aliases for possibly useful
combinations of switches and/or commands.

~~~
sounds
See, switches in git are there because git maintains backward compatibility.
Sometimes a switch really would logically be better as a whole new command.
But it's a common refrain to hear that git hides useful things in the
switches.

This article is a great way to find those hidden things.

~~~
masklinn
> See, switches in git are there because git maintains backward compatibility.

Switches are there because git commands tend to greatly leak the abstraction
and group behaviour in terms of the underlying plumbing, and because Git
maintainers most likely would rather add new switches to existing commands
than new commands intersecting with existing ones.

> Sometimes a switch really would logically be better as a whole new command.

Which could trivially be done without BC issues and thus can't be an
explanation why existing commands keep growing new switches.

> This article is a great way to find those hidden things.

It would have been a useful article if it had been about showcasing
interesting or useful git switches.

------
opk
Some of my aliases, first some basic shortcuts:

    
    
      freshen = commit --amend --no-edit --date=now
      cont = rebase --continue
      br = branch --column
      recent = branch --sort=committerdate
      idiff = diff --cached
      ff = merge --ff-only
      co = checkout
    

Show what's left to do in an interactive rebase:

    
    
      todo = !cat `git rev-parse --git-dir`/rebase-merge/git-rebase-todo
    

Set tracking to to origin/<the same branch name>

    
    
      upstream = !zsh -c 'git branch --set-upstream-to=origin/$(git symbolic-ref --short HEAD) $(git symbolic-ref --short HEAD)'

I use this to swap my work to home e-mail or vice-versa if I've got it wrong.
Need to specify an initial commit.

    
    
      allmine = filter-branch --env-filter 'GIT_COMMITTER_EMAIL=my@email.com GIT_AUTHOR_EMAIL=my@email.com'
    
      vim = "!gvim `git ls-files -m`"

------
CGamesPlay
A few that weren't mentioned below:

    
    
      git alias start 'checkout @{u} -B'
    

All my branches track origin/master (rather than local master), which makes it
easy to git push / git pull without extra arguments. This will checkout a new
branch from whatever the current branch is tracking. Usage: `git start my-new-
feature`

    
    
      git alias track 'track = branch --set-upstream-to'
    

Sets up your current branch to track some other branch. Usage: `git track
origin/master`.

    
    
      git alias gcbr 'gcbr = !git branch --no-track --no-color --merged | sed 's/[ *]*//' | grep -v master | xargs -n1 git branch -d &> /dev/null || exit 0'
    

"Garbage Collect BRanches" will delete any branches which are already merged
into your current branch (excluding master). Basically, any branch which is
"safe" to delete. (This one has been handed down through the ages; I initially
found it on the skeleton dotfiles when I started working at Facebook. Thanks,
whoever!)

~~~
brown9-2
nitpick: I don't think you need --no-track in `branch --merged` as no branch
is being created with that command.

------
k__
I'm no Git wizard, but switching from "merge" to "rebase" and from "add
<file>" to "add -p" cleaned up my repos quite a bit.

~~~
joncp
One reason I like git so much is that right there. The author is interested in
reasoning about what branch certain changes came from, but I have to wonder
why. Once changes are squashed and ff-merged, it's pretty clear where they
came from.

------
michaelmior
> so it’s good practice to create an empty commit as your repository root

While I'm aware of issues with rebasing the root commit, I've never heard this
advice before and it seems unnecessary.

~~~
samstokes
Rebasing the root commit has been possible for a while using `git rebase
--root`.

[http://stackoverflow.com/questions/30277149/how-to-use-
inter...](http://stackoverflow.com/questions/30277149/how-to-use-interactive-
rebase-on-the-first-root-commit-of-a-branch) goes into more detail.

------
leni536
> git it and empty root commit

Why doesn't git init does this by default? It would be nice if every repo had
an empty root commit (with no author, date,... so it has the same commit
hash), then every two repos would have a common commit. Semantically it would
mean that repos would be just specific branches of a hypotetical large repo.

~~~
geofft
You can already merge two divergent histories with the --allow-unrelated-
histories option. (At the data level, merge commits don't care that their
parents have a common ancestor, but the git merge command does this as a
safety check.)

In other words, all repos already _are_ subsets of the ur-repo. The
semantically-interesting data in git is not the commits themselves, but the
branch names given to specific commits in specific (groups of) repos. :)

~~~
leni536
Interesting, now I learned that a git repo can have any number of commits that
have no parents. It can be achieved by fetching an unrelated repo and merging
with --allow-unrelated-histories or by using git checkout --orphan.

> safety check

Why is this considered unsafe? Only to avoid pulling from unrelated repos by
accident? The man says for --allow-unrelated histories:

> As that is a very rare occasion, no configuration variable to enable this by
> default exists and will not be added.

Well this is quite harsh, it looks like it's considered to be rather unsafe.

~~~
geofft
Yeah, I imagine it's unsafe because "git pull" implicitly creates a merge
commit when it can't otherwise update, and your remote URL might now be
pointing at a different history. Maybe github.com/someproject/somecode got
replaced by a clean rewrite and you're actually looking for
someproject/somecode-old. Maybe you were pointing at an unofficial git-svn
import and now there's an official git migration. And so forth.

(Personally, I think the right way to address this is for "git pull" not to
implicitly create merge commits...)

------
creeble
This doesn't even look like I wrote it, but I thought I did and use it all the
time: "What was that (private) branch I worked on a few weeks ago?"

Just put this shell script in /usr/local/bin or wherever as "git-branch-dates"

    
    
      git branch-dates
      #!/bin/bash
    
      for k in `git branch|perl -pe s/^..//`;do echo -e `git show --pretty=format:"%Cgreen%ci %Cblue%cr%Creset" $k|head -n 1`\\t$k;done|sort -r

------
aequitas

      alias such=git
      alias very=git
      alias wow='git status' 
    
      $ wow
      $ such commit
      $ very push
    

[https://twitter.com/chris__martin/status/420992421673988096?...](https://twitter.com/chris__martin/status/420992421673988096?lang=en)

------
SEJeff
My personal favorite two:

    
    
        jschroeder@omniscience:~$ git config alias.up
        pull --rebase
        jschroeder@omniscience:~$ git config alias.down
        push
    

They can be used thusly:

    
    
       git up && git down

~~~
michaelmior
While these aliases are useful, I'm confused why "down" means "push".

~~~
pkamb
"The enemy gate is down."

~~~
sqeaky
I love the implication that the upstream repo is the enemy, so you mentally
put it down. Way to go Ender.

------
radarsat1
I like the `git grog` command, I might actually use that.

The idea of `git please` is not bad but it made me wonder if it could be
possible to go even further.. a variant on --force that only force-pushes IF
NOT ONE HAS PULLED YET.

I can see why this wouldn't be built into git core however, as it's quite
stateful and depends on tracking other people's fetches, which probably isn't
easy.

------
anarcat
one thing i wish git would do would be to allow me to specify default options
for certain aliases. for example, I quite like the `--short --branch` format
of git status. but I can't do this:

    
    
      [alias]
         status = "status -sb"
    

because git just ignores those. i am then forced to setup non-standard
commands just to bend git to my will.

in some cases, there are distinct `git-config` options I can set to get the
right behavior, but that's way less flexible since stuff like `--force-with-
lease` don't have their own config options.

the rationale behind this is, according to the git-config manpage, that "To
avoid confusion and troubles with script usage, aliases that hide existing git
commands are ignored". but then there's --porcelain for that, so i don't
understand that limitation.

sigh.

~~~
eridal

        [alias]
          pull = push
          push = pull
    

:)

------
relics443
Some of the aliases are funny but could be renamed for brevity.

Also grog is making me feel extremely groggy.

~~~
masklinn
"commend" seems especially bad, it's both unclear and longer than "amend"
would be.

My aliases list is pretty small, aside from amend I have

* get (pull --ff-only --all -p), basically a hard sync with all remotes, updating the current branch if there are no conflicts or divergences

* summary (log --oneline --no-merges), given a range from git fetch/pull, provides an overview of the commits you just fetched

* original (!git show $(cat .git/rebase-apply/original-commit)) during a rebase conflict, shows the original commit whose application failed (it can be difficult to untangle a rebase conflict, this sometimes helps a lot)

------
michaelmior
> If in doubt, the long one (git staaash) will always restore your worktree to
> what looks like a fresh clone of your repository.

Not necessarily. This depends on what branch you're on as well as whether or
not it's up to date with the remote.

------
poorman
My most useful bash alias (when you go back to master and want to start at the
most up to date everything before creating a new branch):

    
    
      alias grm="git fetch origin && git reset --hard origin/master"

~~~
etqwzutewzu
same here but I switch to master first:

resync = !git reset --hard HEAD && git checkout master && git reset --hard
origin/master && git pull

or with a parameter to do the same with any branch:

resync = "!f() { git reset --hard HEAD && git checkout $1 && git reset --hard
@{u} && git pull ; }; f"

~~~
derwiki
I use a very similar version of this, and people are often surprised that I'm
working directly on master. Well, when it's ready to go, `git checkout -b
feature/name; git push origin HEAD; git resync`.

------
kuahyeow
My two favourite aliases :

    
    
        mo = ls-files -m
        lol = log --oneline --decorate --graph
    

`git mo tests/` gets you modified files for the tests folder.

`git lol` I think as the command line version of gitk

------
SonOfLilit
Nothing esoteric, but I advise every person I teach git about to put this in
their .bashrc:

    
    
        alias s='git status'
        alias l='git log --graph --oneline --decorate --all --show-signature'
        alias d='git diff'
        alias dc='git diff --cached'
        alias c='git commit -m'
        alias ca='git commit -am'
        alias a='git add -p'
        alias u='git checkout -p'
    

These probably saved me hours of typing by now. Especially `l` I think should
be canon.

~~~
cm3
I don't know if you're aware, so this is just a friendly suggestion: if you
define these as git aliases instead, you won't use your shell's namespace and
have it available for other aliases. As git aliases you would run it like `git
s` or `git ca`. If you also alias git to g it will be `g ca`. If you do that,
don't forget to call `__git_complete g __git_main` in .bashrc for it to auto-
complete aliases for `g` and not just `git`. For example, to define `ca`, run
this

    
    
        git config --global alias.ca 'commit -am'

~~~
SonOfLilit
I am of course aware of git aliases (e.g. because OP uses them a lot) but it
never occured to me specifically to shorten git to g and find a way to keep
autocomplete working.

However, since these are by far my most typed commands (specifically, probably
40% of my executed commands are `s`), I strongly care about the difference
even between 1 and 3 keystrokes.

Thanks for your feedback. I wonder if the downvote was because of using bash
aliases or for something more substantial...

~~~
cm3
I never downvote anything, so I'm not sure, but maybe the use of "advise" and
"canon" triggered negative votes?

~~~
SonOfLilit
Thanks (if you're still reading this thread), I didn't know "advise" carried
negative connotations. I guess it's one of those words that I learned from a
dictionary and never stopped to notice that English speakers use it only in
much more formal situations than the word in my native language that I'm
trying to evoke.

Should that maybe have been "recommend"?

~~~
cm3
English isn't my 1st language either, but the power of downvotes is misused a
lot on the internet, especially when there's no public trail who downvoted.
Advise and canon may just carry too strong of a meaning for those who grew up
in English-speaking school systems.

------
ZenoArrow
I'm still getting to know Git, and I appreciate articles like this as they
touch on approaches to Git that have benefits in the real world, but I can't
help but feel that version control tools should be simpler.

Does anyone have any experience with version control tools that have worked
out simpler to use than Git? I've heard good things about Darcs before (aside
from its performance issues, which projects like Pijul are designed to
address), are there any other tools that are worth investigating?

~~~
pfranz
It depends what you're using it for. Many people use Windows every day without
knowing things like how to remap a network drive--but it's nice to have it
there when you need it.

If you're by yourself and just need to keep a linear history all you need is
'git init' 'git add' and 'git commit'\--if that's all you ever need, svn or
any version control should work about equally well. But if you do need remote
repositories, branches, rewriting your history, or commit hooks, it's there
waiting if you're using git.

I feel like most of the different version control systems differ on those
advanced features or how it scales for special needs. Git is great for
decentralized and remote work, Perforce is better at handling histories with
large binary files, etc. The basic features are fairly similar between them.
Maybe there's a gui you like or a specific workflow, but I feel like that's a
subjective choice.

------
neuroid
_The first commit of a repository can not be rebased like regular commits_

That's not necessarily true. This can be done with:

    
    
      git rebase -i --root

------
RandomBK
Nice list! I'll adapt some for my git workflow.

Here's some that I've created or collected over the years, in case anyone
finds this useful:

[https://github.com/randombk/randombk-
dotfiles/blob/master/ma...](https://github.com/randombk/randombk-
dotfiles/blob/master/master-main/.gitconfig)

------
daenney
For every repo I fork I usually add a remote named upstream pointing to the
original.

So then, whenever I need to get my copy up to date with upstream it's:

    
    
        update = !git fetch upstream && git merge upstream/master'
    

It's probably easily changed to make the branch configurable, but I have only
ever needed this with master.

------
shawkinaw
A couple of aliases I use all the time:

    
    
        [alias]
            graph = log --oneline --decorate --graph --all
            copy-logs-since = !git log --reverse --format=%B "$1".. | pbcopy
    

`graph` is similar to `grog` in the article. `git copy-logs-since <tag>`
copies log messages since the tag, which I use for release notes. Very handy.

------
CUViper
> git commend quietly tacks any staged files onto the last commit you created,
> re-using your existing commit message. So as long as you haven’t pushed yet,
> no-one will be the wiser.

Well, they might notice that the author and committer dates don't match, but
you can add "\--reset-author" to _really_ make this undetectable.

------
joelthelion
I like "git ready":

    
    
        ready = !git checkout master && git fetch -p && git merge --ff-only
    

The use case is for when you're done with a particular feature branch and
ready to start something new. It puts you back on master and updates it in a
safe way.

------
swang
Author's "commend" function I just call "cane"

git (c)ommit --(a)mend --(n)o-(e)dit

------
pattu777

        git diff origin/master -- test.file
    

It will show you a diff of a file between the master branch of remote origin
and your local branch. I use it way more than I can think of.

------
avel
Didn't know about those stash options. I've always used 'git stash save -u' to
bring along the untracked files in the stash, that's the more common scenario.

~~~
OskarS
I just usually do something silly like 'git add . && git stash', i had no idea
it was built in...

------
wnevets
Heres one I like to use sdiff = diff --ignore-space-change

------
etqwzutewzu
One of my personal favorite:

stp = !git stash && git pull && git stash pop

~~~
spinningarrow
If you `git pull --rebase` by default, just set the rebase.autoStash config to
true and git will automatically do this for you.

~~~
eric_the_read
Can't believe I just learned about rebase.autoStash! That is super handy.

------
jackmaney
git config --global alias.stache stash

------
sigmonsays
You baitin... how is a lesser known git command a git alias?

