This Week I Learned: 2020-11-29

  1. The difference between the ADD and COPY Dockerfile commands. ADD does some magic stuff (like extracting files out of an archive) but is deprecated because it was sometimes a bit too magic. COPY does what it says on the tin and is the recommended way to do things now.
  2. How to run a Python Flask app in Docker. The development server only listens to requests on 127.0.0.1 by default, and it turns out that when requests come into the container through the Docker bridge, they don’t get forwarded to 127.0.0.1.
  3. MongoDB has a change streams feature, which allows an application to receive a notification of any database write, without having to poll for changes.
  4. The MongoDB $lookup stage can take let and pipeline parameters, which can be used to perform a subquery and append the results to the return document. I’m looking forward to experimenting with this some more.
  5. I really enjoyed this talk about the Lean Web by Chris Ferdinandi. As a user I’ve never been a fan of the single-page app approach to simple static sites, and I like the example of using a service worker to pre-fetch all the other content the user might want to see instead.

Git for SVN Devs, Part 4: More Recipes

Creating a repository

For a brand new project I prefer to create the project in GitHub and then clone it locally. For existing projects, there is the GitHub Importer tool which should do the trick.

GitHub projects have a “default” branch. The default default is master, but if you are going to use a development branch then GitHub’s UI will be more helpful if you make this the default – instructions here.

Once you’ve created the project, you can click on the “Code” button in GitHub to get the URL, then use this to run git clone https://github.com/myproject.git.

Making a .gitignore File

The .gitignore file tells Git what files you never want to commit to the repository. This is useful for keeping things you don’t want (like IDE settings, cache directories, and compilation output) out of your code base.

Just create a file called .gitignore in the root of your project, and add each path that you want to ignore on a separate line. If you’re using a JetBrains IDE, you’ll want to add the following line to exclude your IDE index files:

.idea/

Throwing Out Uncommitted Code

Sometimes you’ve stuff something up so badly that you just want to chuck it out and start again. If you haven’t committed anything yet this is super easy.

PHPStorm: Right click on the project directory and choose Git -> Rollback. You can also do this for individual directories or files by right-clicking on them.

Terminal: From the project root do git checkout . – or you can specify the path if you want to target a specific directory or file.

If you have committed something you didn’t mean to, there’s an excellent guide here for fixing the mess.

Committing and Pushing Code in PHPSTorm

There are actually 3 steps for getting code from your local file system into the remote repository in Git. This does sound like a lot of overhead, but if you have the right tools you can do it all with a couple of clicks of the mouse.

The first step is adding, where you tell Git that you would like the changes you made to a file to be included in the next commit. Then you commit, which adds the commit to your local repository. Finally you push, which sends your new commit (and any others that hadn’t been sent yet) to the remote.

Some devs prefer to make all their in-progress commits locally, and then push the whole feature to the remote once it’s done. Others prefer to always commit and push, so that their work is backed up somewhere if their hard drive crashes.

In PHPStorm adding is taken care of for you. Once you’re ready to go, right click on the project and choose Git -> Commit Directory. You’ll see a list of all the files you’ve changed, and can deselect any you don’t want to include. You won’t lose your changes to these files – they’ll still be there on your working copy so you can continue working on them, but they won’t be added or committed.

Save yourself a step.

The Commit button on this widget has a secret which is hidden to impatient devs. If you click the little arrow on the right-hand side, it will give you the option to Commit and Push. This will push your commit for you, and create a new remote branch with the same name as your local branch if needed.

Committing in the Terminal

Your first step will be to list the files that you’ve changed. For this you need git status -s, which will list all files which have been changed since the last commit.

Files that haven’t been added will have a little red status text next to them. The ones that have been added will have green status text. Use git add {path} to add what you want, and git reset {path} to un-add anything you don’t want.

Once you’re done, git commit -m "Meaningful commit message" to commit all the added files.

Your last step is to push your branch. git push will work if your branch already has a remote tracking branch. If not, you want git push -u origin {branchname}.

Opening a Pull Request

Open your repository’s GitHub homepage in your browser. If you’ve pushed something recently you may see a yellow banner across the top of the page with a quick link for you to open a PR. If not, you can go to the Pull Requests page and then click the green New Pull Request button.

If you’ve pushed recently you should see a banner like this in GitHub.

Your PR’s “base” branch should be the branch you want to merge into (e.g. development) and the “compare” branch should be the one you’ve built your feature in.

