Skip to content

Commit a91b739

Browse files
committed
Initial import.
0 parents  commit a91b739

4 files changed

Lines changed: 368 additions & 0 deletions

File tree

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2012 Dragonfly Development Inc.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

README.md

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
Git Subsplit
2+
============
3+
4+
Automate and simplify the process of managing one-way read-only
5+
subtree splits.
6+
7+
8+
Install
9+
-------
10+
11+
git-subsplit can be installed and run standalone by executing
12+
`git-subsplit.sh` directly.
13+
14+
git-subsplit can also be installed as a git command by:
15+
16+
./install.sh
17+
18+
19+
Usage
20+
-----
21+
22+
### Initialize
23+
24+
Initialize subsplit with a git repository url:
25+
26+
git subsplit init https://github.com/react-php/react
27+
28+
This will create a working directory for the subsplit. It will contain
29+
a clone of the project's upstream repository.
30+
31+
32+
### Update
33+
34+
Update the subsplit repository with current state of its upstream
35+
repository:
36+
37+
git subsplit update
38+
39+
This command should be called before one or more `publish` commands
40+
are called to ensure that the repository in the working directory
41+
has been updated from its upstream repository.
42+
43+
44+
### Publish
45+
46+
Publish to each subtree split to its own repository:
47+
48+
git subsplit publish \
49+
src/React/EventLoop:git@github.com:react-php/event-loop.git \
50+
--heads=master
51+
52+
The pattern for the splits is `${subPath}:${url}`. Publish can receive
53+
its splits argument as a space separated list:
54+
55+
git subsplit publish "
56+
src/React/EventLoop:git@github.com:react-php/event-loop.git
57+
src/React/Stream/:git@github.com:react-php/stream.git
58+
src/React/Socket/:git@github.com:react-php/socket.git
59+
src/React/Http/:git@github.com:react-php/http.git
60+
src/React/Espresso/:git@github.com:react-php/espresso.git
61+
" --heads=master
62+
63+
This command will create subtree splits of the project's repository
64+
branches and tags. It will then push each branch and tag to the
65+
repository dedicated to the subtree.
66+
67+
68+
#### --update
69+
70+
Passing `--update` to the `publish` command is a shortcut for calling
71+
the `update` command directly.
72+
73+
74+
#### --heads=\<heads\>
75+
76+
To specify a list of heads (instead of letting git-subsplit discover them
77+
from the upstream repository) you can specify them directly. For example:
78+
79+
--heads="master 2.0"
80+
81+
The above will only sync the master and 2.0 branches, no matter which
82+
branches the upstream repository knows about.
83+
84+
85+
#### --no-heads
86+
87+
Do not sync any heads.
88+
89+
90+
#### --tags=\<tags\>
91+
92+
To specify a list of tags (instead of letting git-subsplit discover them
93+
from the upstream repository) you can specify them directly. For example:
94+
95+
--tags="v1.0.0 v1.0.3"
96+
97+
The above will only sync the v1.0.0 and v1.0.3 tags, no matter which
98+
tags the upstream repository knows about.
99+
100+
101+
#### --no-tags
102+
103+
Do not sync any tags.
104+
105+
106+
#### -q,--quiet
107+
108+
As little output as possible.
109+
110+
111+
#### -n,--dry-run
112+
113+
Does not actually publish information to the subsplit repos for each
114+
subtree split. Instead, display the command and execute the command
115+
with `--dry-run` included.
116+
117+
118+
Not Invented Here
119+
-----------------
120+
121+
Inspiration for writing this came from [Guzzle's](http://guzzlephp.org/)
122+
goal of providing components as individually managed packages. Having
123+
seen this already done by [Symfony](http://symfony.com) and liking how
124+
it behaved I wanted to try and see if I could solve this problem in a
125+
general case so more people could take advantage of this workflow.
126+
127+
Much time was spent checking out `git-subtree` and scripts written for
128+
managing [React's](http://nodephp.org/) components.
129+
130+
131+
License
132+
-------
133+
134+
MIT, see LICENSE.
135+
136+
137+
Community
138+
---------
139+
140+
If you have questions or want to help out, join us in the
141+
[#dflydev](irc://irc.freenode.net/#dflydev) channel on irc.freenode.net.

git-subsplit.sh

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
#!/bin/bash
2+
#
3+
# git-subsplit.sh: Automate and simplify the process of managing one-way
4+
# read-only subtree splits.
5+
#
6+
# Copyright (C) 2012 Dragonfly Development Inc.
7+
#
8+
if [ $# -eq 0 ]; then
9+
set -- -h
10+
fi
11+
OPTS_SPEC="\
12+
git subsplit init url
13+
git subsplit publish splits --heads=<heads> --tags=<tags> --splits=<splits>
14+
git subsplit update
15+
--
16+
h,help show the help
17+
q quiet
18+
n,dry-run do everything except actually send the updates
19+
work-dir directory that contains the subsplit working directory
20+
21+
options for 'publish'
22+
heads= only publish for listed heads instead of all heads
23+
no-heads do not publish any heads
24+
tags= only publish for listed tags instead of all tags
25+
no-tags do not publish any tags
26+
update fetch updates from repository before publishing
27+
"
28+
eval "$(echo "$OPTS_SPEC" | git rev-parse --parseopt -- "$@" || echo exit $?)"
29+
30+
# We can run this from anywhere.
31+
NONGIT_OK=1
32+
33+
PATH=$PATH:$(git --exec-path)
34+
35+
. git-sh-setup
36+
37+
ANNOTATE=
38+
QUIET=
39+
COMMAND=
40+
SPLITS=
41+
REPO_URL=
42+
WORK_DIR="${PWD}/.subsplit"
43+
HEADS=
44+
TAGS=
45+
DRY_RUN=
46+
47+
subsplit_main()
48+
{
49+
while [ $# -gt 0 ]; do
50+
opt="$1"
51+
shift
52+
case "$opt" in
53+
-q) QUIET=1 ;;
54+
--heads) HEADS="$1"; shift ;;
55+
--no-heads) NO_HEADS=1 ;;
56+
--tags) TAGS="$1"; shift ;;
57+
--no-tags) NO_TAGS=1 ;;
58+
--update) UPDATE=1 ;;
59+
-n) DRY_RUN="--dry-run" ;;
60+
--dry-run) DRY_RUN="--dry-run" ;;
61+
--) break ;;
62+
*) die "Unexpected option: $opt" ;;
63+
esac
64+
done
65+
66+
COMMAND="$1"
67+
shift
68+
69+
case "$COMMAND" in
70+
init)
71+
if [ $# -lt 1 ]; then die "init command requires url to be passed as first argument"; fi
72+
REPO_URL="$1"
73+
shift
74+
subsplit_init
75+
;;
76+
publish)
77+
if [ $# -lt 1 ]; then die "publish command requires splits to be passed as first argument"; fi
78+
SPLITS="$1"
79+
shift
80+
subsplit_publish
81+
;;
82+
update)
83+
subsplit_update
84+
;;
85+
*) die "Unknown command '$COMMAND'" ;;
86+
esac
87+
}
88+
say()
89+
{
90+
if [ -z "$QUIET" ]; then
91+
echo "$@" >&2
92+
fi
93+
}
94+
95+
subsplit_require_work_dir()
96+
{
97+
if [ ! -e "$WORK_DIR" ]
98+
then
99+
die "Working directory not found at ${WORK_DIR}; please run init first"
100+
fi
101+
102+
pushd "$WORK_DIR" >/dev/null
103+
}
104+
105+
subsplit_init()
106+
{
107+
if [ -e "$WORK_DIR" ]
108+
then
109+
die "Working directory already found at ${WORK_DIR}; please remove or run update"
110+
fi
111+
112+
say "Initializing subsplit from origin (${REPO_URL})"
113+
114+
git clone -q "$REPO_URL" "$WORK_DIR" || die "Could not clone repository"
115+
}
116+
117+
subsplit_publish()
118+
{
119+
subsplit_require_work_dir
120+
121+
if [ -n "$UPDATE" ];
122+
then
123+
subsplit_update
124+
fi
125+
126+
if [ -z "$HEADS" ] && [ -z "$NO_HEADS" ]
127+
then
128+
# If heads are not specified and we want heads, discover them.
129+
HEADS="$(git ls-remote origin 2>/dev/null | grep "refs/heads/" | cut -f3- -d/)"
130+
fi
131+
132+
if [ -z "$TAGS" ] && [ -z "$NO_TAGS" ]
133+
then
134+
# If tags are not specified and we want tags, discover them.
135+
TAGS="$(git ls-remote origin 2>/dev/null | grep -v "^{}" | grep "refs/tags/" | cut -f3 -d/)"
136+
fi
137+
138+
for SPLIT in $SPLITS
139+
do
140+
SUBPATH=$(echo "$SPLIT" | cut -f1 -d:)
141+
REMOTE_URL=$(echo "$SPLIT" | cut -f2- -d:)
142+
REMOTE_NAME=$(echo "$SPLIT" | git hash-object --stdin)
143+
144+
if ! git remote | grep "^${REMOTE_NAME}$" >/dev/null
145+
then
146+
git remote add "$REMOTE_NAME" "$REMOTE_URL"
147+
fi
148+
149+
150+
say "Syncing ${SUBPATH} -> ${REMOTE_URL}"
151+
152+
for HEAD in $HEADS
153+
do
154+
LOCAL_BRANCH="${REMOTE_NAME}-branch-${HEAD}"
155+
say " - syncing branch ${HEAD}"
156+
git branch -D "$LOCAL_BRANCH" >/dev/null 2>&1
157+
git subtree split -q --prefix="$SUBPATH" --branch="$LOCAL_BRANCH" "origin/${HEAD}" >/dev/null 2>&1
158+
if [ $? -eq 0 ]
159+
then
160+
PUSH_CMD="git push -q ${DRY_RUN} --force $REMOTE_NAME ${LOCAL_BRANCH}:${HEAD}"
161+
if [ -n "$DRY_RUN" ]
162+
then
163+
echo \# $PUSH_CMD
164+
$PUSH_CMD
165+
else
166+
$PUSH_CMD
167+
fi
168+
fi
169+
done
170+
171+
for TAG in $TAGS
172+
do
173+
LOCAL_TAG="${REMOTE_NAME}-tag-${TAG}"
174+
say " - syncing tag ${TAG}"
175+
git branch -D "$LOCAL_TAG" >/dev/null 2>&1
176+
git subtree split -q --annotate="${ANNOTATE}" --prefix="$SUBPATH" --branch="$LOCAL_TAG" "$TAG" >/dev/null 2>&1
177+
if [ $? -eq 0 ]
178+
then
179+
PUSH_CMD="git push -q ${DRY_RUN} --force ${REMOTE_NAME} ${LOCAL_TAG}:refs/tags/${TAG}"
180+
if [ -n "$DRY_RUN" ]
181+
then
182+
echo \# $PUSH_CMD
183+
$PUSH_CMD
184+
else
185+
$PUSH_CM
186+
fi
187+
fi
188+
done
189+
done
190+
191+
popd >/dev/null
192+
}
193+
194+
subsplit_update()
195+
{
196+
subsplit_require_work_dir
197+
198+
say "Updating subsplit from origin"
199+
200+
git fetch -q origin
201+
git fetch -q -t origin
202+
203+
popd >/dev/null
204+
}
205+
206+
subsplit_main "$@"

install.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Copy subsplit to where the Git scripts belong.
2+
cp git-subsplit.sh "$(git --exec-path)"/git-subsplit

0 commit comments

Comments
 (0)