diff --git a/pygitweb/actions.py b/pygitweb/actions.py
index 913dd3d..f96502d 100644
--- a/pygitweb/actions.py
+++ b/pygitweb/actions.py
@@ -283,6 +283,10 @@ def git_summary(project: str) -> HTMLResponse:
 			["Description", esc_html(descr)],
 			["Owner", esc_html(owner)],
 			[
+				"Settings",
+				f"<a href='/project/{quote(project, safe='/')}?a=config'>Git configuration</a>",
+			],
+			[
 				esc_html("HEAD"),
 				f"<a href='/project/{project}?a=commit&h={quote(head or '', safe='')}'>{head_short or 'N/A'}</a>",
 			],
diff --git a/pygitweb/config.py b/pygitweb/config.py
index f66282f..0c3e568 100644
--- a/pygitweb/config.py
+++ b/pygitweb/config.py
@@ -23,6 +23,7 @@ ACTIONS = {
 	"blob_plain",
 	"commitdiff",
 	"commit",
+	"config",
 	"heads",
 	"history",
 	"log",
diff --git a/pygitweb/data/config-options.json b/pygitweb/data/config-options.json
new file mode 100644
index 0000000..fa9d3ac
--- /dev/null
+++ b/pygitweb/data/config-options.json
@@ -0,0 +1,2094 @@
+[
+  {
+    "section": "user",
+    "subsection": null,
+    "name": "name",
+    "displayName": "Name",
+    "type": "text",
+    "default": null,
+    "description": "The name used for commits. This should be your full name or a name that identifies you.",
+    "version": ""
+  },
+  {
+    "section": "user",
+    "subsection": null,
+    "name": "email",
+    "displayName": "Email",
+    "type": "email",
+    "default": null,
+    "description": "The email address used for commits. This should be your actual email address.",
+    "version": ""
+  },
+  {
+    "section": "user",
+    "subsection": null,
+    "name": "useConfigOnly",
+    "configName": "useConfigOnly",
+    "displayName": "Use Config Only",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "Instruct Git to avoid trying to guess defaults for user.email and user.name, and instead retrieve the values only from the configuration.",
+    "version": "2.8.0"
+  },
+  {
+    "section": "user",
+    "subsection": null,
+    "name": "signingKey",
+    "configName": "signingKey",
+    "displayName": "Signing Key",
+    "type": "text",
+    "default": null,
+    "description": "If git-tag or git-commit is not selecting the key you want it to automatically when creating a signed tag or commit, you can override the default selection with this variable.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "editor",
+    "displayName": "Editor",
+    "type": "text",
+    "default": null,
+    "description": "The editor that Git will use for commit messages and other text input. Common values: vim, nano, code, notepad.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "autocrlf",
+    "displayName": "Autocrlf",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "input"
+    ],
+    "default": null,
+    "description": "Controls how Git handles line endings. 'true' converts LF to CRLF on checkout, 'input' converts CRLF to LF on commit, 'false' does no conversion.",
+    "version": "1.5.1"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "safecrlf",
+    "displayName": "Safecrlf",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "warn"
+    ],
+    "default": null,
+    "description": "If true, Git will refuse to commit files with mixed line endings. If warn, Git will warn but allow the commit.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "quotepath",
+    "displayName": "Quotepath",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will quote paths with unusual characters. If false, paths are displayed as-is.",
+    "version": "1.5.3"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "ignorecase",
+    "displayName": "Ignorecase",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will ignore case differences in file names. Useful on case-insensitive filesystems.",
+    "version": "1.5.6"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "filemode",
+    "displayName": "Filemode",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will track file permission changes. Set to false on filesystems that don't support permissions.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "repositoryFormatVersion",
+    "configName": "repositoryFormatVersion",
+    "displayName": "Repository Format Version",
+    "type": "text",
+    "default": "0",
+    "description": "Internal variable identifying the repository format and layout version.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "bare",
+    "displayName": "Bare",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, the repository is assumed to be bare with no working directory.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "worktree",
+    "displayName": "Worktree",
+    "type": "text",
+    "default": null,
+    "description": "Path to the root of the working tree.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "logAllRefUpdates",
+    "configName": "logAllRefUpdates",
+    "displayName": "Log All Ref Updates",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "always"
+    ],
+    "default": "true",
+    "description": "If true, Git will log all ref updates to logs/HEAD file.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "precomposeUnicode",
+    "configName": "precomposeUnicode",
+    "displayName": "Precompose Unicode",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will precompose unicode characters on Mac OS X.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "hooksPath",
+    "configName": "hooksPath",
+    "displayName": "Hooks Path",
+    "type": "text",
+    "default": null,
+    "description": "Path to the directory where Git hooks are stored.",
+    "version": "2.9.0"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "abbrev",
+    "displayName": "Abbrev",
+    "type": "text",
+    "default": "7",
+    "description": "Set the length object names are abbreviated to.",
+    "version": "1.7.5"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "commentChar",
+    "configName": "commentChar",
+    "displayName": "Comment Char",
+    "type": "text",
+    "default": "#",
+    "description": "Character used to indicate a comment in config files.",
+    "version": "1.8.2"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "excludesFile",
+    "configName": "excludesFile",
+    "displayName": "Excludes File",
+    "type": "text",
+    "default": null,
+    "description": "Path to a file containing patterns to exclude from git status and other commands.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "askPass",
+    "configName": "askPass",
+    "displayName": "Ask Pass",
+    "type": "text",
+    "default": null,
+    "description": "Program to use for asking for passwords.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "attributesFile",
+    "configName": "attributesFile",
+    "displayName": "Attributes File",
+    "type": "text",
+    "default": null,
+    "description": "Path to a file containing gitattributes.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "compression",
+    "displayName": "Compression",
+    "type": "text",
+    "default": "-1",
+    "description": "Compression level for pack files. -1 is default, 0 is no compression, 1-9 are compression levels.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "deltaBaseCacheLimit",
+    "configName": "deltaBaseCacheLimit",
+    "displayName": "Delta Base Cache Limit",
+    "type": "text",
+    "default": "96",
+    "description": "Maximum number of bytes to cache for delta base objects.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "bigFileThreshold",
+    "configName": "bigFileThreshold",
+    "displayName": "Big File Threshold",
+    "type": "text",
+    "default": "512m",
+    "description": "Files larger than this will be stored as separate files instead of being delta compressed.",
+    "version": "1.7.6"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "packedGitLimit",
+    "configName": "packedGitLimit",
+    "displayName": "Packed Git Limit",
+    "type": "text",
+    "default": "256m",
+    "description": "Maximum size of a packed git file.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "packedGitWindowSize",
+    "configName": "packedGitWindowSize",
+    "displayName": "Packed Git Window Size",
+    "type": "text",
+    "default": "32m",
+    "description": "Size of the window used for delta compression.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "packedRefsTimeout",
+    "configName": "packedRefsTimeout",
+    "displayName": "Packed Refs Timeout",
+    "type": "text",
+    "default": null,
+    "description": "Timeout for reading packed refs.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "preloadIndex",
+    "configName": "preloadIndex",
+    "displayName": "Preload Index",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will preload the index in parallel to speed up operations.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "createObject",
+    "configName": "createObject",
+    "displayName": "Create Object",
+    "type": "select",
+    "options": [
+      "link",
+      "rename"
+    ],
+    "default": "link",
+    "description": "How to create object files: 'link' uses hardlinks, 'rename' uses rename.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "notesRef",
+    "configName": "notesRef",
+    "displayName": "Notes Ref",
+    "type": "text",
+    "default": "refs/notes/commits",
+    "description": "Ref to use for notes.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "noSymbolicLinks",
+    "configName": "noSymbolicLinks",
+    "displayName": "No Symbolic Links",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will not use symbolic links.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "symlinks",
+    "displayName": "Symlinks",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will use symbolic links when possible.",
+    "version": "1.5.1"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "gitProxy",
+    "configName": "gitProxy",
+    "displayName": "Git Proxy",
+    "type": "text",
+    "default": null,
+    "description": "Command to use as a proxy for git:// URLs.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "sshCommand",
+    "configName": "sshCommand",
+    "displayName": "SSH Command",
+    "type": "text",
+    "default": null,
+    "description": "Command to use for SSH connections.",
+    "version": "2.10.0"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "pager",
+    "displayName": "Pager",
+    "type": "text",
+    "default": null,
+    "description": "Command to use as a pager for output.",
+    "version": ""
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "whitespace",
+    "displayName": "Whitespace",
+    "type": "text",
+    "default": null,
+    "description": "Comma-separated list of whitespace problems to detect: blank-at-eol, blank-at-eof, space-before-tab, indent-with-non-tab, tab-in-indent, trailing-space, cr-at-eol.",
+    "version": "1.5.4"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "fsckObjects",
+    "configName": "fsckObjects",
+    "displayName": "Fsck Objects",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will verify objects during transfer.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "fsckSkipList",
+    "configName": "fsckSkipList",
+    "displayName": "Fsck Skip List",
+    "type": "text",
+    "default": null,
+    "description": "Path to a file containing object IDs to skip during fsck.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "receiveFsckObjects",
+    "configName": "receiveFsckObjects",
+    "displayName": "Receive Fsck Objects",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will verify objects during receive.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "receiveUnpackLimit",
+    "configName": "receiveUnpackLimit",
+    "displayName": "Receive Unpack Limit",
+    "type": "text",
+    "default": "1",
+    "description": "Maximum number of objects to unpack during receive.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "transferUnpackLimit",
+    "configName": "transferUnpackLimit",
+    "displayName": "Transfer Unpack Limit",
+    "type": "text",
+    "default": "100",
+    "description": "Maximum number of objects to unpack during transfer.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "unpackedUseKeepFile",
+    "configName": "unpackedUseKeepFile",
+    "displayName": "Unpacked Use Keep File",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will use .keep files for unpacked objects.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "packUseSparse",
+    "configName": "packUseSparse",
+    "displayName": "Pack Use Sparse",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will use sparse pack index.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "checkStat",
+    "configName": "checkStat",
+    "displayName": "Check Stat",
+    "type": "select",
+    "options": [
+      "default",
+      "minimal"
+    ],
+    "default": "default",
+    "description": "When to check stat information: 'default' always checks, 'minimal' checks less frequently.",
+    "version": "1.8.4"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "trustctime",
+    "displayName": "Trust Ctime",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will trust the ctime field of files.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "checkRoundtripEncoding",
+    "configName": "checkRoundtripEncoding",
+    "displayName": "Check Roundtrip Encoding",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will check that encoding roundtrips correctly.",
+    "version": null
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "eol",
+    "displayName": "EOL",
+    "type": "select",
+    "options": [
+      "lf",
+      "crlf",
+      "native"
+    ],
+    "default": "native",
+    "description": "Line ending style: 'lf' for LF, 'crlf' for CRLF, 'native' for platform default.",
+    "version": "1.7.2"
+  },
+  {
+    "section": "core",
+    "subsection": null,
+    "name": "excludesfile",
+    "displayName": "Global Excludes (gitignore)",
+    "type": "text",
+    "default": null,
+    "description": "Path to a file containing patterns to exclude from all repositories.",
+    "version": ""
+  },
+  {
+    "section": "init",
+    "subsection": null,
+    "name": "defaultBranch",
+    "configName": "default-branch",
+    "displayName": "Default Branch",
+    "type": "text",
+    "default": "main",
+    "description": "The default branch name for new repositories. Common values: main, master, develop.",
+    "version": ""
+  },
+  {
+    "section": "push",
+    "subsection": null,
+    "name": "default",
+    "displayName": "Default",
+    "type": "select",
+    "options": [
+      "simple",
+      "upstream",
+      "current",
+      "nothing"
+    ],
+    "default": "simple",
+    "description": "Defines the default push behavior. 'simple' pushes current branch to upstream, 'upstream' pushes to upstream, 'current' pushes current branch to same name, 'nothing' requires explicit specification.",
+    "version": "1.6.3"
+  },
+  {
+    "section": "push",
+    "subsection": null,
+    "name": "autoSetupRemote",
+    "displayName": "Auto Setup Remote",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will automatically set up the remote tracking branch when pushing a new branch.",
+    "version": null
+  },
+  {
+    "section": "pull",
+    "subsection": null,
+    "name": "rebase",
+    "displayName": "Rebase",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will rebase instead of merge when pulling. This creates a linear history.",
+    "version": "1.7.9"
+  },
+  {
+    "section": "pull",
+    "subsection": null,
+    "name": "ff",
+    "displayName": "Fast Forward",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "only"
+    ],
+    "default": null,
+    "description": "Controls how Git handles fast-forward merges. 'true' allows fast-forward, 'false' always creates merge commit, 'only' only allows fast-forward.",
+    "version": "2.0.0"
+  },
+  {
+    "section": "branch",
+    "subsection": null,
+    "name": "autoSetupMerge",
+    "configName": "autoSetupMerge",
+    "displayName": "Auto Setup Merge",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will automatically set up tracking information when creating a new branch.",
+    "version": "1.5.5"
+  },
+  {
+    "section": "branch",
+    "subsection": null,
+    "name": "autoSetupRebase",
+    "configName": "autoSetupRebase",
+    "displayName": "Auto Setup Rebase",
+    "type": "select",
+    "options": [
+      "always",
+      "local",
+      "remote",
+      "never"
+    ],
+    "default": "never",
+    "description": "If set, Git will automatically rebase branches when pulling. 'always' for all branches, 'local' for local branches, 'remote' for remote branches, 'never' to disable.",
+    "version": null
+  },
+  {
+    "section": "branch",
+    "subsection": null,
+    "name": "sort",
+    "displayName": "Sort",
+    "type": "text",
+    "default": null,
+    "description": "Sort key for branch listing. Common values: refname, committerdate, authordate.",
+    "version": "2.19.0"
+  },
+  {
+    "section": "remote",
+    "subsection": null,
+    "name": "default",
+    "displayName": "Default Remote",
+    "type": "text",
+    "default": null,
+    "description": "Default remote name to use when none is specified.",
+    "version": null
+  },
+  {
+    "section": "remote",
+    "subsection": "*",
+    "name": "prune",
+    "displayName": "Prune Remote",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": null,
+    "description": "If true, Git will automatically remove remote-tracking references that no longer exist on the remote.",
+    "version": "1.8.5"
+  },
+  {
+    "section": "fetch",
+    "subsection": null,
+    "name": "prune",
+    "displayName": "Prune",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will automatically remove remote-tracking references that no longer exist on the remote.",
+    "version": "1.8.5"
+  },
+  {
+    "section": "fetch",
+    "subsection": null,
+    "name": "pruneTags",
+    "configName": "pruneTags",
+    "displayName": "Prune Tags",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will automatically remove tags that no longer exist on the remote.",
+    "version": null
+  },
+  {
+    "section": "fetch",
+    "subsection": null,
+    "name": "output",
+    "displayName": "Output",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will show output from fetch operations.",
+    "version": null
+  },
+  {
+    "section": "fetch",
+    "subsection": null,
+    "name": "parallel",
+    "displayName": "Parallel",
+    "type": "text",
+    "default": null,
+    "description": "Number of parallel subprocesses to use when fetching from multiple remotes.",
+    "version": null
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "tool",
+    "displayName": "Tool",
+    "type": "text",
+    "default": null,
+    "description": "Tool to use for merge conflicts. Common values: vimdiff, meld, kdiff3, tortoisemerge.",
+    "version": null
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "conflictStyle",
+    "configName": "conflictStyle",
+    "displayName": "Conflict Style",
+    "type": "select",
+    "options": [
+      "merge",
+      "diff3",
+      "zdiff3"
+    ],
+    "default": "merge",
+    "description": "Style of conflict markers. 'merge' shows standard markers, 'diff3' includes common ancestor, 'zdiff3' is like diff3 but removes matching lines.",
+    "version": ""
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "ff",
+    "displayName": "Fast Forward",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "only"
+    ],
+    "default": "true",
+    "description": "Controls how Git handles fast-forward merges. 'true' allows fast-forward, 'false' always creates merge commit, 'only' only allows fast-forward.",
+    "version": ""
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "commit",
+    "displayName": "Commit",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will automatically commit after a successful merge.",
+    "version": null
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "squash",
+    "displayName": "Squash",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will squash all commits into a single commit when merging.",
+    "version": null
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "stat",
+    "displayName": "Stat",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will show a diffstat after merging.",
+    "version": null
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "verbosity",
+    "displayName": "Verbosity",
+    "type": "select",
+    "options": [
+      "0",
+      "1",
+      "2",
+      "5"
+    ],
+    "default": "2",
+    "description": "Level of verbosity for merge output. 0 is quiet, 5 is very verbose.",
+    "version": ""
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "branchdesc",
+    "displayName": "Branch Desc",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will include branch descriptions in merge commit messages.",
+    "version": null
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "log",
+    "displayName": "Log",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will include shortlog of commits being merged in the merge commit message.",
+    "version": ""
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "suppressDest",
+    "configName": "suppressDest",
+    "displayName": "Suppress Dest",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will suppress the 'into <branch>' message in merge commit messages.",
+    "version": "2.29.0"
+  },
+  {
+    "section": "merge",
+    "subsection": null,
+    "name": "verifySignatures",
+    "configName": "verifySignatures",
+    "displayName": "Verify Signatures",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will verify GPG signatures of commits being merged.",
+    "version": "2.16.0"
+  },
+  {
+    "section": "rebase",
+    "subsection": null,
+    "name": "autoStash",
+    "configName": "autoStash",
+    "displayName": "Auto Stash",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will automatically stash uncommitted changes before rebasing and reapply them after.",
+    "version": "2.6.0"
+  },
+  {
+    "section": "rebase",
+    "subsection": null,
+    "name": "autoSquash",
+    "configName": "autoSquash",
+    "displayName": "Auto Squash",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will automatically squash fixup and squash commits during interactive rebase.",
+    "version": "1.7.3"
+  },
+  {
+    "section": "rebase",
+    "subsection": null,
+    "name": "updateRefs",
+    "configName": "updateRefs",
+    "displayName": "Update Refs",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will update branches that point to commits being rebased.",
+    "version": null
+  },
+  {
+    "section": "rebase",
+    "subsection": null,
+    "name": "missing",
+    "displayName": "Missing",
+    "type": "select",
+    "options": [
+      "error",
+      "warn",
+      "ignore"
+    ],
+    "default": "error",
+    "description": "How to handle missing commits during rebase. 'error' aborts, 'warn' warns but continues, 'ignore' silently continues.",
+    "version": ""
+  },
+  {
+    "section": "rebase",
+    "subsection": null,
+    "name": "rebaseMerges",
+    "configName": "rebaseMerges",
+    "displayName": "Rebase Merges",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "rebase-cousins"
+    ],
+    "default": "false",
+    "description": "If true, Git will preserve merge commits during rebase. 'rebase-cousins' also rebases commits with the same merge base.",
+    "version": null
+  },
+  {
+    "section": "rebase",
+    "subsection": null,
+    "name": "forkPoint",
+    "configName": "forkPoint",
+    "displayName": "Fork Point",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will use the fork point algorithm to determine the base for rebase.",
+    "version": "2.31.0"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "tool",
+    "displayName": "Tool",
+    "type": "text",
+    "default": null,
+    "description": "Tool to use for showing diffs. Common values: vimdiff, meld, kdiff3, difftool.",
+    "version": null
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "algorithm",
+    "displayName": "Algorithm",
+    "type": "select",
+    "options": [
+      "myers",
+      "minimal",
+      "patience",
+      "histogram"
+    ],
+    "default": "myers",
+    "description": "Diff algorithm to use. 'myers' is default, 'minimal' is slower but more accurate, 'patience' and 'histogram' are alternative algorithms.",
+    "version": "1.8.2"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "indentHeuristic",
+    "configName": "indentHeuristic",
+    "displayName": "Indent Heuristic",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will use an indent heuristic to improve diff output.",
+    "version": "2.11.0"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "compactionHeuristic",
+    "configName": "compactionHeuristic",
+    "displayName": "Compaction Heuristic",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will use a compaction heuristic to reduce diff size.",
+    "version": "2.9.1"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "colorMoved",
+    "configName": "colorMoved",
+    "displayName": "Color Moved",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "default",
+      "plain",
+      "blocks",
+      "zebra",
+      "dimmed-zebra"
+    ],
+    "default": "false",
+    "description": "If true, Git will color moved lines in diffs. Various modes available for different highlighting styles.",
+    "version": null
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "colorMovedWS",
+    "configName": "colorMovedWS",
+    "displayName": "Color Moved Whitespace",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "ignore-all",
+      "ignore-space-change",
+      "ignore-space-at-eol",
+      "ignore-at-eol"
+    ],
+    "default": "false",
+    "description": "How to handle whitespace when detecting moved lines.",
+    "version": null
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "renames",
+    "displayName": "Renames",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "copies",
+      "copy"
+    ],
+    "default": "true",
+    "description": "If true, Git will detect renames. 'copies' or 'copy' also detects copies.",
+    "version": ""
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "renameLimit",
+    "configName": "renameLimit",
+    "displayName": "Rename Limit",
+    "type": "text",
+    "default": "400",
+    "description": "Maximum number of files to consider when detecting renames.",
+    "version": ""
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "statGraphWidth",
+    "configName": "statGraphWidth",
+    "displayName": "Stat Graph Width",
+    "type": "text",
+    "default": null,
+    "description": "Width of the graph part in diffstat output.",
+    "version": null
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "statWidth",
+    "configName": "statWidth",
+    "displayName": "Stat Width",
+    "type": "text",
+    "default": null,
+    "description": "Width of the filename part in diffstat output.",
+    "version": null
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "statNameWidth",
+    "configName": "statNameWidth",
+    "displayName": "Stat Name Width",
+    "type": "text",
+    "default": null,
+    "description": "Width of the filename part in diffstat output.",
+    "version": "2.43.0"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "statCount",
+    "configName": "statCount",
+    "displayName": "Stat Count",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will show number of lines added and deleted in diffstat.",
+    "version": null
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "context",
+    "displayName": "Context",
+    "type": "text",
+    "default": "3",
+    "description": "Number of context lines to show in diffs.",
+    "version": "1.8.1"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "interHunkContext",
+    "configName": "interHunkContext",
+    "displayName": "Inter Hunk Context",
+    "type": "text",
+    "default": "0",
+    "description": "Number of context lines between hunks in diffs.",
+    "version": "2.12.0"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "functionContext",
+    "configName": "functionContext",
+    "displayName": "Function Context",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will show function context in diffs.",
+    "version": null
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "wordRegex",
+    "configName": "wordRegex",
+    "displayName": "Word Regex",
+    "type": "text",
+    "default": null,
+    "description": "Regular expression used to determine what constitutes a word for word-diff.",
+    "version": ""
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "external",
+    "displayName": "External",
+    "type": "text",
+    "default": null,
+    "description": "Command to use for external diff.",
+    "version": "1.5.4"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "ignoreSubmodules",
+    "configName": "ignoreSubmodules",
+    "displayName": "Ignore Submodules",
+    "type": "select",
+    "options": [
+      "all",
+      "dirty",
+      "untracked",
+      "none"
+    ],
+    "default": "none",
+    "description": "How to handle submodules in diffs. 'all' ignores all changes, 'dirty' ignores only dirty submodules, 'untracked' ignores untracked files, 'none' shows all changes.",
+    "version": "1.7.3"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "submodule",
+    "displayName": "Submodule",
+    "type": "select",
+    "options": [
+      "short",
+      "log",
+      "diff"
+    ],
+    "default": "short",
+    "description": "Format for submodule diffs. 'short' shows summary, 'log' shows commit log, 'diff' shows full diff.",
+    "version": "1.8.1"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "orderFile",
+    "configName": "orderFile",
+    "displayName": "Order File",
+    "type": "text",
+    "default": null,
+    "description": "File containing patterns to control the order of files in diffs.",
+    "version": "1.9.0"
+  },
+  {
+    "section": "diff",
+    "subsection": null,
+    "name": "wsErrorHighlight",
+    "configName": "wsErrorHighlight",
+    "displayName": "Whitespace Error Highlight",
+    "type": "text",
+    "default": null,
+    "description": "Highlight whitespace errors. Comma-separated list: all, default, new, old, context, none.",
+    "version": "2.11.0"
+  },
+  {
+    "section": "log",
+    "subsection": null,
+    "name": "date",
+    "displayName": "Date",
+    "type": "select",
+    "options": [
+      "relative",
+      "local",
+      "default",
+      "iso",
+      "iso-strict",
+      "rfc",
+      "short",
+      "raw",
+      "unix",
+      "format:"
+    ],
+    "default": "default",
+    "description": "Date format for log output. Various formats available including relative, iso, rfc, etc.",
+    "version": "1.5.6"
+  },
+  {
+    "section": "log",
+    "subsection": null,
+    "name": "decorate",
+    "displayName": "Decorate",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "short",
+      "full",
+      "auto"
+    ],
+    "default": "auto",
+    "description": "If true, Git will show ref names next to commits. 'short' shows short ref names, 'full' shows full ref names, 'auto' shows if output goes to terminal.",
+    "version": ""
+  },
+  {
+    "section": "log",
+    "subsection": null,
+    "name": "mailmap",
+    "displayName": "Mailmap",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will use .mailmap file to map author names and emails.",
+    "version": null
+  },
+  {
+    "section": "log",
+    "subsection": null,
+    "name": "showSignature",
+    "configName": "showSignature",
+    "displayName": "Show Signature",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will show GPG signature information in log output.",
+    "version": "2.10.0"
+  },
+  {
+    "section": "log",
+    "subsection": null,
+    "name": "abbrevCommit",
+    "configName": "abbrevCommit",
+    "displayName": "Abbrev Commit",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will abbreviate commit hashes in log output.",
+    "version": "1.7.6"
+  },
+  {
+    "section": "log",
+    "subsection": null,
+    "name": "follow",
+    "displayName": "Follow",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will follow renames in log output.",
+    "version": ""
+  },
+  {
+    "section": "log",
+    "subsection": null,
+    "name": "all",
+    "displayName": "All",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will show all branches in log output.",
+    "version": null
+  },
+  {
+    "section": "log",
+    "subsection": null,
+    "name": "showRoot",
+    "configName": "showRoot",
+    "displayName": "Show Root",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will show the root commit in log output.",
+    "version": ""
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "showStash",
+    "configName": "showStash",
+    "displayName": "Show Stash",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will show number of stashed changes in status output.",
+    "version": null
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "submoduleSummary",
+    "configName": "submoduleSummary",
+    "displayName": "Submodule Summary",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will show submodule summary in status output.",
+    "version": null
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "branch",
+    "displayName": "Branch",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will show branch information in status output.",
+    "version": "1.8.4"
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "aheadBehind",
+    "configName": "aheadBehind",
+    "displayName": "Ahead Behind",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will show ahead/behind information in status output.",
+    "version": "2.23.0"
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "renames",
+    "displayName": "Renames",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "copies"
+    ],
+    "default": "true",
+    "description": "If true, Git will detect renames in status. 'copies' also detects copies.",
+    "version": "2.18.0"
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "renameLimit",
+    "configName": "renameLimit",
+    "displayName": "Rename Limit",
+    "type": "text",
+    "default": null,
+    "description": "Maximum number of files to consider when detecting renames in status.",
+    "version": null
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "short",
+    "displayName": "Short",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will show short format status output.",
+    "version": "1.8.4"
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "porcelain",
+    "displayName": "Porcelain",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "v1",
+      "v2"
+    ],
+    "default": "false",
+    "description": "If true, Git will show machine-readable status output. 'v1' and 'v2' specify version.",
+    "version": null
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "showUntrackedFiles",
+    "configName": "showUntrackedFiles",
+    "displayName": "Show Untracked Files",
+    "type": "select",
+    "options": [
+      "true",
+      "false",
+      "no",
+      "normal",
+      "all"
+    ],
+    "default": "normal",
+    "description": "How to show untracked files. 'no' or 'false' hides them, 'normal' shows them, 'all' shows individual files in directories.",
+    "version": ""
+  },
+  {
+    "section": "status",
+    "subsection": null,
+    "name": "ignoreSubmodules",
+    "configName": "ignoreSubmodules",
+    "displayName": "Ignore Submodules",
+    "type": "select",
+    "options": [
+      "all",
+      "dirty",
+      "untracked",
+      "none"
+    ],
+    "default": "none",
+    "description": "How to handle submodules in status. 'all' ignores all changes, 'dirty' ignores only dirty submodules, 'untracked' ignores untracked files, 'none' shows all changes.",
+    "version": null
+  },
+  {
+    "section": "tag",
+    "subsection": null,
+    "name": "sort",
+    "displayName": "Sort",
+    "type": "text",
+    "default": null,
+    "description": "Sort key for tag listing. Common values: refname, version:refname, creatordate.",
+    "version": "2.1.0"
+  },
+  {
+    "section": "tag",
+    "subsection": null,
+    "name": "forceSignAnnotated",
+    "configName": "forceSignAnnotated",
+    "displayName": "Force Sign Annotated",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will require GPG signing for annotated tags.",
+    "version": "2.9.0"
+  },
+  {
+    "section": "tag",
+    "subsection": null,
+    "name": "gpgSign",
+    "configName": "gpgSign",
+    "displayName": "GPG Sign",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will automatically GPG sign tags.",
+    "version": "2.23.0"
+  },
+  {
+    "section": "tag",
+    "subsection": null,
+    "name": "abbrev",
+    "displayName": "Abbrev",
+    "type": "text",
+    "default": null,
+    "description": "Set the length tag names are abbreviated to.",
+    "version": null
+  },
+  {
+    "section": "show",
+    "subsection": null,
+    "name": "default",
+    "displayName": "Default",
+    "type": "text",
+    "default": null,
+    "description": "Default format for git show output.",
+    "version": null
+  },
+  {
+    "section": "show",
+    "subsection": null,
+    "name": "notes",
+    "displayName": "Notes",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will show notes in show output.",
+    "version": null
+  },
+  {
+    "section": "show",
+    "subsection": null,
+    "name": "showNotes",
+    "configName": "showNotes",
+    "displayName": "Show Notes",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "true",
+    "description": "If true, Git will show notes in show output.",
+    "version": null
+  },
+  {
+    "section": "show",
+    "subsection": null,
+    "name": "showSignature",
+    "configName": "showSignature",
+    "displayName": "Show Signature",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will show GPG signature information in show output.",
+    "version": null
+  },
+  {
+    "section": "credential",
+    "subsection": null,
+    "name": "helper",
+    "displayName": "Helper",
+    "type": "text",
+    "default": null,
+    "description": "External helper to use for credential storage. Can be specified multiple times.",
+    "version": ""
+  },
+  {
+    "section": "credential",
+    "subsection": null,
+    "name": "useHttpPath",
+    "configName": "useHttpPath",
+    "displayName": "Use HTTP Path",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, Git will use the full URL path when looking up credentials.",
+    "version": null
+  },
+  {
+    "section": "credential",
+    "subsection": null,
+    "name": "username",
+    "displayName": "Username",
+    "type": "text",
+    "default": null,
+    "description": "Default username to use for authentication.",
+    "version": null
+  },
+  {
+    "section": "worktree",
+    "subsection": null,
+    "name": "guessRemote",
+    "configName": "guessRemote",
+    "displayName": "Guess Remote",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "If true, git worktree add will try to find a remote-tracking branch whose name uniquely matches the new branch name.",
+    "version": null
+  },
+  {
+    "section": "worktree",
+    "subsection": null,
+    "name": "useRelativePaths",
+    "configName": "useRelativePaths",
+    "displayName": "Use Relative Paths",
+    "type": "select",
+    "options": [
+      "true",
+      "false"
+    ],
+    "default": "false",
+    "description": "Link worktrees using relative paths (when 'true') or absolute paths (when 'false').",
+    "version": null
+  },
+  {
+    "section": "web",
+    "subsection": null,
+    "name": "browser",
+    "displayName": "Browser",
+    "type": "text",
+    "default": null,
+    "description": "Web browser that may be used by some commands. Currently only git-instaweb and git-help may use it.",
+    "version": null
+  },
+  {
+    "section": "versionsort",
+    "subsection": null,
+    "name": "suffix",
+    "displayName": "Suffix",
+    "type": "text",
+    "default": null,
+    "description": "Even when version sort is used in git-tag, tagnames with the same base version but different suffixes are still sorted lexicographically. This variable can be specified to determine the sorting order of tags with different suffixes.",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "diff",
+    "name": "meta",
+    "displayName": "Meta",
+    "type": "color",
+    "default": null,
+    "description": "Color for diff meta information (file headers, hunk headers, etc.)",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "diff",
+    "name": "frag",
+    "displayName": "Frag",
+    "type": "color",
+    "default": null,
+    "description": "Color for hunk header lines in diffs",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "diff",
+    "name": "old",
+    "displayName": "Old",
+    "type": "color",
+    "default": null,
+    "description": "Color for removed lines in diffs",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "diff",
+    "name": "new",
+    "displayName": "New",
+    "type": "color",
+    "default": null,
+    "description": "Color for added lines in diffs",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "diff",
+    "name": "commit",
+    "displayName": "Commit",
+    "type": "color",
+    "default": null,
+    "description": "Color for commit headers in diffs",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "diff",
+    "name": "whitespace",
+    "displayName": "Whitespace",
+    "type": "color",
+    "default": null,
+    "description": "Color for whitespace errors in diffs",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "diff",
+    "name": "context",
+    "displayName": "Context",
+    "type": "color",
+    "default": null,
+    "description": "Color for context lines in diffs (previously known as 'plain')",
+    "version": "2.4.5"
+  },
+  {
+    "section": "color",
+    "subsection": "status",
+    "name": "header",
+    "displayName": "Header",
+    "type": "color",
+    "default": null,
+    "description": "Color for status header lines",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "status",
+    "name": "added",
+    "displayName": "Added",
+    "type": "color",
+    "default": null,
+    "description": "Color for added files in status",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "status",
+    "name": "changed",
+    "displayName": "Changed",
+    "type": "color",
+    "default": null,
+    "description": "Color for changed files in status",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "status",
+    "name": "untracked",
+    "displayName": "Untracked",
+    "type": "color",
+    "default": null,
+    "description": "Color for untracked files in status",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "status",
+    "name": "branch",
+    "displayName": "Branch",
+    "type": "color",
+    "default": null,
+    "description": "Color for branch name in status",
+    "version": "1.7.4"
+  },
+  {
+    "section": "color",
+    "subsection": "status",
+    "name": "nobranch",
+    "displayName": "No Branch",
+    "type": "color",
+    "default": null,
+    "description": "Color for 'no branch' indicator in status",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "status",
+    "name": "unmerged",
+    "displayName": "Unmerged",
+    "type": "color",
+    "default": null,
+    "description": "Color for unmerged files in status",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "branch",
+    "name": "current",
+    "displayName": "Current",
+    "type": "color",
+    "default": null,
+    "description": "Color for current branch",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "branch",
+    "name": "local",
+    "displayName": "Local",
+    "type": "color",
+    "default": null,
+    "description": "Color for local branches",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "branch",
+    "name": "remote",
+    "displayName": "Remote",
+    "type": "color",
+    "default": null,
+    "description": "Color for remote branches",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "branch",
+    "name": "plain",
+    "displayName": "Plain",
+    "type": "color",
+    "default": null,
+    "description": "Color for plain branch names",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "interactive",
+    "name": "header",
+    "displayName": "Header",
+    "type": "color",
+    "default": null,
+    "description": "Color for interactive header",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "interactive",
+    "name": "help",
+    "displayName": "Help",
+    "type": "color",
+    "default": null,
+    "description": "Color for interactive help text",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "interactive",
+    "name": "prompt",
+    "displayName": "Prompt",
+    "type": "color",
+    "default": null,
+    "description": "Color for interactive prompts",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "interactive",
+    "name": "error",
+    "displayName": "Error",
+    "type": "color",
+    "default": null,
+    "description": "Color for interactive error messages",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "interactive",
+    "name": "reset",
+    "displayName": "Reset",
+    "type": "color",
+    "default": null,
+    "description": "Color for interactive reset",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "grep",
+    "name": "context",
+    "displayName": "Context",
+    "type": "color",
+    "default": null,
+    "description": "Color for grep context lines",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "grep",
+    "name": "filename",
+    "displayName": "Filename",
+    "type": "color",
+    "default": null,
+    "description": "Color for grep filenames",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "grep",
+    "name": "function",
+    "displayName": "Function",
+    "type": "color",
+    "default": null,
+    "description": "Color for grep function names",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "grep",
+    "name": "lineNumber",
+    "configName": "lineNumber",
+    "displayName": "Line Number",
+    "type": "color",
+    "default": null,
+    "description": "Color for grep line numbers",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "grep",
+    "name": "column",
+    "displayName": "Column",
+    "type": "color",
+    "default": null,
+    "description": "Color for grep column numbers",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "grep",
+    "name": "match",
+    "displayName": "Match",
+    "type": "color",
+    "default": null,
+    "description": "Color for grep match text",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "grep",
+    "name": "selected",
+    "displayName": "Selected",
+    "type": "color",
+    "default": null,
+    "description": "Color for grep selected text",
+    "version": null
+  },
+  {
+    "section": "color",
+    "subsection": "grep",
+    "name": "separator",
+    "displayName": "Separator",
+    "type": "color",
+    "default": null,
+    "description": "Color for grep separators",
+    "version": null
+  }
+]
\ No newline at end of file
diff --git a/pygitweb/main.py b/pygitweb/main.py
index f4ac2cf..605babf 100644
--- a/pygitweb/main.py
+++ b/pygitweb/main.py
@@ -61,6 +61,11 @@ from pygitweb.git_helpers import (
 )
 from pygitweb.projects import git_get_project_owner, git_get_projects_list
 from pygitweb.settings import router as settings_router
