(Easy) GIT for SVN users

...showing that (easy) git is easier to use overall than subversion

The Basics
Task based comparisons beyond the basics

The Basics

Core commands:
svn checkout svn+ssh://USER@MACHINE.ORG/PATH/TO/MODULE/trunk MODULE eg clone ssh://USER@MACHINE.ORG/PATH/TO/MODULE.REPO
svn status eg status
svn update eg update
svn diff eg diff
svn add FILE eg add FILE
svn commit eg commit
eg push

Other common commands:
svn blame FILE eg blame FILE
svn cat FILE eg cat FILE
svn help [COMMAND] eg help [COMMAND]
svn info eg info
svn mv OLDNAME NEWNAME eg mv OLDNAME NEWNAME
svn resolved PATH... eg resolved PATH...
svn revert PATH... eg revert PATH...
svn rm FILE... eg rm FILE...

SUMMARY: checkout/clone and commit are slightly different, the rest of the basic commands are the same.

Task based comparisons beyond the basics

Switching to a different version of the repository
Creating tags
Creating branches
Committing changes
Stashing changes away
Reverting changes
Showing changes to file contents
Creating new repositories
Merging
Staging changes
Obtaining a subset of the files in a repository
Pulling changes directly from other developers
Publishing a copy of your local repository
Finding the commit that introduced a bug
Removing recent commits from the current branch
Maintaining an independent patch set
Tracking changes from multiple remote repositories

Switching to a different version of the repository

Switch to an older version, or possibly a newer version (see the Revision specifiers section below).
svn update -r REVISION eg switch REVISION

Switching the working copy over to another branch
svn switch svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/branches/BRANCHNAME eg switch BRANCHNAME

Creating tags

Create a tag in a public repository
svn cp . svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/tags/TAGNAME eg tag TAGNAME
eg push --tag TAGNAME

Create a local tag that does not appear in a public repository
Not Available eg tag TAGNAME

Creating branches

Create a branch in a public repository
svn cp . svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/branches/BRANCHNAME eg branch BRANCHNAME
eg push --branch BRANCHNAME

Create a local branch that does not appear in a public repository
Not Available eg branch BRANCHNAME

Committing changes

For the next three examples, I am assuming that the distinction between recording locally or centrally is not relevant. If you want to record changes on some public server, follow each of the eg commit steps with an 'eg push'

Record changes, using the log message specified on the command line
svn commit -m MESSAGE eg commit -m MESSAGE

Record changes, using the text from a specified file for the log message
svn commit -F FILE_WITH_LOG_MESSAGE eg commit -F FILE_WITH_LOG_MESSAGE

Record changes to just certain files
svn commit FILE1 FILE2... eg commit FILE1 FILE2...

Record changes locally only (perhaps you are offline or just need to checkpoint your work without going through a peer review yet)
Not Available eg commit

Modify the last commit (possibly including extra files, more changes, or just a different log message); note that this is a bad idea if the previous commit has been pushed to a public location.
Not Available eg commit --amend

Stashing changes away

Stash changes away and return to a clean slate
cd /PATH/TO/TOP/OF/REPOSITORY eg stash [save OPTIONAL STASH DESCRIPTION TEXT]
svn diff > NAME-OF-STASH.PATCH

Applying an old stash
cd /PATH/TO/TOP/OF/REPOSITORY eg stash apply [OPTIONAL STASH DESCRIPTION TEXT]
patch -p0 < NAME-OF-STASH.PATCH

Listing available stashes
ls *.PATCH # Hope you don't have lots of other patches eg stash list

Reverting changes

Revert changes to files foo.cc and baz.py since the last commit
svn revert foo.cc baz.py eg revert foo.cc baz.py

Revert all the changes since REVISION (see the Revision specifiers section below)
cd /PATH/TO/TOP/OF/REPOSITORY eg revert --since REVISION
svn diff -r REVISION | patch -p0 -R

