* optional Syntax highlighting
* Error page
* Configurable output
- * configurable file names
+ * configurable file names, with variable substitution
* configurable directory structure
+* Paginated output
* Configurable limits for RAM and disk space usage
* Site-wide and per-repository asset files
This should be followed by the `[gitsy_outputs]` section, which defines which input templates to use, and which files to output. Input templates that are not specified will not be generated, so you can disable any output types that you don't need. Templates can be used as many times as desired, generating arbitrarily many outputs.
+A few special variables can be used in output filenames:
+
+| Variable | Purpose |
+|--------------|------------------------------------------------------------------------|
+| `%REPO%` | Replaced with the name of the current repository. |
+| `%ID%` | Replaced with the ID/hash of the current object (commit, branch, tag). |
+| `%PAGE%` | Replaced with the current page, if pagination is enabled. |
+| `%NAME%` | Replaced with the name of the current object (file) |
+| `%PATH%` | Replaced with the full path of the current object (file) |
+| `%REF%` | Replaced with the reference name of the current object (branch, tag) |
+| `%TEMPLATE%` | Replaced with the template directory path (asset files) |
+
An optional `[gitsy_extra]` section can be used to provide global, user-defined key/value pairs to all of the templates. Use this if you want to add custom site-wide variables for use in your templates.
Finally, zero or more sections with arbitrary names define individual Git repositories to index. Here, you can override most of the global settings at a per-repository level. This is more powerful and allows specifying more metadata than bulk-import.
Tera templates support custom functions and filters, and Itsy-Gitsy defines a few for convenience:
-| Name | Type | Purpose | Example |
-|---------------------|----------|--------------------------------------------|--------------------------------------------------|
-| only_files | filter | Filter the file tree into only files | {{ all_files \| only_files }} |
-| only_dirs | filter | Filter the file tree into only directories | {{ all_files \| only_dirs }} |
-| hex | filter | Output a number as a hex string | {{ 17 \| hex }} |
-| oct | filter | Output a number as an octal string | {{ 17 \| oct }} |
-| mask | filter | Bitwise mask a number with another number | {{ 17 \| mask(mask="0x77") }} |
-| ts_to_date | function | Convert a timestamp and offset to a date | {{ts_to_date(ts=ts_utc, tz=ts_offset)}} |
-| ts_to_git_timestamp | function | Same, but print in standard Git format | {{ts_to_git_timestamp(ts=ts_utc, tz=ts_offset)}} |
+| Name | Type | Purpose | Example | |
+|---------------------|----------|--------------------------------------------|--------------------------------------------------|--------------|
+| only_files | filter | Filter the file tree into only files | {{ all_files \| only_files }} | |
+| only_dirs | filter | Filter the file tree into only directories | {{ all_files \| only_dirs }} | |
+| hex | filter | Output a number as a hex string | {{ 17 \| hex }} | |
+| oct | filter | Output a number as an octal string | {{ 17 \| oct }} | |
+| mask | filter | Bitwise mask a number with another number | {{ 17 \| mask(mask="0x77") }} | |
+| url_string | filter | Convert a string to a url-friendly "slug" | {{ file.path | url_string}} |
+| ts_to_date | function | Convert a timestamp and offset to a date | {{ts_to_date(ts=ts_utc, tz=ts_offset)}} | |
+| ts_to_git_timestamp | function | Same, but print in standard Git format | {{ts_to_git_timestamp(ts=ts_utc, tz=ts_offset)}} | |
+
+`url_string` can be used in conjunction with the `%PATH%` and `%REF%` filename variables. Both use a very primitive form of "slugifying" the strings into a format that can be used in a URL. This allows for basic permalinks.
## Security
Small repositories with dozens to hundreds of commits can be generated on the order of a few seconds or less. Large repositories take *considerably* longer; parsing 1,000,000 commits from the Linux kernel repository with `limit_tree_depth = 3`, `limit_context = 10` and `limit_diffs = 100` took ~30 minutes on a fast laptop, and produced a ~2GB website.
+## Other Considerations
+
+The default templates are provided as a starting point, and demonstrate most features. It is fully expected that you will customize or replace them.
+
+The default templates use the `%ID%` variable for outputting files, directories, branches, and tags. These are guaranteed to be unique and URL-friendly. Links to files and directories, however, are invalidated when changes are made to the repository. To get "permalinks", you can change the output variables to use `%PATH%` instead, and replace `{{file.id}}` and `{{dir.id}}` with `{{file.path | url_string}}` and `{{dir.path | url_string}}` in all of the template files.
+
## Limitations
* Only indexes history of one branch.
-* No permalinks. Links to file contents are invalidated if the file changes.
* High memory usage for large repositories.
* Limited to the pre-defined set of input templates.
* Leaves output in unknown, partial state in case of errors.
git::{dir_listing, parse_repo, GitFile, GitRepo, GitsyMetadata},
loud, louder, loudest, normal, normal_noln,
settings::{GitsyCli, GitsyRepoDescriptions, GitsySettings, GitsySettingsRepo},
- template::{DirFilter, FileFilter, HexFilter, MaskFilter, OctFilter, Pagination, TsDateFn, TsTimestampFn},
+ template::{DirFilter, FileFilter, HexFilter, MaskFilter, OctFilter, Pagination, TsDateFn, TsTimestampFn, UrlStringFilter},
util::{GitsyError, GitsyErrorKind, VERBOSITY},
};
use git2::{Error, Repository};
tera.register_filter("hex", HexFilter {});
tera.register_filter("oct", OctFilter {});
tera.register_filter("mask", MaskFilter {});
+ tera.register_filter("url_string", UrlStringFilter {});
tera.register_function("ts_to_date", TsDateFn {});
tera.register_function("ts_to_git_timestamp", TsTimestampFn {});
Ok(tera)
let mut dst = PathBuf::new();
let safe_full_hash = sanitize_path_component(&self.full_hash);
let safe_ref = self.ref_name.as_deref()
- .map(|v| sanitize_path_component(&v))
+ .map(|v| sanitize_path_component(&urlify_path(v)))
.unwrap_or("%REF%".to_string());
for cmp in src.components() {
let cmp = cmp.as_os_str().to_string_lossy()
// TODO:
//
// * favicon
-// * permalinks (at least for README?)
// * better error propagation
// * automated tests
// * documentation + examples
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::git::GitFile;
+use crate::util::{sanitize_path_component, urlify_path};
use chrono::{naive::NaiveDateTime, offset::FixedOffset, DateTime};
use serde::Serialize;
use std::collections::HashMap;
}
}
+pub struct UrlStringFilter;
+impl Filter for UrlStringFilter {
+ fn filter(&self, value: &Value, _args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
+ let v: String = try_get_value!("url_string", "value", String, value);
+ let sanitized = sanitize_path_component(&urlify_path(&v));
+ Ok(to_value(sanitize_path_component(&sanitized)).unwrap())
+ }
+}
+
pub struct TsDateFn;
impl Function for TsDateFn {
fn call(&self, args: &HashMap<String, Value>) -> Result<Value, tera::Error> {