1. 17
  1. 13

    I use the Fish shell’s abbr (abbreviation) feature for my typing-saving Git aliases. Because Fish replaces the abbreviation with the full command before the command is logged and run, I can more easily spot when I ran certain Git commands when I browse my shell session or search through my command history.

    For example, after defining this abbreviation:

    abbr --add gitst "git status"

    I can type gitst in my prompt:

    $ gitst

    But after pressing Enter, my session looks like this:

    $ git status
    nothing to commit, working tree clean

    Then, if I leave my shell and go back to it later, I can see that I just ran git status. I don’t have to remember what gitst stands for.

    Along the same lines, I can see the long form of flags in my history while spending as much time typing as if I had written their short forms. My gitspu abbreviation expands to git stash push --include-untracked, which is easier to read than git stash push -u.

    1. 10

      I just use magit.

      1. 7

        Surprised noone has mentioned aliasing git push --force-with-lease yet (shame this wasn’t the original default implementation of --force). (Explanation)

           pforce = "push --force-with-lease"

        or pf if you really hate typing.

        (But +1 for using magit and rarely needing to run git commit line commands!)

        1. 1

          I’ve never been sold on the idea of --force-with-lease being the “one true way” or a “correct” default. It seems to just trade-off between fetch vs push -f being ‘safe’. There are scenarios where that trade-off might be a good one to make, but it’s still a judgement call. At least to me, from a logical perspective it seems a more intuitive and consistent default that push would be the ‘unsafe’ operation rather than fetch.

          And just in general I feel like encouraging forced pushes to shared branches is not how I would choose to balance things. It might sometimes be necessary, but I’m perfectly ok if it’s a bit awkward and requires some careful thought.

          1. 4

            Agree that pushing to shared branches is still to be avoided.

            However force-pushing to private branches is something people regularly do if their workflow involves any rebasing, so having the default be a behavior that fails if the branch has somehow become “not private”, rather than just overwriting whatever was there with no obvious sign it wasn’t what was expected, gives some comfort and avoids insidious problems.

            Same if you accidentally go to push to a shared branch (or someone else’s feature branch) without knowing it. Failing is preferable to quietly removing commits.

            In a perfect world where noone makes any mistakes, –force and force-with-lease would be 100% equivalent as the latter would never have a reason to fail.

            Edit: Just realised what you mean about fetch: that with force-with-lease the danger becomes that you don’t notice the remote branch updated when you fetched, and then the force-with-lease succeeds anyhow. Is that right? This is a good point, although I still think some footgun protection beats no footgun protection.

        2. 3

          My favorite yet cursed alias:

          git config alias.save "commit --no-edit --allow-empty-message"

          I say commit early, commit often, and then squash / rebase when you’re going to push something publicly.

          I’ve broken tools that parse git log this way, though.

          1. 3

            I have “git ff” for “git merge --ff-only”. And “git lol” for “git log --format=oneline”.

            1. 2

              I like these threads, I always learn a new trick. :)

              Here’s mine:

                br = branch -vv --sort=-committerdate
                co = checkout
                dc = diff --cached
                ff = pull --ff-only
                fixup = commit --amend --no-edit
                lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
                st = status --short --branch
                wd = diff --word-diff
              1. 2

                I use three, but of those three, only one is worth mentioning:

                psuh = push
                1. 4

                  Interesting option:

                  autoCorrect = 10            # Run guessed command after 1s if there's only one guess.
                  $ git psuh
                  WARNING: You called a Git command named 'psuh', which does not exist.
                  Continuing in 1.0 seconds, assuming that you meant 'push'.
                  Everything up-to-date
                  1. 1

                    why not:

                    p = push

                    Then you 1) cannot misspell it and 2) save 3 key strokes!

                  2. 2

                    Nice, I also have “g s” and “g l”.

                    I can also recommend the meta process: Once in a while look at the statistics of your shell history to see which commands you enter most often. Create shortcuts for those.

                    # most used commands
                    cat $HISTORYFILE | cut -d ' ' -f 2- | sort | uniq -c | sort -n | tail -n 32
                    # most used first words in commands
                    cat $HISTORYFILE | cut -d ' ' -f 2 | sort | uniq -c | sort -n | tail -n 32

                    The output for me currently is:

                    171 cd
                    197 g diff
                    205 g l
                    208 g st
                    542 ls

                    I do have a shortcut “l” for “ls” but somehow I don’t use it.

                     373 m
                     608 v
                     631 apt
                     747 cd
                     829 ls
                    1464 g

                    Interesting, I’m running apt more often than vim these days?

                    1. 2

                      I use similar ones. A few more suggestions I use regularly:

                      conflict = !"$EDITOR" -c '/^[<=|>]\\{7\\}' `git ls-files --unmerged | cut -c 51- | uniq`
                      praise = blame
                      branches = for-each-ref --sort=-committerdate refs/heads/ --format='%(authordate:short) %(color:red)%(objectname:short) %(color:yellow)%(refname:short)%(color:reset) (%(color:green)%(committerdate:relative)%(color:reset))'
                      remotebranches = for-each-ref --sort=-committerdate refs/remotes/ --format='%(authordate:short) %(color:red)%(objectname:short) %(color:yellow)%(refname:short)%(color:reset) (%(color:green)%(committerdate:relative)%(color:reset))'
                      1. 2

                        I have a ton, broken broadly into shortcuts and flow.

                        My most used shortcuts are

                        amend = commit --amend --no-edit
                        s = status
                        co = checkout
                        cof = checkout -f
                        cm = commit -m
                        a = add
                        staged = diff --staged
                        poh = push origin HEAD
                        sync = "!f(){ git stash && git pull && git stash pop; }; f"

                        On the flow side, basically we have a dev branch which each developer creates feature branches off (with a naming convention of <initials>/<feature name>). I have these helpers:

                        dev = "!f(){ git co us/dev/$1; }; f"
                        devs = branch --list /dev/*
                        rmdev = "!f(){ git branch -D us/dev/$1; }; f"
                        rmdevs = "!f(){ git branch -D `git branch --list 'us/dev/*'`; }; f"
                        mkdev = "!f(){ git fetch origin dev:dev && git branch us/dev/$1 dev && git co us/dev/$1; }; f"
                        mgdev = "!f(){ git fetch origin dev:dev && git merge dev; }; f"

                        Sometimes I’m on a machine without my usual .gitconfig and I wonder if I’ve done the right thing muscle-memorizing all my own nonsense :)

                        1. 1

                          Instead of sync you can just use git config --global rebase.autostash true and the result will be similar.

                          1. 1

                            I only use sync in specific circumstances (between local clones on the same branch), so I don’t want it to be the default. But that’s cool to know.

                        2. 1

                          I use these:

                              co = checkout
                              st = status
                              lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit
                              bn = symbolic-ref --short HEAD
                              extend = commit --amend --no-edit
                              reword = commit --amend
                              rb = branch --sort=-committerdate
                          1. 1

                            I have a bunch of aliases, but nowadays I just use magit. The most useful ones I have that weren’t in the article are probably:

                              lol = log --graph --decorate --pretty=oneline --abbrev-commit
                              s = status --short
                              dc = diff --cached --diff-algorithm=minimal -w
                              dw = diff --word-diff
                              cm = commit -m
                              a = add
                              dd = "! git status --short | awk '$1 == \"D\" { print $2}' | xargs git rm --cached"
                            1. 1

                              fish to turn every git alias into a shell alias:

                              git config --list | perl -lne '/^alias[.]([^=]+)/ and $1 ne "alias" and print "alias $1=\"git\\ $1\""' | source


                              eval "$(git config --list | perl -lne '/^alias[.]([^=]+)/ and $1 ne "alias" and print "alias $1=git\\ $1"')"

                              My history just looks like this:

                              vim app/controllers/syntax_highlighting_controller_methods.rb
                              tx origin
                              cb kiv-enable-darkship-globally

                              It looks arcane but having git be so near at hand makes it very easy to move fast.

                              1. 1

                                Ok, so this isn’t that practically useful. 😢 But, let me reiterate what I said earlier: it makes you feel efficient, and maybe there’s some kind of placebo effect that actually makes you more productive.

                                I’d say it’s more important than that. More characters is more cognitive overhead, it’s more places where you can typo and do the wrong command, and if you need to fix something, it’s more characters to erase.

                                1. 1

                                  Doesn’t it seem weird that there’s support for aliases in .gitconfig? I guess this is sort of the logical succession from things like the porcelain commands but it really seems like messy scope creep compared to people just making aliases themselves in their shells or whatnot.

                                  The internals of git feel really clean, but the UI is really hard to deal with…

                                  1. 1

                                    git already is two layered CLI UI-wise. see: porcelain

                                  2. 1

                                    I try to use even shorter aliases through my shell, here are mine: https://github.com/timvisee/dotfiles/blob/master/bash/bash_aliases#L66-L81

                                    1. 1


                                      	ai = add -i
                                      	b = branch
                                      	ca = commit --amend
                                      	ci = commit
                                      	co = checkout
                                      	cob = checkout -b
                                      	lg = log --color --graph --oneline --abbrev-commit
                                      	pr = pull-request
                                      	rci = commit --amend --reuse-message HEAD
                                      	squash = rebase -i @{u}
                                      	st = status -sb
                                      	todo = grep -Ee '\\bTODO:?\\b'
                                      	fixme = grep -Ee '\\bFIX(ME)?:?\\b'
                                      	com = checkout master
                                      	ag = grep
                                      	rg = grep
                                      	ver = tag --sort=version:refname
                                      	skip = update-index --skip-worktree
                                      	unskip = update-index --no-skip-worktree
                                      	publish = push -u hauleth
                                      	cleanup = "!git branch --format='%(refname:lstrip=2)' --no-contains HEAD --no-contains master --merged master | xargs -xn1 git branch -d"