git son: add command to create independent child repositories
Subsystems (3)
cover git son: add command to create independent child repositories
Motivation
==========
When spinning off a new project that is related to an existing repository, there is no built-in way to create a child repository that maintains a link back to its parent without the tight coupling of submodules. Submodules pin the child to a specific commit and require the parent to track the child in its index, which is too heavyweight when the child is meant to be fully independent.
The typical workflow today is manual: git init, git remote add, update .gitignore — three steps that are easy to forget or get wrong. git son automates this and establishes a lightweight convention for the parent-child
relationship: a remote named parent in the child, and nothing in the parent except an ignore rule.
Summary
=======
This series introduces git son, a new porcelain command that creates an independent child repository inside the current working tree. Unlike a submodule, the child is not tracked by the parent; instead, its subdirectory is added to the parent's .gitignore. The child is configured at creation time with a remote named parent pointing back to the parent repository's origin URL (or local path if no origin exists), making the lineage explicit and recoverable via standard Git commands.
Changes
=======
* git-son.sh: new shell script implementing the command, supporting --inherit to fetch parent history at creation time and --branch <branch> (requires --inherit) to check out a specific parent branch
* git-son registered in command-list.txt as mainporcelain
* git-son.sh added to SCRIPT_SH in Makefile and to scripts_sh in meson.build
* Documentation/git-son.adoc: new man page covering synopsis, option descriptions, and worked examples
* Documentation/meson.build: git-son.adoc added to the manpage build list
* t/t5151-son.sh: new test script covering basic creation, parent remote configuration, .gitignore update, idempotency failure, flag validation, and --inherit with a bare remote
Details
=======
The key design property is independence: the child is a fully self-contained repository with no entry in the parent's index. The parent remote is the only artifact linking the two, which means git fetch parent and git log parent/<branch> work as expected from within the child without any special tooling.
The --branch flag is intentionally restricted to --inherit mode. Without fetching, there is no remote-tracking branch to check out from, so accepting --branch alone would be misleading; the command dies with a clear diagnostic in that case.
When no origin URL is available in the parent, the parent remote is set to the parent's absolute local path. This covers the common case of repositories that have never been pushed to a remote.
Testing
=======
t/t5151-son.sh covers the following scenarios:
* Basic child repository creation (directory exists, .git present)
* parent remote is correctly recorded in the child
* Child directory is appended to the parent's .gitignore
* Child starts with exactly one initial commit when --inherit is not used
* Command fails without leaving a directory when the target already exists
* --branch without --inherit is rejected before any filesystem changes
* --inherit fetches parent history and the remote-tracking branch is reachable in the child
Evan Haque (5):
git-son: add command to create independent child repositories
git-son: register in Makefile and meson build system
git-son: add to command list as mainporcelain
git-son: add documentation
git-son: add tests
.gitignore | 1 + Documentation/git-son.adoc | 64 +++++++++++++++++++++++++ Documentation/meson.build | 1 + Makefile | 1 + command-list.txt | 1 + git-son.sh | 97 ++++++++++++++++++++++++++++++++++++++ meson.build | 1 + t/meson.build | 1 + t/t5151-son.sh | 63 +++++++++++++++++++++++++ 9 files changed, 230 insertions(+) create mode 100644 Documentation/git-son.adoc create mode 100755 git-son.sh create mode 100755 t/t5151-son.sh
base-commit: aec3f587505a472db67e9462d0702e7d463a449d
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-2122%2FEvandabest%2Fgit-son-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-2122/Evandabest/git-son-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/2122
Signature · 1 line
gitgitgadget
PATCH 1/5 git-son: add command to create independent child repositories
From: Evan Haque <[email protected]>
Introduce git-son, a new porcelain command that creates an independent child repository inside the current working tree. Unlike submodules, the child is not tracked by the parent; instead its directory is added to the parent's .gitignore and a "parent" remote is configured in the child pointing back to the parent's origin URL or local path.
This gives users a lightweight way to spin off a related repository that knows where it came from without the coupling that submodules impose.
The command supports two optional flags:
--inherit fetch the parent's history into the child at creation
--branch check out a specific parent branch (requires --inherit)
--- git-son.sh | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100755 git-son.sh
git-son.sh new file +97 -0
@@ -0,0 +1,97 @@ +#!/bin/sh +# +# git-son: create an independent child repository that knows its parent +# + +SUBDIRECTORY_OK='Yes' +OPTIONS_SPEC='git son [options] <name> +-- +inherit fetch parent history into the son +branch= start the son from a specific parent branch +' + +. git-sh-setup +require_work_tree +cd_to_toplevel + +inherit= +branch= +while test $# -gt 0 +do + case "$1" in + --inherit) + inherit=1 ;; + --branch) + shift + branch="$1" ;; + --) + shift; break ;; + -*) + usage ;; + *) + break ;; + esac + shift +done + +name="$1" +test -n "$name" || usage + +if test -n "$branch" && test -z "$inherit" +then + die "fatal: --branch requires --inherit" +fi + +parent_dir="$(pwd)" +parent_remote="$(git remote get-url origin 2>/dev/null)" || parent_remote= + +if test -e "$name" +then + die "fatal: '$name' already exists" +fi + +mkdir "$name" || die "fatal: could not create directory '$name'" + +if ! echo "$name/" >> "$parent_dir/.gitignore" 2>/dev/null +then + rm -rf "$name" + die "fatal: could not update .gitignore" +fi + +cd "$name" || die "fatal: could not enter directory '$name'" + +if ! git init +then + rm -rf "$parent_dir/$name" + die "fatal: could not initialize repository in '$name'" +fi + +if test -n "$parent_remote" +then + git remote add parent "$parent_remote" +else + git remote add parent "$parent_dir" +fi + +if test -n "$inherit" +then + git fetch parent || die "fatal: could not fetch from parent" + if test -n "$branch" + then + git checkout -b "$branch" "parent/$branch" || + die "fatal: could not checkout branch '$branch'" + else + git checkout -b main parent/HEAD 2>/dev/null || + git checkout -b main "parent/$(git remote show parent | sed -n 's/.*HEAD branch: //p')" 2>/dev/null || + echo "warning: could not determine parent HEAD, starting empty" + fi +else + echo "# $name" > README.md + git add README.md + git commit -q -m "Initial commit" +fi + +echo "" +echo "Created son repository '$name'" +echo " parent: ${parent_remote:-$parent_dir}" +echo " inherit: ${inherit:-no}"
Signature · 1 line
gitgitgadget
PATCH 2/5 git-son: register in Makefile and meson build system
In reply to Evan Haque via GitGitGadgetFrom: Evan Haque <[email protected]>
Add git-son.sh to SCRIPT_SH in the Makefile and to the scripts_sh array in meson.build so that the script is installed alongside the other shell-based Git commands during "make install" and meson builds.
Also add /git-son to .gitignore so that the build artifact produced from git-son.sh is not flagged as an untracked file.
--- .gitignore | 1 + Makefile | 1 + meson.build | 1 + 3 files changed, 3 insertions(+)
.gitignore +1 -0
@@ -164,6 +164,7 @@ /git-show-branch /git-show-index /git-show-ref +/git-son /git-sparse-checkout /git-stage /git-stash
Makefile +1 -0
@@ -728,6 +728,7 @@ SCRIPT_SH += git-merge-resolve.sh SCRIPT_SH += git-mergetool.sh SCRIPT_SH += git-quiltimport.sh SCRIPT_SH += git-request-pull.sh +SCRIPT_SH += git-son.sh SCRIPT_SH += git-submodule.sh SCRIPT_SH += git-web--browse.sh
meson.build +1 -0
@@ -1973,6 +1973,7 @@ scripts_sh = [ 'git-mergetool.sh', 'git-quiltimport.sh', 'git-request-pull.sh', + 'git-son.sh', 'git-sh-i18n.sh', 'git-sh-setup.sh', 'git-submodule.sh',
Signature · 1 line
gitgitgadget
PATCH 3/5 git-son: add to command list as mainporcelain
In reply to Evan Haque via GitGitGadgetFrom: Evan Haque <[email protected]>
Register git-son in command-list.txt as a mainporcelain command so that it appears in "git help" output and is discoverable through the standard help machinery.
--- command-list.txt | 1 + 1 file changed, 1 insertion(+)
command-list.txt +1 -0
@@ -186,6 +186,7 @@ git-show mainporcelain info git-show-branch ancillaryinterrogators complete git-show-index plumbinginterrogators git-show-ref plumbinginterrogators +git-son mainporcelain git-sparse-checkout mainporcelain git-stage complete git-stash mainporcelain
Signature · 1 line
gitgitgadget
PATCH 4/5 git-son: add documentation
In reply to Evan Haque via GitGitGadgetFrom: Evan Haque <[email protected]>
Add a man page (git-son.adoc) documenting the synopsis, options, and usage examples for the new command. Register the page in Documentation/meson.build so it is built by the meson doc target.
--- Documentation/git-son.adoc | 64 ++++++++++++++++++++++++++++++++++++++ Documentation/meson.build | 1 + 2 files changed, 65 insertions(+) create mode 100644 Documentation/git-son.adoc
Documentation/git-son.adoc new file +64 -0
@@ -0,0 +1,64 @@ +git-son(1) +========== + +NAME +---- +git-son - Create an independent child repository that knows its parent + +SYNOPSIS +-------- +[verse] +'git son' [--inherit] [--branch <branch>] <name> + +DESCRIPTION +----------- + +Create a new independent Git repository inside the current working +tree as a subdirectory named `<name>`. Unlike a submodule, the child +repository is not tracked by the parent; instead, `<name>/` is added +to the parent's `.gitignore`. + +The child repository is configured with a remote called `parent` +pointing back to the parent repository's origin URL (or local path +if no origin is set), allowing the child to fetch from the parent +at any time. + +OPTIONS +------- +--inherit:: + Fetch the parent's history into the child repository at + creation time. Without this flag, the child starts with a + single initial commit. + +--branch <branch>:: + When used with `--inherit`, check out the given branch from + the parent instead of the default branch. This option + requires `--inherit`. + +<name>:: + The name of the subdirectory (and child repository) to create. + Must not already exist. + +EXAMPLES +-------- + +Create a simple child repository: + + git son my-tool + +Create a child that inherits the parent's history: + + git son --inherit my-fork + +Create a child starting from a specific parent branch: + + git son --inherit --branch feature my-experiment + +Later, from within the child, fetch updates from the parent: + + cd my-tool + git fetch parent + +GIT +--- +Part of the linkgit:git[1] suite
Documentation/meson.build +1 -0
@@ -139,6 +139,7 @@ manpages = { 'git-show-ref.adoc' : 1, 'git-show.adoc' : 1, 'git-sh-setup.adoc' : 1, + 'git-son.adoc' : 1, 'git-sparse-checkout.adoc' : 1, 'git-stage.adoc' : 1, 'git-stash.adoc' : 1,
Signature · 1 line
gitgitgadget
PATCH 5/5 git-son: add tests
In reply to Evan Haque via GitGitGadgetFrom: Evan Haque <[email protected]>
Add t5151-son.sh with nine test cases covering:
- basic child repository creation
- parent remote configuration in the child
- .gitignore update in the parent
- initial commit presence in the child
- failure when the target directory already exists
- --branch without --inherit is rejected cleanly
- no leftover directory on validation failure
- --inherit fetches parent history
Register the test in t/meson.build so the meson build system discovers and runs it.
--- t/meson.build | 1 + t/t5151-son.sh | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100755 t/t5151-son.sh
t/meson.build +1 -0
@@ -591,6 +591,7 @@ integration_tests = [ 't5004-archive-corner-cases.sh', 't5100-mailinfo.sh', 't5150-request-pull.sh', + 't5151-son.sh', 't5200-update-server-info.sh', 't5300-pack-object.sh', 't5301-sliding-window.sh',
t/t5151-son.sh new file +63 -0
63 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Quoted 14 lines · Evan Haque via GitGitGadget
Motivation
==========When spinning off a new project that is related to an existing repository, there is no built-in way to create a child repository that maintains a link back to its parent without the tight coupling of submodules. Submodules pin the child to a specific commit and require the parent to track the child in its index, which is too heavyweight when the child is meant to be fully independent.
Quoted 5 lines
The typical workflow today is manual: git init, git remote add, update .gitignore — three steps that are easy to forget or get wrong. git son automates this and establishes a lightweight convention for the parent-child
relationship: a remote named parent in the child, and nothing in the parent except an ignore rule.
I don’t really understand the motivation, but if your goal is to create another repo with the current one as a remote, how does something like
git clone . child
help you? (I’m pretty sure you can even set the remote name to « parent » if you wish.)
You also didn’t mention worktrees or subtrees, which might be useful for you.
Hi ..
I would motivate to fix in git-submodules in stead. I updated the logic around the ignored setting of a submodule, so it is truly ignored - both in git status(already) and git add, which now explicitly requires --force. It now gives the ability to configure your submodule as 'loosely' by tracking branches without the friction of git add is staging it all the time and you get conflicts in PR/integrations. You can use git submodule status to list the sha1 of the submodule for release etc.
https://github.com/gitgitgadget/git/pull/1987
alias "git-add: Skip submodules with ignore=all unless --force and explicit path used by bicschneider · Pull Request #1987 · gitgitgadget/git"
Please try this update and then describe what is ( still ) missing as i am looking into submodules in general and will try to "fix" the friction points of developers.
Best regards
Claus Schneider
Quoted 28 lines · Ben Knoble
Quoted 15 lines · Evan Haque via GitGitGadget
Motivation
==========When spinning off a new project that is related to an existing repository, there is no built-in way to create a child repository that maintains a link back to its parent without the tight coupling of submodules. Submodules pin the child to a specific commit and require the parent to track the child in its index, which is too heavyweight when the child is meant to be fully independent.
Quoted 5 lines
The typical workflow today is manual: git init, git remote add, update .gitignore — three steps that are easy to forget or get wrong. git son automates this and establishes a lightweight convention for the parent-child
relationship: a remote named parent in the child, and nothing in the parent except an ignore rule.I don’t really understand the motivation, but if your goal is to create another repo with the current one as a remote, how does something like
git clone . child
help you? (I’m pretty sure you can even set the remote name to « parent » if you wish.)
You also didn’t mention worktrees or subtrees, which might be useful for you.
Quoted 10 lines · Evan Haque via GitGitGadget
Motivation
==========When spinning off a new project that is related to an existing repository, there is no built-in way to create a child repository that maintains a link back to its parent without the tight coupling of submodules. Submodules pin the child to a specific commit and require the parent to track the child in its index, which is too heavyweight when the child is meant to be fully independent.
Quoted 5 lines
The typical workflow today is manual: git init, git remote add, update .gitignore — three steps that are easy to forget or get wrong. git son automates this and establishes a lightweight convention for the parent-child
relationship: a remote named parent in the child, and nothing in the parent except an ignore rule.
Although I am personally not interested in this topic even for local repository use, I should point out that using this tool to create this combination of repositories would not be useful for other people who want to clone your arrangement. Other than leaving comments in the parent project, there seems to be no way to specify how or where the child projects should be cloned within the working tree of the parent project.
Since Git is primarily a tool for collaboration among people with their own repositories, I fail see how this feature is something we should ship to all Git users as part of the core distribution.
Thanks.