Git Magic
Git Magic
Ben Lynn
Git Magic
by Ben Lynn
Table of Contents
Preface ........................................................................................................................................................v
1. Thanks!...........................................................................................................................................v
2. License ..........................................................................................................................................vi
1. Introduction............................................................................................................................................1
1.1. Work is Play ................................................................................................................................1
1.2. Version Control ...........................................................................................................................1
1.3. Distributed Control......................................................................................................................1
1.3.1. A Silly Superstition ........................................................................................................2
1.4. Merge Conflicts...........................................................................................................................3
2. Basic Tricks ............................................................................................................................................4
2.1. Saving State.................................................................................................................................4
2.1.1. Add, Delete, Rename......................................................................................................4
2.2. Advanced Undo/Redo .................................................................................................................4
2.2.1. Reverting ........................................................................................................................6
2.3. Changelog Generation.................................................................................................................6
2.4. Downloading Files ......................................................................................................................6
2.5. The Bleeding Edge......................................................................................................................6
2.6. Instant Publishing........................................................................................................................7
2.7. What Have I Done? .....................................................................................................................7
2.8. Exercise .......................................................................................................................................8
3. Cloning Around .....................................................................................................................................9
3.1. Sync Computers ..........................................................................................................................9
3.2. Classic Source Control................................................................................................................9
3.2.1. Push versus pull ............................................................................................................10
3.3. Forking a Project .......................................................................................................................10
3.4. Ultimate Backups ......................................................................................................................11
3.5. Light-Speed Multitask...............................................................................................................11
3.6. Guerilla Version Control ...........................................................................................................11
4. Branch Wizardry .................................................................................................................................13
4.1. The Boss Key ............................................................................................................................13
4.2. Dirty Work ................................................................................................................................14
4.3. Quick Fixes ...............................................................................................................................14
4.4. Uninterrupted Workflow ...........................................................................................................15
4.5. Reorganizing a Medley .............................................................................................................16
4.6. Managing Branches...................................................................................................................16
4.7. Temporary Branches .................................................................................................................17
4.8. Work How You Want ................................................................................................................17
5. Lessons of History................................................................................................................................19
5.1. I Stand Corrected ......................................................................................................................19
5.2. . . . And Then Some...................................................................................................................19
5.3. Local Changes Last ...................................................................................................................20
5.4. Rewriting History......................................................................................................................20
5.5. Making History .........................................................................................................................21
5.6. Where Did It All Go Wrong?....................................................................................................22
iii
iv
Preface
Git (http://git.or.cz/) is a version control Swiss army knife. A reliable versatile multipurpose revision
control tool whose extraordinary flexibility makes it tricky to learn, let alone master.
As Arthur C. Clarke observed, any sufficiently advanced technology is indistinguishable from magic.
This is a great way to approach Git: newbies can ignore its inner workings and view Git as a gizmo that
can amaze friends and infuriate enemies with its wondrous abilities.
Rather than go into details, we provide rough instructions for particular effects. After repeated use,
gradually you will understand how each trick works, and how to tailor the recipes for your needs.
Translations
Other Editions
1. Thanks!
Dustin Sallings, Alberto Bertogli, James Cameron, Douglas Livingstone, Michael Budde, Richard
Albury, Tarmigan, Derek Mahar, Frode Aannevik, Keith Rarick, and Andy Somerville contributed
suggestions and improvements.
Francois Marier maintains the Debian package originally created by Daniel Baumann.
JunJie, Meng, JiangWei, Rodrigo Toledo, and Leonardo Siqueira Rodrigues worked on translations of
this guide.
My gratitute goes to many others for your support and praise. If this were a real physical book, Id quote
your generous words on the cover to promote it.
Preface
If Ive left you out by mistake, please tell me or just send me a patch!
Free Git hosting
http://repo.or.cz/ hosts free projects. The first Git hosting site. Founded and maintained by one of the
earliest Git developers.
http://github.com/ hosts open-source projects for free, and private projects for a fee.
2. License
This guide is released under the GNU General Public License version 3
(http://www.gnu.org/licenses/gpl-3.0.html). Naturally, the source is kept in a Git repository, and can be
obtained by typing:
$ git clone git://repo.or.cz/gitmagic.git
vi
Chapter 1. Introduction
Ill use an analogy to introduce version control. See the Wikipedia entry on revision control
(http://en.wikipedia.org/wiki/Revision_control) for a saner explanation.
Chapter 1. Introduction
Chapter 1. Introduction
A small project may only need a fraction of the features offered by such a system, but saying you should
use systems that dont scale well when your project is tiny is like saying you should use Roman numerals
for calculations involving small numbers.
Moreover, your project may grow beyond your original expectations. Using Git from the outset is like
carrying a Swiss army knife even though you mostly use it to open bottles. On the day you desperately
need a screwdriver youll be glad you have more than a plain bottle-opener.
Similarly, if you want Git to forget about certain files, maybe because youve deleted them:
$ git rm OLDFILES...
Renaming a file is the same as removing the old name and adding the new name. Theres also the
shortcut git mv which has the same syntax as the mv command. For example:
$ git mv OLDFILE NEWFILE
shows you a list of recent commits, and their SHA1 hashes. Next, type:
$ git reset --hard SHA1_HASH
to restore the state to a given commit and erase all newer commits from the record permanently.
Other times you want to hop to an old state briefly. In this case, type:
$ git checkout SHA1_HASH
This takes you back in time, while preserving newer commits. However, like time travel in a
science-fiction movie, if you now edit and commit, you will be in an alternate reality, because your
actions are different to what they were the first time around.
This alternate reality is called a branch, and well have more to say about this later. For now, just
remember that
$ git checkout master
will take you back to the present. Also, to stop Git complaining, always commit or reset your changes
before running checkout.
To take the computer game analogy again:
git reset --hard:
load an old save and delete all saved games newer than the one just loaded.
git checkout:
load an old game, but if you play on, the game state will deviate from the newer
saves you made the first time around. Any saved games you make now will end up in a separate
branch representing the alternate reality you have entered. We deal with this later.
You can choose only to restore particular files and subdirectories by appending them after the command:
$ git checkout SHA1_HASH some.file another.file
Take care, as this form of checkout can silently overwrite files. To prevent accidents, commit before
running any checkout command, especially when first learning Git. In general, whenever you feel unsure
about any operation, Git command or not, first run git commit -a.
to jump to the commit that starts with a given message. You can also ask for the 5th-last saved state:
$ git checkout master~5
2.2.1. Reverting
In a court of law, events can be stricken from the record. Likewise, you can pick specific commits to
undo.
$ git commit -a
$ git revert SHA1_HASH
will undo just the commit with the given hash. Running git log reveals the revert is recorded as a new
commit.
For example, to get all the files I used to create this site:
$ git clone git://git.or.cz/gitmagic.git
to download your script. This assumes they have ssh access. If not, run git daemon and tell your users to
instead run:
$ git clone git://your.computer/path/to/script
From now on, every time your script is ready for release, execute:
$ git commit -a -m "Next release"
and your users can upgrade their version by changing to the directory containing your script and typing:
$ git pull
Your users will never end up with a version of your script you dont want them to see. Obviously this
trick works for anything, not just scripts.
Or since yesterday:
$ git diff "@{yesterday}"
In each case the output is a patch that can be applied with git apply. Try also:
$ git whatchanged --since="2 weeks ago"
Often Ill browse history with qgit (http://sourceforge.net/projects/qgit) instead, due to its slick
photogenic interface, or tig (http://jonas.nitro.dk/tig/), a text-mode interface that works well over slow
connections. Alternatively, install a web server, run git instaweb and fire up any web browser.
2.8. 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?
There are at least three solutions. Assuming we are at D:
1. The difference between A and B are the removed files. We can create a patch representing this
difference and apply it:
$ git diff B A | git apply
Which choice is best? Whichever you prefer most. It is easy to get what you want with Git, and often
there are many ways to get it.
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 youre working on. If youve recently
made conflicting edits in the same file, Git will let you know and you should commit again after
resolving them.
On the central server, initialize an empty Git repository with some name, and start the Git daemon if
necessary:
$ GIT_DIR=proj.git git init
$ git daemon --detach # it might already be running
After making changes, the code is checked in to the main server by:
$ 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:
$ git commit -a
$ git pull
Next tell everyone about your fork of the project at your server.
At any later time, you can merge in the changes from the original project with:
$ git pull
10
Git exploits hard links and file sharing as much as safely possible to create this clone, so it will be ready
in a flash, and you can now work on two independent features simultaneously. For example, you can edit
one clone while the other is compiling.
At any time, you can commit and pull changes from the other clone.
$ git pull /the/other/clone HEAD
11
The procedure for giving your changes to everyone else depends on the other version control system. The
new directory contains the files with your changes. Run whatever commands of the other version control
system are needed to upload them to the central repository.
The git svn command automates the above for Subversion repositories, and can also be used to export a
Git project to a Subversion repository
(http://google-opensource.blogspot.com/2008/05/export-git-project-to-google-code.html).
12
We have created a Git repository that tracks one text file containing a certain message. Now type:
$ git checkout -b boss # nothing seems to change after this
$ echo "My boss is smarter than me" > myfile.txt
13
It looks like weve just overwritten our file and committed it. But its an illusion. Type:
$ git checkout master
and hey presto! The text file is restored. And if the boss decides to snoop around this directory, type:
$ git checkout boss
You can switch between the two versions of the file as much as you like, and commit to each
independently.
Now you can add ugly temporary code all over the place. You can even commit these changes. When
youre done,
$ git checkout master
to return to your original work. Observe that any uncommitted changes are carried over.
What if you wanted to save the temporary changes after all? Easy:
$ git checkout -b dirty
and commit before switching back to the master branch. Whenever you want to return to the dirty
changes, simply type
$ git checkout dirty
We touched upon this command in an earlier chapter, when discussing loading old states. At last we can
tell the whole story: the files change to the requested state, but we must leave the master branch. Any
commits made from now on take your files down a different road, which can be named later.
In other words, after checking out an old state, Git automatically puts you in a new, unnamed branch,
which can be named and saved with git checkout -b.
14
Next, work on Part II, committing your changes along the way. To err is human, and often youll want to
go back and fix something in Part I. If youre lucky, or very good, you can skip these lines.
$
$
$
$
#
#
#
#
Go back to Part I.
Fix Part I.
Go back to Part II.
Merge in those fixes.
# Go back to Part I.
# Maybe youre supposed to run something when the
# current working directory is officially ready.
15
Now youre in the master branch again, with Part II in the working directory.
Its easy to extend this trick for any number of parts. Its also easy to branch off retroactively: suppose
you belatedly realize you should have created a branch several commits ago. Then type:
$ git branch -m master part2
$ git checkout SHA1 -b master
The master branch now contains just Part I, and the part2 branch contains the rest.
Next, work on anything: fix bugs, add features, add temporary code, and so forth, committing often along
the way. Then:
$ git checkout sanitized
$ git cherry-pick SHA1_HASH
applies a given commit to the "sanitized" branch. With appropriate cherry-picks you can construct a
branch that contains only permanent code, and has related commits grouped together.
There is always a branch named "master", and you start here by default. Some advocate leaving the
"master" branch untouched and creating new branches for your own edits.
The -d and -m options allow you to delete and move (rename) branches. See git help branch.
16
This saves the current state in a temporary location (a stash) and restores the previous state. Your
working directory appears exactly as it was before you started editing, and you can fix bugs, pull in
upstream changes, and so on. When you want to go back to the stashed state, type:
$ git stash apply
You can have multiple stashes, and manipulate them in various ways. See git help stash. As you may
have guessed, Git maintains branches behind the scenes to perform this magic trick.
17
18
to change the last message. Realized you forgot to add a file? Run git add to add it, and then run the
above command.
Want to include a few more edits in that last commit? Then make those edits and run:
$ git commit --amend -a
and the last 10 commits will appear in your favourite $EDITOR. A sample excerpt:
pick 5c6eb73 Added repo.or.cz link
pick a311a64 Reordered analogies in "Work How You Want"
pick 100834f Added push target to Makefile
19
Replace "pick" with "squash" to merge a commit with the previous one.
Otherwise, run:
$ git rebase --continue
So commit early and commit often: you can easily tidy up later with rebase.
20
commit refs/heads/master
committer Bob <[email protected]> Tue, 14 Mar 2000 01:59:26 -0800
data <<EOT
Replace printf() with write().
EOT
M 100644 inline hello.c
data <<EOT
#include <unistd.h>
21
int main() {
write(1, "Hello, world!\n", 14);
return 0;
}
EOT
The git fast-export command converts any git 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, these commands can send repositories of text files over text-only channels.
Git checks out a state halfway in between. Test the feature, and if its still broken:
$ git bisect bad
If not, replace "bad" with "good". Git again transports you to a state halfway between the known good
and bad versions, narrowing down the possibilities. After a few iterations, this binary search will lead
you to the commit that caused the trouble. Once youve finished your investigation, return to your
original state by typing:
$ git bisect reset
22
Git uses the return value of the given command, typically a one-off script, to decide whether a change is
good or bad: the command should exit with code 0 when good, 125 when the change should be skipped,
and anything else between 1 and 127 if it is bad. A negative return value aborts the bisect.
You can do much more: the help page explains how to visualize bisects, examine or replay the bisect log,
and eliminate known innocent changes for a speedier search.
which annotates every line in the given file showing who last changed it, and when. Unlike many other
version control systems, this operation works offline, reading only from local disk.
23
24
6.1. Who Am I?
Every commit has an author name and email, which is shown by git log. By default, Git uses system
settings to populate these fields. To set them explicitly, type:
$ git config --global user.name "John Doe"
$ git config --global user.email [email protected]
Omit the global flag to set these options only for the current repository.
For older versions of Git, the copy command fails and you should run:
$ chmod a+x hooks/post-update
Now you can publish your latest edits via SSH from any clone:
$ git push web.server:/path/to/proj.git master
25
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:
$ git pull somefile
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:
$ git bundle create somefile HEAD ^COMMON_SHA1
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, type:
$ git tag -f lastbundle HEAD
26
outputs a patch which can be pasted into an email for discussion. In a Git repository, type:
$ git apply < FILE
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 START_COMMIT..END_COMMIT
This applies the incoming patch and also creates a commit, including information such as the author.
With a browser email client, you may need to click a button to see the email in its raw original form
before saving the patch to a file.
There are slight differences for mbox-based email clients, but if you use one of these, youre probably
the sort of person who can figure them out easily without reading tutorials!
27
The branch.master.merge option specifies the default remote branch in a git pull. During the initial
clone, it is set to the current branch of the source repository, so even if the HEAD of the source
repository subsequently moves to a different branch, a later pull will faithfully follow the original branch.
This option only applies to the repository we first cloned from, which is recorded in the option
branch.master.remote. If we pull in from other repositories we must explicitly state which branch
we want:
$ git pull ANOTHER_URL master
The above explains why some of our earlier push and pull examples had no arguments.
These represent branches and the HEAD of the remote repository, and can be used in regular Git
commands. For example, suppose you have made many commits, and wish to compare against the last
fetched version. You could search through the logs for the appropriate SHA1 hash, but its much easier to
type:
$ git diff origin/HEAD
28
Now we have merged in a branch from the second repository, and we have easy access to all branches of
all repositories:
$ git diff origin/experimental^ other/some_branch~5
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:
$ git fetch
$ git fetch other
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. By the way, behind the scenes, a pull is
simply a fetch followed by git merge; recall the latter merges a given commit into the working directory.
Usually we pull because we want to merge after a fetch; this situation is a notable exception.
See git help remote for how to remove remote repositories, ignore certain branches, and more.
6.8. My Preferences
For my projects, I like contributors to prepare Git repositories 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.
29
30
Git will look at the files in the current directory and work out the details by itself. Instead of the second
add command, run git commit -a if you also intend to commit at this time. See git help ignore for
how to specify files that should be ignored.
You can perform the above in a single pass with:
$ git ls-files -d -m -o -z | xargs -0 git update-index --add --remove
The -z and -0 options prevent ill side-effects from filenames containing strange characters. As this
command adds ignored files, you may want to use the -x or -X option.
31
to commit precisely the changes you selected (the staged changes). Make sure you omit the -a option,
otherwise Git will commit all the edits.
What if youve edited many files in many places? Reviewing each change one by one becomes
frustratingly mind-numbing. In this case, use git add -i, whose interface is less straightforward, but more
flexible. With a few keystrokes, you can stage or unstage several files at a time, or review and select
changes in particular files only. Alternatively, run git commit --interactive which automatically commits
after youre done.
32
But suppose you never took it down? Dont 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
7.5. HEAD-hunting
Perhaps ORIG_HEAD isnt enough. Perhaps youve just realized you made a monumental mistake and
you need to go back to an ancient commit in a long-forgotten branch.
By default, Git keeps a commit for at least two weeks, even if you ordered Git to destroy the branch
containing it. The trouble is finding the appropriate hash. You could look at all the hash values in
.git/objects and use trial and error to find the one you want. But theres a much easier way.
Git records every hash of a commit it computes in .git/logs. The subdirectory refs contains the
history of all activity on all branches, while the file HEAD shows every hash value it has ever taken. The
latter can be used to find hashes of commits on branches that have been accidentally lopped off.
The reflog command provides a friendly interface to these log files. Try
$ git reflog
See the Specifying Revisions section of git help rev-parse for more.
33
means a deleted commit will only be permanently lost once 30 days have passed and git gc is run.
You may also wish to disable automatic invocations of git gc:
$ git conifg gc.auto 0
in which case commits will only be deleted when you run git gc manually.
Another is to print the current branch in the prompt, or window title. Invoking
$ git symbolic-ref HEAD
shows the current branch name. In practice, you most likely want to remove the "refs/heads/" and ignore
errors:
$ git symbolic-ref HEAD 2> /dev/null | cut -b 12-
The contrib subdirectory is a treasure trove of tools built on Git. In time, some of them may be
promoted to official commands. On Debian and Ubuntu, this directory lives at
/usr/share/doc/git-core/contrib.
One popular resident is workdir/git-new-workdir. Via clever symlinking, this script creates a new
working directory whose history is shared with the original respository:
$ git-new-workdir an/existing/repo new/directory
34
On the other hand, if you specify particular paths for checkout, then there are no safety checks. The
supplied paths are quietly overwritten. Take care if you use checkout in this manner.
Reset: Reset also fails in the presence of uncommitted changes. To force it through, run:
$ git reset --hard [COMMIT]
Branch: Deleting branches fails if this causes changes to be lost. To force a deletion, type:
$ git branch -D BRANCH
# instead of -d
Similarly, attempting to overwrite a branch via a move fails if data loss would ensue. To force a branch
move, type:
$ git branch -M [SOURCE] TARGET
# instead of -m
Unlike checkout and reset, these two commands defer data destruction. The changes are still stored in the
.git subdirectory, and can be retrieved by recovering the appropriate hash from .git/logs (see
"HEAD-hunting" above). By default, they will be kept for at least two weeks.
Clean: Some git commands refuse to proceed because theyre worried about clobbering untracked files.
If youre certain that all untracked files and directories are expendable, then delete them mercilessly with:
$ git clean -f -d
35
Now Git aborts a commit if useless whitespace or unresolved merge conflicts are detected.
For this guide, I eventually added the following to the beginning of the pre-commit hook to guard
against absent-mindedness:
if git ls-files -o | grep \.txt$; then
echo FAIL! Untracked .txt files.
exit 1
fi
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.
36
8.1. Invisibility
How can Git be so unobtrusive? Aside from occasional commits and merges, you can work as if you
were unaware that version control exists. That is, until you need it, and thats when youre glad Git was
watching over you the whole time.
Other version control systems dont let you forget about them. Permissions of files may be read-only
unless you explicitly tell the server which files you intend to edit. The central server might be keeping
track of whos checked out which code, and when. When the network goes down, youll soon suffer.
Developers constantly struggle with virtual red tape and bureaucracy.
The secret is the .git directory in your working directory. Git keeps the history of your project here.
The initial "." stops it showing up in ls listings. Except when youre pushing and pulling changes, all
version control operations operate within this directory.
You have total control over the fate of your files because Git doesnt care what you do to them. Git can
easily recreate a saved state from .git at any time.
8.2. Integrity
Most people associate cryptography with keeping information secret, but another equally important goal
is keeping information safe. Proper use of cryptographic hash functions can prevent accidental or
malicious data corruption.
A SHA1 hash can be thought of as a unique 160-bit ID number for every string of bytes youll encounter
in your life. Actually more than that: every string of bytes that any human will ever use over many
lifetimes.
As a SHA1 hash is itself a string of bytes, we can hash strings of bytes containing other hashes. This
simple observation is surprisingly useful: look up hash chains. Well later see how Git uses it to
efficiently guarantee data integrity.
37
8.3. Intelligence
How does Git know you renamed a file, even though you never mentioned the fact explicitly? Sure, you
may have run git mv, but that is exactly the same as a git rm followed by a git add.
Git heuristically ferrets out renames and copies between successive versions. In fact, it can detect chunks
of code being moved or copied around between files! Though it cannot cover all cases, it does a decent
job, and this feature is always improving. If it fails to work for you, try options enabling more expensive
copy detection, and consider upgrading.
8.4. Indexing
For every tracked file, Git records information such as its size, creation time and last modification time in
a file known as the index. To determine whether a file has changed, Git compares its current stats with
that held the index. If they match, then Git can skip reading the file again.
Since stat calls are considerably faster than file reads, if you only edit a few files, Git can update its state
in almost no time.
38
8.7.1. Blobs
First, a magic trick. Pick a filename, any filename. In an empty directory:
$
$
$
$
Git is content-addressable: files are not stored according to their filename, but rather by the hash of the
data they contain, in a file we call a blob object. We can think of the hash as a unique ID for a files
contents, so in a sense we are addressing files by their content. The initial "blob 6" is merely a header
consisting of the object type and its length in bytes; it simplifies internal bookkeeping.
Thus I could easily predict what you would see. The files name is irrelevant: only the data inside is used
to construct the blob object.
You may be wondering what happens to identical files. Try adding copies of your file, with any filenames
whatsoever. The contents of .git/objects stay the same no matter how many you add. Git only stores
the data once.
By the way, the files within .git/objects are compressed with zlib so you should not stare at them
directly. Filter them through zpipe -d (http://www.zlib.net/zpipe.c), or type:
$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d
39
8.7.2. Trees
But where are the filenames? They must be stored somewhere at some stage. Git gets around to the
filenames during a commit:
$ git commit # Type some message.
$ find .git/objects -type f
You should now see 3 objects. This time I cannot tell you what the 2 new files are, as it partly depends on
the filename you picked. Well proceed assuming you chose "rose". If you didnt, you can rewrite history
to make it look like you did:
$ git filter-branch --tree-filter mv YOUR_FILENAME rose
$ find .git/objects -type f
Hash verification is trickier via cat-file because its output contains more than the raw uncompressed
object file.
This file is a tree object: a list of tuples consisting of a file type, a filename, and a hash. In our example,
the file type is "100644", which means "rose" is a normal file, and the hash is the blob object that
contains the contents of "rose". Other possible file types are executables, symlinks or directories. In the
last case, the hash points to a tree object.
If you ran filter-branch, youll have old objects you no longer need. Although they will be jettisoned
automatically once the grace period expires, well delete them now to make our toy example easier to
follow:
$ rm -r .git/refs/original
$ git reflog expire --expire=now --all
$ git prune
40
8.7.3. Commits
Weve 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 created. To match what we have here, well have to tweak it a
little:
$ git commit --amend -m Shakespeare # Change the commit message.
$ git filter-branch --env-filter export
GIT_AUTHOR_DATE="Fri 13 Feb 2009 15:31:30 -0800"
GIT_AUTHOR_NAME="Alice"
GIT_AUTHOR_EMAIL="[email protected]"
GIT_COMMITTER_DATE="Fri, 13 Feb 2009 15:31:30 -0800"
GIT_COMMITTER_NAME="Bob"
GIT_COMMITTER_EMAIL="[email protected]" # Rig timestamps and authors.
$ find .git/objects -type f
41
42
Cygwin (http://cygwin.com/), a Linux-like environment for Windows, contains a Windows port of Git
(http://cygwin.com/packages/git/).
43
44
45
This could happen if say filesystem trouble prevented the lockfile from being deleted in a previous Git
command. In this case the solution is to delete the lockfile and try again, after ensuring no Git commands
are still running.
The push command ignores the state of the working directory of the target repository. To prevent
confusion, push only into bare repositories, or setup a dedicated push-only branch.
46
and so on for each text file. You can review your work incrementally:
$ make LANG=tlh
$ firefox book.html
Commit your changes often, then let me know when theyre ready. GitHub.com has an interface that
facilitates this: fork the "gitmagic" project, push your changes, then ask me to merge.
I like to have translations follow the above scheme so my scripts can produce HTML and PDF versions.
Also, it conveniently keeps all the translations in the official repository. But please do whatever suits you
best: for example, the Chinese translators used Google Docs. Im happy as long as your work enables
more people to access my work.
47