top | item 43169435

How Core Git Developers Configure Git

570 points| lnkl | 1 year ago |blog.gitbutler.com

122 comments

order

jelder|1 year ago

My favorite alias is `git out`, which just lists all unpushed commits. I use it all the time.

  [alias]
    out = "log @{u}.."
In my head I always hear it in the voice of The Terminator: https://youtu.be/8cdC1Y5oRFg?t=54

schacon|1 year ago

I should do another article on the best aliases, because this is a great one. I want it just because I want to do the Arnold voice every time.

Now I want to do: `git to-da-choppa`

sedatk|1 year ago

That comes out of the box with Mercurial. (`hg out` or `hg outgoing`).

_kb|1 year ago

While everyone is here considering their life choices (at least as far as they relate to ~/.gitconfig), highly recommend delta [1] as a companion to the git cli.

[1]: https://dandavison.github.io/delta/

leonheld|1 year ago

After using delta for a while, I'm going back to the regular diff view... it's not that it isn't good, but I'm constantly copying diffs (yes, I know I can generate patches) and the pretty output breaks that workflow.

Also, when your terminal is a bit small it's a bit hard to see things. But it's really good software, I recommend anyone who's reading to give it a try.

schacon|1 year ago

I really wanted to add this (I linked it in the last paragraph) but I really wanted to keep the recommendations globally applicable in vanilla git. Delta is awesome though.

chungy|1 year ago

My own ~/.gitconfig looks like this:

    [alias]
     co = checkout
     ci = commit
     st = status
     br = branch
     hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
     type = cat-file -t
     dump = cat-file -p
     dft = difftool
    [tag]
     sort = version:refname
    [tar "tar.xz"]
     command = xz -c
    [tar "tar.zst"]
     command = zstd -T0 -c
    [log]
     date = iso-local
    [pull]
     ff = only
    [diff]
     tool = difftastic
    [safe]
     directory = *
    [advice]
     detachedHead = false
    [init]
     defaultBranch = master

gosub100|1 year ago

Do your aliases even save you any keystrokes? Most shells support auto complete, and you still have to type "git" unless you have a shell alias for it.

I guess what I'm git-ing at is a truly efficient alias would be embedded in the shell. For a while I had "gsno = git status --uno" although it's been so long since I used it, I forget what the options even did. Somehow I get by with only stock commands.

Another helpful alias I used to use was ctrl-space, I had aliased for 'make'. Somehow I liked it because you can almost gesture it with slamming both hands down simultaneously.

chungy|1 year ago

Shortened aliases come from cvs/svn land, sorting tags by a logical manner, adding some extra archive types for "git archive", making it so git log always shows my local time zone, pull will never do a non-ff merge, make it so Git doesn't complain about repositories in places it doesn't like, turning off an annoying message about a detached HEAD state, and shut git up about the default branch.

tome|1 year ago

I agree with the recommendation to use (z)diff3. I think the article massively understates the case! Three way diff makes it possible to resolve some conflicts that are literally impossible to resolve without it. Why impossible? Because with default style adding conflicting things at the same place is indistinguishable from removing the same things at the same place. You need to be able to see the base to determine that.

I wrote up more here: https://stackoverflow.com/a/63739655/997606

I also wrote up how to use diff3 style to make resolving rebase (or merge) conflicts a mechanical procedure: https://h2.jaguarpaw.co.uk/posts/git-rebase-conflicts/

conaclos|1 year ago

Some extra options I like:

  [apply]
    # Remove trailing whitespaces
    whitespace = fix
  [color "diff"]
    whitespace = red reverse
  [diff]
    colorMovedWS = allow-indentation-change
  [format]
    pretty = fuller
  [log]
    date = iso
  [pull]
    ff = only

schacon|1 year ago

I did almost put `log.date iso` in there, I like that one too.

opello|1 year ago

I also use that color.diff.whitespace "red reverse" and along with it diff.wsErrorHighlight all. I recall needing to set both in the past but in trying to find out why that might have been superseded by only needing the latter. I'm not quite sure.

JulianWasTaken|1 year ago

