# Tutorial 3: Commit Sign-Off Commit sign-off is a common pattern in vanilla git projects, where a commit must be approved by one or more people (besides the commit author themselves) in order to be allowed into the primary branch. dehub is able to accomplish this same pattern using only the access controls which have already been covered in this tutorial series and a command which has not: `dehub combine`. This tutorial will guide you through using `dehub combine` to facilitate commit sign-off. This tutorial assumes you have already completed [Tutorial 2](tut2.html), and builds on top of the project which was started there. ## Step 0: Loosen the Previous Restrictions In the [previous tutorial](tut2.html) you took an existing project, added a new user `tot` to it, and then restricted `tot` to only be allowed to make commits in a certain subset of branches which excluded the `main` branch. As seen in that tutorial, `tot` is not able to create commits for the `main` branch _at all_. In this tutorial we're going to open `main` back up to `tot`, but only with a very important caveat: `tot`'s commits must be approved by someone else. In the `hello-world` project which was used for previous tutorials, with the `main` branch checked out, go ahead and modify `.dehub/config.yml` to have the following contents: ``` # contents of .dehub/config.yml --- accounts: - id: tut signifiers: - type: pgp_public_key_file path: ".dehub/tut.asc" - id: tot signifiers: - type: pgp_public_key_file path: ".dehub/tot.asc" access_controls: - action: allow filters: - type: signature account_ids: - tot - type: branch pattern: tot/* - action: deny filters: - type: branch pattern: main - type: not filter: type: signature any_account: true count: 2 ``` and commit the changes: ``` git add .dehub/config.yml dehub commit --as tut change --descr 'require commit sign-offs in main' ``` The primary change was to replace the old access control denying `tot` the ability to commit to anything (outside of `tot/*` branches) with this one: ``` - action: deny filters: - type: branch pattern: main - type: not filter: type: signature any_account: true count: 2 ``` There are two new things here. The first is the new fields on the `signature` filter: `any_account` replaces the `account_ids` field, and refers to any account which is defined in the `accounts` section; `count` declares how many accounts must have a signature on the commit for the filter to match (if not specified it defaults to 1). The second new thing is the `not` filter: `not` wraps any other filter, and reverses whether or not it matches. In this case, it's wrapping our `signature` filter, such that this access control will match only if the commit _does not_ have signature credentials from 2 different accounts. The total effect of this access control is to deny any commits to `main` which have not been signed-off by 2 different accounts. ## Step 1: Some Changes to Merge In the previous tutorial `tot` created a new script, `echo.sh`, in a new branch called `tot/echo-script`. Check that branch out, rebase it on `main` (this will help in later steps), and add another script to it: ``` git checkout tot/echo-script git rebase main echo 'echo "$@" | awk "{ print toupper(\$0) }"' > echo-upper.sh git add echo-upper.sh dehub commit --as tot change --descr 'echo-upper.sh' ``` Now the `tot/echo-script` branch contains two commits which aren't on `main`, both of them signed by `tot`. What will happen next is that the branch's commits will be combined into a single commit, be given accreditation by both `tut` and `tot`, and added to the `main` branch. ## Step 2: Accreditation First, `tot` will accredit both commits, and unify the two descriptions in the process. To do this, you will create your first `credential` commit: ``` dehub commit --as tot credential --start HEAD^^ --descr 'add echo.sh and echo-upper.sh' ``` A `credential` commit, at its core, contains nothing except credentials for any arbitrary fingerprint. To view the credential commit you just made do: `git show`. You should see a commit message like: ``` Credential of AO3dn4Se61hq6OWy4Lm6m3MxdT2ru6TrIobuHaWJJidt --- type: credential commits: - f085f13fa839ece122476601d970460ac249dc69 # these will be different - 40a81ffb4f52dc4149570672f7f7fc053f12226a change_description: add echo.sh and echo-upper.sh fingerprint: AO3dn4Se61hq6OWy4Lm6m3MxdT2ru6TrIobuHaWJJidt credentials: - type: pgp_signature pub_key_id: XXX body: BIG LONG STRING account: tot ``` You'll notice that the credential commit's fingerprint is different than either of the two commits it accredits. This is the fingerprint is based on the _combination_ of the two commits; it is based on the total of the file changes and the description provided by the user. The two commits are enumerated in the `commits` field of the payload, and the description provided by the user is stored in the `change_description` field. The combined commits have now been accredited by `tot`, but not `tut`, and so they still lack a necessary credential. Have `tut` make a credential now: ``` dehub commit --as tut credential --rev HEAD ``` This form of the `credential` sub-command only accredits a single commit. When a single commit is accredited and it itself is a credential commit then the new commit which is created is merely a copy of the specified credential commit with the caller's own credential appended to the `credentials` list. You can see this with `git show`, which should look like: ``` Credential of AO3dn4Se61hq6OWy4Lm6m3MxdT2ru6TrIobuHaWJJidt --- type: credential commits: - f085f13fa839ece122476601d970460ac249dc69 # these will be different - 40a81ffb4f52dc4149570672f7f7fc053f12226a change_description: add echo.sh and echo-upper.sh fingerprint: AO3dn4Se61hq6OWy4Lm6m3MxdT2ru6TrIobuHaWJJidt credentials: - type: pgp_signature pub_key_id: XXX body: BIG LONG STRING account: tot - type: pgp_signature pub_key_id: XXX body: BIG LONG STRING account: tut ``` There are now enough credentials to combine the commits in the `tot/echo-script` branch into a single commit on the `main` branch. ## Step 3: Combination At this point the `tot/echo-script` branch has the following elements in place: * Two change commits, which we want to combine and bring over to `main`. * A credential commit made by `tot` for the combined changes. * A credential commit made by `tut` for the combined changes, which includes `tot`'s credentials. Combining the commits and placing them on `main` is done with a single command: ``` dehub combine --start HEAD^^^^ --end HEAD --onto main ``` This `combine` command combines all changes made within the given commit range, the last change description found in that range (in this case it will be from `tut`'s credential commit), and all credentials for that set of changes. The command combines them into a single commit which it places on the `main` branch. You can see the commit you've just created by doing: ``` git checkout main git show ``` The commit should contain both of the new files, and the message should look something like: ``` add echo.sh and echo-upper.sh --- type: change description: add echo.sh and echo-upper.sh fingerprint: ALOcEuKJkgIdz27z0fjF1NEbK6Y9cEh2RH4/sL3uf3oa credentials: - type: pgp_signature pub_key_id: XXX body: BIG LONG BODY account: tot - type: pgp_signature pub_key_id: XXX body: BIG LONG BODY account: tut ``` The commit is accredited by two different accounts, and so is allowed to be on the `main` branch. This can be verified by doing `dehub verify`. You now are able to require commit sign-off and create signed-off commits! The access control settings surrounding commit sign-offs are entirely up to you and your project's needs. You can require sign-off from specific accounts, any accounts, only on specific files, only in certain branches, etc... all using the same basic access control building blocks.