I feel like this question could be answered with one or two hyperlinks; I'm just not coming up with the right search terms to find those links myself...
I'm trying to make some minor changes to the git p4
subcommand, which is implemented as a 3797-line Python script named /usr/libexec/git-core/git-p4
. Before wading into the code too much farther, I'd like to get a sense of how a Python git
command is structured.
What information does the subcommand get from its caller? What environment variables can it rely on existing? How does it detect the configuration (from .git/config
and/or ~/.gitconfig
)? How are git command-line options passed down from git
(in case there are command-line options common to many different subcommands and you want to make sure their handling is centralized)? What is the current working directory at the time the subcommand is launched? What happens if I change the cwd? How do I communicate back to git
that it should change the repo (make commits, add or delete tags, rewrite history) as part of the functionality of my new subcommand? Can my command "work on" the index directly without making a working tree, and if so, how? How do I handle and report errors? How do I print usage information in a consistent way?
I assume there must be blog posts and things about this topic, but searching "write a git subcommand in python", "create new git subcommand", etc., just turns up ways to run existing git commands from Python and ways to create new git repos, respectively.
Subcommands are keyword that invoke a new set of options and features. For example, the git command has a long series of subcommands, like add and commit . Each can have its own options and implementations.
Git's documentation of course covers this. The short version:
If git foo
is not a built-in command, Git will search PATH
for git-foo
and run that. But it does nothing else at all: it doesn't even verify that you're in a repository directory. (After all, lots of commands don't even need one, like git hash-object
or git ls-remote
.)
While internal and external APIs exist, most new commands are simply written against the existing suite of git
commands. They are how everything happens, and they determine whether things like a working copy are needed or if the index is sufficient.
It’s in cases like this that the “plumbing” commands often become important: in my experience writing a few of these (and using filter-branch
), cat-file
, commit-tree
, for-each-ref
, merge-base
, rev-list
, and rev-parse
have been particularly helpful. Obviously it can depend on what you want to do: others might find the --cached
option to various (otherwise) porcelain commands more relevant.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With