Publish to remote directories using git + ssh.

Quickstart

  1. Initialize a publish repository. This creates a bare repository on host at remote_path and clones it to local_path (default ./<remote_name>). It also creates an initial commit with the message 'Init Version 0'.

    $ git publish init <host>:<remote_path> [local_path]
  2. Add a publish worktree. This creates a directory on the publish server (default origin) that will stay in sync with a branch (default main or init.defaultBranch if set).

    $ git publish worktree <remote_path> [local_path]
  3. Edit files, and publish to a remote repository (default origin). An optional commit message can be used instead of the default. A custom commit message has to be set only once, as if no message is given the last commit message is used with an incremented version number.

    $ touch index.html
    $ git publish [-m "Custom Message"]

Setup

Currently, the required software has to be installed manually.
A install script is provided to install git-publish and it’s documentation.

Requirements

Only a few common programs are required to use git-publish. They can typically be installed with your distro’s package manager.

git

git is required for, well, the git command. It’s required to be installed on both your local machine and the remote server.

ssh

ssh is required for running commands on the remote server and pulling from git repositories. It will also need to be installed on both the local machine and the remote server. The remote server should be running an ssh server that is accessible from the local machine.

bash

Currently, the git-publish script uses a few "bashism"s, it is not guaranteed to run in other shells. Because of this, bash is listed as a requirement, however it may work in others.

Installing git-publish

git-publish is available on github. Clone the repo to get started, then chose from an automatic install script or manual installation of the software.

Installation script

An installation script is provided that copies the needed files to the appropriate directories. You can run this script with make using make install or run the script independently with ./install.sh.

The script will prompt for you to choose a directory on your $PATH to copy the git-publish script to. It will the copy the git-publish.1 man page to the first path returned from the manpath command. You will need write access to both directories!

Running the install.sh script
$ sudo make install
[sudo] password for user:
Select an install directory on your $PATH:
 (1) /usr/local/bin [default]
 (2) /usr/bin
 (3) /bin
 (4) /usr/local/sbin
 (5) /usr/lib/jvm/default/bin
 (6) /opt/microchip/xc8/v2.40/bin
 (7) /home/user/.local/bin/
Input a number (enter for default):
Installing 'git-publish' to '/usr/local/bin' -- success
Installing 'docs/git-publish.1' to '/usr/local/man' -- success
Successfully installed git-publish.

Manual Installation

To install the git publish command manually, simply place the script somewhere it is available on your $PATH and mark it executable.

$ cp git-publish ~/.local/bin
$ chmod a+x ~/.local/bin/git-publish

Alternatively to stay up-to-date with the latest release, link directly to the git repository. It’s recommended to checkout a tag or a commit in this case.

The following has not been tested
$ git checkout --detach main
$ ln -s git-publish ~/.local/bin
$ chmod a+x git-publish

Additional Considerations

.git

Because we create a linked worktree to an existing repo, .git is a file that contains a path to the common repo. This might expose sensitive information e.g. if serving the directory with a web server. An example .git created with the publish command contains the following:

gitdir: /git/website.git/worktrees/example

Linux File Permissions

The ssh user that you use to connect with git needs to have write permissions to both the bare repository and the publish directory. An example setup is a publish group that has rwx permissions to each parent directory. The example below will give the git user appropriate permissions to push to a repository at <server>:/srv/git/example.git and publish files in /srv/publish/example. Additional users can be granted permissions by adding them to the publish group.

# mkdir /srv/git /srv/publish
# groupadd publish
# chgrp publish /srv/git /srv/publish
# chmod g+w /srv/git /srv/publish
# usermod -aG publish git

Alternatively, you could just work in the user’s home directory:

$ mkdir ~/git ~/publish
git publish requires absolute paths for all arguments that take a path.

SSH Host

The publish server is required to be accessible over ssh. This means we have to authenticate with it somehow, however, most of the time it is recommend to disable password logins for security. The easiest way to authenticate using a key pair is by installing the needed key to your ssh-agent. This also means you won’t have to type in any passwords to login to the publish server.

