SCM
Contents
- 1 Git
- 1.1 Configuration
- 1.2 Calculate checksum of remote repository
- 1.3 dangling tree
- 1.4 set-url
- 1.5 Rewrite history
- 1.6 Altering commit data
- 1.7 Merge commits into one
- 1.8 Convert tarballs into git repository
- 1.9 Tags
- 1.10 Search
- 1.11 Repo size without cloning
- 1.12 CVS to Git
- 1.13 SVN to Git
- 1.14 HTTPS checkout
- 1.15 Track remote branches
- 2 Mercurial
- 3 Bazaar
- 4 CVS
- 5 Subversion
- 6 Links
- 7 References
Git
Configuration
Global configuration:[1]
git config --global user.name "John Doe" git config --global user.email john.doe@example.org git config --global color.pager false git config --global pack.threads 0
To send email via git:
git config --global sendemail.smtpserver mail.example.org git config --global sendemail.smtpserverport 587
Calculate checksum of remote repository
$ git ls-remote origin a4c6476651ab11eea605fd09e63acd84bed80284 HEAD a4c6476651ab11eea605fd09e63acd84bed80284 refs/heads/master [...]
Calculate checksum of the local repository:
$ git rev-parse HEAD a4c6476651ab11eea605fd09e63acd84bed80284
git checkout master git fetch origin # May be needed if there are foreign commits in our tree. git reset --hard [commit-before-the-merge] # add origin/master to reset to origin/master
dangling tree
git fsck
would complain about a dangling tree:
$ git fsck --full --strict Checking object directories: 100% (256/256), done. Checking objects: 100% (400/400), done. dangling tree 85200871abce3a8aef5136f1221ff0267ecca339 dangling tree 5050f6d17066212c805458709f18e099b921fd59
The tree was working perfectly and there's only one tree in this repo anyway. After git gc
only one "dangling tree" was left. Another git prune
did the trick, no more dangling trees.
set-url
After cloning a local repository, its origin points to the local resource, instead of the remote resource:
$ git clone foo@alice:/usr/local/src/e2fsprogs-git $ cd e2fsprogs-git $ git remote -v show origin foo@alice:/usr/local/src/e2fsprogs-git (fetch) origin foo@alice:/usr/local/src/e2fsprogs-git (push)
Let's see what the original URL was:
alice$ $ git remote -v origin git://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git (fetch) origin git://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git (push)
So, let's change our newly created clone to point to the upstream repository:
$ git remote set-url origin git://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
Rewrite history
Rewriting history[2] can be done via git amend
. Changing the last commit is easy:
git commit --amend
Changing a commit that is farther back is tricky. Let's rewrite something within the last 3 commits:
git rebase -i HEAD~3
> pick f396a45 abc
> pick 8b29ab1 xxx
> pick cd27e14 yyy
Note: the order here is different from e.g. "git log" and f396a45
is the oldest commit!
In order to change the oldest commit, replace the "pick" to "edit" next to f396a45
and save-exit the editor. Now we can rewrite this commit with:
git commit --amend
With that, rebase
can continue:
git rebase --continue
Altering commit data
Altering various commit entities can be accomplished with git filter-branch
[3]. Let's alter the commit date as an example:
$ git log -n 1 | head -3 commit 27dba1e90110fd1e7b2429c8b17021ba537c471b Author: John Doe <doe@example.org> Date: Sun Feb 22 14:51:54 2015 -0800
But the file we just commited was from a while back:
$ ls -lgo file.c
-rwx------ 1 2767 Feb 5 01:14 file.c
So we want to alter the commit history to match that file date:
H=27dba1e90110fd1e7b2429c8b17021ba537c471b D=2015-02-05T01:14:00 # See git-commit(1): DATE FORMATS
Let's get the correct date format too:
D=$(date -r file.c +%Y-%m-%dT%H:%M:%S)
Now we can alter the commit:
$ git filter-branch --env-filter "if [ \$GIT_COMMIT = $H ]; then \ export GIT_AUTHOR_DATE="$D" export GIT_COMMITTER_DATE="$D"; fi"
The commit date has now been changed (and the commit hash too):
$ git log -n 1 | head -3
commit c510521d1668ff8464e91de96d20fc3b6da70c31
Author: John Doe <doe@example.org>
Date: Thu Feb 5 01:14:00 2015 -0800
The commiter or author can also be changed[4][5] for a single commit:
H=27dba1e90110fd1e7b2429c8b17021ba537c471b AN="Me Surname" AE="me@example.net" CE="committer@example.net" git filter-branch --env-filter "if [ \$GIT_COMMIT = $H ]; then \ export GIT_AUTHOR_NAME="$AN" export GIT_AUTHOR_EMAIL="$AE"; fi"
To rewrite every commit[6] on a branch:
git filter-branch --env-filter "export GIT_AUTHOR_EMAIL=$AE GIT_COMMITTER_EMAIL=$CE" master
git-filter-branch(1)
understands GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_AUTHOR_DATE, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL and GIT_COMMITTER_DATE
Notes:
- “git filter-branch” successfully used to change committer/author, but changes don't reflect on github
- git push after git filter-branch rejected
Merge commits into one
Multiple commits can be merged[7] like this:
$ git log --pretty=oneline 9ef870fca68ecc80a2f3145ed99ffa3baa5521f1 c 69d2072da98250c5ee9f97359ad582b1e8dceb31 b 5ed10e1c6fc507fcd36955e35d650ee7944700da a
We want to combine commits b & c (the last two commits) into one single commit:
git rebase --interactive HEAD~2 > pick 69d2072 b > pick 9ef870f c
Change the "pick" to "squash" for the "c" commit, so that it get's combined with its previous commit, b:
> pick 69d2072 b > squash 9ef870f c
Save & exit from the editor and another editor pops up:
# This is a combination of 2 commits. # The first commit's message is: b # This is the 2nd commit message: c
Edit as needed and save & quit again. Now the history should look like this:
$ git log --pretty=oneline 98f1c6415f98ff9a12a871bc752b678b41c40e13 b and c 5ed10e1c6fc507fcd36955e35d650ee7944700da a
Convert tarballs into git repository
This can be done[8] as such:
git init tar -xf ~/foo.tar git add . git commit -a -s rm -rf *
Now the next tarball can be extracted and we continue with "git add" again:
git add . git commit -a -s rm -rf *
Continue until all tarballs are integrated into the repository.
Tags
Which tag is a certain commit part of?
git describe --contains c022a0acad534fd5f5d5f17280f6d4d135e74e81 v2.6.36-rc1~300^2~1
So, commit c022a0acad534fd5f5d5f17280f6d4d135e74e81
was introduced in v2.6.36-rc1~300^2~1.
Search
To find the commit that introduced a string[9]:
git log -S foo --source path/to/file # Use --all to search the whole repository
Repo size without cloning
For Github repos, their API can be used[10]:
$ curl -s https://api.github.com/repos/git/git | awk '/size/ {print $1, $2, "?B"}' "size": 481892, ?B
...but we don't know the unit size of this value :-\
CVS to Git
While cvs2git may (or may not)[11] be able to do the job, using git-cvsimport was easier to use and actually worked:
$ cat ~/authorconv foobar=Foo Bar <foobar@example.org> $ git cvsimport -v -R -d :pserver:cvs@cvs.example.net:/cvs -A ~/authorconv module
This takes quite some time to complete, but in the end:
$ ls -go .git/cvs-* -rw-r--r--. 1 94 Apr 8 19:55 .git/cvs-authors -rw-r--r--. 1 68611 Apr 8 20:12 .git/cvs-revisions $ git log | grep -c ^commit 712
SVN to Git
Migrating a Subversion repository to Git[12] should be quite straighforward.
Generate the list of authers who commited to the SVN repository:
$ cd ~/project-svn $ svn log --xml | grep \<author\> | perl -pe 's/.*>(.*?)<.*/$1 = /' | sort -u | tee ~/users.txt alice = bob =
We have to define mappings for these authors to git-svn
can use it:
$ cat ~/users.txt alice = Alice <alice@example.net> bob = Bob <bob@example.org>
With ~/users.txt
in place, we can import the repository:
mkdir ~/project-git && cd ~/project-git git svn clone svn://code.example.com/svn/ --authors-file=$HOME/users.txt --no-metadata foobar-git
Now the Git repository should be in place:
$ cd ~/foobar-git $ git log --max-count=1 commit 8b5d45c20bc9cf490e368bc82f058fb4d9739b2c Author: Alice <alice@example.net> Date: Sat Jan 21 19:24:16 2012 +0000 fixed something
HTTPS checkout
When checking out over HTTPS:
$ git clone https://github.com/ioerror/tlsdate.git Cloning into 'tlsdate'... error: Problem with the SSL CA cert (path? access rights?) while accessing https://github.com/ioerror/tlsdate.git/info/refs fatal: HTTP request failed
We may have to install the CA certificates first:
apt-get install ca-certificates
Track remote branches
In an already cloned repository, do:
git remote add foobar https://github.com/kelseyhightower/nocode
Fetch objects from the new branch, including tags:
git fetch --tags foobar
List all remote branches:
$ git remote foobar origin
Update remote branch, locally:
git remote update foobar
Update all remote branches, locally:
git checkout master git remote update
Mercurial
Update a local repository:
hg pull hg update # Or use -u at the pull command
$ hg update -C badbranch # update & switch to badbranch $ hg branches default 2922:c7fa97debb36 badbranch 2924:f58b9c023d3b $ hg commit --user "joe" --close-branch -m "bogus branch" $ hg update -C default # switch back to the default branch again.
View local changes
hg status
Delete everything but .hg* directories, then checkout a clean copy again
ls -A | grep -v .hg | xargs rm -rf hg checkout -C
There's also the Purge Extension. Although it's distributed along with Mercurial, it must be activated:
$ cat ~/.hgrc [extensions] hgext.purge=
Now hg purge
can be used to get rid of untracked objects.
Bazaar
Bazaar seems to be tightly coupled to Launchpad. Checkout out source code requires magical URIs, e.g.:
$ bzr branch lp:gwibber $ cat gwibber/.bzr/branch/branch.conf parent_location = http://bazaar.launchpad.net/~gwibber-committers/gwibber/trunk/
Clean untracked files can be done with Bzrtools:
$ sudo apt-get install bzrtools $ touch foo bar $ bzr clean-tree --dry-run deleting paths: bar foo $ bzr clean-tree --force
CVS
Checkout a module of a remote repository:
cvs -d anonymous@cvs.openssl.org:/openssl-cvs co openssl
Update the repository:
cvs update
List all tags in the repository:
cvs -q log -h | awk -F"[.:]" '/^\t/ && $(NF-1)!=0 {print $1}' | sort -u
List all branches in the repository:
cvs -q log -h | awk -F"[.:]" '/^\t/ && $(NF-1)==0 {print $1}' | sort -u
Setting up a CVS server
tbd...
$CVSROOT/CVRSROOT/passwd virtuser:pwcrypt:unixuser unixuser:pwcrypt anonymous::unixuser (no password) xinetd user = cvs passenv =
PAM, if compiled with --enable-pam /etc/pam.d/cvs @include common-auth @include common-account no $CVSROOT/CVRSROOT/passwd ! xinetd user = root passenv = client: CVSROOT=:pserver:evil:password@sheep:/var/lib/cvs cvs login cvs co . cvs ls -Rl ./
Subversion
branches & tags
List all branches of a repository by listing the /branches
URL. This is often below the same level as /trunk
$ svn info | grep ^URL URL: http://svn.example.com/svn/trunk $ svn ls http://svn.example.com/svn/branches/ 1.3/ 1.4/ 1.5/
List all tags:
$ svn ls ^/tags 1.3-release 1.4-release 1.5-release
Checkout[13] a different branch into a new directory:
svn checkout http://svn.example.com/svn/branches/1.5 ../project-version-1.5
Checkout a different tag into a new directory:
svn checkout http://svn.example.com/svn/tags/1.3-release ../project-1.3-release
switch
To change the repository URL, use "switch"
[14]:
$ svn info | grep ^URL URL: http://svn.example.com/branches/3.4 $ svn switch http://svn.example.com/branches/3.5
Or, using tag instead of branch:
$ svn ls ^/tags | tail -3 3.3/ 3.4/ 3.5/ $ svn switch http://svn.example.com/svn/tags/3.5
But what if the repository URL has changed in a more subtle way? E.g. we want to change the branch and the protocol[15]:
$ svn switch https://svn.example.com/branches/3.5 svn: 'http://svn.example.com/branches/3.4' is not the same repository as 'https://svn.example.com'
Hm, lets try relocate[16] then:
$ svn switch --relocate http://svn.example.com/branches/3.4 https://svn.example.com/branches/3.5 . svn: Relocate can only change the repository part of an URL
However, when we try this in two steps, it seems to work:
svn switch --relocate http://svn.example.com/branches/3.4 https://svn.example.com/branches/3.4 . svn switch https://svn.example.com/branches/3.5 $ svn info | grep ^URL URL: http://svn.example.com/branches/3.5
svn:externals
Adding an external repository to an SVN project-directory. For example, adding the WP-reCAPTCHA repo to the Wordpress source tree:
$ cd wp-content/plugins $ svn propedit svn:externals . > wp-recaptcha https://plugins.svn.wordpress.org/wp-recaptcha/trunk/ > :x $ svn update Fetching external item into 'wp-recaptcha' External at revision 255348.
To add the externals to the SVN root, use the relative path instead:
$ cd wordpress-svn $ svn propedit svn:externals . > wp-content/plugins/wp-recaptcha https://plugins.svn.wordpress.org/wp-recaptcha/trunk/ > :x $ svn update
Links
References
- ↑ Git_Guide: Any other initial setup I need?
- ↑ 6.4 Git Tools - Rewriting History
- ↑ How can one change the timestamp of an old commit in Git?
- ↑ Changing the committer
- ↑ Change commit author at one specific commit
- ↑ Running filter-branch over a range of commits
- ↑ How can I merge two commits into one?
- ↑ Is there a way to easily convert a series of tarballs of a source tree into a git repository?
- ↑ git: finding a commit that introduced a string
- ↑ See the size of a github repo before cloning it?
- ↑ Is there a migration tool from CVS to Git?
- ↑ Git and Other Systems - Migrating to Git: Subversion
- ↑ svn checkout
- ↑ svn switch
- ↑ svn: Repository moved permanently; please relocate
- ↑ svn relocate