+from pygitweb.settings_project import (
+	init_git_config_options_registry,
+	project_git_config_page,
+	project_git_config_set_post,
+)
 from pygitweb.tasks import (
 	board_router,
 	comment_router,
@@ -153,6 +158,7 @@ app.include_router(comment_router, prefix="/comments")
 
 @app.on_event("startup")
 def startup():
+	init_git_config_options_registry()
 	evaluate_gitweb_config()
 	configure_gitweb_features(
 		get_project_config=git_get_project_config,
@@ -507,6 +513,8 @@ def dispatch(
 	# Route to handler
 	if action == "summary":
 		return git_summary(project)
+	if action == "config":
+		return project_git_config_page(project)
 	if action == "tree":
 		return git_tree(project, hash_param, file_name)
 	if action in ("blob", "blob_plain"):
@@ -548,6 +556,18 @@ def dispatch(
 	return HTMLResponse(f"{pre}<p>Action: {esc_html(action)}</p><p>Project: {esc_html(project)}</p>{POSTAMBLE}")
 
 
+@app.post("/project/{project:path}")
+def project_post_dispatch(
+	project: str,
+	a: Annotated[str | None, Query(alias="a")] = None,
+	key: Annotated[str | None, Query(alias="key")] = None,
+	value: Annotated[str | None, Query(alias="value")] = None,
+):
+	if a != "set_config":
+		raise HTTPException(status_code=400, detail="Unknown POST action")
+	return project_git_config_set_post(project, key or "", value)
+
+
 if __name__ == "__main__":
 	import uvicorn
 
diff --git a/pygitweb/pyproject.toml b/pygitweb/pyproject.toml
index 5c9a416..eb2d8d8 100644
--- a/pygitweb/pyproject.toml
+++ b/pygitweb/pyproject.toml
@@ -25,7 +25,7 @@ packages = ["pygitweb"]
 pygitweb = "."
 
 [tool.setuptools.package-data]
-pygitweb = ["templates/**/*", "static/**/*"]
+pygitweb = ["templates/**/*", "static/**/*", "data/**/*"]
 
 [tool.uv.sources]
 distgit = { workspace = true }
diff --git a/pygitweb/settings_project.py b/pygitweb/settings_project.py
new file mode 100644
index 0000000..dfde617
--- /dev/null
+++ b/pygitweb/settings_project.py
@@ -0,0 +1,234 @@
+"""
+Per-project git config UI backed by config-options.json (all sections except color and alias).
+"""
+
+from __future__ import annotations
+
+import json
+from contextlib import suppress
+from pathlib import Path
+from typing import Any, Final
+from urllib.parse import quote
+
+import pygit2
+from fastapi import HTTPException
+from fastapi.responses import HTMLResponse, JSONResponse
+
+from pygitweb import config
+from pygitweb.config import EXPORT_OK, PROJECTROOT, STRICT_EXPORT
+from pygitweb.formatting import esc_html
+from pygitweb.git_helpers import open_repo
+from pygitweb.projects import git_get_projects_list
+from pygitweb.settings import GIT_CONFIG_LEVEL_LOCAL, GIT_CONFIG_LEVEL_WORKTREE
+from pygitweb.templates_env import POSTAMBLE, PREAMBLE, env
+from pygitweb.validation import is_valid_project
+
+_GIT_CONFIG_OPTIONS_PATH: Final[Path] = Path(__file__).resolve().parent / "data" / "config-options.json"
+
+_EXCLUDED_CONFIG_SECTIONS: Final[frozenset[str]] = frozenset({"color", "alias"})
+
+_ORDERED_GIT_CONFIG_OPTIONS: list[dict[str, Any]] = []
+_OPTION_BY_KEY: dict[str, dict[str, Any]] = {}
+
+
+def init_git_config_options_registry() -> None:
+	"""Load config-options.json and index allowed options (startup). Skips [color], [alias], and subsection '*'."""
+	global _ORDERED_GIT_CONFIG_OPTIONS, _OPTION_BY_KEY
+	raw = json.loads(_GIT_CONFIG_OPTIONS_PATH.read_text(encoding="utf-8"))
+	if not isinstance(raw, list):
+		raise RuntimeError("config-options.json must be a JSON array")
+	by_key: dict[str, dict[str, Any]] = {}
+	ordered: list[dict[str, Any]] = []
+	for item in raw:
+		if not isinstance(item, dict):
+			continue
+		sec = item.get("section")
+		if not isinstance(sec, str) or sec in _EXCLUDED_CONFIG_SECTIONS:
+			continue
+		if item.get("subsection") == "*":
+			continue
+		try:
+			k = _git_config_key(item)
+		except (KeyError, TypeError, ValueError):
+			continue
+		if k in by_key:
+			continue
+		by_key[k] = item
+		ordered.append(item)
+	_ORDERED_GIT_CONFIG_OPTIONS = ordered
+	_OPTION_BY_KEY = by_key
+
+
+def _git_config_key(item: dict[str, Any]) -> str:
+	section = str(item["section"])
+	subsection = item.get("subsection")
+	leaf = item.get("configName") or item.get("name")
+	if not isinstance(leaf, str) or not leaf:
+		raise ValueError("invalid config option entry: missing name")
+	if subsection and subsection != "*":
+		return f"{section}.{subsection}.{leaf}"
+	return f"{section}.{leaf}"
+
+
+def _project_in_list(project: str) -> bool:
+	lst = git_get_projects_list(
+		filter_path="",
+		paranoid=STRICT_EXPORT,
+		export_ok=EXPORT_OK,
+	)
+	return any(p.get("path") == project for p in lst)
+
+
+def _validate_project_or_404(project: str | None) -> str:
+	if not project:
+		raise HTTPException(status_code=400, detail="Project needed")
+	if not is_valid_project(
+		project,
+		PROJECTROOT,
+		EXPORT_OK,
+		STRICT_EXPORT,
+		_project_in_list,
+	):
+		raise HTTPException(status_code=404, detail="No such project")
+	return project
+
+
+def _is_option_type(t: str) -> bool:
+	return t == "select"
+
+
+def _is_textual_type(t: str) -> bool:
+	return t in ("text", "email") or not _is_option_type(t)
+
+
+def _repo_config_local_worktree_values(repo: pygit2.Repository) -> dict[str, str]:
+	"""
+	Config values from LOCAL (5) and WORKTREE (6) only; last value wins per name.
+	Same iteration rules as settings._get_project_config_entries.
+	"""
+	cfg = repo.config
+	by_name: dict[str, str] = {}
+	for entry in cfg:
+		if entry.level not in (GIT_CONFIG_LEVEL_LOCAL, GIT_CONFIG_LEVEL_WORKTREE):
+			continue
+		by_name[entry.name] = entry.value
+	return by_name
+
+
+def _field_dict(item: dict[str, Any], local_worktree: dict[str, str]) -> dict[str, Any]:
+	key = _git_config_key(item)
+	current = local_worktree.get(key, "")
+	opt_type = str(item.get("type") or "text")
+	return {
+		"git_key": key,
+		"display_name": str(item.get("displayName") or item.get("name") or key),
+		"description": str(item.get("description") or ""),
+		"control_kind": "select" if _is_option_type(opt_type) else "text",
+		"current": current,
+		"options": list(item["options"]) if isinstance(item.get("options"), list) else [],
+	}
+
+
+def _group_options_by_section_and_subsection(
+	items: list[dict[str, Any]],
+) -> list[dict[str, Any]]:
+	"""Same grouping as gitconfig-generator: section → subsection → options (order preserved)."""
+	section_order: list[str] = []
+	section_subsection_order: dict[str, list[str]] = {}
+	buckets: dict[str, dict[str, list[dict[str, Any]]]] = {}
+	for item in items:
+		sec = str(item.get("section") or "")
+		raw_sub = item.get("subsection")
+		sub = "" if raw_sub in (None, "") else str(raw_sub)
+		if sub == "*":
+			continue
+		if sec not in buckets:
+			buckets[sec] = {}
+			section_order.append(sec)
+			section_subsection_order[sec] = []
+		if sub not in buckets[sec]:
+			buckets[sec][sub] = []
+			section_subsection_order[sec].append(sub)
+		buckets[sec][sub].append(item)
+	out: list[dict[str, Any]] = []
+	for sec in section_order:
+		subs: list[dict[str, Any]] = []
+		for sub in section_subsection_order[sec]:
+			subs.append({"subsection": sub, "items": buckets[sec][sub]})
+		out.append({"section": sec, "subsections": subs})
+	return out
+
+
+def project_git_config_page(project: str) -> HTMLResponse:
+	"""HTML page listing git config options;
+	Values shown are LOCAL + WORKTREE only (see settings._get_project_config_entries)."""
+	project = _validate_project_or_404(project)
+	if not _OPTION_BY_KEY:
+		raise HTTPException(status_code=500, detail="Git config registry not loaded")
+	repo = open_repo(project)
+	local_worktree = _repo_config_local_worktree_values(repo)
+	grouped = _group_options_by_section_and_subsection(_ORDERED_GIT_CONFIG_OPTIONS)
+	sections: list[dict[str, Any]] = []
+	for sec in grouped:
+		subsections_out: list[dict[str, Any]] = []
+		for sub in sec["subsections"]:
+			fields = [_field_dict(item, local_worktree) for item in sub["items"]]
+			subsections_out.append({"subsection": sub["subsection"], "fields": fields})
+		sections.append({"section": sec["section"], "subsections": subsections_out})
+	pre = PREAMBLE.render(
+		title=f"{esc_html(config.SITE_NAME)} - Git config — {esc_html(project)}",
+		site_name=config.SITE_NAME,
+	)
+	project_qp = quote(project, safe="/")
+	body = env.get_template("project_git_config.html").render(
+		project=project,
+		project_esc=esc_html(project),
+		summary_url=f"/project/{project_qp}",
+		set_config_url=f"/project/{project_qp}?a=set_config",
+		sections=sections,
+	)
+	return HTMLResponse(f"{pre}{body}{POSTAMBLE}")
+
+
+def _normalize_set_value(opt: dict[str, Any], raw: str) -> str:
+	opt_type = str(opt.get("type") or "text")
+	stripped = raw.strip()
+	if not stripped:
+		return ""
+	if _is_option_type(opt_type):
+		options = opt.get("options")
+		if not isinstance(options, list) or stripped not in [str(x) for x in options]:
+			raise HTTPException(
+				status_code=400,
+				detail="value must be one of the allowed options for this key",
+			)
+		return stripped
+	if not _is_textual_type(opt_type):
+		raise HTTPException(status_code=400, detail="unsupported config type for this key")
+	return raw
+
+
+def project_git_config_set_post(project: str, key: str, value: str | None = None) -> JSONResponse:
+	"""Set or unset a single local config key validated against config-options.json."""
+	project = _validate_project_or_404(project)
+	if not _OPTION_BY_KEY:
+		raise HTTPException(status_code=500, detail="Git config registry not loaded")
+	lookup = (key or "").strip()
+	if not lookup:
+		raise HTTPException(status_code=400, detail="key is required")
+	opt = _OPTION_BY_KEY.get(lookup)
+	if opt is None:
+		raise HTTPException(status_code=400, detail="unknown or unsupported config key")
+	raw_val = value if value is not None else ""
+	normalized = _normalize_set_value(opt, raw_val)
+	repo = open_repo(project)
+	cfg = repo.config
+	try:
+		if not normalized:
+			with suppress(KeyError):
+				del cfg[lookup]
+		else:
+			cfg[lookup] = normalized
+	except (pygit2.GitError, TypeError, ValueError) as e:
+		raise HTTPException(status_code=400, detail=f"could not update config: {e}") from e
+	return JSONResponse({"ok": True})
diff --git a/pygitweb/static/main.css b/pygitweb/static/main.css
index 502b3d3..c4d7c21 100644
--- a/pygitweb/static/main.css
+++ b/pygitweb/static/main.css
@@ -793,3 +793,160 @@ tbody tr {
 		resize: vertical;
 	}
 }
+
+/* Git config page: collapsible sections (gitconfig-generator style) */
+.gitconfig-sections {
+	display: flex;
+	flex-direction: column;
+	gap: 1rem;
+}
+
+.gitconfig-config-section {
+	margin: 0;
+	padding: 1.25rem;
+	background: var(--pgw-bg-muted);
+	border-radius: 0.5rem;
+	border: 1px solid var(--pgw-border);
+	border-left: 4px solid var(--pgw-link);
+}
+
+.gitconfig-section-header {
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	gap: 0.75rem;
+	margin-bottom: 0;
+	cursor: default;
+}
+
+.gitconfig-config-section.collapsed .gitconfig-section-header {
+	margin-bottom: 0;
+}
+
+.gitconfig-section-title {
+	color: var(--pgw-text);
+	font-size: 1.35rem;
+	font-weight: 600;
+	flex: 1;
+}
+
+.gitconfig-section-toggle {
+	background: transparent;
+	border: 2px solid var(--pgw-border);
+	color: var(--pgw-text);
+	width: 2rem;
+	height: 2rem;
+	border-radius: 0.375rem;
+	cursor: pointer;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	font-size: 1.25rem;
+	font-weight: bold;
+	line-height: 1;
+	padding: 0;
+	flex-shrink: 0;
+	transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
+}
+
+.gitconfig-section-toggle:hover {
+	background: var(--pgw-accent-bg);
+	border-color: var(--pgw-link);
+	color: var(--pgw-link);
+}
+
+.gitconfig-toggle-icon {
+	display: inline-block;
+	user-select: none;
+}
+
+.gitconfig-config-section.collapsed .gitconfig-section-content {
+	display: none;
+}
+
+.gitconfig-section-content {
+	padding-top: 1rem;
+}
+
+.gitconfig-subsection-header {
+	color: var(--pgw-text);
+	margin: 1.25rem 0 0.75rem 0;
+	font-size: 1.1rem;
+	font-weight: 600;
+	padding-bottom: 0.5rem;
+	border-bottom: 2px solid var(--pgw-border-muted);
+}
+
+.gitconfig-subsection-header:first-child {
+	margin-top: 0;
+}
+
+/* Git config rows: label | control | description (gitconfig-generator style) */
+.gitconfig-form-group {
+	display: flex;
+	flex-wrap: wrap;
+	align-items: center;
+	column-gap: 1rem;
+	row-gap: 0.5rem;
+	margin-bottom: 0.75rem;
+	padding-bottom: 0.75rem;
+	border-bottom: 1px solid var(--pgw-border-muted);
+}
+
+.gitconfig-form-group .gitconfig-form-label[title] {
+	cursor: help;
+}
+
+.gitconfig-form-label {
+	flex: 0 0 150px;
+	max-width: min(150px, 35vw);
+	margin-bottom: 0;
+	font-weight: 600;
+	line-height: 1.3;
+}
+
+.gitconfig-form-control {
+	flex: 1 1 auto;
+	min-width: 0;
+	max-width: 25%;
+}
+
+.gitconfig-form-group:not(:has(.gitconfig-form-description)) .gitconfig-form-control {
+	max-width: none;
+}
+
+.gitconfig-form-control .form-select,
+.gitconfig-form-control .form-control {
+	width: 100%;
+	min-width: 0;
+}
+
+.gitconfig-form-description {
+	flex: 1 1 auto;
+	min-width: 0;
+	max-width: 40%;
+	font-size: 0.85rem;
+	font-style: italic;
+	line-height: 1.4;
+	color: var(--pgw-text-muted);
+}
+
+@media (max-width: 991.98px) {
+	.gitconfig-form-group {
+		flex-direction: column;
+		align-items: stretch;
+	}
+
+	.gitconfig-form-label {
+		flex: 0 0 auto;
+		max-width: 100%;
+	}
+
+	.gitconfig-form-control {
+		max-width: 100%;
+	}
+
+	.gitconfig-form-description {
+		max-width: 100%;
+	}
+}
diff --git a/pygitweb/static/project_config.js b/pygitweb/static/project_config.js
new file mode 100644
index 0000000..f3f2faf
--- /dev/null
+++ b/pygitweb/static/project_config.js
@@ -0,0 +1,63 @@
+(function () {
+	const root = document.getElementById("project-git-config");
+	if (!root) return;
+	const base = root.dataset.setUrl;
+	if (!base) return;
+
+	function showErr(msg) {
+		try {
+			alert(msg);
+		} catch (e) {
+			/* ignore */
+		}
+	}
+
+	function onChange(ev) {
+		const el = ev.target;
+		const key = el.dataset.configKey;
+		if (!key) return;
+		const value = el.value;
+		const url =
+			base + "&key=" + encodeURIComponent(key) + "&value=" + encodeURIComponent(value);
+		fetch(url, {
+			method: "POST",
+			credentials: "same-origin",
+			headers: { Accept: "application/json" },
+		})
+			.then(async (r) => {
+				if (!r.ok) {
+					let detail = "Save failed";
+					try {
+						const j = await r.json();
+						if (typeof j.detail === "string") detail = j.detail;
+						else if (j.detail !== undefined) detail = JSON.stringify(j.detail);
+					} catch (e) {
+						try {
+							detail = await r.text();
+						} catch (e2) {
+							/* ignore */
+						}
+					}
+					throw new Error(detail);
+				}
+				return r.json();
+			})
+			.catch((err) => showErr(err.message || String(err)));
+	}
+
+	root.querySelectorAll("[data-config-key]").forEach((el) => {
+		el.addEventListener("change", onChange);
+	});
+
+	root.querySelectorAll("[data-gitconfig-section]").forEach((section) => {
+		const btn = section.querySelector(".gitconfig-section-toggle");
+		const icon = section.querySelector(".gitconfig-toggle-icon");
+		if (!btn) return;
+		btn.addEventListener("click", (e) => {
+			e.preventDefault();
+			const hasCollapsed = section.classList.toggle("collapsed");
+			btn.setAttribute("aria-expanded", hasCollapsed ? "false" : "true");
+			if (icon) icon.textContent = hasCollapsed ? "+" : "\u2212";
+		});
+	});
+})();
diff --git a/pygitweb/templates/project_git_config.html b/pygitweb/templates/project_git_config.html
new file mode 100644
index 0000000..d9de9f3
--- /dev/null
+++ b/pygitweb/templates/project_git_config.html
@@ -0,0 +1,47 @@
+<h1 class="page-title">Git configuration</h1>
+<p class="text-muted mb-3">Project: <a href="{{ summary_url }}">{{ project_esc | safe }}</a></p>
+<div id="project-git-config" class="card" data-set-url="{{ set_config_url }}">
+  <div class="card-body">
+    <p class="text-secondary small mb-4">Changes are saved to this repository&rsquo;s local config. Select &ldquo;unset&rdquo; to remove a value.</p>
+    <div class="gitconfig-sections">
+      {% for sec in sections %}
+      <section class="gitconfig-config-section collapsed" data-gitconfig-section>
+        <div class="gitconfig-section-header">
+          <h2 class="gitconfig-section-title m-0 h3 mb-0">[{{ sec.section | e }}]</h2>
+          <button type="button" class="gitconfig-section-toggle" aria-expanded="false" aria-label="Toggle section">
+            <span class="gitconfig-toggle-icon">+</span>
+          </button>
+        </div>
+        <div class="gitconfig-section-content">
+          {% for sub in sec.subsections %}
+          {% if sub.subsection %}
+          <h3 class="gitconfig-subsection-header">[{{ sec.section | e }} "{{ sub.subsection | e }}"]</h3>
+          {% endif %}
+          {% for f in sub.fields %}
+          <div class="gitconfig-form-group project-git-config-field">
+            <label class="gitconfig-form-label form-label mb-0" for="cfg-{{ f.git_key | replace('.', '-') }}" title="{{ f.git_key | e }}">{{ f.display_name | e }}</label>
+            <div class="gitconfig-form-control">
+              {% if f.control_kind == "select" %}
+              <select class="form-select" id="cfg-{{ f.git_key | replace('.', '-') }}" data-config-key="{{ f.git_key | e }}">
+                <option value="" {% if not f.current %}selected{% endif %}>(unset)</option>
+                {% for o in f.options %}
+                <option value="{{ o | e }}" {% if f.current == o %}selected{% endif %}>{{ o | e }}</option>
+                {% endfor %}
+              </select>
+              {% else %}
+              <input type="text" class="form-control" id="cfg-{{ f.git_key | replace('.', '-') }}" data-config-key="{{ f.git_key | e }}" value="{{ f.current | e }}" autocomplete="off" spellcheck="false">
+              {% endif %}
+            </div>
+            {% if f.description %}
+            <div class="gitconfig-form-description">{{ f.description | e }}</div>
+            {% endif %}
+          </div>
+          {% endfor %}
+          {% endfor %}
+        </div>
+      </section>
+      {% endfor %}
+    </div>
+  </div>
+</div>
+<script src="/static/project_config.js"></script>
