Category: git
-
Useful Git Commands: a Shortcut for GitHub Pull Requests
Normally, when someone asks me to merge something in git I need to add his or her repository using
git remote add
, fetch the branch I need, and then merge it. When someone submits a pull request to a project hosted on GitHub, however, GitHub additionally publishes it as something I can fetch from my own repository:$ git ls-remote origin | grep pull/6 03d7fb7af91a74bb7658a0742fa68bfeb5d50a3f refs/pull/6/head f8ebbc62555143019947f1255b064cda38fd239f refs/pull/6/merge 8a42021dbabcfc222b4d47c5ada47e344154d934 refs/pull/60/head f4d8052000601e59e4e7d4dec4aa4094df4e39a0 refs/pull/61/head 8a519986f5b59721692ec75608edf0f404f88e87 refs/pull/62/head 9c03e723e8b50ca56a1257659d686a68a69e6e40 refs/pull/62/merge f3a983c6fc3ff236c2bc678cfec3885da609f79a refs/pull/63/head e7953c21a77fb37fb7158dd87fc0c156dd8f97ae refs/pull/63/merge
With this I can use one line of configuration to create a convenient shortcut that lets me immediately check out any pull request:
$ git config --add remote.origin.fetch "+refs/pull/*/head:refs/remotes/origin/pull/*" $ git checkout pull/63 Branch pull/63 set up to track remote branch pull/63 from upstream. Switched to a new branch 'pull/63'
-
Useful Git Commands: URL Rewriting
People have to use SSH or HTTPS to push to GitHub, but when fetching one can use git’s own network protocol because it is generally faster. You can make a specific repository on your machine use SSH only for pushing by cloning it with the faster
git://
URL and running something like this:$ git config remote.upstream.pushurl git@github.com:gholms/boto
That works nicely, but you have to do it once for every single repository you want to interact with. That quickly becomes annoying. Thankfully, you can leverage git’s URL-rewriting mechanism to make this easier:
$ git config --global url.git://github.com/.insteadOf github: $ git config --global url.git@github.com:.pushInsteadOf github:
This adds two new rules to your git configuration:
- If a URL starts with
github:
then replace that withgit://github.com/
. - If a URL starts with
github:
and you are pushing then replace that withgit@github.com:
.
After you do that you can simply use URLs like
github:gholms/boto
when cloning. They will get rewritten togit@github.com:gholms/boto
when pushing, andgit://github.com/gholms/boto
the rest of the time, speeding things up without creating additional work in the future.This should work if you prefer HTTPS for pushing to GitHub, or if you use other servers, too. Just tweak the commands.
- If a URL starts with
-
Useful Git Commands: Summarizing Lots of Log Entries
When looking for a summary of a git repository’s history, the output of
git log
isn’t always as informative as one might like. It displays every commit in chronological order, which effectively hides the changes that merges bring in. It is also quite verbose, showing complete log messages, author info, commit hashes, and so on, drowning us with so much info that only a few commits will fit on the screen at once. After supplying the command with the right cocktail of options, though, its output becomes a significantly better summary:The output above came from a command that is long enough that I made an alias, for it,
git graph
, in my~/.gitconfig
file:[alias] graph = log --graph --abbrev-commit --date=relative --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(blue)<%an>%Creset'
Don’t forget that
git log
accepts a list of things to show logs for as well, so if you want to look at the logs forbranch-1
andbranch-2
you can simply usegit graph branch-1 branch-2
to make them both show up in the graph.
-
Reverting a Range of Git Commits Separately
All kinds of articles on the Internet tell you how to revert a range of git commits in one massive, squashed-together revert commit. But to split them up into separate revert commits you have to pass a list of commits to
git revert
, and that list has to go in reverse order to avoid conflicts.$ git rev-list --reverse ${last_good_commit}.. | xargs git revert
-
Speed Up Merges with Git's Index
We’ve all been there, running
git merge
only to have it spewCONFLICT
all over the place and quit partway through. But if we already know that every time there is a conflict then all of “our” code that we are merging changes into should take precedence over “their” changes that we are pulling in, git 1.7.3 provides a handy shortcut for that:$ git merge -X ours theirbranch
If “their” changes that we are pulling in should always take precedence instead then things are equally easy:
$ git merge -X theirs theirbranch
If “ours” or “theirs” style merges will work, but only for certain files, things get a bit more complicated. But with a bit of background knowledge of how git’s index works we can save ourselves a bunch of work.
The index stores what stage a file is in. Normally, a file’s stage is 0.
$ git ls-files -s myfile 100644 4b48deed3a433909bfd6b6ab3d4b91348b6af464 0 myfile
A file with a merge conflict is different because the index actually has three different versions of it: the version that the two branches we are merging most recently had in common, the version on “our” side of the merge, and the version on “their” side of the merge. In git these correspond to stages 1, 2, and 3. This is one reason one has to run
git add
on every conflicting file before completing a merge.$ git ls-files -s myfile 100644 4b48deed3a433909bfd6b6ab3d4b91348b6af464 1 myfile 100644 5be4a414b32cf4204f889469942986d3d783da84 2 myfile 100644 39c5733494077ae8bc45c45c15a708ffe9871966 3 myfile
Rather than opening up the conflicting file and clearing out the parts that clash by hand, we can instead simply copy the version we want from the index into our working tree:
$ git checkout-index -f --stage 3 myfile $ git add myfile $ git commit