After a year of use the Apple Bumper case for my iPhone 4S developed a tear in one corner. The case is still functional, but I found the tear unsightly and used it as an excuse to get a new case for my phone.
Case shopping can be frustrating. Local merchants are likely to only have the popular cases, many of which are garish, which limits your choices. Shopping on the Internet broadens the pool of cases to choose from, but you can’t “test drive” one. After a visits to BestBuy and the local Verizon store, I used a holiday to visit the nearest Apple store, two hours away in Kansas City. None of the cases really pulled my string, so I continued to use the torn bumper case.
I actually like the look and feel of the iPhone naked, but the glass by itself is slippery and I am always afraid I’ll drop it when it’s not in a case.
##Enter the SurfacePad
It’s the perfect cover for those who have an iPhone, and are not afraid to show it.
It’s not a case, per se, nor is it a sleeve. The best description is that it’s a jacket for your phone. A wonderfully supple Napa leather jacket. The leather is less than a 1/10th of an inch thick and weighs next to nothing at under an ounce. TwelveSouth says that
[r]ather than protecting your iPhone from falling off the side of a cliff, the luxury leather SurfacePad shields your iPhone from more common hazards, like the keys in your pocket.
I’ve had my SurfacePad for just over two weeks now and I could not be happier with it. Rather than hide the beautiful design of the iPhone in a plastic or rubber cover, the SurfacePad accentuates phones presence. The juxtaposition of leather with the metal and glass of the phone is perfect.
It slips into and out of my pocket with ease, but it doesn’t feel slippery at all. The leather protects the front glass, and wraps around the volume control side of the phone’s chassis. The back adheres to the phone with “modern adhesive” that has been used for years on TwelveSouth’s SurfacePad for MacBooks for years. The jacket can be removed a reattached.
The jacket is designed to act as a stand for the phone in landscape orientation. As luck would have it my favorite tuner/metronome app works in landscape orientation, so I use the stand on a daily basis.
For most activities I am unaware of the cover, but I do notice it a bit when making phone calls. I am left-handed when it comes to phone calls, so I fold the cover back completely, which slightly increases the phones thickness. It’s not enough to be objectionable, but I do notice it.
TwelveSouth has minimal branding on the jacket. The bottom of the front cover has a small embossed TwelveSouth logo. Inside the cover, oriented so that you can read it when the jacket is being used as a stand, is the product name, SurfacePad. And hidden away in the hinge of the case, is a repeat of the logo and the company name, TwelveSouth. The understated branding suits me very well as it allows the phone to be the center of attention.
The black leather on my jacket is smooth and unblemished. The interior side is a light grey with a very fine nap. It feels a bit like a high quality lens cloth. The double row of stitching on the back where the hinge is located is well done. There are no lose threads and the stitches are nearly flush with the leather. There is a cutout for the rear facing camera and flash. After two plus weeks of use my jacket is staring to feel broken in – like a favorite leather coat.
At $34.99 this isn’t the least expensive nor the most expensive cover for your iPhone. But in terms of quality, fit and finish, and enhancement to the phone, I don’t think you could find anything better.
At the end of January I received an email asking me if I would be willing to read and review a short book on getting started with Sublime Text 2. The email seemed legitimate and after investigating a little bit I decided to take them up on their offer. Here’s the email:
Hi Mark My name is Dyson D’ Souza and I work for Packt Publishing, a UK-based publishing company specializing in focused IT books. We have recently published a book Sublime Text Starter, and I was wondering if you’d be interested in taking a look at the book and sharing your comments on your blog and on Amazon.com. A few details about the book: book composes of around 40 pages, learn something new in an Instant! A short, fast, focused guide delivering immediate results, utilize multiple cursors to edit your text in many locations at the same time, etc. For more information please visit : http://www.packtpub.com/sublime-text-starter/book I’m contacting you because I’m aware that you share good knowledge in the subject this book covers, and so believe that your comments would be important for both consumers and readers alike. For this, I’d be glad to offer you a digital copy of the book.
Would be great to hear from you. Thank you
Kind regards,
Dyson D’ Souza Marketing Research Executive | Packt Publishing | www.PacktPub.com Email: dysond@packtpub.com
About a week after I replied I received a second email that contained the details on an account which had been created for me so I could download a PDF copy of the book. I’ve read through it a couple of times now and my review of it follows.
##Instant Sublime Text Starter At 46 pages, Instant Sublime Text Starter is short and to the point. It covers Sublime Text 2 installation (on Windows, Linux, or Macintosh operating systems), walks you through very basic steps on quickly creating and editing a document, and rounds out with a very brief look at some Sublime Text features.
If you were brand new to text editors and wanted something to get you started quickly Instant Sublime Text Starter might be an acceptable resource. Unfortunately there’s no depth to any of the material covered. It really is just a starter.
For anyone with any experience at all setting up and configuring programming tools, the level of discussion in Instant Sublime Text Starter will be too basic. And the suggestion that you edit Preferences | Settings Default to enable Vintage mode rather than Preferences | Settings User is not a good idea. Changes made to Settings Default will be overlaid the next time you update Sublime. User configurations and settings should always be made in Preferences | Settings User.
There are many resources available to get you started in any new topic, and Sublime Text is no exception. For my money I’d recommend the tutsplus.com Perfect Workflow in Sublime Text 2 to any developer wanting to really explore Sublime Text. For non-technical users wanting just enough information to get started, you might consider Instant Sublime Text Starter.
After reading Jack Perkes’ article Tracking Your iPhone’s Location I decided I wanted to do this with my phone.
##Getting a Copy of the Project
I started by forking the Github findi project. There is a sample Heroku project as well, complete with instructions on using Heroku to host your tracking efforts. While I’m not using Heroku I did make use of the bootstrap.py and record_location.py scripts and requirements.txt file from the findi-heroku-example project.
Once I had cloned the fork to my laptop, I copied the contents of the bootstrap.py, record_location.py, and requirements.txt files to my local copy of the project. This way my fork is based on the findi project itself, and not the example Heroku one, but I have the additional files from the example project.
I also added a Python .gitignore to my fork.
##Testing on the Command Line Next I tested findi against my iPhone. findi wrappers Apple’s Find My iPhone API and therefore is dependent upon your Apple ID. Once you instantiate a findi object using your Apple ID and Password, you can get a list of all the devices you are tracking. In my case that list contained 5 devices: my laptop, an original iPad, a brand-new iPad Mini, my iPhone, and my work iMac.
Plugging the index of my iPhone (3, in a zero-based list) into the API gave my my current latitude and longitude. Using copy and paste I was able to verify my location on Google Maps.
##Modifying the Script for My Device List
The record_location.py script expects a device mapped to your Apple account.
"Fetches an iPhone location from the Apple API and creates a Location Object"
iphone = FindMyIPhone(APPLE_EMAIL, APPLE_PASSWORD)
iphone_location = iphone.locate()Since I have 5 devices I changed the code slightly and hard-coded the index for my iPhone:
"Fetches an iPhone location from the Apple API and creates a Location Object"
iDevices = FindMyIPhone(APPLE_EMAIL, APPLE_PASSWORD)
# my phone is the 4th device (index 3) in the list
iphone_location = iDevices.locate(device_num=3)By default the script records the UTC time for each location.
date = Column(String, default=datetime.utcnow,
onupdate=datetime.utcnow)I’d rather have the time for my timezone, so I changed the utcnow method to be today:
date = Column(String, default=datetime.today,
onupdate=datetime.today)Finally, in order to execute record_location.py easily from cron, I added a hash-bang to the top of the file:
#!/usr/local/bin/python2.6My server through WebFaction provides multiple Python versions, the python2.6 specifies the exact version I want.
##Setting Up PostgreSQL and Cron
I used WebFaction’s control panel to create a new PostgreSQL database, along with a database userid and password. record_location.py expects the Apple Email, Apple Password, and database URL to be in environment variables called APPLE_EMAIL, APPLE_PASSWORD, and DATABASE_URL respectively. At the command prompt I exported these variables, filling in the appropriate values:
$ export APPLE_EMAIL="you@example.com"
$ export APPLE_PASSWORD="lettersAndNumbers"
$ export DATABASE_URL="postgresl://<user>:<password>:@localhost:5432:<databasename>"Running bootstrap.py with the exports in place created the table needed inside the PostgreSQL database.
Running the record_location.py script now resulted in a new row being inserted into the database.
Cron runs in its own environment and knows nothing about my exports, so I created a .cronvars file to house the required variables, so that I could source them, thereby making them available to the cron environment when the the script was executed. Here’s the line I added to my crontab:
* 0 0 0 0 source $HOME/.cronvars; $HOME/pyapps/findi/record_location.py > /dev/nullInitially I set the cron job to run every few minutes so I could see some rows appear in the table and verify that everything was working. Once it was clear that it was working, I changed the time to be 0, or once an hour on the hour.
##Fun and Profit Now I have to wait a while to start collecting data from my phone’s location; which by and large is my location. Eventually I’ll use TileMill and MapBox to create a map. That will be the subject of a future posting.
In July of 2011 I attended the Überconf in Denver Colorado. One of the evening sessions was Developer Productivity Power Up on Mac OS X given by Matthew McCullough. In addition to Github, CloudApp, Dropbox, iTerm2, and EC2, he demonstrated zsh, or z-shell. I had heard of zsh prior to his talk, but had never investigated it.
zsh is a superset of the venerable Bash shell. It has first class functions, modular enhancements, comes pre-installed on Mac OS X, and offers fantastic tab completion. As a jump start to getting a working zsh environment in place, Matthew pointed us toward oh-my-zsh, a framework with tremendous community support. oh-my-zsh offers modular zsh plug-ins, Git repository information in your prompt, a customizable right-side prompt, themes, and more.
By cloning the oh-my-zsh project on Github, selecting one of the themes, and changing your default shell to zsh you can have a flexible and powerful new shell in a matter of minutes. With a little judicious Googling and some experimentation it’s easy to develop a shell environment that’s tuned to your style of working. Over time I built up my prompt (the most visible portion of the environment) to display interactive version control status information from Git, Mercurial (hg), or Subversion (svn) when the current directory was under version control. My right-prompt showed the current RVM managed Ruby and Python virtual environment. I could also see at a glance what user I was, on which machine, and the current directory path. Oh, and all in vibrant colors.
Over time the features of zsh that I’ve come to rely upon the most are the tab completion and some of the directory movement commands. However, recently I had become dissatisfied with the delay when opening a new shell in a tab or window. A several second delay wasn’t unusual. In my efforts to pack as much information as possible into the prompt I created a resource intensive monster that was increasingly annoying. I began to question the need for dynamic svn repository information in the prompt when I rarely worked with Subversion any more.
Additionally, I had switched away from the main oh-my-zsh project to a fork created by Steve Losh. He added a function that exposed more status states for Git repositories. And I was using a Python script of his that exposed Mercurial repository information for the prompt. My prompt was capable of informing me of many things based on the current context, but the underlying structure had grown large and burdensome. Two weeks ago I decided to pare down my zsh setup, and in doing so I hoped to achieve two goals. One, I wanted to have a much better understanding of what was actually driving my shell’s environment. Two, I wanted to have a simpler, and hopefully quicker loading, setup.
My first thoughts were to remove some of the less used information from my zsh prompt theme. I had added svn information since we use svn at work. However my role there shifted and I have no real need (at the moment) for Subversion reporting in my prompt.
It was the matter of a couple minutes work to comment out the Subversion related code from my theme file and source .zshrc. The subjective measure of my prompt’s loading time wasn’t much improved.
Next up I commented out the Mercurial aspects of the prompt. I stared out using hg a few years ago but, following the herd, I’ve moved all my projects to Git and Github. Again, my prompt didn’t really seem to be any faster.
Taking a new tack, I hunted around and found a completely new zsh theme I liked the general looks of called Fino. It had basic Git repository status information, RVM or rbenv Ruby information, and Python virtualenv reporting. With it in place in my .zshrc file the prompt was changed but I started getting error messages. zsh: command not found: rvm_ruby with every command issued.
Googling the message took me to a number of stackOverflow threads and some pages about Mac OS X’s path_helper. Something about the new theme wasn’t getting along with the rest of my zsh environment.
path_helper examines the contents of /etc/paths.d and adds entries to your $PATH. The consensus seems to be that while it’s a good idea, it may not be implemented in the best fashion.
While I was reading all the pages I could find about path_helper, I wasn’t seeing the forest for the trees. RVM, and rbenv, are dependent upon their libraries being first in the PATH. If the PATH is altered after those key libraries are setup, the order may no longer be correct.
Frustrated by my lack of success in either altering my theme or incorporating the Fino theme, and not getting the clue the path_finder Google results were giving me, I reverted to my original theme and changed my primary text editor from TextMate to Sublime Text 2. When in doubt, go shave a yak.
I tend to collect open tabs in my browser. I usually have 30 or 40 open tabs and sometimes as many as 100. Periodically I scrub through them all and “do something” with at least some of them. Mentally stuck on the zsh: command not found error, I wandered through my open tabs and stopped on the nettuts plus Perfect Workflow in Sublime Text free course. Next thing you know I’ve dusted off my previously installed, but never really used, Sublime Text 2 application and started adding plug-ins and a color theme or two. I wasn’t solving my zsh theme issues, but I was using new and different tools to look at the reluctant code. When you can’t see the solution, change glasses, right?
With a newly configured and tweaked editor ready for use I returned to the original project.
There are two Ruby environment managers: RVM and rbenv. I’ve used both in the past, although RVM has been my primary one for longer. The framework I use for my Websites is Octopress, which is Ruby-based. I initially installed RVM to setup and configure the required Ruby and Gems to get my site infrastructure working.
Since the error message I was getting seemed to be pointing a finger at RVM I decided to switch to rbenv. Rather than solve the problem, I thought, I’ll just switch environment managers. Easy as pie.
Rather than jeopardize my working-if-slow-loading environment I decided to use a different machine for my rbenv experiment. I have access to an older, but still serviceable aluminum MacBook Pro. I had recently formatted its hard drive and installed Linux Mint on it just to explore that distribution. I wiped the drive and installed Mountain Lion and rebuilt the machine to match my personal machine as closely as possible with one key difference: I setup rbenv and not RVM.
At this point, with a test bed that I could afford to be risky with, I stepped back from trying to get oh-my-zsh working and considered switching to the other zsh framework I had read about.
zshuery is a much lighter-weight framework than oh-my-zsh. Based on the commit activity it isn’t nearly as frequently updated either. My concerns with it ran deeper than a seeming lack of support. Without digging into both oh-my-zsh and zshuery and comparing them at a nuts and bolts level I wouldn’t know what one had that the other didn’t. What would I gain with zshuery? What would I lose and have to recreate?
While starting out with oh-my-zsh was fantastic, it came with a hidden cost. Since I knew nothing about zsh prior to using the framework I had no way of knowing what was raw zsh and what was framework-added. To me, oh-my-zsh was zsh.
In my college internal data structures course we wrote a sort. After a lecture discussion the ins and outs of the algorithm I wrote my very own bubble sort. The following lecture the professor showed us the JCL sort utility. At first I was upset that I spent all the time and effort to reinvent the sort wheel, but later I came to realize that I had a better understand and appreciation for sorts as a result of the exercise.
My zsh experience was the opposite of my sort experience. I started zsh with the polished, design-decisions-already-made-for-you solution. If I really wanted to get to the bottom of my zsh shell slowness I would need to build my own environment, eschewing frameworks.
Github is a wonderful place as people store all sorts of things there, open to the public. In my sifting of Google zsh search results I stumbled onto a repository cheekily named ze-best-zsh-config. At first glance it looked well laid out. The various aspects of the configuration, options, exports, the prompt, history, aliases, and functions, were all isolated in their own .zsh files, and the resulting components brought together in the .zshrc file.
Here was a starting point for my own, hand-rolled zsh environment. I cloned the repository and started working through the .zsh files it contained line-by-line to understand them.
Installing and using rbenv isn’t any harder than installing and using RVM. Reflecting the current active Ruby in my shell’s right-prompt (RPROMPT) proved to be one of the hardest parts of this project.
I liked the code from the Fino theme for interrogating the host machine for either RVM or rbenv and then determining what Ruby was active. Copying that code to my new prompt.zsh file didn’t work however. The right-prompt either showed nothing at all, or the line of code that should have resolved to the Ruby name. After an evening of Googling and trial and error I finally posted a question on stackOverflow. The next morning I not only had a working answer, I had an explanation of how it worked. I love the Internet.
With my prompt sorted it was time to understand all the configuration options available to me, and setup my environment. Working from the setops included in ze-best-zsh-config, I looked everything up. Some I kept, others I changed, a few I dropped. Next I read through the options defined in zshuery.
I performed similar examinations of the colors, completions, history, exports, aliases, and functions .zsh files. I merged my existing functions and aliases into the starter files, and incorporated some more from zshuery.
From zshuery I liberated the idea of testing the host machine’s operating system (uname) and setting an attribute. This allows me to have options that are OS specific.
During the due diligence process I managed to break the right-prompt. Suddenly the zsh: command not found error was back. Somewhere along the line I had managed to break the path. After much searching and reading I finally created a .zshenv file and copied the code necessary to run path_helper there. I then altered this code to completely clear the path, ensuring that I had no unexpected cruft there. This article about path_helper was instrumental in my getting the problem solved.
I also placed the path and shim setup for rbenv into the .zshenv file. With this file in place the zsh: command not found error disappeared. Hopefully for good.
My new .zshrc file is just a list of .zsh files that get sourced.
source ~/.zsh/checks.zsh
source ~/.zsh/colors.zsh
source ~/.zsh/setopt.zsh
source ~/.zsh/exports.zsh
source ~/.zsh/prompt.zsh
source ~/.zsh/completion.zsh
source ~/.zsh/aliases.zsh
source ~/.zsh/bindkeys.zsh
source ~/.zsh/functions.zsh
source ~/.zsh/history.zsh
source ~/.zsh/zsh_hooks.zsh
source ${HOME}/.dotfiles/z/z.shAnd those files each contain a portion of the overall configuration. I suppose they could all be combined into one large file, but I like breaking them up into logically related components.
Here is where we override path_helper by calling it again ourselves. The PATH is cleared first and then rebuilt. The result is far less duplication of directories. rbenv is also setup here.
# Mac OS X uses path_helper and /etc/paths.d to preload PATH, clear it out first
if [ -x /usr/libexec/path_helper ]; then
PATH=''
eval `/usr/libexec/path_helper -s`
fi
# if rbenv is present, configure it for use
if which rbenv &> /dev/null; then
# Put the rbenv entry at the front of the line
export PATH="$HOME/.rbenv/bin:$PATH"
# enable shims and auto-completion
eval "$(rbenv init -)"
fiThese tests allow me to later isolate exports, or setopt statement based on the host machine’s operating system.
# checks (stolen from zshuery)
if [[ $(uname) = 'Linux' ]]; then
IS_LINUX=1
fi
if [[ $(uname) = 'Darwin' ]]; then
IS_MAC=1
fi
if [[ -x `which brew` ]]; then
HAS_BREW=1
fi
if [[ -x `which apt-get` ]]; then
HAS_APT=1
fi
if [[ -x `which yum` ]]; then
HAS_YUM=1
fiA colorful shell is a happy shell. But the escaped codes are arcane and miserable to work with. Let’s give them some readable names.
autoload colors; colors
# The variables are wrapped in \%\{\%\}. This should be the case for every
# variable that does not contain space.
for COLOR in RED GREEN YELLOW BLUE MAGENTA CYAN BLACK WHITE; do
eval PR_$COLOR='%{$fg_no_bold[${(L)COLOR}]%}'
eval PR_BOLD_$COLOR='%{$fg_bold[${(L)COLOR}]%}'
done
eval RESET='$reset_color'
export PR_RED PR_GREEN PR_YELLOW PR_BLUE PR_WHITE PR_BLACK
export PR_BOLD_RED PR_BOLD_GREEN PR_BOLD_YELLOW PR_BOLD_BLUE
export PR_BOLD_WHITE PR_BOLD_BLACK
# Clear LSCOLORS
unset LSCOLORS
# Main change, you can see directories on a dark background
#expor tLSCOLORS=gxfxcxdxbxegedabagacad
export CLICOLOR=1
export LS_COLORS=exfxcxdxbxegedabagacadThere are a dizzying array of options available. These are a start.
# ===== Basics
setopt no_beep # don't beep on error
setopt interactive_comments # Allow comments even in interactive shells (especially for Muness)
# ===== Changing Directories
setopt auto_cd # If you type foo, and it isn't a command, and it is a directory in your cdpath, go there
setopt cdablevarS # if argument to cd is the name of a parameter whose value is a valid directory, it will become the current directory
setopt pushd_ignore_dups # don't push multiple copies of the same directory onto the directory stack
# ===== Expansion and Globbing
setopt extended_glob # treat #, ~, and ^ as part of patterns for filename generation
# ===== History
setopt append_history # Allow multiple terminal sessions to all append to one zsh command history
setopt extended_history # save timestamp of command and duration
setopt inc_append_history # Add comamnds as they are typed, don't wait until shell exit
setopt hist_expire_dups_first # when trimming history, lose oldest duplicates first
setopt hist_ignore_dups # Do not write events to history that are duplicates of previous events
setopt hist_ignore_space # remove command line from history list when first character on the line is a space
setopt hist_find_no_dups # When searching history don't display results already cycled through twice
setopt hist_reduce_blanks # Remove extra blanks from each command line being added to history
setopt hist_verify # don't execute, just expand history
setopt share_history # imports new commands and appends typed commands to history
# ===== Completion
setopt always_to_end # When completing from the middle of a word, move the cursor to the end of the word
setopt auto_menu # show completion menu on successive tab press. needs unsetop menu_complete to work
setopt auto_name_dirs # any parameter that is set to the absolute name of a directory immediately becomes a name for that directory
setopt complete_in_word # Allow completion from within a word/phrase
unsetopt menu_complete # do not autoselect the first completion entry
# ===== Correction
setopt correct # spelling correction for commands
setopt correctall # spelling correction for arguments
# ===== Prompt
setopt prompt_subst # Enable parameter expansion, command substitution, and arithmetic expansion in the prompt
setopt transient_rprompt # only show the rprompt on the current prompt
# ===== Scripts and Functions
setopt multios # perform implicit tees or cats when multiple redirections are attemptedThe PATH is altered here after having been cleared and started in .zshenv.
# Currently this path is appended to dynamically when picking a ruby version
# zshenv has already started PATH with rbenv so append only here
export PATH=$PATH~/bin:/usr/local/bin:/usr/local/sbin:~/bin
# Set default console Java to 1.6
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/1.6/Home
# Setup terminal, and turn on colors
export TERM=xterm-256color
export CLICOLOR=1
export LSCOLORS=Gxfxcxdxbxegedabagacad
# Enable color in grep
export GREP_OPTIONS='--color=auto'
export GREP_COLOR='3;33'
# This resolves issues install the mysql, postgres, and other gems with native non universal binary extensions
export ARCHFLAGS='-arch x86_64'
export LESS='--ignore-case --raw-control-chars'
export PAGER='less'
export EDITOR='subl -w'
#export NODE_PATH=/opt/github/homebrew/lib/node_modules
#export PYTHONPATH=/usr/local/lib/python2.6/site-packages
# CTAGS Sorting in VIM/Emacs is better behaved with this in place
export LC_COLLATE=C
# Virtual Environment Stuff
export WORKON_HOME=$HOME/.virtualenvs
export PROJECT_HOME=$HOME/Projects/django
source /usr/local/bin/virtualenvwrapper.shThe visible tip of the zsh environment iceberg. Lots going on here.
The Python virtual environment, if one is active, is captured for display in the right-prompt.
The prompt character displayed is normally a $ or maybe a >. I’m using the symbols Steve Losh had in his extravagant zsh prompt
box_name is a clever way to show your preferred name for the machine you are on. Just create a ~/.box-name file and put the name you want there. Or, on Mac OS X, use the sudo scutil --set HostName <your-name-here> command.
A variety of variables are set up for Git repository status reporting, and then there are some functions to interrogate Git. This information is shown in the main prompt.
The current Ruby active is determined by interrogating either RVM or rbenv, which ever is present on the machine. This function, _update_ruby_version(), has been added to the chpwd_functions array like so: chpwd_functions+=(_update_ruby_version). This means it is only executed when the directory changes.
Finally there is a function to get the name of the current working directory.
The primary prompt shows the username, box name, current working directory, and git branch and status (if present). The prompt is preceded by a blank line, and places the prompt character on a line of its own. This makes reading the console far easier.
zsh provides an error prompt, which I’m using to spell correct commands. The export SPROMPT line handles this.
Lastly the troublesome right-prompt or RPROMPT. Here the Python virtual environment information and active Ruby information is displayed. One of the setops defined above, setopt transient_rprompt causes the RPROMPT to only appear on the current, most recent, prompt. I’m not sure yet if I like this, but it does make the console seem less cluttered.
function virtualenv_info {
[ $VIRTUAL_ENV ] && echo '('`basename $VIRTUAL_ENV`') '
}
function prompt_char {
git branch >/dev/null 2>/dev/null && echo '±' && return
hg root >/dev/null 2>/dev/null && echo '☿' && return
echo '○'
}
function box_name {
[ -f ~/.box-name ] && cat ~/.box-name || hostname -s
}
# http://blog.joshdick.net/2012/12/30/my_git_prompt_for_zsh.html
# copied from https://gist.github.com/4415470
# Adapted from code found at <https://gist.github.com/1712320>.
#setopt promptsubst
autoload -U colors && colors # Enable colors in prompt
# Modify the colors and symbols in these variables as desired.
GIT_PROMPT_SYMBOL="%{$fg[blue]%}±"
GIT_PROMPT_PREFIX="%{$fg[green]%} [%{$reset_color%}"
GIT_PROMPT_SUFFIX="%{$fg[green]%}]%{$reset_color%}"
GIT_PROMPT_AHEAD="%{$fg[red]%}ANUM%{$reset_color%}"
GIT_PROMPT_BEHIND="%{$fg[cyan]%}BNUM%{$reset_color%}"
GIT_PROMPT_MERGING="%{$fg_bold[magenta]%}⚡︎%{$reset_color%}"
GIT_PROMPT_UNTRACKED="%{$fg_bold[red]%}u%{$reset_color%}"
GIT_PROMPT_MODIFIED="%{$fg_bold[yellow]%}d%{$reset_color%}"
GIT_PROMPT_STAGED="%{$fg_bold[green]%}s%{$reset_color%}"
# Show Git branch/tag, or name-rev if on detached head
function parse_git_branch() {
(git symbolic-ref -q HEAD || git name-rev --name-only --no-undefined --always HEAD) 2> /dev/null
}
# Show different symbols as appropriate for various Git repository states
function parse_git_state() {
# Compose this value via multiple conditional appends.
local GIT_STATE=""
local NUM_AHEAD="$(git log --oneline @{u}.. 2> /dev/null | wc -l | tr -d ' ')"
if [ "$NUM_AHEAD" -gt 0 ]; then
GIT_STATE=$GIT_STATE${GIT_PROMPT_AHEAD//NUM/$NUM_AHEAD}
fi
local NUM_BEHIND="$(git log --oneline ..@{u} 2> /dev/null | wc -l | tr -d ' ')"
if [ "$NUM_BEHIND" -gt 0 ]; then
GIT_STATE=$GIT_STATE${GIT_PROMPT_BEHIND//NUM/$NUM_BEHIND}
fi
local GIT_DIR="$(git rev-parse --git-dir 2> /dev/null)"
if [ -n $GIT_DIR ] && test -r $GIT_DIR/MERGE_HEAD; then
GIT_STATE=$GIT_STATE$GIT_PROMPT_MERGING
fi
if [[ -n $(git ls-files --other --exclude-standard 2> /dev/null) ]]; then
GIT_STATE=$GIT_STATE$GIT_PROMPT_UNTRACKED
fi
if ! git diff --quiet 2> /dev/null; then
GIT_STATE=$GIT_STATE$GIT_PROMPT_MODIFIED
fi
if ! git diff --cached --quiet 2> /dev/null; then
GIT_STATE=$GIT_STATE$GIT_PROMPT_STAGED
fi
if [[ -n $GIT_STATE ]]; then
echo "$GIT_PROMPT_PREFIX$GIT_STATE$GIT_PROMPT_SUFFIX"
fi
}
# If inside a Git repository, print its branch and state
function git_prompt_string() {
local git_where="$(parse_git_branch)"
[ -n "$git_where" ] && echo "on %{$fg[blue]%}${git_where#(refs/heads/|tags/)}$(parse_git_state)"
}
# determine Ruby version whether using RVM or rbenv
# the chpwd_functions line cause this to update only when the directory changes
function _update_ruby_version() {
typeset -g ruby_version=''
if which rvm-prompt &> /dev/null; then
ruby_version="$(rvm-prompt i v g)"
else
if which rbenv &> /dev/null; then
ruby_version="$(rbenv version | sed -e "s/ (set.*$//")"
fi
fi
}
chpwd_functions+=(_update_ruby_version)
function current_pwd {
echo $(pwd | sed -e "s,^$HOME,~,")
}
PROMPT='
${PR_GREEN}%n%{$reset_color%} %{$FG[239]%}at%{$reset_color%} ${PR_BOLD_BLUE}$(box_name)%{$reset_color%} %{$FG[239]%}in%{$reset_color%} ${PR_BOLD_YELLOW}$(current_pwd)%{$reset_color%} $(git_prompt_string)
$(prompt_char) '
export SPROMPT="Correct $fg[red]%R$reset_color to $fg[green]%r$reset_color [(y)es (n)o (a)bort (e)dit]? "
RPROMPT='${PR_GREEN}$(virtualenv_info)%{$reset_color%} ${PR_RED}${ruby_version}%{$reset_color%}'Tab completion is like magic, and the incantations in the completions.zsh file are magic to me. I need to learn more about these.
autoload -U compinit && compinit
zmodload -i zsh/complist
# man zshcontrib
zstyle ':vcs_info:*' actionformats '%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{3}|%F{1}%a%F{5}]%f '
zstyle ':vcs_info:*' formats '%F{5}(%f%s%F{5})%F{3}-%F{5}[%F{2}%b%F{5}]%f '
zstyle ':vcs_info:*' enable git #svn cvs
# Enable completion caching, use rehash to clear
zstyle ':completion::complete:*' use-cache on
zstyle ':completion::complete:*' cache-path ~/.zsh/cache/$HOST
# Fallback to built in ls colors
zstyle ':completion:*' list-colors ''
# Make the list prompt friendly
zstyle ':completion:*' list-prompt '%SAt %p: Hit TAB for more, or the character to insert%s'
# Make the selection prompt friendly when there are a lot of choices
zstyle ':completion:*' select-prompt '%SScrolling active: current selection at %p%s'
# Add simple colors to kill
zstyle ':completion:*:*:kill:*:processes' list-colors '=(#b) #([0-9]#) ([0-9a-z-]#)*=01;34=0=01'
# list of completers to use
zstyle ':completion:*::::' completer _expand _complete _ignored _approximate
zstyle ':completion:*' menu select=1 _complete _ignored _approximate
# insert all expansions for expand completer
# zstyle ':completion:*:expand:*' tag-order all-expansions
# match uppercase from lowercase
zstyle ':completion:*' matcher-list 'm:{a-z}={A-Z}'
# offer indexes before parameters in subscripts
zstyle ':completion:*:*:-subscript-:*' tag-order indexes parameters
# formatting and messages
zstyle ':completion:*' verbose yes
zstyle ':completion:*:descriptions' format '%B%d%b'
zstyle ':completion:*:messages' format '%d'
zstyle ':completion:*:warnings' format 'No matches for: %d'
zstyle ':completion:*:corrections' format '%B%d (errors: %e)%b'
zstyle ':completion:*' group-name ''
# ignore completion functions (until the _ignored completer)
zstyle ':completion:*:functions' ignored-patterns '_*'
zstyle ':completion:*:scp:*' tag-order files users 'hosts:-host hosts:-domain:domain hosts:-ipaddr"IP\ Address *'
zstyle ':completion:*:scp:*' group-order files all-files users hosts-domain hosts-host hosts-ipaddr
zstyle ':completion:*:ssh:*' tag-order users 'hosts:-host hosts:-domain:domain hosts:-ipaddr"IP\ Address *'
zstyle ':completion:*:ssh:*' group-order hosts-domain hosts-host users hosts-ipaddr
zstyle '*' single-ignored showI’ve had some of these aliases for as long as I’ve been working on *nix-based machines. They are as much a part of my muscle memory as tying my shoe or combing my hair. Since zsh tries to spell correct commands, the nocorrect section is at the top. Here is where you put commands that you don’t want spell checked every time they are used.
# -------------------------------------------------------------------
# use nocorrect alias to prevent auto correct from "fixing" these
# -------------------------------------------------------------------
#alias foobar='nocorrect foobar'
alias g8='nocorrect g8'
# -------------------------------------------------------------------
# Ruby stuff
# -------------------------------------------------------------------
alias ri='ri -Tf ansi' # Search Ruby documentation
alias rake="noglob rake" # necessary to make rake work inside of zsh
#alias be='bundle exec'
#alias bx='bundle exec'
#alias gentags='ctags .'
# -------------------------------------------------------------------
# directory movement
# -------------------------------------------------------------------
alias ..='cd ..'
alias ...='cd ../..'
alias ....='cd ../../..'
alias 'bk=cd $OLDPWD'
# -------------------------------------------------------------------
# directory information
# -------------------------------------------------------------------
alias lh='ls -d .*' # show hidden files/directories only
alias lsd='ls -aFhlG'
alias l='ls -al'
alias ls='ls -GFh' # Colorize output, add file type indicator, and put sizes in human readable format
alias ll='ls -GFhl' # Same as above, but in long listing format
alias tree="ls -R | grep ":$" | sed -e 's/:$//' -e 's/[^-][^\/]*\//--/g' -e 's/^/ /' -e 's/-/|/'"
alias 'dus=du -sckx * | sort -nr' #directories sorted by size
alias 'wordy=wc -w * | sort | tail -n10' # sort files in current directory by the number of words they contain
alias 'filecount=find . -type f | wc -l' # number of files (not directories)
# -------------------------------------------------------------------
# Mac only
# -------------------------------------------------------------------
if [[ $IS_MAC -eq 1 ]]; then
alias ql='qlmanage -p 2>/dev/null' # OS X Quick Look
alias oo='open .' # open current directory in OS X Finder
alias 'today=calendar -A 0 -f /usr/share/calendar/calendar.mark | sort'
alias 'mailsize=du -hs ~/Library/mail'
alias 'smart=diskutil info disk0 | grep SMART' # display SMART status of hard drive
# Hall of the Mountain King
alias cello='say -v cellos "di di di di di di di di di di di di di di di di di di di di di di di di di di"'
# alias to show all Mac App store apps
alias apps='mdfind "kMDItemAppStoreHasReceipt=1"'
# reset Address Book permissions in Mountain Lion (and later presumably)
alias resetaddressbook='tccutil reset AddressBook'
# refresh brew by upgrading all outdated casks
alias refreshbrew='brew outdated | while read cask; do brew upgrade $cask; done'
# rebuild Launch Services to remove duplicate entries on Open With menu
alias rebuildopenwith='/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.fram ework/Support/lsregister -kill -r -domain local -domain system -domain user'
fi
# -------------------------------------------------------------------
# remote machines
# -------------------------------------------------------------------
alias 'palantir=ssh mhn@palantir.ome.ksu.edu -p 11122'
alias 'pvnc=open vnc://palantir.ome.ksu.edu'
alias 'ksunix=ssh mhn@unix.ksu.edu'
alias 'veld=ssh mhn@veld.ome.ksu.edu'
alias 'dev=ssh mhn@ome-dev-as1.ome.campus'
alias 'wf=ssh markn@markn.webfactional.com'
# -------------------------------------------------------------------
# database
# -------------------------------------------------------------------
alias 'psqlstart=/usr/local/pgsql/bin/pg_ctl -D /usr/local/pgsql/data -l logfile start'
alias 'psqlstop=/usr/local/pgsql/bin/pg_ctl stop'
#alias mysql='mysql -u root'
#alias mysqladmin='mysqladmin -u root'
# -------------------------------------------------------------------
# ome devvm start, stop, ssh, and mount
# -------------------------------------------------------------------
alias 'startvm=VBoxHeadless --startvm devvm'
alias 'stopvm=VBoxManage controlvm devvm poweroff'
alias 'devvm=ssh -p 10022 ome@localhost'
# -------------------------------------------------------------------
# Mercurial (hg)
# -------------------------------------------------------------------
alias 'h=hg status'
alias 'hc=hg commit'
alias 'push=hg push'
alias 'pull=hg pull'
alias 'clone=hg clone'
# -------------------------------------------------------------------
# Git
# -------------------------------------------------------------------
alias ga='git add'
alias gp='git push'
alias gl='git log'
alias gpl="git log --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
alias gs='git status'
alias gd='git diff'
alias gm='git commit -m'
alias gma='git commit -am'
alias gb='git branch'
alias gc='git checkout'
alias gcb='git checkout -b'
alias gra='git remote add'
alias grr='git remote rm'
alias gpu='git pull'
alias gcl='git clone'
alias gta='git tag -a -m'
alias gf='git reflog'
alias gv='git log --pretty=format:'%s' | cut -d " " -f 1 | sort | uniq -c | sort -nr'
# leverage aliases from ~/.gitconfig
alias gh='git hist'
alias gt='git today'
# curiosities
# gsh shows the number of commits for the current repos for all developers
alias gsh="git shortlog | grep -E '^[ ]+\w+' | wc -l"
# gu shows a list of all developers and the number of commits they've made
alias gu="git shortlog | grep -E '^[^ ]'"
# -------------------------------------------------------------------
# Python virtualenv
# -------------------------------------------------------------------
alias mkenv='mkvirtualenv'
alias on="workon"
alias off="deactivate"
# -------------------------------------------------------------------
# Oddball stuff
# -------------------------------------------------------------------
alias 'sloc=/usr/local/sloccount/bin/sloccount'
alias 'adventure=emacs -batch -l dunnet' # play adventure in the console
alias 'ttop=top -ocpu -R -F -s 2 -n30' # fancy top
alias 'rm=rm -i' # make rm command (potentially) less destructive
# Force tmux to use 256 colors
alias tmux='TERM=screen-256color-bce tmux'
# alias to cat this file to display
alias acat='< ~/.zsh/aliases.zsh'
alias fcat='< ~/.zsh/functions.zsh'
alias sz='source ~/.zshrc'
# -------------------------------------------------------------------
# some Octopress helpers
# -------------------------------------------------------------------
alias 'generate=date ; rake generate ; date ;'
alias 'gen=date ; rake generate ; date ;'
alias 'ingen=date ; rake integrate ; generate ; date ;'
alias 'deploy=rm deploy.log ; rake deploy > deploy.log ; tail -n 3 deploy.log ;'
alias 'np=newpost.rb'
# copy .htaccess files for zanshin.net and its image sub-directory
alias 'htaccess=scp /Users/mark/Projects/octopress/zanshin/source/htaccess/.htaccess markn@markn.webfactional.com:~/webapps/zanshin ; scp /Users/mark/Projects/octopress/zanshin/source/images/.htaccess markn@markn.webfactional.com:~/webapps/zanshin/images ;'
# deploy zanshin.net and move its .htaccess files
alias 'dz=deploy ; htaccess ;'
# -------------------------------------------------------------------
# Source: http://aur.archlinux.org/packages/lolbash/lolbash/lolbash.sh
# -------------------------------------------------------------------
alias wtf='dmesg'
alias onoz='cat /var/log/errors.log'
alias rtfm='man'
alias visible='echo'
alias invisible='cat'
alias moar='more'
alias icanhas='mkdir'
alias donotwant='rm'
alias dowant='cp'
alias gtfo='mv'
alias hai='cd'
alias plz='pwd'
alias inur='locate'
alias nomz='ps aux | less'
alias nomnom='killall'
alias cya='reboot'
alias kthxbai='halt'Create your own keybindings for fun and profit. Still more material to learn better.
# To see the key combo you want to use just do:
# cat > /dev/null
# And press it
bindkey "^K" kill-whole-line # ctrl-k
bindkey "^R" history-incremental-search-backward # ctrl-r
bindkey "^A" beginning-of-line # ctrl-a
bindkey "^E" end-of-line # ctrl-e
bindkey "[B" history-search-forward # down arrow
bindkey "[A" history-search-backward # up arrow
bindkey "^D" delete-char # ctrl-d
bindkey "^F" forward-char # ctrl-f
bindkey "^B" backward-char # ctrl-b
bindkey -v # Default to standard vi bindings, regardless of editor stringSome of these are my own creation, others I’ve picked up along the way. The most recent addition, path() displays your current PATH beautifully. Immensely helpful when sorting out path issues.
# -------------------------------------------------------------------
# compressed file expander
# (from https://github.com/myfreeweb/zshuery/blob/master/zshuery.sh)
# -------------------------------------------------------------------
ex() {
if [[ -f $1 ]]; then
case $1 in
*.tar.bz2) tar xvjf $1;;
*.tar.gz) tar xvzf $1;;
*.tar.xz) tar xvJf $1;;
*.tar.lzma) tar --lzma xvf $1;;
*.bz2) bunzip $1;;
*.rar) unrar $1;;
*.gz) gunzip $1;;
*.tar) tar xvf $1;;
*.tbz2) tar xvjf $1;;
*.tgz) tar xvzf $1;;
*.zip) unzip $1;;
*.Z) uncompress $1;;
*.7z) 7z x $1;;
*.dmg) hdiutul mount $1;; # mount OS X disk images
*) echo "'$1' cannot be extracted via >ex<";;
esac
else
echo "'$1' is not a valid file"
fi
}
# -------------------------------------------------------------------
# any function from http://onethingwell.org/post/14669173541/any
# search for running processes
# -------------------------------------------------------------------
any() {
emulate -L zsh
unsetopt KSH_ARRAYS
if [[ -z "$1" ]] ; then
echo "any - grep for process(es) by keyword" >&2
echo "Usage: any " >&2 ; return 1
else
ps xauwww | grep -i --color=auto "[${1[1]}]${1[2,-1]}"
fi
}
# -------------------------------------------------------------------
# display a neatly formatted path
# -------------------------------------------------------------------
path() {
echo $PATH | tr ":" "\n" | \
awk "{ sub(\"/usr\", \"$fg_no_bold[green]/usr$reset_color\"); \
sub(\"/bin\", \"$fg_no_bold[blue]/bin$reset_color\"); \
sub(\"/opt\", \"$fg_no_bold[cyan]/opt$reset_color\"); \
sub(\"/sbin\", \"$fg_no_bold[magenta]/sbin$reset_color\"); \
sub(\"/local\", \"$fg_no_bold[yellow]/local$reset_color\"); \
print }"
}
# -------------------------------------------------------------------
# Mac specific functions
# -------------------------------------------------------------------
if [[ $IS_MAC -eq 1 ]]; then
# view man pages in Preview
pman() { ps=`mktemp -t manpageXXXX`.ps ; man -t $@ > "$ps" ; open "$ps" ; }
# function to show interface IP assignments
ips() { foo=`/Users/mark/bin/getip.py; /Users/mark/bin/getip.py en0; /Users/mark/bin/getip.py en1`; echo $foo; }
# notify function - http://hints.macworld.com/article.php?story=20120831112030251
notify() { automator -D title=$1 -D subtitle=$2 -D message=$3 ~/Library/Workflows/DisplayNotification.wflow }
fi
# -------------------------------------------------------------------
# nice mount (http://catonmat.net/blog/another-ten-one-liners-from-commandlingfu-explained)
# displays mounted drive information in a nicely formatted manner
# -------------------------------------------------------------------
function nicemount() { (echo "DEVICE PATH TYPE FLAGS" && mount | awk '$2="";1') | column -t ; }
# -------------------------------------------------------------------
# myIP address
# -------------------------------------------------------------------
function myip() {
ifconfig lo0 | grep 'inet ' | sed -e 's/:/ /' | awk '{print "lo0 : " $2}'
ifconfig en0 | grep 'inet ' | sed -e 's/:/ /' | awk '{print "en0 (IPv4): " $2 " " $3 " " $4 " " $5 " " $6}'
ifconfig en0 | grep 'inet6 ' | sed -e 's/ / /' | awk '{print "en0 (IPv6): " $2 " " $3 " " $4 " " $5 " " $6}'
ifconfig en1 | grep 'inet ' | sed -e 's/:/ /' | awk '{print "en1 (IPv4): " $2 " " $3 " " $4 " " $5 " " $6}'
ifconfig en1 | grep 'inet6 ' | sed -e 's/ / /' | awk '{print "en1 (IPv6): " $2 " " $3 " " $4 " " $5 " " $6}'
}
# -------------------------------------------------------------------
# (s)ave or (i)nsert a directory.
# -------------------------------------------------------------------
s() { pwd > ~/.save_dir ; }
i() { cd "$(cat ~/.save_dir)" ; }
# -------------------------------------------------------------------
# console function
# -------------------------------------------------------------------
function console () {
if [[ $# > 0 ]]; then
query=$(echo "$*"|tr -s ' ' '|')
tail -f /var/log/system.log|grep -i --color=auto -E "$query"
else
tail -f /var/log/system.log
fi
}
# -------------------------------------------------------------------
# shell function to define words
# http://vikros.tumblr.com/post/23750050330/cute-little-function-time
# -------------------------------------------------------------------
givedef() {
if [[ $# -ge 2 ]] then
echo "givedef: too many arguments" >&2
return 1
else
curl "dict://dict.org/d:$1"
fi
}The variables control how the command history is managed.
# HISTORY
HISTSIZE=10000
SAVEHIST=9000
HISTFILE=~/.zsh_historyThis file controls the use of the precmd, preexec and postcmd features. I’m not entirely sure I understand all there is to know about this. It’s on the list to investigate more thoroughly.
function precmd {
# vcs_info
# Put the string "hostname::/full/directory/path" in the title bar:
echo -ne "\e]2;$PWD\a"
# Put the parentdir/currentdir in the tab
echo -ne "\e]1;$PWD:h:t/$PWD:t\a"
}
function set_running_app {
printf "\e]1; $PWD:t:$(history $HISTCMD | cut -b7- ) \a"
}
function preexec {
set_running_app
}
function postexec {
set_running_app
}z tracks your most used directories based on ‘frequency’. By typing z and part of a directory name I can quickly jump to that part of the file system. Obviously the longer you have it installed the better it performs. I highly recommend it.
My original RVM + oh-my-zsh configuration had the path and setup for RVM contained in the .zlogin file. The new rbenv + zsh configuration has the rbenv setup in .zshenv. I need to read more on these two files and decide which ought to be used.
I need to explore the subject of bindkeys more to fully understand what they are and how I can benefit from them. I also need to learn about precmd, preexec, and postexec. There use in my setup was copied from ze-best-zsh-config. And zsh_hooks.
My testbed machine has 100 GB of free space on it’s hard drive that I am planning on using to install Linux Mint. Once that OS is setup and running I want to use my dotfiles repository, including the new zsh configuration, there. As I get things setup and working under Linux I’m sure there will be Linux-specific tweaks to add to the configuration.
In the end I’m not sure I’ve made my prompt load any faster. I certainly understand the processes and configuration behind it far better than before. I haven’t taken the step of removing all the entires from /etc/paths.d and placing them directly into my $PATH. This feels like something that would need constant minding and I’m not ready for that yet.
I’ll need to live with this setup for a while to discover things I’ve lost by leaving oh-my-zsh. Hopefully nothing to drastic or hard to configure myself. It’s been an interesting couple of weeks sorting this out. I have a greater appreciation for my shell now, and a greater understanding of what makes it tick.
{{ “B005X4AMBK” | amazon_mediumleft_image }} The First Rule is the second Joe Pike book the Robert Crais has written. While Elvis Cole plays a minor role in the story it really centers on Pike. The the Elvis Cole series, this book was quick-paced and easy to consume in a couple of sittings. After years of only seeing the still surface of Pike’s character, it is interesting to finally get to plumb his depths.
Wouldn’t it be nice to have a quick command you could run to visualize the commits on your local git repository that weren’t on the remote branch just prior to pushing them? Or perhaps to see what commits had been made remotely before pulling them down to your local copy and triggering a merge?
This brief how-to will explain just such a git log command, and how to setup the upstream tracking branch to enable the functionality required by the log command.
##First Iteration The first iteration of this command hard codes the remote branch name making it useful only if all your work is always on the same branch.
$ git log origin/master..HEAD --graph --pretty=format:'%Cred%h%Creset %d %s %Cgreen(%cr)%Creset %Cblue[%an]%Creset' --abbrev-commit --date=relative###Explanation
The ..HEAD portion of the command specifies a range for the log command to work against. In this case from current position on the master branch graph of the local repository to the HEAD position on origin copy of the master branch. By indicating the range with ..HEAD the log command will show those commits that exist on the local graph but not on the origin graph.
The rest of the command is just one way to condense and format the log command output. In this case we display an ASCII “railroad-style” graph of branchings, colorize the information displayed, abbreviate the commit message, and use relative dates.
As stated above, this command only works for branches called master. A better command would determine the name of the current branch and use it for the comparison.
##Second Iteration
git log @{u}..HEAD --graph --decorate --left-right --boundary --pretty=format:'%Cred%h%Creset %d %s %Cgreen(%cr)%Creset %Cblue[%an]%Creset' --abbrev-commit --date=relativeBy making use of the @{u} parameter the command now works regardless of the current branch. The suffix @{upstream} to a reference (short form @{u}) refers to the branch the reference is set to build on top of. A missing reference defaults to the current branch.
So this second command works for the current branch, regardless of what it is named. But it still only shows the local commit differences. Wouldn’t it be better to see both the local commits not yet on the remote and the remote commits not yet on the local?
##Third Iteration
The notation A..B means revisions in B that are not in A. Adding dot to the list, e.g., A...B means revisions in either A or B that are not in the merge bases for A and B. In other words, show the commits in either local or remote that aren’t in the other.
git log @{u}...HEAD --graph --decorate --left-right --boundary --pretty=format:'%Cred%h%Creset %d %s %Cgreen(%cr)%Creset %Cblue[%an]%Creset' --abbrev-commit --date=relativeNow with one quick command (I suggest creating a gitconfig alias) you can determine what the “commit difference” is between your local branch and it’s upstream copy.
##A Note About Upstream
In order for th @{u}..HEAD or @{u}...HEAD notation to work, your branch must have an upstream tracking branch defined. You can see what the upstream branch is by running this command, substituting the branch name as necessary:
$ git config branch.<branchname>.remoteIf the command returns nothing then you need to set the upstream branch. Run this command one time to establish an upstream tracking branch.
$ git push -u origin <branchname>The -u parameter indicates that we want to establish the origin version of as the upstream tracking branch for the local copy of . Remember that origin is just an alias that was created for a particular repository when the git remote add command was run.
##Conclusion
With an upstream tracking branch in place, using the @{u}...HEAD range specification on a git log command allows you to see the divergence (if any) your local copy has prior to running either a git pull or git push. By setting up and alias in your gitconfig file you can easily run git ahead anytime you want or need.
##References The following stackOverflow pages were instrumental in creating this how-to.
Substitute current git branch name into git log command
Git command to emit name of remote tracking branch
Difference in ‘git log origin/master’ vs ‘git log origin/master..’
When I first started exploring Sublime Text 2, I added what I thought were the necessary preference files and folders to my dotfiles repository. I’ve since come to understand that you only need to house one specific folder to effective version and share your personalized Sublime Text 2 configuration.
First make sure Sublime Text 2 is not running.
Next make a copy of ~/Library/Application Support/Sublime Text 2/Packages/User to the local copy of your dotfiles repository.
$ cp -r ~/Library/Application\ Support/Sublime\ Text\ 2/Packages/Users ~/.dotfilesSwitch to the original location of the Users directory, delete it, and then create a symlink to the copy in your dotfiles repository.
$ cd ~/Library/Application\ Support/Sublime\ Text
$ rm -rf Users
$ ln -s ~/.dotfiles/Users UsersStoring the Users directory in your dotfiles works only if you are using Package Control. Package Control keeps a list of your installed packages in a .sublime-settings file stored in User. When installing Sublime Text 2 on a new machine, you should first install Package Control and then link to your User directory in your local dotfiles repository. Package Controller will find the list of packages, see that it does not have them installed locally, and fetch them in the background (you can observe it working in your Sublime console).
Bonus Tip
If you manage to bork your Sublime Text install and want to nuke it from orbit to start over just remove your data folder. Depending on your operating system here is where the data folder can be found:
~/Library/Application Support/Sublime Text 2~/.config/sublime-text-2%APPDATA%\Sublime Text 2To return to a fresh-out-of the-box install:
This will wipe out all your preference settings and installed packages. Use as a last resort only.
{{ “0399158294” | amazon_mediumleft_image }} Shadow Patrol by Alex Berenson is the third book of the year for me. And the first I’ve read by this author. It centers around a former CIA agent on assignment in Afghanistan. Looking at the flyleaf of the book I discovered a whole series of books preceding it. This one was good enough that I expect to go back and read the ones preceding it to catch up.
This is a fascinating idea. By re-ordering the movies, and outright eliminating one, you get a more coherent and satisfying story. The next time I watch the Star Wars Saga I’ll be using machete order.
Tonight I managed to get 100% on the first quiz from the Coursera Calculus One course. As someone who was a mediocre student overall growing up, and specifically a poor mathematics student, this is huge.
In high school I barely scraped by in Algebra. The rules just never made sense to me. Geometry was different; I loved proofs and theorems. In college my major required that I take “Calculus for Business Majors”. I ended up taking the course twice in order to get a passing grade. It was Algebra all over again, only worse.
The part that stung about failing calculus was how stupid it made me feel. It seemed that no matter how hard I tried I just could not get the same answers as were in the book.
After successfully completing the Functional Programming Principles in Scala course last fall, I decided to try my hand at a new way of learning Calculus. What Coursera does is break the material down into smaller chunks. Each subtopic is captured in a short lecture video. You can watch the videos as many times as you want, pause, backup, re-watch. The material in the videos is presented in very engaging ways, and often reiterated multiple times. There are practice problems that come with step by step hints for when you get stuck. When you miss a problem the system presents you with more like it so you can learn. The quizzes can be taken (for this course) 100 times and your highest score will be used.
In short, the course is laid out in a manner that encourages you to learn, that helps you to learn. In school I often felt like I was being punished for not learning rather than being assisted when I failed. My experience this week with functions, domains, and limits in Calculus One has been refreshing and invigorating. I’m sure that there will be times of frustration in the weeks to come, but I feel like I will learn Calculus as a result of this course. Something I never thought I would be able to count among my accomplishments.
Kudos to Coursera.