9

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.

2
  • 2
    Try "custom" instead of "new". That turns up a lot of blog posts, and e.g. gitpython.readthedocs.io/en/stable/tutorial.html. Basically I think you don't get anything implicitly: git just calls your subcommand, passing it whatever arguments the user passed. Anything else you want, you look up yourself, e.g. by reading config from the .git directory. Commented Mar 2, 2018 at 18:39
  • 1
    Git refers to low-level commands as "plumping" and high-level commands as "porcelain", so depending on what you are planning to right these could be useful Google terms. Commented Mar 2, 2018 at 18:39

3 Answers 3

13

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.

Sign up to request clarification or add additional context in comments.

8 Comments

Makes sense and thanks. This answer could yet be improved by a list/link of the most useful "plumbing" subcommands (IYHO) — I mean, by definition, they're commands I would seldom have seen before, right? :)
@Quuxplusone: I tried to keep it short on opinion, but there you are.
Do you know how to pass the arguments when customizing git commands in python? I know for bash/sh, that is $1.
@K.Symbol: You mean, how do you access command-line arguments in Python? As in, sys.argv?
Yeah, I figured out that later, that's exactly what I want.
|
4

Here are some posts on the subject. The executive summary is that there seems to be no "official" way to access git config, manipulate the repo, etc., from Python — in fact git has no interest in officially supporting Python at all, because of concerns about portability. There are "official" ways of doing some things in shell, available via git-sh-setup.

The other "official" language for git subcommands is Perl. I don't know if there's any "official" library of git-related subroutines for Perl.

Blog post roundup:

Jeff Remer at Coderwall explains that some of the API things I'm looking for do exist if one is willing to write in shell instead of Python:

Turns out git provides a library of shell functions expressly for this purpose, at $(git --exec-path)/git-sh-setup. Use it in your own shell scripts like:

source "$(git --exec-path)/git-sh-setup"

After that you'll have access to a handful of shell functions that perform useful actions and sanity checks or give you access to git information such as handy usage and die methods.

So it looks like if you write in Python, you roll your own everything — which means I've got to wade in and read the git-p4 source code if I want to understand the particular way they rolled it.

As to whether it's possible to manipulate the repo and create history without a working tree: I don't know yet.

1 Comment

Do you know how to pass the arguments when customizing git commands in python? I know for bash/sh, that is $1
0

Chromium depot_tools has the exact same feature. You can refer to the implementation of its git-cl command.

The initial git-cl shell script calls python_runner.sh, which then calls the mathced python file.

You can simply download the depot_tools and add it in $PATH. Compose you custom python file and script warpper, place them at depot_tools (same as git-cl) and you are good to go.

#!/usr/bin/env bash
# Copyright (c) 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
. "$(type -P python_runner.sh)"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.