{"id":15552183,"url":"https://github.com/bahmutov/ggit","last_synced_at":"2025-09-28T23:31:11.119Z","repository":{"id":6902086,"uuid":"8152080","full_name":"bahmutov/ggit","owner":"bahmutov","description":"Local git command wrappers","archived":false,"fork":false,"pushed_at":"2024-10-06T06:29:33.000Z","size":540,"stargazers_count":25,"open_issues_count":36,"forks_count":9,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-27T20:52:11.434Z","etag":null,"topics":["git","nodejs"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bahmutov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-02-12T03:18:39.000Z","updated_at":"2023-09-08T16:37:40.000Z","dependencies_parsed_at":"2023-12-30T04:26:35.059Z","dependency_job_id":"d5657ca4-3de2-490b-bb29-84396775225d","html_url":"https://github.com/bahmutov/ggit","commit_stats":{"total_commits":302,"total_committers":10,"mean_commits":30.2,"dds":0.1556291390728477,"last_synced_commit":"2e85b0316d9c272e067dca5679d39e302f4cb9cd"},"previous_names":[],"tags_count":101,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bahmutov%2Fggit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bahmutov%2Fggit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bahmutov%2Fggit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bahmutov%2Fggit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bahmutov","download_url":"https://codeload.github.com/bahmutov/ggit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234570361,"owners_count":18854155,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git","nodejs"],"created_at":"2024-10-02T14:11:24.337Z","updated_at":"2025-09-28T23:31:05.689Z","avatar_url":"https://github.com/bahmutov.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# ggit\n\n\u003e Local promise-returning git command wrappers\n\n[![NPM][ggit-icon] ][ggit-url]\n\n[![Build status][ggit-ci-image] ][ggit-ci-url]\n[![semantic-release][semantic-image] ][semantic-url]\n[![manpm](https://img.shields.io/badge/manpm-%E2%9C%93-3399ff.svg)](https://github.com/bahmutov/manpm)\n[![renovate-app badge][renovate-badge] ][renovate-app]\n\n[ggit-icon]: https://nodei.co/npm/ggit.svg?downloads=true\n[ggit-url]: https://npmjs.org/package/ggit\n[ggit-ci-image]: https://travis-ci.org/bahmutov/ggit.svg?branch=master\n[ggit-ci-url]: https://travis-ci.org/bahmutov/ggit\n[semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg\n[semantic-url]: https://github.com/semantic-release/semantic-release\n[renovate-badge]: https://img.shields.io/badge/renovate-app-blue.svg\n[renovate-app]: https://renovateapp.com/\n\n\n\n## Stand alone tool\n\nYou can install and run this tool as a stand alone CLI application.\n\n    npm install -g ggit\n    ggit --help\n\n**note** `ggit-last` tool has been moved to\n[git-last](https://github.com/bahmutov/git-last#readme) repo.\n\n## API\n\n### cloneRepo\n\n```javascript\nvar clone = require('ggit').cloneRepo;\nclone({\n    url: 'git@github.com:bahmutov/test-next-updater.git',\n    folder: 'folder to create, should not exist yet'\n}).then(function () {\n    console.log('cloned repo to destination folder');\n});\n```\n\n### exec\n\n```javascript\nvar exec = require('ggit').exec;\nvar cmd = 'rm -rf folder';\nvar verbose = true;\nexec(cmd, verbose).then(function () {\n    console.log('removed folder');\n});\n```\n\n### blame\n\nFinds last person who has touched specific line in a file\n\n* filename - full or partial filename (from the repo's root)\n* lineNumber - starts with 1\n\n```javascript\nvar blame = require('ggit').blame;\nblame(filename, lineNumber).then(function (info) {\n  /*\n    info is object with fields like\n    { commit: '6e65f8ec5ed63cac92ed130b1246d9c23223c04e',\n      author: 'Gleb Bahmutov',\n      committer: 'Gleb Bahmutov',\n      summary: 'adding blame feature',\n      filename: 'test/blame.js',\n      line: 'var blame = require(\\'../index\\').blame;' }\n  */\n});\n```\n\nEquivalent to porcelain git output: see [git-blame](http://git-scm.com/docs/git-blame)\n\n\n\n### isTracked\n\nReturns `true` if given path is tracked in the repo.\n\n* path\n\n```javascript\nvar isTracked = require('ggit').isTracked;\nisTracked(filename).then(function (result) {\n    // result is true or false\n});\n```\n\n\n### hasChanges\n\nReturns `true` if there are local uncommitted stages\n\n```javascript\nvar changed = require('ggit').hasChanges;\nchanged().then(function (result) {\n    // result is true or false\n});\n```\n\n\n### commit\n\nCommit any changes with a given message. Second argument is optional and will\nbe added after a blank line to the short main message.\n\n```js\nvar commit = require('ggit').commit;\ncommit('added foo', 'long text').then(function () {\n    // after commit\n});\n```\n\nYou can pass the entire message if wanted as first argument\n\n```js\nvar fullMessage = 'first line\\n\\nbody of message\\n';\ncommit(fullMessage).then(...);\n```\n\n\n### push\n\nPush commits to the remote\n\n```javascript\nvar psuh = require('ggit').psuh;\npsuh().then(function () {\n    // after the push\n});\n```\n\n\n### commits\n\nReturns list of commits in the given folder as a list or object\n\n```js\n// commits.all - gets all commits\nvar commits = require('ggit').commits;\ncommits.all(gitRepoFolder)\n    .then(R.take(2))\n    .then(console.table)\n    .done();\n// commits.byId - transforms list of commits into object\n// where keys = ids, values = messages\n// For example to get an object with 2 commit ids as keys\ncommits.all(gitRepoFolder)\n    .then(R.take(2))\n    .then(commits.byId)\n    .then(console.log)\n    .done();\n```\n\nEach object has at least 'id', 'message' and (maybe empty) 'body' properties.\n\nYou can also return just the commits starting from the last version tag\n(which usually starts with 'v'). This is useful for semantic release code.\n\n```js\nvar commits = require('ggit').commits;\ncommits.afterLastTag()\n  .then(function (list) { ... })\n```\n\nYou can get commits after certain SHA\n\n```js\nvar commits = require('ggit').commits;\ncommits.after('439...')\n  .then(function (list) { ... })\n```\n\n\n### trackedFiles\n\nReturns all tracked source files in the given folder matching pattern.\nBoth folder and pattern are optional.\n\n```js\nrequire('ggit')\n    .trackedFiles(__dirname, '*.js', options)\n    .then(function (list) {\n        console.log('javascript tracked in the current folder are');\n        console.log(list);\n    })\n    .done();\n```\n\nThe `options` argument is optional, and is passed directly to the \n[glob](https://www.npmjs.com/package/glob) package that does file discovery.\nThe only important option to use is `{ dot: true }` - if you want to find the\nfilenames that start with `.`. For example to find ALL files in the repo call\n\n```js\nrequire('ggit')\n    .trackedFiles(__dirname, '**', { dot: true })\n// returns .gitignore, .travis.yml, index.js etc\n```\n\n\n### untrackedFiles\n\nReturns all untracked source files in the repo.\n\n```js\nrequire('ggit')\n    .untrackedFiles()\n    .then(function (list) {\n        // list is Array of strings, could be empty\n        console.log('untracked files are');\n        console.log(list);\n    })\n    .done();\n```\n\n\n### commitPerLine\n\nReturns an object where for each key (filename) there is a list of commits for each line.\n\n* list of filenames\n\n```js\nvar perLine = require('ggit').commitPerLine;\nperLine(['foo.js', 'bar.js']).then(function (result) {\n    /*\n    {\n        'foo.js': [{\n            commit: '3c6b01eb3c96db1cbdf277904545107ef97cbb56',\n            author: 'Gleb Bahmutov',\n            committer: 'Gleb Bahmutov',\n            summary: 'cool commit',\n            filename: 'foo.js',\n            line: '// actual source line' \n        },\n            ...\n        }],\n        'bar.js': [...]\n    }\n    */\n});\n```\n\n\n### numstat\n\nReturns info for a specific commit - number of lines changed, deleted. \nSame as `$ git show --numstat \u003cid\u003e`.\n\n```js\nrequire('ggit')\n    .numstat('5d3ee3')\n    .then(function (result) {\n        /* result is\n            {\n                commit: \u003cfull commit SHA\u003e,\n                author:\n                message:\n                date:\n                changes: {\n                    'filename 1': {\n                        filename: 'filename 1',\n                        added: 10,\n                        deleted: 3\n                    },\n                    ...\n                }\n            }\n        */\n    })\n    .done();\n```\n\n\n### lastCommitId\n\nReturns last commit id\n\n```js\nrequire('ggit')\n    .lastCommitId()\n    .then(function (str) {\n        // str is full SHA id string\n    })\n    .done();\n```\n\nYou can pass options object as in `lastCommitId(options)` where\n\n* **file** - save id into the JSON file with the given `file` name.\n\nWhen saving into a file, it will grab version from `package.json` (if the file exists),\ncurrent ISO timestamp + Eastern Standard Timezon timestamp, so the full JSON will look\nsomething like this\n\n```json\n{ \n    \"id\": \"d3d9f1656ded06c490b12a9ec5636d80dfd932eb\",\n    \"short\": \"d3d9f16\",\n    \"savedAt\": \"2017-08-24T18:58:27.210Z\",\n    \"EST\": \"2017-08-24T14:58:27-04:00\",\n    \"version\": \"1.2.3\" ,\n    \"branch\": \"master\"\n}\n```\n\nIf you pass option `{message: true}` the output will also have cropped commit subject string,\nmaking finding the deploy easier.\n\n```json\n{ \n    \"id\": \"d3d9f1656ded06c490b12a9ec5636d80dfd932eb\",\n    \"short\": \"d3d9f16\",\n    \"savedAt\": \"2017-08-24T18:58:27.210Z\",\n    \"EST\": \"2017-08-24T14:58:27-04:00\",\n    \"message\": \"feat(build): ad...\",\n    \"version\": \"1.2.3\" \n}\n```\n\n\n### branchName\n\nResolves with the current branch name\n\n```js\nrequire('ggit').branchName()\n    .then(function (name) {\n        // name = \"master\" or whatever\n    });\n```\n\n\n### changedFiles\n\nReturns list of modified files\n\n```javascript\nvar changedFiles = require('ggit').changedFiles;\nchangedFiles()\n    .then(function (files) {})\n    .catch(function (error) {});\n```\n\nThe object `files` groups filenames by modification property\n\n```js\n{\n    A: [...], // list of added files\n    C: [...], // list of copied files\n    M: [...], // list of modified files\n    D: [...]  // list of deleted files\n}\n// each item in the list is\n{\n    diff: 'A' // or C, M, D\n    name: 'src/something.js' // relative to the repo root\n    filename: 'full path',\n    before: 'file contents', // if available (for example M, D)\n    after: 'file contents' // if available (for A, M)\n}\n```\n\nThis is a wrapper around two commands `git diff --name-status --diff-filter=ACMD`\nand `git status --porcelain`\n\n\n\n\n### changedFilesAfter\n\nReturns list of unique files modified / added / deleted after given commit.\nThe commits are limited to specific branch (usually \"master\") to avoid mixing\nup multiple branches.\n\n```javascript\nvar changedFilesAfter = require('ggit').changedFilesAfter;\nchangedFilesAfter('a12f55f', 'master')\n    .then(console.log)\n    .catch(console.error);\n/*\nsomething like\n[ 'README.md',\n  'docs/commits.md',\n  'src/commits.js',\n  'src/get-one-line-log.js',\n  'package.json',\n  'src/last-commit-id.js' ]\n*/\n```\n\n\n### fileContents\n\nReturns the contents of a file at some point\n\n* filename - full or partial filename (from the repo's root)\n* at (optional) - checkpoint, HEAD by default\n\n```javascript\nvar fileContents = require('ggit').fileContents;\nfileContents(filename).then(function (text) { ... });\n```\n\nSame as `git show \u003cat\u003e:\u003cname\u003e`\n\n\n\n### commitMessage\n\nReturns the contents of the Git current commit message,\nusually for validation before the commit.\n\n```js\nrequire('ggit').commitMessage()\n    .then(function (text) {\n      // do something with the message\n    },\n    function () {\n      // file not found\n    });\n```\n\nIf you pass SHA, it will grab that commit's email, subject and body (if exists)\nand return as an object\n\n```js\nrequire('ggit').commitMessage('4df4...')\n    .then(console.log)\n/*\n{\n    \"email\": \"foo@email.com\",\n    \"subject\": \"something something\",\n    \"body\": \"more details about the commit\\nanother line\"\n}\n*/\n```\n\n\n### getGitFolder\n\nReturns the root folder, equivalent to command\nline `git rev-parse --show-toplevel`\n\n```javascript\nrequire('ggit').getGitFolder()\n  .then(folder =\u003e {\n    ...\n  })\n```\n\n\n### tags\n\n\u003e Requires `git` \u003e= 2.0\n\nReturns list of tags in the given folder, including commit ids.\n\n```js\nvar tags = require('ggit').tags;\ntags().then(function (list) {\n  /*\n    each object in list is like\n  {\n    \"commit\": \"7756b5609c5aae651f267fa3fc00763bcd276bf6\",\n    \"tag\": \"v1.3.0\"\n  }\n  */\n})\n```\nYou can return just tags that start with \"v\" by passing\n`true` to `tags`.\n\n```js\ntags(true).then(function (list) {...})\n```\n\nOldest tag is returns as first object, latest tag is the\nlast object in the list.\n\nIf you have older `git` (like Travis does), it will automatically try to\ngrab all the tags and sort them using\n[semantic sort](https://github.com/semver/node-semver#comparison)\n\n### branchTags\n\n\u003e Requires `git` \u003e= 2.7\n\nSimilar to `tags`, `branchTags` returns tags in the given folder,\nbut only those tags accessible from the current branch. Any tags\nin the repository that point to a commit on another branch will\nnot be returned by `branchTags`.\n\n```js\nvar branchTags = require('ggit').branchTags;\nbranchTags().then(function (list) {\n  /*\n    each object in list is like\n  {\n    \"commit\": \"7756b5609c5aae651f267fa3fc00763bcd276bf6\",\n    \"tag\": \"v1.3.0\"\n  }\n  */\n})\n```\nYou can return just tags that start with \"v\" by passing\n`true` to `branchTags`.\n\n```js\nbranchTags(true).then(function (list) {...})\n```\n\nOldest tag is returned as first object, latest tag is the\nlast object in the list.\n\n\n### fetchTags\n\nFetches remote tags from origin.\n\n```js\nvar fetchTags = require('ggit').fetchTags;\nfetchTags().then(function () {\n  // should be same as running command\n  // git pull origin --tags\n})\n```\n\nYou can pass the branch name, by default will fetch\nfrom master\n\n```js\nfetchTags('development')\n```\n\n\n\n\n\n## Development\n\nEdit source, run unit tests, run end to end tests and push the code\nback to Github. The NPM publishing happens automatically using\n[semantic release](https://github.com/semantic-release/semantic-release)\n\n```sh\nnpm test\nnpm run commit\ngit push\n```\n\nTo debug problems, run the command with `DEBUG=ggit` environment variable enabled\nto see verbose logging.\n\n### Unit tests\n\nSome of the unit tests rely on extracting private functions from modules\nusing [scope magic with `describe-it`](https://github.com/bahmutov/describe-it),\nwhich requires Node 4.2.2 and might not work with later Node versions.\n\n### Related projects\n\n* [npm-utils](https://github.com/bahmutov/npm-utils) - small utils for working\nwith NPM commands.\n\n\n\n### Small print\n\nAuthor: Gleb Bahmutov \u0026copy; 2015\n\n* [@bahmutov](https://twitter.com/bahmutov)\n* [glebbahmutov.com](http://glebbahmutov.com)\n* [blog](http://glebbahmutov.com/blog/)\n\nLicense: [MIT](LICENSE) - do anything with the code, but don't blame uTest if it does not work.\n\nSpread the word: tweet, star on github, etc.\n\nSupport: if you find any problems with this module, email / tweet / open\n[issue on Github](https://github.com/bahmutov/ggit/issues)\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbahmutov%2Fggit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbahmutov%2Fggit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbahmutov%2Fggit/lists"}