Skip to content

Commit 5424676

Browse files
authored
Merge pull request #8 from ashishxcode/feat/commit-branch-and-project-filter
feat: per-commit branch label and --project filter
2 parents 5409af3 + 75c1975 commit 5424676

6 files changed

Lines changed: 67 additions & 12 deletions

File tree

‎cmd/commit-chronicle/main.go‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ func parseFlags() (*app.Config, error) {
4848
fs.StringVar(&c.User, "user", "", "GitHub login for PR discovery (default: gh user)")
4949
fs.StringVar(&c.Repos, "repos", "", "comma-separated repo paths (overrides config)")
5050
fs.StringVar(&c.Root, "root", "", "comma-separated dirs to scan for git repos, e.g. ~/work")
51+
fs.StringVar(&c.Project, "project", "", "limit to the repo with this name, e.g. saas-super-admin")
5152
fs.StringVar(&c.Out, "out", "", "output path (default: Downloads, timestamped)")
5253
fs.StringVar(&c.Format, "format", "md", "output format: md | json")
5354
fs.BoolVar(&c.NoEdit, "no-edit", false, "skip the editor step")
@@ -90,6 +91,7 @@ OPTIONS:
9091
--user <login> GitHub login for PR discovery (default: gh user)
9192
--repos a,b,c comma-separated repo paths (overrides config)
9293
--root ~/work comma-separated dirs to auto-discover git repos under
94+
--project <name> limit to the repo with this name, e.g. saas-super-admin
9395
--out <path> output path (default: Downloads, timestamped)
9496
--format md|json output format (default: md)
9597
--all select everything (skip the picker)

‎internal/app/app.go‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
type Config struct {
2323
Since, From, To, Month, Date string
2424
Author, User, Repos, Root string
25+
Project string // limit to the repo with this name
2526
Out, Format string
2627
NoEdit, All, Copy, NoPR bool
2728
Setup bool // force the guided first-run setup
@@ -62,6 +63,15 @@ func Run(c Config) error {
6263
return err
6364
}
6465

66+
// --project narrows the resolved set to a single repo by name, so the
67+
// worklog (and every commit's project/branch labelling) covers just it.
68+
if c.Project != "" {
69+
repos = filterByProject(repos, c.Project)
70+
if len(repos) == 0 {
71+
return fmt.Errorf("no configured repo named %q (matched against repo directory names)", c.Project)
72+
}
73+
}
74+
6575
author := c.Author
6676
if author == "" {
6777
author = gitConfigName(repos[0])
@@ -229,6 +239,19 @@ func resolveRange(c Config, interactive bool) (model.Range, error) {
229239
return model.Preset(model.PresetNames[idx]), nil
230240
}
231241

242+
// filterByProject keeps only the repos whose directory name matches project
243+
// (case-insensitive), letting the user scope a worklog to one project.
244+
func filterByProject(repos []string, project string) []string {
245+
want := strings.ToLower(strings.TrimSpace(project))
246+
var out []string
247+
for _, r := range repos {
248+
if strings.ToLower(filepath.Base(r)) == want {
249+
out = append(out, r)
250+
}
251+
}
252+
return out
253+
}
254+
232255
func splitCSV(s string) []string {
233256
if s == "" {
234257
return nil

‎internal/collect/git.go‎

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ func gitCommits(repos []string, author string, r model.Range) []model.Item {
5454
name := filepath.Base(repo)
5555
base := originURL(repo)
5656

57+
// --source tags each commit with the ref it was reached from, exposed via
58+
// %S; with --all that's the branch (or remote/tag) carrying the commit.
5759
args := []string{
58-
"-C", repo, "log", "--all", "--no-merges",
60+
"-C", repo, "log", "--all", "--source", "--no-merges",
5961
"--author=" + author, "--regexp-ignore-case",
6062
"--date=short",
61-
"--pretty=format:%h" + fieldSep + "%H" + fieldSep + "%ad" + fieldSep + "%s",
63+
"--pretty=format:%h" + fieldSep + "%H" + fieldSep + "%ad" + fieldSep + "%S" + fieldSep + "%s",
6264
}
6365
if r.Since != "" {
6466
args = append(args, "--since="+anchorMidnight(r.Since))
@@ -75,11 +77,11 @@ func gitCommits(repos []string, author string, r model.Range) []model.Item {
7577
if strings.TrimSpace(line) == "" {
7678
continue
7779
}
78-
p := strings.SplitN(line, fieldSep, 4)
79-
if len(p) != 4 {
80+
p := strings.SplitN(line, fieldSep, 5)
81+
if len(p) != 5 {
8082
continue
8183
}
82-
if isNoiseSubject(p[3]) {
84+
if isNoiseSubject(p[4]) {
8385
continue
8486
}
8587
url := ""
@@ -94,13 +96,34 @@ func gitCommits(repos []string, author string, r model.Range) []model.Item {
9496
URL: url,
9597
Hash: p[1],
9698
ShortHash: p[0],
97-
Title: model.CleanText(p[3]),
99+
Branch: shortRef(p[3]),
100+
Title: model.CleanText(p[4]),
98101
})
99102
}
100103
}
101104
return items
102105
}
103106

107+
// shortRef turns a fully-qualified git ref (as emitted by %S under --source)
108+
// into a bare branch name: refs/heads/x → x, refs/remotes/origin/x → x,
109+
// refs/tags/x → x. Anything else is returned trimmed and unchanged.
110+
func shortRef(ref string) string {
111+
ref = strings.TrimSpace(ref)
112+
switch {
113+
case strings.HasPrefix(ref, "refs/heads/"):
114+
return strings.TrimPrefix(ref, "refs/heads/")
115+
case strings.HasPrefix(ref, "refs/remotes/"):
116+
rest := strings.TrimPrefix(ref, "refs/remotes/")
117+
if i := strings.IndexByte(rest, '/'); i >= 0 {
118+
return rest[i+1:] // drop the remote name, keep the branch
119+
}
120+
return rest
121+
case strings.HasPrefix(ref, "refs/tags/"):
122+
return strings.TrimPrefix(ref, "refs/tags/")
123+
}
124+
return ref
125+
}
126+
104127
// Preview returns `git show --stat` for a commit item (for the picker pane).
105128
func Preview(it model.Item) string {
106129
if it.Kind != model.KindCommit || it.Hash == "" {

‎internal/model/item.go‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ type Item struct {
3636
// Commit-only
3737
Hash string
3838
ShortHash string
39+
Branch string // branch the commit was reached from (git source ref)
3940

4041
// PR/Review-only
4142
Number int

‎internal/render/render.go‎

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,13 @@ func line(it model.Item) string {
9898
return fmt.Sprintf("- %s **%s** %s — %s (PR %s) · %s\n",
9999
icon, link(it.Ref()), it.Title, verdict, strings.ToLower(it.State), it.RepoName)
100100
default: // commit
101-
return fmt.Sprintf("- %s _(%s)_\n", it.Title,
102-
link(it.RepoName+"@"+it.ShortHash))
101+
// Lead with the branch (linked to the commit) and keep the repo as
102+
// context; fall back to repo@hash when the branch is unknown.
103+
meta := link(it.RepoName + "@" + it.ShortHash)
104+
if it.Branch != "" {
105+
meta = link(it.Branch) + " · " + it.RepoName
106+
}
107+
return fmt.Sprintf("- %s _(%s)_\n", it.Title, meta)
103108
}
104109
}
105110

@@ -122,6 +127,7 @@ type jsonItem struct {
122127
Kind string `json:"kind"`
123128
Date string `json:"date"`
124129
Repo string `json:"repo"`
130+
Branch string `json:"branch,omitempty"`
125131
Title string `json:"title"`
126132
URL string `json:"url"`
127133
Hash string `json:"hash,omitempty"`
@@ -135,7 +141,7 @@ func JSON(items []model.Item, _ Meta) string {
135141
out := make([]jsonItem, 0, len(items))
136142
for _, it := range items {
137143
out = append(out, jsonItem{
138-
Kind: it.Tag(), Date: it.Date, Repo: it.RepoName,
144+
Kind: it.Tag(), Date: it.Date, Repo: it.RepoName, Branch: it.Branch,
139145
Title: it.Title, URL: it.URL, Hash: it.Hash,
140146
Number: it.Number, State: it.State, ReviewState: it.ReviewState,
141147
})

‎internal/tui/picker.go‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ func (m *pickerModel) applyFilter() {
8484
tokens := strings.Fields(q)
8585
m.filtered = m.filtered[:0]
8686
for i, it := range m.items {
87-
hay := strings.ToLower(it.Tag() + " " + it.Date + " " + it.RepoName + " " + it.Ref() + " " + it.Title)
87+
hay := strings.ToLower(it.Tag() + " " + it.Date + " " + it.RepoName + " " + it.Branch + " " + it.Ref() + " " + it.Title)
8888
ok := true
8989
for _, t := range tokens {
9090
if !strings.Contains(hay, t) {
@@ -254,8 +254,8 @@ func (m pickerModel) View() string {
254254
box = "[x]"
255255
}
256256
// Plain text first so truncation counts real characters.
257-
line := fmt.Sprintf("%s %-7s %s %-16s %-8s %s",
258-
box, it.Tag(), it.Date, truncate(it.RepoName, 16), it.Ref(), it.Title)
257+
line := fmt.Sprintf("%s %-7s %s %-16s %-8s %-14s %s",
258+
box, it.Tag(), it.Date, truncate(it.RepoName, 16), it.Ref(), truncate(it.Branch, 14), it.Title)
259259
line = truncate(line, listW-3)
260260
switch {
261261
case i == m.cursor:

0 commit comments

Comments
 (0)