git-project-core-plugins
Plugins for git-project
This is a set of basic plugins to manage several aspects of projects kept within git repositories. These plugins include commands to:
Configure git-project and its various plugins
Clone repositories
Initialize a project in an existing repository
Manage branches
Manage worktrees
Run commands (e.g. configure/build/install)
Associate artifacts with a project
Show extended help for a command
These plugins add a number of commands to git-project. Each command has an associated --help option to describe its function and options. There is also a help command that accesses more extensive manpage-like descriptions of commands.
Setup
pip install git-project-core-plugins
Worktree environment
A number of commands can have knowledge of a “worktree environment” with a specific layout:
<path> .<name>.git .git worktree1 worktree2 worktree3
That is, either a bare clone is done, or an existing clone is converted to a bare clone via git <project> init --worktree. The bare repository is the hidden .<name>.git child, named for the last component of the remote url, and <path> holds it alongside the worktrees. The .git beside it is a file holding gitdir: .<name>.git, so git works from <path> too. It is a file, not a directory or a symlink, because a go build run from <path> fails if .git resolves to a directory there. A repository that is already bare when init --worktree runs keeps its name and gets no .git file, and its parent directory holds the worktrees. Any conversion will abort if the worktree is dirty. Typically, an ordinary git clone is followed immediately by git <project> init --worktree.
Either route also creates a worktree for the project’s main branch, so the layout is usable straight away. The main branch is taken from the repository when it can be determined uniquely, and you are asked which to use when it cannot.
The point of the layout is that each worktree can own its own build and install trees. Switching from worktree to worktree then does not result in “rebuilding the world.”
Examples
Initial setup
git clone <url> git <project> init --worktree
Add convenience substitution variables
Configured values may name other configured values. Inside a worktree, {path} is the worktree’s path and {worktree} is its name. So a build directory written once gives every worktree its own:
git <project> config srcdir "{path}"
git <project> config builddir "{srcdir}/build/{worktree}"
git <project> config make "make -C {srcdir} BUILDDIR={builddir} {build}"
The Substitution and Scopes sections of the git-project documentation describe the names, and how a worktree’s values override the project’s: https://pypi.org/project/git-project/
Adding custom commands
git <project> run --make-alias configure
git <project> run --make-alias build
git <project> run --make-alias install
git <project> add configure debug "mkdir -p {builddir} && cd {builddir} && cmake -DCMAKE_BUILD_TYPE=Debug {srcdir}"
git <project> configure debug
Command Reference
artifact
The artifact command adds or removes associations between git config objects and file-system objects.
Summary:
git <project> artifact add <subsection> <path> git <project> artifact rm <subsection> [<path>]
<subsection> is a git config section which will appear under the <project>.artifact section. Artifacts look up objects associated with <subsection> and perform substitutions on paths to yield the final associated file-system object. The artifact rm command simply removes an artifact association, it does not remove the artifact itself. A <subsection> may hold several paths. With <path>, artifact rm removes only that association. Give <path> as it was added, before substitution.
For example:
git <project> artifact add worktree.myworktree /path/to/artifact
Presumably, /path/to/artifact is in some way created in association with myworktree, for example by the run command. When we delete myworktree, the artifact association causes /path/to/artifact to also be removed.
Removal runs without a shell. A leading ~ and $VAR references are expanded. A path that exists is removed as written. Otherwise it is expanded as a glob, and each match is removed. A symbolic link is removed, not its target.
Removal refuses the root, the home directory, the current worktree and the repository’s git directory, and any directory that contains one of them. A refused path stops the command before anything is removed.
Substitutions can make artifact associations easier to manage:
git <project> artifact add worktree /path/to/{worktree}/artifact
Notice that we’ve added the artifact under the more general worktree subsection instead of naming a worktree explicitly as before. Because the {worktree} substitution appears in the artifact path, deleting any worktree will cause the worktree’s name to be substituted into the artifact path, forming a unique artifact path to remove. If a worktree also has its own worktree.<name> artifact, removing that worktree uses only its own, not the general one.
We may make this even more general:
git <project> config srcdir "{path}"
git <project> config builddir "{srcdir}/build/{worktree}"
git <project> config make "make -C {srcdir} BUILDDIR={builddir} {build}"
git <project> run --make-alias build
git <project> add build debug "{make}"
git <project> add build release "{make}"
git <project> add build check "{make}"
git <project> artifact add worktree "{builddir}"
We’ve added a single artifact association that will handle any worktree and all of our different build types. When we delete the worktree, all artifacts related to debug, release and check builds will also be removed.
See also:
config run worktree
branch
The branch command queries the status of branches against the project’s branches and prunes branches that are finished.
Summary:
git <project> branch status (--all | --all-user | <pattern>) [<target>] git <project> branch prune [--force] [--no-ask] [--keep-remote-branch] (--all-user | <pattern>)
A <pattern> matches every branch whose name starts with it, and a name without refs/ is looked up under refs/heads/. –all-user matches the branches under user/<login>.
branch status prints a table of the matching branches, with whether each is merged to a project branch and whether it is pushed to a project remote. –all reports on every local branch. With <target>, merged means merged to <target> instead. For example:
git <project> config --add branch release git <project> branch status mybranch
The project branch defaults to the repository’s main branch, main or master, when none is configured. config –add adds more.
branch prune asks whether to delete each matching branch that is merged to a project branch, locally or on a project remote. If the answer is yes, it deletes the local branch and its copy on each project remote. A pattern matches project branches too, so check the list before you answer.
With –force, branch prune offers every matching branch, merged or not. With –no-ask, it deletes without asking. With –keep-remote-branch, it deletes only the local branch and leaves the remote copies in place. worktree rm takes the same option, and it matters more here, because branch prune acts on every branch that matches the pattern.
See also:
config worktree
clone
The clone command clones a repository.
Summary:
git <project> clone <url> [<path>] [--bare]
By itself clone does a basic clone, then sets the project’s defaults in the new repository. With no <path> it clones into the last component of <url> under the current directory, keeping any .git suffix, where git clone drops it. It offers no ssh key, so an ssh url fails to authenticate.
Plugins add options to clone. For example, the worktree command adds a –worktree option to have clone create a worktree layout.
See also:
worktree
config
The config command manages git config settings under the <project> section.
Summary:
git <project> config [--add] [--unset] <name> [<value>]
The config command operates much like git’s built-in config command, except all configuration keys are prefixed with <project>, keeping values under a single project namespace. This is a convenient way to store parameters for other commands. For example:
git <project> config builddir /path/to/build
git <project> add run build "make BUILDDIR={builddir} all"
A value may name other values as {name}. The commands that use a value, such as run, substitute it when they run, not when it is set. For example, inside a worktree {worktree} is that worktree’s name:
git <project> config builddir /path/to/{worktree}/build
The Substitution and Scopes sections of the git-project documentation describe the names and how a worktree’s values override the project’s: https://pypi.org/project/git-project/
The config command also adds a config subcommand to worktree, which sets a value for one worktree and takes that worktree’s name first:
git <project> worktree config <ident> <name> [<value>]
See also:
run worktree
help
The help command displays tutorial-style help for commands.
Summary:
git <project> add help [--manpage] <subsection> <text> git <project> rm help [--manpage] <subsection> git <project> help [<command> | <section>... <name>]
With no argument, help shows git-project’s general help. help <command> shows the command’s manual. help <section>... <name> shows the manpage stored for that subsection, so help run build shows the one stored for run.build.
Users may add help to any project config section. For example:
git <project> add help run.build "Perform a build" git <project> add help run.check "Run tests"
All help is stored under a <project>.help config sub-section. If a command supports it, such help may appear in the command’s own help output by querying the appropriate <project>.help sub-section:
git <project> run --help
<standard help text>
Available runs:
check - Run tests
build - Perform a build
In this way projects can self-document their configurations. Normally <text> is stored in <project>.help.<subsection>.short. With –manpage, <text> is stored in <project>.help.<subsection>.manpage. Commands may reference short help or manpages in various ways to present help.
See also:
run
init
The init command initializes project state.
Summary:
git <project> init
init itself changes nothing. Any command run in a repository sets the project’s branch to the main branch and its remote to origin, when they are not set already.
Plugins add options to init. For example the worktree command adds a –worktree option to convert an existing local clone to a worktree layout.
See also:
worktree
run
The run command executes commands via a shell.
Summary:
git <project> add run <name> <command> git <project> rm run <name> git <project> run --make-alias <name> git <project> run <name> [<option>...]
Full shell substitution is supported, as well as config {key} substitution, where the text {key} is replaced by key’s value. The command is printed after substitution. The words given after <name> on the command line are shell-quoted, so the shell sees each as one argument and never runs it. In those words only a plain {name} is substituted, and other text in braces is passed on as it is.
The add run command associates a command string with a name, and rm run removes it. The run command itself invokes the command string via a shell. With –make-alias, run registers a new command, such as build, that works like run. For example:
git <project> run --make-alias build
git <project> add build all "make -C {git_workdir} all"
git <project> build all
Each such command keeps its own list of names. So in the example above this fails, because all was added as a build, not a run:
git <project> run all
In this way we may use the same <name> for different commands, which can be convenient:
git <project> build all git <project> check all
The Substitution section of the git-project documentation lists the names a command may use, such as {git_workdir} and {branch}: https://pypi.org/project/git-project/
The run command adds its own. {run}, or the alias name such as {build}, gives the name of the command being run. An example will make this more clear:
git <project> config cmd "make -C {git_workdir} BLDDIR=/path/to/{build} {build}"
git <project> add build all "{cmd}"
git <project> add build some "{cmd}"
We have configured two different build flavors, each which place build results in separate directories and invoke different targets. Substitution proceeds as follows:
git <project> build all -> make -C /cur/workarea BLDDIR=/path/to/all all git <project> build some -> make -C /cur/workarea BLDDIR=/path/to/some some
Extra options passed to the run command may be referenced in the command string:
git <project> add build extra "echo {options}"
git <project> build extra hello world! -> echo hello world!
Individual options may also be referenced:
git <project> add build extra "echo {options_1} {options_0}"
git <project> build extra hello world! -> echo world! hello
A special dash-separated “key” string composed of option values may be generated:
git <project> add build any "make {option_key}"
git <project> build any target debug -> make target-debug
A special {option_keysep} substitution will result in a - if there are options and the empty string otherwise:
git <project> add build any "make my{option_keysep}{option_key}"
git <project> build any target debug -> make my-target-debug
git <project> build any -> make my
Other substitutions may be used in options:
git <project> add build branch "make {options}"
git <project> build branch {branch} -> make dev # On the dev branch
A special substitution {option_names} gives the options separated by spaces, with their braces removed, so they are not substituted:
git <project> add build branch "make {option_names} {options}"
git <project> build branch {branch} -> make branch dev # On the dev branch
Inside a worktree, a value set for that worktree overrides the project’s value of the same name. See the Scopes section of the git-project documentation.
See also:
config worktree
worktree
The worktree command manages worktrees and connects them to projects.
Summary:
git <project> worktree add [-b <branch>] <name-or-path> [<committish>]
git <project> worktree rm [-f] [--keep-branch] [--keep-remote-branch]
<name>
git <project> worktree config <ident> [--add] [--unset] <name> [<value>]
worktree add creates a worktree at <name-or-path> and checks out a branch in it. The worktree’s name is the last component of <name-or-path>. The branch is named after <name-or-path>: a simple name gives a branch of that name, and a relative path such as ../user/topic gives user/topic, the part after any ... That branch is created at <committish>, or at HEAD, when it does not exist yet. An existing branch is checked out as it is, and <committish> is then ignored. With -b <branch>, the new branch <branch> is created at <committish> or HEAD and checked out instead.
A relative path that does not start with .. is placed under the root of the current worktree, or under the current directory in a bare repository. A path that starts with .. is taken from the current directory. So worktree add ../topic, run in the main worktree, puts the new worktree beside it, and worktree add topic puts it inside.
To keep things simple, we’ll usually always name worktrees similarly (or identically) to the branches they reference, though it is not strictly necessary to do so.
worktree rm removes a worktree and its workarea. It first removes the paths that artifact associates with the worktree, and if one of those is refused, nothing is removed. The branch is deleted too, locally and on each project remote, unless a flag says otherwise: --keep-branch leaves the branch alone everywhere, and --keep-remote-branch deletes only the local copy. A branch the project configures is never deleted, whatever the flags say. Removing a worktree whose branch is unmerged requires -f, unless --keep-branch means the branch survives anyway.
The key idea behind project worktrees is that they are connected to various artifacts. Worktrees are managed together with these artifacts to provide a project-level view of various tasks. For example, a run command can create artifacts associated with a worktree. Removing the worktree implicitly removes these artifacts, making build cleanups easy and convenient. Commands may use the {worktree} substitution to create worktree-unique artifacts. Other substitutions may also reference {worktree} in a recursive manner.
Here is a concrete example:
git <project> config srcdir "{path}"
git <project> config builddir "{srcdir}/build/{worktree}"
git <project> config make "make -C {srcdir} BUILDDIR={builddir} {build}"
git <project> run --make-alias build
git <project> add build release "{make}"
Assuming the build system uses BUILDDIR to determine where build artifacts go, each worktree will get a unique set of build artifacts, via the {builddir} and, recursively, {worktree} substitutions. When we delete the worktree, we’ll also delete the associated build directory.
We associate artifacts with worktrees via the artifact commands.
Another important benefit of worktrees and associated builds is that switching to work on a new worktree (by simply editing sources in a different worktree directory) will not result in build artifacts from the previous worktree being overwritten. Thus we avoid the rebuild the world problems of switching branches within the same workarea. Generally, each created branch will have its own worktree and we will rarely, if ever, switch branches within a worktree.
A worktree layers a config scope on top of the global project scope, so that configuring a key in the worktree with the same name as a key in the project will cause the worktree key’s value to override the project key’s value:
git <project> config buildwidth 16 git <project> worktree config myworktree buildwidth 32
The worktree to configure is named explicitly, so myworktree gets buildwidth=32 while the project keeps 16.
Inside myworktree, wherever {buildwidth} appears (say, in a run command), 32 is substituted instead of 16. Outside myworktree, or in a worktree with no buildwidth of its own, {buildwidth} gives 16. The Scopes section of the git-project documentation describes the rule: https://pypi.org/project/git-project/
The worktree plugin also adds a –worktree option to the clone and init commands. Both set up the worktree layout described in the package documentation. The bare repository is a hidden child directory named for the last component of the remote url, such as .myrepo.git. The top-level directory holds it alongside the worktrees.
clone --worktree clones bare, then rewrites the fetch refspec and sets the main branch to track its remote branch, so fetch and pull behave as they do in a regular clone. The refs/heads and refs/remotes namespaces remain, and every other local branch is deleted. Add --bare to skip the refspec rewrite. The clone still gets the .git file and the main worktree. With no <path>, clone --worktree uses the current directory itself as the top-level directory, where a plain clone makes a new one.
init --worktree converts an existing clone in place. The workarea must be clean. The conversion deletes every file in the top-level directory except the git directory, so preserve anything there that is not part of the repository. Only the main branch gets a worktree, so a different checked-out branch gets none, though one is easy to add afterward.
On a repository that is already bare, init --worktree deletes nothing. It refuses to run while a branch other than the main one exists, because it cannot know which remote each branch should go to.
See also:
artifact config run
Metadata
Release files for git-project-core-plugins 0.0.28
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| git_project_core_plugins-0.0.28.tar.gz | 60.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| git_project_core_plugins-0.0.28-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 115.6 kB
Release files / git_project_core_plugins-0.0.28.tar.gz
| Download URL | git_project_core_plugins-0.0.28.tar.gz |
|---|---|
| Size | 60.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e74fbc7fa5de2de62725ba87fecd5725604ab3687cd6896d99680c6137074854
|
|
BLAKE2b-256 checksum How to use checksums |
6eba77659663ab85f86ace837fa9eab201c12b5f8f5227c8e9112290234eff24
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / git_project_core_plugins-0.0.28-py3-none-any.whl
| Download URL | git_project_core_plugins-0.0.28-py3-none-any.whl |
|---|---|
| Size | 55.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
579e371719a9bf9865562cf5657145e12c685f5c671f278e6c107355dfc1803c
|
|
BLAKE2b-256 checksum How to use checksums |
bd51c5cc0f2be413482630d75f1922f3e0ae451ca0fda883e22e7ce5c88b6192
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|