Configuration

URL Format

Repo type and address is RFC3986 style URLs. You may recognize this from pip’s VCS URL format.

Config locations

You can place the file in one of three places:

  1. Home: ~/.vcspull.yaml

  2. XDG home directory: $XDG_CONFIG_HOME/vcspull/

    Example: ~/.config/vcspull/myrepos.yaml

    XDG_CONFIG_HOME is often ~/.config/vcspull/, but can vary on platform, to check:

    $ echo $XDG_CONFIG_HOME
    
  3. Anywhere (and trigger via vcspull sync -c ./path/to/file.yaml sync [repo_name])

Schema

Warning

This structure is subject to break in upcoming releases.

~/workdir/:
  repo_name:
    remotes:
      origin: git_repo_url

Examples

~/workspace/:
  kaptan:
    url: git+https://github.com/emre/kaptan
    remotes:
      upstream: git+https://github.com/emre/kaptan
      marksteve: git+https://github.com/marksteve/kaptan.git
      tony: git+ssh://[email protected]/tony/kaptan.git

To pull kaptan:

$ vcspull sync kaptan

Christmas tree

config showing off every current feature and inline shortcut available.

~/study/:
    linux: git+git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    freebsd: git+https://github.com/freebsd/freebsd.git
    sphinx: hg+https://bitbucket.org/birkenfeld/sphinx
    docutils: svn+http://svn.code.sf.net/p/docutils/code/trunk
~/github_projects/:
    kaptan:
        url: git+https://github.com/emre/kaptan
        remotes:
            upstream: git+https://github.com/emre/kaptan
            marksteve: git+https://github.com/marksteve/kaptan.git
            tony: [email protected]:tony/kaptan.git
~:
    .vim:
        url: [email protected]:tony/vim-config.git
    .tmux:
        url: [email protected]:tony/tmux-config.git
~/pinned/:
    # Global pin — no automated command can modify this entry
    internal-fork:
        repo: [email protected]:myorg/internal-fork.git
        options:
            pin: true
            pin_reason: "pinned to company fork  update manually"
    # Per-operation pin — only import and fmt are blocked
    my-framework:
        repo: [email protected]:myorg/my-framework.git
        options:
            pin:
                import: true
                fmt: true
            pin_reason: "URL managed manually; formatting intentional"
    # Shorthand — equivalent to pin: {import: true}
    stable-dep:
        repo: git+https://github.com/upstream/stable-dep.git
        options:
            allow_overwrite: false

Code scholar

This file is used to checkout and sync multiple open source configs.

YAML:

~/study/c:
  awesome: 'git+git://git.naquadah.org/awesome.git'
  weechat: 'git+git://git.sv.gnu.org/weechat.git'
  retroarch: 'git+https://github.com/Themaister/RetroArch.git'
  linux: 'git+https://github.com/torvalds/linux.git'
  freebsd: 'git+https://github.com/freebsd/freebsd.git'
  ncmpc: 'git+git://git.musicpd.org/master/ncmpc.git'
  tmux: 'git+git://git.code.sf.net/p/tmux/tmux-code'
  git: 'git+https://github.com/git/git.git'
  postgres: 'git+https://github.com/postgres/postgres.git'
  pgadmin3: 'git+git://git.postgresql.org/git/pgadmin3.git'
  sandy: 'git+http://git.suckless.org/sandy'
  understate: 'git+https://github.com/L3V3L9/understate.git'
  util-cursor: 'git+git://anongit.freedesktop.org/xcb/util-cursor'
  zsh: 'git+git://git.code.sf.net/p/zsh/code'
  cpython: 'hg+http://hg.python.org/cpython/'
  vim: 'hg+https://vim.googlecode.com/hg/'
  nginx: 'hg+http://hg.nginx.org/nginx'
