A modernized porcelain for git.
Apparently terminals are going through some sort of modern renaissance where old, antiquated tools are being replaced with hip, fresh tools. I'm hopping on the bandwagon.
See:
Anyway, I've always found git's standard porcelain to be... confusing. I'm sure many newer users may also feel that way, so I'd like to see if I can do something about it. Maybe I can't. Whatever.
The original idea credit goes to eevee/tcup, but that project seems to be have been long abandoned. Many moons ago it was even written in Rust 0.6.
Because short words are good for CLI tools and the phrase 'git milk?' was funny to me. Whatever.
As it turns out, eevee has already done a fair amount of inspirational
planning. Alas, this is not
tcup
! This is milk
. My plan is slightly different, but the general idea
remains the same. Several pieces are taken from eevee's, but a few are just
things that I wish were easier to get out of git.
- I appreciate eevee's
--batch
and--json
ideas, butmilk
is initially intended to just improve the human experience of interacting with git repositories, not the machine experience. - Not sure I agree with being able to do everything that git can do; there's a lot of dark magic hidden away in git's plumbing that is so arcane that it almost never comes up. I just want to support the majority of common operations.
If there's a checkbox next to it, it means it has at least minimal functionality. It does not mean it's completely finished.
-
ls
- I like being able to browse the clean git tree like I would browse the dirty working tree -
show
- Likegit cat-file -p <id>
but better. -
me
- Funny, but also helpful when you may have multiple identities for various repos (eg, personal / work emails) -
head
- Just display the currentHEAD
. This is probably obsolete because ofshow
defaulting toHEAD
. -
where
- Show the base directory for the working repo -
status
- Obvious -
diff
- Obvious -
log
- Obvious
-
track [paths]
- Start tracking things? Does this make sense? I've always found it weird thatgit add
will move something from untracked to staged or from modified to staged. Feels like there's a missing step but I don't know if it actually makes any sense to break it up like this. -
ignore [paths]
- Add things to.gitignore
-
stage [paths]
- Stage files. -
unstage [paths]
- Unstage files. Likegit reset --mixed
, but you can't move your HEAD at the same time. That's weird. -
clean [paths]
- Clean all local modifications. Likegit reset --hard
. This adds all dirty files to the ODB and prints out the OIDs , just in case you really oof yourself and need to get them back. Seerestore
below to restore oopsied files. -
restore <blob> <path>
- Place the contents of<blob>
from the ODB into a file at<path>
.
-
switch
- Switch HEAD to something else -
update
- Try to pull new changes from a remote, including fastforwarding local branches and stuff -
sync
- Try to push/pull new changes from a remote, prompting the user to rebase if there's not a fast-forward avaialble -
commit
- Obvious -
merge
- Obvious, but with a change: when merging two branches, both branches will update to the merge commit. I find too often that Icheckout master; merge dev; checkout dev; merge master
to get a clean branching point fordev
.
-
branch new
- Create a branch -
branch rm
- Remove a branch -
branch ls
- List all branches -
branch rename
- Rename a branch -
branch mv
- Move a branch from its current location to a new one
I don't have a really solid vision aside from "easier to use", but here are some areas I've noted in git's existing porcelain:
- Many commands are extremely overloaded, which leads to some context-sensitive
ambiguity. These commands can often do unexpected things or are intended to
do things completely different from the way they're actually used. Notably,
I've been using
git reset
as a way to unstage all changes, or unstage changes for a particular file viagit reset <path>
. I recently learnedgit reset
actually sets theHEAD
to a specific revision and sets the index to the state of that revision. Effectively, this does unstage things... but it also does a lot more than that, which is rather unintuitive. Also, it has some odd ambiguous cases for arguments -git reset <tree-ish>
andgit reset <path>
. If a user ever tries to use an ambiguous command, it will notify them - but it's still surprising. I don't like being surprised by my tooling. git checkout
is similarly overloaded. Also, both of these don't really have names that represent what I actually want to do.reset
is actually used to unstage, andcheckout
is used to both switch branches and to undo local changes. Weird stuff.git branch
is super overloaded. Enumerating, creating, renaming, moving, deleting, and copying are all the same command, just with a different flag. I'd like to normalize this with subcommands instead of flags, eg:git-branch -v
->milk branch ls
git-branch <branch>
->milk branch new <branch>
git branch -m <old-name> <new-name>
->milk branch rename <old> <new>
git-branch -f <branch> <commit-ish>
->milk branch mv <branch> <commit-ish>
git branch -d <branch>
->milk branch rm <branch>
git stash
is also fairly overloaded, but with subcommands instead. That's weird, but I guess it makes sense sometimes? I don't have many ideas to fix this one.git add .
andgit commit -a
make it too easy to intentionally add garbage that shouldn't be tracked.git add -p
is excellent, but it makes the process of committing a bit more of a chore. How can this be streamlined so users are encouraged to make small, logical commits instead of large, lumpy commits?git commit -m
encourages users to write short, unhelpful messages. It's GONE.commit-ish
,tree-ish
, and their ilk are sometimes ambiguous and confusing. I'd like to create some notation to clearly indicate what an entity is expected to be, for examplebeef
may refer to commit,@beef
may refer to a branch, and#beef
may refer to a tag. Dunno yet.- Probably other stuff too. Dunno.
- eevee also lists many more gripes