Tweets by @markusgattol |
A few introducing words...
My Notions on the MatterFor those who want to know what SCM systems are out there and how they compare — there is a list of SCM systems as well as a comparison available. There is also a comparison among SVN and GIT available. As of now (August 2008), I mainly use GIT (= a random three-letter combination) to manage code and to do all kinds of work related to software/data on my computer systems. Before that, my main code revision and management system for about two years or so had been SVN (Subversion). And even before that I used a greater variety of SCM (Software Configuration Management) systems including CVS (Concurrent Versions System), GNU Arch and Darcs. The situation now is that I use mainly GIT and SVN and a little bit of CVS and GNU Arch every now and then. GIT is used for my own projects and those I actively contribute to. I also contribute to projects using SVN but for the most part SVN usage is limited to get the SVN HEAD from the remote repository to my local working copy. CVS and GNU Arch is only used for updating the local working copies only — I do not use them for active development anymore. Roughly speaking, the reason why I ended up only using two (one to be more precisely) SCM systems actively now is that, for some time now, I try to consolidate1 pretty much everything. Also, I abandoned every redundancy I could identify because I do not need/want two or more things providing the same functionality. The gain from doing so is that one frees up a lot of time for other things plus one gets to know those things that are left in more detail and thus he is able to work more efficient. Why GIT?It is important to note that GIT is very different from most SCM systems that we may be familiar with. Subversion, CVS, Perforce, Mercurial and the like all use Delta Storage systems — they store the differences between one commit and the next. GIT does not do this — it stores a snapshot of what all the data in our project looks like in the tree structure each time we commit. This is a very important concept to understand when using GIT. Some of the reasons why I finally favor GIT over all other SCM systems can be told in brief:
There are other reasons as well but those are the main reasons why I find GIT the best solution for me and what I do on a daily basis. It is even so that I import code from other SCMs into GIT, work on the code and when I am done, I push the code from GIT back to whatever upstream SCM system a particular project uses. I cover this further down... GIT Glossary and PrinciplesI decided to intentionally put this not to the end of the page put here. Best would be to skim over it once, then go read the reminder of the page and finally read it a second time in-depth. Glossary
PrinciplesAside from all the terms used with GIT, it is important to understand the core principles how GIT works in order to use it successfully. The nature of a DSCM SystemOf course, there are fundamental differences in how centralized and decentralized SCM systems build and work. This subsection names two major differences and, from my point of view, advantages of DSCM systems. Everything is LocalThis is basically true of all the distributed SCM systems, but in my
experience even more so with GIT. There is very little outside of That may not sound like a big deal, but many of us often work offline. Being able to branch, merge, commit and browse history of a project while on the plane, train or riding with the AEP (Autonomous Expedition Platform) vehicle trough the Outback while your buddy is driving, is a big plus that comes with a DSCM system as is GIT.
Even in Mercurial, common commands like This means that it is very easy to have copies of not only our branches, but also of everyone else's branches that we are working with in our GIT repository without having to mess up their stuff. No Single Point of FailureI already mentioned that above but it is actually so great that I am talking about it again. One of the coolest features of any of the Distributed SCMs, GIT included, is that it is distributed. This means that instead of doing a checkout of the current tip of the source code, we do a clone of the entire repository. This means that even if we are using a centralized workflow, every user has what is essentially a full backup of the main repository, each of which could be pushed up to replace the main repository in the event of a hardware failure or software triggered corruption. There is basically no single point of failure with GIT unless there is only a single point e.g. a repository that has not been mirrored/cloned by someone else. Repository LayoutIt is quite interesting and helpful to grasp the big picture about GIT
aside from the daily usage of GIT. Understanding the layout of a GIT
repository and its meaning and implications on daily usage can be very
helpful in avoiding misuse of GIT that may badly affect ones work. Tree vs. CommitA tree is a particular object type. It represents a particular directory state of a working directory whereas a commit represents that state in time, and explains how we got there. We create a commit object by giving it the tree that describes the state at the time of the commit, and a list of parent trees (those tree states that lead up to the current one). Working Tree vs. Index vs. HEADWhen we have a piece of code/data under GIT's control and make changes
to it (e.g. editing some text file, removing/adding/altering a bitmap,
etc.), the journey those changes take are in essence like this:
I will go into more detail later when we talk about GIT's workflow. Anyway, there is a number of commands which are useful for keeping track of what we are about to commit:
Now, the alerted reader might have asked himself already, we can
commit changes all the way from the working tree, over the index,
right into
However, how do we get changes from the working tree into the index
without committing them all the way through to
The index holds a snapshot of the content of the working tree, and it
is this snapshot that is taken as the contents of the next commit.
Thus after making any changes to the working directory, and before
running Of course, as the best practices example outlined,
Detailed Look at the IndexMany a times the subject comes up on the mailing list or IRC (Internet Relay Chat) channel, Why keep the index? or The index is a performance trick?. The truth is, the index is a staging area. Every SCM system has it, but GIT explicitly exposes it to us. A staging areaFor those familiar with CVS, SVN or similar archaic stuff, what
happens when we do With the second command, we can finally commit. But what happens to
the other modified files? Are they committed? The answer is no, the
last revision is updated with the new version of So really, it is neither a new concept, nor an intimidating one. The
index comes naturally to us when we issue And here comes the difference to CVS: once we put something into the
index, a simple One special case exists though. Let us assume we issue This operation — save the current staging area, construct a new one, commit it, and then restore the staging area — seems a bit illogical, since we would usually expect only one staging area. However, in practice it happens quite often that we forget to commit something very important. So, all we have to do is to just edit the respective files, commit just these, and continue with what we were doing before. In essence: The index is a staging area for the next commit, but for
convenience, passing filenames explicitely to MergesNormally, a GIT user will rarely be exposed to the index if he is not committing a revision. But there is one notable exception: merging. When we merge the work of others, sometimes conflicts happen. These are put in the index. Strictly speaking, the whole merge is done inside the index by inserting the current version, the version of the branch-to-be-merged, and the merge base into the index, and merging them using a three-way-diff. If there are no conflicts, these three entries are collapsed into a single entry. Otherwise the three entries stay there, with the common ancestor being replaced by the result of the merge. Again, GIT is intelligent about what to show us upon a Now we know what the index is good for — as mentioned above, the index it is neither a new concept, nor an intimidating one. The index is our friend and companion! File stagesAssuming two branches contain the same file i.e. Recall that the commit which will be committed after we resolve this conflict will have two parents instead of the usual one:
During the merge, the index holds three versions of each file. Each of these three file stages represents a different version of the file:
Each time we resolve the conflicts in a file and update the index Installing and Configuring GITThis section will tell about how to install GIT and how to configure it afterwards. Installing GITInstalling GIT is trivial. Just issue wks:/home/sa# apt-get install git-core Reading package lists... Done Building dependency tree Reading state information... Done git-core is already the newest version. 0 upgraded, 0 newly installed, 0 to remove and 2 not upgraded. wks:/home/sa# which does the trick and installs GIT. Note, that I already had it installed. One might find it a bit strange "Just one package and that is it? ... I do not believe...". This person might take a look at ,----[ apt-file list git-core | grep bin/ ] | git-core: usr/bin/git | git-core: usr/bin/git-add | git-core: usr/bin/git-add--interactive [skipping a lot of lines...] | git-core: usr/bin/git-am | git-core: usr/bin/git-whatchanged | git-core: usr/bin/git-write-tree `---- That is the current (Sat Aug 25 16:53:27 UTC 2007) status of the
notable contents of the I strongly recommend to also the package sa@wks:~$ acsn git | grep ^git-doc git-doc - fast, scalable, distributed revision control system (documentation) sa@wks:~$ For later use you might install more as you need it — DebianGNU/Linux provides a bunch of GIT related packages ,----[ apt-cache search --names-only git | grep ^git ] | git - GNU Interactive Tools, a file browser/viewer and process viewer/killer | git-arch - fast, scalable, distributed revision control system (arch interoperability) | git-buildpackage - Suite to help with Debian packages in Git repositories | git-completion - content addressable filesystem (bash completion) | git-core - fast, scalable, distributed revision control system | git-cvs - fast, scalable, distributed revision control system (cvs interoperability) | git-daemon-run - fast, scalable, distributed revision control system (git-daemon service) | git-doc - fast, scalable, distributed revision control system (documentation) | git-email - fast, scalable, distributed revision control system (email add-on) | git-gui - fast, scalable, distributed revision control system (GUI) | git-load-dirs - Import upstream archives into git | git-svn - fast, scalable, distributed revision control system (svn interoperability) | gitk - fast, scalable, distributed revision control system (revision tree visualizer) | gitweb - fast, scalable, distributed revision control system (web interface) | git-p4 - fast, scalable, distributed revision control system (p4 interoperability) `---- Do not get confused about the package sa@wks:~$ acsn git | grep '^git ' git - GNU Interactive Tools, a file browser/viewer and process viewer/killer sa@wks:~$ Configure GITWe will postpone this until we have seen how to carry out basic tasks with GIT. TaxonomyIt is so that the GIT community identifies several sets of commands depending on their abstraction level (high level versus low level) and if they belong to the core git package or to some ancillary tools. We name high level (porcelain) commands and low level (plumbing) commands:
These matters are beyond the scope of this page and will not be
covered since it is only of interest to the power-user or developer.
However, the interested reader might issue Using GITThere is lots and lots of information available to all sorts of tasks
one might carry out with GIT. Because of that, I will not provide
another tutorial nor write some documentation. If you are new to GIT
then you might want to take a look at GIT Wikis documentation page
and/or read the GIT user manual. I also strongly recommend to read the
man page i.e. However, I will provide some shortscreen dumps and information on topics that I needed for myself. This section is split into two subsections — one covering knowledge that everyone needs on a daily basis and the second subsection covering some things that look a bit deeper into what can be done with GIT. WorkflowThis is probably one of the most interesting subsections to read for folks who are planning on using GIT or maybe have already started using GIT. Here I will tell about the workflow with regards to GIT from different angles:
Low-level Look a the Local WorkflowGenerally, all GIT operations work on the index file. Some operations work purely on the index file (showing the current state of the index), but most operations move data to and from the index file. Either from the database or from the working directory. Thus there are four main combinations:
Below we will look at all of those four combinations, but before we do so, there is a sketch picturing the local workflow right below:
This piece of ASCII art illustrates how various pieces fit together.
It features the current states (boxes) and the commands to make the
transition from one state to another with the name of the objects at
the current states. Please note that all the commands mentioned below
are not intended to be used by the end user i.e. instead of
git-commit-tree commit obj +----+ | | | | V V +-----------+ | Object DB | | Backing | | Store | +-----------+ ^ git-write-tree | | tree obj | | | | git-read-tree | | tree obj V +------------------+ | Index | +------------------+ ^ git-update-index | blob obj | | | | git-checkout-index -u | | git-checkout-index stat | | blob obj V +-----------+ | Working | | Directory | +-----------+ Working Directory to IndexWe update the index with information from the working directory with
the However, to avoid common mistakes with filename globbing etc., the command will not normally add totally new entries or remove old entries, i.e. it will normally just update existing cache entries. To tell git that yes, we really do realize that certain files no
longer exist, or that new files should be added, we should use the
As a special case, we can also do Index to Object DatabaseWe write our current index file to a tree object with Object Database to IndexWe read a tree file from the object database (also known as GIT back end), and use that to populate (and overwrite i.e. we should not do this if our index contains any unsaved state that we might want to restore later!) our current index. The low-level operation to accomplish this would be Index to Working DirectoryWe update our working directory from the index by checking out files.
This is not a very common operation, since normally we would just keep
our files updated rather than write to our working directory, we would
tell the index files about the changes in our working directory (i.e.
However, if we decide to jump to a new version, or check out somebody
else's version, or just restore a previous tree, we would populate our
index file with
High-Level Look at the WorkflowI suppose this is probably the most interesting subsection within the workflow section — a high level view on the workflow, involving not just the local repository but also interacting with remote repositories, this time using GIT's high-level commands also known as porcelains. Instead of going to explain things with words, I opted to have one picture that pretty much tells us all there is about ones every day workflow with GIT.
I used Inkscape to create this work. I got asked a lot if I could provide a PDF — here it is, optimized for DIN A4 for those who would like to print it. However, the PDF export scrambles the fonts a bit and so I would recommend to stick with the bitmap. Update: I also found another nice imagery on the net depicting GIT's high-level workflow Workflow ModelsOne of the amazing things about GIT is that because of its distributed nature and super branching system, we can easily implement pretty much any workflow we can think of. Subversion-Style WorkflowA very common GIT workflow, especially from people transitioning from a centralized system, is a centralized workflow. GIT will not allow us to push if someone has pushed since the last time we fetched, so a centralized model where all developers push to the same server works just fine.
Integration Manager WorkflowAnother common GIT workflow is where there is an integration manager — a single person who commits to the blessed repository, and then a number of developers who clone from that repository, push to their own independent repositories and ask the integrator to pull in their changes. This is the type of development model we often see with open source repositories. I also use this model to maintain and further develop this website/platform i.e. I am the integration manager who solely maintains the blessed repository where all contributors pull/fetch from. They make changes, I then fetch from their independent repositories and so forth. Of course I am also a contributor aside from being the integration manager ;-]... Thanks to GIT's mighty branching powers, that is no problem...
Dictator and Lieutenants WorkflowFor more massive projects, we can setup our developers similar to the way the Linux kernel is run, where people are in charge of a specific subsystem of the project (the lieutenants) and merge in all changes that have to do with that subsystem. Then another integrator (the dictator) can pull changes from only his/her lieutenants and then push to the blessed repository that everyone then clones from again.
Again, GIT is entirely flexible about this, so we can mix and match and choose the workflow that is right for us. Mandatory KnowledgeThis subsection is about what I need on a daily basis and thus it is knowledge that should be known without ever having to look things up. Getting HelpThe best help is what is at our hands at any times. With the If we have access to the Internet then we might also want to check at
sa@wks:~$ git --help usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [-p|--paginate|--no-pager] [--bare] [--git-dir=GIT_DIR] [--work-tree=GIT_WORK_TREE] [--help] COMMAND [ARGS] The most commonly used git commands are: add Add file contents to the index apply Apply a patch on a git index file and a working tree archive Create an archive of files from a named tree bisect Find the change that introduced a bug by binary search branch List, create, or delete branches checkout Checkout and switch to a branch cherry-pick Apply the change introduced by an existing commit clone Clone a repository into a new directory commit Record changes to the repository diff Show changes between commits, commit and working tree, etc fetch Download objects and refs from another repository grep Print lines matching a pattern init Create an empty git repository or reinitialize an existing one log Show commit logs merge Join two or more development histories together mv Move or rename a file, a directory, or a symlink prune Prune all unreachable objects from the object database pull Fetch from and merge with another repository or a local branch push Update remote refs along with associated objects rebase Forward-port local commits to the updated upstream head reset Reset current HEAD to the specified state revert Revert an existing commit rm Remove files from the working tree and from the index show Show various types of objects show-branch Show branches and their commits status Show the working tree status tag Create, list, delete or verify a tag object signed with GPG (use 'git help -a' to get a list of all installed git commands) sa@wks:~$ The person who knows and understands these commands (the main set from porcelains) can pretty much do anything he ever wants to do. All the rest that git offers is thought to be beyond the scope of the every-day-users needs. If we need anything aside from the above then we can simply go look it up in the man files or elsewhere. I know the above commands, use them on a daily basis from the CLI (Command Line Interface) or even better, I use the emacs frontend and it is not often that I have to use some other commands except for maintenance on repositories matters. In order to get help about a particular command e.g. Creating A New RepositoryWith this subsection, I will show how to create a new repository in a few ways depending on the current situation where we start from. From a Common Directory:Usually people have their directory structure already in place when they start out using GIT — thus they want to bring their file system or parts of it under version control with GIT. 1 sa@wks:~$ cd /tmp/ 2 sa@wks:/tmp$ mkdir commondir 3 sa@wks:/tmp$ cd commondir/ 4 sa@wks:/tmp/commondir$ cp /ws/local/scm.muse . 5 sa@wks:/tmp/commondir$ la 6 total 96 7 drwxr-xr-x 2 sa sa 4096 2007-09-13 14:10 . 8 drwxrwxrwt 20 root root 12288 2007-09-13 14:10 .. 9 -rw-r--r-- 1 sa sa 74463 2007-09-13 14:10 scm.muse 10 sa@wks:/tmp/commondir$ Nothing unusual here. All I did was to create a new directory (line 2) and copy a file into it (line 4). For now the directory contains only this particular file as we can see in lines 6 to 9. There is no repository in place so far. 11 sa@wks:/tmp/commondir$ git init 12 Initialized empty git repository in .git/ 13 sa@wks:/tmp/commondir$ git add . 14 sa@wks:/tmp/commondir$ line 11. This command creates an empty GIT repository — basically a
We now have a fully functional GIT repository with content already
under version control. GIT needs to store all information about the
repository in 15 sa@wks:/tmp/commondir$ la .git/ 16 total 44 17 drwxr-xr-x 7 sa sa 4096 2007-09-13 15:09 . 18 drwxr-xr-x 3 sa sa 4096 2007-09-13 15:08 .. 19 drwxr-xr-x 2 sa sa 4096 2007-09-13 15:08 branches 20 -rw-r--r-- 1 sa sa 92 2007-09-13 15:08 config 21 -rw-r--r-- 1 sa sa 58 2007-09-13 15:08 description 22 -rw-r--r-- 1 sa sa 23 2007-09-13 15:08 head 23 drwxr-xr-x 2 sa sa 4096 2007-09-13 15:08 hooks 24 -rw-r--r-- 1 sa sa 104 2007-09-13 15:09 index 25 drwxr-xr-x 2 sa sa 4096 2007-09-13 15:08 info 26 drwxr-xr-x 5 sa sa 4096 2007-09-13 15:09 objects 27 drwxr-xr-x 4 sa sa 4096 2007-09-13 15:08 refs 28 sa@wks:/tmp/commondir$ Time for a short recap. We created a directory, populated it with
content ( 29 sa@wks:/tmp/commondir$ git status 30 # On branch master 31 # 32 # Initial commit 33 # 34 # Changes to be committed: 35 # (use "git rm --cached <file>..." to unstage) 36 # 37 # new file: scm.muse 38 # 39 sa@wks:/tmp/commondir$ As we can see, we are currently on/in the master branch (line 30) of our repository. As I said above, line 34 tells us that there is nothing to be committed from the index (formerly known as directory cache) to GITs back end (which roughly speaking consists of the references and the object database) since nothing changed in the working tree — the index and the working tree are the same at this point in time.
40 sa@wks:/tmp/commondir$ git diff 41 sa@wks:/tmp/commondir$ git diff --cached 42 sa@wks:/tmp/commondir$ git diff HEAD 43 sa@wks:/tmp/commondir$ To proof what I said above (all three stages in the repository (working tree, index, back end) contain the same at this point in time i.e. the working tree is clean) I issued lines 40 to 43. Line 40 shows that there are no differences between the working tree
and the index. Line 41 tells us that there are no differences between
the index and the latest commit (if there is no explicit commit
specified — as is here — it points to the current active branch
Finally, we have to commit the changes. Of course, we have not made
changes so far but the GIT back end is empty at that point — it does
not know about the index and the repository contents. Running Line 44 shows how to commit changes made to the repository. Actually
what we do is using 44 sa@wks:/tmp/commondir$ git commit -m "This is the inital commit." 45 Created initial commit a4325c8: This is the inital commit. 46 1 files changed, 2235 insertions(+), 0 deletions(-) 47 create mode 100644 scm.muse 48 sa@wks:/tmp/commondir$ Note the Line 45 shows the SHA1 hash ( sa@wks:/tmp/commondir$ lsO scm.muse name file type octal permissions human readable permissions group name owner user name owner size in bytes scm.muse regular file 644 -rw-r--r-- sa sa 81330 sa@wks:/tmp/commondir$
Last but not least, we check the last commit we did. Line 49 issues the command. Line 50 shows the commit's unique identifier and line 51 who committed changes. Line 52 is a time stamp and in line 54 we can see the commit/log message supplied in line 44. 49 sa@wks:/tmp/commondir$ git log 50 commit a4325c8a50f4b277fbc3b255b8d77ceb17e5daad 51 Author: markus gattol <sa@wks> 52 Date: Sat Sep 15 10:31:41 2007 +0100 53 54 This is the inital commit. 55 sa@wks:/tmp/commondir$ From a tarball:Aside from extracting the tarball, this the same as the former example. 1 sa@wks:/tmp$ mkdir test 2 sa@wks:/tmp$ mv my_tarball.tar.bz2 test/ 3 sa@wks:/tmp$ cd test/ 4 sa@wks:/tmp/test$ la 5 total 1552 6 drwxr-xr-x 2 sa sa 4096 2007-09-15 18:23 . 7 drwxrwxrwt 21 root root 12288 2007-09-15 18:23 .. 8 -rw-r--r-- 1 sa sa 1568674 2007-09-15 18:22 my_tarball.tar.bz2 9 sa@wks:/tmp/test$ tar -xjf my_tarball.tar.bz2 10 sa@wks:/tmp/test$ la 11 total 1556 12 drwxr-xr-x 3 sa sa 4096 2007-09-15 18:24 . 13 drwxrwxrwt 21 root root 12288 2007-09-15 18:23 .. 14 -rw-r--r-- 1 sa sa 1568674 2007-09-15 18:22 my_tarball.tar.bz2 15 drwxr-xr-x 2 sa sa 4096 2007-09-15 18:22 nose 16 sa@wks:/tmp/test$ cd nose/ 17 sa@wks:/tmp/test/nose$ la 18 total 9236 19 drwxr-xr-x 2 sa sa 4096 2007-09-15 18:22 . 20 drwxr-xr-x 3 sa sa 4096 2007-09-15 18:24 .. 21 -rw-r--r-- 1 sa sa 732731 2007-09-15 18:20 bashref.html 22 -rw-r--r-- 1 sa sa 24071 2007-09-15 18:20 crypto.html 23 -rw-r--r-- 1 sa sa 3581730 2007-09-15 18:20 elisp.html 24 -rw-r--r-- 1 sa sa 3035824 2007-09-15 18:20 emacs.html 25 -rw-r--r-- 1 sa sa 823905 2007-09-15 18:20 emacs-lisp-intro.html 26 -rw-r--r-- 1 sa sa 1159227 2007-09-15 18:20 texinfo.html 27 -rw-r--r-- 1 sa sa 52965 2007-09-15 18:20 vserver_configuration.html 28 sa@wks:/tmp/test/nose$ git init 29 Initialized empty Git repository in .git/ 30 sa@wks:/tmp/test/nose$ git add . 31 sa@wks:/tmp/test/nose$ git commit -m "Intial commit from just extracted tarball." 32 Created initial commit 2b36f4f: Intial commit from just extracted tarball. 33 7 files changed, 166507 insertions(+), 0 deletions(-) 34 create mode 100644 bashref.html 35 create mode 100644 crypto.html 36 create mode 100644 elisp.html 37 create mode 100644 emacs-lisp-intro.html 38 create mode 100644 emacs.html 39 create mode 100644 texinfo.html 40 create mode 100644 vserver_configuration.html 41 sa@wks:/tmp/test/nose$ git log HEAD 42 commit 2b36f4f83dc95d0e05a23f974415f9bd6b55fa66 43 Author: markus gattol <sa@wks> 44 Date: Sat Sep 15 18:25:34 2007 +0100 45 46 Intial commit from just extracted tarball. 47 sa@wks:/tmp/test/nose$ In line 9 we extract the tarball. The was nothing but the tarball in
the From a remote repository:There is just one command we need to know. In line 1 we are issuing
1 sa@wks:/tmp$ git clone git://git.kernel.org/pub/scm/git/git.git 2 Initialized empty Git repository in /tmp/git/.git/ 3 remote: Counting objects: 92034, done. 4 remote: Compressing objects: 100% (24736/24736), done. 5 remote: Total 92034 (delta 67243), reused 90062 (delta 65711) 6 Receiving objects: 100% (92034/92034), 19.30 MiB | 1743 KiB/s, done. 7 Resolving deltas: 100% (67243/67243), done. 8 sa@wks:/tmp$ du -sh git/ 9 36M git/ 10 sa@wks:/tmp$ As of now (February 2009) the whole GIT source tree has a size of
about 36 MiB as line 9 shows. Note, that there is no need to run Importing/Exporting data from/to SVNBefore we actually start, folks familiar to SVN but not GIT might read the this. Also, I am not going to explicitly cover grafts here. Install git-svnNow, in order to import from SVN to GIT we need sa@wks:~$ dpl git-svn* | grep ^ii ii git-svn 1:1.6.1.3-1 fast, scalable, distributed revision control sa@wks:~$ installed. After that, what is the usual case, one creates a GIT
repository by importing from an SVN branch ( Importing from SVN to GIT
sa@wks:~$ which git-svnimport | xargs file /usr/bin/git-svnimport: perl script text executable sa@wks:~$
There are a bunch of options to
I am now going to demonstrate how to import code within a remote SVN
repository into a local GIT repository. Therefore I am going to use
1 sa@wks:/tmp/free_nas$ time rsync -avz rsync://freenas.svn.sourceforge.net/svn/freenas/* freenas_svn 2 3 receiving file list... done 4 created directory freenas_svn 5 README.txt 6 format 7 conf/ 8 conf/authz 9 conf/passwd 10 conf/svnserve.conf 11 dav/ 12 13 [skipping a lot of lines...] 14 15 locks/ 16 locks/db-logs.lock 17 locks/db.lock 18 19 sent 82622 bytes received 12498149 bytes 178450.65 bytes/sec 20 total size is 28174045 speedup is 2.24 21 22 real 1m10.264s 23 user 0m0.496s 24 sys 0m0.524s 25 sa@wks:/tmp/free_nas$ du -sh freenas_svn/ 26 39M freenas_svn/ 27 sa@wks:/tmp/free_nas$ As can be seen in line 1, I issued Then I tried to directly use sa@wks:/tmp/test$ time git-svnimport -C freenas_git -v https://freenas.svn.sourceforge.net/svnroot/freenas Initialized empty Git repository in /tmp/test/freenas_git/.git/ Processing from 1 to 1856... Fetching from 1 to 1001... 1: Unrecognized path: /docs Tree ID 4b825dc642cb6eb9a060e54bf8d69288fbee4904 Committed change 1:/ 2006-06-13 18:35:33) Commit ID 0b322615b786db60612b02003af741c3d313ada8 Writing to refs/heads/origin [skipping a lot of lines...] ... 26 /trunk/www/disks_raid_gvinum.php... ... 26 /trunk/www/disks_raid_gvinum_edit.php... ... 26 /trunk/www/disks_raid_gvinum_info.php... real 9m22.255s user 0m1.660s sys 0m1.404s sa@wks:/tmp/test$ du -sh freenas_git/ 2.3M freenas_git/ sa@wks:/tmp/test$ I did not let it finish since after ~9 minutes I became impatient and
simply used sa@wks:/tmp/test/freenas_git$ python Python 2.4.4 (#2, Aug 16 2007, 02:03:40) [GCC 4.1.3 20070812 (prerelease) (Debian 4.1.2-15)] on linux2 Type "help", "copyright", "credits" or "license" for more information. >>> ((9/2)*39) 156 >>> sa@wks:/tmp/test/freenas_git$ minutes which is totally unacceptable. The next thing I wanted to try is to just checkout some revisions and not the whole history — in other words, I am just interested in the current state. 1 sa@wks:/tmp/free_nas$ svnlook history freenas_svn | head -n3 2 REVISION PATH 3 -------- ---- 4 1853 / 5 sa@wks:/tmp/free_nas$ git-svnimport -s 1850 -C freenas_git file:///tmp/free_nas/freenas_svn 6 Initialized empty Git repository in /tmp/free_nas/freenas_git/.git/ 7 fatal: Needed a single revision 8 1852: cannot find commit '0.685'! 9 Generating pack... 10 Done counting 16 objects. 11 Deltifying 16 objects... 12 100% (16/16) done 13 Writing 16 objects... 14 100% (16/16) done 15 Total 16 (delta 1), reused 0 (delta 0) 16 Pack pack-eb83e4e47346935a574558bfb8cd7cd4d6a464dc created. 17 Removing unused objects 100%... 18 Done. 19 sa@wks:/tmp/free_nas$ du -sh freenas_git/ 20 212K freenas_git/ 21 sa@wks:/tmp/free_nas$ cd freenas_git/ 22 sa@wks:/tmp/free_nas/freenas_git$ la 23 total 24 24 drwxr-xr-x 6 sa sa 4096 2007-09-17 11:50 . 25 drwxr-xr-x 4 sa sa 4096 2007-09-17 11:50 .. 26 drwxr-xr-x 3 sa sa 4096 2007-09-17 11:50 build 27 drwxr-xr-x 3 sa sa 4096 2007-09-17 11:50 etc 28 drwxr-xr-x 7 sa sa 4096 2007-09-17 11:50 .git 29 drwxr-xr-x 2 sa sa 4096 2007-09-17 11:50 www 30 sa@wks:/tmp/free_nas/freenas_git$ As we can see in line 4 there are 1853 revisions. In line 5 I intended to check out the last three revisions but this time from the mirrored SVN repository. Of course, as one might think because of not retrieving the whole history we run into problems (line 7 and 8).
From now on I am doing anything with
The reminder of this subsection is now going to show how to checkout
(full as well as partial checkout) an SVN repository using Common SVN Checkout:Since some time now, I wanted to have the FAI (Fully Automatic Installation) code in a GIT repository so I am going to do that now but first I am demonstrating the usual SVN case: 1 sa@wks:/tmp$ svn co svn://svn.debian.org/svn/fai/ 2 A trunk/utils 3 A trunk/utils/create-nfsroot-tar 4 A trunk/utils/tlink 5 6 [skipping a lot of lines...] 7 8 A fai/tags/2.8.4sarge1/examples/etc/bootptab 9 A fai/tags/2.8.4sarge1/examples/etc/sources.list 10 Checked out revision 4599. 11 12 real 5m18.425s 13 user 0m17.533s 14 sys 0m14.873s 15 sa@wks:/tmp$ du -sh fai/ 16 461M fai/ 17 sa@wks:/tmp$ date -u 18 Tue Sep 18 12:31:51 UTC 2007 19 sa@wks:/tmp$ In line 1 shows how to checkout the SVN repository of FAI into a local working copy. Line 6 indicates that there is a bunch of lines skipped from the actual checkout since they are of no further interest here. We can see, there are 4599 revisions so far (line 10) and the whole working copy has a size of about 461 MiB (line 16) as of now (line 18).
Full git svn clone Checkout:1 sa@wks:~/work/git$ time git svn clone svn://svn.debian.org/svn/fai/ 2 Initialized empty Git repository in .git/ 3 W: +empty_dir: branches 4 W: +empty_dir: tags 5 W: +empty_dir: trunk 6 r1 = d274ebe781031609a12ea15a226c5189adb0bbfe (git-svn) 7 A trunk/kernel/config-2.2.15 8 A trunk/kernel/emptydosdisk.gz 9 A trunk/kernel/imagegen_firstblock 10 A trunk/debian/control 11 12 [skipping a lot of lines...] 13 14 D people/h01ger/faicd/templates/syslinux/pxelinux.cfg 15 D people/h01ger/faicd/templates/syslinux/splash.rle 16 D people/h01ger/faicd/ 17 W: -empty_dir: people/h01ger/faicd 18 r4599 = 542524e653ccb051d055034d2f2211001dfc3f2a (git-svn) 19 Checking 14424 files out... 20 100% (14424/14424) done 21 Checked out HEAD: 22 svn://svn.debian.org/svn/fai r4599 23 24 real 52m38.281s 25 user 10m27.819s 26 sys 6m15.283s 27 sa@wks:~/work/git$ Line 1 is how to create (clone) a local GIT repository from a remote
SVN repository. Again, I issued I indicated the usual exclusion of output in line 12. If we compare the revision number in line 18 to the example above (line 10) we can see they perfectly match. 28 sa@wks:~/work/git$ du -sh fai/ 29 274M fai/ 30 sa@wks:~/work/git$ ll 31 total 4.0K 32 drwxr-xr-x 7 sa sa 4.0K 2007-09-19 13:00 fai 33 sa@wks:~/work/git$ cd fai/ 34 sa@wks:~/work/git/fai$ la 35 total 28 36 drwxr-xr-x 7 sa sa 4096 2007-09-19 13:00 . 37 drwxr-xr-x 3 sa sa 4096 2007-09-19 12:07 .. 38 drwxr-xr-x 3 sa sa 4096 2007-09-19 12:59 branches 39 drwxr-xr-x 9 sa sa 4096 2007-09-19 13:00 .git 40 drwxr-xr-x 9 sa sa 4096 2007-09-19 12:59 people 41 drwxr-xr-x 65 sa sa 4096 2007-09-19 13:00 tags 42 drwxr-xr-x 11 sa sa 4096 2007-09-19 13:00 trunk 43 sa@wks:~/work/git/fai$ time git-gc 44 Generating pack... 45 Counting objects: 4560 46 Done counting 27583 objects. 47 Deltifying 27583 objects... 48 100% (27583/27583) done 49 Writing 27583 objects... 50 100% (27583/27583) done 51 Total 27583 (delta 19160), reused 0 (delta 0) 52 Pack pack-4be8a9921b4eaff77f2b8bf57c8a280941c5bb6b created. 53 Removing unused objects 100%... 54 Done. 55 56 real 2m12.321s 57 user 0m8.353s 58 sys 0m3.288s 59 sa@wks:~/work/git/fai$ cd .. 60 sa@wks:~/work/git$ du -sh fai/ 61 146M fai/ 62 sa@wks:~/work/git$
In lines 28 and 29, we check the size of our just created GIT
repository. In line 30 I am just using an alias in my Partial git svn clone Checkout:This is pretty much the same as with the full checkout above. 1 sa@wks:/tmp/pco$ time git svn clone svn://svn.debian.org/svn/fai -r HEAD 2 Initialized empty Git repository in .git/ 3 A trunk/utils/create-nfsroot-tar 4 A trunk/utils/tlink 5 A trunk/utils/prtnetgr 6 7 [skipping a lot of lines...] 8 9 A tags/2.8.4sarge1/examples/etc/hosts 10 A tags/2.8.4sarge1/examples/etc/netgroup 11 A tags/2.8.4sarge1/examples/etc/dhcpd.conf 12 A tags/2.8.4sarge1/examples/etc/bootptab 13 A tags/2.8.4sarge1/examples/etc/sources.list 14 W: +empty_dir: people/Mrfai 15 W: +empty_dir: people/eartoast/bugfix 16 W: +empty_dir: people/lazyboy/rhel-install-fixes_3.1.8/examples/rhel-install-d 17 emo/basefiles 18 W: +empty_dir: people/lazyboy/rhel-install-fixes_3.1.8/examples/rhel-install-d 19 emo/files/opt/apache-tomcat-6.0.13/conf/tomcat-users.xml 20 W: +empty_dir: people/mugwump/vserver/examples/simple/files/etc/resolv.conf 21 r4599 = 63f8800b68f22ef70ed3cce056cb42bec8e8ea8f (git-svn) 22 Checking 14424 files out... 23 100% (14424/14424) done 24 Checked out HEAD: 25 svn://svn.debian.org/svn/fai r4599 26 27 real 6m35.551s 28 user 0m32.238s 29 sys 1m9.244s As we can see, pretty much the same as the full checkout. Note the 30 sa@wks:/tmp/pco$ ll 31 total 4.0K 32 drwxr-xr-x 7 sa sa 4.0K 2007-09-19 19:43 fai 33 sa@wks:/tmp/pco$ du -sh fai/ 34 165M fai/ 35 sa@wks:/tmp/pco$ cd fai && time git gc && cd .. && du -sh fai 36 Generating pack... 37 Done counting 3946 objects. 38 Deltifying 3946 objects... 39 100% (3946/3946) done 40 Writing 3946 objects... 41 100% (3946/3946) done 42 Total 3946 (delta 2096), reused 0 (delta 0) 43 Pack pack-7b471078885463ecd28262260bfe0b715f04f8a0 created. 44 Removing unused objects 100%... 45 Done. 46 47 real 0m10.908s 48 user 0m3.856s 49 sys 0m0.588s 50 142M fai 51 sa@wks:/tmp/pco$ Of course, since we just checked out the up-to-date SVN repository ( ScreenshotFor no specific reason I just decided to take a sceenshot and put it here — as can be seen, I use GNU Emacs with several windows. The current one is the one with the green mode line. The reason why I put this screenshot here is because I think the reader might find it pretty interesting to actually see how the work he is looking at had been made. However, if that is not the case for you then just ignore it ;-) Bidirectional Operations between SVN and GITAssuming we have used Importing and Updating the local GIT repository1 sa@wks:/tmp$ mkdir demo_import 2 sa@wks:/tmp$ cd demo_import/ 3 sa@wks:/tmp/demo_import$ la 4 total 4 5 drwxr-xr-x 2 sa sa 6 2009-02-23 14:30 . 6 drwxrwxrwt 12 root root 4096 2009-02-23 14:30 .. 7 sa@wks:/tmp/demo_import$ git svn clone http://tracker.trollfot.org/svn/projects/sd.app 8 Initialized empty Git repository in /tmp/demo_import/sd.app/.git/ 9 W: Ignoring error from SVN, path probably does not exist: (175007): HTTP Path Not Found: '/svn/!svn/bc/100/projects/sd.app' path not found 10 W: Do not be alarmed at the above message git-svn is just searching aggressively for old history. 11 This may take a while on large repositories 12 r156 = dab9d194c4af04e2dca36c517e5edbdf9bad6cb3 (git-svn) 13 W: +empty_dir: trunk 14 r157 = a15a95575de0c116fba937afa09c9f6af7426286 (git-svn) 15 16 17 [skipping a lot of lines...] 18 19 20 D branches/sd.app-2.5-simplification/setup.py 21 D branches/sd.app-2.5-simplification/ 22 W: -empty_dir: branches/sd.app-2.5-simplification 23 r430 = a265c628d2a766a04678e48762a026cbf6272ded (git-svn) 24 M trunk/sd/app/contents/browser/nextprevious.py 25 r651 = 2cfa3de1e9ebc3678841803e51b221323a3f9864 (git-svn) 26 Checked out HEAD: 27 http://tracker.trollfot.org/svn/projects/sd.app r651 28 sa@wks:/tmp/demo_import$ cd sd.app/ && gllol | head -n3 29 2cfa3de1e9ebc3678841803e51b221323a3f9864 4 weeks ago CN: trollfot AN: trollfot S: Corrected silly assertion that SD is an AT object. 30 a265c628d2a766a04678e48762a026cbf6272ded 9 weeks ago CN: trollfot AN: trollfot S: Moving branch to new trunk. 31 2ee9ea6b44b9e215392713fb21c04c743f59d77f 9 weeks ago CN: trollfot AN: trollfot S: removing tagged trunk. There is nothing really worth mentioning here — we did Another concern most folks have when doing bidirectional work amongst their local GIT repository and a remote SVN repository is the repository layout i.e. do I get/need the exact same layout the SVN repository has within my GIT repository? No, we do not need it and it must not mirror the SVN repository layout but when it does, then it is fine too. In other words, from my point of view it is best practice to have the local GIT repository layout look like this (looking from the repository root down) 32 sa@wks:/tmp/demo_import/sd.app$ la 33 total 0 34 drwxr-xr-x 6 sa sa 55 2009-02-23 14:35 . 35 drwxr-xr-x 3 sa sa 19 2009-02-23 14:41 .. 36 drwxr-xr-x 3 sa sa 28 2009-02-23 14:35 branches 37 drwxr-xr-x 9 sa sa 144 2009-02-23 14:35 .git 38 drwxr-xr-x 3 sa sa 18 2009-02-23 14:35 tags 39 drwxr-xr-x 4 sa sa 58 2009-02-23 14:35 trunk 40 sa@wks:/tmp/demo_import/sd.app$ git svn rebase 41 Current branch master is up to date. As we can see from lines 33 to 39, the local GIT repository we just
cloned with the command in line 7 looks exactly the same as the remote
SVN repository does (except for the metadata of course i.e. Line 40 is the most important one until now — it updates the local
GIT repository with all the changes that have been made to the remote
SVN repository. In short, it is the equivalent to
Merging and rebasing are two major concepts of GIT, which in the end,
both lead to the same result but get there on different paths. A
repository history made up of merges (and maybe also rebase actions)
has a non-linear history (first screenshot below; upper-left corner)
whereas a repository where Now, those who are interested in more details can go read The rebasing concept might be a bit tricky to understand — even more so if one gets a notion what else can be done with it except of what we just tried in line 40. However, from my point of view, rebasing is probably the most powerful thing one can do with GIT if he understands its full potential. Creating a new Topic Branch used to carry out our Work42 sa@wks:/tmp/demo_import/sd.app$ git checkout -b topic_branch_implementing_something 43 Switched to a new branch "topic_branch_implementing_something" 44 sa@wks:/tmp/demo_import/sd.app$ git branch 45 master 46 * topic_branch_implementing_something 47 sa@wks:/tmp/demo_import/sd.app$ echo "my comments blabla" >> trunk/README.txt 48 sa@wks:/tmp/demo_import/sd.app$ git dwi 49 diff --git a/trunk/README.txt b/trunk/README.txt 50 index e69de29..7c399ee 100644 51 --- a/trunk/README.txt 52 +++ b/trunk/README.txt 53 @@ -0,0 +1 @@ 54 +my comments blabla Now that we have imported a SVN repository and updated it (line 40),
we are going to do some work. We do not use the currently active
branch In line 47 we did our first change to our currently active branch
55 sa@wks:/tmp/demo_import/sd.app$ git cwh -m 'added some additional comments' 56 [topic_branch_implementing_something]: created e160df1: "added some additional comments" 57 1 files changed, 1 insertions(+), 0 deletions(-) 58 sa@wks:/tmp/demo_import/sd.app$ gllol | head -3 59 e160df17cd4061664a200d27b40ebeb72426205a 76 seconds ago CN: Markus Gattol AN: Markus Gattol S: added some additional comments 60 2cfa3de1e9ebc3678841803e51b221323a3f9864 4 weeks ago CN: trollfot AN: trollfot S: Corrected silly assertion that SD is an AT object. 61 a265c628d2a766a04678e48762a026cbf6272ded 9 weeks ago CN: trollfot AN: trollfot S: Moving branch to new trunk. 62 sa@wks:/tmp/demo_import/sd.app$ git diff master 63 diff --git a/trunk/README.txt b/trunk/README.txt 64 index e69de29..7c399ee 100644 65 --- a/trunk/README.txt 66 +++ b/trunk/README.txt 67 @@ -0,0 +1 @@ 68 +my comments blabla In line 55 we commit those changes and then take a look at the last three commits logs in line 58, using a somewhat fancy command of mine. What is also very interesting is to take a look at the differences
amongst branches which we do in lines 62 to 68 — remember, we are
currently on branch At this point, branch 69 sa@wks:/tmp/demo_import/sd.app$ touch trunk/new_file 70 sa@wks:/tmp/demo_import/sd.app$ git add . 71 sa@wks:/tmp/demo_import/sd.app$ git dwh 72 diff --git a/trunk/new_file b/trunk/new_file 73 new file mode 100644 74 index 0000000..e69de29 75 sa@wks:/tmp/demo_import/sd.app$ git dih 76 diff --git a/trunk/new_file b/trunk/new_file 77 new file mode 100644 78 index 0000000..e69de29 79 sa@wks:/tmp/demo_import/sd.app$ git cwh -m 'added a new file' 80 [topic_branch_implementing_something]: created d34621c: "added a new file" 81 0 files changed, 0 insertions(+), 0 deletions(-) 82 create mode 100644 trunk/new_file 83 sa@wks:/tmp/demo_import/sd.app$ gllol | head -n3 84 d34621c1941d9e0e97b7fab25a8fce7bfa5c780e 3 minutes ago CN: Markus Gattol AN: Markus Gattol S: added a new file 85 e160df17cd4061664a200d27b40ebeb72426205a 36 minutes ago CN: Markus Gattol AN: Markus Gattol S: added some additional comments 86 2cfa3de1e9ebc3678841803e51b221323a3f9864 4 weeks ago CN: trollfot AN: trollfot S: Corrected silly assertion that SD is an AT object. 87 sa@wks:/tmp/demo_import/sd.app$ git diff master 88 diff --git a/trunk/README.txt b/trunk/README.txt 89 index e69de29..7c399ee 100644 90 --- a/trunk/README.txt 91 +++ b/trunk/README.txt 92 @@ -0,0 +1 @@ 93 +my comments blabla 94 diff --git a/trunk/README.txt b/trunk/new_file 95 similarity index 100% 96 copy from trunk/README.txt 97 copy to trunk/new_file In line 69 we create a new file ( Line 71 is also a GIT alias, showing us the differences between the
working tree and We commit those changes in line 79 and issue another Assuming we are done making our changes, now is the time to get our
changes back from Getting Changes back from a Topic Branch into MasterOnce we are done working on the topic branch we need to get our
changes back into
98 sa@wks:/tmp/demo_import/sd.app$ git rebase topic_branch_implementing_something master 99 First, rewinding head to replay your work on top of it... 100 Fast-forwarded master to topic_branch_implementing_something. 101 sa@wks:/tmp/demo_import/sd.app$ git branch 102 * master 103 topic_branch_implementing_something 104 sa@wks:/tmp/demo_import/sd.app$ git diff topic_branch_implementing_something 105 sa@wks:/tmp/demo_import/sd.app$ gitk In line 98 we rebase the branch Line 104 does not produce any results simply because, at this point,
there are no more differences between branch 106 sa@wks:/tmp/demo_import/sd.app$ git checkout topic_branch_implementing_something 107 Switched to branch "topic_branch_implementing_something" 108 sa@wks:/tmp/demo_import/sd.app$ gitk With line 106 we switch back to branch
Committing Changes to remote SVN RepositoryGetting our changes from WRITEME TrustAgain, take a look at the GIT glossary and read about the definition of object and object name in order to easily understand the following.
So, to introduce some real trust in the system, the only thing we need to do is to digitally sign just one special note, which includes the name of a top-level commit. Our digital signature shows others that we trust that commit, and the immutability of the history of commits tells others that they can therefore also trust the whole history. In other words, we can easily validate a whole archive by just sending out a single email that tells the people the name (SHA1 hash) of the top commit, and digitally sign that email using something like GPG/PGP. To assist in this, GIT also provides the tag object. Note that despite the tag features, GIT itself only handles content integrity — the trust framework (i.e. signature provision and verification) has to come from the outside (e.g. GPG (GNU Privacy Guard)). Cryptographically signing a CommitSo, GIT takes care about integrity, the rest is up to the user. We need to have GPG (GNU Privacy Guard) installed sa@wks:~$ dpl gnupg | grep ^ii ii gnupg 1.4.9-3 GNU privacy guard - a free PGP replacement sa@wks:~$ As we can see, I have already installed it. If one has not then
Create a GPG keypairLet us start with some comic ;-]
Update: This key (the one that is created below, fingerprint/key ID
However, there is nothing to worry about this change since it is just an internal change in GPG and no change to how it is used or behaves. From the users point of view there is actually no difference at all! After we have installed GPG we need to create a keypair which is then
used by Issuing 1 sa@wks:~$ gpg --gen-key 2 gpg (GnuPG) 1.4.9; Copyright (C) 2008 Free Software Foundation, Inc. 3 This is free software: you are free to change and redistribute it. 4 There is NO WARRANTY, to the extent permitted by law. 5 6 Please select what kind of key you want: 7 (1) DSA and Elgamal (default) 8 (2) DSA (sign only) 9 (5) RSA (sign only) 10 Your selection? 11 DSA keypair will have 1024 bits. 12 ELG-E keys may be between 1024 and 4096 bits long. 13 What keysize do you want? (2048) 4096 14 Requested keysize is 4096 bits 15 Please specify how long the key should be valid. 16 0 = key does not expire 17 <n> = key expires in n days 18 <n>w = key expires in n weeks 19 <n>m = key expires in n months 20 <n>y = key expires in n years 21 Key is valid for? (0) 22 Key does not expire at all 23 Is this correct? (y/N) y 24 In line 1 we can see that I am issuing the command in order to create
a new key. From line 6 to 10 I got prompted for a particular type of
key — I opted for the default — just hitting 25 You need a user ID to identify your key; the software constructs the user ID 26 from the Real Name, Comment and Email Address in this form: 27 "Heinrich Heine (Der Dichter) <[email protected]>" 28 29 Real name: Markus Gattol 30 Email address: foo[at]bar.org 31 Comment: 32 You selected this USER-ID: 33 "Markus Gattol () <foo[at]bar.org>" 34 35 Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O 36 You need a Passphrase to protect your secret key. 37 From line 29 to 31 I am prompted for my real name and the like which I
entered and confirmed in line 35 (using We are prompted for a passphrase (we should use a strong passphrase)
which we need for various actions later on e.g. signing stuff with
this particular key, editing the key parameters like the email address
(e.g. from 38 We need to generate a lot of random bytes. It is a good idea to perform 39 some other action (type on the keyboard, move the mouse, utilize the 40 disks) during the prime generation; this gives the random number 41 generator a better chance to gain enough entropy. 42 ..++++++++++++++++++++.+++++++++++++++.+++++.+++++++++++++++++++++++++++++++++ 43 44 Not enough random bytes available. Please do some other work to give 45 the OS a chance to collect more entropy! (Need 10 more bytes) 46 We need to generate a lot of random bytes. It is a good idea to perform 47 some other action (type on the keyboard, move the mouse, utilize the 48 disks) during the prime generation; this gives the random number 49 generator a better chance to gain enough entropy. 50 .+++++.+++++++++++++++.+++++.+++++....+++++.+++++.+++++++++++++++ 51 gpg: key C0EC7E38 marked as ultimately trusted 52 public and secret key created and signed. 53 54 gpg: checking the trustdb 55 gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model 56 gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u 57 pub 1024D/C0EC7E38 2009-02-06 58 Key fingerprint = F6F7 8566 432A 78A9 0D39 CDAE 48E9 4AC6 C0EC 7E38 59 uid Markus Gattol () <foo[at]bar.org> 60 sub 4096g/34233DEF 2009-02-06 61 62 sa@wks:~$ In lines 38 to 50, all that happens is, the OS (Operating System) tries to actually create the key and thus needs random data — (this might take a couple of minutes depending on the amount of randomness available to the OS at this point). Then in lines 51 to 60, what we get is a lot of status report on the newly created key. We are now ready to use the key. Caution: this is beyond the scope of this page but as we see in lines 54 to 56, the actual degree of trust is low — one wants to get his key signed by others in order to elevate the trust level. Doing so is easy by joining key signing parties. When we are done creating a new key respectively also in case we
already had one issuing sa@wks:~$ gpg --list-keys | egrep -{A,B}1 www.markus-gattol.name pub 1024D/C0EC7E38 2009-02-06 uid Markus Gattol () <foo[at]bar.org> sub 4096g/34233DEF 2009-02-06 sa@wks:~$ In this example we can see the just created key. I only wanted to show
this one so I used Signing:Now that we have the key we can go ahead and actually do what we intended to do from the beginning — we are going to digitally sign a commit. Signing a commit works by signing a tag object. Do not confuse tag with tag object — the latter one is referenced by the former one. A tag object usually points to another object (see also object type. In case a tag object gets signed as well it is called a signed tag object, containing not just an optional message and a ref to another object but also a digital signature. So, all we need to do is to create a tag object and sign it. Easy said, easy done... 1 sa@wks:~$ cd /tmp/ && mkdir test_signing && cd test_signing 2 sa@wks:/tmp/test_signing$ touch my_file && echo "some text..." > my_file 3 sa@wks:/tmp/test_signing$ git init 4 Initialized empty Git repository in /tmp/test_signing/.git/ 5 sa@wks:/tmp/test_signing$ git add . 6 sa@wks:/tmp/test_signing$ git commit -a -m "initial commit" 7 Created initial commit 8aaf01a: initial commit 8 1 files changed, 1 insertions(+), 0 deletions(-) 9 create mode 100644 my_file 10 sa@wks:/tmp/test_signing$ echo "some changes" >> my_file 11 sa@wks:/tmp/test_signing$ git diff 12 diff --git a/my_file b/my_file 13 index 9d75b34..30331e4 100644 14 --- a/my_file 15 +++ b/my_file 16 @@ -1 +1,2 @@ 17 some text... 18 +some changes Nothing special in lines 1 to 18... those are just provided for the sake of completeness. 19 sa@wks:/tmp/test_signing$ git commit -a -m "We made some changes." 20 Created commit 216b0d2: We made some changes. 21 1 files changed, 1 insertions(+), 0 deletions(-) 22 sa@wks:/tmp/test_signing$ git tag -u Markus -m "Creating a signed tag object." first_tag 23 24 You need a passphrase to unlock the secret key for 25 user: "Markus Gattol () <foo[at]bar.org>" 26 1024-bit DSA key, ID C0EC7E38, created 2009-02-06 27 28 sa@wks:/tmp/test_signing$ type la && la .git/refs/tags/ 29 la is aliased to `ls -la' 30 total 4 31 drwxr-xr-x 2 sa sa 22 2009-02-07 01:38 . 32 drwxr-xr-x 4 sa sa 29 2009-02-07 01:30 .. 33 -rw-r--r-- 1 sa sa 41 2009-02-07 01:38 first_tag In line 19 we do another commit since we did some changes in line 10.
Line 22 is what it is all about. I am determining to use the former
created key by providing the argument Note line 24. We are prompted for a passphrase. It is the one that we
needed to provide while we created the key with 34 sa@wks:/tmp/test_signing$ git rev-parse --tags 35 7c2557f9fe84df13db8e4ebf29791e6b3eaa51f8 36 sa@wks:/tmp/test_signing$ git rev-parse --tags | xargs git cat-file -t 37 tag 38 sa@wks:/tmp/test_signing$ git rev-parse --tags | xargs git cat-file -p 39 object 216b0d2c95da8fb8813b51b915acd82367660bb6 40 type commit 41 tag first_tag 42 tagger markus gattol <sa@wks.(none)> Sat Feb 7 01:38:41 2009 +0100 43 44 Creating a signed tag object. 45 -----BEGIN PGP SIGNATURE----- 46 Version: GnuPG v1.4.9 (GNU/Linux) 47 48 iEYEABECAAYFAkmM2BEACgkQSOlKxsDsfjjd1gCZAaREGDPSFrrtSkNF1drl3B/m 49 SEQAn3QlNTUcku03KLCL4L9nKwOwAVd5 50 =ZoCW 51 -----END PGP SIGNATURE----- 52 sa@wks:/tmp/test_signing$ git tag -v first_tag 53 object 216b0d2c95da8fb8813b51b915acd82367660bb6 54 type commit 55 tag first_tag 56 tagger markus gattol <sa@wks.(none)> 1233967121 +0100 57 58 Creating a signed tag object. 59 gpg: Signature made Sat 07 Feb 2009 01:38:41 AM CET using DSA key ID C0EC7E38 60 gpg: Good signature from "Markus Gattol () <foo[at]bar.org>" 61 sa@wks:/tmp/test_signing$
In line 34, we are using an ancillary GIT command to figure which tag
refs we have so far — in our case there can be just one since we did
not create more than one so far. The ref from line 35 can then be used
to gather more information about an object since the ref can be passed
to other GIT commands e.g. Note, that line 35 and 39 are not the same although they belong to the same object in some way — line 35 is the tag ref and line 39 is the object name (the objects unique SHA1 hash value). Lines 38 to 51 are pretty much the same as lines 52 to 60 — only difference is that we used two different possibilities to show the content information of our signed tag object. I prefer the command in line 38 over the one in line 52 simply because I find the output easier to read plus it nicely shows the digital signature on the tag object. Managing BranchesWell, before we manage something we should understand it. What is a branch? What are they good for and why is branching so easy done and popular amongst GIT folks? A lot of questions... Let us try to look at them one by one. Introducing BranchesProbably GIT's most compelling feature that really makes it stand apart from nearly every other SCM system out there is its branching model. When it comes to branching, GIT is completely different from any of the SCM systems out there — most of which recommend that the best approach at branching is basically to clone/copy the repository to a new directory below the repository root. GIT does not work like that. GIT will allow us to have multiple local branches that can be entirely independent of each other and the creation, merging and deletion of those lines of development take mostly only fractions of seconds. In practical terms this means that we can do things like:
Importantly, when we push to a remote repository, we do not have to push all of our branches. We can only share one or a few of our branches and not all of them. This tends to free people to try new ideas without worrying about having to plan how and when they are going to merge it in or share it with others. We can find ways to do some of this with other systems, but the work involved is much more difficult and error-prone. GIT makes this process incredibly easy and it changes the way most developers work when they learn it. Detailed Look at Branches?If we take a quick look at the glossary we will see that there is not just a branch but different types of branches. Next to the common term of a branch, there is also a topic branch and a tracking branch. Branches can be local or remote (not on ones local computer but somewhere else) which, in practice, is related to what type of branch someone declares a branch by simply using it in a dedicated way. A general description of Branches in GIT:A project history is born by recording a particular state (also known as revision) as a root commit, and built up by recording subsequent states (revisions) on top of the previous commits. Thus, a group of commits connected by their parent fields form a DAG (Directed Acyclic Graph). Often this linkage between commits by their parent fields is called ancestry chain, and a commit that has another commit in its parent field is called a child commit of the latter. There can be multiple root commits in the history of a project. In other words, projects born independently can later be glued together to become a single project. The history is grown by building on top of previous commits, and by the nature of distributed development, many lineages of histories are grown simultaneously. Each lineage is called a branch. A commit, that can be reached by following the ancestry chain from a commit that is on the branch, is also on the branch. A commit that cannot be reached by following the ancestry chain from any commit that is on the branch is not on the branch. The commit that bootstraps this recursive definition of on the branch, is called its branch head, the tip of the branch, or the top commit. In other words, it is topologically the latest commit on the branch. The above does not mean the top commit of a branch does not have any child commit in the global project histories. It just means that these children are not on the branch; they may be on some other branches, forked from it. To create a branch whose on the branch commits are a strict superset of on the branch commits of another branch is called forking the branch. Different points of view on branches:
Why GIT folks like branching so muchOne cannot discuss branching without also discuss merging. The branching and merging thing with GIT is pretty straightforward. Compared to doing branching and merging with SVN, doing it with GIT is like heaven opens up and angels start singing — I hated it back then with SVN. With SVN one gets no merge tracking but he has to do it manually — I leave it to the readers imagination to conceive at what folks look at after a few weeks or so. One might use SVK on top of SVN but it still is not comparable to what we get with GIT out of the box. The basic question is if the maintenance costs of managing branches outweigh the potential benefits? If the answer is no, then we should branch. With GIT the maintenance cost for branches is in fact not existent and that is why GIT folks use branching almost on a regular and for various things. With other SCM (Software Configuration Management) systems, where there is a high cost of maintaining branches, folks do rarely enjoy the benefit of branching simply because they are shy of all the maintenance labor that comes with it. When should we create a Branch?As I said, with GIT we might create a branch for almost anything, work on that branch and when done merge it back into a branch that we declared our main branch (some particular topic branch) but that we never touch directly — we just merge into our main branch when we are sure the branch we are merging from is sane. Such short-lived branches are often called lightweight branches or topic branches in which we carry out a potentially disruptive task that would otherwise break the main branch (or some other important release branch). Once the task is complete, the changes are merged all at once back to the main branch (from one topic branch where we did our changes into another topic branch, our main branch), hopefully without breakage and minimizing disruption. A lightweight branches life effectively comes to an end at this point and we delete it again, creating a new lightweight branch and so forth. Reasons for creating lightweight branches might be:
Creating and Toying with local BranchesMost folks think the definition for local branch determines that the branch is located on their local computer (no remote working via SSH and such). While that is mostly true it actually determines that, when speaking of a local branch, we refer to a branch in the currently active repository. Mostly that repository happens to be on the local machine so... See remote branch to get the big picture... 1 sa@wks:~/work/git/test$ git branch 2 * master 3 sa@wks:~/work/git/test$ git branch my_first_branch 4 sa@wks:~/work/git/test$ git branch 5 * master 6 my_first_branch 7 sa@wks:~/work/git/test$ git checkout my_first_branch 8 Switched to branch "my_first_branch" 9 sa@wks:~/work/git/test$ git branch 10 master 11 * my_first_branch 12 sa@wks:~/work/git/test$ git checkout -b my_second_branch && git branch 13 Switched to a new branch "my_second_branch" 14 master 15 my_first_branch 16 * my_second_branch 17 sa@wks:~/work/git/test$ git branch -d my_first_branch 18 Deleted branch my_first_branch. 19 sa@wks:~/work/git/test$ git branch 20 master 21 * my_second_branch We are issuing We are creating a new branch in line 3 which is called
In line 7 we are switching to another branch ( 22 sa@wks:~/work/git/test$ git branch -v 23 master e973aea Initial commmit. 24 * my_second_branch e973aea Initial commmit. 25 sa@wks:~/work/git/test$ ll 26 total 4.0K 27 -rw-r--r-- 1 sa sa 10 2007-09-23 08:50 my_file 28 sa@wks:~/work/git/test$ echo "more text" >> my_file 29 sa@wks:~/work/git/test$ git diff 30 diff --git a/my_file b/my_file 31 index 7b57bd2..4bfbf30 100644 32 --- a/my_file 33 +++ b/my_file 34 @@ -1 +1,2 @@ 35 some text 36 +more text 37 sa@wks:~/work/git/test$ git commit -a -m "Some changes on this branch head" 38 Created commit bb34607: Some changes on this branch head 39 1 files changed, 1 insertions(+), 0 deletions(-) 40 sa@wks:~/work/git/test$ git branch -v 41 master e973aea Initial commmit. 42 * my_second_branch bb34607 Some changes on this branch head 43 sa@wks:~/work/git/test$ git checkout master 44 Switched to branch "master" 45 sa@wks:~/work/git/test$ git diff 46 sa@wks:~/work/git/test$ Line 22 triggers the verbose version of After switching back to Non-default basing of Branches:Until now, we always based newly created branches on In fact, when creating a new branch it can be made descendant from any branch (local or remote) and any commit that took ever place on a particular branch. For now, we just scratched the surface of what can be done. However, upcoming things are as simple as were those we already looked into before. Basing on a Branch other than the current One1 sa@wks:~$ git branch 2 fatal: Not a git repository 3 sa@wks:~$ cd work/git/test/ 4 sa@wks:~/work/git/test$ git branch 5 * master 6 my_second_branch 7 sa@wks:~/work/git/test$ git branch new_branch_based_on my_second_branch 8 sa@wks:~/work/git/test$ git branch -v 9 * master e973aea Initial commmit. 10 my_second_branch bb34607 Some changes on this branch head 11 new_branch_based_on bb34607 Some changes on this branch head I am not perfect as can be seen — meanwhile I did something else and
thus was not inside my GIT repository (line 1 and 2). Line 4 issues
In line 7, we are issuing a command to create a new branch
( Basing on a Tag12 sa@wks:~/work/git/test$ git rev-parse --tags 13 sa@wks:~/work/git/test$ git tag -m "This is a random tag." first_tag 14 sa@wks:~/work/git/test$ git tag 15 first_tag 16 sa@wks:~/work/git/test$ git rev-parse --tags 17 8b3f6ba8e7b6b53d2d77a069742bcf983183cd83 18 sa@wks:~/work/git/test$ git tag -v first_tag 19 object e973aea29c60b1d8031ab661a1f466b7e95821bf 20 type commit 21 tag first_tag 22 tagger markus gattol <sa@wks.(none)> 1234023501 +0100 23 24 This is a random tag. 25 gpg: no valid OpenPGP data found. 26 gpg: the signature could not be verified. 27 Please remember that the signature file (.sig or .asc) 28 should be the first file given on the command line. 29 error: could not verify the tag 'first_tag' 30 sa@wks:~/work/git/test$ git tag 31 first_tag 32 sa@wks:~/work/git/test$ git branch based_on_first_tag first_tag 33 sa@wks:~/work/git/test$ git branch -v 34 based_on_first_tag e973aea Initial commmit. 35 * master e973aea Initial commmit. 36 my_second_branch bb34607 Some changes on this branch head 37 new_branch_based_on bb34607 Some changes on this branch head 38 sa@wks:~/work/git/test$ In line 12, I tried to figure what tags are in place if any — the
command Then, in line 14, we check again if there is a tag around now... of course there is now that we created one. Line 16 is the same as line 12 but this time, because we have created a tag, we also get the tag ref (a tag basically is a ref) in line 17. Note, that line 17 and 19 are not the same although they belong to the same object in some way — line 17 is the tag ref and line 19 is the object name (the objects unique SHA1 hash value). What we actually wanted to do is to create a new branch as an
descendant from a tag. This is done in line 32. As we can see in lines
19 and 34 to 37, the tag ( Basing on non-HEAD Commits1 sa@wks:~/work/git/test$ git checkout my_second_branch 2 Switched to branch "my_second_branch" 3 sa@wks:~/work/git/test$ git branch -v 4 based_on_first_tag e973aea Initial commmit. 5 master e973aea Initial commmit. 6 * my_second_branch bb34607 Some changes on this branch head 7 new_branch_based_on bb34607 Some changes on this branch head 8 sa@wks:~/work/git/test$ git log --pretty=oneline 9 bb3460772fd9a287b26c89d8752da8d7038c8056 Some changes on this branch head 10 e973aea29c60b1d8031ab661a1f466b7e95821bf Initial commmit. Nothing unusual in line 1 to 7. Line 8 shows how to take a lock at the
currents branch ( 11 sa@wks:~/work/git/test$ git branch based_on_non-HEAD HEAD^ 12 sa@wks:~/work/git/test$ git branch -v 13 based_on_first_tag e973aea Initial commmit. 14 based_on_non-HEAD e973aea Initial commmit. 15 master e973aea Initial commmit. 16 * my_second_branch bb34607 Some changes on this branch head 17 new_branch_based_on bb34607 Some changes on this branch head 18 sa@wks:~/work/git/test$ Line 11 is the important one with this example. In fact, we could do numerous examples like the one before because a
new branch can be created with a All the following would work since GIT deals with object names
internally which it either receives directly from the user or it maps
stuff like git branch my_new_branch HEAD^^ # two commits before HEAD i.e. grandparent of HEAD git branch my_new_branch some_existing_branch~3 # three commits before tip of branch some_existing_branch git branch my_new_branch v1.3 # based on a tag named v1.3 And a more interesting example which creates a branch based on what our current branch was one day before the current one (take a look here and here). sa@wks:~/work/git/test$ git show my_second_branch@{1} --pretty=oneline | head -n1 | cut -d ' ' -f1 e973aea29c60b1d8031ab661a1f466b7e95821bf sa@wks:~/work/git/test$ git-branch my_new_branch `git-show my_second_branch@{1} --pretty=oneline | head -n1 | cut -d ' ' -f1` All that is needed is a way provide the desired ID like for example git show master@{one.week.ago} --pretty=oneline | head -n1 | cut -d ' ' -f1 As I said above ... may be given as a branch name, a commit ID, a reflog, or as a tag... the fact that one can supply commit IDs makes the whole branching thing the Wild West... there are no limits... go wild! Remote BranchesFrom the implementation point of view, a branch is a remote branch if
it is in the
A branch is considered a remote branch if it gets fetched (
If we take a look at sa@wks:~/work/git/test$ git branch -r && git remote sa@wks:~/work/git/test$ we can see that there is currently no remote branch in this particular repository that I used to demonstrate things so far. This is simply because I created the repository local, added a file, did some changes to the file (repository contents), committed them and so on. We do have branches sa@wks:~/work/git/test$ git branch based_on_first_tag based_on_non-HEAD master * my_second_branch new_branch_based_on sa@wks:~/work/git/test$ but these are local ones. I created them earlier (see above). A remote Branch by cloning a remote Repository:We simply clone the GIT repository itself 1 sa@wks:~/work/git$ git clone git://git.kernel.org/pub/scm/git/git.git 2 Initialized empty Git repository in /home/sa/work/git/git/.git/ 3 remote: Counting objects: 92034, done. 4 remote: Compressing objects: 100% (24736/24736), done. 5 remote: Total 92034 (delta 67243), reused 90062 (delta 65711) 6 Receiving objects: 100% (92034/92034), 19.30 MiB | 1739 KiB/s, done. 7 Resolving deltas: 100% (67243/67243), done. 8 sa@wks:~/work/git$ du -sh git/ 9 36M git/ Line 1, the clone command creates a new directory named after the
project i.e. Generally, 10 sa@wks:~/work/git$ cd git/ && git remote 11 origin 12 sa@wks:~/work/git/git$ grep -A3 "\[remote" .git/config 13 [remote "origin"] 14 url = git://git.kernel.org/pub/scm/git/git.git 15 fetch = +refs/heads/*:refs/remotes/origin/* 16 [branch "master"] 17 sa@wks:~/work/git/git$ git remote show origin 18 * remote origin 19 URL: git://git.kernel.org/pub/scm/git/git.git 20 Remote branch merged with 'git pull' while on branch master 21 master 22 Tracked remote branches 23 html maint man master next pu todo 24 sa@wks:~/work/git/git$ git branch 25 * master 26 sa@wks:~/work/git/git$ git branch -r 27 origin/HEAD 28 origin/html 29 origin/maint 30 origin/man 31 origin/master 32 origin/next 33 origin/pu 34 origin/todo 35 sa@wks:~/work/git/git$ la .git/refs/remotes/origin/ 36 total 32 37 drwxr-xr-x 2 sa sa 94 2009-02-07 20:44 . 38 drwxr-xr-x 3 sa sa 19 2009-02-07 20:44 .. 39 -rw-r--r-- 1 sa sa 32 2009-02-07 20:44 HEAD 40 -rw-r--r-- 1 sa sa 41 2009-02-07 20:44 html 41 -rw-r--r-- 1 sa sa 41 2009-02-07 20:44 maint 42 -rw-r--r-- 1 sa sa 41 2009-02-07 20:44 man 43 -rw-r--r-- 1 sa sa 41 2009-02-07 20:44 master 44 -rw-r--r-- 1 sa sa 41 2009-02-07 20:44 next 45 -rw-r--r-- 1 sa sa 41 2009-02-07 20:44 pu 46 -rw-r--r-- 1 sa sa 41 2009-02-07 20:44 todo 47 sa@wks:~/work/git/git$ Line 10 shows, next to changing into the currently cloned repository,
that we issue Lines 17 to 23 pretty much show the same as lines 12 to 16 but in a
more human/novice readable format. Lines 24 to 46 are to check for the
current local branch (line 25), respectively to check for all current
remote tracking branches with A remote Branch by adding to an existing local Repository:This is done via 1 sa@wks:~/work/git/git$ git remote 2 origin 3 sa@wks:~/work/git/git$ git peek-remote git://linux-nfs.org/pub/linux/nfs-2.6.git 4 ae1a25da8448271a99745da03100d5299575a269 HEAD 5 ae1a25da8448271a99745da03100d5299575a269 refs/heads/bugfixes 6 7 [skipping a lot of lines...] 8 9 18e352e4a73465349711a9324767e1b2453383e2 refs/tags/v2.6.29-rc3^{} 10 sa@wks:~/work/git/git$ git remote add linux_nfs git://linux-nfs.org/pub/linux/nfs-2.6.git 11 sa@wks:~/work/git/git$ git remote 12 linux_nfs 13 origin 14 sa@wks:~/work/git/git$ git fetch linux_nfs 15 warning: no common commits 16 remote: Counting objects: 1066646, done. 17 remote: Compressing objects: 100% (188545/188545), done. 18 remote: Total 1066646 (delta 874040), reused 1065276 (delta 873215) 19 Receiving objects: 100% (1066646/1066646), 259.86 MiB | 1744 KiB/s, done. 20 Resolving deltas: 100% (874040/874040), done. 21 From git://linux-nfs.org/pub/linux/nfs-2.6 22 * [new branch] bugfixes -> linux_nfs/bugfixes 23 24 [skipping a lot of lines...] 25 26 * [new tag] v2.6.29-rc2 -> v2.6.29-rc2 27 * [new tag] v2.6.29-rc3 -> v2.6.29-rc3 The command issued in line 1 shows us how much and what remote
branches we are currently tracking. As we can see in line 3, we can
use 28 sa@wks:~/work/git/git$ git remote 29 linux_nfs 30 origin 31 sa@wks:~/work/git/git$ git branch -r 32 linux_nfs/bugfixes 33 linux_nfs/devel 34 linux_nfs/linux-mm 35 linux_nfs/linux-next 36 linux_nfs/master 37 origin/HEAD 38 origin/html 39 origin/maint 40 origin/man 41 origin/master 42 origin/next 43 origin/pu 44 origin/todo 45 sa@wks:~/work/git/git$ git branch 46 * master 47 sa@wks:~/work/git/git$ git checkout -b linux_nfs_devel linux_nfs/devel 48 Checking out files: 100% (28273/28273), done. 49 Branch linux_nfs_devel set up to track remote branch refs/remotes/linux_nfs/devel. 50 Switched to a new branch "linux_nfs_devel" 51 sa@wks:~/work/git/git$ git branch 52 * linux_nfs_devel 53 master 54 sa@wks:~/work/git/git$ Lines 29 and 30 show the list of remotes. Lines 32 to 44 lists the
available remote tracking branches. Line 46 tells us that our
currently active branch is Now we want to work on another branch belonging to one of our tracking branches — we are checking out a branch to become our currently active branch in line 47. As we can see in line 52, we have made one of the remote tracking branches our currently active branch that we might work with. Fetch a Branch from a remote Repository and give it a new name in our local Repository:We could also do something more swiftly like the below if we know we just need one particular branch. The downside is, if we are on the go and we might need another branch, we could not get it without connectivity to the Internet... thus, I mostly grab the whole repository (as shown above). $ git fetch git://example.com/project.git theirbranch:mybranch $ git fetch git://example.com/project.git v2.6.15:mybranch Remote Tracking Branches:Generally, if we are dealing with remote branches, no matter how we
get them, we might want to track them ( Please note, when we clone from a remote repository or even if we
fetch from a remote repository and we have a default configured GIT
repository, our branches are already set up as being remote tracking
branches. See Exploring the History of a RepositoryBefore we start, I would like to mention that reflogs can be very helpful when it comes to investigating a repositories history. So what are we talking about? What exactly is a repositories history and how is this information useful to us? Well, as we already know from above, GIT remembers states instead of deltas. A sequence of states within a repository is a repositories history. Yes, it is as simple as that. The question now is, how can we get information about it, and further down the road, how can we use this information to boost productivity? We will see all this answered shortly but before that, let us recap/clarify a few things: We will sometimes represent GIT history using diagrams like the one
below. Commits are shown as o--o--o <-- Branch A / o--o--o <-- master \ o--o--o <-- Branch B If we need to talk about a particular commit, the character When we need to be precise, we will use the word branch to denote a
line of development, and branch head (or just head) to denote a
reference to the most recent commit on a branch. In the example above,
the branch head named GIT is best thought of as a tool for storing the history of a collection of files. It does this by storing compressed snapshots of the contents of a file hierarchy, together with commits which show the relationships between these snapshots. GIT provides extremely flexible and fast tools for exploring the history of a project. We start with one specialized tool that is useful for finding the commit that introduced a bug into a project. Viewing commit logs with git logLet us start with something we already used. sa@wks:~/work/git/git$ git log -n1 commit ae1a25da8448271a99745da03100d5299575a269 Merge: fd9fc84... 42f15d7... Author: Linus Torvalds <[email protected]> Date: Fri Feb 6 18:37:22 2009 -0800 Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable * git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable: (37 commits) Btrfs: Make sure dir is non-null before doing S_ISGID checks [skipping a lot of lines...] Btrfs: Catch missed bios in the async bio submission thread Btrfs: fix readdir on 32 bit machines ...
On its own, it shows all commits reachable from the parent commit within the current branch (line 2). 1 sa@wks:~/work/git/git$ git log | grep ^commit | wc -l 2 131070 3 sa@wks:~/work/git/git$ date 4 Sun Feb 8 14:09:50 CET 2009 A notion of TimeWe can of course make more specific requests using the afore mentioned
There is a lot more that we could do instead of 5 sa@wks:~/work/git/git$ git log --since="2 days ago" | grep ^commit | wc -l 6 17 7 sa@wks:~/work/git/git$ git tag -l *29* 8 29 9 v2.6.29-rc1 10 v2.6.29-rc2 11 v2.6.29-rc3 12 sa@wks:~/work/git/git$ git log v2.6.29-rc3.. | grep ^commit | wc -l 13 697 Now we want to check for tags carrying Sets of CommitsIf we have several branches, we might want to only take a look at the commit logs within one branch. Lines 17 and 19 do just that. 14 sa@wks:~/work/git/git$ git branch 15 * linux_nfs_devel 16 master 17 sa@wks:~/work/git/git$ git log master..linux_nfs_devel | grep ^commit | wc -l 18 131070 19 sa@wks:~/work/git/git$ git log linux_nfs_devel..master | grep ^commit | wc -l 20 17473 21 sa@wks:~/work/git/git$ git log linux_nfs_devel...master | grep ^commit | wc -l 22 148543 23 sa@wks:~/work/git/git$ echo "131070 + 17473" | bc 24 148543 In line 18 we get to see all commits reachable from Line 22 shows the summed up commits, reachable either from branch
Logs from a particular File/DirectorySometimes we are just interested in the commit logs of a particular file (line 30) or any changes made below a particular directory (line 32). 25 sa@wks:~/work/git/git$ type ll 26 ll is aliased to `ls -lh' 27 sa@wks:~/work/git/git$ ll | egrep scripts\|Make 28 -rw-r--r-- 1 sa sa 54K 2009-02-07 22:29 Makefile 29 drwxr-xr-x 12 sa sa 4.0K 2009-02-07 22:29 scripts 30 sa@wks:~/work/git/git$ git log Makefile | grep ^commit | wc -l 31 425 32 sa@wks:~/work/git/git$ git log scripts/ | grep ^commit | wc -l 33 919 A notion of file contentWhich commits played a role in adding or removing file data containing
the string 34 sa@wks:~/work/git/git$ git log -S'googlegroups' | grep ^commit | wc -l 35 3 Output formating, Combining Commands, Statistics, PatchesI leave it to the reader to go over the following lines and figure things out — in essence, we can see how to combine several of the commands used so far (e.g. line 95). We also use objects names not just branches or tags. Another thing we are doing is to list the patch that goes with a particular commit. Sometimes we are only interested in commits that do not involve merging. Statistics about what and how much was committed are also interesting and last but not least we can see how to change to order of commits listed. 36 sa@wks:~/work/git/git$ git log --pretty=oneline -n2 v2.6.29-rc3.. 37 ae1a25da8448271a99745da03100d5299575a269 Merge git://git.kernel.org/pub/scm/linux/kernel/git/maso 38 fd9fc842bbab0cb5560b0d52ce4598c898707863 eCryptfs: Regression in unencrypted filename symlinks 39 sa@wks:~/work/git/git$ git log --stat -n1 fd9fc842bbab0cb5560b0d52ce4598c898707863 40 commit fd9fc842bbab0cb5560b0d52ce4598c898707863 41 Author: Tyler Hicks <[email protected]> 42 Date: Fri Feb 6 18:06:51 2009 -0600 43 44 eCryptfs: Regression in unencrypted filename symlinks 45 46 The addition of filename encryption caused a regression in unencrypted 47 48 [skipping a lot of lines...] 49 50 Signed-off-by: Tyler Hicks <[email protected]> 51 Signed-off-by: Linus Torvalds <[email protected]> 52 53 fs/ecryptfs/crypto.c | 4 ++-- 54 1 files changed, 2 insertions(+), 2 deletions(-) 55 sa@wks:~/work/git/git$ git log -p -n1 fd9fc842bbab0cb5560b0d52ce4598c898707863 56 commit fd9fc842bbab0cb5560b0d52ce4598c898707863 57 Author: Tyler Hicks <[email protected]> 58 Date: Fri Feb 6 18:06:51 2009 -0600 59 60 eCryptfs: Regression in unencrypted filename symlinks 61 62 The addition of filename encryption caused a regression in unencrypted 63 64 [skipping a lot of lines...] 65 66 Signed-off-by: Tyler Hicks <[email protected]> 67 Signed-off-by: Linus Torvalds <[email protected]> 68 69 diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c 70 index c01e043..f6caeb1 100644 71 --- a/fs/ecryptfs/crypto.c 72 +++ b/fs/ecryptfs/crypto.c 73 @@ -1716,7 +1716,7 @@ static int ecryptfs_copy_filename(char **copied_name, size_t *copied_name_size, 74 { 75 int rc = 0; 76 77 - (*copied_name) = kmalloc((name_size + 2), GFP_KERNEL); 78 + (*copied_name) = kmalloc((name_size + 1), GFP_KERNEL); 79 if (!(*copied_name)) { 80 rc = -ENOMEM; 81 goto out; 82 @@ -1726,7 +1726,7 @@ static int ecryptfs_copy_filename(char **copied_name, size_t *copied_name_size, 83 * in printing out the 84 * string in debug 85 * messages */ 86 - (*copied_name_size) = (name_size + 1); 87 + (*copied_name_size) = name_size; 88 out: 89 return rc; 90 } 91 sa@wks:~/work/git/git$ git log scripts/ | grep ^commit | wc -l 92 919 93 sa@wks:~/work/git/git$ git log --no-merges scripts/ | grep ^commit | wc -l 94 854 95 sa@wks:~/work/git/git$ git log --no-merges -S'KBUILD_VERBOSE' scripts/ | grep ^commit | wc -l 96 3 97 sa@wks:~/work/git/git$ git log -n4 --pretty=format:'%h : %s' --date-order --graph 98 * ae1a25d : Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable 99 |\ 100 * | fd9fc84 : eCryptfs: Regression in unencrypted filename symlinks 101 * | eeb9485 : Merge branch 'x86/fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/frob/linux-2.6-roland 102 |\ \ 103 | * | c09249f : x86-64: fix int $0x80 -ENOSYS return 104 sa@wks:~/work/git/git$ Naming commits with git showWe have seen several ways of specify commits already:
sa@wks:~/work/git/git$ git show -n1 ae1a25 | head -n3 commit ae1a25da8448271a99745da03100d5299575a269 Merge: fd9fc84... 42f15d7... Author: Linus Torvalds <[email protected]> sa@wks:~/work/git/git$ git show -n1 master | head -n3 commit ba743d1b0ce0b44c797c0de06c9db2781e4d1fdd Merge: 7b75b33... 441adf0... Author: Junio C Hamano <[email protected]> sa@wks:~/work/git/git$ git show -n1 v2.6.29-rc3 | head -n3 tag v2.6.29-rc3 Tagger: Linus Torvalds <[email protected]> Date: Wed Jan 28 10:49:44 2009 -0800 sa@wks:~/work/git/git$ git show -n1 HEAD | head -n3 commit ae1a25da8448271a99745da03100d5299575a269 Merge: fd9fc84... 42f15d7... Author: Linus Torvalds <[email protected]> sa@wks:~/work/git/git$ There are many more ways to specify commits as the specifying
revisions section of the $ git show fb47ddb2 # the first few characters of the object name are usually enough to specify it uniquely $ git show HEAD^ # the parent of the HEAD commit $ git show HEAD^^ # the grandparent $ git show HEAD~4 # the great-great-grandparent Merge commits may have more than one parent. By default, $ git show HEAD^1 # show the first parent of HEAD $ git show HEAD^2 # show the second parent of HEAD In addition to The When we discuss merges later on, we will also see the special name
We have used sa@wks:~/work/git/git$ git rev-parse origin ba743d1b0ce0b44c797c0de06c9db2781e4d1fdd sa@wks:~/work/git/git$ git rev-parse linux_nfs_devel ae1a25da8448271a99745da03100d5299575a269 sa@wks:~/work/git/git$ git rev-parse FETCH_HEAD ae1a25da8448271a99745da03100d5299575a269 sa@wks:~/work/git/git$ git rev-parse HEAD ae1a25da8448271a99745da03100d5299575a269 sa@wks:~/work/git/git$ TagsNow we are going to take a closer look at tags. How can we specify them, create them and finally use them to explore a repositories history. Naming branches, tags, and other referencesBranches, remote-tracking branches, and tags are all references to
commits. We already know this. All references are named with a
slash-separated path name starting with
The full name is occasionally useful if, for example, there ever exists a tag and a branch with the same name. Newly created references are actually stored in the As another useful shortcut, the Creating tagsAbove, when talking about branches, we have already seen how to create
a tag. What we did there was to create a tag object. However, we can
also create a so-called lightweight tag — this involves no tag object
and therefore no comment message ( 1 sa@wks:~/work/git/git$ git tag | wc -l 2 387 3 sa@wks:~/work/git/git$ git tag -u Markus -m "A real tag: with messge and signed" my_real_tag 4 5 You need a passphrase to unlock the secret key for 6 user: "Markus Gattol () <foo[at]bar.org>" 7 1024-bit DSA key, ID C0EC7E38, created 2009-02-06 8 9 sa@wks:~/work/git/git$ git tag my_lightweight_tag $(git rev-parse HEAD) 10 sa@wks:~/work/git/git$ git tag | wc -l 11 389 12 sa@wks:~/work/git/git$ git tag -v my_lightweight_tag 13 error: ae1a25da8448271a99745da03100d5299575a269: cannot verify a non-tag object of type commit. 14 error: could not verify the tag 'my_lightweight_tag' 15 sa@wks:~/work/git/git$ git tag -v my_real_tag 16 object ae1a25da8448271a99745da03100d5299575a269 17 type commit 18 tag my_real_tag 19 tagger markus gattol <sa@wks.(none)> 1234186536 +0100 20 21 A real tag: with messge and signed 22 gpg: Signature made Mon 09 Feb 2009 02:35:36 PM CET using DSA key ID C0EC7E38 23 gpg: Good signature from "Markus Gattol () <foo[at]bar.org>" 24 sa@wks:~/work/git/git$ As can be seen from lines 2 and 11, we created two tags, If we do specify an object name however as we did in line 9, then no tag object is created — only a reference to some commit. We see proof of this in lines 12 to 14. Tags and Repository HistoryA tag, well, is a tag. Humans put them on the work they do in order to mark their achievements. For example, we are working on a long therm goal. On our journey of getting there we pass milestones — projects states that describe or relate to achieving a high-level intermediary step on our way towards the final goal. A good example is the Linux kernel. There we got version numbers like
those which can be seen in lines 2 to 5 below. Those tags reflect
milestones for the Linux kernel. We create those tags on projects as
time progresses which can be seen when we compare the date when tags
have been made ( 1 sa@wks:~/work/git/git$ git tag | tail -n4 2 v2.6.29-rc1 3 v2.6.29-rc2 4 v2.6.29-rc3 5 v2.6.29-rc4 6 sa@wks:~/work/git/git$ git-for-each-ref --format="%(refname) %(taggerdate:relative)" --sort=taggerdate refs/tags/v2.6.29* 7 refs/tags/v2.6.29-rc1 4 weeks ago 8 refs/tags/v2.6.29-rc2 3 weeks ago 9 refs/tags/v2.6.29-rc3 12 days ago 10 refs/tags/v2.6.29-rc4 23 hours ago 11 sa@wks:~/work/git/git$ git-for-each-ref --format="%(refname) %(objectname) %(taggerdate:relative)" --sort=taggerdate refs/tags/v2.6.29* 12 refs/tags/v2.6.29-rc1 7a3862d6e9934ffe107fe7ddfbe2c63dba321793 4 weeks ago 13 refs/tags/v2.6.29-rc2 d31ce8060b0e875179ba5ca1d40475dc2a082cc7 3 weeks ago 14 refs/tags/v2.6.29-rc3 8be00154b8e949bf4b89ac198aef9a247532ac2d 12 days ago 15 refs/tags/v2.6.29-rc4 87c16e9e8bb74f14f4504305957e4346e7fc46ea 23 hours ago In lines 12 to 15 we have the same information as in lines 7 to 10 plus the actual object names (40-hexdigit string that is). So, what has all this to do with a repositories history? Let us assume that the commit 16 sa@wks:~/work/git/git$ git rev-parse v2.6.29-rc2^ 17 71556b9800fff8bf59075d2c1622acc9d99113ef fixed a certain problem. Now we would like to find the earliest tagged
release that contains that fix. Of course, there may be more than one
answer — if the history branched after commit
We could just visually inspect the commits since
A CLI junkie like me would rather use something like 18 sa@wks:~/work/git/git$ git name-rev 71556b98 19 71556b98 tags/v2.6.29-rc2~1
20 sa@wks:~/work/git/git$ git describe $(git rev-parse v2.6.29-rc2^) 21 v2.6.29-rc1-611-g71556b9 If we just want to verify whether a given tagged version contains a
given commit, we could use 22 sa@wks:~/work/git/git$ git merge-base $(git rev-parse v2.6.29-rc1) $(git rev-parse v2.6.29-rc2) 23 c59765042f53a79a7a65585042ff463b69cb248c 24 sa@wks:~/work/git/git$ git name-rev c59765042f53a79a7a6 25 c59765042f53a79a7a6 tags/v2.6.29-rc1^0 26 sa@wks:~/work/git/git$
Dealing with RegressionsWhat a regression is can be read here. How do regressions happen?
Suppose version 1 sa@wks:~/work/git/git$ git bisect start 2 sa@wks:~/work/git/git$ git branch 3 linux_nfs_devel 4 * master 5 sa@wks:~/work/git/git$ git bisect good $(git rev-parse v2.6.28-rc2) 6 sa@wks:~/work/git/git$ git bisect bad master 7 Bisecting: 8736 revisions left to test after this 8 [8807d321af394ffb2180d085669337bcd5018c50] Merge branch 'maint' 9 sa@wks:~/work/git/git$ git branch 10 * (no branch) 11 linux_nfs_devel 12 master If we run 13 sa@wks:~/work/git/git$ git bisect bad 14 Bisecting: 4367 revisions left to test after this 15 [568907f52051f340dc29a907f67e69260d7d4e7a] Added logged warnings for CVS error returns 16 sa@wks:~/work/git/git$ git rev-parse HEAD 17 568907f52051f340dc29a907f67e69260d7d4e7a 18 sa@wks:~/work/git/git$ git good 19 git: 'good' is not a git-command. See 'git --help'. 20 sa@wks:~/work/git/git$ git bisect good 21 Bisecting: 2183 revisions left to test after this 22 [a153adf683d2b6e22c7e892ed8a161b140156186] gitweb: Fix setting $/ in parse_commit() 23 sa@wks:~/work/git/git$ git bisect bad 24 Bisecting: 1091 revisions left to test after this 25 [65a4e98a22eab9317a05d1485c7c5a9c5befd589] Git.pm: Don't #define around die 26 sa@wks:~/work/git/git$ git rev-parse HEAD 27 65a4e98a22eab9317a05d1485c7c5a9c5befd589 28 sa@wks:~/work/git/git$ git branch 29 * (no branch) 30 linux_nfs_devel 31 master 32 sa@wks:~/work/git/git$ git bisect reset 33 Switched to branch "master" 34 sa@wks:~/work/git/git$ git branch 35 linux_nfs_devel 36 * master 37 sa@wks:~/work/git/git$ In line 14 we can see that we currently have
After some time, it will output the commit ID of the guilty commit. We
can then examine the commit with Note that the revision which Instead of There are also ways to automate the bisecting process if we have a
test script that can tell a good from a bad commit. Viewing old Revisions of some FileWe can always view an old version of a file by just checking out the
correct revision first. But sometimes it is more convenient to be able
to view an old revision of a single file without checking anything
out. The command in line 28 does just that. The command in line 3 is
just an alias in my 1 sa@wks:~/work/git/git$ type pi 2 pi is aliased to `ls -la | grep' 3 sa@wks:~/work/git/git$ pi READ 4 -rw-r--r-- 1 sa sa 2166 2009-02-10 13:29 README 5 sa@wks:~/work/git/git$ git show $(git log -n1 --no-merges --pretty=oneline README | cut -d' ' -f1) 6 commit 8a124b82a03240b10c83085559e5988bc92ea7e2 7 Author: Joey Hess <[email protected]> 8 Date: Tue Jan 6 23:23:37 2009 -0500 9 10 README: tutorial.txt is now called gittutorial.txt 11 12 Signed-off-by: Joey Hess <[email protected]> 13 Signed-off-by: Junio C Hamano <[email protected]> 14 15 diff --git a/README b/README 16 index 548142c..5fa41b7 100644 17 --- a/README 18 +++ b/README 19 @@ -24,7 +24,7 @@ It was originally written by Linus Torvalds with help of a group of 20 hackers around the net. It is currently maintained by Junio C Hamano. 21 22 Please read the file INSTALL for installation instructions. 23 -See Documentation/tutorial.txt to get started, then see 24 +See Documentation/gittutorial.txt to get started, then see 25 Documentation/everyday.txt for a useful minimum set of commands, 26 and "man git-commandname" for documentation of each command. 27 CVS users may also want to read Documentation/cvs-migration.txt. 28 sa@wks:~/work/git/git$ git show $(git rev-parse v2.6.29-rc1):README | head 29 Linux kernel release 2.6.xx <http://kernel.org/> 30 31 These are the release notes for Linux version 2.6. Read them carefully, 32 as they tell you what this is all about, explain how to install the 33 kernel, and what to do if something goes wrong. 34 35 WHAT IS LINUX? 36 37 Linux is a clone of the operating system Unix, written from scratch by 38 Linus Torvalds with assistance from a loosely-knit team of hackers across 39 sa@wks:~/work/git/git$ The command in line 5 is also interesting, showing us the last commit
that modified In line 28, before the colon may be anything that names a commit, and
after it may be any path to a file tracked by GIT, Grep through the entire HistoryWe have If we wanted to search through history as well, then we would use Repository Maintenance and EfficiencyThere are basically two commands to maintain a repository and one particular with regards to storage capacity:
Configure GITGIT has various things that can be configured. Almost anything can be
done with
GIT's Configuration FilesIf not set explicitly with
If no further options are given, all reading options will read all of
these files that are available. If the global or the system-wide
configuration files are not available (which is the default) they will
be ignored. If the repository configuration file is not available or
readable, All writing options will per default write to the repository specific
configuration file. Note that this also affects options like
We can override these rules either by command line options or by environment variables — both cases will be shown further down. The The Basic SettingsLet us first create a new playground in lines 1 to 6 and also take a
look at 1 sa@wks:/tmp$ mkdir test && cd test && cp /home/sa/.emacs . 2 sa@wks:/tmp/test$ git init && git add . && git commit -a -m "Initial Commit." 3 Initialized empty Git repository in /tmp/test/.git/ 4 Created initial commit 8b1a702: Initial Commit. 5 1 files changed, 5609 insertions(+), 0 deletions(-) 6 create mode 100644 .emacs 7 sa@wks:/tmp/test$ cat .git/config 8 [core] 9 repositoryformatversion = 0 10 filemode = true 11 bare = false 12 logallrefupdates = true 13 sa@wks:/tmp/test$ cd I am not going to talk about the default options listed within the
What we are really after right now is putting the some basic
configuration settings into our global GIT configuration file i.e.
14 sa@wks:~$ pi gitcon 15 sa@wks:~$ git config --global user.name "Markus Gattol" 16 sa@wks:~$ pi gitcon 17 -rw-r--r-- 1 sa sa 24 2009-02-10 18:45 .gitconfig 18 sa@wks:~$ cat .gitconfig 19 [user] 20 name = Markus Gattol 21 sa@wks:~$ git config --global user.email "[email protected]" 22 sa@wks:~$ git config --global user.signingkey "Markus" In 14 I am just checking (using an alias from my One might consult the manual page ( Finally, after issuing line 15, line 21 and 22 are responsible for putting the last bits and pieces of basic configuration information into place. Those three basically tell GIT who we are and how to contact us — enough to work and carry out the most of the tasks GIT can be used for. Nice-to-have SettingsBesides the basic configuration information we can put a lot more into our configuration files as can be seen below. 23 sa@wks:~$ git config --global core.excludesfile ~/.gitignore 24 sa@wks:~$ git config --global alias.lcr "cat-file commit HEAD" 25 sa@wks:~$ git config --global alias.com "commit -a -s" 26 sa@wks:~$ git config --global alias.st "status" 27 sa@wks:~$ git config --global alias.che "checkout" 28 sa@wks:~$ git config --global alias.llnm "log -n6 --no-merges HEAD" 29 sa@wks:~$ git config --global alias.ll "log -n6 HEAD" Line 23 is used to tell GIT about our ignorefile — a file, filled
with globs, in order to ignore particular files like for example
editor backup files, files ending in Note that In order to ignore files in a repository basis and not on a per user
basis, we would put a For example, the repository Line 24 to 29 are aliases. Let us take a look at line 24 for example.
When I type
One might have noticed the I, as many others, consider this best practices and therefore, whenever I commit, I sign off the commit. Anybody should do this in fact. Note, that signing of a commit has noting to do with digitally signing a commit so do not confuse them. If the alias expansion is prefixed with an exclamation point, it will
be treated as a shell command. For example, defining What about some colored output? Well, The fact that I am using GNU Emacs respectively Bash from within Emacs should not confuse the reader as the same commands produce exactly the same results from a native Bash CLI (Command Line Interface) as can be seen in the second screenshot. Finally, we can take a look at 30 sa@wks:~$ cat .gitconfig 31 [user] 32 name = Markus Gattol 33 email = [email protected] 34 signingkey = Markus 35 [core] 36 excludesfile = /home/sa/.gitignore 37 [alias] 38 lcr = cat-file commit HEAD 39 com = commit -a -s 40 st = status 41 che = checkout 42 llnm = log -n4 --no-merges HEAD 43 ll = log -n6 HEAD 44 [color] 45 ui = true 46 [diff] 47 renames = copy 48 sa@wks:~$ cat .gitignore 49 *\.~[[:digit:]]* 50 *.elc 51 *.[oa] 52 *.html 53 sa@wks:~$ alias | grep gllol 54 alias gllol='/usr/local/bin/git/my_show_recent_logs.sh' 55 sa@wks:~$ alias | grep gllol | cut -d \' -f2 | xargs cat 56 #! /bin/sh 57 git log -n15 --date=relative --pretty=format:'%H %cr CN: %cn AN: %an S: %s' | perl -lpe 's/\s+(.*?)(\s)\s/sprintf "%-20s", " $1"/eg' 58 sa@wks:~$ Above, in line 23, we told GIT where to find our ignore file.
Meanwhile I have created this file and filled with some sane ignore
patterns (lines 49 to 52). Line 49 will make GIT ignore backup files
created by GNU Emacs. Line 50 will ignore bytecompiled GNU Emacs
files. Line 51 will GIT make ignore object files (
The second screenshot above also shows a command which is an alias too
( We could of course chose to put it directly into Also, the sa@wks:~$ echo $PATH /usr/local/bin:/usr/bin:/bin:/usr/games sa@wks:~$ Last but not least, and most importantly, what exactly does this script (some may call it one-liner) do? Well, what it does is showing me the last 15 commits, one per line, each with the commit name, relative commit time, committer name, author name and the subject line of the commit message. Environment VariablesAside from The user's name and email address should be defined in the
configuration file ( $ export GIT_AUTHOR_NAME="Jenna Haze" $ export GIT_COMMITTER_NAME="Jenna Haze" $ export GIT_AUTHOR_EMAIL="[email protected]" MergingIn this subsection we are going to take a look at merging. What merging does is to join two or more development histories together. With regards to merging, when we speak of histories, what we actually referring to are branches. We can merge branches i.e. we are joining development histories together. We know three different types of merging:
Merge TrackingMerge tracking is a facility offered by some SCM (Software Configuration Management) systems. It is the ability to remember which changes have been merged from one line of development (also known as branch) to another and act accordingly. GIT records this information, along with other information, in its commit objects. Other systems not initially build with a notion of merge tracking in mind, have lately started efforts in trying to provide it too. It works fundamentally different though — compared to what GIT does, the information needed for mergre tracking is stored on separate metadata entities and a user has to explicitly flag merge commits manually (something that GIT does automatically). The problem with having system without automatic merge tracking is that it is the responsibility of the user to manually record which changes have been previously merged and act appropriately (for example, by not attempting to merge the same change twice). This will sooner or later lead to problems e.g. when someone flags the wrong commit or simply forgets to do it entirely. In all but the simplest cases of merging, merge tracking is a significant time saver and a real convenience which encourages developers to use branches in ways that improve their productivity and the quality of their work. A lack of merge tracking can actually be a disincentive for branching at all, and for those that choose to branch anyway it can be a significant drain on their time. Finally, one can be pretty sure to mess up sooner or later by manually tracking things which might then result in a disaster... No merge ConflictsLet us start with the most trivial case — merging two branches whereas no merge conflict will arise. 1 sa@wks:~$ cd work/git/ 2 sa@wks:~/work/git$ mkdir t 3 sa@wks:~/work/git$ cd t/ 4 sa@wks:~/work/git/t$ touch some_file 5 sa@wks:~/work/git/t$ git init && git cwi 6 Initialized empty Git repository in /home/sa/work/git/t/.git/ 7 sa@wks:~/work/git/t$ git st 8 # On branch master 9 # 10 # Initial commit 11 # 12 # Changes to be committed: 13 # (use "git rm --cached <file>..." to unstage) 14 # 15 # new file: some_file 16 # 17 sa@wks:~/work/git/t$ git cwh -m "initial commit" 18 [master (root-commit)]: created f4965fe: "initial commit" 19 0 files changed, 0 insertions(+), 0 deletions(-) 20 create mode 100644 some_file 21 sa@wks:~/work/git/t$ git st 22 # On branch master 23 nothing to commit (working directory clean) 24 sa@wks:~/work/git/t$ git branch 25 * master There is really nothing special in lines 1 to 25. All we did was to
start a new repository from scratch. However, the commands used in
line 5 (
This has been discussed above already. Right now, this is what my
sa@wks:~$ pwd /home/sa sa@wks:~$ cat .gitconfig [user] name = Markus Gattol email = [email protected] signingkey = Markus [core] excludesfile = /home/sa/.gitignore [color] ui = true [alias] # status llnm = log -n6 --no-merges HEAD ll = log -n6 HEAD st = status wc = whatchanged -n1 wcd = whatchanged -n1 -p #diff dwi = diff dih = diff --cached dwh = diff HEAD dwt = diff --check # commit cwh = commit -a -s cih = commit -s cwi = add . #misc lcr = cat-file commit HEAD [diff] renames = copy [rerere] enabled = true autoupdate = true [gui] recentrepo = /tmp/demo_git_source/git [gc] reflogexpire = 365 reflogexpireunreachable = 180 [push] default = matching sa@wks:~$ 26 sa@wks:~/work/git/t$ git checkout -b my_first_branch 27 Switched to a new branch "my_first_branch" 28 sa@wks:~/work/git/t$ git branch 29 master 30 * my_first_branch 31 sa@wks:~/work/git/t$ echo "some text" > some_file 32 sa@wks:~/work/git/t$ git dwi 33 diff --git a/some_file b/some_file 34 index e69de29..7b57bd2 100644 35 --- a/some_file 36 +++ b/some_file 37 @@ -0,0 +1 @@ 38 +some text 39 sa@wks:~/work/git/t$ git cwh -m "we made some changes to some_file" 40 [my_first_branch]: created 2667776: "we made some changes to some_file" 41 1 files changed, 1 insertions(+), 0 deletions(-) Line 26 is where it starts to become interesting when we create and
switch to a new branch which we check issuing line 28. We are at
branch Line 32 shows us the difference between the working tree and the index
which we then commit in line 39. At this point branch 42 sa@wks:~/work/git/t$ git checkout master 43 Switched to branch "master" 44 sa@wks:~/work/git/t$ cat some_file 45 sa@wks:~/work/git/t$ git merge my_first_branch 46 Updating f4965fe..2667776 47 Fast forward 48 some_file | 1 + 49 1 files changed, 1 insertions(+), 0 deletions(-) 50 sa@wks:~/work/git/t$ cat some_file 51 some text In line 42 we switch back from branch The actual magic happens after we issued line 45. It tells GIT to
merge branch 52 sa@wks:~/work/git/t$ git diff master my_first_branch 53 sa@wks:~/work/git/t$ echo "more text" >> some_file 54 sa@wks:~/work/git/t$ git diff master my_first_branch 55 sa@wks:~/work/git/t$ git cwh -m "and again, changes to some_file" 56 [master]: created 865e8b4: "and again, changes to some_file" 57 1 files changed, 1 insertions(+), 0 deletions(-) 58 sa@wks:~/work/git/t$ git diff my_first_branch 59 diff --git a/some_file b/some_file 60 index 7b57bd2..4bfbf30 100644 61 --- a/some_file 62 +++ b/some_file 63 @@ -1 +1,2 @@ 64 some text 65 +more text 66 sa@wks:~/work/git/t$ Now branch Sure we did but the getting no output at that point is actually
correct. Our changes did not made it into After we commit in line 55, we can see that the heads of branch It is exactly as simple and wild as it sounds. It is exactly as cool as it sounds. It is exactly as helpful as it sounds. It is exactly what we have already discussed before. Encountering merge ConflictsWhile the above example did not cause any conflicts while merging two
branches, we are now going to create a textbook case where it will
happen. However, before we start, I would like to take a look at some
goody called Another thing important to understand is that in case of a merge conflict, until it is resolved, the index contains all versions of a file:
Reuse Recorded ResolutionThis comes in handy in a workflow that employs relatively long lived topic branches. With such branches, the developer sometimes needs to resolve the same conflict over and over again until the topic branches are done (either merged to the release branch (yet another topic branch), or sent out and accepted upstream). So the users benefit is, sa@wks:~$ git config --global rerere.enabled true sa@wks:~$ git config --global rerere.autoupdate true sa@wks:~$ git config --get rerere.enabled true sa@wks:~$ git config --get rerere.autoupdate true sa@wks:~$ The merge Conflict1 sa@wks:~/work/git/test$ type la && la 2 la is aliased to `ls -la' 3 total 0 4 drwxr-xr-x 2 sa sa 6 2009-02-17 20:15 . 5 drwxr-xr-x 11 sa sa 113 2009-02-17 18:08 .. 6 sa@wks:~/work/git/test$ touch file 7 sa@wks:~/work/git/test$ git init 8 Initialized empty Git repository in /home/sa/work/git/test/.git/ 9 sa@wks:~/work/git/test$ git add . 10 sa@wks:~/work/git/test$ git cwh -m "initial commit" 11 [master (root-commit)]: created 623df59: "initial commit" 12 0 files changed, 0 insertions(+), 0 deletions(-) 13 create mode 100644 file Nothing special in lines 1 to 13. Just a shell alias in line 1 and a
GIT alias in line 10 ( 14 sa@wks:~/work/git/test$ git checkout -b experimental 15 Switched to a new branch "experimental" 16 sa@wks:~/work/git/test$ echo a > file 17 sa@wks:~/work/git/test$ git cwh -m "altering file on branch experimental" 18 [experimental]: created 0247263: "altering file on branch experimental" 19 1 files changed, 1 insertions(+), 0 deletions(-) 20 sa@wks:~/work/git/test$ git checkout master 21 Switched to branch "master" 22 sa@wks:~/work/git/test$ git branch 23 experimental 24 * master 25 sa@wks:~/work/git/test$ echo b > file 26 sa@wks:~/work/git/test$ git cwh -m "altering file on branch master" 27 [master]: created 5f8d808: "altering file on branch master" 28 1 files changed, 1 insertions(+), 0 deletions(-) Lines 14 to 28 are also rather unspectacular. All we did is checkout
(and thereby create) the branch 29 sa@wks:~/work/git/test$ git merge experimental 30 Auto-merging file 31 CONFLICT (content): Merge conflict in file 32 Recorded preimage for 'file' 33 Automatic merge failed; fix conflicts and then commit the result. 34 sa@wks:~/work/git/test$ git log --merge 35 commit 5f8d808a4b820e46f9afcec73054c7c62aaafbf0 36 Author: Markus Gattol <[email protected]> 37 Date: Tue Feb 17 18:25:35 2009 +0100 38 39 altering file on branch master 40 41 Signed-off-by: Markus Gattol <[email protected]> 42 43 commit 024726369ba62f9e5f7533b91ff993f2dd14c3d7 44 Author: Markus Gattol <[email protected]> 45 Date: Tue Feb 17 18:25:09 2009 +0100 46 47 altering file on branch experimental 48 49 Signed-off-by: Markus Gattol <[email protected]> The interesting part is when we try to merge branch For now, I just want the reader to recognize line 32 and recap what we looked at earlier, when we were talking about reuse recorded resolution, which merge tracking enables us to do. More on that shortly...
50 sa@wks:~/work/git/test$ git diff experimental 51 diff --git a/file b/file 52 index 7898192..d7ef451 100644 53 --- a/file 54 +++ b/file 55 @@ -1 +1,5 @@ 56 +<<<<<<< HEAD:file 57 +b 58 +======= 59 a 60 +>>>>>>> experimental:file Line 50 issues the command that shows us the differences between
branch 61 sa@wks:~/work/git/test$ git ls-files -v -u file 62 M 100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 1 file 63 M 100644 61780798228d17af2d34fce4cfbdf35556832472 2 file 64 M 100644 78981922613b2afb6025042ff6bd878ac1994e85 3 file 65 sa@wks:~/work/git/test$ git show :1:file && echo "branch master, common ancestor" 66 branch master, common ancestor 67 sa@wks:~/work/git/test$ git show :2:file && echo "branch master, HEAD" 68 b 69 branch master, HEAD 70 sa@wks:~/work/git/test$ git show :3:file && echo "branch experimental, HEAD" 71 a 72 branch experimental, HEAD If we recap, I mentioned something about stage files above. As of now
we have three — well, actually four, including the file with the
conflict markers — files involved with the merge (lines 62 to 64).
The columns are pretty much self-explaining — the last one says it is
about file In lines 65 to 72 we take a look at the contents of each, also telling about what stage represents what file e.g. branch master, common ancestor etc. For more information, please take a look above. Stage 2 and 3 are no surprise as they are a result of line 25 and 16
respectively. What I want the reader to note here is that we get no
result from line 65 (except for the So the rationale why line 65 does not show anything is, because there
is nothing to show — the ancestor (line 62, ID 73 sa@wks:~/work/git/test$ git branch 74 experimental 75 * master 76 sa@wks:~/work/git/test$ cat file 77 <<<<<<< HEAD:file 78 b 79 ======= 80 a 81 >>>>>>> experimental:file 82 sa@wks:~/work/git/test$ echo a > file 83 sa@wks:~/work/git/test$ cat file 84 a 85 sa@wks:~/work/git/test$ git cwh -m "resolving merge conflict on branch master" 86 Recorded resolution for 'file'. 87 [master]: created 595ed02: "resolving merge conflict on branch master" 88 sa@wks:~/work/git/test$ git merge experimental 89 Already up-to-date. 90 sa@wks:~/work/git/test$ cat .git/rr-cache/0630df854874fc5ffb92a197732cce0d8928e898/preimage 91 <<<<<<< 92 a 93 ======= 94 b 95 >>>>>>> 96 sa@wks:~/work/git/test$ cat .git/rr-cache/0630df854874fc5ffb92a197732cce0d8928e898/postimage 97 a 98 sa@wks:~/work/git/test$ At this point we know all we need to know. We know we have a merge
conflict and we also know what caused it and how to resolve it. In
order to resolve it, we can work on either one of the stage files 2
or 3. All three are in the index, Then we commit again and thereby resolve the merge conflict. Line 86 is about the same fabulous thing as is line 32 — reuse recorded resolution. For it to work, the information how a merge conflict was resolved needs to be stored somewhere — lines 90 to 97. Finally I would like to add that the above scenario could have also involved not just local branches but also remote branches, and even a mixture of the both. And as always, specifying various commits can be done in various ways. Fixing Mistakes
We all make mistakes all the time. In order for life to evolve we need mistakes to happen.
Experience is that marvelous thing that enables you recognize a mistake GIT can ease the pain after a mistake has been made. In most cases, GIT can make it go away entirely by using one of three commands:
The Mistake has not been committed yetIn case we messed up the working tree, but have not yet committed our
mistake, we can return the entire working tree as well as the index to
the last committed state with 1 sa@wks:/tmp$ mkdir test 2 sa@wks:/tmp$ cd test/ 3 sa@wks:/tmp/test$ touch file_{a,b} 4 sa@wks:/tmp/test$ ll 5 total 0 6 -rw-r--r-- 1 sa sa 0 2009-02-18 18:24 file_a 7 -rw-r--r-- 1 sa sa 0 2009-02-18 18:24 file_b 8 sa@wks:/tmp/test$ git add . 9 fatal: Not a git repository (or any of the parent directories): .git 10 sa@wks:/tmp/test$ git init 11 Initialized empty Git repository in /tmp/test/.git/ 12 sa@wks:/tmp/test$ git add . 13 sa@wks:/tmp/test$ git cwh -m "initial commit" 14 [master (root-commit)]: created 8eda414: "initial commit" 15 0 files changed, 0 insertions(+), 0 deletions(-) 16 create mode 100644 file_a 17 create mode 100644 file_b 18 sa@wks:/tmp/test$ rm file_b 19 sa@wks:/tmp/test$ echo "I will regret this" > file_a 20 sa@wks:/tmp/test$ ll 21 total 4.0K 22 -rw-r--r-- 1 sa sa 19 2009-02-18 18:25 file_a 23 sa@wks:/tmp/test$ cat file_a 24 I will regret this 25 sa@wks:/tmp/test$ git reset --hard HEAD 26 HEAD is now at 8eda414 initial commit 27 sa@wks:/tmp/test$ ll 28 total 0 29 -rw-r--r-- 1 sa sa 0 2009-02-18 18:25 file_a 30 -rw-r--r-- 1 sa sa 0 2009-02-18 18:25 file_b 31 sa@wks:/tmp/test$ cat file_a In line 13 a usual commit happens. Then we mess up — line 18 and 19.
No problem, line 25 returns he working tree right to the state it had
been at line 13 i.e. file The above example is trivial because no commit happened which would
already commit a mistake — the mess was contained to the working tree
and had not made it to the index or The Mistake has already been committedIf we make a commit that we later wish we had not, there are two fundamentally different ways to fix the problem:
Fixing a mistake with a new commitCreating a new commit that reverts an earlier change is easy. In order
to do so, the working tree must be clean. Then, all we need to do is
to pass a ref of the bad commit to 32 sa@wks:/tmp/test$ echo "making a mistake and committing it" > file_b 33 sa@wks:/tmp/test$ git cwh -m 'omg, I am committing a mistake' 34 [master]: created 0708ce3: "omg, I am committing a mistake" 35 1 files changed, 1 insertions(+), 0 deletions(-) 36 sa@wks:/tmp/test$ cat file_b 37 making a mistake and committing it 38 sa@wks:/tmp/test$ git revert HEAD 39 40 41 [ here the default editor opened...] 42 43 44 [master]: created 1bf6c3d: "Revert "omg, I am committing a mistake"" 45 1 files changed, 0 insertions(+), 1 deletions(-) 46 sa@wks:/tmp/test$ cat file_b 47 sa@wks:/tmp/test$ gllol 48 1bf6c3d8de2942b9c7dc3c9a6f8dbdeedb39f32b 2 minutes ago CN: Markus Gattol AN: Markus Gattol S: Revert "omg, I am committing a mistake" 49 0708ce3f00eb4108bc36735641eaae46b948f84b 5 minutes ago CN: Markus Gattol AN: Markus Gattol S: omg, I am committing a mistake 50 8eda414d07791a8a1160a7b4ac5c913b1a06643d 42 minutes ago CN: Markus Gattol AN: Markus Gattol S: initial commit 51 sa@wks:/tmp/test$ As we can see in lines 32 to 51, reverting a change works just fine. Line 36 and 46 proof it. What can now also be seen from lines 48 to 50 is that the command issued in line 25 did not create any commit but either one, line 33 and 38 did. Those commits are now part of the history which is perfectly fine even though they represent a back and forth action. The point is, prior to it and also after it, the history is coherent and intact. Of course, we can also revert an earlier change, for example, the
grandparent Going back even further is also possible (any ID is possible) but may become more and more tricky based on the complexity of some project. However, again, the point is, as long as the history is coherent and intact, we will not make mistakes we cannot recover from... GIT just prevents us from doing so as long as we are not explicitly fiddling with the history on a low level. Fixing a mistake by editing historyWe have used
Optionally we can also reset the index and working tree to match that
commit if we use the If the problematic commit is the most recent commit, and we have not
yet made that commit public, then we may just destroy it using
1 sa@wks:/tmp/test$ la 2 total 4 3 drwxr-xr-x 2 sa sa 6 2009-02-19 11:30 . 4 drwxrwxrwt 14 root root 4096 2009-02-19 11:18 .. 5 sa@wks:/tmp/test$ touch our_file 6 sa@wks:/tmp/test$ git init && git add . 7 Initialized empty Git repository in /tmp/test/.git/ 8 sa@wks:/tmp/test$ git cwh -m 'initial commit' 9 [master (root-commit)]: created b076931: "initial commit" 10 0 files changed, 0 insertions(+), 0 deletions(-) 11 create mode 100644 our_file 12 sa@wks:/tmp/test$ echo "this will be corrected using --soft" > our_file 13 sa@wks:/tmp/test$ git cwh -m 'wrote some content into our_file' 14 [master]: created cb565f1: "wrote some content into our_file" 15 1 files changed, 1 insertions(+), 0 deletions(-) 16 sa@wks:/tmp/test$ gllol 17 cb565f167f1f8405edd940159763f79d2aef7f61 7 seconds ago CN: Markus Gattol AN: Markus Gattol S: wrote some content into our_file 18 b07693168359a71b6bb4635de6d62cb6f1119a76 73 seconds ago CN: Markus Gattol AN: Markus Gattol S: initial commit 19 sa@wks:/tmp/test$ cat .git/HEAD 20 ref: refs/heads/master 21 sa@wks:/tmp/test$ cat .git/refs/heads/master 22 cb565f167f1f8405edd940159763f79d2aef7f61 23 sa@wks:/tmp/test$ git reset --soft HEAD^ 24 sa@wks:/tmp/test$ cat .git/ORIG_HEAD 25 cb565f167f1f8405edd940159763f79d2aef7f61 26 sa@wks:/tmp/test$ 27 sa@wks:/tmp/test$ gllol 28 b07693168359a71b6bb4635de6d62cb6f1119a76 5 minutes ago CN: Markus Gattol AN: Markus Gattol S: initial commit 29 sa@wks:/tmp/test$ cat our_file 30 this will be corrected using --soft 31 sa@wks:/tmp/test$ echo 'editing working tree; --soft did not change the working tree nor the index' > our_file 32 sa@wks:/tmp/test$ git commit -a -c ORIG_HEAD 33 34 35 [ here the default editor opened...] 36 37 38 sa@wks:/tmp/test$ gllol 39 b8c2b79d917608d2ac08597ee8008a862ad47fe1 2 minutes ago CN: Markus Gattol AN: Markus Gattol S: wrote some content into our_file (corrected version) 40 b07693168359a71b6bb4635de6d62cb6f1119a76 9 minutes ago CN: Markus Gattol AN: Markus Gattol S: initial commit 41 sa@wks:/tmp/test$ cat .git/ORIG_HEAD 42 cb565f167f1f8405edd940159763f79d2aef7f61 43 sa@wks:/tmp/test$ cat .git/HEAD 44 ref: refs/heads/master 45 sa@wks:/tmp/test$ cat .git/refs/heads/master 46 b8c2b79d917608d2ac08597ee8008a862ad47fe1 What can be is most often done when we remember that what we just committed is incomplete, or we misspelled our commit message, or both. Again, I want to point out that this sort of fixing a mistake is only recommended as long as the tainted commit/history has not been made public. That we really just replaced one commit with another one without
resetting the working tree can be seen from lines 17 and 22
respectively 39 and 46. Bottom line here is, we can replace a commit and edit its commit
message without destroying the working tree or the index. This is
different to Alternatively, we can edit the working directory and update the index
to fix our mistake, just as if we were going to create a new commit,
then run The commit 47 sa@wks:/tmp/test$ git st 48 # On branch master 49 nothing to commit (working directory clean) 50 sa@wks:/tmp/test$ ll 51 total 4.0K 52 -rw-r--r-- 1 sa sa 75 2009-02-19 11:38 our_file 53 sa@wks:/tmp/test$ cat our_file 54 editing working tree; --soft did not change the working tree nor the index 55 sa@wks:/tmp/test$ git commit --amend 56 57 58 [ here the default editor opened...] 59 60 61 [master]: created 12c9cf6: "wrote some content into our_file (corrected version of the corrected version)" 62 1 files changed, 1 insertions(+), 0 deletions(-) 63 sa@wks:/tmp/test$ gllol 64 12c9cf603286326553dcdc10b90086be5f62cd33 2 minutes ago CN: Markus Gattol AN: Markus Gattol S: wrote some content into our_file (corrected version of the corrected version) 65 b07693168359a71b6bb4635de6d62cb6f1119a76 2 hours ago CN: Markus Gattol AN: Markus Gattol S: initial commit 66 sa@wks:/tmp/test$ cat .git/refs/heads/master 67 12c9cf603286326553dcdc10b90086be5f62cd33 68 sa@wks:/tmp/test$ cat our_file 69 a 70 sa@wks:/tmp/test$ Ooops, we did it again ;-]... We just swapped the old commit for a new one in line 55, just as we did above in line 23. The new commit message can be seen in line 64 after I issued my well beloved gllol. The cherry on top thing is that, the author and time stamp is not being altered by all the replacing games we just did i.e. it is taken/reused from the commit in line 13. Of course, we can also amend those (see manual files for detailed information). So, we swapped the commit again and we also edited the commit message
again and made changes to the working tree ( Again, we should never do this to a commit that may already have been
merged into another branch i.e. which has been made public — one
should use Checking out an old version of a fileIn the process of undoing a previous bad change, we may find it useful
to check out an older version of a particular file using
If we just want to look at an older version of the file, without
modifying the working directory, 1 sa@wks:/tmp$ mkdir git_demo && cd git_demo && touch my_file && git init && git add . && git cwh -m 'initial commit' 2 Initialized empty Git repository in /tmp/git_demo/.git/ 3 [master (root-commit)]: created 89f870d: "initial commit" 4 0 files changed, 0 insertions(+), 0 deletions(-) 5 create mode 100644 my_file 6 sa@wks:/tmp/git_demo$ la 7 total 8 8 drwxr-xr-x 3 sa sa 31 2009-02-19 14:16 . 9 drwxrwxrwt 14 root root 4096 2009-02-19 14:16 .. 10 drwxr-xr-x 9 sa sa 4096 2009-02-19 14:16 .git 11 -rw-r--r-- 1 sa sa 0 2009-02-19 14:16 my_file 12 sa@wks:/tmp/git_demo$ git st 13 # On branch master 14 nothing to commit (working directory clean) 15 sa@wks:/tmp/git_demo$ type bani 16 bani is aliased to `banshee --query-{artist,title} >& `tty`' 17 sa@wks:/tmp/git_demo$ bani > my_file 18 sa@wks:/tmp/git_demo$ cat my_file 19 artist: Patricia Barber 20 title: Morpheus 21 sa@wks:/tmp/git_demo$ git cwh -m "Markus\'s current track" 22 [master]: created 83e5cf6: "Markus\'s current track" 23 1 files changed, 2 insertions(+), 0 deletions(-) 24 sa@wks:/tmp/git_demo$ banshee --query-{artist,title} | tee -a my_file && cat my_file 25 artist: Paul Hardcastle 26 title: Rain Forest 27 artist: Patricia Barber 28 title: Morpheus 29 artist: Paul Hardcastle 30 title: Rain Forest 31 sa@wks:/tmp/git_demo$ git cwh -m "last two tracks (including current one)" 32 [master]: created 5a978e9: "last two tracks (including current one)" 33 1 files changed, 2 insertions(+), 0 deletions(-) Nothing special in lines 1 to 23. In line 24 I basically use the 34 sa@wks:/tmp/git_demo$ git show HEAD^:my_file 35 artist: Patricia Barber 36 title: Morpheus 37 sa@wks:/tmp/git_demo$ cat my_file 38 artist: Patricia Barber 39 title: Morpheus 40 artist: Paul Hardcastle 41 title: Rain Forest 42 sa@wks:/tmp/git_demo$ git show HEAD:my_file 43 artist: Patricia Barber 44 title: Morpheus 45 artist: Paul Hardcastle 46 title: Rain Forest 47 sa@wks:/tmp/git_demo$ git checkout HEAD^ my_file 48 sa@wks:/tmp/git_demo$ cat my_file 49 artist: Patricia Barber 50 title: Morpheus 51 sa@wks:/tmp/git_demo$ git st 52 # On branch master 53 # Changes to be committed: 54 # (use "git reset HEAD <file>..." to unstage) 55 # 56 # modified: my_file 57 # 58 sa@wks:/tmp/git_demo$ Line 34 is a perfect example of how to use Sharing our ChangesNow is a good time to revisit the workflow section again. Sharing our changes with others is one of the most important, if not the most important purpose why one would want to use a SCM (Software Configuration Management) system. General ConsiderationsBefore we start with detailed issues, there are some things we should consider and keep in mind whenever we make commits and/or prepare patches. Suppose we are contributors to a large project, and we want to add a complicated feature. We want to present it to the other developers in a way that makes it easy for them to read our changes, verify that they are correct, and understand why we made each change.
So the ideal is usually to produce a series of patches/commits such that the following checklist gets a nod on every item:
Below I will introduce some tools that can help us do this, explain how to use them, and then explain some of the problems that can arise because we are rewriting history. Creating good Commit/Log MessagesThough not required, it is a good idea to begin the commit message with a single short line summarizing the change, followed by a blank line and then a more thorough description. Tools that turn commits into email, for example, use the first line on the subject line and the rest of the commit in the body. Provide a GIT Repository to the PublicGITosis aims to make hosting GIT repositories easier and safer. It manages multiple repositories under one user account, using SSH (Secure Shell) keys to identify users. End users do not need their own fully fledged user account on the server, they will all talk to one shared user account that will not let them run arbitrary commands. GITosis is written in Python which is why we are going to install it too if not already installed — since we install software via APT (Advanced Packaging Tool), Python will be installed as a dependency of GITosis anyway. There are other ways of providing a public repository as well e.g. not using SSH for push and pull actions, creating a distinct user account for any contributor, access via HTTP (Hypertext Transfer Protocol) etc. All this works but I do not like it because there is something better... there is GITosis! I opted to only cover one particular use case which is the most secure one, the one that scales best, and the one that CLI (Command Line Interface) folks are most comfortable with i.e. I opted to cover setting up a public GIT repository using GITosis. Under the HoodAs we know, GIT does not need to be setup and run in a star topology setup simply because it is no centralized SCM (Software Configuration Management) system like for example SVN but, rather, it is a decentralized SCM system which means, any clone contains the full history (all commits ever made and the metadata information that goes with it e.g. who did what and when) and can therefore be merged/diffed/etc. back and forth with any other clone/branch out there. We can think of centralized SCM systems of enforcing the unavoidable star topology on its usres, and of decentralized systems, well, as everything from fully connected to star or, even better, anything that can be seen below. The point is, decentralized SCM systems, as opposed to centralized ones, do not enforce silly limits with regards to topology and usage but rather leave the choice to their users. However, sometimes it makes sense to even use a decentralized SCM system like GIT in a star topology — one such use case is with GITosis, where we have one remote machine running GITosis and therefore hosting GIT repositories for us. The GITosis machine makes for the center of the star and we, the users, are all leaves the central server running GITosis:
As already mentioned above, GITosis uses just a single system user
account for all repositories and users with write/commit/push
permissions to one or several of those repositories on the remote
machine e.g. the server within the datacenter that is going to host
our GIT repositories. This remote machine runs GITosis under the
system user account name GITosis itself is basically just used to manage/control who can write/commit/push to which repository — GITosis does not, because it does not need to, be concerned about who can read/pull/fetch since this can easily be done via GIT-daemon i.e. GIT-daemon can be used to provide anonymous read/pull/fetch access to our repositories if needed. In order to differentiate amongst folks with write/commit/push
permissions, even though we only have one shared system user account
called For those who's public key is placed onto the remote machine and therefore GITosis does recognize them, read and write access, or rather pull/fetch and push in GIT terms, then happens via SSH (Secure Shell) i.e. it is secure. Read respectively pull/fetch access via GIT-daemon, can, but does not have to be set up for SSH — that is solely in the hands of the administrator of the GITosis server.
The PKA (Public Key Authentication) setup for the system user Because of the fact that a system user account is used for the user
Also, as for any other way of hosting GIT repositories, firewall
settings in table For those who have permissions to write/commit/push, the SSH service ports are relevant thus the firewall has to allow the SSH port for inbound and outbound traffic. Of course, we can and we will use a non-standard listening port for sshd as we will see below. For the more paranoid, even port knocking might be set up if needed — I leave it to the particular user group to decide whether or not they might find it to much of a hassle or not... Installing and Configuring GITosis1 sa@wks:~$ ssh dolmen-devel 2 3 / \ _-' 4 _/ \-''- _ / 5 __-' { \ 6 / \ 7 / "o. |o } 8 | \ ; YOU ARE BEING WATCHED! 9 ', 10 \_ __\ 11 ''-_ \.// 12 / '-____' 13 / 14 _' 15 _-' 16 17 18 This computer system is the private property of its owner, whether individual, corporate or government. It is 19 for authorized use only. Users (authorized or unauthorized) have no explicit or implicit expectation of 20 privacy. 21 22 Any or all uses of this system and all files on this system may be intercepted, monitored, recorded, copied, 23 audited, inspected, and disclosed to your employer, to authorized site, government, and law enforcement 24 personnel, as well as authorized officials of government agencies, both domestic and foreign. 25 26 By using this system, the user consents to such interception, monitoring, recording, copying, auditing, 27 inspection, and disclosure at the discretion of such personnel or officials. 28 29 30 UNAUTHORIZED OR IMPROPER USE OF THIS SYSTEM MAY RESULT 31 IN CIVIL AND CRIMINAL PENALTIES AND ADMINISTRATIVE OR 32 DISCIPLINARY ACTION, AS APPROPRIATE !! 33 34 35 By continuing to use this system you indicate your awareness of and consent to these terms and conditions of 36 use. LOG OFF IMMEDIATELY if you do not agree to the conditions stated in this warning. However, if you are 37 authorized personal with no bad intentions please continue. Have a nice day! :-) 38 39 sa@rh0-ve3:~$ su 40 Password: 41 rh0-ve3:/home/sa# type dpl; dpl git* | grep ii 42 dpl is aliased to `dpkg -l' 43 ii git-core 1:1.6.3.3-1 fast, scalable, distributed revision control 44 ii gitosis 0.2+20080825-14 git repository hosting application 45 rh0-ve3:/home/sa# grep git /etc/passwd 46 gitosis:x:105:108:git repository hosting,,,:/srv/gitosis:/bin/sh 47 rh0-ve3:/home/sa# cd /srv/gitosis/ 48 rh0-ve3:/srv/gitosis# type la; la 49 la is aliased to `ls -la' 50 total 8 51 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-09 12:53 . 52 drwxr-xr-x 3 root root 4096 2009-07-09 11:33 .. 53 lrwxrwxrwx 1 root root 25 2009-07-09 11:33 git -> /srv/gitosis/repositories We are going to install GITosis on a remote machine located within a
datacenter. In order to do so, we use SSH (Secure Shell) to leave our
local machine, my workstation with its hostname These days, we make use of some virtualization technology of course. In the current case we are going to use OpenVZ i.e. our remote machine is a VE (Virtual Environment) which means it shares a HNs (Hardware Nodes) resources with other VEs — a VE however behaves and feels no differently than any non-virtualized Debian machine. On the remote machine, we start with installing the By installing Next thing to do is to create a public SSH key for PKA (Public Key
Authentication) for the GITosis administrator account on the remote
machine running GITosis. That we need to do on our local machine i.e.
As the above link shows, I already have my SSH keypair and thus a
public SSH key ( 54 rh0-ve3:/srv/gitosis# exit 55 exit 56 sa@rh0-ve3:~$ exit 57 logout 58 Connection to devel.example.com closed. 59 sa@wks:~$ cd .ssh/keypairs/; type pi; pi Markus 60 pi is aliased to `ls -la | grep' 61 -rw------- 1 sa sa 6431 2009-03-13 11:11 ssh_pka_key_for_user_Markus_Ano 62 -rw-r--r-- 1 sa sa 1501 2009-03-13 11:11 ssh_pka_key_for_user_Markus_Ano.pub 64 sa@wks:~/.ssh/keypairs$ scp -P 58445 ssh_pka_key_for_user_Markus_Ano.pub devel.example.com:/tmp 65 66 67 [skipping a lot of lines...] 68 69 70 ssh_pka_key_for_user_Markus_Ano.pub 100% 1501 1.5KB/s 00:00 71 sa@wks:~/.ssh/keypairs$ ssh dolmen-devel 72 73 74 [skipping a lot of lines...] 75 76 77 sa@rh0-ve3:~$ cd /tmp/ 78 sa@rh0-ve3:/tmp$ pi Markus 79 -rw-r--r-- 1 sa sa 1501 2009-07-12 13:48 ssh_pka_key_for_user_Markus_Ano.pub 80 sa@rh0-ve3:/tmp$ su 81 Password: 82 rh0-ve3:/tmp# dpl sudo* | grep ii 83 ii sudo 1.7.0-1 Provide limited super user privileges to specific users So, as mentioned we need to either create or grab the public SSH key on our local machine if it already exists there and then transfer it onto the remote machine running GITosis. With line 58 we have finally left With line 64 we use SCP (Secure Copy) to copy the public key (the one
with the The For both cases, domain name or IP address, the important thing is that
there is an The With installing the debian package 84 rh0-ve3:/tmp# sudo -H -u gitosis gitosis-init < /tmp/ssh_pka_key_for_user_Markus_Ano.pub 85 Initialized empty Git repository in /srv/gitosis/repositories/gitosis-admin.git/ 86 Reinitialized existing Git repository in /srv/gitosis/repositories/gitosis-admin.git/ 87 rh0-ve3:/tmp# cd /srv/gitosis/repositories/gitosis-admin.git/ 88 rh0-ve3:/srv/gitosis/repositories/gitosis-admin.git# type la; la 89 la is aliased to `ls -la' 90 total 52 91 drwxr-x--- 8 gitosis gitosis 4096 2009-07-12 13:52 . 92 drwxr-xr-x 3 gitosis gitosis 4096 2009-07-12 13:52 .. 93 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-12 13:52 branches 94 -rw-r--r-- 1 gitosis gitosis 66 2009-07-12 13:52 config 95 -rw-r--r-- 1 gitosis gitosis 73 2009-07-12 13:52 description 96 -rw-r--r-- 1 gitosis gitosis 90 2009-07-12 13:52 gitosis.conf 97 drwxr-xr-x 3 gitosis gitosis 4096 2009-07-12 13:52 gitosis-export 98 -rw-r--r-- 1 gitosis gitosis 23 2009-07-12 13:52 HEAD 99 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-12 13:52 hooks 100 -rw-r--r-- 1 gitosis gitosis 272 2009-07-12 13:52 index 101 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-12 13:52 info 102 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-12 13:52 objects 103 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-12 13:52 refs 104 rh0-ve3:/srv/gitosis/repositories/gitosis-admin.git# la hooks/post-update 105 lrwxrwxrwx 1 gitosis gitosis 61 2009-07-12 13:52 hooks/post-update -> /usr/share/pyshared/gitosis/templates/admin/hooks/post-update 106 rh0-ve3:/srv/gitosis/repositories/gitosis-admin.git# la /usr/share/pyshared/gitosis/templates/admin/hooks/post-update 107 -rwxr-xr-x 1 root root 69 2009-04-25 14:38 /usr/share/pyshared/gitosis/templates/admin/hooks/post-update 108 rh0-ve3:/srv/gitosis/repositories/gitosis-admin.git# cd .. 109 rh0-ve3:/srv/gitosis/repositories# !! 110 cd .. We are back on
That we succeed with line 84 can be seen from lines 85 and 86. After
taking a look around in lines 93 to 103, inside our just created
GITosis administrator area, (yes, that is a standard GIT
repository layout — we are using GIT to manage our GIT hosting
platform... how cool is that?! ;-]) we check if our post update hook
has the correct permission i.e. can be executed by others than root
itself or members of group root — it is all good, the permissions are
all right as they are 111 rh0-ve3:/srv/gitosis# la 112 total 20 113 drwxr-xr-x 5 gitosis gitosis 4096 2009-07-12 13:52 . 114 drwxr-xr-x 3 root root 4096 2009-07-09 11:33 .. 115 lrwxrwxrwx 1 root root 25 2009-07-09 11:33 git -> /srv/gitosis/repositories 116 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-12 13:52 gitosis 117 lrwxrwxrwx 1 gitosis gitosis 56 2009-07-12 13:52 .gitosis.conf -> /srv/gitosis/repositories/gitosis-admin.git/gitosis.conf 118 drwxr-xr-x 3 gitosis gitosis 4096 2009-07-12 13:52 repositories 119 drwx------ 2 gitosis gitosis 4096 2009-07-12 13:52 .ssh 120 rh0-ve3:/srv/gitosis# la repositories/ 121 total 12 122 drwxr-xr-x 3 gitosis gitosis 4096 2009-07-12 13:52 . 123 drwxr-xr-x 5 gitosis gitosis 4096 2009-07-12 13:52 .. 124 drwxr-x--- 8 gitosis gitosis 4096 2009-07-12 13:52 gitosis-admin.git 125 rh0-ve3:/srv/gitosis# cat .gitosis.conf 126 [gitosis] 127 128 [group gitosis-admin] 129 writable = gitosis-admin 130 members = markusgattol 131 132 rh0-ve3:/srv/gitosis# la .ssh/ 133 total 12 134 drwx------ 2 gitosis gitosis 4096 2009-07-12 13:52 . 135 drwxr-xr-x 5 gitosis gitosis 4096 2009-07-12 13:52 .. 136 -rw-r--r-- 1 gitosis gitosis 1652 2009-07-12 13:52 authorized_keys 137 rh0-ve3:/srv/gitosis# cat .ssh/authorized_keys 138 ### autogenerated by gitosis, DO NOT EDIT 139 command="gitosis-serve markusgattol",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaC [skipping a lot of characters...] TuB4zOt+Ay9dfoq5nMIekW2TNts24F/9k2NQ== PKA (Public Key Authentication) SSH keypair for user Markus Gattol; reach me at markusgattol If we compare lines 51 to 53 with lines 113 to 119, we can see that
the command from line 84 also created a symmetric link to our
The most important file it includes can be seen in lines 126 to 131 —
in line 129 it says, that the repository Security is good, as mentioned above already, even I cannot issue
arbitrary commands because of the
140 rh0-ve3:/srv/gitosis# grep AllowUsers /etc/ssh/sshd_config 141 AllowUsers [email protected].* [email protected].* [email protected] 142 rh0-ve3:/srv/gitosis# nano /etc/ssh/sshd_config 143 144 145 [ here we use nano to edit /etc/ssh/sshd_config... ] 146 147 148 rh0-ve3:/srv/gitosis# grep AllowUsers /etc/ssh/sshd_config 149 AllowUsers [email protected].* [email protected].* [email protected] gitosis@* 150 rh0-ve3:/srv/gitosis# /etc/init.d/ssh reload 151 Reloading OpenBSD Secure Shell server's configuration: sshd. 152 rh0-ve3:/srv/gitosis# exit 153 exit 154 sa@rh0-ve3:~$ exit 155 logout 156 Connection to devel.example.com closed. Since AllowUsers is used for the SSH setup, we have to explicitly
grant our system user 157 sa@wks:~$ cd 0/ 158 sa@wks:~/0$ mkdir -p gitosis_projects/dolmen 159 sa@wks:~/0$ cd gitosis_projects/dolmen/ 160 sa@wks:~/0/gitosis_projects/dolmen$ la 161 total 8 162 drwxr-xr-x 2 sa sa 4096 2009-07-12 18:36 . 163 drwxr-xr-x 3 sa sa 4096 2009-07-12 18:36 .. 164 sa@wks:~/0/gitosis_projects/dolmen$ nano /home/sa/.ssh/config 165 166 167 [ here we use nano to edit ~/.ssh/config... ] 168 169 170 sa@wks:~/0/gitosis_projects/dolmen$ grep -C4 gitosis ~/.ssh/config 171 ###_ , devel.example.com 172 # description: just a dummy stanza to make git push/pull work with 173 # devel.example.com 174 Host devel.example.com 175 User gitosis 176 Port 58445 177 Hostname devel.example.com 178 IdentityFile %d/.ssh/keypairs/ssh_pka_key_for_user_Markus_Ano 179 TCPKeepAlive yes Back on the local machine, I decided to have a dedicated directory to host all my GITosis administrator related data for several projects that I am going to migrate to GIT, the first of which is Dolmen. As I mentioned above already, we are using a non-standard listening
port for the sshd running on Line 174 is what we specify on the CLI (Command Line Interface) i.e.
Line 178 is useful if we have several SSH keys loaded into our
SSH-agent (on our local machine, Note, I do have another stanza of course as well Host dolmen-devel User sa HostName devel.example.com Port 58445 IdentityFile %d/.ssh/keypairs/ssh_pka_key_for_user_Markus_Ano TCPKeepAlive yes which I use for regular SSH access to Again... we have two different stanzas in ~/.ssh/config, same VE, same sshd, but different users and thus permissions because of the two stanzas:
180 sa@wks:~/0/gitosis_projects/dolmen$ git clone [email protected]:gitosis-admin.git 181 Initialized empty Git repository in /home/sa/0/gitosis_projects/dolmen/gitosis-admin/.git/ 182 183 / \ _-' 184 _/ \-''- _ / 185 __-' { \ 186 / \ 187 / "o. |o } 188 | \ ; YOU ARE BEING WATCHED! 189 ', 190 \_ __\ 191 ''-_ \.// 192 / '-____' 193 / 194 _' 195 _-' 196 197 198 This computer system is the private property of its owner, whether individual, corporate or government. It is 199 for authorized use only. Users (authorized or unauthorized) have no explicit or implicit expectation of 200 privacy. 201 202 Any or all uses of this system and all files on this system may be intercepted, monitored, recorded, copied, 203 audited, inspected, and disclosed to your employer, to authorized site, government, and law enforcement 204 personnel, as well as authorized officials of government agencies, both domestic and foreign. 205 206 By using this system, the user consents to such interception, monitoring, recording, copying, auditing, 207 inspection, and disclosure at the discretion of such personnel or officials. 208 209 210 UNAUTHORIZED OR IMPROPER USE OF THIS SYSTEM MAY RESULT 211 IN CIVIL AND CRIMINAL PENALTIES AND ADMINISTRATIVE OR 212 DISCIPLINARY ACTION, AS APPROPRIATE !! 213 214 215 By continuing to use this system you indicate your awareness of and consent to these terms and conditions of 216 use. LOG OFF IMMEDIATELY if you do not agree to the conditions stated in this warning. However, if you are 217 authorized personal with no bad intentions please continue. Have a nice day! :-) 218 219 remote: Counting objects: 5, done. 220 remote: Compressing objects: 100% (5/5), done. 221 remote: Total 5 (delta 0), reused 5 (delta 0) 222 Receiving objects: 100% (5/5), done. 223 sa@wks:~/0/gitosis_projects/dolmen$ la 224 total 12 225 drwxr-xr-x 3 sa sa 4096 2009-07-12 18:45 . 226 drwxr-xr-x 3 sa sa 4096 2009-07-12 18:36 .. 227 drwxr-xr-x 4 sa sa 4096 2009-07-12 18:45 gitosis-admin 228 sa@wks:~/0/gitosis_projects/dolmen$ cd gitosis-admin/ 229 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ la 230 total 20 231 drwxr-xr-x 4 sa sa 4096 2009-07-12 18:45 . 232 drwxr-xr-x 3 sa sa 4096 2009-07-12 18:45 .. 233 drwxr-xr-x 8 sa sa 4096 2009-07-12 18:45 .git 234 -rw-r--r-- 1 sa sa 90 2009-07-12 18:45 gitosis.conf 235 drwxr-xr-x 2 sa sa 4096 2009-07-12 18:45 keydir 236 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ la keydir/ 237 total 12 238 drwxr-xr-x 2 sa sa 4096 2009-07-12 18:45 . 239 drwxr-xr-x 4 sa sa 4096 2009-07-12 18:45 .. 240 -rw-r--r-- 1 sa sa 1501 2009-07-12 18:45 markusgattol.pub 241 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ cat keydir/markusgattol.pub 242 ssh-rsa AAAAB3NzaC1yc [skipping a lot of characters...] q5nMIekW2TNts24F/9k2NQ== PKA (Public Key Authentication) SSH keypair for user Markus Gattol; reach me at markusgattol 243 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ cat gitosis.conf 244 [gitosis] 245 246 [group gitosis-admin] 247 writable = gitosis-admin 248 members = markusgattol 249 With line 180 we transfer those bits and pieces needed for
administering GITosis onto my local machine ( What follows is the usual banner message and some GIT specific chatter in lines 219 to 222. From now on, everybody will see this banner message when cloning/pulling/fetching from one of our GIT repositories via SSH — of course, one might alter the banner message to whatever he might think fits better e.g. some message telling those who clone that this is GITosis they are talking to, company info, some URL to some website, etc. The result of line 180 can be seen from lines 227 onwards like for
example As we can see, That part is pure GIT power — a decentralized SCM system does not need to be connected to some central instance all in order for us to get some work done. We could for example configure a new repository while sitting on some
airplane without connectivity to the Internet and then, once we have
Internet connectivity again, just issue Adding Users250 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ 251 252 253 [ here we use nano to edit /home/sa/0/gitosis_projects/dolmen/gitosis-admin/gitosis.conf... ] 254 255 256 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ cat gitosis.conf 257 [gitosis] 258 259 [group gitosis-admin] 260 writable = gitosis-admin 261 members = markusgattol 262 263 [group dolmen] 264 members = markusgattol 265 writable = dolmen 266 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ git dwh 267 diff --git a/gitosis.conf b/gitosis.conf 268 index b8000ed..621dc63 100644 269 --- a/gitosis.conf 270 +++ b/gitosis.conf 271 @@ -4,3 +4,6 @@ 272 writable = gitosis-admin 273 members = markusgattol 274 275 +[group dolmen] 276 +members = markusgattol 277 +writable = dolmen 278 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ git cwh -m 'allow Markus Gattol write access to dolmen' 279 [master d939ac7] allow Markus Gattol write access to dolmen 280 1 files changed, 3 insertions(+), 0 deletions(-) Next we edit our local With our current setup, we will now also specify the name of a new
project called Dolmen i.e. we will have Therefore we create a new group in line 263 — it makes sense to name
the group Naming generally works like this: The repository name on the remote
machine Starting with line 266 we make use of some of my
aliases in ~/.gitconfig like for example With line 278 we commit the changes to our local clone of
281 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ git push 282 283 284 [skipping a lot of lines...] 285 286 287 Counting objects: 5, done. 288 Delta compression using up to 4 threads. 289 Compressing objects: 100% (3/3), done. 290 Writing objects: 100% (3/3), 393 bytes, done. 291 Total 3 (delta 0), reused 0 (delta 0) 292 To [email protected]:gitosis-admin.git 293 3c86640..d939ac7 master -> master 294 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ gllol 295 d939ac7fa4f9a29e517541f494e00285d18a4b63 10 seconds ago CN: Markus Gattol AN: Markus Gattol S: allow Markus Gattol write access to dolmen 296 3c866407f4fabd7c7afbcf434b76024b1476e58d 24 hours ago CN: Gitosis Admin AN: Gitosis Admin S: Automatic creation of gitosis repository. That the push was successful can be seen from lines 287 to 293.
Internally, for this push, GITosis checked whether we are in
possession of the private key gllol in line 294 is a somewhat fancy command which I have come to like a lot since it shows me what I need to know quite easily and with not much effort. Adding Repositories297 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin$ mkdir dolmen; cd dolmen 298 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ git init 299 Initialized empty Git repository in /home/sa/0/gitosis_projects/dolmen/gitosis-admin/dolmen/.git/ 300 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ git remote add origin [email protected]:dolmen.git 301 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ echo "WRITEME" > README 302 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ la 303 total 16 304 drwxr-xr-x 3 sa sa 4096 2009-07-13 16:11 . 305 drwxr-xr-x 5 sa sa 4096 2009-07-13 16:09 .. 306 drwxr-xr-x 7 sa sa 4096 2009-07-13 16:10 .git 307 -rw-r--r-- 1 sa sa 8 2009-07-13 16:11 README 308 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ git add README 309 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ git status 310 # On branch master 311 # 312 # Initial commit 313 # 314 # Changes to be committed: 315 # (use "git rm --cached <file>..." to unstage) 316 # 317 # new file: README 318 # 319 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ git cwh -m 'initial commit' 320 [master (root-commit) ac84821] initial commit 321 1 files changed, 1 insertions(+), 0 deletions(-) 322 create mode 100644 README 323 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ gllol 324 ac8482172485bc3322ab7e22a189dd320bf666f9 2 seconds ago CN: Markus Gattol AN: Markus Gattol S: initial commit 325 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ cat .git/config 326 [core] 327 repositoryformatversion = 0 328 filemode = true 329 bare = false 330 logallrefupdates = true 331 [remote "origin"] 332 url = [email protected]:dolmen.git 333 fetch = +refs/heads/*:refs/remotes/origin/* We have already specified a new group above in line 263 and specified that our new repository will allow write/commit/push actions. That is very cool but then, it would be even cooler if we actually had that repository too no? ;-] With line 297/298 we create it on our local machine, add the remote
information in line 300 and add some file in line 301. Line 300, where
we set the origin, is yet another line which implicitly uses our SSH
settings in After looking at the current status with line 309, we commit the
changes with line 319 which works fine as can be seen. Again, Now is a good time to take a look at the config file of our just
created repository. As we can see in line 332, we have successfully
added/created a bare repository on 334 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ git push origin master:refs/heads/master 335 336 337 [skipping a lot of lines...] 338 339 340 Initialized empty Git repository in /srv/gitosis/repositories/dolmen.git/ 341 Counting objects: 3, done. 342 Writing objects: 100% (3/3), 233 bytes, done. 343 Total 3 (delta 0), reused 0 (delta 0) 344 To [email protected]:dolmen.git 345 * [new branch] master -> master Last but not least, after making our local clone think it got cloned
from 346 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ ssh dolmen-devel 347 348 349 [skipping a lot of lines...] 350 351 352 sa@rh0-ve3:~$ su 353 Password: 354 rh0-ve3:/home/sa# cd /srv/gitosis/repositories/ 355 rh0-ve3:/srv/gitosis/repositories# la 356 total 16 357 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-13 14:13 . 358 drwxr-xr-x 5 gitosis gitosis 4096 2009-07-12 13:52 .. 359 drwxr-x--- 7 gitosis gitosis 4096 2009-07-13 14:13 dolmen.git 360 drwxr-x--- 8 gitosis gitosis 4096 2009-07-13 13:41 gitosis-admin.git 361 rh0-ve3:/srv/gitosis/repositories# cd dolmen.git/ 362 rh0-ve3:/srv/gitosis/repositories/dolmen.git# la 363 total 40 364 drwxr-x--- 7 gitosis gitosis 4096 2009-07-13 14:13 . 365 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-13 14:13 .. 366 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 branches 367 -rw-r--r-- 1 gitosis gitosis 66 2009-07-13 14:13 config 368 -rw-r--r-- 1 gitosis gitosis 73 2009-07-13 14:13 description 369 -rw-r--r-- 1 gitosis gitosis 23 2009-07-13 14:13 HEAD 370 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 hooks 371 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 info 372 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-13 14:13 objects 373 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-13 14:13 refs 374 rh0-ve3:/srv/gitosis/repositories/dolmen.git# cat config 375 [core] 376 repositoryformatversion = 0 377 filemode = true 378 bare = true 379 rh0-ve3:/srv/gitosis/repositories/dolmen.git# la info/ 380 total 12 381 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 . 382 drwxr-x--- 7 gitosis gitosis 4096 2009-07-13 14:13 .. 383 -rw-r--r-- 1 gitosis gitosis 240 2009-07-13 14:13 exclude 384 rh0-ve3:/srv/gitosis/repositories/dolmen.git# la refs/ 385 total 16 386 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-13 14:13 . 387 drwxr-x--- 7 gitosis gitosis 4096 2009-07-13 14:13 .. 388 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 heads 389 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 tags 390 rh0-ve3:/srv/gitosis/repositories/dolmen.git# la refs/heads/ 391 total 12 392 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 . 393 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-13 14:13 .. 394 -rw-r--r-- 1 gitosis gitosis 41 2009-07-13 14:13 master 395 rh0-ve3:/srv/gitosis/repositories/dolmen.git# cat refs/heads/master 396 ac848ac8482172485bc3322ab7e22a189dd320bf666f9 397 rh0-ve3:/srv/gitosis/repositories/dolmen.git# exit 398 exit 399 sa@rh0-ve3:~$ exit 400 logout 401 Connection to devel.example.com closed. 402 sa@wks:~/0/gitosis_projects/dolmen/gitosis-admin/dolmen$ cd /tmp/test/ Lines 346 to 397 are just to take another look around on Note that this time, in line 346, we use The thing that is most interesting here is with line 396 — the
object type we are looking at here is a so-called commit object, the
one ( Providing a Repository to the WorldThere are two ways this can be done
403 sa@wks:/tmp/test$ git clone [email protected]:dolmen.git 404 Initialized empty Git repository in /tmp/test/dolmen/.git/ 405 406 / \ _-' 407 _/ \-''- _ / 408 __-' { \ 409 / \ 410 / "o. |o } 411 | \ ; YOU ARE BEING WATCHED! 412 ', 413 \_ __\ 414 ''-_ \.// 415 / '-____' 416 / 417 _' 418 _-' 419 420 421 This computer system is the private property of its owner, whether individual, corporate or government. It is 422 for authorized use only. Users (authorized or unauthorized) have no explicit or implicit expectation of 423 privacy. 424 425 Any or all uses of this system and all files on this system may be intercepted, monitored, recorded, copied, 426 audited, inspected, and disclosed to your employer, to authorized site, government, and law enforcement 427 personnel, as well as authorized officials of government agencies, both domestic and foreign. 428 429 By using this system, the user consents to such interception, monitoring, recording, copying, auditing, 430 inspection, and disclosure at the discretion of such personnel or officials. 431 432 433 UNAUTHORIZED OR IMPROPER USE OF THIS SYSTEM MAY RESULT 434 IN CIVIL AND CRIMINAL PENALTIES AND ADMINISTRATIVE OR 435 DISCIPLINARY ACTION, AS APPROPRIATE !! 436 437 438 By continuing to use this system you indicate your awareness of and consent to these terms and conditions of 439 use. LOG OFF IMMEDIATELY if you do not agree to the conditions stated in this warning. However, if you are 440 authorized personal with no bad intentions please continue. Have a nice day! :-) 441 442 remote: Counting objects: 3, done. 443 remote: Total 3 (delta 0), reused 0 (delta 0) 444 Receiving objects: 100% (3/3), done. 445 sa@wks:/tmp/test$ la 446 total 12 447 drwxr-xr-x 3 sa sa 4096 2009-07-13 17:19 . 448 drwxrwxrwt 26 root root 4096 2009-07-13 17:19 .. 449 drwxr-xr-x 3 sa sa 4096 2009-07-13 17:19 dolmen 450 sa@wks:/tmp/test$ cat dolmen/README 451 WRITEME 452 sa@wks:/tmp/test$ We issue line 403 and what happens is just plain lovely! Cloning as a
user who has write/commit/push permissions worked just fine as line
444 shows — on my local machine, We now have a clone of Dolmen in Ok, great, cloning works but what about doing some changes locally and
then pushing them back again onto GITosis hosting platform that, among
other GIT repositories, houses 453 sa@wks:/tmp/test$ cd dolmen/ 454 sa@wks:/tmp/test/dolmen$ la 455 total 16 456 drwxr-xr-x 3 sa sa 4096 2009-07-13 17:19 . 457 drwxr-xr-x 3 sa sa 4096 2009-07-13 17:19 .. 458 drwxr-xr-x 8 sa sa 4096 2009-07-13 17:19 .git 459 -rw-r--r-- 1 sa sa 8 2009-07-13 17:19 README 460 sa@wks:/tmp/test/dolmen$ git st 461 # On branch master 462 nothing to commit (working directory clean) 463 sa@wks:/tmp/test/dolmen$ echo "PLEASE WRITEME" > README 464 sa@wks:/tmp/test/dolmen$ cat README 465 PLEASE WRITEME 466 sa@wks:/tmp/test/dolmen$ git st 467 # On branch master 468 # Changed but not updated: 469 # (use "git add <file>..." to update what will be committed) 470 # (use "git checkout -- <file>..." to discard changes in working directory) 471 # 472 # modified: README 473 # 474 no changes added to commit (use "git add" and/or "git commit -a") 475 sa@wks:/tmp/test/dolmen$ git cwh -m 'did some changes to README' 476 [master a137d2d] did some changes to README 477 1 files changed, 1 insertions(+), 1 deletions(-) 478 sa@wks:/tmp/test/dolmen$ git push 479 480 / \ _-' 481 _/ \-''- _ / 482 __-' { \ 483 / \ 484 / "o. |o } 485 | \ ; YOU ARE BEING WATCHED! 486 ', 487 \_ __\ 488 ''-_ \.// 489 / '-____' 490 / 491 _' 492 _-' 493 494 495 This computer system is the private property of its owner, whether individual, corporate or government. It is 496 for authorized use only. Users (authorized or unauthorized) have no explicit or implicit expectation of 497 privacy. 498 499 Any or all uses of this system and all files on this system may be intercepted, monitored, recorded, copied, 500 audited, inspected, and disclosed to your employer, to authorized site, government, and law enforcement 501 personnel, as well as authorized officials of government agencies, both domestic and foreign. 502 503 By using this system, the user consents to such interception, monitoring, recording, copying, auditing, 504 inspection, and disclosure at the discretion of such personnel or officials. 505 506 507 UNAUTHORIZED OR IMPROPER USE OF THIS SYSTEM MAY RESULT 508 IN CIVIL AND CRIMINAL PENALTIES AND ADMINISTRATIVE OR 509 DISCIPLINARY ACTION, AS APPROPRIATE !! 510 511 512 By continuing to use this system you indicate your awareness of and consent to these terms and conditions of 513 use. LOG OFF IMMEDIATELY if you do not agree to the conditions stated in this warning. However, if you are 514 authorized personal with no bad intentions please continue. Have a nice day! :-) 515 516 Counting objects: 5, done. 517 Writing objects: 100% (3/3), 286 bytes, done. 518 Total 3 (delta 0), reused 0 (delta 0) 519 To [email protected]:dolmen.git 520 ac84821..a137d2d master -> master 521 sa@wks:/tmp/test/dolmen$ gllol 522 a137d2d85c68dd44a6b755ea8c020d6b1116c283 10 seconds ago CN: Markus Gattol AN: Markus Gattol S: did some changes to README 523 ac8482172485bc3322ab7e22a189dd320bf666f9 2 days ago CN: Markus Gattol AN: Markus Gattol S: initial commit We do an edit in line 463, check the status with line 466 and commit the change/edit from line 463 with line 475. Now, will the push towards upstream work? It sure does as we can see from line 478 onward ... piece of cake! ;-] The closer look in line 521 and following provides us with more details — we just went full circle... we cloned the upstream repository, made local edits/changes which we committed and finally pushed them back into the upstream repository onto our GITosis hosting platform. Excellent! Next we are going to set up GIT-daemon for anonymous read/pull/fetch access, the second one of two possible choices. 524 sa@wks:/tmp/test/dolmen$ ssh dolmen-devel 525 526 527 [skipping a lot of lines...] 528 529 530 sa@rh0-ve3:~$ su 531 Password: 532 rh0-ve3:/home/sa# aptitude install git-daemon-run 533 Reading package lists... Done 534 Building dependency tree 535 Reading state information... Done 536 537 538 [skipping a lot of lines...] 539 540 541 Reading extended state information 542 Initializing package states... Done 543 Writing extended state information... Done 544 At first we need to enter 545 rh0-ve3:/home/sa# cd /usr/share/doc/git-daemon-run/ 546 rh0-ve3:/usr/share/doc/git-daemon-run# la 547 total 300 548 drwxr-xr-x 2 root root 4096 2009-07-15 06:37 . 549 drwxr-xr-x 273 root root 12288 2009-07-15 06:37 .. 550 -rw-r--r-- 1 root root 15971 2009-06-26 08:18 changelog.Debian.gz 551 -rw-r--r-- 1 root root 259657 2009-06-26 08:18 changelog.gz 552 -rw-r--r-- 1 root root 3412 2009-06-26 08:18 copyright 553 -rw-r--r-- 1 root root 1143 2009-06-26 08:18 README.Debian 554 rh0-ve3:/usr/share/doc/git-daemon-run# cat /var/log/git-daemon/current 555 2009-07-15_06:37:39.93133 git-daemon starting. 556 rh0-ve3:/usr/share/doc/git-daemon-run# sv stat git-daemon 557 run: git-daemon: (pid 6156) 3275s; run: log: (pid 6155) 3275s 558 rh0-ve3:/usr/share/doc/git-daemon-run# ls -la /etc/init.d/ | grep git 559 rh0-ve3:/usr/share/doc/git-daemon-run# ln -s /usr/bin/sv /etc/init.d/git-daemon 560 rh0-ve3:/usr/share/doc/git-daemon-run# ls -la /etc/init.d/ | grep git 561 lrwxrwxrwx 1 root root 11 2009-07-15 07:33 git-daemon -> /usr/bin/sv 562 rh0-ve3:/usr/share/doc/git-daemon-run# cd /srv/gitosis/repositories/ 563 rh0-ve3:/srv/gitosis/repositories# la 564 total 16 565 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-13 14:13 . 566 drwxr-xr-x 5 gitosis gitosis 4096 2009-07-12 13:52 .. 567 drwxr-x--- 7 gitosis gitosis 4096 2009-07-15 11:36 dolmen.git 568 drwxr-x--- 8 gitosis gitosis 4096 2009-07-15 11:33 gitosis-admin.git 569 rh0-ve3:/srv/gitosis/repositories# sudo -u gitosis touch dolmen.git/git-daemon-export-ok 570 rh0-ve3:/srv/gitosis/repositories# la dolmen.git/ 571 total 40 572 drwxr-x--- 7 gitosis gitosis 4096 2009-07-15 11:36 . 573 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-13 14:13 .. 574 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 branches 575 -rw-r--r-- 1 gitosis gitosis 66 2009-07-13 14:13 config 576 -rw-r--r-- 1 gitosis gitosis 73 2009-07-13 14:13 description 577 -rw-r--r-- 1 gitosis gitosis 0 2009-07-15 11:36 git-daemon-export-ok 578 -rw-r--r-- 1 gitosis gitosis 23 2009-07-13 14:13 HEAD 579 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 hooks 580 drwxr-xr-x 2 gitosis gitosis 4096 2009-07-13 14:13 info 581 drwxr-xr-x 10 gitosis gitosis 4096 2009-07-15 05:15 objects 582 drwxr-xr-x 4 gitosis gitosis 4096 2009-07-13 14:13 refs As usual, One important thing to note here is that GIT-daemon on Debian makes
use of In order to make it work as usual i.e. in order to do
We are going to setup GIT-daemon in a way, that by default, it does not grant read/pull/fetch access to a newly created GIT repository — that is recommended since it does avoid situations where a repository is available to the entire world when it should not be. If the default is to not allow read/pull/fetch access, we need to
explicitly allow read/pull/fetch access for each repository. Line 569
shows how this is done — GIT-daemon looks for a file called
Note, that with using GITosis next to GIT-daemon, there is an even
smarter way to do this i.e. as it is shown in line 569, we have to log
into the remote machine [gitosis] [group gitosis-admin] writable = gitosis-admin members = markusgattol [group dolmen] writable = dolmen members = markusgattol user2 user3 user4 [repo dolmen] daemon = yes i.e. if we add a Again, this is good since we could do it on an airplane for example
where we do not currently have access to the Internet and then later
if we have Internet again, issue 583 rh0-ve3:/srv/gitosis/repositories# cat /etc/sv/git-daemon/run 584 #!/bin/sh 585 exec 2>&1 586 echo 'git-daemon starting.' 587 exec chpst -ugitdaemon \ 588 /usr/lib/git-core/git-daemon --verbose --base-path=/var/cache /var/cache/git 589 rh0-ve3:/srv/gitosis/repositories# 590 591 592 [ here we use nano to edit /etc/sv/git-daemon/run... ] 593 594 595 rh0-ve3:/srv/gitosis/repositories# cat /etc/sv/git-daemon/run 596 #!/bin/sh 597 exec 2>&1 598 echo 'git-daemon starting.' 599 exec chpst -ugitosis /usr/lib/git-core/git-daemon --base-path=/srv/gitosis/repositories 600 rh0-ve3:/srv/gitosis/repositories# sv restart git-daemon 601 ok: run: git-daemon: (pid 8282) 0s 602 rh0-ve3:/srv/gitosis/repositories# sv stat git-daemon 603 run: git-daemon: (pid 8282) 6s; run: log: (pid 6155) 18182s 604 rh0-ve3:/srv/gitosis/repositories# netstat -tulpen | grep 9418 605 tcp 0 0 0.0.0.0:9418 0.0.0.0:* LISTEN 105 680150 8282/git-daemon 606 tcp6 0 0 :::9418 :::* LISTEN 105 680151 8282/git-daemon 607 rh0-ve3:/srv/gitosis/repositories# type psa; psa git 608 psa is aliased to `ps aux | grep' 609 root 6154 0.0 0.0 108 28 ? Ss 06:37 0:00 runsv git-daemon 610 gitlog 6155 0.0 0.0 128 40 ? S 06:37 0:00 svlogd -tt /var/log/git-daemon 611 gitosis 8282 0.0 0.0 48972 1520 ? S 11:40 0:00 /usr/lib/git-core/git-daemon --base-path=/srv/gitosis/repositories 612 root 8288 0.0 0.0 7264 788 pts/1 S+ 11:41 0:00 grep git 613 rh0-ve3:/srv/gitosis/repositories# exit 614 exit 615 sa@rh0-ve3:~$ exit 616 logout 617 Connection to devel.example.com closed. 618 sa@wks:/tmp/test/dolmen$ cd ../..; mkdir test_git-daemon; cd test_git-daemon 619 sa@wks:/tmp/test_git-daemon$ git clone git://devel.dolmen-project.org/dolmen.git 620 Initialized empty Git repository in /tmp/test_git-daemon/dolmen/.git/ 621 remote: Counting objects: 6, done. 622 remote: Compressing objects: 100% (2/2), done. 623 remote: Total 6 (delta 0), reused 0 (delta 0) 624 Receiving objects: 100% (6/6), done. 625 sa@wks:/tmp/test_git-daemon$ diff dolmen/README ../test/dolmen/README 626 sa@wks:/tmp/test_git-daemon$ cat dolmen/README 627 PLEASE WRITEME 628 sa@wks:/tmp/test_git-daemon$ Now we need to tell GIT-daemon where to find our repositories which is
done by altering After restarting GIT-daemon in line 600 and checking if it is up and
running with line 602, we also take a look at services on If we have a firewall in place it has to allow access to port Line 611 is yet another quick check in order to be sure everything is up and running as expected... we are done setting up GIT-daemon. Again, well done! ;-] The rest is all about testing anonymous read/pull/fetch access and then compare the former work with what we see right now — there is no difference i.e. line 627 shows the change we did above with line 463 when we were testing the write/commit/push functionality. Automate Repository Creation with GITosisWe wrote a Python script to automate the manual steps shown in lines
263 to 265, 281, 278, 297, 298, 300, 301, 319, 334. One way to use it
is With our current setup it puts Later, when we are going to set up GITweb, we will see how
Browsing our GIT Repositories: Next we are going to put an additional
HTTP layer (GITweb) on top of the GITosis and GIT-daemon setup so
users can download snapshots in GITwebWe have successfully set up GITosis and GIT-daemon above. Now we want our users to be able to browse the GIT repositories on the server via GITweb. Before we start though, let us take a look at some screenshots taken while setting up GITweb on http://gitweb.dolmen-project.org — those mark our progress so the reader can acquire some taste for what is to come: 629 sa@wks:/tmp/test_git_daemon$ cd 630 sa@wks:~$ ssh dolmen-devel 631 sa@rh0-ve3:~$ su 632 Password: 633 rh0-ve3:/home/sa# type dpl; dpl gitweb | grep ii 634 dpl is aliased to `dpkg -l' 635 ii gitweb 1:1.6.3.3-2 fast, scalable, distributed revision control system (web interface) 636 rh0-ve3:/home/sa# dpl apache* | grep ii 637 ii apache2-mpm-worker 2.2.11-6 Apache HTTP Server - high speed threaded mod 638 ii apache2-utils 2.2.11-6 utility programs for web servers 639 ii apache2.2-bin 2.2.11-6 Apache HTTP Server common binary files 640 ii apache2.2-common 2.2.11-6 Apache HTTP Server common files Two things need to be installed as can be seen above —
Enabling HTTPFirst however, we make a little detour by enabling the HTTP (Hypertext
Transfer Protocol) for cloning/fetching/pulling our repositories
hosted on In other words, we will be able to do 641 rh0-ve3:/home/sa# cd /srv/gitosis/repositories/ 642 rh0-ve3:/srv/gitosis/repositories# la 643 total 168 644 drwxr-xr-x 42 gitosis gitosis 4096 2009-08-05 09:38 . 645 drwxr-xr-x 5 gitosis gitosis 4096 2009-07-12 13:52 .. 646 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-31 17:27 dolmen.app.authentication.git 647 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-31 17:27 dolmen.app.content.git 648 649 650 [skipping a lot of lines...] 651 652 653 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-31 17:29 snappy.transform.git 654 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-30 15:55 snappy.video.player.git 655 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-30 15:55 snappy.video.transforms.git 656 rh0-ve3:/srv/gitosis/repositories# cat snappy.video.transforms.git/hooks/post-update.sample 657 #!/bin/sh 658 # 659 # An example hook script to prepare a packed repository for use over 660 # dumb transports. 661 # 662 # To enable this hook, rename this file to "post-update". 663 664 exec git-update-server-info 665 rh0-ve3:/srv/gitosis/repositories# mv snappy.video.transforms.git/hooks/post-update{.sample,} First of two things to do is to enable the post-update hook within
each repository we have. We do this by removing the As said, that has to be done for each repository but then it is only
shown here once for the Second thing to do is to enable read access for Apache to the
directory containing all our GIT repositories
( 666 rh0-ve3:/srv/gitosis/repositories# grep -A15 'clone via http' /etc/apache2/sites-available/default 667 ### clone via http 668 669 <VirtualHost *:80> 670 ServerName devel.dolmen-project.org 671 DocumentRoot "/srv/gitosis/repositories" 672 673 <Directory "/srv/gitosis/repositories"> 674 Options FollowSymlinks 675 Allow from all 676 AllowOverride all 677 Order allow,deny 678 </Directory> 679 </VirtualHost> 680 681 682 rh0-ve3:/srv/gitosis/repositories# apache2ctl graceful 683 apache2: Could not reliably determine the server's fully qualified domain name, using xx.xxx.xxx.xxx for ServerName 684 rh0-ve3:/srv/gitosis/repositories# netstat -tulpen | grep apach 685 tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 0 1112381 614/apache2 686 rh0-ve3:/srv/gitosis/repositories# exit 687 exit 688 sa@rh0-ve3:~$ exit 689 logout 690 Connection to devel.dolmen-project.org closed. 691 sa@wks:~$ cd /tmp 692 sa@wks:/tmp$ git clone http://devel.dolmen-project.org/misc.git 693 Initialized empty Git repository in /tmp/misc/.git/ 694 got c29800bf59b3c329e8b012c04af452a9b49de7c6 695 walk c29800bf59b3c329e8b012c04af452a9b49de7c6 696 got 6508b60b0fc7aa20ddbcd1c3f7d2f2ff8b1e2fc0 697 got f100a6b63b7a13e8f3d154813e0d8e7260983d47 698 699 700 [skipping a lot of lines...] 701 702 703 got a862fa709e3d8717717a7b49d45d675cb594c80d 704 walk a862fa709e3d8717717a7b49d45d675cb594c80d 705 got 16e0a5e646e103bac9581e67dc8ff129c98cffd0 706 sa@wks:/tmp$ la misc/ 707 total 132 708 drwxr-xr-x 3 sa sa 4096 2009-08-07 12:11 . 709 drwxrwxrwt 29 root root 4096 2009-08-07 12:11 .. 710 -rwxr-xr-x 1 sa sa 6162 2009-08-07 12:11 create_repo.py 711 -rw-r--r-- 1 sa sa 65450 2009-08-07 12:11 dolmen_logo_big.png 712 -rw-r--r-- 1 sa sa 37254 2009-08-07 12:11 dolmen.svg 713 drwxr-xr-x 8 sa sa 4096 2009-08-07 12:11 .git 714 -rw-r--r-- 1 sa sa 284 2009-08-07 12:11 .gitignore There is not much to say here except that it works as can be seen from
lines 692 to 705. Note that we left Configuring GITweb715 sa@wks:/tmp$ ssh dolmen-devel 716 sa@rh0-ve3:~$ su 717 Password: 718 rh0-ve3:/home/sa# cd /usr/share/gitweb/ 719 rh0-ve3:/usr/share/gitweb# la 720 total 24 721 drwxr-xr-x 2 root root 4096 2009-08-04 16:22 . 722 drwxr-xr-x 83 root root 4096 2009-08-04 16:22 .. 723 -rw-r--r-- 1 root root 164 2009-06-29 01:20 git-favicon.png 724 -rw-r--r-- 1 root root 208 2009-06-29 01:20 git-logo.png 725 -rw-r--r-- 1 root root 7431 2009-06-29 01:20 gitweb.css 726 rh0-ve3:/usr/share/gitweb# touch {footer,home}.html 727 rh0-ve3:/usr/share/gitweb# la 728 total 36 729 drwxr-xr-x 2 root root 4096 2009-08-08 09:06 . 730 drwxr-xr-x 83 root root 4096 2009-08-04 16:22 .. 731 -rw-r--r-- 1 root root 139 2009-08-08 09:02 footer.html 732 -rw-r--r-- 1 root root 164 2009-06-29 01:20 git-favicon.png 733 -rw-r--r-- 1 root root 208 2009-06-29 01:20 git-logo.png 734 -rw-r--r-- 1 root root 7514 2009-08-08 08:59 gitweb.css 735 -rw-r--r-- 1 root root 7499 2009-08-08 09:02 home.html 736 rh0-ve3:/usr/share/gitweb# cd /tmp/ 737 rh0-ve3:/tmp# git clone git://devel.dolmen-project.org/misc.git 738 Initialized empty Git repository in /tmp/misc/.git/ 739 remote: Counting objects: 32, done. 740 remote: Compressing objects: 100% (31/31), done. 741 remote: Total 32 (delta 12), reused 0 (delta 0) 742 Receiving objects: 100% (32/32), 282.30 KiB, done. 743 Resolving deltas: 100% (12/12), done. 744 rh0-ve3:/tmp# cd /usr/share/gitweb/ 745 rh0-ve3:/usr/share/gitweb# cp /tmp/misc/a_dolmen_with_tree_in_front.jpg . 746 rh0-ve3:/usr/share/gitweb# cp /tmp/misc/dolmen_logo_big.png . 747 rh0-ve3:/usr/share/gitweb# la 748 total 316 749 drwxr-xr-x 2 root root 4096 2009-08-08 09:19 . 750 drwxr-xr-x 83 root root 4096 2009-08-04 16:22 .. 751 -rw-r--r-- 1 root root 210461 2009-08-08 09:19 a_dolmen_with_tree_in_front.jpg 752 -rw-r--r-- 1 root root 65450 2009-08-08 09:19 dolmen_logo_big.png 753 -rw-r--r-- 1 root root 139 2009-08-08 09:02 footer.html 754 -rw-r--r-- 1 root root 164 2009-06-29 01:20 git-favicon.png 755 -rw-r--r-- 1 root root 208 2009-06-29 01:20 git-logo.png 756 -rw-r--r-- 1 root root 7514 2009-08-08 08:59 gitweb.css 757 -rw-r--r-- 1 root root 7499 2009-08-08 09:02 home.html 758 rh0-ve3:/usr/share/gitweb# cd /usr/lib/cgi-bin/ 759 rh0-ve3:/usr/lib/cgi-bin# la 760 total 212 761 drwxr-xr-x 2 root root 4096 2009-08-08 10:17 . 762 drwxr-xr-x 42 root root 16384 2009-08-03 17:00 .. 763 -rwxr-xr-x 1 root root 190145 2009-06-29 01:20 gitweb.cgi 764 rh0-ve3:/usr/lib/cgi-bin# ln -s /usr/share/gitweb/footer.html 765 rh0-ve3:/usr/lib/cgi-bin# ln -s /usr/share/gitweb/home.html 766 rh0-ve3:/usr/lib/cgi-bin# ln -s /usr/share/gitweb/git-favicon.png 767 rh0-ve3:/usr/lib/cgi-bin# ln -s /usr/share/gitweb/git-logo.png 768 rh0-ve3:/usr/lib/cgi-bin# ln -s /usr/share/gitweb/gitweb.css 769 rh0-ve3:/usr/lib/cgi-bin# ln -s /usr/share/gitweb/a_dolmen_with_tree_in_front.jpg 770 rh0-ve3:/usr/lib/cgi-bin# ln -s /usr/share/gitweb/dolmen_logo_big.png 771 rh0-ve3:/usr/lib/cgi-bin# la 772 total 212 773 drwxr-xr-x 2 root root 4096 2009-08-08 10:18 . 774 drwxr-xr-x 42 root root 16384 2009-08-03 17:00 .. 775 lrwxrwxrwx 1 root root 49 2009-08-08 10:18 a_dolmen_with_tree_in_front.jpg -> /usr/share/gitweb/a_dolmen_with_tree_in_front.jpg 776 lrwxrwxrwx 1 root root 37 2009-08-08 10:18 dolmen_logo_big.png -> /usr/share/gitweb/dolmen_logo_big.png 777 lrwxrwxrwx 1 root root 29 2009-08-08 10:17 footer.html -> /usr/share/gitweb/footer.html 778 lrwxrwxrwx 1 root root 33 2009-08-08 10:17 git-favicon.png -> /usr/share/gitweb/git-favicon.png 779 lrwxrwxrwx 1 root root 30 2009-08-08 10:17 git-logo.png -> /usr/share/gitweb/git-logo.png 780 -rwxr-xr-x 1 root root 190145 2009-06-29 01:20 gitweb.cgi 781 lrwxrwxrwx 1 root root 28 2009-08-08 10:17 gitweb.css -> /usr/share/gitweb/gitweb.css 782 lrwxrwxrwx 1 root root 27 2009-08-08 10:17 home.html -> /usr/share/gitweb/home.html When we install What we do above is simply linking the files from We also grab two images directly from one of our already existing GIT repositories as can be seen in lines 737 to 746. 783 rh0-ve10:/usr/lib/cgi-bin# cat /etc/gitweb.conf 784 ### GITweb config file for gitweb.dolmen-project.org 785 786 # directory to use for temp files 787 $git_temp = "/tmp"; 788 789 # HTML text to include/render 790 $home_text = "home.html"; 791 $site_footer = "footer.html"; 792 793 # Sorting key for main page 794 $default_projects_order = "age"; 795 796 # Project root for GITweb. This is the parent directory for all of 797 # your GIT repositories. As an example, 'gitosis-admin.git' should 798 # reside in this directory. 799 $projectroot = "/srv/gitosis/repositories"; 800 $projects_list = $projectroot; 801 802 # Web display files. These are all _relative_ paths from the active 803 # gitweb.cgi file. If all three of these files are located in the same 804 # directory as gitweb.cgi (/urs/lib/cgi-bin), then the below settings 805 # should work fine. Remember that if they are in a different 806 # directory, you will need to give your Apache user/group read access 807 # to them! 808 $stylesheet = "/gitweb.css"; 809 $logo = "/git-logo.png"; 810 $favicon = "/git-favicon.png"; 811 812 # Site name 813 $site_name = "The Dolmen Project's GIT Repositories"; 814 815 # URL formatting. You can use this to make pretty URLs if you like. I 816 # am doing this using Apache rewrite rules, and so am not using these 817 # settings. 818 #$my_uri = "http://gitweb.dolmen-project.org/"; 819 #$home_link = $my_uri; 820 821 # Base URL for repositories. This is used to prefix each of the GIT 822 # repositories on the webpages. So in my case, if you were viewing a 823 # GIT repository/tree called 'foo.git', the webpage would tell you 824 # that the tree was located at: 825 # 'ssh://[email protected]:1234/foo.git'. Note that 826 # escaping the '@' character is necessary to render the URL properly. 827 @git_base_url_list = ("git://devel.dolmen-project.org"); 828 829 # Length of the project description column in the webpage. 830 $projects_list_description_width = 70; 831 832 # Only export repositories we are allowing to be publically cloned. 833 # What this setting actually says is that if the given file _exists_ 834 # in the GIT repository, then the repository/tree can be exported to 835 # the web. So, for example, the file: 836 # /srv/git/repositories/configs.git/git-daemon-export-ok file exists, 837 # so configs.git will be exported via Gitweb. This file can be created 838 # with a simple '$ touch git-daemon-export-ok'. I am using this 839 # filename as it doubles for the same use with the GIT export daemon 840 # which we set via gitosis.conf. If this setting does not exist, then 841 # all trees will be exported by default. Note that there ARE other 842 # methods for controlling which repositories get exported. This is 843 # just the one I prefer. 844 $export_ok = "git-daemon-export-ok"; 845 846 # Enable PATH_INFO so the server can produce URLs of the form: 847 # http://devel.dolmen-project.org/project.git/xxx/xxx This allows for 848 # pretty URLs *within* the GIT repository, where my Apache rewrite 849 # rules are not active. 850 $feature{'pathinfo'}{'default'} = [1]; 851 852 # Enable blame, pickaxe search, snapshop, search, and grep support, 853 # but still allow individual projects to turn them off. These are 854 # features that users can use to interact with your GIT repositories. 855 # They consume some CPU whenever a user uses them, so you can turn 856 # them off if you need to. Note that the 'override' option means that 857 # you can override the setting on a per-repository basis. 858 $feature{'blame'}{'default'} = [1]; 859 $feature{'blame'}{'override'} = [1]; 860 861 $feature{'pickaxe'}{'default'} = [1]; 862 $feature{'pickaxe'}{'override'} = [1]; 863 864 $feature{'search'}{'default'} = [1]; 865 866 $feature{'grep'}{'default'} = [1]; 867 $feature{'grep'}{'override'} = [1]; 868 869 $feature{'snapshot'}{'default'} = ['zip', 'tgz']; 870 $feature{'snapshot'}{'override'} = [1]; Next we take look at the config file for GITweb in lines 783 to 870. In addition to the very verbose comments, there are a few important things to say about it:
871 rh0-ve3:/usr/lib/cgi-bin# 872 873 874 [ here we use nano to edit home.html, footer.html, gitweb.css... ] 875 876 877 rh0-ve3:/usr/lib/cgi-bin# grep -A33 '### Gitweb' /etc/apache2/sites-available/default 878 ### Gitweb 879 880 <VirtualHost *:80> 881 ServerName gitweb.dolmen-project.org 882 DocumentRoot "/usr/lib/cgi-bin" 883 DirectoryIndex gitweb.cgi 884 SetEnv GITWEB_CONFIG /etc/gitweb.conf 885 886 <Directory "/usr/lib/cgi-bin"> 887 Options FollowSymlinks ExecCGI 888 Allow from all 889 AllowOverride all 890 Order allow,deny 891 892 <Files gitweb.cgi> 893 SetHandler cgi-script 894 </Files> 895 896 RewriteEngine on 897 RewriteCond %{REQUEST_FILENAME} !-f 898 RewriteCond %{REQUEST_FILENAME} !-d 899 RewriteRule ^.* /gitweb.cgi/$0 [L,PT] 900 </Directory> 901 902 <Directory "/srv/gitosis/repositories"> 903 Allow from all 904 </Directory> 905 906 # I only used those debug rewrite rules 907 #RewriteLog /var/log/httpd/rewrite_log 908 #RewriteLogLevel 9 909 910 #ErrorLog /var/log/httpd/gitweb 911 </VirtualHost> Next we need to alter a few files if we want/need to i.e. we for example change the CSS (Cascading Style Sheets) a bit, provide a footer and maybe a main page text. I do so as can be seen from the sceenshots. We are done except for one last thing — we need to configure yet another Apache virtual host to make it work. I am not going into details about lines 880 to 911 since there is a lot of information about Apache on the Internet already. In addition, I recommend to make some changes to maybe elevate security a little bit — that step is totally optional however. Apache's rewrite functionality is no core functionality which is why we load the rewrite module in line 912 and then restart Apache in line 915. 912 rh0-ve3:/usr/lib/cgi-bin# a2enmod rewrite 913 Enabling module rewrite. 914 Run '/etc/init.d/apache2 restart' to activate new configuration! 915 rh0-ve3:/usr/lib/cgi-bin# apache2ctl graceful 916 apache2: Could not reliably determine the server's fully qualified domain name, using xx.xxx.xxx.xxx for ServerName 917 918 919 [ here we use nano to edit /etc/passwd... ] 920 921 922 rh0-ve3:/usr/lib/cgi-bin# grep gitosis /etc/passwd 923 gitosis:x:105:108:Dolmen Project,,,:/srv/gitosis:/bin/sh 924 rh0-ve3:/usr/lib/cgi-bin# cd /srv/gitosis/repositories/ 925 rh0-ve3:/srv/gitosis/repositories# la 926 total 168 927 drwxr-xr-x 42 gitosis gitosis 4096 2009-08-05 09:38 . 928 drwxr-xr-x 5 gitosis gitosis 4096 2009-07-12 13:52 .. 929 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-31 17:27 dolmen.app.authentication.git 930 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-31 17:27 dolmen.app.content.git 931 932 933 [skipping a lot of lines...] 934 935 936 drwxr-xr-x 7 gitosis gitosis 4096 2009-08-07 07:16 misc.git 937 drwxr-xr-x 7 gitosis gitosis 4096 2009-08-07 07:16 snappy.git 938 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-30 15:46 snappy.site.git 939 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-31 17:29 snappy.transform.git 940 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-30 15:55 snappy.video.player.git 941 drwxr-xr-x 7 gitosis gitosis 4096 2009-07-30 15:55 snappy.video.transforms.git 942 rh0-ve3:/srv/gitosis/repositories# cat misc.git/description 943 Repository for miscellaneous stuff with regards to the Dolmen Project. 944 rh0-ve3:/srv/gitosis/repositories# exit 945 exit 946 sa@rh0-ve3:~$ exit 947 logout 948 Connection to devel.dolmen-project.org closed. 949 sa@wks:~$ grep -A2 '\[repo misc\]' 0/gitosis_projects/dolmen/gitosis-admin/gitosis.conf 950 [repo misc] 951 daemon = yes 952 description = Repository for miscellaneous stuff with regards to the Dolmen Project. 953 sa@wks:~$ We are done except for two minor things — we want to give the
repositories a one-line description and provide owner information when
they are shown on GITweb. One easy way is shown in line 923. This way
we can set a default owner and if needed, overwrite that default with
settings in [gitweb] owner = "Markus Gattol" Also on the server, each repository has a However, since we are using GITosis, there is a smarter way to do it
as is shown in lines 950 to 952 — note that we logged out of We are done, the final version looks like this: Note how all repositories have the default owner set but just a few so far have their individual description... Sharing Changes via pull/fetch and pushOnes local repository can be used by others to pull changes from ( Pushing will push/synchronize the local branch(es) with the corresponding remote branch(es) — note that this works generally only over SSH (Secure Shell) or HTTP with special web server setup. It is highly recommended to setup a SSH to use keys (also known as PKA (Public Key Authentication)) and the SSH-agent mechanism so that there is no need to type in a password all the time.
GIT can work with the same workflow as Subversion, with a group of
developers using a single repository for exchange for their work (a
bare repository). The only change is that their changes are not
submitted automatically but they have to use It is also possible to exchange patches using email. GIT has very good
support for patches incoming by mail. We can apply them by feeding
mailboxes with patch emails to WRITEME Sharing Changes via PatchesAside from using such popular sites like for example GIThub or setting up our own public repository with GITosis, we can share changes using email. In order to do so, the sender needs to create appropriate patches from his changes and the receiver needs to process those changes which were send to him via email. In fact, sharing changes via email seems to be the most common way how changes are shared among folks as of now (February 2009). The reason why this is, is probably because it is one of the least cumbersome setups one can have — right after using sites like GIThub for example, which in my opinion is going to become the most common method for sharing changes in the future. Most folks are just minor contributors and therefore they use Mostly, whenever someone make changes, he creates a topic branch, makes changes, tests, merges back into master and then deletes the topic branch after he is done. Now he needs to share his changes with the upstream repository. Probably the easiest way to do so, is to send the changes to someone who is considered a major contributor to the project. Another things many folks do is send their patches directly to a project's mailing list which is good for reasons of scrutiny etc. We are now going to take a look at how to create patches that can be used to share changes via email and also, we are going to take a look at how to process such emails assuming we are on the receivers end of the pipe. Creating PatchesFirst of all we shall pay attention to some things considered best practices when it comes to creating patches:
Submitting PatchesWRITEME Importing PatchesWRITEME GIThub and FriendsGitHub is a web-based hosting service for projects that use GIT as their SCM (Software Configuration Management) system. There are others too like for example GITorious, http://repo.or.cz/ etc. (see here for more information). I have chosen to host/manage the source code for this website/platform on GIThub simply because I figured that as of now (February 2009) it has the best tool set with regards to social interaction for folks so they can contribute. What still sucks though is the lack of a decent ticketing system but then, we will see what the situation looks like in a year from now; I am pretty sure the folks at GIThub are very skilled and hardworking geeks ;-] Another thing that I would like to see is the whole source code for GIThub to be released under some FLOSS (Free/Libre Open Source Software) license. The fact that some project uses a web-based source code hosting system like for example GIThub also enables non-geeks and/or folks with just little time, to contribute to the project — they might for example fix typos using the web interface i.e. there is no need to be a GIT expert, Debian developer or maybe some long-time GNU Emacs user or some other kind of geek of that magnitude. Upload a project to GIThubBefore that can be done, we need to create an account on GIThub. The information on how to do that on GIThub is fool-proof so I am not going to repeat anything here. Once we have an account on GIThub, we need to put the public key of an SSH (Secure Shell) key pair into the account on GIThub. 1 sa@wks:~/.ssh$ ssh-keygen -b 8192 -t rsa 2 Generating public/private rsa key pair. 3 Enter file in which to save the key (/home/sa/.ssh/id_rsa): github_id_rsa 4 Enter passphrase (empty for no passphrase): 5 Enter same passphrase again: 6 Your identification has been saved in github_id_rsa. 7 Your public key has been saved in github_id_rsa.pub. 8 The key fingerprint is: 9 44:42:af:ea:d9:bf:b7:99:4b:24:ad:1a:ad:00:80:70 sa@wks 10 The key's randomart image is: 11 +--[ RSA 8192]----+ 12 | . | 13 | . . | 14 | . E . + | 15 | o . + . . | 16 | . o S . o | 17 | . . . + | 18 | . o o o | 19 | . o . =.o . | 20 | o ..*o..o. | 21 +-----------------+ I opted to create a new pair especially to use it for GIThub (line 1
to 21). What we can also see from line 1 is that I created a key pair
which has a higher number of bits than the default one which is The name chosen in line 3 is of course one that indicates its usage —
I have tens of key pairs for different usage so sa@wks:~$ type pwg pwg is aliased to `pwgen -sncB 55 1' sa@wks:~$ pwg jwKJcgs7uvnijwp73v4uxbbojghiaeesepwT3gUovKjbhFmzdmgNP7c sa@wks:~$ 22 sa@wks:~/.ssh$ pi github 23 -rw------- 1 sa sa 6431 2009-02-25 15:43 github_id_rsa 24 -rw-r--r-- 1 sa sa 1412 2009-02-25 15:43 github_id_rsa.pub 25 sa@wks:~/.ssh$ cat github_id_rsa.pub 26 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAABAEAretHEeiycQbbEvoQqB9l+9UP4iHFDwDJgQ33b44pMY0lXauE 27 OiLHZM3oqmgqPDpzF2O4qFJil1L+b9owEhkD51UIHe3kdoaTxdwxsm/1+dLl06yL3ZdmDbkRt3Vc9bFla0Sm 28 29 30 [skipping a lot of lines...] 31 32 33 QNIL0n0WCC6llFA+8H+4xsA0/fHd24UoXR9E7Mjy6XxGF49nJVZYy6kj8g6RywwnNNP4sHcanVRh+Lz3s09D 34 WiSE0lTR87qbVNwG/zEhwWAU8hIsGnZZxBZyg8sDabPjIHm4Cb5Pzt6XCQ== sa@wks In our current case We get our public key onto GIThub by copy pasting the output from lines 26 to 34 into the specified field on the account page (screenshot below). The private key however must never be shown to someone and kept secure! 35 sa@wks:~/.ssh$ cd ../0/0/ 36 sa@wks:~/0/0$ la 37 total 12 38 drwxr-xr-x 7 sa sa 71 2009-02-25 19:00 . 39 drwxr-xr-x 32 sa sa 4096 2009-02-25 23:01 .. 40 drwxr-xr-x 5 sa sa 54 2008-02-04 20:47 blog 41 drwxr-xr-x 5 sa sa 43 2008-03-12 16:28 misc 42 drwxr-xr-x 7 sa sa 88 2008-06-02 09:52 pim 43 -rw-r--r-- 1 sa sa 1844 2009-02-25 19:00 README 44 drwxr-xr-x 8 sa sa 111 2008-08-29 21:35 ws 45 sa@wks:~/0/0$ git init && git add . && git cwh -m 'inital commit' 46 Initialized empty Git repository in /tmp/0/.git/ 47 [master (root-commit)]: created b79875f: "inital commit" 48 1143 files changed, 101682 insertions(+), 0 deletions(-) 49 create mode 100644 README 50 create mode 100644 blog/local/weblog.business.muse 51 create mode 100644 blog/local/weblog.debian.muse 52 53 54 [skipping a lot of lines...] 55 56 57 create mode 100644 ws/latex/latex2png-dm-crypt_luks__3904075528.png 58 create mode 100644 ws/latex/latex2png-dm-crypt_luks__3905517320.png 59 create mode 100644 ws/latex/latex2png-dm-crypt_luks__976832061.png 60 create mode 100644 ws/latex/latex2png-misc__2526884390.png 61 create mode 100644 ws/latex/latex2png-planner__2617796.png After we have uploaded the public key ( 62 sa@wks:~/0/0$ git st 63 # On branch master 64 nothing to commit (working directory clean) 65 sa@wks:~/0/0$ gllol 66 b79875f3b2267915179313184ac84436984ad33d 14 seconds ago CN: Markus Gattol AN: Markus Gattol S: inital commit 67 sa@wks:~/0/0$ git remote add origin [email protected]:markusgattol/0.git 68 sa@wks:~/0/0$ ssh-add ~/.ssh/github_id_rsa 69 Enter passphrase for /home/sa/.ssh/github_id_rsa: 70 Identity added: /home/sa/.ssh/github_id_rsa (/home/sa/.ssh/github_id_rsa) 71 sa@wks:~/0/0$ git push origin master 72 Counting objects: 1054, done. 73 Compressing objects: 100% (1049/1049), done. 74 Writing objects: 100% (1054/1054), 110.71 MiB | 88 KiB/s, done. 75 Total 1054 (delta 203), reused 0 (delta 0) 76 To [email protected]:markusgattol/0.git 77 * [new branch] master -> master 78 sa@wks:~/0/0$ Before we can push our local repository onto GIThub, we need to add a remote branch in line 67. Actually we make our just created local repository think it got cloned from a remote bare repository on GIThub. Next we need to tell the SSH authentication agent about our new key
pair (line 68) since, with every Note that this information — because SSH-agent keeps its information within RAM (Random Access Memory) which is a volatile memory — does not survive a reboot or any other kind of power outage for that matter ergo line 68 need be issued after each reboot. The passphrase requested in line 69 is the one we supplied in lines 4 and 5 respectively. Finally, in line 71 we can trigger the initial push which might take a while. When this command finishes, which it did here, we have successfully uploaded a GIT repository to GIThub in order to start collaborating with others like for example it is intended with this website/platform. Update: After restructuring my SSH setup, I am now using the following stanza within ~/.ssh/config sa@wks:~$ grep -A9 -m1 ', github' .ssh/config ###_ , github # description: just a dummy stanza to make git push work with # github.com i.e. to pick the right keyfile Host github.com User git Port 22 Hostname github.com IdentityFile %d/.ssh/github_id_rsa TCPKeepAlive yes IdentitiesOnly yes sa@wks:~$ However, if we were just using a standard SSH setup for
Nice to knowThe contents in this section, I consider nice to know but not in anyway mandatory for folks who would like to complete a full workflow circle with GIT. Creating a tarball plus Changelog for a Software ReleaseWe can use 1 sa@wks:~/0/openvz/vzpkg_test$ gllol | head -n2 2 617669671fadd24edb1f3176153dd5fdd7f86053 5 months ago CN: Robert Nelson AN: Robert Nelson S: Fix read_vz_conf return code so it doesn't cause "set -e" scripts to fail. 3 5615b8134d16020617ba5b30fcbf1cd2fa6360ca 5 months ago CN: Robert Nelson AN: Robert Nelson S: Fix return value from read_vzpkg_conf. 4 sa@wks:~/0/openvz/vzpkg_test$ git archive -l 5 tar 6 zip 7 sa@wks:~/0/openvz/vzpkg_test$ git archive --format=tar --prefix=openvz_vzpkg2/ HEAD | gzip > vzpkg2_`date +%F`.tar.gz 8 sa@wks:~/0/openvz/vzpkg_test$ git archive --format=tar --prefix=openvz_vzpkg2/ HEAD | bzip2 > vzpkg2_`date +%F`.tar.bz2 9 sa@wks:~/0/openvz/vzpkg_test$ pi vzpkg2 10 -rw-r--r-- 1 sa sa 45574 2009-02-27 10:41 vzpkg2_2009-02-27.tar.bz2 11 -rw-r--r-- 1 sa sa 47614 2009-02-27 10:39 vzpkg2_2009-02-27.tar.gz 12 sa@wks:~/0/openvz/vzpkg_test$ tar -tjf vzpkg2_2009-02-27.tar.bz2 | head -n4 13 openvz_vzpkg2/ 14 openvz_vzpkg2/COPYING 15 openvz_vzpkg2/Makefile 16 openvz_vzpkg2/NEWS 17 sa@wks:~/0/openvz/vzpkg_test$ The above example creates a tarball release for an OpenVZ utility
called vzpkg2. As we can see in lines 5 and 6, as of now (February
2009) I opted to create The tarball is created using Mostly, when releasing a new version of a software project, we may
want to simultaneously make a changelog to include in the release
announcement. Linus Torvalds, for example, makes new kernel releases
by tagging them, then running #!/bin/sh stable="$1" last="$2" new="$3" echo "# git tag v$new" echo "git archive --prefix=linux-$new/ v$new | gzip -9 > ../linux-$new.tar.gz" echo "git diff v$stable v$new | gzip -9 > ../patch-$new.gz" echo "git log --no-merges v$new ^v$last > ../ChangeLog-$new" echo "git shortlog --no-merges v$new ^v$last > ../ShortLog" echo "git diff --stat --summary -M v$last v$new > ../diffstat-$new" and then he just cuts and pastes the output after verifying that it looks good. Last but not least, anybody should then of course digitally sign the just created tarball using GPG (GNU Privacy Guard) in order to ensure verifiable data integrity and authenticity to users who use this tarball sa@wks:~/0/openvz/vzpkg_test$ gpg --detach-sign --armor vzpkg2_2009-02-27.tar.gz You need a passphrase to unlock the secret key for user: "Markus Gattol () <[email protected]>" 1024-bit DSA key, ID C0EC7E38, created 2009-02-06 sa@wks:~/0/openvz/vzpkg_test$ pi gz -rw-r--r-- 1 sa sa 47614 2009-02-27 10:39 vzpkg2_2009-02-27.tar.gz -rw-r--r-- 1 sa sa 197 2009-02-27 11:14 vzpkg2_2009-02-27.tar.gz.asc sa@wks:~/0/openvz/vzpkg_test$ cat *.asc -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEABECAAYFAkmnvOIACgkQSOlKxsDsfjgd7wCfVwH6ZxhnALPuS7CsdZIy7ozv RbcAmwatP59hppcEWMnn0Q7O7N8WUFQ+ =YG39 -----END PGP SIGNATURE----- sa@wks:~/0/openvz/vzpkg_test$ gpg --verify vzpkg2_2009-02-27.tar.gz.asc vzpkg2_2009-02-27.tar.gz gpg: Signature made Fri 27 Feb 2009 11:13:54 AM CET using DSA key ID C0EC7E38 gpg: Good signature from "Markus Gattol () <[email protected]>" sa@wks:~/0/openvz/vzpkg_test$ Finding Commits referencing a File with given ContentLet us assume somebody hands us a copy of a file ( 1 sa@wks:/tmp$ mkdir demo; cd demo 2 sa@wks:/tmp/demo$ la 3 total 4 4 drwxr-xr-x 2 sa sa 6 2009-02-27 12:59 . 5 drwxrwxrwt 14 root root 4096 2009-02-27 13:00 .. 6 sa@wks:/tmp/demo$ echo 'some blabla' > our_file 7 sa@wks:/tmp/demo$ ll 8 total 4.0K 9 -rw-r--r-- 1 sa sa 12 2009-02-27 13:00 our_file 10 sa@wks:/tmp/demo$ git init; git add .; git cwh -m 'initial commit' 11 Initialized empty Git repository in /tmp/demo/.git/ 12 [master (root-commit)]: created 8de1f5c: "initial commit" 13 1 files changed, 1 insertions(+), 0 deletions(-) 14 create mode 100644 our_file 15 sa@wks:/tmp/demo$ echo 'more text' >> our_file 16 sa@wks:/tmp/demo$ git cwh -m 'added more content to file our_file' 17 [master]: created 45cb121: "added more content to file our_file" 18 1 files changed, 1 insertions(+), 0 deletions(-) 19 sa@wks:/tmp/demo$ gllol 20 45cb1212da5d48b062622785841fa4ea489c6b6c 35 minutes ago CN: Markus Gattol AN: Markus Gattol S: added more content to file our_file 21 8de1f5cc61482c965ded2d9abfaf8f0648d9cac9 36 minutes ago CN: Markus Gattol AN: Markus Gattol S: initial commit 22 sa@wks:/tmp/demo$ cp our_file received_file 23 sa@wks:/tmp/demo$ git log --raw -r --abbrev=40 --pretty=oneline our_file | grep -B1 $(git hash-object received_file) 24 45cb1212da5d48b062622785841fa4ea489c6b6c added more content to file our_file 25 :100644 100644 99e51806133707c0c518ed4ad2586b799196ab5a 538e6b4f74c64a7af7eaae4289f77d88405441b8 M our_file 26 sa@wks:/tmp/demo$ The answer is with line 24 — it shows that the file Figuring out why and how this works is left as an exercise to the
reader — the person who understands line 25 and its meaning will
understand the whole thing we just did. The man pages for Recovering lost ChangesEven if it might look quite similar, recovering lost changes is not to be confused with fixing mistakes. ReflogSay we modify a branch with
Fortunately, GIT also keeps a log, called a reflog, of all the
previous values of each branch. So in this case we can still find the
old history using, for example, This lists the commits reachable from the previous version of git show master@{2} See where the branch pointed 2, git show master@{3} 3... commits ago git show master@{one.week.ago} where master used to point to one week ago gitk master@{yesterday} See where it pointed yesterday, gitk master@{"1 week ago"} ... or last week git log --walk-reflogs master show reflog entries for master A separate reflog is kept for each The reflogs are kept by default for 30 days, after which they may be
pruned —
I decided to configure the values for how long a reflog entry is kept
before it gets pruned by e.g. sa@wks:~/0/0$ git config --global gc.reflogexpire 365 sa@wks:~/0/0$ git config --global gc.reflogexpireunreachable 180 sa@wks:~/0/0$ git config --get gc.reflogexpireunreachable 180 sa@wks:~/0/0$ git config --get gc.reflogexpire 365 sa@wks:~/0/0$ Last but not least, a very important to understand fact on the reflog history — the reflog history is very different from normal GIT history. While normal history is shared by every repository that works on the same project, the reflog history is not shared i.e. it tells us only about how the branches in our local repository have changed over time. Examining dangling ObjectsIn some situations the reflog may not be able to save us. For example, suppose we delete a branch (the reflog is also deleted when deleting the branch), then we realize that we need the history it contained. If we have not yet pruned the repository by running git fsck dangling commit 7281251ddd2a61e38657c827739c57015671a6b3 dangling commit 2706a059f258c6b245f298dc4ff2ccd30ec21a63 dangling commit 13472b7c4b80851a1bc551779171dcb03655e9b5 [skipping a lot of lines...] We can examine one of those dangling commits with, for example, Notice that it might not be just one commit — we only report the tip of the line as being dangling, but there might be a whole deep and complex commit history that was dropped. If we decide we want the history back, we can always create a new
reference pointing to it, for example, a new branch Other types of dangling objects (e.g. blobs and trees) are also possible, and dangling objects can arise in other situations. Temporarily setting aside Work in ProgressThis one I love! It is not just totally practical because it reflects how humans think and work, but it also allows me to obey best practices... We use
For example, while we are in the middle of working on something complicated, we might find an unrelated but obvious and trivial bug or something that can be seen as a recursion of what we are currently working on i.e. some intermediate step which is a logical unit for itself and deserves a separate commit. Usually, a humans workflow is where we want to go from A to B but then
figure that there is a C necessary to be done before B can be finished
and so we use Below is an example where we use It is just for the sake of brevity that I do not provide a demo on completing some intermediate step (optionally after doing so on a different branch and then coming back) which as we might have figured is a logical unit of itself. 1 sa@wks:/tmp$ mkdir demo; cd demo; touch afile; git init; git cwi; git cwh -m 'initial commit' 2 Initialized empty Git repository in /tmp/demo/.git/ 3 [master (root-commit)]: created 10decd6: "initial commit" 4 0 files changed, 0 insertions(+), 0 deletions(-) 5 create mode 100644 afile 6 sa@wks:/tmp/demo$ echo 'some teeeeeext' > afile; git cwh -m 'added some text' 7 [master]: created aff733c: "added some text" 8 1 files changed, 1 insertions(+), 0 deletions(-) 9 sa@wks:/tmp/demo$ gllol 10 aff733c4edacc845a26c4546c3cf3275043244b5 2 seconds ago CN: Markus Gattol AN: Markus Gattol S: added some text 11 10decd6a751c109afc0ce81d54cb2420af7e728e 17 seconds ago CN: Markus Gattol AN: Markus Gattol S: initial commit 12 sa@wks:/tmp/demo$ head -n3 afile; git dwh | wc -l 13 some teeeeeext 14 0 The whole example is self-explanatory so I will just mention the most
important steps taken during this demo. As said, our intention is to
fix some trivia (typo in line 13) using 15 sa@wks:/tmp/demo$ for ((i=0; i < 500; i+=1)); do echo $i; done >> afile; head -n3 afile; git dwh | wc -l 16 some teeeeeext 17 0 18 1 19 506 20 sa@wks:/tmp/demo$ git stash list 21 sa@wks:/tmp/demo$ git stash save 'fixing some trivia' 22 Saved working directory and index state "On master: fixing some trivia" 23 HEAD is now at aff733c added some text 24 sa@wks:/tmp/demo$ head -n3 afile; git dwh | wc -l 25 some teeeeeext 26 0 27 sa@wks:/tmp/demo$ git branch -a 28 * master 29 sa@wks:/tmp/demo$ nano afile 30 31 32 [ here the default editor opened...] 33 34 Line 15 is to simulate some work in progress before we figure out we might need to set aside some work to address some trivia or intermediate work. While line 19 shows us that the working tree as well as the index are not clean because of the work we did, we can see (line 26) that line 21 does what it is intended to do — it sets back the working tree and the index to the state of the last commit. In order to fix the typo I have chosen to use nano as can be seen in line 32. 35 sa@wks:/tmp/demo$ head -n3 afile; git dwh | wc -l 36 some text 37 7 38 sa@wks:/tmp/demo$ git cwh -m 'some intermediate step (a logical unit of itself)' 39 [master]: created 4e97eff: "some intermediate step (a logical unit of itself)" 40 1 files changed, 1 insertions(+), 1 deletions(-) 41 sa@wks:/tmp/demo$ gllol 42 4e97effd3505fdad3fc66781ea6be14ec19ef914 4 seconds ago CN: Markus Gattol AN: Markus Gattol S: some intermediate step (a logical unit of itself) 43 aff733c4edacc845a26c4546c3cf3275043244b5 5 minutes ago CN: Markus Gattol AN: Markus Gattol S: added some text 44 10decd6a751c109afc0ce81d54cb2420af7e728e 6 minutes ago CN: Markus Gattol AN: Markus Gattol S: initial commit 45 sa@wks:/tmp/demo$ git stash apply 46 Auto-merging afile 47 CONFLICT (content): Merge conflict in afile 48 sa@wks:/tmp/demo$ head -n8 afile 49 <<<<<<< Updated upstream:afile 50 some text 51 ======= 52 some teeeeeext 53 0 54 1 55 2 56 3 After the trivia is fixed, we commit this logical unit in line 38. Note, only for this demo do we make a separate commit for a single typo. Usually we should always create one commit for one logical unit. As can be seen above, after issuing line 45, it might happen that we run into a merge conflict which we simply resolve manually (line 57). 57 sa@wks:/tmp/demo$ nano afile 58 59 60 [ here the default editor opened...] 61 62 63 sa@wks:/tmp/demo$ head -n5 afile 64 some text 65 0 66 1 67 2 68 3 69 sa@wks:/tmp/demo$ git stash list 70 stash@{0}: On master: fixing some trivia 71 sa@wks:/tmp/demo$ git cwh -m 'finished yet another logial unit' 72 [master]: created 821616a: "finished yet another logial unit" 73 1 files changed, 501 insertions(+), 0 deletions(-) 74 sa@wks:/tmp/demo$ gllol 75 821616a41e03562159427896669b318731608154 2 seconds ago CN: Markus Gattol AN: Markus Gattol S: finished yet another logial unit 76 4e97effd3505fdad3fc66781ea6be14ec19ef914 2 minutes ago CN: Markus Gattol AN: Markus Gattol S: some intermediate step (a logical unit of itself) 77 aff733c4edacc845a26c4546c3cf3275043244b5 7 minutes ago CN: Markus Gattol AN: Markus Gattol S: added some text 78 10decd6a751c109afc0ce81d54cb2420af7e728e 7 minutes ago CN: Markus Gattol AN: Markus Gattol S: initial commit 79 sa@wks:/tmp/demo$ git dwh 80 sa@wks:/tmp/demo$ Aside from seeing the final result of our actions in lines 64 to 68, what is interesting are lines 75 to 78 as it shows that we have a succession of commits representing a logical unit each — it is not so that, because we do not know better or because the SCM system we use is incapable of, we would have to put the logical units from line 75 and 76 into a single commit. Modifying a single CommitIn an earlier section we saw how to
fix a mistake by editing the history, which for example works by
replacing the most recent commit using We can also use a combination of this and Then we check out that commit using git checkout bad [ make changes here and update the index... ] git commit --amend git rebase --onto HEAD bad mywork I think some explanation for
When we are done, we will be left with branch Note that the immutable nature of GIT history means that we have not really modified existing commits. Instead, we have replaced the old commits with new commits having new object names. Problems with rewriting HistoryThe primary problem with rewriting the history of a branch has to do with merging. Suppose somebody fetches our branch and merges it into their branch, with a result something like this: o--o--O--o--o--o <-- origin \ \ t--t--t--m <-- their branch Then suppose we modify the last three commits: o--o--o <-- new head of origin / o--o--O--o--o--o <-- old head of origin If we examine all this history together in one repository, it will look like: o--o--o <-- new head of origin / o--o--O--o--o--o <-- old head of origin \ \ t--t--t--m <-- their branch: GIT has no way of knowing that the new head is an updated version of the old head — it treats this situation exactly the same as it would if two developers had independently done the work on the old and new heads in parallel. At this point, if someone attempts to merge the new head in to their branch, GIT will attempt to merge together the two (old and new) lines of development, instead of trying to replace the old by the new. The results are likely to be unexpected. We may still choose to publish branches whose history is rewritten, and it may be useful for others to be able to fetch those branches in order to examine or test them, but they should not attempt to pull such branches into their own work. As I said many times above already: For true distributed development that supports proper merging, published branches should never be rewritten! Inside GITA look under the hood... Examining the DataWe can examine the data represented in the object database (also known
as GIT back end) and the index with various helper tools. For every
object, we can use 1 sa@wks:/tmp/spear.clan$ git cat-file -t $(git rev-parse HEAD) 2 commit 3 sa@wks:/tmp/spear.clan$ git cat-file -s $(git rev-parse HEAD) 4 423 5 sa@wks:/tmp/spear.clan$ git cat-file commit $(git rev-parse HEAD) 6 tree 5dccb7bc01b01992f06185cb642f7f4f96b078b3 7 parent b1eba669f85e8d6b978217fcef2827d3f2c26eb2 8 author trollfot <trollfot@82af7df8-bc4b-4ebc-8022-2999806f7efb> 1235418218 +0000 9 committer trollfot <trollfot@82af7df8-bc4b-4ebc-8022-2999806f7efb> 1235418218 +0000 10 11 using the last spear.content way to declare portal_type 12 13 14 git-svn-id: http://tracker.trollfot.org/svn/projects/spear.clan@760 82af7df8-bc4b-4ebc-8022-2999806f7efb Line 2 shows the type of the object, and once we have the type (which
is usually implicit in where we find the object), we can use line 5 to
show its contents. It is especially instructive to look at commit objects, since those
tend to be small and fairly self-explanatory. In particular, if we
follow the convention of having the top commit name in 15 sa@wks:/tmp/spear.clan$ git cat-file commit HEAD 16 tree 5dccb7bc01b01992f06185cb642f7f4f96b078b3 17 parent b1eba669f85e8d6b978217fcef2827d3f2c26eb2 18 author trollfot <trollfot@82af7df8-bc4b-4ebc-8022-2999806f7efb> 1235418218 +0000 19 committer trollfot <trollfot@82af7df8-bc4b-4ebc-8022-2999806f7efb> 1235418218 +0000 20 21 using the last spear.content way to declare portal_type 22 23 24 git-svn-id: http://tracker.trollfot.org/svn/projects/spear.clan@760 82af7df8-bc4b-4ebc-8022-2999806f7efb 25 sa@wks:/tmp/spear.clan$ to see what the top commit was. With this convention obeyed, line 5 and 15 cater for the same result as can be seen. Note: Trees have binary content, and as a result there is a special
helper for showing that content, called How GIT stores objects efficiently: pack filesWe have seen how GIT stores each object in a file named after the object's SHA1 hash. Unfortunately this system becomes inefficient once a project has a lot of objects. For example, the source for this website/platform looks like the below 1 sa@wks:~/0/0$ git count-objects 2 1168 objects, 118688 kilobytes The first number ( We can save space and make GIT faster by moving those loose objects
into a so-called pack file, which stores a group of objects in an
efficient compressed format — the details of how pack files are
formatted can be found in 3 sa@wks:~/0/0$ git repack 4 Counting objects: 1163, done. 5 Compressing objects: 100% (1150/1150), done. 6 Writing objects: 100% (1163/1163), done. 7 Total 1163 (delta 264), reused 0 (delta 0) 8 sa@wks:~/0/0$ git count-objects 9 1168 objects, 118688 kilobytes 10 sa@wks:~/0/0$ git prune 11 sa@wks:~/0/0$ git count-objects 12 0 objects, 0 kilobytes 13 sa@wks:~/0/0$ The actual magic is with lines 3 to 7. Line 10 removes any of the
loose objects that are now contained in the pack. This will also
remove any unreferenced objects (which may be created whenever we use
Although the object files are gone, any commands that refer to those objects will work exactly as they did before because of the pack index. As mentioned before already, the Dangling ObjectsThe The most common cause of dangling objects is that we have rebased a branch, or we have pulled from somebody else who rebased a branch. In that case, the old head of the original branch still exists, as does everything it pointed to. The branch pointer itself just does not exist anymore since we replaced it with another one. There are also other situations that cause dangling objects. For
example, a dangling blob may arise because we did a Similarly, when the recursive merge strategy runs, and finds that there are criss-cross merges and thus more than one merge base (which is fairly unusual, but it does happen), it will generate one temporary midway tree (or possibly even more, if we had lots of criss-crossing merges and more than two merge bases) as a temporary internal merge base, and again, those are real objects, but the end result will not end up pointing to them, so they end up dangling in our repository. Generally, dangling objects are not anything to worry about. They can even be very useful e.g. if we screw something up, the dangling objects can be how we recover our old tree (say, we did a rebase, and realized that we really did not want to — we can look at what dangling objects we have, and decide to reset our head to some old dangling state). For commits, we can just use something like For blobs and trees, we can not do the same, but we can still examine
them. We can just do Usually, dangling blobs and trees are not very interesting. They are
almost always the result of either being a half-way mergebase (the
blob will often even have the conflict markers from a merge in it, if
we have had conflicting merges that we fixed up by hand), or simply
because we interrupted a Anyway, once we are sure that we are not interested in any dangling state, we can just prune all unreachable objects and they will be be gone. But we should only run The same is true of HooksGo here for information. Sample scripts can be found in
sa@wks:/tmp/git/contrib/hooks$ la total 48 drwxr-xr-x 2 sa sa 102 2009-03-01 19:27 . drwxr-xr-x 19 sa sa 4096 2009-03-01 19:27 .. -rw-r--r-- 1 sa sa 19324 2009-03-01 19:27 post-receive-email -rw-r--r-- 1 sa sa 1291 2009-03-01 19:27 pre-auto-gc-battery -rw-r--r-- 1 sa sa 6920 2009-03-01 19:27 setgitperms.perl -rw-r--r-- 1 sa sa 11647 2009-03-01 19:27 update-paranoid sa@wks:/tmp/git/contrib/hooks$
UsageOne nice example is with this source code of my website/platform
itself. In order to get rid of trailing whitespace (which we know is
bad), I decided to activate the pre-commit script by deleting the
sa@wks:~/0/0$ ll .git/hooks/ | grep pre-comm -rwxr-xr-x 1 sa sa 519 2009-02-25 15:52 pre-commit sa@wks:~/0/0$ Depending on what method is chosen to edit the source code (I use
GNU Emacs) there may or may not be a means of control in place in
order to check for trailing whitespace — in my case, in order to get
rid of it automatically, I use However, this hook ( 1 sa@wks:~/0/0$ cat .git/hooks/pre-commit 2 #!/bin/sh 3 # 4 # An example hook script to verify what is about to be committed. 5 # Called by git-commit with no arguments. The hook should 6 # exit with non-zero status after issuing an appropriate message if 7 # it wants to stop the commit. 8 # 9 # To enable this hook, rename this file to "pre-commit". 10 11 12 ## added by Markus Gattol 13 exec git add . 14 15 16 ## default 17 if git-rev-parse --verify HEAD 2>/dev/null 18 then 19 against=HEAD 20 else 21 # Initial commit: diff against an empty tree object 22 against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 23 fi 24 25 exec git diff-index --check --cached $against -- 26 sa@wks:~/0/0$ If we take a closer look, we can also see that I added some additional code in line 13. This line insures that, for example, new files/images/etc. I added are not forgotten to be added under version control with GIT. MiscellaneousThis section is used to drop anything GIT related here but which on its own does not deserve a section on its own. The subsections here must not necessarily have anything to do with each another, except for the fact that GIT may be the only thing they have in common. Bash PromptWhy not enhance our Bash prompt to show GIT related information? Something like it is shown below, where information like the current active branch, whether or not we have uncommitted changes etc. is displayed directly within the Bash prompt. Go here for more information.
/etc under Version ControlThe most obvious benefits of putting Another obvious reason is — resulting in pretty much the same actions
as above; looking at the changes (e.g. via A third reason why having There are many more reasons but the former three are those which I
already experienced myself — once
Come quickly, I am tasting stars! isisetupisisetup is one possibility to put ones etckeeperThe
What is etckeeper? What does it do?etckeeper is a collection of tools in order to put It tracks file metadata that GIT does not normally support, but that
is important for etckeeper has special support to handle changes to We can also run Install and Configure etckeeper1 sa@wks:/etc/etckeeper$ dpl etckeeper | grep ^ii 2 ii etckeeper 0.30 store /etc in git, mercurial, bzr or darcs 3 sa@wks:/etc/etckeeper$ type gr && gr HIGHLEVEL etckeeper.conf 4 gr is aliased to `grep -rni --color' 5 29:HIGHLEVEL_PACKAGE_MANAGER=apt 6 sa@wks:/etc/etckeeper$ cd .. I have already installed etckeeper as can be seen in line 2. The 7 sa@wks:/etc$ su 8 Password: 9 wks:/etc# etckeeper init 10 Initialized empty Git repository in /etc/.git/ 11 wks:/etc# git commit -a -m "Initial Commit" 12 13 [skipping a lot of lines...] 14 15 create mode 100644 xpdf/xpdfrc-arabic 16 create mode 100644 xpdf/xpdfrc-cyrillic 17 create mode 100644 xpdf/xpdfrc-greek 18 create mode 100644 xpdf/xpdfrc-hebrew 19 create mode 100644 xpdf/xpdfrc-latin2 20 create mode 100644 xpdf/xpdfrc-thai 21 create mode 100644 xpdf/xpdfrc-turkish 22 create mode 100644 yaird/Default.cfg 23 create mode 100644 yaird/Templates.cfg 24 wks:/etc# git gc 25 Counting objects: 2931, done. 26 Compressing objects: 100% (2177/2177), done. 27 Writing objects: 100% (2931/2931), done. 28 Total 2931 (delta 267), reused 0 (delta 0) 29 wks:/etc# ls -lat | head 30 total 1588 31 drwx------ 8 root root 4096 2009-02-14 19:21 .git 32 -rwx------ 1 root root 6101 2009-02-14 19:21 .etckeeper 33 drwxr-xr-x 171 root root 12288 2009-02-14 19:20 . 34 -rw------- 1 root root 433 2009-02-14 19:20 .gitignore 35 drwxr-xr-x 10 root root 4096 2009-02-14 17:47 etckeeper 36 -rw-r--r-- 1 root root 23 2009-02-14 14:29 resolv.conf 37 -rw-r--r-- 1 root root 111633 2009-02-14 13:52 ld.so.cache 38 drwxr-xr-x 2 root root 4096 2009-02-14 13:52 cron.daily 39 drwxr-xr-x 2 root root 4096 2009-02-14 13:52 bash_completion.d 40 wks:/etc# exit 41 exit 42 sa@wks:/etc$ ll etckeeper/post-install.d/ 43 total 12K 44 -rwxr-xr-x 1 root root 462 2008-12-17 00:14 50vcs-commit 45 -rwxr-xr-x 1 root root 22 2009-02-15 01:15 99git-gc 46 -rw-r--r-- 1 root root 141 2008-12-17 00:14 README 47 sa@wks:/etc$ cat etckeeper/post-install.d/99git-gc 48 #!/bin/sh 49 echo -e "\ngit repository housekeeping using git gc..." 50 git gc 51 echo -e "git gc finished successfully...\n" 52 sa@wks:/etc$ In line 9 I am initializing the GIT repository — using Update: As of version 0.38, issuing sa@wks:~$ zcat /usr/share/doc/etckeeper/changelog.gz | head -n7 etckeeper (0.38) unstable; urgency=low * Use hostname if hostname -f fails. Closes: #533295 * Automatically commit on initial install, so users can begin relying on etckeeper right away. Closes: #533290 -- Joey Hess <[email protected]> Wed, 08 Jul 2009 14:40:58 -0400 sa@wks:~$ We can then After that finished we can run In lines 31 to 39 we can see things like We have now successfully installed and setup etckeeper, the repository
will track all changes made to GNU Emacs and GITNo matter what SCM (Software Configuration Management) I am working
with, I usually use Emacs as a frontend since it is a lot faster then
using the CLI (Command Line Interface) and even much more speedy than
using some nonsense GUI (Graphical User Interface). Next to the saving
me a lot of time, using Emacs as a frontend also allows to use the
whole mighty range of Emacs magic that I am used to. I use
As of now (August 2007) DVC undergoes heavy development and is not
fully ready for action that is why I use git.elDVC (Distributed Version Control)DVC is an Emacs frontend for various Decentralized Revision Control systems. It is the successor, and still includes Xtla, which is the Emacs frontend to tla and baz (GNU Arch client).
Take a look at the aliases in my cd ~ bzr get http://bzr.xsteve.at/dvc/ cd ~/dvc autoconf ./configure make Finally, take a look at the settings in my .emacs (plain text version)
how I load the code, what keybindings I have etc. — search for the
string git-mergetoolThe git manual says e.g. Emacs ediff can be used to resolve conflicts. You may also use git-mergetool(1), which lets you merge the unmerged files using external tools such as emacs or kdiff3. 1. Well, there is not much to say into that. My personal experience as well as my observations are like this: After two decades or so a person has pretty much seen everything related to his area of expertise and thus is able to not just judge things instantly but also to avoid redundancy i.e. repeatedly doing the same work several times where the single one correct approach would have been enough. Some call that experience and collected knowledge others just call it getting older. My non-abstract statement here is, I do not use two or more tools to go from A to B anymore, I automatize anything possible, I try to save as much time as possible from repeating tasks and use this time savings to either not having to work 70+ hours a week or otherwise to make progress on really demanding areas of my research interests. Finally, I fell lucky, I am now able to judge things by just glancing towards them and make instant decisions. The tools (e.g. OS, Editor, SCM, Hardware, etc.) I use are the best solution — there is no redundancy at all anymore plus a have tailored the whole thing to fit my needs. The best technical solution is worth nothing if it requires a human to invest to much time for it e.g. GNU Emacs is just worth to go through the initial 6 month of pain because in the long-term it probably saves one the tenfold amount of time... Same goes for DebianGNU/Linux, enterprise-class hardware e.g. IBM Blade Center, helicopter flying licence, etc. 2. If you are with Gnus then visit the group buffer, type 3. Generally, 4. However, one can setup a post-commit hook that will automatically push for him every time he commits in his local repository. The downside is he loses the flexibility to fix up a screwed commit in his local repository by doing so. 5. We do not mirror the SVN repository as is locally. What |