~/study/c++:
  vimpc: 'git+https://github.com/boysetsfrog/vimpc'
  mpd: 'git+git://git.musicpd.org/master/mpd.git'
  libmpd: 'git+git://git.musicpd.org/master/libmpd.git'
  ncmpcpp: 'git+git://git.musicpd.org/mirror/ncmpcpp.git'
  libmpdclient: 'git+git://git.musicpd.org/master/libmpdclient.git'
  node: 'git+https://github.com/joyent/node.git'
  libzmp: 'git+https://github.com/zeromq/libzmq.git'
  doubanfm-qt: 'git+https://github.com/zonyitoo/doubanfm-qt.git'
  retroarch-phoenix: 'git+https://github.com/Themaister/RetroArch-Phoenix.git'
  clementine: 'git+https://code.google.com/p/clementine-player/'
  amarok: 'git+git://anongit.kde.org/amarok.git'
~/study/node:
  async: 'git+http://github.com/caolan/async.git'
  request: 'git+git://github.com/mikeal/request.git'
  express: 'git+https://github.com/visionmedia/express.git'
  node-optimist: 'git+http://github.com/substack/node-optimist.git'
  commander.js: 'git+https://github.com/visionmedia/commander.js.git' 
  colors.js: 'git+git://github.com/Marak/colors.js.git'
  uglify.js: 'git+git://github.com/mishoo/UglifyJS.git'
  connect: 'git+git://github.com/senchalabs/connect.git'
  socket.io: 'git+https://github.com/LearnBoost/socket.io.git'
  node-mkdirp: 'git+https://github.com/substack/node-mkdirp.git'
  jade: 'git+https://github.com/visionmedia/jade.git'
  redis: 'git+https://github.com/antirez/redis.git'
  node-uuid: 'git+https://github.com/broofa/node-uuid.git'
  node-mime: 'git+https://github.com/broofa/node-mime.git'
  mime-magic: 'git+https://github.com/SaltwaterC/mime-magic.git'
  debug: 'git+https://github.com/visionmedia/debug.git'
  winston: 'git+https://github.com/flatiron/winston.git'
  less.js: 'git+https://github.com/cloudhead/less.js.git'
  less: 'git+https://github.com/cloudhead/less.git'
  todo: 'git+https://github.com/cloudhead/toto.git'
  http-control: 'git+https://github.com/cloudhead/http-console.git'
  cradle: 'git+https://github.com/cloudhead/cradle.git'
  journey: 'git+https://github.com/cloudhead/journey.git'
  pilgim: 'git+https://github.com/cloudhead/pilgrim.git'
  node-glob: 'git+https://github.com/isaacs/node-glob.git'
  jsdom: 'git+https://github.com/tmpvar/jsdom.git'
  node-mongodb-native: 'git+https://github.com/mongodb/node-mongodb-native.git'
  node-pkginfo: 'git+https://github.com/indexzero/node-pkginfo.git'
  nodejs-intro: 'git+https://github.com/indexzero/nodejs-intro.git'
  wrench-js: 'git+https://github.com/ryanmcgrath/wrench-js.git'
  grunt: 'git+https://github.com/gruntjs/grunt.git'
  moment: 'git+https://github.com/timrwood/moment.git'
  q: 'git+https://github.com/kriskowal/q.git'
  mocha: 'git+https://github.com/visionmedia/mocha.git'
  node-semvar: 'git+https://github.com/isaacs/node-semver.git'
  handlebars.js: 'git+https://github.com/wycats/handlebars.js.git'
  underscore.string: 'git+https://github.com/epeli/underscore.string.git'
  node-oauth: 'git+https://github.com/ciaranj/node-oauth.git'
  vows: 'git+https://github.com/cloudhead/vows.git'
  cheerio: 'git+https://github.com/MatthewMueller/cheerio.git'
  node-mysql: 'git+https://github.com/felixge/node-mysql.git'
  node-querystring: 'git+https://github.com/visionmedia/node-querystring.git'
  node-browserify: 'git+https://github.com/substack/node-browserify.git'
  node-http-proxy: 'git+https://github.com/nodejitsu/node-http-proxy.git'
  through: 'git+https://github.com/dominictarr/through.git'
  superagent: 'git+https://github.com/visionmedia/superagent.git'
  supertest: 'git+https://github.com/visionmedia/supertest.git'
  npm: 'git+https://github.com/isaacs/npm.git'
  passport-oauth: 'git+https://github.com/jaredhanson/passport-oauth.git'
  watch: 'git+https://github.com/mikeal/watch.git'
  hogan.js: 'git+https://github.com/twitter/hogan.js.git'
  mustache: 'git+https://github.com/defunkt/mustache.git'
  node-temp: 'git+https://github.com/bruce/node-temp.git'
  node-sprintf: 'git+https://github.com/maritz/node-sprintf.git'
  nodeunit: 'git+https://github.com/caolan/nodeunit.git'
  cli-color: 'git+git://github.com/medikoo/cli-color.git'
  node-jshint: 'git+https://github.com/jshint/node-jshint.git'
  node-static: 'git+https://github.com/cloudhead/node-static.git'
  passport: 'git+https://github.com/jaredhanson/passport.git'
  shelljs: 'git+https://github.com/arturadib/shelljs.git'
  tutorial-nodejs-cli: 'git+https://github.com/oscmejia/tutorial-nodejs-cli.git'
  cli-table: 'git+https://github.com/LearnBoost/cli-table.git'
  mongoose: 'git+https://github.com/LearnBoost/mongoose.git'
  browserbuild: 'git+https://github.com/LearnBoost/browserbuild.git'
  engine.io: 'git+https://github.com/LearnBoost/engine.io.git'
  engine.io-client: 'git+https://github.com/LearnBoost/engine.io-client.git'
  socket.io: 'git+https://github.com/LearnBoost/socket.io.git'
  socket.io-client: 'git+https://github.com/LearnBoost/socket.io-client.git'
  knox: 'git+https://github.com/LearnBoost/knox.git'
  jsonp: 'git+https://github.com/LearnBoost/jsonp.git'
  node-tar: 'git+https://github.com/isaacs/node-tar.git'
  node-bindings: 'git+https://github.com/TooTallNate/node-bindings.git'
  node-fs-extra: 'git+https://github.com/jprichardson/node-fs-extra.git'
  chai: 'git+https://github.com/chaijs/chai.git'
  grunt-lib-contrib: 'git+https://github.com/gruntjs/grunt-lib-contrib.git'
  node-irc: 'git+https://github.com/martynsmith/node-irc.git'
  jasmine-node: 'git+https://github.com/mhevery/jasmine-node.git'
  node-querystring: 'git+https://github.com/visionmedia/node-querystring.git'
  highlight.js: 'git+https://github.com/isagalaev/highlight.js.git'