Sometimes, you may not have an ssh-agent running or maybe you are running the server on a non-standard port. These situations require you to configure the ssh connection. The current recommended way to do this is by editing ~/.ssh/config and configuring a custom host for your ssh server. An example of one such host is given below:

Host <custom-host>
    Hostname       <ip>
    Port           <port>
    User           <user>
    IdentityFile   <path/to/private_key>

All configuration options are optional, just use what you need to change from the defaults. You can now use <custom-host> as a hostname like:

$ git publish init <custom-host>:/path/to/repo.git

Usage

The basic workflow this suits is to keep a remote directory in sync with one on your local machine. It achieves this by pushing a local repository to a remote repository over ssh. This means an intermediate git host such as Github is not required. A git server hook that fires after a successful push is used to trigger a force checkout of the main branch for every published worktree on the server. These worktree directories contain a copy of the latest committed files.

Initialize Publish Repository

When you initialize a repository with the git publish init command, it does three main things:

  1. Creates a bare repository on the given host and installs a post-receive hook.

  2. Initializes a local repository and adds the remote as origin.

  3. Creates an empty commit and pushes it to the configured branch.

Let’s initialize a publish repository on example.com. We’ll call it website.

Example 1. Initializing a publish repository
$ git publish init example.com:/git/website.git
Connecting to server: example.com
Repo: /git/website.git
[email protected]'s password:
Initialized empty Git repository in /git/website.git/
Find this repo at: example.com:/git/website.git
Cloning into local directory: website/
Initialized empty Git repository in /home/user/website/.git/
[main (root-commit) aa3d154] Init Version 0
[email protected]'s password:
Enumerating objects: 2, done.
Counting objects: 100% (2/2), done.
Writing objects: 100% (2/2), 170 bytes | 170.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0), pack-reused 0
To example.com:/git/website.git
 * [new branch]      main -> main

This created two repositories: /git/website.git on our server and website/.git on our local machine.

Inside of our local repo, if we run git remote get-url origin (or whatever you set the remote to be) we can see it is set up to push/pull from example.com:/git/website.git. The git-publish command just runs a git init and then git remote add to setup the local repository. It then creates an empty commit and pushes it to the remote with the message 'Init Version 0'. This empty commit can be viewed as the 'creation' commit.

In our server repo, the only interesting thing you will find is a shell script post-receive that gets installed in <repo>/hooks/ e.g. example.git/hooks/post-receive in the case of the example above. This script, which runs after a successful push, simply finds all *.publish files in the hooks/ directory and executes them. Other than this simple one-line script, the bare repository is bog standard.

Create Publish Worktree

The worktree(s) that hold the actual published files are created with the git publish worktree command.

Let’s create a publish worktree on example.com at /var/www/website/. Note how we don’t have to specify a host like example.com, it is extracted from the configured remote (default origin).

Example 2. Create a publish worktree
$ git publish worktree /var/www/example
Using remote 'origin' @ example.com:/git/website.git
Creating publish worktree: /var/www/example
[email protected]'s password:
Preparing worktree (detached HEAD cf7f00d)
HEAD is now at cf7f00d Init Version 0
Successfully created worktree: /var/www/example

This created a new linked worktree at /var/www/example and checked out the latest commit in the configured branch. If you haven’t yet published any files, the directory will be empty except for the .git file. Refer to the .git section in Additional Considerations for info about this file.

This command also places a .publish script into the common repository’s hook/ directory. This script contains the worktree path and the branch to stay up-to-date with. The file is named after the worktree path, so the example worktree’s script would be var-www-example.publish. This script gets run on every successful push and checks out the latest commit in the configured branch. There is currently no way to change the branch a worktree tracks.

Publish Files to Worktrees

Files are published to remote worktrees with the git publish command. Most of the time, no arguments are required.

Let’s publish an index.html to our /var/www/example/.

Example 3. Publish a file
$ touch index.html
$ git publish index.html
Publishing files: index.html
Using default commit message: Publish Version
Using default version: 1
[main 3e9a0d8] Publish Version 1
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 index.html
[email protected]'s password:
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 259 bytes | 259.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0), pack-reused 0
remote: Updating worktree: /var/www/example
remote: HEAD is now at 3e9a0d8 Publish Version 1
To example.com:/git/website.git
   cf7f00d..3e9a0d8  main -> main
