When I switched to git from subversion at my old business, I stored notes on how to do certain tasks. I’m pasting it below. Maybe some of this will help you out.
If you know a better way to do these tricks, please let me now.
My git diary
============
.. contents::
Slowly learning how to use git.
Typical upgrade flow
--------------------
I just committed a bunch of code and I want to see what the diffs were,
so I ran this::
$ git diff HEAD^
My production server runs branch 3.5.1 of my software. I want to start
work on a scary new feature that may take a long time, so I made a new
branch called 3.5.2 in my local sandbox::
$ git checkout -b 3.5.2
Now I can commit all my intermediate stuff in here.
Somebody found a bug in the production site (running 3.5.1) so I switch
to my 3.5.1 checkout::
$ git checkout 3.5.1
$ vi # fixing the problem
$ git commit -a -m "Fixed the prod bug"
$ git push origin 3.5.1
That last line sends my local commits to my remote bare repository.
That remote bare repository is on a box with an SSH server and RAID
storage.
Then I connect to the production box and pull down the most recent
changes::
$ ssh prod
matt@prod$ cd where-the-repo-is
matt@prod$ git pull origin 3.5.1
matt@prod$ restart-everything
That restart-everything command is a homemade script that does just what
you think it does.
Now back in my dev sandbox, I want to pull the bug fix from 3.5.1 into
my 3.5.2 branch. So I do it like this::
$ git checkout 3.5.2
$ git pull . 3.5.1
And now my 3.5.2 branch has that code in it. Hurray! Understand that
the dot (.) in git pull . 3.5.1 means that git should retrieve code from
this repository, not the fancypants remote one.
Pulling stuff
-------------
When I run::
$ git pull origin 3.5.1
That means to pull from the origin's 3.5.1 branch into whatever branch I
currently have checked out.
A typical day
-------------
I have two branches, an experimental branch and a public branch.
The production server used by customer runs the public branch.
Usually I work in my development branch. Sometimes I have to do some
code into production so this is how I do it::
$ git checkout stable # switch my local copy to the stable branch.
$ git pull origin stable # update local copy, just in case.
$ vi blah.py # do the bug fixes.
$ git commit -a -m "Notes about bug fixes"
$ git push origin # This is my deploy system.
$ git checkout dev # Go back to my development branch.
$ git pull origin stable # merge in that bug fix.
So far, this works well. I did something similar with SVN, and it also
worked fine. But git is way better at safely merging and it is much
faster.
Undo changes to a single file
-----------------------------
I typically edit a dozen files and then figure out that I want to undo
some stuff. If I did::
$ git reset --hard
Then my whole working copy would be destroyed. Usually, I just want to
do something like revert one file. So this is how::
$ git checkout that/particular/file
That deletes my working-copy changes just there. Everything else is
left as-is.
How to submit patches by email
------------------------------
I cloned the ditz project and tweaked the code, and I wanted to submit a
patch, so this is what they told me to do::
For this type of thing, it's "very simple". git commit -a, add a
one-line description followed by a blank line and more commentary, and
then git format-patch HEAD^.
When I ran git format-patch HEAD^, that produced a file on my local
machine that had a pretty formatted patch. Then I emailed that patch to
the list with some text.
How to branch from a remote repository
--------------------------------------
This is what somebody in the #git room told me to do, since I use a
remote bare repository::
$ git fetch origin
$ git checkout -b newbranch origin/original
Here's another person's opinion::
$ git fetch && git checkout --track -b mynewbranchagain remotename/mynewbranch
Set the remote branch
---------------------
Normally I have to pull from my remote repo and specify the branch I
want to pull in like this::
$ git checkout experimental
$ git pull origin experimental
It is possible to associate my local branch of experimental with that
remote branch of experimental like this::
$ git config --add branch.experimental.remote origin
$ git config --add branch.experimental.merge experimental
And now I can run::
$ git pull origin
without specifying the remote branch. In fact this works too::
$ git pull
Compare one file across two local branches
------------------------------------------
I want to look at a diff of x.py in my public branch vs my experimental
branch, so this is what I did::
$ git diff public experimental -- x.py
I can see everything different between the two branches like this::
$ git diff public experimental
Break up a whole bunch of changes into different commits
--------------------------------------------------------
I'm irresponsible about committing after each conceptual unit of work.
Lots of time, I'll edit a file to fix one bug, then while I'm in there,
I'll edit some other code because I see a better way to do something
else. Then I'll maybe add a few doctests to a completely different
section just because I want to.
After a few hours, typically inside of the same file, I have edits
related to multiple separate tasks.
Before I commit my changes, I run::
$ git add -p frob.py
Which walks through all the changes in that file and asks me if I want
to stage each one. In the first pass, I stage all the hunks related to
the first issue. Then I commit those changes. Then I rerun git add -p
frob.py again and march through the file for the next the second issue.
Keep in mind that I committed my changes after the first pass, so when I
go through the file the second time, I won't get prompted for those
changes.
This is one of those git features that you just couldn't do with svn.
Find the commits in branch A that are not in branch B
-----------------------------------------------------
Sometimes I'll patch a production bug in my production branch and then I
will forget to merge it into the development branch. This is how I can
check for that::
$ git checkout production_branch
$ git cherry dev_branch
This will spit out a list of commits that are in production_branch but
not in dev_branch.
It will not return any commits made to dev_branch but not in
production_branch.
See a file as of a point in time
--------------------------------
Looking at a commit shows the changes. Sometimes I want to see the file
itself.
Here's how to look at foo.py as of two commits ago::
$ git show HEAD^^:b/foo.py
Here's how to see what foo.py looked as of a particular commit::
$ git show bf51ebdbc:b/foo.py
b/foo.py is the path to the foo.py from the top of the repository. I'm
not certain, but I don't think the current working directory matters.
Copy a file from one branch to another
--------------------------------------
I committed a file into one branch then checked out a new branch. This
is how I copied that file into the new branch WITHOUT merging it in::
$ git checkout newbranch # 1
$ git checkout otherbranch foo.py # 2
Step 1 moves me into the newbranch. Step 2 gets me a copy of the file
foo.py from otherbranch and saves it into this branch named newbranch.
See a file in a different branch
--------------------------------
After I checkout the maxhenry branch I want to see the version of
printable.kid in the mayfield branch::
$ git checkout maxhenry
$ git show mayfield:bazman/bazman/templates/printable/printable.kid
Set a file to an old version of itself
--------------------------------------
Sometimes I want to get the version of a file as of a certain commit and
check that in. There are lots of ways to do this, including using git
revert or git reset, but I've had good luck with this approach::
$ git log # Use this to find the commit you want.
$ git checkout ac778cbb5517e1aeef446c9a8a1092eef81717fa:repo-top/a/foo.py
After you run this, the index will have this old copy queued up to be
commited. You can use git diff --cached to see the changes.
Create a new branch on the remote repo
--------------------------------------
We just pushed the branch **mollusk** up to production, so now it is
time to create a new branch named **nosehair**::
$ git branch nosehair # creates the new branch
$ git checkout nosehair # switches to the new branch
$ git push origin nosehair # pushes it up to origin
Switch to a new branch that already exists on a remote repo
-----------------------------------------------------------
After somebody else already created the branch nosehair, and pushed it
up to the remote repository, here's how to switch to work on the
nosehair branch::
$ git fetch origin
$ git branch -a # make sure origin/nosehair exists!
$ git checkout -b nosehair origin/nosehair
Search through commit messages
------------------------------
This is how I found all commits that had a commit message that included
the word CCPL::
$ git log --grep=CCPL
This is how I limited it to just my (Matt's) commits for CCPL work::
$ git log --grep=CCPL --author=matt --all-match
Without the --all-match option, you'll see all commits by matt or that
have CCPL in the commit message.