-
Notifications
You must be signed in to change notification settings - Fork 1
Description
Git Config Files
An intro to hidden files and configuration files
- Files and directories start with a dot (
.) are hidden - Hidden files are often configuration files meant to be read by programs - and only tampered by people who know what they are doing.
Most development tools and platforms today support some level of configuration as code - meaning that the state of the tool can be created from some level of code - usually a hidden file or folder in the root of the repository.
You probably noticed the .github folder in the root of this repository. It holds configuration data for GitHub Classroom (classroom), GitHub Actions (workflows) and I even created my own scope called template I used to copy the state of the template repository to this one.
There's also a folder called .git next to .github You can't see it in the explorer on VS Code. but you can see it if you run ls -a in the terminal. .git contains alle the control files to the git repositories, an it's not likely that you will need to mess around with any of these files (of course there are always exceptions - so we will hack at least one of the files in there later).
But this is why VC Code shows you .github but not .git: Hidden files are for you, as a developer - only .git specifically - is not.
Most often configuration files are written in either yaml or json. But git uses it's own format - salled git config files. Git uses the git config files to store tuples; that is pairs of keys and values. much like environment variables in a Linux shell.
Here's an excerpt of the global .gitconfig file on my computer
[core]
editor = nano
[push]
default = current
[alias]
undo-commit = reset --soft HEAD^
addremove = add -A .
circelci = !open https://app.circleci.com/pipelines/github/lakruzz
hub = !gh repo view --web
recommit = commit -C HEAD --amend
co = checkout
tree = log --graph --full-history --all --color --date=short --pretty=format:\"%Cred%x09%h %Creset%ad%Cblue%d %Creset %s %C(bold)(%an)%Creset\"
root = rev-parse --show-toplevel
backward = checkout HEAD^1
forward = !git checkout $(git log --all --ancestry-path ^HEAD --format=format:%H | tail -n 1)
[github]
user = Lakruzz
[ghi]
token = !security find-internet-password -a lakruzz -s github.com -l 'ghi token' -w
[user]
email = lakruzz@inc-inc.dk
name = Lars Kruse
[semver]
prefix = v
initial = 0.0.0
[init]
defaultBranch = masterSome of the settings I put there myself, some of them are added by plug-ins or extensions I've installed. Some of the settings simply changes the default behaviour of git - if I remove them git still works - only differently. I use the git config to tweak git to my personal liking.
You can see your own global git config file by running:
cat ~/.gitconfigGit Config is an in-line editor and reader
Git has a subcommand called config. It's an in-line editor/reader. The syntax for using it is:
usage: git config [<options>]
Config file location
--global use global config file
--system use system config file
--local use repository config file
--worktree use per-worktree config file
-f, --file <file> use given config file
--blob <blob-id> read config from given blob object
Action
--get get value: name [value-pattern]
--get-all get all values: key [value-pattern]
--get-regexp get values for regexp: name-regex [value-pattern]
--get-urlmatch get value specific for the URL: section[.var] URL
--replace-all replace all matching variables: name value [value-pattern]
--add add a new variable: name value
--unset remove a variable: name [value-pattern]
--unset-all remove all matches: name [value-pattern]
--rename-section rename section: old-name new-name
--remove-section remove a section: name
-l, --list list all
--fixed-value use string equality when comparing values to 'value-pattern'
-e, --edit open an editor
--get-color find the color configured: slot [default]
--get-colorbool find the color setting: slot [stdout-is-tty]
Type
-t, --type <type> value is given this type
--bool value is "true" or "false"
--int value is decimal number
--bool-or-int value is --bool or --int
--bool-or-str value is --bool or string
--path value is a path (file or directory name)
--expiry-date value is an expiry date
Other
-z, --null terminate values with NUL byte
--name-only show variable names only
--includes respect include directives on lookup
--show-origin show origin of config (file, standard input, blob, command line)
--show-scope show scope of config (worktree, local, global, system, command)
--default <value> with --get, use default value when missing entry
Reading values out of gitconfig
- Which files does
--local,--globaland--systemread from respectively? - What's special about these three locations - Why do you think that exactly these locations are used?
- Which (one) of the three scope files are inside your repository
- Is it version controlled?
You can use the --get switch to git config read out values from everything that git knows from it's config files. Like this:
git config --get user.name
git config actually has three predefined scopes (files) it reads from: --local, --global and --system. When you use the --get switch and don't specify a scope it it read from all scopes and returns the first value match it finds.
You can also use git config to set a value - rather than read it; you simply use the --add switch instead.
If you don't specify a scope it goes to --local if you want it int --global og --system you must say it.
Try this
git root
git config --global --add alias.root "rev-parse --show-toplevel"
git root
...You've just made an alias!
Run the following commands - one at a time
git config --get user.name
git config --get user.email
git config --list
git config --list --show-origin
git config --list --show-scope
git config --local --get user.email
git config --global --get user.email
git config --system --get user.email
git config --local --list
git config --global --list
git config --system --list
You can use a fourth level --file
- What does
--filedo? - what does
$(git rev-parse --show-toplevel)do?
Run these commands one at a time:
git config --get template.repo
git config --file $(git rev-parse --show-toplevel)/.github/template/gitconfig template.repo
Dive into Command Substitution
git rev-parse --show-toplevel is a command that returns a string - and in the example above we uset the output from the command as a string - which we concatenate with another string to build the qualified path to the config file.
It's supported in bash - and the concept goes by the name Command Substitution
Here's what the man page for bash has to say:
Command substitution allows the output of a command to replace the command name. >There are two forms:
$(command)or
`command`
Consequently these two commands does the same job:
git config --file $(git rev-parse --show-toplevel)/.github/template/gitconfig template.repo
git config --file `git rev-parse --show-toplevel`/.github/template/gitconfig template.repoYou can even include config files in other files
You can include more files in any of the others using the key include.path. Dig this!
git config --get template.repo
git config --add include.path ../.github/template/gitconfig
git config --get template.repo
git config --list --show-origin
Dublets are allowed - but not supported - what a mess!
- Don't mess around too much - stick to
--global! Why? - Read order of scopes
- Read order inside files
- How to clean up (your mess)
You can add the same key more than once - this can easily lead to undesired situation.
Let's add a a misspelled alias and then add the correct one, and then see what happens:
git config --add --global alias.co cheeckout
git co
git config --add --global alias.co checkout
git co
OK! That went well - except that they are both still three - the bad alias wasn't replaced a new one was merely inserted.
Run:
git config --show-scope --get-regexp alias.co
It's like a stack it read everyting and the last value put in the stack counts.
git config --add --global alias.co chestnut
git co
git config --show-scope --get-regexp alias.co
...you see?
These files aren't wildly complicated and often the easiest way to sort issues is simply to open the files in an editor.
Fix it by running:
git config --edit --global
Or let's recreate the faulty situation and fix it using --unset and --unset-all
git config --add --global alias.co cheeckout
git config --add --global alias.co checkout
git config --add --global alias.co chestnut
git config --global --unset alias.co
git config --global --unset-all alias.co
Let's run one last test, to learn the read order of the scopes.
git config --add --file .gitconfig alias.scopetest '!echo file'
git config --add --global alias.scopetest '!echo global'
git config --add --local alias.scopetest '!echo local'
sudo git config --add --system alias.scopetest '!echo system'
git config --add --local include.path ../.gitconfig
git config --show-scope --get-regexp alias.scopetest
That was fun! - let's read and then delete them in the order they are read.
git scopetest
rm .gitconfig
git config --local --unset include.path
git scopetest
git config --local --unset alias.scopetest
git scopetest
git config --global --unset alias.scopetest
git scopetest
sudo git config --system --unset alias.scopetest
git scopetest
ENOUGH - lets use it.
Except! Did you notice the ! ?
Aliases are expected default to contain git commands - so the command git is implicitly prefixed to aliases - unless the value starts with an exclamation mark ! - then its a bash command.
Dig this!
[alias]
root = rev-parse --show-toplevel
scopetest = !echo file
root = !git rev-parse --show-toplevel