@jaspermayone.com's dotfiles
at main 430 lines 12 kB view raw
1{ 2 lib, 3 config, 4 pkgs, 5 isDarwin, 6 ... 7}: 8with lib; 9{ 10 options.jsp.git = { 11 enable = mkEnableOption "Git configuration"; 12 }; 13 14 config = mkIf config.jsp.git.enable { 15 programs.git = { 16 enable = true; 17 lfs.enable = true; 18 19 # Conditional includes for different work contexts 20 includes = [ 21 { 22 path = pkgs.writeText "gitconfig-phishdir" '' 23 [user] 24 email = jasper.mayone@phish.directory 25 ''; 26 condition = "gitdir/i:~/dev/projects/phishdirectory/**"; 27 } 28 { 29 path = pkgs.writeText "gitconfig-singlefeather" '' 30 [user] 31 email = jasper.mayone@singlefeather.com 32 ''; 33 condition = "gitdir/i:~/dev/work/singlefeather/**"; 34 } 35 { 36 path = pkgs.writeText "gitconfig-mlh" '' 37 [user] 38 email = jasper.mayone@majorleaguehacking.com 39 ''; 40 condition = "gitdir/i:~/dev/work/mlh/eng/**"; 41 } 42 { 43 path = pkgs.writeText "gitconfig-patchwork" '' 44 [user] 45 email = jasper@patchworklabs.org 46 ''; 47 condition = "gitdir/i:~/dev/patchwork/**"; 48 } 49 { 50 path = pkgs.writeText "gitconfig-school" '' 51 [user] 52 email = mayonej@wit.edu 53 ''; 54 condition = "gitdir/i:~/dev/school/**"; 55 } 56 { 57 path = pkgs.writeText "gitconfig-personal" '' 58 [user] 59 email = me@jaspermayone.com 60 ''; 61 condition = "gitdir/i:~/dev/personal/**"; 62 } 63 ]; 64 65 # Global git ignore 66 ignores = [ 67 # Compiled source 68 "*.com" 69 "*.class" 70 "*.dll" 71 "*.exe" 72 "*.o" 73 "*.so" 74 75 # Packages 76 "*.7z" 77 "*.dmg" 78 "*.gz" 79 "*.iso" 80 "*.jar" 81 "*.rar" 82 "*.tar" 83 "*.zip" 84 85 # Logs 86 "*.log" 87 88 # OS generated files 89 ".DS_Store" 90 ".DS_Store?" 91 "*/.DS_Store" 92 "**/.DS_Store" 93 "._*" 94 ".Spotlight-V100" 95 ".Trashes" 96 "ehthumbs.db" 97 "Thumbs.db" 98 99 # Claude Code 100 ".llm-orc/*" 101 "CLAUDE.local.md" 102 ]; 103 104 settings = { 105 alias = { 106 # Quick shortcuts 107 co = "checkout"; 108 br = "branch"; 109 ci = "commit"; 110 st = "status"; 111 unstage = "reset HEAD --"; 112 last = "log -1 HEAD"; 113 pushfwl = "push --force-with-lease --force-if-includes"; 114 115 # View abbreviated SHA, description, and history graph of the latest 20 commits 116 l = "log --pretty=oneline -n 20 --graph --abbrev-commit"; 117 lg = "log --oneline --graph --decorate"; 118 119 # View the current working tree status using the short format 120 s = "status -s"; 121 122 # Show the diff between the latest commit and the current state 123 d = "!git diff-index --quiet HEAD -- || clear; git --no-pager diff --patch-with-stat"; 124 125 # `git di $number` shows the diff between the state `$number` revisions ago and the current state 126 di = "!d() { git diff --patch-with-stat HEAD~$1; }; git diff-index --quiet HEAD -- || clear; d"; 127 128 # Pull in remote changes for the current repository and all its submodules 129 p = "pull --recurse-submodules"; 130 131 # Clone a repository including all submodules 132 c = "clone --recursive"; 133 134 # Commit all changes 135 ca = "!git add ':(exclude,attr:builtin_objectmode=160000)' && git commit -av"; 136 137 # Switch to a branch, creating it if necessary 138 go = "!f() { git checkout -b \"$1\" 2> /dev/null || git checkout \"$1\"; }; f"; 139 140 # Show verbose output about tags, branches or remotes 141 tags = "tag -l"; 142 branches = "branch --all"; 143 remotes = "remote --verbose"; 144 145 # List aliases 146 aliases = "config --get-regexp alias"; 147 148 # Amend the currently staged files to the latest commit 149 amend = "commit --amend --reuse-message=HEAD"; 150 151 # Credit an author on the latest commit 152 credit = "!f() { git commit --amend --author \"$1 <$2>\" -C HEAD; }; f"; 153 154 # Interactive rebase with the given number of latest commits 155 reb = "!r() { git rebase -i HEAD~$1; }; r"; 156 157 # Remove the old tag with this name and tag the latest commit with it 158 retag = "!r() { git tag -d $1 && git push origin :refs/tags/$1 && git tag $1; }; r"; 159 160 # Find branches containing commit 161 fb = "!f() { git branch -a --contains $1; }; f"; 162 163 # Find tags containing commit 164 ft = "!f() { git describe --always --contains $1; }; f"; 165 166 # Find commits by source code 167 fc = "!f() { git log --pretty=format:'%C(yellow)%h %Cblue%ad %Creset%s%Cgreen [%cn] %Cred%d' --decorate --date=short -S$1; }; f"; 168 169 # Find commits by commit message 170 fm = "!f() { git log --pretty=format:'%C(yellow)%h %Cblue%ad %Creset%s%Cgreen [%cn] %Cred%d' --decorate --date=short --grep=$1; }; f"; 171 172 # Remove branches that have already been merged with main (a.k.a. 'delete merged') 173 dm = "!git branch --merged | grep -v '\\\\*' | xargs -n 1 git branch -d"; 174 175 # List contributors with number of commits 176 contributors = "shortlog --summary --numbered"; 177 178 # Show the user email for the current repository 179 whoami = "config user.email"; 180 }; 181 182 user = { 183 name = "Jasper Mayone"; 184 email = "me@jaspermayone.com"; 185 signingKey = "14D0D45A1DADAAFA"; 186 }; 187 188 init.defaultBranch = "main"; 189 190 apply.whitespace = "fix"; 191 192 core = { 193 editor = "code --wait"; 194 pager = "less"; 195 # Treat spaces before tabs and trailing whitespace as errors 196 whitespace = "space-before-tab,-indent-with-non-tab,trailing-space"; 197 # Make `git rebase` safer on macOS 198 trustctime = false; 199 # Prevent showing files with non-ASCII names as unversioned 200 precomposeunicode = false; 201 # Speed up commands involving untracked files 202 untrackedCache = true; 203 }; 204 205 color = { 206 ui = "auto"; 207 branch = { 208 current = "yellow reverse"; 209 local = "yellow"; 210 remote = "green"; 211 }; 212 }; 213 214 diff = { 215 algorithm = "histogram"; 216 tool = "windsurf"; 217 renames = "copies"; # Detect copies as well as renames 218 }; 219 220 "difftool \"windsurf\"".cmd = "windsurf --diff $LOCAL $REMOTE"; 221 222 # Binary file diff using hexdump 223 "diff \"bin\"".textconv = "hexdump -v -C"; 224 225 # Bun lockfile diff 226 "diff \"lockb\"" = { 227 textconv = "bun"; 228 binary = true; 229 }; 230 231 # Include summaries of merged commits in merge commit messages 232 merge.log = true; 233 234 pull.rebase = true; 235 236 push = { 237 default = "simple"; 238 followTags = true; 239 autoSetupRemote = true; 240 }; 241 242 rebase.autoStash = true; 243 244 status = { 245 submoduleSummary = true; 246 showUntrackedFiles = "all"; 247 }; 248 249 tag = { 250 sort = "version:refname"; 251 forceSignAnnotated = true; 252 gpgsign = true; 253 }; 254 255 versionsort = { 256 prereleaseSuffix = [ 257 "-pre" 258 ".pre" 259 "-beta" 260 ".beta" 261 "-rc" 262 ".rc" 263 ]; 264 }; 265 266 commit.gpgSign = true; 267 268 gpg = { 269 program = if isDarwin then "/opt/homebrew/bin/gpg" else "gpg"; 270 format = "openpgp"; 271 }; 272 273 help.autocorrect = 1; 274 275 # URL shorthands 276 "url \"git@github.com:\"" = { 277 insteadOf = "gh:"; 278 pushInsteadOf = "https://github.com/"; 279 }; 280 "url \"git://github.com/\"".insteadOf = "github:"; 281 "url \"git@gist.github.com:\"".insteadOf = "gst:"; 282 "url \"git://gist.github.com/\"".insteadOf = "gist:"; 283 284 sequence.editor = "code --wait"; 285 286 branch.sort = "-committerdate"; 287 column.ui = "auto"; 288 } 289 // ( 290 if isDarwin then 291 { 292 # macOS specific 293 credential = { 294 helper = "osxkeychain"; 295 }; 296 "credential \"https://dev.azure.com\"".useHttpPath = true; 297 } 298 else 299 { } 300 ); 301 }; 302 303 # Delta for better diffs 304 programs.delta = { 305 enable = true; 306 options = { 307 navigate = true; 308 light = false; 309 line-numbers = true; 310 }; 311 }; 312 313 # GitHub CLI 314 programs.gh = { 315 enable = true; 316 settings = { 317 git_protocol = "ssh"; 318 }; 319 }; 320 321 # Lazygit 322 programs.lazygit = { 323 enable = true; 324 settings = { 325 gui.theme = { 326 lightTheme = false; 327 activeBorderColor = [ 328 "blue" 329 "bold" 330 ]; 331 inactiveBorderColor = [ "black" ]; 332 selectedLineBgColor = [ "default" ]; 333 }; 334 }; 335 }; 336 337 # GitHub Dashboard 338 programs.gh-dash = { 339 enable = true; 340 settings = { 341 prSections = [ 342 { 343 title = "Mine"; 344 filters = "is:open author:@me updated:>={{ nowModify \"-3w\" }} sort:updated-desc archived:false"; 345 layout.author.hidden = true; 346 } 347 { 348 title = "Review"; 349 filters = "sort:updated-desc is:pr is:open review-requested:jaspermayone archived:false"; 350 } 351 { 352 title = "All"; 353 filters = "sort:updated-desc is:pr is:open user:@me archived:false"; 354 } 355 ]; 356 issuesSections = [ 357 { 358 title = "Assigned"; 359 filters = "is:issue state:open archived:false assignee:@me sort:updated-desc"; 360 } 361 { 362 title = "Created"; 363 filters = "author:@me is:open archived:false"; 364 } 365 { 366 title = "All"; 367 filters = "is:issue involves:@me archived:false sort:updated-desc is:open"; 368 } 369 ]; 370 defaults = { 371 view = "prs"; 372 refetchIntervalMinutes = 5; 373 layout.prs = { 374 repoName = { 375 grow = true; 376 width = 10; 377 hidden = false; 378 }; 379 base.hidden = true; 380 }; 381 preview = { 382 open = true; 383 width = 84; 384 }; 385 prsLimit = 20; 386 issuesLimit = 20; 387 }; 388 repoPaths = { 389 "jaspermayone/*" = "~/dev/personal/*"; 390 "phishdirectory/*" = "~/dev/projects/phishdirectory/*"; 391 }; 392 keybindings = { 393 universal = [ 394 { 395 key = "g"; 396 name = "lazygit"; 397 command = "cd {{.RepoPath}} && lazygit"; 398 } 399 ]; 400 prs = [ 401 { 402 key = "O"; 403 builtin = "checkout"; 404 } 405 { 406 key = "m"; 407 command = "gh pr merge --admin --repo {{.RepoName}} {{.PrNumber}}"; 408 } 409 { 410 key = "a"; 411 name = "lazygit add"; 412 command = "cd {{.RepoPath}} && git add -A && lazygit"; 413 } 414 { 415 key = "v"; 416 name = "approve"; 417 command = "gh pr review --repo {{.RepoName}} --approve --body \"$(gum input --prompt='Approval Comment: ')\" {{.PrNumber}}"; 418 } 419 ]; 420 }; 421 theme = { 422 ui = { 423 sectionsShowCount = true; 424 table.compact = false; 425 }; 426 }; 427 }; 428 }; 429 }; 430}