README.org
July 15, 2024 · View on GitHub
#+property: header-args :eval never
- Git utilities by Hraban Luyat
Collection of Git utilities that can’t be found in vanilla Git or [[https://magit.vc/][Magit]]:
- =git children= :: list all child branches of a commit
- =git graft= :: cut & paste an entire subtree of git branches
- =git pullfetch= :: git fetch and pull combo
- =git split= :: split a commit up into 1 commit per file
- Commands
** =git hly=: Top-level wrapper
To get a list of all subcommands, refer to =git hly=:
#+begin_example $ git hly No such command: NIL
Valid commands:
- children
- graft
- pullfetch
- split
For in-depth help, pass --help to a subcommand. #+end_example
** =git children=: List all child branches of a commit :PROPERTIES: :CUSTOM_ID: git-children :END:
*** Usage
#+begin_src shell git childen REF #+end_src
Non inclusive, i.e. the output does not include the branch you passed, itself.
*** Example
For this repo:
#+begin_example
A -> B -> master -> C -> D -> origin/master
--> E -> branch1 -> F -> branch2
-----> G -> branch3
#+end_example
This command:
#+begin_src shell git children master #+end_src
Will return:
#+begin_example branch1 branch2 branch3 #+end_example
** =git graft=: Cut & paste an entire subtree of git branches :PROPERTIES: :CUSTOM_ID: git-graft :END:
Typical use case: you have a long list of PRs which depend on each other; when origin/master is updated, you want to rebase all of them to the new origin/master. Instead of rebasing each branch individually, you want to “snip” the bottom off the old master, and “glue” it back onto the new one.
*** Usage
#+begin_src shell :eval never git graft ROOT ONTO #+end_src
- =ROOT= :: the first commit under (and including) which all local branches will be selected.
- =ONTO= :: the commit onto which the subtree will be moved.
This git-graft won’t actually run the commands: it just prints them to stdout. You’re meant to manually copy & paste them to your shell. This is a temporary hack to allow a human to sanity check the results, and it provides rollback.
**** Advanced
Once you get comfortable with the tool, you can pipe directly to bash:
#+begin_src shell :eval never git graft ROOT ONTO | bash #+end_src
This will print undo commands, so in case of trouble you can always rollback.
*** Example
say after a git fetch, you end up with:
#+begin_example
A -> B -> master -> C -> D -> origin/master
--> E -> branch1 -> F -> branch2
-----> G -> branch3
#+end_example
Execute this:
#+begin_src shell :eval never git graft E origin/master #+end_src
And you will get a bunch of git commands on stdout. Execute them all, and you should end up with:
#+begin_example
... -> D -> origin/master -> E -> branch1 -> F -> branch2
-----> G -> branch3
#+end_example
Every local branch under (and including) branch1 will be moved, so be careful choosing something like (e.g.) master. It will work if all branches under master are indeed feature branches that you “own”, but if you locally checked out someone else's branch, it will also be rebased.
Git-graft also prints “undo” commands, commented out. These are not expected to be necessary for normal operation, but if something goes wrong midway through a big graft, you can use those commands to go back to the pre-graft state.
The argument to git-graft, the root, is inclusive. This is at odds with git conventions, where you normally specify a commit's “parent”, under which all relevant commits are found. The reason is that sometimes, e.g. the example above, choosing the parent can lead to too many branches being included: you'd get origin/master, and all potential other local branches already ported onto it.
** =git pullfetch=: Git fetch and git pull combo :PROPERTIES: :CUSTOM_ID: git-pullfetch :END:
When cooperating with others on a busy repo this command helps “pull all changes.” Update master if it’s been updated, purge deleted branches, etc.
Usage:
#+begin_src shell :eval never git pullfetch #+end_src
** =git split=: Split HEAD into one commit per file
Useful for splitting up a convoluted commit as part of a larger rebase workflow.
See [[https://stackoverflow.com/questions/40698651/how-to-split-every-commit-by-file]].
- Installation :PROPERTIES: :CUSTOM_ID: installation :END:
** Stand-alone binary
#+begin_quote [!TIP] This is easiest! #+end_quote
- Download the latest release for [[https://github.com/hraban/git-hly/releases/download/latest/git-hly-linux.tar.bz2][Linux]] or [[https://github.com/hraban/git-hly/releases/download/latest/git-hly-macos.tar.bz2][Mac]]
- Unpack and place in your =PATH= (e.g.: =/usr/bin=)
Or you can just invoke it directly, like =./git-hly=.
** Nix
#+begin_quote [!NOTE] If you don’t know Nix, don’t worry about this. If you do, this is the way to go! #+end_quote
This project is available through Nix.
*** Non-flakes (aka. "channel based")
Old-school Nix:
Install globally:
#+begin_src shell nix-env -if https://github.com/hraban/git-hly/archive/master.tar.gz #+end_src
Or just build it in a temporary directory to play around with it:
#+begin_src shell nix-build https://github.com/hraban/git-hly/archive/master.tar.gz #+end_src
The binary can be found in =./result/bin/=. Copy it somewhere to your PATH.
*** Flakes
Do you use the hip new Nix Flakes? You can install this package in your profile:
#+begin_src shell nix profile install github:hraban/git-hly #+end_src
Or you can just run it stand-alone (but it won’t integrate with the top-level =git= CLI):
#+begin_src shell nix run github:hraban/git-hly -- ... #+end_src
Or you can build it locally and copy the binary out of =./result/bin= to your PATH:
#+begin_src shell nix build github:hraban/git-hly #+end_src
*** NixOS / nix-darwin / ...
If you use these I’m sure you don’t need my help :)
** Native
#+begin_quote [!WARNING] This is hard, I can’t think of why anybody would want to do this. Good luck. #+end_quote
Building this project natively is complicated and very dependent on your local setup. You need a more recent version of ASDF than comes bundled with SBCL. I recommend loading the project up in SLIME, with a new ASDF version pre-loaded. You can compile it to a binary using =(asdf:make "git-hly")=.
- License
git-hly - Hraban’s Git utilities Copyright © 2022–2024 Hraban Luyat
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, version 3 of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.