Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Tools
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Daniel Finnie

Coding while deploying with Git Deployment Directories

Daniel Finnie
Saturday, June 15, 2013

Two tenets of Pivotal are continuous integration and coding if you’re in front of a computer.  These goals can conflict when you’ve just wrapped up a story and are about to push to a staging environment – while your final tests and deploy are running, you can’t modify your Git working tree.  So you grab some coffee or check your email.

In the spirit of eliminating breaks enforced by devops, Dirk Kelly and Mik Freedman paired on a great way to run deploys in the background.  In essence, you create a separate Git repository on your development machine, we’ll call it deployer, that has your original Git code repository as a remote.  Your deploy scripts then run in background from the deployer directory while you happily code in your original directory.

If your deployment process is anything more than a push to GitHub, this saves a good amount of time.  If your project involves more than 1 pair, continuous integration and automatic deployment of all Git branches can alleviate the pain of long deploys.
Continue reading →

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mark Rushakoff

git rebase vs. git merge: an agile perspective

Mark Rushakoff
Friday, May 17, 2013

At Pivotal Labs, we’ve been using Quandora for about 6 months as an easier way to archive and discover discussions about the hows and whys of consulting and software engineering here. Earlier this week, I asked my colleagues:

There are some git workflows that would have you regularly work in feature branches and then merge back into master only when the feature is ready for acceptance. However, on every project I’ve worked on at Pivotal, we have preferred to rebase and commit to master regularly.

Why prefer rebasing over merging?

I received some excellent answers.

Rasheed explains:

The more code diverges, the more difficult it is to integrate. If you want continuous integration, it’s a lot easier to do so on one branch, not many. It also drives out stories that are small, have the smallest actionable work, and are easy to accept. This leads to tight feedback loops.

Chad had some information to add to Rasheed’s answer:

Tight feedback loops are one of the things that Pivotal values.

We place priority on CI and small stories. This makes it easier to work on master – it’s not a big deal if it is accidentally broken, it’ll be easily fixed or reverted.

In my experience, we still do use topic branches when appropriate. E.g., in the middle of a story at the end of a day, or for a bigger feature that you don’t want to do in one commit, but would break mainline (master) with the intermediate commit. On my project, in this case, we usually merge –squash –no-commit onto master, to squash multiple commits on the topic branch into one commit on master.

Even in git, branching is still painful. The longer a topic branch lives, the harder it is to merge. Yes, you can rebase often, but that means you have to rewrite history to push the rebased changes to the server. This can cause confusion if the branch lives long and is worked on by multiple pairs or on multiple machines. So, it’s usually less net effort to just work on master, because we have good tests and can trust CI to quickly tell us if there’s any glaring logical merge conflicts that were missed. On teams without CI that rely solely on manual QA, this is much more of a risk and more expensive.

And Jacob, the director of our new Boston office, had this to say:

I’ve seen both patterns at Pivotal. Teams that rebase tend to be small and haven’t had a production release. Large teams have a quickly changing repo and feature branches make the history more readable. And teams that have released use feature branches to relegate incomplete code to a future release. They can also (in theory if not practice) easily roll-back an entire feature branch if something goes wrong in production.

Of course, as Rasheed mentions, trying to wrangle large unreleased changes causes all kinds of problems. Better to get faster feedback with smaller releasable features. If your team still feels it needs to have incomplete features in master, or needs to disable misbehaving pieces of the app, read up about feature switches and look into a tool like rollout or flipper.

So, it seems that in general, we tend to prefer rebasing because it helps facilitate some core concepts of agile software development:

  • Continuous deployment
  • Tight feedback loops
  • Small deliverables

Thanks to Rasheed, Chad, and Jacob for helping provide some great content!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
George Dean

A Stately Resque

George Dean
Thursday, February 7, 2013

Helps

ActiveRecord save race condition in Resque

We have a Rails app that is saving a new ActiveRecord object, and then immediately afterwards, enqueueing a Resque job that looks up that record by its ID. Sometimes, the lookup inside the resque job fails – it claims no such row with that ID exists.

