svn to git cheat sheet
Please consider using ssh keys for git. To use SSH keys, see
this article. Consult with SH if you need more help - modern Linux (and Mac/Windows?) environments provide secure wallet where unlocked ssh keys are kept, so you don't have to retype SSH private key password.
Public/Private keypair in Centos 7
- run seahorse (from terminal) or Applications/Utilities/Passwords and Keys
- Click
(big "+")
- Select Secure Shell Key
- Click Continue
- add description
- Click Just Create Key
- enter (twice) password. Must have at least 5 characters
Once you are done, you will find public part of the key in ~/.ssh/id_rsa.pub. Copy content of this file to Github settings page and you are done
Alternatively:
- ssh-keygen (for the command line inclined)
Keys on Mac, Windows
Please see
https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent for details.
Setting up git
You need to tell git who you are and what's your email, so it can produce proper log entries. To do that, run:
%CODE{ lang="bash" }%
git config --global user.name "My Name"
git config --global user.email
%ENDCODE%
from console/terminal. This is a one-time action on a given machine - you don't need to repeat the exercise. Replace options with the one matching your GitHub account (email is what is used for user identification).
It's higly encouraged to add some colors with:
%CODE{ lang="bash" }%
git config --global color.ui auto
%ENDCODE%
Cheat sheet
You can specify git URL (used in a clone,..) starting with git@github.com or https://<username>@github.com/. For the git@github.com, you will need to set up public/private key pair (see above).
svn |
git |
comments |
svn co (checkout) https://svn.lbto.org/repos/tcs |
git clone git@github.com:LBTO/tcs.git |
svn up (update) |
git pull |
svn add |
git add |
svn diff |
git diff |
svn ci (commit) |
git commit && git push or |
git commit git push |
svn log |
git log |
svn blame |
git blame |
svn sw (switch) ^/branches/2018A |
git fetch --all && git checkout 2018A git checkout origin/2018A |
git fetch --all is needed only if the branch isn't in your local repository, e.g. was created after clone |
svn merge ^/trunk |
git merge master |
svn merge -c 12345 ^/trunk |
git cherry-pick <sha-has> |
see Gitrevisions for ways how to spell revisions |
rm filename && svn up |
git checkout -- filename |
Differences between SVN and GIT:
- git uses two stages commit. The git commit command (preceded by git add for new files) tells GIT to commit locally file. No changes are done on a remote end (e.g. GitHub). The local commits are propagated (pushed) to remote end with git push
- this allows you to develop, commit your work locally once you are happy, stage commits, and then push back to remote end once you are happy with the result
- moreover, that allows you to push to multiple remote ends (design feature of GIT for kernel developers)
- git commit without argument will commit all changed files (which were *git add*ed or were pulled from the repository). It is always better to provide a list of files to commit. Tabulator completion will give you the list of files you can commit.
- git pull command will complain if you make some local change. You have two options:
- run git pull --rebase to automerge remote end, without creating an extra entry in git log
- run git stash, followed by git pull, and git stash pop - that will put your diff into "stash", which is temporary storage for diffs, and then pop it from stash list (= will apply again the changes you made).
GIT advantages
- fast (particularly log browsing/history browsing), as you have a complete copy of the repository. Compare the speed of svn diff -r 12345 with what gitg GUI provides
- you can play locally before sending all to master
- stash command to get out your changes as diff and make your copy clean (without any changes) to later re-apply with stash pop command
- you can start local branches and after you are happy, merge to master - you don't spoil master with your progress, but can still commit after achieving part of the fix
- rebasing (SVN doesn't have anything like that)
Git philosophy
Being a distributed version control system, Git must obey a bit different (and sometimes odd) rules on how to carry development. Understanding those algorithms will help a bit understanding what's going on.
When you clone a repository, you do not only hold the history of changes (the current files plus all changes applied to them from the point the files were created), but also metadata (who committed changes, tags, ..) and, more importantly, git hashes. Git hashes (the concept of hashes is beyond scope of this document, see Cryptographic hash function on Wikipedia) computed from commit metadata and previous commit (=history) allows hashes to be used to verify the integrity of the Git (and history of commits). Git can quickly compare (using a hash) if two heads (=commits) are the same, the feature used when pulling/merging requests to a server.
Git is a sort of distributed ledger. Your ledger has entries secured by unique hashcodes, which depends on previous activity (as hashcode is calculated from your latest commits, combined with what was already committed. You have to keep synchronizing your local repository with others (which is in our case greatly simplified by having the central repository on GitHub). The commands to list what your local copy knows about others (what others know about your copy is irrelevant) are listed below.
The oddities are described on this and those web pages - but please bear in mind this is pre-GitHub (which allow for a kind of centralized development and bring in pull request) and does not dive into other source versioning systems problems. Except for pull request, the major upgrade from SVN is in rebasing - SVN does not allow for that.
Description of how the branching can be used is in there.
Git kung-fu
Following paragraphs provides some command useful in everyday git use.
Git Rebasing
You are probably familiar with merging branches. Git introduces another option to join branches - rebasing. In order to know how and when to use it, you must understand how it works and what it does. Rebasing works similar to merge, allowing to join development from two branches. Usually, you call git rebase <destination_branch> from your origin branch, so the command knows about two branches (the one you are into and the one you are rebasing to). There is Git documentation on the topic, but to make things quicker, rebasing work in following steps:
- find a common ancestor between your branch and branch you are rebasing to (since everything in Git repository starts from a single branch, there must be such ancestor)
- removes from your history all changes between your branch and the common ancestor
- switch into the destination branch (the branch you are rebasing to)
- replay on top of the destination branch changes performed in your local branch (reapply what was done in your origin branch)
After rebasing, your origin branch head is put on top of the destination branch. You can then merge (with ff) to move destination branch top to be equal to the origin branch and delete (git branch -d <origin>) the origin branch.
Git reset
Git reset is the almost omnipotent command, having three main uses:
%CODE{ lang="bash" }%
# reverts files staged for commit (added with git add)
git reset
# removes entries from ledger (git log) up to a given point; changes to files are kept, see git diff after performing reset
# the last entry
git reset HEAD~1
# the last 3
git reset HEAD~3
# down to a given hash
git reset af3443f213abcdf
# hard, overwrite changes (THIS ONE IS DANGEROUS)
git reset --hard
# hard to latest state at given branch (THIS IS DANGEROUS AS WELL)
git reset --hard upstream 2018A
%ENDCODE%
Restoring files
If you hit a dead end during your development, or you are only adding temporary log entries to see how is the algorithm working or where can be a problem and would like to get rid of your temporary changes, use git checkout --:
%CODE{ lang="bash" }%
# restore changes to Main.cpp (OVERWRITE YOUR LOCAL CHANGES)
git checkout -- Main.cpp
%ENDCODE%
Git and references
You local git clone keeps entries describing where the code is kept, so git pull and git push commands works without providing details where to look on:
%CODE{ lang="bash" }%
# show remote (aliases)
git remote -v show
origin git@github.com:LBTO/tcs (fetch)
origin git@github.com:LBTO/tcs (push)
# shows branches and their remote tracking (where pull/push without target argument will look), * (star) marks the current branch (use git checkout to switch branches)
git branch -vv
2018A 9da53f1 [origin/2018A] Merge pull request #20 from pkubanek/2018A
* IT3949 353fc50 [origin/IT3949] start TTL thread
master 1e18a29 [origin/master] Merge pull request #14 from krsummers/master
%ENDCODE%
The above is the expected configuration. Push and pull without a target will be directed to LBTO/tcs GitHub repository.
Example - disentangling two changes
On shared home drives, in /home/pkubanek/git-ex1-tcs, is stored a Git snapshot. While working on Time To Limit issues (IT3949), it was discovered that C++11 standard usage will bring a lot of benefits to the code. Changes were made to accommodate for the C++11 standard.
Now those are included in changes for IT3949, which isn't good. Separating those into a different branch would be great. You might reproduce the first steps by cp -ra /home/pkubanek/git-ex1-tcs /tmp/tcs && cd /tmp/tcs and working in /tmp/tcs. Please don't try to push anything.
%CODE{lang="sh"}%
# while on IT3949 branch
[tcs]$ git add tcs/core/Configuration.cpp tcs/core/Configuration.hpp pcs/TimeToLimit.cpp pcs/TimeToLimit.hpp pcs/PCS.cpp pcs/pcsgui/pcsgui.cpp pcs/etc/pcs.conf pcs/PCS.hpp pcs/test/
[tcs]$ git commit
IT3949 dc1edb6] TTL events times are configurable
12 files changed, 202 insertions(+), 56 deletions(-)
create mode 100755 pcs/test/3949/left_LUCI
create mode 100644 pcs/test/3949/stars_rot.dat
create mode 100644 pcs/test/3949/test.dat
[tcs]$ git checkout master
M Makefile
M aos/aosupervisor/AONanWrapper.hpp
M dds/DDS.hpp
M dds/Main.cpp
M ecs/Main.cpp
M env/Main.cpp
M iif/commands/RotateCommon.hpp
M mcs/Main.cpp
M oss/Main.cpp
M pcs/Main.cpp
M pmc/Main.cpp
M psf/Main.cpp
M psf/PSF.hpp
M psf/PSFSecondaryMirror.hpp
M psf/PrimaryMirror.hpp
Switched to branch 'master'
[tcs]$ git checkout -b C++11
M Makefile
M aos/aosupervisor/AONanWrapper.hpp
M dds/DDS.hpp
M dds/Main.cpp
M ecs/Main.cpp
M env/Main.cpp
M iif/commands/RotateCommon.hpp
M mcs/Main.cpp
M oss/Main.cpp
M pcs/Main.cpp
M pmc/Main.cpp
M psf/Main.cpp
M psf/PSF.hpp
M psf/PSFSecondaryMirror.hpp
M psf/PrimaryMirror.hpp
Switched to a new branch 'C++11
# change in Configuration.cpp is needed
[tcs]$ vim tcs/core/Configuration.cpp
[tcs]$ git diff tcs/core/Configuration.cpp
diff --git a/tcs/core/Configuration.cpp b/tcs/core/Configuration.cpp
index 6a43bab..500c989 100644
a/tcs/core/Configuration.cpp
+++ b/tcs/core/Configuration.cpp
@@ -124,7 +124,7 @@ public:
set< string > const & getFormattedLines() const { return *lines_; }
private:
- shared_ptr< set< string > > lines_;
+ tr1::shared_ptr< set< string > > lines_;
};
# make sure the branch compiles
[tcs]$ make clean && make -j8
# commit (and push -u to create C++11 branch, but don't try to push for you)
[tcs]$ git commit .
[C++11 b51fb03] C++11 compilation
16 files changed, 51 insertions(+), 50 deletions(-)
# [tcs]$ git push -u origin C++11
# Counting objects: 63, done.
# Delta compression using up to 8 threads.
# Compressing objects: 100% (32/32), done.
# Writing objects: 100% (32/32), 2.54 KiB | 0 bytes/s, done.
# Total 32 (delta 31), reused 0 (delta 0)
# remote: Resolving deltas: 100% (31/31), completed with 31 local objects.
# To git@github.com:LBTO/tcs
# * [new branch] C++11 -> C++11
# Branch C++11 set up to track remote branch C++11 from origin.
%ENDCODE%
After that, a pull request from C++11 to master is made (create pull request on GitHub). After approval, those steps needs to follow (you cannot reproduce those on your setup, as those were already done):
%CODE{lang="sh"}%
[tcs]$ git checkout master
Switched to branch 'master'
%ENDCODE%
GIT GUI
- gitg - install with sudo yum install gitg
- gitk and git-gui - old timer tcl-tk (unrivalled last time I checked but that was a long time ago. MB)
- More guis..
About GIT
Converting SVN to GIT
First, store as users.txt the following text:
cbiddick = Chris Biddick <cbiddick@lbto.org>
cjb = Chris Biddick <cbiddick@lbto.org>
ksummers = Kelleee Summers <ksummers@lbto.org>
pgrenz = Paul Grenz <pgrenz@lbti.org>
pkubanek = Petr Kubanek <pkubanek@lbto.org>
shooper = Stephen Hooper <shooper@lbto.org>
tedgin = Tony Edgin <tedgin@lbto.org>
(no author) = No Author <anonymouse@lbto.org>
delapena = Michele De La Pena <mdelapena@lbto.org>
mdelapena = Michele De La Pena <mdelapena@lbto.org>
shooper = Stephen Hooper <shooper@lbto.org>
svnadmin = SVN admin <svnadmin@lbto.org>
dlt = David Terrett <david.terrett@stfc.ac.uk>
dterret = David Terrett <david.terrett@stfc.ac.uk>
dterrett = David Terrett <david.terrett@stfc.ac.uk>
jeff = Jeff Neubauer <jneubauer@lbto.org>
lbtscm = SVN admin <svnadmin@lbto.org>
tsergant = Tom Sargent <tsargent@lbto.org>
tsargent = Tom Sargent <tsargent@lbto.org>
trowitzsch = Jan Trowitzsch <trowitzsch@mpia.de>
briegel = Florian Briegel <briegel@mpia.de>
borelli = José Borelli <borelli@mpia.de>
jborelli = José Borelli <borelli@mpia.de>
ccox = Chris Cox <ccox@lbto.org>
leibold = Torsten Leibold <tleibold@lbto.org>
felix = Felix Krämer <kraemer@leibniz-kis.de>
tmh = Thomas Hahn <thahn@aip.de>
thahn = Thomas Hahn <thahn@aip.de>
tgolota = Taras Golota <tgolota@email.arizona.edu>
The best is to follow instructions from https://epicserve-docs.readthedocs.io/en/latest/git/svn_to_git.html:
%CODE{lang="sh"}%
git svn clone https://svn.lbto.org/repos/ --no-metadata --authors-files=users.txt --stdlayout /tmp/temp
# convert svn:ignore to .gitignore
cd /tmp/temp
git svn show-ignore -i trunk > .gitignore
# edit .gitignore and clean it
git add .gitignore
git commit -m 'Convert svn:ignore to .gitignore.'
git init --bare /tmp/new-bare.git
cd /tmp/new-bare.git
git symbolic-ref HEAD refs/head/trunk
# push repository to a bare git repository
cd /tmp/temp
git remote add bare /tmp/new-bare.git
git config remote.bare.push 'refs/remotes/*:refs/heads/*'
git push bare
# rename trunk branch to master
cd /tmp/new-bare.git
git branch -m trunk master
# clean up branches and tags
git for-each-ref --format='%(refname)' refs/heads/tags |
cut -d / -f 4 |
while read ref
do
git tag "$ref" "refs/heads/tags/$ref";
git branch -D "tags/$ref";
done
%ENDCODE%
When this is done (sometimes the process fails and needs to be restarted, rm -rf ${DIR} and try again), you can run the following to populate GitHub repository from your local git created from SVN. Be aware that push -f will overwrite GitHub repository - use the command with caution!!:
%CODE{lang="sh"}%
cd /tmp/new-bare.git
git remote add origin git@github.com:LBTO/ # repository shall be created, it's up to you which name you agree on
git push -f # think twice; this will force overwrite LBTO/ on GitHub!
%ENDCODE%
If you need to convert only a directory from SVN repository, you can do that using for clone:
%CODE{lang="sh"}%
DIR= && git svn clone https://svn/repos/ --trunk="trunk/${DIR}" --branches="branches/*/${DIR}" --authors-file=users.txt --no-metadata -s ${DIR}
%ENDCODE%
where you can fill <directory> with SVN directory you want to convert (or leave it blank) and <repo> with SVN repository name. If you remove --no-metadata. git log will not include reference to SVN commits IDs.
-- %USERSIG{PetrKubanek - 2018-03-14}%