Subrepo command wasn't checked in correctly (#1737)

* Remove subrepo

* Fix subrepo
This commit is contained in:
Rich Chiodo 2024-11-18 09:50:34 -08:00 committed by GitHub
parent 73be8fb5df
commit f4ba976121
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
189 changed files with 12841 additions and 1 deletions

@ -1 +0,0 @@
Subproject commit cce3d93e1ad5dbb93c54fb78bd0eeec576eb98fb

1
build/git-subrepo/.gitattributes vendored Normal file
View file

@ -0,0 +1 @@
* text eol=lf

View file

@ -0,0 +1,12 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = https://github.com/ingydotnet/git-subrepo.git
branch = 0.4.1
commit = a04d8c2e55c31931d66b5c92ef6d4fe4c59e3226
parent = b341532bdd2eacaddca2d9c63421ef3ac9d3d239
method = merge
cmdver = 0.4.1

38
build/git-subrepo/.rc Normal file
View file

@ -0,0 +1,38 @@
#!bash
#------------------------------------------------------------------------------
#
# This is the `git-subrepo` initialization script.
#
# This script turns on the `git-subrepo` Git subcommand, its manpages and TAB
# completion for the *Bash* and *zsh* shells.
#
# Just add a line like this to your shell startup configuration:
#
# source /path/to/git-subrepo/.rc
#
#------------------------------------------------------------------------------
if [[ ${BASH_VERSION} ]]; then
if [[ ${BASH_VERSINFO[0]} -lt 4 ]] ; then
echo "The git-subrepo command requires that 'Bash 4+' is installed."
echo "It doesn't need to be your shell, but it must be in your PATH."
if [[ $OSTYPE == darwin* ]]; then
echo "You appear to be on macOS."
echo "Try: 'brew install bash'."
echo "This will not change your user shell, it just installs 'Bash 5.x'."
fi
return
fi
fi
[[ -n ${ZSH_VERSION-} ]] &&
GIT_SUBREPO_ROOT=$0 ||
GIT_SUBREPO_ROOT=$BASH_SOURCE
[[ $GIT_SUBREPO_ROOT =~ ^/ ]] ||
GIT_SUBREPO_ROOT=$PWD/$GIT_SUBREPO_ROOT
export GIT_SUBREPO_ROOT=$(cd "$(dirname "$GIT_SUBREPO_ROOT")"; pwd)
export PATH=$GIT_SUBREPO_ROOT/lib:$PATH
export MANPATH=$GIT_SUBREPO_ROOT/man:$MANPATH
source "$GIT_SUBREPO_ROOT/share/enable-completion.sh"

View file

@ -0,0 +1,31 @@
language: shell
matrix:
include:
- name: Git 2.21 on Ubuntu Xenial
dist: xenial
language: shell
# === This will need to removed when Travis drops support ===
- name: Git 2.15.1 on Ubuntu Trusty
dist: trusty
language: shell
- name: Git on Mac
os: osx
osx_image: xcode8.3
addons:
homebrew:
packages:
- bash
update: true
script:
# NOTE: we have to make sure we're on a branch (rather than on a detached HEAD)
# because tests rely on it.
- git checkout -b travis-ci-dummy-branch-name
- GIT_COMMITTER_NAME=Bob
GIT_COMMITTER_EMAIL=bob@blob.net
GIT_AUTHOR_NAME='Bob Blob'
GIT_AUTHOR_EMAIL=bob@blob.net
PROVEOPT=-v make test

110
build/git-subrepo/Changes Normal file
View file

@ -0,0 +1,110 @@
---
version: 0.4.1
date: Thu Jan 9 17:11:21 CST 2020
- Fix Bash version error messages and add to .rc
- Nicer YAML formatting in .travis.yml
- Wrap a long line
- Update the docs
- Force `make update` to always update docs
- Don't use XXX in perl stuff
- Add testing on MacOS
- Remove conflicting -C from install -d commands.
- Update version requirement documentation
- Correct error message in branch
- Use topo-order in subrepo branch
- Make “git subrepo clean -f ...” delete refs correctly
- Fix #410 Push empty repositories with recent git versions
- Make subrepo work when run in a worktree
- Simplify finding subrepos
- Ask git to find the .gitrepo files
- Doc: fix sentence repetition
- Fix typos
- Fixed typo
- Travis CI not checking out a branch.
---
version: 0.4.0
date: Thu Nov 8 12:26:38 CET 2018
changes:
- Fix #325 Do not squash main repo commits
- Improve error message for worktree handling
- Make version checking portable. #307
- #307, improve version check
- #307, update version requirement
- Fix part #308, Add stderr output if commands fail
- Fix #306: Add check to prevent following commits with no .gitrepo
- Remove dry-run flag as it's not implemented. Make sure branch --force delete worktree
- Fix #296, Replace --first-parent with --ancestry-path
- Fix #291, specify Trusty host to get new 2.x git
- Fix #258, add --no-tags to git fetch
- Test that no remotes are created during clone
- #257 Remove remote creation to avoid problems with fetch --all
- (origin/issue/150_to_0.4.0) Fix remove-worktree, remove unused parameters
- Regenerate completion files
- filter-branch operation should not be done on HEAD
- Cleanup push and add hint to push directly after pull
- Simplify cleanup and add worktree to status
- Add --method option to init/clone, add a 'config' command
- Updated unit tests to support the new logic
- Use 'git worktree' for merge/rebase
- Update docs to reflect how things should work
- Make it possible to specify commit messages
- Redesign, trash the tree hash approach and use merges instead
- Add release branches to travis-ci
- Add --method option to init/clone, add a 'config' command
- Detect multiple pulls, use -u flag to decide
- Don't reuse previous commit message when using --all
- Update the docs for pull and push
- Update error messages when failing merge/rebase
- Fix env var bug in test/push.t
- Do not overwrite author information
---
version: 0.3.1
date: Tue Jan 3 23:08:56 PST 2017
changes:
- Updated release for homebrew
- Fix #192
---
version: 0.3.0
date: Wed Dec 2 19:19:43 PST 2015
changes:
- Fix issue #98 and host of others (89, 91, 95, 96)
- Adds support for the merge-base command
- Adds stability to many commands
- Command completion updates
- Rename `init` to `.rc`
- @grimmySwe++ @dzzh++ @jrosdahl++ @perlpunk++
---
version: 0.2.3
date: Sun Aug 9 13:44:22 PDT 2015
changes:
- Fix issues #75 and #76
---
version: 0.2.2
date: Wed Jul 22 09:45:13 PDT 2015
changes:
- Added the `init` subcommand
- Applied doc fixes
---
version: 0.2.1
date: Sat Mar 28 07:52:22 PDT 2015
changes:
- Allows subrepo clone to clone to an empty branch; fixes #26.
- Refs in status
- Empty parent set to 'none' in .gitrepo file.
- Bug fixes
---
version: 0.2.0
date: Sat Jan 24 06:22:05 PST 2015
changes:
- Massive overhaul
- .gitrepo files remain the same so backwards compatible
- Introduce the branch and commit subcommands
- The checkout subcommand goes away
- Operations work much smoother like normal Git flow
- Much more testing
- Better doc
---
version: 0.1.0
date: Fri Feb 21 12:25:53 2014 -0800
changes:
- First version

509
build/git-subrepo/Intro.pod Normal file
View file