Revert all the changes made in REVISION (OLDREVISION below is obtained by taking REVISION and subtracting one; see the Revision specifiers section below)
cd /PATH/TO/TOP/OF/REPOSITORY eg revert --in REVISION
svn diff -r OLDREVISION:REVISION | patch -p0 -R

Showing changes to file contents

To understand the form of REVISION, REV1, etc. in this section, see the Revision specifiers section below.

Show the changes made since a specified REVISION, in patch format
svn diff -r REVISION eg diff REVISION

Show the changes between REV1 and REV2 to FILE
svn diff -r REV1:REV2 FILE eg diff REV1 REV2 FILE
Note: You can add a double dash ("--") before FILE in the eg case to distinguish between files and revisions, if needed.

Show the changes between the branch BRANCH and revision REVISION (for the subversion case, we have to assume that REVISION was a change made to trunk, otherwise the svn command changes).
svn diff svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/branches/BRANCH svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/trunk@REVISION eg diff BRANCH REVISION

Show the changes between the branch BRANCH and the working copy
Not Available eg diff BRANCH

Show the changes between the tag TAG and mainline development (trunk for svn and master for eg)
svn diff svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/tags/TAG svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/trunk eg diff TAG master

Show the changes between the tag TAG and the working copy
Not Available eg diff TAG