~/study/javascript:
  backbone.deepmodel: 'git+https://github.com/powmedia/backbone-deep-model.git'
  underscore: 'git+https://github.com/documentcloud/underscore.git'
  lodash: 'git+https://github.com/bestiejs/lodash.git'
  backbone: 'git+https://github.com/documentcloud/backbone.git'
  requirejs: 'git+https://github.com/jrburke/requirejs.git'
  r.js: 'git+https://github.com/jrburke/r.js.git'
  volo: 'git+https://github.com/volojs/volo.git'
  create-responsive-template: 'git+https://github.com/volojs/create-responsive-template.git'
  yeoman: 'git+https://github.com/yeoman/yeoman.git'
  cajon: 'git+https://github.com/requirejs/cajon.git'
  jquery: 'git+https://github.com/jrburke/jquery.git'
  backbone.marionette: 'git+https://github.com/marionettejs/backbone.marionette.git'
  backbone.wreqr: 'git+https://github.com/marionettejs/backbone.wreqr.git'
  backbone.babysitter: 'git+https://github.com/marionettejs/backbone.babysitter.git'
  flight: 'git+https://github.com/twitter/flight.git'
  bower: 'git+https://github.com/twitter/bower.git'
  codemirror: 'git+https://github.com/marijnh/CodeMirror.git'
  doctorjs: 'git+https://github.com/mozilla/doctorjs.git'
