Version 20 (modified by urkle, 12 years ago)

Added "shortcut" hack to getting git-svn metadata from github clone. and cleaned up top part of document

Getting the GIT checkout

Import Straight from the SVN repo

First, we need to fetch a copy of the repository.

    git svn clone -s -r 40000 https://svn.parrot.org/parrot # choose some recent-ish commit

-s is for --stdlayout which presumes the svn recommended layout for tags, trunk, and branches.

-r is for the revision to start taking history from. If you want to include all of the history, just leave that option off, but it will take a very long time, and you really don't need all of it.

NOTE: The older a revision you choose, the longer it will take to import. But you will not be able to "git blame" past the earliest revision you import. Choose wisely.

This takes a clone of the repository at that revision; to update it to HEAD, you now need:

    git svn rebase

Which is very similar to svn up.

From the leto's github mirror

Because importing all of history is *very* time consuming, DukeLeto has already gone through the trouble and maintains a github mirror of Parrot at  http://github.com/leto/parrot/tree/upstream .

    git clone git://github.com/leto/parrot.git

This will create a directory in the current directory named 'parrot'. If you want to specify the directory to create, pass one more argument.

The "upstream" branch is what directly mirrors Parrot trunk. To start a local branch that tracks the upstream branch:

    cd parrot && git fetch && git checkout -b upstream origin/upstream

Adding git-svn metadata to the github clone

If you want to re-populate the SVN metadata that can quickly be done by taking your up-to-date clone of leto's repo off of gihub and adding this to your .git/config

    [svn-remote "svn"]
            url = https://svn.parrot.org/parrot
            fetch = trunk:refs/remotes/trunk

Then run this command to find the "top commit"

     git show origin/upstream | head -n 1

and put that commit hash into a file .git/refs/remotes/trunk (obviously replace the has with the one from the above command)

     echo c85aaa38b99cedb087e5f6fb69ce6d4a6ac57a0b > .git/refs/remotes/trunk

and finally

     git svn fetch

From full git-svn tarball

You can also download a tar archive containing the complete history and all branches as well as svn meta data from

Basic local workflow

Now, start working on patches. The simplest way to work, without local branches, is:

    hack; hack; hack; hack

    git diff # look at differences since the last commit

If you want to commit all of these changes, run:

    git commit -a

and enter your commit message. This creates a local commit only. It hasn't been pushed to the SVN server yet. If you only want to add changes from some files but not others, run:

    git add file1 file2 file3 ...
    git add -i file4 # if you only want to add some diff hunks and not others
    git commit

and enter your commit message. After this, 'git diff' will still show the uncommitted changes. While you're adding changes to be committed, you can run 'git diff --cached' to see what's going to go in the next commit.

After you've made however many commits, you can push up to the svn server with:

    git svn dcommit

That will also bring your local tree up to date. To bring your tree up to date in general, run:

    git svn rebase

This will update your local checkout and then re-apply your local un-submitted commits on top of the new trunk.

If you want to get the commits for all branches that exist in your clone:

    git svn fetch

Neat Tricks

Patches Between Local Changes and Trunk

If you want to see the patchset representing your commits and the remote server:

    git format-patch remotes/trunk

Change trunk to the name of the branch, if you're working against a branch. This will create a series of numbered patches, corresponding to every commit you've made. You can review them as you like.

Interactive Rebase

One of the neat things about git is that you can modify your commits before you push them up to the SVN server. Let's say you've made 10 commits to your local git repository, but you want it to look like two when it gets pushed up to SVN. All you need to do is type

    git rebase -i HEAD~10

