I sometimes write little web app utilities that are often statically hosted. But to use some of the new ES features means I need to drop in a build process that creates a bundle before the code gets checked in.

There are two parts to this.

The raw source code. The ES6+7+next and all the nifty new js features I want to leverage today.

The bundled es5 output.

In some of these projects it means I also check in the bundled code into git. A common use case is to be able to use github's pages feature to host this content (err bundled output) as well as the raw source.

The problem we can run into is if you make a change to the source, commit and push - nothing happened... Because the bundled code didn't get re-bundled, the github hosted pages page doesn't pull the latest changes in.

I'm a fan of using git pre-commit hooks to catch you early in the development life cycle to on things like test errors (or in this case a bundle issues).

So I came up with an example that allows me to make code changes and catch myself from committing when the raw source has changed, but the bundle did not reflect that.

So what is this thing?

The gist is it's a .js script that runs a set of actions and tests for the bundle.js currently vs what's about to be committed. Failing to commit if the current bundle doesn't match what the previous bundle is... Meaning, when we run our build (webpack in this case) if the bundle.js didn't change, we can commit.

This ensures that whatever bundle.js is committed is tied to the code-change the original source. Avoiding "fixing" something in the source and it not actually getting deployed because the bundle is out of date.

First get a pre-commit tool

There are some good options in the npm/node world for pre-commit hooks. Check out husky or pre-commit. However you get your precommit hook setup - great...

In my case I used husky and here are the relevant bits to my package.json.

Thought I'd share how I'm configuring user.name and user.email for git on my work computer. This is really just a post so when I forget how I did in the future I can google my own blog and be reminded...

I have always struggled with accidentally committing to an OSS project my work name/email or visa-versa, committing to a work git repo with my personal name/email.

For most, user.name shouldn't change, unless your company ties your user.name to something specific to the company like a username. (Contrast: user.name = Jason Jarrett and user.name = jjarrett).

When I clone projects I always clone them into a folder structure that looks like

|____~/code
| |____personal/ <--- thisis where I would put some OSS projects that I may be working onor contributing to.
| |____work/ <--- obviously work code goes in here

Thanks to this post where I learned about direnv and followed the last option I basically used these steps...

Setup

Install direnv - brew install direnv (What about Windows? see this github issue and help make it work)

Create .envrc file for each profile needing to be setup with the following content

What did this do?

Each time we cd into either a personal/ or work/ folder direnv will setup our shell with environment variables contained in that folder's .envrc file. This will then allow Git which respects these env vars and now we don't have to think about committing the wrong name/email to the wrong Git repositories.

When it comes to solid dev habits, source control should be on the top of your list as a given. So much so that I'm not going to talk about the pro's and pro's (see what I did there?) of source control. I did however, want to walk through something I call my git dance which is mostly just the rhythmic steps I take while pulling in changes from a remote repository while working on a project.