~/study/python:
  jmespath: 'git+https://github.com/boto/jmespath.git'
  anyvcs: 'git+https://github.com/ScottDuckworth/python-anyvcs.git'
  pip: 'git+git://github.com/pypa/pip.git'
  ipdb: 'git+http://github.com/gotcha/ipdb.git'
  virtualenv: 'git+https://github.com/pypa/virtualenv.git'
  jinja2: 'git+https://github.com/mitsuhiko/jinja2.git'
  flask: 'git+https://github.com/mitsuhiko/flask.git'
  flask-script: 'git+https://github.com/techniq/flask-script.git'
  frozen-flask: 'git+https://github.com/SimonSapin/Frozen-Flask.git'
  werkzeug: 'git+https://github.com/mitsuhiko/werkzeug.git'
  logbook: 'git+https://github.com/mitsuhiko/logbook.git'
  cjklib: 'git+https://github.com/cburgmer/cjklib.git'
  pudb: 'git+http://git.tiker.net/trees/pudb.git'
  ipython: 'git+https://github.com/ipython/ipython.git'
  blessing: 'git+https://github.com/erikrose/blessings.git'
  salt: 'git+https://github.com/saltstack/salt.git'
  salt-ui: 'git+https://github.com/saltstack/salt-ui.git'
  salt-formulae: 'git+https://github.com/saltstack/formulae'
  salt-api: 'git+https://github.com/saltstack/salt-api.git'
  salt-cloud: 'git+https://github.com/saltstack/salt-cloud.git'
  salt-vagrant: 'git+https://github.com/saltstack/salty-vagrant.git'
  salt-contrib: 'git+https://github.com/saltstack/salt-contrib.git'
  sqlalchemy: 'git+https://github.com/zzzeek/sqlalchemy.git'
  wtforms: 'git+https://github.com/Khan/wtforms.git'
  botocore: 'git+https://github.com/boto/botocore.git'
  libcloud: 'git+https://github.com/apache/libcloud.git'

  argcomplete: 'git+https://github.com/kislyuk/argcomplete.git'

  lxml: 'git+https://github.com/lxml/lxml.git'

  httpbin: 'git+https://github.com/kennethreitz/httpbin.git'
  envoy: 'git+https://github.com/kennethreitz/envoy.git'
  legit: 'git+https://github.com/kennethreitz/legit.git'
  tablib: 'git+https://github.com/kennethreitz/tablib.git'
  requests: 'git+https://github.com/kennethreitz/requests.git'
  grequests: 'git+https://github.com/kennethreitz/grequests.git'

  kr-sphinx-themes: 'git+https://github.com/kennethreitz/kr-sphinx-themes.git'

  gist-api: 'git+https://github.com/kennethreitz/gistapi.py.git'

  pystache: 'git+https://github.com/defunkt/pystache.git'
  pandas: 'git+https://github.com/pydata/pandas'
  ncmpy: 'git+https://github.com/cykerway/ncmpy.git'

  gevent: 'git+https://github.com/surfly/gevent.git'
  tornado: 'git+https://github.com/facebook/tornado.git'

  soundcloud-cli: 'git+https://github.com/0xPr0xy/soundcloud-cli.git'
  pyradio: 'git+https://github.com/coderholic/pyradio.git'

  sh: 'git+https://github.com/amoffat/sh.git'
  envoy: 'git+https://github.com/kennethreitz/envoy.git'

  glances: 'git+https://github.com/nicolargo/glances.git'
  powerline: 'git+https://github.com/Lokaltog/powerline.git'
  jieba: 'git+https://github.com/fxsjy/jieba.git'
  storm: 'git+https://github.com/emre/storm.git'
  fabric: 'git+https://github.com/fabric/fabric.git'
  ansible: 'git+https://github.com/ansible/ansible'
  fn.py: 'git+https://github.com/kachayev/fn.py.git'
  buildbot: 'git+https://github.com/buildbot/buildbot.git'
  beets: 'git+https://github.com/sampsyo/beets.git'
  magicmethods: 'git+https://github.com/RafeKettler/magicmethods.git'

  gateone: 'git+https://github.com/liftoff/GateOne.git'
  glue: 'git+https://github.com/jorgebastida/glue.git'
  objbrowser: 'git+https://github.com/titusjan/objbrowser'

  pythagora: 'git+https://github.com/tarmack/Pythagora.git'
  youtube-dl: 'git+https://github.com/rg3/youtube-dl.git'
  twisted: 'git+https://github.com/twisted/twisted.git'

  ckan: 'git+https://github.com/okfn/ckan.git'
  hackthebox: 'git+https://github.com/moloch--/RootTheBox.git'
  mediagoblin: 'git+git://gitorious.org/mediagoblin/mediagoblin.git'
  ckan: 'git+https://github.com/okfn/ckan.git'
  reddit: 'git+https://github.com/reddit/reddit.git'

  python_koans: 'git+https://github.com/gregmalcolm/python_koans.git'
  python-guide: 'git+https://github.com/kennethreitz/python-guide.git'
  #introduction_to_sqlalchemy: 'git+https://bitbucket.org/zzzeek/pycon2013_student_package.git'
  probabilistic-programming-and-beyesian-methods: 'git+https://github.com/CamDavidsonPilon/Probabilistic-Programming-and-Bayesian-Methods-for-Hackers.git'
  python-patterns: 'git+https://github.com/faif/python-patterns.git'
  learn-pandas: 'git+https://bitbucket.org/hrojas/learn-pandas.git'

  w3lib: 'git+https://github.com/scrapy/w3lib'
  scrapy: 'git+https://github.com/scrapy/scrapy.git'
  scrapely: 'git+https://github.com/scrapy/scrapely.git'

  calibre: 'git+git://github.com/kovidgoyal/calibre.git'

  kaptan: 'git+https://github.com/emre/kaptan.git'
  aws-cli: 'git+https://github.com/aws/aws-cli.git'

  youcompleteme: 'git+https://github.com/Valloric/YouCompleteMe.git'

  ranger: 'git+git://git.savannah.nongnu.org/ranger.git'

  readthedocs: 'git+https://github.com/rtfd/readthedocs.org.git'
  django: 'git+https://github.com/django/django.git'
  norman: 'hg+https://bitbucket.org/aquavitae/norman/src'
  bpython: 'hg+https://bitbucket.org/bobf/bpython/'
  urwid: 'hg+https://excess.org/hg/urwid/'
  sphinx: 'hg+https://bitbucket.org/birkenfeld/sphinx'
  sphinx-contrib: 'hg+https://bitbucket.org/birkenfeld/sphinx-contrib'
  pexpect-u: 'hg+https://bitbucket.org/takluyver/pexpect'
  pygments: 'hg+http://bitbucket.org/birkenfeld/pygments-main'
  docutils: 'svn+http://svn.code.sf.net/p/docutils/code/trunk'

