diff options
author | Minijackson <minijackson@riseup.net> | 2023-02-04 13:42:14 +0100 |
---|---|---|
committer | Minijackson <minijackson@riseup.net> | 2023-02-04 13:42:14 +0100 |
commit | 1be58044a9a17eca349652fcf9db7b0113f663dd (patch) | |
tree | fa12246b86fde3ddea33b5ba2b36c180ee5a3c64 | |
parent | a878ccf4d32a1083c18408f6183c905f18023196 (diff) | |
download | nixos-config-reborn-1be58044a9a17eca349652fcf9db7b0113f663dd.tar.gz nixos-config-reborn-1be58044a9a17eca349652fcf9db7b0113f663dd.zip |
git: setup git-branchless
-rw-r--r-- | common/commandline/git.nix | 10 | ||||
-rw-r--r-- | dotfiles/git-branchless.zsh | 276 |
2 files changed, 285 insertions, 1 deletions
diff --git a/common/commandline/git.nix b/common/commandline/git.nix index 8ccb528..fb9bed2 100644 --- a/common/commandline/git.nix +++ b/common/commandline/git.nix | |||
@@ -3,7 +3,14 @@ inputs: | |||
3 | { config, pkgs, lib, ... }: | 3 | { config, pkgs, lib, ... }: |
4 | 4 | ||
5 | { | 5 | { |
6 | # TODO: add signing | 6 | users.users.minijackson.packages = [pkgs.git-branchless]; |
7 | |||
8 | environment.shellAliases.git = "git-branchless wrap --"; | ||
9 | |||
10 | programs.zsh.interactiveShellInit = '' | ||
11 | source ${../../dotfiles/git-branchless.zsh} | ||
12 | ''; | ||
13 | |||
7 | home-manager.users.minijackson = { ... }: { | 14 | home-manager.users.minijackson = { ... }: { |
8 | programs.git = { | 15 | programs.git = { |
9 | enable = true; | 16 | enable = true; |
@@ -68,6 +75,7 @@ inputs: | |||
68 | }; | 75 | }; |
69 | }; | 76 | }; |
70 | 77 | ||
78 | # TODO: move common to NixOS' programs.git.config | ||
71 | home-manager.users.root = { ... }: { | 79 | home-manager.users.root = { ... }: { |
72 | programs.git = with lib; | 80 | programs.git = with lib; |
73 | mkMerge [ | 81 | mkMerge [ |
diff --git a/dotfiles/git-branchless.zsh b/dotfiles/git-branchless.zsh new file mode 100644 index 0000000..c34b241 --- /dev/null +++ b/dotfiles/git-branchless.zsh | |||
@@ -0,0 +1,276 @@ | |||
1 | local common_options=( | ||
2 | "-C[Change to the given directory before executing the rest of the program]:directory:_path_files -/" | ||
3 | "--color=[Flag to force enable or disable terminal colors]:color flag:(auto always never)" | ||
4 | {-h,--help}"[Print help information]" | ||
5 | ) | ||
6 | |||
7 | _git-branchless() { | ||
8 | local line state ret | ||
9 | |||
10 | _arguments -C \ | ||
11 | "1:Command:->cmds" \ | ||
12 | "*::arg:->args" | ||
13 | |||
14 | case "$state" in | ||
15 | cmds) | ||
16 | _values "git-branchless command" \ | ||
17 | "amend[Amend the current HEAD commit]" \ | ||
18 | "bug-report[Gather information about recent operations to upload as part of a bug report]" \ | ||
19 | "gc[Run internal garbage collection]" \ | ||
20 | "hide[Hide the provided commits from the smartlog]" \ | ||
21 | "init[Initialize the branchless workflow for this repository]" \ | ||
22 | "move[Move a subtree of commits from one location to another]" \ | ||
23 | "next[Move to a later commit in the current stack]" \ | ||
24 | "prev[Move to an earlier commit in the current stack]" \ | ||
25 | "query[Query the commit graph using the \"revset\" language and print matching commits]" \ | ||
26 | "repair[Restore internal invariants by reconciling the internal operation log with the state of the Git repository]" \ | ||
27 | "restack[Fix up commits abandoned by a previous rewrite operation]" \ | ||
28 | "record[Create a commit by interactively selecting which changes to include]" \ | ||
29 | "reword[Reword commits]" \ | ||
30 | "smartlog[Display a nice graph of the commits you've recently worked on]" \ | ||
31 | "submit[Push commits to a remote]" \ | ||
32 | "switch[Switch to the provided branch or commit]" \ | ||
33 | "sync[Move any local commit stacks on top of the main branch]" \ | ||
34 | "test[Run a command on each commit in a given set and aggregate the results]" \ | ||
35 | "undo[Browse or return to a previous state of the repository]" \ | ||
36 | "unhide[Unhide previously-hidden commits from the smartlog]" \ | ||
37 | "wrap[Wrap a Git command inside a branchless transaction]" \ | ||
38 | "help[Print this message or the help of the given subcommand(s)]" | ||
39 | ret=0 | ||
40 | ;; | ||
41 | args) | ||
42 | _call_function ret "_git-branchless-${line[1]}" || _message "no completion for ${line[1]}" | ||
43 | ;; | ||
44 | esac | ||
45 | return ret | ||
46 | } | ||
47 | |||
48 | _git-branchless-amend() { | ||
49 | _arguments -s \ | ||
50 | "${common_options[@]}" \ | ||
51 | {-f,--force-rewrite}"[Force moving public commits, even though other people may have access to those commits]" \ | ||
52 | {-m,--merge}"[Attempt to resolve merge conflicts, if any]" | ||
53 | return 0 | ||
54 | } | ||
55 | |||
56 | _git-branchless-bug-report() { | ||
57 | _arguments -s "${common_options[@]}" | ||
58 | return 0 | ||
59 | } | ||
60 | |||
61 | _git-branchless-gc() { | ||
62 | _arguments -s "${common_options[@]}" | ||
63 | return 0 | ||
64 | } | ||
65 | |||
66 | _git-branchless-hide() { | ||
67 | _arguments -s \ | ||
68 | "${common_options[@]}" \ | ||
69 | {-D,--delete-branches}"[Also delete any branches that are abandoned as a result of this hide]" \ | ||
70 | {-r,--recursive}"[Also recursively hide all visible children commits of the provided commits]" \ | ||
71 | "*::revsets:__git_commit_ranges" | ||
72 | return 0 | ||
73 | } | ||
74 | |||
75 | _git-branchless-init() { | ||
76 | _arguments -s \ | ||
77 | "${common_options[@]}" \ | ||
78 | "--uninstall[Uninstall the branchless workflow instead of initializing it]" \ | ||
79 | "--main-branch[Use the provided name as the name of the main branch]:main branch:__git_branch_names" | ||
80 | return 0 | ||
81 | } | ||
82 | |||
83 | _git-branchless-move() { | ||
84 | _arguments -s \ | ||
85 | "${common_options[@]}" \ | ||
86 | {-s,--source=}"[The source commit to move]:source:__git_recent_commits" \ | ||
87 | {-b,--base=}"[A commit inside a subtree to move]:base:__git_recent_commits" \ | ||
88 | "*"{-x,--exact=}"[A set of specific commits to move]:base:__git_recent_commits" \ | ||
89 | {-d,--dest=}"[The destination commit to move all source commits onto]:base:__git_recent_commits" \ | ||
90 | {-m,--merge}"[Attempt to resolve merge conflicts, if any]" \ | ||
91 | {-I,--insert}"[Insert the subtree between the destination and it's children, if any]" | ||
92 | return 0 | ||
93 | } | ||
94 | |||
95 | _git-branchless-next() { | ||
96 | _arguments -s \ | ||
97 | "${common_options[@]}" \ | ||
98 | {-b,--branch}"[Move the specified number of branches rather than commits]" \ | ||
99 | {-o,--oldest}"[When encountering multiple next commits, choose the oldest]" \ | ||
100 | {-n,--newest}"[When encountering multiple next commits, choose the newest]" \ | ||
101 | {-i,--interactive}"[When encountering multiple next commits, interactively prompt which to advance to]" \ | ||
102 | {-m,--merge}"[If the local changes conflict with the destination commit, attempt to merge them]" \ | ||
103 | {-f,--force}"[If the local changes conflict with the destination commit, discard them (Use with caution!)]" \ | ||
104 | "::num commits:_numbers -u commit -d 1" | ||
105 | return 0 | ||
106 | } | ||
107 | |||
108 | # TODO: factor with next | ||
109 | _git-branchless-prev() { | ||
110 | _arguments -s \ | ||
111 | "${common_options[@]}" \ | ||
112 | {-b,--branch}"[Move the specified number of branches rather than commits]" \ | ||
113 | {-o,--oldest}"[When encountering multiple next commits, choose the oldest]" \ | ||
114 | {-n,--newest}"[When encountering multiple next commits, choose the newest]" \ | ||
115 | {-i,--interactive}"[When encountering multiple next commits, interactively prompt which to advance to]" \ | ||
116 | {-m,--merge}"[If the local changes conflict with the destination commit, attempt to merge them]" \ | ||
117 | {-f,--force}"[If the local changes conflict with the destination commit, discard them (Use with caution!)]" \ | ||
118 | "::num commits:_numbers -u commit -d 1" | ||
119 | return 0 | ||
120 | } | ||
121 | |||
122 | _git-branchless-query() { | ||
123 | _arguments -s \ | ||
124 | "${common_options[@]}" \ | ||
125 | {-b,--branches}"[Print the branches attached to the resulting commits, rather than the commits themselves]" \ | ||
126 | {-r,--raw}"[Print the OID of each matching commit, one per line]" \ | ||
127 | "::revsets:__git_commit_ranges" | ||
128 | return 0 | ||
129 | } | ||
130 | |||
131 | _git-branchless-record() { | ||
132 | _arguments -s \ | ||
133 | "${common_options[@]}" \ | ||
134 | {-m,--message=}"[The commit message to use]:message" \ | ||
135 | {-i,--interactive}"[Select changes to include interactively, rather than using the current staged/unstaged changes]" \ | ||
136 | {-d,--detach}"[Detach the current branch before committing]" \ | ||
137 | "::revsets:__git_commit_ranges" | ||
138 | return 0 | ||
139 | } | ||
140 | |||
141 | _git-branchless-repair() { | ||
142 | _arguments -s "${common_options[@]}" | ||
143 | return 0 | ||
144 | } | ||
145 | |||
146 | _git-branchless-restack() { | ||
147 | _arguments -s \ | ||
148 | "${common_options[@]}" \ | ||
149 | {-f,--force-rewrite}"[Force moving public commits, even though other people may have access to those commits]" \ | ||
150 | {-m,--merge}"[Attempt to resolve merge conflicts, if any]" \ | ||
151 | "*::revsets:__git_commit_ranges" | ||
152 | return 0 | ||
153 | } | ||
154 | |||
155 | _git-branchless-reword() { | ||
156 | _arguments -s \ | ||
157 | "${common_options[@]}" \ | ||
158 | {-f,--force-rewrite}"[Force moving public commits, even though other people may have access to those commits]" \ | ||
159 | {-m,--message=}"[Message to apply to commits]:message" \ | ||
160 | {-d,--discard}"[Throw away the original commit messages]" \ | ||
161 | "--fixup=[ A commit to \"fix up\"]:commit:__git_recent_commits" \ | ||
162 | "*::revsets:__git_commit_ranges" | ||
163 | return 0 | ||
164 | } | ||
165 | |||
166 | _git-branchless-smartlog() { | ||
167 | _arguments -s \ | ||
168 | "${common_options[@]}" \ | ||
169 | "::revset:__git_commit_ranges" | ||
170 | return 0 | ||
171 | } | ||
172 | |||
173 | _git-branchless-submit() { | ||
174 | _arguments -s \ | ||
175 | "${common_options[@]}" \ | ||
176 | {-c,--create}"[If there is no remote branch for a given local branch, create the remote branch by pushing the local branch to the default push remote]" \ | ||
177 | "::revset:__git_commit_ranges" | ||
178 | return 0 | ||
179 | } | ||
180 | |||
181 | _git-branchless-switch() { | ||
182 | _arguments -s \ | ||
183 | "${common_options[@]}" \ | ||
184 | {-c,--create}"[When checking out the target commit, also create a branch with the provided name pointing to that commit]:branch name" \ | ||
185 | {-f,--force}"[Forcibly switch commits, discarding any working copy changes if necessary]" \ | ||
186 | {-m,--merge}"[If the current working copy changes do not apply cleanly to the target commit, start merge conflict resolution instead of aborting]" \ | ||
187 | "::target:__git_recent_commits" | ||
188 | return 0 | ||
189 | } | ||
190 | |||
191 | _git-branchless-sync() { | ||
192 | _arguments -s \ | ||
193 | "${common_options[@]}" \ | ||
194 | {-p,--pull}'[Run `git fetch` to update remote references before carrying out the sync]' \ | ||
195 | {-f,--force-rewrite}"[Force moving public commits, even though other people may have access to those commits]" \ | ||
196 | {-m,--merge}"[Attempt to resolve merge conflicts, if any]" \ | ||
197 | "*::revset:__git_commit_ranges" | ||
198 | return 0 | ||
199 | } | ||
200 | |||
201 | _git-branchless-test() { | ||
202 | local line state | ||
203 | |||
204 | _arguments -C -s \ | ||
205 | "${common_options[@]}" \ | ||
206 | "1:git-branchless test subcommand:->cmds" \ | ||
207 | "*::arg:->args" | ||
208 | |||
209 | case "$state" in | ||
210 | cmds) | ||
211 | _values "git-branchless test command" \ | ||
212 | "run[Run a given command on a set of commits and present the successes and failures]" \ | ||
213 | "show[Show the results of a set of previous test runs]" \ | ||
214 | "clean[Clean any cached test results]" \ | ||
215 | "help[Print this message or the help of the given subcommand(s)]" | ||
216 | ret=0 | ||
217 | ;; | ||
218 | args) | ||
219 | _call_function ret "_git-branchless-test-${line[1]}" | ||
220 | ;; | ||
221 | esac | ||
222 | |||
223 | return ret | ||
224 | } | ||
225 | |||
226 | _git-branchless-test-clean() { | ||
227 | _arguments -s \ | ||
228 | "${common_options[@]}" \ | ||
229 | "::revset:__git_commit_ranges" | ||
230 | return 0 | ||
231 | } | ||
232 | |||
233 | _git-branchless-test-run() { | ||
234 | _arguments -s \ | ||
235 | "${common_options[@]}" \ | ||
236 | {-x,--exec=}'[An ad-hoc command to execute on each commit]:exec:_path_commands' \ | ||
237 | {-v,--verbose}"[Show the test output as well]" \ | ||
238 | {-s,--strategy=}"[How to execute the tests]:strategy:(working-copy worktree)" \ | ||
239 | {-j,--jobs=}"[How many jobs to execute in parallel]:jobs:_numbers -u jobs" \ | ||
240 | "::revset:__git_commit_ranges" | ||
241 | return 0 | ||
242 | } | ||
243 | |||
244 | _git-branchless-test-show() { | ||
245 | _arguments -s \ | ||
246 | "${common_options[@]}" \ | ||
247 | {-x,--exec=}'[An ad-hoc command to execute on each commit]:exec:_path_commands' \ | ||
248 | {-v,--verbose}"[Show the test output as well]" \ | ||
249 | "::revset:__git_commit_ranges" | ||
250 | return 0 | ||
251 | } | ||
252 | |||
253 | _git-branchless-undo() { | ||
254 | _arguments -s \ | ||
255 | "${common_options[@]}" \ | ||
256 | {-i,--interactive}"[Interactively browse through previous states of the repository before selecting one to return to]" \ | ||
257 | {-y,--yes}"[Skip confirmation and apply changes immediately]" | ||
258 | return 0 | ||
259 | } | ||
260 | |||
261 | _git-branchless-unhide() { | ||
262 | _arguments -s \ | ||
263 | "${common_options[@]}" \ | ||
264 | {-r,--recursive}"[Also recursively unhide all children commits of the provided commits]" | ||
265 | return 0 | ||
266 | } | ||
267 | |||
268 | _git-branchless-wrap() { | ||
269 | service="git" | ||
270 | _arguments -s \ | ||
271 | "${common_options[@]}" \ | ||
272 | "*::git command:_git" | ||
273 | return 0 | ||
274 | } | ||
275 | |||
276 | compdef _git-branchless git-branchless | ||