The description field can be useful for a variety of things – testing or upgrade instructions, a brief explanation of why you chose to implement things the way you did, mentioning any limitations or future work that may be required. This becomes part of your project’s documentation and is easily searchable through GitHub after the PR has been closed.

Reviewing and Merging a Pull Request

The Files Changed tab is the best place to go through the code line-by-line and see what’s changed. You will notice a little blue “+” button appears on the left-hand margin of the line that you’re hovering over – you can click on this to add a note on a specific line. Use the “Start a Review” option, which just limits the amount of notification spam the PR’s author receives.

Once you’ve done your line-by-line review (and checked out and tested the code if desired) you can leave any general comments on the Conversation tab, then submit your review.

Once any concerns you raise have been addressed, it’s time to merge the PR. Once you’ve merged it, delete the branch (GitHub will give you a button to do this) since it’s no longer useful.

Deleting remote branches

As mentioned above, the easiest way to delete remote branches is to nuke them through GitHub as soon as they’ve been merged. If you didn’t do this, another option is to go to the repository home page in GitHub, open the branch switcher and choose “View All Branches” at the bottom, and then click on the delete icon next to the offending branch.

If you’d rather do it through the terminal, you can:

git push origin --delete {branchname}

Release Tags

A tag is a snapshot of the code base at a particular moment in time. They are most commonly used to record releases, so that you have a quick reference to see what code was released when. Making a new release tag off the master branch is typically one of the last things you would do before you actually publish the code onto your production servers.

The easiest way to make a release tag is to click on the link on the right-hand sidebar of your repository’s GitHub home page.

The link to create a new release is hidden in the right-hand sidebar on your project’s GitHub page.

To make a new branch from the terminal:

git tag -a {v1.1} -m "{tag title}"

git push origin {v.1.1}

Summary

This concludes my series on Git for SVN devs. This is not a comprehensive guide to everything that Git has to offer, but I hope it has been a useful introduction to get you up to speed and working with Git on a daily basis. Happy coding!

Other Posts in This Series

This Week I Learned: 2020-11-22

  1. A bit about the internals of Docker, thanks to LinkedIn unlocking this course for me for a week. I found the demonstration of multi-layer builds to reduce the size of the final image particularly interesting.
  2. An overview of the Parsimmon library for building parsers in JavaScript. The use of the composition pattern to build quite complicated parsers out of very simple components is beautiful. Thanks to Hackle Wayne for presenting this talk.
  3. A comparison of the different I/O approaches and performance of PHP, Java, Node and Golang. Conclusion – Node is not truly non-blocking because the V8 engine runs each script in a single thread. In fact, Node was worse than or comparable with Java in all the benchmarks presented, while Golang was consistently ahead of the pack.
  4. I’m fascinated by this glossary of data patterns from the MongoDB blog. I haven’t had time to dig into them all yet, but look forward to putting a couple of them into use on my next project.
  5. Websites which use ARIA have 11 more accessibility errors on average than websites that don’t. I loved this entertaining video about ARIA by @heydonworks, complete with cameo appearances by potty-mouthed Ada Lovelace, a layout table dinosaur, and a herd of stoned horny goats.

Git for SVN Devs, Part 3: Recipes

The two major differences between Git and SVN are that it is a distributed VCS (meaning that you have your own local repository as well as the one on GitHub) and the way its internal structure makes merging branches easier.

This post is primarily focused on recipes for getting the latest code into your local repository and keeping it in sync with GitHub. The next post will explain how to get your completed code back into the development branch and provide some other miscellaneous tips.

Why Do I Need Two Repositories?

Personally speaking, I prefer Git mainly for the ease of branching. If your team’s workflow is designed so that you don’t finish up with multiple devs working on the same branch, I can’t think of any particularly compelling bonus that having a local repo gives you. Sure, it enables you to work without the internet if you want to – but how much work are you going to get done without your esteemed colleagues Google and Stack Overflow?

The good news is that having that local repo doesn’t introduce a lot of overhead into the way I work day-to-day. You can treat Git more or less like SVN from that perspective, and just push every commit as soon as you make it.

And on that note … on to the recipes! In this section I’ll cover what you need to know to get the right branch of code in front of you at the right time. Stay tuned for part 4 where I explain how to commit your code and then how it finds its way back into the development branch.

Initial checkout

Find the URL of your project in GitHub. Open a terminal, switch to the parent directory that your projects live in, and …

git clone https://github.com/{myorg}/{myproject}.git

