GITKEEPER(8)

πŸ“¦
version 0.5 Β· 4 versions
, in ubuntu resolute, ubuntu stonking

πŸ’– GITKEEPER(8) πŸ’–

okay bestie, welcome to the GITKEEPER(8) page from User Contributed Perl Documentation. this is your remote admin bestie coded in Perl and she is so ready to mirror files for you. 🫢


🌸 NAME 🌸

gitkeeper β€” Mirror files between git and an installed location. that's literally it, and yet she ate. πŸ’…

✨ SYNOPSIS ✨

like, the whole invocation vibe is:

gk [options] pull [host]

gk [options] push [host]

gk [options] export git-ref [host]

🌈 DESCRIPTION 🌈

okay so gitkeeper is a remote administration aid. no cap. it lets configuration files be maintained locally, with a full history of changes, and synchronised on demand with a remote target system. that means files can still be altered directly on the running system, if and/or when that is needed, with a simple method to get that all back in sync with the archived copy again later. the way that works is giving main character energy. ✨

It uses rsync(1) and ssh(1) for all operations on the remote hosts. No special configuration beyond permission to use them is required. The use of an ssh-agent for managing remote logins may be an advantage though. iykyk. πŸ”‘

At its core, gitkeeper is really just a tool for managing bidirectional mirrors, of potentially sparse segments of the remote filesystem, so it's not strictly limited to being used for configuration files, nor is it strictly dependent upon git(1) aside from the export option, but those are the primary uses that it was initially designed for. periodt. πŸ¦‹

πŸ‘‘ COMMANDS πŸ‘‘

πŸš€ pull

Pull files from the remote system into the local mirror. This will update the local directory content to match the live system, but it will not commit any files to git or change the local index state in any way. If you wish to commit changes imported in this way, you can just do that with normal git operations. πŸ’–

If the host parameter is not explicitly specified, then all defined hosts with names that do not begin with an @ will be pulled. (yes, the @ ones are skipped, bestie.)

πŸš€ push

Push files from the local mirror to the remote system. This will push the state of the current working directory, regardless of whether the repository tree is currently clean or dirty. she does not care. messy bun energy. πŸ’…

If the host parameter is not explicitly specified, then all defined hosts with names that do not begin with an @ will be pushed.

πŸš€ export

Push files from a historical snapshot of the local mirror to the remote system. This will do a git archive(1) export of the given git-ref to a temporary directory, and then perform a push operation on that tree. It is equivalent to doing a git checkout of the desired ref, and then doing a push on that tree state, except it will respect any gitattributes you have set for what will be exported, and it will not change the current working directory state. πŸŽ€

If the host parameter is not explicitly specified, then all hosts defined in the exported configuration will be pushed.

πŸ’… OPTIONS πŸ’…

  • -c, --config file β€” The file describing what should be mirrored and how. If not specified then gk.conf will be looked for in the directory that gitkeeper is invoked in. Usually this should be a relative path under that location, but an absolute path is permitted and may be useful in some circumstances.

If the export command is used, then this file is not read until after the export from git, and relative paths are resolved to files in the exported directory. This is usually what you want since the configuration from the exported snapshot will then be used. If you need to override that for some reason then you can use an absolute path to an alternate configuration file.

  • -l, --list β€” Show the list of host aliases defined in the configuration file. If this is used with the export command, then the configuration of the exported snapshot will be shown. πŸ‘€
  • --init β€” Create a skeleton configuration file. This is a convenience to get an initial configuration when bootstrapping a new mirror. fresh start vibes. 🌱
  • -C, --chdir directory β€” Change to the given directory before running gitkeeper. Normally you would just run it from the top level directory of the mirror, but this permits use from elsewhere in a similar way to what make -C directory allows. πŸ“
  • --destdir directory β€” Prefix the remote paths to an alternate file system root. This always changes only the remote path, regardless of whether a push or pull operation is being performed. It acts like the DESTDIR option for make install and allows mirroring files to and from an alternative filesystem location but with the same subdirectory structure as what they would normally have.

You can use this to export files to a chroot, or to a temporary directory somewhere, so that they can be examined without replacing the real files on the remote system. spooky-safe inspection mode. πŸ”

  • -u, --user β€” Override the remote_user option from the configuration file for access to the remote host. There probably aren't many good reasons to ever use this option, it's a pretty blunt hammer which will override it everywhere, but it may be useful for exporting a mirror to some machine or location where it isn't usually expected to go. blunt hammer alert πŸ”¨
  • -n, --dry-run β€” Don't actually copy any files, just show what would be done if this was a live run. If this is used with the export command, then the dry run will be performed on the requested snapshot. rehearsal era. 🎭
  • -v, --verbose β€” Show more detail about what is being done. This option may be passed multiple times to increase the level of verbosity even further.