Worktree configuration

Repositories can declare worktrees—additional checkouts of specific tags, branches, or commits in separate directories. Worktrees are listed under the worktrees key of a repository entry:

# Worktree configuration example
#
# vcspull can manage git worktrees declaratively. Each repository can
# define a list of worktrees under the "worktrees" key. Worktrees
# are checked out in separate directories alongside the main clone.
#
# Each worktree entry requires:
#   dir:     Path for the worktree (relative to workspace root or absolute)
#   Exactly one of:
#     tag:    Tag to checkout (creates detached HEAD)
#     branch: Branch to checkout (updatable via pull)
#     commit: Commit SHA to checkout (creates detached HEAD)
#
# Optional fields:
#   lock:        Lock the worktree to prevent accidental removal
#                (passes --lock to `git worktree add`). This is a
#                *worktree-level* lock — it is separate from the
#                repo-level options.pin that guards configuration
#                entries against mutation by vcspull commands.
#   lock_reason: Reason for locking (implies lock: true)

~/code/:
  myproject:
    url: "git+https://github.com/myorg/myproject.git"
    worktrees:
      # Pin a stable release for reference
      - dir: "../myproject-v2.0"
        tag: "v2.0.0"
        lock: true
        lock_reason: "production reference"

      # Track a development branch
      - dir: "../myproject-dev"
        branch: "develop"

      # Pin a specific commit for debugging
      - dir: "../myproject-bisect"
        commit: "abc1234"

  another-repo:
    url: "git+https://github.com/myorg/another-repo.git"
    worktrees:
      - dir: "../another-repo-v1.0"
        tag: "v1.0.0"
      - dir: "../another-repo-stable"
        branch: "stable"