Fetching Remote Branches

Your local repository keeps metadata about all of the branches that are in the remote repository. This will get stale as branches are created and deleted on the remote. A “fetch” updates that list of branches. If you ever want to checkout a remote branch but you can’t find it, a fetch should set you right.

PHPSotrm’s project Git menu

PHPStorm: Right-click on project -> Git -> Repository -> Fetch

Terminal: git fetch

Seeing Available Branches

When closed, the PHPStorm branch switcher shows the name of the current branch

PHPStorm has a handy-dandy branch switcher in the bottom right-hand corner. This will show you a list of all of the remote and local branches.

Clicking on a branch name will give you a menu of options for that branch

Or if you’d rather use the terminal:

See Local branches: git branch or git branch -l

See Remote branches: git branch -r

See All branches (local and remote): git branch -a

The PHPStorm branch switcher handily shows which remote branch each local branch is “tracking”. If you want to see something similar in the terminal, it’s included in the output of git branch -vv.

See what branch I’m on

The current local branch is highlighted and displayed with an asterisk in the output from git branch. In PHPStorm, it’s displayed in the status bar, where you click to open the branch switcher.

Checkout a Remote Branch

If you want to work with a branch locally and you don’t already have it in your repo, you’ll need to check it out from its remote source. In most scenarios there is only one remote, called origin, which is the GitHub repository. You want to create and checkout a local branch which is tracking the origin branch.

PHPStorm: Click on the remote branch in the bottom right-hand branch switcher, and choose “Checkout as new local branch”.

Terminal: git checkout {branchname} origin/{branchname}

With the current version of Git you can skip the second argument if you want your local branch to have the same name as the remote branch, e.g. you can just say git checkout {branchname}. If Git can’t find a local branch with that name, it will look for a remote branch that matches and create a local branch with the same name.

Checkout a Local Branch

The process is very similar if you already have a local branch that you want to checkout. Just choose it from PHPStorm’s branch switcher, or from the terminal:

git checkout {branchname}

Create a new branch

When you create a new branch, the local branch that you already have checked out becomes its “parent”, if you will. Always make sure you have the right branch checked out first, and that it’s up-to-date with the remote if appropriate. Let’s consider a typical scenario where you’re going to start a new feature, so you want a new branch off development.

PHPStorm: checkout the development branch. Pull remote changes from the remote tracking branch (origin/development) by right-clicking on the project and choosing Git -> Repository -> Pull. Now open the branch switcher and choose New Branch (the item right at the top).

The terminal equivalent is:

git checkout development

git pull

git branch -b {newbranchname}

Your branch will only exist in your local repository at this stage – I’ll talk about how to push it to the origin in the next part.

Merge origin into my branch

Your local development branch will get out of date very quickly as new features get merged into the origin. You can update it by switching to the branch and then running git pull in the terminal, or right-clicking on the project and choosing Git -> Repository -> Pull.

Keeping a Feature Branch Up to Date

You should keep your feature branches up to date with development, so that as you develop you are working against the latest state of the code base.

I really prefer to do this in my IDE, as I like to have a UI to help me resolve any conflicts that might arise. In PHPStorm, checkout your feature branch, then open the branch switcher, click on the remote development branch, and choose Merge into Current.

If you’d really rather do it in the terminal, the equivalent is:

git merge origin/development

Deleting local branches

First switch to a different branch – you can’t delete a branch that you’ve currently checked out.

Then either git branch -d {branchname} or in PHPStorm open the branch switcher, click on your local branch, and choose Delete.

This won’t delete the remote tracking branch. That’s a topic for next time…

Pruning Orphaned Branches

After you’ve been working on a project for a while, you are likely to accumulate an impressive collection of orphaned local branches. These are branches that you built a feature in, where that feature has now been merged into development and the remote branch has been deleted.

Output of git branch -vv. “newbranch” is currently checked out. The “katedemo” branch is orphaned.

Personally I just delete them one at a time in the PHPStorm branch switcher every now and then – it’s a nice quick thing I can do while I’m waiting for unit tests to run or for something to download. However, I know that most devs hate doing repetitive work like this and would rather automate it.

Git doesn’t provide a great built-in way to remove the orphaned branches for you. There are a lot of sample scripts on Stack Overflow, and the safest strategy seems to be to check for the “:gone” string appearing in the output from git branch -vv. This will protect any other local branches which have never been pushed to the remote from being nuked as well.

Other Posts in This Series