aboutsummaryrefslogtreecommitdiffgithub
diff options
context:
space:
mode:
authorAustin Adams <git@austinjadams.com>2017-03-04 20:09:08 -0500
committerAustin Adams <git@austinjadams.com>2017-03-04 20:09:08 -0500
commitd2cb63c8c70cdfbe88ac4ba05c64bd1d4fa3b354 (patch)
tree24508db462f169aa926662a4626e371e7a9075ab
parentc01b6580a07b138f0f22b130a101b1582cecd72f (diff)
downloadwebzone-d2cb63c8c70cdfbe88ac4ba05c64bd1d4fa3b354.tar.gz
webzone-d2cb63c8c70cdfbe88ac4ba05c64bd1d4fa3b354.tar.xz
blog: Add post on recent git commits on homepage
Write a new blog post on how I included recent git commits in my statically-generated homepage.
-rw-r--r--content/blog/including-recent-commits-hugo.md128
-rw-r--r--static/img/gustave/commit-diagram.svg443
2 files changed, 571 insertions, 0 deletions
diff --git a/content/blog/including-recent-commits-hugo.md b/content/blog/including-recent-commits-hugo.md
new file mode 100644
index 0000000..23d9118
--- /dev/null
+++ b/content/blog/including-recent-commits-hugo.md
@@ -0,0 +1,128 @@
++++
+date = "2017-02-28T17:24:44-05:00"
+title = "Including a List of Recent git Commits in a Hugo page"
+draft = false
+description = "How I made my Hugo homepage include a list of recent commits"
++++
+
+When redesigning my website recently, I realized I needed some meat on
+my homepage. Ideally, instead of a blank space, I could show off stuff
+I'm working on without having to maintain my show-off list by hand. Two
+ideas came to mind: recent blog posts and recent git commits.
+
+Since I already stored my blog posts in the static site generator
+[Hugo][8], adding them to the homepage took me only a few minutes. But
+since Hugo provides no way of interfacing with git repositories, adding
+my git commits was much more challenging and, of course, much more fun.
+
+On a high level, my solution works like the following:
+
+ 1. A script runs, which scans my git repositories for recent commits
+ and generates Hugo content files for them.
+ 2. *Then* I run Hugo, and my Hugo templates can access the commits
+ 3. Whenever I push to one of my git repositories, rebuild the site.
+ (This currently takes less than a tenth of a second.)
+
+Part 1: The Script
+==================
+
+Named after the station inspector played by Sacha Baron Cohen in
+[*Hugo*][3] (for a weak attempt at humor), [gustave][2] is about 400
+lines of C (for speed) which uses [libgit2][1] (for convenience).
+
+Because I [use cgit for hosting my git repositories][4], gustave reads
+the list of repository paths to check from `/etc/cgitrc` ([manpage][5]),
+ignoring those with `repo.hide=1`. With the git repositories to check
+down, there's one thing left for the script to do:
+
+Finding Recent Commits in a git Repository
+------------------------------------------
+
+Unlike in version control systems like Subversion which have a linear
+revision history, git commits form a a directed acyclic graph by
+pointing to their parents. Consequently, to find the most recent N git
+commits, you can't simply walk backwards from the most recent one.
+Instead, you have to traverse the graph starting from the commit to
+which the target branch points.
+
+The following diagram shows a real example [from the repository where
+this blog is stored][6]. Notice the commit [cf7a0d083c][7] has two
+parents!
+
+![Diagram of my webzone repository][i1]
+
+My first idea for traversal was brute force; that is, looking at every
+dang commit in the repository and then sorting them by date. However,
+for gigantic repositories like the Linux kernel, this approach
+introduces searches much more of the commit graph than necessary.
+
+To inspect as little of the graph as possible while still finding recent
+commits, I settled on a [breadth-first search][9] which stops after
+examining 32 commits. This allows for examining as much of the graph as
+possible nearby the starting commit without going too deep into old
+stuff.
+
+I then combine each set of 32 commits from each repository into one big
+array, and sort the whole array by date, writing the latest 8 as Hugo
+content.
+
+Part 2: Regenerating the Site after a Push
+==========================================
+
+I already wrote [a post about regenerating Hugo sites with git
+hooks][10], so I only needed to make some small changes to my
+post-receive hook to make it work when called from any repository.
+
+ #!/bin/bash
+ set -e
+
+ # Change to the repository with the site to rebuild
+ siterepo=/path/to/webzone/repo
+ pushd "$siterepo"
+
+ worktree="$(mktemp -d)"
+ desttmp=/var/www/webzone
+ destlink="$desttmp/HEAD"
+ destlinktmp="$destlink.tmp"
+ destdir="$desttmp/$(git rev-parse HEAD)"
+ # Important change from the old script: Don't assume this generation
+ # has a different commit in the webzone repo than the last — this
+ # push is not necessarily an update to the webzone repo!
+ destdir="$(mktemp -p "$desttmp" -d "$(git rev-parse HEAD).XXXXXXXXXX")"
+ olddestdir="$(readlink -e "$destlink" || true)"
+
+ # Create a temporary working tree and delete it
+ # after building the site
+ GIT_WORK_TREE="$worktree" git checkout -f
+ pushd "$worktree"
+ # Run gustave to generate commit content files
+ mkdir content/commit
+ gustave
+ hugo --destination="$destdir"
+ popd
+ rm -rvf "$worktree"
+
+ # Point to the new version of the site (atomically!)
+ # and delete the old version of the site
+ ln -svnrf "$destdir" "$destlinktmp"
+ # Use rename(2), which is atomic
+ mv -Tv "$destlinktmp" "$destlink"
+ rm -rvf "$olddestdir"
+
+Then I simply added this as a post-receive hook to all the git
+repositories that I wanted to update my homepage upon receving a push.
+(In truth, to make the situation less messy, I made it a binary in
+`/usr/local/bin` called by the post-receive hook in every repository.)
+
+[1]: https://libgit2.github.com/
+[2]: https://code.austinjadams.com/gustave/
+[3]: https://en.wikipedia.org/wiki/Hugo_(film)
+[4]: https://code.austinjadams.com/
+[5]: https://git.zx2c4.com/cgit/tree/cgitrc.5.txt
+[6]: https://code.austinjadams.com/webzone/commit/?id=1b60a667d22d7f5665c7e25fd027249a22dbbc7f
+[7]: https://code.austinjadams.com/webzone/commit/?id=cf7a0d083caabd4e237ad8fd688381df8185a114
+[8]: https://gohugo.io/
+[9]: https://en.wikipedia.org/wiki/Breadth-first_search
+[10]: {{< ref "blog/using-git-to-deploy-a-hugo-blog-atomically.md" >}}
+
+[i1]: /img/gustave/commit-diagram.svg
diff --git a/static/img/gustave/commit-diagram.svg b/static/img/gustave/commit-diagram.svg
new file mode 100644
index 0000000..fa4e99b
--- /dev/null
+++ b/static/img/gustave/commit-diagram.svg
@@ -0,0 +1,443 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="11.429999cm"
+ height="7.0555553cm"
+ viewBox="0 0 405 250"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="commit-diagram.svg">
+ <defs
+ id="defs4">
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker8631"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path8633"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker6893"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lend"
+ inkscape:collect="always">
+ <path
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path6895"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5803"
+ style="overflow:visible"
+ inkscape:isstock="true"
+ inkscape:collect="always">
+ <path
+ id="path5805"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker5515"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lend"
+ inkscape:collect="always">
+ <path
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path5517"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend"
+ style="overflow:visible"
+ inkscape:isstock="true"
+ inkscape:collect="always">
+ <path
+ id="path4173"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker5515-1"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lend">
+ <path
+ inkscape:connector-curvature="0"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path5517-2" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker5515-1-6"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lend">
+ <path
+ inkscape:connector-curvature="0"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path5517-2-0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker5803-8"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ inkscape:connector-curvature="0"
+ id="path5805-7"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ <marker
+ inkscape:isstock="true"
+ style="overflow:visible"
+ id="marker5515-7"
+ refX="0"
+ refY="0"
+ orient="auto"
+ inkscape:stockid="Arrow2Lend"
+ inkscape:collect="always">
+ <path
+ inkscape:connector-curvature="0"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ id="path5517-3" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow2Lend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow2Lend-1"
+ style="overflow:visible"
+ inkscape:isstock="true"
+ inkscape:collect="always">
+ <path
+ inkscape:connector-curvature="0"
+ id="path4173-2"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.625;stroke-linejoin:round;stroke-opacity:1"
+ d="M 8.7185878,4.0337352 -2.2072895,0.01601326 8.7185884,-4.0017078 c -1.7454984,2.3720609 -1.7354408,5.6174519 -6e-7,8.035443 z"
+ transform="matrix(-1.1,0,0,-1.1,-1.1,0)" />
+ </marker>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="2.8"
+ inkscape:cx="238.7432"
+ inkscape:cy="82.558087"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ fit-margin-top="0"
+ fit-margin-left="0"
+ fit-margin-right="0"
+ fit-margin-bottom="0"
+ inkscape:window-width="3200"
+ inkscape:window-height="1671"
+ inkscape:window-x="0"
+ inkscape:window-y="55"
+ inkscape:window-maximized="1"
+ units="m">
+ <sodipodi:guide
+ position="-29.823352,770.6925"
+ orientation="1,0"
+ id="guide5505" />
+ <sodipodi:guide
+ position="301.00161,84.798956"
+ orientation="1,0"
+ id="guide5793" />
+ <sodipodi:guide
+ position="214.63357,58.534986"
+ orientation="1,0"
+ id="guide5795" />
+ <sodipodi:guide
+ position="-169.72947,140.86242"
+ orientation="0,1"
+ id="guide6951" />
+ <sodipodi:guide
+ position="538.63999,50.832577"
+ orientation="0,1"
+ id="guide7397" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(-77.300522,-517.44533)">
+ <rect
+ style="opacity:1;fill:#ffffff;stroke:#000000;stroke-width:0.59428364"
+ id="rect4136"
+ width="129.25668"
+ height="29.71418"
+ x="167.59465"
+ y="518.50665" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="189.77472"
+ y="538.34863"
+ id="text4138"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="189.77472"
+ y="538.34863"
+ id="tspan4142"
+ style="font-size:13.37138081px">1b60a667d2</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59428364;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4.75426888, 2.37713445;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow2Lend)"
+ d="m 338.93726,532.63787 -40.22124,0"
+ id="path4146"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="343.1395"
+ y="536.53998"
+ id="text4702"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4704"
+ x="343.1395"
+ y="536.53998"
+ style="font-size:13.37138081px">master</tspan></text>
+ <rect
+ style="opacity:1;fill:#ffffff;stroke:#000000;stroke-width:0.59428364"
+ id="rect4136-3"
+ width="129.25668"
+ height="29.71418"
+ x="167.58878"
+ y="572.28119" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="190.06592"
+ y="592.12311"
+ id="text4138-6"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="190.06592"
+ y="592.12311"
+ id="tspan4142-7"
+ style="font-size:13.37138081px">d25ae221b8</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59428364px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5515)"
+ d="m 232.03727,548.67183 0,23.13461"
+ id="path5507"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="opacity:1;fill:#ffffff;stroke:#000000;stroke-width:0.59428364"
+ id="rect4136-3-7"
+ width="129.25668"
+ height="29.71418"
+ x="167.62119"
+ y="625.48688" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="192.95804"
+ y="645.32886"
+ id="text4138-6-0"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="192.95804"
+ y="645.32886"
+ style="font-size:13.37138081px"
+ id="tspan5633">cf7a0d083c</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59428364px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5515-1)"
+ d="m 232.06967,601.87755 0,23.13461"
+ id="path5507-3"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="opacity:1;fill:#ffffff;stroke:#000000;stroke-width:0.59428364"
+ id="rect4136-3-7-6"
+ width="129.25668"
+ height="29.71418"
+ x="258.44318"
+ y="683.9306" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="281.27942"
+ y="703.77252"
+ id="text4138-6-0-2"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="281.27942"
+ y="703.77252"
+ style="font-size:13.37138081px"
+ id="tspan5633-6">27ad9c2e8b</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59428364px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5803)"
+ d="m 232.54396,655.21859 89.44724,28.23732"
+ id="path5507-3-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59428364px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker6893)"
+ d="m 232.63457,655.35977 -94.24978,28.23732"
+ id="path5507-3-1-2"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <rect
+ style="opacity:1;fill:#ffffff;stroke:#000000;stroke-width:0.59428364"
+ id="rect4136-3-7-6-3"
+ width="129.25668"
+ height="29.71418"
+ x="258.74335"
+ y="737.40894" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="281.09644"
+ y="757.25092"
+ id="text4138-6-0-2-7"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="281.09644"
+ y="757.25092"
+ style="font-size:13.37138081px"
+ id="tspan5633-6-5">8b839e9e30</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59428364px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker5515-7)"
+ d="m 321.86042,713.48787 0,23.1346"
+ id="path5507-6"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59428364;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4.75426888, 2.37713445;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow2Lend-1)"
+ d="m 429.04718,697.43429 -40.22125,0"
+ id="path4146-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="433.24939"
+ y="701.33636"
+ id="text4702-3"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="433.24939"
+ y="701.33636"
+ style="font-size:13.37138081px"
+ id="tspan8424">fullsite</tspan></text>
+ <rect
+ style="opacity:1;fill:#ffffff;stroke:#000000;stroke-width:0.59428364"
+ id="rect4136-3-7-6-7"
+ width="129.25668"
+ height="29.71418"
+ x="77.597664"
+ y="683.95563" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="99.875679"
+ y="703.79761"
+ id="text4138-6-0-2-8"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="99.875679"
+ y="703.79761"
+ style="font-size:13.37138081px"
+ id="tspan5633-6-4">67424826d2</tspan></text>
+ <rect
+ style="opacity:1;fill:#ffffff;stroke:#000000;stroke-width:0.59428364"
+ id="rect4136-3-7-6-3-5"
+ width="129.25668"
+ height="29.71418"
+ x="77.897835"
+ y="737.43402" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:23.77134514px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="100.61329"
+ y="757.27594"
+ id="text4138-6-0-2-7-0"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="100.61329"
+ y="757.27594"
+ style="font-size:13.37138081px"
+ id="tspan5633-6-5-3">d48d4867c6</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.59428364px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;marker-end:url(#marker8631)"
+ d="m 141.01489,713.51293 0,23.13462"
+ id="path5507-6-6"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>