For a little context, this workflow is what I usually do while working on an internal project using git with a team where we may or may not be using pull requests, but the goal of this dance is to bring in any remote changes and layer my changes in on the master branch (or whatever branch we're developing on).

My favorite thing about git is there is no "right way" to do things and when you get good enough at it the 'rules' people place on using git can mostly be wiped away. However, the below steps, while not always followed, are generally what I use to stay happy and healthy when dealing with others changes.

I prefer to fetch any changes as this gives me the opportunity to review a visual of the commit graph before any action is taken. This allows me to take different paths depending on what the before commit graph looks like vs what I want the after to look like. Git pull is essentially a short-cut for both fetching and mergeing the code and I often don't want to just merge a remote change.

After I have any remote changes pulled (err fetched) locally I like to get a mental model of the recent commits.

Review remote changes

gitk --all

Now the gitk U.I. is horrible to look at (and I'm sure you have your favorite git visualizer), but it provides enough info to review changes between the current HEAD of my local branch and any remote changes that have come in with the git fetch step above. The --all is important in gitk, as it will show you all branches (including remote branches) that were fetched.

I use this overview to not only code review changes, but to help determine whether I want to merge or rebase with the remote changes.

Merge or Rebase

Once my review is complete, I've taken a look at the overall changes, and I have a mental model of what the current commit graph looks like. I generate a mental visual of what I want the commit graph to look like when I'm done. I will decide to either:

Merge

git merge [origin/master | otherBranch]

Or

Rebase

git rebase [origin/master | otherBranch]

Once my changes have been synced up with the remote changes, I push them up.

Ship changes up to the origin

git push origin master

or

git push origin <branchName>

Now it's not a complicated workflow and it can get a bit trickier, but the key here is doing the fetch, review, integrate workflow over automatically pulling in remote changes. This allows me the space to potentially interactive rebase or muck with my commits locally before pushing anything public.

One technique I use regularly when working with a team or even by myself is lots of individual code reviews.

This is not a formal (sit down with someone) code review.

This is a personal practice I found extremely useful.

Granted, I'm not talking about reviewing every commit in a repository the size of Facebooks. Most of the projects I have participated in are on small enough teams (between 1-10 people) that reviewing code changes either as they come in or in some block during the day is completely feasible.

Depending on the project, how many developers, time zones of committers, I shift how often or how thorough I review the changes but I try to review each and every commit (or at least the branch merge). This is one reason why segregating your code commits into tiny topical changes is important.

If you're working with a source control that doesn't have an easy way to do pull requests. Or some other code review functionality, you can still use the source control system to manually review changes or diffs between branches. Way back when I was forced to use TFS source control this was a much more manual process. I would open up Visual Studio, browse our TFS server and begin reviewing. These days, the power of a pull request is generally suffecient for this purpose.

What happens in this review?

One approach I've taken in the past is to block some time, possibly while warming up with a cup of coffee to begin reviewing the previous day's commits. I would use this time to not only review other developer's changes, but I would also re-review my own commits. This helped remind me what I worked on and possibly what I was struggling through the previous day. Reviewing code the next day provided time for my mental perspective to have shifted. I was probably thinking about things a little more clearly after a nights rest. Maybe I'd learned something that could be applied to the problem being worked on and provide better setup for changes needing to be made that day.

Some other benefits the morning review process provided include:

It was a bit of a meditative process in the early morning that put my brain on a code thinking track for the day.

I could take notes on changes other's had made that were not clear. (Note: this is generally done in an environment that didn't perscribe peer-reviewing code changes). So I essentially took the job on personally to review other's changes. Provide feedback, or ask question to get more understanding of their changes.

Learn from others. I'd like to think I'm a pretty good develoepr, but there is so much to know and learn that reading other's code provides a great way to learn something new (or possibly what not to do).

I once had a fellow developer say to me that they were blown away at how quick I could spin up on a brownfield project and get an architectural understanding that I could apply that knowledge and not only contribute quickly, but spot the places where we could improve general architectural patterns or jump in and become productive on a problem set.

I believe that reviewing the changes going into a code base each day allowed me to keep a strong perspective on what is shifting within the project and be able to talk intelligibly when others start to get stuck on their work.

Formal Code Reviews

If your team practices some form of code review workflow, this is great for those participating in the review process. However, it leaves the rest of the developers out of that specific review.

This should not stop those not in the review room (or pull request) from participating and learning from the changes.

Many of the places I've worked don't have a formal review process in place, so by putting this little ritual in place for myself I can not only come up to speed on a project but can be helpful to all aspects of the project. Reviewing changes every day allows me to more easily understand what people were working on when they announce it during the morning standup.

Just Review Everything

If you project is small enough and you can - spend a moment each day and review everything going on in the project. You and your team will likely be better off.

I sometimes come upon existing applications that use Git where files were added to the repository that should have originally been ignored. Sometimes this is because the .gitignore file is missing, or because certain rules were not include that probably should have been from the start.

There are ways in git to completely rewrite history to remove all traces of the file, however most of the time, it's good to leave a commit saying removed files that should have originally been ignored as a good starting point (depending on the project/repo).

Here is a small workflow I've found to be pretty effective in helping to clean up a repository, or at least let you know what files would have originally been ignored had you setup a .gitignore file with some project standards...

Create a well meaning .gitignore file

You can go to gitignore.io and type some words like xcode, node, osx, or whatever platform/IDE you use for development and generate a pretty good .gitignore base file.

Create or Update local .gitignore

Once you've update your local .gitignore file with the rules you'd like to use, you can use the below to commit your changes;

git add .gitignore
git commit -m 'updating .gitignore'

Remove the files that should have originally been ignored.

Now we want to figure out what files may have been added originally that shouldn't be there. You can run the following two commands to see which files to find this out.

git rm --cached -r .
git add .

Now if you git status you can see what files should probably be deleted from the git repo and using the newly updated .gitignore file will now be ignored going forward.

Go ahead an commit these changes (assuming you're happy with what is being deleted and ignored).

How to setup git to allow different merge/diff tools based on file extension.

I'm going to put this here so I can find it later...

Create a merge-wrapper script

To allow us the flexibility we want to determine which diff tool should be used for different file extensions, we need to break out the logic in to an external script. Below I have 2 samples of a merge script. I started with the .sh file (bottom), but changed over to the .js version (above that) since it is easier for me to maintain.

I haven't yet tried to run this on windows, but suspect we can wrap the merge-wrapper.js in a .cmd file calling it with Node.JS.

Customize the merge-wrapper.js

Extend with other diff tool support:

If you want to extend the script to add support for your own diff tool, just create a new function that returns an object following the pattern of the existing createP4MergeCommand or createOpenDiffCommand.

Modify which diff tool is used per extension:

If you want to change which tool is used per file extensions you can modify the diffLookup hash to map various extensions to whatever tool you setup.

Leave me a comment (either here - or in the gist) of what diff tool you added.

I'd be happy to take contributions of other diff tools in this gist if you leave a comment with yours...

Below was an attempt at using a bash script to manage what I do above, it'll work for some, but I didn't want to maintain this - prefer the JS version instead.

Setup ~/.gitconfig

Now that we've created our merge wrapper script we need to tell git how to use it.

Say we placed our merge script in the following directory: $HOME/dotfiles/tools/merge-wrapper.js. You can add the below to your ~/.gitconfig file and when you use git difftool our new merge-wrapper will be used to pick diff tools based on file extension.

How to update a git branch OTHER than the one you're currently on.

git branch -f {branch-to-change} {commit-to-change-to}

I recently setup a C.I. server to automatically generate builds of an iOS application and upload to TestFlight. I don't want each and every push to master to trigger a new TestFlight build, so I configured my C.I. server to watch the release branch.

I was starting to dislike the switch branch dance to trigger a new build.

What I used to do:

# When I was on the master branch
git checkout release
git merge master
git push

When things start to hurt, look for a better alternative. And with Git, there is almost always a more efficient way.

After digging a bit, I found the answer.

git branch -f {branch-to-change} {commit-to-change-to}

So to trigger a new build from master I can just:

git branch -f release master
git push origin release

Or wrap that in a Gulp task gulp tf. And with CommandAllThings I can now type rake tf, or grunt tf or gulp tf and they all trigger a new build to come out of TestFlight.

Or if you wanted to skip moving your local branch, you could just update the remote branch directly.

A standard task I began placing in my projects lately is one that easily integrates with my build tool of choice (rake, psake, etc…) and when run, installs a git pre-commit hook into my local copy of the repo that will run tests before code is committ. I’ve fancied calling the task putOnAHelmet.

Come checkout a small little github repo I started to keep track of various versions of this and feel free to open an issue or send a pull request with another one!

I don’t want to get into details about pre-commit hooks and how you should author them in this post (maybe we can expand the language in the repo’s readme…). You can also check out other writings… But one of the biggest problems I have with them is gits in-ability to easily keep track of pre-commit hooks much like it can with the rest of the projects source.

Now it’s true that different people need the ability to customize these, but a general “running of tests” before committing is a great first step and I’ve found these set of tasks the easiest way to carry them from repo to repo.

If you're not using source control for your coding projects, get off my lawn. :P (#JustHadToSayIt)

Now that I'm only reaching people who use source control (serious developers), I'd like to ask that you focus hard to only commit changes that belong to a single topic at a time. Think SRP for code commits/check-ins.

What is a topical commit?

That almost looks like 'tropical' and wouldn't it be nice to be in a tropical place doing commits to your code, but I digress...

The topics I'm referring to are specific functional units of change where each tiny commit is related to a single topic or theme.

For example:

adding feature X

refactoring

bug fix

spelling fix

Updating comments/documentation

*** code formatting ***

I highlighted that last one for a reason as it's the impetus for writing this post.

Don't mix topics/themes within a single commit!

If you have a one line bug fix but the file has 20 lines of code formatting changes (you know who you are). It makes determining what the bug fix is nearly impossible from the changes that are code formatting.

Using tools like git bisect to look at history become difficult. Pull Requests in GitHub become difficult to understand. If you've left a codebase for a while and come back, try reading changes that have happened over time to get caught up can be extremely difficult to do if your commits are not topically based.

I was doing some basic Git training for a customer this past week and they asked about how to setup their repositories to push/pull from a network share. I thought it would be simple and we spent a few minutes in class trying to accomplish it. We stopped trying in class and I took it as a homework assignment to figure it out before the next lesson. It was a little bit of a struggle to get this working for me, so I thought I’d throw this out there for any windows developers trying to do a similar thing.

I tend to prefer the command line to any of the git UI tools (except when visualizing history, and diffing files). In this post I’m going to show how you can do it through a command line, but I’ll also show how you can do it with git gui which, in this case, is a few less steps.

How to push a local repository up to an (un-initialized) remote windows share.

Command Line:

I tend to run git within PowerShell, however the following set of commands cannot be run within the PowerShell prompt. If you figure out a way, I’d love to hear about it. And since I use the PowerShell prompt, I’m not sure how this would play out with the bash command.

Open a command prompt (cmd.exe) and follow the below steps to create a remote windows repository share.

CD into the context of your local repository. Say my repo was at “C:\Code\MyGitRepo1”.

cd C:\Code\MyGitRepo1

Next we’re going to change our current directory to the remote share location.

Something I learned during this process is that cmd.exe doesn’t allow you to “cd” into a UNC network share path.

To get around not being allowed to “cd” into a UNC network share we’ll use the pushd command. The reason this works is because it is actually going to map a network drive to the network share location.

Kind of a pain at the command prompt really, but it’s not something that’s done all that often.

Using Git gui instead:

Open up the GUI

git gui

Click the [Remote->Add] menu option to bring up the “Add Remote” dialog.

Enter the name for your remote “origin” is pretty typical for the central repository, but you can call this whatever you want. Then type the remote location. Notice the direction of the slashes.

Now you should be good to go.

Hope this helps someone else, and if anyone knows of a better/easier way I’d love to hear it.

Comments

Jason.Jarrett

Thanks for the nice comment. In regards to your question, I'm afraid I'm not familiar enough to be of help.

You might take your question over to StackOverflow.com as there are some very smart people over there that might be able to help.

Good Luck

Djilali Tabbouche

Hi Jason, thanks for your post.I'm using this exact setup to deploy applications to both linux and windows server.No problem on linux using ssh and pushing to windows throuh network shares works fine but I have one issue with post-receive hooks: I use this hook to checkout the remote repository to the application directory and run configuration tasks and on windows, the git command use the local computer environment (git-dir and work-tree).I've tried every options without success.