Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Git config #7

Open
12 tasks
lakruzz opened this issue Feb 12, 2023 · 0 comments
Open
12 tasks

Git config #7

lakruzz opened this issue Feb 12, 2023 · 0 comments
Labels
Template Template issue

Comments

@lakruzz
Copy link
Member

lakruzz commented Feb 12, 2023

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 = [email protected]
  name = Lars Kruse

[semver]
  prefix = v
  initial = 0.0.0

[init]
  defaultBranch = master

Some 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 ~/.gitconfig

Git 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, --global and --system read 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 --file do?
  • 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.repo

You 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

@lakruzz lakruzz added the Template Template issue label Feb 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Template Template issue
Projects
None yet
Development

No branches or pull requests

1 participant