Mine is here [1], I basically had all of these set already (other than column ui which I don't like), but I suspect that's just because I've probably previously read nice posts by Scott and others talking about them.

Maybe trading alias tips is another useful thing to do though, hence sharing the link.

[1]: https://github.com/Julian/dotfiles/blob/main/.config/git/con...

sandreas|1 year ago

I wonder why commit signing is not part of these configs... it is pretty easy to do with a modern ssh key:

  [user]
    name = xyz
    email = xyz@domain.com
    signingkey = ~/.ssh/id_algorithm.pub
  
  [commit]
    gpgsign = true
  [tag]
    gpgsign = true

  [gpg]
    format = ssh

  # restrict allowed signers
  # echo "$(git config --get user.email) namespaces=\"git\" $(cat ~/.ssh/id_*.pub)" >> ~/.git_allowed_signers
  [gpg "ssh"]
    allowedSignersFile = ~/git_allowed_signers  

On github you can add the ssh key for authentication but also for signing. Unfortunately you have to add the key twice but once you've done it, you get rid of the `unverified` label within a commit.

ajross|1 year ago

> Personally, I think the default behavior of Git should be to make your remote references as close to what is on the remote as possible. Prune stuff that’s gone, etc.

Yikes, no. Remote junk disappears all the time, and you never know when you'll have to recover something. Old versions of GitHub pull requests, in particular, tend to be garbage collected at the backend rapidly. It's a semi-regular occurrence for me that I have to dig through reflog to get to early work that everything else has forgotten about.

Just in general, don't delete stuff you don't know you don't need. That's just data robustness 101. nothing to do with git. Deletions should be as manual as possible, and generally done following a backup.

cassepipe|1 year ago

I discovered that you can have git use the pager you like so I set it to my darling : bat

    git config --global core.pager bat
https://github.com/sharkdp/bat/

oneeyedpigeon|1 year ago

Is there an advantage to doing this over setting the GIT_PAGER environment variable?

(Weirdly, git doesn't seem to honour the PAGER var (which would be even better), although its man page claims it does)

betimsl|1 year ago

It just makes everything perfect.

rowanseymour|1 year ago

I really wish they'd update the default config once there's consensus that something new is optimal. I'm wary of even adding the config settings in this article in case things change in the future and I continue oblivious with a now dated setup.

neals|1 year ago

How much of this is for a noob like me that just uses vscode? I hardly ever see the git command line, and when I do see it, its trouble beyond what anybody can fix...

manbitesdog|1 year ago

VSCode git interface is actually pretty powerful, 99.9% of the git actions I do are already covered there, including merging, stashing, tagging, getting output from the git hooks...

However changing the diff algorithm is still a nice one, both for solving conflicts and to avoid the ocassional bad automated merges (the latter is scarier as you don't even know you pushed something wrong).

lucasoshiro|1 year ago

I wouldn't like to be this kind of person, but, in the case of git, the best you can do you is forcing yourself little by little to use the Git CLI. It's far from being beginner friendly, but it shows the things the way the really are. GUI git tools hide things under their hood, which is ok for most cases but when you need a little more complex thing you are limited to their capabilities.

For instance, I wrote this post last year about how you can use Git as a powerful debugging tool: https://news.ycombinator.com/item?id=39877637

jbverschoor|1 year ago

Nothing wrong with a gui. Esp with git imo. The process doesn’t require blazing fast agility, but careful review and selection.

Graphical diff viewers are way better anyway.

pfg_|1 year ago

Setting merge conflictstyle diff3 or zdiff3 will improve your experience if you ever do any merging

musikele|1 year ago

I also have this alias to my config

    [alias]
       lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --branches
It shows a coincise colored graph of your commits and branches.

alex_smart|1 year ago

I have something almost identical, except for the `—-graph` part. That way I have the flexibility to get either the linear view or the graph view by adding that single flag.

rstuart4133|1 year ago

I think this should be the default:

    git push --force-with-lease
Forces pushes are dangerous enough as it is, so I'm mystified on why git doesn't insist you know the state of upstream before running it.

Sadly you can't make it the default, so I resort to:

    [alias]
        force-push = push --force-with-lease
As for the article, I must the the weird one because I prefer most of the settings are they are or don't care. Even some of those designated as "clearly better" don't look to me.

account42|1 year ago

It's unfortunat that --force-with-lease was given such a long name compared to the strictly more dangerous --force. And yes, it would be great if we could configure --force to behave like --force-with-lease - it's not like original --force behavior is ever desirable for human operators.

idoubtit|1 year ago

That was a useful read. I'd been frustrated by `merge.conflictstyle = diff3` so I'm glad I learned about `zdiff3`.

I also discovered `fetch.prune` and `pull.autoSetupRemote` which will slightly enhance my workflow.

My only disagreement is with the diff prefixes. I prefer to display one path starting with ".", so that I can double-click it and paste it. So I don't want contextual prefixes, I'll keep my `diff.dstPrefix = ./`.

account42|1 year ago

> # clearly makes git better

> [...]

> [push]

> autoSetupRemote = true

I strongly disagree. Publishing a new branch on the remote should be an explicit operation. And git push will already tell you the command you need to run so it isn't an issue with having to remember another random command.

rmccue|1 year ago

Nit for schacon: in the "Listing branches" section, you say branch.sort + column.ui and talk about these as first/second options, but the commands are in the opposite order so it reads a little confusingly.

trebligdivad|1 year ago

I took most of the 'Clearly better' and a few others; but NOT the prune options. I don't like things getting deleted without me saying yes!

Galanwe|1 year ago

I use `defaultBranch = master`, because the prude trend of forbidding words based on obvious miscontextualization is more harmful than helpful.

tobyhinloopen|1 year ago

I immediately applied most of these, thanks!

dark-star|1 year ago

Wow this is neat. I never really bothered to deep-dive into my git configuration, but some of these are really cool.

The diff changes are awesome, and I always wondered why there isn't a global .gitignore file in the first place, seeing that every .gitignore file basically has (mostly) the same content

schacon|1 year ago

I mentioned it in the post as well, but Git will look in `.config/git/ignore` if you prefer to just use that rather than setting a new path.

There's also a possible downside here in having things hidden for you but not for others since it's not in your `.gitignore` project file.

I'm honestly kind of on the fence about this one, I don't have much in that file for those reasons.

whirlwin|1 year ago

There's also an ongoing effort among creators to not pollute the home directory with too many hidden files. Instead, as the blog post mentions, there exist .config/git/ignore which I think is a more scalable approach in the long run. Especially looking at the number of tools and utilities requiring one or several config files.

wh33zle|1 year ago

One of the coolest things I've learned about recently is `.git/info/exclude`. It allows you to ignore files in the local repo without modifying the repo's .gitignore

Very useful if you want to add your own .envrc or shell.nix to a repo.

leslielurker|1 year ago

Wow I had no idea these were available. I really hope some of these become the default config.

Dlooooloo|1 year ago

I like main as a branch default.

It's shorter, sounds nicer.

recroad|1 year ago

I’ve been practicing trunk-based development for the last year and none of these Git tips apply.

schacon|1 year ago

I'm curious what you mean. I've been practicing trunk based development since 2011[0] and all of these config settings help that flow. Arguably you don't want the rebase thing, but otherwise I can't imagine what you think here would not apply.

[0] https://scottchacon.com/2011/08/31/github-flow/

lylejantzi3rd|1 year ago

# clearly makes git better

If these options clearly make git better, why aren't they the defaults?

Forge36|1 year ago

The default rarely change avoid breaking changes.

carlosneves|1 year ago

Greatly appreciate these tips! I'll apply most of them, especially the diff ones.

silvanocerza|1 year ago

Happy to notice I already use most of these. The only ones I didn't know are diff.mnemonicPrefix, diff.renames and commit.verbose.

My config if you'd like to steal it, also here: https://github.com/silvanocerza/dotfiles/blob/master/git/git...

Mind that you need diff-so-fancy for this work correctly. https://github.com/so-fancy/diff-so-fancy

    [alias]
        co = checkout
        ci = commit
        cl = clone
        st = status
        f = fetch
        br = branch
        lg = log --graph --abbrev-commit --decorate --date=relative --format=format:'%C(bold blue)%h%C(reset) %C(bold green)(%ar)%C(reset) %C(yellow)- %an%C(reset)%C(auto)%d%C(reset)%n"        "%C(white)%s%C(reset)' --all
        lgg = log --graph --abbrev-commit --decorate --format=format:'%C(bold blue)%h%C(reset) - %C(bold cyan)%aD%C(reset) %C(bold green)(%ar)%C(reset)%C(auto)%d%C(reset)%n''          %C(white)%s%C(reset) %C(dim white)- %an%C(reset)'
        type = cat-file -t
        dump = cat-file -p
        # Lists all local branches that have been deleted on remote
        gone = ! "git for-each-ref --format '%(refname:short) %(upstream:track)' | awk '$2 == \"[gone]\" {print $1}' | xargs git branch -D"
    
    [color]
        ui = true
    
    [push]
        default = simple
    
    [merge]
        tool = meld
        conflictStyle = zdiff3
    
    [diff]
        algorithm = histogram
        colorMoved = dimmed-zebra
        colorMovedWS = no
        mnemonicPrefix = true
        renames = true
    
    [rerere]
        enabled = true
    
    [pager]
        branch = false
    
    [core]
        pager = diff-so-fancy | less --tabs=4 -RFX
        fsmonitor = true
        untrackedCache = true
    
    [color "diff-highlight"]
        oldNormal = red bold
        oldHighlight = red bold 52
        newNormal = bold
        newHighlight = green bold 22
    
    [color "diff"]
        meta = yellow
        frag = magenta bold
        commit = yellow bold
        old = red bold
        new = green bold
        whitespace = red reverse
    
    [diff-so-fancy]
        markEmptyLines = false
    
    [filter "lfs"]
        clean = git-lfs clean -- %f
        smudge = git-lfs smudge -- %f
        process = git-lfs filter-process
        required = true
    
    [pull]
        rebase = false
    
    [init]
        defaultBranch = main
    
    [column]
        ui = auto
    
    [branch]
        sort = -committerdate
    
    [commit]
        verbose = true

FirmwareBurner|1 year ago

What about the famous "disable garbage collection"?

high_byte|1 year ago

this is amazing. I've been swearing at diffs which I now know are myers algorithm and histogram diffs are exactly what I've been wanting!!

nialv7|1 year ago

when's git-fsmonitor going to support linux?

thro949494i|1 year ago

> Default branch

> Personally, I don’t have a problem with master

> updating the default value. I wish Git had some taste here, but they don't

So author has no preference, but git does not have a good taste here, for not breaking backward compatibility. Typical passive agresive bullshit!

I think "main" is not inclusive enough, we should use something like "non-specific-but-strong-branch". Or something that includes even stronger message. And change it every year (or month). It could be automated using github actions on all existing repos!

Also please rename "git" command, it is very insensitive word!!!

randunel|1 year ago

While your comment is in jest, I agree with the sentiment. The whole "main" debacle sucks and, to this day, still breaks everyone's git usage.

penguin_booze|1 year ago

You've no idea how much real social change has been brought about, when Git changed its default branch from master to main. Pardon me, I'm tearing up right now.

aboardRat4|1 year ago

[deleted]

becquerel|1 year ago

There is truly no suggestions of change to people's lives small enough that someone won't make opposing it part of their identity.

ahartmetz|1 year ago

But "main" is a much better name anyway (maybe my non-native English bias), so I don't mind in this particular case.

matusp|1 year ago

In Europe we of course use "royal"

leonheld|1 year ago

Nobody is stopping you from using master, you can do whatever you want. The author even recognizes that very clearly in the text.

You can name all your default branches `megazord`. Hell, you can fork git, call it "gitzord" and enforce your `megazord` branch as The Correct Main Branch Name for every user. Feels good to be free, doesn't it?

And PS: I'm not from the USA ;-)

sliq|1 year ago

Why is git even in development? It's exactly the same like 15 years ago, right? ... right?

Cthulhu_|1 year ago

Most of the items in this list were introduced more recently (a lot from '22) and are definite quality of life improvements. But sure, the core of Git remains mostly unchanged - the 'porcelain', if you will - although the one major change is that they're changing the default hashing algorithm (I'm not sure if this has been done yet, I know it was decided upon in 2018).