Skip to content

Gh Elao🔗

gh-elao is a tool for Github for the internal Elao Git flow.

It:

  • merges Github PRs from command line in an interactive way
  • keeps up-to-date your local branches before merge
  • warns you if you're trying to merge a PR for which CI checks are failing or not run yet
  • automatically squashes PRs with multiple commits
  • formats merge commits in a meaningful way
  • helps generate changelogs
  • merges a PR in another branch, without closing the PR
  • thanks your teammates for their awesome work 💖

Installation🔗

The tool is provided as a standalone static PHP binary (using static-php-cli).
Install it using:

1
curl "https://gist.githubusercontent.com/ogizanagi/34103b428a1cc6a9f890d8f01116dfd3/raw/bash-installer.sh" -H 'cache-control:no-cache' -H "pragma:no-cache" | bash

which should setup the gh-elao binary on your system.

Tip

The binary is also available as a git command.
Either use gh-elao <command> or git elao <command>.
This documentation will always uses gh-elao for consistency.

Try executing gh-elao from your terminal. You should see configuration documentation as well as the available tool commands. The simplest way to configure for all project at once is to use:

1
2
3
git config --global gh.token <GITHUB_TOKEN>
# or
git config --global github.token <GITHUB_TOKEN>

Note

You may already have a github.token. Otherwise, generate one at https://github.com/settings/tokens/new with repo permissions at least.

Gitignore files generated by the tool🔗

The tool relies on creating some temporary files on your filesystem to store notes & detect transitory states (e.g: when resolving conflicts during a merge) so you can continue the process where you left it. These files can be annoying and should never be merged, so let's ignore those globally:

1
2
3
/.*-suspended
/.notes
/.commit

Update🔗

Re-execute the installation command to update the tool.

Formatted merge commits🔗

Instead of the Github default merge commit format Merge pull request #PR_NUMBER from BRANCH,
the tool merges PRs using a meaningful message: [hash] category #PR_NUMBER PR_TITLE (author),
were:

  • hash is the merge commit hash
  • category is one of feature, bug, minor or security
  • #PR_NUMBER allows to jump to the original PR
  • PR_TITLE is the PR title the developer should carefully choose
  • author is the one(s) with commit(s) in the PR

E.g: bug #1008 Fix homepage header (ameliedefrance)

These merge commits helps generating a changelog between versions.

Generate changelogs🔗

The following command helps you generate a changelog between versions:

1
gh-elao changelog <previous-ref> [upper-ref]

E.g:

1
2
gh-elao changelog v1.8.0 v1.9.0 # changelog between v1.8.0 and v1.9.0
gh-elao changelog v1.8.0 # changelog between v1.8.0 and HEAD (i.e: all changes since v1.8.0)

This command is actually a proxy to a git alias you can add to your own git config (the command shows it to you)

Tip

OS X users: use pbcopy to copy/paste the changelog to your clipboard:

1
gh-elao changelog v1.8.0|pbcopy

Merge a PR🔗

Merge a PR using:

1
gh-elao merge <PR_NUMBER>

An interactive prompt will start, checking CI tasks, asking whether to squash commits, the category (feature, bug, ...), ...

The PR code is merged into the target branch with a formatted merge commit. You don't have to checkout nor fetch the target branch. The tool will do this automatically for you. When the merge is done, your local current branch is the one targeted by the PR, up-to-date, allowing you to perform a release directly if you need to.

Warning

Some considerations
while it doesn't require you to keep your local branches up-to-date, any commit you have locally on the target branch that is not present on remote will be pushed. This allows you to tweek a PR right after merge and before pushing it to the remote. But this can also lead to unexpected commits to the targeted branch. First rule: do never commit anything on targeted branches directly. I.E, typically: don't commit changes on your local master or staging branches unless you plan to push it.

Understanding the merge process🔗

The tool is nothing but a wrapper around git + Github API. Whenever you're trying to merge a PR, the tool:

  • fetches the PR locally using git fetch origin pull/ID/head:pull/ID (see https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/checking-out-pull-requests-locally)
  • checkouts the targeted branch (e.g: master)
  • pulls master changes from remote
  • rebases the PR local branch to the target: at this point, you might have some conflict to fix. Re-run the command to resume the merge once all conflict are fixed.
  • merges the PR into the target branch locally using git merge --no-ff once you answered everything
  • ask you if you want to push the changes right now or not. This step allows you to add some more commits before answering yes (or answering no and pushing manually)

Merge a PR to another branch🔗

The tool is especially useful in scenarios in which you want to merge a PR to another target than the original one. E.g: a PR is made on master but required to be merged to staging first for client review. Merge this PR to another branch using:

1
gh-elao merge <PR_NUMBER> --switch=NEW_TARGET_BRANCH

E.g:

1
gh-elao merge 81 --switch=staging

The PR code is merged into the new targeted branch, while the PR is still open, allowing you to merge to original branch later. E.g: Whenever the client approved the PR on staging, merge to master.

Tip

Since it's not much useful to thanks your teammates when merging on staging for client review, and since it might get merged multiple times before being validated, you can use the --no-thanks option. You may also want to register aliases of your own. E.g:

1
alias gh-staging='gh-elao merge --no-thanks --switch=staging' # Usage: gh-staging <PR_NUMBER>

Merge new commits from a PR🔗

In the previously mentioned --switch=staging scenario, the code might need some changes after client review. The simplest way is to append a new commit and run the tool again:

1
gh-elao merge 81 --switch=staging

Git will automatically detect already merged references and ignore those, merging only the newly appended commit (do not use squash). In case the PR was rebased in between, the commit hash differs and conflicts might have been solved in-between. Git is unable to detect the patch was already applied for those commits and will try to merge them anyway, leading to such errors:

1
2
3
4
5
6
7
8
[...]
Applying: Already merged commit
Using index info to reconstruct a base tree...
M       README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Patch failed at 0001 Already merged commit
See full error
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Unable to rebase the patch for <comment>pull/1</comment>
The command "'git' 'rebase' '--onto' 'staging' 'master' 'pull/1'" failed.

Exit Code: 1(General error)

Working directory: [...]

Output:
================
First, rewinding head to replay your work on top of it...
Applying: Already merged commit
Using index info to reconstruct a base tree...
M       README.md
Falling back to patching base and 3-way merge...
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Patch failed at 0001 Already merged commit
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
You can instead skip this commit: run "git rebase --skip".
To abort and get back to the state before "git rebase", run "git rebase --abort".


Error Output:
================
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch' to see the failed patch

Unable to rebase
Fix the conflicts
git add FIXED_FILES
git rebase --continue

Then rerun this command.

This error indicates you multiple ways to fix the issue:

  • either by fixing conflict
  • either by skipping the commit

As we for sure know this commit is already merged, the second solution is the right one. Use git rebase --skip for each of the already merged commits. Then, re-execute the same tool command line to resume the merge.

For ref:

  • No changes -- Patch already applied
    means the commit changes are properly detected as already merged and causes no issue, skipping the commit automatically
  • Applying: commit 1 [...] CONFLICT (content): [...] Patch failed at 0001 commit 1
    means the commit is likely to be already merged and should be skipped using git rebase --skip
  • Applying: commit 3 with no more hint means the commit was successfully merged

Handling conflicts🔗

Conflicts can be handled directly while merging. When you get such an error:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Error Output:
================
error: Failed to merge in the changes.
hint: Use 'git am --show-current-patch' to see the failed patch

Unable to rebase
Fix the conflicts
git add FIXED_FILES
git rebase --continue

Then rerun this command.

the merge process is interrupted in order to let you fix the conflicts. Just follow the instructions and re-run the exact same command to resume the process.

Troubleshoots🔗

Nothing to commit🔗

This error typically means everything was already merged into the targeted branch:

Your branch is up to date with 'origin/XXX'
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<error>Unable to commit branch pull/1</error>.

In ProcessHelper.php line 97:

  The command "'git' 'commit' '--file=[...]/.commit'" failed.  

  Exit Code: 1(General error)                                                                   

  Working directory: [...]                                     

  Output:                                                                                       
  ================                                                                              
  On branch staging                                                                             
  Your branch is up to date with 'origin/staging'.                                              

  Untracked files:                                                                              
        .commit                                                                                      
        .notes                                                                                       

  nothing added to commit but untracked files present                                           


  Error Output:                                                                                 
  ================

merge [-s|--switch SWITCH] [--no-commit] [--force-squash] [--] <number>                                                                                       

Just remove the .commit and .notes files and go back to work!


Last update: December 20, 2024