# HG changeset patch # User Steve Losh # Date 1263605420 18000 # Node ID e8cb0776f67e3242aaec246b1ca18f7715ff768c # Parent 5ba1a0fdf8d54a316b0ab6b1b9765ef8b98b84c3 Massive cleanup (linebreaks, tocs, doc links, etc). diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/blog/2009/01/going-open-source.html --- a/content/blog/2009/01/going-open-source.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/blog/2009/01/going-open-source.html Fri Jan 15 20:30:20 2010 -0500 @@ -32,15 +32,14 @@ You also have the chance to post on [Twitter][], [Tumblr][], or other places about the source code of the site. Each of those posts can expose more people to the website. It's easy to overdo this though. I heartily agree with the -sentiments of -[howtousetwitterformarketingandpr.com](http://howtousetwitterformarketingandpr.com) -on that issue. +sentiments of [howtousetwitterformarketingandpr.com][htutfmapr] on that issue. Don't worry, I won't be obnoxious about it. [DjangoSites]: http://djangosites.org/ [Twitter]: http://twitter.com/ [Tumblr]: http://tumblr.com/ +[htutfmapr]: http://howtousetwitterformarketingandpr.com I Can Give Back to the (Programming) Community ---------------------------------------------- diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/blog/2009/01/site-redesign.html --- a/content/blog/2009/01/site-redesign.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/blog/2009/01/site-redesign.html Fri Jan 15 20:30:20 2010 -0500 @@ -11,9 +11,11 @@ Well, I've redesigned the site again. This time it's all me. I built the site from the ground up with [Django][] and [Python][]. Why? Several reasons: -* I already pay for hosting at [WebFaction][] for other sites so also paying [Squarespace][] for my personal one was too wasteful. +* I already pay for hosting at [WebFaction][] for other sites so also paying + [Squarespace][] for my personal one was too wasteful. * I wanted to practice designing another site with [Django][]. -* I wanted more flexibility than [Squarespace][] supports and I'm not afraid to get my hands dirty with code. +* I wanted more flexibility than [Squarespace][] supports and I'm not afraid + to get my hands dirty with code. If you want to know more about how I created the site, check out the site's [project page][]. diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/blog/2009/02/how-i-shoot-dances.html --- a/content/blog/2009/02/how-i-shoot-dances.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/blog/2009/02/how-i-shoot-dances.html Fri Jan 15 20:30:20 2010 -0500 @@ -22,6 +22,8 @@ [Gordon Webster]: http://webster.suresong.com/ [LindyBloggers]: http://lindybloggers.com/ +[TOC] + Shooting Dancers is Tough ------------------------- @@ -245,9 +247,15 @@ camera with a built-in flash, but to really have the flexibility that will make you happy you need the following things: -* **A camera with a hot shoe or PC jack and manual controls**. I use a Pentax K20D when I feel like lugging it out and a Canon G10 when I don't. -* **An external flash.** I use an $80 Vivitar 285hv which will work with anything, so no complaining about how your camera maker only sells $400 speedlights. -* **A way to sync your flash with the camera.** Some of the more expensive flashes have a wireless mode. If not, you can buy radio triggers for $60 or so, or buy a sync cable for $15 to $20. Trust me, having the flash *off* of the camera makes things so much easier. +* **A camera with a hot shoe or PC jack and manual controls**. I use a Pentax + K20D when I feel like lugging it out and a Canon G10 when I don't. +* **An external flash.** I use an $80 Vivitar 285hv which will work with + anything, so no complaining about how your camera maker only sells $400 + speedlights. +* **A way to sync your flash with the camera.** Some of the more expensive + flashes have a wireless mode. If not, you can buy radio triggers for $60 or + so, or buy a sync cable for $15 to $20. Trust me, having the flash *off* of + the camera makes things so much easier. @@ -316,10 +324,15 @@ width="181" height="240" alt="GW-0656" class="right" /> -* **If the background is dark or there is not enough blur,** you need to use a longer shutter speed to let more ambient light in. -* **If the dancers are not sharp enough,** you need *more* flash power. Turn it up a little bit at a time. -* **If people's shirts, faces and limbs are overexposed *and not blurry*,** you need to turn down the flash. Bring it down a bit by reducing the power, narrowing the aperture or reducing the ISO. -* **If shirts, faces and limbs are overexposed *and blurry*,** you need to shorten the shutter speed to let less ambient light in. +* **If the background is dark or there is not enough blur,** you need to use a + longer shutter speed to let more ambient light in. +* **If the dancers are not sharp enough,** you need *more* flash power. Turn + it up a little bit at a time. +* **If people's shirts, faces and limbs are overexposed *and not blurry*,** + you need to turn down the flash. Bring it down a bit by reducing the power, + narrowing the aperture or reducing the ISO. +* **If shirts, faces and limbs are overexposed *and blurry*,** you need to + shorten the shutter speed to let less ambient light in. I'm warning you now, fixing one of these will probably fuck something else up. It can be really infuriating, but if you slow down, stay calm and think @@ -328,8 +341,15 @@ There are two other tricky problems that I'm still working on myself: -* **Dances are unevenly lit.** When you walk over to the other side of the room to shoot, you'll have to adjust the shutter speed to compensate for the different amount of ambient light. You really only have a range of about half a stop where the ambient gives you the right amount of blur, so you have to be careful and stay on your toes. -* **Watch where you bounce.** In rooms with low ceilings, where you point the flash on the ceiling has a huge effect on the lighting. You need to be conscious of what part of the ceiling you're bouncing from to get good results. +* **Dances are unevenly lit.** When you walk over to the other side of the + room to shoot, you'll have to adjust the shutter speed to compensate for the + different amount of ambient light. You really only have a range of about + half a stop where the ambient gives you the right amount of blur, so you + have to be careful and stay on your toes. +* **Watch where you bounce.** In rooms with low ceilings, where you point the + flash on the ceiling has a huge effect on the lighting. You need to be + conscious of what part of the ceiling you're bouncing from to get good + results. Good Luck! ---------- diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/blog/2009/06/how-to-contribute-to-mercurial.html --- a/content/blog/2009/06/how-to-contribute-to-mercurial.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/blog/2009/06/how-to-contribute-to-mercurial.html Fri Jan 15 20:30:20 2010 -0500 @@ -30,9 +30,11 @@ I'm going to go ahead and assume a few (small) things: -1. You're running OS X or some form of Linux. If you're using Windows, I'm sorry, but I just don't use it enough to really help. +1. You're running OS X or some form of Linux. If you're using Windows, I'm + sorry, but I just don't use it enough to really help. 2. You've got [Python][] installed. -3. You've got a copy of Mercurial installed in some way (MacPorts, apt-get, source, etc). +3. You've got a copy of Mercurial installed in some way (MacPorts, apt-get, + source, etc). 4. You're familiar with (and have clients for) IRC and email. If those things are true, let's get started. The entire process should take @@ -268,8 +270,10 @@ This makes it simple to test your work as you go. I keep two terminal windows open while working on the Mercurial source: -* One is in my local crew repository to diff, commit, etc with the standard version of Mercurial. -* The other is in a test repository. I've run `workon hg-dev` to switch to my modified version so I can try out my changes. +* One is in my local crew repository to diff, commit, etc with the standard + version of Mercurial. +* The other is in a test repository. I've run `workon hg-dev` to switch to my + modified version so I can try out my changes. Get To Work! ------------ @@ -277,10 +281,12 @@ That's it for the initial setup. Now you can start adding all the wonderful features you have in mind. A simple workflow might go something like this: -1. Open two terminal windows and use `workon hg-dev` in the second one to switch to the virtual environment. +1. Open two terminal windows and use `workon hg-dev` in the second one to + switch to the virtual environment. 2. Make some changes to the Mercurial codebase. 3. Test your changes in the second terminal window. -4. Repeat 3 and 4, using the first terminal window to commit to the local repository as you go. +4. Repeat 3 and 4, using the first terminal window to commit to the local + repository as you go. Once you're finished with your shiny new feature, how do you get it into the main Mercurial code so we can all benefit from it? diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/blog/2009/08/a-guide-to-branching-in-mercurial.html --- a/content/blog/2009/08/a-guide-to-branching-in-mercurial.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/blog/2009/08/a-guide-to-branching-in-mercurial.html Fri Jan 15 20:30:20 2010 -0500 @@ -280,8 +280,10 @@ Using a branch name to specify a revision is shorthand for "the tip changeset of this named branch". In this example repository: -* Running `hg update default` would update to changeset 3, which is the tip of the `default` branch. -* Running `hg update feature` would update to changeset 4, which is the tip of the `feature` branch. +* Running `hg update default` would update to changeset 3, which is the tip of + the `default` branch. +* Running `hg update feature` would update to changeset 4, which is the tip of + the `feature` branch. Neither of these labels actually exist as an object anywhere on disk (like a bookmark would). When you use them Mercurial calculates the appropriate diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/blog/2009/11/my-sitesprint-project-lindyhub.html --- a/content/blog/2009/11/my-sitesprint-project-lindyhub.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/blog/2009/11/my-sitesprint-project-lindyhub.html Fri Jan 15 20:30:20 2010 -0500 @@ -64,12 +64,25 @@ websites for Lindy Hop and blues events are very basic and unpolished. Some of the things I've noticed about almost every event site: -* They have a form for you to register for the event, but the form almost never has any nice inline validation of your input. -* Every site's form is different -- you have to read every single one carefully to see exactly what they want. -* You end up entering the same information every single time for every event: name, phone number, email, etc. -* Housing is a toss up. Sometimes they have a place for it in the registration form, other times they just say "Email aaa@bbb.com if you need housing." That's a lot of emails for the housing coordinator to sort through and quite often it can become a mess. -* Some of them list addresses for each venue, which is great for those of us that have GPSes. Sometimes they just give driving directions to and from each one, which is terrible if you just want to type each one into your GPS or print off directions from Google Maps before you go. -* Only one event that I've gone to ([Blues Muse][] in Philly) has had a "Who's Coming" list so you could see who else was going to the event (you could choose whether or not to let them show your name when you registered). Sometimes you can figure this out from Facebook event pages, but that's pretty unreliable. +* They have a form for you to register for the event, but the form almost + never has any nice inline validation of your input. +* Every site's form is different -- you have to read every single one + carefully to see exactly what they want. +* You end up entering the same information every single time for every event: + name, phone number, email, etc. +* Housing is a toss up. Sometimes they have a place for it in the registration + form, other times they just say "Email aaa@bbb.com if you need housing." + That's a lot of emails for the housing coordinator to sort through and quite + often it can become a mess. +* Some of them list addresses for each venue, which is great for those of us + that have GPSes. Sometimes they just give driving directions to and from + each one, which is terrible if you just want to type each one into your GPS + or print off directions from Google Maps before you go. +* Only one event that I've gone to ([Blues Muse][] in Philly) has had a "Who's + Coming" list so you could see who else was going to the event (you could + choose whether or not to let them show your name when you registered). + Sometimes you can figure this out from Facebook event pages, but that's + pretty unreliable. There are other things, but those are the big ones. @@ -117,17 +130,30 @@ Here are some of the things the site can do *right now*: -* Users can sign up and create profiles for themselves. "Forgot my password" and such is supported. -* They can view (and search for) events that are happening in the future, and comment on them. +* Users can sign up and create profiles for themselves. "Forgot my password" + and such is supported. +* They can view (and search for) events that are happening in the future, and + comment on them. * They can create a new event and add things to its schedule. -* The schedule is formatted nicely. If an event starts before 5 AM it's grouped with the previous day's events because it's probably a late night dance. +* The schedule is formatted nicely. If an event starts before 5 AM it's + grouped with the previous day's events because it's probably a late night + dance. * Venues will add links to Google Maps if the organizers put in the address. -* Organizers can let LindyHub handle event registration for them. They specify how they want to be paid: door, check, and/or PayPal. If it's PayPal, LindyHub takes the email address that should receive the money and handles creating the PayPal "Buy Now" button. -* Early registration is supported -- organizers can specify a cutoff date and the early/late prices if they like. -* LindyHub doesn't *have* to be used for the registration. Organizers can opt to handle registration themselves and just put the event on LindyHub to help more people find it. -* Users can register for events quickly and easily. LindyHub will ask them how they want to pay and tell them how to do it. -* When they register they can allow other people to see that they're going to the event. If they do, they'll show up in the list on the event page. -* Organizers can see nicely formatted lists of registration/payment information and housing requests. +* Organizers can let LindyHub handle event registration for them. They specify + how they want to be paid: door, check, and/or PayPal. If it's PayPal, + LindyHub takes the email address that should receive the money and handles + creating the PayPal "Buy Now" button. +* Early registration is supported -- organizers can specify a cutoff date and + the early/late prices if they like. +* LindyHub doesn't *have* to be used for the registration. Organizers can opt + to handle registration themselves and just put the event on LindyHub to help + more people find it. +* Users can register for events quickly and easily. LindyHub will ask them how + they want to pay and tell them how to do it. +* When they register they can allow other people to see that they're going to + the event. If they do, they'll show up in the list on the event page. +* Organizers can see nicely formatted lists of registration/payment + information and housing requests. My Goals for the Sprint (and Beyond) ------------------------------------ @@ -135,17 +161,41 @@ Some of the things I'm planning on adding to the site in during the sprint are: -* Redesign the interface. I'm not a designer, but I can certainly do better than the spartan design I've thrown together during development. -* Better, Javascript-based form fields. They'll validate the input before you even click "Submit" (so you can correct mistakes immediately) and provide nice date/time pickers for the date and time fields. I wanted to make sure the site works 100% for people with JavaScript turned off before I started relying on it to make things pretty. +* Redesign the interface. I'm not a designer, but I can certainly do better + than the spartan design I've thrown together during development. +* Better, Javascript-based form fields. They'll validate the input before you + even click "Submit" (so you can correct mistakes immediately) and provide + nice date/time pickers for the date and time fields. I wanted to make sure + the site works 100% for people with JavaScript turned off before I started + relying on it to make things pretty. * Mobile stylesheets for iPhone users. -* Tagging events with tags like "lindyhop, blues, workshop, exchange, gordonwebster, etc." This will make it easier to find events you're interested in. This will also be easy to add. -* "Friending" other users. This would let you see what your friends are going to and would be awesome (for me, at least). -* Location-based features. LindyHub can already store your hometown and the locations of events. It wouldn't be terribly difficult to parse this information into a latitude and longitude. Once that's done it's simple to calculate the distance to events and say "Show me all events within 300 miles of my hometown" or "Show me everyone going to this event that lives within 30 miles of me because we might want to carpool." -* Automatically email reminders to people a week before the event. The emails could include links to Google Maps for the first (and other) venues to make it take only one click to print directions. There are a lot of other things that would be nice to include too -- I've been holding off on this because it really requires the attention of a copywriter to get right. -* Provide an easy-to-use interface for organizers to assign people that have requested housing to people that offer it. -* Send emails to guest/host groups automatically to make sure guests have their host's contact information (and vice versa). -* Translations into different languages. This will be pretty simple if someone wants to volunteer to translate (I only speak English and American Sign Language myself). -* Suggesting events a user might like. This is something for the far future, but it would give me a chance to play around in another favorite area of mine: artificial intelligence. I have some ideas on how to implement it but it's not critical so I'm not looking at it right now. +* Tagging events with tags like "lindyhop, blues, workshop, exchange, + gordonwebster, etc." This will make it easier to find events you're + interested in. This will also be easy to add. +* "Friending" other users. This would let you see what your friends are going + to and would be awesome (for me, at least). +* Location-based features. LindyHub can already store your hometown and the + locations of events. It wouldn't be terribly difficult to parse this + information into a latitude and longitude. Once that's done it's simple to + calculate the distance to events and say "Show me all events within 300 + miles of my hometown" or "Show me everyone going to this event that lives + within 30 miles of me because we might want to carpool." +* Automatically email reminders to people a week before the event. The emails + could include links to Google Maps for the first (and other) venues to make + it take only one click to print directions. There are a lot of other things + that would be nice to include too -- I've been holding off on this because + it really requires the attention of a copywriter to get right. +* Provide an easy-to-use interface for organizers to assign people that have + requested housing to people that offer it. +* Send emails to guest/host groups automatically to make sure guests have + their host's contact information (and vice versa). +* Translations into different languages. This will be pretty simple if someone + wants to volunteer to translate (I only speak English and American Sign + Language myself). +* Suggesting events a user might like. This is something for the far future, + but it would give me a chance to play around in another favorite area of + mine: artificial intelligence. I have some ideas on how to implement it but + it's not critical so I'm not looking at it right now. There are a whole slew of other things I have tumbling about in my head, but those should be enough to give you an idea of where the site can go. diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/blog/2010/01/moving-from-django-to-hyde.html --- a/content/blog/2010/01/moving-from-django-to-hyde.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/blog/2010/01/moving-from-django-to-hyde.html Fri Jan 15 20:30:20 2010 -0500 @@ -9,8 +9,8 @@ {% block article %} Almost exactly one year ago I posted a blog entry about how I [rewrote this -site][rewrite] using [Django][]. It's a new year, and once again I've rewritten -the site. +site][rewrite] using [Django][]. It's a new year, and once again I've +rewritten the site. If you've visited my site before you may have noticed some small style tweaks. I've cleaned up a few things and I think the site looks better overall. The @@ -43,38 +43,64 @@ ### Less Memory Needed -I use [WebFaction][] for my web hosting, and with my current plan I have a limit on how much memory I can use at any given time. Each Django site takes up around 20mb of memory on the server. +I use [WebFaction][] for my web hosting, and with my current plan I have a +limit on how much memory I can use at any given time. Each Django site takes +up around 20mb of memory on the server. -By switching to a static set of HTML pages for my site I save those 20mb so I can use them for something else (like a staging site for another project). My site doesn't particularly *need* all of the functionality Django can provide, so I figured I'd switch to a static site and save the memory. +By switching to a static set of HTML pages for my site I save those 20mb so I +can use them for something else (like a staging site for another project). My +site doesn't particularly *need* all of the functionality Django can provide, +so I figured I'd switch to a static site and save the memory. [WebFaction]: {{links.webfaction}} ### Static Pages are Faster -My site doesn't get an enormous amount of traffic, so the Django instances weren't really working very hard. Still, serving a static page through Apache or nginx is always going to be faster than running it through Django. Faster-loading pages is always a good thing, even if the speed increase isn't very large. +My site doesn't get an enormous amount of traffic, so the Django instances +weren't really working very hard. Still, serving a static page through Apache +or nginx is always going to be faster than running it through Django. +Faster-loading pages is always a good thing, even if the speed increase isn't +very large. ### Easier to Maintain -This is the main reason I switched. Previously, to edit a blog entry I would log in through Django's admin interface and edit the entry. With Hyde, I simply edit a file (or create a new one) on my machine and then run a single command to publish it. +This is the main reason I switched. Previously, to edit a blog entry I would +log in through Django's admin interface and edit the entry. With Hyde, I +simply edit a file (or create a new one) on my machine and then run a single +command to publish it. -Another problem with the old site is that I needed to be connected to the internet to get to the admin interface, so I couldn't update entries (or publish new ones) offline. I usually have an internet connection, but occasionally I don't. Now I can edit as much as I like on my own machine and only need a connection to publish the finished product. +Another problem with the old site is that I needed to be connected to the +internet to get to the admin interface, so I couldn't update entries (or +publish new ones) offline. I usually have an internet connection, but +occasionally I don't. Now I can edit as much as I like on my own machine and +only need a connection to publish the finished product. ### Easier to Backup -One more advantage of Hyde is that the site structure *and* content are all held in the same place. I keep everything in a [Mercurial][] repository, and so every time I push that repository somewhere it creates a full backup of the site's code *and* content. If WebFaction's server catches on fire I still have everything on my local machine (and on BitBucket). +One more advantage of Hyde is that the site structure *and* content are all +held in the same place. I keep everything in a [Mercurial][] repository, and +so every time I push that repository somewhere it creates a full backup of the +site's code *and* content. If WebFaction's server catches on fire I still have +everything on my local machine (and on BitBucket). -I've toyed around with backing up Django's database tables when I had my old site, but this new method is far less work. +I've toyed around with backing up Django's database tables when I had my old +site, but this new method is far less work. [Mercurial]: {{links.mercurial}} Getting Started --------------- -I wanted a fresh start when I was rewriting the site, so I went ahead and created a brand new folder and Mercurial repository for it. +I wanted a fresh start when I was rewriting the site, so I went ahead and +created a brand new folder and Mercurial repository for it. -I used `hyde --init` to lay out a skeleton in the new folder. Then I stripped out a lot of the default items that get added with `--init` and created the directory structure I wanted to use, with stubs for each of the main content files I knew I'd need. +I used `hyde --init` to lay out a skeleton in the new folder. Then I stripped +out a lot of the default items that get added with `--init` and created the +directory structure I wanted to use, with stubs for each of the main content +files I knew I'd need. -Finally, I went ahead and filled some of the basic values in the `settings.py` file. +Finally, I went ahead and filled some of the basic values in the `settings.py` +file. Layout and Styling ------------------ @@ -83,19 +109,34 @@ ### Cleanup -The templates created by `hyde --init` are functional, but when you look at the code they're a mess. The indentation is strange and inconsistent and there's trailing whitespace all over the place. I like clean code so I sat down and cleaned everything up before I started making any real changes. +The templates created by `hyde --init` are functional, but when you look at +the code they're a mess. The indentation is strange and inconsistent and +there's trailing whitespace all over the place. I like clean code so I sat +down and cleaned everything up before I started making any real changes. ### Rewriting the Layout and Styles -After I finished cleaning up the templates I duplicated the HTML structure and styles of the old site from scratch. The old site had gone through a bunch of iterations and I was a bit sloppy in my editing, so there was a lot of cruft that had snuck in. I wanted a truly *fresh* start for the site, so I buckled down and did it all again. +After I finished cleaning up the templates I duplicated the HTML structure and +styles of the old site from scratch. The old site had gone through a bunch of +iterations and I was a bit sloppy in my editing, so there was a lot of cruft +that had snuck in. I wanted a truly *fresh* start for the site, so I buckled +down and did it all again. ### A New CSS Framework -Previously I used the [Blueprint CSS framework][blueprint] to make laying out the site easier. Blueprint is a great framework, but it's more powerful than I need for a site as simple as this one. The site now uses [Aardvark Legs][aal], which is a much simpler framework that simply sets up a great vertical rhythm and leaves you free to lay out the horizontal structure yourself. +Previously I used the [Blueprint CSS framework][blueprint] to make laying out +the site easier. Blueprint is a great framework, but it's more powerful than I +need for a site as simple as this one. The site now uses [Aardvark Legs][aal], +which is a much simpler framework that simply sets up a great vertical rhythm +and leaves you free to lay out the horizontal structure yourself. ### Pagination -Before the rewrite the list of blog entries used to be paginated. Hyde supports pagination but I decided against using it because I simply don't write enough to make it necessary. All the blog entries are now listed on a single page, which means that you can use Cmd+F to find an article if you're looking for something specific. +Before the rewrite the list of blog entries used to be paginated. Hyde +supports pagination but I decided against using it because I simply don't +write enough to make it necessary. All the blog entries are now listed on a +single page, which means that you can use Cmd+F to find an article if you're +looking for something specific. [blueprint]: http://www.blueprintcss.org/ [aal]: {{links.aarkvarklegs}} @@ -103,11 +144,16 @@ Using Fabric to Type Less ------------------------- -I realized very quickly that typing `hyde -g -s . && hyde -w -s .` would get old pretty quickly, so I installed [Fabric][] and wrote a fabfile. +I realized very quickly that typing `hyde -g -s . && hyde -w -s .` would get +old pretty quickly, so I installed [Fabric][] and wrote a fabfile. -Fabric is a tool written in Python that lets you define tasks and execute them by running `fab taskname`. Fabfiles are pure Python, so you can build larger tasks out of smaller ones very easily and do just about anything you want. It's similar to [ant][], but without the excessive over-engineering. +Fabric is a tool written in Python that lets you define tasks and execute them +by running `fab taskname`. Fabfiles are pure Python, so you can build larger +tasks out of smaller ones very easily and do just about anything you want. +It's similar to [ant][], but without the excessive over-engineering. -You can view the [current fabfile][fabfile] on BitBucket. At the time of this entry it looks like this: +You can view the [current fabfile][fabfile] on BitBucket. At the time of this +entry it looks like this: :::python from fabric.api import * @@ -145,11 +191,14 @@ delete=True ) -The task I use most often while developing is `fab reserve`, which regenerates the site and then starts serving it so I can view the result in a browser. +The task I use most often while developing is `fab reserve`, which regenerates +the site and then starts serving it so I can view the result in a browser. -I use `fab smush` whenever I add new images. This runs [smusher][] on all of the images to optimize them without reducing quality. +I use `fab smush` whenever I add new images. This runs [smusher][] on all of +the images to optimize them without reducing quality. -When I'm ready to publish changes to the live site I run `fab publish`, which will regenerate my local version and copy it up to the WebFaction server. +When I'm ready to publish changes to the live site I run `fab publish`, which +will regenerate my local version and copy it up to the WebFaction server. [Fabric]: {{links.fabric}} [ant]: http://ant.apache.org/ @@ -159,9 +208,15 @@ Moving the Content ------------------ -The content of the old site (blog entries, projects, and static pages like the [about page][]) was fairly easy to migrate over to the new one, because both version use [Markdown][] to format the text. +The content of the old site (blog entries, projects, and static pages like the +[about page][]) was fairly easy to migrate over to the new one, because both +version use [Markdown][] to format the text. -First I created an empty file for each page with a filename that matched the "slug" (last part of the URL) of the old page. Then I manually copied over the title, creation time and content for every page. I could have written a script to do this, but I don't have enough pages on the site to make it worth the time. +First I created an empty file for each page with a filename that matched the +"slug" (last part of the URL) of the old page. Then I manually copied over the +title, creation time and content for every page. I could have written a script +to do this, but I don't have enough pages on the site to make it worth the +time. [about page]: /about/ [Markdown]: {{links.markdown}} @@ -169,15 +224,29 @@ Converting the Comments ----------------------- -At this point the new site was looking very much like the old one. The styles were (roughly) matching and the blog posts, entries, and static pages were all rendered nicely. +At this point the new site was looking very much like the old one. The styles +were (roughly) matching and the blog posts, entries, and static pages were all +rendered nicely. -The next big task was migrating all of the comments. Since the new site is static it can't handle dynamic content like comments on its own, so I decided to use [Disqus][]. I use Disqus for the comments on the [hg tip][] site and it's very nice. For that site I used it from the beginning, but for this rewrite I needed to somehow import all the old comments. +The next big task was migrating all of the comments. Since the new site is +static it can't handle dynamic content like comments on its own, so I decided +to use [Disqus][]. I use Disqus for the comments on the [hg tip][] site and +it's very nice. For that site I used it from the beginning, but for this +rewrite I needed to somehow import all the old comments. -To migrate the comments over I used [django-disqus][], but there was a small snag that I needed to deal with first. +To migrate the comments over I used [django-disqus][], but there was a small +snag that I needed to deal with first. -When I first wrote the old site I was just getting back into Django after not using it for a long time. I didn't know about Django's built-in comment models, so I created my own. They weren't as good as Django's, but they did the job and I didn't care enough to change them. +When I first wrote the old site I was just getting back into Django after not +using it for a long time. I didn't know about Django's built-in comment +models, so I created my own. They weren't as good as Django's, but they did +the job and I didn't care enough to change them. -This became a problem when it was time to import the old comments into Disqus. django-disqus only supports importing comments from Django's built-in comment models. To work around this I first had to convert the old comments into Django's built-in ones. I wrote a [small, hacky Python script][convert-comments] to do it: +This became a problem when it was time to import the old comments into Disqus. +django-disqus only supports importing comments from Django's built-in comment +models. To work around this I first had to convert the old comments into +Django's built-in ones. I wrote a [small, hacky Python +script][convert-comments] to do it: :::python #!/usr/bin/env python @@ -223,9 +292,13 @@ c.save() # print 'http://%s%s' % (site.domain, c.content_object.get_absolute_url()) -Yes, it's ugly. No, I don't care. It was run once or twice locally and once on the live server. It worked and I'll never need to run it again. +Yes, it's ugly. No, I don't care. It was run once or twice locally and once on +the live server. It worked and I'll never need to run it again. -Once I had the comments converted I could use django-disqus to migrate them. The import went very smoothly -- I ran one command and after a couple of minutes everything was in Disqus. Once that finished it was just a matter of adding the Disqus JavaScript to one of the templates. +Once I had the comments converted I could use django-disqus to migrate them. +The import went very smoothly -- I ran one command and after a couple of +minutes everything was in Disqus. Once that finished it was just a matter of +adding the Disqus JavaScript to one of the templates. [Disqus]: http://disqus.com/ [hg tip]: {{links.hgtip}} @@ -235,19 +308,27 @@ Rewriting the Old URLs ---------------------- -Since I was pretty much starting from scratch with this rewrite I decided to clean up the URL structure of my blog. Previously a blog entry's URL looked like this: +Since I was pretty much starting from scratch with this rewrite I decided to +clean up the URL structure of my blog. Previously a blog entry's URL looked +like this: :::text http://stevelosh.com/blog/entry/2009/08/30/a-guide-to-branching-in-mercurial/ -The `/entry/` part of the URL was useless -- it just took up space, so I got rid of it. The year and month are useful to get an idea of how old an entry is just by looking at the link, so I left them in. The day, however, probably doesn't matter that much, so I took it out. +The `/entry/` part of the URL was useless -- it just took up space, so I got +rid of it. The year and month are useful to get an idea of how old an entry is +just by looking at the link, so I left them in. The day, however, probably +doesn't matter that much, so I took it out. The new URLs look like this: :::text http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-mercurial/ -The problem with rewriting the URL structure is that there are already links around the web pointing at my entries. I didn't want those old links to break, so I crafted an [`.htaccess` file][htaccess] that would rewrite the old URLs into the new ones: +The problem with rewriting the URL structure is that there are already links +around the web pointing at my entries. I didn't want those old links to break, +so I crafted an [`.htaccess` file][htaccess] that would rewrite the old URLs +into the new ones: {% templatetag openblock %} if GENERATE_CLEAN_URLS {% templatetag closeblock %} RewriteEngine on @@ -269,68 +350,99 @@ {% templatetag openblock %} endif {% templatetag closeblock %} -Most of that file is the stock Hyde `.htaccess` file -- the two lines under `# Old URLs` are the ones that I added. It took me a while to get it right because I don't work with Apache's `mod_rewrite` very often, but it was worth it to avoid breaking all of the old links. +Most of that file is the stock Hyde `.htaccess` file -- the two lines under `# +Old URLs` are the ones that I added. It took me a while to get it right +because I don't work with Apache's `mod_rewrite` very often, but it was worth +it to avoid breaking all of the old links. [htaccess]: http://bitbucket.org/sjl/stevelosh/src/tip/content/.htaccess Creating an RSS Feed -------------------- -I had an RSS feed for the old site which Django made very easy to set up. I definitely needed a feed for the new site, and fortunately Hyde provides a simple sample template that can be used to make an ATOM feed. +I had an RSS feed for the old site which Django made very easy to set up. I +definitely needed a feed for the new site, and fortunately Hyde provides a +simple sample template that can be used to make an ATOM feed. -I cleaned up the whitespace and formatting of that template a bit, adjusted a few variables for my needs, put it on [FeedBurner][] and everything was ready. +I cleaned up the whitespace and formatting of that template a bit, adjusted a +few variables for my needs, put it on [FeedBurner][] and everything was ready. [FeedBurner]: http://www.feedburner.com/ Merging the Repositories ------------------------ -The last step before I was finished was to merge the old and new repositories together so I wouldn't lose any of the history. It's probably not *too* important to keep the old site's history around, but I put a lot of work into it over the past year and it has some sentimental value. +The last step before I was finished was to merge the old and new repositories +together so I wouldn't lose any of the history. It's probably not *too* +important to keep the old site's history around, but I put a lot of work into +it over the past year and it has some sentimental value. -Fortunately it's very easy to [combine Mercurial repositories][combinerepos]. I just pulled the old repository into the new one, merged the old head into the new one while discarding all the changes, and pushed it up to BitBucket. +Fortunately it's very easy to [combine Mercurial repositories][combinerepos]. +I just pulled the old repository into the new one, merged the old head into +the new one while discarding all the changes, and pushed it up to BitBucket. [combinerepos]: http://hgtip.com/tips/advanced/2009-11-17-combining-repositories/ Still to Come ------------- -At this point I felt the site was ready to be released, so I set up a new site on WebFaction and used Fabric to deploy it. +At this point I felt the site was ready to be released, so I set up a new site +on WebFaction and used Fabric to deploy it. -I'm very happy with the result, but there are still a few things I'm going to fix/change in the future. +I'm very happy with the result, but there are still a few things I'm going to +fix/change in the future. ### "Ago" Dates -If you look at the top of each blog entry and project there's a line beneath the title that looks something like: +If you look at the top of each blog entry and project there's a line beneath +the title that looks something like: :::text Posted on Monday, November 16, 2009 (1 month, 3 weeks ago). -The `(1 month, 3 weeks ago)` part of that line is something that I really appreciate on blogs. When they just list the date I always have to do the math in my head to figure out roughly how old something is. - -With a static site, however, those times will quickly become inaccurate if I don't regenerate and publish the site regularly. I'm still thinking about the best way to work this out. +The `(1 month, 3 weeks ago)` part of that line is something that I really +appreciate on blogs. When they just list the date I always have to do the math +in my head to figure out roughly how old something is. -One option is to use a cron job on the WebFaction server to regenerate the site every day, which would keep the times *fairly* accurate. +With a static site, however, those times will quickly become inaccurate if I +don't regenerate and publish the site regularly. I'm still thinking about the +best way to work this out. -Another option would be to use a bit of JavaScript to calculate and render the time when the page is loaded. This would make it *completely* accurate but wouldn't work if someone is browsing with JavaScript turned off. +One option is to use a cron job on the WebFaction server to regenerate the +site every day, which would keep the times *fairly* accurate. + +Another option would be to use a bit of JavaScript to calculate and render the +time when the page is loaded. This would make it *completely* accurate but +wouldn't work if someone is browsing with JavaScript turned off. ### Categories -Right now all the blog entries (and projects) are listed in a single chronological list. It would be great to break them up into categories so people can easily find the articles they're interested in. +Right now all the blog entries (and projects) are listed in a single +chronological list. It would be great to break them up into categories so +people can easily find the articles they're interested in. -Hyde supports categories but I haven't spent the time to learn how to use them yet. I also need to figure out a way to work a list of categories into the design without cluttering things up too much. +Hyde supports categories but I haven't spent the time to learn how to use them +yet. I also need to figure out a way to work a list of categories into the +design without cluttering things up too much. ### Use LessCSS -[LessCSS][] is a language that extends CSS with some useful features like variables, mixins, operations and nested rules. It can make the styles of a site much, much cleaner. +[LessCSS][] is a language that extends CSS with some useful features like +variables, mixins, operations and nested rules. It can make the styles of a +site much, much cleaner. -Hyde includes a LessCSS "processor" that will automatically render your LessCSS files into normal CSS. I'm planning on rewriting the site's styles in LessCSS and using the processor once I get some free time. +Hyde includes a LessCSS "processor" that will automatically render your +LessCSS files into normal CSS. I'm planning on rewriting the site's styles in +LessCSS and using the processor once I get some free time. [lesscss]: {{links.lesscss}} View the Code ------------- -The code is on [BitBucket][bb-repo] and [GitHub][gh-repo]. Feel free to poke around and see how I've set things up. If you have questions or suggestions I'd love to hear them! +The code is on [BitBucket][bb-repo] and [GitHub][gh-repo]. Feel free to poke +around and see how I've set things up. If you have questions or suggestions +I'd love to hear them! [bb-repo]: http://bitbucket.org/sjl/stevelosh/ [gh-repo]: http://github.com/sjl/stevelosh/ diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/projects/django-hoptoad.html --- a/content/projects/django-hoptoad.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/projects/django-hoptoad.html Fri Jan 15 20:30:20 2010 -0500 @@ -14,208 +14,13 @@ You can get it from [the repository on BitBucket][repo]. +Check out the [documentation][docs] to find out how to use it. + [Django]: {{links.django}} [Hoptoad]: http://hoptoadapp.com/ [ponies]: http://djangopony.com/ [repo]: http://bitbucket.org/sjl/django-hoptoad/ - -[TOC] - -Requirements ------------- - -django-hoptoad requires: - -* [Python][] 2.5+ (preferably 2.6+ as that's what I've tested it with) -* [PyYAML][] (`pip install pyyaml` or `easy_install pyyaml`) -* [Django][] 1.0+ -* A [Hoptoad][] account - -[Python]: {{links.python}} -[PyYAML]: http://pyyaml.org/ - -Installation ------------- - -Grab the the django-hoptoad code by cloning the [Mercurial][] repository (or -just [download the latest version][tip-dl] and unzip it somewhere): - - :::text - hg clone http://bitbucket.org/sjl/django-hoptoad/ - -There's a git mirror too if you *really* want it. - - :::text - git clone git://github.com/sjl/django-hoptoad.git - -Once you download it, you can install it in the usual manner: - - :::text - cd django-hoptoad - python setup.py install - -If you'd prefer to be able to update at any time by pulling down changes with -Mercurial or git, you can symlink the module into your `site-packages` -directory instead of using `python setup.py install`: - - :::text - ln -s /full/path/to/django-hoptoad/hoptoad /full/path/to/site-packages/ - -To make sure it works you can run: - - :::text - python -c 'import hoptoad' - -[Mercurial]: {{links.mercurial}} -[tip-dl]: http://bitbucket.org/sjl/django-hoptoad/get/tip.zip - -Usage ------ - -To set up a Django project to notify Hoptoad of its errors, you need to do two -things in the `settings.py` file. - -First, add the `HoptoadNotifierMiddleware` as the last item in your -`MIDDLEWARE_CLASSES` setting: - - :::python - MIDDLEWARE_CLASSES = ( - # ... other middleware classes ... - 'hoptoad.middleware.HoptoadNotifierMiddleware', - ) - -Next, you'll need to add a `HOPTOAD_API_KEY` setting. You can get the key from -the Hoptoad project page. - - :::python - HOPTOAD_API_KEY = 'Your Hoptoad API key.' - -Optional Settings ------------------ - -There are a few extra things you can configure if you'd like to tweak the -notification process a bit. - -### Notify Hoptoad While in DEBUG Mode - -By default the Middleware will **not** report errors to Hoptoad when your -project is in `DEBUG` mode. The idea behind this is that if you can already -see the error information right in the browser you probably don't need to see -it in Hoptoad too. If you want to always notify Hoptoad of errors, even while -in `DEBUG` mode, add the following setting: - - :::python - HOPTOAD_NOTIFY_WHILE_DEBUG = True - -### Specify a Default Timeout - -The notification middleware will make sure the error notification gets to -Hoptoad before the response gets sent back to the user. If it takes a while to -contact Hoptoad the user will notice a delay. - -By default, the amount of time the notifier will wait before giving up is -Python's "global default timeout setting". I have no idea what that is because -the [documentation][urllib2docs] does not see fit to explain that to me. - -If you'd like to change that amount you can use the `HOPTOAD_TIMEOUT` setting. -You **must** be running Python 2.6+ to use this. - - :::python - HOPTOAD_TIMEOUT = 5 - -The number is the number of seconds the notifier will wait before timing out. -Yes, you can use a float like `0.5` to specify fractions of a second. - -[urllib2docs]: http://docs.python.org/library/urllib2.html - -### Track 404 Errors - -By default Hoptoad will **not** be notified of 404 (page not found) errors. If -you'd like to change this you'll need to add the following setting: - - :::python - HOPTOAD_NOTIFY_404 = True - -**IMPORTANT**: If you are using Django's `flatpages` app and want to track 404 -errors, you need to make sure the `FlatpageFallbackMiddleware` comes *after* -the `HoptoadNotifierMiddleware`. If you don't do this Hoptoad will be notified -of 404 errors even if the user actually sees a Flatpage. - -To track 404s while using the `flatpages` app your `MIDDLEWARE_CLASSES` -setting should look like this: - - :::python - MIDDLEWARE_CLASSES = ( - # ... other middleware classes ... - 'hoptoad.middleware.HoptoadNotifierMiddleware', - 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', - ) - -A couple of things to note: - -* If your website doesn't have a favicon specified most browsers will request it each time. This will result in one (or possibly two, if it tries to append a slash) 404 errors for every page view. -* At the moment all 404 errors are grouped together as "similar" errors in Hoptoad. I am trying to figure out what causes this. - -### Track 403 Errors - -By default Hoptoad will **not** be notified of 403 (forbidden) errors. If -you'd like to change this you'll need to add the following setting: - - :::python - HOPTOAD_NOTIFY_403 = True - -Note: - -* At the moment all 403 errors are grouped together as "similar" errors in - Hoptoad. I am trying to figure out what causes this. - -### Ignore Specific User Agents - -If you'd like to ignore all errors from certain User Agents you can use the -following setting: - - :::python - HOPTOAD_IGNORE_AGENTS = ['MSIE 6.0', 'Trident'] - -If any of the strings in the list appear *anywhere* in the User Agent string, -Hoptoad will not be notified of the error. - -The strings are actually regular expressions, so you can be more specific if -you like: - - :::python - HOPTOAD_IGNORE_AGENTS = [r'^Mozilla.*compatible; MSIE \d+\.\d+.*$'] - -One thing this is useful for (aside from hating on IE) is ignoring errors from -web crawlers. Often bots will mangle URLs and if you're tracking 404 errors -you'll see a *lot* of errors that you probably don't care about. - -This would probably be a good starting point for ignoring crawlers: - - :::python - HOPTOAD_IGNORE_AGENTS = ['Googlebot', 'Yahoo! Slurp', 'YahooSeeker'] - -Troubleshooting ---------------- - -If things don't go smoothly, the first thing to do is run the tests to see if -they can determine what's wrong. To enable the tests you'll need to add -django-hoptoad to your `INSTALLED_APPS` setting: - - :::python - INSTALLED_APPS = ( - # ... other apps ... - 'hoptoad', - # ... other apps ... - ) - -Once you've done that you can run the unit tests: - - :::text - python manage.py test hoptoad - -**NOTE**: The unit tests are very simple at the moment. I'm working on more, -but please feel free to submit ideas (or better yet: patches). +[docs]: http://sjl.bitbucket.org/django-hoptoad/ Suggestions ----------- diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/projects/grabtweets.html --- a/content/projects/grabtweets.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/projects/grabtweets.html Fri Jan 15 20:30:20 2010 -0500 @@ -42,7 +42,8 @@ :::console $ grabtweets.py -u USERNAME FILE -`grabtweets` will pull down the 200 newest tweets from USERNAME and store them in FILE. +`grabtweets` will pull down the 200 newest tweets from USERNAME and store them +in FILE. You probably have more than 200 tweets. However, Twitter will only let you pull down 200 at a time and hitting the server too fast will result in Twitter diff -r 5ba1a0fdf8d5 -r e8cb0776f67e content/projects/hg-prompt.html --- a/content/projects/hg-prompt.html Fri Jan 15 20:15:45 2010 -0500 +++ b/content/projects/hg-prompt.html Fri Jan 15 20:30:20 2010 -0500 @@ -12,152 +12,13 @@ information. It's designed to be used in a shell prompt. You can grab the code from [the repository on BitBucket](http://bitbucket.org/sjl/hg-prompt/). +Check out the [documentation][docs] to find out how to use it. + Here's what it looks like: ![My bash prompt while using hg-prompt.](/media/images/projects/{{ page.page_name }}/prompt.png "My bash prompt while using hg-prompt.") -[TOC] - -Requirements ------------- - -hg-prompt requires Python 2.5+ and (obviously) Mercurial. - -Installing ----------- - -Clone the repository: - - :::text - $ hg clone http://bitbucket.org/sjl/hg-prompt/ - -Edit the `[extensions]` section in your `~/.hgrc` file: - - :::ini - [extensions] - prompt = (path to)/hg-prompt/prompt.py - -Using the Command ------------------ - -The `hg prompt` command takes a single string as an argument and outputs it. -Here's a simple (and useless) example: - - :::text - $ hg prompt "test" - test - -Keywords in curly braces can be used to output repository information: - - :::text - $ hg prompt "currently on {branch}" - currently on default - -Keywords also have an extended form: - - :::text - {optional text{branch}more optional text} - -This form will output the text and the expanded keyword **only** if the -keyword successfully expands. This can be useful for displaying extra text -only if it's applicable: - - :::text - $ hg prompt "currently on {branch} and at {bookmark}" - currently on branch default and at - - $ hg prompt "currently on {branch} {and at {bookmark}}" - currently on branch default - - $ hg bookmark my-book - - $ hg prompt "currently on {branch} {and at {bookmark}}" - currently on branch default and at my-book - -Available Keywords ------------------- - -There a number of keywords available. If you have any suggestions for more -please let me know. - -* **bookmark:** the current bookmark (requires the [bookmarks][] extension) -* **branch:** the current branch -* **node:** the (full) changeset hash of the current parent -* **node|short:** a short form of the changeset hash of the current parent -* **node|merge:** the (full) changeset hash of the changeset you're merging with if you're currently merging, otherwise nothing. -* **node|merge|short:** a short form of the changeset hash of the changeset you're merging with if you're currently merging, otherwise nothing -* **patch:** the topmost currently-applied patch (requires the mq extension) -* **patch|count:** the number of patches in the queue -* **patch|applied:** the number of currently applied patches -* **patch|unapplied:** the number of unapplied patches in the queue -* **rev:** the repository-local changeset number of the current parent -* **rev|merge:** the repository-local changeset number of the changeset you're merging with if you're currently merging, otherwise nothing -* **root:** the full path to the root of the current repository, without a trailing slash -* **root|basename:** the directory name of the root of the current repository. For example, if the repository is in `/home/u/myrepo` then this keyword would expand to `myrepo`. -* **status:** `!` if the repository has any changed/added/removed files, otherwise `?` if it has any untracked (but not ignored) files, otherwise nothing -* **status|modified:** `!` if the current repository contains files that have been modified, added, removed, or deleted, otherwise nothing -* **status|unknown:** `?` if the current repository contains untracked files, otherwise nothing -* **status|modified|unknown:** `!` if the current repository contains files that have been modified, added, removed, or deleted, *and* `?` if it contains untracked (and not ignored) files, otherwise nothing -* **tags:** the tags of the current parent, separated by a space -* **tags|SEP:** the tags of the current parent, separated by `SEP` -* **task:** the current task (requires the [tasks][] extension) -* **tip:** the repository-local changeset number of the current tip -* **tip|node:** the (full) changeset hash of the current tip -* **tip|node|short:** a short form of the changeset hash of the current tip -* **update:** `^` if the current parent is not the tip of the current branch, otherwise nothing. In effect, this lets you see if running `hg update` would do something. - -[bookmarks]: http://mercurial.selenic.com/wiki/BookmarksExtension -[tasks]: http://bitbucket.org/alu/hgtasks/wiki/Home - -Remote Status Keywords ----------------------- - -There are several keywords available to monitor the status of remote -repositories. Because this can be an expensive operation if the remote -repository is across a network, they cache their results in -`.hg/prompt/cache/`. The cache is updated roughly every fifteen minutes. - -* **incoming:** this keyword prints nothing on its own. If the default path contains incoming changesets the extra text will be expanded. For example: `{incoming changes{incoming}}` will expand to `incoming changes` if there are changes, or nothing otherwise. -* **incoming|count:** the number of incoming changesets if greater than 0 -* **outgoing:** this keyword prints nothing on its own. If the current repository contains outgoing changesets (to default) the extra text will be expanded. For example: `{outgoing changes{outgoing}}` will expand to `outgoing changes` if there are changes, or nothing otherwise. -* **outgoing|count:** the number of outgoing changesets if greater than 0 - -Putting it in a Bash Prompt ---------------------------- - -To put it in your bash prompt, edit your `~/.bashrc` file to include something -like this: - - :::bash - hg_ps1() { - hg prompt "{ on {branch}}{ at {bookmark}}{status}" 2> /dev/null - } - - export PS1='\u at \h in \w$(hg_ps1)\n$ ' - -`source ~/.bashrc` after to test it out. Make sure you're in a Mercurial -repository or you won't see anything. This little prompt will give you -something like this: - - :::text - steve at myhost in ~/src/hg-prompt on default at feature-bookmark? - $ - -How about something a little more interesting? - - :::bash - hg_ps1() { - hg prompt "{[+{incoming|count}]-->}{root|basename}{/{branch}}{-->[+{outgoing|count}]}{ at {bookmark}}{status}" 2> /dev/null - } - - export PS1='$(hg_ps1)\n\u at \h in \w\n$ ' - -And the result (this example assumes one incoming changeset and two outgoing): - - :::text - [+1]-->hg-prompt/default-->[+2] at feature-bookmark - steve at myhost in ~/src/hg-prompt - $ +[docs]: http://sjl.bitbucket.org/hg-prompt/ Questions, Comments, Suggestions --------------------------------