1 | # ggit
|
2 |
|
3 | > Local promise-returning git command wrappers
|
4 |
|
5 | [![NPM][ggit-icon] ][ggit-url]
|
6 |
|
7 | [![Build status][ggit-ci-image] ][ggit-ci-url]
|
8 | [![dependencies][ggit-dependencies-image] ][ggit-dependencies-url]
|
9 | [![devdependencies][ggit-devdependencies-image] ][ggit-devdependencies-url]
|
10 | [![semantic-release][semantic-image] ][semantic-url]
|
11 | [![manpm](https://img.shields.io/badge/manpm-%E2%9C%93-3399ff.svg)](https://github.com/bahmutov/manpm)
|
12 |
|
13 | [ggit-icon]: https://nodei.co/npm/ggit.svg?downloads=true
|
14 | [ggit-url]: https://npmjs.org/package/ggit
|
15 | [ggit-ci-image]: https://travis-ci.org/bahmutov/ggit.svg?branch=master
|
16 | [ggit-ci-url]: https://travis-ci.org/bahmutov/ggit
|
17 | [ggit-dependencies-image]: https://david-dm.org/bahmutov/ggit.svg
|
18 | [ggit-dependencies-url]: https://david-dm.org/bahmutov/ggit
|
19 | [ggit-devdependencies-image]: https://david-dm.org/bahmutov/ggit/dev-status.svg
|
20 | [ggit-devdependencies-url]: https://david-dm.org/bahmutov/ggit#info=devDependencies
|
21 | [semantic-image]: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
|
22 | [semantic-url]: https://github.com/semantic-release/semantic-release
|
23 |
|
24 |
|
25 |
|
26 | ## Stand alone tool
|
27 |
|
28 | You can install and run this tool as a stand alone CLI application.
|
29 |
|
30 | npm install -g ggit
|
31 | ggit --help
|
32 | # get last commit id in the current folder, save into json file
|
33 | ggit last -f build.json
|
34 |
|
35 | ## API
|
36 |
|
37 | ### cloneRepo
|
38 |
|
39 | ```javascript
|
40 | var clone = require('ggit').cloneRepo;
|
41 | clone({
|
42 | url: 'git@github.com:bahmutov/test-next-updater.git',
|
43 | folder: 'folder to create, should not exist yet'
|
44 | }).then(function () {
|
45 | console.log('cloned repo to destination folder');
|
46 | });
|
47 | ```
|
48 |
|
49 | ### exec
|
50 |
|
51 | ```javascript
|
52 | var exec = require('ggit').exec;
|
53 | var cmd = 'rm -rf folder';
|
54 | var verbose = true;
|
55 | exec(cmd, verbose).then(function () {
|
56 | console.log('removed folder');
|
57 | });
|
58 | ```
|
59 |
|
60 | ### blame
|
61 |
|
62 | Finds last person who has touched specific line in a file
|
63 |
|
64 | * filename - full or partial filename (from the repo's root)
|
65 | * lineNumber - starts with 1
|
66 |
|
67 | ```javascript
|
68 | var blame = require('ggit').blame;
|
69 | blame(filename, lineNumber).then(function (info) {
|
70 | /*
|
71 | info is object with fields like
|
72 | { commit: '6e65f8ec5ed63cac92ed130b1246d9c23223c04e',
|
73 | author: 'Gleb Bahmutov',
|
74 | committer: 'Gleb Bahmutov',
|
75 | summary: 'adding blame feature',
|
76 | filename: 'test/blame.js',
|
77 | line: 'var blame = require(\'../index\').blame;' }
|
78 | */
|
79 | });
|
80 | ```
|
81 |
|
82 | Equivalent to porcelain git output: see [git-blame](http://git-scm.com/docs/git-blame)
|
83 |
|
84 |
|
85 |
|
86 | ### isTracked
|
87 |
|
88 | Returns `true` if given path is tracked in the repo.
|
89 |
|
90 | * path
|
91 |
|
92 | ```javascript
|
93 | var isTracked = require('ggit').isTracked;
|
94 | isTracked(filename).then(function (result) {
|
95 | // result is true or false
|
96 | });
|
97 | ```
|
98 |
|
99 |
|
100 | ### hasChanges
|
101 |
|
102 | Returns `true` if there are local uncommitted stages
|
103 |
|
104 | ```javascript
|
105 | var changed = require('ggit').hasChanges;
|
106 | changed().then(function (result) {
|
107 | // result is true or false
|
108 | });
|
109 | ```
|
110 |
|
111 |
|
112 | ### commit
|
113 |
|
114 | Commit any changes with a given message. Second argument is optional and will
|
115 | be added after a blank line to the short main message.
|
116 |
|
117 | ```js
|
118 | var commit = require('ggit').commit;
|
119 | commit('added foo', 'long text').then(function () {
|
120 | // after commit
|
121 | });
|
122 | ```
|
123 |
|
124 | You can pass the entire message if wanted as first argument
|
125 |
|
126 | ```js
|
127 | var fullMessage = 'first line\n\nbody of message\n';
|
128 | commit(fullMessage).then(...);
|
129 | ```
|
130 |
|
131 |
|
132 | ### push
|
133 |
|
134 | Push commits to the remote
|
135 |
|
136 | ```javascript
|
137 | var psuh = require('ggit').psuh;
|
138 | psuh().then(function () {
|
139 | // after the push
|
140 | });
|
141 | ```
|
142 |
|
143 |
|
144 | ### commits
|
145 |
|
146 | Returns list of commits in the given folder as a list or object
|
147 |
|
148 | ```js
|
149 | // commits.all - gets all commits
|
150 | var commits = require('ggit').commits;
|
151 | commits.all(gitRepoFolder)
|
152 | .then(R.take(2))
|
153 | .then(console.table)
|
154 | .done();
|
155 | // commits.byId - transforms list of commits into object
|
156 | // where keys = ids, values = messages
|
157 | // For example to get an object with 2 commit ids as keys
|
158 | commits.all(gitRepoFolder)
|
159 | .then(R.take(2))
|
160 | .then(commits.byId)
|
161 | .then(console.log)
|
162 | .done();
|
163 | ```
|
164 |
|
165 | Each object has at least 'id', 'message' and (maybe empty) 'body' properties.
|
166 |
|
167 | You can also return just the commits starting from the last version tag
|
168 | (which usually starts with 'v'). This is useful for semantic release code.
|
169 |
|
170 | ```js
|
171 | var commits = require('ggit').commits;
|
172 | commits.afterLastTag()
|
173 | .then(function (list) { ... })
|
174 | ```
|
175 |
|
176 | You can get commits after certain SHA
|
177 |
|
178 | ```js
|
179 | var commits = require('ggit').commits;
|
180 | commits.after('439...')
|
181 | .then(function (list) { ... })
|
182 | ```
|
183 |
|
184 |
|
185 | ### trackedFiles
|
186 |
|
187 | Returns all tracked source files in the given folder matching pattern.
|
188 | Both folder and pattern are optional.
|
189 |
|
190 | ```js
|
191 | require('ggit')
|
192 | .trackedFiles(__dirname, '*.js', options)
|
193 | .then(function (list) {
|
194 | console.log('javascript tracked in the current folder are');
|
195 | console.log(list);
|
196 | })
|
197 | .done();
|
198 | ```
|
199 |
|
200 | The `options` argument is optional, and is passed directly to the
|
201 | [glob](https://www.npmjs.com/package/glob) package that does file discovery.
|
202 | The only important option to use is `{ dot: true }` - if you want to find the
|
203 | filenames that start with `.`. For example to find ALL files in the repo call
|
204 |
|
205 | ```js
|
206 | require('ggit')
|
207 | .trackedFiles(__dirname, '**', { dot: true })
|
208 | // returns .gitignore, .travis.yml, index.js etc
|
209 | ```
|
210 |
|
211 |
|
212 | ### untrackedFiles
|
213 |
|
214 | Returns all untracked source files in the repo.
|
215 |
|
216 | ```js
|
217 | require('ggit')
|
218 | .untrackedFiles()
|
219 | .then(function (list) {
|
220 | // list is Array of strings, could be empty
|
221 | console.log('untracked files are');
|
222 | console.log(list);
|
223 | })
|
224 | .done();
|
225 | ```
|
226 |
|
227 |
|
228 | ### commitPerLine
|
229 |
|
230 | Returns an object where for each key (filename) there is a list of commits for each line.
|
231 |
|
232 | * list of filenames
|
233 |
|
234 | ```js
|
235 | var perLine = require('ggit').commitPerLine;
|
236 | perLine(['foo.js', 'bar.js']).then(function (result) {
|
237 | /*
|
238 | {
|
239 | 'foo.js': [{
|
240 | commit: '3c6b01eb3c96db1cbdf277904545107ef97cbb56',
|
241 | author: 'Gleb Bahmutov',
|
242 | committer: 'Gleb Bahmutov',
|
243 | summary: 'cool commit',
|
244 | filename: 'foo.js',
|
245 | line: '// actual source line'
|
246 | },
|
247 | ...
|
248 | }],
|
249 | 'bar.js': [...]
|
250 | }
|
251 | */
|
252 | });
|
253 | ```
|
254 |
|
255 |
|
256 | ### numstat
|
257 |
|
258 | Returns info for a specific commit - number of lines changed, deleted.
|
259 | Same as `$ git show --numstat <id>`.
|
260 |
|
261 | ```js
|
262 | require('ggit')
|
263 | .numstat('5d3ee3')
|
264 | .then(function (result) {
|
265 | /* result is
|
266 | {
|
267 | commit: <full commit SHA>,
|
268 | author:
|
269 | message:
|
270 | date:
|
271 | changes: {
|
272 | 'filename 1': {
|
273 | filename: 'filename 1',
|
274 | added: 10,
|
275 | deleted: 3
|
276 | },
|
277 | ...
|
278 | }
|
279 | }
|
280 | */
|
281 | })
|
282 | .done();
|
283 | ```
|
284 |
|
285 |
|
286 | ### lastCommitId
|
287 |
|
288 | Returns last commit id
|
289 |
|
290 | ```js
|
291 | require('ggit')
|
292 | .lastCommitId()
|
293 | .then(function (str) {
|
294 | // str is full SHA id string
|
295 | })
|
296 | .done();
|
297 | ```
|
298 |
|
299 | You can pass options object as in `lastCommitId(options)` where
|
300 |
|
301 | * **file** - save id into the JSON file with the given `file` name.
|
302 |
|
303 |
|
304 | ### branchName
|
305 |
|
306 | Resolves with the current branch name
|
307 |
|
308 | ```js
|
309 | require('ggit').branchName()
|
310 | .then(function (name) {
|
311 | // name = "master" or whatever
|
312 | });
|
313 | ```
|
314 |
|
315 |
|
316 | ### changedFiles
|
317 |
|
318 | Returns list of modified files
|
319 |
|
320 | ```javascript
|
321 | var changedFiles = require('ggit').changedFiles;
|
322 | changedFiles()
|
323 | .then(function (files) {})
|
324 | .catch(function (error) {});
|
325 | ```
|
326 |
|
327 | The object `files` groups filenames by modification property
|
328 |
|
329 | ```js
|
330 | {
|
331 | A: [...], // list of added files
|
332 | C: [...], // list of copied files
|
333 | M: [...], // list of modified files
|
334 | D: [...] // list of deleted files
|
335 | }
|
336 | // each item in the list is
|
337 | {
|
338 | diff: 'A' // or C, M, D
|
339 | name: 'src/something.js' // relative to the repo root
|
340 | filename: 'full path',
|
341 | before: 'file contents', // if available (for example M, D)
|
342 | after: 'file contents' // if available (for A, M)
|
343 | }
|
344 | ```
|
345 |
|
346 | This is a wrapper around two commands `git diff --name-status --diff-filter=ACMD`
|
347 | and `git status --porcelain`
|
348 |
|
349 |
|
350 |
|
351 |
|
352 | ### changedFilesAfter
|
353 |
|
354 | Returns list of unique files modified / added / deleted after given commit.
|
355 | The commits are limited to specific branch (usually "master") to avoid mixing
|
356 | up multiple branches.
|
357 |
|
358 | ```javascript
|
359 | var changedFilesAfter = require('ggit').changedFilesAfter;
|
360 | changedFilesAfter('a12f55f', 'master')
|
361 | .then(console.log)
|
362 | .catch(console.error);
|
363 | /*
|
364 | something like
|
365 | [ 'README.md',
|
366 | 'docs/commits.md',
|
367 | 'src/commits.js',
|
368 | 'src/get-one-line-log.js',
|
369 | 'package.json',
|
370 | 'src/last-commit-id.js' ]
|
371 | */
|
372 | ```
|
373 |
|
374 |
|
375 | ### fileContents
|
376 |
|
377 | Returns the contents of a file at some point
|
378 |
|
379 | * filename - full or partial filename (from the repo's root)
|
380 | * at (optional) - checkpoint, HEAD by default
|
381 |
|
382 | ```javascript
|
383 | var fileContents = require('ggit').fileContents;
|
384 | fileContents(filename).then(function (text) { ... });
|
385 | ```
|
386 |
|
387 | Same as `git show <at>:<name>`
|
388 |
|
389 |
|
390 |
|
391 | ### commitMessage
|
392 |
|
393 | Returns the contents of the Git current commit message,
|
394 | usually for validation before the commit.
|
395 |
|
396 | ```js
|
397 | require('ggit').commitMessage()
|
398 | .then(function (text) {
|
399 | // do something with the message
|
400 | },
|
401 | function () {
|
402 | // file not found
|
403 | });
|
404 | ```
|
405 |
|
406 |
|
407 | ### getGitFolder
|
408 |
|
409 | Returns the root folder, equivalent to command
|
410 | line `git rev-parse --show-toplevel`
|
411 |
|
412 | ```javascript
|
413 | require('ggit').getGitFolder()
|
414 | .then(folder => {
|
415 | ...
|
416 | })
|
417 | ```
|
418 |
|
419 |
|
420 | ### tags
|
421 |
|
422 | > Requires `git` >= 2.0
|
423 |
|
424 | Returns list of tags in the given folder, including commit ids.
|
425 |
|
426 | ```js
|
427 | var tags = require('ggit').tags;
|
428 | tags().then(function (list) {
|
429 | /*
|
430 | each object in list is like
|
431 | {
|
432 | "commit": "7756b5609c5aae651f267fa3fc00763bcd276bf6",
|
433 | "tag": "v1.3.0"
|
434 | }
|
435 | */
|
436 | })
|
437 | ```
|
438 | You can return just tags that start with "v" by passing
|
439 | `true` to `tags`.
|
440 |
|
441 | ```js
|
442 | tags(true).then(function (list) {...})
|
443 | ```
|
444 |
|
445 | Oldest tag is returns as first object, latest tag is the
|
446 | last object in the list.
|
447 |
|
448 | If you have older `git` (like Travis does), it will automatically try to
|
449 | grab all the tags and sort them using
|
450 | [semantic sort](https://github.com/semver/node-semver#comparison)
|
451 |
|
452 | ### branchTags
|
453 |
|
454 | > Requires `git` >= 2.7
|
455 |
|
456 | Similar to `tags`, `branchTags` returns tags in the given folder,
|
457 | but only those tags accessible from the current branch. Any tags
|
458 | in the repository that point to a commit on another branch will
|
459 | not be returned by `branchTags`.
|
460 |
|
461 | ```js
|
462 | var branchTags = require('ggit').branchTags;
|
463 | branchTags().then(function (list) {
|
464 | /*
|
465 | each object in list is like
|
466 | {
|
467 | "commit": "7756b5609c5aae651f267fa3fc00763bcd276bf6",
|
468 | "tag": "v1.3.0"
|
469 | }
|
470 | */
|
471 | })
|
472 | ```
|
473 | You can return just tags that start with "v" by passing
|
474 | `true` to `branchTags`.
|
475 |
|
476 | ```js
|
477 | branchTags(true).then(function (list) {...})
|
478 | ```
|
479 |
|
480 | Oldest tag is returned as first object, latest tag is the
|
481 | last object in the list.
|
482 |
|
483 |
|
484 | ### fetchTags
|
485 |
|
486 | Fetches remote tags from origin.
|
487 |
|
488 | ```js
|
489 | var fetchTags = require('ggit').fetchTags;
|
490 | fetchTags().then(function () {
|
491 | // should be same as running command
|
492 | // git pull origin --tags
|
493 | })
|
494 | ```
|
495 |
|
496 | You can pass the branch name, by default will fetch
|
497 | from master
|
498 |
|
499 | ```js
|
500 | fetchTags('development')
|
501 | ```
|
502 |
|
503 |
|
504 |
|
505 |
|
506 |
|
507 | ## Development
|
508 |
|
509 | Edit source, run unit tests, run end to end tests and push the code
|
510 | back to Github. The NPM publishing happens automatically using
|
511 | [semantic release](https://github.com/semantic-release/semantic-release)
|
512 |
|
513 | ```sh
|
514 | npm test
|
515 | npm run commit
|
516 | git push
|
517 | ```
|
518 |
|
519 | To debug problems, run the command with `DEBUG=ggit` environment variable enabled
|
520 | to see verbose logging.
|
521 |
|
522 | ### Unit tests
|
523 |
|
524 | Some of the unit tests rely on extracting private functions from modules
|
525 | using [scope magic with `describe-it`](https://github.com/bahmutov/describe-it),
|
526 | which requires Node 4.2.2 and might not work with later Node versions.
|
527 |
|
528 | ### Related projects
|
529 |
|
530 | * [npm-utils](https://github.com/bahmutov/npm-utils) - small utils for working
|
531 | with NPM commands.
|
532 |
|
533 |
|
534 |
|
535 | ### Small print
|
536 |
|
537 | Author: Gleb Bahmutov © 2015
|
538 |
|
539 | * [@bahmutov](https://twitter.com/bahmutov)
|
540 | * [glebbahmutov.com](http://glebbahmutov.com)
|
541 | * [blog](http://glebbahmutov.com/blog/)
|
542 |
|
543 | License: [MIT](LICENSE) - do anything with the code, but don't blame uTest if it does not work.
|
544 |
|
545 | Spread the word: tweet, star on github, etc.
|
546 |
|
547 | Support: if you find any problems with this module, email / tweet / open
|
548 | [issue on Github](https://github.com/bahmutov/ggit/issues)
|
549 |
|
550 |
|