├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .npmrc ├── .travis.yml ├── .verb.md ├── LICENSE ├── README.md ├── example.js ├── index.js ├── package.json └── test ├── expected └── _gitconfig.js ├── fixtures ├── _gitconfig ├── _gitconfig-branch └── _gitconfig.local └── test.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org/ 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | indent_size = 2 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "eslint:recommended" 4 | ], 5 | 6 | "env": { 7 | "browser": false, 8 | "es6": true, 9 | "node": true, 10 | "mocha": true 11 | }, 12 | 13 | "parserOptions":{ 14 | "ecmaVersion": 9, 15 | "sourceType": "module", 16 | "ecmaFeatures": { 17 | "modules": true, 18 | "experimentalObjectRestSpread": true 19 | } 20 | }, 21 | 22 | "globals": { 23 | "document": false, 24 | "navigator": false, 25 | "window": false 26 | }, 27 | 28 | "rules": { 29 | "accessor-pairs": 2, 30 | "arrow-spacing": [2, { "before": true, "after": true }], 31 | "block-spacing": [2, "always"], 32 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 33 | "comma-dangle": [2, "never"], 34 | "comma-spacing": [2, { "before": false, "after": true }], 35 | "comma-style": [2, "last"], 36 | "constructor-super": 2, 37 | "curly": [2, "multi-line"], 38 | "dot-location": [2, "property"], 39 | "eol-last": 2, 40 | "eqeqeq": [2, "allow-null"], 41 | "generator-star-spacing": [2, { "before": true, "after": true }], 42 | "handle-callback-err": [2, "^(err|error)$" ], 43 | "indent": [2, 2, { "SwitchCase": 1 }], 44 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 45 | "keyword-spacing": [2, { "before": true, "after": true }], 46 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 47 | "new-parens": 2, 48 | "no-array-constructor": 2, 49 | "no-caller": 2, 50 | "no-class-assign": 2, 51 | "no-cond-assign": 2, 52 | "no-const-assign": 2, 53 | "no-control-regex": 2, 54 | "no-debugger": 2, 55 | "no-delete-var": 2, 56 | "no-dupe-args": 2, 57 | "no-dupe-class-members": 2, 58 | "no-dupe-keys": 2, 59 | "no-duplicate-case": 2, 60 | "no-empty-character-class": 2, 61 | "no-eval": 2, 62 | "no-ex-assign": 2, 63 | "no-extend-native": 2, 64 | "no-extra-bind": 2, 65 | "no-extra-boolean-cast": 2, 66 | "no-extra-parens": [2, "functions"], 67 | "no-fallthrough": 2, 68 | "no-floating-decimal": 2, 69 | "no-func-assign": 2, 70 | "no-implied-eval": 2, 71 | "no-inner-declarations": [2, "functions"], 72 | "no-invalid-regexp": 2, 73 | "no-irregular-whitespace": 2, 74 | "no-iterator": 2, 75 | "no-label-var": 2, 76 | "no-labels": 2, 77 | "no-lone-blocks": 2, 78 | "no-mixed-spaces-and-tabs": 2, 79 | "no-multi-spaces": 2, 80 | "no-multi-str": 2, 81 | "no-multiple-empty-lines": [2, { "max": 1 }], 82 | "no-native-reassign": 0, 83 | "no-negated-in-lhs": 2, 84 | "no-new": 2, 85 | "no-new-func": 2, 86 | "no-new-object": 2, 87 | "no-new-require": 2, 88 | "no-new-wrappers": 2, 89 | "no-obj-calls": 2, 90 | "no-octal": 2, 91 | "no-octal-escape": 2, 92 | "no-proto": 0, 93 | "no-redeclare": 2, 94 | "no-regex-spaces": 2, 95 | "no-return-assign": 2, 96 | "no-self-compare": 2, 97 | "no-sequences": 2, 98 | "no-shadow-restricted-names": 2, 99 | "no-spaced-func": 2, 100 | "no-sparse-arrays": 2, 101 | "no-this-before-super": 2, 102 | "no-throw-literal": 2, 103 | "no-trailing-spaces": 0, 104 | "no-undef": 2, 105 | "no-undef-init": 2, 106 | "no-unexpected-multiline": 2, 107 | "no-unneeded-ternary": [2, { "defaultAssignment": false }], 108 | "no-unreachable": 2, 109 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 110 | "no-useless-call": 0, 111 | "no-with": 2, 112 | "one-var": [0, { "initialized": "never" }], 113 | "operator-linebreak": [0, "after", { "overrides": { "?": "before", ":": "before" } }], 114 | "padded-blocks": [0, "never"], 115 | "quotes": [2, "single", "avoid-escape"], 116 | "radix": 2, 117 | "semi": [2, "always"], 118 | "semi-spacing": [2, { "before": false, "after": true }], 119 | "space-before-blocks": [2, "always"], 120 | "space-before-function-paren": [2, "never"], 121 | "space-in-parens": [2, "never"], 122 | "space-infix-ops": 2, 123 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 124 | "spaced-comment": [0, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!", ","] }], 125 | "use-isnan": 2, 126 | "valid-typeof": 2, 127 | "wrap-iife": [2, "any"], 128 | "yoda": [2, "never"] 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.* text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # always ignore files 2 | *.DS_Store 3 | .idea 4 | .vscode 5 | *.sublime-* 6 | 7 | # test related, or directories generated by tests 8 | test/actual 9 | actual 10 | coverage 11 | .nyc* 12 | 13 | # npm 14 | node_modules 15 | npm-debug.log 16 | 17 | # yarn 18 | yarn.lock 19 | yarn-error.log 20 | 21 | # misc 22 | _gh_pages 23 | _draft 24 | _drafts 25 | bower_components 26 | vendor 27 | temp 28 | tmp 29 | TODO.md 30 | package-lock.json 31 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | - osx 5 | - windows 6 | language: node_js 7 | node_js: 8 | - node 9 | - '11' 10 | - '10' 11 | - '9' 12 | - '8' 13 | -------------------------------------------------------------------------------- /.verb.md: -------------------------------------------------------------------------------- 1 | ## Usage 2 | 3 | ```js 4 | const parse = require('{%= name %}'); 5 | 6 | // sync 7 | console.log(parse.sync()); 8 | 9 | // using async/await 10 | (async () => console.log(await parse()))(); 11 | ``` 12 | 13 | ## Options 14 | 15 | ### cwd 16 | 17 | The starting directory to search from. 18 | 19 | **Type**: `string` 20 | 21 | **Default**: `process.cwd()` (current working directory) 22 | 23 | 24 | ### path 25 | 26 | Either the absolute path to .git `config`, or the path relative to the current working directory. 27 | 28 | **Type**: `string` 29 | 30 | **Default**: `.git/config` 31 | 32 | 33 | ### Examples config object 34 | 35 | Parsed config object will look something like: 36 | 37 | ```js 38 | { core: 39 | { repositoryformatversion: '0', 40 | filemode: true, 41 | bare: false, 42 | logallrefupdates: true, 43 | ignorecase: true, 44 | precomposeunicode: true }, 45 | 'remote "origin"': 46 | { url: 'https://github.com/jonschlinkert/parse-git-config.git', 47 | fetch: '+refs/heads/*:refs/remotes/origin/*' }, 48 | 'branch "master"': { remote: 'origin', merge: 'refs/heads/master', ... } } 49 | ``` 50 | 51 | ## API 52 | {%= apidocs('index.js') %} 53 | 54 | ### .expandKeys examples 55 | 56 | Converts ini-style keys into objects: 57 | 58 | **Example 1** 59 | 60 | ```js 61 | const parse = require('parse-git-config'); 62 | const config = { 63 | 'foo "bar"': { doStuff: true }, 64 | 'foo "baz"': { doStuff: true } 65 | }; 66 | 67 | console.log(parse.expandKeys(config)); 68 | ``` 69 | 70 | Results in: 71 | 72 | ```js 73 | { 74 | foo: { 75 | bar: { doStuff: true }, 76 | baz: { doStuff: true } 77 | } 78 | } 79 | ``` 80 | 81 | **Example 2** 82 | 83 | ```js 84 | const parse = require('parse-git-config'); 85 | const config = { 86 | 'remote "origin"': { 87 | url: 'https://github.com/jonschlinkert/normalize-pkg.git', 88 | fetch: '+refs/heads/*:refs/remotes/origin/*' 89 | }, 90 | 'branch "master"': { 91 | remote: 'origin', 92 | merge: 'refs/heads/master' 93 | }, 94 | 'branch "dev"': { 95 | remote: 'origin', 96 | merge: 'refs/heads/dev', 97 | rebase: true 98 | } 99 | }; 100 | 101 | console.log(parse.expandKeys(config)); 102 | ``` 103 | 104 | Results in: 105 | 106 | ```js 107 | { 108 | remote: { 109 | origin: { 110 | url: 'https://github.com/jonschlinkert/normalize-pkg.git', 111 | fetch: '+refs/heads/*:refs/remotes/origin/*' 112 | } 113 | }, 114 | branch: { 115 | master: { 116 | remote: 'origin', 117 | merge: 'refs/heads/master' 118 | }, 119 | dev: { 120 | remote: 'origin', 121 | merge: 'refs/heads/dev', 122 | rebase: true 123 | } 124 | } 125 | } 126 | ``` 127 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-present, Jon Schlinkert. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # parse-git-config [![NPM version](https://img.shields.io/npm/v/parse-git-config.svg?style=flat)](https://www.npmjs.com/package/parse-git-config) [![NPM monthly downloads](https://img.shields.io/npm/dm/parse-git-config.svg?style=flat)](https://npmjs.org/package/parse-git-config) [![NPM total downloads](https://img.shields.io/npm/dt/parse-git-config.svg?style=flat)](https://npmjs.org/package/parse-git-config) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/parse-git-config.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/parse-git-config) 2 | 3 | > Parse `.git/config` into a JavaScript object. sync or async. 4 | 5 | Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support. 6 | 7 | ## Install 8 | 9 | Install with [npm](https://www.npmjs.com/): 10 | 11 | ```sh 12 | $ npm install --save parse-git-config 13 | ``` 14 | 15 | ## Usage 16 | 17 | ```js 18 | const parse = require('parse-git-config'); 19 | 20 | // sync 21 | console.log(parse.sync()); 22 | 23 | // using async/await 24 | (async () => console.log(await parse()))(); 25 | ``` 26 | 27 | ## Options 28 | 29 | ### cwd 30 | 31 | The starting directory to search from. 32 | 33 | **Type**: `string` 34 | 35 | **Default**: `process.cwd()` (current working directory) 36 | 37 | ### path 38 | 39 | Either the absolute path to .git `config`, or the path relative to the current working directory. 40 | 41 | **Type**: `string` 42 | 43 | **Default**: `.git/config` 44 | 45 | ### Examples config object 46 | 47 | Parsed config object will look something like: 48 | 49 | ```js 50 | { core: 51 | { repositoryformatversion: '0', 52 | filemode: true, 53 | bare: false, 54 | logallrefupdates: true, 55 | ignorecase: true, 56 | precomposeunicode: true }, 57 | 'remote "origin"': 58 | { url: 'https://github.com/jonschlinkert/parse-git-config.git', 59 | fetch: '+refs/heads/*:refs/remotes/origin/*' }, 60 | 'branch "master"': { remote: 'origin', merge: 'refs/heads/master', ... } } 61 | ``` 62 | 63 | ## API 64 | 65 | ### [parse](index.js#L42) 66 | 67 | Asynchronously parse a `.git/config` file. If only the callback is passed, the `.git/config` file relative to `process.cwd()` is used. 68 | 69 | **Params** 70 | 71 | * `options` **{Object|String|Function}**: Options with `cwd` or `path`, the cwd to use, or the callback function. 72 | * `callback` **{Function}**: callback function if the first argument is options or cwd. 73 | * `returns` **{Object}** 74 | 75 | **Example** 76 | 77 | ```js 78 | parse((err, config) => { 79 | if (err) throw err; 80 | // do stuff with config 81 | }); 82 | 83 | // or, using async/await 84 | (async () => { 85 | console.log(await parse()); 86 | console.log(await parse({ cwd: 'foo' })); 87 | console.log(await parse({ cwd: 'foo', path: 'some/.git/config' })); 88 | })(); 89 | ``` 90 | 91 | ### [.sync](index.js#L88) 92 | 93 | Synchronously parse a `.git/config` file. If no arguments are passed, the `.git/config` file relative to `process.cwd()` is used. 94 | 95 | **Params** 96 | 97 | * `options` **{Object|String}**: Options with `cwd` or `path`, or the cwd to use. 98 | * `returns` **{Object}** 99 | 100 | **Example** 101 | 102 | ```js 103 | console.log(parse.sync()); 104 | console.log(parse.sync({ cwd: 'foo' })); 105 | console.log(parse.sync({ cwd: 'foo', path: 'some/.git/config' })); 106 | ``` 107 | 108 | ### [.expandKeys](index.js#L134) 109 | 110 | Returns an object with only the properties that had ini-style keys converted to objects. 111 | 112 | **Params** 113 | 114 | * `config` **{Object}**: The parsed git config object. 115 | * `returns` **{Object}** 116 | 117 | **Example** 118 | 119 | ```js 120 | const config = parse.sync({ path: '/path/to/.gitconfig' }); 121 | const obj = parse.expandKeys(config); 122 | ``` 123 | 124 | ### .expandKeys examples 125 | 126 | Converts ini-style keys into objects: 127 | 128 | **Example 1** 129 | 130 | ```js 131 | const parse = require('parse-git-config'); 132 | const config = { 133 | 'foo "bar"': { doStuff: true }, 134 | 'foo "baz"': { doStuff: true } 135 | }; 136 | 137 | console.log(parse.expandKeys(config)); 138 | ``` 139 | 140 | Results in: 141 | 142 | ```js 143 | { 144 | foo: { 145 | bar: { doStuff: true }, 146 | baz: { doStuff: true } 147 | } 148 | } 149 | ``` 150 | 151 | **Example 2** 152 | 153 | ```js 154 | const parse = require('parse-git-config'); 155 | const config = { 156 | 'remote "origin"': { 157 | url: 'https://github.com/jonschlinkert/normalize-pkg.git', 158 | fetch: '+refs/heads/*:refs/remotes/origin/*' 159 | }, 160 | 'branch "master"': { 161 | remote: 'origin', 162 | merge: 'refs/heads/master' 163 | }, 164 | 'branch "dev"': { 165 | remote: 'origin', 166 | merge: 'refs/heads/dev', 167 | rebase: true 168 | } 169 | }; 170 | 171 | console.log(parse.expandKeys(config)); 172 | ``` 173 | 174 | Results in: 175 | 176 | ```js 177 | { 178 | remote: { 179 | origin: { 180 | url: 'https://github.com/jonschlinkert/normalize-pkg.git', 181 | fetch: '+refs/heads/*:refs/remotes/origin/*' 182 | } 183 | }, 184 | branch: { 185 | master: { 186 | remote: 'origin', 187 | merge: 'refs/heads/master' 188 | }, 189 | dev: { 190 | remote: 'origin', 191 | merge: 'refs/heads/dev', 192 | rebase: true 193 | } 194 | } 195 | } 196 | ``` 197 | 198 | ## About 199 | 200 |
201 | Contributing 202 | 203 | Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new). 204 | 205 |
206 | 207 |
208 | Running Tests 209 | 210 | Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command: 211 | 212 | ```sh 213 | $ npm install && npm test 214 | ``` 215 | 216 |
217 | 218 |
219 | Building docs 220 | 221 | _(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_ 222 | 223 | To generate the readme, run the following command: 224 | 225 | ```sh 226 | $ npm install -g verbose/verb#dev verb-generate-readme && verb 227 | ``` 228 | 229 |
230 | 231 | ### Related projects 232 | 233 | You might also be interested in these projects: 234 | 235 | * [git-user-name](https://www.npmjs.com/package/git-user-name): Get a user's name from git config at the project or global scope, depending on… [more](https://github.com/jonschlinkert/git-user-name) | [homepage](https://github.com/jonschlinkert/git-user-name "Get a user's name from git config at the project or global scope, depending on what git uses in the current context.") 236 | * [git-username](https://www.npmjs.com/package/git-username): Get the username (or 'owner' name) from a git/GitHub remote origin URL. | [homepage](https://github.com/jonschlinkert/git-username "Get the username (or 'owner' name) from a git/GitHub remote origin URL.") 237 | * [parse-author](https://www.npmjs.com/package/parse-author): Parse an author, contributor, maintainer or other 'person' string into an object with name, email… [more](https://github.com/jonschlinkert/parse-author) | [homepage](https://github.com/jonschlinkert/parse-author "Parse an author, contributor, maintainer or other 'person' string into an object with name, email and url properties following npm conventions.") 238 | * [parse-authors](https://www.npmjs.com/package/parse-authors): Parse a string into an array of objects with `name`, `email` and `url` properties following… [more](https://github.com/jonschlinkert/parse-authors) | [homepage](https://github.com/jonschlinkert/parse-authors "Parse a string into an array of objects with `name`, `email` and `url` properties following npm conventions. Useful for the `authors` property in package.json or for parsing an AUTHORS file into an array of authors objects.") 239 | * [parse-github-url](https://www.npmjs.com/package/parse-github-url): Parse a github URL into an object. | [homepage](https://github.com/jonschlinkert/parse-github-url "Parse a github URL into an object.") 240 | * [parse-gitignore](https://www.npmjs.com/package/parse-gitignore): Parse a .gitignore or .npmignore file into an array of patterns. | [homepage](https://github.com/jonschlinkert/parse-gitignore "Parse a .gitignore or .npmignore file into an array of patterns.") 241 | 242 | ### Contributors 243 | 244 | | **Commits** | **Contributor** | 245 | | --- | --- | 246 | | 66 | [jonschlinkert](https://github.com/jonschlinkert) | 247 | | 4 | [doowb](https://github.com/doowb) | 248 | | 1 | [daviwil](https://github.com/daviwil) | 249 | | 1 | [LexSwed](https://github.com/LexSwed) | 250 | | 1 | [sam3d](https://github.com/sam3d) | 251 | | 1 | [suarasaur](https://github.com/suarasaur) | 252 | 253 | ### Author 254 | 255 | **Jon Schlinkert** 256 | 257 | * [GitHub Profile](https://github.com/jonschlinkert) 258 | * [Twitter Profile](https://twitter.com/jonschlinkert) 259 | * [LinkedIn Profile](https://linkedin.com/in/jonschlinkert) 260 | 261 | ### License 262 | 263 | Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert). 264 | Released under the [MIT License](LICENSE). 265 | 266 | *** 267 | 268 | _This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.8.0, on November 20, 2018._ -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const parse = require('./'); 4 | const gitconfig = { 5 | email: 'email', 6 | 'remote "origin"': { 7 | url: 'https://github.com/jonschlinkert/normalize-pkg.git', 8 | fetch: '+refs/heads/*:refs/remotes/origin/*' 9 | }, 10 | 'branch "master"': { 11 | remote: 'origin', merge: 'refs/heads/master' 12 | }, 13 | 'branch "dev"': { 14 | remote: 'origin', merge: 'refs/heads/dev', rebase: true 15 | } 16 | }; 17 | 18 | const config = parse.expandKeys(gitconfig); 19 | console.log(config); 20 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * parse-git-config 3 | * 4 | * Copyright (c) 2015-present, Jon Schlinkert. 5 | * Released under the MIT License. 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const fs = require('fs'); 11 | const os = require('os'); 12 | const path = require('path'); 13 | const util = require('util'); 14 | const ini = require('ini'); 15 | const configPath = require('git-config-path'); 16 | const expand = str => (str ? str.replace(/^~/, os.homedir()) : ''); 17 | 18 | /** 19 | * Asynchronously parse a `.git/config` file. If only the callback is passed, 20 | * the `.git/config` file relative to `process.cwd()` is used. 21 | * 22 | * ```js 23 | * parse((err, config) => { 24 | * if (err) throw err; 25 | * // do stuff with config 26 | * }); 27 | * 28 | * // or, using async/await 29 | * (async () => { 30 | * console.log(await parse()); 31 | * console.log(await parse({ cwd: 'foo' })); 32 | * console.log(await parse({ cwd: 'foo', path: 'some/.git/config' })); 33 | * })(); 34 | * ``` 35 | * @name parse 36 | * @param {Object|String|Function} `options` Options with `cwd` or `path`, the cwd to use, or the callback function. 37 | * @param {Function} `callback` callback function if the first argument is options or cwd. 38 | * @return {Object} 39 | * @api public 40 | */ 41 | 42 | const parse = (options, callback) => { 43 | if (typeof options === 'function') { 44 | callback = options; 45 | options = null; 46 | } 47 | 48 | if (typeof callback !== 'function') { 49 | return parse.promise(options); 50 | } 51 | 52 | return parse.promise(options) 53 | .then(config => callback(null, config)) 54 | .catch(callback); 55 | }; 56 | 57 | parse.promise = options => { 58 | let filepath = parse.resolveConfigPath(options); 59 | let read = util.promisify(fs.readFile); 60 | let stat = util.promisify(fs.stat); 61 | if (!filepath) return Promise.resolve(null); 62 | 63 | return stat(filepath) 64 | .then(() => read(filepath, 'utf8')) 65 | .then(str => { 66 | if (options && options.include === true) { 67 | str = injectInclude(str, path.resolve(path.dirname(filepath))); 68 | } 69 | return parseIni(str, options); 70 | }); 71 | }; 72 | 73 | /** 74 | * Synchronously parse a `.git/config` file. If no arguments are passed, 75 | * the `.git/config` file relative to `process.cwd()` is used. 76 | * 77 | * ```js 78 | * console.log(parse.sync()); 79 | * console.log(parse.sync({ cwd: 'foo' })); 80 | * console.log(parse.sync({ cwd: 'foo', path: 'some/.git/config' })); 81 | * ``` 82 | * @name .sync 83 | * @param {Object|String} `options` Options with `cwd` or `path`, or the cwd to use. 84 | * @return {Object} 85 | * @api public 86 | */ 87 | 88 | parse.sync = options => { 89 | let filepath = parse.resolveConfigPath(options); 90 | 91 | if (filepath && fs.existsSync(filepath)) { 92 | let input = fs.readFileSync(filepath, 'utf8'); 93 | if (options && options.include === true) { 94 | let cwd = path.resolve(path.dirname(filepath)); 95 | input = injectInclude(input, cwd); 96 | } 97 | return parseIni(input, options); 98 | } 99 | 100 | return {}; 101 | }; 102 | 103 | /** 104 | * Resolve the git config path 105 | */ 106 | 107 | parse.resolveConfigPath = options => { 108 | if (typeof options === 'string') options = { type: options }; 109 | const opts = Object.assign({ cwd: process.cwd() }, options); 110 | const fp = opts.path ? expand(opts.path) : configPath(opts.type); 111 | return fp ? path.resolve(opts.cwd, fp) : null; 112 | }; 113 | 114 | /** 115 | * Deprecated: use `.resolveConfigPath` instead 116 | */ 117 | 118 | parse.resolve = options => parse.resolveConfigPath(options); 119 | 120 | /** 121 | * Returns an object with only the properties that had ini-style keys 122 | * converted to objects. 123 | * 124 | * ```js 125 | * const config = parse.sync({ path: '/path/to/.gitconfig' }); 126 | * const obj = parse.expandKeys(config); 127 | * ``` 128 | * @name .expandKeys 129 | * @param {Object} `config` The parsed git config object. 130 | * @return {Object} 131 | * @api public 132 | */ 133 | 134 | parse.expandKeys = config => { 135 | for (let key of Object.keys(config)) { 136 | let m = /(\S+) "(.*)"/.exec(key); 137 | if (!m) continue; 138 | let prop = m[1]; 139 | config[prop] = config[prop] || {}; 140 | config[prop][m[2]] = config[key]; 141 | delete config[key]; 142 | } 143 | return config; 144 | }; 145 | 146 | function parseIni(str, options) { 147 | let opts = Object.assign({}, options); 148 | 149 | str = str.replace(/\[(\S+) "(.*)"\]/g, (m, $1, $2) => { 150 | return $1 && $2 ? `[${$1} "${$2.split('.').join('\\.')}"]` : m; 151 | }); 152 | 153 | let config = ini.parse(str); 154 | if (opts.expandKeys === true) { 155 | return parse.expandKeys(config); 156 | } 157 | return config; 158 | } 159 | 160 | function injectInclude(input, cwd) { 161 | let lines = input.split('\n').filter(line => line.trim() !== ''); 162 | let len = lines.length; 163 | let res = []; 164 | 165 | for (let i = 0; i < len; i++) { 166 | let line = lines[i]; 167 | if (line.indexOf('[include]') === 0) { 168 | let filepath = lines[i + 1].replace(/^\s*path\s*=\s*/, ''); 169 | let fp = path.resolve(cwd, expand(filepath)); 170 | res.push(fs.readFileSync(fp)); 171 | } else { 172 | res.push(line); 173 | } 174 | } 175 | return res.join('\n'); 176 | } 177 | 178 | /** 179 | * Expose `parse` 180 | */ 181 | 182 | module.exports = parse; 183 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "parse-git-config", 3 | "description": "Parse `.git/config` into a JavaScript object. sync or async.", 4 | "version": "3.0.0", 5 | "homepage": "https://github.com/jonschlinkert/parse-git-config", 6 | "author": "Jon Schlinkert (https://github.com/jonschlinkert)", 7 | "contributors": [ 8 | "j. suárez (http://suarez.systems)", 9 | "Jon Schlinkert (http://twitter.com/jonschlinkert)", 10 | "Sam Holmes (https://samholmes.net)" 11 | ], 12 | "repository": "jonschlinkert/parse-git-config", 13 | "bugs": { 14 | "url": "https://github.com/jonschlinkert/parse-git-config/issues" 15 | }, 16 | "license": "MIT", 17 | "files": [ 18 | "index.js" 19 | ], 20 | "main": "index.js", 21 | "engines": { 22 | "node": ">=8" 23 | }, 24 | "scripts": { 25 | "test": "mocha" 26 | }, 27 | "dependencies": { 28 | "git-config-path": "^2.0.0", 29 | "ini": "^1.3.5" 30 | }, 31 | "devDependencies": { 32 | "gulp-format-md": "^2.0.0", 33 | "mocha": "^5.2.0" 34 | }, 35 | "keywords": [ 36 | "config", 37 | "git", 38 | "parse" 39 | ], 40 | "verb": { 41 | "run": true, 42 | "toc": false, 43 | "layout": "default", 44 | "tasks": [ 45 | "readme" 46 | ], 47 | "plugins": [ 48 | "gulp-format-md" 49 | ], 50 | "related": { 51 | "list": [ 52 | "git-user-name", 53 | "git-username", 54 | "parse-author", 55 | "parse-authors", 56 | "parse-github-url", 57 | "parse-gitignore" 58 | ] 59 | }, 60 | "lint": { 61 | "reflinks": true 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/expected/_gitconfig.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | user: { 3 | email: 'email', 4 | name: 'name', 5 | signingkey: 'https://help.github.com/articles/generating-a-new-gpg-key/' 6 | }, 7 | github: { 8 | user: 'name', 9 | token: 'https://github.com/settings/tokens' 10 | }, 11 | commit: { 12 | gpgsign: true 13 | }, 14 | tag: { 15 | gpgsign: true, 16 | path: '_gitconfig.local', 17 | sort: 'version:refname' 18 | }, 19 | core: { 20 | legacyheaders: false, 21 | quotepath: false, 22 | trustctime: false, 23 | precomposeunicode: false, 24 | pager: 'cat', 25 | logAllRefUpdates: true, 26 | excludesfile: '~/.gitignore' 27 | }, 28 | repack: { 29 | usedeltabaseoffset: true 30 | }, 31 | merge: { 32 | log: true, 33 | conflictstyle: 'diff3' 34 | }, 35 | apply: { 36 | whitespace: 'fix' 37 | }, 38 | help: { 39 | autocorrect: '1' 40 | }, 41 | rerere: { 42 | enabled: true 43 | }, 44 | color: { 45 | diff: 'auto', 46 | status: 'auto', 47 | branch: 'auto', 48 | interactive: 'auto', 49 | ui: 'always' 50 | }, 51 | 'color "diff"': { 52 | meta: 'yellow bold', 53 | frag: 'magenta', 54 | plain: 'white bold', 55 | old: 'red bold', 56 | new: 'green bold', 57 | commit: 'yellow bold', 58 | func: 'green dim', 59 | whitespace: 'red reverse' 60 | }, 61 | 'color "status"': { 62 | added: 'yellow', 63 | changed: 'green', 64 | untracked: 'cyan' 65 | }, 66 | 'color "branch"': { 67 | current: 'yellow reverse', 68 | local: 'yellow', 69 | remote: 'green' 70 | }, 71 | diff: { 72 | renames: 'copies', 73 | algorithm: 'patience', 74 | compactionHeuristic: true, 75 | wsErrorHighlight: 'all' 76 | }, 77 | 'diff "bin"': { 78 | textconv: 'hexdump -v -C' 79 | }, 80 | credential: { 81 | helper: 'store' 82 | }, 83 | status: { 84 | relativePaths: true, 85 | showUntrackedFiles: 'no' 86 | }, 87 | pull: { 88 | rebase: true 89 | }, 90 | push: { 91 | default: 'current', 92 | followTags: true 93 | }, 94 | alias: { 95 | a: 'commit --amend', 96 | c: 'commit -am', 97 | d: '!git diff --exit-code && git diff --cached', 98 | dif: 'diff', 99 | git: '!exec git', 100 | p: 'push -u', 101 | r: 'reset --soft HEAD~1', 102 | s: 'status', 103 | sc: 'clone --depth=1', 104 | l: 'log --graph --pretty=format:\'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset\' --abbrev-commit -n 15' 105 | }, 106 | 'remote "origin"': { 107 | fetch: '+refs/tags/*:refs/tags/*' 108 | }, 109 | branch: { 110 | autosetupmerge: 'always', 111 | autosetuprebase: 'always' 112 | }, 113 | http: { 114 | sslverify: false 115 | }, 116 | submodule: { 117 | fetchJobs: '0' 118 | }, 119 | fetch: { 120 | prune: true 121 | } 122 | }; 123 | -------------------------------------------------------------------------------- /test/fixtures/_gitconfig: -------------------------------------------------------------------------------- 1 | [include] 2 | path = _gitconfig.local 3 | [core] 4 | legacyheaders = false 5 | quotepath = false 6 | trustctime = false 7 | precomposeunicode = false 8 | pager = cat 9 | logAllRefUpdates = true 10 | excludesfile = ~/.gitignore 11 | [repack] 12 | usedeltabaseoffset = true 13 | [merge] 14 | log = true 15 | conflictstyle = diff3 16 | [apply] 17 | whitespace = fix 18 | [help] 19 | autocorrect = 1 20 | [rerere] 21 | enabled = true 22 | [color] 23 | diff = auto 24 | status = auto 25 | branch = auto 26 | interactive = auto 27 | ui = always 28 | [color "diff"] 29 | meta = yellow bold 30 | frag = magenta 31 | plain = white bold 32 | old = red bold 33 | new = green bold 34 | commit = yellow bold 35 | func = green dim 36 | whitespace = red reverse 37 | [color "status"] 38 | added = yellow 39 | changed = green 40 | untracked = cyan 41 | [color "branch"] 42 | current = yellow reverse 43 | local = yellow 44 | remote = green 45 | [diff] 46 | renames = copies 47 | algorithm = patience 48 | compactionHeuristic = true 49 | wsErrorHighlight = all 50 | [diff "bin"] 51 | textconv = hexdump -v -C 52 | [credential] 53 | helper = store 54 | [status] 55 | relativePaths = true 56 | showUntrackedFiles = no 57 | [pull] 58 | rebase = true 59 | [push] 60 | default = current 61 | followTags = true 62 | [alias] 63 | a = commit --amend 64 | c = commit -am 65 | d = !git diff --exit-code && git diff --cached 66 | dif = diff 67 | git = !exec git 68 | p = push -u 69 | r = reset --soft HEAD~1 70 | s = status 71 | sc = clone --depth=1 72 | l = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit -n 15 73 | [remote "origin"] 74 | fetch = +refs/pr/*/head:refs/remotes/origin/pr/* 75 | fetch = +refs/tags/*:refs/tags/* 76 | [branch] 77 | autosetupmerge = always 78 | autosetuprebase = always 79 | [http] 80 | sslverify = false 81 | [submodule] 82 | fetchJobs = 0 83 | [fetch] 84 | prune = true 85 | [tag] 86 | sort = version:refname 87 | -------------------------------------------------------------------------------- /test/fixtures/_gitconfig-branch: -------------------------------------------------------------------------------- 1 | [branch "2.0"] 2 | remote = origin 3 | merge = refs/heads/devel 4 | 5 | [branch "devel"] 6 | remote = origin 7 | merge = refs/heads/devel 8 | 9 | [branch "master"] 10 | remote = origin 11 | merge = refs/heads/devel 12 | -------------------------------------------------------------------------------- /test/fixtures/_gitconfig.local: -------------------------------------------------------------------------------- 1 | [user] 2 | email = email 3 | name = name 4 | signingkey = https://help.github.com/articles/generating-a-new-gpg-key/ 5 | [github] 6 | user = name 7 | token = https://github.com/settings/tokens 8 | [commit] 9 | gpgsign = true 10 | [tag] 11 | gpgsign = true -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | require('mocha'); 4 | const isTravis = process.env.TRAVIS || process.env.CLI; 5 | const os = require('os'); 6 | const assert = require('assert'); 7 | const path = require('path'); 8 | const parse = require('..'); 9 | 10 | const cwd = (...args) => path.resolve(__dirname, ...args); 11 | const fixture = name => cwd('fixtures', name); 12 | 13 | describe('parse-git-config', function() { 14 | describe('async', function() { 15 | it('should return a promise when callback is not passed', function(cb) { 16 | parse({ path: fixture('_gitconfig') }) 17 | .then(config => { 18 | assert(config.hasOwnProperty('core')); 19 | cb(); 20 | }) 21 | .catch(cb); 22 | }); 23 | 24 | it('should parse .git/config', function(cb) { 25 | parse({ path: fixture('_gitconfig') }, function(err, config) { 26 | assert(!err); 27 | assert(config.hasOwnProperty('core')); 28 | cb(); 29 | }); 30 | }); 31 | 32 | it('should expand keys in config', function(cb) { 33 | parse({ path: fixture('_gitconfig'), expandKeys: true }) 34 | .then(config => { 35 | assert(config.hasOwnProperty('color')); 36 | assert(config.color.hasOwnProperty('diff')); 37 | cb(); 38 | }) 39 | .catch(cb); 40 | }); 41 | 42 | it('should not expand dots in keys in config', function(cb) { 43 | parse({ path: fixture('_gitconfig-branch'), expandKeys: true }) 44 | .then(config => { 45 | assert.deepEqual(Object.keys(config.branch), ['devel', 'master', '2.0']); 46 | cb(); 47 | }) 48 | .catch(cb); 49 | }); 50 | 51 | it('should include other config sources', function(cb) { 52 | parse({ path: fixture('_gitconfig'), include: true }, function(err, config) { 53 | assert(!err); 54 | assert.deepEqual(config, require('./expected/_gitconfig.js')); 55 | cb(); 56 | }); 57 | }); 58 | 59 | it('should throw an error when .git/config does not exist', function(cb) { 60 | parse({ path: 'foo' }, function(err, config) { 61 | assert(err instanceof Error); 62 | assert(/ENOENT.*parse-git-config/.test(err.message)); 63 | cb(); 64 | }); 65 | }); 66 | }); 67 | 68 | describe('promise', function() { 69 | it('should return a promise', function(cb) { 70 | parse.promise({ path: fixture('_gitconfig') }) 71 | .then(config => { 72 | assert(config.hasOwnProperty('core')); 73 | cb(); 74 | }); 75 | }); 76 | 77 | it('should include other config sources', function() { 78 | return parse.promise({ path: fixture('_gitconfig'), include: true }) 79 | .then(config => { 80 | assert.deepEqual(config, require('./expected/_gitconfig.js')); 81 | }); 82 | }); 83 | }); 84 | 85 | describe('sync', function() { 86 | it('should return an object', function() { 87 | assert(parse.sync({path: fixture('_gitconfig') }).hasOwnProperty('core')); 88 | }); 89 | }); 90 | 91 | describe('.expandKeys', function() { 92 | it('should expand ini-style keys', function() { 93 | const config = { 94 | 'foo "bar"': { doStuff: true }, 95 | 'foo "baz"': { doStuff: true } 96 | }; 97 | 98 | assert.deepEqual(parse.expandKeys(config), { 99 | foo: { 100 | bar: { doStuff: true }, 101 | baz: { doStuff: true } 102 | } 103 | }); 104 | }); 105 | }); 106 | 107 | describe('resolve', function() { 108 | it('should resolve the git config in the cwd by default', function() { 109 | assert.equal(parse.resolve(), path.resolve(process.cwd(), '.git/config')); 110 | }); 111 | 112 | it('should allow override path', function() { 113 | const fp = path.resolve(os.homedir(), '.gitconfig'); 114 | assert.equal(parse.resolve({ path: fp }), fp); 115 | }); 116 | 117 | it('should include other config sources', function() { 118 | const fp = path.join(__dirname, 'fixtures/_gitconfig'); 119 | const actual = parse.sync({ path: fp, include: true }); 120 | assert.deepEqual(actual, require('./expected/_gitconfig.js')); 121 | }); 122 | 123 | it('should resolve relative path to cwd', function() { 124 | assert.equal(parse.resolve({ path: '.config' }), path.resolve(process.cwd(), '.config')); 125 | }); 126 | 127 | it('should resolve relative path to the global git config when `global` is passed', function() { 128 | if (isTravis && os.platform() === 'darwin') return this.skip(); 129 | assert.equal(parse.resolve('global'), path.resolve(os.homedir(), '.gitconfig')); 130 | }); 131 | 132 | it('should allow override of cwd', function() { 133 | const actual = parse.resolve({ path: '.config', cwd: '/opt/config' }); 134 | assert.equal(actual, path.resolve('/opt/config/.config')); 135 | }); 136 | }); 137 | }); 138 | --------------------------------------------------------------------------------