If we put the lookup into a loop that catches that exception and retries the exact same lookup, it always eventually succeeds, usually after 1-2 seconds.

The ID exists on the ActiveRecord object before the job is enqueued, which (I think) means that the save transaction has completed. But the resque job is using a different database connection, so maybe there's some weird caching going on? But here Google has failed us.

Any ideas? We could leave it in a loop that catches the exception, sleeps 1 second, and retries, but: ugh.

Some ideas from the audience:
There is a connection object that can be queried about transaction states.
Wait for transaction count increment/decrement?
Could be a problem with the way that sqlite fakes nested transactions.

Best practices for individual users on GitHub

Usually we have a single GitHub account for a project that contains a key for each of the machines being used on a project. On our project we want to use a separate account for each person on the project. Is there a way to do this without a lot of trouble shuffling SSH keys around?

Pivotal Git Scripts may have some tools for this.
You can also use HTTPS URLs and enter username/password on each commit.

Interestings

Lobot – Now with per-project chef recipes

If you need to write your own chef recipes to install your project's dependencies, you can add a cookbooks directory to the root of your project. Make sure to delete the cookbook_paths section from your lobot.yml (to use the default values), or add ./chef/project-cookbooks to the cookbook_paths section.

So, to have a bacon recipe, you should have cookbooks/pork/recipes/bacon.rb file in your repository.

Stately – A font of the US states

http://intridea.github.com/stately/

For all those times you need a vector representation of the USA and to color the states separately.

Apiary – Public API documentation

http://apiary.io/

Takes a well formed file and generates really usable REST API documentation. Integrates with Github for collaboration

Events

Thursday: Inaugural Thursday Night Pair Exchange tonight

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ian Zabel

Level up your development workflow with GitHub & Pivotal Tracker

Ian Zabel
Saturday, November 3, 2012

I’ve been working with my client, Unpakt, for a while now. One of their core values is making people’s lives easier. They’re specifically focused on making it easy for people moving to a new home or office to find a mover, compare prices and book their move online.

As a development team, we’ve taken that same core value of making things easier and applied it to our software development & deployment workflow. Over time, we’ve progressively improved our process. We’re now at the point where we’re happy with it, and I wanted to share what we’ve been up to.

The Typical Flow

Let’s assume I’m using GitHub and Pivotal Tracker and I’ve just completed a story. What’s next? Push my changes up to GitHub, click Finish on my story, wait for the build to go green, deploy to staging. Then, click Deliver on all the stories that have just been deployed. Other stories were most likely ready to be delivered as well, so I’ll check that they were actually included in the deployment, and then Deliver them in Tracker.

The typical flow goes like this:

  1. Commit the changes to my story
  2. Push to GitHub
  3. Click Finish on story in tracker
  4. Wait for green build
  5. Deploy to staging
  6. Click Deliver in Tracker

That’s not really that bad. I’m lazy though. If something can be automated, I want it to be automated. For instance, the deployment process should be automatically handled by my Continuous Integration server.

If we take this to the next step and automate our build and deployments as described in Robbie Clutton’s Building Confidence post, we can reduce that flow to this:

  1. Commit the changes to my story
  2. Push to GitHub
  3. Click Finish on story in tracker
  4. Wait for green build
  5. Click Deliver in Tracker

Let’s assume we’ve done that. That’s a nice improvement, as deploys will happen automatically whenever there’s a green build. That in itself is amazing.

But, what if we could reduce the steps in my everyday flow to this?

  1. Commit the changes to my story
  2. Push to GitHub

I want that!

A Streamlined Flow

Here’s the basic idea: tie the commit message to the Tracker Story ID. This enables all kinds of functionality that will get us down to that two step process.

GitHub & Pivotal Tracker Integration

GitHub supports integration with Pivotal Tracker. Dan Podsedly blogged about this back in 2010. You just tag your commits with Tracker Story IDs, and GitHub automatically comments on your story with the commit message. And, most importantly, it will click Finish on the story for you.

First, configure the Pivotal Tracker service hook on GitHub.

Once this is enabled, you can start tagging your commits to use the hook. The syntax for your commit message is pretty simple. Add the following at the end of your commit message:

