* [PATCH 1/5] git-son: add command to create independent child repositories
2026-05-26 16:47 [PATCH 0/5] git son: add command to create independent child repositories Evan Haque via GitGitGadget
@ 2026-05-26 16:47 ` Evan Haque via GitGitGadget
2026-05-26 16:47 ` [PATCH 2/5] git-son: register in Makefile and meson build system Evan Haque via GitGitGadget
` (5 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Evan Haque via GitGitGadget @ 2026-05-26 16:47 UTC (permalink / raw)
To: git; +Cc: Evan Haque, Evan Haque
From: Evan Haque <evanhaque1@gmail.com>
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)
Assisted-by: Claude Opus 4.6
Signed-off-by: Evan Haque <evanhaque1@gmail.com>
---
git-son.sh | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
create mode 100755 git-son.sh
diff --git a/git-son.sh b/git-son.sh
new file mode 100755
index 0000000000..a212c7b69f
--- /dev/null
+++ b/git-son.sh
@@ -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}"
--
gitgitgadget
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 2/5] git-son: register in Makefile and meson build system
2026-05-26 16:47 [PATCH 0/5] git son: add command to create independent child repositories Evan Haque via GitGitGadget
2026-05-26 16:47 ` [PATCH 1/5] git-son: " Evan Haque via GitGitGadget
@ 2026-05-26 16:47 ` Evan Haque via GitGitGadget
2026-05-26 16:47 ` [PATCH 3/5] git-son: add to command list as mainporcelain Evan Haque via GitGitGadget
` (4 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Evan Haque via GitGitGadget @ 2026-05-26 16:47 UTC (permalink / raw)
To: git; +Cc: Evan Haque, Evan Haque
From: Evan Haque <evanhaque1@gmail.com>
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.
Assisted-by: Claude Opus 4.6
Signed-off-by: Evan Haque <evanhaque1@gmail.com>
---
.gitignore | 1 +
Makefile | 1 +
meson.build | 1 +
3 files changed, 3 insertions(+)
diff --git a/.gitignore b/.gitignore
index 4da58c6754..5f329179c8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -164,6 +164,7 @@
/git-show-branch
/git-show-index
/git-show-ref
+/git-son
/git-sparse-checkout
/git-stage
/git-stash
diff --git a/Makefile b/Makefile
index fb50c57e4f..4791f47af1 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/meson.build b/meson.build
index 052c81f288..538bd4025f 100644
--- a/meson.build
+++ b/meson.build
@@ -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',
--
gitgitgadget
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 3/5] git-son: add to command list as mainporcelain
2026-05-26 16:47 [PATCH 0/5] git son: add command to create independent child repositories Evan Haque via GitGitGadget
2026-05-26 16:47 ` [PATCH 1/5] git-son: " Evan Haque via GitGitGadget
2026-05-26 16:47 ` [PATCH 2/5] git-son: register in Makefile and meson build system Evan Haque via GitGitGadget
@ 2026-05-26 16:47 ` Evan Haque via GitGitGadget
2026-05-26 16:47 ` [PATCH 4/5] git-son: add documentation Evan Haque via GitGitGadget
` (3 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Evan Haque via GitGitGadget @ 2026-05-26 16:47 UTC (permalink / raw)
To: git; +Cc: Evan Haque, Evan Haque
From: Evan Haque <evanhaque1@gmail.com>
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.
Assisted-by: Claude Opus 4.6
Signed-off-by: Evan Haque <evanhaque1@gmail.com>
---
command-list.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/command-list.txt b/command-list.txt
index 21b802c420..880177e0fd 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -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
--
gitgitgadget
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 4/5] git-son: add documentation
2026-05-26 16:47 [PATCH 0/5] git son: add command to create independent child repositories Evan Haque via GitGitGadget
` (2 preceding siblings ...)
2026-05-26 16:47 ` [PATCH 3/5] git-son: add to command list as mainporcelain Evan Haque via GitGitGadget
@ 2026-05-26 16:47 ` Evan Haque via GitGitGadget
2026-05-26 16:47 ` [PATCH 5/5] git-son: add tests Evan Haque via GitGitGadget
` (2 subsequent siblings)
6 siblings, 0 replies; 9+ messages in thread
From: Evan Haque via GitGitGadget @ 2026-05-26 16:47 UTC (permalink / raw)
To: git; +Cc: Evan Haque, Evan Haque
From: Evan Haque <evanhaque1@gmail.com>
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.
Assisted-by: Claude Opus 4.6
Signed-off-by: Evan Haque <evanhaque1@gmail.com>
---
Documentation/git-son.adoc | 64 ++++++++++++++++++++++++++++++++++++++
Documentation/meson.build | 1 +
2 files changed, 65 insertions(+)
create mode 100644 Documentation/git-son.adoc
diff --git a/Documentation/git-son.adoc b/Documentation/git-son.adoc
new file mode 100644
index 0000000000..17ec992bfd
--- /dev/null
+++ b/Documentation/git-son.adoc
@@ -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
diff --git a/Documentation/meson.build b/Documentation/meson.build
index f4854f802d..1ae7e5f644 100644
--- a/Documentation/meson.build
+++ b/Documentation/meson.build
@@ -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,
--
gitgitgadget
^ permalink raw reply related [flat|nested] 9+ messages in thread* [PATCH 5/5] git-son: add tests
2026-05-26 16:47 [PATCH 0/5] git son: add command to create independent child repositories Evan Haque via GitGitGadget
` (3 preceding siblings ...)
2026-05-26 16:47 ` [PATCH 4/5] git-son: add documentation Evan Haque via GitGitGadget
@ 2026-05-26 16:47 ` Evan Haque via GitGitGadget
2026-05-26 21:27 ` [PATCH 0/5] git son: add command to create independent child repositories Ben Knoble
2026-06-01 5:33 ` Junio C Hamano
6 siblings, 0 replies; 9+ messages in thread
From: Evan Haque via GitGitGadget @ 2026-05-26 16:47 UTC (permalink / raw)
To: git; +Cc: Evan Haque, Evan Haque
From: Evan Haque <evanhaque1@gmail.com>
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.
Assisted-by: Claude Opus 4.6
Signed-off-by: Evan Haque <evanhaque1@gmail.com>
---
t/meson.build | 1 +
t/t5151-son.sh | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 64 insertions(+)
create mode 100755 t/t5151-son.sh
diff --git a/t/meson.build b/t/meson.build
index fd955f44ef..523062df66 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -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',
diff --git a/t/t5151-son.sh b/t/t5151-son.sh
new file mode 100755
index 0000000000..826cbbfa66
--- /dev/null
+++ b/t/t5151-son.sh
@@ -0,0 +1,63 @@
+#!/bin/sh
+
+test_description='Test git son command.'
+
+GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
+export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
+
+. ./test-lib.sh
+
+test_expect_success 'setup parent repository' '
+ echo "parent content" >file.txt &&
+ git add file.txt &&
+ git commit -m "Initial parent commit"
+'
+
+test_expect_success 'son creates child repository' '
+ git son my-child &&
+ test -d my-child &&
+ test -d my-child/.git
+'
+
+test_expect_success 'son sets parent remote in child' '
+ (
+ cd my-child &&
+ git remote get-url parent
+ )
+'
+
+test_expect_success 'son adds child to parent .gitignore' '
+ grep "my-child/" .gitignore
+'
+
+test_expect_success 'son child has initial commit' '
+ (
+ cd my-child &&
+ test $(git log --oneline | wc -l) -eq 1
+ )
+'
+
+test_expect_success 'son fails if target already exists' '
+ test_must_fail git son my-child
+'
+
+test_expect_success 'son with --branch requires --inherit' '
+ test_must_fail git son --branch main branch-child
+'
+
+test_expect_success 'son with --branch leaves no directory on failure' '
+ ! test -e branch-child
+'
+
+test_expect_success 'son with --inherit fetches parent history' '
+ git init --bare "$TRASH_DIRECTORY/parent.git" &&
+ git push "$TRASH_DIRECTORY/parent.git" main &&
+ git remote add origin "file://$TRASH_DIRECTORY/parent.git" &&
+ git son --inherit inherited-child &&
+ (
+ cd inherited-child &&
+ git log --oneline parent/main
+ )
+'
+
+test_done
--
gitgitgadget
^ permalink raw reply related [flat|nested] 9+ messages in thread* Re: [PATCH 0/5] git son: add command to create independent child repositories
2026-05-26 16:47 [PATCH 0/5] git son: add command to create independent child repositories Evan Haque via GitGitGadget
` (4 preceding siblings ...)
2026-05-26 16:47 ` [PATCH 5/5] git-son: add tests Evan Haque via GitGitGadget
@ 2026-05-26 21:27 ` Ben Knoble
2026-05-29 12:35 ` Claus Schneider
2026-06-01 5:33 ` Junio C Hamano
6 siblings, 1 reply; 9+ messages in thread
From: Ben Knoble @ 2026-05-26 21:27 UTC (permalink / raw)
To: Evan Haque via GitGitGadget; +Cc: git, Evan Haque
> Le 26 mai 2026 à 13:08, Evan Haque via GitGitGadget <gitgitgadget@gmail.com> a écrit :
>
>
> 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.
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.
^ permalink raw reply [flat|nested] 9+ messages in thread* Re: [PATCH 0/5] git son: add command to create independent child repositories
2026-05-26 21:27 ` [PATCH 0/5] git son: add command to create independent child repositories Ben Knoble
@ 2026-05-29 12:35 ` Claus Schneider
0 siblings, 0 replies; 9+ messages in thread
From: Claus Schneider @ 2026-05-29 12:35 UTC (permalink / raw)
To: Ben Knoble; +Cc: Evan Haque via GitGitGadget, git, Evan Haque
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
On Tue, May 26, 2026 at 11:29 PM Ben Knoble <ben.knoble@gmail.com> wrote:
>
>
> > Le 26 mai 2026 à 13:08, Evan Haque via GitGitGadget <gitgitgadget@gmail.com> a écrit :
> >
> >
> > 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.
>
> 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.
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 0/5] git son: add command to create independent child repositories
2026-05-26 16:47 [PATCH 0/5] git son: add command to create independent child repositories Evan Haque via GitGitGadget
` (5 preceding siblings ...)
2026-05-26 21:27 ` [PATCH 0/5] git son: add command to create independent child repositories Ben Knoble
@ 2026-06-01 5:33 ` Junio C Hamano
6 siblings, 0 replies; 9+ messages in thread
From: Junio C Hamano @ 2026-06-01 5:33 UTC (permalink / raw)
To: Evan Haque via GitGitGadget; +Cc: git, Evan Haque
"Evan Haque via GitGitGadget" <gitgitgadget@gmail.com> writes:
> 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.
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.
^ permalink raw reply [flat|nested] 9+ messages in thread