├── .fish.rc ├── .gitattributes ├── .github └── workflows │ ├── pre-commit.yml │ └── test.yml ├── .gitignore ├── .pre-commit-config.yaml ├── .rc ├── Changes ├── Intro.pod ├── License ├── Makefile ├── Meta ├── ReadMe.pod ├── doc ├── comparison.swim ├── git-subrepo.swim └── intro-to-subrepo.swim ├── ext ├── bashplus │ ├── .gitrepo │ ├── .travis.yml │ ├── Changes │ ├── License │ ├── Makefile │ ├── Meta │ ├── ReadMe.pod │ ├── bin │ │ └── bash+ │ ├── doc │ │ └── bash+.swim │ ├── lib │ │ └── bash+.bash │ ├── man │ │ ├── man1 │ │ │ └── bash+.1 │ │ └── man3 │ │ │ └── bash+.3 │ └── test │ │ ├── base.t │ │ ├── die.t │ │ ├── fcopy.t │ │ ├── lib │ │ └── foo │ │ │ ├── bar.bash │ │ │ └── foo.bash │ │ ├── setup │ │ ├── shellcheck.t │ │ ├── source-bash+-std.t │ │ ├── source-bash+.t │ │ ├── use.t │ │ └── version-check.t └── test-more-bash │ ├── .gitrepo │ ├── .travis.yml │ ├── Changes │ ├── License │ ├── Makefile │ ├── Meta │ ├── ReadMe.pod │ ├── doc │ └── test-more.swim │ ├── ext │ ├── bashplus │ │ ├── .gitrepo │ │ ├── .travis.yml │ │ ├── Changes │ │ ├── License │ │ ├── Makefile │ │ ├── Meta │ │ ├── ReadMe.pod │ │ ├── bin │ │ │ └── bash+ │ │ ├── doc │ │ │ └── bash+.swim │ │ ├── lib │ │ │ └── bash+.bash │ │ ├── man │ │ │ ├── man1 │ │ │ │ └── bash+.1 │ │ │ └── man3 │ │ │ │ └── bash+.3 │ │ └── test │ │ │ ├── base.t │ │ │ ├── die.t │ │ │ ├── fcopy.t │ │ │ ├── lib │ │ │ └── foo │ │ │ │ ├── bar.bash │ │ │ │ └── foo.bash │ │ │ ├── setup │ │ │ ├── shellcheck.t │ │ │ ├── source-bash+-std.t │ │ │ ├── source-bash+.t │ │ │ └── use.t │ └── test-tap-bash │ │ ├── .gitrepo │ │ ├── .travis.yml │ │ ├── Changes │ │ ├── License │ │ ├── Makefile │ │ ├── Meta │ │ ├── ReadMe.pod │ │ ├── doc │ │ └── test-tap.swim │ │ ├── lib │ │ └── test │ │ │ └── tap.bash │ │ ├── man │ │ └── man3 │ │ │ └── test-tap.3 │ │ └── test │ │ ├── bail_out.t │ │ ├── done.t │ │ ├── fail.t │ │ ├── fail_fast.t │ │ ├── helper.bash │ │ ├── pass.t │ │ ├── plan.t │ │ ├── shellcheck.t │ │ ├── skip_all.t │ │ ├── tap.t │ │ └── test │ │ ├── bail.t │ │ ├── fail.t │ │ ├── fail_fast.t │ │ ├── skip-all-init.t │ │ └── skip-all-plan.t │ ├── lib │ └── test │ │ └── more.bash │ ├── man │ └── man3 │ │ └── test-more.3 │ └── test │ ├── fail.t │ ├── more.t │ ├── pass.t │ ├── setup │ ├── shellcheck.t │ ├── skip_all.t │ └── test │ ├── fail1.t │ └── skip_all.t ├── lib ├── git-subrepo └── git-subrepo.d │ ├── bash+.bash │ └── help-functions.bash ├── man └── man1 │ └── git-subrepo.1 ├── note ├── 0.4.0 ├── AllGitCmds ├── Cases ├── Commands ├── Dags ├── Gists ├── Links ├── Plugins ├── Spec ├── Story1 ├── ToDo ├── design.swim ├── design2.swim ├── pull-dance.txt ├── recreate-rebase-conflict.sh ├── subtree-rebase-fail-example │ └── test.bash ├── test-subrepo-push.sh └── test.sh ├── pkg └── bin │ ├── generate-completion.pl │ └── generate-help-functions.pl ├── share ├── completion.bash ├── enable-completion.sh ├── git-completion.bash ├── git-subrepo.fish └── zsh-completion │ └── _git-subrepo └── test ├── Dockerfile ├── branch-all.t ├── branch-rev-list-one-path.t ├── branch-rev-list.t ├── branch.t ├── clean.t ├── clone-annotated-tag.t ├── clone.t ├── compile.t ├── config.t ├── encode.t ├── error.t ├── fetch.t ├── gitignore.t ├── init.t ├── issue29.t ├── issue95.t ├── issue96.t ├── pull-all.t ├── pull-merge.t ├── pull-message.t ├── pull-new-branch.t ├── pull-ours.t ├── pull-theirs.t ├── pull-twice.t ├── pull-worktree.t ├── pull.t ├── push-after-init.t ├── push-after-push-no-changes.t ├── push-force.t ├── push-new-branch.t ├── push-no-changes.t ├── push-squash.t ├── push.t ├── rebase.t ├── reclone.t ├── repo ├── bar │ ├── HEAD │ ├── config │ ├── objects │ │ ├── 87 │ │ │ └── 46903fdb1b9c2101377880125917c2e05b4d69 │ │ ├── 94 │ │ │ └── c86ffc745232d89f78c6f895e11e71272518db │ │ ├── 1f │ │ │ └── 0c4b264caed0126814a0ede851a1e0b4e16ae6 │ │ ├── c6 │ │ │ └── 76c57b6576743fa56278527aa60ebd2e202a7c │ │ ├── e6 │ │ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 │ │ └── f6 │ │ │ └── 2a8ff3feadf39b0a98f1a86ec6d1eb33858ee9 │ └── refs │ │ ├── heads │ │ └── master │ │ └── tags │ │ └── A ├── foo │ ├── HEAD │ ├── config │ ├── objects │ │ ├── a0 │ │ │ └── f4cdaaf533a936296cdebbed8206c3b9ededa8 │ │ ├── e2 │ │ │ └── 1291a1ad392a9d4c51dd9586804f1467b28afd │ │ └── e6 │ │ │ └── 9de29bb2d1d6434b8b29ae775ad8c2e48c5391 │ └── refs │ │ └── heads │ │ └── master └── init │ ├── HEAD │ ├── config │ ├── objects │ ├── 11 │ │ └── 523f5dcf03b4c89b592dc8a3d0308f68da2386 │ ├── 14 │ │ └── 2addf8ec5f37334e837440122c62f2c68a29ad │ ├── 32 │ │ └── 5180321750a21cd7a4e7ecda319e557a4f6a09 │ ├── 58 │ │ └── 931fc1bd559b59c41ea738fc7ad04f9ad01bd3 │ ├── 75 │ │ └── fa6584e748f57eff06eebdc55e9ac21d4fcbf2 │ ├── 80 │ │ └── 2d5edbd5e1cb7fca82b5bd38e7c8a0a496fb20 │ ├── 94 │ │ └── 7b3d714c38791e95ad6f928b48c98bb8708acd │ ├── 95 │ │ └── e1f2df3f4d5f3d7a60588c25a7ca8a913d3c2a │ ├── 3d │ │ └── 918c6901c02f43af5d31779dd5e1f9166aeb36 │ ├── 3e │ │ └── 4cb596066dce63ba4d047abddb677389b65e19 │ ├── 4b │ │ └── 6e53022e7a04f07887697e4f3d7c377fd9822b │ ├── 5e │ │ └── c0c28e1b806f25efdca18fcf7a74b49c3755bd │ ├── b1 │ │ └── 5f4a7666baf40d949548ead946a3370e273479 │ ├── c3 │ │ └── ee8978c4c5d84c3b7d00ba8e5906933d027882 │ ├── c8 │ │ └── b0bffbc405ef3fad7354ff833fbec36d67ddfa │ ├── dd │ │ └── 8bdb934ec848137f011fe423b185505c343626 │ ├── e2 │ │ └── 9be58c767cfeb27235c995d293a7d71aac0135 │ ├── ee │ │ └── 1224401fc6aac595145fa727dcf6706ac8aec1 │ └── f1 │ │ └── cc1a657b2e805c400f5dcaaa76bd29c6178b1b │ └── refs │ └── heads │ └── master ├── setup ├── status.t ├── submodule.t └── zsh.t /.fish.rc: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------ 2 | # 3 | # This is the `git-subrepo` initialization script. 4 | # 5 | # This script turns on the `git-subrepo` Git subcommand and its manpages, for 6 | # the *Fish* shell. 7 | # 8 | # Just add a line like this to your `~/.config/fish/config.fish`: 9 | # 10 | # source /path/to/git-subrepo/.fish.rc 11 | # 12 | #------------------------------------------------------------------------------ 13 | 14 | set GIT_SUBREPO_ROOT (dirname (realpath (status --current-filename))) 15 | set PATH $GIT_SUBREPO_ROOT/lib $PATH 16 | 17 | set -q MANPATH || set MANPATH '' 18 | set MANPATH $MANPATH $GIT_SUBREPO_ROOT/man 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | name: pre-commit 2 | 3 | on: 4 | pull_request: 5 | push: 6 | 7 | jobs: 8 | pre-commit: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v3 12 | - uses: actions/setup-python@v3 13 | - uses: pre-commit/action@v3.0.1 14 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ '*' ] 6 | pull_request: 7 | branches: [ '*' ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, macos-latest] 15 | 16 | steps: 17 | - uses: actions/checkout@v2 18 | - if: startsWith(matrix.os, 'macos') 19 | run: brew install bash 20 | - run: 21 | git config --global user.email "you@example.com"; 22 | git config --global user.name "Your Name"; 23 | git config --global init.defaultBranch "master"; 24 | git config --global --add safe.directory "$PWD"; 25 | git config --global --add safe.directory "$PWD.git"; 26 | - if: startsWith(matrix.os, 'macos') 27 | run: make test 28 | - if: startsWith(matrix.os, 'ubuntu') 29 | run: make docker-tests 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /test/tmp/ 2 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: check-yaml 6 | - id: end-of-file-fixer 7 | - id: check-illegal-windows-names 8 | - id: check-merge-conflict 9 | - id: trailing-whitespace 10 | - id: check-executables-have-shebangs 11 | - id: check-shebang-scripts-are-executable 12 | exclude: \.t$ 13 | - repo: https://github.com/koalaman/shellcheck-precommit 14 | rev: v0.10.0 15 | hooks: 16 | - id: shellcheck 17 | args: [-x] 18 | exclude: | 19 | (?x)^(ext/.*| 20 | share/git-completion.bash 21 | )$ 22 | -------------------------------------------------------------------------------- /.rc: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash disable=2128 2 | 3 | #------------------------------------------------------------------------------ 4 | # 5 | # This is the `git-subrepo` initialization script. 6 | # 7 | # This script turns on the `git-subrepo` Git subcommand, its manpages and TAB 8 | # completion for the *Bash* and *zsh* shells. 9 | # 10 | # Just add a line like this to your shell startup configuration: 11 | # 12 | # source /path/to/git-subrepo/.rc 13 | # 14 | #------------------------------------------------------------------------------ 15 | 16 | [[ -n ${ZSH_VERSION-} ]] && 17 | GIT_SUBREPO_ROOT=$0 || 18 | GIT_SUBREPO_ROOT=$BASH_SOURCE 19 | 20 | [[ $GIT_SUBREPO_ROOT =~ ^/ ]] || 21 | GIT_SUBREPO_ROOT=$PWD/$GIT_SUBREPO_ROOT 22 | 23 | GIT_SUBREPO_ROOT=$( 24 | cd "$(dirname "$GIT_SUBREPO_ROOT")" || return 25 | pwd 26 | ) || return 27 | export GIT_SUBREPO_ROOT 28 | 29 | export PATH=$GIT_SUBREPO_ROOT/lib:$PATH 30 | export MANPATH=$GIT_SUBREPO_ROOT/man:$MANPATH 31 | 32 | source "$GIT_SUBREPO_ROOT/share/enable-completion.sh" 33 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2020 Ingy döt Net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | 3 | # Make sure we have git: 4 | ifeq ($(shell which git),) 5 | $(error 'git' is not installed on this system) 6 | endif 7 | 8 | # Set variables: 9 | NAME := git-subrepo 10 | LIB := lib/$(NAME) 11 | DOC := doc/$(NAME).swim 12 | MAN1 := man/man1 13 | EXT := $(LIB).d 14 | EXTS := $(shell find $(EXT) -type f) \ 15 | $(shell find $(EXT) -type l) 16 | SHARE = share 17 | 18 | # Install variables: 19 | PREFIX ?= /usr/local 20 | INSTALL_LIB ?= $(DESTDIR)$(shell git --exec-path) 21 | INSTALL_EXT ?= $(INSTALL_LIB)/$(NAME).d 22 | INSTALL_MAN1 ?= $(DESTDIR)$(PREFIX)/share/man/man1 23 | 24 | # Docker variables: 25 | DOCKER_TAG ?= 0.0.7 26 | DOCKER_IMAGE := ingy/bash-testing:$(DOCKER_TAG) 27 | BASH_VERSIONS ?= 5.2 5.1 5.0 4.4 4.3 4.2 4.1 4.0 28 | DOCKER_TESTS := $(BASH_VERSIONS:%=docker-test-%) 29 | GIT_VERSIONS := 2.48 2.40 2.30 2.29 2.25 2.17 2.7 30 | 31 | prove ?= 32 | test ?= test/ 33 | bash ?= 5.2 34 | git ?= 2.48 35 | 36 | # Basic targets: 37 | default: help 38 | 39 | help: 40 | @echo 'Makefile rules:' 41 | @echo '' 42 | @echo 'test Run all tests' 43 | @echo 'install Install $(NAME)' 44 | @echo 'uninstall Uninstall $(NAME)' 45 | @echo 'env Show environment variables to set' 46 | 47 | .PHONY: test 48 | test: 49 | prove $(prove) $(test) 50 | 51 | test-all: test docker-tests 52 | 53 | docker-test: 54 | $(call docker-make-test,$(bash),$(git)) 55 | 56 | docker-tests: $(DOCKER_TESTS) 57 | 58 | $(DOCKER_TESTS): 59 | $(call docker-make-test,$(@:docker-test-%=%),$(git)) 60 | 61 | # Install support: 62 | install: 63 | install -d -m 0755 $(INSTALL_LIB)/ 64 | install -C -m 0755 $(LIB) $(INSTALL_LIB)/ 65 | install -d -m 0755 $(INSTALL_EXT)/ 66 | install -C -m 0644 $(EXTS) $(INSTALL_EXT)/ 67 | install -d -m 0755 $(INSTALL_MAN1)/ 68 | install -C -m 0644 $(MAN1)/$(NAME).1 $(INSTALL_MAN1)/ 69 | 70 | # Uninstall support: 71 | uninstall: 72 | rm -f $(INSTALL_LIB)/$(NAME) 73 | rm -fr $(INSTALL_EXT) 74 | rm -f $(INSTALL_MAN1)/$(NAME).1 75 | 76 | env: 77 | @echo "export PATH=\"$$PWD/lib:\$$PATH\"" 78 | @echo "export MANPATH=\"$$PWD/man:\$$MANPATH\"" 79 | 80 | # Doc rules: 81 | .PHONY: doc 82 | update: doc compgen 83 | 84 | force: 85 | 86 | doc: ReadMe.pod Intro.pod $(MAN1)/$(NAME).1 87 | perl pkg/bin/generate-help-functions.pl $(DOC) > \ 88 | $(EXT)/help-functions.bash 89 | 90 | ReadMe.pod: $(DOC) force 91 | swim --to=pod --wrap --complete $< > $@ 92 | 93 | Intro.pod: doc/intro-to-subrepo.swim force 94 | swim --to=pod --wrap --complete $< > $@ 95 | 96 | $(MAN1)/%.1: doc/%.swim Makefile force 97 | swim --to=man --wrap $< > $@ 98 | 99 | compgen: force 100 | perl pkg/bin/generate-completion.pl bash $(DOC) $(LIB) > \ 101 | $(SHARE)/completion.bash 102 | perl pkg/bin/generate-completion.pl zsh $(DOC) $(LIB) > \ 103 | $(SHARE)/zsh-completion/_git-subrepo 104 | perl pkg/bin/generate-completion.pl fish $(DOC) $(LIB) > \ 105 | $(SHARE)/git-subrepo.fish 106 | 107 | clean: 108 | rm -fr tmp test/tmp 109 | 110 | define docker-make-test 111 | docker run --rm \ 112 | -v $(PWD):/git-subrepo \ 113 | -w /git-subrepo \ 114 | $(DOCKER_IMAGE) \ 115 | /bin/bash -c ' \ 116 | set -x && \ 117 | [[ -d /bash-$(1) ]] && \ 118 | [[ -d /git-$(2) ]] && \ 119 | export PATH=/bash-$(1)/bin:/git-$(2)/bin:$$PATH && \ 120 | bash --version && \ 121 | git --version && \ 122 | make test prove=$(prove) test=$(test) \ 123 | ' 124 | endef 125 | -------------------------------------------------------------------------------- /Meta: -------------------------------------------------------------------------------- 1 | =meta: 0.0.2 2 | 3 | name: git-subrepo 4 | version: 0.4.9 5 | abstract: Git Submodule Alternative 6 | homepage: https://github.com/ingydotnet/git-subrepo#readme 7 | license: MIT 8 | copyright: 2013-2024 9 | 10 | author: 11 | name: Ingy döt Net 12 | email: ingy@ingy.net 13 | github: ingydotnet 14 | twitter: ingydotnet 15 | freenode: ingy 16 | homepage: http://ingy.net 17 | 18 | requires: 19 | bash: 4.0.0 20 | git: 2.7.0 21 | test: 22 | cmd: make test 23 | install: make install 24 | 25 | devel: 26 | git: git@github.org:ingydotnet/git-subrepo.git 27 | irc: irc.freenode.net/gitcommands 28 | bug: https://github.com/ingydotnet/git-subrepo/issues/ 29 | -------------------------------------------------------------------------------- /doc/comparison.swim: -------------------------------------------------------------------------------- 1 | = Comparing `submodule` and `subrepo` 2 | 3 | This document compares Git's `submodule` command to the new `subrepo` command, 4 | with examples and discussion of all the common operations. I'll use the term 5 | "External" to mean the general concept of an external repo that might be used 6 | as a submodule or a subrepo. 7 | 8 | = Overview 9 | 10 | 11 | 12 | = Adding a new External 13 | 14 | As an owner or collaborator, you have decided to add a new External to your 15 | repo: 16 | 17 | - Submodule :: `git submodule add git@github.com/user/external` 18 | - Subtree :: `git subtree --squash --prefix=external git@github.com/user/external` 19 | - Subrepo :: `git subrepo clone git@github.com/user/external` 20 | 21 | /…to be completed…/ 22 | 23 | = Updating from a changed External 24 | 25 | = Pushing External changes upstream 26 | 27 | = Moving/Renaming an External 28 | 29 | = Making an External on a branch 30 | 31 | = Changing the tracking branch of an External 32 | 33 | = Removing an External 34 | 35 | = Migration from One to the Other 36 | -------------------------------------------------------------------------------- /ext/bashplus/.gitrepo: -------------------------------------------------------------------------------- 1 | ; DO NOT EDIT (unless you know what you are doing) 2 | ; 3 | ; This subdirectory is a git "subrepo", and this file is maintained by the 4 | ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme 5 | ; 6 | [subrepo] 7 | remote = git@github.com:ingydotnet/bashplus.git 8 | branch = master 9 | commit = 030d196bf621e971e223e95e73c235e6992b85e0 10 | parent = 2c14be68fc5196ed1210d759421b33ef91c3e3db 11 | cmdver = 0.4.1 12 | method = merge 13 | -------------------------------------------------------------------------------- /ext/bashplus/.travis.yml: -------------------------------------------------------------------------------- 1 | # C language gives closest shell env. 2 | language: c 3 | 4 | script: 5 | - git submodule update --init --recursive 6 | - PROVEOPT=-v make test 7 | -------------------------------------------------------------------------------- /ext/bashplus/Changes: -------------------------------------------------------------------------------- 1 | --- 2 | version: 0.1.0 3 | date: Sat 14 Nov 2020 10:14:14 AM EST 4 | changes: 5 | - Add tests for version-check 6 | - Improve version-check 7 | - Move PATH assignment into test/setup 8 | - Meta bashplus supports Bash 3.2 9 | --- 10 | version: 0.0.9 11 | date: Wed 11 Nov 2020 02:19:32 PM EST 12 | changes: 13 | - Apply shellcheck fixes 14 | - Modernize bash code 15 | --- 16 | version: 0.0.8 17 | date: Fri Aug 21 08:00:45 PDT 2020 18 | changes: 19 | - Support paths with spaces @admorgan++ 20 | --- 21 | version: 0.0.7 22 | date: Sat Jan 23 16:28:59 PST 2016 23 | changes: 24 | - Update tooling, and copyright 25 | --- 26 | version: 0.0.6 27 | date: Fri Jan 23 21:05:15 PST 2015 28 | changes: 29 | - Update tooling, and copyright 30 | --- 31 | version: 0.0.1 32 | date: Sun Oct 27 19:07:51 PDT 2013 33 | changes: 34 | - First release. 35 | -------------------------------------------------------------------------------- /ext/bashplus/License: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright © 2013-2020 Ingy döt Net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the ‘Software’), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ext/bashplus/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(MAKECMDGOALS),install) 2 | ifeq "$(shell bpan version 2>/dev/null)" "" 3 | $(error 'BPAN not installed. See http://bpan.org') 4 | endif 5 | endif 6 | 7 | NAME := bash+ 8 | LIB := lib/$(NAME).bash 9 | DOC := doc/$(NAME).swim 10 | MAN1 := man/man1 11 | MAN3 := man/man3 12 | 13 | INSTALL_LIB ?= $(shell bpan env BPAN_LIB) 14 | INSTALL_DIR ?= test 15 | INSTALL_MAN1 ?= $(shell bpan env BPAN_MAN1) 16 | INSTALL_MAN3 ?= $(shell bpan env BPAN_MAN3) 17 | 18 | DOCKER_IMAGE := ingy/bash-testing:0.0.1 19 | DOCKER_TESTS := 5.1 5.0 4.4 4.3 4.2 4.1 4.0 3.2 20 | DOCKER_TESTS := $(DOCKER_TESTS:%=docker-test-%) 21 | 22 | default: help 23 | 24 | help: 25 | @echo 'Rules: test, install, doc' 26 | 27 | .PHONY: test 28 | test: 29 | prove $(PROVEOPT:%=% )test/ 30 | 31 | test-all: test docker-test 32 | 33 | docker-test: $(DOCKER_TESTS) 34 | 35 | $(DOCKER_TESTS): 36 | $(call docker-make-test,$(@:docker-test-%=%)) 37 | 38 | install: 39 | install -C -d -m 0755 $(INSTALL_LIB)/$(INSTALL_DIR)/ 40 | install -C -m 0755 $(LIB) $(INSTALL_LIB)/$(INSTALL_DIR)/ 41 | install -C -d -m 0755 $(INSTALL_MAN1)/ 42 | install -C -d -m 0755 $(INSTALL_MAN3)/ 43 | install -C -m 0644 $(MAN1)/$(NAME).1 $(INSTALL_MAN1)/ 44 | install -C -m 0644 $(MAN3)/$(NAME).3 $(INSTALL_MAN3)/ 45 | 46 | .PHONY: doc 47 | doc: ReadMe.pod $(MAN1)/$(NAME).1 $(MAN3)/$(NAME).3 48 | 49 | ReadMe.pod: $(DOC) 50 | swim --to=pod --complete --wrap $< > $@ 51 | 52 | $(MAN1)/%.1: doc/%.swim 53 | swim --to=man $< > $@ 54 | 55 | $(MAN3)/%.3: doc/%.swim 56 | swim --to=man $< > $@ 57 | 58 | define docker-make-test 59 | docker run -i -t --rm \ 60 | -v $(PWD):/git-subrepo \ 61 | -w /git-subrepo \ 62 | $(DOCKER_IMAGE) \ 63 | /bin/bash -c ' \ 64 | set -x && \ 65 | [[ -d /bash-$(1) ]] && \ 66 | export PATH=/bash-$(1)/bin:$$PATH && \ 67 | bash --version && \ 68 | make test \ 69 | ' 70 | endef 71 | -------------------------------------------------------------------------------- /ext/bashplus/Meta: -------------------------------------------------------------------------------- 1 | =meta: 0.0.2 2 | 3 | name: bashplus 4 | version: 0.1.0 5 | abstract: Modern Bash Programming 6 | homepage: https://github.com/ingydotnet/bashplus 7 | 8 | license: MIT 9 | copyright: 2013-2020 10 | author: 11 | name: Ingy döt Net 12 | email: ingy@ingy.net 13 | github: ingydotnet 14 | twitter: ingydotnet 15 | freenode: ingy 16 | homepage: http://ingy.net 17 | 18 | requires: 19 | bash: 3.2 20 | test: 21 | cmd: make test 22 | install: 23 | cmd: make install 24 | 25 | devel: 26 | git: git@github.org/ingydotnet/bashplus 27 | irc: irc.freenode.net/bpan 28 | bug: https://github.com/ingydotnet/bashplus/issues/ 29 | -------------------------------------------------------------------------------- /ext/bashplus/ReadMe.pod: -------------------------------------------------------------------------------- 1 | =pod 2 | 3 | =for comment 4 | DO NOT EDIT. This Pod was generated by Swim v0.1.48. 5 | See http://github.com/ingydotnet/swim-pm#readme 6 | 7 | =encoding utf8 8 | 9 | =head1 Name 10 | 11 | Bash+(1) - Modern Bash Programming 12 | 13 | =for html 14 | bashplus 15 | 16 | =head1 Synopsis 17 | 18 | source bash+ :std :array 19 | 20 | use Foo::Bar this that 21 | 22 | Array.new args "$@" 23 | 24 | if args.empty?; then 25 | die "I need args!" 26 | fi 27 | 28 | Foo::Bar.new foo args 29 | 30 | this is awesome # <= this is a real command! (You just imported it) 31 | 32 | =head1 Description 33 | 34 | Bash+ is just Bash... B some libraries that can make Bash programming a 35 | lot nicer. 36 | 37 | =for comment # Installation 38 | 39 | Get the source code from GitHub: 40 | 41 | git clone git@github.com:ingydotnet/bashplus 42 | 43 | Then run: 44 | 45 | make test 46 | make install # Possibly with 'sudo' 47 | 48 | =head1 Usage 49 | 50 | For now look at some libraries the use Bash+: 51 | 52 | =over 53 | 54 | =item * L 55 | 56 | =item * L 57 | 58 | =item * L 59 | 60 | =back 61 | 62 | =head1 Status 63 | 64 | If you are interested in chatting about this, C on 65 | irc.freenode.net. 66 | 67 | =head1 Author 68 | 69 | Written by Ingy döt Net 70 | 71 | =head1 Copyright & License 72 | 73 | Copyright 2013-2020. Ingy döt Net. 74 | 75 | The MIT License (MIT). 76 | 77 | =cut 78 | -------------------------------------------------------------------------------- /ext/bashplus/bin/bash+: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #------------------------------------------------------------------------------ 3 | # Bash+ - Modern Bash Programming 4 | # 5 | # Copyright (c) 2013-2020 Ingy döt Net 6 | #------------------------------------------------------------------------------ 7 | 8 | set -e 9 | shopt -s compat31 &>/dev/null || true 10 | 11 | #------------------------------------------------------------------------------ 12 | # Determine how `bash+` was called, and do the right thing: 13 | #------------------------------------------------------------------------------ 14 | if [[ ${BASH_SOURCE[0]} != "$0" ]]; then 15 | # 'bash+' is being sourced: 16 | [[ ${BASH_SOURCE[0]} =~ /bin/bash\\+$ ]] || { 17 | echo "Invalid Bash+ path '${BASH_SOURCE[0]}'" 2> /dev/null 18 | exit 1 19 | } 20 | source "${BASH_SOURCE[0]%/bin/*}"/lib/bash+.bash || return $? 21 | bash+:import "$@" 22 | return $? 23 | 24 | else 25 | if [[ $# -eq 1 ]] && [[ $1 == --version ]]; then 26 | echo 'bash+ version 0.0.9' 27 | else 28 | cat <<'...' 29 | 30 | Greetings modern Bash programmer. Welcome to Bash+! 31 | 32 | Bash+ is framework that makes Bash programming more like Ruby and Perl. 33 | 34 | See: https://github.com/bpan-org/bashplus 35 | 36 | If you got here trying to use bash+ in a program, you need to source it: 37 | 38 | source bash+ 39 | 40 | Happy Bash Hacking! 41 | 42 | ... 43 | fi 44 | fi 45 | -------------------------------------------------------------------------------- /ext/bashplus/doc/bash+.swim: -------------------------------------------------------------------------------- 1 | Bash+(1) 2 | ======== 3 | 4 | Modern Bash Programming 5 | 6 | 7 | 8 | = Synopsis 9 | 10 | source bash+ :std :array 11 | 12 | use Foo::Bar this that 13 | 14 | Array.new args "$@" 15 | 16 | if args.empty?; then 17 | die "I need args!" 18 | fi 19 | 20 | Foo::Bar.new foo args 21 | 22 | this is awesome # <= this is a real command! (You just imported it) 23 | 24 | = Description 25 | 26 | Bash+ is just Bash... *plus* some libraries that can make Bash programming a 27 | lot nicer. 28 | 29 | ## Installation 30 | 31 | Get the source code from GitHub: 32 | 33 | git clone git@github.com:ingydotnet/bashplus 34 | 35 | Then run: 36 | 37 | make test 38 | make install # Possibly with 'sudo' 39 | 40 | = Usage 41 | 42 | For now look at some libraries the use Bash+: 43 | 44 | * https://github.com/ingydotnet/git-hub 45 | * https://github.com/ingydotnet/json-bash 46 | * https://github.com/ingydotnet/test-more-bash 47 | 48 | = Status 49 | 50 | If you are interested in chatting about this, `/join #bpan` on 51 | irc.freenode.net. 52 | 53 | = Author 54 | 55 | Written by Ingy döt Net 56 | 57 | = Copyright & License 58 | 59 | Copyright 2013-2020. Ingy döt Net. 60 | 61 | The MIT License (MIT). 62 | -------------------------------------------------------------------------------- /ext/bashplus/lib/bash+.bash: -------------------------------------------------------------------------------- 1 | # bash+ - Modern Bash Programming 2 | # 3 | # Copyright (c) 2013-2020 Ingy döt Net 4 | 5 | set -e 6 | 7 | [[ ${BASHPLUS_VERSION-} ]] && return 0 8 | 9 | BASHPLUS_VERSION=0.1.0 10 | 11 | bash+:version-check() { 12 | local cmd want got out 13 | 14 | IFS=' ' read -r -a cmd <<< "${1:?}" 15 | IFS=. read -r -a want <<< "${2:?}" 16 | : "${want[0]:=0}" 17 | : "${want[1]:=0}" 18 | : "${want[2]:=0}" 19 | 20 | if [[ ${cmd[*]} == bash ]]; then 21 | got=("${BASH_VERSINFO[@]}") 22 | BASHPLUS_VERSION_CHECK=${BASH_VERSION-} 23 | else 24 | [[ ${#cmd[*]} -gt 1 ]] || cmd+=(--version) 25 | out=$("${cmd[@]}") || 26 | { echo "Failed to run '${cmd[*]}'" >&2; exit 1; } 27 | [[ $out =~ ([0-9]+\.[0-9]+(\.[0-9]+)?) ]] || 28 | { echo "Can't determine version number from '${cmd[*]}'" >&2; exit 1; } 29 | BASHPLUS_VERSION_CHECK=${BASH_REMATCH[1]} 30 | IFS=. read -r -a got <<< "$BASHPLUS_VERSION_CHECK" 31 | fi 32 | : "${got[2]:=0}" 33 | 34 | (( got[0] > want[0] || (( 35 | got[0] == want[0] && (( 36 | got[1] > want[1] || (( 37 | got[1] == want[1] && got[2] >= want[2] 38 | )) )) )) )) 39 | } 40 | 41 | bash+:version-check bash 3.2 || 42 | { echo "The 'bashplus' library requires 'Bash 3.2+'." >&2; exit 1; } 43 | 44 | @() (echo "$@") # XXX do we want to keep this? 45 | 46 | bash+:export:std() { 47 | set -o pipefail 48 | 49 | if bash+:version-check bash 4.4; then 50 | set -o nounset 51 | shopt -s inherit_errexit 52 | fi 53 | 54 | echo use die warn 55 | } 56 | 57 | # Source a bash library call import on it: 58 | bash+:use() { 59 | local library_name=${1:?bash+:use requires library name}; shift 60 | local library_path=; library_path=$(bash+:findlib "$library_name") || true 61 | [[ $library_path ]] || 62 | bash+:die "Can't find library '$library_name'." 1 63 | 64 | source "$library_path" 65 | if bash+:can "$library_name:import"; then 66 | "$library_name:import" "$@" 67 | else 68 | bash+:import "$@" 69 | fi 70 | } 71 | 72 | # Copy bash+: functions to unprefixed functions 73 | bash+:import() { 74 | local arg= 75 | for arg; do 76 | if [[ $arg =~ ^: ]]; then 77 | # Word splitting required here 78 | # shellcheck disable=2046 79 | bash+:import $(bash+:export"$arg") 80 | else 81 | bash+:fcopy "bash+:$arg" "$arg" 82 | fi 83 | done 84 | } 85 | 86 | # Function copy 87 | bash+:fcopy() { 88 | bash+:can "${1:?bash+:fcopy requires an input function name}" || 89 | bash+:die "'$1' is not a function" 2 90 | local func 91 | func=$(type "$1" 3>/dev/null | tail -n+3) 92 | [[ ${3-} ]] && "$3" 93 | eval "${2:?bash+:fcopy requires an output function name}() $func" 94 | } 95 | 96 | # Find the path of a library 97 | bash+:findlib() { 98 | local library_name 99 | library_name=$(tr '[:upper:]' '[:lower:]' <<< "${1//:://}").bash 100 | local lib=${BASHPLUSLIB:-${BASHLIB:-$PATH}} 101 | library_name=${library_name//+/\\+} 102 | IFS=':' read -r -a libs <<< "$lib" 103 | find "${libs[@]}" -name "${library_name##*/}" 2>/dev/null | 104 | grep -E "$library_name\$" | 105 | head -n1 106 | } 107 | 108 | bash+:die() { 109 | local msg=${1:-Died} 110 | msg=${msg//\\n/$'\n'} 111 | 112 | printf "%s" "$msg" >&2 113 | if [[ $msg == *$'\n' ]]; then 114 | exit 1 115 | else 116 | printf "\n" 117 | fi 118 | 119 | local c 120 | IFS=' ' read -r -a c <<< "$(caller "${DIE_STACK_LEVEL:-${2:-0}}")" 121 | if (( ${#c[@]} == 2 )); then 122 | msg=" at line %d of %s" 123 | else 124 | msg=" at line %d in %s of %s" 125 | fi 126 | 127 | # shellcheck disable=2059 128 | printf "$msg\n" "${c[@]}" >&2 129 | exit 1 130 | } 131 | 132 | bash+:warn() { 133 | local msg=${1:-Warning} 134 | printf "%s" "${msg//\\n/$'\n'}\n" >&2 135 | } 136 | 137 | bash+:can() { 138 | [[ $(type -t "${1:?bash+:can requires a function name}") == function ]] 139 | } 140 | -------------------------------------------------------------------------------- /ext/bashplus/man/man1/bash+.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) 2 | .\" 3 | .\" Standard preamble: 4 | .\" ======================================================================== 5 | .de Sp \" Vertical space (when we can't use .PP) 6 | .if t .sp .5v 7 | .if n .sp 8 | .. 9 | .de Vb \" Begin verbatim text 10 | .ft CW 11 | .nf 12 | .ne \\$1 13 | .. 14 | .de Ve \" End verbatim text 15 | .ft R 16 | .fi 17 | .. 18 | .\" Set up some character translations and predefined strings. \*(-- will 19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left 20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will 21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and 22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, 23 | .\" nothing in troff, for use with C<>. 24 | .tr \(*W- 25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' 26 | .ie n \{\ 27 | . ds -- \(*W- 28 | . ds PI pi 29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch 30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch 31 | . ds L" "" 32 | . ds R" "" 33 | . ds C` "" 34 | . ds C' "" 35 | 'br\} 36 | .el\{\ 37 | . ds -- \|\(em\| 38 | . ds PI \(*p 39 | . ds L" `` 40 | . ds R" '' 41 | . ds C` 42 | . ds C' 43 | 'br\} 44 | .\" 45 | .\" Escape single quotes in literal strings from groff's Unicode transform. 46 | .ie \n(.g .ds Aq \(aq 47 | .el .ds Aq ' 48 | .\" 49 | .\" If the F register is >0, we'll generate index entries on stderr for 50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index 51 | .\" entries marked with X<> in POD. Of course, you'll have to process the 52 | .\" output yourself in some meaningful fashion. 53 | .\" 54 | .\" Avoid warning from groff about undefined register 'F'. 55 | .de IX 56 | .. 57 | .nr rF 0 58 | .if \n(.g .if rF .nr rF 1 59 | .if (\n(rF:(\n(.g==0)) \{\ 60 | . if \nF \{\ 61 | . de IX 62 | . tm Index:\\$1\t\\n%\t"\\$2" 63 | .. 64 | . if !\nF==2 \{\ 65 | . nr % 0 66 | . nr F 2 67 | . \} 68 | . \} 69 | .\} 70 | .rr rF 71 | .\" ======================================================================== 72 | .\" 73 | .IX Title "STDIN 1" 74 | .TH STDIN 1 "November 2020" "Generated by Swim v0.1.48" "Modern Bash Programming" 75 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes 76 | .\" way too many mistakes in technical documents. 77 | .if n .ad l 78 | .nh 79 | .SH "Name" 80 | .IX Header "Name" 81 | Bash+(1) \- Modern Bash Programming 82 | .SH "Synopsis" 83 | .IX Header "Synopsis" 84 | .Vb 1 85 | \& source bash+ :std :array 86 | \& 87 | \& use Foo::Bar this that 88 | \& 89 | \& Array.new args "$@" 90 | \& 91 | \& if args.empty?; then 92 | \& die "I need args!" 93 | \& fi 94 | \& 95 | \& Foo::Bar.new foo args 96 | \& 97 | \& this is awesome # <= this is a real command! (You just imported it) 98 | .Ve 99 | .SH "Description" 100 | .IX Header "Description" 101 | Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer. 102 | .PP 103 | Get the source code from GitHub: 104 | .PP 105 | .Vb 1 106 | \& git clone git@github.com:ingydotnet/bashplus 107 | .Ve 108 | .PP 109 | Then run: 110 | .PP 111 | .Vb 2 112 | \& make test 113 | \& make install # Possibly with \*(Aqsudo\*(Aq 114 | .Ve 115 | .SH "Usage" 116 | .IX Header "Usage" 117 | For now look at some libraries the use Bash+: 118 | .IP "\(bu" 4 119 | 120 | .IP "\(bu" 4 121 | 122 | .IP "\(bu" 4 123 | 124 | .SH "Status" 125 | .IX Header "Status" 126 | If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net. 127 | .SH "Author" 128 | .IX Header "Author" 129 | Written by Ingy döt Net 130 | .SH "Copyright & License" 131 | .IX Header "Copyright & License" 132 | Copyright 2013\-2020. Ingy döt Net. 133 | .PP 134 | The \s-1MIT\s0 License (\s-1MIT\s0). 135 | -------------------------------------------------------------------------------- /ext/bashplus/man/man3/bash+.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) 2 | .\" 3 | .\" Standard preamble: 4 | .\" ======================================================================== 5 | .de Sp \" Vertical space (when we can't use .PP) 6 | .if t .sp .5v 7 | .if n .sp 8 | .. 9 | .de Vb \" Begin verbatim text 10 | .ft CW 11 | .nf 12 | .ne \\$1 13 | .. 14 | .de Ve \" End verbatim text 15 | .ft R 16 | .fi 17 | .. 18 | .\" Set up some character translations and predefined strings. \*(-- will 19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left 20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will 21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and 22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, 23 | .\" nothing in troff, for use with C<>. 24 | .tr \(*W- 25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' 26 | .ie n \{\ 27 | . ds -- \(*W- 28 | . ds PI pi 29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch 30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch 31 | . ds L" "" 32 | . ds R" "" 33 | . ds C` "" 34 | . ds C' "" 35 | 'br\} 36 | .el\{\ 37 | . ds -- \|\(em\| 38 | . ds PI \(*p 39 | . ds L" `` 40 | . ds R" '' 41 | . ds C` 42 | . ds C' 43 | 'br\} 44 | .\" 45 | .\" Escape single quotes in literal strings from groff's Unicode transform. 46 | .ie \n(.g .ds Aq \(aq 47 | .el .ds Aq ' 48 | .\" 49 | .\" If the F register is >0, we'll generate index entries on stderr for 50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index 51 | .\" entries marked with X<> in POD. Of course, you'll have to process the 52 | .\" output yourself in some meaningful fashion. 53 | .\" 54 | .\" Avoid warning from groff about undefined register 'F'. 55 | .de IX 56 | .. 57 | .nr rF 0 58 | .if \n(.g .if rF .nr rF 1 59 | .if (\n(rF:(\n(.g==0)) \{\ 60 | . if \nF \{\ 61 | . de IX 62 | . tm Index:\\$1\t\\n%\t"\\$2" 63 | .. 64 | . if !\nF==2 \{\ 65 | . nr % 0 66 | . nr F 2 67 | . \} 68 | . \} 69 | .\} 70 | .rr rF 71 | .\" ======================================================================== 72 | .\" 73 | .IX Title "STDIN 1" 74 | .TH STDIN 1 "November 2020" "Generated by Swim v0.1.48" "Modern Bash Programming" 75 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes 76 | .\" way too many mistakes in technical documents. 77 | .if n .ad l 78 | .nh 79 | .SH "Name" 80 | .IX Header "Name" 81 | Bash+(1) \- Modern Bash Programming 82 | .SH "Synopsis" 83 | .IX Header "Synopsis" 84 | .Vb 1 85 | \& source bash+ :std :array 86 | \& 87 | \& use Foo::Bar this that 88 | \& 89 | \& Array.new args "$@" 90 | \& 91 | \& if args.empty?; then 92 | \& die "I need args!" 93 | \& fi 94 | \& 95 | \& Foo::Bar.new foo args 96 | \& 97 | \& this is awesome # <= this is a real command! (You just imported it) 98 | .Ve 99 | .SH "Description" 100 | .IX Header "Description" 101 | Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer. 102 | .PP 103 | Get the source code from GitHub: 104 | .PP 105 | .Vb 1 106 | \& git clone git@github.com:ingydotnet/bashplus 107 | .Ve 108 | .PP 109 | Then run: 110 | .PP 111 | .Vb 2 112 | \& make test 113 | \& make install # Possibly with \*(Aqsudo\*(Aq 114 | .Ve 115 | .SH "Usage" 116 | .IX Header "Usage" 117 | For now look at some libraries the use Bash+: 118 | .IP "\(bu" 4 119 | 120 | .IP "\(bu" 4 121 | 122 | .IP "\(bu" 4 123 | 124 | .SH "Status" 125 | .IX Header "Status" 126 | If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net. 127 | .SH "Author" 128 | .IX Header "Author" 129 | Written by Ingy döt Net 130 | .SH "Copyright & License" 131 | .IX Header "Copyright & License" 132 | Copyright 2013\-2020. Ingy döt Net. 133 | .PP 134 | The \s-1MIT\s0 License (\s-1MIT\s0). 135 | -------------------------------------------------------------------------------- /ext/bashplus/test/base.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | source bash+ :std 6 | 7 | ok $? "'source bash+' works" 8 | 9 | is "$BASHPLUS_VERSION" '0.1.0' 'BASHPLUS_VERSION is 0.1.0' 10 | 11 | done_testing 2 12 | -------------------------------------------------------------------------------- /ext/bashplus/test/die.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | source bash+ :std 6 | 7 | got=$(die "Nope" 2>&1) || true 8 | want="Nope 9 | at line 7 in main of test/die.t" 10 | is "$got" "$want" "die() msg ok" 11 | 12 | got=$(die "Nope\n" 2>&1) || true 13 | want="Nope" 14 | is "$got" "$want" "die() msg ok" 15 | 16 | done_testing 2 17 | -------------------------------------------------------------------------------- /ext/bashplus/test/fcopy.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | source bash+ 6 | 7 | foo() { 8 | echo O HAI 9 | } 10 | 11 | like "$(type bar 2>&1)" 'bar: not found' \ 12 | 'bar is not yet a function' 13 | 14 | bash+:fcopy foo bar 15 | 16 | type -t bar &>/dev/null 17 | ok $? 'bar is now a function' 18 | is "$(type foo | tail -n+3)" "$(type bar | tail -n+3)" \ 19 | 'Copy matches original' 20 | 21 | done_testing 3 22 | -------------------------------------------------------------------------------- /ext/bashplus/test/lib/foo/bar.bash: -------------------------------------------------------------------------------- 1 | Foo__Bar_VERSION='1.2.3' 2 | 3 | Foo::Bar:baz() { :;} 4 | -------------------------------------------------------------------------------- /ext/bashplus/test/lib/foo/foo.bash: -------------------------------------------------------------------------------- 1 | Foo::Foo:import() { 2 | echo $1---$2 3 | } 4 | -------------------------------------------------------------------------------- /ext/bashplus/test/setup: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | #------------------------------------------------------------------------------ 4 | # This is a tiny version of test-more-bash that I use here. test-more-bash uses 5 | # bash+, so I want to avoid the circular dependency. This little guy does 6 | # 80-90% what test-more-bash does, with minimal code. It's a good example of 7 | # how nice Bash can be. 8 | #------------------------------------------------------------------------------ 9 | 10 | set -e -o pipefail 11 | 12 | PATH=$PWD/bin:$PATH 13 | 14 | run=0 15 | 16 | plan() { 17 | echo "1..$1" 18 | } 19 | 20 | pass() { 21 | (( ++run )) 22 | echo "ok $run${1:+ - $1}" 23 | } 24 | 25 | fail() { 26 | (( ++run )) 27 | echo "not ok $run${1:+ - $1}" 28 | } 29 | 30 | is() { 31 | if [[ $1 == "$2" ]]; then 32 | pass "$3" 33 | else 34 | fail "$3" 35 | diag "Got: $1" 36 | diag "Want: $2" 37 | fi 38 | } 39 | 40 | ok() { 41 | if (exit "${1:-$?}"); then 42 | pass "$2" 43 | else 44 | fail "$2" 45 | fi 46 | } 47 | 48 | like() { 49 | if [[ $1 =~ $2 ]]; then 50 | pass "$3" 51 | else 52 | fail "$3" 53 | diag "Got: $1" 54 | diag "Like: $2" 55 | fi 56 | } 57 | 58 | unlike() { 59 | if [[ ! $1 =~ $2 ]]; then 60 | pass "$3" 61 | else 62 | fail "$3" 63 | diag "Got: $1" 64 | diag "Dont: $2" 65 | fi 66 | } 67 | 68 | done_testing() { 69 | echo "1..${1:-$run}" 70 | } 71 | 72 | diag() { 73 | echo "# ${1//$'\n'/$'\n'# }" >&2 74 | } 75 | 76 | note() { 77 | echo "# ${1//$'\n'/$'\n'# }" 78 | } 79 | 80 | #! vim: ft=sh sw=2: 81 | -------------------------------------------------------------------------------- /ext/bashplus/test/shellcheck.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | source bash+ 6 | 7 | if ! command -v shellcheck >/dev/null; then 8 | plan skip_all "The 'shellcheck' utility is not installed" 9 | fi 10 | if [[ ! $(shellcheck --version) =~ 0\.7\.1 ]]; then 11 | plan skip_all "This test wants shellcheck version 0.7.1" 12 | fi 13 | 14 | IFS=$'\n' read -d '' -r -a shell_files <<< "$( 15 | find bin -type f 16 | find lib -type f 17 | echo test/setup 18 | find test -name '*.t' 19 | )" || true 20 | 21 | skips=( 22 | # We want to keep these 2 here always: 23 | SC1090 # Can't follow non-constant source. Use a directive to specify location. 24 | SC1091 # Not following: bash+ was not specified as input (see shellcheck -x). 25 | ) 26 | 27 | skip=$(IFS=,; echo "${skips[*]}") 28 | 29 | for file in "${shell_files[@]}"; do 30 | [[ $file == *swp ]] && continue 31 | is "$(shellcheck -e "$skip" "$file")" "" \ 32 | "The shell file '$file' passes shellcheck" 33 | done 34 | 35 | done_testing 36 | 37 | # vim: set ft=sh: 38 | -------------------------------------------------------------------------------- /ext/bashplus/test/source-bash+-std.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | source bash+ :std 6 | 7 | ok "$(bash+:can use)" 'use is imported' 8 | ok "$(bash+:can die)" 'die is imported' 9 | ok "$(bash+:can warn)" 'warn is imported' 10 | 11 | ok "$(! bash+:can import)" 'import is not imported' 12 | ok "$(! bash+:can main)" 'main is not imported' 13 | ok "$(! bash+:can fcopy)" 'fcopy is not imported' 14 | ok "$(! bash+:can findlib)" 'findlib is not imported' 15 | ok "$(! bash+:can can)" 'can is not imported' 16 | 17 | done_testing 8 18 | -------------------------------------------------------------------------------- /ext/bashplus/test/source-bash+.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | source bash+ 6 | 7 | functions=( 8 | use 9 | import 10 | fcopy 11 | findlib 12 | die 13 | warn 14 | can 15 | ) 16 | 17 | for f in "${functions[@]}"; do 18 | is "$(type -t "bash+:$f")" function \ 19 | "bash+:$f is a function" 20 | done 21 | 22 | done_testing 7 23 | -------------------------------------------------------------------------------- /ext/bashplus/test/use.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | source bash+ :std can 6 | 7 | # shellcheck disable=2034 8 | BASHLIB=test/lib 9 | 10 | use Foo::Bar 11 | 12 | ok $? 'use Foo::Bar - works' 13 | ok "$(can Foo::Bar:baz)" 'Function Foo::Bar:baz exists' 14 | 15 | # shellcheck disable=2016,2154 16 | is "$Foo__Bar_VERSION" 1.2.3 '$Foo__Bar_VERSION == 1.2.3' 17 | 18 | output=$(use Foo::Foo Boo Booo) 19 | ok $? 'use Foo::Foo Boo Booo - works' 20 | is "$output" Boo---Booo 'Correct import called' 21 | 22 | done_testing 5 23 | -------------------------------------------------------------------------------- /ext/bashplus/test/version-check.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | PATH=$PWD/bin:$PATH 6 | source bash+ version-check 7 | 8 | t1() (echo 0.1.2) 9 | t2() (echo 0.1) 10 | 11 | ok "$(version-check t1 0)" "0.1.2 >= 0" 12 | ok "$(version-check t1 0.1)" "0.1.2 >= 0.1" 13 | ok "$(version-check t1 0.1.1)" "0.1.2 >= 0.1.1" 14 | ok "$(version-check t1 0.1.2)" "0.1.2 >= 0.1.2" 15 | ok "$(! version-check t1 0.2)" "0.1.2 >= 0.2 fails" 16 | ok "$(! version-check t1 0.1.3)" "0.1.2 >= 0.1.3 fails" 17 | 18 | ok "$(version-check t2 0)" "0.1 >= 0" 19 | ok "$(version-check t2 0.1)" "0.1 >= 0.1" 20 | ok "$(! version-check t2 0.2)" "0.1 >= 0.2 fails" 21 | ok "$(! version-check t2 0.1.1)" "0.1 >= 0.1.1" 22 | 23 | done_testing 10 24 | -------------------------------------------------------------------------------- /ext/test-more-bash/.gitrepo: -------------------------------------------------------------------------------- 1 | ; DO NOT EDIT (unless you know what you are doing) 2 | ; 3 | ; This subdirectory is a git "subrepo", and this file is maintained by the 4 | ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme 5 | ; 6 | [subrepo] 7 | remote = git@github.com:ingydotnet/test-more-bash.git 8 | branch = master 9 | commit = c7df24fcb0814fbb62a33d92dc3c8d526ff710f0 10 | parent = 914c2ae8e3b881f62ca987c1c3343df6a034d865 11 | cmdver = 0.4.1 12 | method = merge 13 | -------------------------------------------------------------------------------- /ext/test-more-bash/.travis.yml: -------------------------------------------------------------------------------- 1 | # C language gives closest shell env. 2 | language: c 3 | 4 | script: 5 | - git submodule update --init --recursive 6 | - PROVEOPT=-v make test 7 | -------------------------------------------------------------------------------- /ext/test-more-bash/Changes: -------------------------------------------------------------------------------- 1 | --- 2 | version: 0.0.5 3 | date: Wed 11 Nov 2020 02:33:42 PM EST 4 | changes: 5 | - Refactor to modern bash 6 | --- 7 | version: 0.0.4 8 | date: Fri Sep 4 12:26:58 2020 -0700 9 | changes: 10 | - Make up to date 11 | - Apply a few PRs 12 | --- 13 | version: 0.0.3 14 | date: Sat Jan 23 16:39:20 PST 2016 15 | changes: 16 | - Make up to date 17 | --- 18 | version: 0.0.2 19 | date: Fri Jan 23 21:26:18 PST 2015 20 | changes: 21 | - Make up to date 22 | --- 23 | version: 0.0.1 24 | date: Sun Oct 27 22:53:10 PDT 2013 25 | changes: 26 | - First release. 27 | -------------------------------------------------------------------------------- /ext/test-more-bash/License: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright © 2013-2020. Ingy döt Net. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the ‘Software’), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ext/test-more-bash/Makefile: -------------------------------------------------------------------------------- 1 | NAME := test-more 2 | DOC := doc/$(NAME).swim 3 | MAN3 := man/man3 4 | 5 | DOCKER_IMAGE := ingy/bash-testing:0.0.1 6 | DOCKER_TESTS := 5.1 5.0 4.4 4.3 4.2 4.1 4.0 3.2 7 | DOCKER_TESTS := $(DOCKER_TESTS:%=docker-test-%) 8 | 9 | default: help 10 | 11 | help: 12 | @echo 'Rules: test, doc' 13 | 14 | .PHONY: test 15 | test: 16 | prove $(PROVEOPT:%=% )test/ 17 | 18 | test-all: test docker-test 19 | 20 | docker-test: $(DOCKER_TESTS) 21 | 22 | $(DOCKER_TESTS): 23 | $(call docker-make-test,$(@:docker-test-%=%)) 24 | 25 | doc: ReadMe.pod $(MAN3)/$(NAME).3 26 | 27 | ReadMe.pod: $(DOC) 28 | swim --to=pod --complete --wrap $< > $@ 29 | 30 | $(MAN3)/%.3: doc/%.swim 31 | swim --to=man $< > $@ 32 | 33 | define docker-make-test 34 | docker run -i -t --rm \ 35 | -v $(PWD):/git-subrepo \ 36 | -w /git-subrepo \ 37 | $(DOCKER_IMAGE) \ 38 | /bin/bash -c ' \ 39 | set -x && \ 40 | [[ -d /bash-$(1) ]] && \ 41 | export PATH=/bash-$(1)/bin:$$PATH && \ 42 | bash --version && \ 43 | make test \ 44 | ' 45 | endef 46 | -------------------------------------------------------------------------------- /ext/test-more-bash/Meta: -------------------------------------------------------------------------------- 1 | =meta: 0.0.2 2 | 3 | name: test-more 4 | version: 0.0.5 5 | abstract: TAP Testing for Bash 6 | homepage: http://bpan.org/package/test-more/ 7 | 8 | license: MIT 9 | copyright: 2013-2020 10 | author: 11 | name: Ingy döt Net 12 | email: ingy@ingy.net 13 | github: ingydotnet 14 | twitter: ingydotnet 15 | freenode: ingy 16 | homepage: http://ingy.net 17 | 18 | requires: 19 | bash: 4.4.0 20 | bashplus: 0.0.9 21 | test-tap: 0.0.5 22 | test: 23 | cmd: make test 24 | install: 25 | cmd: make install 26 | 27 | devel: 28 | git: git@github.org/ingydotnet/test-more-bash.git 29 | irc: irc.freenode.net#bpan 30 | bug: https://github.com/ingydotnet/test-more-bash/issues/ 31 | -------------------------------------------------------------------------------- /ext/test-more-bash/ReadMe.pod: -------------------------------------------------------------------------------- 1 | =pod 2 | 3 | =for comment 4 | DO NOT EDIT. This Pod was generated by Swim v0.1.48. 5 | See http://github.com/ingydotnet/swim-pm#readme 6 | 7 | =encoding utf8 8 | 9 | =head1 Name 10 | 11 | Test::More - TAP Testing for Bash 12 | 13 | =for html 14 | test-more-bash 15 | 16 | =head1 Synopsis 17 | 18 | Write a test file like this. Maybe call it C: 19 | 20 | #!/usr/bin/env bash 21 | 22 | TEST_MORE_PATH="/path/to/test-more-bash" 23 | BASHLIB="` 24 | find $TEST_MORE_PATH -type d | 25 | grep -E '/(bin|lib)$' | 26 | xargs -n1 printf "%s:"`" 27 | PATH="$BASHLIB$PATH" 28 | 29 | source bash+ :std 30 | 31 | use Test::More 32 | 33 | plan tests 8 34 | 35 | some-command 36 | ok $? 'some-command is ok' 37 | 38 | # or: 39 | ok "`some-command`" 'some-command is ok' 40 | 41 | pass 'This will always pass' 42 | 43 | fail 'This will always fail' 44 | 45 | is `echo foo` 'foo' 'foo is foo' 46 | 47 | isnt foo bar "foo isn't bar" 48 | 49 | like food foo 'food is like foo' 50 | 51 | unlike team I "There's no 'I' in 'team'" 52 | 53 | diag "A message for stderr" 54 | 55 | note "A message for stdout" 56 | 57 | output=( $(ls) ) 58 | expected=(README lib bin) 59 | cmp-array output expected "list files" 60 | 61 | Run the test with C like this: 62 | 63 | prove test/test.t 64 | 65 | Prove knows it's Bash from the first line (the hashbang), and it just works. 66 | 67 | =head1 Description 68 | 69 | Test::More is the tried and true testing library for Perl. It uses TAP (the 70 | Test Anything Protocol). This is the same thing for Bash. For the most part it 71 | should work exactly the same. 72 | 73 | =head1 Methods 74 | 75 | This is the basic usage: 76 | 77 | =over 78 | 79 | =item * C 80 | 81 | =item * C 82 | 83 | =item * C 84 | 85 | =item * C 86 | 87 | =item * C 88 | 89 | =item * C 90 | 91 | =item * C 92 | 93 | =item * C 94 | 95 | =item * C 96 | 97 | =item * C 98 | 99 | =item * C 100 | 101 | =item * C 102 | 103 | =item * C 104 | 105 | =item * `cmp-array output expected "message" 106 | 107 | =back 108 | 109 | More detailed info coming soon. 110 | 111 | =head1 Author 112 | 113 | Ingy döt Net 114 | 115 | =head1 Copyright & License 116 | 117 | Copyright 2013-2020. Ingy döt Net. 118 | 119 | The MIT License (MIT) 120 | 121 | =cut 122 | -------------------------------------------------------------------------------- /ext/test-more-bash/doc/test-more.swim: -------------------------------------------------------------------------------- 1 | Test::More 2 | ========== 3 | 4 | TAP Testing for Bash 5 | 6 | 7 | 8 | = Synopsis 9 | 10 | Write a test file like this. Maybe call it `test/test.t`: 11 | 12 | #!/usr/bin/env bash 13 | 14 | TEST_MORE_PATH="/path/to/test-more-bash" 15 | BASHLIB="` 16 | find $TEST_MORE_PATH -type d | 17 | grep -E '/(bin|lib)$' | 18 | xargs -n1 printf "%s:"`" 19 | PATH="$BASHLIB$PATH" 20 | 21 | source bash+ :std 22 | 23 | use Test::More 24 | 25 | plan tests 8 26 | 27 | some-command 28 | ok $? 'some-command is ok' 29 | 30 | # or: 31 | ok "`some-command`" 'some-command is ok' 32 | 33 | pass 'This will always pass' 34 | 35 | fail 'This will always fail' 36 | 37 | is `echo foo` 'foo' 'foo is foo' 38 | 39 | isnt foo bar "foo isn't bar" 40 | 41 | like food foo 'food is like foo' 42 | 43 | unlike team I "There's no 'I' in 'team'" 44 | 45 | diag "A message for stderr" 46 | 47 | note "A message for stdout" 48 | 49 | output=( $(ls) ) 50 | expected=(README lib bin) 51 | cmp-array output expected "list files" 52 | 53 | Run the test with `prove` like this: 54 | 55 | prove test/test.t 56 | 57 | Prove knows it's Bash from the first line (the hashbang), and it just works. 58 | 59 | = Description 60 | 61 | Test::More is the tried and true testing library for Perl. It uses TAP (the 62 | Test Anything Protocol). This is the same thing for Bash. For the most part it 63 | should work exactly the same. 64 | 65 | = Methods 66 | 67 | This is the basic usage: 68 | 69 | * `plan tests $count` 70 | * `ok $status_code "$label"` 71 | * `pass "$label"` 72 | * `fail "$label"` 73 | * `is "$got" "$want" "label"` 74 | * `isnt "$got" "$unwanted" "$label"` 75 | * `like "$got" "$regex" "$label"` 76 | * `unlike "$got" "$regex" "$label"` 77 | * `diag "$message"` 78 | * `note "$message"` 79 | * `done_testing $count` 80 | * `plan skip_all "$reason"` 81 | * `BAIL_OUT "$reason"` 82 | * `cmp-array output expected "message" 83 | 84 | More detailed info coming soon. 85 | 86 | = Author 87 | 88 | Ingy döt Net 89 | 90 | = Copyright & License 91 | 92 | Copyright 2013-2020. Ingy döt Net. 93 | 94 | The MIT License (MIT) 95 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/.gitrepo: -------------------------------------------------------------------------------- 1 | ; DO NOT EDIT (unless you know what you are doing) 2 | ; 3 | ; This subdirectory is a git "subrepo", and this file is maintained by the 4 | ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme 5 | ; 6 | [subrepo] 7 | remote = git@github.com:ingydotnet/bashplus.git 8 | branch = master 9 | commit = e49f45a1457fed3cceb15bd4a82b0f7515efd8e5 10 | parent = c978e2afd6861203138f28d0021e03fa1ffbba0c 11 | cmdver = 0.4.1 12 | method = merge 13 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/.travis.yml: -------------------------------------------------------------------------------- 1 | # C language gives closest shell env. 2 | language: c 3 | 4 | script: 5 | - git submodule update --init --recursive 6 | - PROVEOPT=-v make test 7 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/Changes: -------------------------------------------------------------------------------- 1 | --- 2 | version: 0.0.9 3 | date: Wed 11 Nov 2020 02:19:32 PM EST 4 | changes: 5 | - Apply shellcheck fixes 6 | - Modernize bash code 7 | --- 8 | version: 0.0.8 9 | date: Fri Aug 21 08:00:45 PDT 2020 10 | changes: 11 | - Support paths with spaces @admorgan++ 12 | --- 13 | version: 0.0.7 14 | date: Sat Jan 23 16:28:59 PST 2016 15 | changes: 16 | - Update tooling, and copyright 17 | --- 18 | version: 0.0.6 19 | date: Fri Jan 23 21:05:15 PST 2015 20 | changes: 21 | - Update tooling, and copyright 22 | --- 23 | version: 0.0.1 24 | date: Sun Oct 27 19:07:51 PDT 2013 25 | changes: 26 | - First release. 27 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/License: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright © 2013-2020 Ingy döt Net 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the ‘Software’), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(MAKECMDGOALS),install) 2 | ifeq "$(shell bpan version 2>/dev/null)" "" 3 | $(error 'BPAN not installed. See http://bpan.org') 4 | endif 5 | endif 6 | 7 | NAME := bash+ 8 | LIB := lib/$(NAME).bash 9 | DOC := doc/$(NAME).swim 10 | MAN1 := man/man1 11 | MAN3 := man/man3 12 | 13 | INSTALL_LIB ?= $(shell bpan env BPAN_LIB) 14 | INSTALL_DIR ?= test 15 | INSTALL_MAN1 ?= $(shell bpan env BPAN_MAN1) 16 | INSTALL_MAN3 ?= $(shell bpan env BPAN_MAN3) 17 | 18 | DOCKER_IMAGE := ingy/bash-testing:0.0.1 19 | DOCKER_TESTS := 5.1 5.0 4.4 4.3 4.2 4.1 4.0 3.2 20 | DOCKER_TESTS := $(DOCKER_TESTS:%=docker-test-%) 21 | 22 | default: help 23 | 24 | help: 25 | @echo 'Rules: test, install, doc' 26 | 27 | .PHONY: test 28 | test: 29 | prove $(PROVEOPT:%=% )test/ 30 | 31 | test-all: test docker-test 32 | 33 | docker-test: $(DOCKER_TESTS) 34 | 35 | $(DOCKER_TESTS): 36 | $(call docker-make-test,$(@:docker-test-%=%)) 37 | 38 | install: 39 | install -C -d -m 0755 $(INSTALL_LIB)/$(INSTALL_DIR)/ 40 | install -C -m 0755 $(LIB) $(INSTALL_LIB)/$(INSTALL_DIR)/ 41 | install -C -d -m 0755 $(INSTALL_MAN1)/ 42 | install -C -d -m 0755 $(INSTALL_MAN3)/ 43 | install -C -m 0644 $(MAN1)/$(NAME).1 $(INSTALL_MAN1)/ 44 | install -C -m 0644 $(MAN3)/$(NAME).3 $(INSTALL_MAN3)/ 45 | 46 | .PHONY: doc 47 | doc: ReadMe.pod $(MAN1)/$(NAME).1 $(MAN3)/$(NAME).3 48 | 49 | ReadMe.pod: $(DOC) 50 | swim --to=pod --complete --wrap $< > $@ 51 | 52 | $(MAN1)/%.1: doc/%.swim 53 | swim --to=man $< > $@ 54 | 55 | $(MAN3)/%.3: doc/%.swim 56 | swim --to=man $< > $@ 57 | 58 | define docker-make-test 59 | docker run -i -t --rm \ 60 | -v $(PWD):/git-subrepo \ 61 | -w /git-subrepo \ 62 | $(DOCKER_IMAGE) \ 63 | /bin/bash -c ' \ 64 | set -x && \ 65 | [[ -d /bash-$(1) ]] && \ 66 | export PATH=/bash-$(1)/bin:$$PATH && \ 67 | bash --version && \ 68 | make test \ 69 | ' 70 | endef 71 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/Meta: -------------------------------------------------------------------------------- 1 | =meta: 0.0.2 2 | 3 | name: bashplus 4 | version: 0.0.9 5 | abstract: Modern Bash Programming 6 | homepage: http://bpan.org/package/bashplus/ 7 | 8 | license: MIT 9 | copyright: 2013-2020 10 | author: 11 | name: Ingy döt Net 12 | email: ingy@ingy.net 13 | github: ingydotnet 14 | twitter: ingydotnet 15 | freenode: ingy 16 | homepage: http://ingy.net 17 | 18 | requires: 19 | bash: 4.4.0 20 | test: 21 | cmd: make test 22 | install: 23 | cmd: make install 24 | 25 | devel: 26 | git: git@github.org/ingydotnet/bashplus 27 | irc: irc.freenode.net/bpan 28 | bug: https://github.com/ingydotnet/bashplus/issues/ 29 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/ReadMe.pod: -------------------------------------------------------------------------------- 1 | =pod 2 | 3 | =for comment 4 | DO NOT EDIT. This Pod was generated by Swim v0.1.48. 5 | See http://github.com/ingydotnet/swim-pm#readme 6 | 7 | =encoding utf8 8 | 9 | =head1 Name 10 | 11 | Bash+(1) - Modern Bash Programming 12 | 13 | =for html 14 | bashplus 15 | 16 | =head1 Synopsis 17 | 18 | source bash+ :std :array 19 | 20 | use Foo::Bar this that 21 | 22 | Array.new args "$@" 23 | 24 | if args.empty?; then 25 | die "I need args!" 26 | fi 27 | 28 | Foo::Bar.new foo args 29 | 30 | this is awesome # <= this is a real command! (You just imported it) 31 | 32 | =head1 Description 33 | 34 | Bash+ is just Bash... B some libraries that can make Bash programming a 35 | lot nicer. 36 | 37 | =for comment # Installation 38 | 39 | Get the source code from GitHub: 40 | 41 | git clone git@github.com:ingydotnet/bashplus 42 | 43 | Then run: 44 | 45 | make test 46 | make install # Possibly with 'sudo' 47 | 48 | =head1 Usage 49 | 50 | For now look at some libraries the use Bash+: 51 | 52 | =over 53 | 54 | =item * L 55 | 56 | =item * L 57 | 58 | =item * L 59 | 60 | =back 61 | 62 | =head1 Status 63 | 64 | If you are interested in chatting about this, C on 65 | irc.freenode.net. 66 | 67 | =head1 Author 68 | 69 | Written by Ingy döt Net 70 | 71 | =head1 Copyright & License 72 | 73 | Copyright 2013-2020. Ingy döt Net. 74 | 75 | The MIT License (MIT). 76 | 77 | =cut 78 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/bin/bash+: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #------------------------------------------------------------------------------ 3 | # Bash+ - Modern Bash Programming 4 | # 5 | # Copyright (c) 2013-2020 Ingy döt Net 6 | #------------------------------------------------------------------------------ 7 | 8 | set -e 9 | shopt -s compat31 &>/dev/null || true 10 | 11 | #------------------------------------------------------------------------------ 12 | # Determine how `bash+` was called, and do the right thing: 13 | #------------------------------------------------------------------------------ 14 | if [[ ${BASH_SOURCE[0]} != "$0" ]]; then 15 | # 'bash+' is being sourced: 16 | [[ ${BASH_SOURCE[0]} =~ /bin/bash\\+$ ]] || { 17 | echo "Invalid Bash+ path '${BASH_SOURCE[0]}'" 2> /dev/null 18 | exit 1 19 | } 20 | source "${BASH_SOURCE[0]%/bin/*}"/lib/bash+.bash || return $? 21 | bash+:import "$@" 22 | return $? 23 | 24 | else 25 | if [[ $# -eq 1 ]] && [[ $1 == --version ]]; then 26 | echo 'bash+ version 0.0.9' 27 | else 28 | cat <<'...' 29 | 30 | Greetings modern Bash programmer. Welcome to Bash+! 31 | 32 | Bash+ is framework that makes Bash programming more like Ruby and Perl. 33 | 34 | See: https://github.com/bpan-org/bashplus 35 | 36 | If you got here trying to use bash+ in a program, you need to source it: 37 | 38 | source bash+ 39 | 40 | Happy Bash Hacking! 41 | 42 | ... 43 | fi 44 | fi 45 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/doc/bash+.swim: -------------------------------------------------------------------------------- 1 | Bash+(1) 2 | ======== 3 | 4 | Modern Bash Programming 5 | 6 | 7 | 8 | = Synopsis 9 | 10 | source bash+ :std :array 11 | 12 | use Foo::Bar this that 13 | 14 | Array.new args "$@" 15 | 16 | if args.empty?; then 17 | die "I need args!" 18 | fi 19 | 20 | Foo::Bar.new foo args 21 | 22 | this is awesome # <= this is a real command! (You just imported it) 23 | 24 | = Description 25 | 26 | Bash+ is just Bash... *plus* some libraries that can make Bash programming a 27 | lot nicer. 28 | 29 | ## Installation 30 | 31 | Get the source code from GitHub: 32 | 33 | git clone git@github.com:ingydotnet/bashplus 34 | 35 | Then run: 36 | 37 | make test 38 | make install # Possibly with 'sudo' 39 | 40 | = Usage 41 | 42 | For now look at some libraries the use Bash+: 43 | 44 | * https://github.com/ingydotnet/git-hub 45 | * https://github.com/ingydotnet/json-bash 46 | * https://github.com/ingydotnet/test-more-bash 47 | 48 | = Status 49 | 50 | If you are interested in chatting about this, `/join #bpan` on 51 | irc.freenode.net. 52 | 53 | = Author 54 | 55 | Written by Ingy döt Net 56 | 57 | = Copyright & License 58 | 59 | Copyright 2013-2020. Ingy döt Net. 60 | 61 | The MIT License (MIT). 62 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/lib/bash+.bash: -------------------------------------------------------------------------------- 1 | # bash+ - Modern Bash Programming 2 | # 3 | # Copyright (c) 2013-2020 Ingy döt Net 4 | 5 | set -e 6 | 7 | [[ ${BASHPLUS_VERSION-} ]] && return 0 8 | 9 | BASHPLUS_VERSION=0.0.9 10 | 11 | bash+:version-check() { 12 | local cmd want got out 13 | 14 | IFS=' ' read -r -a cmd <<< "${1:?}" 15 | IFS=. read -r -a want <<< "${2:?}" 16 | : "${want[2]:=0}" 17 | 18 | if [[ ${cmd[*]} == bash ]]; then 19 | got=("${BASH_VERSINFO[@]}") 20 | BASHPLUS_VERSION_CHECK=${BASH_VERSION-} 21 | else 22 | [[ ${#cmd[*]} -gt 1 ]] || cmd+=(--version) 23 | out=$("${cmd[@]}") || 24 | { echo "Failed to run '${cmd[*]}'" >&2; exit 1; } 25 | [[ $out =~ ([0-9]+\.[0-9]+(\.[0-9]+)?) ]] || 26 | { echo "Can't determine version number from '${cmd[*]}'" >&2; exit 1; } 27 | BASHPLUS_VERSION_CHECK=${BASH_REMATCH[1]} 28 | IFS=. read -r -a got <<< "$BASHPLUS_VERSION_CHECK" 29 | fi 30 | : "${got[2]:=0}" 31 | 32 | (( 33 | got[0] > want[0] || 34 | got[0] == want[0] && got[1] > want[1] || 35 | got[0] == want[0] && got[1] == want[1] && got[2] >= want[2] 36 | )) || return 1 37 | 38 | return 0 39 | } 40 | 41 | bash+:version-check bash 3.2 || 42 | { echo "The 'bashplus' library requires 'Bash 3.2+'." >&2; exit 1; } 43 | 44 | @() (echo "$@") # XXX do we want to keep this? 45 | 46 | bash+:export:std() { 47 | set -o pipefail 48 | 49 | if bash+:version-check bash 4.4; then 50 | set -o nounset 51 | shopt -s inherit_errexit 52 | fi 53 | 54 | echo use die warn 55 | } 56 | 57 | # Source a bash library call import on it: 58 | bash+:use() { 59 | local library_name=${1:?bash+:use requires library name}; shift 60 | local library_path=; library_path=$(bash+:findlib "$library_name") || true 61 | [[ $library_path ]] || 62 | bash+:die "Can't find library '$library_name'." 1 63 | 64 | source "$library_path" 65 | if bash+:can "$library_name:import"; then 66 | "$library_name:import" "$@" 67 | else 68 | bash+:import "$@" 69 | fi 70 | } 71 | 72 | # Copy bash+: functions to unprefixed functions 73 | bash+:import() { 74 | local arg= 75 | for arg; do 76 | if [[ $arg =~ ^: ]]; then 77 | # Word splitting required here 78 | # shellcheck disable=2046 79 | bash+:import $(bash+:export"$arg") 80 | else 81 | bash+:fcopy "bash+:$arg" "$arg" 82 | fi 83 | done 84 | } 85 | 86 | # Function copy 87 | bash+:fcopy() { 88 | bash+:can "${1:?bash+:fcopy requires an input function name}" || 89 | bash+:die "'$1' is not a function" 2 90 | local func 91 | func=$(type "$1" 3>/dev/null | tail -n+3) 92 | [[ ${3-} ]] && "$3" 93 | eval "${2:?bash+:fcopy requires an output function name}() $func" 94 | } 95 | 96 | # Find the path of a library 97 | bash+:findlib() { 98 | local library_name 99 | library_name=$(tr '[:upper:]' '[:lower:]' <<< "${1//:://}").bash 100 | local lib=${BASHPLUSLIB:-${BASHLIB:-$PATH}} 101 | library_name=${library_name//+/\\+} 102 | IFS=':' read -r -a libs <<< "$lib" 103 | find "${libs[@]}" -name "${library_name##*/}" 2>/dev/null | 104 | grep -E "$library_name\$" | 105 | head -n1 106 | } 107 | 108 | bash+:die() { 109 | local msg=${1:-Died} 110 | msg=${msg//\\n/$'\n'} 111 | 112 | printf "%s" "$msg" >&2 113 | if [[ $msg == *$'\n' ]]; then 114 | exit 1 115 | else 116 | printf "\n" 117 | fi 118 | 119 | local c 120 | IFS=' ' read -r -a c <<< "$(caller "${DIE_STACK_LEVEL:-${2:-0}}")" 121 | if (( ${#c[@]} == 2 )); then 122 | msg=" at line %d of %s" 123 | else 124 | msg=" at line %d in %s of %s" 125 | fi 126 | 127 | # shellcheck disable=2059 128 | printf "$msg\n" "${c[@]}" >&2 129 | exit 1 130 | } 131 | 132 | bash+:warn() { 133 | local msg=${1:-Warning} 134 | printf "%s" "${msg//\\n/$'\n'}\n" >&2 135 | } 136 | 137 | bash+:can() { 138 | [[ $(type -t "${1:?bash+:can requires a function name}") == function ]] 139 | } 140 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/man/man1/bash+.1: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) 2 | .\" 3 | .\" Standard preamble: 4 | .\" ======================================================================== 5 | .de Sp \" Vertical space (when we can't use .PP) 6 | .if t .sp .5v 7 | .if n .sp 8 | .. 9 | .de Vb \" Begin verbatim text 10 | .ft CW 11 | .nf 12 | .ne \\$1 13 | .. 14 | .de Ve \" End verbatim text 15 | .ft R 16 | .fi 17 | .. 18 | .\" Set up some character translations and predefined strings. \*(-- will 19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left 20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will 21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and 22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, 23 | .\" nothing in troff, for use with C<>. 24 | .tr \(*W- 25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' 26 | .ie n \{\ 27 | . ds -- \(*W- 28 | . ds PI pi 29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch 30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch 31 | . ds L" "" 32 | . ds R" "" 33 | . ds C` "" 34 | . ds C' "" 35 | 'br\} 36 | .el\{\ 37 | . ds -- \|\(em\| 38 | . ds PI \(*p 39 | . ds L" `` 40 | . ds R" '' 41 | . ds C` 42 | . ds C' 43 | 'br\} 44 | .\" 45 | .\" Escape single quotes in literal strings from groff's Unicode transform. 46 | .ie \n(.g .ds Aq \(aq 47 | .el .ds Aq ' 48 | .\" 49 | .\" If the F register is >0, we'll generate index entries on stderr for 50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index 51 | .\" entries marked with X<> in POD. Of course, you'll have to process the 52 | .\" output yourself in some meaningful fashion. 53 | .\" 54 | .\" Avoid warning from groff about undefined register 'F'. 55 | .de IX 56 | .. 57 | .nr rF 0 58 | .if \n(.g .if rF .nr rF 1 59 | .if (\n(rF:(\n(.g==0)) \{\ 60 | . if \nF \{\ 61 | . de IX 62 | . tm Index:\\$1\t\\n%\t"\\$2" 63 | .. 64 | . if !\nF==2 \{\ 65 | . nr % 0 66 | . nr F 2 67 | . \} 68 | . \} 69 | .\} 70 | .rr rF 71 | .\" ======================================================================== 72 | .\" 73 | .IX Title "STDIN 1" 74 | .TH STDIN 1 "November 2020" "Generated by Swim v0.1.48" "Modern Bash Programming" 75 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes 76 | .\" way too many mistakes in technical documents. 77 | .if n .ad l 78 | .nh 79 | .SH "Name" 80 | .IX Header "Name" 81 | Bash+(1) \- Modern Bash Programming 82 | .SH "Synopsis" 83 | .IX Header "Synopsis" 84 | .Vb 1 85 | \& source bash+ :std :array 86 | \& 87 | \& use Foo::Bar this that 88 | \& 89 | \& Array.new args "$@" 90 | \& 91 | \& if args.empty?; then 92 | \& die "I need args!" 93 | \& fi 94 | \& 95 | \& Foo::Bar.new foo args 96 | \& 97 | \& this is awesome # <= this is a real command! (You just imported it) 98 | .Ve 99 | .SH "Description" 100 | .IX Header "Description" 101 | Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer. 102 | .PP 103 | Get the source code from GitHub: 104 | .PP 105 | .Vb 1 106 | \& git clone git@github.com:ingydotnet/bashplus 107 | .Ve 108 | .PP 109 | Then run: 110 | .PP 111 | .Vb 2 112 | \& make test 113 | \& make install # Possibly with \*(Aqsudo\*(Aq 114 | .Ve 115 | .SH "Usage" 116 | .IX Header "Usage" 117 | For now look at some libraries the use Bash+: 118 | .IP "\(bu" 4 119 | 120 | .IP "\(bu" 4 121 | 122 | .IP "\(bu" 4 123 | 124 | .SH "Status" 125 | .IX Header "Status" 126 | If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net. 127 | .SH "Author" 128 | .IX Header "Author" 129 | Written by Ingy döt Net 130 | .SH "Copyright & License" 131 | .IX Header "Copyright & License" 132 | Copyright 2013\-2020. Ingy döt Net. 133 | .PP 134 | The \s-1MIT\s0 License (\s-1MIT\s0). 135 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/man/man3/bash+.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) 2 | .\" 3 | .\" Standard preamble: 4 | .\" ======================================================================== 5 | .de Sp \" Vertical space (when we can't use .PP) 6 | .if t .sp .5v 7 | .if n .sp 8 | .. 9 | .de Vb \" Begin verbatim text 10 | .ft CW 11 | .nf 12 | .ne \\$1 13 | .. 14 | .de Ve \" End verbatim text 15 | .ft R 16 | .fi 17 | .. 18 | .\" Set up some character translations and predefined strings. \*(-- will 19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left 20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will 21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and 22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, 23 | .\" nothing in troff, for use with C<>. 24 | .tr \(*W- 25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' 26 | .ie n \{\ 27 | . ds -- \(*W- 28 | . ds PI pi 29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch 30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch 31 | . ds L" "" 32 | . ds R" "" 33 | . ds C` "" 34 | . ds C' "" 35 | 'br\} 36 | .el\{\ 37 | . ds -- \|\(em\| 38 | . ds PI \(*p 39 | . ds L" `` 40 | . ds R" '' 41 | . ds C` 42 | . ds C' 43 | 'br\} 44 | .\" 45 | .\" Escape single quotes in literal strings from groff's Unicode transform. 46 | .ie \n(.g .ds Aq \(aq 47 | .el .ds Aq ' 48 | .\" 49 | .\" If the F register is >0, we'll generate index entries on stderr for 50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index 51 | .\" entries marked with X<> in POD. Of course, you'll have to process the 52 | .\" output yourself in some meaningful fashion. 53 | .\" 54 | .\" Avoid warning from groff about undefined register 'F'. 55 | .de IX 56 | .. 57 | .nr rF 0 58 | .if \n(.g .if rF .nr rF 1 59 | .if (\n(rF:(\n(.g==0)) \{\ 60 | . if \nF \{\ 61 | . de IX 62 | . tm Index:\\$1\t\\n%\t"\\$2" 63 | .. 64 | . if !\nF==2 \{\ 65 | . nr % 0 66 | . nr F 2 67 | . \} 68 | . \} 69 | .\} 70 | .rr rF 71 | .\" ======================================================================== 72 | .\" 73 | .IX Title "STDIN 1" 74 | .TH STDIN 1 "November 2020" "Generated by Swim v0.1.48" "Modern Bash Programming" 75 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes 76 | .\" way too many mistakes in technical documents. 77 | .if n .ad l 78 | .nh 79 | .SH "Name" 80 | .IX Header "Name" 81 | Bash+(1) \- Modern Bash Programming 82 | .SH "Synopsis" 83 | .IX Header "Synopsis" 84 | .Vb 1 85 | \& source bash+ :std :array 86 | \& 87 | \& use Foo::Bar this that 88 | \& 89 | \& Array.new args "$@" 90 | \& 91 | \& if args.empty?; then 92 | \& die "I need args!" 93 | \& fi 94 | \& 95 | \& Foo::Bar.new foo args 96 | \& 97 | \& this is awesome # <= this is a real command! (You just imported it) 98 | .Ve 99 | .SH "Description" 100 | .IX Header "Description" 101 | Bash+ is just Bash... \fBplus\fR some libraries that can make Bash programming a lot nicer. 102 | .PP 103 | Get the source code from GitHub: 104 | .PP 105 | .Vb 1 106 | \& git clone git@github.com:ingydotnet/bashplus 107 | .Ve 108 | .PP 109 | Then run: 110 | .PP 111 | .Vb 2 112 | \& make test 113 | \& make install # Possibly with \*(Aqsudo\*(Aq 114 | .Ve 115 | .SH "Usage" 116 | .IX Header "Usage" 117 | For now look at some libraries the use Bash+: 118 | .IP "\(bu" 4 119 | 120 | .IP "\(bu" 4 121 | 122 | .IP "\(bu" 4 123 | 124 | .SH "Status" 125 | .IX Header "Status" 126 | If you are interested in chatting about this, \f(CW\*(C`/join #bpan\*(C'\fR on irc.freenode.net. 127 | .SH "Author" 128 | .IX Header "Author" 129 | Written by Ingy döt Net 130 | .SH "Copyright & License" 131 | .IX Header "Copyright & License" 132 | Copyright 2013\-2020. Ingy döt Net. 133 | .PP 134 | The \s-1MIT\s0 License (\s-1MIT\s0). 135 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/base.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | PATH=$PWD/bin:$PATH 6 | source bash+ :std 7 | 8 | ok $? "'source bash+' works" 9 | 10 | is "$BASHPLUS_VERSION" '0.0.9' 'BASHPLUS_VERSION is 0.0.9' 11 | 12 | done_testing 2 13 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/die.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | PATH=$PWD/bin:$PATH 6 | source bash+ :std 7 | 8 | got=$(die "Nope" 2>&1) || true 9 | want="Nope 10 | at line 8 in main of test/die.t" 11 | is "$got" "$want" "die() msg ok" 12 | 13 | got=$(die "Nope\n" 2>&1) || true 14 | want="Nope" 15 | is "$got" "$want" "die() msg ok" 16 | 17 | done_testing 2 18 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/fcopy.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | PATH=$PWD/bin:$PATH 6 | source bash+ 7 | 8 | foo() { 9 | echo O HAI 10 | } 11 | 12 | like "$(type bar 2>&1)" 'bar: not found' \ 13 | 'bar is not yet a function' 14 | 15 | bash+:fcopy foo bar 16 | 17 | type -t bar &>/dev/null 18 | ok $? 'bar is now a function' 19 | is "$(type foo | tail -n+3)" "$(type bar | tail -n+3)" \ 20 | 'Copy matches original' 21 | 22 | done_testing 3 23 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/lib/foo/bar.bash: -------------------------------------------------------------------------------- 1 | Foo__Bar_VERSION='1.2.3' 2 | 3 | Foo::Bar:baz() { :;} 4 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/lib/foo/foo.bash: -------------------------------------------------------------------------------- 1 | Foo::Foo:import() { 2 | echo $1---$2 3 | } 4 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/setup: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | #------------------------------------------------------------------------------ 4 | # This is a tiny version of test-more-bash that I use here. test-more-bash uses 5 | # bash+, so I want to avoid the circular dependency. This little guy does 6 | # 80-90% what test-more-bash does, with minimal code. It's a good example of 7 | # how nice Bash can be. 8 | #------------------------------------------------------------------------------ 9 | 10 | set -e -u -o pipefail 11 | [[ $BASH_VERSION == 4.0* ]] && set +u 12 | 13 | run=0 14 | 15 | plan() { 16 | echo "1..$1" 17 | } 18 | 19 | pass() { 20 | (( ++run )) 21 | echo "ok $run${1:+ - $1}" 22 | } 23 | 24 | fail() { 25 | (( ++run )) 26 | echo "not ok $run${1:+ - $1}" 27 | } 28 | 29 | is() { 30 | if [[ $1 == "$2" ]]; then 31 | pass "$3" 32 | else 33 | fail "$3" 34 | diag "Got: $1" 35 | diag "Want: $2" 36 | fi 37 | } 38 | 39 | ok() { 40 | if (exit "${1:-$?}"); then 41 | pass "$2" 42 | else 43 | fail "$2" 44 | fi 45 | } 46 | 47 | like() { 48 | if [[ $1 =~ $2 ]]; then 49 | pass "$3" 50 | else 51 | fail "$3" 52 | diag "Got: $1" 53 | diag "Like: $2" 54 | fi 55 | } 56 | 57 | unlike() { 58 | if [[ ! $1 =~ $2 ]]; then 59 | pass "$3" 60 | else 61 | fail "$3" 62 | diag "Got: $1" 63 | diag "Dont: $2" 64 | fi 65 | } 66 | 67 | done_testing() { 68 | echo "1..${1:-$run}" 69 | } 70 | 71 | diag() { 72 | echo "# ${1//$'\n'/$'\n'# }" >&2 73 | } 74 | 75 | note() { 76 | echo "# ${1//$'\n'/$'\n'# }" 77 | } 78 | 79 | #! vim: ft=sh sw=2: 80 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/shellcheck.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | PATH=$PWD/bin:$PATH 6 | source bash+ 7 | 8 | if ! command -v shellcheck >/dev/null; then 9 | plan skip_all "The 'shellcheck' utility is not installed" 10 | fi 11 | if [[ ! $(shellcheck --version) =~ 0\.7\.1 ]]; then 12 | plan skip_all "This test wants shellcheck version 0.7.1" 13 | fi 14 | 15 | IFS=$'\n' read -d '' -r -a shell_files <<< "$( 16 | find bin -type f 17 | find lib -type f 18 | echo test/setup 19 | find test -name '*.t' 20 | )" || true 21 | 22 | skips=( 23 | # We want to keep these 2 here always: 24 | SC1090 # Can't follow non-constant source. Use a directive to specify location. 25 | SC1091 # Not following: bash+ was not specified as input (see shellcheck -x). 26 | ) 27 | 28 | skip=$(IFS=,; echo "${skips[*]}") 29 | 30 | for file in "${shell_files[@]}"; do 31 | [[ $file == *swp ]] && continue 32 | is "$(shellcheck -e "$skip" "$file")" "" \ 33 | "The shell file '$file' passes shellcheck" 34 | done 35 | 36 | done_testing 37 | 38 | # vim: set ft=sh: 39 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/source-bash+-std.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | PATH=$PWD/bin:$PATH 6 | source bash+ :std 7 | 8 | ok "$(bash+:can use)" 'use is imported' 9 | ok "$(bash+:can die)" 'die is imported' 10 | ok "$(bash+:can warn)" 'warn is imported' 11 | 12 | ok "$(! bash+:can import)" 'import is not imported' 13 | ok "$(! bash+:can main)" 'main is not imported' 14 | ok "$(! bash+:can fcopy)" 'fcopy is not imported' 15 | ok "$(! bash+:can findlib)" 'findlib is not imported' 16 | ok "$(! bash+:can can)" 'can is not imported' 17 | 18 | done_testing 8 19 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/source-bash+.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | PATH=$PWD/bin:$PATH 6 | source bash+ 7 | 8 | functions=( 9 | use 10 | import 11 | fcopy 12 | findlib 13 | die 14 | warn 15 | can 16 | ) 17 | 18 | for f in "${functions[@]}"; do 19 | is "$(type -t "bash+:$f")" function \ 20 | "bash+:$f is a function" 21 | done 22 | 23 | done_testing 7 24 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/bashplus/test/use.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | PATH=$PWD/bin:$PATH 6 | source bash+ :std can 7 | 8 | # shellcheck disable=2034 9 | BASHLIB=test/lib 10 | 11 | use Foo::Bar 12 | 13 | ok $? 'use Foo::Bar - works' 14 | ok "$(can Foo::Bar:baz)" 'Function Foo::Bar:baz exists' 15 | 16 | # shellcheck disable=2016,2154 17 | is "$Foo__Bar_VERSION" 1.2.3 '$Foo__Bar_VERSION == 1.2.3' 18 | 19 | output=$(use Foo::Foo Boo Booo) 20 | ok $? 'use Foo::Foo Boo Booo - works' 21 | is "$output" Boo---Booo 'Correct import called' 22 | 23 | done_testing 5 24 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/.gitrepo: -------------------------------------------------------------------------------- 1 | ; DO NOT EDIT (unless you know what you are doing) 2 | ; 3 | ; This subdirectory is a git "subrepo", and this file is maintained by the 4 | ; git-subrepo command. See https://github.com/git-commands/git-subrepo#readme 5 | ; 6 | [subrepo] 7 | remote = git@github.com:ingydotnet/test-tap-bash.git 8 | branch = master 9 | commit = a9b33446848440a445f664a942f676d726788eff 10 | parent = 24ba0cb0c97d2591cfc2c106a70b1d1f8c559e01 11 | cmdver = 0.4.1 12 | method = merge 13 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/.travis.yml: -------------------------------------------------------------------------------- 1 | # C language gives closest shell env. 2 | language: c 3 | 4 | script: 5 | - make test PROVEOPT=-v 6 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/Changes: -------------------------------------------------------------------------------- 1 | --- 2 | version: 0.0.6 3 | date: Tue 03 Nov 2020 05:20:54 PM EST 4 | changes: 5 | - Workaround for bash 4.0 bug 6 | - Docker testing for all bash versions 7 | --- 8 | version: 0.0.5 9 | date: Tue 03 Nov 2020 01:15:41 PM EST 10 | changes: 11 | - Use more modern Bash idioms 12 | --- 13 | version: 0.0.4 14 | date: Sat Jan 23 16:32:22 PST 2016 15 | changes: 16 | - Make up to date. 17 | --- 18 | version: 0.0.3 19 | date: Fri Jan 23 21:39:39 PST 2015 20 | changes: 21 | - Make up to date. 22 | --- 23 | version: 0.0.1 24 | date: Sun Oct 27 23:02:11 PDT 2013 25 | changes: 26 | - First release. 27 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/License: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright © 2013-2020. Ingy döt Net. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the ‘Software’), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/Makefile: -------------------------------------------------------------------------------- 1 | SHELL := bash 2 | 3 | ifeq ($(MAKECMDGOALS),install) 4 | ifeq "$(shell bpan version 2>/dev/null)" "" 5 | $(error 'BPAN not installed. See http://bpan.org') 6 | endif 7 | endif 8 | 9 | NAME := test-tap 10 | LIB := lib/test/tap.bash 11 | MAN3 := man/man3 12 | 13 | INSTALL_LIB ?= $(shell bpan env BPAN_LIB) 14 | INSTALL_DIR ?= test 15 | INSTALL_MAN3 ?= $(shell bpan env BPAN_MAN3) 16 | 17 | DOCKER_IMAGE := ingy/bash-testing:0.0.1 18 | 19 | default: help 20 | 21 | help: 22 | @echo 'Rules: test, install, doc' 23 | 24 | .PHONY: test 25 | test: 26 | prove $(PROVEOPT:%=% )test/ 27 | 28 | test-all: test docker-test 29 | 30 | docker-test: 31 | -$(call docker-make-test,3.2) 32 | -$(call docker-make-test,4.0) 33 | -$(call docker-make-test,4.1) 34 | -$(call docker-make-test,4.2) 35 | -$(call docker-make-test,4.3) 36 | -$(call docker-make-test,4.4) 37 | -$(call docker-make-test,5.0) 38 | -$(call docker-make-test,5.1) 39 | 40 | install: 41 | install -C -d -m 0755 $(INSTALL_LIB)/$(INSTALL_DIR)/ 42 | install -C -m 0755 $(LIB) $(INSTALL_LIB)/$(INSTALL_DIR)/ 43 | install -C -d -m 0755 $(INSTALL_MAN3)/ 44 | install -C -m 0644 $(MAN3)/$(NAME).3 $(INSTALL_MAN3)/ 45 | 46 | .PHONY: doc 47 | doc: ReadMe.pod $(MAN3)/$(NAME).3 48 | 49 | ReadMe.pod: doc/test-tap.swim 50 | swim --to=pod --complete --wrap $< > $@ 51 | 52 | man/man3/%.3: doc/%.swim 53 | swim --to=man $< > $@ 54 | 55 | define docker-make-test 56 | docker run -i -t --rm \ 57 | -v $(PWD):/git-subrepo \ 58 | -w /git-subrepo \ 59 | $(DOCKER_IMAGE) \ 60 | /bin/bash -c ' \ 61 | set -x && \ 62 | [[ -d /bash-$(1) ]] && \ 63 | export PATH=/bash-$(1)/bin:$$PATH && \ 64 | bash --version && \ 65 | make test \ 66 | ' 67 | endef 68 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/Meta: -------------------------------------------------------------------------------- 1 | =meta: 0.0.2 2 | 3 | name: test-tap 4 | version: 0.0.6 5 | abstract: TAP Test Base for Bash 6 | homepage: http://bpan.org/package/test-tap/ 7 | 8 | license: MIT 9 | copyright: 2013-2020 10 | author: 11 | name: Ingy döt Net 12 | email: ingy@ingy.net 13 | github: ingydotnet 14 | twitter: ingydotnet 15 | freenode: ingy 16 | homepage: http://ingy.net 17 | 18 | requires: 19 | bash: 3.2.57 20 | test: 21 | cmd: make test 22 | install: 23 | cmd: make install 24 | 25 | devel: 26 | git: git@github.org/ingydotnet/test-tap-bash.git 27 | irc: irc.freenode.net/bpan 28 | bug: https://github.com/ingydotnet/test-tap-bash/issues/ 29 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/ReadMe.pod: -------------------------------------------------------------------------------- 1 | =pod 2 | 3 | =for comment 4 | DO NOT EDIT. This Pod was generated by Swim v0.1.48. 5 | See http://github.com/ingydotnet/swim-pm#readme 6 | 7 | =encoding utf8 8 | 9 | =head1 Name 10 | 11 | Test::Tap - TAP Test Base for Bash 12 | 13 | =for html 14 | test-tap-bash 15 | 16 | =head1 Synopsis 17 | 18 | source test/tap.bash 19 | 20 | Test::Tap:plan tests 1 21 | 22 | pass 'Everything is OK!' 23 | 24 | =head1 Description 25 | 26 | This is a TAP testing base class for Bash. It has all the basic TAP functions, 27 | and works properly from a TAP harness, like the C utility. 28 | 29 | test-tap-bash is used as the base for test-more-bash, which is what you want 30 | if you are writing tests in bash. 31 | 32 | See: L 33 | 34 | =head1 Functions 35 | 36 | =over 37 | 38 | =item C 39 | 40 | Must be called first for every test file/process. 41 | 42 | =item C 43 | 44 | Used to set the plan. 45 | 46 | =back 47 | 48 | =head1 TODO 49 | 50 | =over 51 | 52 | =item * Finish this doc. 53 | 54 | =back 55 | 56 | =head1 Author 57 | 58 | Written by Ingy döt Net 59 | 60 | =head1 Copyright & License 61 | 62 | Copyright 2013-2020. Ingy döt Net. 63 | 64 | The MIT License (MIT). 65 | 66 | =cut 67 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/doc/test-tap.swim: -------------------------------------------------------------------------------- 1 | Test::Tap 2 | ========= 3 | 4 | TAP Test Base for Bash 5 | 6 | 7 | 8 | = Synopsis 9 | 10 | source test/tap.bash 11 | 12 | Test::Tap:plan tests 1 13 | 14 | pass 'Everything is OK!' 15 | 16 | = Description 17 | 18 | This is a TAP testing base class for Bash. It has all the basic TAP functions, 19 | and works properly from a TAP harness, like the `prove` utility. 20 | 21 | test-tap-bash is used as the base for test-more-bash, which is what you want 22 | if you are writing tests in bash. 23 | 24 | See: https://github.com/ingydotnet/test-more-bash/ 25 | 26 | = Functions 27 | 28 | - `Test::Tap:init` 29 | 30 | Must be called first for every test file/process. 31 | 32 | - `Test::Tap::plan` 33 | 34 | Used to set the plan. 35 | 36 | = TODO 37 | 38 | * Finish this doc. 39 | 40 | = Author 41 | 42 | Written by Ingy döt Net 43 | 44 | = Copyright & License 45 | 46 | Copyright 2013-2020. Ingy döt Net. 47 | 48 | The MIT License (MIT). 49 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/man/man3/test-tap.3: -------------------------------------------------------------------------------- 1 | .\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) 2 | .\" 3 | .\" Standard preamble: 4 | .\" ======================================================================== 5 | .de Sp \" Vertical space (when we can't use .PP) 6 | .if t .sp .5v 7 | .if n .sp 8 | .. 9 | .de Vb \" Begin verbatim text 10 | .ft CW 11 | .nf 12 | .ne \\$1 13 | .. 14 | .de Ve \" End verbatim text 15 | .ft R 16 | .fi 17 | .. 18 | .\" Set up some character translations and predefined strings. \*(-- will 19 | .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left 20 | .\" double quote, and \*(R" will give a right double quote. \*(C+ will 21 | .\" give a nicer C++. Capital omega is used to do unbreakable dashes and 22 | .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, 23 | .\" nothing in troff, for use with C<>. 24 | .tr \(*W- 25 | .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' 26 | .ie n \{\ 27 | . ds -- \(*W- 28 | . ds PI pi 29 | . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch 30 | . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch 31 | . ds L" "" 32 | . ds R" "" 33 | . ds C` "" 34 | . ds C' "" 35 | 'br\} 36 | .el\{\ 37 | . ds -- \|\(em\| 38 | . ds PI \(*p 39 | . ds L" `` 40 | . ds R" '' 41 | . ds C` 42 | . ds C' 43 | 'br\} 44 | .\" 45 | .\" Escape single quotes in literal strings from groff's Unicode transform. 46 | .ie \n(.g .ds Aq \(aq 47 | .el .ds Aq ' 48 | .\" 49 | .\" If the F register is >0, we'll generate index entries on stderr for 50 | .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index 51 | .\" entries marked with X<> in POD. Of course, you'll have to process the 52 | .\" output yourself in some meaningful fashion. 53 | .\" 54 | .\" Avoid warning from groff about undefined register 'F'. 55 | .de IX 56 | .. 57 | .nr rF 0 58 | .if \n(.g .if rF .nr rF 1 59 | .if (\n(rF:(\n(.g==0)) \{\ 60 | . if \nF \{\ 61 | . de IX 62 | . tm Index:\\$1\t\\n%\t"\\$2" 63 | .. 64 | . if !\nF==2 \{\ 65 | . nr % 0 66 | . nr F 2 67 | . \} 68 | . \} 69 | .\} 70 | .rr rF 71 | .\" ======================================================================== 72 | .\" 73 | .IX Title "STDIN 1" 74 | .TH STDIN 1 "November 2020" "Generated by Swim v0.1.48" "\s-1TAP\s0 Test Base for Bash" 75 | .\" For nroff, turn off justification. Always turn off hyphenation; it makes 76 | .\" way too many mistakes in technical documents. 77 | .if n .ad l 78 | .nh 79 | .SH "Name" 80 | .IX Header "Name" 81 | Test::Tap \- \s-1TAP\s0 Test Base for Bash 82 | .SH "Synopsis" 83 | .IX Header "Synopsis" 84 | .Vb 1 85 | \& source test/tap.bash 86 | \& 87 | \& Test::Tap:plan tests 1 88 | \& 89 | \& pass \*(AqEverything is OK!\*(Aq 90 | .Ve 91 | .SH "Description" 92 | .IX Header "Description" 93 | 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. 94 | .PP 95 | test-tap-bash is used as the base for test-more-bash, which is what you want if you are writing tests in bash. 96 | .PP 97 | See: 98 | .SH "Functions" 99 | .IX Header "Functions" 100 | .ie n .IP """Test::Tap:init""" 4 101 | .el .IP "\f(CWTest::Tap:init\fR" 4 102 | .IX Item "Test::Tap:init" 103 | Must be called first for every test file/process. 104 | .ie n .IP """Test::Tap::plan""" 4 105 | .el .IP "\f(CWTest::Tap::plan\fR" 4 106 | .IX Item "Test::Tap::plan" 107 | Used to set the plan. 108 | .SH "TODO" 109 | .IX Header "TODO" 110 | .IP "\(bu" 4 111 | Finish this doc. 112 | .SH "Author" 113 | .IX Header "Author" 114 | Written by Ingy döt Net 115 | .SH "Copyright & License" 116 | .IX Header "Copyright & License" 117 | Copyright 2013\-2020. Ingy döt Net. 118 | .PP 119 | The \s-1MIT\s0 License (\s-1MIT\s0). 120 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/bail_out.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/helper.bash 4 | source lib/test/tap.bash 5 | 6 | Test::Tap:init tests 1 7 | 8 | output=$(prove -v test/test/{b,f}ail.t 2>&1) || true 9 | 10 | test-helper:like \ 11 | "$output" \ 12 | 'Bailout called. Further testing stopped: Get me outta here' \ 13 | 'Test::Tap:BAIL_OUT works' 14 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/done.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init 6 | 7 | Test::Tap:pass one 8 | Test::Tap:pass two 9 | 10 | Test::Tap:done_testing 11 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/fail.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/helper.bash 4 | source lib/test/tap.bash 5 | 6 | Test::Tap:init tests 2 7 | 8 | output=$(prove -v test/test/fail.t 2>&1) || true 9 | 10 | # echo "# >>>${output//$'\n'/$'\n'# }<<<" >&2 11 | 12 | test-helper:like \ 13 | "$output" \ 14 | 'not ok 1 - I am a failure' \ 15 | 'Test::Tap:fail works' 16 | 17 | test-helper:like \ 18 | "$output" \ 19 | 'Failed 1/1 subtests' \ 20 | 'Proper summary' 21 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/fail_fast.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/helper.bash 4 | source lib/test/tap.bash 5 | 6 | Test::Tap:init tests 1 7 | 8 | output=$(prove -v test/test/fail_fast.t 2>&1) || true 9 | 10 | # echo ">>>$output<<<" >&2 11 | 12 | test-helper:like \ 13 | "$output" \ 14 | 'Further testing stopped: Bailing out on status=1' \ 15 | 'Test::Tap:BAIL_OUT works' 16 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/helper.bash: -------------------------------------------------------------------------------- 1 | test-helper:like() { 2 | local got=$1 regex=$2 label=$3 3 | if [[ $got =~ $regex ]]; then 4 | Test::Tap:pass "$label" 5 | else 6 | Test::Tap:fail "$label" 7 | Test::Tap:diag "Got: '$got'" 8 | fi 9 | } 10 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/pass.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init tests 3 6 | 7 | Test::Tap:pass 'pass 1 - with label' 8 | Test::Tap:pass 9 | Test::Tap:pass 'pass 3 - 2 has no label' 10 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/plan.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init 6 | Test::Tap:plan tests 3 7 | 8 | for n in 1 2 3; do 9 | Test::Tap:pass "Test #$n" 10 | done 11 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/shellcheck.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | if ! command -v shellcheck >/dev/null; then 6 | Test::Tap:init skip_all "The 'shellcheck' utility is not installed" 7 | fi 8 | if [[ ! $(shellcheck --version) =~ 0\.7\.1 ]]; then 9 | Test::Tap:init skip_all "This test wants shellcheck version 0.7.1" 10 | fi 11 | 12 | Test::Tap:init 13 | 14 | IFS=$'\n' read -d '' -r -a shell_files <<< "$( 15 | find lib -type f 16 | echo test/helper.bash 17 | find test -name '*.t' 18 | )" || true 19 | 20 | skips=( 21 | # We want to keep these 2 here always: 22 | SC1090 # Can't follow non-constant source. Use a directive to specify location. 23 | SC1091 # Not following: bash+ was not specified as input (see shellcheck -x). 24 | ) 25 | 26 | skip=$(IFS=,; echo "${skips[*]}") 27 | 28 | for file in "${shell_files[@]}"; do 29 | [[ $file == *swp ]] && continue 30 | label="The shell file '$file' passes shellcheck" 31 | got=$(shellcheck -e "$skip" "$file" 2>&1) || true 32 | if [[ -z $got ]]; then 33 | Test::Tap:pass "$label" 34 | else 35 | Test::Tap:fail "$label" 36 | Test::Tap:diag "$got" 37 | fi 38 | done 39 | 40 | Test::Tap:done_testing 41 | 42 | # vim: set ft=sh: 43 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/skip_all.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/helper.bash 4 | source lib/test/tap.bash 5 | 6 | Test::Tap:init tests 4 7 | 8 | for s in plan init; do 9 | output=$(prove test/test/skip-all-$s.t) 10 | 11 | test-helper:like \ 12 | "$output" \ 13 | "skipped: Test for skip_all from $s" \ 14 | "skip_all from $s: it works" 15 | 16 | test-helper:like \ 17 | "$output" \ 18 | 'Result: NOTESTS' \ 19 | "skip_all from $s: No tests run" 20 | done 21 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/tap.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init tests 3 # 4 6 | 7 | Test::Tap:pass 'pass with label' 8 | Test::Tap:pass 9 | Test::Tap:pass 'previous test has no label' 10 | 11 | # TODO this test no longer working: 12 | # msg=$(Test::Tap:fail 'faaaaailll' 2>/dev/null) || true 13 | # 14 | # if [[ $msg =~ not\ ok\ 4\ -\ faaaaailll ]]; then 15 | # Test::Tap:pass 'fail works' 16 | # else 17 | # Test::Tap:fail 'fail works' 18 | # Test::Tap:diag "$msg" 19 | # fi 20 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/test/bail.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init tests 5 6 | 7 | Test::Tap:pass 'test #1' 8 | Test::Tap:pass 'test #2' 9 | Test::Tap:pass 'test #3' 10 | 11 | Test::Tap:BAIL_OUT 'Get me outta here' 12 | 13 | Test::Tap:pass 'test #4' 14 | Test::Tap:fail 'test #5' 15 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/test/fail.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init tests 1 6 | 7 | Test::Tap:fail 'I am a failure' 8 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/test/fail_fast.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init tests 5 6 | Test::Tap:BAIL_ON_FAIL 7 | 8 | Test::Tap:pass 'test #1' 9 | Test::Tap:pass 'test #2' 10 | Test::Tap:fail 'test #3' 11 | Test::Tap:pass 'test #4' 12 | Test::Tap:pass 'test #5' 13 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/test/skip-all-init.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init skip_all 'Test for skip_all from init' 6 | 7 | Test::Tap:diag "This code should not be run" 8 | Test::Tap:fail 9 | -------------------------------------------------------------------------------- /ext/test-more-bash/ext/test-tap-bash/test/test/skip-all-plan.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source lib/test/tap.bash 4 | 5 | Test::Tap:init 6 | Test::Tap:plan skip_all 'Test for skip_all from plan' 7 | 8 | Test::Tap:diag "This code should not be run" 9 | Test::Tap:fail 10 | -------------------------------------------------------------------------------- /ext/test-more-bash/lib/test/more.bash: -------------------------------------------------------------------------------- 1 | # test/more.bash - Complete TAP test framework for Bash 2 | # 3 | # Copyright (c) 2013-2020. Ingy döt Net. 4 | 5 | set -e -u -o pipefail 6 | 7 | # shellcheck disable=2034 8 | Test__More_VERSION=0.0.5 9 | 10 | source bash+ :std version-check 11 | 12 | version-check bash 3.2 || 13 | die "test-more-bash requires bash 3.2+" 14 | 15 | use Test::Tap 16 | 17 | Test::More:import() { Test::Tap:init "$@"; } 18 | 19 | plan() { Test::Tap:plan "$@"; } 20 | pass() { Test::Tap:pass "$@"; } 21 | fail() { Test::Tap:fail "$@"; } 22 | diag() { Test::Tap:diag "$@"; } 23 | note() { Test::Tap:note "$@"; } 24 | done_testing() { Test::Tap:done_testing "$@"; } 25 | BAIL_OUT() { Test::Tap:BAIL_OUT "$@"; } 26 | BAIL_ON_FAIL() { Test::Tap:BAIL_ON_FAIL "$@"; } 27 | 28 | is() { 29 | local got=$1 want=$2 label=${3-} 30 | if [[ $got == "$want" ]]; then 31 | Test::Tap:pass "$label" 32 | else 33 | Test::Tap:fail "$label" Test::More:is-fail 34 | fi 35 | } 36 | 37 | Test::More:is-fail() { 38 | local Test__Tap_CALL_STACK_LEVEL= 39 | Test__Tap_CALL_STACK_LEVEL=$(( Test__Tap_CALL_STACK_LEVEL + 1 )) 40 | if [[ $want =~ $'\n' ]]; then 41 | echo "$got" > /tmp/got-$$ 42 | echo "$want" > /tmp/want-$$ 43 | diff -u /tmp/{want,got}-$$ >&2 || true 44 | wc /tmp/{want,got}-$$ >&2 45 | rm -f /tmp/{got,want}-$$ 46 | else 47 | Test::Tap:diag "\ 48 | got: '$got' 49 | expected: '$want'" 50 | fi 51 | } 52 | 53 | isnt() { 54 | local Test__Tap_CALL_STACK_LEVEL= 55 | Test__Tap_CALL_STACK_LEVEL=$(( Test__Tap_CALL_STACK_LEVEL + 1 )) 56 | local got=$1 dontwant=$2 label=${3-} 57 | if [[ $got != "$dontwant" ]]; then 58 | Test::Tap:pass "$label" 59 | else 60 | Test::Tap:fail "$label" Test::More:isnt-fail 61 | fi 62 | } 63 | 64 | Test::More:isnt-fail() { 65 | Test::Tap:diag "\ 66 | got: '$got' 67 | expected: anything else" 68 | } 69 | 70 | ok() { 71 | if (exit "${1:-$?}"); then 72 | Test::Tap:pass "${2-}" 73 | else 74 | Test::Tap:fail "${2-}" 75 | fi 76 | } 77 | 78 | like() { 79 | local got=$1 regex=$2 label=${3-} 80 | if [[ $got =~ $regex ]]; then 81 | Test::Tap:pass "$label" 82 | else 83 | Test::Tap:fail "$label" Test::More:like-fail 84 | fi 85 | } 86 | 87 | Test::More:like-fail() { 88 | Test::Tap:diag "Got: '$got'" 89 | } 90 | 91 | unlike() { 92 | local got=$1 regex=$2 label=${3-} 93 | if [[ ! $got =~ $regex ]]; then 94 | Test::Tap:pass "$label" 95 | else 96 | Test::Tap:fail "$label" Test::More:unlike-fail 97 | fi 98 | } 99 | 100 | Test::More:unlike-fail() { 101 | Test::Tap:diag "Got: '$got'" 102 | } 103 | 104 | cmp-array() { 105 | local arrayname="$1[@]" 106 | local expname="$2[@]" 107 | local label=${3-} 108 | 109 | local array=("${!arrayname}") 110 | local expected=("${!expname}") 111 | 112 | is "$(printf "%s\n" "${array[@]}")" \ 113 | "$(printf "%s\n" "${expected[@]}")" \ 114 | "$label" 115 | } 116 | -------------------------------------------------------------------------------- /ext/test-more-bash/test/fail.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | use Test::More 6 | 7 | output=$(prove -v test/test/fail1.t 2>&1) || true 8 | 9 | like "$output" 'not ok 1 - fail with label' \ 10 | 'fail with label' 11 | like "$output" 'not ok 2' \ 12 | 'fail with no label' 13 | like "$output" 'not ok 3 - is foo bar' \ 14 | 'fail output is correct' 15 | like "$output" 'not ok 4 - command output more' \ 16 | 'fail output is correct' 17 | like "$output" 'not ok 5 - command output less' \ 18 | 'fail output is correct' 19 | like "$output" 'not ok 6 - command output diff' \ 20 | 'fail output is correct' 21 | like "$output" "# got: 'foo'" \ 22 | 'difference reporting - got' 23 | like "$output" "# expected: 'bar'" \ 24 | 'difference reporting - want' 25 | 26 | like "$output" "line2. *\+line3." \ 27 | 'array comparison (more)' 28 | like "$output" "line1. *-line2." \ 29 | 'array comparison (less)' 30 | like "$output" "-line2.*\+foo" \ 31 | 'array comparison (diff)' 32 | 33 | 34 | done_testing 11 35 | -------------------------------------------------------------------------------- /ext/test-more-bash/test/more.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # shellcheck disable=2034 4 | 5 | source test/setup 6 | use Test::More 7 | 8 | plan tests 6 9 | 10 | pass 'This test always passes' 11 | 12 | is 'foo' "foo" 'foo is foo' 13 | 14 | ok "$(true)" 'true is true' 15 | 16 | ok "$([ 123 -eq "$((61+62))" ])" 'Math works' 17 | 18 | # shellcheck disable=2050 19 | ok "$([[ ! team =~ I ]])" "There's no I in team" 20 | 21 | # diag "A msg for stderr" 22 | 23 | note "A msg for stdout" 24 | 25 | expected=(line1 line2) 26 | 27 | command_output=(line1 line2 ) 28 | cmp-array command_output expected "command output more" 29 | -------------------------------------------------------------------------------- /ext/test-more-bash/test/pass.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | use Test::More tests 3 6 | 7 | pass 'pass 1 - with label' 8 | pass 9 | pass 'pass 3 - 2 has no label' 10 | -------------------------------------------------------------------------------- /ext/test-more-bash/test/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e -u -o pipefail 4 | 5 | BASHLIB=$(find "$PWD" -type d | grep -E '/(bin|lib)$' | xargs -n1 printf "%s:") 6 | PATH=$BASHLIB:$PATH 7 | 8 | source bash+ :std 9 | -------------------------------------------------------------------------------- /ext/test-more-bash/test/shellcheck.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | use Test::More 5 | 6 | if ! command -v shellcheck >/dev/null; then 7 | plan skip_all "The 'shellcheck' utility is not installed" 8 | fi 9 | if [[ ! $(shellcheck --version) =~ 0\.7\.1 ]]; then 10 | plan skip_all "This test wants shellcheck version 0.7.1" 11 | fi 12 | 13 | IFS=$'\n' read -d '' -r -a shell_files <<< "$( 14 | find lib -type f 15 | echo test/setup 16 | find test -name '*.t' 17 | )" || true 18 | 19 | skips=( 20 | # We want to keep these 2 here always: 21 | SC1090 # Can't follow non-constant source. Use a directive to specify location. 22 | SC1091 # Not following: bash+ was not specified as input (see shellcheck -x). 23 | ) 24 | 25 | skip=$(IFS=,; echo "${skips[*]}") 26 | 27 | for file in "${shell_files[@]}"; do 28 | [[ $file == *swp ]] && continue 29 | is "$(shellcheck -e "$skip" "$file")" "" \ 30 | "The shell file '$file' passes shellcheck" 31 | done 32 | 33 | done_testing 34 | 35 | # vim: set ft=sh: 36 | -------------------------------------------------------------------------------- /ext/test-more-bash/test/skip_all.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | use Test::More 5 | 6 | output=$(prove -v test/test/skip_all.t 2>&1) || true 7 | 8 | like "$output" 'skipped: Skipping this test to demo skip_all' \ 9 | 'skip_all works' 10 | 11 | done_testing 1 12 | -------------------------------------------------------------------------------- /ext/test-more-bash/test/test/fail1.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # shellcheck disable=2034 4 | 5 | source test/setup 6 | use Test::More 7 | 8 | fail 'fail with label' 9 | 10 | fail 11 | 12 | is foo bar 'is foo bar' 13 | 14 | expected=(line1 line2) 15 | 16 | command_output=(line1 line2 line3) 17 | cmp-array command_output expected "command output more" 18 | 19 | command_output=(line1) 20 | cmp-array command_output expected "command output less" 21 | 22 | command_output=(line1 foo) 23 | cmp-array command_output expected "command output diff" 24 | 25 | done_testing 6 26 | -------------------------------------------------------------------------------- /ext/test-more-bash/test/test/skip_all.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | use Test::More 5 | 6 | plan skip_all 'Skipping this test to demo skip_all' 7 | 8 | fail "Don't run this code" 9 | 10 | done_testing 11 | -------------------------------------------------------------------------------- /lib/git-subrepo.d/bash+.bash: -------------------------------------------------------------------------------- 1 | ../../ext/bashplus/lib/bash+.bash -------------------------------------------------------------------------------- /note/0.4.0: -------------------------------------------------------------------------------- 1 | - Update docs 2 | - Review issue/142 3 | - Compare operations to master 4 | - Add a --no-push or --dry-run flag for push 5 | - Add a --rebase option for push 6 | - Add --message= and --edit to the init command ?? 7 | - Also add to doc 8 | - Update doc for steps of pull command to include merge and rebase 9 | - Test squashing on pull operations 10 | - Update push doc after understanding it more 11 | - Is --rebase for pull or push? 12 | - Make sure docs are correct too. 13 | -------------------------------------------------------------------------------- /note/AllGitCmds: -------------------------------------------------------------------------------- 1 | add 2 | add--interactive 3 | am 4 | annotate 5 | apply 6 | archive 7 | bisect 8 | bisect--helper 9 | blame 10 | branch 11 | bundle 12 | cat-file 13 | check-attr 14 | check-ref-format 15 | checkout 16 | checkout-index 17 | cherry 18 | cherry-pick 19 | clean 20 | clone 21 | commit 22 | commit-tree 23 | config 24 | count-objects 25 | credential-cache 26 | credential-cache--daemon 27 | credential-store 28 | daemon 29 | describe 30 | diff 31 | diff-files 32 | diff-index 33 | diff-tree 34 | difftool 35 | difftool--helper 36 | fast-export 37 | fast-import 38 | fetch 39 | fetch-pack 40 | filter-branch 41 | fmt-merge-msg 42 | for-each-ref 43 | format-patch 44 | fsck 45 | fsck-objects 46 | gc 47 | get-tar-commit-id 48 | grep 49 | hash-object 50 | help 51 | http-backend 52 | http-fetch 53 | http-push 54 | hub 55 | imap-send 56 | index-pack 57 | init 58 | init-db 59 | instaweb 60 | log 61 | lost-found 62 | ls-files 63 | ls-remote 64 | ls-tree 65 | mailinfo 66 | mailsplit 67 | merge 68 | merge-base 69 | merge-file 70 | merge-index 71 | merge-octopus 72 | merge-one-file 73 | merge-ours 74 | merge-recursive 75 | merge-resolve 76 | merge-subtree 77 | merge-tree 78 | mergetool 79 | mktag 80 | mktree 81 | mv 82 | name-rev 83 | notes 84 | pack-objects 85 | pack-redundant 86 | pack-refs 87 | patch-id 88 | peek-remote 89 | prune 90 | prune-packed 91 | pull 92 | push 93 | quiltimport 94 | read-tree 95 | rebase 96 | receive-pack 97 | reflog 98 | relink 99 | remote 100 | remote-ext 101 | remote-fd 102 | remote-ftp 103 | remote-ftps 104 | remote-http 105 | remote-https 106 | remote-testgit 107 | remove-submodule 108 | repack 109 | replace 110 | repo-config 111 | request-pull 112 | rerere 113 | reset 114 | rev-list 115 | rev-parse 116 | revert 117 | rm 118 | send-pack 119 | sh-i18n--envsubst 120 | shell 121 | shortlog 122 | show 123 | show-branch 124 | show-index 125 | show-ref 126 | stage 127 | stash 128 | status 129 | stripspace 130 | submodule 131 | subrepo 132 | subtree 133 | symbolic-ref 134 | tag 135 | tar-tree 136 | unpack-file 137 | unpack-objects 138 | update-index 139 | update-ref 140 | update-server-info 141 | upload-archive 142 | upload-pack 143 | var 144 | verify-pack 145 | verify-tag 146 | web--browse 147 | whatchanged 148 | write-tree 149 | -------------------------------------------------------------------------------- /note/Cases: -------------------------------------------------------------------------------- 1 | == Users 2 | 3 | - art — project lead 4 | - bob — end user 5 | - cab — collab dev (push rights) 6 | - dim — forker dev (no push rights) 7 | - ell — third party repo owner 8 | 9 | == Repos 10 | - art/gallery — art's main repo 11 | - art/painting — subrepo owned by art 12 | - ell/tickets — subrepo owned by ell 13 | 14 | == Starting histories: 15 | - art/gallery — A---B 16 | - art/painting — P---Q 17 | - ell/tickets — T 18 | 19 | == Scenario 1 20 | art art/gallery$ git log 21 | A---B 22 | art art/gallery$ git subrepo clone art/painting 23 | art art/gallery$ ls 24 | painting/ 25 | art art/gallery$ git log 26 | A---B---C 27 | / 28 | Q' 29 | art art/gallery$ 30 | -------------------------------------------------------------------------------- /note/Commands: -------------------------------------------------------------------------------- 1 | $$$ for cmd in `cat AllGitCmds`; do git help $cmd; done 2 | 3 | 4 | == Inteesting Plumbing Commands: 5 | - git ls-tree HEAD 6 | - git cat-file -t 7 | - git cat-file commit 8 | - git cat-file -p 9 | - git ls-tree 10 | - git ls-files --stage 11 | - git write-tree 12 | - git update-ref refs/heads/master 13 | - git symbolic-ref HEAD refs/heads/master 14 | - git show-branch 15 | - git hash-object 16 | 17 | - git rev-list --max-parents=0 HEAD — Find root commit 18 | - git cherry — find commits in one branch that are not in another 19 | - git replace 20 | 21 | == Commands with DAG diagrams in their manpages: 22 | 23 | - git-commit 24 | - git-filter-branch 25 | - git-log 26 | - git-merge 27 | - git-merge-base 28 | - git-pull 29 | - git-push 30 | - git-rebase 31 | - git-rerere 32 | - git-rev-list 33 | - git-rev-parse 34 | -------------------------------------------------------------------------------- /note/Gists: -------------------------------------------------------------------------------- 1 | - https://gist.github.com/kentfredric/9285653 2 | 3 | - https://gist.github.com/anonymous/55bcf30acab99308714b 4 | # a + b = o 5 | - https://gist.github.com/anonymous/0544b465c57f01f8c4a6 6 | - https://gist.github.com/ingydotnet/bf92949734b6a23f2af6 7 | - https://gist.github.com/ingydotnet/a61b3cd1c422a17c6e5d 8 | -------------------------------------------------------------------------------- /note/Links: -------------------------------------------------------------------------------- 1 | - http://stackoverflow.com/questions/1186535/how-to-modify-a-specified-commit/ 2 | - http://git-scm.com/book/en/Git-Tools-Rewriting-History 3 | - http://git.661346.n2.nabble.com/Editing-the-root-commit-td7561714.html 4 | - http://stackoverflow.com/questions/645450/git-how-to-insert-a-commit-as-the-first-shifting-all-the-others 5 | - http://stackoverflow.com/questions/6149520/git-remove-root-commit *** 6 | - http://fourkitchens.com/blog/2009/01/19/creating-common-branch-ancestry-hard-problem 7 | - http://stackoverflow.com/questions/1488753/how-to-merge-two-branches-without-a-common-ancestor 8 | - http://sahd.lamafam.org/?p=1534 9 | 10 | - http://www.claassen.net/geek/blog/2011/02/git-merge-strategytheirs.html 11 | - sahd.lamafam.org/?p=1534 12 | - http://stackoverflow.com/questions/2945344/selecting-merge-strategy-options-for-git-rebase 13 | - http://stackoverflow.com/questions/2428137/how-to-rebase-one-git-repository-onto-another-one 14 | - http://stackoverflow.com/questions/3080509/git-list-commits-not-pushed-to-the-origin-yet 15 | - http://stackoverflow.com/questions/2263674/how-do-i-find-the-next-commit-in-git 16 | 17 | - http://git-scm.com/book/ch6-7.html (Subtree Merge) 18 | - https://www.kernel.org/pub/software/scm/git/docs/howto/using-merge-subtree.html 19 | - https://help.github.com/articles/working-with-subtree-merge 20 | - git pull -s subtree foo master 21 | 22 | - http://git-scm.com/2010/03/17/replace.html 23 | 24 | - http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html 25 | - http://ruleant.blogspot.com/2013/06/git-subtree-module-with-gittrees-config.html 26 | -------------------------------------------------------------------------------- /note/Plugins: -------------------------------------------------------------------------------- 1 | - https://github.com/nothingmuch/git-svn-abandon 2 | - https://github.com/schacon?tab=repositories 3 | - https://github.com/jbalogh/git-tools 4 | - https://github.com/wesabe/git-tools 5 | - https://github.com/timcharper/git-helpers 6 | - https://github.com/schacon/git-pulls 7 | - https://github.com/node-gh/gh 8 | - https://github.com/splitbrain/git-pull-request 9 | - https://github.com/mloughran/git-cleanup 10 | - https://github.com/seveas/git-hub 11 | -------------------------------------------------------------------------------- /note/Spec: -------------------------------------------------------------------------------- 1 | = `git-hub` Design Specification 2 | 3 | == Main Commands 4 | 5 | - `clone` 6 | 7 | git subrepo clone [] 8 | 9 | - `pull` 10 | 11 | - `push` 12 | 13 | == Worker Commands 14 | 15 | - `fetch` 16 | 17 | git subrepo fetch 18 | 19 | - `branch` 20 | 21 | - `checkout` 22 | 23 | - `commit` 24 | 25 | - `reset` 26 | 27 | == Info Commands 28 | 29 | - `status` 30 | 31 | - `log` 32 | 33 | == Other Commands 34 | 35 | - `clean` 36 | 37 | - `help` 38 | 39 | - `version` 40 | -------------------------------------------------------------------------------- /note/Story1: -------------------------------------------------------------------------------- 1 | == git subrepo clone git@genius --branch=bob ext/genius 2 | 3 | - create orphan branch repo/genius 4 | - fetch git@genius --branch=bob 5 | - create local branch repo/genius 6 | - checkout repo/genius into ext/genius 7 | - SYNC-DANCE: 8 | - add ext/genius/.gitrepo 9 | - commit the add and prune the commit 10 | - merge the pruned commit into mianline 11 | 12 | == git subrepo pull ext/genius 13 | 14 | - checkout ext/genius 15 | - fetch git@genius --branch=bob 16 | - merge/rebase FETCH_HEAD 17 | - commit if merge successful 18 | - checkout original branch 19 | - SYNC-DANCE 20 | 21 | == git subrepo branch ext/genius 22 | - filter-branch subdir ext/genius 23 | - filter-branch 'rm .gitrepo' 24 | - add parent of original 25 | - name the commit subrepo/ext/genius 26 | 27 | == git subrepo checkout ext/genius 28 | - subrepo branch ext/genius 29 | - checkout subrepo/ext/genius 30 | 31 | == git subrepo push ext/genius 32 | - subrepo branch ext/genius 33 | - checkout repo/genius 34 | - merge/rebase subrepo/ext/genius 35 | - push repo/genius git@genius/bob 36 | - checkout original 37 | 38 | 39 | = Commands = 40 | 41 | git subrepo clone git@github.com:ingydotnet/bashplus.git 42 | git subrepo clone git@github.com:ingydotnet/bashplus.git ext/bashplus 43 | git subrepo clone git@github.com:ingydotnet/bashplus.git --branch=devel 44 | git subrepo clone git@github.com:ingydotnet/bashplus.git --reclone 45 | 46 | 47 | git subrepo pull git@github.com:ingydotnet/bashplus.git 48 | git subrepo pull git@github.com:ingydotnet/bashplus.git --branch=master 49 | git subrepo pull git@github.com:ingydotnet/bashplus.git --rebase 50 | 51 | git subrepo branch ext/bashplus # create refs/heads/subrepo/ext/bashplus 52 | 53 | git subrepo checkout ext/bashplus 54 | 55 | git subrepo push ext/bashplus 56 | -------------------------------------------------------------------------------- /note/ToDo: -------------------------------------------------------------------------------- 1 | == Next 2 | - Write test for push after init 3 | - With and without --remote 4 | - With --update 5 | - Write tests for --update 6 | 7 | == Development 8 | 9 | - Add version commit id to cmdver in .gitrepo. 10 | - Update Makefile to install: 11 | - git-subrepo.d/remote 12 | - git-subrepo.d/commit 13 | 14 | + Add remote-url and remote-branch on fetch 15 | - unless config.subrepo.auto-remote-add == false 16 | - unless config.subrepo.auto-branch-add == false 17 | 18 | - Write more tests: 19 | - Make simple fixtures 20 | - Multiple push sessions 21 | - Tests for reported rebase errors and other subtree badness 22 | 23 | - Make the 'clean' command remove grafts 24 | 25 | - Resolve outstanding github issues: 26 | - Allow commands from any subdir. 27 | 28 | - Update VERSION to 0.1.1 29 | - Tag when ready. 30 | 31 | - When .gitrepo is stable: 32 | - Delete and re-clone all subrepos in all repos using them 33 | 34 | - Rewrite the Intro doc. 35 | 36 | == Promotion 37 | 38 | - Git Commands 39 | - #git-commands 40 | - github.com/git-commands 41 | - Fork projects 42 | - git-commands.hacktive.com 43 | - Make a CogWeb site 44 | 45 | - Invent a good symbolic system for dags 46 | - main and sub repo 47 | - users: owner, user, collab 48 | - commits: upstream sr head, local sr head, clone, pull, gitrepo 49 | 50 | owner/main: A-----B 51 | owner/subr: S-----T 52 | git subrepo clone owner/subr foo 53 | A-----B-----C 54 | / 55 | T'--T'+ 56 | -------------------------------------------------------------------------------- /note/design.swim: -------------------------------------------------------------------------------- 1 | git-subrepo Design 2 | ================== 3 | 4 | How the subrepo commands should work. 5 | 6 | Glossary: 7 | 8 | - `subrepo` :: An external repository integrated as a repo subdirectory 9 | - `subdir` :: The directory in the work tree where the subrepo lives 10 | - `upstream` :: The remote repo that the subrepo is tracking 11 | - `local` :: The local parts of the subrepo 12 | - `.gitrepo` :: The subrepo state file 13 | - `remote` :: The subrepo url 14 | - `branch` :: The remote branch the subrepo is tracking 15 | - `commit` :: The upstream commit id that we last synced to 16 | - `former` :: The local commit that we last synced to 17 | 18 | == clone 19 | 20 | This is the action that adds a new subrepo. It feels very much like a clone, 21 | so we call it that. 22 | 23 | Usages: 24 | 25 | # Add a subrepo into a subdir (track remote HEAD branch): 26 | git subrepo clone 27 | # Guess subdir from remote url: 28 | git subrepo clone 29 | # Use a named branch instead of the remote HEAD: 30 | git subrepo clone [] -b 31 | 32 | Steps: 33 | 34 | * Assert clean, and chdir to root (adjusting subdir) 35 | * Determine the remote HEAD 36 | * Else error, branch (-b) needed 37 | * Fetch the remote branch commits 38 | * Subtree merge the remote subrepo history into the subdir 39 | * Squash the history to a single commit 40 | * Merge the subtree commit into the mainline HEAD 41 | * Add a .gitrepo file to the subdir 42 | * Amend the .gitrepo into the HEAD merge commit 43 | * Create a branch .git/refs/subrepo/remote/ 44 | * Add a remote called subrepo/ 45 | 46 | == pull 47 | 48 | Fetch and merge the remote content. This could be a single operation, or may 49 | require a manual process: (see checkout) 50 | 51 | Usages: 52 | 53 | # Fetch and rebase subdir/subrepo: 54 | git subrepo pull --rebase subdir 55 | # Strategies: --rebase --ours --theirs --merge (recursive): 56 | git subrepo pull -- subdir 57 | # Pull all the subrepos: 58 | git subrepo pull -- --all 59 | # Change the tracking branch and pull it: 60 | git subrepo pull -- -b 61 | # Pull in the hand merged branch: 62 | # git subrepo pull 63 | 64 | Steps: 65 | 66 | * Assert clean, and chdir to root (adjusting subdir) 67 | * Fetch the remote branch content 68 | * Update remotes and the refs in .git/ 69 | * The might not be the same repo that did the clone 70 | * If not hand merged 71 | * Create a branch called subrepo/ 72 | * Checkout the new branch 73 | * Apply the merge strategy to the remote branch 74 | * If not clean 75 | * Error message 76 | * Reset to starting state 77 | * Subtree merge the subrepo/ branch into repo 78 | git subrepo clone 79 | * Squash the upstream history 80 | * Merge the subtree into mainline 81 | * Update the .gitrepo file 82 | * Amend .gitrepo into HEAD 83 | * Delete the subrepo/ branch 84 | 85 | == push 86 | 87 | Push a merged subrepo history to the remote. This could be a single operation, 88 | or may require a manual process. 89 | 90 | Usages: 91 | 92 | # Fetch, apply strategy, push to remote: 93 | git subrepo push 94 | # Do all subrepos: 95 | git subrepo push --all 96 | 97 | Steps: 98 | 99 | * Assert clean, and chdir to root (adjusting subdir) 100 | * Update remotes and the refs in .git/ 101 | * Fetch the remote branch content 102 | * Make sure we have pulled the latest remote changes 103 | * Checkout subrepo/ branch 104 | * Rebase the local change s on top 105 | * Push it to subrepo/ remote 106 | 107 | == checkout 108 | 109 | Create a branch of the local changes to the subrepo and checkout it. Also 110 | fetch the remote tracking branch, so it can can be used for merges. This 111 | command is used to do a pull/merge by hand. After you merge, use the `pull` 112 | command to finish up. 113 | 114 | Usages: 115 | 116 | # Checkout a subdir/subrepo 117 | git checkout 118 | 119 | Steps: 120 | 121 | * Assert clean, and chdir to root (adjusting subdir) 122 | * Assert repo is on a branch 123 | * Update remotes and the refs in .git/ 124 | * Fetch the remote branch content 125 | * Create a branch of local subrepo changes subrepo/local/ 126 | * Checkout the new branch 127 | 128 | == status 129 | 130 | Give a full status of specific subrepos or all of them. 131 | 132 | Usages: 133 | 134 | # Get status for all subrepos: 135 | git subrepo status 136 | # Get status for specific subrepos: 137 | git subrepo status [ ...] 138 | -------------------------------------------------------------------------------- /note/design2.swim: -------------------------------------------------------------------------------- 1 | git-subrepo Design 2 | ================== 3 | 4 | This document details wow the git-subrepo commands should work. 5 | 6 | Glossary: 7 | 8 | - `subrepo` :: An external repository integrated as a repo subdirectory 9 | - `subdir` :: The directory in the work tree where the subrepo lives 10 | - `upstream` :: The remote repo that the subrepo is tracking 11 | - `local` :: The local parts of the subrepo 12 | - `.gitrepo` :: The subrepo state file 13 | - `remote` :: The subrepo url 14 | - `branch` :: The remote branch the subrepo is tracking 15 | - `commit` :: The upstream commit id that we last synced to 16 | - `former` :: The local commit that we last synced to 17 | 18 | = Commands and usages: 19 | 20 | There are 4 main commands: 21 | 22 | * git subrepo clone 23 | * git subrepo pull 24 | * git subrepo push 25 | * git subrepo checkout 26 | 27 | # Clone forms: 28 | git subrepo clone git@github.com:you/foo.git 29 | git subrepo clone git@github.com:you/foo.git ext/subfoo 30 | git subrepo clone git@github.com:you/foo.git -b alternate-branch 31 | 32 | # Single command pull: 33 | git subrepo pull --rebase ext/foo 34 | git subrepo pull --merge ext/foo -b remote-branch 35 | git subrepo pull --ours ext/foo -B remote-branch # change .gitrepo 36 | git subrepo pull --rebase --all 37 | 38 | # Manual pull: 39 | git subrepo checkout ext/foo 40 | git rebase subrepo/remote/ext/foo subrepo/ext/foo 41 | make test 42 | git subrepo pull ext/foo --continue 43 | 44 | # Push forms: 45 | git subrepo push ext/foo 46 | git subrepo push ext/foo -b remote-branch 47 | git subrepo push ext/foo -B remote-branch 48 | git subrepo push --all 49 | 50 | # Manual push: 51 | git subrepo checkout ext/foo --rebase 52 | 53 | make test 54 | git subrepo push ext/foo --continue 55 | 56 | # Checkout forms: 57 | git subrepo checkout ext/foo 58 | git subrepo checkout ext/foo -- 59 | git subrepo checkout ext/foo --fetch 60 | git subrepo checkout -b create-local-branch-name 61 | git subrepo checkout --all 62 | 63 | = Command Logic 64 | 65 | This section goes over all the things that a command needs to do: 66 | 67 | == Setup 68 | 69 | There are certain steps that each command must do first: 70 | 71 | * Assert that the system commands and git version are correct. 72 | * Assert that the repository is in a clean state. 73 | * Assert that the repo is on a branch. 74 | * Assert that the current dir is top level of repo. 75 | * Assert the command arguments are valid. 76 | * Assert the subdir is a valid subrepo dir. 77 | * For `clone` make sure subdir does not exist yet. 78 | * Read the .gitrepo file (not for clone). 79 | * For `clone`, make sure we know what upstream branch to use. 80 | * Note the starting point (branch and commit). 81 | 82 | == Clone command: 83 | 84 | * Fetch upstream content for our tracking branch. 85 | -------------------------------------------------------------------------------- /note/pull-dance.txt: -------------------------------------------------------------------------------- 1 | == Pull Dancing 2 | 3 | ( 4 | git sr pull --rebase ext/bar 5 | ) 6 | 7 | ( 8 | git sr checkout ext/bar --rebase 9 | make test 10 | git sr pull --continue 11 | ) 12 | 13 | ( 14 | git sr checkout ext/bar 15 | git rebase subrepo/remote/ext/bar 16 | make test 17 | git sr pull --continue 18 | ) 19 | -------------------------------------------------------------------------------- /note/recreate-rebase-conflict.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | set -x 5 | 6 | # Make a directory to work in: 7 | { 8 | ROOT=${BASH_SOURCE%.sh} 9 | [ -n "$ROOT" ] || exit 1 10 | rm -fr "$ROOT" 11 | mkdir "$ROOT" 12 | } 13 | 14 | ( 15 | cd "$ROOT" 16 | 17 | # Start a new repo: 18 | git init 19 | 20 | # Make an empty file called `file`: 21 | touch file 22 | git add file 23 | 24 | # Make 3 commits to `file`: 25 | for n in {1..3}; do 26 | echo "Line $n" >> file 27 | git commit -a -m "Commit #$n" 28 | done 29 | 30 | # Now `file` has 3 lines. 31 | 32 | # Make a new branch with one commit where the file has 3 lines: 33 | git checkout --orphan subrepo-fake 34 | git commit -a -m 'Initial commit on subrepo-fake branch' 35 | 36 | # Return to master and lop off the last commit, so that the `file` has 2 37 | # lines (and 2 commits): 38 | git checkout master 39 | git reset --hard HEAD^ 40 | 41 | # Now we rebase like `git subrepo push` does where master is the upstream 42 | # with 2 commits, and `subrepo-fake` is our fake subrepo branch with one 43 | # commit: 44 | git rebase master subrepo-fake || true 45 | 46 | # Command fails on conflict but `|| true` prevents `set -e` from stopping 47 | # here. 48 | 49 | # Show the current branch status: 50 | git status 51 | 52 | # Show the rebase conflict: 53 | cat file 54 | 55 | # This is exactly what is happening with a `git subrepo push`. 56 | ) 57 | -------------------------------------------------------------------------------- /note/subtree-rebase-fail-example/test.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | rm -fr repo{1,2,3} 6 | mkdir repo{1,2} 7 | ( 8 | cd repo1/ 9 | git init 10 | touch foo 11 | git add foo 12 | git commit -m "First commit" 13 | ) 14 | ( 15 | cd repo2 16 | git init 17 | touch bar 18 | git add bar 19 | git commit -m "add bar" 20 | ) 21 | git clone repo1 repo3 22 | ( 23 | cd repo3/ 24 | git subrepo clone ../repo2 subrepo 25 | bash 26 | git rebase -i HEAD^ 27 | git log -p 28 | ls 29 | ) 30 | -------------------------------------------------------------------------------- /note/test-subrepo-push.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | # Make a directory to work in: 6 | { 7 | ROOT=${BASH_SOURCE%.sh} 8 | [ -n "$ROOT" ] || exit 1 9 | rm -fr "$ROOT" 10 | mkdir "$ROOT" 11 | } 12 | 13 | ( 14 | cd "$ROOT" 15 | 16 | # Create "bare" repos to act as pushable upstreams 17 | git init --bare parent-upstream 18 | git init --bare child-upstream 19 | 20 | # Clone the upstreams into local repos 21 | git clone parent-upstream parent 22 | git clone child-upstream child 23 | 24 | ( 25 | cd parent 26 | echo 'Initial parent commit' > parent.txt 27 | git add parent.txt 28 | git commit -m 'initial parent commit' 29 | git push 30 | ) 31 | 32 | ( 33 | cd child 34 | echo 'Initial child commit' > child.txt 35 | git add child.txt 36 | git commit -m 'Initial child commit' 37 | git push 38 | ) 39 | 40 | ( 41 | cd parent 42 | git subrepo clone ../child-upstream childrepo 43 | ) 44 | 45 | ( 46 | cd child 47 | echo 'Commit from child' >> child.txt 48 | git commit -a -m 'commit from child' 49 | git push 50 | ) 51 | 52 | ( 53 | cd parent 54 | git subrepo pull childrepo 55 | ) 56 | 57 | ( 58 | cd parent 59 | echo 'Commit from parent for pushing' >> childrepo/child.txt 60 | echo 'Commit from parent for pushing' >> parent.txt 61 | git commit -a -m 'Commit from parent for pushing' 62 | git push 63 | ) 64 | 65 | ( 66 | cd parent 67 | git subrepo push childrepo -d || bash -i 68 | ) 69 | ) 70 | -------------------------------------------------------------------------------- /note/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -x 4 | 5 | # Take some random github repo 6 | repo=${1:-ingydotnet/boolean-pm} 7 | 8 | # Delete test dir 9 | rm -fr test-repo 10 | 11 | # Clone repo into test dir 12 | git clone "git@github.com:$repo" test-repo 13 | 14 | ( 15 | # cd into test-repo 16 | cd test-repo || exit 17 | # Tag original head 18 | git tag o 19 | # Pick a commit in the middle of the commit history 20 | S=$(git rev-parse HEAD^^^^) 21 | # Reset to the middle commit 22 | git reset --hard "$S" 23 | # Tag that history as 'a' 24 | git tag a 25 | # Reset to original 26 | git reset --hard o 27 | # Take the tail of the history. 28 | git filter-branch -f --parent-filter "sed 's/-p $S//'" "$S..HEAD" 29 | # Mark that sequence as 'b' 30 | git tag b 31 | # Reset to a 32 | git reset --hard a 33 | # Add a commit 34 | echo foobar >> README 35 | git commit README -m 'a change' 36 | # Tag as a2 37 | git tag a2 38 | # Find commits for top of 'a' and root of 'b' 39 | A=$(git rev-parse a) 40 | RB=$(git rev-list --max-parents=0 b) 41 | # Graft them together 42 | echo "$RB $A" >> .git/info/grafts 43 | 44 | git rebase --abort; git rebase -s recursive -X patience a2 b 45 | # # Make the graft permanent. ie join head+tail 46 | # # The commits should rewrite to match original history 47 | # git filter-branch -f $RB^..b 48 | # # Get commits for new 'b' and original 49 | # B=$(git rev-parse b) 50 | # O=$(git rev-parse o) 51 | # # Check to see if it works 52 | # if [ "$B" == "$O" ]; then 53 | # echo "It worked: $B" 54 | # else 55 | # echo "If failed: $B, $O" 56 | # fi 57 | bash 58 | ) 59 | -------------------------------------------------------------------------------- /pkg/bin/generate-help-functions.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use strict; 4 | 5 | sub main { 6 | my $input = do { local $/; <> }; 7 | $input =~ s/.*?\n= Commands\n//s; 8 | $input =~ s/(.*?)\n= Command Options\n.*?\n==? .*/$1/s; 9 | my @list; 10 | while ($input =~ s/.*?^- (.*?)(?=\n- |\n== |\z)//ms) { 11 | my $text = $1; 12 | $text =~ /\A(.*)\n((?s:.*?))\n*\z/ 13 | or die "Bad text '$text'"; 14 | my ($usage, $desc) = ($1, $2); 15 | $usage =~ s/\A`(.*)`\z/$1/ 16 | or die "Bad usage: '$text' '$usage' '$desc'"; 17 | $usage =~ s/^git subrepo //; 18 | (my $name = $usage) =~ s/ .*//; 19 | push @list, [$name, $usage, $desc]; 20 | } 21 | @list = sort { $a->[0] cmp $b->[0] } @list; 22 | write_start(); 23 | write_all_function(@list); 24 | for my $item (@list) { 25 | write_usage_function(@$item); 26 | } 27 | write_finish(); 28 | } 29 | 30 | sub write_start { 31 | print <<'...'; 32 | # DO NOT EDIT. This file generated by pkg/bin/generate-help-functions.pl. 33 | 34 | set -e 35 | ... 36 | } 37 | 38 | sub write_all_function { 39 | my (@list) = @_; 40 | my $out = ''; 41 | for my $item (@list) { 42 | $out .= sprintf "%-20s %s\n", $item->[0], $item->[1]; 43 | } 44 | chomp $out; 45 | print <<"...."; 46 | 47 | help:all() { 48 | cat <<'...' 49 | $out 50 | ... 51 | } 52 | .... 53 | } 54 | 55 | sub write_usage_function { 56 | my ($name, $usage, $desc) = @_; 57 | print <<"...."; 58 | 59 | help:$name() { 60 | cat <<'...' 61 | 62 | Usage: git subrepo $usage 63 | 64 | $desc 65 | ... 66 | } 67 | .... 68 | } 69 | 70 | sub write_help_function { 71 | my ($name, $usage, $desc) = @_; 72 | } 73 | 74 | sub write_finish { 75 | print <<'...'; 76 | 77 | # vim: set sw=2 lisp: 78 | ... 79 | } 80 | 81 | main; 82 | 83 | __END__ 84 | Bad usage: '`help` 85 | Show this manpage. 86 | ' '' ' Show this manpage.' at tool/gerate-help-functions.pl line 15, <> line 1. 87 | -------------------------------------------------------------------------------- /share/completion.bash: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT. This file generated by pkg/bin/generate-completion.pl. 2 | 3 | _git_subrepo() { 4 | local _opts=" -h --help --version -a --all -A --ALL -b= --branch= -e --edit -f --force -F --fetch -M= --method= -m= --message= --file= -r= --remote= -s --squash -u --update -q --quiet -v --verbose -d --debug -x --DEBUG" 5 | local subcommands="branch clean clone commit config fetch help init pull push status upgrade version" 6 | local subdircommands="branch clean commit config fetch pull push status" 7 | local subcommand 8 | subcommand="$(__git_find_on_cmdline "$subcommands")" 9 | 10 | if [ -z "$subcommand" ]; then 11 | # no subcommand yet 12 | # shellcheck disable=SC2154 13 | case "$cur" in 14 | -*) 15 | __gitcomp "$_opts" 16 | ;; 17 | *) 18 | __gitcomp "$subcommands" 19 | esac 20 | 21 | else 22 | 23 | case "$cur" in 24 | -*) 25 | __gitcomp "$_opts" 26 | return 27 | ;; 28 | esac 29 | 30 | if [[ "$subcommand" == "help" ]]; then 31 | __gitcomp "$subcommands" 32 | return 33 | fi 34 | 35 | local subdircommand 36 | subdircommand="$(__git_find_on_cmdline "$subdircommands")" 37 | if [ -n "$subdircommand" ]; then 38 | local git_subrepos 39 | git_subrepos=$(git subrepo status -q) 40 | __gitcomp "$git_subrepos" 41 | fi 42 | 43 | fi 44 | } 45 | -------------------------------------------------------------------------------- /share/enable-completion.sh: -------------------------------------------------------------------------------- 1 | # shellcheck shell=bash 2 | 3 | # Enable git-subrepo completion facilities 4 | if [[ -n ${BASH_VERSION-} ]]; then 5 | # Bash 6 | if [[ $(type -t __gitcomp 2> /dev/null) != function ]]; then 7 | # The standard Git completion script for Bash does not seem to be 8 | # loaded, because its shell function `__gitcomp`, which our Bash 9 | # completion script uses, does not appear to exist. 10 | { 11 | # Look for the Git completion script at the file-paths whereat it 12 | # might be found, according to 13 | # 14 | # and experimental and anecdotal evidence. 15 | for f in \ 16 | /{usr,opt/local}/share/bash-completion/{completions/,}git \ 17 | {,/usr/local,~/.homebrew}/etc/bash_completion.d/git 18 | do 19 | # If a file is found at the location being checked… 20 | # shellcheck source=/dev/null 21 | if [[ -f $f ]]; then 22 | # …source it. 23 | source "$f" 24 | [[ $(type -t __gitcomp) != function ]] && 25 | echo "WARNING: Git completion script '$f' does not provide a '__gitcomp' function" 26 | # Proceed to loading our Bash completion facilities. 27 | break 28 | # We could instead `break` only if `source $f` succeeds, or if 29 | # `$f` provides a `__gitcomp` function, but that would entail 30 | # plowing ahead to the next potential Git completion script if 31 | # this one errors partway through being sourced, possibly 32 | # resulting in an inconsistent mishmash of Git completion scripts. 33 | fi 34 | done 35 | } || { 36 | # If no file was found at any of the potential file-paths checked 37 | # above, source a bundled copy of 38 | # . 39 | 40 | # shellcheck source=share/git-completion.bash 41 | source "$GIT_SUBREPO_ROOT/share/git-completion.bash" 42 | } 43 | fi 44 | # Load our Bash completion facilities. 45 | 46 | # shellcheck source=share/completion.bash 47 | source "$GIT_SUBREPO_ROOT/share/completion.bash" 48 | elif [[ -n ${ZSH_VERSION-} ]]; then 49 | # Zsh 50 | # 51 | # Prepend to `fpath` the path of the directory containing our zsh 52 | # completion script, so that our completion script will be hooked into the 53 | # zsh completion system. 54 | fpath=("$GIT_SUBREPO_ROOT/share/zsh-completion" "${fpath[@]}") 55 | fi 56 | -------------------------------------------------------------------------------- /share/git-subrepo.fish: -------------------------------------------------------------------------------- 1 | # DO NOT EDIT. This file generated by pkg/bin/generate-completion.pl. 2 | 3 | function __fish_git_subrepo_subdirs 4 | git subrepo status -q 5 | end 6 | 7 | complete -c git-subrepo -f 8 | complete -c git-subrepo -n '__fish_use_subcommand' -a 'branch clean clone commit config fetch help init pull push status upgrade version' 9 | complete -c git-subrepo -s h -d 'Show the command summary' 10 | complete -c git-subrepo -l help -d 'Help overview' 11 | complete -c git-subrepo -l version -d 'Print the git-subrepo version number' 12 | complete -c git-subrepo -l all -s a -d 'Perform command on all current subrepos' 13 | complete -c git-subrepo -l ALL -s A -d 'Perform command on all subrepos and subsubrepos' 14 | complete -c git-subrepo -l branch -s b -d 'Specify the upstream branch to push/pull/fetch' -r 15 | complete -c git-subrepo -l edit -s e -d 'Edit commit message' 16 | complete -c git-subrepo -l force -s f -d 'Force certain operations' 17 | complete -c git-subrepo -l fetch -s F -d 'Fetch the upstream content first' 18 | complete -c git-subrepo -l method -s M -d 'Join method: '"'"'merge'"'"' (default) or '"'"'rebase'"'"'' -r 19 | complete -c git-subrepo -l message -s m -d 'Specify a commit message' -r 20 | complete -c git-subrepo -l file -d 'Specify a commit message file' -r 21 | complete -c git-subrepo -l remote -s r -d 'Specify the upstream remote to push/pull/fetch' -r 22 | complete -c git-subrepo -l squash -s s -d 'Squash commits on push' 23 | complete -c git-subrepo -l update -s u -d 'Add the --branch and/or --remote overrides to .gitrepo' 24 | complete -c git-subrepo -l quiet -s q -d 'Show minimal output' 25 | complete -c git-subrepo -l verbose -s v -d 'Show verbose output' 26 | complete -c git-subrepo -l debug -s d -d 'Show the actual commands used' 27 | complete -c git-subrepo -l DEBUG -s x -d 'Turn on -x Bash debugging' 28 | complete -c git-subrepo -n '__fish_git_using_command branch' -a '(__fish_git_subrepo_subdirs)' 29 | complete -c git-subrepo -n '__fish_git_using_command clean' -a '(__fish_git_subrepo_subdirs)' 30 | complete -c git-subrepo -n '__fish_git_using_command commit' -a '(__fish_git_subrepo_subdirs)' 31 | complete -c git-subrepo -n '__fish_git_using_command config' -a '(__fish_git_subrepo_subdirs)' 32 | complete -c git-subrepo -n '__fish_git_using_command fetch' -a '(__fish_git_subrepo_subdirs)' 33 | complete -c git-subrepo -n '__fish_git_using_command pull' -a '(__fish_git_subrepo_subdirs)' 34 | complete -c git-subrepo -n '__fish_git_using_command push' -a '(__fish_git_subrepo_subdirs)' 35 | complete -c git-subrepo -n '__fish_git_using_command status' -a '(__fish_git_subrepo_subdirs)' 36 | complete -c git-subrepo -F -n '__fish_git_using_command clone' -a '(__fish_git_remotes)' -d 'Repository to clone from' 37 | -------------------------------------------------------------------------------- /share/zsh-completion/_git-subrepo: -------------------------------------------------------------------------------- 1 | #compdef git-subrepo -P git\ ##subrepo 2 | #description perform git-subrepo operations 3 | 4 | # DO NOT EDIT. This file generated by pkg/bin/generate-completion.pl. 5 | 6 | _git-subrepo() { 7 | typeset -A opt_args 8 | local curcontext="$curcontext" state line context 9 | 10 | _arguments -s \ 11 | '1: :->subcmd' \ 12 | '*: :->args' \ 13 | '-h[Show the command summary]' \ 14 | '--help[Help overview]' \ 15 | '--version[Print the git-subrepo version number]' \ 16 | '(-a --all)'{-a,--all}'[Perform command on all current subrepos]' \ 17 | '(-A --ALL)'{-A,--ALL}'[Perform command on all subrepos and subsubrepos]' \ 18 | '(-b --branch)'{-b,--branch}'[Specify the upstream branch to push/pull/fetch]:b' \ 19 | '(-e --edit)'{-e,--edit}'[Edit commit message]' \ 20 | '(-f --force)'{-f,--force}'[Force certain operations]' \ 21 | '(-F --fetch)'{-F,--fetch}'[Fetch the upstream content first]' \ 22 | '(-M --method)'{-M,--method}'[Join method: '"'"'merge'"'"' (default) or '"'"'rebase'"'"']:M' \ 23 | '(-m --message)'{-m,--message}'[Specify a commit message]:m' \ 24 | '--file[Specify a commit message file]:file' \ 25 | '(-r --remote)'{-r,--remote}'[Specify the upstream remote to push/pull/fetch]:r' \ 26 | '(-s --squash)'{-s,--squash}'[Squash commits on push]' \ 27 | '(-u --update)'{-u,--update}'[Add the --branch and/or --remote overrides to .gitrepo]' \ 28 | '(-q --quiet)'{-q,--quiet}'[Show minimal output]' \ 29 | '(-v --verbose)'{-v,--verbose}'[Show verbose output]' \ 30 | '(-d --debug)'{-d,--debug}'[Show the actual commands used]' \ 31 | '(-x --DEBUG)'{-x,--DEBUG}'[Turn on -x Bash debugging]' \ 32 | && ret=0 33 | 34 | case $state in 35 | subcmd) 36 | compadd branch clean clone commit config fetch help init pull push status upgrade version 37 | ;; 38 | 39 | args) 40 | case $line[1] in 41 | 42 | clone) 43 | _arguments -C \ 44 | '1: :->subcmd' \ 45 | '2: :->repo' \ 46 | '*: :->subdir' \ 47 | && ret=0 48 | case $state in 49 | subdir|repo) 50 | _files 51 | ;; 52 | esac 53 | ;; 54 | 55 | branch|clean|commit|config|fetch|pull|push|status) 56 | _compadd_subdirs 57 | ;; 58 | 59 | init) 60 | _files 61 | ;; 62 | 63 | help) 64 | compadd branch clean clone commit config fetch help init pull push status upgrade version 65 | ;; 66 | esac 67 | ;; 68 | esac 69 | 70 | } 71 | 72 | _compadd_subdirs() { 73 | local subrepos 74 | IFS=$'\n' set -A subrepos `git subrepo status -q` 75 | compadd -X "subrepos: " $subrepos 76 | } 77 | -------------------------------------------------------------------------------- /test/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:20.04 2 | 3 | RUN apt-get update 4 | 5 | RUN apt-get install -y \ 6 | bison \ 7 | build-essential \ 8 | git \ 9 | vim \ 10 | wget \ 11 | && true 12 | 13 | # Install shellcheck-0.7.1 14 | RUN cd /root \ 15 | && wget https://github.com/koalaman/shellcheck/releases/download/v0.7.1/shellcheck-v0.7.1.linux.x86_64.tar.xz \ 16 | && tar xf shellcheck-v0.7.1.linux.x86_64.tar.xz \ 17 | && mv shellcheck-v0.7.1/shellcheck /usr/local/bin/ \ 18 | && rm -fr shellcheck* \ 19 | && true 20 | 21 | # Build/install bash-3.2.57 22 | RUN cd /root \ 23 | && wget https://ftp.gnu.org/gnu/bash/bash-3.2.57.tar.gz \ 24 | && tar -xzf bash-3.2.57.tar.gz \ 25 | && cd bash-3.2.57 \ 26 | && ./configure --prefix=/bash-3.2 \ 27 | && make \ 28 | && make install \ 29 | && rm -fr bash* \ 30 | && true 31 | 32 | # Buil/install bash-4.0 33 | RUN cd /root \ 34 | && wget https://ftp.gnu.org/gnu/bash/bash-4.0.tar.gz \ 35 | && tar -xzf bash-4.0.tar.gz \ 36 | && cd bash-4.0 \ 37 | && ./configure --prefix=/bash-4.0 \ 38 | && make \ 39 | && make install \ 40 | && rm -fr bash* \ 41 | && true 42 | 43 | # Buil/install bash-4.1 44 | RUN cd /root \ 45 | && wget https://ftp.gnu.org/gnu/bash/bash-4.1.tar.gz \ 46 | && tar -xzf bash-4.1.tar.gz \ 47 | && cd bash-4.1 \ 48 | && ./configure --prefix=/bash-4.1 \ 49 | && make \ 50 | && make install \ 51 | && rm -fr bash* \ 52 | && true 53 | 54 | # Buil/install bash-4.2 55 | RUN cd /root \ 56 | && wget https://ftp.gnu.org/gnu/bash/bash-4.2.tar.gz \ 57 | && tar -xzf bash-4.2.tar.gz \ 58 | && cd bash-4.2 \ 59 | && ./configure --prefix=/bash-4.2 \ 60 | && make \ 61 | && make install \ 62 | && rm -fr bash* \ 63 | && true 64 | 65 | # Buil/install bash-4.3 66 | RUN cd /root \ 67 | && wget https://ftp.gnu.org/gnu/bash/bash-4.3.tar.gz \ 68 | && tar -xzf bash-4.3.tar.gz \ 69 | && cd bash-4.3 \ 70 | && ./configure --prefix=/bash-4.3 \ 71 | && make \ 72 | && make install \ 73 | && rm -fr bash* \ 74 | && true 75 | 76 | # Buil/install bash-4.4 77 | RUN cd /root \ 78 | && wget https://ftp.gnu.org/gnu/bash/bash-4.4.tar.gz \ 79 | && tar -xzf bash-4.4.tar.gz \ 80 | && cd bash-4.4 \ 81 | && ./configure --prefix=/bash-4.4 \ 82 | && make \ 83 | && make install \ 84 | && rm -fr bash* \ 85 | && true 86 | 87 | # Buil/install bash-5.0 88 | RUN cd /root \ 89 | && wget https://ftp.gnu.org/gnu/bash/bash-5.0.tar.gz \ 90 | && tar -xzf bash-5.0.tar.gz \ 91 | && cd bash-5.0 \ 92 | && ./configure --prefix=/bash-5.0 \ 93 | && make \ 94 | && make install \ 95 | && rm -fr bash* \ 96 | && true 97 | 98 | # Buil/install bash-5.1-rc1 99 | RUN cd /root \ 100 | && wget https://ftp.gnu.org/gnu/bash/bash-5.1-rc1.tar.gz \ 101 | && tar -xzf bash-5.1-rc1.tar.gz \ 102 | && cd bash-5.1-rc1 \ 103 | && ./configure --prefix=/bash-5.1 \ 104 | && make \ 105 | && make install \ 106 | && rm -fr bash* \ 107 | && true 108 | 109 | RUN git config --global user.email "you@example.com" \ 110 | && git config --global user.name "Your Name" \ 111 | && git config --global --add safe.directory /git-subrepo/.git \ 112 | && true 113 | -------------------------------------------------------------------------------- /test/branch-all.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | note "Test commands work using --all flag" 10 | 11 | clone-foo-and-bar 12 | 13 | ( 14 | cd "$OWNER/foo" 15 | git subrepo clone --quiet "$UPSTREAM/bar" one 16 | git subrepo clone --quiet "$UPSTREAM/bar" two 17 | add-new-files two/file 18 | ) 19 | 20 | ok "$( 21 | cd "$OWNER/foo" 22 | git subrepo branch --all &> /dev/null 23 | )" "branch command works with --all even when a subrepo has no new commits" 24 | 25 | ok "$( 26 | cd "$OWNER/foo" 27 | git:branch-exists subrepo/two 28 | )" "The 'subrepo/two' branch exists" 29 | 30 | test-exists "$OWNER/foo/.git/tmp/subrepo/two/" 31 | 32 | ok "$( 33 | cd "$OWNER/foo" 34 | git:branch-exists subrepo/one 35 | )" "The 'subrepo/one' branch exists" 36 | 37 | test-exists "$OWNER/foo/.git/tmp/subrepo/one/" 38 | 39 | done_testing 40 | 41 | teardown 42 | -------------------------------------------------------------------------------- /test/branch-rev-list-one-path.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/foo" 15 | branchpoint=$(git rev-parse HEAD) 16 | add-new-files bar/file1 17 | add-new-files bar/file2 18 | git checkout -b other "$branchpoint" 19 | add-new-files bar/file3 20 | add-new-files bar/file4 21 | add-new-files bar/file5 22 | git merge master 23 | ) >& /dev/null || die 24 | 25 | test-exists "$OWNER/foo/bar/file1" "$OWNER/foo/bar/file2" "$OWNER/foo/bar/file3" "$OWNER/foo/bar/file4" "$OWNER/foo/bar/file5" 26 | 27 | is "$( 28 | cd "$OWNER/foo" 29 | git subrepo branch bar 30 | )" \ 31 | "Created branch 'subrepo/bar' and worktree '.git/tmp/subrepo/bar'." \ 32 | "subrepo branch command output is correct" 33 | 34 | got="$( 35 | cd "$OWNER/foo" 36 | git rev-list subrepo/bar | wc -l 37 | )" 38 | is "${got// /}" "6" "We have only created commits for one of the paths" 39 | 40 | done_testing 41 | 42 | teardown 43 | -------------------------------------------------------------------------------- /test/branch-rev-list.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/foo" 15 | branchpoint=$(git rev-parse HEAD) 16 | add-new-files bar/file1 17 | # We push here to force subrepo to handle 18 | # histories where it's not first parent 19 | git subrepo push bar 20 | add-new-files bar/file2 21 | git checkout -b other "$branchpoint" 22 | add-new-files bar/file3 23 | add-new-files bar/file4 24 | add-new-files bar/file5 25 | git merge master 26 | ) >& /dev/null || die 27 | 28 | test-exists "$OWNER/foo/bar/file1" "$OWNER/foo/bar/file2" "$OWNER/foo/bar/file3" "$OWNER/foo/bar/file4" "$OWNER/foo/bar/file5" 29 | 30 | # -F is needed for branch to fetch new information 31 | is "$( 32 | cd "$OWNER/foo" 33 | git subrepo -F branch bar 34 | )" \ 35 | "Created branch 'subrepo/bar' and worktree '.git/tmp/subrepo/bar'." \ 36 | "subrepo branch command output is correct" 37 | 38 | got="$( 39 | cd "$OWNER/foo" 40 | git rev-list subrepo/bar | wc -l 41 | )" 42 | is "${got// /}" "5" "We have only created commits for one of the paths" 43 | 44 | done_testing 45 | 46 | teardown 47 | -------------------------------------------------------------------------------- /test/branch.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | before=$(date -r "$OWNER"/foo/Foo '+%s') 14 | 15 | ( 16 | cd "$OWNER"/foo 17 | add-new-files bar/file 18 | add-new-files .gitrepo 19 | ) 20 | 21 | save-original-state "$OWNER"/foo bar 22 | 23 | # Make sure that time stamps differ 24 | sleep 1 25 | 26 | is "$( 27 | cd "$OWNER"/foo 28 | git subrepo branch bar 29 | )" \ 30 | "Created branch 'subrepo/bar' and worktree '.git/tmp/subrepo/bar'." \ 31 | "subrepo branch command output is correct" 32 | 33 | 34 | after=$(date -r "$OWNER"/foo/Foo '+%s') 35 | assert-original-state "$OWNER"/foo bar 36 | 37 | # Check that we haven't checked out any temporary files 38 | is "$before" "$after" \ 39 | "No modification on Foo" 40 | 41 | test-exists "$OWNER"/foo/.git/tmp/subrepo/bar/ 42 | 43 | is "$( 44 | cd "$OWNER"/foo/.git/tmp/subrepo/bar 45 | git branch | grep '\*' 46 | )" \ 47 | "* subrepo/bar" \ 48 | "Correct branch is checked out" 49 | 50 | done_testing 51 | 52 | teardown 53 | -------------------------------------------------------------------------------- /test/clean.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/foo" 15 | add-new-files bar/file 16 | git subrepo --quiet branch bar 17 | ) 18 | 19 | test-exists \ 20 | "$OWNER/foo/.git/refs/heads/subrepo/bar" \ 21 | "$OWNER/foo/.git/refs/subrepo/bar/fetch" 22 | 23 | is "$( 24 | cd "$OWNER/foo" 25 | git subrepo clean bar 26 | )" \ 27 | "Removed branch 'subrepo/bar'." \ 28 | "subrepo clean command output is correct" 29 | 30 | test-exists \ 31 | "!$OWNER/foo/.git/refs/heads/subrepo/bar" 32 | 33 | ( 34 | cd "$OWNER/foo" 35 | git subrepo clean --force bar 36 | ) 37 | 38 | test-exists \ 39 | "!$OWNER/foo/.git/refs/subrepo/bar/fetch" 40 | 41 | done_testing 42 | 43 | teardown 44 | -------------------------------------------------------------------------------- /test/clone-annotated-tag.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | ( 12 | cd "$OWNER/bar" 13 | git tag -a annotated_tag -m "My annotated tag" 14 | git tag lightweight_tag 15 | ) # >& /dev/null || die 16 | 17 | # Do the subrepo clone with tag and test the output: 18 | { 19 | clone_output=$( 20 | cd "$OWNER/foo" 21 | git subrepo clone ../bar/.git -b lightweight_tag light 22 | ) 23 | 24 | # Check output is correct: 25 | is "$clone_output" \ 26 | "Subrepo '../bar/.git' (lightweight_tag) cloned into 'light'." \ 27 | 'subrepo clone lightweight tag command output is correct' 28 | } 29 | 30 | # Do the subrepo clone with tag and test the output: 31 | { 32 | clone_output=$( 33 | cd "$OWNER/foo" 34 | git subrepo clone ../bar/.git -b annotated_tag ann 2>&1 || true 35 | ) 36 | 37 | # Check output is correct: 38 | is "$clone_output" \ 39 | "Subrepo '../bar/.git' (annotated_tag) cloned into 'ann'." \ 40 | 'subrepo clone annotated command output is correct' 41 | } 42 | 43 | done_testing 44 | 45 | teardown 46 | -------------------------------------------------------------------------------- /test/clone.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | ( 12 | mkdir -p "$OWNER/empty" 13 | git init "$OWNER/empty" 14 | ) 15 | 16 | # Test that the repos look ok: 17 | { 18 | test-exists \ 19 | "$OWNER/foo/.git/" \ 20 | "$OWNER/foo/Foo" \ 21 | "!$OWNER/foo/bar/" \ 22 | "$OWNER/bar/.git/" \ 23 | "$OWNER/bar/Bar" \ 24 | "$OWNER/empty/.git/" 25 | } 26 | 27 | # Do the subrepo clone and test the output: 28 | { 29 | clone_output=$( 30 | cd "$OWNER/foo" 31 | git subrepo clone "$UPSTREAM/bar" 32 | ) 33 | 34 | # Check output is correct: 35 | is "$clone_output" \ 36 | "Subrepo '$UPSTREAM/bar' (master) cloned into 'bar'." \ 37 | 'subrepo clone command output is correct' 38 | 39 | is "$( 40 | cd "$OWNER/foo" 41 | git remote -v | grep subrepo/bar 42 | )" \ 43 | "" \ 44 | 'No remotes created' 45 | 46 | clone_output_empty=$( 47 | cd "$OWNER/empty" 48 | catch git subrepo clone "$UPSTREAM/bar" 49 | ) 50 | 51 | # Check output is correct: 52 | is "$clone_output_empty" \ 53 | "git-subrepo: You can't clone into an empty repository" \ 54 | 'subrepo empty clone command output is correct' 55 | } 56 | 57 | # Check that subrepo files look ok: 58 | gitrepo=$OWNER/foo/bar/.gitrepo 59 | { 60 | test-exists \ 61 | "$OWNER/foo/bar/" \ 62 | "$OWNER/foo/bar/Bar" \ 63 | "$gitrepo" \ 64 | "!$OWNER/empty/bar/" 65 | } 66 | 67 | # Test foo/bar/.gitrepo file contents: 68 | { 69 | foo_clone_commit=$(cd "$OWNER/foo"; git rev-parse HEAD^) 70 | bar_head_commit=$(cd "$OWNER/bar"; git rev-parse HEAD) 71 | test-gitrepo-comment-block 72 | test-gitrepo-field "remote" "$UPSTREAM/bar" 73 | test-gitrepo-field "branch" "master" 74 | test-gitrepo-field "commit" "$bar_head_commit" 75 | test-gitrepo-field "parent" "$foo_clone_commit" 76 | test-gitrepo-field "cmdver" "$(git subrepo --version)" 77 | } 78 | 79 | # Make sure status is clean: 80 | { 81 | git_status=$( 82 | cd "$OWNER/foo" 83 | git status -s 84 | ) 85 | 86 | is "$git_status" \ 87 | "" \ 88 | 'status is clean' 89 | 90 | git_status_empty=$( 91 | cd "$OWNER/empty" 92 | git status -s 93 | ) 94 | 95 | is "$git_status_empty" \ 96 | "" \ 97 | 'status is clean' 98 | } 99 | 100 | done_testing 101 | 102 | teardown 103 | -------------------------------------------------------------------------------- /test/compile.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | { 10 | source lib/git-subrepo 11 | pass 'source lib/git-subrepo' 12 | 13 | source ext/bashplus/lib/bash+.bash 14 | pass 'source ext/bashplus/lib/bash+.bash' 15 | } 16 | 17 | done_testing 2 18 | 19 | teardown 20 | -------------------------------------------------------------------------------- /test/config.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | git clone "$UPSTREAM/init" "$OWNER/init" &>/dev/null 10 | 11 | # shellcheck disable=2034 12 | gitrepo=$OWNER/init/doc/.gitrepo 13 | 14 | ( 15 | cd "$OWNER/init" 16 | git subrepo init doc 17 | ) > /dev/null 18 | 19 | # Test init/doc/.gitrepo file contents: 20 | { 21 | test-gitrepo-field "remote" "none" 22 | test-gitrepo-field "branch" "master" 23 | test-gitrepo-field "commit" "" 24 | test-gitrepo-field "parent" "" 25 | test-gitrepo-field "method" "merge" 26 | test-gitrepo-field "cmdver" "$(git subrepo --version)" 27 | } 28 | 29 | is "$( 30 | cd "$OWNER/init" 31 | git subrepo config doc method rebase 32 | )" \ 33 | "Subrepo 'doc' option 'method' set to 'rebase'." 34 | 35 | { 36 | test-gitrepo-field "remote" "none" 37 | test-gitrepo-field "branch" "master" 38 | test-gitrepo-field "commit" "" 39 | test-gitrepo-field "parent" "" 40 | test-gitrepo-field "method" "rebase" 41 | test-gitrepo-field "cmdver" "$(git subrepo --version)" 42 | } 43 | 44 | is "$( 45 | cd "$OWNER/init" 46 | git subrepo config doc method 47 | )" \ 48 | "Subrepo 'doc' option 'method' has value 'rebase'." 49 | 50 | is "$( 51 | cd "$OWNER/init" 52 | catch git subrepo config doc branch new 53 | )" \ 54 | "git-subrepo: This option is autogenerated, use '--force' to override." 55 | 56 | 57 | done_testing 58 | 59 | teardown 60 | -------------------------------------------------------------------------------- /test/encode.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | export round=0 10 | test_round() { 11 | clone-foo-and-bar 12 | 13 | round=$(( round + 1 )) 14 | normalize_dir=$1 15 | normalize_dir=${normalize_dir#./} 16 | normalize_dir=${normalize_dir%/} 17 | while [[ $normalize_dir =~ (//+) ]]; do normalize_dir=${normalize_dir//${BASH_REMATCH[1]}/\/}; done 18 | 19 | clone_output=$( 20 | cd "$OWNER/foo" 21 | git subrepo clone "$UPSTREAM/bar" -- "$normalize_dir" 22 | ) 23 | 24 | # Check output is correct: 25 | is "$clone_output" \ 26 | "Subrepo '$UPSTREAM/bar' (master) cloned into '$normalize_dir'." \ 27 | 'subrepo clone command output is correct' 28 | 29 | test-exists "$OWNER/foo/$normalize_dir/" 30 | 31 | ( 32 | cd "$OWNER/bar" 33 | git pull 34 | add-new-files Bar2-$round 35 | git push 36 | ) &> /dev/null || die 37 | 38 | # Do the pull and check output: 39 | { 40 | is "$( 41 | cd "$OWNER/foo" 42 | git subrepo pull -- "$normalize_dir" 43 | )" \ 44 | "Subrepo '$normalize_dir' pulled from '$UPSTREAM/bar' (master)." \ 45 | 'subrepo pull command output is correct' 46 | } 47 | 48 | test-exists "$OWNER/foo/$normalize_dir/" 49 | 50 | ( 51 | cd "$OWNER/foo/$normalize_dir" 52 | git pull 53 | add-new-files new-$round 54 | git push 55 | ) &> /dev/null || die 56 | 57 | # Do the push and check output: 58 | { 59 | is "$( 60 | cd "$OWNER/foo" 61 | git subrepo push -- "$normalize_dir" 62 | )" \ 63 | "Subrepo '$normalize_dir' pushed to '$UPSTREAM/bar' (master)." \ 64 | 'subrepo push command output is correct' 65 | } 66 | } 67 | 68 | test_round normal 69 | test_round .dot 70 | test_round ......dots 71 | test_round 'spa ce' 72 | test_round 'per%cent' 73 | test_round 'back-sl\as/h' 74 | test_round 'end-with.lock' 75 | # test_round '@' # TODO Fix. This broke on recent git version... 76 | test_round '@{' 77 | test_round '[' 78 | test_round '-begin-with-minus' 79 | test_round 'tailing-slash/' 80 | test_round 'tailing-dots...' 81 | test_round 'special-char:^[?*' 82 | test_round 'many////slashes' 83 | test_round '_under_scores_' 84 | 85 | test_round '.str%a\nge...' 86 | test_round ~'////......s:a^t?r a*n[g@{e.lock' 87 | 88 | 89 | done_testing 90 | 91 | teardown 92 | -------------------------------------------------------------------------------- /test/error.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | export GIT_SUBREPO_TEST_ERRORS=true 9 | 10 | note "Test all error message conditions in git-subrepo" 11 | 12 | clone-foo-and-bar 13 | 14 | { 15 | is "$( 16 | cd "$OWNER/bar" 17 | git subrepo --quiet clone "$UPSTREAM/foo" 18 | add-new-files foo/file 19 | git subrepo --quiet branch foo 20 | catch git subrepo branch foo 21 | )" \ 22 | "git-subrepo: Branch 'subrepo/foo' already exists. Use '--force' to override." \ 23 | "Error OK: can't create a branch that exists" 24 | 25 | ( 26 | cd "$OWNER/bar" 27 | git subrepo --quiet clean foo 28 | git reset --quiet --hard HEAD^ 29 | ) 30 | } 31 | 32 | { 33 | like "$(catch git subrepo clone --foo)" \ 34 | "error: unknown option \`foo" \ 35 | "Error OK: unknown command option" 36 | } 37 | 38 | { 39 | is "$(catch git subrepo main 1 2 3)" \ 40 | "git-subrepo: 'main' is not a command. See 'git subrepo help'." \ 41 | "Error OK: unknown command" 42 | } 43 | 44 | { 45 | is "$(catch git subrepo pull --update)" \ 46 | "git-subrepo: Can't use '--update' without '--branch' or '--remote'." \ 47 | "Error OK: --update requires --branch or --remote options" 48 | } 49 | 50 | { 51 | is "$(catch git subrepo clone --all)" \ 52 | "git-subrepo: Invalid option '--all' for 'clone'." \ 53 | "Error OK: Invalid option '--all' for 'clone'" 54 | } 55 | 56 | { 57 | like "$( 58 | cd "$OWNER/bar" 59 | catch git subrepo pull /home/user/bar/foo 60 | )" \ 61 | "git-subrepo: The subdir '.*/home/user/bar/foo' should not be absolute path." \ 62 | "Error OK: check subdir is not absolute path" 63 | } 64 | 65 | { 66 | # XXX add 'commit' to cmds here when implemented: 67 | for cmd in pull push fetch branch commit clean; do 68 | is "$( 69 | cd "$OWNER/bar" 70 | catch git subrepo "$cmd" 71 | )" \ 72 | "git-subrepo: Command '$cmd' requires arg 'subdir'." \ 73 | "Error OK: check that '$cmd' requires subdir" 74 | done 75 | } 76 | 77 | { 78 | is "$( 79 | cd "$OWNER/bar" 80 | catch git subrepo clone foo bar baz quux 81 | )" \ 82 | "git-subrepo: Unknown argument(s) 'baz quux' for 'clone' command." \ 83 | "Error OK: extra arguments for clone" 84 | } 85 | 86 | { 87 | is "$( 88 | cd "$OWNER/bar" 89 | catch git subrepo clone .git 90 | )" \ 91 | "git-subrepo: Can't determine subdir from '.git'." \ 92 | "Error OK: check error in subdir guess" 93 | } 94 | 95 | { 96 | is "$( 97 | cd "$OWNER/bar" 98 | catch git subrepo pull lala 99 | )" \ 100 | "git-subrepo: No 'lala/.gitrepo' file." \ 101 | "Error OK: check for valid subrepo subdir" 102 | } 103 | 104 | { 105 | is "$( 106 | cd "$OWNER/bar" 107 | git checkout --quiet "$(git rev-parse master)" 108 | catch git subrepo status 109 | )" \ 110 | "git-subrepo: Must be on a branch to run this command." \ 111 | "Error OK: check repo is on a branch" 112 | ( 113 | cd "$OWNER/bar" 114 | git checkout --quiet master 115 | ) 116 | } 117 | 118 | { 119 | is "$( 120 | cd .git 121 | catch git subrepo status 122 | )" \ 123 | "git-subrepo: Can't 'subrepo status' outside a working tree." \ 124 | "Error OK: check inside working tree" 125 | } 126 | 127 | { 128 | like "$( 129 | cd "$OWNER/bar" 130 | touch me 131 | git add me 132 | catch git subrepo clone "$UPSTREAM/foo" 133 | )" \ 134 | "git-subrepo: Can't clone subrepo. Working tree has changes." \ 135 | "Error OK: check no working tree changes" 136 | ( 137 | cd "$OWNER/bar" 138 | git reset --quiet --hard 139 | ) 140 | } 141 | 142 | { 143 | is "$( 144 | cd lib 145 | catch git subrepo status 146 | )" \ 147 | "git-subrepo: Need to run subrepo command from top level directory of the repo." \ 148 | "Error OK: check cwd is at top level" 149 | } 150 | 151 | { 152 | is "$( 153 | cd "$OWNER/bar" 154 | catch git subrepo clone dummy bard 155 | )" \ 156 | "git-subrepo: The subdir 'bard' exists and is not empty." \ 157 | "Error OK: non-empty clone subdir target" 158 | } 159 | 160 | { 161 | is "$( 162 | cd "$OWNER/bar" 163 | catch git subrepo clone dummy-repo 164 | )" \ 165 | "git-subrepo: Command failed: 'git ls-remote --symref dummy-repo'." \ 166 | "Error OK: clone non-repo" 167 | } 168 | 169 | done_testing 170 | 171 | teardown 172 | -------------------------------------------------------------------------------- /test/fetch.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/bar" 15 | add-new-files Bar2 16 | git tag -a CoolTag -m "Should stay in subrepo" 17 | git push 18 | ) &> /dev/null || die 19 | 20 | 21 | # Fetch information 22 | { 23 | is "$( 24 | cd "$OWNER/foo" 25 | git subrepo fetch bar 26 | )" \ 27 | "Fetched 'bar' from '$UPSTREAM/bar' (master)." \ 28 | 'subrepo fetch command output is correct' 29 | } 30 | 31 | # Check that there is no tags fetched 32 | { 33 | is "$( 34 | cd "$OWNER/foo" 35 | git tag -l 'CoolTag' 36 | )" \ 37 | "" \ 38 | 'No tag is available' 39 | } 40 | 41 | done_testing 42 | 43 | teardown 44 | -------------------------------------------------------------------------------- /test/gitignore.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/bar" 15 | add-new-files Bar2 16 | git push 17 | ) &> /dev/null || die 18 | 19 | ( 20 | cd "$OWNER/foo" 21 | echo ".*" >> .gitignore 22 | git add .gitignore 23 | git commit -m "Add gitignore" 24 | git push 25 | ) &> /dev/null || die 26 | 27 | 28 | 29 | # Do the pull and check output: 30 | { 31 | is "$( 32 | cd "$OWNER/foo" 33 | git subrepo pull bar 34 | )" \ 35 | "Subrepo 'bar' pulled from '$UPSTREAM/bar' (master)." \ 36 | 'subrepo pull command output is correct' 37 | } 38 | 39 | # Test subrepo file content: 40 | gitrepo=$OWNER/foo/bar/.gitrepo 41 | { 42 | test-exists \ 43 | "$OWNER/foo/bar/Bar2" \ 44 | "$gitrepo" 45 | } 46 | 47 | # Test foo/bar/.gitrepo file contents: 48 | { 49 | foo_pull_commit=$(cd "$OWNER/foo"; git rev-parse HEAD^) 50 | bar_head_commit=$(cd "$OWNER/bar"; git rev-parse HEAD) 51 | test-gitrepo-comment-block 52 | test-gitrepo-field "remote" "$UPSTREAM/bar" 53 | test-gitrepo-field "branch" "master" 54 | test-gitrepo-field "commit" "$bar_head_commit" 55 | test-gitrepo-field "parent" "$foo_pull_commit" 56 | test-gitrepo-field "cmdver" "$(git subrepo --version)" 57 | } 58 | 59 | done_testing 60 | 61 | teardown 62 | -------------------------------------------------------------------------------- /test/init.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | git clone "$UPSTREAM/init" "$OWNER/init" &>/dev/null 10 | 11 | gitrepo=$OWNER/init/doc/.gitrepo 12 | 13 | # Test that the initial repo look ok: 14 | { 15 | test-exists \ 16 | "$OWNER/init/.git/" \ 17 | "$OWNER/init/ReadMe" \ 18 | "$OWNER/init/doc/" \ 19 | "$OWNER/init/doc/init.swim" \ 20 | "!$gitrepo" 21 | } 22 | 23 | output=$( 24 | cd "$OWNER/init" 25 | git subrepo init doc 26 | ) 27 | 28 | is "$output" "Subrepo created from 'doc' (with no remote)." \ 29 | 'Command output is correct' 30 | 31 | { 32 | test-exists \ 33 | "$gitrepo" 34 | } 35 | 36 | # Test init/doc/.gitrepo file contents: 37 | { 38 | test-gitrepo-comment-block 39 | test-gitrepo-field "remote" "none" 40 | test-gitrepo-field "branch" "master" 41 | test-gitrepo-field "commit" "" 42 | test-gitrepo-field "parent" "" 43 | test-gitrepo-field "method" "merge" 44 | test-gitrepo-field "cmdver" "$(git subrepo --version)" 45 | } 46 | 47 | rm -fr "$OWNER/init" 48 | git clone "$UPSTREAM/init" "$OWNER/init" &>/dev/null 49 | ( 50 | cd "$OWNER/init" 51 | git subrepo init doc -r git@github.com:user/repo -b foo -M rebase 52 | ) >/dev/null 53 | 54 | test-gitrepo-field "remote" "git@github.com:user/repo" 55 | test-gitrepo-field "branch" "foo" 56 | test-gitrepo-field "commit" "" 57 | test-gitrepo-field "parent" "" 58 | test-gitrepo-field "method" "rebase" 59 | test-gitrepo-field "cmdver" "$(git subrepo --version)" 60 | 61 | done_testing 62 | 63 | teardown 64 | -------------------------------------------------------------------------------- /test/issue29.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | # if [[ $1 == ReRun ]]; then 10 | # set -x 11 | # else 12 | # "$0" ReRun 2>&1 | tee log 13 | # exit 0 14 | # fi 15 | 16 | cd "$TMP" 17 | 18 | # Make 3 new repos: 19 | ( 20 | mkdir share main1 main2 21 | git init share 22 | git init main1 23 | git init main2 24 | ) > /dev/null 25 | 26 | # Add an empty 'readme' to the share repo: 27 | ( 28 | cd share 29 | echo '* text eol=lf' > .gitattributes 30 | touch readme 31 | git add readme .gitattributes 32 | git commit -m "Initial share" 33 | # To push into here later we must not have working copy on master branch: 34 | git checkout -b temp 35 | ) &> /dev/null 36 | 37 | # `subrepo clone` the share repo into main1: 38 | ( 39 | cd main1 40 | touch main1 41 | git add main1 42 | git commit -m "Initial main1" 43 | git subrepo clone ../share share -b "$DEFAULTBRANCH" 44 | ) > /dev/null 45 | 46 | # `subrepo clone` the share repo into main2: 47 | ( 48 | cd main2 49 | touch main2 50 | git add main2 51 | git commit -m "Initial main2" 52 | git subrepo clone ../share share -b "$DEFAULTBRANCH" 53 | ) > /dev/null 54 | 55 | 56 | # Make a change to the main1 subrepo and push it: 57 | msg_main1="main1 initial add to subrepo" 58 | ( set -x 59 | cd main1 60 | echo "$msg_main1" >> share/readme 61 | git add share/readme 62 | git commit -m "$msg_main1" 63 | 64 | git subrepo push share 65 | ) &> /dev/null 66 | 67 | ok "$(! git:branch-exists "subrepo-push/share")" \ 68 | "The subrepo-push/share branch was deleted after push" 69 | 70 | # TODO Check the state of refs made 71 | 72 | # Pull in the subrepo changes from above into main2. 73 | # Make a local change to the main2 subrepo and push it: 74 | msg_main2="main2 initial add to subrepo" 75 | ( set -x 76 | cd main2 77 | git subrepo pull share 78 | echo "$msg_main2" >> share/readme 79 | git add share/readme 80 | git commit -m "$msg_main2" 81 | 82 | git subrepo push share 83 | ) &> /dev/null || die 84 | 85 | # Go back into main1 and pull the subrepo updates: 86 | ( set -x 87 | cd main1 88 | git subrepo pull share 89 | ) &> /dev/null || die 90 | 91 | # The readme file should have both changes: 92 | is "$(cat main1/share/readme)" \ 93 | "$msg_main1"$'\n'"$msg_main2" \ 94 | "The readme file in the share repo has both subrepo commits" 95 | 96 | done_testing 97 | 98 | teardown 99 | -------------------------------------------------------------------------------- /test/issue95.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | { 10 | cd "$TMP" 11 | 12 | # Make two new repos 13 | ( 14 | mkdir host sub 15 | git init host 16 | git init sub 17 | ) > /dev/null 18 | 19 | # Initialize host repo 20 | ( 21 | cd host 22 | touch host 23 | git add host 24 | git commit -m "host initial commit" 25 | ) > /dev/null 26 | 27 | # Initialize sub repo 28 | ( 29 | cd sub 30 | git init 31 | touch subrepo 32 | git add subrepo 33 | git commit -m "subrepo initial commit" 34 | ) > /dev/null 35 | 36 | # Make sub a subrepo of host 37 | ( 38 | cd host 39 | git subrepo clone ../sub sub 40 | ) > /dev/null 41 | 42 | # Create a branch in host and make some changes in it 43 | ( 44 | cd host 45 | git checkout -b feature 46 | touch feature 47 | git add feature 48 | git commit -m "feature added" 49 | git checkout "$DEFAULTBRANCH" 50 | ) &> /dev/null 51 | 52 | # Commit directly to subrepo 53 | ( 54 | cd sub 55 | echo "direct change in sub" >> subrepo 56 | git commit -a -m "direct change in sub" 57 | ) > /dev/null 58 | 59 | # pull subrepo changes 60 | ( 61 | cd host 62 | git subrepo pull sub 63 | ) > /dev/null 64 | 65 | # commit directly to subrepo 66 | ( 67 | cd sub 68 | echo "another direct change in sub" >> subrepo 69 | git commit -a -m "another direct change in sub" 70 | ) > /dev/null 71 | 72 | # commit to host/sub 73 | ( 74 | cd host 75 | echo "change from host" >> sub/subrepo-host 76 | git add sub/subrepo-host 77 | git commit -m "change from host" 78 | ) > /dev/null 79 | 80 | # merge previously created feature branch 81 | ( 82 | cd host 83 | git merge --no-ff --no-edit feature 84 | ) > /dev/null 85 | 86 | # pull subrepo changes 87 | # expected: successful pull without conflicts 88 | is "$( 89 | cd host 90 | git subrepo pull sub 91 | )" \ 92 | "Subrepo 'sub' pulled from '../sub' ($DEFAULTBRANCH)." 93 | 94 | } 95 | 96 | done_testing 1 97 | 98 | teardown 99 | -------------------------------------------------------------------------------- /test/issue96.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | cd "$TMP" 10 | 11 | # Make two new repos 12 | ( 13 | mkdir host sub 14 | git init host 15 | git init sub 16 | ) > /dev/null 17 | 18 | # Initialize host repo 19 | ( 20 | cd host 21 | touch host 22 | git add host 23 | git commit -m "host initial commit" 24 | ) > /dev/null 25 | 26 | # Initialize sub repo 27 | ( 28 | cd sub 29 | git init 30 | touch subrepo 31 | git add subrepo 32 | git commit -m "subrepo initial commit" 33 | ) > /dev/null 34 | 35 | # Make sub a subrepo of host 36 | ( 37 | cd host 38 | git subrepo clone ../sub sub 39 | ) > /dev/null 40 | 41 | # Commit some changes to the host repo 42 | ( 43 | cd host 44 | touch feature 45 | git add feature 46 | git commit -m "feature added" 47 | ) &> /dev/null 48 | 49 | # Commit directly to subrepo 50 | ( 51 | cd sub 52 | echo "direct change in sub" >> subrepo 53 | git commit -a -m "direct change in sub" 54 | ) > /dev/null 55 | 56 | # Pull subrepo changes 57 | ( 58 | cd host 59 | git subrepo pull sub 60 | ) > /dev/null 61 | 62 | # Commit directly to subrepo 63 | ( 64 | cd sub 65 | echo "another direct change in sub" >> subrepo 66 | git commit -a -m "another direct change in sub" 67 | git checkout -b temp # otherwise push to master will fail 68 | ) &> /dev/null 69 | 70 | # Commit to host/sub 71 | ( 72 | cd host 73 | echo "change from host" >> sub/subrepo-host 74 | git add sub/subrepo-host 75 | git commit -m "change from host" 76 | ) > /dev/null 77 | 78 | # Pull subrepo changes 79 | # expected: successful pull without conflicts 80 | is "$( 81 | cd host 82 | git subrepo pull sub 83 | )" \ 84 | "Subrepo 'sub' pulled from '../sub' ($DEFAULTBRANCH)." 85 | 86 | # Push subrepo changes 87 | # expected: successful push without conflicts 88 | is "$( 89 | cd host 90 | git subrepo push sub -b "$DEFAULTBRANCH" -u 91 | )" \ 92 | "Subrepo 'sub' pushed to '../sub' ($DEFAULTBRANCH)." 93 | 94 | done_testing 2 95 | 96 | teardown 97 | -------------------------------------------------------------------------------- /test/pull-all.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | ( 12 | cd "$OWNER/foo" 13 | git subrepo clone ../bar bar1 14 | git subrepo clone ../bar bar2 15 | ) &> /dev/null || die 16 | 17 | ( 18 | cd "$OWNER/bar" 19 | modify-files Bar 20 | ) &> /dev/null || die 21 | 22 | ( 23 | cd "$OWNER/foo" 24 | git subrepo pull --all 25 | ) &> /dev/null || die 26 | 27 | { 28 | is "$(cat "$OWNER/foo/bar1/Bar")" \ 29 | "a new line" \ 30 | 'bar1/Bar content correct' 31 | is "$(cat "$OWNER/foo/bar2/Bar")" \ 32 | "a new line" \ 33 | 'bar2/Bar content correct' 34 | } 35 | 36 | done_testing 37 | 38 | teardown 39 | -------------------------------------------------------------------------------- /test/pull-merge.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | note "Pull - Conflict - Merge ours/theirs - Push" 14 | 15 | ( 16 | cd "$OWNER/bar" 17 | add-new-files Bar2 18 | git push 19 | ) &> /dev/null || die 20 | 21 | # shellcheck disable=2034 22 | gitrepo=$OWNER/foo/bar/.gitrepo 23 | # Test foo/bar/.gitrepo file contents: 24 | { 25 | foo_pull_commit=$(cd "$OWNER/foo"; git rev-parse HEAD^) 26 | bar_head_commit=$(cd "$OWNER/bar"; git rev-parse HEAD^) 27 | test-gitrepo-field "commit" "$bar_head_commit" 28 | test-gitrepo-field "parent" "$foo_pull_commit" 29 | } 30 | 31 | foo_pull_commit=$(cd "$OWNER/foo"; git rev-parse HEAD) 32 | 33 | ( 34 | cd "$OWNER/foo" 35 | git subrepo pull bar 36 | modify-files-ex bar/Bar2 37 | git push 38 | ) &> /dev/null || die 39 | 40 | ( 41 | cd "$OWNER/bar" 42 | modify-files-ex Bar2 43 | git push 44 | ) &> /dev/null || die 45 | 46 | ( 47 | cd "$OWNER/foo" 48 | git subrepo pull bar || { 49 | cd .git/tmp/subrepo/bar 50 | echo "Merged Bar2" > Bar2 51 | git add Bar2 52 | git commit --file ../../../../.git/worktrees/bar/MERGE_MSG 53 | cd ../../../.. 54 | git subrepo commit bar 55 | git subrepo clean bar 56 | } 57 | ) &> /dev/null || die 58 | 59 | test-exists \ 60 | "$OWNER/foo/bar/Bar2" \ 61 | "$OWNER/bar/Bar2" \ 62 | 63 | is "$(cat "$OWNER/foo/bar/Bar2")" \ 64 | "Merged Bar2" \ 65 | "The readme file in the mainrepo is merged" 66 | 67 | # Check commit messages 68 | { 69 | foo_new_commit_message=$(cd "$OWNER/foo"; git log --format=%B -n 1) 70 | like "$foo_new_commit_message" \ 71 | "git subrepo commit \(merge\) bar" \ 72 | "subrepo pull should have merge message" 73 | } 74 | 75 | # Test foo/bar/.gitrepo file contents: 76 | { 77 | bar_head_commit=$(cd "$OWNER/bar"; git rev-parse HEAD) 78 | test-gitrepo-field "commit" "$bar_head_commit" 79 | test-gitrepo-field "parent" "$foo_pull_commit" 80 | } 81 | 82 | ( 83 | cd "$OWNER/foo" 84 | git subrepo push bar 85 | ) &> /dev/null || die 86 | 87 | # Check commit messages 88 | { 89 | foo_new_commit_message=$(cd "$OWNER/foo"; git log --format=%B -n 1) 90 | like "$foo_new_commit_message" \ 91 | "git subrepo push bar" \ 92 | "subrepo push should not have merge message" 93 | } 94 | 95 | ( 96 | cd "$OWNER/bar" 97 | git pull 98 | ) &> /dev/null || die 99 | 100 | test-exists \ 101 | "$OWNER/foo/bar/Bar2" \ 102 | "$OWNER/bar/Bar2" \ 103 | 104 | is "$(cat "$OWNER/foo/bar/Bar2")" \ 105 | "Merged Bar2" \ 106 | "The readme file in the mainrepo is merged" 107 | 108 | is "$(cat "$OWNER/bar/Bar2")" \ 109 | "Merged Bar2" \ 110 | "The readme file in the subrepo is merged" 111 | 112 | done_testing 113 | 114 | teardown 115 | -------------------------------------------------------------------------------- /test/pull-message.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/bar" 15 | add-new-files Bar2 16 | git push 17 | ) &> /dev/null || die 18 | 19 | 20 | # Do the pull and check output, use -m: 21 | { 22 | is "$( 23 | cd "$OWNER/foo" 24 | git subrepo pull -m 'Hello World' bar 25 | )" \ 26 | "Subrepo 'bar' pulled from '$UPSTREAM/bar' (master)." \ 27 | 'subrepo pull command output is correct' 28 | } 29 | 30 | # Check -m commit messages 31 | { 32 | foo_new_commit_message=$(cd "$OWNER/foo"; git log --format=%B -n 1) 33 | like "$foo_new_commit_message" \ 34 | "Hello World" \ 35 | "subrepo pull commit message" 36 | } 37 | 38 | ( 39 | cd "$OWNER/bar" 40 | add-new-files Bar3 41 | git push 42 | ) &> /dev/null || die 43 | 44 | # Do the pull and check output, use -e: 45 | { 46 | is "$( 47 | cd "$OWNER/foo" 48 | GIT_EDITOR='echo cowabunga >' git subrepo pull -e bar 49 | )" \ 50 | "Subrepo 'bar' pulled from '$UPSTREAM/bar' (master)." \ 51 | 'subrepo pull command output is correct' 52 | } 53 | 54 | # Check -e commit messages 55 | { 56 | foo_new_commit_message="$(cd "$OWNER/foo"; git log --format=%B -n 1)" 57 | like "$foo_new_commit_message" \ 58 | "cowabunga" \ 59 | "subrepo pull edit commit message" 60 | } 61 | 62 | ( 63 | cd "$OWNER/bar" 64 | add-new-files Bar4 65 | git push 66 | ) &> /dev/null || die 67 | 68 | # Do the pull and check output, use -e and -m: 69 | { 70 | is "$( 71 | cd "$OWNER/foo" 72 | GIT_EDITOR=true git subrepo pull -e -m original bar 73 | )" \ 74 | "Subrepo 'bar' pulled from '$UPSTREAM/bar' (master)." \ 75 | 'subrepo pull command output is correct' 76 | } 77 | 78 | # Check -e commit messages 79 | { 80 | foo_new_commit_message="$(cd "$OWNER/foo"; git log --format=%B -n 1)" 81 | like "$foo_new_commit_message" \ 82 | "original" \ 83 | "subrepo pull edit and message commit message" 84 | } 85 | 86 | done_testing 87 | 88 | teardown 89 | -------------------------------------------------------------------------------- /test/pull-new-branch.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source test/setup 4 | 5 | use Test::More 6 | 7 | clone-foo-and-bar 8 | 9 | subrepo-clone-bar-into-foo 10 | 11 | ( 12 | cd "$OWNER/bar" 13 | git checkout -b branch1 14 | git push --set-upstream origin branch1 15 | ) &> /dev/null || die 16 | 17 | # Test subrepo file content: 18 | # shellcheck disable=2034 19 | gitrepo=$OWNER/foo/bar/.gitrepo 20 | 21 | { 22 | foo_pull_commit=$(cd "$OWNER/foo" || exit; git rev-parse HEAD^) 23 | bar_head_commit=$(cd "$OWNER/bar" || exit; git rev-parse HEAD) 24 | test-gitrepo-comment-block 25 | test-gitrepo-field remote "$UPSTREAM/bar" 26 | test-gitrepo-field branch master 27 | test-gitrepo-field commit "$bar_head_commit" 28 | test-gitrepo-field parent "$foo_pull_commit" 29 | test-gitrepo-field cmdver "$(git subrepo --version)" 30 | } 31 | 32 | ( 33 | cd "$OWNER/foo" 34 | git subrepo pull bar -b branch1 -u 35 | ) &> /dev/null || die 36 | 37 | { 38 | foo_pull_commit=$(cd "$OWNER/foo" || exit; git rev-parse HEAD^) 39 | bar_head_commit=$(cd "$OWNER/bar" || exit; git rev-parse HEAD) 40 | test-gitrepo-comment-block 41 | test-gitrepo-field remote "$UPSTREAM/bar" 42 | test-gitrepo-field branch branch1 43 | test-gitrepo-field commit "$bar_head_commit" 44 | test-gitrepo-field parent "$foo_pull_commit" 45 | test-gitrepo-field cmdver "$(git subrepo --version)" 46 | } 47 | 48 | { 49 | is "$( 50 | cd "$OWNER/foo" || exit 51 | git subrepo pull bar 52 | )" \ 53 | "Subrepo 'bar' is up to date." \ 54 | 'subrepo detects that we dont need to pull' 55 | } 56 | 57 | done_testing 58 | 59 | teardown 60 | -------------------------------------------------------------------------------- /test/pull-ours.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | note "Pull - Conflict - Use ours - Push" 14 | 15 | # 16 | # When you perform rebase ours/theirs are reversed, so this test case will 17 | # test using local change (ours) although in the step below 18 | # we actually use git checkout --theirs to accomplish this 19 | # 20 | 21 | ( 22 | cd "$OWNER/bar" 23 | add-new-files Bar2 24 | git push 25 | ) &> /dev/null || die 26 | 27 | ( 28 | cd "$OWNER/foo" 29 | git subrepo pull bar 30 | modify-files-ex bar/Bar2 31 | cat bar/Bar2 32 | git push 33 | ) &> /dev/null || die 34 | 35 | ( 36 | cd "$OWNER/bar" 37 | modify-files-ex Bar2 38 | git push 39 | ) &> /dev/null || die 40 | 41 | before=$(date -r "$OWNER/foo/Foo" '+%s') 42 | 43 | ( 44 | cd "$OWNER/foo" 45 | git subrepo pull bar || { 46 | cd .git/tmp/subrepo/bar 47 | git checkout --ours Bar2 48 | git add Bar2 49 | git commit --file ../../../../.git/worktrees/bar/MERGE_MSG 50 | cd ../../../.. 51 | git subrepo commit bar 52 | git subrepo clean bar 53 | } 54 | ) &> /dev/null || die 55 | 56 | sleep 1 57 | after=$(date -r "$OWNER/foo/Foo" '+%s') 58 | 59 | is "$before" "$after" \ 60 | "No modification on Foo" 61 | 62 | test-exists \ 63 | "$OWNER/foo/bar/Bar2" \ 64 | "$OWNER/bar/Bar2" \ 65 | 66 | is "$(cat "$OWNER/foo/bar/Bar2")" \ 67 | "new file Bar2"$'\n'"bar/Bar2" \ 68 | "The readme file in the mainrepo is ours" 69 | 70 | ( 71 | cd "$OWNER/foo" 72 | git subrepo push bar 73 | ) &> /dev/null || die 74 | 75 | ( 76 | cd "$OWNER/bar" 77 | git pull 78 | ) &> /dev/null || die 79 | 80 | test-exists \ 81 | "$OWNER/foo/bar/Bar2" \ 82 | "$OWNER/bar/Bar2" \ 83 | 84 | is "$(cat "$OWNER/bar/Bar2")" \ 85 | "new file Bar2"$'\n'"bar/Bar2" \ 86 | "The readme file in the subrepo is ours" 87 | 88 | done_testing 89 | 90 | teardown 91 | -------------------------------------------------------------------------------- /test/pull-theirs.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | note "Pull - Conflict - Use theirs - Push" 14 | 15 | # 16 | # When you perform rebase ours/theirs are reversed, so this test case will 17 | # test using the subrepo change (theirs) although in the step below 18 | # we actually use git checkout --ours to accomplish this 19 | # 20 | 21 | ( 22 | cd "$OWNER/bar" 23 | add-new-files Bar2 24 | git push 25 | ) &> /dev/null || die 26 | 27 | ( 28 | cd "$OWNER/foo" 29 | git subrepo pull bar 30 | modify-files-ex bar/Bar2 31 | git push 32 | ) &> /dev/null || die 33 | 34 | ( 35 | cd "$OWNER/bar" 36 | modify-files-ex Bar2 37 | git push 38 | ) &> /dev/null || die 39 | 40 | ( 41 | cd "$OWNER/foo" 42 | git subrepo pull bar || { 43 | cd .git/tmp/subrepo/bar 44 | git checkout --theirs Bar2 45 | git add Bar2 46 | git commit --file ../../../../.git/worktrees/bar/MERGE_MSG 47 | cd ../../../.. 48 | git subrepo commit bar 49 | git subrepo clean bar 50 | } 51 | ) &> /dev/null || die 52 | 53 | test-exists \ 54 | "$OWNER/foo/bar/Bar2" \ 55 | "$OWNER/bar/Bar2" \ 56 | 57 | is "$(cat "$OWNER/foo/bar/Bar2")" \ 58 | "new file Bar2"$'\n'"Bar2" \ 59 | "The readme file in the mainrepo is theirs" 60 | 61 | ( 62 | cd "$OWNER/foo" 63 | cat bar/Bar2 64 | git subrepo push bar 65 | ) &> /dev/null || die 66 | 67 | ( 68 | cd "$OWNER/bar" 69 | git pull 70 | ) &> /dev/null || die 71 | 72 | test-exists \ 73 | "$OWNER/foo/bar/Bar2" \ 74 | "$OWNER/bar/Bar2" \ 75 | 76 | is "$(cat "$OWNER/bar/Bar2")" \ 77 | "new file Bar2"$'\n'"Bar2" \ 78 | "The readme file in the subrepo is theirs" 79 | 80 | done_testing 81 | 82 | teardown 83 | -------------------------------------------------------------------------------- /test/pull-twice.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/bar" 15 | add-new-files Bar2 16 | git push 17 | ) &> /dev/null || die 18 | 19 | ( 20 | cd "$OWNER/foo" 21 | add-new-files bar/Foo2 22 | git push 23 | git subrepo pull bar 24 | ) &> /dev/null || die 25 | 26 | ( 27 | cd "$OWNER/bar" 28 | add-new-files Bar3 29 | git push 30 | ) &> /dev/null || die 31 | 32 | ( 33 | cd "$OWNER/foo" 34 | git subrepo pull bar 35 | ) &> /dev/null || die 36 | 37 | test-exists \ 38 | "$OWNER/foo/bar/Bar2" \ 39 | "$OWNER/foo/bar/Bar3" \ 40 | "$OWNER/foo/bar/Foo2" \ 41 | 42 | done_testing 43 | 44 | teardown 45 | -------------------------------------------------------------------------------- /test/pull-worktree.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | ( 12 | cd "$OWNER/foo" 13 | git subrepo clone ../bar bar 14 | git worktree add -b test ../wt 15 | ) &> /dev/null || die 16 | 17 | ( 18 | cd "$OWNER/bar" 19 | modify-files Bar 20 | ) &> /dev/null || die 21 | 22 | ( 23 | cd "$OWNER/wt" 24 | git subrepo pull --all 25 | ) &> /dev/null || die 26 | 27 | ( 28 | cd "$OWNER/foo" 29 | git merge test 30 | ) &> /dev/null || die 31 | 32 | { 33 | is "$(cat "$OWNER/foo/bar/Bar")" \ 34 | "a new line" \ 35 | 'bar/Bar content correct' 36 | } 37 | 38 | done_testing 39 | 40 | teardown 41 | -------------------------------------------------------------------------------- /test/pull.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/bar" 15 | add-new-files Bar2 16 | git push 17 | ) &> /dev/null || die 18 | 19 | 20 | # Do the pull and check output: 21 | { 22 | is "$( 23 | cd "$OWNER/foo" 24 | git subrepo pull bar 25 | )" \ 26 | "Subrepo 'bar' pulled from '$UPSTREAM/bar' (master)." \ 27 | 'subrepo pull command output is correct' 28 | } 29 | 30 | # Test subrepo file content: 31 | gitrepo=$OWNER/foo/bar/.gitrepo 32 | { 33 | test-exists \ 34 | "$OWNER/foo/bar/Bar2" \ 35 | "$gitrepo" 36 | } 37 | 38 | # Test foo/bar/.gitrepo file contents: 39 | { 40 | foo_pull_commit=$(cd "$OWNER/foo"; git rev-parse HEAD^) 41 | bar_head_commit=$(cd "$OWNER/bar"; git rev-parse HEAD) 42 | test-gitrepo-comment-block 43 | test-gitrepo-field "remote" "$UPSTREAM/bar" 44 | test-gitrepo-field "branch" "master" 45 | test-gitrepo-field "commit" "$bar_head_commit" 46 | test-gitrepo-field "parent" "$foo_pull_commit" 47 | test-gitrepo-field "cmdver" "$(git subrepo --version)" 48 | } 49 | 50 | # Check commit messages 51 | { 52 | foo_new_commit_message=$(cd "$OWNER/foo"; git log --format=%B -n 1) 53 | like "$foo_new_commit_message" \ 54 | "git subrepo pull bar" \ 55 | "Subrepo pull commit message OK" 56 | bar_commit_short=$(git rev-parse --short "$bar_head_commit") 57 | like "$foo_new_commit_message" \ 58 | "merged: \"$bar_commit_short" \ 59 | "Pull commit contains merged" 60 | } 61 | 62 | # Check that we detect that we don't need to pull 63 | { 64 | is "$( 65 | cd "$OWNER/foo" 66 | git subrepo pull bar 67 | )" \ 68 | "Subrepo 'bar' is up to date." \ 69 | 'subrepo detects that we dont need to pull' 70 | } 71 | 72 | # Test pull if we have rebased the original subrepo so that our clone 73 | # commit is no longer present in the history 74 | ( 75 | cd "$OWNER/bar" 76 | git reset --hard master^^ 77 | add-new-files Bar3 78 | git push --force 79 | ) &> /dev/null || die 80 | 81 | { 82 | test-exists \ 83 | !"$OWNER/foo/pull_failed" 84 | } 85 | 86 | ( 87 | cd "$OWNER/foo" 88 | git subrepo pull bar || touch pull_failed 89 | ) &> /dev/null || die 90 | 91 | # We check that the control file was created 92 | { 93 | test-exists \ 94 | "$OWNER/foo/pull_failed" 95 | } 96 | 97 | done_testing # 9 98 | 99 | teardown 100 | -------------------------------------------------------------------------------- /test/push-after-init.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | # Create directory and init git locally as this will test some corner 10 | # cases when you don't have any previous commits to rely on 11 | # see issue/122 12 | ( 13 | mkdir -p "$OWNER/init" 14 | cd "$OWNER/init" 15 | git init 16 | mkdir doc 17 | add-new-files doc/FooBar 18 | git subrepo init doc || die 19 | mkdir ../upstream 20 | git init --bare ../upstream || die 21 | ) &> /dev/null 22 | 23 | output=$( 24 | cd "$OWNER/init" 25 | git subrepo push doc --remote=../upstream 26 | ) 27 | 28 | is "$output" "Subrepo 'doc' pushed to '../upstream' (master)." \ 29 | 'Command output is correct' 30 | 31 | # Test init/doc/.gitrepo file contents: 32 | # shellcheck disable=2034 33 | gitrepo=$OWNER/init/doc/.gitrepo 34 | { 35 | test-gitrepo-field "remote" "../upstream" 36 | test-gitrepo-field "branch" "master" 37 | } 38 | 39 | ( 40 | cd "$OWNER" 41 | git clone upstream up 42 | ) &>/dev/null 43 | 44 | { 45 | test-exists \ 46 | "$OWNER/up/.git/" \ 47 | "!$OWNER/up/.gitrepo" 48 | } 49 | 50 | done_testing 51 | 52 | teardown 53 | -------------------------------------------------------------------------------- /test/push-after-push-no-changes.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | subrepo-clone-bar-into-foo 11 | 12 | { 13 | message=$( 14 | cd "$OWNER/foo" 15 | git subrepo push bar > /dev/null 16 | add-new-files bar/Bar1 17 | catch git subrepo push bar 18 | ) 19 | 20 | is "$message" \ 21 | "Subrepo 'bar' pushed to '$UPSTREAM/bar' (master)." \ 22 | "Output OK: Check that 'push' after an empty push works." 23 | } 24 | 25 | done_testing 1 26 | 27 | teardown 28 | -------------------------------------------------------------------------------- /test/push-force.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | subrepo-clone-bar-into-foo 12 | 13 | ( 14 | cd "$OWNER/bar" 15 | add-new-files Bar2 16 | git push 17 | ) &> /dev/null || die 18 | 19 | ( 20 | cd "$OWNER/foo" 21 | add-new-files bar/Foo1 22 | git subrepo push bar --force 23 | ) &> /dev/null || die 24 | 25 | ( 26 | cd "$OWNER/foo" 27 | git subrepo pull bar 28 | ) &> /dev/null || die 29 | 30 | test-exists \ 31 | "$OWNER/foo/bar/Foo1" \ 32 | !"$OWNER/foo/bar/Bar2" \ 33 | 34 | # Pull here will actually merge the old master with the new one 35 | ( 36 | set +x 37 | cd "$OWNER/bar" 38 | git pull --rebase=false 39 | ) &> /dev/null || die 40 | 41 | test-exists \ 42 | "$OWNER/bar/Bar2" \ 43 | "$OWNER/bar/Foo1" \ 44 | 45 | 46 | # Test that a fresh repo is not contaminated 47 | ( 48 | git clone "$UPSTREAM/bar" "$OWNER/newbar" 49 | ) &> /dev/null || die 50 | 51 | test-exists \ 52 | "$OWNER/newbar/Foo1" \ 53 | !"$OWNER/foo/bar/Bar2" \ 54 | 55 | done_testing 56 | 57 | teardown 58 | -------------------------------------------------------------------------------- /test/push-new-branch.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | # Make various changes to the repos for testing subrepo push: 12 | ( 13 | # In the main repo: 14 | cd "$OWNER/foo" 15 | 16 | # Clone the subrepo into a subdir 17 | git subrepo clone "$UPSTREAM/bar" 18 | 19 | # Make a commit: 20 | add-new-files bar/FooBar 21 | ) &> /dev/null || die 22 | 23 | # Do the subrepo push to another branch: 24 | { 25 | message=$( 26 | cd "$OWNER/foo" 27 | git subrepo push bar --branch newbar 28 | ) 29 | 30 | # Test the output: 31 | is "$message" \ 32 | "Subrepo 'bar' pushed to '$UPSTREAM/bar' (newbar)." \ 33 | 'First push message is correct ' 34 | } 35 | 36 | # Do the subrepo push to another branch again: 37 | { 38 | message=$( 39 | cd "$OWNER/foo" 40 | git subrepo push bar --branch newbar 41 | ) 42 | 43 | # Test the output: 44 | is "$message" \ 45 | "Subrepo 'bar' has no new commits to push." \ 46 | 'Second push message is correct' 47 | } 48 | 49 | # Pull the changes from UPSTREAM/bar in OWNER/bar 50 | ( 51 | cd "$OWNER/bar" 52 | git fetch 53 | git checkout newbar 54 | ) &> /dev/null || die 55 | 56 | test-exists \ 57 | "$OWNER/bar/FooBar" \ 58 | 59 | done_testing 60 | 61 | teardown 62 | -------------------------------------------------------------------------------- /test/push-no-changes.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | note "Test output from successful git-subrepo commands" 10 | 11 | clone-foo-and-bar 12 | 13 | { 14 | is "$( 15 | cd "$OWNER/bar" 16 | git subrepo --quiet clone "$UPSTREAM/foo" 17 | catch git subrepo push foo 18 | )" \ 19 | "Subrepo 'foo' has no new commits to push." \ 20 | "Output OK: Check that 'push' requires changes to push" 21 | ( 22 | cd "$OWNER/bar" 23 | git subrepo --quiet clean foo 24 | ) 25 | } 26 | 27 | done_testing 1 28 | 29 | teardown 30 | -------------------------------------------------------------------------------- /test/push-squash.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | # Make various changes to the repos for testing subrepo push: 12 | ( 13 | # In the main repo: 14 | cd "$OWNER/foo" 15 | 16 | # Clone the subrepo into a subdir 17 | git subrepo clone "$UPSTREAM/bar" 18 | 19 | # Make a series of commits: 20 | add-new-files bar/FooBar1 21 | add-new-files bar/FooBar2 22 | modify-files bar/FooBar1 23 | add-new-files ./FooBar 24 | modify-files ./FooBar bar/FooBar2 25 | ) &> /dev/null || die 26 | 27 | # Do the subrepo push and test the output: 28 | { 29 | message=$( 30 | cd "$OWNER/foo" 31 | git subrepo push bar --squash 32 | ) 33 | 34 | # Test the output: 35 | is "$message" \ 36 | "Subrepo 'bar' pushed to '$UPSTREAM/bar' (master)." \ 37 | 'push message is correct' 38 | } 39 | 40 | ( 41 | cd "$OWNER/bar" 42 | git pull 43 | ) &> /dev/null || die 44 | 45 | # Check that all commits arrived in subrepo 46 | test-commit-count "$OWNER/bar" HEAD 3 47 | 48 | test-exists \ 49 | "$OWNER/bar/Bar" \ 50 | "$OWNER/bar/FooBar1" \ 51 | "$OWNER/bar/FooBar2" \ 52 | "!$OWNER/bar/.gitrepo" \ 53 | 54 | done_testing 55 | 56 | teardown 57 | -------------------------------------------------------------------------------- /test/push.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | unset GIT_{AUTHOR,COMMITTER}_{EMAIL,NAME} 10 | 11 | clone-foo-and-bar 12 | 13 | # Make various changes to the repos for testing subrepo push: 14 | ( 15 | # In the main repo: 16 | cd "$OWNER/foo" 17 | 18 | # Clone the subrepo into a subdir 19 | git subrepo clone "$UPSTREAM/bar" 20 | 21 | # Make a series of commits: 22 | add-new-files bar/FooBar 23 | add-new-files ./FooBar 24 | modify-files bar/FooBar 25 | modify-files ./FooBar 26 | modify-files ./FooBar bar/FooBar 27 | ) &> /dev/null || die 28 | 29 | ( 30 | cd "$OWNER/bar" 31 | add-new-files bargy 32 | git push 33 | ) &> /dev/null || die 34 | 35 | # Do the subrepo push and test the output: 36 | { 37 | message=$( 38 | cd "$OWNER/foo" 39 | git config user.name 'PushUser' 40 | git config user.email 'push@push' 41 | git subrepo pull --quiet bar 42 | git subrepo push bar 43 | ) 44 | 45 | # Test the output: 46 | is "$message" \ 47 | "Subrepo 'bar' pushed to '$UPSTREAM/bar' (master)." \ 48 | 'push message is correct' 49 | } 50 | 51 | ( 52 | cd "$OWNER/bar" 53 | git pull 54 | ) &> /dev/null || die 55 | 56 | { 57 | pullCommit=$( 58 | cd "$OWNER/bar" 59 | git log HEAD -1 --pretty='format:%an %ae %cn %ce' 60 | ) 61 | 62 | is "$pullCommit" \ 63 | "PushUser push@push PushUser push@push" \ 64 | "Pull commit has PushUser as both author and committer" 65 | } 66 | 67 | { 68 | subrepoCommit=$( 69 | cd "$OWNER/bar" 70 | git log HEAD^ -1 --pretty='format:%an %ae %cn %ce' 71 | ) 72 | 73 | is "$subrepoCommit" \ 74 | "FooUser foo@foo PushUser push@push" \ 75 | "Subrepo commits has FooUser as author but PushUser as committer" 76 | } 77 | 78 | # Check that all commits arrived in subrepo 79 | test-commit-count "$OWNER/bar" HEAD 7 80 | 81 | # Test foo/bar/.gitrepo file contents: 82 | # shellcheck disable=2034 83 | gitrepo=$OWNER/foo/bar/.gitrepo 84 | { 85 | foo_pull_commit=$(cd "$OWNER/foo"; git rev-parse HEAD^) 86 | bar_head_commit=$(cd "$OWNER/bar"; git rev-parse HEAD) 87 | test-gitrepo-field "remote" "$UPSTREAM/bar" 88 | test-gitrepo-field "branch" "master" 89 | test-gitrepo-field "commit" "$bar_head_commit" 90 | test-gitrepo-field "parent" "$foo_pull_commit" 91 | test-gitrepo-field "cmdver" "$(git subrepo --version)" 92 | } 93 | 94 | ( 95 | # In the main repo: 96 | cd "$OWNER/foo" 97 | add-new-files bar/FooBar2 98 | modify-files bar/FooBar 99 | ) &> /dev/null || die 100 | 101 | { 102 | message=$( 103 | cd "$OWNER/foo" 104 | git subrepo push bar 105 | ) 106 | 107 | # Test the output: 108 | is "$message" \ 109 | "Subrepo 'bar' pushed to '$UPSTREAM/bar' (master)." \ 110 | 'push message is correct' 111 | } 112 | 113 | # Pull the changes from UPSTREAM/bar in OWNER/bar 114 | ( 115 | cd "$OWNER/bar" 116 | git pull 117 | ) &> /dev/null || die 118 | 119 | test-exists \ 120 | "$OWNER/bar/Bar" \ 121 | "$OWNER/bar/FooBar" \ 122 | "$OWNER/bar/bard/" \ 123 | "$OWNER/bar/bargy" \ 124 | "!$OWNER/bar/.gitrepo" \ 125 | 126 | ( 127 | # In the main repo: 128 | cd "$OWNER/foo" 129 | add-new-files bar/FooBar3 130 | modify-files bar/FooBar 131 | git subrepo push bar 132 | add-new-files bar/FooBar4 133 | modify-files bar/FooBar3 134 | ) &> /dev/null || die 135 | 136 | { 137 | message=$( 138 | cd "$OWNER/foo" 139 | git subrepo push bar 140 | ) 141 | 142 | # Test the output: 143 | is "$message" \ 144 | "Subrepo 'bar' pushed to '$UPSTREAM/bar' (master)." \ 145 | 'Seqential pushes are correct' 146 | } 147 | 148 | ( 149 | # In the subrepo 150 | cd "$OWNER/bar" 151 | git pull 152 | add-new-files barBar2 153 | git push 154 | ) &> /dev/null || die 155 | 156 | ( 157 | # In the main repo: 158 | cd "$OWNER/foo" 159 | add-new-files bar/FooBar5 160 | modify-files bar/FooBar3 161 | ) &> /dev/null || die 162 | 163 | { 164 | message=$( 165 | cd "$OWNER/foo" 166 | git subrepo push bar 2>&1 || true 167 | ) 168 | 169 | # Test the output: 170 | is "$message" \ 171 | "git-subrepo: There are new changes upstream, you need to pull first." \ 172 | 'Stopped by other push' 173 | } 174 | 175 | done_testing 176 | 177 | teardown 178 | -------------------------------------------------------------------------------- /test/rebase.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | # Setup foo with 2 branches, one before the subrepo 10 | # is added and one after so that we can rebase 11 | # thus destroying the parent in two ways. The first 12 | # destroys the second parent, but leave a reference to 13 | # a merge point. The second such that no history of 14 | # a merge point exists. 15 | 16 | clone-foo-and-bar 17 | 18 | ( 19 | cd "$OWNER/foo" 20 | git switch -c branch1 21 | add-new-files foo1 22 | subrepo-clone-bar-into-foo 23 | git branch branch2 24 | add-new-files foo2 25 | ) &> /dev/null || die 26 | 27 | ( 28 | cd "$OWNER/bar" 29 | add-new-files bar2 30 | git push 31 | ) &> /dev/null || die 32 | 33 | ( 34 | cd "$OWNER/foo" 35 | # Rebasing onto this merge point will still 36 | # be able to find the merge point at branch1 37 | git subrepo pull bar 38 | ) 39 | 40 | ( 41 | cd "$OWNER/foo" 42 | git switch branch2 43 | add-new-files foo-branch2 44 | git switch branch1 45 | git rebase branch2 46 | ) &> /dev/null || die 47 | 48 | # Force subrepo to search of the parent SHA, 49 | # validate it found the prevous merge point 50 | { 51 | output=$( 52 | cd "$OWNER/foo" 53 | git subrepo clean --force --all 54 | catch git subrepo branch bar 55 | ) 56 | 57 | like "$output" "caused by a rebase" \ 58 | "subrepo detected merge point" 59 | } 60 | 61 | done_testing 1 62 | 63 | teardown 64 | -------------------------------------------------------------------------------- /test/reclone.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | ( 12 | cd "$OWNER/foo" 13 | git subrepo --quiet clone "$UPSTREAM/bar" 14 | ) 15 | 16 | test-exists \ 17 | "$OWNER/foo/bar/bard/" 18 | 19 | # Test that reclone is not done if not needed. 20 | export XYZ=1 21 | is "$( 22 | cd "$OWNER/foo" 23 | git subrepo --force clone "$UPSTREAM/bar" 24 | )" \ 25 | "Subrepo 'bar' is up to date." \ 26 | "No reclone if same commit" 27 | 28 | # Test that reclone of a different ref works. 29 | ( 30 | cd "$OWNER/foo" 31 | git subrepo --quiet clone --force "$UPSTREAM/bar" --branch=refs/tags/A 32 | ) 33 | 34 | is "$(git -C "$OWNER"/foo subrepo config bar branch)" \ 35 | "Subrepo 'bar' option 'branch' has value 'refs/tags/A'." 36 | test-exists \ 37 | "!$OWNER/foo/bar/bard/" 38 | 39 | # Test that reclone back to (implicit) master works. 40 | ( 41 | cd "$OWNER/foo" 42 | git subrepo --quiet clone -f "$UPSTREAM/bar" 43 | ) 44 | 45 | is "$(git -C "$OWNER"/foo subrepo config bar branch)" \ 46 | "Subrepo 'bar' option 'branch' has value 'master'." 47 | test-exists \ 48 | "$OWNER/foo/bar/bard/" 49 | 50 | done_testing 51 | 52 | teardown 53 | -------------------------------------------------------------------------------- /test/repo/bar/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/repo/bar/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | -------------------------------------------------------------------------------- /test/repo/bar/objects/1f/0c4b264caed0126814a0ede851a1e0b4e16ae6: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/bar/objects/1f/0c4b264caed0126814a0ede851a1e0b4e16ae6 -------------------------------------------------------------------------------- /test/repo/bar/objects/87/46903fdb1b9c2101377880125917c2e05b4d69: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/bar/objects/87/46903fdb1b9c2101377880125917c2e05b4d69 -------------------------------------------------------------------------------- /test/repo/bar/objects/94/c86ffc745232d89f78c6f895e11e71272518db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/bar/objects/94/c86ffc745232d89f78c6f895e11e71272518db -------------------------------------------------------------------------------- /test/repo/bar/objects/c6/76c57b6576743fa56278527aa60ebd2e202a7c: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/bar/objects/c6/76c57b6576743fa56278527aa60ebd2e202a7c -------------------------------------------------------------------------------- /test/repo/bar/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/bar/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 -------------------------------------------------------------------------------- /test/repo/bar/objects/f6/2a8ff3feadf39b0a98f1a86ec6d1eb33858ee9: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/bar/objects/f6/2a8ff3feadf39b0a98f1a86ec6d1eb33858ee9 -------------------------------------------------------------------------------- /test/repo/bar/refs/heads/master: -------------------------------------------------------------------------------- 1 | 94c86ffc745232d89f78c6f895e11e71272518db 2 | -------------------------------------------------------------------------------- /test/repo/bar/refs/tags/A: -------------------------------------------------------------------------------- 1 | f62a8ff3feadf39b0a98f1a86ec6d1eb33858ee9 2 | -------------------------------------------------------------------------------- /test/repo/foo/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/repo/foo/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = true 5 | -------------------------------------------------------------------------------- /test/repo/foo/objects/a0/f4cdaaf533a936296cdebbed8206c3b9ededa8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/foo/objects/a0/f4cdaaf533a936296cdebbed8206c3b9ededa8 -------------------------------------------------------------------------------- /test/repo/foo/objects/e2/1291a1ad392a9d4c51dd9586804f1467b28afd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/foo/objects/e2/1291a1ad392a9d4c51dd9586804f1467b28afd -------------------------------------------------------------------------------- /test/repo/foo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/foo/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 -------------------------------------------------------------------------------- /test/repo/foo/refs/heads/master: -------------------------------------------------------------------------------- 1 | e21291a1ad392a9d4c51dd9586804f1467b28afd 2 | -------------------------------------------------------------------------------- /test/repo/init/HEAD: -------------------------------------------------------------------------------- 1 | ref: refs/heads/master 2 | -------------------------------------------------------------------------------- /test/repo/init/config: -------------------------------------------------------------------------------- 1 | [core] 2 | repositoryformatversion = 0 3 | filemode = true 4 | bare = false 5 | logallrefupdates = true 6 | -------------------------------------------------------------------------------- /test/repo/init/objects/11/523f5dcf03b4c89b592dc8a3d0308f68da2386: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/11/523f5dcf03b4c89b592dc8a3d0308f68da2386 -------------------------------------------------------------------------------- /test/repo/init/objects/14/2addf8ec5f37334e837440122c62f2c68a29ad: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/14/2addf8ec5f37334e837440122c62f2c68a29ad -------------------------------------------------------------------------------- /test/repo/init/objects/32/5180321750a21cd7a4e7ecda319e557a4f6a09: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/32/5180321750a21cd7a4e7ecda319e557a4f6a09 -------------------------------------------------------------------------------- /test/repo/init/objects/3d/918c6901c02f43af5d31779dd5e1f9166aeb36: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/3d/918c6901c02f43af5d31779dd5e1f9166aeb36 -------------------------------------------------------------------------------- /test/repo/init/objects/3e/4cb596066dce63ba4d047abddb677389b65e19: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/3e/4cb596066dce63ba4d047abddb677389b65e19 -------------------------------------------------------------------------------- /test/repo/init/objects/4b/6e53022e7a04f07887697e4f3d7c377fd9822b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/4b/6e53022e7a04f07887697e4f3d7c377fd9822b -------------------------------------------------------------------------------- /test/repo/init/objects/58/931fc1bd559b59c41ea738fc7ad04f9ad01bd3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/58/931fc1bd559b59c41ea738fc7ad04f9ad01bd3 -------------------------------------------------------------------------------- /test/repo/init/objects/5e/c0c28e1b806f25efdca18fcf7a74b49c3755bd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/5e/c0c28e1b806f25efdca18fcf7a74b49c3755bd -------------------------------------------------------------------------------- /test/repo/init/objects/75/fa6584e748f57eff06eebdc55e9ac21d4fcbf2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/75/fa6584e748f57eff06eebdc55e9ac21d4fcbf2 -------------------------------------------------------------------------------- /test/repo/init/objects/80/2d5edbd5e1cb7fca82b5bd38e7c8a0a496fb20: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/80/2d5edbd5e1cb7fca82b5bd38e7c8a0a496fb20 -------------------------------------------------------------------------------- /test/repo/init/objects/94/7b3d714c38791e95ad6f928b48c98bb8708acd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/94/7b3d714c38791e95ad6f928b48c98bb8708acd -------------------------------------------------------------------------------- /test/repo/init/objects/95/e1f2df3f4d5f3d7a60588c25a7ca8a913d3c2a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/95/e1f2df3f4d5f3d7a60588c25a7ca8a913d3c2a -------------------------------------------------------------------------------- /test/repo/init/objects/b1/5f4a7666baf40d949548ead946a3370e273479: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/b1/5f4a7666baf40d949548ead946a3370e273479 -------------------------------------------------------------------------------- /test/repo/init/objects/c3/ee8978c4c5d84c3b7d00ba8e5906933d027882: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/c3/ee8978c4c5d84c3b7d00ba8e5906933d027882 -------------------------------------------------------------------------------- /test/repo/init/objects/c8/b0bffbc405ef3fad7354ff833fbec36d67ddfa: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/c8/b0bffbc405ef3fad7354ff833fbec36d67ddfa -------------------------------------------------------------------------------- /test/repo/init/objects/dd/8bdb934ec848137f011fe423b185505c343626: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/dd/8bdb934ec848137f011fe423b185505c343626 -------------------------------------------------------------------------------- /test/repo/init/objects/e2/9be58c767cfeb27235c995d293a7d71aac0135: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/e2/9be58c767cfeb27235c995d293a7d71aac0135 -------------------------------------------------------------------------------- /test/repo/init/objects/ee/1224401fc6aac595145fa727dcf6706ac8aec1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/ee/1224401fc6aac595145fa727dcf6706ac8aec1 -------------------------------------------------------------------------------- /test/repo/init/objects/f1/cc1a657b2e805c400f5dcaaa76bd29c6178b1b: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ingydotnet/git-subrepo/4f60dd7da6493d3ab0bd1523f7885c7fa73d3374/test/repo/init/objects/f1/cc1a657b2e805c400f5dcaaa76bd29c6178b1b -------------------------------------------------------------------------------- /test/repo/init/refs/heads/master: -------------------------------------------------------------------------------- 1 | 325180321750a21cd7a4e7ecda319e557a4f6a09 2 | -------------------------------------------------------------------------------- /test/status.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | clone-foo-and-bar 10 | 11 | ( 12 | cd "$OWNER"/foo 13 | git subrepo clone "$UPSTREAM"/bar 14 | git subrepo clone "$UPSTREAM"/foo bar/foo 15 | mkdir lib 16 | git subrepo clone "$UPSTREAM"/bar lib/bar 17 | git subrepo clone "$UPSTREAM"/foo lib/bar/foo 18 | ) &> /dev/null || die 19 | 20 | { 21 | output=$( 22 | cd "$OWNER"/foo 23 | git subrepo status --all 24 | ) 25 | 26 | like "$output" "2 subrepos:" \ 27 | "'status' intro ok" 28 | 29 | like "$output" "Git subrepo 'bar':" \ 30 | "bar is in 'status'" 31 | 32 | like "$output" "Git subrepo 'lib/bar':" \ 33 | "lib/bar is in 'status'" 34 | 35 | unlike "$output" "Git subrepo 'bar/foo':" \ 36 | "bar/foo is not in 'status'" 37 | 38 | unlike "$output" "Git subrepo 'lib/bar/foo':" \ 39 | "lib/bar/foo is not in 'status'" 40 | } 41 | 42 | { 43 | output=$( 44 | cd "$OWNER"/foo 45 | git subrepo status --ALL 46 | ) 47 | 48 | like "$output" "4 subrepos:" \ 49 | "'status --ALL' intro ok" 50 | 51 | like "$output" "Git subrepo 'bar':" \ 52 | "bar is in 'status --ALL'" 53 | 54 | like "$output" "Git subrepo 'lib/bar':" \ 55 | "lib/bar is in 'status --ALL'" 56 | 57 | like "$output" "Git subrepo 'bar/foo':" \ 58 | "bar/foo is in 'status --ALL'" 59 | 60 | like "$output" "Git subrepo 'lib/bar/foo':" \ 61 | "lib/bar/foo is in 'status --ALL'" 62 | } 63 | 64 | { 65 | output=$( 66 | cd "$OWNER"/foo 67 | git subrepo status --all 68 | ) 69 | 70 | like "$output" "2 subrepos:" \ 71 | "'status --all' intro ok" 72 | 73 | like "$output" "Git subrepo 'bar':" \ 74 | "bar is in 'status --all'" 75 | 76 | like "$output" "Git subrepo 'lib/bar':" \ 77 | "lib/bar is in 'status --all'" 78 | 79 | unlike "$output" "Git subrepo 'bar/foo':" \ 80 | "bar/foo is not in 'status --all'" 81 | 82 | unlike "$output" "Git subrepo 'lib/bar/foo':" \ 83 | "lib/bar/foo is not in 'status --all'" 84 | } 85 | 86 | done_testing 15 87 | 88 | teardown 89 | -------------------------------------------------------------------------------- /test/submodule.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Test that a subrepo that contains a submodule retains the submodule reference 4 | # so that the tree hash stays the same. (However, the subrepo's submodule won't 5 | # be directly usable since the .gitmodules file isn't in the top level.) 6 | 7 | set -e 8 | 9 | source test/setup 10 | 11 | use Test::More 12 | 13 | clone-foo-and-bar 14 | 15 | # Add submodule reference along with a new file to the bar repo: 16 | ( 17 | cd "$OWNER/bar" 18 | git clone ../foo submodule 19 | add-new-files file 20 | git add submodule file 21 | git commit --amend -C HEAD 22 | ) &> /dev/null || die 23 | 24 | ( 25 | cd "$OWNER/foo" 26 | git subrepo clone ../bar 27 | ) &> /dev/null || die 28 | 29 | ( 30 | cd "$OWNER/bar" 31 | modify-files file 32 | ) &> /dev/null || die 33 | 34 | { 35 | is "$( 36 | cd "$OWNER/foo" 37 | git subrepo pull bar 38 | )" \ 39 | "Subrepo 'bar' pulled from '../bar' (master)." \ 40 | 'subrepo pull command output is correct' 41 | } 42 | 43 | done_testing 44 | 45 | teardown 46 | -------------------------------------------------------------------------------- /test/zsh.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | source test/setup 6 | 7 | use Test::More 8 | 9 | if ! command -v docker >/dev/null; then 10 | plan skip_all "The 'docker' utility is not installed" 11 | fi 12 | 13 | for zsh_version in 5.8 5.6 5.0.1 4.3.11; do 14 | error=$( 15 | docker run --rm -it \ 16 | --volume="$PWD:/git-subrepo" \ 17 | --entrypoint='' \ 18 | "zshusers/zsh:$zsh_version" \ 19 | zsh -c 'source /git-subrepo/.rc 2>&1' 20 | ) || true 21 | 22 | is "$error" "" "'source.rc' works for zsh-$zsh_version" 23 | done 24 | 25 | done_testing 26 | 27 | # vim: set ft=sh: 28 | --------------------------------------------------------------------------------