[(Finishes|Fixes|Delivers) #TRACKER_STORY_ID]

So, if you just fixed a CSS bug for story #35192613:

When you push to GitHub, the post-receive hook will then call back to Tracker and put a comment on the story with a link to the commit on GitHub:

At this point, we’ve eliminated step 3 from above. Now our workflow looks like this:

  1. Commit the changes to my story
  2. Push to GitHub
  3. Wait for green build
  4. Click Deliver in Tracker

Honestly, that’s a pretty nice workflow. If I’m diligent about tagging each commit with the Tracker Story ID, GitHub will handle Finishing the story for me. It’ll also add a comment to the story, so others can take a look at the commit related to the story. Technical PMs especially appreciate this, since their curiousity about what changed is easily satisfied by just clicking on the comment in the story.

But I want more. I want to automate steps 3 and 4.

Deliver Stories Automatically

Pivotal Tracker has an API hook to deliver all finished stories. However, it’s not what we’re looking for. If our CI server calls that API hook after a green build, it’s possible for other stories to have been finished in the meantime that will not be included in the deploy. So, calling this API endpoint will just cause false positives.

A side-effect of tagging all your commits with Tracker Story IDs is that the git log now contains a nice little list of stories that have been finished. This makes it trivial to automate delivery of only the stories that have been deployed.

Unpakt was keen to share the script that makes this happen. They’ve thrown it up as a gist:

#!/usr/bin/env ruby

# gem 'pivotal-tracker'
require 'pivotal-tracker'

TRACKER_TOKEN = "..."
TRACKER_PROJECT_ID = "..."

PivotalTracker::Client.token = TRACKER_TOKEN
PivotalTracker::Client.use_ssl = true

unpakt_project = PivotalTracker::Project.find(TRACKER_PROJECT_ID)
stories = unpakt_project.stories.all(:state => "finished", :story_type => ['bug', 'feature'])

staging_deploy_tag = `git tag | grep staging | tail -n1`

stories.each do | story |
  puts "Searching for #{story.id} in local git repo."
  search_result = `git log --grep #{story.id} #{staging_deploy_tag}`
  if search_result.length > 0
    puts "Found #{story.id}, marking as delivered."
    story.notes.create(:text => "Delivered by staging deploy script.")
    story.update({"current_state" => "delivered"})
  else
    puts "Coult not find #{story.id} in git repo."
  end
end

Using the pivotal-tracker gem, the script finds all Finished stories in your Tracker project, and looks for a commit in the git log with that Story ID. If it finds one, it marks the story as Delivered.

It also adds a note to the story:

Our New Development Workflow

So, here’s what our development workflow now looks like:

  1. Commit the changes to my story (including the Tracker Story ID in the commit message)
  2. Push to GitHub

GitHub will mark my story as finished, and add a comment. CI will then run. If there’s a green build, it will deploy to staging. The deploy script will then run the deliver_stories script shown above, and all stories that have been deployed will be marked as delivered.

This is really, really nice. Having this much of the process automated for me just makes my life easier. It also gives our PM much quicker feedback about what’s happening. It’s very easy to forget to click Finish on a story, or deliver stories that weren’t deployed, or forget to click deliver on stories that were. This workflow eliminates these concerns.

Caveat

For this to work properly, the script that deploys to staging must checkout the same SHA as the green build from CI. If the git log shows commits from after the green build, some stories may be marked as Delivered when they haven’t actually made it through a green build and been deployed.

Caution

This workflow is not for every team. For example, it may be that your team functions better with developers verifying that each story is actually available and working on the staging environment before clicking Deliver in Tracker. No Product Manager wants to try to accept a story only to find that it’s not actually available on the staging environment. It may also be that on your project “Deployed” is not the same as “Delivered”.

You should only consider adopting a flow like this if your project is of a nature where deploys are easy, everyone on the team is playing along, and you can agree that “Deployed” and “Delivered” are one and the same.

The workflow described in this post has been working perfectly for months at Unpakt. We initially ran into the problem described above in the Caveat section, because our CI server running our deploy script while HEAD was checked out instead of the green build’s SHA. We haven’t had any stories accidentally marked as Delivered since we resolved that with TeamCity’s Snapshot Dependency feature.

Unpakt is hiring!

Unpakt is a small team with solid financial backing, led by a highly successful entrepreneur. They are creating an intense but fun and respectful culture and are looking for talented people to join their team. Check out their job listings!

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
John Barker

Cleaning old branches

John Barker
Thursday, October 11, 2012

We’re using Github pull requests on our project. Which means whenever a pull request is accepted, a branch is left lying around.

So I wrote a quick script to remove all remote branches that have been merged into develop (our working branch, you’ll have to alter the first instance of ‘develop’ to master if you use a more typical git branching model).

Here goes:

git branch -r --merged develop | sed 's/ *origin///' | grep -v 'master$' | grep -v HEAD | xargs -n 1 printf "echo git push origin :%sn" | bash
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mark Rushakoff

A simple way to detect unused files in a project using git

Mark Rushakoff
Wednesday, August 1, 2012

After finding that we had a few images checked into our project’s repository but that were not referenced in the project, I wanted to write a script to quickly see if there were any other unused assets.

This was a one-off script, so it probably won’t suit everyone’s needs, but here’s how we approached the problem:

First, we needed to get a list of the files that git was tracking in our image directory. While you could use ls for that, I wanted to be sure that we weren’t going to list any files that git was ignoring, so we started with git ls-files, whose output will look something like this if called as git ls-files ./img:

img/foo.png
img/bar.png

(For the sake of the example, we’ll assume that foo.png is referenced in the project and bar.png isn’t.)

The next thing we want to do is to see if those filenames are referenced anywhere in the code. At this point, I wasn’t sure if they would be referenced by a relative or absolute path, so I knew I wanted to just search for e.g. foo.png. I like to check my work with an intermediate command, so the next command we tried out was

for FILE in $(git ls-files ./img); do
    echo $(basename "$FILE")
done

(basename gives you everything after the last slash of its input — in this case, just the raw filename.) And when we ran that command, we saw the expected output:

foo.png
bar.png

Now that we know we are correctly extracting the desired part of the path, we can check whether that filename is referenced anywhere in the code. git grep works enough like regular grep, but it only searches tracked files in the working tree (if you call it without a commit-ish), so we don’t have to worry about excluding the .git directory or .gitignored files.

If we call git grep foo.png manually, we will see some output like

src/index.html: <img src="../img/foo.png"/>

and git grep bar.png will have no output. But it isn’t the output we care about so much as the exit status (noting that git grep will return non-zero when no results are found) — so let’s run our command again, and verify that we will only remove the expected files:

for FILE in $(git ls-files ./img); do
    git grep $(basename "$FILE") || echo "would remove $FILE"
done

Oops, the output contains the output from git grep still:

src/index.html: <img src="../img/foo.png"/>
would remove img/bar.png

Let’s redirect git grep‘s output to /dev/null and move along.

for FILE in $(git ls-files ./img); do
    git grep $(basename "$FILE") > /dev/null || echo "would remove $FILE"
done

The output looks correct! The last thing to do is to actually remove the file:

for FILE in $(git ls-files ./img); do
    git grep $(basename "$FILE") > /dev/null || git rm "$FILE"
done

After that, run your tests again to make sure that nothing was broken by removing these assumed-to-be-unused files. If you’re green, you’re probably good to commit!

I think these are the two biggest limitations of this roughly-one-liner:

  • You might be globbing your resources, so you might not be able to find references via git grep
  • If you have spaces in your filenames or in your directories, you’ll have to set IFS to properly read the lines from the output of git ls-files.

But if those don’t affect you, then you can probably use that mini-script to get rid of some “noise” files from your working tree.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Kris Hicks

Rewinding git pull

Kris Hicks
Monday, July 9, 2012

If you’re using a rebase strategy for the first time you may run git pull in a situation where Git practically tells you to do it, but you don’t actually want to do it. The situation is described below, and the method to unwind it follows. I’ve added pictures of the branches between hashes to make it clear what the state of the world is at any given point prior to or after running a particular command, which I hope makes it easier to follow.

First, you update master, as it’s out of date:

#############################
A--B--C--D origin/master
A--B--C master
#############################

(master) $ git pull
Updating C..D
Fast-forward
...snip...

#############################
A--B--C--D origin/master
A--B--C--D master
#############################

You create and checkout a topic branch, topicA:

(master) $ git checkout -b topicA
Switched to a new branch 'topicA'

#############################
A--B--C--D origin/master
A--B--C--D master
          
           topicA
#############################

You make some changes, commit, and push it to origin.

(topicA) $ git commit -am "Changed README"

#############################
A--B--C--D origin/master
A--B--C--D master
          
           E topicA
#############################

(topicA) $ git push -u origin
(topicA) Counting objects: 28, done.
...snip...
(topicA)  * [new branch]      topicA -> topicA
(topicA) Branch topicA set up to track remote branch topicA from origin.

#############################
A--B--C--D origin/master
A--B--C--D master
          
           E topicA
A--B--C--D--E origin/topicA
#############################

At this point you have your local topicA, and also origin has a copy of topicA:

(topicA) $ git branch -a
* topicA
remotes/origin/topicA

You continue doing work, making more commits on your branch. Some time has passed since you branched from master, and another commit, F, has been pushed to origin/master.

(topicA) $ git fetch # to update your local repository's knowledge of the remote

#############################
A--B--C--D--F origin/master
A--B--C--D master
          
           E--G--H topicA
A--B--C--D--E origin/topicA
#############################

At this point you want to rebase on top of origin/master to get the updates (in this case, F).

$ git rebase origin/master
First, rewinding head to replay your work on top of it...
...snip...

#############################
A--B--C--D--F origin/master
             
              E'--G'--H' topicA
A--B--C--D master
A--B--C--D--E origin/topicA
#############################

And then you want to push your updated topicA, with your new commits and the new commit from origin/master up to origin:

The following assumes you have git config.push default upstream set. This configuration parameter limits the branch that git will attempt to push. I highly recommend you set that value as the default is to push all branches, which you probably do not want.

(topicA) $ git push
To git@github.com:intentmedia/code.git
! [rejected]        topicA -> topicA (non-fast-forward)
error: failed to push some refs to 'git@github.com:krishicks/krishicks.git'
To prevent you from losing history, non-fast-forward updates were rejected
Merge the remote changes (e.g. 'git pull') before pushing again.  See the
'Note about fast-forwards' section of 'git push --help' for details.

This is where trouble sets in.

First, Git tells you the push was rejected because you would have lost history. Then comes the troublesome line: “Merge the remote changes (e.g. ‘git pull’) before pushing again.”

If you were working on master and made some commits, and someone else made some commits on master as well and pushed before you did, you would end up with the same situation as above. The push would be rejected because you don’t have the commits that the other person made. Thus, if your push were to succeed their work would be lost on origin/master. This is what Git is referring to when it says “To prevent you from losing history..” The history that would be lost would be the work the other person did.

In the situation we’ve been building up, making commits and rebasing topicA, you’re the only person committing to the branch and the history you would “lose” is the set of pre-rebase commits that you pushed to the remote originally (in this case, E). You’ve overwritten E with E’ during the rebase, and Git doesn’t want you to lose the E that’s on the remote.

What you should do in this situation is git push -f to force-push the branch. You know you’re going to lose E that’s on the remote, but that’s fine because you have E’ on your local topicA. You intend to replace whatever is on the remote with whatever you have locally.

The problem is that it says to do a git pull, which will pull the remote E into your local, which you don’t want. That will give you a merge commit, I:

A--B--C--D--F origin/master
             
              E'--G'--H' topicA
                       
                        I
A--B--C--D master      /
A--B--C--D------------E origin/topicA

If you did a git log at this point you’d see that the merge commit I brought in E, which has the same message but a different SHA than your rebased E’.

(topicA) $ git log --graph --oneline
* I Merge branch 'origin/topicA' into topicA
|
| * E
* | H'
* | G'
* | E'
|/
* D
* C
* B
* A

So, how do you fix this? You can hard reset back to H’, which gets rid of the merge commit, but only if you didn’t already make more commits after the faulty git pull.

(topicA) $ git reset --hard H' # Only if you didn't make any more commits!

If you have made commits after, with your log looking like this:

(topicA) $ git log --graph --oneline
* K
* J
* I Merge branch 'origin/topicA' into topicA
|
| * E
* | H'
* | G'
* | E'
|/
* D
* C
* B
* A

You need to use the two-argument form of git rebase –onto:

(topicA) $ git rebase --onto H' J~1

That will get rid of the merge commit, leaving you with:

(topicA) $ git log --graph --oneline
* K
* J
* H'
* G'
* E'
* D
* C
* B
* A

And now you can git push -f. The docs on git-push (or git push --help) do give you a better explanation than the message when the push is rejected, in the section NOTE ABOUT FAST-FORWARDS.

A simple rule about git pull is to not ever use it unless you’re on master and have made no commits that put you ahead of origin/master, which you can easily tell with:

(master) $ git fetch
(master) $ git log @{u}..

or

(master) $ git fetch
(master) $ git status
# On branch master
nothing to commit (working directory clean)

If the result from git log @{u} is empty or you don’t get “# Your branch is ahead of ‘origin/master’ by commit(s)” message after git status, you’re OK to git pull. In no other case do you need to, or should you, git pull.

I generally recommend always working on a topic branch and keeping master clean to avoid accidentally running git push -f on master, and to enforce the idea that after you fetch, you’re rebasing on top of origin/master directly instead of doing git pull --rebase while on master, which hides the fact that you’re rebasing on top of origin/master.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Kris Hicks

quick tips

Kris Hicks
Tuesday, July 3, 2012

The following are things I found very helpful, which you also may find make your day-to-day usage of Git more enjoyable.

Go HEADless

In many (and perhaps all) cases HEAD is implied when no ref is given, such as the following equivalent statements:

$ git log origin/master..HEAD
$ git log origin/master..

Upstream reference

Before you start referring to the upstream, you’ll want to do a git fetch to update your refs/heads:

$ git fetch

You only need to do this once in a while, or whenever you know the upstream to have changed.

You can refer to the upstream of any branchname or otherwise symbolic-ref by appending @{u} to the ref, such as:

$ git log master@{u}

If you just want the upstream of the current branch, whatever it may be, you can replace master from above with HEAD:

$ git log HEAD@{u}

Upstream difference

Oftentimes I’ll want to see what commits are on upstream that I don’t have. I usually will do this via:

$ git log @{u}..

Which, when on master, is equivalent to:

$ git log origin/master..HEAD

You can see the reverse difference, or the commits are on the remote that you don’t have, by reversing the range:

$ git log ..@{u}

Which, again on master, is equivalent to:

$ git log HEAD..origin/master

To see the full list of formats recognizable, see git revisions --help

If you want to see both of the above at the same time, you can use git log with the –left-right parameter. I’ve also included –format=” and –oneline (which must be passed in that order), which may be optional depending on your format configuration, but for me are not:

$ git log HEAD@{u}...HEAD --left-right --format='' --oneline

Which can be shortened to:

$ git log @{u}... --left-right --format='' --oneline

Note: The above example uses three dots, not two, to show only the commits which are reachable as ancestors of one branch but not the other.

You can replace HEAD in the above with any symbolic-ref or branchname to use as a reference instead. The output (with the format given previously) will look like:

< e86bd83 D
< 6ea2155 C
> eb2de55 B
> 74829bd A

Commits beginning with “<” are only on the left side of the triple-dot (in this case, the upstream), commits beginning with “>” are only on the right side (in this case, the current branch). A..D are the commit messages.

Is it merged?

To find out if a particular branch has been merged into origin/master, you could dig through the log on master and search for it:

$ git log origin/master

Or, if you knew who would have merged it, you could limit the above with that:

$ git log origin/master --author Kris

I wanted a better way, and I found it. I wanted to see if a particular branch had been merged to master without changing branches or having to dig through a log looking for the merge of the branch.

The situation that prompted this was that I had a topic branch, topicB, which depended on another topic branch, topicA. I wanted to see if topicA had been merged into master to find out if I could rebase onto master to get up to date, or if I should continue rebasing on top of the branch.

$ git fetch
$ git branch --contains topicB@{u} -r

This gives you a list of all the remote branches that can reach (as an ancestor) the ref given. origin/master will be right at the top if the branch has been merged into it.

Many thanks to John Wiegley, Scott Chacon and Mark Longair for the sources of the tips.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Kris Hicks

git config rerere.enabled true

Kris Hicks
Wednesday, June 13, 2012

There have been times where I performed a rebase and had to resolve conflicts as part of the rebase, and then decided to abort the rebase for one reason or another. Without rerere the next time I went to perform the rebase I’d end up having to resolve at least some of the same conflicts I had previously, which is annoying.

This is where rerere comes in. It stands for reuse recorded resolution.

What rerere does is save the resolution of a conflict so that it can be re-applied later if it sees the same conflict again. When Git sees the conflict which it already has a resolution recorded for, it will apply the resolution automatically for you, and give you the opportunity to accept the resolution as applied, or change it.

Turning it on can be done two ways: set it as a configuration parameter using git config rerere.enabled true, or use it only when you think you might need it with git rerere both before and after the resolution of a conflict.

A more verbose explanation of rerere exists in Scott Chacon’s Pro Git.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Kris Hicks

exec

Kris Hicks
Thursday, June 7, 2012

(Update #1 below)

Say you’re going to do an interactive rebase where you’re going to be squashing commits or reordering them. During this process you may want Git to execute a command after applying certain items of the todo list. An example of this would be when you want to run rake or similar to ensure a newly-squashed commit is still green.

You can do this by adding a task to the todo list, exec, followed by the command you’d like Git to run at that point in the rebase. If the command you specify should return a non-zero exit code, Git will pause the rebase and allow you to sort it out, in the same way that it pauses when a conflict arises while applying the todo list during any other rebase.

Here’s an example of the above situation, where two commits are going to be squashed, and I want Git to run rake after it does the squash.

Pre-edits, this would look like:

pick dad8d12 Commit #1
pick f613ac1 Commit #2
pick 58822ee Commit #3

Post-edits, this would look like:

pick dad8d12 Commit #1
f f613ac1 Commit #2
x rake
pick 58822ee Commit #3

What happens here is Git will fixup Commit #2 into Commit #1, creating a new commit, then run rake. If rake returns a zero exit code, Git applies Commit #3 and completes the rebase. If rake had returned a non-zero exit code, Git would have paused the rebase operation at that point, allowing any necessary changes to be made to the HEAD commit, which is the squashed #1/#2.

I typically do this separate from doing an initial rebase, where I rebased and made a change to Commit #1 and had to resolve conflicts throughout the rest of the commits. This way I can keep my head straight while doing the rebase, then fix anything I missed as a second operation.

Update #1: As of Git 1.7.12 you can pass -x <cmd> to git rebase -i to have Git run the exec command after every commit in the resulting history: git rebase -i <treeish> -x <cmd>.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (783)
  • rails (117)
  • testing (90)
  • ruby (86)
  • ruby on rails (71)
  • jobs (62)
  • javascript (59)
  • techtalk (44)
  • ironblogger (42)
  • rspec (39)
  • bloggerdome (34)
  • productivity (34)
  • activerecord (30)
  • rubymine (30)
  • git (29)
  • gogaruco (29)
  • nyc (27)
  • design (24)
  • mobile (23)
  • pivotal tracker (22)
  • process (21)
  • cucumber (21)
  • jasmine (19)
  • ios (18)
  • tracker ecosystem (17)
  • webos (17)
  • objective-c (17)
  • fun (16)
  • android (16)
  • palm (16)
  • ci (16)
  • "soft" ware (16)
  • bdd (15)
  • tdd (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • css (14)
  • gem (13)
  • mouse-free development (12)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • api (12)
  • keyboard (11)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
Subscribe to git Feed
  1. 1
  2. 2
  3. 3
  4. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Tools
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >