Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions documentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

### limitations

Time tracking for other work items (e.g. tasks, epics) is currently not supported. See also https://github.com/ndu2/gitlab-time-tracker/issues/33
Time tracking and reporting for epics is currently not supported.

## requirements

Expand Down Expand Up @@ -58,7 +58,7 @@ token: 01234567891011

You will need node and npm to build the project

* [node.js](https://nodejs.org/en/download) version >= 22
* [node.js](https://nodejs.org/en/download) version 24
* [npm](https://github.com/npm/npm)


Expand Down Expand Up @@ -197,6 +197,15 @@ gtt edit

You can omit the id to edit to bring up a list of the latest records to choose from.


**In-Console edit records**

```shell
gtt edit -i
gtt edit -i --this_week
gtt edit -i --week 2026-02-04
```

**Delete a local time record by the given id:**

```shell
Expand Down Expand Up @@ -389,7 +398,7 @@ gtt report --no_warnings
gtt report --date_format="DD.MM.YYYY HH:mm:ss"
```

*Note: [Click here](http://momentjs.com/docs/#/displaying/format/) for a further documentation on the date format.*
*Note: [Click here](https://day.js.org/docs/en/display/format) for a further documentation on the date format.*

#### Set time format for the report

Expand Down Expand Up @@ -561,20 +570,24 @@ mergeRequestColumns:

# Include the given columns in the time record table
# See --record_columns option for more information
# defaults to user, date, type, iid, time
# defaults to user, date, project, type, iid, title, time, note
recordColumns:
- user
- date
- project
- type
- iid
- title
- time
- note

# Add columns for each project member to issue and
# merge request table, including their total time spent
# defaults to true
userColumns: true

# Date format
# Click here for format options: http://momentjs.com/docs/#/displaying/format/
# Click here for format options: https://day.js.org/docs/en/display/format
# defaults to DD.MM.YYYY HH:mm:ss
dateFormat: DD.MM.YYYY HH:mm:ss

Expand Down
24 changes: 0 additions & 24 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
"author": "kriskbx",
"license": "GPL-2.0",
"dependencies": {
"@inquirer/checkbox": "^5.2.1",
"@inquirer/confirm": "^6.1.1",
"@inquirer/select": "^5.2.1",
"camelcase": "^9.0.0",
Expand Down
6 changes: 3 additions & 3 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ gtt is a fully featured command line interface for GitLab's time tracking featur

## About this fork (ndu2)

Shout-out to kris for creating this handy tool. As we use gtt regularly and maintenance on the original repo stopped, I maintain the code base in the foreseeable future on this fork (versions 1.8.x).
Shout-out to kris for creating this handy tool. As we use gtt regularly and maintenance on the original repo stopped, I maintain the code base in the foreseeable future on this fork (versions 1.8.x and later).

Feel free to contact me or create a Pull Request if you have some patches or proposals.

### Where to get gtt v1.8
### Where to get gtt

There are various options:

Expand All @@ -33,7 +33,7 @@ How to install and use gtt? You can find the documentation [here](documentation.

### limitations

Time tracking for other work items (e.g. tasks, epics) is currently not supported.
Time tracking and reporting for epics is currently not supported.


## license
Expand Down
28 changes: 17 additions & 11 deletions spec/output/base.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@ import Config from '../../src/core/config.js';
import Output from '../../src/reporting/output/base.js';
import calculateStats from '../../src/reporting/stats.js';

function makeTime({ user = 'alice', seconds = 0, date = '2026-01-05T10:00:00Z', iid = 1, project = 'group/project' } = {}) {
return { user, seconds, iid, project_namespace: project, date: dayjs(date) };
function makeTime({ user = 'alice', seconds = 0, date = '2026-01-05T10:00:00Z', iid = 1, project = 'group/project', note = null, chargeRatio = 1.0 } = {}) {
return { user, seconds, iid, project_namespace: project, date: dayjs(date), note, chargeRatio };
}

function makeIssue({ iid = 1, labels = [], times = [], days = {}, estimate = 0, spent = 0 } = {}) {
function makeIssue({ iid = 1, labels = [], times = [], estimate = 0, spent = 0, project_id = 1, title = 'issue' } = {}) {
return {
iid,
project_id,
title,
labels,
times,
days,
stats: { time_estimate: estimate, total_time_spent: spent }
total_spent_s : spent,
total_estimate_s : estimate
};
}

Expand Down Expand Up @@ -104,15 +106,19 @@ describe('calculateStats', () => {
});

it('consolidates per-day data of all issues', () => {
config.set('dateFormatGroupReport', 'YYYY-MM-DD');

const output = calculate([
makeIssue({ iid: 1, days: { '2026-01-01': 'a' } }),
makeIssue({ iid: 2, days: { '2026-01-01': 'b', '2026-01-02': 'c' } })
makeIssue({ iid: 1, times: [makeTime({ date: '2026-01-01T10:00:00Z', note: 'a', seconds: 60 })] }),
makeIssue({ iid: 2, times: [
makeTime({ date: '2026-01-01T10:00:00Z', note: 'b', seconds: 120 }),
makeTime({ date: '2026-01-02T10:00:00Z', note: 'c', seconds: 180 })
] })
]);

expect(output.daysNew).to.deep.equal({
'2026-01-01': ['a', 'b'],
'2026-01-02': ['c']
});
expect(Object.keys(output.daysNew)).to.deep.equal(['2026-01-01', '2026-01-02']);
expect(output.daysNew['2026-01-01'].map(d => d.getNotes())).to.deep.equal([['a'], ['b']]);
expect(output.daysNew['2026-01-02'].map(d => d.getNotes())).to.deep.equal([['c']]);
});
});

Expand Down
2 changes: 1 addition & 1 deletion src/core/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const defaults = {
weeksPerMonth: 4,
issueColumns: ['iid', 'title', 'spent', 'total_estimate'],
mergeRequestColumns: ['iid', 'title', 'spent', 'total_estimate'],
recordColumns: ['user', 'date', 'type', 'iid', 'time'],
recordColumns: ['user', 'date', 'project', 'type', 'iid', 'title', 'time', 'note'],
userColumns: false,
dateFormat: 'DD.MM.YYYY HH:mm:ss',
timeFormat: Time.defaultTimeFormat,
Expand Down
22 changes: 22 additions & 0 deletions src/core/issue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import CoreTask from './task.js';
import GitlabClient from './gitlab-client.js';

class Issue extends CoreTask {
static resoureType = 'issues';

constructor(config, data, client) {
super(config, data, client, Issue.resoureType);
}

static list(config, project, state, my, client = new GitlabClient(config)) {
const query = `scope=${my ? "assigned-to-me" : "all"}&state=${state}`;
const path = project
? `projects/${encodeURIComponent(project)}/${Issue.resoureType}?${query}`
: `${Issue.resoureType}/?${query}`;

return client.get(path)
.then(response => response.body.map(data => new this(config, data, client)));
}
}

export default Issue;
22 changes: 22 additions & 0 deletions src/core/mergeRequest.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import CoreTask from './task.js';
import GitlabClient from './gitlab-client.js';

class MergeRequest extends CoreTask {
static resoureType = 'merge_requests';

constructor(config, data, client) {
super(config, data, client, MergeRequest.resoureType);
}

static list(config, project, state, my, client = new GitlabClient(config)) {
const query = `scope=${my ? "assigned-to-me" : "all"}&state=${state}`;
const path = project
? `projects/${encodeURIComponent(project)}/${MergeRequest.resoureType}?${query}`
: `${MergeRequest.resoureType}}/?${query}`;

return client.get(path)
.then(response => response.body.map(data => new this(config, data, client)));
}
}

export default MergeRequest;
Loading