@ -0,0 +1,509 @@
=pod
=for comment
DO NOT EDIT. This Pod was generated by Swim v0.1.48.
See http://github.com/ingydotnet/swim-pm#readme
=encoding utf8
=head1 Introducing Git Subrepos
There is a new git command called C<subrepo> that is meant to be a solid
alternative to the C<submodule> and C<subtree> commands. All 3 of these
commands allow you to include external repositories (pinned to specific
commits) in your main repository. This is an often needed feature for project
development under a source control system like Git. Unfortunately, the
C<submodule> command is severely lacking, and the C<subtree> command (an
attempt to make things better) is also very flawed. Fortunately, the
C<subrepo> command is here to save the day.
This article will discuss how the previous commands work, and where they go
wrong, while explaining how the new C<subrepo> command fixes the issues.
It should be noted that there are 3 distinct roles (ways people use repos)
involved in discussing this topic:
=over
=item * B<owner> — The primary author and repo owner
=item * B<collaborators> — Other developers who contribute to the repo
=item * B<users> — People who simply use the repo software
=back
=head2 Introducing C<subrepo>
While the main point is to show how subrepo addresses the shortcomings
of submodule and subtree, I'll start by giving a quick intro to the
subrepo command.
Let's say that you have a project repo called 'freebird' and you want to have
it include 2 other external repos, 'lynyrd' and 'skynyrd'. You would do the
following:
git clone git@github.com/you/freebird
cd freebird
git subrepo clone git@github.com/you/lynyrd ext/lynyrd
git subrepo clone git@github.com/you/skynyrd ext/skynyrd --branch=1975
What these commands do (at a high level) should be obvious. They "clone" (add)
the repos content into the subdirectories you told them to. The details of
what is happening to your repo will be discussed later, but adding new
subrepos is easy. If you need to update the subrepos later:
git subrepo pull ext/lynyrd
git subrepo pull ext/skynyrd --branch=1976
The lynyrd repo is tracking the upstream master branch, and you've changed the
skynyrd subrepo to the 1976 branch. Since these subrepos are owned by 'you',
you might want to change them in the context of your freebird repo. When
things are working, you can push the subrepo changes back:
git subrepo push ext/lynyrd
git subrepo push ext/skynyrd
Looks simple right? It's supposed to be. The intent of C<subrepo> is to do the
right things, and to not cause problems.
Of course there's more to it under the hood, and that's what the rest of this
article is about.
=head2 Git Submodules
Submodules tend to receive a lot of bad press. Here's some of it:
=over
=item * L<http://ayende.com/blog/4746/the-problem-with-git-submodules>
=item * L<http://somethingsinistral.net/blog/git-submodules-are-probably-not-the-answer/>
=item * L<http://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/>
=back
A quick recap of some of the good and bad things about submodules:
Good:
=over
=item * Use an external repo in a dedicated subdir of your project.
=item * Pin the external repo to a specific commit.
=item * The C<git-submodule> command is a core part of the Git project.
=back
Bad:
=over
=item * Users have to know a repo has submodules.
=item * Users have to get the subrepos manually.
=item * Pulling a repo with submodules won't pull in the new submodule changes.
=item * A submodule will break if the referenced repo goes away.
=item * A submodule will break if a forced push removes the referenced commit.
=item * Can't use different submodules/commits per main project branch.
=item * Can't "try out" a submodule on alternate branch.
=item * Main repo can be pushed upstream pointing to unpushed submod commits.
=item * Command capability differs across Git versions.
=item * Often need to change remote url, to push submodule changes upstream.
=item * Removing or renaming a submodule requires many steps.
=back
Internally, submodules are a real mess. They give the strong impression of
being bolted on, well after Git was designed. Some commands are aware of the
existence of submodules (although usually half-heartedly), and many commands
are oblivious. For instance the git-clone command has a C<--recursive> option
to clone all subrepos, but it's not a default, so you still need to be aware
of the need. The git-checkout command does nothing with the submodules, even
if they are intended to differ across branches.
Let's talk a bit about how submodules are implemented in Git. Information
about them is stored in 3 different places (in the top level repo directory):
=over
=item * C<.gitmodules>
=item * C<.git/config>
=item * C<.git/modules> — The submodule repo's meta data (refs/objects)
=back
So some of the information lives in the repo history (.gitmodules), but other
info (.git/) is only known to the local repo.
In addition, the submodule introduces a new low level concept, to the
commitI<tree>blob graph. Normally a git tree object points to blob (file)
objects and more tree (directory) objects. Submodules have tree objects point
to B<commit> objects. While this seems clever and somewhat reasonable, it also
means that every other git command (which was built on the super clean Git
data model) has to be aware of this new possibility (and deal with it
appropriately).
The point is that, while submodules are a real need, and a lot of work has
gone into making them work decently, they are essentially a kludge to the Git
model, and it is quite understandable why they haven't worked out as well as
people would expect.
NOTE: Submodules I<are> getting better with each release of Git, but it's
still an endless catch up game.
=head2 Git Subtrees
One day, someone decided to think different. Instead of pointing to external
repos, why not just include them into the main repo (but also allow them to be
pulled and pushed separately as needed)?
At first this may feel like a wasteful approach. Why keep other repos
physically inside your main one? But if you think about it abstractly, what's
the difference? You want your users and collaborators to have all this code
because your project needs it. So why worry about how it happens? In the end,
the choice is yours, but I've grown very comfortable with this concept and
I'll try to justify it well. I should note that the first paragraph of the
C<submodule> doc suggests considering this alternative.
The big win here is that you can do this using the existing git model. Nothing
new is added. You are just adding commits to a history. You can do it
different on every branch. You can merge branches sensibly.
The git-subtree command seems to have been inspired by Git's subtree merge
strategy, which it uses internally, and possibly got its name from. A subtree
merge allows you to take a completely separate Git history and make it be a
subdirectory of your repo.
Adding a subtree was the easy part. All that needed to be done after that was
to figure out a way to pull upstream changes and push local ones back
upstream. And that's what the C<git-subtree> command does.
So what's the problem with git-subtree then?
Well unfortunately, it drops a few balls. The main problems come down to an
overly complicated commandline UX, poor collaborator awareness, and a fragile
and messy implementation.
Good:
=over
=item * Use an external repo in a dedicated subdir of your project.
=item * Pin the external repo to a specific commit.
=item * Users get everything with a normal clone command.
=item * Users don't need to know that subtrees are involved.
=item * Can use different submodules/commits per main project branch.
=item * Users don't need the subtree command. Only owners and collaborators.
=back
Bad:
=over
=item * The remote url and branch info is not saved (except in the history).
=item * Owners and collaborators have to enter the remote for every command.
=item * Collaborators aren't made aware that subtrees are involved.
=item * Pulled history is not squashed by default.
=item * Creates a messy historical view. (See below)
=item * Bash code is complicated.
=item * Only one test file. Currently is failing.
=back
As you can see, subtree makes quite a few things better, but after trying it
for a while, the experience was more annoying than submodules. For example,
consider this usage:
$ git subtree add --squash --prefix=foo git@github.com:my/thing mybranch
# weeks go by…
$ git subtree pull --squash --prefix=foo git@github.com:my/thing mybranch
# time to push local subtree changes back upstream
$ git subtree push --prefix=foo git@github.com:my/thing mybranch
The first thing you notice is the overly verbose syntax. It's justified in the
first command, but in the other 2 commands I really don't want to have to
remember what the remote and branch are that I'm using.
Moreover, my collaborators have no idea that subtrees are involved, let alone
where they came from.
Consider the equivalent subrepo commands:
$ git subrepo clone git@github.com:my/thing foo -b mybranch
$ git subrepo pull foo
$ git subrepo push foo
Collaborators see a file called 'foo/.gitrepo', and know that the subdir is a
subrepo. The file contains all the information needed by future commands
applied to that subrepo.
=head2 Git Subrepos
Now is a good time to dive into the techinical aspects of the C<subrepo>
command, but first let me explain how it came about.
As you may have surmised by now, I am the author of git-subrepo. I'd used
submodules on and off for years, and when I became aware of subtree I gave it
a try, but I quickly realized its problems. I decided maybe it could be
improved. I decided to write down my expected commandline usage and my ideals
of what it would and would not do. Then I set off to implement it. It's been a
long road, but what I ended up with was even better than what I wanted from
the start.
Let's review the Goods and Bads:
Good:
=over
=item * Use an external repo in a dedicated subdir of your project.
=item * Pin the external repo to a specific commit.
=item * Users get everything with a normal clone command.
=item * Users don't need to know that subrepos are involved.
=item * Can use different submodules/commits per main project branch.
=item * Meta info is kept in an obvious place.
=item * Everyone knows when a subdir is a subrepo.
=item * Commandline UX is minimal and intuitive.
=item * Pulled history is always squashed out locally.
=item * Pushed history is kept intact.
=item * Creates a clean historical view. (See below)
=item * Bash code is very simple and easy to follow.
=item * Comprehensive test suite. Currently passing on travis:
=back
=for html
<a href="https://travis-ci.org/ingydotnet/git-subrepo"><img src="https://travis-ci.org/ingydotnet/git-subrepo.png" alt="git-subrepo"></a>
Bad:
=over
=item * --Subrepo is very new.-- (no longer true)
=item * --Not well tested in the wild.-- (no longer true)
=back
This review may seem somewhat slanted, but I honestly am not aware of any
"bad" points that I'm not disclosing. That said, I am sure time will reveal
bugs and shortcomings. Those can usually be fixed. Hopefully the B<model> is
correct, because that's harder to fix down the road.
OK. So how does it all work?
There are 3 main commands: cloneI<pull>push. Let's start with the clone
command. This is the easiest part. You give it a remote url, possibly a new
subdir to put it, and possibly a remote branch to use. I say possibly, because
the command can guess the subdir name (just like the git-clone command does),
and the branch can be the upstream default branch.
Given this we do the following steps internally:
=over
=item * Fetch the remote content (for a specific refspec)
=item * Read the remote head tree into the index
=item * Checkout the index into the new subdir
=item * Create a new subrepo commit object for the subdir content
=item * Add a state file called .gitrepo to the new subrepo/subdir
=item * Amend the merge commit with this new file
=back
This process adds something like this to the top of your history:
* 9b6ddc9 git subrepo clone git@github.com:you/foo.git foo/
* 37c61a5 Previous head commit of your repo
The entire history has been squashed down into one commit, and placed on
top of your history. This is important as it keeps your history as clean
as possible. You don't need to have the subrepo history in your main
project, since it is immutably available elsewhere, and you have a pointer
to that place.
The new foo/.gitrepo file looks like this:
[subrepo]
remote = git@github.com:you/foo.git
branch = master
commit = 14c96c6931b41257b2d42b2edc67ddc659325823
parent = 37c61a5a234f5dd6f5c2aec037509f50d3a79b8f
cmdver = 0.1.0
It contains all the info needed now and later. Note that the repo url is the
generally pushable form, rather than the publically readable (L<https://…)>
form. This is the best practice. Users of your repo don't need access to this
url, because the content is already in your repo. Only you and your
collaborators need this url to pull/push in the future.
The next command is the pull command. Normally you just give it the subrepo's
subdir path (although you can change the branch with -b), and it will get the
other info from the subdir/.gitrepo file.
The pull command does these steps:
=over
=item * Fetch the upstream content
=item * Check if anything needs pulling
=item * Create a branch of local subrepo commits since last pull
=item * Rebase this branch onto the upstream commits
=item * Commit the HEAD of the rebased content
=item * Update/amend the .gitrepo file
=back
=head3 Clean History
I've talked a bit about clean history but let me show you a comparison between
subrepo and subtree. Let's run this command sequence using both methods. Note
the differences between I<both> the command syntax required, and the branch
history produced.
Subrepo first:
$ git subrepo clone git@github.com:user/abc
$ git subrepo clone git@github.com:user/def xyz
$ git subrepo pull abc
$ git subrepo pull xyz
The resulting history is:
* b1f60cc subrepo pull xyz
* 4fb0276 subrepo pull abc
* bcef2a0 subrepo clone git@github.com:user/def xyz
* bebf0db subrepo clone git@github.com:user/abc
* 64eeaa6 (origin/master, origin/HEAD) O HAI FREND
Compare that to B<subtree>. This:
$ git subtree add abc git@github.com:user/abc master
$ git subtree add xyz git@github.com:user/def master
$ git subtree pull abc git@github.com:user/abc master
$ git subtree pull xyz git@github.com:user/def master
Produces this:
* 739e45a (HEAD, master) Merge commit '5f563469d886d53e19cb908b3a64e4229f88a2d1'
|\
| * 5f56346 Squashed 'xyz/' changes from 08c7421..365409f
* | 641f5e5 Merge commit '8d88e90ce5f653ed2e7608a71b8693a2174ea62a'
|\ \
| * | 8d88e90 Squashed 'abc/' changes from 08c7421..365409f
* | | 1703ed2 Merge commit '0e091b672c4bbbbf6bc4f6694c475d127ffa21eb' as 'xyz'
|\ \ \
| | |/
| |/|
| * | 0e091b6 Squashed 'xyz/' content from commit 08c7421
| /
* | 07b77e7 Merge commit 'cd2b30a0229d931979ed4436b995875ec563faea' as 'abc'
|\ \
| |/
| * cd2b30a Squashed 'abc/' content from commit 08c7421
* 64eeaa6 (origin/master, origin/HEAD) O HAI FREND
This was from a minimal case. Subtree history (when viewed this way at least)
gets unreasonably ugly fast. Subrepo history, by contrast, always looks as
clean as shown.
The final command, push, bascially just does the pull/rebase dance above
described, and pushes the resulting history back. It does not squash the
commits made locally, because it assumed that when you changed the local
subrepo, you made messages that were intended to eventually be published
back upstream.
=head2 Conflict Resolution
The commands described above can also be done "by hand". If something fails
during a pull or push (generally in the rebasing) then the command will tell
you what to do to finish up.
You might choose to do everything by hand, and do your own merging strategies.
This is perfectly reasonable. The C<subrepo> command offers a few other helper
commands to help you get the job done:
=over
=item * C<fetch> - Fetch the upstream and create a C<< subrepo/remote/<subdir> >> ref.
=item * C<branch> - Create a branch of local subdir commits since the last pull, called C<< subrepo/<subdir> >>.
=item * C<commit> - Commit a merged branch's HEAD back into your repo.
=item * C<status> - Show lots of useful info about the current state of the subrepos.
=item * C<clean> - Remove branches, ref and remotes created by subrepo commands.
=item * C<help> - Read the complete documentation!
=back
=head2 Conclusion
Hopefully by now, you see that submodules are a painful choice with a dubious
future, and that subtree, while a solid idea has many usage issues.
Give C<subrepo> a try. It's painless, easily revertable and just might be what
the doctor ordered.
=head2 Reference Links
=over
=item * L<http://longair.net/blog/2010/06/02/git-submodules-explained/>
=item * L<http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/>
=back
=cut

21
build/git-subrepo/License Normal file
View file

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2013-2020 Ingy döt Net
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,82 @@
# Make sure we have 'git' and it works OK:
ifeq ($(shell which git),)
$(error 'git' is not installed on this system)
endif
# Set variables:
NAME := git-subrepo
LIB := lib/$(NAME)
DOC := doc/$(NAME).swim
MAN1 := man/man1
EXT := $(LIB).d
EXTS := $(shell find $(EXT) -type f) \
$(shell find $(EXT) -type l)
SHARE = share
# Install variables:
PREFIX ?= /usr/local
INSTALL_LIB ?= $(DESTDIR)$(shell git --exec-path)
INSTALL_EXT ?= $(INSTALL_LIB)/$(NAME).d
INSTALL_MAN1 ?= $(DESTDIR)$(PREFIX)/share/man/man1
# Basic targets:
default: help
help:
@echo 'Makefile rules:'
@echo ''
@echo 'test Run all tests'
@echo 'install Install $(NAME)'
@echo 'uninstall Uninstall $(NAME)'
@echo 'env Show environment variables to set'
.PHONY: test
test:
prove $(PROVEOPT:%=% )test/
# Install support:
install:
install -d -m 0755 $(INSTALL_LIB)/
install -C -m 0755 $(LIB) $(INSTALL_LIB)/
install -d -m 0755 $(INSTALL_EXT)/
install -C -m 0755 $(EXTS) $(INSTALL_EXT)/
install -d -m 0755 $(INSTALL_MAN1)/
install -C -m 0644 $(MAN1)/$(NAME).1 $(INSTALL_MAN1)/
# Uninstall support:
uninstall:
rm -f $(INSTALL_LIB)/$(NAME)
rm -fr $(INSTALL_EXT)
rm -f $(INSTALL_MAN1)/$(NAME).1
env:
@echo "export PATH=\"$$PWD/lib:\$$PATH\""
@echo "export MANPATH=\"$$PWD/man:\$$MANPATH\""
# Doc rules:
.PHONY: doc
update: doc compgen
force:
doc: ReadMe.pod Intro.pod $(MAN1)/$(NAME).1
perl pkg/bin/generate-help-functions.pl $(DOC) > \
$(EXT)/help-functions.bash
ReadMe.pod: $(DOC) force
swim --to=pod --wrap --complete $< > $@
Intro.pod: doc/intro-to-subrepo.swim force
swim --to=pod --wrap --complete $< > $@
$(MAN1)/%.1: doc/%.swim Makefile force
swim --to=man --wrap $< > $@
compgen: force
perl pkg/bin/generate-completion.pl bash $(DOC) $(LIB) > \
$(SHARE)/completion.bash
perl pkg/bin/generate-completion.pl zsh $(DOC) $(LIB) > \
$(SHARE)/zsh-completion/_git-subrepo
clean purge:
rm -fr tmp

28
build/git-subrepo/Meta Normal file
View file

@ -0,0 +1,28 @@
=meta: 0.0.2
name: git-subrepo
version: 0.4.1
abstract: Git Submodule Alternative
homepage: https://github.com/ingydotnet/git-subrepo#readme
license: MIT
copyright: 2013-2020
author:
name: Ingy döt Net
email: ingy@ingy.net
github: ingydotnet
twitter: ingydotnet
freenode: ingy
homepage: http://ingy.net
requires:
bash: 4.0.0
git: 2.7.0
test:
cmd: make test
install: make install
devel:
git: git@github.org:ingydotnet/git-subrepo.git
irc: irc.freenode.net/gitcommands
bug: https://github.com/ingydotnet/git-subrepo/issues/

View file

@ -0,0 +1,698 @@
=pod
=for comment
DO NOT EDIT. This Pod was generated by Swim v0.1.48.
See http://github.com/ingydotnet/swim-pm#readme
=encoding utf8
=head1 Name
git-subrepo - Git Submodule Alternative
=for html
<a href="https://travis-ci.org/ingydotnet/git-subrepo"><img src="https://travis-ci.org/ingydotnet/git-subrepo.png" alt="git-subrepo"></a>
=head1 Synopsis
git subrepo -h # Help Overview
git subrepo clone <remote-url> [<subdir>]
git subrepo init <subdir>
git subrepo pull <subdir>
git subrepo push <subdir>
git subrepo fetch <subdir>
git subrepo branch <subdir>
git subrepo commit <subdir>
git subrepo config <subdir>
git subrepo status [<subdir>]
git subrepo clean <subdir>
git subrepo help [<command> | --all]
git subrepo version
git subrepo upgrade
=head1 Description
This git command "clones" an external git repo into a subdirectory of your
repo. Later on, upstream changes can be pulled in, and local changes can be
pushed back. Simple.
=head1 Benefits
This command is an improvement from C<git-submodule> and C<git-subtree>; two
other git commands with similar goals, but various problems.
It assumes there are 3 main roles of people interacting with a repo, and
attempts to serve them all well:
=over
=item * B<owner> - The person who authors/owns/maintains a repo.
=item * B<users> - People who are just using/installing the repo.
=item * B<collaborators> - People who commit code to the repo and subrepos.
=back
The C<git-subrepo> command benefits these roles in the following ways:
=over
=item * Simple and intuitive commandline usage (with tab completion).
=item * Users get your repo and all your subrepos just by cloning your repo.
=item * Users do not need to install C<git-subrepo>, ever.
=item * Collaborators do not need to install unless they want to push/pull.
=item * Collaborators know when a subdir is a subrepo (it has a C<.gitrepo> file).
=item * The C<.gitrepo> file never gets pushed back to the subrepo upstream.
=item * Well named branches and remotes are generated for manual operations.
=item * Owners do not deal with the complications of keeping submodules in sync.
=item * Subrepo repositories can contain subrepos themselves.
=item * Branching with subrepos JustWorks™.
=item * Different branches can have different subrepos in different states, etc.
=item * Moving/renaming/deleting a subrepo subdir JustWorks™.
=item * You can C<init> an existing subdirectory into a subrepo.
=item * Your git history is kept squeaky clean.
=item * Upstream history (clone/pull) is condensed into a single commit.
=item * Pulls can use a C<merge>, C<rebase> or C<force> strategies.
=item * You can see the subrepo history with C<< git log subrepo/<subdir>/fetch >>.
=item * Commits pushed back upstream are B<not> condensed (by default).
=item * Trivial to try any subrepo operations and then reset back.
=item * No configuration required.
=item * Does not introduce history that messes up other git commands.
=item * Fixes known rebase failures with C<git-subtree>.
=back
=head1 Installation
The best short answer is:
git clone https://github.com/ingydotnet/git-subrepo /path/to/git-subrepo
echo 'source /path/to/git-subrepo/.rc' >> ~/.bashrc
The complete "Installation Instructions" can be found below.
Note: git-subrepo needs a git version (> 2.7) that supports worktree:s.
=head1 Commands
All the B<subrepo> commands use names of actual Git commands and try to do
operations that are similar to their Git counterparts. They also attempt to
give similar output in an attempt to make the subrepo usage intuitive to
experienced Git users.
Please note that the commands are I<not> exact equivalents, and do not take
all the same arguments. Keep reading…
=over
=item C<< git subrepo clone <repository> [<subdir>] [-b <branch>] [-f] [-m <msg>] [-e] [--method <merge|rebase>] >>
Add a repository as a subrepo in a subdir of your repository.
This is similar in feel to C<git clone>. You just specify the remote repo url,
and optionally a sub-directory and/or branch name. The repo will be fetched
and merged into the subdir.
The subrepo history is I<squashed> into a single commit that contains the
reference information. This information is also stored in a special file
called C<< <subdir>/.gitrepo >>. The presence of this file indicates that the
directory is a subrepo.
All subsequent commands refer to the subrepo by the name of the
I<subdir>. From the subdir, all the current information about the subrepo
can be obtained.
The C<--force> option will "reclone" (completely replace) an existing subdir.
The C<--method> option will decide how the join process between branches are
performed. The default option is merge.
The C<clone> command accepts the C<--branch=> C<--edit>, C<--force> and C<--
message=> options.
=item C<< git subrepo init <subdir> [-r <remote>] [-b <branch>] [--method <merge|rebase>] >>
Turn an existing subdirectory into a subrepo.
If you want to expose a subdirectory of your project as a published subrepo,
this command will do that. It will split out the content of a normal
subdirectory into a branch and start tracking it as a subrepo. Afterwards your
original repo will look exactly the same except that there will be a C<<
<subdir>/.gitrepo >> file.
If you specify the C<--remote> (and optionally the C<--branch>) option, the
values will be added to the C<< <subdir>/.gitrepo >> file. The C<--remote>
option is the upstream URL, and the C<--branch> option is the upstream branch
to push to. These values will be needed to do a C<git subrepo push> command,
but they can be provided later on the C<push> command (and saved to C<<
<subdir>/.gitrepo >> if you also specify the C<--update> option).
Note: You will need to create the empty upstream repo and push to it on your
own, using C<< git subrepo push <subdir> >>.
The C<--method> option will decide how the join process between branches are
performed. The default option is merge.
The C<init> command accepts the C<--branch=> and C<--remote=> options.
=item C<< git subrepo pull <subdir>|--all [-M|-R|-f] [-m <msg>] [-e] [-b <branch>] [-r <remote>] [-u] >>
Update the subrepo subdir with the latest upstream changes.
The C<pull> command fetches the latest content from the remote branch pointed
to by the subrepo's C<.gitrepo> file, and then tries to merge the changes into
the corresponding subdir. It does this by making a branch of the local commits
to the subdir and then merging or rebasing (see below) it with the fetched
upstream content. After the merge, the content of the new branch replaces your
subdir, the C<.gitrepo> file is updated and a single 'pull' commit is added to
your mainline history.
The C<pull> command will attempt to do the following commands in one go:
git subrepo fetch <subdir>
git subrepo branch <subdir>
git merge/rebase subrepo/<subdir>/fetch subrepo/<subdir>
git subrepo commit <subdir>
# Only needed for a consequential push:
git update-ref refs/subrepo/<subdir>/pull subrepo/<subdir>
In other words, you could do all the above commands yourself, for the same
effect. If any of the commands fail, subrepo will stop and tell you to finish
this by hand. Generally a failure would be in the merge or rebase part, where
conflicts can happen. Since Git has lots of ways to resolve conflicts to your
personal tastes, the subrepo command defers to letting you do this by hand.
When pulling new data, the method selected in clone/init is used. This has no
effect on the final result of the pull, since it becomes a single commit. But
it does affect the resulting C<< subrepo/<subdir> >> branch, which is often
used for a subrepo C<push> command. See 'push' below for more information. If
you want to change the method you can use the C<config> command for this.
When you pull you can assume a fast-forward strategy (default) or you can
specify a C<--rebase>, C<--merge> or C<--force> strategy. The latter is the
same as a C<clone --force> operation, using the current remote and branch.
Like the C<clone> command, C<pull> will squash all the changes (since the last
pull or clone) into one commit. This keeps your mainline history nice and
clean. You can easily see the subrepo's history with the C<git log> command:
git log refs/subrepo/<subdir>/fetch
The set of commands used above are described in detail below.
The C<pull> command accepts the C<--all>, C<--branch=>, C<--edit>, C<--force>,
C<--message=>, C<--remote=> and C<--update> options.
=item C<< git subrepo push <subdir>|--all [<branch>] [-r <remote>] [-b <branch>] [-M|-R] [-u] [-f] [-s] [-N] >>
Push a properly merged subrepo branch back upstream.
This command takes the subrepo branch from a successful pull command and
pushes the history back to its designated remote and branch. You can also use
the C<branch> command and merge things yourself before pushing if you want to
(although that is probably a rare use case).
The C<push> command requires a branch that has been properly merged/rebased
with the upstream HEAD (unless the upstream HEAD is empty, which is common
when doing a first C<push> after an C<init>). That means the upstream HEAD is
one of the commits in the branch.
By default the branch ref C<< refs/subrepo/<subdir>/pull >> will be pushed,
but you can specify a (properly merged) branch to push.
After that, the C<push> command just checks that the branch contains the
upstream HEAD and then pushes it upstream.
The C<--force> option will do a force push. Force pushes are typically
discouraged. Only use this option if you fully understand it. (The C<--force>
option will NOT check for a proper merge. ANY branch will be force pushed!)
The C<push> command accepts the C<--all>, C<--branch=>, C<--dry-run>, C<--
force>, C<--merge>, C<--rebase>, C<--remote=>, C<--squash> and C<--
update> options.
=item C<< git subrepo fetch <subdir>|--all [-r <remote>] [-b <branch>] >>
Fetch the remote/upstream content for a subrepo.
It will create a Git reference called C<< subrepo/<subdir>/fetch >> that
points at the same commit as C<FETCH_HEAD>. It will also create a remote
called C<< subrepo/<subdir> >>. These are temporary and you can easily remove
them with the subrepo C<clean> command.
The C<fetch> command accepts the C<--all>, C<--branch=> and C<--
remote=> options.
=item C<< git subrepo branch <subdir>|--all [-f] [-F] >>
Create a branch with local subrepo commits.
Scan the history of the mainline for all the commits that affect the C<subdir>
and create a new branch from them called C<< subrepo/<subdir> >>.
This is useful for doing C<pull> and C<push> commands by hand.
Use the C<--force> option to write over an existing C<< subrepo/<subdir>
>> branch.
The C<branch> command accepts the C<--all>, C<--fetch> and C<--force> options.
=item C<< git subrepo commit <subdir> [<subrepo-ref>] [-m <msg>] [-e] [-f] [-F] >>
Add subrepo branch to current history as a single commit.
This command is generally used after a hand-merge. You have done a C<subrepo
branch> and merged (rebased) it with the upstream. This command takes the HEAD
of that branch, puts its content into the subrepo subdir and adds a new commit
for it to the top of your mainline history.
This command requires that the upstream HEAD be in the C<< subrepo/<subdir> >>
branch history. That way the same branch can push upstream. Use the C<--force>
option to commit anyway.
The C<commit> command accepts the C<--edit>, C<--fetch>, C<--force> and C<--
message=> options.
=item C<< git subrepo status [<subdir>|--all|--ALL] [-F] [-q|-v] >>
Get the status of a subrepo. Uses the C<--all> option by default. If the C<--
quiet> flag is used, just print the subrepo names, one per line.
The C<--verbose> option will show all the recent local and upstream commits.
Use C<--ALL> to show the subrepos of the subrepos (ie the
"subsubrepos"), if any.
The C<status> command accepts the C<--all>, C<--ALL>, C<--fetch>, C<--quiet>
and C<--verbose> options.
=item C<< git subrepo clean <subdir>|--all|--ALL [-f] >>
Remove artifacts created by C<fetch> and C<branch> commands.
The C<fetch> and C<branch> operations (and other commands that call them)
create temporary things like refs, branches and remotes. This command removes
all those things.
Use C<--force> to remove refs. Refs are not removed by default because they
are sometimes needed between commands.
Use C<--all> to clean up after all the current subrepos. Sometimes you might
change to a branch where a subrepo doesn't exist, and then C<--all> won't find
it. Use C<--ALL> to remove any artifacts that were ever created by subrepo.
To remove ALL subrepo artifacts:
git subrepo clean --ALL --force
The C<clean> command accepts the C<--all>, C<--ALL>, and C<--force> options.
=item C<< git subrepo config <subdir> <option> [<value>] [-f] >>
Read or update configuration values in the subdir/.gitrepo file.
Because most of the values stored in the .gitrepo file are generated you
will need to use C<--force> if you want to change anything else then the
C<method> option.
Example to update the C<method> option for a subrepo:
git subrepo config foo method rebase
=item C<< git subrepo help [<command>|--all] >>
Same as C<git help subrepo>. Will launch the manpage. For the shorter usage,
use C<git subrepo -h>.
Use C<< git subrepo help <command> >> to get help for a specific command. Use
C<--all> to get a summary of all commands.
The C<help> command accepts the C<--all> option.
=item C<git subrepo version [-q|-v]>
This command will display version information about git-subrepo and its
environment. For just the version number, use C<git subrepo --version>. Use
C<--verbose> for more version info, and C<--quiet> for less.
The C<version> command accepts the C<--quiet> and C<--verbose> options.
=item C<git subrepo upgrade>
Upgrade the C<git-subrepo> software itself. This simply does a C<git pull>
on the git repository that the code is running from. It only works if you
are on the C<master> branch. It won't work if you installed C<git-subrepo>
using C<make install>; in that case you'll need to C<make install> from the
latest code.
=back
=head1 Command Options
=over
=item C<-h>
Show a brief view of the commands and options.
=item C<--help>
Gives an overview of the help options available for the subrepo command.
=item C<--version>
Print the git-subrepo version. Just the version number. Try the C<version>
command for more version info.
=item C<--all> (C<-a>)
If you have multiple subrepos, issue the command to all of them (if
applicable).
=item C<--ALL> (C<-A>)
If you have subrepos that also have subrepos themselves, issue the command to
ALL of them. Note that the C<--ALL> option only works for a subset of the
commands that C<--all> works for.
=item C<< --branch=<branch-name> >> (C<< -b <branch-name> >>)
Use a different upstream branch-name than the remote HEAD or the one saved in
C<.gitrepo> locally.
=item C<--dry-run> (C<-N>)
For the push command, do everything up until the push and then print out the
actual C<git push> command needed to finish the operation.
=item C<--edit> (C<-e>)
Edit the commit message before committing.
=item C<--fetch> (C<-F>)
Use this option to fetch the upstream commits, before running the command.
=item C<--force> (C<-f>)
Use this option to force certain commands that fail in the general case.
NOTE: The C<--force> option means different things for different commands.
Read the command specific doc for the exact meaning.
=item C<--merge> (C<-M>)
Use a C<merge> strategy to include upstream subrepo commits on a pull (or
setup for push).
=item C<< --message=<message> >> (C<< -m <message> >>)
Specify your own commit message on the command line.
=item C<--rebase> (C<-R>)
Use a C<rebase> strategy to include upstream subrepo commits on a pull (or
setup for push).
=item C<< --remote=<remote-url> >> (C<< -r <remote-url> >>)
Use a different remote-url than the one saved in C<.gitrepo> locally.
=item C<--squash> (C<-s>)
Squash all commits on a push into one new commit.
=item C<--update> (C<-u>)
If C<--branch> or C<--remote> are used, and the command updates the
C<.gitrepo> file, include these values to the update.
=back
=head1 Output Options
=over
=item C<--quiet> (C<-q>)
Print as little info as possible. Applicable to most commands.
=item C<--verbose> (C<-v>)
Print more information about the command execution and results. Applicable to
most commands.
=item C<--debug> (C<-d>)
Show the actual git (and other) commands being executed under the hood.
Applicable to most commands.
=item C<--DEBUG> (C<-x>)
Use the Bash C<set -x> option which prints every command before it is
run. VERY noisy, but extremely useful in deep debugging. Applicable to
all commands.
=back
=head1 Environment Variables
The C<git-subrepo> command exports and honors some environment variables:
=over
=item C<GIT_SUBREPO_ROOT>
This is set by the C<.rc> file, if you use that method to install / enable C<git-
subrepo>. It contains the path of the C<git-subrepo> repository.
=item C<GIT_SUBREPO_RUNNING>
This variable is exported when C<git-subrepo> is running. It is set to the pid
of the C<git-subrepo> process that is running. Other processes, like git hooks
for instance, can use this information to adjust accordingly.
=item C<GIT_SUBREPO_COMMAND>
This variable is exported when C<git-subrepo> is running. It is set to the
name of the C<git-subrepo> subcommand that is running.
=item C<GIT_SUBREPO_PAGER>
Use this to specify the pager to use for long output commands. Defaults to
C<$PAGER> or C<less>.
=item C<GIT_SUBREPO_QUIET>
Set this for quiet (C<-q>) output.
=item C<GIT_SUBREPO_VERBOSE>
Set this for verbose (C<-v>) output.
=item C<GIT_SUBREPO_DEBUG>
Set this for debugging (C<-d>) output.
=back
=head1 Installation Instructions
There are currently 3 ways to install C<git-subrepo>. For all of them you need
to get the source code from GitHub:
git clone https://github.com/ingydotnet/git-subrepo /path/to/git-subrepo
The first installation method is preferred: C<source> the C<.rc> file. Just
add a line like this one to your shell startup script:
source /path/to/git-subrepo/.rc
That will modify your C<PATH> and C<MANPATH>, and also enable command
completion.
The second method is to do these things by hand. This might afford you more
control of your shell environment. Simply add the C<lib> and C<man>
directories to your C<PATH> and C<MANPATH>:
export GIT_SUBREPO_ROOT="/path/to/git-subrepo"
export PATH="/path/to/git-subrepo/lib:$PATH"
export MANPATH="/path/to/git-subrepo/man:$MANPATH"
See below for info on how to turn on Command Completion.
The third method is a standard system install, which puts C<git-subrepo> next
to your other git commands:
make install # Possibly with 'sudo'
This method does not account for upgrading and command completion yet.
=head2 Windows
This command is known to work in these Windows environments:
=over
=item * Git for Windows -- L<https://git-for-windows.github.io/>
=item * Babun -- L<http://babun.github.io/>
=item * Cygwin -- L<https://www.cygwin.com/>
=back
Let us know if there are others that it works (or doesn't work) in.
=head1 Testing
The C<git-subrepo> repository comes with a extensive test suite. You can
run it with:
make test
or if you don't have C<make> on your system:
prove -v test
=head1 Upgrading
If you used the C<.rc> or C<PATH> method of installation, just run this to
upgrade C<git-subrepo>:
git subrepo upgrade
Or (same thing):
cd /path/to/git-subrepo
git pull
If you used C<make install> method, then run this again (after C<git pull>):
make install # Possibly with 'sudo'
=head1 Command Completion
The C<git subrepo> command supports C<< <TAB> >>-based command completion. If
you don't use the C<.rc> script (see Installation, above), you'll need to
enable this manually to use it.
=head2 In Bash
If your Bash setup does not already provide command completion for Git, you'll
need to enable that first:
source <Git completion script>
On your system, the Git completion script might be found at any of the
following locations (or somewhere else that we don't know about):
=over
=item * C</etc/bash_completion.d/git>
=item * C</usr/share/bash-completion/git>
=item * C</usr/share/bash-completion/completions/git>
=item * C</opt/local/share/bash-completion/completions/git>
=item * C</usr/local/etc/bash_completion.d/git>
=item * C<~/.homebrew/etc/bash_completion.d/git>
=back
In case you can't find any of these, this repository contains a copy of the
Git completion script:
source /path/to/git-subrepo/share/git-completion.bash
Once Git completion is enabled (whether you needed to do that manually or
not), you can turn on C<git-subrepo> completion with a command like this:
source /path/to/git-subrepo/share/completion.bash
=head2 In zsh
In the Z shell (zsh), you can manually enable C<git-subrepo> completion by
adding the following line to your C<~/.zshrc>, B<before> the C<compinit>
function is called:
fpath=('/path/to/git-subrepo/share/zsh-completion' $fpath)
=head1 Status
The git-subrepo command has been in use for well over a year and seems to get
the job done. Development is still ongoing but mostly just for fixing bugs.
Trying subrepo out is simple and painless (this is not C<git submodule>).
Nothing is permanent (if you do not push to shared remotes). ie You can always
play around and reset back to the beginning without pain.
This command has a test suite (run C<make test>), but surely has many bugs. If
you have expertise with Git and subcommands, please review the code, and file
issues on anything that seems wrong.
If you want to chat about the C<git-subrepo> command, join C<#gitcommands> on
C<irc.freenode.net>.
=head1 Notes
=over
=item * Works on POSIX systems: Linux, BSD, OSX, etc.
=item * Works on various Windows environments. See "Windows" section above.
=item * The C<git-subrepo> repo itself has 2 subrepos under the C<ext/> subdirectory.
=item * Written in (very modern) Bash, with full test suite. Take a look.
=item * A C<.gitrepo> file never is in the top level dir (next to a C<.git/> dir).
=back
=head1 Authors
=over
=item * Ingy döt Net <ingy@ingy.net>
=item * Magnus Carlsson <grimmymail@gmail.com>
=back
=head1 License and Copyright
The MIT License (MIT)
Copyright (c) 2013-2020 Ingy döt Net
=cut

View file

@ -0,0 +1,35 @@
= Comparing `submodule` and `subrepo`
This document compares Git's `submodule` command to the new `subrepo` command,
with examples and discussion of all the common operations. I'll use the term
"External" to mean the general concept of an external repo that might be used
as a submodule or a subrepo.
= Overview
= Adding a new External
As an owner or collaborator, you have decided to add a new External to your
repo:
- Submodule :: `git submodule add git@github.com/user/external`
- Subtree :: `git subtree --squash --prefix=external git@github.com/user/external`
- Subrepo :: `git subrepo clone git@github.com/user/external`
/…to be completed…/
= Updating from a changed External
= Pushing External changes upstream
= Moving/Renaming an External
= Making an External on a branch
= Changing the tracking branch of an External
= Removing an External
= Migration from One to the Other

View file

@ -0,0 +1,608 @@
git-subrepo
===========
Git Submodule Alternative
<badge travis ingydotnet/git-subrepo>
= Synopsis
git subrepo -h # Help Overview
git subrepo clone <remote-url> [<subdir>]
git subrepo init <subdir>
git subrepo pull <subdir>
git subrepo push <subdir>
git subrepo fetch <subdir>
git subrepo branch <subdir>
git subrepo commit <subdir>
git subrepo config <subdir>
git subrepo status [<subdir>]
git subrepo clean <subdir>
git subrepo help [<command> | --all]
git subrepo version
git subrepo upgrade
= Description
This git command "clones" an external git repo into a subdirectory of your
repo. Later on, upstream changes can be pulled in, and local changes can be
pushed back. Simple.
= Benefits
This command is an improvement from `git-submodule` and `git-subtree`; two
other git commands with similar goals, but various problems.
It assumes there are 3 main roles of people interacting with a repo, and
attempts to serve them all well:
* *owner* - The person who authors\/owns\/maintains a repo.
* *users* - People who are just using/installing the repo.
* *collaborators* - People who commit code to the repo and subrepos.
The `git-subrepo` command benefits these roles in the following ways:
* Simple and intuitive commandline usage (with tab completion).
* Users get your repo and all your subrepos just by cloning your repo.
* Users do not need to install `git-subrepo`, ever.
* Collaborators do not need to install unless they want to push/pull.
* Collaborators know when a subdir is a subrepo (it has a `.gitrepo` file).
* The `.gitrepo` file never gets pushed back to the subrepo upstream.
* Well named branches and remotes are generated for manual operations.
* Owners do not deal with the complications of keeping submodules in sync.
* Subrepo repositories can contain subrepos themselves.
* Branching with subrepos JustWorks™.
* Different branches can have different subrepos in different states, etc.
* Moving\/renaming\/deleting a subrepo subdir JustWorks™.
* You can `init` an existing subdirectory into a subrepo.
* Your git history is kept squeaky clean.
* Upstream history (clone/pull) is condensed into a single commit.
* Pulls can use a `merge`, `rebase` or `force` strategies.
* You can see the subrepo history with `git log subrepo/<subdir>/fetch`.
* Commits pushed back upstream are *not* condensed (by default).
* Trivial to try any subrepo operations and then reset back.
* No configuration required.
* Does not introduce history that messes up other git commands.
* Fixes known rebase failures with `git-subtree`.
= Installation
The best short answer is:
git clone https://github.com/ingydotnet/git-subrepo /path/to/git-subrepo
echo 'source /path/to/git-subrepo/.rc' >> ~/.bashrc
The complete "Installation Instructions" can be found below.
Note: git-subrepo needs a git version (> 2.7) that supports worktree:s.
= Commands
All the *subrepo* commands use names of actual Git commands and try to do
operations that are similar to their Git counterparts. They also attempt to
give similar output in an attempt to make the subrepo usage intuitive to
experienced Git users.
Please note that the commands are /not/ exact equivalents, and do not take all
the same arguments. Keep reading…
- `git subrepo clone <repository> [<subdir>] [-b <branch>] [-f] [-m <msg>] [-e] [--method <merge|rebase>]`
Add a repository as a subrepo in a subdir of your repository.
This is similar in feel to `git clone`. You just specify the remote repo
url, and optionally a sub-directory and/or branch name. The repo will be
fetched and merged into the subdir.
The subrepo history is /squashed/ into a single commit that contains the
reference information. This information is also stored in a special file
called `<subdir>/.gitrepo`. The presence of this file indicates that the
directory is a subrepo.
All subsequent commands refer to the subrepo by the name of the /subdir/.
From the subdir, all the current information about the subrepo can be
obtained.
The `--force` option will "reclone" (completely replace) an existing subdir.
The `--method` option will decide how the join process between branches are
performed. The default option is merge.
The `clone` command accepts the `--branch=` `--edit`, `--force` and
`--message=` options.
- `git subrepo init <subdir> [-r <remote>] [-b <branch>] [--method <merge|rebase>]`
Turn an existing subdirectory into a subrepo.
If you want to expose a subdirectory of your project as a published subrepo,
this command will do that. It will split out the content of a normal
subdirectory into a branch and start tracking it as a subrepo. Afterwards
your original repo will look exactly the same except that there will be a
`<subdir>/.gitrepo` file.
If you specify the `--remote` (and optionally the `--branch`) option, the
values will be added to the `<subdir>/.gitrepo` file. The `--remote` option
is the upstream URL, and the `--branch` option is the upstream branch to push
to. These values will be needed to do a `git subrepo push` command, but they
can be provided later on the `push` command (and saved to `<subdir>/.gitrepo`
if you also specify the `--update` option).
Note: You will need to create the empty upstream repo and push to it on your
own, using `git subrepo push <subdir>`.
The `--method` option will decide how the join process between branches
are performed. The default option is merge.
The `init` command accepts the `--branch=` and `--remote=` options.
- `git subrepo pull <subdir>|--all [-M|-R|-f] [-m <msg>] [-e] [-b <branch>] [-r <remote>] [-u]`
Update the subrepo subdir with the latest upstream changes.
The `pull` command fetches the latest content from the remote branch pointed
to by the subrepo's `.gitrepo` file, and then tries to merge the changes into
the corresponding subdir. It does this by making a branch of the local
commits to the subdir and then merging or rebasing (see below) it with the
fetched upstream content. After the merge, the content of the new branch
replaces your subdir, the `.gitrepo` file is updated and a single 'pull'
commit is added to your mainline history.
The `pull` command will attempt to do the following commands in one go:
git subrepo fetch <subdir>
git subrepo branch <subdir>
git merge/rebase subrepo/<subdir>/fetch subrepo/<subdir>
git subrepo commit <subdir>
# Only needed for a consequential push:
git update-ref refs/subrepo/<subdir>/pull subrepo/<subdir>
In other words, you could do all the above commands yourself, for the same
effect. If any of the commands fail, subrepo will stop and tell you to finish
this by hand. Generally a failure would be in the merge or rebase part, where
conflicts can happen. Since Git has lots of ways to resolve conflicts to your
personal tastes, the subrepo command defers to letting you do this by hand.
When pulling new data, the method selected in clone/init is used. This has
no effect on the final result of the pull, since it becomes a single commit.
But it does affect the resulting `subrepo/<subdir>` branch, which is often
used for a subrepo `push` command. See 'push' below for more information.
If you want to change the method you can use the `config` command for this.
When you pull you can assume a fast-forward strategy (default) or you can
specify a `--rebase`, `--merge` or `--force` strategy. The latter is the same
as a `clone --force` operation, using the current remote and branch.
Like the `clone` command, `pull` will squash all the changes (since the last
pull or clone) into one commit. This keeps your mainline history nice and
clean. You can easily see the subrepo's history with the `git log` command:
git log refs/subrepo/<subdir>/fetch
The set of commands used above are described in detail below.
The `pull` command accepts the `--all`, `--branch=`, `--edit`, `--force`,
`--message=`, `--remote=` and `--update` options.
- `git subrepo push <subdir>|--all [<branch>] [-r <remote>] [-b <branch>] [-M|-R] [-u] [-f] [-s] [-N]`
Push a properly merged subrepo branch back upstream.
This command takes the subrepo branch from a successful pull command and
pushes the history back to its designated remote and branch. You can also use
the `branch` command and merge things yourself before pushing if you want to
(although that is probably a rare use case).
The `push` command requires a branch that has been properly merged/rebased
with the upstream HEAD (unless the upstream HEAD is empty, which is common
when doing a first `push` after an `init`). That means the upstream HEAD is
one of the commits in the branch.
By default the branch ref `refs/subrepo/<subdir>/pull` will be pushed, but
you can specify a (properly merged) branch to push.
After that, the `push` command just checks that the branch contains the
upstream HEAD and then pushes it upstream.
The `--force` option will do a force push. Force pushes are typically
discouraged. Only use this option if you fully understand it. (The `--force`
option will NOT check for a proper merge. ANY branch will be force pushed!)
The `push` command accepts the `--all`, `--branch=`, `--dry-run`, `--force`,
`--merge`, `--rebase`, `--remote=`, `--squash` and `--update` options.
- `git subrepo fetch <subdir>|--all [-r <remote>] [-b <branch>]`
Fetch the remote/upstream content for a subrepo.
It will create a Git reference called `subrepo/<subdir>/fetch` that points at
the same commit as `FETCH_HEAD`. It will also create a remote called
`subrepo/<subdir>`. These are temporary and you can easily remove them with
the subrepo `clean` command.
The `fetch` command accepts the `--all`, `--branch=` and `--remote=` options.
- `git subrepo branch <subdir>|--all [-f] [-F]`
Create a branch with local subrepo commits.
Scan the history of the mainline for all the commits that affect the `subdir`
and create a new branch from them called `subrepo/<subdir>`.
This is useful for doing `pull` and `push` commands by hand.
Use the `--force` option to write over an existing `subrepo/<subdir>` branch.
The `branch` command accepts the `--all`, `--fetch` and `--force` options.
- `git subrepo commit <subdir> [<subrepo-ref>] [-m <msg>] [-e] [-f] [-F]`
Add subrepo branch to current history as a single commit.
This command is generally used after a hand-merge. You have done a `subrepo
branch` and merged (rebased) it with the upstream. This command takes the
HEAD of that branch, puts its content into the subrepo subdir and adds a new
commit for it to the top of your mainline history.
This command requires that the upstream HEAD be in the `subrepo/<subdir>`
branch history. That way the same branch can push upstream. Use the
`--force` option to commit anyway.
The `commit` command accepts the `--edit`, `--fetch`, `--force` and
`--message=` options.
- `git subrepo status [<subdir>|--all|--ALL] [-F] [-q|-v]`
Get the status of a subrepo. Uses the `--all` option by default. If the
`--quiet` flag is used, just print the subrepo names, one per line.
The `--verbose` option will show all the recent local and upstream commits.
Use `--ALL` to show the subrepos of the subrepos (ie the "subsubrepos"), if
any.
The `status` command accepts the `--all`, `--ALL`, `--fetch`, `--quiet` and
`--verbose` options.
- `git subrepo clean <subdir>|--all|--ALL [-f]`
Remove artifacts created by `fetch` and `branch` commands.
The `fetch` and `branch` operations (and other commands that call them)
create temporary things like refs, branches and remotes. This command
removes all those things.
Use `--force` to remove refs. Refs are not removed by default because they
are sometimes needed between commands.
Use `--all` to clean up after all the current subrepos. Sometimes you might
change to a branch where a subrepo doesn't exist, and then `--all` won't find
it. Use `--ALL` to remove any artifacts that were ever created by subrepo.
To remove ALL subrepo artifacts:
git subrepo clean --ALL --force
The `clean` command accepts the `--all`, `--ALL`, and `--force` options.
- `git subrepo config <subdir> <option> [<value>] [-f]`
Read or update configuration values in the subdir/.gitrepo file.
Because most of the values stored in the .gitrepo file are generated you
will need to use `--force` if you want to change anything else then the
`method` option.
Example to update the `method` option for a subrepo:
git subrepo config foo method rebase
- `git subrepo help [<command>|--all]`
Same as `git help subrepo`. Will launch the manpage. For the shorter usage,
use `git subrepo -h`.
Use `git subrepo help <command>` to get help for a specific command. Use
`--all` to get a summary of all commands.
The `help` command accepts the `--all` option.
- `git subrepo version [-q|-v]`
This command will display version information about git-subrepo and its
environment. For just the version number, use `git subrepo --version`. Use
`--verbose` for more version info, and `--quiet` for less.
The `version` command accepts the `--quiet` and `--verbose` options.
- `git subrepo upgrade`
Upgrade the `git-subrepo` software itself. This simply does a `git pull` on
the git repository that the code is running from. It only works if you are on
the `master` branch. It won't work if you installed `git-subrepo` using `make
install`; in that case you'll need to `make install` from the latest code.
= Command Options
- `-h`
Show a brief view of the commands and options.
- `--help`
Gives an overview of the help options available for the subrepo command.
- `--version`
Print the git-subrepo version. Just the version number. Try the `version`
command for more version info.
- `--all` (`-a`)
If you have multiple subrepos, issue the command to all of them (if
applicable).
- `--ALL` (`-A`)
If you have subrepos that also have subrepos themselves, issue the command to
ALL of them. Note that the `--ALL` option only works for a subset of the
commands that `--all` works for.
- `--branch=<branch-name>` (`-b <branch-name>`)
Use a different upstream branch-name than the remote HEAD or the one saved in
`.gitrepo` locally.
- `--dry-run` (`-N`)
For the push command, do everything up until the push and then print out the
actual `git push` command needed to finish the operation.
- `--edit` (`-e`)
Edit the commit message before committing.
- `--fetch` (`-F`)
Use this option to fetch the upstream commits, before running the command.
- `--force` (`-f`)
Use this option to force certain commands that fail in the general case.
NOTE: The `--force` option means different things for different commands.
Read the command specific doc for the exact meaning.
- `--merge` (`-M`)
Use a `merge` strategy to include upstream subrepo commits on a pull (or
setup for push).
- `--message=<message>` (`-m <message>`)
Specify your own commit message on the command line.
- `--rebase` (`-R`)
Use a `rebase` strategy to include upstream subrepo commits on a pull (or
setup for push).
- `--remote=<remote-url>` (`-r <remote-url>`)
Use a different remote-url than the one saved in `.gitrepo` locally.
- `--squash` (`-s`)
Squash all commits on a push into one new commit.
- `--update` (`-u`)
If `--branch` or `--remote` are used, and the command updates the `.gitrepo`
file, include these values to the update.
= Output Options
- `--quiet` (`-q`)
Print as little info as possible. Applicable to most commands.
- `--verbose` (`-v`)
Print more information about the command execution and results. Applicable
to most commands.
- `--debug` (`-d`)
Show the actual git (and other) commands being executed under the hood.
Applicable to most commands.
- `--DEBUG` (`-x`)
Use the Bash `set -x` option which prints every command before it is run.
VERY noisy, but extremely useful in deep debugging. Applicable to all
commands.
= Environment Variables
The `git-subrepo` command exports and honors some environment variables:
- `GIT_SUBREPO_ROOT`
This is set by the `.rc` file, if you use that method to install / enable
`git-subrepo`. It contains the path of the `git-subrepo` repository.
- `GIT_SUBREPO_RUNNING`
This variable is exported when `git-subrepo` is running. It is set to the pid
of the `git-subrepo` process that is running. Other processes, like git hooks
for instance, can use this information to adjust accordingly.
- `GIT_SUBREPO_COMMAND`
This variable is exported when `git-subrepo` is running. It is set to the
name of the `git-subrepo` subcommand that is running.
- `GIT_SUBREPO_PAGER`
Use this to specify the pager to use for long output commands. Defaults to
`$PAGER` or `less`.
- `GIT_SUBREPO_QUIET`
Set this for quiet (`-q`) output.
- `GIT_SUBREPO_VERBOSE`
Set this for verbose (`-v`) output.
- `GIT_SUBREPO_DEBUG`
Set this for debugging (`-d`) output.
= Installation Instructions
There are currently 3 ways to install `git-subrepo`. For all of them you need
to get the source code from GitHub:
git clone https://github.com/ingydotnet/git-subrepo /path/to/git-subrepo
The first installation method is preferred: `source` the `.rc` file. Just add a
line like this one to your shell startup script:
source /path/to/git-subrepo/.rc
That will modify your `PATH` and `MANPATH`, and also enable command completion.
The second method is to do these things by hand. This might afford you more
control of your shell environment. Simply add the `lib` and `man` directories
to your `PATH` and `MANPATH`:
export GIT_SUBREPO_ROOT="/path/to/git-subrepo"
export PATH="/path/to/git-subrepo/lib:$PATH"
export MANPATH="/path/to/git-subrepo/man:$MANPATH"
See below for info on how to turn on Command Completion.
The third method is a standard system install, which puts `git-subrepo` next to
your other git commands:
make install # Possibly with 'sudo'
This method does not account for upgrading and command completion yet.
== Windows
This command is known to work in these Windows environments:
* Git for Windows -- https://git-for-windows.github.io/
* Babun -- http://babun.github.io/
* Cygwin -- https://www.cygwin.com/
Let us know if there are others that it works (or doesn't work) in.
= Testing
The `git-subrepo` repository comes with a extensive test suite. You can run it
with:
make test
or if you don't have `make` on your system:
prove -v test
= Upgrading
If you used the `.rc` or `PATH` method of installation, just run this to
upgrade `git-subrepo`:
git subrepo upgrade
Or (same thing):
cd /path/to/git-subrepo
git pull
If you used `make install` method, then run this again (after `git pull`):
make install # Possibly with 'sudo'
= Command Completion
The `git subrepo` command supports `<TAB>`-based command completion. If you
don't use the `.rc` script (see Installation, above), you'll need to enable
this manually to use it.
== In Bash
If your Bash setup does not already provide command completion for Git, you'll
need to enable that first:
source <Git completion script>
On your system, the Git completion script might be found at any of the
following locations (or somewhere else that we don't know about):
* `/etc/bash_completion.d/git`
* `/usr/share/bash-completion/git`
* `/usr/share/bash-completion/completions/git`
* `/opt/local/share/bash-completion/completions/git`
* `/usr/local/etc/bash_completion.d/git`
* `~/.homebrew/etc/bash_completion.d/git`
In case you can't find any of these, this repository contains a copy of the
Git completion script:
source /path/to/git-subrepo/share/git-completion.bash
Once Git completion is enabled (whether you needed to do that manually or
not), you can turn on `git-subrepo` completion with a command like this:
source /path/to/git-subrepo/share/completion.bash
== In zsh
In the Z shell (zsh), you can manually enable `git-subrepo` completion by
adding the following line to your `~/.zshrc`, *before* the `compinit` function
is called:
fpath=('/path/to/git-subrepo/share/zsh-completion' $fpath)
= Status
The git-subrepo command has been in use for well over a year and seems to get
the job done. Development is still ongoing but mostly just for fixing bugs.
Trying subrepo out is simple and painless (this is not `git submodule`).
Nothing is permanent (if you do not push to shared remotes). ie You can always
play around and reset back to the beginning without pain.
This command has a test suite (run `make test`), but surely has many bugs. If
you have expertise with Git and subcommands, please review the code, and file
issues on anything that seems wrong.
If you want to chat about the `git-subrepo` command, join `#gitcommands` on
`irc.freenode.net`.
= Notes
* Works on POSIX systems: Linux, BSD, OSX, etc.
* Works on various Windows environments. See "Windows" section above.
* The `git-subrepo` repo itself has 2 subrepos under the `ext/` subdirectory.
* Written in (very modern) Bash, with full test suite. Take a look.
* A `.gitrepo` file never is in the top level dir (next to a `.git/` dir).
= Authors
* Ingy döt Net <ingy@ingy.net>
* Magnus Carlsson <grimmymail@gmail.com>
= License and Copyright
The MIT License (MIT)
Copyright (c) 2013-2020 Ingy döt Net

View file

@ -0,0 +1,387 @@
= Introducing Git Subrepos
There is a new git command called `subrepo` that is meant to be a solid
alternative to the `submodule` and `subtree` commands. All 3 of these commands
allow you to include external repositories (pinned to specific commits) in
your main repository. This is an often needed feature for project development
under a source control system like Git. Unfortunately, the `submodule` command
is severely lacking, and the `subtree` command (an attempt to make things
better) is also very flawed. Fortunately, the `subrepo` command is here to
save the day.
This article will discuss how the previous commands work, and where they go
wrong, while explaining how the new `subrepo` command fixes the issues.
It should be noted that there are 3 distinct roles (ways people use repos)
involved in discussing this topic:
* *owner* — The primary author and repo owner
* *collaborators* — Other developers who contribute to the repo
* *users* — People who simply use the repo software
== Introducing `subrepo`
While the main point is to show how subrepo addresses the shortcomings of
submodule and subtree, I'll start by giving a quick intro to the subrepo
command.
Let's say that you have a project repo called 'freebird' and you want to have
it include 2 other external repos, 'lynyrd' and 'skynyrd'. You would do the
following:
git clone git@github.com/you/freebird
cd freebird
git subrepo clone git@github.com/you/lynyrd ext/lynyrd
git subrepo clone git@github.com/you/skynyrd ext/skynyrd --branch=1975
What these commands do (at a high level) should be obvious. They "clone"
(add) the repos content into the subdirectories you told them to. The details
of what is happening to your repo will be discussed later, but adding new
subrepos is easy. If you need to update the subrepos later:
git subrepo pull ext/lynyrd
git subrepo pull ext/skynyrd --branch=1976
The lynyrd repo is tracking the upstream master branch, and you've changed the
skynyrd subrepo to the 1976 branch. Since these subrepos are owned by 'you',
you might want to change them in the context of your freebird repo. When
things are working, you can push the subrepo changes back:
git subrepo push ext/lynyrd
git subrepo push ext/skynyrd
Looks simple right? It's supposed to be. The intent of `subrepo` is to do the
right things, and to not cause problems.
Of course there's more to it under the hood, and that's what the rest of this
article is about.
== Git Submodules
Submodules tend to receive a lot of bad press. Here's some of it:
* http://ayende.com/blog/4746/the-problem-with-git-submodules
* http://somethingsinistral.net/blog/git-submodules-are-probably-not-the-answer/
* http://codingkilledthecat.wordpress.com/2012/04/28/why-your-company-shouldnt-use-git-submodules/
A quick recap of some of the good and bad things about submodules:
Good:
* Use an external repo in a dedicated subdir of your project.
* Pin the external repo to a specific commit.
* The `git-submodule` command is a core part of the Git project.
Bad:
* Users have to know a repo has submodules.
* Users have to get the subrepos manually.
* Pulling a repo with submodules won't pull in the new submodule changes.
* A submodule will break if the referenced repo goes away.
* A submodule will break if a forced push removes the referenced commit.
* Can't use different submodules/commits per main project branch.
* Can't "try out" a submodule on alternate branch.
* Main repo can be pushed upstream pointing to unpushed submod commits.
* Command capability differs across Git versions.
* Often need to change remote url, to push submodule changes upstream.
* Removing or renaming a submodule requires many steps.
Internally, submodules are a real mess. They give the strong impression of
being bolted on, well after Git was designed. Some commands are aware of the
existence of submodules (although usually half-heartedly), and many commands
are oblivious. For instance the git-clone command has a `--recursive` option
to clone all subrepos, but it's not a default, so you still need to be aware
of the need. The git-checkout command does nothing with the submodules, even
if they are intended to differ across branches.
Let's talk a bit about how submodules are implemented in Git. Information
about them is stored in 3 different places (in the top level repo directory):
* `.gitmodules`
* `.git/config`
* `.git/modules` — The submodule repo's meta data (refs/objects)
So some of the information lives in the repo history (.gitmodules), but other
info (.git/) is only known to the local repo.
In addition, the submodule introduces a new low level concept, to the
commit/tree/blob graph. Normally a git tree object points to blob (file)
objects and more tree (directory) objects. Submodules have tree objects point
to *commit* objects. While this seems clever and somewhat reasonable, it also
means that every other git command (which was built on the super clean Git
data model) has to be aware of this new possibility (and deal with it
appropriately).
The point is that, while submodules are a real need, and a lot of work has
gone into making them work decently, they are essentially a kludge to the Git
model, and it is quite understandable why they haven't worked out as well as
people would expect.
NOTE: Submodules /are/ getting better with each release of Git, but it's still
an endless catch up game.
== Git Subtrees
One day, someone decided to think different. Instead of pointing to external
repos, why not just include them into the main repo (but also allow them to be
pulled and pushed separately as needed)?
At first this may feel like a wasteful approach. Why keep other repos
physically inside your main one? But if you think about it abstractly, what's
the difference? You want your users and collaborators to have all this code
because your project needs it. So why worry about how it happens? In the end,
the choice is yours, but I've grown very comfortable with this concept and
I'll try to justify it well. I should note that the first paragraph of the
`submodule` doc suggests considering this alternative.
The big win here is that you can do this using the existing git model.
Nothing new is added. You are just adding commits to a history. You can do it
different on every branch. You can merge branches sensibly.
The git-subtree command seems to have been inspired by Git's subtree merge
strategy, which it uses internally, and possibly got its name from. A subtree
merge allows you to take a completely separate Git history and make it be a
subdirectory of your repo.
Adding a subtree was the easy part. All that needed to be done after that was
to figure out a way to pull upstream changes and push local ones back
upstream. And that's what the `git-subtree` command does.
So what's the problem with git-subtree then?
Well unfortunately, it drops a few balls. The main problems come down to an
overly complicated commandline UX, poor collaborator awareness, and a fragile
and messy implementation.
Good:
* Use an external repo in a dedicated subdir of your project.
* Pin the external repo to a specific commit.
* Users get everything with a normal clone command.
* Users don't need to know that subtrees are involved.
* Can use different submodules/commits per main project branch.
* Users don't need the subtree command. Only owners and collaborators.
Bad:
* The remote url and branch info is not saved (except in the history).
* Owners and collaborators have to enter the remote for every command.
* Collaborators aren't made aware that subtrees are involved.
* Pulled history is not squashed by default.
* Creates a messy historical view. (See below)
* Bash code is complicated.
* Only one test file. Currently is failing.
As you can see, subtree makes quite a few things better, but after trying it
for a while, the experience was more annoying than submodules. For example,
consider this usage:
$ git subtree add --squash --prefix=foo git@github.com:my/thing mybranch
# weeks go by…
$ git subtree pull --squash --prefix=foo git@github.com:my/thing mybranch
# time to push local subtree changes back upstream
$ git subtree push --prefix=foo git@github.com:my/thing mybranch
The first thing you notice is the overly verbose syntax. It's justified in the
first command, but in the other 2 commands I really don't want to have to
remember what the remote and branch are that I'm using.
Moreover, my collaborators have no idea that subtrees are involved, let alone
where they came from.
Consider the equivalent subrepo commands:
$ git subrepo clone git@github.com:my/thing foo -b mybranch
$ git subrepo pull foo
$ git subrepo push foo
Collaborators see a file called 'foo/.gitrepo', and know that the subdir is a
subrepo. The file contains all the information needed by future commands
applied to that subrepo.
== Git Subrepos
Now is a good time to dive into the techinical aspects of the `subrepo`
command, but first let me explain how it came about.
As you may have surmised by now, I am the author of git-subrepo. I'd used
submodules on and off for years, and when I became aware of subtree I gave it
a try, but I quickly realized its problems. I decided maybe it could be
improved. I decided to write down my expected commandline usage and my ideals
of what it would and would not do. Then I set off to implement it. It's been a
long road, but what I ended up with was even better than what I wanted from
the start.
Let's review the Goods and Bads:
Good:
* Use an external repo in a dedicated subdir of your project.
* Pin the external repo to a specific commit.
* Users get everything with a normal clone command.
* Users don't need to know that subrepos are involved.
* Can use different submodules/commits per main project branch.
* Meta info is kept in an obvious place.
* Everyone knows when a subdir is a subrepo.
* Commandline UX is minimal and intuitive.
* Pulled history is always squashed out locally.
* Pushed history is kept intact.
* Creates a clean historical view. (See below)
* Bash code is very simple and easy to follow.
* Comprehensive test suite. Currently passing on travis:
<badge travis ingydotnet/git-subrepo>
Bad:
* --Subrepo is very new.-- (no longer true)
* --Not well tested in the wild.-- (no longer true)
This review may seem somewhat slanted, but I honestly am not aware of any
"bad" points that I'm not disclosing. That said, I am sure time will reveal
bugs and shortcomings. Those can usually be fixed. Hopefully the *model* is
correct, because that's harder to fix down the road.
OK. So how does it all work?
There are 3 main commands: clone/pull/push. Let's start with the clone
command. This is the easiest part. You give it a remote url, possibly a new
subdir to put it, and possibly a remote branch to use. I say possibly, because
the command can guess the subdir name (just like the git-clone command does),
and the branch can be the upstream default branch.
Given this we do the following steps internally:
* Fetch the remote content (for a specific refspec)
* Read the remote head tree into the index
* Checkout the index into the new subdir
* Create a new subrepo commit object for the subdir content
* Add a state file called .gitrepo to the new subrepo/subdir
* Amend the merge commit with this new file
This process adds something like this to the top of your history:
* 9b6ddc9 git subrepo clone git@github.com:you/foo.git foo/
* 37c61a5 Previous head commit of your repo
The entire history has been squashed down into one commit, and placed on top of
your history. This is important as it keeps your history as clean as possible.
You don't need to have the subrepo history in your main project, since it is
immutably available elsewhere, and you have a pointer to that place.
The new foo/.gitrepo file looks like this:
[subrepo]
remote = git@github.com:you/foo.git
branch = master
commit = 14c96c6931b41257b2d42b2edc67ddc659325823
parent = 37c61a5a234f5dd6f5c2aec037509f50d3a79b8f
cmdver = 0.1.0
It contains all the info needed now and later. Note that the repo url is the
generally pushable form, rather than the publically readable (https://…) form.
This is the best practice. Users of your repo don't need access to this url,
because the content is already in your repo. Only you and your collaborators
need this url to pull/push in the future.
The next command is the pull command. Normally you just give it the subrepo's
subdir path (although you can change the branch with -b), and it will get the
other info from the subdir/.gitrepo file.
The pull command does these steps:
* Fetch the upstream content
* Check if anything needs pulling
* Create a branch of local subrepo commits since last pull
* Rebase this branch onto the upstream commits
* Commit the HEAD of the rebased content
* Update/amend the .gitrepo file
=== Clean History
I've talked a bit about clean history but let me show you a comparison between
subrepo and subtree. Let's run this command sequence using both methods. Note
the differences between /both/ the command syntax required, and the branch
history produced.
Subrepo first:
$ git subrepo clone git@github.com:user/abc
$ git subrepo clone git@github.com:user/def xyz
$ git subrepo pull abc
$ git subrepo pull xyz
The resulting history is:
* b1f60cc subrepo pull xyz
* 4fb0276 subrepo pull abc
* bcef2a0 subrepo clone git@github.com:user/def xyz
* bebf0db subrepo clone git@github.com:user/abc
* 64eeaa6 (origin/master, origin/HEAD) O HAI FREND
Compare that to *subtree*. This:
$ git subtree add abc git@github.com:user/abc master
$ git subtree add xyz git@github.com:user/def master
$ git subtree pull abc git@github.com:user/abc master
$ git subtree pull xyz git@github.com:user/def master
Produces this:
* 739e45a (HEAD, master) Merge commit '5f563469d886d53e19cb908b3a64e4229f88a2d1'
|\
| * 5f56346 Squashed 'xyz/' changes from 08c7421..365409f
* | 641f5e5 Merge commit '8d88e90ce5f653ed2e7608a71b8693a2174ea62a'
|\ \
| * | 8d88e90 Squashed 'abc/' changes from 08c7421..365409f
* | | 1703ed2 Merge commit '0e091b672c4bbbbf6bc4f6694c475d127ffa21eb' as 'xyz'
|\ \ \
| | |/
| |/|
| * | 0e091b6 Squashed 'xyz/' content from commit 08c7421
| /
* | 07b77e7 Merge commit 'cd2b30a0229d931979ed4436b995875ec563faea' as 'abc'
|\ \
| |/
| * cd2b30a Squashed 'abc/' content from commit 08c7421
* 64eeaa6 (origin/master, origin/HEAD) O HAI FREND
This was from a minimal case. Subtree history (when viewed this way at least)
gets unreasonably ugly fast. Subrepo history, by contrast, always looks as
clean as shown.
The final command, push, bascially just does the pull/rebase dance above
described, and pushes the resulting history back. It does not squash the
commits made locally, because it assumed that when you changed the local
subrepo, you made messages that were intended to eventually be published back
upstream.
== Conflict Resolution
The commands described above can also be done "by hand". If something fails
during a pull or push (generally in the rebasing) then the command will tell
you what to do to finish up.
You might choose to do everything by hand, and do your own merging strategies.
This is perfectly reasonable. The `subrepo` command offers a few other helper
commands to help you get the job done:
* `fetch` - Fetch the upstream and create a `subrepo/remote/<subdir>` ref.
* `branch` - Create a branch of local subdir commits since the last pull,
called `subrepo/<subdir>`.
* `commit` - Commit a merged branch's HEAD back into your repo.
* `status` - Show lots of useful info about the current state of the subrepos.
* `clean` - Remove branches, ref and remotes created by subrepo commands.
* `help` - Read the complete documentation!
== Conclusion
Hopefully by now, you see that submodules are a painful choice with a dubious
future, and that subtree, while a solid idea has many usage issues.
Give `subrepo` a try. It's painless, easily revertable and just might be what
the doctor ordered.
== Reference Links
* http://longair.net/blog/2010/06/02/git-submodules-explained/
* http://blogs.atlassian.com/2013/05/alternatives-to-git-submodule-git-subtree/

View file

View file

@ -0,0 +1,11 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = git@github.com:ingydotnet/bashplus.git
branch = master
commit = d9183af6f46946fabdef1dd8f37824c042a378f8
parent = 9b8f13e94677b2680a33ad204bebadcdb1ff9081
cmdver = 0.3.0

View file

@ -0,0 +1,6 @@
# C language gives closest shell env.
language: c
script:
- git submodule update --init --recursive
- PROVEOPT=-v make test

View file

@ -0,0 +1,15 @@
---
version: 0.0.7
date: Sat Jan 23 16:28:59 PST 2016
changes:
- Update tooling, and copyright
---
version: 0.0.6
date: Fri Jan 23 21:05:15 PST 2015
changes:
- Update tooling, and copyright
---
version: 0.0.1
date: Sun Oct 27 19:07:51 PDT 2013
changes:
- First release.

View file

@ -0,0 +1,21 @@
(The MIT License)
Copyright © 2013-2016 Ingy döt Net
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the Software), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,45 @@
ifeq ($(MAKECMDGOALS),install)
ifeq "$(shell bpan version 2>/dev/null)" ""
$(error 'BPAN not installed. See http://bpan.org')
endif
endif
NAME := bash+
LIB := lib/$(NAME).bash
DOC := doc/$(NAME).swim
MAN1 := man/man1
MAN3 := man/man3
INSTALL_LIB ?= $(shell bpan env BPAN_LIB)
INSTALL_DIR ?= test
INSTALL_MAN1 ?= $(shell bpan env BPAN_MAN1)
INSTALL_MAN3 ?= $(shell bpan env BPAN_MAN3)
default: help
help:
@echo 'Rules: test, install, doc'
.PHONY: test
test:
prove $(PROVEOPT:%=% )test/
install:
install -C -d -m 0755 $(INSTALL_LIB)/$(INSTALL_DIR)/
install -C -m 0755 $(LIB) $(INSTALL_LIB)/$(INSTALL_DIR)/
install -C -d -m 0755 $(INSTALL_MAN1)/
install -C -d -m 0755 $(INSTALL_MAN3)/
install -C -m 0644 $(MAN1)/$(NAME).1 $(INSTALL_MAN1)/
install -C -m 0644 $(MAN3)/$(NAME).3 $(INSTALL_MAN3)/
.PHONY: doc
doc: ReadMe.pod $(MAN1)/$(NAME).1 $(MAN3)/$(NAME).3
ReadMe.pod: $(DOC)
swim --to=pod --complete --wrap $< > $@
$(MAN1)/%.1: doc/%.swim
swim --to=man $< > $@
$(MAN3)/%.3: doc/%.swim
swim --to=man $< > $@

View file

@ -0,0 +1,28 @@
=meta: 0.0.2
name: bashplus
version: 0.0.7
abstract: Modern Bash Programming
homepage: http://bpan.org/package/bashplus/
license: MIT
copyright: 2013-2016
author:
name: Ingy döt Net
email: ingy@ingy.net
github: ingydotnet
twitter: ingydotnet
freenode: ingy
homepage: http://ingy.net
requires:
bash: 3.2.0
test:
cmd: make test
install:
cmd: make install
devel:
git: git@github.org/ingydotnet/bashplus
irc: irc.freenode.net/bpan
bug: https://github.com/ingydotnet/bashplus/issues/

View file

@ -0,0 +1,77 @@
=pod
=for comment
DO NOT EDIT. This Pod was generated by Swim v0.1.41.
See http://github.com/ingydotnet/swim-pm#readme
=encoding utf8
=head1 Name
Bash+(1) - Modern Bash Programming
=for html
<a href="https://travis-ci.org/ingydotnet/bashplus"><img src="https://travis-ci.org/ingydotnet/bashplus.png" alt="bashplus"></a>
=head1 Synopsis
source bash+ :std :array
use Foo::Bar this that
Array.new args "$@"
if args.empty?; then
die "I need args!"
fi
Foo::Bar.new foo args
this is awesome # <= this is a real command! (You just imported it)
=head1 Description
Bash+ is just Bash... B<plus> some libraries that can make Bash programming a
lot nicer.
=for comment # Installation
Get the source code from GitHub:
git clone git@github.com:ingydotnet/bashplus
Then run:
make test
make install # Possibly with 'sudo'
=head1 Usage
For now look at some libraries the use Bash+:
=over
=item * L<https://github.com/ingydotnet/git-hub>
=item * L<https://github.com/ingydotnet/json-bash>
=item * L<https://github.com/ingydotnet/test-more-bash>
=back
=head1 Status
If you are interested in chatting about this, C</join #bpan> on
irc.freenode.net.
=head1 Author
Written by Ingy döt Net <ingy@ingy.net>
=head1 Copyright & License
Copyright 2013-2016. Ingy döt Net.
The MIT License (MIT).
=cut

View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# Bash+ - Modern Bash Programming
#
# Copyright (c) 2013-2016 Ingy döt Net
#------------------------------------------------------------------------------
set -e
shopt -s compat31&>/dev/null||:
#------------------------------------------------------------------------------
# Determine how `bash+` was called, and do the right thing:
#------------------------------------------------------------------------------
if [ "${BASH_SOURCE[0]}" != "$0" ]; then
# 'bash+' is being sourced:
[[ "${BASH_SOURCE[0]}" =~ /bin/bash\\+$ ]] || {
echo "Invalid Bash+ path '${BASH_SOURCE[0]}'" 2> /dev/null
exit 1
}
source "${BASH_SOURCE[0]%/bin/*}"/lib/bash+.bash || return $?
bash+:import "$@"
return $?
else
if [ $# -eq 1 -a "$1" == --version ]; then
echo 'bash+ version 0.0.7'
else
cat <<...
Greetings modern Bash programmer. Welcome to Bash+!
Bash+ is framework that makes Bash programming more like Ruby and Perl.
See: https://github.com/bpan-org/bashplus
If you got here trying to use bash+ in a program, you need to source it:
source bash+
Happy Bash Hacking!
...
fi
fi

View file

@ -0,0 +1,61 @@
Bash+(1)
========
Modern Bash Programming
<badge travis ingydotnet/bashplus>
= Synopsis
source bash+ :std :array
use Foo::Bar this that
Array.new args "$@"
if args.empty?; then
die "I need args!"
fi
Foo::Bar.new foo args
this is awesome # <= this is a real command! (You just imported it)
= Description
Bash+ is just Bash... *plus* some libraries that can make Bash programming a
lot nicer.
## Installation
Get the source code from GitHub:
git clone git@github.com:ingydotnet/bashplus
Then run:
make test
make install # Possibly with 'sudo'
= Usage
For now look at some libraries the use Bash+:
* https://github.com/ingydotnet/git-hub
* https://github.com/ingydotnet/json-bash
* https://github.com/ingydotnet/test-more-bash
= Status
If you are interested in chatting about this, `/join #bpan` on
irc.freenode.net.
= Author
Written by Ingy döt Net <ingy@ingy.net>
= Copyright & License
Copyright 2013-2016. Ingy döt Net.
The MIT License (MIT).

View file

@ -0,0 +1,134 @@
.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{
. if \nF \{
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "Bash+(1) 1"
.TH Bash+(1) 1 "January 2016" "Generated by Swim v0.1.41" "Modern Bash Programming"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "Name"
.IX Header "Name"
Bash+(1) \- Modern Bash Programming
.SH "Synopsis"
.IX Header "Synopsis"
.Vb 1
\& source bash+ :std :array
\&
\& use Foo::Bar this that
\&
\& Array.new args "$@"
\&
\& if args.empty?; then
\& die "I need args!"
\& fi
\&
\& Foo::Bar.new foo args
\&
\& this is awesome # <= this is a real command! (You just imported it)
.Ve
.SH "Description"
.IX Header "Description"
Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer.
.PP
Get the source code from GitHub:
.PP
.Vb 1
\& git clone git@github.com:ingydotnet/bashplus
.Ve
.PP
Then run:
.PP
.Vb 2
\& make test
\& make install # Possibly with \*(Aqsudo\*(Aq
.Ve
.SH "Usage"
.IX Header "Usage"
For now look at some libraries the use Bash+:
.IP "\(bu" 4
<https://github.com/ingydotnet/git\-hub>
.IP "\(bu" 4
<https://github.com/ingydotnet/json\-bash>
.IP "\(bu" 4
<https://github.com/ingydotnet/test\-more\-bash>
.SH "Status"
.IX Header "Status"
If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net.
.SH "Author"
.IX Header "Author"
Written by Ingy döt Net <ingy@ingy.net>
.SH "Copyright & License"
.IX Header "Copyright & License"
Copyright 2013\-2016. Ingy döt Net.
.PP
The \s-1MIT\s0 License (\s-1MIT\s0).

View file

@ -0,0 +1,134 @@
.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{
. if \nF \{
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "Bash+(1) 1"
.TH Bash+(1) 1 "January 2016" "Generated by Swim v0.1.41" "Modern Bash Programming"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "Name"
.IX Header "Name"
Bash+(1) \- Modern Bash Programming
.SH "Synopsis"
.IX Header "Synopsis"
.Vb 1
\& source bash+ :std :array
\&
\& use Foo::Bar this that
\&
\& Array.new args "$@"
\&
\& if args.empty?; then
\& die "I need args!"
\& fi
\&
\& Foo::Bar.new foo args
\&
\& this is awesome # <= this is a real command! (You just imported it)
.Ve
.SH "Description"
.IX Header "Description"
Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer.
.PP
Get the source code from GitHub:
.PP
.Vb 1
\& git clone git@github.com:ingydotnet/bashplus
.Ve
.PP
Then run:
.PP
.Vb 2
\& make test
\& make install # Possibly with \*(Aqsudo\*(Aq
.Ve
.SH "Usage"
.IX Header "Usage"
For now look at some libraries the use Bash+:
.IP "\(bu" 4
<https://github.com/ingydotnet/git\-hub>
.IP "\(bu" 4
<https://github.com/ingydotnet/json\-bash>
.IP "\(bu" 4
<https://github.com/ingydotnet/test\-more\-bash>
.SH "Status"
.IX Header "Status"
If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net.
.SH "Author"
.IX Header "Author"
Written by Ingy döt Net <ingy@ingy.net>
.SH "Copyright & License"
.IX Header "Copyright & License"
Copyright 2013\-2016. Ingy döt Net.
.PP
The \s-1MIT\s0 License (\s-1MIT\s0).

View file

@ -0,0 +1,12 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+ :std
ok $? '`source bash+` works'
is "$BASHPLUS_VERSION" '0.0.7' 'BASHPLUS_VERSION is 0.0.7'
done_testing 2

View file

@ -0,0 +1,22 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+
foo() {
echo O HAI
}
like "$(type bar 2>&1)" 'bar: not found' \
'bar is not yet a function'
bash+:fcopy foo bar
type -t bar &>/dev/null
ok $? 'bar is now a function'
is "$(type foo | tail -n+3)" "$(type bar | tail -n+3)" \
'Copy matches original'
done_testing 3

View file

@ -0,0 +1,18 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+ :std
ok "`bash+:can use`" 'use is imported'
ok "`bash+:can die`" 'die is imported'
ok "`bash+:can warn`" 'warn is imported'
ok "`! bash+:can import`" 'import is not imported'
ok "`! bash+:can main`" 'main is not imported'
ok "`! bash+:can fcopy`" 'fcopy is not imported'
ok "`! bash+:can findlib`" 'findlib is not imported'
ok "`! bash+:can can`" 'can is not imported'
done_testing 8

View file

@ -0,0 +1,23 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+
functions=(
use
import
fcopy
findlib
die
warn
can
)
for f in ${functions[@]}; do
is "$(type -t "bash+:$f")" function \
"bash+:$f is a function"
done
done_testing 7

View file

@ -0,0 +1,70 @@
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# This is a tiny version of test-more-bash that I use here. test-more-bash uses
# bash+, so I want to avoid the circular dependency. This little guy does
# 80-90% what test-more-bash does, with minimal code. It's a good example of
# how nice Bash can be.
#------------------------------------------------------------------------------
plan() {
echo "1..$1"
}
pass() {
let run=run+1
echo "ok $run${1:+ - $1}"
}
fail() {
let run=run+1
echo "not ok $run${1:+ - $1}"
}
is() {
if [ "$1" == "$2" ]; then
pass "$3"
else
fail "$3"
diag "Got: $1"
diag "Want: $2"
fi
}
ok() {
(exit ${1:-$?}) &&
pass "$2" ||
fail "$2"
}
like() {
if [[ "$1" =~ "$2" ]]; then
pass "$3"
else
fail "$3"
diag "Got: $1"
diag "Like: $2"
fi
}
unlike() {
if [[ ! "$1" =~ "$2" ]]; then
pass "$3"
else
fail "$3"
diag "Got: $1"
diag "Dont: $2"
fi
}
done_testing() {
echo "1..${1:-$run}"
}
diag() {
echo "# ${1//$'\n'/$'\n'# }" >&2
}
note() {
echo "# ${1//$'\n'/$'\n'# }"
}

View file

@ -0,0 +1,19 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+ :std can
BASHLIB=test/lib
use Foo::Bar
ok $? 'use Foo::Bar - works'
ok "`can Foo::Bar:baz`" 'Function Foo::Bar:baz exists'
is "$Foo__Bar_VERSION" 1.2.3 '$Foo__Bar_VERSION == 1.2.3'
output=`use Foo::Foo Boo Booo`
ok $? 'use Foo::Foo Boo Booo - works'
is "$output" Boo---Booo 'Correct import called'
done_testing 5

View file

@ -0,0 +1,11 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = git@github.com:ingydotnet/test-more-bash.git
branch = master
commit = 24a6cceabff4d62a5fd25b8945bab6f618fb0b88
parent = 69e22c2e7f6aeadfecb8b00d57c976958da03733
cmdver = 0.3.0

View file

@ -0,0 +1,6 @@
# C language gives closest shell env.
language: c
script:
- git submodule update --init --recursive
- PROVEOPT=-v make test

View file

@ -0,0 +1,15 @@
---
version: 0.0.3
date: Sat Jan 23 16:39:20 PST 2016
changes:
- Make up to date
---
version: 0.0.2
date: Fri Jan 23 21:26:18 PST 2015
changes:
- Make up to date
---
version: 0.0.1
date: Sun Oct 27 22:53:10 PDT 2013
changes:
- First release.

View file

@ -0,0 +1,21 @@
(The MIT License)
Copyright © 2013-2016. Ingy döt Net.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the Software), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,20 @@
NAME := test-more
DOC := doc/$(NAME).swim
MAN3 := man/man3
default: help
help:
@echo 'Rules: test, doc'
.PHONY: test
test:
prove $(PROVEOPT:%=% )test/
doc: ReadMe.pod $(MAN3)/$(NAME).3
ReadMe.pod: $(DOC)
swim --to=pod --complete --wrap $< > $@
$(MAN3)/%.3: doc/%.swim
swim --to=man $< > $@

View file

@ -0,0 +1,30 @@
=meta: 0.0.2
name: test-more
version: 0.0.3
abstract: TAP Testing for Bash
homepage: http://bpan.org/package/test-more/
license: MIT
copyright: 2013-2016
author:
name: Ingy döt Net
email: ingy@ingy.net
github: ingydotnet
twitter: ingydotnet
freenode: ingy
homepage: http://ingy.net
requires:
bash: 3.2.0
bashplus: 0.0.7
test-tap: 0.0.4
test:
cmd: make test
install:
cmd: make install
devel:
git: git@github.org/ingydotnet/test-more-bash.git
irc: irc.freenode.net#bpan
bug: https://github.com/ingydotnet/test-more-bash/issues/

View file

@ -0,0 +1,115 @@
=pod
=for comment
DO NOT EDIT. This Pod was generated by Swim v0.1.41.
See http://github.com/ingydotnet/swim-pm#readme
=encoding utf8
=head1 Name
Test::More - TAP Testing for Bash
=for html
<a href="https://travis-ci.org/ingydotnet/test-more-bash"><img src="https://travis-ci.org/ingydotnet/test-more-bash.png" alt="test-more-bash"></a>
=head1 Synopsis
Write a test file like this. Maybe call it C<test/test.t>:
#!/usr/bin/env bash
TEST_MORE_PATH="/path/to/test-more-bash"
BASHLIB="`
find $TEST_MORE_PATH -type d |
grep -E '/(bin|lib)$' |
xargs -n1 printf "%s:"`"
PATH="$BASHLIB$PATH"
source bash+ :std
use Test::More
plan tests 8
some-command
ok $? 'some-command is ok'
# or:
ok "`some-command`" 'some-command is ok'
pass 'This will always pass'
fail 'This will always fail'
is `echo foo` 'foo' 'foo is foo'
isnt foo bar "foo isn't bar"
like food foo 'food is like foo'
unlike team I "There's no 'I' in 'team'"
diag "A message for stderr"
note "A message for stdout"
Run the test with C<prove> like this:
prove test/test.t
Prove knows it's Bash from the first line (the hashbang), and it just works.
=head1 Description
Test::More is the tried and true testing library for Perl. It uses TAP (the
Test Anything Protocol). This is the same thing for Bash. For the most part it
should work exactly the same.
=head1 Methods
This is the basic usage:
=over
=item * C<plan tests $count>
=item * C<ok $status_code "$label">
=item * C<pass "$label">
=item * C<fail "$label">
=item * C<is "$got" "$want" "label">
=item * C<isnt "$got" "$unwanted" "$label">
=item * C<like "$got" "$regex" "$label">
=item * C<unlike "$got" "$regex" "$label">
=item * C<diag "$message">
=item * C<note "$message">
=item * C<done_testing $count>
=item * C<plan skip_all "$reason">
=item * C<BAIL_OUT "$reason">
=back
More detailed info coming soon.
=head1 Author
Ingy döt Net <ingy@bpan.org>
=head1 Copyright & License
Copyright 2013-2016. Ingy döt Net.
The MIT License (MIT)
=cut

View file

@ -0,0 +1,89 @@
Test::More
==========
TAP Testing for Bash
<badge travis ingydotnet/test-more-bash>
= Synopsis
Write a test file like this. Maybe call it `test/test.t`:
#!/usr/bin/env bash
TEST_MORE_PATH="/path/to/test-more-bash"
BASHLIB="`
find $TEST_MORE_PATH -type d |
grep -E '/(bin|lib)$' |
xargs -n1 printf "%s:"`"
PATH="$BASHLIB$PATH"
source bash+ :std
use Test::More
plan tests 8
some-command
ok $? 'some-command is ok'
# or:
ok "`some-command`" 'some-command is ok'
pass 'This will always pass'
fail 'This will always fail'
is `echo foo` 'foo' 'foo is foo'
isnt foo bar "foo isn't bar"
like food foo 'food is like foo'
unlike team I "There's no 'I' in 'team'"
diag "A message for stderr"
note "A message for stdout"
Run the test with `prove` like this:
prove test/test.t
Prove knows it's Bash from the first line (the hashbang), and it just works.
= Description
Test::More is the tried and true testing library for Perl. It uses TAP (the
Test Anything Protocol). This is the same thing for Bash. For the most part it
should work exactly the same.
= Methods
This is the basic usage:
* `plan tests $count`
* `ok $status_code "$label"`
* `pass "$label"`
* `fail "$label"`
* `is "$got" "$want" "label"`
* `isnt "$got" "$unwanted" "$label"`
* `like "$got" "$regex" "$label"`
* `unlike "$got" "$regex" "$label"`
* `diag "$message"`
* `note "$message"`
* `done_testing $count`
* `plan skip_all "$reason"`
* `BAIL_OUT "$reason"`
More detailed info coming soon.
= Author
Ingy döt Net <ingy@bpan.org>
= Copyright & License
Copyright 2013-2016. Ingy döt Net.
The MIT License (MIT)

View file

@ -0,0 +1,11 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = git@github.com:ingydotnet/bashplus.git
branch = master
commit = d9183af6f46946fabdef1dd8f37824c042a378f8
parent = 05a1bddbe237bbf2c390048c80ca70d4f66c6a37
cmdver = 0.3.0

View file

@ -0,0 +1,6 @@
# C language gives closest shell env.
language: c
script:
- git submodule update --init --recursive
- PROVEOPT=-v make test

View file

@ -0,0 +1,15 @@
---
version: 0.0.7
date: Sat Jan 23 16:28:59 PST 2016
changes:
- Update tooling, and copyright
---
version: 0.0.6
date: Fri Jan 23 21:05:15 PST 2015
changes:
- Update tooling, and copyright
---
version: 0.0.1
date: Sun Oct 27 19:07:51 PDT 2013
changes:
- First release.

View file

@ -0,0 +1,21 @@
(The MIT License)
Copyright © 2013-2016 Ingy döt Net
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the Software), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,45 @@
ifeq ($(MAKECMDGOALS),install)
ifeq "$(shell bpan version 2>/dev/null)" ""
$(error 'BPAN not installed. See http://bpan.org')
endif
endif
NAME := bash+
LIB := lib/$(NAME).bash
DOC := doc/$(NAME).swim
MAN1 := man/man1
MAN3 := man/man3
INSTALL_LIB ?= $(shell bpan env BPAN_LIB)
INSTALL_DIR ?= test
INSTALL_MAN1 ?= $(shell bpan env BPAN_MAN1)
INSTALL_MAN3 ?= $(shell bpan env BPAN_MAN3)
default: help
help:
@echo 'Rules: test, install, doc'
.PHONY: test
test:
prove $(PROVEOPT:%=% )test/
install:
install -C -d -m 0755 $(INSTALL_LIB)/$(INSTALL_DIR)/
install -C -m 0755 $(LIB) $(INSTALL_LIB)/$(INSTALL_DIR)/
install -C -d -m 0755 $(INSTALL_MAN1)/
install -C -d -m 0755 $(INSTALL_MAN3)/
install -C -m 0644 $(MAN1)/$(NAME).1 $(INSTALL_MAN1)/
install -C -m 0644 $(MAN3)/$(NAME).3 $(INSTALL_MAN3)/
.PHONY: doc
doc: ReadMe.pod $(MAN1)/$(NAME).1 $(MAN3)/$(NAME).3
ReadMe.pod: $(DOC)
swim --to=pod --complete --wrap $< > $@
$(MAN1)/%.1: doc/%.swim
swim --to=man $< > $@
$(MAN3)/%.3: doc/%.swim
swim --to=man $< > $@

View file

@ -0,0 +1,28 @@
=meta: 0.0.2
name: bashplus
version: 0.0.7
abstract: Modern Bash Programming
homepage: http://bpan.org/package/bashplus/
license: MIT
copyright: 2013-2016
author:
name: Ingy döt Net
email: ingy@ingy.net
github: ingydotnet
twitter: ingydotnet
freenode: ingy
homepage: http://ingy.net
requires:
bash: 3.2.0
test:
cmd: make test
install:
cmd: make install
devel:
git: git@github.org/ingydotnet/bashplus
irc: irc.freenode.net/bpan
bug: https://github.com/ingydotnet/bashplus/issues/

View file

@ -0,0 +1,77 @@
=pod
=for comment
DO NOT EDIT. This Pod was generated by Swim v0.1.41.
See http://github.com/ingydotnet/swim-pm#readme
=encoding utf8
=head1 Name
Bash+(1) - Modern Bash Programming
=for html
<a href="https://travis-ci.org/ingydotnet/bashplus"><img src="https://travis-ci.org/ingydotnet/bashplus.png" alt="bashplus"></a>
=head1 Synopsis
source bash+ :std :array
use Foo::Bar this that
Array.new args "$@"
if args.empty?; then
die "I need args!"
fi
Foo::Bar.new foo args
this is awesome # <= this is a real command! (You just imported it)
=head1 Description
Bash+ is just Bash... B<plus> some libraries that can make Bash programming a
lot nicer.
=for comment # Installation
Get the source code from GitHub:
git clone git@github.com:ingydotnet/bashplus
Then run:
make test
make install # Possibly with 'sudo'
=head1 Usage
For now look at some libraries the use Bash+:
=over
=item * L<https://github.com/ingydotnet/git-hub>
=item * L<https://github.com/ingydotnet/json-bash>
=item * L<https://github.com/ingydotnet/test-more-bash>
=back
=head1 Status
If you are interested in chatting about this, C</join #bpan> on
irc.freenode.net.
=head1 Author
Written by Ingy döt Net <ingy@ingy.net>
=head1 Copyright & License
Copyright 2013-2016. Ingy döt Net.
The MIT License (MIT).
=cut

View file

@ -0,0 +1,43 @@
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# Bash+ - Modern Bash Programming
#
# Copyright (c) 2013-2016 Ingy döt Net
#------------------------------------------------------------------------------
set -e
shopt -s compat31&>/dev/null||:
#------------------------------------------------------------------------------
# Determine how `bash+` was called, and do the right thing:
#------------------------------------------------------------------------------
if [ "${BASH_SOURCE[0]}" != "$0" ]; then
# 'bash+' is being sourced:
[[ "${BASH_SOURCE[0]}" =~ /bin/bash\\+$ ]] || {
echo "Invalid Bash+ path '${BASH_SOURCE[0]}'" 2> /dev/null
exit 1
}
source "${BASH_SOURCE[0]%/bin/*}"/lib/bash+.bash || return $?
bash+:import "$@"
return $?
else
if [ $# -eq 1 -a "$1" == --version ]; then
echo 'bash+ version 0.0.7'
else
cat <<...
Greetings modern Bash programmer. Welcome to Bash+!
Bash+ is framework that makes Bash programming more like Ruby and Perl.
See: https://github.com/bpan-org/bashplus
If you got here trying to use bash+ in a program, you need to source it:
source bash+
Happy Bash Hacking!
...
fi
fi

View file

@ -0,0 +1,61 @@
Bash+(1)
========
Modern Bash Programming
<badge travis ingydotnet/bashplus>
= Synopsis
source bash+ :std :array
use Foo::Bar this that
Array.new args "$@"
if args.empty?; then
die "I need args!"
fi
Foo::Bar.new foo args
this is awesome # <= this is a real command! (You just imported it)
= Description
Bash+ is just Bash... *plus* some libraries that can make Bash programming a
lot nicer.
## Installation
Get the source code from GitHub:
git clone git@github.com:ingydotnet/bashplus
Then run:
make test
make install # Possibly with 'sudo'
= Usage
For now look at some libraries the use Bash+:
* https://github.com/ingydotnet/git-hub
* https://github.com/ingydotnet/json-bash
* https://github.com/ingydotnet/test-more-bash
= Status
If you are interested in chatting about this, `/join #bpan` on
irc.freenode.net.
= Author
Written by Ingy döt Net <ingy@ingy.net>
= Copyright & License
Copyright 2013-2016. Ingy döt Net.
The MIT License (MIT).

View file

@ -0,0 +1,134 @@
.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{
. if \nF \{
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "Bash+(1) 1"
.TH Bash+(1) 1 "January 2016" "Generated by Swim v0.1.41" "Modern Bash Programming"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "Name"
.IX Header "Name"
Bash+(1) \- Modern Bash Programming
.SH "Synopsis"
.IX Header "Synopsis"
.Vb 1
\& source bash+ :std :array
\&
\& use Foo::Bar this that
\&
\& Array.new args "$@"
\&
\& if args.empty?; then
\& die "I need args!"
\& fi
\&
\& Foo::Bar.new foo args
\&
\& this is awesome # <= this is a real command! (You just imported it)
.Ve
.SH "Description"
.IX Header "Description"
Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer.
.PP
Get the source code from GitHub:
.PP
.Vb 1
\& git clone git@github.com:ingydotnet/bashplus
.Ve
.PP
Then run:
.PP
.Vb 2
\& make test
\& make install # Possibly with \*(Aqsudo\*(Aq
.Ve
.SH "Usage"
.IX Header "Usage"
For now look at some libraries the use Bash+:
.IP "\(bu" 4
<https://github.com/ingydotnet/git\-hub>
.IP "\(bu" 4
<https://github.com/ingydotnet/json\-bash>
.IP "\(bu" 4
<https://github.com/ingydotnet/test\-more\-bash>
.SH "Status"
.IX Header "Status"
If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net.
.SH "Author"
.IX Header "Author"
Written by Ingy döt Net <ingy@ingy.net>
.SH "Copyright & License"
.IX Header "Copyright & License"
Copyright 2013\-2016. Ingy döt Net.
.PP
The \s-1MIT\s0 License (\s-1MIT\s0).

View file

@ -0,0 +1,134 @@
.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{
. if \nF \{
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "Bash+(1) 1"
.TH Bash+(1) 1 "January 2016" "Generated by Swim v0.1.41" "Modern Bash Programming"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "Name"
.IX Header "Name"
Bash+(1) \- Modern Bash Programming
.SH "Synopsis"
.IX Header "Synopsis"
.Vb 1
\& source bash+ :std :array
\&
\& use Foo::Bar this that
\&
\& Array.new args "$@"
\&
\& if args.empty?; then
\& die "I need args!"
\& fi
\&
\& Foo::Bar.new foo args
\&
\& this is awesome # <= this is a real command! (You just imported it)
.Ve
.SH "Description"
.IX Header "Description"
Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer.
.PP
Get the source code from GitHub:
.PP
.Vb 1
\& git clone git@github.com:ingydotnet/bashplus
.Ve
.PP
Then run:
.PP
.Vb 2
\& make test
\& make install # Possibly with \*(Aqsudo\*(Aq
.Ve
.SH "Usage"
.IX Header "Usage"
For now look at some libraries the use Bash+:
.IP "\(bu" 4
<https://github.com/ingydotnet/git\-hub>
.IP "\(bu" 4
<https://github.com/ingydotnet/json\-bash>
.IP "\(bu" 4
<https://github.com/ingydotnet/test\-more\-bash>
.SH "Status"
.IX Header "Status"
If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net.
.SH "Author"
.IX Header "Author"
Written by Ingy döt Net <ingy@ingy.net>
.SH "Copyright & License"
.IX Header "Copyright & License"
Copyright 2013\-2016. Ingy döt Net.
.PP
The \s-1MIT\s0 License (\s-1MIT\s0).

View file

@ -0,0 +1,12 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+ :std
ok $? '`source bash+` works'
is "$BASHPLUS_VERSION" '0.0.7' 'BASHPLUS_VERSION is 0.0.7'
done_testing 2

View file

@ -0,0 +1,22 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+
foo() {
echo O HAI
}
like "$(type bar 2>&1)" 'bar: not found' \
'bar is not yet a function'
bash+:fcopy foo bar
type -t bar &>/dev/null
ok $? 'bar is now a function'
is "$(type foo | tail -n+3)" "$(type bar | tail -n+3)" \
'Copy matches original'
done_testing 3

View file

@ -0,0 +1,18 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+ :std
ok "`bash+:can use`" 'use is imported'
ok "`bash+:can die`" 'die is imported'
ok "`bash+:can warn`" 'warn is imported'
ok "`! bash+:can import`" 'import is not imported'
ok "`! bash+:can main`" 'main is not imported'
ok "`! bash+:can fcopy`" 'fcopy is not imported'
ok "`! bash+:can findlib`" 'findlib is not imported'
ok "`! bash+:can can`" 'can is not imported'
done_testing 8

View file

@ -0,0 +1,23 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+
functions=(
use
import
fcopy
findlib
die
warn
can
)
for f in ${functions[@]}; do
is "$(type -t "bash+:$f")" function \
"bash+:$f is a function"
done
done_testing 7

View file

@ -0,0 +1,70 @@
#!/usr/bin/env bash
#------------------------------------------------------------------------------
# This is a tiny version of test-more-bash that I use here. test-more-bash uses
# bash+, so I want to avoid the circular dependency. This little guy does
# 80-90% what test-more-bash does, with minimal code. It's a good example of
# how nice Bash can be.
#------------------------------------------------------------------------------
plan() {
echo "1..$1"
}
pass() {
let run=run+1
echo "ok $run${1:+ - $1}"
}
fail() {
let run=run+1
echo "not ok $run${1:+ - $1}"
}
is() {
if [ "$1" == "$2" ]; then
pass "$3"
else
fail "$3"
diag "Got: $1"
diag "Want: $2"
fi
}
ok() {
(exit ${1:-$?}) &&
pass "$2" ||
fail "$2"
}
like() {
if [[ "$1" =~ "$2" ]]; then
pass "$3"
else
fail "$3"
diag "Got: $1"
diag "Like: $2"
fi
}
unlike() {
if [[ ! "$1" =~ "$2" ]]; then
pass "$3"
else
fail "$3"
diag "Got: $1"
diag "Dont: $2"
fi
}
done_testing() {
echo "1..${1:-$run}"
}
diag() {
echo "# ${1//$'\n'/$'\n'# }" >&2
}
note() {
echo "# ${1//$'\n'/$'\n'# }"
}

View file

@ -0,0 +1,19 @@
#!/bin/bash -e
source test/test.bash
PATH=$PWD/bin:$PATH
source bash+ :std can
BASHLIB=test/lib
use Foo::Bar
ok $? 'use Foo::Bar - works'
ok "`can Foo::Bar:baz`" 'Function Foo::Bar:baz exists'
is "$Foo__Bar_VERSION" 1.2.3 '$Foo__Bar_VERSION == 1.2.3'
output=`use Foo::Foo Boo Booo`
ok $? 'use Foo::Foo Boo Booo - works'
is "$output" Boo---Booo 'Correct import called'
done_testing 5

View file

@ -0,0 +1,11 @@
; DO NOT EDIT (unless you know what you are doing)
;
; This subdirectory is a git "subrepo", and this file is maintained by the
; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme
;
[subrepo]
remote = git@github.com:ingydotnet/test-tap-bash.git
branch = master
commit = 7890df93f13e684715750a2d6a608e7e79671ca4
parent = 3faca582ee96a320b5675ad67b47ad0f16730446
cmdver = 0.3.0

View file

@ -0,0 +1,6 @@
# C language gives closest shell env.
language: c
script:
- git submodule update --init --recursive
- PROVEOPT=-v make test

View file

@ -0,0 +1,15 @@
---
version: 0.0.4
date: Sat Jan 23 16:32:22 PST 2016
changes:
- Make up to date.
---
version: 0.0.3
date: Fri Jan 23 21:39:39 PST 2015
changes:
- Make up to date.
---
version: 0.0.1
date: Sun Oct 27 23:02:11 PDT 2013
changes:
- First release.

View file

@ -0,0 +1,21 @@
(The MIT License)
Copyright © 2013-2016. Ingy döt Net.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the Software), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,37 @@
ifeq ($(MAKECMDGOALS),install)
ifeq "$(shell bpan version 2>/dev/null)" ""
$(error 'BPAN not installed. See http://bpan.org')
endif
endif
NAME := test-tap
LIB := lib/test/tap.bash
MAN3 := man/man3
INSTALL_LIB ?= $(shell bpan env BPAN_LIB)
INSTALL_DIR ?= test
INSTALL_MAN3 ?= $(shell bpan env BPAN_MAN3)
default: help
help:
@echo 'Rules: test, install, doc'
.PHONY: test
test:
prove $(PROVEOPT:%=% )test/
install:
install -C -d -m 0755 $(INSTALL_LIB)/$(INSTALL_DIR)/
install -C -m 0755 $(LIB) $(INSTALL_LIB)/$(INSTALL_DIR)/
install -C -d -m 0755 $(INSTALL_MAN3)/
install -C -m 0644 $(MAN3)/$(NAME).3 $(INSTALL_MAN3)/
.PHONY: doc
doc: ReadMe.pod $(MAN3)/$(NAME).3
ReadMe.pod: doc/test-tap.swim
swim --to=pod --complete --wrap $< > $@
man/man3/%.3: doc/%.swim
swim --to=man $< > $@

View file

@ -0,0 +1,28 @@
=meta: 0.0.2
name: test-tap
version: 0.0.4
abstract: TAP Test Base for Bash
homepage: http://bpan.org/package/test-tap/
license: MIT
copyright: 2013-2016
author:
name: Ingy döt Net
email: ingy@ingy.net
github: ingydotnet
twitter: ingydotnet
freenode: ingy
homepage: http://ingy.net
requires:
bash: 3.2.0
test:
cmd: make test
install:
cmd: make install
devel:
git: git@github.org/ingydotnet/test-tap-bash.git
irc: irc.freenode.net/bpan
bug: https://github.com/ingydotnet/test-tap-bash/issues/

View file

@ -0,0 +1,66 @@
=pod
=for comment
DO NOT EDIT. This Pod was generated by Swim v0.1.41.
See http://github.com/ingydotnet/swim-pm#readme
=encoding utf8
=head1 Name
Test::Tap - TAP Test Base for Bash
=for html
<a href="https://travis-ci.org/ingydotnet/test-tap-bash"><img src="https://travis-ci.org/ingydotnet/test-tap-bash.png" alt="test-tap-bash"></a>
=head1 Synopsis
source test/tap.bash
Test::Tap:plan tests 1
pass 'Everything is OK!'
=head1 Description
This is a TAP testing base class for Bash. It has all the basic TAP functions,
and works properly from a TAP harness, like the C<prove> utility.
test-tap-bash is used as the base for test-more-bash, which is what you want
if you are writing tests in bash.
See: L<https://github.com/ingydotnet/test-more-bash/>
=head1 Functions
=over
=item C<Test::Tap:init>
Must be called first for every test file/process.
=item C<Test::Tap::plan>
Used to set the plan.
=back
=head1 TODO
=over
=item * Finish this doc.
=back
=head1 Author
Written by Ingy döt Net <ingy@ingy.net>
=head1 Copyright & License
Copyright 2013-2016. Ingy döt Net.
The MIT License (MIT).
=cut

View file

@ -0,0 +1,48 @@
Test::Tap
=========
TAP Test Base for Bash
<badge travis ingydotnet/test-tap-bash>
= Synopsis
source test/tap.bash
Test::Tap:plan tests 1
pass 'Everything is OK!'
= Description
This is a TAP testing base class for Bash. It has all the basic TAP functions,
and works properly from a TAP harness, like the `prove` utility.
test-tap-bash is used as the base for test-more-bash, which is what you want
if you are writing tests in bash.
See: https://github.com/ingydotnet/test-more-bash/
= Functions
- `Test::Tap:init`
Must be called first for every test file/process.
- `Test::Tap::plan`
Used to set the plan.
= TODO
* Finish this doc.
= Author
Written by Ingy döt Net <ingy@ingy.net>
= Copyright & License
Copyright 2013-2016. Ingy döt Net.
The MIT License (MIT).

View file

@ -0,0 +1,119 @@
.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{
. if \nF \{
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "Test::Tap 1"
.TH Test::Tap 1 "January 2016" "Generated by Swim v0.1.41" "\s-1TAP\s0 Test Base for Bash"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "Name"
.IX Header "Name"
Test::Tap \- \s-1TAP\s0 Test Base for Bash
.SH "Synopsis"
.IX Header "Synopsis"
.Vb 1
\& source test/tap.bash
\&
\& Test::Tap:plan tests 1
\&
\& pass \*(AqEverything is OK!\*(Aq
.Ve
.SH "Description"
.IX Header "Description"
This is a \s-1TAP\s0 testing base class for Bash. It has all the basic \s-1TAP\s0 functions, and works properly from a \s-1TAP\s0 harness, like the \f(CW\*(C`prove\*(C'\fR utility.
.PP
test-tap-bash is used as the base for test-more-bash, which is what you want if you are writing tests in bash.
.PP
See: <https://github.com/ingydotnet/test\-more\-bash/>
.SH "Functions"
.IX Header "Functions"
.ie n .IP """Test::Tap:init""" 4
.el .IP "\f(CWTest::Tap:init\fR" 4
.IX Item "Test::Tap:init"
Must be called first for every test file/process.
.ie n .IP """Test::Tap::plan""" 4
.el .IP "\f(CWTest::Tap::plan\fR" 4
.IX Item "Test::Tap::plan"
Used to set the plan.
.SH "TODO"
.IX Header "TODO"
.IP "\(bu" 4
Finish this doc.
.SH "Author"
.IX Header "Author"
Written by Ingy döt Net <ingy@ingy.net>
.SH "Copyright & License"
.IX Header "Copyright & License"
Copyright 2013\-2016. Ingy döt Net.
.PP
The \s-1MIT\s0 License (\s-1MIT\s0).

View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
source test/helper.bash
source lib/test/tap.bash
Test::Tap:init tests 1
output=$(prove -v test/test/{b,f}ail.t 2>&1)
test-helper:like \
"$output" \
'Bailout called. Further testing stopped: Get me outta here' \
'Test::Tap:BAIL_OUT works'

View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init
Test::Tap:pass one
Test::Tap:pass two
Test::Tap:done_testing

View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
source test/helper.bash
source lib/test/tap.bash
Test::Tap:init tests 2
output="$(prove -v test/test/fail.t 2>&1)"
# echo "# >>>${output//$'\n'/$'\n'# }<<<" >&2
test-helper:like \
"$output" \
'not ok 1 - I am a failure' \
'Test::Tap:fail works'
test-helper:like \
"$output" \
'Failed 1/1 subtests' \
'Proper summary'

View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
source test/helper.bash
source lib/test/tap.bash
Test::Tap:init tests 1
output="$(prove -v test/test/fail_fast.t 2>&1)"
# echo ">>>$output<<<" >&2
test-helper:like \
"$output" \
'Further testing stopped: Bailing out on status=1' \
'Test::Tap:BAIL_OUT works'

View file

@ -0,0 +1,9 @@
test-helper:like() {
local got=$1 regex=$2 label=$3
if [[ "$got" =~ "$regex" ]]; then
Test::Tap:pass "$label"
else
Test::Tap:fail "$label"
Test::Tap:diag "Got: '$got'"
fi
}

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init tests 3
Test::Tap:pass 'pass 1 - with label'
Test::Tap:pass
Test::Tap:pass 'pass 3 - 2 has no label'

View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init
Test::Tap:plan tests 3
for n in 1 2 3; do
Test::Tap:pass "Test #$n"
done

View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
source test/helper.bash
source lib/test/tap.bash
Test::Tap:init tests 4
for s in plan init; do
output=$(prove test/test/skip-all-$s.t)
test-helper:like \
"$output" \
"skipped: Test for skip_all from $s" \
"skip_all from $s: it works"
test-helper:like \
"$output" \
'Result: NOTESTS' \
"skip_all from $s: No tests run"
done

View file

@ -0,0 +1,13 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init tests 4
Test::Tap:pass 'pass with label'
Test::Tap:pass
Test::Tap:pass 'previous test has no label'
msg="$(Test::Tap:fail 'faaaaailll' 2>/dev/null)"
if [[ "$msg" =~ not\ ok\ 4\ -\ faaaaailll ]]; then
Test::Tap:pass 'fail works'
fi

View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init tests 5
Test::Tap:pass 'test #1'
Test::Tap:pass 'test #2'
Test::Tap:pass 'test #3'
Test::Tap:BAIL_OUT 'Get me outta here'
Test::Tap:pass 'test #4'
Test::Tap:fail 'test #5'

View file

@ -0,0 +1,7 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init tests 1
Test::Tap:fail 'I am a failure'

View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init tests 5
Test::Tap:BAIL_ON_FAIL
Test::Tap:pass 'test #1'
Test::Tap:pass 'test #2'
Test::Tap:fail 'test #3'
Test::Tap:pass 'test #4'
Test::Tap:pass 'test #5'

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init skip_all 'Test for skip_all from init'
Test::Tap:diag "This code should not be run"
Test::Tap:fail

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
source lib/test/tap.bash
Test::Tap:init
Test::Tap:plan skip_all 'Test for skip_all from plan'
Test::Tap:diag "This code should not be run"
Test::Tap:fail

View file

@ -0,0 +1,173 @@
.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is turned on, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{
. if \nF \{
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "Test::More 1"
.TH Test::More 1 "January 2016" "Generated by Swim v0.1.41" "\s-1TAP\s0 Testing for Bash"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "Name"
.IX Header "Name"
Test::More \- \s-1TAP\s0 Testing for Bash
.SH "Synopsis"
.IX Header "Synopsis"
Write a test file like this. Maybe call it \f(CW\*(C`test/test.t\*(C'\fR:
.PP
.Vb 1
\& #!/usr/bin/env bash
\&
\& TEST_MORE_PATH="/path/to/test\-more\-bash"
\& BASHLIB="\`
\& find $TEST_MORE_PATH \-type d |
\& grep \-E \*(Aq/(bin|lib)$\*(Aq |
\& xargs \-n1 printf "%s:"\`"
\& PATH="$BASHLIB$PATH"
\&
\& source bash+ :std
\&
\& use Test::More
\&
\& plan tests 8
\&
\& some\-command
\& ok $? \*(Aqsome\-command is ok\*(Aq
\&
\& # or:
\& ok "\`some\-command\`" \*(Aqsome\-command is ok\*(Aq
\&
\& pass \*(AqThis will always pass\*(Aq
\&
\& fail \*(AqThis will always fail\*(Aq
\&
\& is \`echo foo\` \*(Aqfoo\*(Aq \*(Aqfoo is foo\*(Aq
\&
\& isnt foo bar "foo isn\*(Aqt bar"
\&
\& like food foo \*(Aqfood is like foo\*(Aq
\&
\& unlike team I "There\*(Aqs no \*(AqI\*(Aq in \*(Aqteam\*(Aq"
\&
\& diag "A message for stderr"
\&
\& note "A message for stdout"
.Ve
.PP
Run the test with \f(CW\*(C`prove\*(C'\fR like this:
.PP
.Vb 1
\& prove test/test.t
.Ve
.PP
Prove knows it's Bash from the first line (the hashbang), and it just works.
.SH "Description"
.IX Header "Description"
Test::More is the tried and true testing library for Perl. It uses \s-1TAP \s0(the Test Anything Protocol). This is the same thing for Bash. For the most part it should work exactly the same.
.SH "Methods"
.IX Header "Methods"
This is the basic usage:
.IP "\(bu" 4
\&\f(CW\*(C`plan tests $count\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`ok $status_code "$label"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`pass "$label"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`fail "$label"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`is "$got" "$want" "label"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`isnt "$got" "$unwanted" "$label"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`like "$got" "$regex" "$label"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`unlike "$got" "$regex" "$label"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`diag "$message"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`note "$message"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`done_testing $count\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`plan skip_all "$reason"\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`BAIL_OUT "$reason"\*(C'\fR
.PP
More detailed info coming soon.
.SH "Author"
.IX Header "Author"
Ingy döt Net <ingy@bpan.org>
.SH "Copyright & License"
.IX Header "Copyright & License"
Copyright 2013\-2016. Ingy döt Net.
.PP
The \s-1MIT\s0 License (\s-1MIT\s0)

View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
source test/setup
use Test::More
output=$(prove -v test/test/fail1.t 2>&1) || true
like "$output" 'not ok 1 - fail with label' \
'fail with label'
like "$output" 'not ok 2' \
'fail with no label'
like "$output" 'not ok 3 - is foo bar' \
'fail output is correct'
like "$output" "# got: 'foo'" \
'difference reporting - got'
like "$output" "# expected: 'bar'" \
'difference reporting - want'
done_testing 5

View file

@ -0,0 +1,20 @@
#!/usr/bin/env bash
source test/setup
use Test::More
plan tests 5
pass 'This test always passes'
is 'foo' "foo" 'foo is foo'
ok "`true`" 'true is true'
ok "`[ 123 -eq $((61+62)) ]`" 'Math works'
ok "`[[ ! team =~ I ]]`" "There's no I in team"
# diag "A msg for stderr"
note "A msg for stdout"

View file

@ -0,0 +1,9 @@
#!/usr/bin/env bash
source test/setup
use Test::More tests 3
pass 'pass 1 - with label'
pass
pass 'pass 3 - 2 has no label'

View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -e
BASHLIB="`find $PWD -type d | grep -E '/(bin|lib)$' | xargs -n1 printf "%s:"`"
PATH="$BASHLIB:$PATH"
source bash+ :std

View file

@ -0,0 +1,11 @@
#!/usr/bin/env bash
source test/setup
use Test::More
output=$(prove -v test/test/skip_all.t 2>&1) || true
like "$output" 'skipped: Skipping this test to demo skip_all' \
'skip_all works'
done_testing 1

View file

@ -0,0 +1,12 @@
#!/usr/bin/env bash
source test/setup
use Test::More
fail 'fail with label'
fail
is foo bar 'is foo bar'
done_testing 3

View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
source test/setup
use Test::More
plan skip_all 'Skipping this test to demo skip_all'
fail "Don't run this code"
done_testing

View file

@ -0,0 +1,743 @@
.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35)
.\"
.\" Standard preamble:
.\" ========================================================================
.de Sp \" Vertical space (when we can't use .PP)
.if t .sp .5v
.if n .sp
..
.de Vb \" Begin verbatim text
.ft CW
.nf
.ne \\$1
..
.de Ve \" End verbatim text
.ft R
.fi
..
.\" Set up some character translations and predefined strings. \*(-- will
.\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left
.\" double quote, and \*(R" will give a right double quote. \*(C+ will
.\" give a nicer C++. Capital omega is used to do unbreakable dashes and
.\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff,
.\" nothing in troff, for use with C<>.
.tr \(*W-
.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p'
.ie n \{\
. ds -- \(*W-
. ds PI pi
. if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
. if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
. ds L" ""
. ds R" ""
. ds C` ""
. ds C' ""
'br\}
.el\{\
. ds -- \|\(em\|
. ds PI \(*p
. ds L" ``
. ds R" ''
. ds C`
. ds C'
'br\}
.\"
.\" Escape single quotes in literal strings from groff's Unicode transform.
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.\"
.\" If the F register is >0, we'll generate index entries on stderr for
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
.\" entries marked with X<> in POD. Of course, you'll have to process the
.\" output yourself in some meaningful fashion.
.\"
.\" Avoid warning from groff about undefined register 'F'.
.de IX
..
.nr rF 0
.if \n(.g .if rF .nr rF 1
.if (\n(rF:(\n(.g==0)) \{\
. if \nF \{\
. de IX
. tm Index:\\$1\t\\n%\t"\\$2"
..
. if !\nF==2 \{\
. nr % 0
. nr F 2
. \}
. \}
.\}
.rr rF
.\" ========================================================================
.\"
.IX Title "STDIN 1"
.TH STDIN 1 "January 2020" "Generated by Swim v0.1.48" "Git Submodule Alternative"
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
.\" way too many mistakes in technical documents.
.if n .ad l
.nh
.SH "Name"
.IX Header "Name"
git-subrepo \- Git Submodule Alternative
.SH "Synopsis"
.IX Header "Synopsis"
.Vb 1
\& git subrepo \-h # Help Overview
\&
\& git subrepo clone <remote\-url> [<subdir>]
\& git subrepo init <subdir>
\& git subrepo pull <subdir>
\& git subrepo push <subdir>
\&
\& git subrepo fetch <subdir>
\& git subrepo branch <subdir>
\& git subrepo commit <subdir>
\& git subrepo config <subdir>
\&
\& git subrepo status [<subdir>]
\& git subrepo clean <subdir>
\&
\& git subrepo help [<command> | \-\-all]
\& git subrepo version
\& git subrepo upgrade
.Ve
.SH "Description"
.IX Header "Description"
This git command \*(L"clones\*(R" an external git repo into a subdirectory of your
repo. Later on, upstream changes can be pulled in, and local changes can be
pushed back. Simple.
.SH "Benefits"
.IX Header "Benefits"
This command is an improvement from \f(CW\*(C`git\-submodule\*(C'\fR and \f(CW\*(C`git\-subtree\*(C'\fR; two
other git commands with similar goals, but various problems.
.PP
It assumes there are 3 main roles of people interacting with a repo, and
attempts to serve them all well:
.IP "\(bu" 4
\&\fBowner\fR \- The person who authors/owns/maintains a repo.
.IP "\(bu" 4
\&\fBusers\fR \- People who are just using/installing the repo.
.IP "\(bu" 4
\&\fBcollaborators\fR \- People who commit code to the repo and subrepos.
.PP
The \f(CW\*(C`git\-subrepo\*(C'\fR command benefits these roles in the following ways:
.IP "\(bu" 4
Simple and intuitive commandline usage (with tab completion).
.IP "\(bu" 4
Users get your repo and all your subrepos just by cloning your repo.
.IP "\(bu" 4
Users do not need to install \f(CW\*(C`git\-subrepo\*(C'\fR, ever.
.IP "\(bu" 4
Collaborators do not need to install unless they want to push/pull.
.IP "\(bu" 4
Collaborators know when a subdir is a subrepo (it has a \f(CW\*(C`.gitrepo\*(C'\fR file).
.IP "\(bu" 4
The \f(CW\*(C`.gitrepo\*(C'\fR file never gets pushed back to the subrepo upstream.
.IP "\(bu" 4
Well named branches and remotes are generated for manual operations.
.IP "\(bu" 4
Owners do not deal with the complications of keeping submodules in sync.
.IP "\(bu" 4
Subrepo repositories can contain subrepos themselves.
.IP "\(bu" 4
Branching with subrepos JustWorks™.
.IP "\(bu" 4
Different branches can have different subrepos in different states, etc.
.IP "\(bu" 4
Moving/renaming/deleting a subrepo subdir JustWorks™.
.IP "\(bu" 4
You can \f(CW\*(C`init\*(C'\fR an existing subdirectory into a subrepo.
.IP "\(bu" 4
Your git history is kept squeaky clean.
.IP "\(bu" 4
Upstream history (clone/pull) is condensed into a single commit.
.IP "\(bu" 4
Pulls can use a \f(CW\*(C`merge\*(C'\fR, \f(CW\*(C`rebase\*(C'\fR or \f(CW\*(C`force\*(C'\fR strategies.
.IP "\(bu" 4
You can see the subrepo history with \f(CW\*(C`git log subrepo/<subdir>/fetch\*(C'\fR.
.IP "\(bu" 4
Commits pushed back upstream are \fBnot\fR condensed (by default).
.IP "\(bu" 4
Trivial to try any subrepo operations and then reset back.
.IP "\(bu" 4
No configuration required.
.IP "\(bu" 4
Does not introduce history that messes up other git commands.
.IP "\(bu" 4
Fixes known rebase failures with \f(CW\*(C`git\-subtree\*(C'\fR.
.SH "Installation"
.IX Header "Installation"
The best short answer is:
.PP
.Vb 2
\& git clone https://github.com/ingydotnet/git\-subrepo /path/to/git\-subrepo
\& echo \*(Aqsource /path/to/git\-subrepo/.rc\*(Aq >> ~/.bashrc
.Ve
.PP
The complete \*(L"Installation Instructions\*(R" can be found below.
.PP
Note: git-subrepo needs a git version (> 2.7) that supports worktree:s.
.SH "Commands"
.IX Header "Commands"
All the \fBsubrepo\fR commands use names of actual Git commands and try to do
operations that are similar to their Git counterparts. They also attempt to
give similar output in an attempt to make the subrepo usage intuitive to
experienced Git users.
.PP
Please note that the commands are \fInot\fR exact equivalents, and do not take
all the same arguments. Keep reading…
.ie n .IP """git subrepo clone <repository> [<subdir>] [\-b <branch>] [\-f] [\-m <msg>] [\-e] [\-\-method <merge|rebase>]""" 4
.el .IP "\f(CWgit subrepo clone <repository> [<subdir>] [\-b <branch>] [\-f] [\-m <msg>] [\-e] [\-\-method <merge|rebase>]\fR" 4
.IX Item "git subrepo clone <repository> [<subdir>] [-b <branch>] [-f] [-m <msg>] [-e] [--method <merge|rebase>]"
Add a repository as a subrepo in a subdir of your repository.
.Sp
This is similar in feel to \f(CW\*(C`git clone\*(C'\fR. You just specify the remote repo url,
and optionally a sub-directory and/or branch name. The repo will be fetched
and merged into the subdir.
.Sp
The subrepo history is \fIsquashed\fR into a single commit that contains the
reference information. This information is also stored in a special file
called \f(CW\*(C`<subdir>/.gitrepo\*(C'\fR. The presence of this file indicates that the
directory is a subrepo.
.Sp
All subsequent commands refer to the subrepo by the name of the
\&\fIsubdir\fR. From the subdir, all the current information about the subrepo
can be obtained.
.Sp
The \f(CW\*(C`\-\-force\*(C'\fR option will \*(L"reclone\*(R" (completely replace) an existing subdir.
.Sp
The \f(CW\*(C`\-\-method\*(C'\fR option will decide how the join process between branches are
performed. The default option is merge.
.Sp
The \f(CW\*(C`clone\*(C'\fR command accepts the \f(CW\*(C`\-\-branch=\*(C'\fR \f(CW\*(C`\-\-edit\*(C'\fR, \f(CW\*(C`\-\-force\*(C'\fR and \f(CW\*(C`\-\-
message=\*(C'\fR options.
.ie n .IP """git subrepo init <subdir> [\-r <remote>] [\-b <branch>] [\-\-method <merge|rebase>]""" 4
.el .IP "\f(CWgit subrepo init <subdir> [\-r <remote>] [\-b <branch>] [\-\-method <merge|rebase>]\fR" 4
.IX Item "git subrepo init <subdir> [-r <remote>] [-b <branch>] [--method <merge|rebase>]"
Turn an existing subdirectory into a subrepo.
.Sp
If you want to expose a subdirectory of your project as a published subrepo,
this command will do that. It will split out the content of a normal
subdirectory into a branch and start tracking it as a subrepo. Afterwards your
original repo will look exactly the same except that there will be a \f(CW\*(C`<subdir>/.gitrepo\*(C'\fR file.
.Sp
If you specify the \f(CW\*(C`\-\-remote\*(C'\fR (and optionally the \f(CW\*(C`\-\-branch\*(C'\fR) option, the
values will be added to the \f(CW\*(C`<subdir>/.gitrepo\*(C'\fR file. The \f(CW\*(C`\-\-remote\*(C'\fR
option is the upstream \s-1URL,\s0 and the \f(CW\*(C`\-\-branch\*(C'\fR option is the upstream branch
to push to. These values will be needed to do a \f(CW\*(C`git subrepo push\*(C'\fR command,
but they can be provided later on the \f(CW\*(C`push\*(C'\fR command (and saved to \f(CW\*(C`<subdir>/.gitrepo\*(C'\fR if you also specify the \f(CW\*(C`\-\-update\*(C'\fR option).
.Sp
Note: You will need to create the empty upstream repo and push to it on your
own, using \f(CW\*(C`git subrepo push <subdir>\*(C'\fR.
.Sp
The \f(CW\*(C`\-\-method\*(C'\fR option will decide how the join process between branches are
performed. The default option is merge.
.Sp
The \f(CW\*(C`init\*(C'\fR command accepts the \f(CW\*(C`\-\-branch=\*(C'\fR and \f(CW\*(C`\-\-remote=\*(C'\fR options.
.ie n .IP """git subrepo pull <subdir>|\-\-all [\-M|\-R|\-f] [\-m <msg>] [\-e] [\-b <branch>] [\-r <remote>] [\-u]""" 4
.el .IP "\f(CWgit subrepo pull <subdir>|\-\-all [\-M|\-R|\-f] [\-m <msg>] [\-e] [\-b <branch>] [\-r <remote>] [\-u]\fR" 4
.IX Item "git subrepo pull <subdir>|--all [-M|-R|-f] [-m <msg>] [-e] [-b <branch>] [-r <remote>] [-u]"
Update the subrepo subdir with the latest upstream changes.
.Sp
The \f(CW\*(C`pull\*(C'\fR command fetches the latest content from the remote branch pointed
to by the subrepo's \f(CW\*(C`.gitrepo\*(C'\fR file, and then tries to merge the changes into
the corresponding subdir. It does this by making a branch of the local commits
to the subdir and then merging or rebasing (see below) it with the fetched
upstream content. After the merge, the content of the new branch replaces your
subdir, the \f(CW\*(C`.gitrepo\*(C'\fR file is updated and a single 'pull' commit is added to
your mainline history.
.Sp
The \f(CW\*(C`pull\*(C'\fR command will attempt to do the following commands in one go:
.Sp
.Vb 6
\& git subrepo fetch <subdir>
\& git subrepo branch <subdir>
\& git merge/rebase subrepo/<subdir>/fetch subrepo/<subdir>
\& git subrepo commit <subdir>
\& # Only needed for a consequential push:
\& git update\-ref refs/subrepo/<subdir>/pull subrepo/<subdir>
.Ve
.Sp
In other words, you could do all the above commands yourself, for the same
effect. If any of the commands fail, subrepo will stop and tell you to finish
this by hand. Generally a failure would be in the merge or rebase part, where
conflicts can happen. Since Git has lots of ways to resolve conflicts to your
personal tastes, the subrepo command defers to letting you do this by hand.
.Sp
When pulling new data, the method selected in clone/init is used. This has no
effect on the final result of the pull, since it becomes a single commit. But
it does affect the resulting \f(CW\*(C`subrepo/<subdir>\*(C'\fR branch, which is often
used for a subrepo \f(CW\*(C`push\*(C'\fR command. See 'push' below for more information. If
you want to change the method you can use the \f(CW\*(C`config\*(C'\fR command for this.
.Sp
When you pull you can assume a fast-forward strategy (default) or you can
specify a \f(CW\*(C`\-\-rebase\*(C'\fR, \f(CW\*(C`\-\-merge\*(C'\fR or \f(CW\*(C`\-\-force\*(C'\fR strategy. The latter is the
same as a \f(CW\*(C`clone \-\-force\*(C'\fR operation, using the current remote and branch.
.Sp
Like the \f(CW\*(C`clone\*(C'\fR command, \f(CW\*(C`pull\*(C'\fR will squash all the changes (since the last
pull or clone) into one commit. This keeps your mainline history nice and
clean. You can easily see the subrepo's history with the \f(CW\*(C`git log\*(C'\fR command:
.Sp
.Vb 1
\& git log refs/subrepo/<subdir>/fetch
.Ve
.Sp
The set of commands used above are described in detail below.
.Sp
The \f(CW\*(C`pull\*(C'\fR command accepts the \f(CW\*(C`\-\-all\*(C'\fR, \f(CW\*(C`\-\-branch=\*(C'\fR, \f(CW\*(C`\-\-edit\*(C'\fR, \f(CW\*(C`\-\-force\*(C'\fR,
\&\f(CW\*(C`\-\-message=\*(C'\fR, \f(CW\*(C`\-\-remote=\*(C'\fR and \f(CW\*(C`\-\-update\*(C'\fR options.
.ie n .IP """git subrepo push <subdir>|\-\-all [<branch>] [\-r <remote>] [\-b <branch>] [\-M|\-R] [\-u] [\-f] [\-s] [\-N]""" 4
.el .IP "\f(CWgit subrepo push <subdir>|\-\-all [<branch>] [\-r <remote>] [\-b <branch>] [\-M|\-R] [\-u] [\-f] [\-s] [\-N]\fR" 4
.IX Item "git subrepo push <subdir>|--all [<branch>] [-r <remote>] [-b <branch>] [-M|-R] [-u] [-f] [-s] [-N]"
Push a properly merged subrepo branch back upstream.
.Sp
This command takes the subrepo branch from a successful pull command and
pushes the history back to its designated remote and branch. You can also use
the \f(CW\*(C`branch\*(C'\fR command and merge things yourself before pushing if you want to
(although that is probably a rare use case).
.Sp
The \f(CW\*(C`push\*(C'\fR command requires a branch that has been properly merged/rebased
with the upstream \s-1HEAD\s0 (unless the upstream \s-1HEAD\s0 is empty, which is common
when doing a first \f(CW\*(C`push\*(C'\fR after an \f(CW\*(C`init\*(C'\fR). That means the upstream \s-1HEAD\s0 is
one of the commits in the branch.
.Sp
By default the branch ref \f(CW\*(C`refs/subrepo/<subdir>/pull\*(C'\fR will be pushed,
but you can specify a (properly merged) branch to push.
.Sp
After that, the \f(CW\*(C`push\*(C'\fR command just checks that the branch contains the
upstream \s-1HEAD\s0 and then pushes it upstream.
.Sp
The \f(CW\*(C`\-\-force\*(C'\fR option will do a force push. Force pushes are typically
discouraged. Only use this option if you fully understand it. (The \f(CW\*(C`\-\-force\*(C'\fR
option will \s-1NOT\s0 check for a proper merge. \s-1ANY\s0 branch will be force pushed!)
.Sp
The \f(CW\*(C`push\*(C'\fR command accepts the \f(CW\*(C`\-\-all\*(C'\fR, \f(CW\*(C`\-\-branch=\*(C'\fR, \f(CW\*(C`\-\-dry\-run\*(C'\fR, \f(CW\*(C`\-\-
force\*(C'\fR, \f(CW\*(C`\-\-merge\*(C'\fR, \f(CW\*(C`\-\-rebase\*(C'\fR, \f(CW\*(C`\-\-remote=\*(C'\fR, \f(CW\*(C`\-\-squash\*(C'\fR and \f(CW\*(C`\-\-
update\*(C'\fR options.
.ie n .IP """git subrepo fetch <subdir>|\-\-all [\-r <remote>] [\-b <branch>]""" 4
.el .IP "\f(CWgit subrepo fetch <subdir>|\-\-all [\-r <remote>] [\-b <branch>]\fR" 4
.IX Item "git subrepo fetch <subdir>|--all [-r <remote>] [-b <branch>]"
Fetch the remote/upstream content for a subrepo.
.Sp
It will create a Git reference called \f(CW\*(C`subrepo/<subdir>/fetch\*(C'\fR that
points at the same commit as \f(CW\*(C`FETCH_HEAD\*(C'\fR. It will also create a remote
called \f(CW\*(C`subrepo/<subdir>\*(C'\fR. These are temporary and you can easily remove
them with the subrepo \f(CW\*(C`clean\*(C'\fR command.
.Sp
The \f(CW\*(C`fetch\*(C'\fR command accepts the \f(CW\*(C`\-\-all\*(C'\fR, \f(CW\*(C`\-\-branch=\*(C'\fR and \f(CW\*(C`\-\-
remote=\*(C'\fR options.
.ie n .IP """git subrepo branch <subdir>|\-\-all [\-f] [\-F]""" 4
.el .IP "\f(CWgit subrepo branch <subdir>|\-\-all [\-f] [\-F]\fR" 4
.IX Item "git subrepo branch <subdir>|--all [-f] [-F]"
Create a branch with local subrepo commits.
.Sp
Scan the history of the mainline for all the commits that affect the \f(CW\*(C`subdir\*(C'\fR
and create a new branch from them called \f(CW\*(C`subrepo/<subdir>\*(C'\fR.
.Sp
This is useful for doing \f(CW\*(C`pull\*(C'\fR and \f(CW\*(C`push\*(C'\fR commands by hand.
.Sp
Use the \f(CW\*(C`\-\-force\*(C'\fR option to write over an existing \f(CW\*(C`subrepo/<subdir>\*(C'\fR branch.
.Sp
The \f(CW\*(C`branch\*(C'\fR command accepts the \f(CW\*(C`\-\-all\*(C'\fR, \f(CW\*(C`\-\-fetch\*(C'\fR and \f(CW\*(C`\-\-force\*(C'\fR options.
.ie n .IP """git subrepo commit <subdir> [<subrepo\-ref>] [\-m <msg>] [\-e] [\-f] [\-F]""" 4
.el .IP "\f(CWgit subrepo commit <subdir> [<subrepo\-ref>] [\-m <msg>] [\-e] [\-f] [\-F]\fR" 4
.IX Item "git subrepo commit <subdir> [<subrepo-ref>] [-m <msg>] [-e] [-f] [-F]"
Add subrepo branch to current history as a single commit.
.Sp
This command is generally used after a hand-merge. You have done a \f(CW\*(C`subrepo
branch\*(C'\fR and merged (rebased) it with the upstream. This command takes the \s-1HEAD\s0
of that branch, puts its content into the subrepo subdir and adds a new commit
for it to the top of your mainline history.
.Sp
This command requires that the upstream \s-1HEAD\s0 be in the \f(CW\*(C`subrepo/<subdir>\*(C'\fR
branch history. That way the same branch can push upstream. Use the \f(CW\*(C`\-\-force\*(C'\fR
option to commit anyway.
.Sp
The \f(CW\*(C`commit\*(C'\fR command accepts the \f(CW\*(C`\-\-edit\*(C'\fR, \f(CW\*(C`\-\-fetch\*(C'\fR, \f(CW\*(C`\-\-force\*(C'\fR and \f(CW\*(C`\-\-
message=\*(C'\fR options.
.ie n .IP """git subrepo status [<subdir>|\-\-all|\-\-ALL] [\-F] [\-q|\-v]""" 4
.el .IP "\f(CWgit subrepo status [<subdir>|\-\-all|\-\-ALL] [\-F] [\-q|\-v]\fR" 4
.IX Item "git subrepo status [<subdir>|--all|--ALL] [-F] [-q|-v]"
Get the status of a subrepo. Uses the \f(CW\*(C`\-\-all\*(C'\fR option by default. If the \f(CW\*(C`\-\-
quiet\*(C'\fR flag is used, just print the subrepo names, one per line.
.Sp
The \f(CW\*(C`\-\-verbose\*(C'\fR option will show all the recent local and upstream commits.
.Sp
Use \f(CW\*(C`\-\-ALL\*(C'\fR to show the subrepos of the subrepos (ie the
\&\*(L"subsubrepos\*(R"), if any.
.Sp
The \f(CW\*(C`status\*(C'\fR command accepts the \f(CW\*(C`\-\-all\*(C'\fR, \f(CW\*(C`\-\-ALL\*(C'\fR, \f(CW\*(C`\-\-fetch\*(C'\fR, \f(CW\*(C`\-\-quiet\*(C'\fR
and \f(CW\*(C`\-\-verbose\*(C'\fR options.
.ie n .IP """git subrepo clean <subdir>|\-\-all|\-\-ALL [\-f]""" 4
.el .IP "\f(CWgit subrepo clean <subdir>|\-\-all|\-\-ALL [\-f]\fR" 4
.IX Item "git subrepo clean <subdir>|--all|--ALL [-f]"
Remove artifacts created by \f(CW\*(C`fetch\*(C'\fR and \f(CW\*(C`branch\*(C'\fR commands.
.Sp
The \f(CW\*(C`fetch\*(C'\fR and \f(CW\*(C`branch\*(C'\fR operations (and other commands that call them)
create temporary things like refs, branches and remotes. This command removes
all those things.
.Sp
Use \f(CW\*(C`\-\-force\*(C'\fR to remove refs. Refs are not removed by default because they
are sometimes needed between commands.
.Sp
Use \f(CW\*(C`\-\-all\*(C'\fR to clean up after all the current subrepos. Sometimes you might
change to a branch where a subrepo doesn't exist, and then \f(CW\*(C`\-\-all\*(C'\fR won't find
it. Use \f(CW\*(C`\-\-ALL\*(C'\fR to remove any artifacts that were ever created by subrepo.
.Sp
To remove \s-1ALL\s0 subrepo artifacts:
.Sp
.Vb 1
\& git subrepo clean \-\-ALL \-\-force
.Ve
.Sp
The \f(CW\*(C`clean\*(C'\fR command accepts the \f(CW\*(C`\-\-all\*(C'\fR, \f(CW\*(C`\-\-ALL\*(C'\fR, and \f(CW\*(C`\-\-force\*(C'\fR options.
.ie n .IP """git subrepo config <subdir> <option> [<value>] [\-f]""" 4
.el .IP "\f(CWgit subrepo config <subdir> <option> [<value>] [\-f]\fR" 4
.IX Item "git subrepo config <subdir> <option> [<value>] [-f]"
Read or update configuration values in the subdir/.gitrepo file.
.Sp
Because most of the values stored in the .gitrepo file are generated you
will need to use \f(CW\*(C`\-\-force\*(C'\fR if you want to change anything else then the
\&\f(CW\*(C`method\*(C'\fR option.
.Sp
Example to update the \f(CW\*(C`method\*(C'\fR option for a subrepo:
.Sp
.Vb 1
\& git subrepo config foo method rebase
.Ve
.ie n .IP """git subrepo help [<command>|\-\-all]""" 4
.el .IP "\f(CWgit subrepo help [<command>|\-\-all]\fR" 4
.IX Item "git subrepo help [<command>|--all]"
Same as \f(CW\*(C`git help subrepo\*(C'\fR. Will launch the manpage. For the shorter usage,
use \f(CW\*(C`git subrepo \-h\*(C'\fR.
.Sp
Use \f(CW\*(C`git subrepo help <command>\*(C'\fR to get help for a specific command. Use
\&\f(CW\*(C`\-\-all\*(C'\fR to get a summary of all commands.
.Sp
The \f(CW\*(C`help\*(C'\fR command accepts the \f(CW\*(C`\-\-all\*(C'\fR option.
.ie n .IP """git subrepo version [\-q|\-v]""" 4
.el .IP "\f(CWgit subrepo version [\-q|\-v]\fR" 4
.IX Item "git subrepo version [-q|-v]"
This command will display version information about git-subrepo and its
environment. For just the version number, use \f(CW\*(C`git subrepo \-\-version\*(C'\fR. Use
\&\f(CW\*(C`\-\-verbose\*(C'\fR for more version info, and \f(CW\*(C`\-\-quiet\*(C'\fR for less.
.Sp
The \f(CW\*(C`version\*(C'\fR command accepts the \f(CW\*(C`\-\-quiet\*(C'\fR and \f(CW\*(C`\-\-verbose\*(C'\fR options.
.ie n .IP """git subrepo upgrade""" 4
.el .IP "\f(CWgit subrepo upgrade\fR" 4
.IX Item "git subrepo upgrade"
Upgrade the \f(CW\*(C`git\-subrepo\*(C'\fR software itself. This simply does a \f(CW\*(C`git pull\*(C'\fR
on the git repository that the code is running from. It only works if you
are on the \f(CW\*(C`master\*(C'\fR branch. It won't work if you installed \f(CW\*(C`git\-subrepo\*(C'\fR
using \f(CW\*(C`make install\*(C'\fR; in that case you'll need to \f(CW\*(C`make install\*(C'\fR from the
latest code.
.SH "Command Options"
.IX Header "Command Options"
.ie n .IP """\-h""" 4
.el .IP "\f(CW\-h\fR" 4
.IX Item "-h"
Show a brief view of the commands and options.
.ie n .IP """\-\-help""" 4
.el .IP "\f(CW\-\-help\fR" 4
.IX Item "--help"
Gives an overview of the help options available for the subrepo command.
.ie n .IP """\-\-version""" 4
.el .IP "\f(CW\-\-version\fR" 4
.IX Item "--version"
Print the git-subrepo version. Just the version number. Try the \f(CW\*(C`version\*(C'\fR
command for more version info.
.ie n .IP """\-\-all"" (""\-a"")" 4
.el .IP "\f(CW\-\-all\fR (\f(CW\-a\fR)" 4
.IX Item "--all (-a)"
If you have multiple subrepos, issue the command to all of them (if
applicable).
.ie n .IP """\-\-ALL"" (""\-A"")" 4
.el .IP "\f(CW\-\-ALL\fR (\f(CW\-A\fR)" 4
.IX Item "--ALL (-A)"
If you have subrepos that also have subrepos themselves, issue the command to
\&\s-1ALL\s0 of them. Note that the \f(CW\*(C`\-\-ALL\*(C'\fR option only works for a subset of the
commands that \f(CW\*(C`\-\-all\*(C'\fR works for.
.ie n .IP """\-\-branch=<branch\-name>"" (""\-b <branch\-name>"")" 4
.el .IP "\f(CW\-\-branch=<branch\-name>\fR (\f(CW\-b <branch\-name>\fR)" 4
.IX Item "--branch=<branch-name> (-b <branch-name>)"
Use a different upstream branch-name than the remote \s-1HEAD\s0 or the one saved in
\&\f(CW\*(C`.gitrepo\*(C'\fR locally.
.ie n .IP """\-\-dry\-run"" (""\-N"")" 4
.el .IP "\f(CW\-\-dry\-run\fR (\f(CW\-N\fR)" 4
.IX Item "--dry-run (-N)"
For the push command, do everything up until the push and then print out the
actual \f(CW\*(C`git push\*(C'\fR command needed to finish the operation.
.ie n .IP """\-\-edit"" (""\-e"")" 4
.el .IP "\f(CW\-\-edit\fR (\f(CW\-e\fR)" 4
.IX Item "--edit (-e)"
Edit the commit message before committing.
.ie n .IP """\-\-fetch"" (""\-F"")" 4
.el .IP "\f(CW\-\-fetch\fR (\f(CW\-F\fR)" 4
.IX Item "--fetch (-F)"
Use this option to fetch the upstream commits, before running the command.
.ie n .IP """\-\-force"" (""\-f"")" 4
.el .IP "\f(CW\-\-force\fR (\f(CW\-f\fR)" 4
.IX Item "--force (-f)"
Use this option to force certain commands that fail in the general case.
.Sp
\&\s-1NOTE:\s0 The \f(CW\*(C`\-\-force\*(C'\fR option means different things for different commands.
Read the command specific doc for the exact meaning.
.ie n .IP """\-\-merge"" (""\-M"")" 4
.el .IP "\f(CW\-\-merge\fR (\f(CW\-M\fR)" 4
.IX Item "--merge (-M)"
Use a \f(CW\*(C`merge\*(C'\fR strategy to include upstream subrepo commits on a pull (or
setup for push).
.ie n .IP """\-\-message=<message>"" (""\-m <message>"")" 4
.el .IP "\f(CW\-\-message=<message>\fR (\f(CW\-m <message>\fR)" 4
.IX Item "--message=<message> (-m <message>)"
Specify your own commit message on the command line.
.ie n .IP """\-\-rebase"" (""\-R"")" 4
.el .IP "\f(CW\-\-rebase\fR (\f(CW\-R\fR)" 4
.IX Item "--rebase (-R)"
Use a \f(CW\*(C`rebase\*(C'\fR strategy to include upstream subrepo commits on a pull (or
setup for push).
.ie n .IP """\-\-remote=<remote\-url>"" (""\-r <remote\-url>"")" 4
.el .IP "\f(CW\-\-remote=<remote\-url>\fR (\f(CW\-r <remote\-url>\fR)" 4
.IX Item "--remote=<remote-url> (-r <remote-url>)"
Use a different remote-url than the one saved in \f(CW\*(C`.gitrepo\*(C'\fR locally.
.ie n .IP """\-\-squash"" (""\-s"")" 4
.el .IP "\f(CW\-\-squash\fR (\f(CW\-s\fR)" 4
.IX Item "--squash (-s)"
Squash all commits on a push into one new commit.
.ie n .IP """\-\-update"" (""\-u"")" 4
.el .IP "\f(CW\-\-update\fR (\f(CW\-u\fR)" 4
.IX Item "--update (-u)"
If \f(CW\*(C`\-\-branch\*(C'\fR or \f(CW\*(C`\-\-remote\*(C'\fR are used, and the command updates the
\&\f(CW\*(C`.gitrepo\*(C'\fR file, include these values to the update.
.SH "Output Options"
.IX Header "Output Options"
.ie n .IP """\-\-quiet"" (""\-q"")" 4
.el .IP "\f(CW\-\-quiet\fR (\f(CW\-q\fR)" 4
.IX Item "--quiet (-q)"
Print as little info as possible. Applicable to most commands.
.ie n .IP """\-\-verbose"" (""\-v"")" 4
.el .IP "\f(CW\-\-verbose\fR (\f(CW\-v\fR)" 4
.IX Item "--verbose (-v)"
Print more information about the command execution and results. Applicable to
most commands.
.ie n .IP """\-\-debug"" (""\-d"")" 4
.el .IP "\f(CW\-\-debug\fR (\f(CW\-d\fR)" 4
.IX Item "--debug (-d)"
Show the actual git (and other) commands being executed under the hood.
Applicable to most commands.
.ie n .IP """\-\-DEBUG"" (""\-x"")" 4
.el .IP "\f(CW\-\-DEBUG\fR (\f(CW\-x\fR)" 4
.IX Item "--DEBUG (-x)"
Use the Bash \f(CW\*(C`set \-x\*(C'\fR option which prints every command before it is
run. \s-1VERY\s0 noisy, but extremely useful in deep debugging. Applicable to
all commands.
.SH "Environment Variables"
.IX Header "Environment Variables"
The \f(CW\*(C`git\-subrepo\*(C'\fR command exports and honors some environment variables:
.ie n .IP """GIT_SUBREPO_ROOT""" 4
.el .IP "\f(CWGIT_SUBREPO_ROOT\fR" 4
.IX Item "GIT_SUBREPO_ROOT"
This is set by the \f(CW\*(C`.rc\*(C'\fR file, if you use that method to install / enable \f(CW\*(C`git\-
subrepo\*(C'\fR. It contains the path of the \f(CW\*(C`git\-subrepo\*(C'\fR repository.
.ie n .IP """GIT_SUBREPO_RUNNING""" 4
.el .IP "\f(CWGIT_SUBREPO_RUNNING\fR" 4
.IX Item "GIT_SUBREPO_RUNNING"
This variable is exported when \f(CW\*(C`git\-subrepo\*(C'\fR is running. It is set to the pid
of the \f(CW\*(C`git\-subrepo\*(C'\fR process that is running. Other processes, like git hooks
for instance, can use this information to adjust accordingly.
.ie n .IP """GIT_SUBREPO_COMMAND""" 4
.el .IP "\f(CWGIT_SUBREPO_COMMAND\fR" 4
.IX Item "GIT_SUBREPO_COMMAND"
This variable is exported when \f(CW\*(C`git\-subrepo\*(C'\fR is running. It is set to the
name of the \f(CW\*(C`git\-subrepo\*(C'\fR subcommand that is running.
.ie n .IP """GIT_SUBREPO_PAGER""" 4
.el .IP "\f(CWGIT_SUBREPO_PAGER\fR" 4
.IX Item "GIT_SUBREPO_PAGER"
Use this to specify the pager to use for long output commands. Defaults to
\&\f(CW$PAGER\fR or \f(CW\*(C`less\*(C'\fR.
.ie n .IP """GIT_SUBREPO_QUIET""" 4
.el .IP "\f(CWGIT_SUBREPO_QUIET\fR" 4
.IX Item "GIT_SUBREPO_QUIET"
Set this for quiet (\f(CW\*(C`\-q\*(C'\fR) output.
.ie n .IP """GIT_SUBREPO_VERBOSE""" 4
.el .IP "\f(CWGIT_SUBREPO_VERBOSE\fR" 4
.IX Item "GIT_SUBREPO_VERBOSE"
Set this for verbose (\f(CW\*(C`\-v\*(C'\fR) output.
.ie n .IP """GIT_SUBREPO_DEBUG""" 4
.el .IP "\f(CWGIT_SUBREPO_DEBUG\fR" 4
.IX Item "GIT_SUBREPO_DEBUG"
Set this for debugging (\f(CW\*(C`\-d\*(C'\fR) output.
.SH "Installation Instructions"
.IX Header "Installation Instructions"
There are currently 3 ways to install \f(CW\*(C`git\-subrepo\*(C'\fR. For all of them you need
to get the source code from GitHub:
.PP
.Vb 1
\& git clone https://github.com/ingydotnet/git\-subrepo /path/to/git\-subrepo
.Ve
.PP
The first installation method is preferred: \f(CW\*(C`source\*(C'\fR the \f(CW\*(C`.rc\*(C'\fR file. Just
add a line like this one to your shell startup script:
.PP
.Vb 1
\& source /path/to/git\-subrepo/.rc
.Ve
.PP
That will modify your \f(CW\*(C`PATH\*(C'\fR and \f(CW\*(C`MANPATH\*(C'\fR, and also enable command
completion.
.PP
The second method is to do these things by hand. This might afford you more
control of your shell environment. Simply add the \f(CW\*(C`lib\*(C'\fR and \f(CW\*(C`man\*(C'\fR
directories to your \f(CW\*(C`PATH\*(C'\fR and \f(CW\*(C`MANPATH\*(C'\fR:
.PP
.Vb 3
\& export GIT_SUBREPO_ROOT="/path/to/git\-subrepo"
\& export PATH="/path/to/git\-subrepo/lib:$PATH"
\& export MANPATH="/path/to/git\-subrepo/man:$MANPATH"
.Ve
.PP
See below for info on how to turn on Command Completion.
.PP
The third method is a standard system install, which puts \f(CW\*(C`git\-subrepo\*(C'\fR next
to your other git commands:
.PP
.Vb 1
\& make install # Possibly with \*(Aqsudo\*(Aq
.Ve
.PP
This method does not account for upgrading and command completion yet.
.SS "Windows"
.IX Subsection "Windows"
This command is known to work in these Windows environments:
.IP "\(bu" 4
Git for Windows \*(-- <https://git\-for\-windows.github.io/>
.IP "\(bu" 4
Babun \*(-- <http://babun.github.io/>
.IP "\(bu" 4
Cygwin \*(-- <https://www.cygwin.com/>
.PP
Let us know if there are others that it works (or doesn't work) in.
.SH "Testing"
.IX Header "Testing"
The \f(CW\*(C`git\-subrepo\*(C'\fR repository comes with a extensive test suite. You can
run it with:
.PP
.Vb 1
\& make test
.Ve
.PP
or if you don't have \f(CW\*(C`make\*(C'\fR on your system:
.PP
.Vb 1
\& prove \-v test
.Ve
.SH "Upgrading"
.IX Header "Upgrading"
If you used the \f(CW\*(C`.rc\*(C'\fR or \f(CW\*(C`PATH\*(C'\fR method of installation, just run this to
upgrade \f(CW\*(C`git\-subrepo\*(C'\fR:
.PP
.Vb 1
\& git subrepo upgrade
.Ve
.PP
Or (same thing):
.PP
.Vb 2
\& cd /path/to/git\-subrepo
\& git pull
.Ve
.PP
If you used \f(CW\*(C`make install\*(C'\fR method, then run this again (after \f(CW\*(C`git pull\*(C'\fR):
.PP
.Vb 1
\& make install # Possibly with \*(Aqsudo\*(Aq
.Ve
.SH "Command Completion"
.IX Header "Command Completion"
The \f(CW\*(C`git subrepo\*(C'\fR command supports \f(CW\*(C`<TAB>\*(C'\fR\-based command completion. If
you don't use the \f(CW\*(C`.rc\*(C'\fR script (see Installation, above), you'll need to
enable this manually to use it.
.SS "In Bash"
.IX Subsection "In Bash"
If your Bash setup does not already provide command completion for Git, you'll
need to enable that first:
.PP
.Vb 1
\& source <Git completion script>
.Ve
.PP
On your system, the Git completion script might be found at any of the
following locations (or somewhere else that we don't know about):
.IP "\(bu" 4
\&\f(CW\*(C`/etc/bash_completion.d/git\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`/usr/share/bash\-completion/git\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`/usr/share/bash\-completion/completions/git\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`/opt/local/share/bash\-completion/completions/git\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`/usr/local/etc/bash_completion.d/git\*(C'\fR
.IP "\(bu" 4
\&\f(CW\*(C`~/.homebrew/etc/bash_completion.d/git\*(C'\fR
.PP
In case you can't find any of these, this repository contains a copy of the
Git completion script:
.PP
.Vb 1
\& source /path/to/git\-subrepo/share/git\-completion.bash
.Ve
.PP
Once Git completion is enabled (whether you needed to do that manually or
not), you can turn on \f(CW\*(C`git\-subrepo\*(C'\fR completion with a command like this:
.PP
.Vb 1
\& source /path/to/git\-subrepo/share/completion.bash
.Ve
.SS "In zsh"
.IX Subsection "In zsh"
In the Z shell (zsh), you can manually enable \f(CW\*(C`git\-subrepo\*(C'\fR completion by
adding the following line to your \f(CW\*(C`~/.zshrc\*(C'\fR, \fBbefore\fR the \f(CW\*(C`compinit\*(C'\fR
function is called:
.PP
.Vb 1
\& fpath=(\*(Aq/path/to/git\-subrepo/share/zsh\-completion\*(Aq $fpath)
.Ve
.SH "Status"
.IX Header "Status"
The git-subrepo command has been in use for well over a year and seems to get
the job done. Development is still ongoing but mostly just for fixing bugs.
.PP
Trying subrepo out is simple and painless (this is not \f(CW\*(C`git submodule\*(C'\fR).
Nothing is permanent (if you do not push to shared remotes). ie You can always
play around and reset back to the beginning without pain.
.PP
This command has a test suite (run \f(CW\*(C`make test\*(C'\fR), but surely has many bugs. If
you have expertise with Git and subcommands, please review the code, and file
issues on anything that seems wrong.
.PP
If you want to chat about the \f(CW\*(C`git\-subrepo\*(C'\fR command, join \f(CW\*(C`#gitcommands\*(C'\fR on
\&\f(CW\*(C`irc.freenode.net\*(C'\fR.
.SH "Notes"
.IX Header "Notes"
.IP "\(bu" 4
Works on \s-1POSIX\s0 systems: Linux, \s-1BSD, OSX,\s0 etc.
.IP "\(bu" 4
Works on various Windows environments. See \*(L"Windows\*(R" section above.
.IP "\(bu" 4
The \f(CW\*(C`git\-subrepo\*(C'\fR repo itself has 2 subrepos under the \f(CW\*(C`ext/\*(C'\fR subdirectory.
.IP "\(bu" 4
Written in (very modern) Bash, with full test suite. Take a look.
.IP "\(bu" 4
A \f(CW\*(C`.gitrepo\*(C'\fR file never is in the top level dir (next to a \f(CW\*(C`.git/\*(C'\fR dir).
.SH "Authors"
.IX Header "Authors"
.IP "\(bu" 4
Ingy döt Net <ingy@ingy.net>
.IP "\(bu" 4
Magnus Carlsson <grimmymail@gmail.com>
.SH "License and Copyright"
.IX Header "License and Copyright"
The \s-1MIT\s0 License (\s-1MIT\s0)
.PP
Copyright (c) 2013\-2020 Ingy döt Net

View file

@ -0,0 +1,12 @@
- Update docs
- Review issue/142
- Compare operations to master
- Add a --no-push or --dry-run flag for push
- Add a --rebase option for push
- Add --message= and --edit to the init command ??
- Also add to doc
- Update doc for steps of pull command to include merge and rebase
- Test squashing on pull operations
- Update push doc after understanding it more
- Is --rebase for pull or push?
- Make sure docs are correct too.

View file

@ -0,0 +1,148 @@
add
add--interactive
am
annotate
apply
archive
bisect
bisect--helper
blame
branch
bundle
cat-file
check-attr
check-ref-format
checkout
checkout-index
cherry
cherry-pick
clean
clone
commit
commit-tree
config
count-objects
credential-cache
credential-cache--daemon
credential-store
daemon
describe
diff
diff-files
diff-index
diff-tree
difftool
difftool--helper
fast-export
fast-import
fetch
fetch-pack
filter-branch
fmt-merge-msg
for-each-ref
format-patch
fsck
fsck-objects
gc
get-tar-commit-id
grep
hash-object
help
http-backend
http-fetch
http-push
hub
imap-send
index-pack
init
init-db
instaweb
log
lost-found
ls-files
ls-remote
ls-tree
mailinfo
mailsplit
merge
merge-base
merge-file
merge-index
merge-octopus
merge-one-file
merge-ours
merge-recursive
merge-resolve
merge-subtree
merge-tree
mergetool
mktag
mktree
mv
name-rev
notes
pack-objects
pack-redundant
pack-refs
patch-id
peek-remote
prune
prune-packed
pull
push
quiltimport
read-tree
rebase
receive-pack
reflog
relink
remote
remote-ext
remote-fd
remote-ftp
remote-ftps
remote-http
remote-https
remote-testgit
remove-submodule
repack
replace
repo-config
request-pull
rerere
reset
rev-list
rev-parse
revert
rm
send-pack
sh-i18n--envsubst
shell
shortlog
show
show-branch
show-index
show-ref
stage
stash
status
stripspace
submodule
subrepo
subtree
symbolic-ref
tag
tar-tree
unpack-file
unpack-objects
update-index
update-ref
update-server-info
upload-archive
upload-pack
var
verify-pack
verify-tag
web--browse
whatchanged
write-tree

View file

@ -0,0 +1,32 @@
== Users
- art — project lead
- bob — end user
- cab — collab dev (push rights)
- dim — forker dev (no push rights)
- ell — third party repo owner
== Repos
- art/gallery — art's main repo
- art/painting — subrepo owned by art
- ell/tickets — subrepo owned by ell
== Starting histories:
- art/gallery — A---B
- art/painting — P---Q
- ell/tickets — T
== Scenario 1
art art/gallery$ git log
A---B
art art/gallery$ git subrepo clone art/painting
art art/gallery$ ls
painting/
art art/gallery$ git log
A---B---C
/
Q'
art art/gallery$

View file

@ -0,0 +1,33 @@
$$$ for cmd in `cat AllGitCmds`; do git help $cmd; done
== Inteesting Plumbing Commands:
- git ls-tree HEAD
- git cat-file -t <object>
- git cat-file commit <commit>
- git cat-file -p <commit>
- git ls-tree <tree>
- git ls-files --stage
- git write-tree
- git update-ref refs/heads/master <commit>
- git symbolic-ref HEAD refs/heads/master
- git show-branch
- git hash-object <file>
- git rev-list --max-parents=0 HEAD — Find root commit
- git cherry — find commits in one branch that are not in another
- git replace
== Commands with DAG diagrams in their manpages:
- git-commit
- git-filter-branch
- git-log
- git-merge
- git-merge-base
- git-pull
- git-push
- git-rebase
- git-rerere
- git-rev-list
- git-rev-parse

199
build/git-subrepo/note/Dags Normal file
View file

@ -0,0 +1,199 @@
== ASCII commmit DAG diagrams
D--E--F--G--H
/ /
A--B-----C
.-A---M---N---O---P
/ / / / /
I B C D E
\ / / / /
`-------------'
.-A---N---O
/ / /
I---------D
.-A---M---N---O---P
/ / / / /
I B / D /
\ / / / /
`-------------'
.-A---M---N---O
/ / /
I B D
\ / /
`---------'
D---E-------F
/ \ \
B---C---G---H---I---J
/ \
A-------K---------------L--M
E-------F
\ \
G---H---I---J
\
L--M
A---B---C topic
/
D---E---F---G master
A---B---C topic
/ \
D---E---F---G---H master
o---o---o---B
/
---o---1---o---o---o---A
o---o---o---o---C
/
/ o---o---o---B
/ /
---2---1---o---o---o---A
o---o---o---o---o
/ \
/ o---o---o---o---M
/ /
---2---1---o---o---o---A
---1---o---A
\ /
X
/ \
---2---o---o---B
A---B---C master on origin
/
D---E---F---G master
A---B---C remotes/origin/master
/ \
D---E---F---G---H master
B
/
---X---A
B---C
/ /
---X---A
B D
/ /
---X---A
o---o---o---A---B origin/master
\
X---Y---Z dev
A---B (unnamed branch)
/
o---o---o---X---Y---Z master
A---B---C topic
/
D---E---F---G master
A'--B'--C' topic
/
D---E---F---G master
A---B---C topic
/
D---E---A'---F master
B'---C' topic
/
D---E---A'---F master
o---o---o---o---o master
| \
| o'--o'--o' topic
\
o---o---o---o---o next
H---I---J topicB
/
E---F---G topicA
/
A---B---C---D master
H'--I'--J' topicB
/
| E---F---G topicA
|/
A---B---C---D master
E---F---G---H---I---J topicA
E---H'---I'---J' topicA
X
\
A---M---B
/
---o---O---P---Q
o---o---o---o---o---o---o---o---o master
\
o---o---o---o---o subsystem
\
*---*---* topic
o---o---o---o---o---o---o---o master
\ \
o---o---o---o---o o'--o'--o'--o'--o' subsystem
\
*---*---* topic
o---o---o---o---o---o---o---o master
\ \
o---o---o---o---o o'--o'--o'--o'--o'--M subsystem
\ /
*---*---*-..........-*--* topic
o---o---o---o---o---o---o---o master
\
o'--o'--o'--o'--o' subsystem
\
*---*---* topic
o---*---o topic
/
o---o---o---*---o---o master
o---*---o---+ topic
/ /
o---o---o---*---o---o master
o---*---o---+---o---o topic
/ / \
o---o---o---*---o---o---o---o---+ master
o---*---o-------o---o topic
/
o---o---o---*---o---o---o---o master
$ git rebase master topic
o---*---o-------o---o topic
/
o---o---o---*---o---o---o---o master
G H I J
\ / \ /
D E F
\ | / \
\ | / |
\|/ |
B C
\ /
\ /
A

View file

@ -0,0 +1,7 @@
- https://gist.github.com/kentfredric/9285653
- https://gist.github.com/anonymous/55bcf30acab99308714b
# a + b = o
- https://gist.github.com/anonymous/0544b465c57f01f8c4a6
- https://gist.github.com/ingydotnet/bf92949734b6a23f2af6
- https://gist.github.com/ingydotnet/a61b3cd1c422a17c6e5d

View file

@ -0,0 +1,25 @@
- http://stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit/
- http://git-scm.com/book/en/Git-Tools-Rewriting-History
- http://git.661346.n2.nabble.com/Editing-the-root-commit-td7561714.html
- http://stackoverflow.com/questions/645450/git-how-to-insert-a-commit-as-the-first-shifting-all-the-others
- http://stackoverflow.com/questions/6149520/git-remove-root-commit ***
- http://fourkitchens.com/blog/2009/01/19/creating-common-branch-ancestry-hard-problem
- http://stackoverflow.com/questions/1488753/how-to-merge-two-branches-without-a-common-ancestor
- http://sahd.lamafam.org/?p=1534
- http://www.claassen.net/geek/blog/2011/02/git-merge-strategytheirs.html
- sahd.lamafam.org/?p=1534
- http://stackoverflow.com/questions/2945344/selecting-merge-strategy-options-for-git-rebase
- http://stackoverflow.com/questions/2428137/how-to-rebase-one-git-repository-onto-another-one
- http://stackoverflow.com/questions/3080509/git-list-commits-not-pushed-to-the-origin-yet
- http://stackoverflow.com/questions/2263674/how-do-i-find-the-next-commit-in-git
- http://git-scm.com/book/ch6-7.html (Subtree Merge)
- https://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html
- https://help.github.com/articles/working-with-subtree-merge
- git pull -s subtree foo master
- http://git-scm.com/2010/03/17/replace.html
- http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html
- http://ruleant.blogspot.com/2013/06/git-subtree-module-with-gittrees-config.html

View file

@ -0,0 +1,10 @@
- https://github.com/nothingmuch/git-svn-abandon
- https://github.com/schacon?tab=repositories
- https://github.com/jbalogh/git-tools
- https://github.com/wesabe/git-tools
- https://github.com/timcharper/git-helpers
- https://github.com/schacon/git-pulls
- https://github.com/node-gh/gh
- https://github.com/splitbrain/git-pull-request
- https://github.com/mloughran/git-cleanup
- https://github.com/seveas/git-hub

View file

@ -0,0 +1,39 @@
= `git-hub` Design Specification
== Main Commands
- `clone`
git subrepo clone <git-url> <subdir> [<options>]
- `pull`
- `push`
== Worker Commands
- `fetch`
git subrepo fetch <subdir>
- `branch`
- `checkout`
- `commit`
- `reset`
== Info Commands
- `status`
- `log`
== Other Commands
- `clean`
- `help`
- `version`

View file

@ -0,0 +1,57 @@
== git subrepo clone git@genius --branch=bob ext/genius
- create orphan branch repo/genius
- fetch git@genius --branch=bob
- create local branch repo/genius
- checkout repo/genius into ext/genius
- SYNC-DANCE:
- add ext/genius/.gitrepo
- commit the add and prune the commit
- merge the pruned commit into mianline
== git subrepo pull ext/genius
- checkout ext/genius
- fetch git@genius --branch=bob
- merge/rebase FETCH_HEAD
- commit if merge successful
- checkout original branch
- SYNC-DANCE
== git subrepo branch ext/genius
- filter-branch subdir ext/genius
- filter-branch 'rm .gitrepo'
- add parent of original
- name the commit subrepo/ext/genius
== git subrepo checkout ext/genius
- subrepo branch ext/genius
- checkout subrepo/ext/genius
== git subrepo push ext/genius
- subrepo branch ext/genius
- checkout repo/genius
- merge/rebase subrepo/ext/genius
- push repo/genius git@genius/bob
- checkout original
= Commands =
git subrepo clone git@github.com:ingydotnet/bashplus.git
git subrepo clone git@github.com:ingydotnet/bashplus.git ext/bashplus
git subrepo clone git@github.com:ingydotnet/bashplus.git --branch=devel
git subrepo clone git@github.com:ingydotnet/bashplus.git --reclone
git subrepo pull git@github.com:ingydotnet/bashplus.git
git subrepo pull git@github.com:ingydotnet/bashplus.git --branch=master
git subrepo pull git@github.com:ingydotnet/bashplus.git --rebase
git subrepo branch ext/bashplus # create refs/heads/subrepo/ext/bashplus
git subrepo checkout ext/bashplus
git subrepo push ext/bashplus

Some files were not shown because too many files have changed in this diff Show more