As I mentioned in the previous post about a git hook to insert ticket numbers into commit message, it integrates well with another script of mine that allows me to check out a PR easily.
The script
The script fetches the HEAD of the PR branch from Github and also uses the Github API to get the URL of the fork and the name of the branch. In my case it’s located at ~/bin/git-pr.sh:
the original repository, where the PRs are submitted to, is under the default name origin (check with git remote -v);
the URL scheme used for origin is SSH-based: git@github.example.org:MyOrg/project.git;
I’m using it with a Github Enterprise instance, and it may also work with the default github, but I haven’t tried that;
the script needs an access token with the repo access, stored in the GITHUB_AUTH environment variable: user:token.
I don’t really like how I’m passing two values of the GitHub’s API response from jq to the shell, but it’s worked fine so far and I couldn’t find a better way. Shell scripting is extremely convenient when I’m starting to automate something with a small script, but can quickly become unwieldy because the shell language is pretty odd and limited.
The first alias, fpr, allows me to fetch the commits in the PR without checking out any branches and disturbing the working directory. That PR is now available at FETCH_HEAD (until another fetch):
12345678910
$ g fpr 1123remote: Enumerating objects: 93, done.
remote: Counting objects: 100% (93/93), done.
remote: Total 110(delta 93), reused 93(delta 93), pack-reused 17Receiving objects: 100% (110/110), 25.27 KiB |1.94 MiB/s, done.
Resolving deltas: 100% (98/98), completed with 78local objects.
From github.example.org:MyOrg/project
* branch refs/pull/1123/head -> FETCH_HEAD
$ g lb FETCH_HEAD
(Where lb could be an alias for log --graph --abbrev-commit --pretty=oneline --decorate=short).
The git cpr alias just runs the script at the top of the post with the given PR number.
A big feature for me is that it sets up the push settings correctly so that I can commit into this pr/123 branch and just git push it, and it will be pushed to the correct PR branch, something like git push git@github.example.org:alice/project.git HEAD:task/PROJ42/treat_warnings_as_errors. This is a big deal because I sometimes help other team members to resolve conflicts in their PRs with the updated base branch after something else is merged.
$ g cpr 12345remote: Enumerating objects: 73, done.
remote: Counting objects: 100% (73/73), done.
remote: Compressing objects: 100% (15/15), done.
remote: Total 73(delta 58), reused 70(delta 58), pack-reused 0Unpacking objects: 100% (73/73), 39.40 KiB |209.00 KiB/s, done.
From github.example.org:MyOrg/project
* branch refs/pull/12345/head -> FETCH_HEAD
Previous HEAD position was 98b4aa03 Merge pull request #12340Switched to a new branch 'pr/12345'# git fetch --all$ g fa
Fetching origin
remote: Enumerating objects: 165, done.
remote: Counting objects: 100% (102/102), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 50(delta 39), reused 35(delta 29), pack-reused 0Unpacking objects: 100% (50/50), 7.32 KiB |98.00 KiB/s, done.
From github.example.org:MyOrg/project
abcdef..012345 master -> origin/master
Fetching my
$ g merge origin/master
Auto-merging Podfile.lock
CONFLICT (content): Merge conflict in Podfile.lock
Auto-merging Podfile
Recorded preimage for'Podfile.lock'Automatic merge failed; fix conflicts and then commit the result.
$ g ch --theirs -- Podfile.lock && g add Podfile.lock && podin && gs && gd && ga
Analyzing dependencies
Downloading dependencies
…
Generating Pods project
Pod installation complete! There are 6 dependencies from the Podfile and 7 total pods installed.
## pr/12345M AppDelegate.swift
M Podfile
MM Podfile.lock
M README.md
diff --git a/Podfile.lock b/Podfile.lock
index 30bc34..13aa96 100644--- a/Podfile.lock
+++ b/Podfile.lock
@@ -442,6 +442,6 @@ SPEC CHECKSUMS:
LibraryA: 7e16842b8386fda06b104ac50d73a3f1fcbcab7b
LibraryB: eb6b7a8206790fe93992ea07aeea84774cd4a116
-PODFILE CHECKSUM: 8decc82ec18a57d7cb3a9c2ef09381e272a6b0e8
+PODFILE CHECKSUM: b43753e746f43e38722b97c4d9d24bebf2be6b7c
COCOAPODS: 1.10.1
(1/1) Stage this hunk [y,n,q,a,d,e,?]? y
$ gc -m 'Merge latest `master` to resolve Podfile.lock conflicts'Recorded resolution for'Podfile.lock'.
[pr/12345 98983cb7] TA880: Merge latest `master` to resolve Podfile.lock conflicts
$ g push
Enumerating objects: 25, done.
Counting objects: 100% (25/25), done.
Delta compression using up to 8 threads
Compressing objects: 100% (9/9), done.
Writing objects: 100% (9/9), 935 bytes |155.00 KiB/s, done.
Total 9(delta 8), reused 0(delta 0), pack-reused 0remote: Resolving deltas: 100% (8/8), completed with 8local objects.
To github.example.org:fork/project.git
b3c81ac..98983cb7 pr/12345 -> task/TA880_disable_verbose_logging
Note how I’m git committing with a message and the resulting message automatically has the ticket number TA880. Then I simply git push and the merge goes to the correct branch in the correct fork!
g ch --theirs -- Podfile.lock && g add Podfile.lock && podin && gs && gd && ga is a one-liner that I came up with after doing a number of conflict resolutions of the Podfile.lock and becoming tired of entering the commands one by one. It checks out the Podfile.lock from the merged, upstream branch (here, master), adds that version, pod installs whatever is necessary, prints git status, shows me git diff to verify the changes and finally asks to add the changes with git add -p.
Updated commit message hook
Here’s the updated prepare-commit-msg hook that has a special case for such PR branches. Specifically, if the PR 123’s branch is named task/TA1234/foo, locally it will be pr/123, but the hook is able to insert the TA1234: ticket number from the branch name.
12345678910111213141516171819202122
#!/usr/bin/env bash## A hook script to ensure that the commit message is prepended with a ticket# number from the current branch name. The hook runs before the user edits# the message.#COMMIT_MSG_FILE="$1"# Can be: <empty>, `message`, `template`, `merge`, `squash`, `commit`.# See `man githooks`.#COMMIT_SOURCE="$2"#SHA1="$3"BRANCH="$( git rev-parse --abbrev-ref HEAD )"# A special case for PR branches created with `g cpr`, which have the original# branch name in the push specification.if[["${BRANCH:0:3}"=="pr/"]];thenBRANCH="$( git config --local branch."$BRANCH".merge )"fiTICKET_NUMBER="$( grep -Eo '(US|DE|TA|F)\d+'<<<"$BRANCH")"[[ -z "$TICKET_NUMBER"]]|| perl -pi -e "$.==1 && s/^(?!$TICKET_NUMBER:|fixup!|squash!)/$TICKET_NUMBER: /""$COMMIT_MSG_FILE"