Compare commits

...

8 Commits

Author SHA1 Message Date
Julien Castiaux
e03e38fb64 wip 2023-06-05 17:00:35 +02:00
Julien Castiaux
4bbcf65b50 wip 2023-06-01 17:48:29 +02:00
Julien Castiaux
270c926f2f wip 2023-05-31 18:54:35 +02:00
Julien Castiaux
dd3fcf677b wip 2023-05-31 14:53:52 +02:00
Julien Castiaux
6ed6881eaf wip 2023-05-30 18:37:43 +02:00
Julien Castiaux
95639a575c wip 2023-05-22 16:57:06 +02:00
Julien Castiaux
04ddd0bcbe wip 2023-05-05 16:21:33 +02:00
Julien Castiaux
6fe7cb3a67 wip 2023-05-03 16:01:01 +02:00
8 changed files with 684 additions and 3 deletions

View File

@@ -67,11 +67,11 @@ and then push them to GitHub. Skip this section and go to
$ cd /CommunityPath
$ git remote add dev git@github.com:odoo-dev/odoo.git
$ git remote set-url --push origin no_push
$ git remote set-url --push origin ''
$ cd /EnterprisePath
$ git remote add dev git@github.com:odoo-dev/enterprise.git
$ git remote set-url --push origin no_push
$ git remote set-url --push origin ''
#. That's it! You are ready to :ref:`make your first contribution
<contributing/development/first-contribution>`.

View File

@@ -12,6 +12,7 @@ Tutorials
tutorials/define_module_data
tutorials/restrict_data_access
tutorials/unit_tests
tutorials/git
tutorials/pdf_reports
tutorials/dashboards
@@ -34,7 +35,6 @@ Tutorials
.. card:: Restrict access to data
:target: tutorials/restrict_data_access
:tag: Beginner
Implement security measures to restrict access to sensitive data with the help of groups,
access rights, and record rules.
@@ -46,6 +46,13 @@ Tutorials
Write effective unit tests in Python to ensure the resilience of your code and safeguard it
against unexpected behaviors and regressions.
.. card:: Collaborate using git
:target: tutorials/git
:tag: Beginner
Hands on git and github, the two softwares that are used by both Odoo employees and external
people to work together on the source code and this documentation.
.. card:: Build PDF reports
:target: tutorials/pdf_reports

View File

@@ -0,0 +1,374 @@
=====================
Collaborate using git
=====================
Introduction
============
*What are git and github, what are there for*
Setup git and github
====================
Follow the same steps as in :ref:`contributing/development/setup`.
Explore the history
===================
We begin this tutorial by exploring the git history of Odoo. This section should help you get a first gasp of what git, commits and branches are for. Remember that whatever you do on your own computer stayes on your computer, git is not like dropbox in that regard. You are free to move around, open and read files and even modify them, it doesn't disturb anybody.
Status
------
Before doing anything complicated, we'll start with the command that show "what's going on": ``git status``. Open a terminal and move to the community path, then type ``git status``.
.. code-block:: console
:caption: status just after a clone
$ git status
On branch 16.0
Your branch is up to date with 'origin/16.0'.
nothing to commit, working tree clean
If you haven't done anything (beside changing the configuration), this is what the status should looks like.
The first line reads "On branch 16.0" (it is ok if it reports another version), it means that you are looking at the state of the Odoo source code as it is for the version 16. The second line reads "Your branch is up to date with 'origin/16.0'.", it means that your local copy of Odoo 16.0 is up to date with the remote copy of Odoo 16.0 on Github, in this context ``origin`` refers to https://github.com/odoo/odoo. The third line reads "nothing to commit, working tree clean", it means that you haven't modified any file.
.. warning::
Depending on your system configuration, the message may have been printed in French or in another language than English. We suggest you `configure <https://stackoverflow.com/a/10872202>`_ git to always print the messages in English, at least for this tutorial.
Log
---
You can open the file at ``odoo/release.py`` in a text editor (Windows users can use Notepad), it is a Python source code file with some informations about the software, among them there is the current version (again, it is fine if you have another version):
.. code-block:: python
:caption: version inside of odoo/release.py
version_info = (16, 0, 0, FINAL, 0, '')
In the introduction, we said git was also used to keep an history of all the changes done, you can consult this history using the ``git log`` command:
.. code-block:: console
:caption: all commits (revisions) in the history of the 16.0 version for the ``odoo/release.py`` file, one per line
$ git log 16.0 --oneline odoo/release.py
fa58938b3e24 [REL] 16.0 FINAL
1c0d46b68b1e [FIX] release: change version level to beta
2636bea44775 [REL] 16.0
00d36ec92971 [FIX] core: decrement master release version
e5361e93be3e [IMP] core: bump master release to 16.1 alpha
c336dddab8bc [IMP] release: correct typo in code comment
082aa4d35289 [IMP] core: bump master release to 15.4 alpha
8cc0a225a541 [IMP] core: bump master release to 15.3 alpha
c6600d1ea493 [IMP] core: bump master release version to 15.2
...
a84070f3ba49 [REL] 9.saas~13
9e64f9f95141 [REF] openerp: move `openerp` to `odoo`
.. tip::
In case there are more lines than your terminal can show at a same time, git shows the result inside of a *pager*. You can move inside the pager using the arrow keys and you can quit it by pressing ``q``.
Each line reports a different revision of the file, each revision is called a "commit" in git jargon. The first column is the commit hash, a unique identifier created by git to identify the commit. The second column is a tag that describe the type of change beeing done inside of the commit, REL for a release, FIX for a bug fix, IMP for an improvement, there are a few others not listed here. Finally, the rest of the line is the commit message title, a short description of what beeing done inside of the commit.
Using ``-p`` instead of ``--oneline``, we can show each commit with all its details: complete hash, date, author, full message and the lines that changed.
.. code-block:: console
:caption: meta informations of the commit fa58938b3e24…
$ git log 16.0 -p odoo/release.py
commit fa58938b3e2477f0db22cc31d4f5e6b5024f478b
Author: Christophe Monniez <moc@odoo.com>
Date: Tue Oct 11 14:01:40 2022 +0000
.. code-block:: text
:caption: free description (message) of the commit fa58938b3e24…
[REL] 16.0 FINAL
closes odoo/odoo#103147
Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>
.. code-block:: udiff
:caption: lines changed (diff) by the commit fa58938b3e24…
-version_info = (16, 0, 0, BETA, 0, '')
+version_info = (16, 0, 0, FINAL, 0, '')
We only show here the first (most recent) commit that changed of file ``odoo/release.py`` in the history of the 16.0 branch. In your own terminal there is a pager that shows you all the commits that modified this file, you can quit it by pressing ``q``.
Each commit is separated in three sections, some meta informations, the commit message and the commit *diff*:
1. The meta part lists the unique full 40-chararacter longs :abbr:`commit hash (fa58938b3e2477f0db22cc31d4f5e6b5024f478b)`, the :abbr:`author (Christophe Monniez)` of the commit and the :abbr:`date (11 Oct. 2022)`.
#. The commit message is a free text written by Christophe, it contains a :abbr:`title/subject ([REL] 16.0 FINAL)`, an empty body and some trailers (:abbr:`Closes (closes odoo/odoo#103147)`, :abbr:`Signed-off-by (Signed-off-by: Xavier Morel (xmo) <xmo@odoo.com>)`). In this example, the two trailers were automatically added, the first is a `reference <https://github.com/odoo/odoo/pull/103147>`_ to a pull-request on Github, the second means that Xavier reviewed the changes and validated them.
#. The *diff* (difference) shows what lines changed during this revision. It is quite complicated, what matters are the two lines ``-version_info = (16, 0, 0, BETA, 0, '')`` and ``+version_info = (16, 0, 0, FINAL, 0, '')``. The lines beginning with a single ``-`` means that they were removed, wheras the ones beginning with a single ``+`` means they were added. It reads that the line ``version_info = (16, 0, 0, BETA, 0, '')`` was replaced by ``version_info = (16, 0, 0, FINAL, 0, '')``, i.e. ``BETA`` was replaced by ``FINAL``.
Reading all those informations we learn that this commit was the one at released Odoo 16.0, from a beta version, to the final release.
We can also study the history of other versions, like to list all commits that modified this ``odoo/release.py`` but this time inside of the 15.0 version
.. code-block:: text
:caption: all commits (revisions) in the history of the 15.0 version for the ``odoo/release.py`` file, one per line
$ git log 15.0 --oneline odoo/release.py
b50796d51607 [REL] 15.0
15b4cc97f302 [REL] saas-14.5
c2179731372d [IMP] core: bump master release version to 14.5 alpha
6f9aa96c16a2 [IMP] core: bump master version to 14.4 alpha1
55986ffa21da [IMP] core: bump master version to 14.3 alpha1
8fd7232a0e7c [IMP] core: bump master version to 14.2 alpha1
...
a84070f3ba49 [REL] 9.saas~13
9e64f9f95141 [REF] openerp: move `openerp` to `odoo`
Again, using ``-p`` instead of ``--oneline`` to show all details:
.. code-block:: console
:caption: meta informations of the commit b50796d51607…
$ git log -p 15.0 odoo/release.py
commit b50796d5160745d9f85992467d632d9ce2476697
Author: Christophe Monniez <moc@odoo.com>
Date: Tue Oct 5 09:28:30 2021 +0200
.. code-block:: text
:caption: free description (message) of the commit b50796d51607…
[REL] 15.0
.. code-block:: udiff
:caption: lines changed (diff) by the commit b50796d51607…
diff --git a/odoo/release.py b/odoo/release.py
index 7c114b120700..546d1c49a12f 100644
--- a/odoo/release.py
+++ b/odoo/release.py
@@ -12,7 +12,7 @@ RELEASE_LEVELS_DISPLAY = {ALPHA: ALPHA,
# properly comparable using normal operarors, for example:
# (6,1,0,'beta',0) < (6,1,0,'candidate',1) < (6,1,0,'candidate',2)
# (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0)
-version_info = ('saas~14', 5, 0, FINAL, 0, '')
+version_info = (15, 0, 0, FINAL, 0, '')
version = '.'.join(str(s) for s in version_info[:2]) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '') + version_info[5]
series = serie = major_version = '.'.join(str(s) for s in version_info[:2])
Reading all those informations, we learn the version saas-14.5 became known as 15.0. Please note that usually new saas releases are forked from master. The full release (e.g. 14.0, 15.0) are an exception as they are generally based on the lastest saas-x.5 release (itself forked from master).
Show
----
A second way to study the history is to look at a precise commit. Say you are reading the *oneline* history and that one of the commit titles get your attention, that you want to print all the details of that specific commit. That's what ``show`` is for. Let's say you wonder how long your session is going to last, like how often <odoo.com> is going to ask you to type your password again because your session would had expired. Technically this is known as the "session lifetime" so you can search the history looking for those two words:
.. code-block:: console
:caption: all commits in 16.0 mentionning *session* and *lifetime*, one per line
$ git log --oneline --grep 'session' --grep 'lifetime' --all-match
05ff9a2db32c [FIX] http: make session lifetime consistent and configurable
f61aa39ff119 [REF] core: HTTPocalypse (9) ORM initialization
17e6a69b9189 [IMP] core: use Savepoint object in TestCursor
1fbafa4e69ee [MERGE][IMP] im_livechat: random assignation of conversation
The commit ``[FIX] http: make session lifetime consistent and configurable`` gets your attention, using ``show`` you can reveal all its secrets:
.. code-block:: console
$ git show 05ff9a2db32c
.. tabs::
.. group-tab:: Headers & Trailers
commit
05ff9a2db32c2fb1afa107ac005423218f452290
Author
Olivier Dony <odo@odoo.com>
Date
Tue May 30 10:59:42 2023 +0000
closes
odoo/odoo#122888
Signed-off-by
Julien Castiaux (juc) <juc@odoo.com>
.. group-tab:: Message
**[FIX] http: make session lifetime consistent and configurable**
Before 16.0 and `HTTPocalypse <HTTPocalypse_>`_ the session cookie duration was set to 3 months, but the server-side garbage collection of inactive session was reaping them after 7 days of inactivity. The cookie lifetime was essentially superseded by the server-side GC.
After `HTTPocalypse <HTTPocalypse_>`_ these limits were made consistent with each other, but the lifetime value was kept at 3 months, which is a bit too long as a default.
This commit changes the default ``SESSION_LIFETIME`` back to 7 days for both limits.
In addition, since the server-side GC is now implemented by a database-specific cron job, this commit introduces an optional system parameter ``sessions.max_inactivity_seconds`` that can be set to override the default server-side GC threshold, to make it shorter.
Note 1: the ICP does not modify the cookie lifetime which will remain set to the default 7 days. This means normal browser sessions won't stay alive for longer than 7 days of inactivity. So ``sessions.max_inactivity_seconds`` can't be effectively set to a longer expiration time. This seems like a reasonably safe default.
Note 2: the session GC happens during the execution of the autovacuum cron job ("Base: Auto-vacuum internal data") which is scheduled once per day by default. When setting a small ``sessions.max_inactivity_seconds`` value, it may be necessary to increase the frequency of that cron job accordingly.
.. group-tab:: Diff
.. code-block:: udiff
diff --git a/odoo/addons/base/models/ir_http.py b/odoo/addons/base/models/ir_http.py
index 951459bbc4be..a35e0b5afa7c 100644
--- a/odoo/addons/base/models/ir_http.py
+++ b/odoo/addons/base/models/ir_http.py
@@ -216,7 +216,9 @@ class IrHttp(models.AbstractModel):
@api.autovacuum
def _gc_sessions(self):
- http.root.session_store.vacuum()
+ ICP = self.env["ir.config_parameter"]
+ max_lifetime = int(ICP.get_param('sessions.max_inactivity_seconds', http.SESSION_LIFETIME))
+ http.root.session_store.vacuum(max_lifetime=max_lifetime)
@api.model
def get_translations_for_webclient(self, modules, lang):
diff --git a/odoo/http.py b/odoo/http.py
index aa7369e9a5f2..6b3f3fb1ce2d 100644
--- a/odoo/http.py
+++ b/odoo/http.py
@@ -261,9 +261,10 @@ if parse_version(werkzeug.__version__) >= parse_version('2.0.2'):
# let's add the websocket key only when appropriate.
ROUTING_KEYS.add('websocket')
-# The duration of a user session before it is considered expired,
-# three months.
-SESSION_LIFETIME = 60 * 60 * 24 * 90
+# The default duration of a user session cookie. Inactive sessions are reaped
+# server-side as well with a threshold that can be set via an optional
+# config parameter `sessions.max_inactivity_seconds` (default: SESSION_LIFETIME)
+SESSION_LIFETIME = 60 * 60 * 24 * 7
# The cache duration for static content from the filesystem, one week.
STATIC_CACHE = 60 * 60 * 24 * 7
@@ -858,8 +859,8 @@ class FilesystemSessionStore(sessions.FilesystemSessionStore):
session.should_rotate = False
self.save(session)
- def vacuum(self):
- threshold = time.time() - SESSION_LIFETIME
+ def vacuum(self, max_lifetime=SESSION_LIFETIME):
+ threshold = time.time() - max_lifetime
for fname in glob.iglob(os.path.join(root.session_store.path, '*', '*')):
path = os.path.join(root.session_store.path, fname)
with contextlib.suppress(OSError):
.. _HTTPocalypse: https://github.com/odoo/odoo/pull/78857
If you have had a prior experience with Git, you might be surprised to read such a lenghty commit message. It is the company policy to thoroughly describe the context of every change and to explain why they are necessary. This way we make sure that all contributors can understand the scope and rationnals of every feature and give feedback even if they were not part of the original design/specification of that feature. This is quite important for an open-source software company with hundreds external contributors
Contrary to ``git log`` that can list all commits that modified to a single file, ``git show`` lists all modifications over all the files that a single commit modified. In this example we see that two files were modified: ``odoo/addons/base/models/ir_http.py`` and ``odoo/http.py``. It is not relevant to this tutorial to understand what actually changed but you can give it a try.
Understand the branching model
==============================
The last section was about discovering git, discovering what the history and commits are for, it was also about understanding that multiple versions co-exist. This section is about going deeper in this notion of co-existing versions, called *branches* in git jargon. What's the difference between the various branches out there, namely the differences between stable, *master* and development branches.
Stable branches
---------------
Stable branches are branches that are deployed on Odoo Online and that are used by customers for their business. Those branches are labelled *stable* because developers don't try anything new on those branches, the list of features is frozen at the moment of the release and developers focus on fixing bugs.
The stable branches all reside in the official odoo/odoo github repository, they are the versions named "xx.0" and "saas-xx.y". The "xx.0" versions (e.g. 14.0, 15.0, 16.0, ...) are released every year during the Odoo Experience, most customers and partners use those versions. The "saas-xx.y" versions (saas-15.2, saas-16.1, ...) are released every two/three months and while available for all are generally only used by customers on Odoo Online.
.. example::
Since its release in late 2020, there have been more than 6.000 new commits on the 14.0 branch, of them 84% are bug fixes and 7% are feature improvement.
.. image:: git/plot-commits-14-0.png
:alt: Plot of the various [IMP] and [FIX] commits in the 14.0 branch indexed per month in the period Sept 2022 - March 2023
Master branch
-------------
The master branch is the branch where all new features are integrated once ready, tested and validated. This branch is halfway stable and halfway development, it is expected to always work bug-free but this assumption often fails because programming is hard and integrating many applications together is even more so. Because it is not that stable, the master branch is never deployed.
The master branch is split every two/three months and the new branch becomes the new stable release. The new branch isn't immediately deployed on Odoo Online, it first undergoes a stabilization process where the applications are extensively tested. Usually the new branch is made available to customers on Odoo Online after a few weeks.
The master branch is also hosted in the official odoo/odoo github repository.
.. note::
Because new features will be rarely added on the new stable branch, the process of splitting master in two is known at Odoo as the *freeze* even though bugfix commits will still be added.
.. example::
Since late 2020, there have been more than 18.000 new commits on the master branch, of them 58% are bug fixes, 32% are features and 5% are refactors.
.. image:: git/plot-commits-master.png
:alt: Plot of the various [IMP] and [FIX] commits in the master branch indexed per month in the period Sept 2022 - March 2023
Development branches
--------------------
The development branches are the branches were new features are being imagined, developed and tested. They are usually based on the latest revision of one of the official branches (stables or master) in which they will be ultimately integrated (if validated).
Contrary to the official branches, development branches are not hosted on the official odoo/odoo GitHub repository. All companies all individuals that contributes to Odoo work on their own *fork* (copy) of the official odoo/odoo repository, by example Odoo employees host their development branches on odoo-dev/odoo.
The integration is done via pull-requests (PR) on GitHub, a pull-request is a place where the developers publish their work for others to review and test. Reviews are mandatory at Odoo, all works must be validated by someone with "r+ rights" (a senior Odoo developer). Tests are mandatory too, every pull-request is ran against an extensible automated test suite that will block the pull-request in case of failure.
.. important::
The operation to integrate the work done in a development branch into an official one is known as *merging the branch into master/16.0/...*. This can be misleading as development branches are usually not integrated using a merge commit but are instead rebased and fast-forwaded, more on that later. Still "merge" is somewhat the de-facto way of designing this operation.
Branching model visualized
--------------------------
.. image:: git/odoo-workflow.drawio.png
:alt: Graph of the git workflow at Odoo
The graph shows a summary of the branching model inside the odoo/odoo (top half) and odoo-dev/odoo (bottom half) repositories. The graph features 8 branches: 2 stable branches (15.0 and 16.0), the master branch and 5 unammed development branches.
Each solid circle is a commit, blue for a new feature, greed for a release, yellow for a bugfix. Each callout poinThe callouts point to the last commit of their branch. the solid arrows show the order of the commits. The first commit of the master branch (top-left blue one) is present in all branches, the second commit of the master branch (second blue one) is present in 16.0, master and 4 of the unammed developemnt branches.
When a commit has two next commits it means that the branch was split. When the new branch is hosted in odoo/odoo, it means that there has been a new release, hence the green [REL] commits. When the new branch is hosted in odoo-dev/odoo (or in any other company or individual fork) it means that a new development has started.
New features are usually reserved for the master branch, the graph reflects that situation as once released stable branches only receive bugfixes.
Most features and bugfix can generally be grouped into a single comprehensive commit but sometime it is welcome to have multiple commits inside of a single branch, e.g. to fix a pre-existing bug that is revealed by a new feature. The development branches reflect that, most of them hold a single commit, except for the branch based on the latest master that hold multiple commits.
Not all development branches are ready for review, some features require several days/weeks to be completed but the branch is still saved on GitHub for the sake of saving it somewhere. The third unamed branch on the graph reflects that. That branch is also based on an outdated revision of the 16.0 official branch, the new commits from 16.0 should be integrated in the development branch via a *rebase*, more on that later.
Modify files
============
*Make them create a branch named "master-sandbox-<trigram>" based on the latest master, simply say that it is to isolate their changes from the ones of the others, don't explain branches yet*
*Deep dive the few status/diff/add/rm/restore/commit commands*
*Basically do stuff on the working directory, type status/diff to understand what changed, then add or rm or restore or commit, again status/diff/log to understand what happened*
Backup changes on Github
========================
*Make them push their branch so they can see it on github*
*Visit the various logs/diffs/blames/files this time using the github interface*
*Make them remove their branch so they can fetch it back from github*
Gather feedback
===============
*Basically pull requests, reviews, runbot*
*The ci/style will be red because we made them push commits with bad titles*
Cleanup the mess
================
*Explanation about keeping the history clean and writing good commit messages with hands on example in the history*
*Make them clean their branch basically using commit --amend, interactive rebase, and push --force-with-lease*
*Give a formal explaination of what branches are, with nice graphs*
*Make them fetch the latest master branch, show the difference git log master vs git log origin/master*
*Make them fast-forward their local master branch*
*Make them rebase their sandbox branch and push it back*
*Runbot should be green*

View File

@@ -0,0 +1,19 @@
The *drawio* graphes have been generated thanks to [diagrams.net].
To modify the graphes, open the web-app, import one of the `drawio` files and make your changes.
To save your modification, click *save* from the *file* menu and download the file (as XML).
To render a new PNG image, select everything with ctrl-a, click *export as PNG* from the *file*
menu and enter the following settings:
* Zoom 200%
* Border Width: 5
* [x] Selection Only
* Size: Diagram
* [ ] Transparent Background
* [ ] Shadow
* [ ] Grid
* [ ] Include a copy of my diagram
[diagrams.net]: https://app.diagrams.net/?src=about

View File

@@ -0,0 +1,281 @@
<mxfile host="app.diagrams.net" modified="2023-06-05T14:59:34.748Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/113.0" etag="CL9WIreMFmtxhxDaCxs9" version="21.3.0" type="device">
<diagram id="eDTlZYB63iw13qqHwxX-" name="Page-1">
<mxGraphModel dx="398" dy="213" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="_4l3IvHe-3uS5yegyjGe-203" value="odoo-dev/odoo" style="rounded=0;whiteSpace=wrap;html=1;fontSize=11;verticalAlign=bottom;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="70" y="550" width="370" height="60" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-202" value="odoo/odoo" style="rounded=0;whiteSpace=wrap;html=1;fontSize=11;verticalAlign=top;fontStyle=1" parent="1" vertex="1">
<mxGeometry x="70" y="360" width="370" height="190" as="geometry" />
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-22" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="1" source="_4l3IvHe-3uS5yegyjGe-63" target="UUBjyu-xDhq7s3pnQ-3j-14" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="203" y="517" as="sourcePoint" />
<mxPoint x="160" y="630" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-24" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="1" source="_4l3IvHe-3uS5yegyjGe-67" target="_4l3IvHe-3uS5yegyjGe-170" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="203" y="517" as="sourcePoint" />
<mxPoint x="130" y="600" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-25" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="1" source="_4l3IvHe-3uS5yegyjGe-69" target="_4l3IvHe-3uS5yegyjGe-168" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="293" y="487" as="sourcePoint" />
<mxPoint x="250" y="600" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-30" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" parent="1" source="_4l3IvHe-3uS5yegyjGe-40" target="_4l3IvHe-3uS5yegyjGe-162" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="360" y="430" as="sourcePoint" />
<mxPoint x="360" y="640" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-39" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="_4l3IvHe-3uS5yegyjGe-52" target="_4l3IvHe-3uS5yegyjGe-50" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="320" y="410" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-40" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="340" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-42" value="I" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;fontFamily=Verdana;" parent="1" vertex="1">
<mxGeometry x="300" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-44" value="I" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;fontFamily=Verdana;" parent="1" vertex="1">
<mxGeometry x="260" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-46" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="220" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-48" value="I" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;fontFamily=Verdana;" parent="1" vertex="1">
<mxGeometry x="180" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-50" value="I" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;fontFamily=Verdana;" parent="1" vertex="1">
<mxGeometry x="140" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-51" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;dashed=1;dashPattern=1 4;" parent="1" source="_4l3IvHe-3uS5yegyjGe-52" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="80" y="410" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-52" value="I" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;fontFamily=Verdana;" parent="1" vertex="1">
<mxGeometry x="100" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-59" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;endArrow=classic;endFill=1;" parent="1" source="_4l3IvHe-3uS5yegyjGe-57" target="_4l3IvHe-3uS5yegyjGe-61" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-57" value="R" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="130" y="430" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-61" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="160" y="460" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-63" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="190" y="490" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-67" value="R" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" vertex="1">
<mxGeometry x="250" y="430" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-69" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="280" y="460" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-87" value="15.0" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=11;size=5;position=0.25;position2=0;" parent="1" vertex="1">
<mxGeometry x="200" y="465" width="40" height="25" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-88" value="16.0" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=11;size=5;position=0.25;position2=0;" parent="1" vertex="1">
<mxGeometry x="290" y="435" width="40" height="25" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-90" value="master" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=11;size=5;position=0.25;position2=0;" parent="1" vertex="1">
<mxGeometry x="350" y="375" width="40" height="25" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-160" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="150" y="570" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-162" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="330" y="570" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-163" value="I" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;fontFamily=Verdana;" parent="1" vertex="1">
<mxGeometry x="370" y="570" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-168" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="270" y="570" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-170" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
<mxGeometry x="230" y="570" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-1" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="180" y="410" as="targetPoint" />
<mxPoint x="160" y="410" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-2" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="220" y="410" as="targetPoint" />
<mxPoint x="200" y="410" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="260" y="410" as="targetPoint" />
<mxPoint x="240" y="410" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-4" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="300" y="410" as="targetPoint" />
<mxPoint x="280" y="410" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="340" y="410" as="targetPoint" />
<mxPoint x="320" y="410" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-6" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;endArrow=classic;endFill=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="177" y="477" as="sourcePoint" />
<mxPoint x="193" y="493" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-7" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;endArrow=classic;endFill=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="117" y="417" as="sourcePoint" />
<mxPoint x="133" y="433" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-8" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;endArrow=classic;endFill=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="237" y="417" as="sourcePoint" />
<mxPoint x="253" y="433" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-9" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;endArrow=classic;endFill=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="267" y="447" as="sourcePoint" />
<mxPoint x="283" y="463" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-10" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;endArrow=block;endFill=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="207" y="507" as="sourcePoint" />
<mxPoint x="223" y="523" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-11" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;dashed=1;" parent="1" vertex="1">
<mxGeometry x="220" y="520" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-12" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0;entryDx=0;entryDy=0;endArrow=block;endFill=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="297" y="477" as="sourcePoint" />
<mxPoint x="313" y="493" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-13" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;dashed=1;" parent="1" vertex="1">
<mxGeometry x="310" y="490" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-14" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=block;endFill=0;" parent="1" source="_4l3IvHe-3uS5yegyjGe-40" target="d4MCrwgoJ-HxTQabZ7jv-15" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="367" y="407" as="sourcePoint" />
<mxPoint x="383" y="423" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="d4MCrwgoJ-HxTQabZ7jv-15" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;dashed=1;" parent="1" vertex="1">
<mxGeometry x="380" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-193" value="" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=11;size=10;position=0;position2=0.5;base=10;" parent="1" vertex="1">
<mxGeometry x="260" y="560" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-194" value="" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=11;size=10;position=0;position2=0.5;base=10;" parent="1" vertex="1">
<mxGeometry x="220" y="560" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-199" value="" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=11;size=10;position=0;position2=0.5;base=10;" parent="1" vertex="1">
<mxGeometry x="360" y="560" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-179" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;startArrow=none;startFill=0;endArrow=classic;endFill=1;dashed=1;" parent="1" source="_4l3IvHe-3uS5yegyjGe-160" target="d4MCrwgoJ-HxTQabZ7jv-11" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="230" y="540" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-188" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;startArrow=none;startFill=0;endArrow=classic;endFill=1;dashed=1;" parent="1" source="_4l3IvHe-3uS5yegyjGe-163" edge="1" target="d4MCrwgoJ-HxTQabZ7jv-15">
<mxGeometry relative="1" as="geometry">
<mxPoint x="390" y="420" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-182" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;startArrow=none;startFill=0;endArrow=classic;endFill=1;dashed=1;" parent="1" source="_4l3IvHe-3uS5yegyjGe-168" target="d4MCrwgoJ-HxTQabZ7jv-13" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="313" y="507" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-1" value="Branch" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=12;size=10;position=0;position2=0.5;base=10;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;spacing=4;" vertex="1" parent="1">
<mxGeometry x="450" y="490" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-2" value="[FIX]" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;spacing=4;" vertex="1" parent="1">
<mxGeometry x="450" y="400" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-3" value="[IMP]" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#dae8fc;strokeColor=#6c8ebf;fontFamily=Helvetica;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;spacing=4;" vertex="1" parent="1">
<mxGeometry x="450" y="430" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-4" value="[REL]" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#d5e8d4;strokeColor=#82b366;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;spacing=4;" vertex="1" parent="1">
<mxGeometry x="450" y="460" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-5" value="Next commit" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;fontSize=12;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;spacing=14;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="450" y="520" as="sourcePoint" />
<mxPoint x="470" y="540" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-8" value="Commit" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;spacing=4;" vertex="1" parent="1">
<mxGeometry x="450" y="370" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-9" value="Repository" style="whiteSpace=wrap;html=1;aspect=fixed;spacing=4;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;" vertex="1" parent="1">
<mxGeometry x="450" y="580" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-13" value="Pull-Request" style="group;spacing=4;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;" vertex="1" connectable="0" parent="1">
<mxGeometry x="450" y="550" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-12" value="" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f5f5f5;strokeColor=#666666;fontColor=#333333;dashed=1;" vertex="1" parent="UUBjyu-xDhq7s3pnQ-3j-13">
<mxGeometry x="10" y="10" width="10" height="10" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-6" value="" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontSize=12;startArrow=none;startFill=0;endArrow=classic;endFill=1;dashed=1;spacing=14;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;endSize=4;" edge="1" parent="UUBjyu-xDhq7s3pnQ-3j-13" target="UUBjyu-xDhq7s3pnQ-3j-12">
<mxGeometry relative="1" as="geometry">
<mxPoint x="10" y="10" as="targetPoint" />
<mxPoint as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-14" value="F" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="1">
<mxGeometry x="100" y="570" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-16" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="_4l3IvHe-3uS5yegyjGe-63" target="_4l3IvHe-3uS5yegyjGe-160">
<mxGeometry relative="1" as="geometry">
<mxPoint x="203" y="517" as="sourcePoint" />
<mxPoint x="127" y="583" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-17" style="edgeStyle=none;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fontSize=11;startArrow=none;startFill=0;endArrow=classic;endFill=1;dashed=1;" edge="1" parent="1" source="UUBjyu-xDhq7s3pnQ-3j-14" target="d4MCrwgoJ-HxTQabZ7jv-11">
<mxGeometry relative="1" as="geometry">
<mxPoint x="232" y="546" as="targetPoint" />
<mxPoint x="178" y="584" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="_4l3IvHe-3uS5yegyjGe-192" value="" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=11;size=10;position=0;position2=0.5;base=10;" parent="1" vertex="1">
<mxGeometry x="140" y="560" width="20" height="20" as="geometry" />
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-18" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=classic;endFill=1;" edge="1" parent="1" source="_4l3IvHe-3uS5yegyjGe-162" target="_4l3IvHe-3uS5yegyjGe-163">
<mxGeometry relative="1" as="geometry">
<mxPoint x="299" y="490" as="sourcePoint" />
<mxPoint x="291" y="580" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="UUBjyu-xDhq7s3pnQ-3j-19" value="" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;fontSize=11;size=10;position=0;position2=0.5;base=10;" vertex="1" parent="1">
<mxGeometry x="90" y="560" width="20" height="20" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB