* [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
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ 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] 7+ 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
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ 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] 7+ 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
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ 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] 7+ 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
2026-05-26 21:27 ` [PATCH 0/5] git son: add command to create independent child repositories Ben Knoble
5 siblings, 0 replies; 7+ 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] 7+ 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
5 siblings, 0 replies; 7+ 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] 7+ 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
5 siblings, 0 replies; 7+ 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] 7+ messages in thread