And git will launch your $EDITOR and allow you to perform what's referred to as an "interactive rebase."

    pick 08464ae bug 9074 Fix bad logging call.
    pick 8750643 bug 9995 Run the supplied architecture through arch_extract() to change 'x64' into 'x86_64' when it occurs.
    pick af6f7ae bug 9995 Make sure the architecture string occurs on word boundries (ie, it's not part of a larger word)
    pick 8e4ef0c Don't generate a warning for generic apps.
    pick 1e4d124 Add a module for handling script-based custom applications.
    pick 808607d bug 9879 Resolve paths to entity IDs at the start of a process.
    pick e02a0db bug 9879 Cosmetic code formatting changes.
    pick 3e1cc94 bug 10010 Added an optional comparator argument to compare_homes
    pick 218805c bug 10010 Added comparator argument to default implementation for consistency
    pick 21e20fa bug 10010 Made procs_under_home use compare_homes to handle case insensitivity on windows

    # Rebase 05e760e..21e20fa onto 05e760e
    #
    # Commands:
    #  pick = use commit
    #  edit = use commit, but stop for amending
    #  squash = use commit, but meld into previous commit
    #
    # If you remove a line here THAT COMMIT WILL BE LOST.
    # However, if you remove everything, the rebase will be aborted.
    #

If you change any of the lines that start with "pick" to start with "squash" instead, when you exit your $EDITOR, that commit will be squashed into the commit above it and you'll be popped back into $EDITOR to combine the commit messages into one. You can also squash multiple commits into more than one commit in a single session; $EDITOR will be fired up for each of the resulting commits for you to combine the commit messages.

You can also reorder commits just by moving them up or down in the list, and you can drop commits entirely by removing them from the list.

Attached to this page is a perl script called gsquash which uses git log to look for commits that haven't been pushed to SVN yet and builds the appropriate git rebase -i HEAD~n command.

git stash

Let's say you're working on some stuff that's not quite ready to be checked in, but you suddenly need to work on something else. You can type git stash, which will record the current state of your work and give you a clean working directory again. You can then make whatever changes you want to your working directory, commit them, dcommit them to SVN, and then type git stash pop to get what you were working on before back. You can also build a stack of stashed work by repeatedly making changes followed by git stash commands; you can then list these stashed modifications with git stash list. For full documentation on the fun of git stashing, type git stash --help .

Local Branching

Another benefit of git is it's extremely easy to create a branch in your local git repository:

    git checkout master
    git branch rt1739
    git checkout rt1739

This makes a local branch off your master branch called "rt1739". You can commit changes to this branch that won't affect your master branch, and you can switch back and forth between your local branches and your master branch. In addition, each of these branches is connected to the trunk of the SVN repository, so any changes dcommitted from these branches will automagically be merged into the SVN trunk and appear in your local trunk the next time you run git svn rebase when you have your trunk checked out.

Other developers may access your local branches, but in order to do so, they would need to be able to access your git repository.

Tracking SVN branches with GIT

Start svn branch in git.

    git svn branch -m "Branch for work on extra cool feature" extra_cool_feature
    git checkout --track -b extra_cool_feature_local remotes/extra_cool_feature # to avoid warning from git about ambiguity
    git reset --hard remotes/extra_cool_feature
    ... hack/git commit/git commit --amend/git rebase -i HEAD~10/... as usual

dcommit to svn

    git svn dcommit --dry-run # check that it will commit to proper svn branch.
    git svn dcommit

Merging trunk into branch

    git checkout master
    git svn rebase
    git checkout extra_cool_branch
    git merge --squash master
    git commit -m "Bring branch up-to-date with trunk" 

Merging branch back to trunk

    git checkout master
    git svn rebase
    git merge --squash extra_cool_feature
    git commit -m "Merge branch into trunk" 

Hints and caveats:

  1. Don't try to rebase already dcommited changes. They are not belong to you anymore.

  1. Always run "git svn dcommit --dry-run" before real commit to check in which branch your changes will be committed.

  1. Always merge branches with "--squash". Otherwise git-svn will do it for you and can epicly fail sometime.
  1. svn isn't smart enough to track merges. So, don't try to merge branches with pure svn and git-svn on same branch.

Ignoring generated files

Svn stores ignores as meta data, git in .gitignore files. You can ignore the SVN metadata (and generated files) with these files. First, edit your .git/info/exclude file to contain:

    .gitignore

... or else git-svn will want to track all .gitignore files. Now generate the .gitignore files automatically by running

    git svn create-ignore

If you have not excluded these files, they will be staged for your next commit. You probably do not want this, as dcommitting them will add them to Parrot's SVN. Beware.

Questions

We've heard a lot about how git will ease the pain of working in branches; can you provide an example, showing a branch that all committers can collaborate on, that can be rebased against trunk as needed, and then merged back to trunk?

More information on git and git-svn

 http://utsl.gen.nz/talks/git-svn/intro.html

 http://www-cs-students.stanford.edu/~blynn/gitmagic/

 http://book.git-scm.com/

Attachments