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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ vendor/

# Generated docs
web/docs/*.html
!web/docs/_template.html
web/docs/commands/*.html

# Agents
Expand Down
2 changes: 1 addition & 1 deletion .task/checksum/docs
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1a9df1fa4242917b6b61be3f7a0b62e
b47ffd14ec64cbc5fff8aa93bffea04d
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ hourgit version
## Table of Contents

- [Commands](#commands)
- [Time Tracking](#time-tracking) — init, log, edit, remove, checkout, report, history
- [Time Tracking](#time-tracking) — init, log, edit, remove, sync, report, history
- [Project Management](#project-management) — project add/assign/list/remove
- [Schedule Configuration](#schedule-configuration) — config get/set/reset/report
- [Default Schedule](#default-schedule) — defaults get/set/reset/report
Expand All @@ -106,7 +106,7 @@ hourgit version

Core commands for recording, viewing, and managing your time entries.

Commands: `init` · `log` · `edit` · `remove` · `checkout` · `report` · `history`
Commands: `init` · `log` · `edit` · `remove` · `sync` · `report` · `history`

#### `hourgit init`

Expand Down Expand Up @@ -204,18 +204,16 @@ hourgit remove <hash> [--project <name>] [--yes]

> Works with both log and checkout entries (unlike `edit`, which only supports log entries). Shows entry details and asks for confirmation before deleting. If the entry is not found in the current repo's project, all projects are searched.

#### `hourgit checkout`
#### `hourgit sync`

Record a branch checkout event. Called internally by the post-checkout git hook to track branch transitions.
Sync branch checkouts from git reflog. Called automatically by the post-checkout hook, or run manually to backfill history.

```bash
hourgit checkout --prev <branch> --next <branch> [--project <name>]
hourgit sync [--project <name>]
```

| Flag | Default | Description |
|------|---------|-------------|
| `--prev` | — | Previous branch name (required) |
| `--next` | — | Next branch name (required) |
| `--project` | auto-detect | Project name or ID |

#### `hourgit report`
Expand Down Expand Up @@ -480,6 +478,8 @@ hourgit completion generate fish | source

### Other

Commands: `version` · `update`

#### `hourgit version`

Print version information.
Expand All @@ -490,6 +490,16 @@ hourgit version

No flags.

#### `hourgit update`

Check for and install updates. Always checks the latest version from GitHub, bypassing the cache TTL used by the automatic update check.

```bash
hourgit update
```

No flags.

## Configuration

Hourgit uses a schedule system to define working hours. The factory default is **Monday-Friday, 9 AM - 5 PM**.
Expand Down
73 changes: 0 additions & 73 deletions internal/cli/checkout.go

This file was deleted.

135 changes: 0 additions & 135 deletions internal/cli/checkout_test.go

This file was deleted.

10 changes: 2 additions & 8 deletions internal/cli/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,10 @@ func hookScript(binPath, version string) string {
# Only act on branch checkouts (flag=1), skip file checkouts (flag=0)
[ "$3" = "0" ] && exit 0

# Skip if old and new HEAD are the same SHA (e.g. pull, fetch, rebase)
# Skip if old and new HEAD are the same SHA (e.g. pull, fetch)
[ "$1" = "$2" ] && exit 0

# Resolve branch names from SHA refs provided by git ($1=prev HEAD, $2=new HEAD)
PREV=$(git name-rev --name-only --refs='refs/heads/*' "$1" 2>/dev/null)
NEXT=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)

if [ -n "$PREV" ] && [ -n "$NEXT" ] && [ "$PREV" != "$NEXT" ]; then
%s checkout --prev "$PREV" --next "$NEXT" 2>/dev/null || true
fi
%s sync --skip-updates 2>/dev/null || true
`, project.HookMarker, version, binPath)
}

Expand Down
11 changes: 7 additions & 4 deletions internal/cli/init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func TestInitInGitRepo(t *testing.T) {
require.NoError(t, err)
assert.Contains(t, string(content), project.HookMarker)
assert.Contains(t, string(content), "#!/bin/sh")
assert.Contains(t, string(content), `checkout --prev "$PREV" --next "$NEXT"`)
assert.Contains(t, string(content), "sync")
assert.Contains(t, string(content), `[ "$3" = "0" ] && exit 0`)

info, err := os.Stat(hookPath)
Expand Down Expand Up @@ -377,11 +377,14 @@ func TestHookScript(t *testing.T) {
assert.Contains(t, script, "#!/bin/sh")
assert.Contains(t, script, project.HookMarker)
assert.Contains(t, script, "(version: 1.2.3)")
assert.Contains(t, script, `/usr/local/bin/hourgit checkout --prev "$PREV" --next "$NEXT"`)
assert.Contains(t, script, `/usr/local/bin/hourgit sync --skip-updates`)
assert.Contains(t, script, `[ "$3" = "0" ] && exit 0`)
assert.Contains(t, script, `[ "$1" = "$2" ] && exit 0`)
assert.Contains(t, script, `git name-rev --name-only --refs='refs/heads/*'`)
assert.Contains(t, script, `git rev-parse --abbrev-ref HEAD`)
assert.NotContains(t, script, `checkout --prev`)
assert.NotContains(t, script, `git name-rev`)
assert.NotContains(t, script, `git symbolic-ref`)
assert.NotContains(t, script, `git rev-parse --git-dir`)
assert.NotContains(t, script, `rebase-merge`)
}

func TestInitRegistered(t *testing.T) {
Expand Down
8 changes: 7 additions & 1 deletion internal/cli/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,26 @@ func newRootCmd() *cobra.Command {
logCmd,
editCmd,
removeCmd,
checkoutCmd,
syncCmd,
reportCmd,
historyCmd,
versionCmd,
projectCmd,
configCmd,
defaultsCmd,
completionCmd,
updateCmd,
},
}.Build()
cmd.SilenceUsage = true
cmd.SilenceErrors = true
cmd.CompletionOptions.DisableDefaultCmd = true
cmd.SetHelpFunc(colorizedHelpFunc())
cmd.PersistentFlags().Bool("skip-updates", false, "skip the automatic update check")
cmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error {
checkForUpdate(cmd, defaultUpdateDeps())
return nil
}
return cmd
}

Expand Down
Loading