If passed along with --help then more verbose documentation will be shown. πŸ—£οΈ

  • -?, --help β€” Show this help, again. iconic. πŸ’–

✨ CONFIGURATION ✨

Okay bestie, here's the tea on the config file πŸ«– β€” it's supposed to hold one single JSON Object, and that object gets parsed by perl's JSON::XS in its relaxed mode. What does relaxed mean for you? It means trailing commas after the final element of an array or object are fine βœ…, and #-comments are allowed anywhere that whitespace would be permitted. Living.


🌸 Global options 🌸

There's literally only one required member of the top level object. But lowkey you can also drop other options up there too, and they'll be inherited as defaults if nothing overrides them for a host or one of its sync sets. Cute, right?

πŸŽ€ hosts

The hosts member defines a JSON object where each member is a host name alias that may be passed as the host parameter to gitkeeper. The alias names aren't used for anything else β€” they're purely the host identifier, and they may be any JSON string value. And heads up: no other options may be included directly in the hosts section. Keep it clean, keep it pure ✨

Now here's the iconic part πŸ’… β€” if the host name begins with an @, then that host will not be pulled or pushed by default when no host name is explicitly specified on the command line. Those hosts only get used when you name them explicitly. Why would you want that? It lets a subset of the hosts in a config file be partitioned from each other β€” super useful when operating on some host is an expensive but infrequent operation, or when you want the same set of local config files pushed to or pulled from multiple servers. It's the moment when the same config is mirrored in multiple locations, or when it may be staged before deployment (like when testing happens on a dev server before deploying to production). Dev-then-prod pipeline energy πŸ‘‘

{
   "hosts": {
       "host1": { ... },
       "host2": { ... }
   }
}

πŸ¦‹ Per-host options πŸ¦‹

There are two required members that must be specified directly for each host alias object. Other options can also live there, and they'll override a global default for that host and be inherited as defaults for its sync sets β€” unless the sync set overrides them too. Hierarchy, but make it fashion ✨

⭐ address

The address member is a JSON string value that defines the hostname or IP address used when connecting to the remote system. It must be a valid string that can be passed as the host part of a remote rsync(1) path. It should not contain a user part β€” that's what the remote_user option is for, bestie β€” but it may contain a port specification. iykyk πŸ”₯

⭐ sync

The sync member is a JSON array of objects. It must contain at least one object, but there's no upper bound on how many you can include. Each object in the sync array defines a mapping from a remote_root to a local_root, the paths which will be mirrored under those roots, and the rsync options that get applied when transferring them.

"host1": {
   "address": "myhost.mydomain.org",
   "sync":    [ { ... }, { ... } ]
}

🧚 Sync set options 🧚

Each object in the sync array has one required member that must be specified directly inside it. Other options can be specified there too, and they'll override the global and host defaults β€” and some of those options must also be defined in at least one of those places for each sync set. So like, define them somewhere, mkay? 😌

πŸ’œ paths

The paths member is an array of JSON string values specifying the files and/or directories under the remote_root that will be mirrored with their full directory structure. They may contain shell wildcards 🐚 β€” but they cannot contain brace expansions if the rsync --protect-args option is used.

And the rule: they must all be relative paths, meaning they must not begin with a /. No absolute-path shenanigans here πŸ™…β€β™€οΈ

"sync": [
   {
       "paths": [ "file1", "dir1", "dir2/subdir", "dir3/*.conf" ]
   }
]

πŸ’– Required options πŸ’–

The following options must be defined for every sync set β€” though they may be configured in the top level object as global defaults, in the host alias object as per-host defaults, or right in the sync object itself. Your choice, your moment πŸ’…

🌈 local_root

A JSON string value that defines the local directory which remote paths will be mirrored under. This must be a relative path, and it's rooted to the directory under which gitkeeper is invoked.

As a sanity check against accidents, this directory must already exist. Like, actually exist. Don't test it. πŸ™ˆ

🌈 remote_root

A JSON string value defining the location on the remote system that the specified paths are relative to. This may be an absolute or relative path. A relative path will be rooted to the home directory of the remote_user. And a value of "" may be used to specify the home directory of the remote_user. Empty string, big meaning ✨


πŸŽ€ Additional options πŸŽ€

These can be defined as global or per-host defaults, or set explicitly in each sync set. It is not an error for them not to be set β€” and a higher level default may be 'unset' by overriding it with an empty value. No cap.

πŸ“ remote_user