Published Version: 1

This published the specified files, and then checked out the commit in the remote worktree. Because this was our first commit (the previous commit’s version was 0) and no message was specified with -m, the commit message defaults to 'Publish Version' plus the version appended to the end. Note how this changes in the example below.

Now, let’s add some error pages.

Example 4. Publish a directory
$ touch 404.html 403.html
$ git publish
Publishing directory: /home/user/website
Using last commit message: Publish Version 1
Incrementing commit version: 1 + 1
[main 3e9a0d8] Publish Version 2
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 404.html
 create mode 100644 403.html
[email protected]'s password:
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 4 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 259 bytes | 259.00 KiB/s, done.
Total 2 (delta 0), reused 0 (delta 0), pack-reused 0
remote: Updating worktree: /var/www/example
remote: HEAD is now at 3e9a0d8 Publish Version 2
To example.com:/git/website.git
   cf7f00d..3e9a0d8  main -> main
Published Version: 2

Since we didn’t specify any files to publish, it publishes every file in the directory. No commit message was specified, so it defaults to the last commit message with an incremented version number.

Existing Repositories

The publish command works on any existing git repository. The publish worktree command works on repositories that are configured to use an ssh remote and you have general SSH access to the server hosting the repository. Note this means it will not work on hosted git solutions e.g. GitHub.

When the repository already exists on the host, you should use the publish hook command to update worktrees automatically. This command installs a post-receive hooks that gets run after every commit. After the hook has been installed, you can commit and push as normal and any worktrees that have been created will be updated.

Example 5. Install hook
$ git publish hook example.com:/git/website.git
Connecting to server: example.com
Repo: /git/website.git
Installed post-receive hook: /git/website.git/hooks/post-receive

git-publish(1)

Synopsis

git publish [init|worktree|hook] [-r remote] [-b branch] [-m message] [files|host:remote_path|remote_path] [local_path]

git publish [-r] [-b] [-m message] [files]
git publish init [-r] [-b] host:remote_path [local_path]
git publish worktree [-r] [-b] [-d] remote_path
git publish hook host:remote_path

Options

All commands accept some common configuration options in case you’d like to change the defaults. All dashed arguments should come before any positional arguments and after the subcommand.

-r remote

The name of the remote to perform the publish operation on. (Default origin)

-b branch

The name of the branch to perform the publish operation on. (Default main or init.defaultBranch if set)

Commands

When run with no arguments, it will publish all files in the repository to <remote>/<branch>. Any worktrees that are on <remote> and are configured to track <branch> will get updated.
Note that all remote_path arguments MUST be absolute paths.

publish [<options>] [files]

Publish files to <remote>/<branch>. Any worktrees that have been created on origin will be updated.

-m message

Commit message to use. If no version is found in the message, one will be appended to the end. (Default: Last commit if exists, else "Publish Version")

files

List of files to publish. (Default: -A All files)

publish init [<options>] host:remote_path [local_path]

Initialize a repository on host at remote_path and link it with local_path.

host

Remote host url. Accepts [user@]host and ssh://[user@]host[:port] formats.

remote_path

Absolute path of the bare git repository on host. e.g. /git/something-like.git

local_path

Optional path to the local repository. (Default: Create a directory with the remote repository’s name e.g. ./something-like/)

publish worktree [<options>] remote_path

Create a new worktree on <remote> at remote_path that gets updated on every push to <branch>. If the -d flag is specified, delete an existing worktree.

-d

Delete an existing worktree instead of creating a new one.

remote_path

Absolute path of worktree on the remote.

publish hook host:remote_path

Installs a post-receive hook to remote_path on host. This allows publish worktrees to be created on an existing repository. Note that this command can be run from anywhere.

host

Remote host url. Accepts [user@]host and ssh://[user@]host[:port] formats.

remote_path

Absolute path of the bare git repository on host. e.g. /git/something-like.git

Exit status

0

Success.
The program thinks everything was successful (you be the judge).

1

Error.
An error occurred, probably an invalid argument or git operation.


github | documentation
© 2022-2024 Rex McKinnon