Alphabetical list of surnames of everyone who ever committed to this repository.
Auto-generated from tools/list_CONTRIBUTORS.
+Skurikhin Alexander <a@skurih.in>
Eric Bouchut <ebouchut@gmail.com>
Dridi Boukelmoune <dridi.boukelmoune@gmail.com>
Rob Cornish <jrmcornish@gmail.com>
Vincent Demeester <vincent@demeester.fr>
+Mert Dirik <mertdirik@gmail.com>
+Jeff Fein-Worton <jeff@fein-worton.com>
Thomas Ferris Nicolaisen <tfnico@gmail.com>
martin f. krafft <madduck@madduck.net>
Alessandro Ghedini <alessandro@ghedini.me>
+Thorsten Glaser <tg@mirbsd.org>
+G.raud <graud@gmx.com>
Mikhail Gusarov <dottedmag@dottedmag.net>
Valentin Haenel <valentin.haenel@gmx.de>
Richard Hartmann <richih@debian.org>
Gregor Jasny <gjasny@googlemail.com>
+Errietta Kostala <errietta@errietta.me>
+Yuval Langer <yuval.langer@gmail.com>
Caleb Maclennan <caleb@alerque.com>
Markus Martin <markus@archwyrm.net>
mek-apelsin <mek@pels.in>
Dieter Plaetinck <dieter@plaetinck.be>
Corey Quinn <corey@sequestered.net>
Pavlos Ratis <dastergon@gentoo.org>
+Dewey Sasser <dewey@sasser.com>
Gernot Schulz <post@gernot-schulz.com>
+Aaron Schumacher <ajschumacher@gmail.com>
+Andrew Schwartzmeyer <andrew@schwartzmeyer.com>
+Dato Simó <dato@net.com.org.es>
Alexander Skurikhin <a.skurihin@gmail.com>
Jonathan Sternberg <jonathansternberg@gmail.com>
Frank Terbeck <ft@bewatermyfriend.org>
+mirabilos <tg@debian.org>
Aaron VonderHaar <gruen0aermel@gmail.com>
Tony <zearin@gonk.net>
-PREFIX=/usr
+PREFIX?=/usr
DOCDIR_PREFIX=$(PREFIX)/share/doc
DOCDIR=$(DOCDIR_PREFIX)/$(self)
ZSHDIR=$(PREFIX)/share/zsh/vendor-completions
@if which git > /dev/null ; then :; else echo "'git' not found, exiting..."; exit 1; fi
moo:
- @if [ -x /usr/games/cowsay ]; then /usr/games/cowsay "I hope you're happy now..."; fi
+ @ which cowsay >/dev/null 2>&1 && cowsay "I hope you're happy now..."
# Index
-1. [30 second howto](#30-second-howto)
+1. [30 Second How-to](#30-second-how-to)
2. [Introduction](#introduction)
-3. [Usage Exmaples](#usage-examples)
-4. [Overview](#overview)
-5. [Getting Started](#getting-started)
-6. [Contact](#contact)
+3. [Contact](#contact)
-# 30 second howto
+# 30 Second How-to
While it may appear that there's an overwhelming amount of documentation and
while the explanation of the concepts behind `vcsh` needs to touch a few gory
or interfering otherwise. By default, all Git repositories maintained via
`vcsh` store the actual files in `$HOME` but you can override this setting if
you want to.
+
All this means that you can have one repository per application or application
family, i.e. `zsh`, `vim`, `ssh`, etc. This, in turn, allows you to clone
custom sets of configurations onto different machines or even for different
`ssh` on your personal and your work machines.
A lot of modern UNIX-based systems offer packages for `vcsh`. In case yours
-does not read `INSTALL.md` for install instructions or `PACKAGING.md` to create
-a package, yourself. If you do end up packaging `vcsh` please let us know so we
-can give you your own packaging branch in the upstream repository.
+does not, read [INSTALL.md](doc/INSTALL.md) for install instructions or
+[PACKAGING.md](doc/PACKAGING.md) to create a package yourself. If you do end
+up packaging `vcsh` please let us know so we can give you your own packaging
+branch in the upstream repository.
## Talks
[on the author's talk page][talks].
-# Usage Examples
-
-There are three different ways to interact with `vcsh` repositories; this
-section will only show the simplest and easiest way.
-Certain more advanced use cases require the other two ways, but don't worry
-about this for now. If you never even bother playing with the other two
-modes you will still be fine.
-`vcsh enter` and `vcsh run` will be covered in later sections.
-
-
-| Task | Command |
-| ----------------------------------------------------- | ------------------------------------------------- |
-| _Initialize a new repository called "vim"_ | `vcsh init vim` |
-| _Clone an existing repository_ | `vcsh clone <remote> <repository_name>` |
-| _Add files to repository "vim"_ | `vcsh vim add ~/.vimrc ~/.vim` |
-| | `vcsh vim commit -m 'Update Vim configuration'` |
-| _Add a remote for repository "vim"_ | `vcsh vim remote add origin <remote>` |
-| | `vcsh vim push origin master:master` |
-| | `vcsh vim branch --track master origin/master` |
-| _Push to remote of repository "vim"_ | `vcsh vim push` |
-| _Pull from remote of repository "vim"_ | `vcsh vim pull` |
-| _Show status of changed files in all repositories_ | `vcsh status` |
-| _Pull from all repositories_ | `vcsh pull` |
-| _Push to all repositories_ | `vcsh push` |
-
-
-# Overview
-
-## From zero to vcsh
-
-You put a lot of effort into your configuration and want to both protect and
-distribute this configuration.
-
-Most people who decide to put their dotfiles under version control start with a
-single repository in `$HOME`, adding all their dotfiles (and possibly more)
-to it. This works, of course, but can become a nuisance as soon as you try to
-manage more than one host.
-
-The next logical step is to create single-purpose repositories in, for example,
-`~/.dotfiles` and to create symbolic links into `$HOME`. This gives you the
-flexibility to check out only certain repositories on different hosts. The
-downsides of this approach are the necessary manual steps of cloning and
-symlinking the individual repositories.
-
-`vcsh` takes this approach one step further. It enables single-purpose
-repositories and stores them in a hidden directory. However, it does not create
-symbolic links in `$HOME`; it puts the actual files right into `$HOME`.
-
-As `vcsh` allows you to put an arbitrary number of distinct repositories into
-your `$HOME`, you will end up with a lot of repositories very quickly.
-
-To manage both `vcsh` and other repositories, we suggest using [mr](mr). `mr`
-takes care of pulling in and pushing out new data for a variety of version
-control systems.
-
-`vcsh` was designed with [mr][mr], a tool to manage Multiple Repositories, in
-mind and the two integrate very nicely. `mr` has native support for `vcsh`
-repositories and to `vcsh`, `mr` is just another configuration to track.
-This make setting up any new machine a breeze. It takes literally less than
-five minutes to go from standard installation to fully set up system
-
-This is where `mr` comes in. While the use of `mr` is technically
-optional, but it will be an integral part of the proposed system that follows.
-
-## Default Directory Layout
-
-To illustrate, this is what a possible directory structure looks like.
-
- $HOME
- |-- $XDG_CONFIG_HOME (defaults to $HOME/.config)
- | |-- mr
- | | |-- available.d
- | | | |-- zsh.vcsh
- | | | |-- gitconfigs.vcsh
- | | | |-- lftp.vcsh
- | | | |-- offlineimap.vcsh
- | | | |-- s3cmd.vcsh
- | | | |-- tmux.vcsh
- | | | |-- vim.vcsh
- | | | |-- vimperator.vcsh
- | | | `-- snippets.git
- | | `-- config.d
- | | |-- zsh.vcsh -> ../available.d/zsh.vcsh
- | | |-- gitconfigs.vcsh -> ../available.d/gitconfigs.vcsh
- | | |-- tmux.vcsh -> ../available.d/tmux.vcsh
- | | `-- vim.vcsh -> ../available.d/vim.vcsh
- | `-- vcsh
- | |-- config
- | `-- repo.d
- | |-- zsh.git -----------+
- | |-- gitconfigs.git |
- | |-- tmux.git |
- | `-- vim.git |
- |-- [...] |
- |-- .zshrc <----------------------+
- |-- .gitignore.d
- | `-- zsh
- |-- .mrconfig
- `-- .mrtrust
-
-### available.d
-
-The files you see in $XDG\_CONFIG\_HOME/mr/available.d are mr configuration files
-that contain the commands to manage (checkout, update etc.) a single
-repository. vcsh repo configs end in .vcsh, git configs end in .git, etc. This
-is optional and your preference. For example, this is what a zsh.vcsh
-with read-only access to my zshrc repo looks likes. I.e. in this specific
-example, push can not work as you will be using the author's repository. This
-is for demonstration, only. Of course, you are more than welcome to clone from
-this repository and fork your own.
-
- [$XDG_CONFIG_HOME/vcsh/repo.d/zsh.git]
- checkout = vcsh clone 'git://github.com/RichiH/zshrc.git' zsh
- update = vcsh zsh pull
- push = vcsh zsh push
- status = vcsh zsh status
- gc = vcsh zsh gc
-
-### config.d
-
-$XDG\_CONFIG\_HOME/mr/available.d contains *all available* repositories. Only
-files/links present in mr/config.d, however, will be used by mr. That means
-that in this example, only the zsh, gitconfigs, tmux and vim repositories will
-be checked out. A simple `mr update` run in $HOME will clone or update those
-four repositories listed in config.d.
-
-### ~/.mrconfig
-
-Finally, ~/.mrconfig will tie together all those single files which will allow
-you to conveniently run `mr up` etc. to manage all repositories. It looks like
-this:
-
- [DEFAULT]
- include = cat ${XDG_CONFIG_HOME:-$HOME/.config}/mr/config.d/*
-
-### repo.d
-
-$XDG\_CONFIG\_HOME/vcsh/repo.d is the directory where all git repositories which
-are under vcsh's control are located. Since their working trees are configured
-to be in $HOME, the files contained in those repositories will be put in $HOME
-directly.
-Of course, [mr] [mr] will work with this layout if configured according to this
-document (see above).
-
-vcsh will check if any file it would want to create exists. If it exists, vcsh
-will throw a warning and exit. Move away your old config and try again.
-Optionally, merge your local and your global configs afterwards and push with
-`vcsh foo push`.
-
-## Moving into a New Host
-
-To illustrate further, the following steps could move your desired
-configuration to a new host.
-
-1. Clone the mr repository (containing available.d, config.d etc.); for
- example: `vcsh clone git://github.com/RichiH/vcsh_mr_template.git mr`
-2. Choose your repositories by linking them in config.d (or go with the default
- you may have already configured by adding symlinks to git).
-3. Run mr to clone the repositories: `cd; mr update`.
-4. Done.
-
-Hopefully the above could help explain how this approach saves time by
-
-1. making it easy to manage, clone and update a large number of repositories
- (thanks to mr) and
-2. making it unnecessary to create symbolic links in $HOME (thanks to vcsh).
-
-If you want to give vcsh a try, follow the instructions below.
-
-
-# Getting Started
-
-Below, you will find a few different methods for setting up vcsh:
-
-1. The Template Way
-2. The Steal-from-Template Way
-3. The Manual Way
-
-### The Template Way
-
-#### Prerequisites
-
-Make sure none of the following files and directories exist for your test
-(user). If they do, move them away for now:
-
-* ~/.gitignore.d
-* ~/.mrconfig
-* $XDG\_CONFIG\_HOME/mr/available.d/mr.vcsh
-* $XDG\_CONFIG\_HOME/mr/available.d/zsh.vcsh
-* $XDG\_CONFIG\_HOME/mr/config.d/mr.vcsh
-* $XDG\_CONFIG\_HOME/vcsh/repo.d/mr.git/
-
-All of the files are part of the template repository, the directory is where
-the template will be stored.
-
- apt-get install mr
-
-### Install vcsh
-
-#### Debian
-
-If you are using Debian Squeeze, you will need to enable backports.
-From Wheezy onwards, you can install it directly:
-
- apt-get install vcsh
-
-#### Gentoo
-
-To install vcsh in Gentoo Linux just give the following command as root:
-
- emerge dev-vcs/vcsh
-
-#### Arch Linux
-
-vcsh is availabe via [AUR](https://aur.archlinux.org/packages.php?ID=54164)
-and further documentation about the use of AUR is available
-[on Arch's wiki](https://wiki.archlinux.org/index.php/Arch_User_Repository).
-
- cd /var/abs/local/
- wget https://aur.archlinux.org/packages/vc/vcsh-git/vcsh-git.tar.gz
- tar xfz vcsh-git.tar.gz
- cd vcsh-git
- makepkg -s
- pacman -U vcsh*.pkg.tar.xz
-
-#### From source
-
- # choose a location for your checkout
- mkdir -p ~/work/git
- cd ~/work/git
- git clone git://github.com/RichiH/vcsh.git
- cd vcsh
- sudo ln -s vcsh /usr/local/bin # or add it to your PATH
- cd
-
-#### Clone the Template
-
- vcsh clone git://github.com/RichiH/vcsh_mr_template.git mr
-
-#### Enable Your Test Repository
-
- mv ~/.zsh ~/zsh.bak
- mv ~/.zshrc ~/zshrc.bak
- cd $XDG_CONFIG_HOME/mr/config.d/
- ln -s ../available.d/zsh.vcsh . # link, and thereby enable, the zsh repository
- cd
- mr up
-
-#### Set Up Your Own Repositories
-
-Now, it's time to edit the template config and fill it with your own remotes:
-
- vim $XDG_CONFIG_HOME/mr/available.d/mr.vcsh
- vim $XDG_CONFIG_HOME/mr/available.d/zsh.vcsh
-
-And then create your own stuff:
-
- vcsh init foo
- vcsh foo add bar baz quux
- vcsh foo remote add origin git://quuux
- vcsh foo commit
- vcsh foo push
-
- cp $XDG_CONFIG_HOME/mr/available.d/mr.vcsh $XDG_CONFIG_HOME/mr/available.d/foo.vcsh
- vim $XDG_CONFIG_HOME/mr/available.d/foo.vcsh # add your own repo
-
-Done!
-
-### The Steal-from-Template Way
-
-You're welcome to clone the example repository:
-
- vcsh clone git://github.com/RichiH/vcsh_mr_template.git mr
- # make sure 'include = cat /usr/share/mr/vcsh' points to an exiting file
- vim .mrconfig
-
-Look around in the clone. It should be reasonably simple to understand. If not,
-poke me, RichiH, on Freenode (query) or OFTC (#vcs-home).
-
-
-### The Manual Way
-
-This is how my old setup procedure looked like. Adapt it to your own style or
-copy mine verbatim, either is fine.
-
- # Create workspace
- mkdir -p ~/work/git
- cd !$
-
- # Clone vcsh and make it available
- git clone git://github.com/RichiH/vcsh.git vcsh
- sudo ln -s ~/work/git/vcsh/vcsh /usr/bin/local
- hash -r
-
-Grab my mr config. see below for details on how I set this up
-
- vcsh clone ssh://<remote>/mr.git
- cd $XDG_CONFIG_HOME/mr/config.d/
- ln -s ../available.d/* .
-
-
-mr is used to actually retrieve configs, etc
-
- ~ % cat ~/.mrconfig
- [DEFAULT]
- # adapt /usr/share/mr/vcsh to your system if needed
- include = cat /usr/share/mr/vcsh
- include = cat $XDG_CONFIG_HOME/mr/config.d/*
- ~ % echo $XDG_CONFIG_HOME
- /home/richih/.config
- ~ % ls $XDG_CONFIG_HOME/mr/available.d # random selection of my repos
- git-annex gitk.vcsh git.vcsh ikiwiki mr.vcsh reportbug.vcsh snippets.git wget.vcsh zsh.vcsh
- ~ %
- # then simply ln -s whatever you want on your local machine from
- # $XDG_CONFIG_HOME/mr/available.d to $XDG_CONFIG_HOME/mr/config.d
- ~ % cd
- ~ % mr -j 5 up
-
-
-# mr usage ; will be factored out & rewritten
-
-### Keeping repositories Up-to-Date
-
-This is the beauty of it all. Once you are set up, just run:
-
- mr up
- mr push
-
-Neat.
-
-### Making Changes
-
-After you have made some changes, for which you would normally use `git add`
-and `git commit`, use the vcsh wrapper (like above):
-
- vcsh foo add bar baz quux
- vcsh foo commit
- vcsh foo push
-
-### Using vcsh without mr
-
-vcsh encourages you to use [mr][mr]. It helps you manage a large number of
-repositories by running the necessary vcsh commands for you. You may choose not
-to use mr, in which case you will have to run those commands manually or by
-other means.
-
-
-To initialize a new repository: `vcsh init zsh`
-
-To clone a repository: `vcsh clone ssh://<remote>/zsh.git`
-
-To interact with a repository, use the regular Git commands, but prepend them
-with `vcsh run $repository_name`. For example:
-
- vcsh zsh status
- vcsh zsh add .zshrc
- vcsh zsh commit
-
-Obviously, without mr keeping repositories up-to-date, it will have to be done
-manually. Alternatively, you could try something like this:
-
- for repo in `vcsh list`; do
- vcsh run $repo git pull;
- done
-
-
# Contact
There are several ways to get in touch with the author and a small but committed
* Pull requests or issues on [https://github.com/RichiH/vcsh][vcsh]
-[mr]: http://kitenet.net/~joey/code/mr/
+[myrepos]: http://myrepos.branchable.com/
[talks]: http://richardhartmann.de/talks/
[vcsh]: https://github.com/RichiH/vcsh
[vcs-home-list]: http://lists.madduck.net/listinfo/vcs-home
(( CURRENT == 2 )) && __vcsh_repositories
}
+function _vcsh-list-untracked () {
+ _nothing
+}
+
function _vcsh-pull () {
_nothing
}
fi
}
+function _vcsh-status () {
+ (( CURRENT == 2 )) && __vcsh_repositories
+}
+
function _vcsh-upgrade () {
(( CURRENT == 2 )) && __vcsh_repositories
}
subcommands=(
"clone:clone an existing repository"
+ "commit:commit in all repositories"
"delete:delete an existing repository"
"enter:enter repository; spawn new <\$SHELL>"
"help:display help"
"list:list all local vcsh repositories"
"list-tracked:list all files tracked by vcsh"
"list-tracked-by:list files tracked by a repository"
+ "list-untracked:list all files not tracked by vcsh"
"pull:pull from all vcsh remotes"
+ "push:push to vcsh remotes"
"rename:rename a repository"
"run:run command with <\$GIT_DIR> and <\$GIT_WORK_TREE> set"
+ "status:show statuses of all/one vcsh repositories"
"upgrade:upgrade repository to currently recommended settings"
"version:print version information"
"which:find <substring> in name of any tracked file"
+2014-10-26 Richard Hartmann <richih.mailinglist@gmail.com>
+
+ * Release 1.20141026
+ * FIX fatal()
+ * Improve error handling of clone()
+
+2014-10-25 Richard Hartmann <richih.mailinglist@gmail.com>
+
+ * Release 1.20141025
+ * `vcsh which dontexist` exits 1
+ * `vcsh status` shows commits ahead/behind remote tracking branch
+ * Support overlay functions
+ * Support `vcsh list-untracked`, optionally recursively
+ * Support `vcsh list-untracked $repo`
+ * Improve error handling of clone()
+ * Rename `list-tracked-by` to `list-tracked <repo>`
+ * Support repo-specific config files
+ * Various minor improvements
+ * More moo
+
+2014-10-09 Richard Hartmann <richih.mailinglist@gmail.com>
+
+ * Display full paths in list-tracked*
+ * Lots of help improvements
+
+2014-05-08 Richard Hartmann <richih.mailinglist@gmail.com>
+
+ * Revert "Introduce static manpage as part of normal repo"
+
+2014-05-07 Richard Hartmann <richih.mailinglist@gmail.com>
+
+ * Increase portability
+ * Improve hooks
+ * Improve docs
+ * Various minor improvements
+
+2014-03-13 Richard Hartmann <richih.mailinglist@gmail.com>
+
+ * Release 1.20140313 -- the "Git won a prize" edition
+ * Initialize repos with --shared=0600
+ * Fix cloning from some spefically-named remotes
+ * Support custom gitattribute files
+ * Support filenames with whitespace in gitignores
+ * Pass out git exit codes
+ * Switch to static, pre-built manpage
+ * Improve documentation
+ * Use more portable find(1) syntax
+ * Various minor improvements
+
2013-12-29 Richard Hartmann <richih.mailinglist@gmail.com>
* Release 1.20131229
--- /dev/null
+vcsh - Version Control System for $HOME - multiple Git repositories in $HOME
+
+
+# Index
+
+1. [30 Second How-to](#30-second-how-to)
+2. [Introduction](#introduction)
+3. [Usage Examples](#usage-examples)
+4. [Overview](#overview)
+5. [Getting Started](#getting-started)
+6. [Contact](#contact)
+
+
+# 30 Second How-to
+
+While it may appear that there's an overwhelming amount of documentation and
+while the explanation of the concepts behind `vcsh` needs to touch a few gory
+details of `git` internals, getting started with `vcsh` is extremely simple.
+
+Let's say you want to version control your `vim` configuration:
+
+ vcsh init vim
+ vcsh vim add ~/.vimrc ~/.vim
+ vcsh vim commit -m 'Initial commit of my Vim configuration'
+ # optionally push your files to a remote
+ vcsh vim remote add origin <remote>
+ vcsh vim push -u origin master
+ # from now on you can push additional commits like this
+ vcsh vim push
+
+If all that looks a _lot_ like standard `git`, that's no coincidence; it's
+a design feature.
+
+
+# Introduction
+
+[vcsh][vcsh] allows you to maintain several Git repositories in one single
+directory. They all maintain their working trees without clobbering each other
+or interfering otherwise. By default, all Git repositories maintained via
+`vcsh` store the actual files in `$HOME` but you can override this setting if
+you want to.
+
+All this means that you can have one repository per application or application
+family, i.e. `zsh`, `vim`, `ssh`, etc. This, in turn, allows you to clone
+custom sets of configurations onto different machines or even for different
+users; picking and mixing which configurations you want to use where.
+For example, you may not need to have your `mplayer` configuration on a server
+or available to root and you may want to maintain different configuration for
+`ssh` on your personal and your work machines.
+
+A lot of modern UNIX-based systems offer packages for `vcsh`. In case yours
+does not read `INSTALL.md` for install instructions or `PACKAGING.md` to create
+a package, yourself. If you do end up packaging `vcsh` please let us know so we
+can give you your own packaging branch in the upstream repository.
+
+## Talks
+
+Some people found it useful to look at slides and videos explaining how `vcsh`
+works instead of working through the docs.
+All slides, videos, and further information can be found
+[on the author's talk page][talks].
+
+
+# Usage Examples
+
+There are three different ways to interact with `vcsh` repositories; this
+section will only show the simplest and easiest way.
+
+Certain more advanced use cases require the other two ways, but don't worry
+about this for now. If you never even bother playing with the other two
+modes you will still be fine.
+
+`vcsh enter` and `vcsh run` will be covered in later sections.
+
+
+| Task | Command |
+| ----------------------------------------------------- | ------------------------------------------------- |
+| _Initialize a new repository called "vim"_ | `vcsh init vim` |
+| _Clone an existing repository_ | `vcsh clone <remote> <repository_name>` |
+| _Add files to repository "vim"_ | `vcsh vim add ~/.vimrc ~/.vim` |
+| | `vcsh vim commit -m 'Update Vim configuration'` |
+| _Add a remote for repository "vim"_ | `vcsh vim remote add origin <remote>` |
+| | `vcsh vim push origin master:master` |
+| | `vcsh vim branch --track master origin/master` |
+| _Push to remote of repository "vim"_ | `vcsh vim push` |
+| _Pull from remote of repository "vim"_ | `vcsh vim pull` |
+| _Show status of changed files in all repositories_ | `vcsh status` |
+| _Pull from all repositories_ | `vcsh pull` |
+| _Push to all repositories_ | `vcsh push` |
+
+
+# Overview
+
+## From zero to vcsh
+
+You put a lot of effort into your configuration and want to both protect and
+distribute this configuration.
+
+Most people who decide to put their dotfiles under version control start with a
+single repository in `$HOME`, adding all their dotfiles (and possibly more)
+to it. This works, of course, but can become a nuisance as soon as you try to
+manage more than one host.
+
+The next logical step is to create single-purpose repositories in, for example,
+`~/.dotfiles` and to create symbolic links into `$HOME`. This gives you the
+flexibility to check out only certain repositories on different hosts. The
+downsides of this approach are the necessary manual steps of cloning and
+symlinking the individual repositories.
+
+`vcsh` takes this approach one step further. It enables single-purpose
+repositories and stores them in a hidden directory. However, it does not create
+symbolic links in `$HOME`; it puts the actual files right into `$HOME`.
+
+As `vcsh` allows you to put an arbitrary number of distinct repositories into
+your `$HOME`, you will end up with a lot of repositories very quickly.
+
+`vcsh` was designed with [myrepos][myrepos], a tool to manage Multiple
+Repositories, in mind and the two integrate very nicely. The myrepos tool
+(`mr`) has native support for `vcsh` repositories and the configuration for
+myrepos is just another set of files that you can track with `vcsh` like any
+other. This makes setting up any new machine a breeze. It can take literally
+less than five minutes to go from standard installation to fully set up system.
+
+We suggest using [myrepos][myrepos] to manage both `vcsh` and other
+repositories. The `mr` utility takes care of pulling in and pushing
+out new data for a variety of version control systems. While the use
+of myrepos is technically optional, it will be an integral part of the
+proposed system that follows. For instance, you can use
+[myrepos][myrepos] to track repositories in home such as `.emacs.d`,
+which `mr` can clone and update for you automatically. To do this,
+just add a `mr` configuration file to `available.d` with a `checkout`
+command to clone the repo, and set the [title] to the desired
+location, e.g. `$HOME/.emacs.d`. Try the `mr register` command in an
+existing repository, then view `~/.mrconfig` for an example.
+
+## Default Directory Layout
+
+To illustrate, this is what a possible directory structure looks like.
+
+ $HOME
+ |-- $XDG_CONFIG_HOME (defaults to $HOME/.config)
+ | |-- mr
+ | | |-- available.d
+ | | | |-- zsh.vcsh
+ | | | |-- gitconfigs.vcsh
+ | | | |-- lftp.vcsh
+ | | | |-- offlineimap.vcsh
+ | | | |-- s3cmd.vcsh
+ | | | |-- tmux.vcsh
+ | | | |-- vim.vcsh
+ | | | |-- vimperator.vcsh
+ | | | `-- snippets.git
+ | | `-- config.d
+ | | |-- zsh.vcsh -> ../available.d/zsh.vcsh
+ | | |-- gitconfigs.vcsh -> ../available.d/gitconfigs.vcsh
+ | | |-- tmux.vcsh -> ../available.d/tmux.vcsh
+ | | `-- vim.vcsh -> ../available.d/vim.vcsh
+ | `-- vcsh
+ | |-- config
+ | `-- repo.d
+ | |-- zsh.git -----------+
+ | |-- gitconfigs.git |
+ | |-- tmux.git |
+ | `-- vim.git |
+ |-- [...] |
+ |-- .zshrc <----------------------+
+ |-- .gitignore.d
+ | `-- zsh
+ |-- .mrconfig
+ `-- .mrtrust
+
+### available.d
+
+The files you see in $XDG\_CONFIG\_HOME/mr/available.d are myrepos
+configuration files that contain the commands to manage (checkout, update
+etc.) a single repository. vcsh repo configs end in .vcsh, git configs end
+in .git, etc. This is optional and your preference. For example, this is
+what a zsh.vcsh with read-only access to my zshrc repo looks likes. I.e. in
+this specific example, push can not work as you will be using the author's
+repository. This is for demonstration, only. Of course, you are more than
+welcome to clone from this repository and fork your own.
+
+ [$XDG_CONFIG_HOME/vcsh/repo.d/zsh.git]
+ checkout = vcsh clone 'git://github.com/RichiH/zshrc.git' 'zsh'
+ update = vcsh zsh pull
+ push = vcsh zsh push
+ status = vcsh zsh status
+ gc = vcsh zsh gc
+
+ [$HOME/.emacs.d]
+ checkout = vcsh clone 'git://github.com/andschwa/emacs.git' '.emacs.d'
+
+### config.d
+
+$XDG\_CONFIG\_HOME/mr/available.d contains *all available* repositories. Only
+files/links present in mr/config.d, however, will be used by myrepos. That means
+that in this example, only the zsh, gitconfigs, tmux and vim repositories will
+be checked out. A simple `mr update` run in $HOME will clone or update those
+four repositories listed in config.d.
+
+### ~/.mrconfig
+
+Finally, ~/.mrconfig will tie together all those single files which will allow
+you to conveniently run `mr up` etc. to manage all repositories. It looks like
+this:
+
+ [DEFAULT]
+ include = cat ${XDG_CONFIG_HOME:-$HOME/.config}/mr/config.d/*
+
+### repo.d
+
+$XDG\_CONFIG\_HOME/vcsh/repo.d is the directory where all git repositories which
+are under vcsh's control are located. Since their working trees are configured
+to be in $HOME, the files contained in those repositories will be put in $HOME
+directly.
+
+Of course, [myrepos][myrepos] will work with this layout if configured according to
+this document (see above).
+
+vcsh will check if any file it would want to create exists. If it exists, vcsh
+will throw a warning and exit. Move away your old config and try again.
+Optionally, merge your local and your global configs afterwards and push with
+`vcsh repo_name push`.
+
+## Moving into a New Host
+
+To illustrate further, the following steps could move your desired
+configuration to a new host.
+
+1. Clone the myrepos repository (containing available.d, config.d etc.); for
+ example: `vcsh clone git://github.com/RichiH/vcsh_mr_template.git mr`
+2. Choose your repositories by linking them in config.d (or go with the default
+ you may have already configured by adding symlinks to git).
+3. Run myrepos to clone the repositories: `cd; mr update`.
+4. Done.
+
+Hopefully the above could help explain how this approach saves time by
+
+1. making it easy to manage, clone and update a large number of repositories
+ (thanks to myrepos) and
+2. making it unnecessary to create symbolic links in $HOME (thanks to vcsh).
+
+If you want to give vcsh a try, follow the instructions below.
+
+
+# Getting Started
+
+Below, you will find a few different methods for setting up vcsh:
+
+1. The Template Way
+2. The Steal-from-Template Way
+3. The Manual Way
+
+### The Template Way
+
+#### Prerequisites
+
+Make sure none of the following files and directories exist for your test
+(user). If they do, move them away for now:
+
+* `~/.gitignore.d`
+* `~/.mrconfig`
+* `$XDG\_CONFIG\_HOME/mr/available.d/mr.vcsh`
+* `$XDG\_CONFIG\_HOME/mr/available.d/zsh.vcsh`
+* `$XDG\_CONFIG\_HOME/mr/config.d/mr.vcsh`
+* `$XDG\_CONFIG\_HOME/vcsh/repo.d/mr.git/`
+
+All of the files are part of the template repository, the directory is where
+the template will be stored.
+
+### Install vcsh
+
+#### Debian
+
+If you are using Debian Squeeze, you will need to enable backports and the
+package name for myrepos will be 'mr'.
+
+From Wheezy onwards, you can install both directly:
+
+ apt-get install myrepos vcsh
+
+#### Gentoo
+
+To install vcsh in Gentoo Linux just give the following command as root:
+
+ emerge dev-vcs/vcsh
+
+Note the portage package for myrepos still has the old project name:
+
+ emerge dev-vcs/mr
+
+#### Arch Linux
+
+vcsh is available via this [AUR](https://aur.archlinux.org/packages/vcsh/)
+package. Likewise myrepos is available [here](https://aur.archlinux.org/packages/myrepos/).
+You may install both useing your favorite AUR helper. e.g. with yaourt:
+
+ yaourt -Sya myrepos vcsh
+
+Or you can do it yourself manually using the documentation on installing AUR packages
+[on Arch's wiki](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages).
+
+If you prefer to use the devel package that installs the git HEAD version it
+is available [here](https://aur.archlinux.org/packages/vcsh-git/).
+
+#### Mac OS X
+
+Formulas are available for vcsh as well as git and myrepos through [homebrew](http://brew.sh). The
+vcsh formula is set to depend on myrepos, so you only need one install command:
+
+ brew install vcsh
+
+#### From source
+
+To install the latest version from git:
+
+ # choose a location for your checkout
+ mkdir -p ~/work/git
+ cd ~/work/git
+ git clone git://github.com/RichiH/vcsh.git
+ cd vcsh
+ sudo ln -s vcsh /usr/local/bin # or add it to your PATH
+
+For myrepos:
+
+ # use checkout location from above
+ cd ~/work/git
+ git clone git://myrepos.branchable.com/ myrepos
+ cd myrepos
+ make install
+
+#### Clone the Template
+
+ vcsh clone git://github.com/RichiH/vcsh_mr_template.git mr
+
+#### Enable Your Test Repository
+
+ mv ~/.zsh ~/zsh.bak
+ mv ~/.zshrc ~/zshrc.bak
+ cd $XDG_CONFIG_HOME/mr/config.d/
+ ln -s ../available.d/zsh.vcsh . # link, and thereby enable, the zsh repository
+ cd
+ mr up
+
+#### Set Up Your Own Repositories
+
+Now, it's time to edit the template config and fill it with your own remotes:
+
+ vim $XDG_CONFIG_HOME/mr/available.d/mr.vcsh
+ vim $XDG_CONFIG_HOME/mr/available.d/zsh.vcsh
+
+And then create your own stuff:
+
+
+ vcsh init repo_name
+ vcsh repo_name add bar baz quux
+ vcsh repo_name remote add origin git://quuux
+ vcsh repo_name commit
+ vcsh repo_name push
+
+ cp $XDG_CONFIG_HOME/mr/available.d/mr.vcsh $XDG_CONFIG_HOME/mr/available.d/repo_name.vcsh
+ vim $XDG_CONFIG_HOME/mr/available.d/repo_name.vcsh # add your own repo
+
+Done!
+
+### The Steal-from-Template Way
+
+You're welcome to clone the example repository:
+
+ vcsh clone git://github.com/RichiH/vcsh_mr_template.git mr
+ # make sure 'include = cat /usr/share/mr/vcsh' points to an exiting file
+ vim .mrconfig
+
+Look around in the clone. It should be reasonably simple to understand. If not,
+poke me, RichiH, on Freenode (query) or OFTC (#vcs-home).
+
+
+### The Manual Way
+
+This is how my old setup procedure looked like. Adapt it to your own style or
+copy mine verbatim, either is fine.
+
+ # Create workspace
+ mkdir -p ~/work/git
+ cd !$
+
+ # Clone vcsh and make it available
+ git clone git://github.com/RichiH/vcsh.git vcsh
+ sudo ln -s ~/work/git/vcsh/vcsh /usr/bin/local
+ hash -r
+
+Grab my myrepos config. see below for details on how I set this up
+
+ vcsh clone ssh://<remote>/mr.git
+ cd $XDG_CONFIG_HOME/mr/config.d/
+ ln -s ../available.d/* .
+
+
+[myrepos][myrepos] is used to actually retrieve configs, etc.
+
+ ~ % cat ~/.mrconfig
+ [DEFAULT]
+ include = cat $XDG_CONFIG_HOME/mr/config.d/*
+ ~ % echo $XDG_CONFIG_HOME
+ /home/richih/.config
+ ~ % ls $XDG_CONFIG_HOME/mr/available.d # random selection of my repos
+ git-annex gitk.vcsh git.vcsh ikiwiki mr.vcsh reportbug.vcsh snippets.git wget.vcsh zsh.vcsh
+ ~ %
+ # then simply ln -s whatever you want on your local machine from
+ # $XDG_CONFIG_HOME/mr/available.d to $XDG_CONFIG_HOME/mr/config.d
+ ~ % cd
+ ~ % mr -j 5 up
+
+
+# myrepos usage ; will be factored out & rewritten
+
+### Keeping repositories Up-to-Date
+
+This is the beauty of it all. Once you are set up, just run:
+
+ mr up
+ mr push
+
+Neat.
+
+### Making Changes
+
+After you have made some changes, for which you would normally use `git add`
+and `git commit`, use the vcsh wrapper (like above):
+
+ vcsh repo_name add bar baz quux
+ vcsh repo_name commit
+ vcsh repo_name push
+
+### Using vcsh without myrepos
+
+vcsh encourages you to use [myrepos][myrepos]. It helps you manage a large number of
+repositories by running the necessary vcsh commands for you. You may choose not
+to use myrepos, in which case you will have to run those commands manually or by
+other means.
+
+
+To initialize a new repository: `vcsh init zsh`
+
+To clone a repository: `vcsh clone ssh://<remote>/zsh.git`
+
+To interact with a repository, use the regular Git commands, but prepend them
+with `vcsh run $repository_name`. For example:
+
+ vcsh zsh status
+ vcsh zsh add .zshrc
+ vcsh zsh commit
+
+Obviously, without myrepos keeping repositories up-to-date, it will have to be done
+manually. Alternatively, you could try something like this:
+
+ for repo in `vcsh list`; do
+ vcsh run $repo git pull;
+ done
+
+
+# Contact
+
+There are several ways to get in touch with the author and a small but committed
+community around the general idea of version controlling your (digital) life.
+
+* IRC: #vcs-home on irc.oftc.net
+
+* Mailing list: [http://lists.madduck.net/listinfo/vcs-home][vcs-home-list]
+
+* Pull requests or issues on [https://github.com/RichiH/vcsh][vcsh]
+
+
+[myrepos]: http://myrepos.branchable.com/
+[talks]: http://richardhartmann.de/talks/
+[vcsh]: https://github.com/RichiH/vcsh
+[vcs-home-list]: http://lists.madduck.net/listinfo/vcs-home
* post-enter
* pre-init
* post-init
+* pre-merge
+ Use this hook to detect and handle merge conflicts before vcsh's native code
+ finds and errors on them. This is useful for allowing clones on top of existing
+ files.
+* post-merge
+ Use this hook to finish handling any merge conflicts found in the pre-merge hook.
* pre-pull
* post-pull
* pre-push
--- /dev/null
+#!/bin/sh
+
+# This adds a remote origin at $GITURL/dotfiles/$VCSH_DIRECTORY which
+# is helpful for creating new dotfile repositories that you plan to
+# store on (e.g.) Github.
+#
+# You must set $GITURL in order to use this hook. For example, add the
+# following to your .bashrc (or equivalent for your shell), replacing
+# YOURUSERNAME with your github username:
+#
+# export GITURL="git@github.com:YOURUSERNAME"
+
+if [ -z $GITURL ]; then
+ echo "\$GITURL is not set; please see post-init-add-origin hook"
+ exit 1;
+fi
+
+vcsh $VCSH_DIRECTORY remote add origin $GITURL/dotfiles/$VCSH_DIRECTORY
--- /dev/null
+#!/bin/sh
+
+# This sets up your new repos with mr.
+
+cat > $HOME/.config/mr/available.d/$VCSH_DIRECTORY.vcsh << EOF
+[\$HOME/.config/vcsh/repo.d/$VCSH_DIRECTORY.git]
+checkout = vcsh clone $GITURL/dotfiles/$VCSH_DIRECTORY.git
+EOF
+
+ln -s $HOME/.config/mr/available.d/$VCSH_DIRECTORY.vcsh $HOME/.config/mr/config.d/
--- /dev/null
+#!/bin/sh
+
+# This finds objects that the pre-merge script moved out of the way to
+# avoid conflicts when running `vcsh clone` and moves them back to their
+# original places. The result is that the Git repository gets checked out
+# without error and the pre-existing files end up back in the working
+# directory. Git and thus vcsh now see these as un-staged changes to the
+# working branch and you can deal with them as usual.
+
+find . -name '*.vcsh-unclobber' -execdir rename .vcsh-unclobber '' {} \;
--- /dev/null
+#!/bin/sh
+
+# This code does amost exactly what the native vcsh sanity checking code
+# does except that on finding a potential merge conflict, it moves existing
+# files out of the way temporarily. Merging (part of `vcsh clone`) happens
+# cleanly, and a post-merge hook can be used to figure out what to do with
+# the now-renamed files.
+
+for object in $(git ls-tree -r origin/master | awk '{print $4}'); do
+ [ -e "$object" ] && mv "$object" "$object.vcsh-unclobber"
+done
`vcsh` [<options>] <command>
-`vcsh` clone <url> [<repo>]
+`vcsh` clone [-b <branch>] <url> [<repo>]
`vcsh` delete <repo>
`vcsh` list
-`vcsh` list-tracked
+`vcsh` list-tracked [<rpoe>]
-`vcsh` list-tracked-by <repo>
+`vcsh` list-untracked [<-r>] [<repo>]
`vcsh` pull
If you need to clone a bundle of repositories, look into the
`post-clone-retired` hook.
+ You can also use a single git repository with several branches. Use the `-b`
+ option to specify a branch at clone time, the default is `master`.
+
* commit:
Commit in all repositories
* list-tracked:
List all files tracked by vcsh.
+ If you want to list files tracked by a specific repository, simply
+ append the repository's name last.
+
* list-tracked-by:
List files tracked by a repository.
+ This is a legacy command; you should use `list-tracked <repo>` instead.
+
+* list-untracked:
+ List all files NOT tracked by vcsh.
+
+ By default, the file list is shallow and stops at directory levels where
+ possible. If you prefer to get a list of all files, append `-r` for
+ recursive mode.
+
+ If you want to list files not tracked by a specific repository, simply
+ append the repository's name last.
+
* pull:
Pull from all vcsh remotes.
Interesting knobs you can turn:
+* <$VCSH_GITATTRIBUTES>:
+ Can be <none>, or any other value.
+
+ <none> will not maintain Git attributes in a special location.
+
+ If set to any other value, repo-specific gitattributes files will be maintained.
+
+ Defaults to <none>.
+
* <$VCSH_GITIGNORE>:
Can be <exact>, <none>, or <recursive>.
If you need more, vcsh is trivial to patch, but please let upstream know so
we can ship them by default.
+## OVERLAY SYSTEM
+
+`vcsh` also provides an overlay system. Similar to hooks, the recommended
+locations are <$XDG_CONFIG_HOME/vcsh/overlays-available> and
+<$XDG_CONFIG_HOME/vcsh/overlays-enabled>.
+
+Overlays follow the same rules as hooks and you are free to overwrite any
+and all functions. Same as hooks, you can use global or repository-specific
+overlays by using either <$VCSH_OVERLAY_D/$VCSH_COMMAND> or
+<$VCSH_OVERLAY_D/$VCSH_REPO_NAME.$VCSH_COMMAND>.
+
+Please note that nothing stops you from, e.g. overwriting `status()` in
+<$VCSH_OVERLAY_D/commit>. As the overlays will be sourced and you are
+replacing arbitrary functions, any and all features may stop working, or you
+may even lose data.
+
+You have been warned.
+
## DETAILED HOWTO AND FURTHER READING
Manpages are often short and sometimes useless to glean best practices from.
#!/bin/sh
+# This program is licensed under the GNU GPL version 2 or later.
+# (c) Richard "RichiH" Hartmann <richih@debian.org>, 2012-2014
+# For details, see LICENSE. To submit patches, you have to agree to
+# license your code under the GNU GPL version 2 or later.
+
+
echo 'Alphabetical list of surnames of everyone who ever committed to this repository.
Auto-generated from tools/list_CONTRIBUTORS.
'
#!/bin/sh
# This program is licensed under the GNU GPL version 2 or later.
-# (c) Richard "RichiH" Hartmann <richih@debian.org>, 2011-2013
+# (c) Richard "RichiH" Hartmann <richih@debian.org>, 2011-2014
# For details, see LICENSE. To submit patches, you have to agree to
# license your code under the GNU GPL version 2 or later.
# If '.git-HEAD' is appended to the version, you are seeing an unreleased
# version of vcsh; the master branch is supposed to be clean at all times
# so you can most likely just use it nonetheless
-VERSION='1.20131229.git-HEAD'
+VERSION='1.20141026'
SELF=$(basename $0)
fatal() {
echo "$SELF: fatal: $1" >&2
+ [ -z $2 ] && exit 1
exit $2
}
# options that will modify our behaviour.
# Commands are handled at the end of this script.
while getopts "c:dv" flag; do
- if [ "$1" = '-d' ] || [ "$1" = '--debug' ]; then
+ if [ x"$1" = x'-d' ] || [ x"$1" = x'--debug' ]; then
set -vx
VCSH_DEBUG=1
echo "debug mode on"
echo "$SELF $VERSION"
- elif [ "$1" = '-v' ]; then
+ elif [ x"$1" = x'-v' ]; then
VCSH_VERBOSE=1
echo "verbose mode on"
echo "$SELF $VERSION"
- elif [ "$1" = '-c' ]; then
+ elif [ x"$1" = x'-c' ]; then
VCSH_OPTION_CONFIG=$OPTARG
fi
shift 1
source_all() {
# Source file even if it's in $PWD and does not have any slashes in it
- case "$1" in
+ case $1 in
*/*) . "$1";;
*) . "$PWD/$1";;
esac;
# Read configuration and set defaults if anything's not set
[ -n "$VCSH_DEBUG" ] && set -vx
-: ${XDG_CONFIG_HOME:=$HOME/.config}
+: ${XDG_CONFIG_HOME:="$HOME/.config"}
# Read configuration files if there are any
[ -r "/etc/vcsh/config" ] && . "/etc/vcsh/config"
[ -n "$VCSH_DEBUG" ] && set -vx
# Read defaults
-: ${VCSH_REPO_D:=$XDG_CONFIG_HOME/vcsh/repo.d}
-: ${VCSH_HOOK_D:=$XDH_CONFIG_HOME/vcsh/hooks-enabled}
-: ${VCSH_BASE:=$HOME}
+: ${VCSH_REPO_D:="$XDG_CONFIG_HOME/vcsh/repo.d"}
+: ${VCSH_HOOK_D:="$XDG_CONFIG_HOME/vcsh/hooks-enabled"}
+: ${VCSH_OVERLAY_D:="$XDG_CONFIG_HOME/vcsh/overlays-enabled"}
+: ${VCSH_BASE:="$HOME"}
: ${VCSH_GITIGNORE:=exact}
+: ${VCSH_GITATTRIBUTES:=none}
: ${VCSH_WORKTREE:=absolute}
if [ ! "x$VCSH_GITIGNORE" = 'xexact' ] && [ ! "x$VCSH_GITIGNORE" = 'xnone' ] && [ ! "x$VCSH_GITIGNORE" = 'xrecursive' ]; then
-v Enable verbose mode
commands:
- clone <remote> \\
+ clone [-b <branch>] \\
+ <remote> \\
[<repo>] Clone from an existing repository
commit Commit in all repositories
delete <repo> Delete an existing repository
help Display this help text
init <repo> Initialize a new repository
list List all repositories
- list-tracked List all files tracked by vcsh
- list-tracked-by \\
- <repo> List files tracked by a repository
+ list-tracked \\
+ [<repo>] List all files tracked all or one repositories
+ list-untracked \\
+ [<-r>] [<repo>] List all files not tracked by all or one repositories
pull Pull from all vcsh remotes
push Push to vcsh remotes
rename <repo> \\
hook pre-clone
init
git remote add origin "$GIT_REMOTE"
- git config branch.master.remote origin
- git config branch.master.merge refs/heads/master
- if [ $(git ls-remote origin master 2> /dev/null | wc -l ) -lt 1 ]; then
- info "remote is empty, not merging anything"
+ git checkout -b "$VCSH_BRANCH" || return $?
+ git config branch."$VCSH_BRANCH".remote origin
+ git config branch."$VCSH_BRANCH".merge refs/heads/"$VCSH_BRANCH"
+ if [ $(git ls-remote origin "$VCSH_BRANCH" 2> /dev/null | wc -l ) -lt 1 ]; then
+ info "remote is empty, not merging anything.
+ You should add files to your new repository."
exit
fi
- git fetch
- for object in $(git ls-tree -r origin/master | awk '{print $4}'); do
+ git fetch origin "$VCSH_BRANCH"
+ hook pre-merge
+ git ls-tree -r --name-only origin/"$VCSH_BRANCH" | (while read object; do
[ -e "$object" ] &&
error "'$object' exists." &&
VCSH_CONFLICT=1
done
- [ "$VCSH_CONFLICT" = '1' ] &&
+ [ x"$VCSH_CONFLICT" = x'1' ]) &&
fatal "will stop after fetching and not try to merge!
- Once this situation has been resolved, run 'vcsh run $VCSH_REPO_NAME git pull' to finish cloning." 17
- git merge origin/master
+ Once this situation has been resolved, run 'vcsh $VCSH_REPO_NAME pull' to finish cloning." 17
+ git merge origin/"$VCSH_BRANCH"
+ hook post-merge
hook post-clone
retire
hook post-clone-retired
hook pre-commit
for VCSH_REPO_NAME in $(list); do
echo "$VCSH_REPO_NAME: "
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
use
git commit --untracked-files=no --quiet
+ VCSH_COMMAND_RETURN_CODE=$?
echo
done
hook post-commit
}
hook() {
- for hook in $VCSH_HOOK_D/$1* $VCSH_HOOK_D/$VCSH_REPO_NAME.$1*; do
+ for hook in "$VCSH_HOOK_D/$1"* "$VCSH_HOOK_D/$VCSH_REPO_NAME.$1"*; do
[ -x "$hook" ] || continue
verbose "executing '$hook'"
"$hook"
[ ! -e "$GIT_DIR" ] || fatal "'$GIT_DIR' exists" 10
mkdir -p "$VCSH_BASE" || fatal "could not create '$VCSH_BASE'" 50
cd "$VCSH_BASE" || fatal "could not enter '$VCSH_BASE'" 11
- git init
+ git init --shared=0600
upgrade
hook post-init
}
list() {
for repo in "$VCSH_REPO_D"/*.git; do
- [ -d "$repo" ] && [ -r "$repo" ] && echo $(basename "$repo" .git)
+ [ -d "$repo" ] && [ -r "$repo" ] && echo "$(basename "$repo" .git)"
done
}
get_files() {
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
git ls-files
}
list_tracked() {
- for VCSH_REPO_NAME in $(list); do
- get_files
- done | sort -u
+ VCSH_REPO_NAME=$2; export VCSH_REPO_NAME
+ if [ -n "$VCSH_REPO_NAME" ]; then
+ get_files | list_tracked_helper
+ else
+ for VCSH_REPO_NAME in $(list); do
+ get_files
+ done | list_tracked_helper
+ fi
+}
+
+list_tracked_helper() {
+ sed "s,^,$(printf '%s\n' "$VCSH_BASE/" | sed 's/[,\&]/\\&/g')," | sort -u
}
list_tracked_by() {
- use
- git ls-files | sort -u
+ list_tracked $2
+}
+
+list_untracked() {
+ command -v 'comm' >/dev/null 2>&1 || fatal "Could not find 'comm'"
+
+ temp_file_others=$(mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX") || fatal 'Could not create temp file'
+ temp_file_untracked=$(mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX") || fatal 'Could not create temp file'
+ temp_file_untracked_copy=$(mktemp "${TMPDIR:-/tmp}/tmp.XXXXXXXXXX") || fatal 'Could not create temp file'
+
+ # Hack in support for `vcsh list-untracked -r`...
+ directory_opt="--directory"
+ shift 1
+ while getopts "r" flag; do
+ if [ x"$1" = x'-r' ]; then
+ unset directory_opt
+ fi
+ shift 1
+ done
+ # ...and parse for a potential parameter afterwards. As we shifted things out of $* in during getops, we need to look at $1
+ VCSH_REPO_NAME=$1; export VCSH_REPO_NAME
+
+ if [ -n "$VCSH_REPO_NAME" ]; then
+ list_untracked_helper $VCSH_REPO_NAME
+ else
+ for VCSH_REPO_NAME in $(list); do
+ list_untracked_helper $VCSH_REPO_NAME
+ done
+ fi
+ cat $temp_file_untracked
+
+ unset directory_opt directory_component
+ rm -f $temp_file_others $temp_file_untracked $temp_file_untracked_copy || fatal 'Could not delete temp files'
+}
+
+list_untracked_helper() {
+ export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
+ git ls-files --others "$directory_opt" | (
+ while read line; do
+ echo "$line"
+ directory_component=${line%%/*}
+ [ -d "$directory_component" ] && printf '%s/\n' "$directory_component"
+ done
+ ) | sort -u > $temp_file_others
+ if [ -z "$ran_once" ]; then
+ ran_once=1
+ cp $temp_file_others $temp_file_untracked || fatal 'Could not copy temp file'
+ fi
+ cp $temp_file_untracked $temp_file_untracked_copy || fatal 'Could not copy temp file'
+ comm -12 --nocheck-order $temp_file_others $temp_file_untracked_copy > $temp_file_untracked
}
pull() {
hook pre-pull
for VCSH_REPO_NAME in $(list); do
- printf "$VCSH_REPO_NAME: "
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
+ printf '%s: ' "$VCSH_REPO_NAME"
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
use
git pull
+ VCSH_COMMAND_RETURN_CODE=$?
echo
done
hook post-pull
push() {
hook pre-push
for VCSH_REPO_NAME in $(list); do
- printf "$VCSH_REPO_NAME: "
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
+ printf '%s: ' "$VCSH_REPO_NAME"
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
use
git push
+ VCSH_COMMAND_RETURN_CODE=$?
echo
done
hook post-push
# Now that the repository has been renamed, we need to fix up its configuration
# Overwrite old name..
- GIT_DIR="$GIT_DIR_NEW"
- VCSH_REPO_NAME="$VCSH_REPO_NAME_NEW"
+ GIT_DIR=$GIT_DIR_NEW
+ VCSH_REPO_NAME=$VCSH_REPO_NAME_NEW
# ..and clobber all old configuration
upgrade
}
hook pre-run
use
"$@"
+ VCSH_COMMAND_RETURN_CODE=$?
hook post-run
}
status() {
- if [ ! "x$VCSH_REPO_NAME" = "x" ]; then
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
- use
- git status --short --untracked-files='no'
+ if [ -n "$VCSH_REPO_NAME" ]; then
+ status_helper $VCSH_REPO_NAME
else
for VCSH_REPO_NAME in $(list); do
echo "$VCSH_REPO_NAME:"
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
- use
- git status --short --untracked-files='no'
+ status_helper $VCSH_REPO_NAME
echo
done
fi
}
+status_helper() {
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
+ use
+ remote_tracking_branch=$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2> /dev/null) && {
+ commits_behind=$(git log ..${remote_tracking_branch} --oneline | wc -l)
+ commits_ahead=$(git log ${remote_tracking_branch}.. --oneline | wc -l)
+ [ ${commits_behind} -ne 0 ] && echo "Behind $remote_tracking_branch by $commits_behind commits"
+ [ ${commits_ahead} -ne 0 ] && echo "Ahead of $remote_tracking_branch by $commits_ahead commits"
+ }
+ git status --short --untracked-files='no'
+ VCSH_COMMAND_RETURN_CODE=$?
+}
+
upgrade() {
hook pre-upgrade
# fake-bare repositories are not bare, actually. Set this to false
# core.worktree may be absolute or relative to $GIT_DIR, depending on
# user preference
if [ ! "x$VCSH_WORKTREE" = 'xabsolute' ]; then
- git config core.worktree $(cd $GIT_DIR && GIT_WORK_TREE="$VCSH_BASE" git rev-parse --show-cdup)
+ git config core.worktree "$(cd "$GIT_DIR" && GIT_WORK_TREE=$VCSH_BASE git rev-parse --show-cdup)"
elif [ ! "x$VCSH_WORKTREE" = 'xrelative' ]; then
git config core.worktree "$VCSH_BASE"
fi
[ ! "x$VCSH_GITIGNORE" = 'xnone' ] && git config core.excludesfile ".gitignore.d/$VCSH_REPO_NAME"
- git config vcsh.vcsh 'true'
+ [ ! "x$VCSH_GITATTRIBUTES" = 'xnone' ] && git config core.attributesfile ".gitattributes.d/$VCSH_REPO_NAME"
+ git config vcsh.vcsh 'true'
use
[ -e "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME" ] && git add -f "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME"
+ [ -e "$VCSH_BASE/.gitattributes.d/$VCSH_REPO_NAME" ] && git add -f "$VCSH_BASE/.gitattributes.d/$VCSH_REPO_NAME"
hook post-upgrade
}
use() {
git_dir_exists
- export VCSH_DIRECTORY="$VCSH_REPO_NAME"
+ VCSH_DIRECTORY=$VCSH_REPO_NAME; export VCSH_DIRECTORY
}
which() {
+ [ -e "$VCSH_COMMAND_PARAMETER" ] || fatal "'$VCSH_COMMAND_PARAMETER' does not exist" 1
for VCSH_REPO_NAME in $(list); do
for VCSH_FILE in $(get_files); do
- echo $VCSH_FILE | grep -q "$VCSH_COMMAND_PARAMETER" && echo "$VCSH_REPO_NAME: $VCSH_FILE"
+ echo "$VCSH_FILE" | grep -q "$VCSH_COMMAND_PARAMETER" && echo "$VCSH_REPO_NAME: $VCSH_FILE"
done
done | sort -u
}
use
cd "$VCSH_BASE" || fatal "could not enter '$VCSH_BASE'" 11
+ OLDIFS=$IFS
+ IFS=$(printf '\n\t')
gitignores=$(for file in $(git ls-files); do
while true; do
- echo $file; new="${file%/*}"
- [ "$file" = "$new" ] && break
- file="$new"
+ echo "$file"; new=${file%/*}
+ [ x"$file" = x"$new" ] && break
+ file=$new
done;
done | sort -u)
{ echo "$gitignore/*" | sed 's@^@!/@' >> "$tempfile" || fatal "could not write to '$tempfile'" 57; }
fi
done
+ IFS=$OLDIFS
if diff -N "$tempfile" "$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME" > /dev/null; then
rm -f "$tempfile" || error "could not delete '$tempfile'"
exit
fatal "could not move '$tempfile' to '$VCSH_BASE/.gitignore.d/$VCSH_REPO_NAME'" 53
}
-debug `git version`
+debug $(git version)
if [ ! "x$VCSH_GITIGNORE" = 'xexact' ] && [ ! "x$VCSH_GITIGNORE" = 'xnone' ] && [ ! "x$VCSH_GITIGNORE" = 'xrecursive' ]; then
fatal "'\$VCSH_GITIGNORE' must equal 'exact', 'none', or 'recursive'" 1
fi
-export VCSH_COMMAND="$1"
+VCSH_COMMAND=$1; export VCSH_COMMAND
-case "$VCSH_COMMAND" in
+case $VCSH_COMMAND in
clon|clo|cl) VCSH_COMMAND=clone;;
commi|comm|com|co) VCSH_COMMAND=commit;;
delet|dele|del|de) VCSH_COMMAND=delete;;
write|writ|wri|wr) VCSH_COMMAND=write-gitignore;;
esac
-if [ "$VCSH_COMMAND" = 'clone' ]; then
+if [ x"$VCSH_COMMAND" = x'clone' ]; then
+ VCSH_BRANCH=
+ if [ "$2" = -b ]; then
+ VCSH_BRANCH=$3
+ shift
+ shift
+ fi
[ -z "$2" ] && fatal "$VCSH_COMMAND: please specify a remote" 1
GIT_REMOTE="$2"
- [ -n "$3" ] && VCSH_REPO_NAME="$3" || VCSH_REPO_NAME=$(basename "$GIT_REMOTE" .git)
+ [ -n "$VCSH_BRANCH" ] || if [ "$3" = -b ]; then
+ VCSH_BRANCH=$4
+ shift
+ shift
+ fi
+ if [ -n "$3" ]; then
+ VCSH_REPO_NAME=$3
+ [ -z "$VCSH_BRANCH" ] && [ "$4" = -b ] && VCSH_BRANCH=$5
+ else
+ VCSH_REPO_NAME=$(basename "${GIT_REMOTE#*:}" .git)
+ fi
[ -z "$VCSH_REPO_NAME" ] && fatal "$VCSH_COMMAND: could not determine repository name" 1
export VCSH_REPO_NAME
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
+ [ -n "$VCSH_BRANCH" ] || VCSH_BRANCH=master
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
elif [ "$VCSH_COMMAND" = 'version' ]; then
echo "$SELF $VERSION"
git version
exit
-elif [ "$VCSH_COMMAND" = 'which' ]; then
+elif [ x"$VCSH_COMMAND" = x'which' ]; then
[ -z "$2" ] && fatal "$VCSH_COMMAND: please specify a filename" 1
[ -n "$3" ] && fatal "$VCSH_COMMAND: too many parameters" 1
- export VCSH_COMMAND_PARAMETER="$2"
-elif [ "$VCSH_COMMAND" = 'delete' ] ||
- [ "$VCSH_COMMAND" = 'enter' ] ||
- [ "$VCSH_COMMAND" = 'init' ] ||
- [ "$VCSH_COMMAND" = 'list-tracked-by' ] ||
- [ "$VCSH_COMMAND" = 'rename' ] ||
- [ "$VCSH_COMMAND" = 'run' ] ||
- [ "$VCSH_COMMAND" = 'upgrade' ] ||
- [ "$VCSH_COMMAND" = 'write-gitignore' ]; then
- [ -z $2 ] && fatal "$VCSH_COMMAND: please specify repository to work on" 1
- [ "$VCSH_COMMAND" = 'rename' -a -z "$3" ] && fatal "$VCSH_COMMAND: please specify a target name" 1
- [ "$VCSH_COMMAND" = 'run' -a -z "$3" ] && fatal "$VCSH_COMMAND: please specify a command" 1
- export VCSH_REPO_NAME="$2"
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
- [ "$VCSH_COMMAND" = 'rename' ] && { export VCSH_REPO_NAME_NEW="$3";
- export GIT_DIR_NEW="$VCSH_REPO_D/$VCSH_REPO_NAME_NEW.git"; }
- [ "$VCSH_COMMAND" = 'run' ] && shift 2
-elif [ "$VCSH_COMMAND" = 'commit' ] ||
- [ "$VCSH_COMMAND" = 'list' ] ||
- [ "$VCSH_COMMAND" = 'list-tracked' ] ||
- [ "$VCSH_COMMAND" = 'pull' ] ||
- [ "$VCSH_COMMAND" = 'push' ]; then
+ VCSH_COMMAND_PARAMETER=$2; export VCSH_COMMAND_PARAMETER
+elif [ x"$VCSH_COMMAND" = x'delete' ] ||
+ [ x"$VCSH_COMMAND" = x'enter' ] ||
+ [ x"$VCSH_COMMAND" = x'init' ] ||
+ [ x"$VCSH_COMMAND" = x'list-tracked-by' ] ||
+ [ x"$VCSH_COMMAND" = x'rename' ] ||
+ [ x"$VCSH_COMMAND" = x'run' ] ||
+ [ x"$VCSH_COMMAND" = x'upgrade' ] ||
+ [ x"$VCSH_COMMAND" = x'write-gitignore' ]; then
+ [ -z "$2" ] && fatal "$VCSH_COMMAND: please specify repository to work on" 1
+ [ x"$VCSH_COMMAND" = x'rename' ] && [ -z "$3" ] && fatal "$VCSH_COMMAND: please specify a target name" 1
+ [ x"$VCSH_COMMAND" = x'run' ] && [ -z "$3" ] && fatal "$VCSH_COMMAND: please specify a command" 1
+ VCSH_REPO_NAME=$2; export VCSH_REPO_NAME
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
+ [ x"$VCSH_COMMAND" = x'rename' ] && { VCSH_REPO_NAME_NEW=$3; export VCSH_REPO_NAME_NEW;
+ GIT_DIR_NEW=$VCSH_REPO_D/$VCSH_REPO_NAME_NEW.git; export GIT_DIR_NEW; }
+ [ x"$VCSH_COMMAND" = x'run' ] && shift 2
+elif [ x"$VCSH_COMMAND" = x'commit' ] ||
+ [ x"$VCSH_COMMAND" = x'list' ] ||
+ [ x"$VCSH_COMMAND" = x'list-tracked' ] ||
+ [ x"$VCSH_COMMAND" = x'list-untracked' ] ||
+ [ x"$VCSH_COMMAND" = x'pull' ] ||
+ [ x"$VCSH_COMMAND" = x'push' ]; then
:
-elif [ "$VCSH_COMMAND" = 'status' ]; then
- export VCSH_REPO_NAME="$2"
+elif [ x"$VCSH_COMMAND" = x'status' ]; then
+ VCSH_REPO_NAME=$2; export VCSH_REPO_NAME
elif [ -n "$2" ]; then
- export VCSH_COMMAND='run'
- export VCSH_REPO_NAME="$1"
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
- [ -d $GIT_DIR ] || { help; exit 1; }
+ VCSH_COMMAND='run'; export VCSH_COMMAND
+ VCSH_REPO_NAME=$1; export VCSH_REPO_NAME
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
+ [ -d "$GIT_DIR" ] || { help; exit 1; }
shift 1
set -- "git" "$@"
elif [ -n "$VCSH_COMMAND" ]; then
- export VCSH_COMMAND='enter'
- export VCSH_REPO_NAME="$1"
- export GIT_DIR="$VCSH_REPO_D/$VCSH_REPO_NAME.git"
- [ -d $GIT_DIR ] || { help; exit 1; }
+ VCSH_COMMAND='enter'; export VCSH_COMMAND
+ VCSH_REPO_NAME=$1; export VCSH_REPO_NAME
+ GIT_DIR=$VCSH_REPO_D/$VCSH_REPO_NAME.git; export GIT_DIR
+ [ -d "$GIT_DIR" ] || { help; exit 1; }
else
# $1 is empty, or 'help'
help && exit
# Did we receive a directory instead of a name?
# Mangle the input to fit normal operation.
-if echo $VCSH_REPO_NAME | grep -q '/'; then
- export GIT_DIR=$VCSH_REPO_NAME
- export VCSH_REPO_NAME=$(basename "$VCSH_REPO_NAME" .git)
+if echo "$VCSH_REPO_NAME" | grep -q '/'; then
+ GIT_DIR=$VCSH_REPO_NAME; export GIT_DIR
+ VCSH_REPO_NAME=$(basename "$VCSH_REPO_NAME" .git); export VCSH_REPO_NAME
fi
check_dir() {
check_dir "$VCSH_REPO_D"
[ ! "x$VCSH_GITIGNORE" = 'xnone' ] && check_dir "$VCSH_BASE/.gitignore.d"
+[ ! "x$VCSH_GITATTRIBUTES" = 'xnone' ] && check_dir "$VCSH_BASE/.gitattributes.d"
verbose "$VCSH_COMMAND begin"
-export VCSH_COMMAND=$(echo $VCSH_COMMAND | sed 's/-/_/g')
+VCSH_COMMAND=$(echo "$VCSH_COMMAND" | sed 's/-/_/g'); export VCSH_COMMAND
+
+# Source repo-specific configuration file
+[ -r "$XDG_CONFIG_HOME/vcsh/config.d/$VCSH_REPO_NAME" ] && . "$XDG_CONFIG_HOME/vcsh/config.d/$VCSH_REPO_NAME"
+
+# source overlay functions
+for overlay in "$VCSH_OVERLAY_D/$VCSH_COMMAND"* "$VCSH_OVERLAY_D/$VCSH_REPO_NAME.$VCSH_COMMAND"*; do
+ [ -r "$overlay" ] || continue
+ info "sourcing '$overlay'"
+ . "$overlay"
+done
+
hook pre-command
$VCSH_COMMAND "$@"
hook post-command
verbose "$VCSH_COMMAND end, exiting"
+exit $VCSH_COMMAND_RETURN_CODE