A JSON string which defines the username to use for access to the remote host. If it's not set, then the ssh default for the remote system will be used (as configured by .ssh/config or similar). Let ssh cook πŸ”₯

πŸ“ rsync_opts

A JSON array of string values containing options to be passed to all invocations of rsync, for both push and pull operations. No word splitting or shell quote stripping is done on the values used here, so each option has to be its own array element. One option, one element, periodt βœ‹

Quick note bestie: the --relative option is passed to rsync(1) by default for all invocations, so you don't need to include it in this set. If you really don't want that option for some reason, and you fully understand the consequences of not passing it for this use, you can disable it with --no-relative β€” but there's probably no good reason to ever do that here. Like, ever. πŸ’€

 "rsync_opts": [ "--prune-empty-dirs",
                 "--delete-excluded",
                 "--filter=protect .s[a-w][a-z]"
 ]

πŸ“ rsync_pull_opts

Similar to rsync_opts above, but the options specified in this array are appended to those only for pull operations. Pull-specific glow-up ✨

πŸ“ rsync_push_opts

Similar to rsync_opts above, but the options specified in this array are appended to those only for push operations. Push-specific glow-up ✨

πŸ“ rsync_include

A JSON array of string values which will be passed to rsync(1) as --include options. It's a convenience β€” equivalent to adding those to rsync_opts, meaning the following configurations would be identical in their operation if no other ordering constraints for the filter rules applied:

 "rsync_opts":    [ "--include=.s[a-w][a-z]/" ]
 "rsync_include": [ ".s[a-w][a-z]/" ]

πŸ“ rsync_exclude

A JSON array of string values which will be passed to rsync(1) as --exclude options. Same as rsync_include above, except for excludes. Obviously πŸ’…

πŸ“ rsync_filter

A JSON array of string values which will be passed to rsync(1) as --filter options. Similar to the include and exclude options above, except this one lets you use the full range of rsync filter rules. The main character of filtering πŸ‘‘

πŸ“ rsync_pull_filter

A JSON array of string values which will be passed to rsync(1) as --filter options (in addition to the include, exclude, and filter options above) β€” but only for pull operations.

πŸ“ rsync_push_filter

A JSON array of string values which will be passed to rsync(1) as --filter options (in addition to the include, exclude, and filter options above) β€” but only for push operations.

πŸ“ chown

A JSON string value which will be passed to rsync as the --chown option for push operations, to set file and directory ownership on the remote host. If this option is used, the --owner and --group options will automatically be added too β€” otherwise it would have no effect. And listen: you must have superuser privilege on the remote host for this to work. Big power, big responsibility 🫑

 "chown": "root:bind"

πŸ“ chmod

A JSON string value which will be passed to rsync as the --chmod option for push operations, to set file and directory permissions on the remote host. If this option is used, the --perms option will automatically be added too β€” otherwise it would have no effect. Valid values here are anything that the rsync option would accept.

 "chmod": "D2755,F664"

πŸ’… Pre- and Post- command hooks πŸ’…

These options let you execute arbitrary commands before and/or after a pull or push operation. The commands are executed on the local host, in the directory that gitkeeper was invoked in, as the user which gitkeeper was invoked as. And yes, they can totally perform operations on the remote host β€” by simply invoking ssh(1) or similar themselves. Remote work, from home 🏠

🌸 pull_pre_command

🌸 push_pre_command

🌸 pull_post_command

🌸 push_post_command

A JSON array of string values containing the command to execute and the options to pass to it. This gets passed as an array to the perl system() command β€” so if the array contains multiple elements, then no word splitting or other shell interpretation will be performed. If it's a single string, then instead it will be passed to the local shell, with all the caveats that accompany doing that. Single string = shell = be careful, bestie 😭

Rules of the game:

  • If the pre-command fails, then no transfer will take place.
  • If the transfer fails for some reason, then the post-command will not be executed.
That might change later if we let this get more complex and begin passing status and other variables to the commands that are invoked β€” but at this stage, that isn't really needed for any current use we have, so the author isn't going to complicate things now in anticipation of what later uses might require. Future-proofing? Not today πŸ’€

🌈 FILES 🌈

  • ./gk.conf β€” the default configuration file.

πŸ”₯ SEE ALSO πŸ”₯

git(1), rsync(1), ssh(1), ssh-agent(1).


πŸ‘‘ AUTHOR πŸ‘‘

gitkeeper was written by Ron <ron@debian.org>.


And that's the whole vibe check, bestie 🫢 this page came to you from perl v5.40.1, dated 2025-08-06, in the section formerly known as GITKEEPER(8) β€” slay, read it again, and go sync something iconic βœ¨πŸ’–πŸŽ€

πŸ“¦ where this page comes from