Each worktree entry requires:

  • dir: Path for the worktree (relative to workspace root or absolute)

  • Exactly one of tag, branch, or commit

Optional fields:

  • lock: Lock the worktree to prevent accidental removal

  • lock_reason: Reason for locking (implies lock: true)

See vcspull worktree for full command documentation.

Repository pinning

Repositories can be pinned to prevent automated commands from modifying their configuration entries. This is useful for pinned forks, company mirrors, or any repository whose URL or config shape you manage by hand.

Here is a configuration showing all three pin forms side by side:

~/code/:
  # Global pin — blocks ALL operations (import, add, discover, fmt, merge)
  internal-fork:
    repo: "[email protected]:myorg/internal-fork.git"
    options:
      pin: true
      pin_reason: "pinned to company fork  update manually"

  # Per-operation pin — only import and fmt are blocked
  my-framework:
    repo: "[email protected]:myorg/my-framework.git"
    options:
      pin:
        import: true
        fmt: true
      pin_reason: "URL managed manually; formatting intentional"

  # Shorthand — equivalent to pin: {import: true}
  stable-dep:
    repo: "git+https://github.com/upstream/stable-dep.git"
    options:
      allow_overwrite: false

Pin all operations

Set pin: true inside options to block every mutation command. This is the simplest form — no automated vcspull command can modify this entry:

~/code/:
  internal-fork:
    repo: "[email protected]:myorg/internal-fork.git"
    options:
      pin: true
      pin_reason: "pinned to company fork  update manually"

Pin specific operations

Pass a mapping instead of a boolean to pin only the operations you care about. Unlisted keys default to false (unpinned):

~/code/:
  my-framework:
    repo: "[email protected]:myorg/my-framework.git"
    options:
      pin:
        import: true
        fmt: true
      pin_reason: "URL managed manually; formatting intentional"

Available pin keys:

Key

Blocks

import

vcspull import --sync from replacing this URL

add

vcspull add from overwriting this entry

discover

vcspull discover from overwriting this entry

fmt

vcspull fmt from normalizing this entry

merge

Duplicate-workspace-root merge from displacing this entry

Shorthand: allow_overwrite

allow_overwrite: false is a convenience shorthand equivalent to pin: {import: true}. It only guards against vcspull import --sync:

~/code/:
  stable-dep:
    repo: "git+https://github.com/upstream/stable-dep.git"
    options:
      allow_overwrite: false

Pin behavior

  • Defaults — repositories are unpinned. All operations proceed normally unless a pin is explicitly set.

  • Boolean pinpin: true blocks all five operations (import, add, discover, fmt, merge).

  • Per-operation pin — only the listed keys are blocked; unlisted keys default to false (unpinned).

  • pin_reason — an optional human-readable string shown in log output when an operation is skipped. It is purely informational and does not imply pin: true on its own.

  • Advisory — pins prevent automated commands from modifying the entry. You can still edit the configuration file by hand at any time.

Each command handles pins differently:

Command

Pin effect

Log level

vcspull import --sync

Skips URL replacement

info

vcspull add

Skips with warning

warning

vcspull discover

Silently skips

debug

vcspull fmt

Preserves entry verbatim

(silent)

Workspace merge

Pinned entry wins conflict

info

Note

The pin and pin_reason fields described here live under options and guard the configuration entry against mutation by vcspull commands.

This is different from the worktree-level lock / lock_reason that lives inside individual worktrees entries and passes --lock to git worktree add. See vcspull worktree for worktree locking.

Import provenance

When repositories are imported with --sync or --prune, vcspull records which service and owner the import came from. This is stored in a metadata.imported_from field:

~/code/:
  my-project:
    repo: "[email protected]:myorg/my-project.git"
    metadata:
      imported_from: "github:myorg"

The metadata block is managed by vcspull — you generally don’t need to edit it by hand. It is used to scope pruning: when re-importing with --sync, only entries tagged with the matching source are candidates for removal.

See vcspull import for full details on --sync and --prune.

Caveats

SSH Git URLs

For git remotes using SSH authorization such as git+git@github.com:tony/kaptan.git use git+ssh:

git+ssh://[email protected]/tony/kaptan.git