diff --git a/book.css b/book.css index 547923d0..ed2fb1ae 100644 --- a/book.css +++ b/book.css @@ -13,7 +13,8 @@ pre { } ul li p { - margin-bottom:0; + margin: 0; + padding: 0; } /* Based on http://phrogz.net/CSS/columns3.html */ diff --git a/en/basic.txt b/en/basic.txt index 730672fc..96f49e0c 100644 --- a/en/basic.txt +++ b/en/basic.txt @@ -23,7 +23,7 @@ To save the state again: === Add, Delete, Rename === -The above will only keep track of the files that were present when you first ran *git add*. If you add new files or subdirectories, you'll have to tell Git: +The above only keeps track of the files that were present when you first ran *git add*. If you add new files or subdirectories, you'll have to tell Git: $ git add readme.txt Documentation @@ -164,7 +164,7 @@ and your users can upgrade their version by changing to the directory containing $ git pull -Your users will never end up with a version of your script you don't want them to see. Obviously this trick works for anything, not just scripts. +Your users will never end up with a version of your script you don't want them to see. === What Have I Done? === @@ -193,7 +193,7 @@ fire up any web browser. === Exercise === -Let A, B, C, D be four successive commits where B is the same as A except some files have been removed. We want to add the files back at D and not at B. How can we do this? +Let A, B, C, D be four successive commits where B is the same as A except some files have been removed. We want to add the files back at D. How can we do this? There are at least three solutions. Assuming we are at D: diff --git a/en/branch.txt b/en/branch.txt index 047eeb02..dde53ff0 100644 --- a/en/branch.txt +++ b/en/branch.txt @@ -82,7 +82,7 @@ Then once you've fixed the bug: and resume work on your original task. -You can even merge in the bugfix you just made, either by typing: +You can even 'merge' in the bugfix you just made, either by typing: $ git merge fixes @@ -94,20 +94,20 @@ since you have already pushed the bugfix to the main repository. === Merging === -With many version control systems, creating branches is easy but merging them +With some version control systems, creating branches is easy but merging them back together is tough. With Git, merging is so trivial that you might be unaware of it happening. Indeed, though we have just introduced *git merge*, we encountered merging long ago. The *pull* command in fact fetches commits and then merges them into your current branch. If you have no local changes, then the merge is a 'fast forward', a degenerate case akin to fetching the latest version in a centralized version control system. But if you do have local changes, Git will automatically merge, and report any conflicts. -Ordinarily, a commit has exactly one parent, namely, the previous commit. -Merging other branches creates a commit with at least two parents. This begs -the question: what commit does `HEAD~10` really refer to? A commit could have -multiple parents, so which one do we follow? +Ordinarily, a commit has exactly one 'parent commit', namely, the previous +commit. Merging branches together produces a commit with at least two parents. +This begs the question: what commit does `HEAD~10` really refer to? A commit +could have multiple parents, so which one do we follow? -It turns out we follow the first parent at every step. This is usually desired, -because commits in the current branch always become first parents in a *git -merge*; frequently you're only concerned with the changes you made in the +It turns out this notation chooses the first parent every time. This is +desirable because commits in the current branch become the first parents during +a merge; frequently you're only concerned with the changes you made in the current branch, as opposed to changes merged in from other branches. You can refer to a specific parent with a caret. For example, to show @@ -115,17 +115,17 @@ the logs from the second parent: $ git log HEAD^2 -If you want the first parent, you can leave out the number. For example, to -show the differences with the first parent: +You may omit the number for the first parent. For example, to show the +differences with the first parent: $ git diff HEAD^ You can combine this notation with other types. For example: - $ git checkout 1bd6^^2~10 -b ancient + $ git checkout 1b6d^^2~10 -b ancient starts a new branch ``ancient'' representing the state 10 commits back from the -second parent of the first parent of the commit starting with 1bd6. +second parent of the first parent of the commit starting with 1b6d. === Uninterrupted Workflow === @@ -230,14 +230,14 @@ You can have multiple stashes, and manipulate them in various ways. See === Work How You Want === You might wonder if branches are worth the bother. After all, clones are almost -as fast, and you can use *cd* to switch between them, instead of esoteric Git +as fast, and you can switch between them with *cd* instead of esoteric Git commands. Consider web browsers. Why support multiple tabs as well as multiple windows? Because allowing both accommodates a wide variety of styles. Some users like to keep only one browser window open, and use tabs for multiple webpages. Others might insist on the other extreme: multiple windows with no extra tabs anywhere. -Yet others prefer something in between. +Others still prefer something in between. Branching is like tabs for your working directory, and cloning is like opening a new browser window. These operations are fast and local, so why not diff --git a/en/clone.txt b/en/clone.txt index f570583e..078ce93a 100644 --- a/en/clone.txt +++ b/en/clone.txt @@ -2,11 +2,11 @@ In older version control systems, checkout is the standard operation to get files. You retrieve a bunch of files in the requested saved state. -In Git and other distributed version control systems, cloning is the standard operation. To get files you create a clone of the entire repository. In other words, you practically mirror the central server. Anything the main repository can do, you can do. +In Git and other distributed version control systems, cloning is the standard operation. To get files, you create a 'clone' of the entire repository. In other words, you practically mirror the central server. Anything the main repository can do, you can do. === Sync Computers === -This is the reason I first used Git. I can tolerate making tarballs or using *rsync* for backups and basic syncing. But sometimes I edit on my laptop, other times on my desktop, and the two may not have talked to each other in between. +I can tolerate making tarballs or using *rsync* for backups and basic syncing. But sometimes I edit on my laptop, other times on my desktop, and the two may not have talked to each other in between. This situation drove me to learn Git in the first place. Initialize a Git repository and commit your files on one machine. Then on the other: @@ -17,7 +17,7 @@ to create a second copy of the files and Git repository. From now on, $ git commit -a $ git pull other.computer:/path/to/files HEAD -will pull in the state of the files on the other computer into the one you're working on. If you've recently made conflicting edits in the same file, Git will let you know and you should commit again after resolving them. +will 'pull' in the state of the files on the other computer into the one you're working on. If you've recently made conflicting edits in the same file, Git will let you know and you should commit again after resolving them. === Classic Source Control === @@ -27,38 +27,61 @@ Initialize a Git repository for your files: $ git add . $ git commit -m "Initial commit" -On the central server, initialize an empty Git repository with some name, -and start the Git daemon if necessary: +On the central server, initialize a 'bare repository' in some directory: - $ GIT_DIR=proj.git git init - $ git daemon --detach # it might already be running + $ mkdir proj.git + $ cd proj.git + $ git init --bare + $ # one-liner alternative: GIT_DIR=proj.git git init + +Start the Git daemon if necessary: + + $ git daemon --detach # it may already be running For Git hosting services, follow the instructions to setup the initially empty Git repository. Typically one fills in a form on a webpage. -Push your project to the central server with: +'Push' your project to the central server with: $ git push git://central.server/path/to/proj.git HEAD -The central server now holds a 'bare repository': a repository with no working directory. In other words, the central server has the history of your project, but never contains a snapshot of it. To check out the source, a developer types: +The bare repository is so named because it contains no working directory and exposes files that are hidden away in the `.git` subdirectory of a normal Git repository. In other words, the central server maintains the history of your project, and never carries a snapshot of any given version. + +To check out the source, a developer types: $ git clone git://central.server/path/to/proj.git -After making changes, the code is checked in to the main server by: +After making changes, the developer saves changes locally: $ git commit -a - $ git push -If the main server has been updated, the latest version needs to be checked out before the push. To sync to the latest version: +To update to the latest version: - $ git commit -a $ git pull +Any merge conflicts should be resolved then committed: + + $ git commit -a + +To check in local changes into the central repository: + + $ git push + +If the main server has new changes due to activity by other developers, the +push fails, and the developer should pull the latest version, resolve any merge +conflicts, then try again. + === Push versus pull === -A push is more convenient than a pull in the above example. If we pulled from the server, we would have to login to the server first, and give the pull command the network address of the machine we're pulling from. Firewalls may interfere, and we might not even have shell access to the server. +A push is more convenient than a pull in the above example. Firstly, pull fails +on bare repositories (you must 'fetch' instead; we discuss this in a later +chapter). But even if we kept a normal repository on the central server, +pulling into it is still cumbersome. Each time, we would have to login to the +server first, and give the pull command the network address of the machine +we're pulling from. Firewalls may interfere, and we might not even have shell +access to the server. -However, we usually avoid pushing into a repository, because confusion can ensue if the destination has a working directory with changes. But since a bare repository by definition has no working directory, pushing to a bare repository is a straightforward operation. +However, apart from this case, we discourage pushing into a repository, because confusion can ensue when the destination has a working directory. In short, while learning Git, only push when the target is a bare repository; otherwise pull. diff --git a/en/grandmaster.txt b/en/grandmaster.txt index 039dea36..238d90ab 100644 --- a/en/grandmaster.txt +++ b/en/grandmaster.txt @@ -86,9 +86,9 @@ But how can you go back to the future? The past commits know nothing of the futu If you have the SHA1 of the original HEAD then: - $ git reset 1bd6 + $ git reset 1b6d -But suppose you never took it down? Don't worry, for commands like these, Git saves the original HEAD as a tag called ORIG_HEAD, and you can return safe and sound with: +But suppose you never took it down? Don't worry: for commands like these, Git saves the original HEAD as a tag called ORIG_HEAD, and you can return safe and sound with: $ git reset ORIG_HEAD @@ -161,7 +161,7 @@ One popular resident is +workdir/git-new-workdir+. Via clever symlinking, this s $ git-new-workdir an/existing/repo new/directory -The new directory and files within can be thought of as a clone, except since the history is shared, the two trees automatically stay in sync. There's no need to merge, push or pull. +The new directory and the files within can be thought of as a clone, except since the history is shared, the two trees automatically stay in sync. There's no need to merge, push or pull. === Daring Stunts === @@ -177,7 +177,7 @@ On the other hand, if you specify particular paths for checkout, then there are *Reset*: Reset also fails in the presence of uncommitted changes. To force it through, run: - $ git reset --hard 1bd6 + $ git reset --hard 1b6d *Branch*: Deleting branches fails if this causes changes to be lost. To force a deletion, type: @@ -200,10 +200,10 @@ directories are expendable, then delete them mercilessly with: Next time, that pesky command will work! -=== Improve Your Public Image === +=== Preventing Bad Commits === Stupid mistakes abound in the histories of many of my projects. The most -frightening are missing files due to forgetting to run *git add*. Luckily I +frightening are missing files due to a forgotten *git add*. Luckily I have yet to lose crucial data though accidental omission because I rarely delete original working directories. I typically notice the error a few commits later, so the only damage is a bit of missing history and a sheepish admission @@ -238,5 +238,7 @@ Several git operations support hooks; see *git help hooks*. One can write hooks to complain about spelling mistakes in commit messages, add new files, indent paragraphs, append an entry to a webpage, play a sound, and so on. -We encountered the *post-update* hook earlier when discussing Git over -HTTP. This hook updates a few files Git needs for non-native communication. +We activated the sample *post-update* hook earlier when discussing Git over +HTTP; this causes Git to run this script whenever the head has moved. The +sample post-update script updates a few files Git needs for communication over +Git-agnostic transports such as HTTP. diff --git a/en/history.txt b/en/history.txt index f82a478f..94b73e8c 100644 --- a/en/history.txt +++ b/en/history.txt @@ -6,9 +6,6 @@ history which you alone possess. Just as nations forever argue over who committed what atrocity, if someone else has a clone whose version of history differs to yours, you will have trouble reconciling when your trees interact. -Of course, if you control all the other trees too, you can simply overwrite -them. - Some developers strongly feel history should be immutable, warts and all. Others feel trees should be made presentable before they are unleashed in public. Git accommodates both viewpoints. Like cloning, branching and merging, @@ -44,12 +41,14 @@ Then: - Remove commits by deleting lines. - Reorder commits by reordering lines. -- Replace "pick" with "edit" to mark a commit for amending. -- Replace "pick" with "reword" to change the log message. -- Replace "pick" with "squash" to merge a commit with the previous one. -- Replace "pick" with "fixup" to merge a commit with the previous one and discard the log message. +- Replace `pick` with: + * `edit` to mark a commit for amending. + * `reword` to change the log message. + * `squash` to merge a commit with the previous one. + * `fixup` to merge a commit with the previous one and discard the log message. -If you marked a commit for editing, then run: +Save and quit. If you marked a commit for editing, then +run: $ git commit --amend @@ -57,7 +56,7 @@ Otherwise, run: $ git rebase --continue -So commit early and commit often: you can easily tidy up later with rebase. +So commit early and commit often: you can tidy up later with rebase. === Local Changes Last === @@ -147,9 +146,9 @@ You can checkout the latest version of the project with: $ git checkout master . -The *git fast-export* command converts any git repository to the +The *git fast-export* command converts any repository to the *git fast-import* format, whose output you can study for writing exporters, -and also to transport git repositories in a human-readable format. Indeed, +and also to transport repositories in a human-readable format. Indeed, these commands can send repositories of text files over text-only channels. === Where Did It All Go Wrong? === @@ -161,7 +160,7 @@ can pinpoint the problem: $ git bisect start $ git bisect bad HEAD - $ git bisect good 1bd6 + $ git bisect good 1b6d Git checks out a state halfway in between. Test the feature, and if it's still broken: diff --git a/en/multiplayer.txt b/en/multiplayer.txt index 9d27559a..bcce8e2f 100644 --- a/en/multiplayer.txt +++ b/en/multiplayer.txt @@ -29,9 +29,7 @@ Download, compile and install Git in your account, and create a repository in your web directory: $ GIT_DIR=proj.git git init - -In the `proj.git` directory, run: - + $ cd proj.git $ git --bare update-server-info $ cp hooks/post-update.sample hooks/post-update @@ -60,9 +58,8 @@ The sender creates a 'bundle': $ git bundle create somefile HEAD then transports the bundle, +somefile+, to the other party somehow: email, -thumb drive, floppy disk, an *xxd* printout and an OCR machine, -reading bits over the phone, smoke signals, etc. The receiver retrieves -commits from the bundle by typing: +thumb drive, an *xxd* printout and an OCR scanner, reading bits over the phone, +smoke signals, etc. The receiver retrieves commits from the bundle by typing: $ git pull somefile @@ -70,10 +67,10 @@ The receiver can even do this from an empty repository. Despite its size, +somefile+ contains the entire original git repository. In larger projects, eliminate waste by bundling only changes the other -repository lacks. For example, suppose the commit ``1bd6...'' is the most +repository lacks. For example, suppose the commit ``1b6d...'' is the most recent commit shared by both parties: - $ git bundle create somefile HEAD ^1bd6 + $ git bundle create somefile HEAD ^1b6d If done frequently, one could easily forget which commit was last sent. The help page suggests using tags to solve this. Namely, after you send a bundle, @@ -95,7 +92,7 @@ your side, all you require is an email account: there's no need to setup an onli Recall from the first chapter: - $ git diff 1bd6 > my.patch + $ git diff 1b6d > my.patch outputs a patch which can be pasted into an email for discussion. In a Git repository, type: @@ -107,11 +104,11 @@ to apply the patch. In more formal settings, when author names and perhaps signatures should be recorded, generate the corresponding patches past a certain point by typing: - $ git format-patch 1bd6 + $ git format-patch 1b6d The resulting files can be given to *git-send-email*, or sent by hand. You can also specify a range of commits: - $ git format-patch 1bd6..HEAD^^ + $ git format-patch 1b6d..HEAD^^ On the receiving end, save an email to a file, then type: @@ -129,7 +126,7 @@ without reading tutorials! After cloning a repository, running *git push* or *git pull* will automatically push to or pull from the original URL. How does Git do this? The secret lies in -config options initialized created with the clone. Let's take a peek: +config options created with the clone. Let's take a peek: $ git config --list @@ -199,15 +196,14 @@ easy access to all branches of all repositories: But what if we just want to compare their changes without affecting our own work? In other words, we want to examine their branches without having -their changes invade our working directory. In this case, rather than pull, -run: +their changes invade our working directory. Then rather than pull, run: $ git fetch # Fetch from origin, the default. $ git fetch other # Fetch from the second programmer. -This fetches their histories and nothing more, so although the working -directory remains untouched, we can refer to any branch of any repository in -a Git command because we now possess a local copy. +This just fetches histories. Although the working directory remains untouched, +we can refer to any branch of any repository in a Git command because we now +possess a local copy. Recall that behind the scenes, a pull is simply a *fetch* then *merge*. Usually we *pull* because we want to merge the latest commit after a fetch; @@ -218,14 +214,13 @@ branches, and more. === My Preferences === -For my projects, I like contributors to prepare Git repositories which I can +For my projects, I like contributors to prepare repositories from which I can pull. Some Git hosting services let you host your own fork of a project with the click of a button. After I fetch a tree, I run Git commands to navigate and examine the changes, -which ideally are well-organized and well-described. I merge unpublished -changes of my own, and perhaps make further edits. Once satisfied, I push to -the official repository. +which ideally are well-organized and well-described. I merge my own changes, +and perhaps make further edits. Once satisfied, I push to the main repository. Though I infrequently receive contributions, I believe this approach scales well. See @@ -233,7 +228,6 @@ http://torvalds-family.blogspot.com/2009/06/happiness-is-warm-scm.html[this blog post by Linus Torvalds]. Staying in the Git world is slightly more convenient than patch files, as it -saves me the step of converting them to Git commits. Furthermore, Git -automatically handles details such as recording the author's name and email -address, as well as the time and date, and asks the author to describe -their own change. +saves me from converting them to Git commits. Furthermore, Git handles details +such as recording the author's name and email address, as well as the time and +date, and asks the author to describe their own change. diff --git a/en/secrets.txt b/en/secrets.txt index 7e8a2b6c..264f8058 100644 --- a/en/secrets.txt +++ b/en/secrets.txt @@ -48,9 +48,16 @@ This http://lkml.org/lkml/2005/4/6/121[Linux Kernel Mailing List post] describes === The Object Database === -Here's how to write a Git-like system from scratch in a few hours. +Every version of your data is kept in the 'object database', which lives in the +subdirectory `.git/objects`. The other residents of `.git` store lesser data +such as the index, branch names, tags, configuration files, logs, the current +location of the head commit, and so on. The object database is elementary yet +elegant, and the secret to Git's power. -==== Blobs ==== +Each file within `.git/objects` is an `object`. There are 3 kinds of objects +that concern us: 'blob' objects, 'tree' objects, and 'commit' objects. + +=== Blobs === First, a magic trick. Pick a filename, any filename. In an empty directory: @@ -94,7 +101,7 @@ http://www.zlib.net/zpipe.c[zpipe -d], or type: which pretty-prints the given object. -==== Trees ==== +=== Trees === But where are the filenames? They must be stored somewhere at some stage. Git gets around to the filenames during a commit: @@ -145,7 +152,7 @@ command is running at the same time, or a sudden power outage occurs? In general, refs should be deleted with *git update-ref -d*, though usually it's safe to remove +refs/original+ by hand. -==== Commits ==== +=== Commits === We've explained 2 of the 3 objects. The third is a 'commit' object. Its contents depend on the commit message as well as the date and time it was