Show the changes on the current branch since 2 commits before the last one on this branch
svn log # Look for the revision corresponding to two commits ago eg diff HEAD~2
svn diff -r REVISION_WE_LOOKED_UP
(Explanation: Even if you know the current revision number in subversion, you can't always simply subtract 2, because the last two revisions may have been for different branches. In eg, HEAD means the current branch (and branches always point to their most recent commit), so you simply need to request two commits before it.)

Creating new repositories

Create a new empty repository and associated working copy
svnadmin create /RANDOM/PATH/FOR/REPOSITORY mkdir project
svn mkdir file:///RANDOM/PATH/FOR/REPOSITORY/project/{trunk,tags,branches} -m "Set up initial repository structure" cd project
svn checkout file:///RANDOM/PATH/FOR/REPOSITORY/project/trunk project eg init
cd project

Create a new repository and associated working copy, based on an import of files in an existing directory
svnadmin create /RANDOM/PATH/FOR/REPOSITORY cd project
svn import project file:///RANDOM/PATH/FOR/REPOSITORY/project/trunk -m "Initial import of project" eg init
rm -rf project eg add .
svn checkout file:///RANDOM/PATH/FOR/REPOSITORY/project/trunk project eg commit -m "Initial import of project"
cd project

Merging

Merge all changes that were not previously merged in from branch BRANCH into the working copy
svn info svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/branches/BRANCH eg merge BRANCH
<Somehow pick out which changes have not yet been merged in, to obtain the revision range REV1:REV2>
svn merge -r REV1:REV2 svn+ssh://USER@SVN.MACHINE.ORG/PATH/TO/MODULE/branches/BRANCH
Caveats for svn: If you obtain the wrong REV1 and REV2 for subversion, you are likely to get extra spurious changes and conflicts. Rerunning the subversion command a second time guarantees that you'll get conflicts, whereas the eg command will do nothing if immediately rerun. Also, you'll need to carefully check the output in subversion, because the results will likely be wrong if any renames were done on BRANCH. Finally, there will be no record of the merge occurring; the commit logs for the merged changes will not be shown when running svn log, and there's no way to recover that information..unless you manually include any such details in your commit message after performing the merge. That also means that when you are performing a merge, you should carefully look at all preceding log messages for prior merge information.

Staging changes

Mark the current changes in some files as being "ready to be committed"
Not Available eg stage FILE1 FILE2...
Note for users of traditional git: "stage" and "add" are aliases.

Get the changes to FILE1 and FILE2 that are staged (i.e. "ready to be committed")
Not Available eg diff --staged FILE1 FILE2

Get all unstaged changes (i.e. changes that haven't explicitly been marked as ready to be committed)
Not Available eg diff --unstaged

Get all changes (both staged and unstaged) to a file
Not Applicable eg diff FILE

Commit all changes (whether staged or not)
Not Applicable eg commit -a
Note: The -a is short for --all-known.

Commit only the staged changes
Not Available eg commit --staged

Unstage the changes to a file
Not Available eg unstage FILE

Obtaining a subset of the files in a repository

This is a feature of subversion that may be dropped in the future (look for "detachability" in the email pointed to by that link), in order to gain some of the advantages of other systems like git.

Get a copy of just one file, with no associated working copy
svn export svn+ssh://USER@MACHINE.ORG/PATH/TO/MODULE/trunk/PATH/TO/FILE Not Available

Check out one subdirectory, and have it serve as a working copy
svn checkout svn+ssh://USER@MACHINE.ORG/PATH/TO/MODULE/trunk/PATH/TO/SUBDIR Not Available

Pulling changes directly from other developers

This section skips some of the more advanced pulling operations you can do with "refspecs", remote tracking branches, and named remote repositories.

Pull the changes from someone's published http repository (assumes they only have one branch in their repository, or that you have pulled from it before)
Not Available eg pull http://WWW.SERVER.ORG/~USER/PATH/TO/REPO.GIT

Pull the changes from the stable branch of Bob's project in his home directory
Not Available eg pull --branch stable /HOME/BOB/PROJECT

Pull changes from both the testing and funny-stuff branches of a fellow developer's repository on a remote machine (accessed via ssh), merging both branches into your working copy. Then after seeing the merge succeed and be committed, decide to undo the work.
Not Available eg pull --branch testing --branch funny-stuff USER@MACHINE.ORG:/PATH/TO/PROJECT
eg reset --working-copy ORIG_HEAD
Note: ORIG_HEAD is always set at the start of a merge or pull operation to enable easily undoing these operations.

Publishing a copy of your local repository

Publish a copy of your repository on the a machine you have ssh access to
<Configure an svn server to run on the remote machine; see chapter 6 of the svnbook> eg publish USER@EG.EXAMPLE.COM:PUBLIC_HTML/MY_COOL_PROJECT
ssh ADMIN@SVN.EXAMPLE.COM "svnadmin create /PATH/TO/REPOSITORIES/SVN-MIRROR"
<Configure remote svn repository's pre-revprop-change and start-commit hook script to make the repository read-only, or deal with your "recipe for disaster"; see the repository replication section of the svnbook>
svnsync initialize http://SVN.EXAMPLE.COM/SVN-MIRROR /PATH/TO/LOCAL/REPOSITORY --username SYNCUSER --password SYNCPASS
svnsync synchronize http://SVN.EXAMPLE.COM/SVN-MIRROR --username SYNCUSER --password SYNCPASS
Caveats for svn: The published subversion repository has a very limited set of capabilities relative to the eg/git case. For the subversion case, the copied repository has severely restricted capabilities. In particular, one cannot independently commit to the local and remote repository and then merge changes between the two repositories as needed. If one tries to use the copied subversion repository as anything other than a read-only mirror (something the svn developers call a "recipe for disaster"), then the repositories diverge with no reasonable method of reconciliation; they can no longer be used for the same project.

Update the remote repository copy
svnsync synchronize http://SVN.EXAMPLE.COM/SVN-MIRROR --username SYNCUSER --password SYNCPASS eg push

Getting a checkout based on the repository copy
svn checkout http://SVN.EXAMPLE.COM/SVN-MIRROR/trunk PROJECTNAME eg clone http://EG.EXAMPLE.COM/~USER/MY_COOL_PROJECT PROJECTNAME

Finding the commit that introduced a bug

An important note: All of the steps below for eg can be done offline, disconnected from the original repository that you cloned from -- and as a side effect, the operations are also much faster in eg. For svn, log and update both require network connectivity to the original repository and operations are relatively slower. This speed and online vs. offline behavior of eg and svn is true of other operations as well, but the distinction is even more pronounced in this use case than in others.

Getting started at tracking down the buggy commit
<Somehow determine a good revision from history, as well as the bad revision (usually the current one). Typically, one uses 'svn log' and one's own memory for this.> <Somehow determine a good revision from history, as well as the bad revision (usually the current one). Typically, one uses 'eg log' and one's own memory for this.>
eg bisect start

Doing the first split
<Somehow determine a revision in the middle of the good and bad ones. If there are few commits on other branches, averaging the two might work. Otherwise, use 'svn log' and start counting.> eg bisect bad [REVISION_OTHER_THAN_CURRENT_ONE]
svn update -r REVISION_IN_MIDDLE eg bisect good REVISION

If you have to manually test each revision, repeat the following steps as often as necessary:

If you have a script that can compile, link, and test whether a revision works, you can simply use:

Returning the working copy to normal when you are done
svn update eg bisect reset

Removing recent commits from the current branch

Note: This section is not about reverting changes from previous commits by recording a new commit with the old contents. See the reverting changes section for that; this section is about actually removing recent commits from the current branch. This is rarely a good thing to do with commits that have been made available to other people (it can break future merging), but can come in quite handy for commits that have been kept local.

Remove the last 3 commits from the current branch, also returning the working copy to the state of the repository before the last 3 commits. (Note that HEAD always refers to the current branch in eg)
Not Available eg reset --working-copy HEAD~3

Remove the last 2 commits from the current branch, but leave the working copy as it is (could be used, for example, to turn the last two commits into a single commit).
Not Available eg reset HEAD~2

Retroactively place the last 6 commits of the current branch on a different branch, and switch to that branch
Not Available eg branch NEW_DIFFERENT_BRANCH
eg reset --working-copy HEAD~6
eg switch NEW_DIFFERENT_BRANCH

Technically, one can also add commits to the current branch and move the current branch to match an entirely different branch...

Maintaining an independent patch set

Note: An independent patch set would typically be tracked in eg using a separate local branch with all the patches applied in order. This usually doesn't make sense in subversion, since branches are shared and global, so an independent patch set is typically kept as a series of files. One can use eg with a set of separate patch files easily, but maintaining a patch set as a separate branch in subversion makes no sense -- svn has no facility for updating the patches to be relative to new changes on trunk.

Using subversion, you must repeat the following sequence for each patch:

Using eg, you can update the whole patch set as follows:

With eg if any patches could not be automatically rewritten with the above command, then when the above command stops simply fix up the relevant files and then run

Note: Using eg, there is a powerful --interactive flag to rebase that allows you to easily split, combine, remove, insert, reorder, or edit commits. Run 'eg help rebase' for details.

Tracking changes from multiple remote repositories

List all named remote repositories ('remotes' for short)
Not Available eg info
(Just look for the "Named remote repositories" section of the output of eg info)

Add a new remote, giving it an associated name
Not Available eg remote add REMOTENAME URL

You can use the nickname of a remote repository in the place of a URL, but there are also some additional things you can do with remotes:

Delete a named remote repository, including all related remote tracking branches (and any special configuration settings for the named remote)
Not Available eg remote rm REMOTENAME

Grab updates from all(*) named remote repositories, i.e. run 'eg fetch REMOTE' for each remote
Not Available eg remote update
(*) Remotes can be manually configured to be skipped, so this may not really mean 'all' remotes.

Special Note: Revision specifiers

Version control systems use different methods to refer to different versions of the repository. svn counts forward globally, while eg counts backwards relative to each branch. Thus a typical version for svn would be something like "218" while for eg it would be "master~23". You can use the "svn log" and "eg log" commands to get the name of a revision for a particular commit.

The tilde character ("~") is used in eg since minus signs can serve as dashes in branch names (master-23 could be a branch).

Other advantages

A few extra notable advantages not obvious from the list above:

For subversion:

For git: