Commits

Anonymous committed 41dc7e0 Merge

Merge branch 'master' of git://repo.or.cz/git/fastimport

* 'master' of git://repo.or.cz/git/fastimport: (81 commits)
S_IFLNK != 0140000
Don't do non-fastforward updates in fast-import.
Support RFC 2822 date parsing in fast-import.
Minor fast-import documentation corrections.
Remove unnecessary null pointer checks in fast-import.
Correct fast-import timezone documentation.
Correct minor style issue in fast-import.
Correct compiler warnings in fast-import.
Remove --branch-log from fast-import.
Initial draft of fast-import documentation.
Don't support shell-quoted refnames in fast-import.
Reduce memory usage of fast-import.
Include checkpoint command in the BNF.
Accept 'inline' file data in fast-import commit structure.
Reduce value duplication in t9300-fast-import.
Create test case for fast-import.
Support delimited data regions in fast-import.
Remove unnecessary options from fast-import.
Use fixed-size integers when writing out the index in fast-import.
Always use struct pack_header for pack header in fast-import.
...

Comments (0)

Files changed (5)

 git-diff-stages
 git-diff-tree
 git-describe
+git-fast-import
 git-fetch
 git-fetch-pack
 git-findtags

Documentation/git-fast-import.txt

+git-fast-import(1)
+==================
+
+NAME
+----
+git-fast-import - Backend for fast Git data importers.
+
+
+SYNOPSIS
+--------
+frontend | 'git-fast-import' [options]
+
+DESCRIPTION
+-----------
+This program is usually not what the end user wants to run directly.
+Most end users want to use one of the existing frontend programs,
+which parses a specific type of foreign source and feeds the contents
+stored there to git-fast-import (gfi).
+
+gfi reads a mixed command/data stream from standard input and
+writes one or more packfiles directly into the current repository.
+When EOF is received on standard input, fast import writes out
+updated branch and tag refs, fully updating the current repository
+with the newly imported data.
+
+The gfi backend itself can import into an empty repository (one that
+has already been initialized by gitlink:git-init[1]) or incrementally
+update an existing populated repository.  Whether or not incremental
+imports are supported from a particular foreign source depends on
+the frontend program in use.
+
+
+OPTIONS
+-------
+--date-format=<fmt>::
+	Specify the type of dates the frontend will supply to
+	gfi within `author`, `committer` and `tagger` commands.
+	See ``Date Formats'' below for details about which formats
+	are supported, and their syntax.
+
+--force::
+	Force updating modified existing branches, even if doing
+	so would cause commits to be lost (as the new commit does
+	not contain the old commit).
+
+--max-pack-size=<n>::
+	Maximum size of each output packfile, expressed in MiB.
+	The default is 4096 (4 GiB) as that is the maximum allowed
+	packfile size (due to file format limitations). Some
+	importers may wish to lower this, such as to ensure the
+	resulting packfiles fit on CDs.
+
+--depth=<n>::
+	Maximum delta depth, for blob and tree deltification.
+	Default is 10.
+
+--active-branches=<n>::
+	Maximum number of branches to maintain active at once.
+	See ``Memory Utilization'' below for details.  Default is 5.
+
+--export-marks=<file>::
+	Dumps the internal marks table to <file> when complete.
+	Marks are written one per line as `:markid SHA-1`.
+	Frontends can use this file to validate imports after they
+	have been completed.
+
+Performance
+-----------
+The design of gfi allows it to import large projects in a minimum
+amount of memory usage and processing time.  Assuming the frontend
+is able to keep up with gfi and feed it a constant stream of data,
+import times for projects holding 10+ years of history and containing
+100,000+ individual commits are generally completed in just 1-2
+hours on quite modest (~$2,000 USD) hardware.
+
+Most bottlenecks appear to be in foreign source data access (the
+source just cannot extract revisions fast enough) or disk IO (gfi
+writes as fast as the disk will take the data).  Imports will run
+faster if the source data is stored on a different drive than the
+destination Git repository (due to less IO contention).
+
+
+Development Cost
+----------------
+A typical frontend for gfi tends to weigh in at approximately 200
+lines of Perl/Python/Ruby code.  Most developers have been able to
+create working importers in just a couple of hours, even though it
+is their first exposure to gfi, and sometimes even to Git.  This is
+an ideal situation, given that most conversion tools are throw-away
+(use once, and never look back).
+
+
+Parallel Operation
+------------------
+Like `git-push` or `git-fetch`, imports handled by gfi are safe to
+run alongside parallel `git repack -a -d` or `git gc` invocations,
+or any other Git operation (including `git prune`, as loose objects
+are never used by gfi).
+
+gfi does not lock the branch or tag refs it is actively importing.
+After the import, during its ref update phase, gfi tests each
+existing branch ref to verify the update will be a fast-forward
+update (the commit stored in the ref is contained in the new
+history of the commit to be written).  If the update is not a
+fast-forward update, gfi will skip updating that ref and instead
+prints a warning message.  gfi will always attempt to update all
+branch refs, and does not stop on the first failure.
+
+Branch updates can be forced with `--force`, but its recommended that
+this only be used on an otherwise quiet repository.  Using `--force`
+is not necessary for an initial import into an empty repository.
+
+
+Technical Discussion
+--------------------
+gfi tracks a set of branches in memory.  Any branch can be created
+or modified at any point during the import process by sending a
+`commit` command on the input stream.  This design allows a frontend
+program to process an unlimited number of branches simultaneously,
+generating commits in the order they are available from the source
+data.  It also simplifies the frontend programs considerably.
+
+gfi does not use or alter the current working directory, or any
+file within it.  (It does however update the current Git repository,
+as referenced by `GIT_DIR`.)  Therefore an import frontend may use
+the working directory for its own purposes, such as extracting file
+revisions from the foreign source.  This ignorance of the working
+directory also allows gfi to run very quickly, as it does not
+need to perform any costly file update operations when switching
+between branches.
+
+Input Format
+------------
+With the exception of raw file data (which Git does not interpret)
+the gfi input format is text (ASCII) based.  This text based
+format simplifies development and debugging of frontend programs,
+especially when a higher level language such as Perl, Python or
+Ruby is being used.
+
+gfi is very strict about its input.  Where we say SP below we mean
+*exactly* one space.  Likewise LF means one (and only one) linefeed.
+Supplying additional whitespace characters will cause unexpected
+results, such as branch names or file names with leading or trailing
+spaces in their name, or early termination of gfi when it encounters
+unexpected input.
+
+Date Formats
+~~~~~~~~~~~~
+The following date formats are supported.  A frontend should select
+the format it will use for this import by passing the format name
+in the `--date-format=<fmt>` command line option.
+
+`raw`::
+	This is the Git native format and is `<time> SP <tz>`.
+	It is also gfi's default format, if `--date-format` was
+	not specified.
++
+The time of the event is specified by `<time>` as the number of
+seconds since the UNIX epoch (midnight, Jan 1, 1970, UTC) and is
+written as an ASCII decimal integer.
++
+The timezone is specified by `<tz>` as a positive or negative offset
+from UTC.  For example EST (which is typically 5 hours behind GMT)
+would be expressed in `<tz>` by ``-0500'' while GMT is ``+0000''.
++
+If the timezone is not available in the source material, use
+``+0000'', or the most common local timezone.  For example many
+organizations have a CVS repository which has only ever been accessed
+by users who are located in the same location and timezone.  In this
+case the user's timezone can be easily assumed.
++
+Unlike the `rfc2822` format, this format is very strict.  Any
+variation in formatting will cause gfi to reject the value.
+
+`rfc2822`::
+	This is the standard email format as described by RFC 2822.
++
+An example value is ``Tue Feb 6 11:22:18 2007 -0500''.  The Git
+parser is accurate, but a little on the lenient side.  Its the
+same parser used by gitlink:git-am[1] when applying patches
+received from email.
++
+Some malformed strings may be accepted as valid dates.  In some of
+these cases Git will still be able to obtain the correct date from
+the malformed string.  There are also some types of malformed
+strings which Git will parse wrong, and yet consider valid.
+Seriously malformed strings will be rejected.
++
+If the source material is formatted in RFC 2822 style dates,
+the frontend should let gfi handle the parsing and conversion
+(rather than attempting to do it itself) as the Git parser has
+been well tested in the wild.
++
+Frontends should prefer the `raw` format if the source material
+is already in UNIX-epoch format, or is easily convertible to
+that format, as there is no ambiguity in parsing.
+
+`now`::
+	Always use the current time and timezone.  The literal
+	`now` must always be supplied for `<when>`.
++
+This is a toy format.  The current time and timezone of this system
+is always copied into the identity string at the time it is being
+created by gfi.  There is no way to specify a different time or
+timezone.
++
+This particular format is supplied as its short to implement and
+may be useful to a process that wants to create a new commit
+right now, without needing to use a working directory or
+gitlink:git-update-index[1].
++
+If separate `author` and `committer` commands are used in a `commit`
+the timestamps may not match, as the system clock will be polled
+twice (once for each command).  The only way to ensure that both
+author and committer identity information has the same timestamp
+is to omit `author` (thus copying from `committer`) or to use a
+date format other than `now`.
+
+Commands
+~~~~~~~~
+gfi accepts several commands to update the current repository
+and control the current import process.  More detailed discussion
+(with examples) of each command follows later.
+
+`commit`::
+	Creates a new branch or updates an existing branch by
+	creating a new commit and updating the branch to point at
+	the newly created commit.
+
+`tag`::
+	Creates an annotated tag object from an existing commit or
+	branch.  Lightweight tags are not supported by this command,
+	as they are not recommended for recording meaningful points
+	in time.
+
+`reset`::
+	Reset an existing branch (or a new branch) to a specific
+	revision.  This command must be used to change a branch to
+	a specific revision without making a commit on it.
+
+`blob`::
+	Convert raw file data into a blob, for future use in a
+	`commit` command.  This command is optional and is not
+	needed to perform an import.
+
+`checkpoint`::
+	Forces gfi to close the current packfile, generate its
+	unique SHA-1 checksum and index, and start a new packfile.
+	This command is optional and is not needed to perform
+	an import.
+
+`commit`
+~~~~~~~~
+Create or update a branch with a new commit, recording one logical
+change to the project.
+
+....
+	'commit' SP <ref> LF
+	mark?
+	('author' SP <name> SP LT <email> GT SP <when> LF)?
+	'committer' SP <name> SP LT <email> GT SP <when> LF
+	data
+	('from' SP <committish> LF)?
+	('merge' SP <committish> LF)?
+	(filemodify | filedelete)*
+	LF
+....
+
+where `<ref>` is the name of the branch to make the commit on.
+Typically branch names are prefixed with `refs/heads/` in
+Git, so importing the CVS branch symbol `RELENG-1_0` would use
+`refs/heads/RELENG-1_0` for the value of `<ref>`.  The value of
+`<ref>` must be a valid refname in Git.  As `LF` is not valid in
+a Git refname, no quoting or escaping syntax is supported here.
+
+A `mark` command may optionally appear, requesting gfi to save a
+reference to the newly created commit for future use by the frontend
+(see below for format).  It is very common for frontends to mark
+every commit they create, thereby allowing future branch creation
+from any imported commit.
+
+The `data` command following `committer` must supply the commit
+message (see below for `data` command syntax).  To import an empty
+commit message use a 0 length data.  Commit messages are free-form
+and are not interpreted by Git.  Currently they must be encoded in
+UTF-8, as gfi does not permit other encodings to be specified.
+
+Zero or more `filemodify` and `filedelete` commands may be
+included to update the contents of the branch prior to the commit.
+These commands can be supplied in any order, gfi is not sensitive
+to pathname or operation ordering.
+
+`author`
+^^^^^^^^
+An `author` command may optionally appear, if the author information
+might differ from the committer information.  If `author` is omitted
+then gfi will automatically use the committer's information for
+the author portion of the commit.  See below for a description of
+the fields in `author`, as they are identical to `committer`.
+
+`committer`
+^^^^^^^^^^^
+The `committer` command indicates who made this commit, and when
+they made it.
+
+Here `<name>` is the person's display name (for example
+``Com M Itter'') and `<email>` is the person's email address
+(``cm@example.com'').  `LT` and `GT` are the literal less-than (\x3c)
+and greater-than (\x3e) symbols.  These are required to delimit
+the email address from the other fields in the line.  Note that
+`<name>` is free-form and may contain any sequence of bytes, except
+`LT` and `LF`.  It is typically UTF-8 encoded.
+
+The time of the change is specified by `<when>` using the date format
+that was selected by the `--date-format=<fmt>` command line option.
+See ``Date Formats'' above for the set of supported formats, and
+their syntax.
+
+`from`
+^^^^^^
+Only valid for the first commit made on this branch by this
+gfi process.  The `from` command is used to specify the commit
+to initialize this branch from.  This revision will be the first
+ancestor of the new commit.
+
+Omitting the `from` command in the first commit of a new branch will
+cause gfi to create that commit with no ancestor. This tends to be
+desired only for the initial commit of a project.  Omitting the
+`from` command on existing branches is required, as the current
+commit on that branch is automatically assumed to be the first
+ancestor of the new commit.
+
+As `LF` is not valid in a Git refname or SHA-1 expression, no
+quoting or escaping syntax is supported within `<committish>`.
+
+Here `<committish>` is any of the following:
+
+* The name of an existing branch already in gfi's internal branch
+  table.  If gfi doesn't know the name, its treated as a SHA-1
+  expression.
+
+* A mark reference, `:<idnum>`, where `<idnum>` is the mark number.
++
+The reason gfi uses `:` to denote a mark reference is this character
+is not legal in a Git branch name.  The leading `:` makes it easy
+to distingush between the mark 42 (`:42`) and the branch 42 (`42`
+or `refs/heads/42`), or an abbreviated SHA-1 which happened to
+consist only of base-10 digits.
++
+Marks must be declared (via `mark`) before they can be used.
+
+* A complete 40 byte or abbreviated commit SHA-1 in hex.
+
+* Any valid Git SHA-1 expression that resolves to a commit.  See
+  ``SPECIFYING REVISIONS'' in gitlink:git-rev-parse[1] for details.
+
+The special case of restarting an incremental import from the
+current branch value should be written as:
+----
+	from refs/heads/branch^0
+----
+The `^0` suffix is necessary as gfi does not permit a branch to
+start from itself, and the branch is created in memory before the
+`from` command is even read from the input.  Adding `^0` will force
+gfi to resolve the commit through Git's revision parsing library,
+rather than its internal branch table, thereby loading in the
+existing value of the branch.
+
+`merge`
+^^^^^^^
+Includes one additional ancestor commit, and makes the current
+commit a merge commit.  An unlimited number of `merge` commands per
+commit are permitted by gfi, thereby establishing an n-way merge.
+However Git's other tools never create commits with more than 15
+additional ancestors (forming a 16-way merge).  For this reason
+it is suggested that frontends do not use more than 15 `merge`
+commands per commit.
+
+Here `<committish>` is any of the commit specification expressions
+also accepted by `from` (see above).
+
+`filemodify`
+^^^^^^^^^^^^
+Included in a `commit` command to add a new file or change the
+content of an existing file.  This command has two different means
+of specifying the content of the file.
+
+External data format::
+	The data content for the file was already supplied by a prior
+	`blob` command.  The frontend just needs to connect it.
++
+....
+	'M' SP <mode> SP <dataref> SP <path> LF
+....
++
+Here `<dataref>` can be either a mark reference (`:<idnum>`)
+set by a prior `blob` command, or a full 40-byte SHA-1 of an
+existing Git blob object.
+
+Inline data format::
+	The data content for the file has not been supplied yet.
+	The frontend wants to supply it as part of this modify
+	command.
++
+....
+	'M' SP <mode> SP 'inline' SP <path> LF
+	data
+....
++
+See below for a detailed description of the `data` command.
+
+In both formats `<mode>` is the type of file entry, specified
+in octal.  Git only supports the following modes:
+
+* `100644` or `644`: A normal (not-executable) file.  The majority
+  of files in most projects use this mode.  If in doubt, this is
+  what you want.
+* `100755` or `755`: A normal, but executable, file.
+* `120000`: A symlink, the content of the file will be the link target.
+
+In both formats `<path>` is the complete path of the file to be added
+(if not already existing) or modified (if already existing).
+
+A `<path>` string must use UNIX-style directory seperators (forward
+slash `/`), may contain any byte other than `LF`, and must not
+start with double quote (`"`).
+
+If an `LF` or double quote must be encoded into `<path>` shell-style
+quoting should be used, e.g. `"path/with\n and \" in it"`.
+
+The value of `<path>` must be in canoncial form. That is it must not:
+
+* contain an empty directory component (e.g. `foo//bar` is invalid),
+* end with a directory seperator (e.g. `foo/` is invalid),
+* start with a directory seperator (e.g. `/foo` is invalid),
+* contain the special component `.` or `..` (e.g. `foo/./bar` and
+  `foo/../bar` are invalid).
+
+It is recommended that `<path>` always be encoded using UTF-8.
+
+`filedelete`
+^^^^^^^^^^^^
+Included in a `commit` command to remove a file from the branch.
+If the file removal makes its directory empty, the directory will
+be automatically removed too.  This cascades up the tree until the
+first non-empty directory or the root is reached.
+
+....
+	'D' SP <path> LF
+....
+
+here `<path>` is the complete path of the file to be removed.
+See `filemodify` above for a detailed description of `<path>`.
+
+`mark`
+~~~~~~
+Arranges for gfi to save a reference to the current object, allowing
+the frontend to recall this object at a future point in time, without
+knowing its SHA-1.  Here the current object is the object creation
+command the `mark` command appears within.  This can be `commit`,
+`tag`, and `blob`, but `commit` is the most common usage.
+
+....
+	'mark' SP ':' <idnum> LF
+....
+
+where `<idnum>` is the number assigned by the frontend to this mark.
+The value of `<idnum>` is expressed as an ASCII decimal integer.
+The value 0 is reserved and cannot be used as
+a mark.  Only values greater than or equal to 1 may be used as marks.
+
+New marks are created automatically.  Existing marks can be moved
+to another object simply by reusing the same `<idnum>` in another
+`mark` command.
+
+`tag`
+~~~~~
+Creates an annotated tag referring to a specific commit.  To create
+lightweight (non-annotated) tags see the `reset` command below.
+
+....
+	'tag' SP <name> LF
+	'from' SP <committish> LF
+	'tagger' SP <name> SP LT <email> GT SP <when> LF
+	data
+	LF
+....
+
+where `<name>` is the name of the tag to create.
+
+Tag names are automatically prefixed with `refs/tags/` when stored
+in Git, so importing the CVS branch symbol `RELENG-1_0-FINAL` would
+use just `RELENG-1_0-FINAL` for `<name>`, and gfi will write the
+corresponding ref as `refs/tags/RELENG-1_0-FINAL`.
+
+The value of `<name>` must be a valid refname in Git and therefore
+may contain forward slashes.  As `LF` is not valid in a Git refname,
+no quoting or escaping syntax is supported here.
+
+The `from` command is the same as in the `commit` command; see
+above for details.
+
+The `tagger` command uses the same format as `committer` within
+`commit`; again see above for details.
+
+The `data` command following `tagger` must supply the annotated tag
+message (see below for `data` command syntax).  To import an empty
+tag message use a 0 length data.  Tag messages are free-form and are
+not interpreted by Git.  Currently they must be encoded in UTF-8,
+as gfi does not permit other encodings to be specified.
+
+Signing annotated tags during import from within gfi is not
+supported.  Trying to include your own PGP/GPG signature is not
+recommended, as the frontend does not (easily) have access to the
+complete set of bytes which normally goes into such a signature.
+If signing is required, create lightweight tags from within gfi with
+`reset`, then create the annotated versions of those tags offline
+with the standard gitlink:git-tag[1] process.
+
+`reset`
+~~~~~~~
+Creates (or recreates) the named branch, optionally starting from
+a specific revision.  The reset command allows a frontend to issue
+a new `from` command for an existing branch, or to create a new
+branch from an existing commit without creating a new commit.
+
+....
+	'reset' SP <ref> LF
+	('from' SP <committish> LF)?
+	LF
+....
+
+For a detailed description of `<ref>` and `<committish>` see above
+under `commit` and `from`.
+
+The `reset` command can also be used to create lightweight
+(non-annotated) tags.  For example:
+
+====
+	reset refs/tags/938
+	from :938
+====
+
+would create the lightweight tag `refs/tags/938` referring to
+whatever commit mark `:938` references.
+
+`blob`
+~~~~~~
+Requests writing one file revision to the packfile.  The revision
+is not connected to any commit; this connection must be formed in
+a subsequent `commit` command by referencing the blob through an
+assigned mark.
+
+....
+	'blob' LF
+	mark?
+	data
+....
+
+The mark command is optional here as some frontends have chosen
+to generate the Git SHA-1 for the blob on their own, and feed that
+directly to `commit`.  This is typically more work than its worth
+however, as marks are inexpensive to store and easy to use.
+
+`data`
+~~~~~~
+Supplies raw data (for use as blob/file content, commit messages, or
+annotated tag messages) to gfi.  Data can be supplied using an exact
+byte count or delimited with a terminating line.  Real frontends
+intended for production-quality conversions should always use the
+exact byte count format, as it is more robust and performs better.
+The delimited format is intended primarily for testing gfi.
+
+Exact byte count format::
+	The frontend must specify the number of bytes of data.
++
+....
+	'data' SP <count> LF
+	<raw> LF
+....
++
+where `<count>` is the exact number of bytes appearing within
+`<raw>`.  The value of `<count>` is expressed as an ASCII decimal
+integer.  The `LF` on either side of `<raw>` is not
+included in `<count>` and will not be included in the imported data.
+
+Delimited format::
+	A delimiter string is used to mark the end of the data.
+	gfi will compute the length by searching for the delimiter.
+	This format is primarly useful for testing and is not
+	recommended for real data.
++
+....
+	'data' SP '<<' <delim> LF
+	<raw> LF
+	<delim> LF
+....
++
+where `<delim>` is the chosen delimiter string.  The string `<delim>`
+must not appear on a line by itself within `<raw>`, as otherwise
+gfi will think the data ends earlier than it really does.  The `LF`
+immediately trailing `<raw>` is part of `<raw>`.  This is one of
+the limitations of the delimited format, it is impossible to supply
+a data chunk which does not have an LF as its last byte.
+
+`checkpoint`
+~~~~~~~~~~~~
+Forces gfi to close the current packfile and start a new one.
+As this requires a significant amount of CPU time and disk IO
+(to compute the overall pack SHA-1 checksum and generate the
+corresponding index file) it can easily take several minutes for
+a single `checkpoint` command to complete.
+
+....
+	'checkpoint' LF
+	LF
+....
+
+Packfile Optimization
+---------------------
+When packing a blob gfi always attempts to deltify against the last
+blob written.  Unless specifically arranged for by the frontend,
+this will probably not be a prior version of the same file, so the
+generated delta will not be the smallest possible.  The resulting
+packfile will be compressed, but will not be optimal.
+
+Frontends which have efficient access to all revisions of a
+single file (for example reading an RCS/CVS ,v file) can choose
+to supply all revisions of that file as a sequence of consecutive
+`blob` commands.  This allows gfi to deltify the different file
+revisions against each other, saving space in the final packfile.
+Marks can be used to later identify individual file revisions during
+a sequence of `commit` commands.
+
+The packfile(s) created by gfi do not encourage good disk access
+patterns.  This is caused by gfi writing the data in the order
+it is received on standard input, while Git typically organizes
+data within packfiles to make the most recent (current tip) data
+appear before historical data.  Git also clusters commits together,
+speeding up revision traversal through better cache locality.
+
+For this reason it is strongly recommended that users repack the
+repository with `git repack -a -d` after gfi completes, allowing
+Git to reorganize the packfiles for faster data access.  If blob
+deltas are suboptimal (see above) then also adding the `-f` option
+to force recomputation of all deltas can significantly reduce the
+final packfile size (30-50% smaller can be quite typical).
+
+Memory Utilization
+------------------
+There are a number of factors which affect how much memory gfi
+requires to perform an import.  Like critical sections of core
+Git, gfi uses its own memory allocators to ammortize any overheads
+associated with malloc.  In practice gfi tends to ammoritize any
+malloc overheads to 0, due to its use of large block allocations.
+
+per object
+~~~~~~~~~~
+gfi maintains an in-memory structure for every object written in
+this execution.  On a 32 bit system the structure is 32 bytes,
+on a 64 bit system the structure is 40 bytes (due to the larger
+pointer sizes).  Objects in the table are not deallocated until
+gfi terminates.  Importing 2 million objects on a 32 bit system
+will require approximately 64 MiB of memory.
+
+The object table is actually a hashtable keyed on the object name
+(the unique SHA-1).  This storage configuration allows gfi to reuse
+an existing or already written object and avoid writing duplicates
+to the output packfile.  Duplicate blobs are surprisingly common
+in an import, typically due to branch merges in the source.
+
+per mark
+~~~~~~~~
+Marks are stored in a sparse array, using 1 pointer (4 bytes or 8
+bytes, depending on pointer size) per mark.  Although the array
+is sparse, frontends are still strongly encouraged to use marks
+between 1 and n, where n is the total number of marks required for
+this import.
+
+per branch
+~~~~~~~~~~
+Branches are classified as active and inactive.  The memory usage
+of the two classes is significantly different.
+
+Inactive branches are stored in a structure which uses 96 or 120
+bytes (32 bit or 64 bit systems, respectively), plus the length of
+the branch name (typically under 200 bytes), per branch.  gfi will
+easily handle as many as 10,000 inactive branches in under 2 MiB
+of memory.
+
+Active branches have the same overhead as inactive branches, but
+also contain copies of every tree that has been recently modified on
+that branch.  If subtree `include` has not been modified since the
+branch became active, its contents will not be loaded into memory,
+but if subtree `src` has been modified by a commit since the branch
+became active, then its contents will be loaded in memory.
+
+As active branches store metadata about the files contained on that
+branch, their in-memory storage size can grow to a considerable size
+(see below).
+
+gfi automatically moves active branches to inactive status based on
+a simple least-recently-used algorithm.  The LRU chain is updated on
+each `commit` command.  The maximum number of active branches can be
+increased or decreased on the command line with `--active-branches=`.
+
+per active tree
+~~~~~~~~~~~~~~~
+Trees (aka directories) use just 12 bytes of memory on top of the
+memory required for their entries (see ``per active file'' below).
+The cost of a tree is virtually 0, as its overhead ammortizes out
+over the individual file entries.
+
+per active file entry
+~~~~~~~~~~~~~~~~~~~~~
+Files (and pointers to subtrees) within active trees require 52 or 64
+bytes (32/64 bit platforms) per entry.  To conserve space, file and
+tree names are pooled in a common string table, allowing the filename
+``Makefile'' to use just 16 bytes (after including the string header
+overhead) no matter how many times it occurs within the project.
+
+The active branch LRU, when coupled with the filename string pool
+and lazy loading of subtrees, allows gfi to efficiently import
+projects with 2,000+ branches and 45,114+ files in a very limited
+memory footprint (less than 2.7 MiB per active branch).
+
+
+Author
+------
+Written by Shawn O. Pearce <spearce@spearce.org>.
+
+Documentation
+--------------
+Documentation by Shawn O. Pearce <spearce@spearce.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
 PROGRAMS = \
 	git-convert-objects$X git-fetch-pack$X git-fsck$X \
 	git-hash-object$X git-index-pack$X git-local-fetch$X \
+	git-fast-import$X \
 	git-merge-base$X \
 	git-daemon$X \
 	git-merge-index$X git-mktag$X git-mktree$X git-patch-id$X \
+/*
+Format of STDIN stream:
+
+  stream ::= cmd*;
+
+  cmd ::= new_blob
+        | new_commit
+        | new_tag
+        | reset_branch
+        | checkpoint
+        ;
+
+  new_blob ::= 'blob' lf
+	mark?
+    file_content;
+  file_content ::= data;
+
+  new_commit ::= 'commit' sp ref_str lf
+    mark?
+    ('author' sp name '<' email '>' when lf)?
+    'committer' sp name '<' email '>' when lf
+    commit_msg
+    ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
+    ('merge' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)*
+    file_change*
+    lf;
+  commit_msg ::= data;
+
+  file_change ::= file_del | file_obm | file_inm;
+  file_del ::= 'D' sp path_str lf;
+  file_obm ::= 'M' sp mode sp (hexsha1 | idnum) sp path_str lf;
+  file_inm ::= 'M' sp mode sp 'inline' sp path_str lf
+    data;
+
+  new_tag ::= 'tag' sp tag_str lf
+    'from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf
+	'tagger' sp name '<' email '>' when lf
+    tag_msg;
+  tag_msg ::= data;
+
+  reset_branch ::= 'reset' sp ref_str lf
+    ('from' sp (ref_str | hexsha1 | sha1exp_str | idnum) lf)?
+    lf;
+
+  checkpoint ::= 'checkpoint' lf
+    lf;
+
+     # note: the first idnum in a stream should be 1 and subsequent
+     # idnums should not have gaps between values as this will cause
+     # the stream parser to reserve space for the gapped values.  An
+	 # idnum can be updated in the future to a new object by issuing
+     # a new mark directive with the old idnum.
+	 #
+  mark ::= 'mark' sp idnum lf;
+  data ::= (delimited_data | exact_data)
+    lf;
+
+    # note: delim may be any string but must not contain lf.
+    # data_line may contain any data but must not be exactly
+    # delim.
+  delimited_data ::= 'data' sp '<<' delim lf
+    (data_line lf)*
+	delim lf;
+
+     # note: declen indicates the length of binary_data in bytes.
+     # declen does not include the lf preceeding the binary data.
+     #
+  exact_data ::= 'data' sp declen lf
+    binary_data;
+
+     # note: quoted strings are C-style quoting supporting \c for
+     # common escapes of 'c' (e..g \n, \t, \\, \") or \nnn where nnn
+	 # is the signed byte value in octal.  Note that the only
+     # characters which must actually be escaped to protect the
+     # stream formatting is: \, " and LF.  Otherwise these values
+	 # are UTF8.
+     #
+  ref_str     ::= ref;
+  sha1exp_str ::= sha1exp;
+  tag_str     ::= tag;
+  path_str    ::= path    | '"' quoted(path)    '"' ;
+  mode        ::= '100644' | '644'
+                | '100755' | '755'
+                | '120000'
+                ;
+
+  declen ::= # unsigned 32 bit value, ascii base10 notation;
+  bigint ::= # unsigned integer value, ascii base10 notation;
+  binary_data ::= # file content, not interpreted;
+
+  when         ::= raw_when | rfc2822_when;
+  raw_when     ::= ts sp tz;
+  rfc2822_when ::= # Valid RFC 2822 date and time;
+
+  sp ::= # ASCII space character;
+  lf ::= # ASCII newline (LF) character;
+
+     # note: a colon (':') must precede the numerical value assigned to
+	 # an idnum.  This is to distinguish it from a ref or tag name as
+     # GIT does not permit ':' in ref or tag strings.
+	 #
+  idnum   ::= ':' bigint;
+  path    ::= # GIT style file path, e.g. "a/b/c";
+  ref     ::= # GIT ref name, e.g. "refs/heads/MOZ_GECKO_EXPERIMENT";
+  tag     ::= # GIT tag name, e.g. "FIREFOX_1_5";
+  sha1exp ::= # Any valid GIT SHA1 expression;
+  hexsha1 ::= # SHA1 in hexadecimal format;
+
+     # note: name and email are UTF8 strings, however name must not
+	 # contain '<' or lf and email must not contain any of the
+     # following: '<', '>', lf.
+	 #
+  name  ::= # valid GIT author/committer name;
+  email ::= # valid GIT author/committer email;
+  ts    ::= # time since the epoch in seconds, ascii base10 notation;
+  tz    ::= # GIT style timezone;
+*/
+
+#include "builtin.h"
+#include "cache.h"
+#include "object.h"
+#include "blob.h"
+#include "tree.h"
+#include "commit.h"
+#include "delta.h"
+#include "pack.h"
+#include "refs.h"
+#include "csum-file.h"
+#include "strbuf.h"
+#include "quote.h"
+
+#define PACK_ID_BITS 16
+#define MAX_PACK_ID ((1<<PACK_ID_BITS)-1)
+
+struct object_entry
+{
+	struct object_entry *next;
+	uint32_t offset;
+	unsigned type : TYPE_BITS;
+	unsigned pack_id : PACK_ID_BITS;
+	unsigned char sha1[20];
+};
+
+struct object_entry_pool
+{
+	struct object_entry_pool *next_pool;
+	struct object_entry *next_free;
+	struct object_entry *end;
+	struct object_entry entries[FLEX_ARRAY]; /* more */
+};
+
+struct mark_set
+{
+	union {
+		struct object_entry *marked[1024];
+		struct mark_set *sets[1024];
+	} data;
+	unsigned int shift;
+};
+
+struct last_object
+{
+	void *data;
+	unsigned long len;
+	uint32_t offset;
+	unsigned int depth;
+	unsigned no_free:1;
+};
+
+struct mem_pool
+{
+	struct mem_pool *next_pool;
+	char *next_free;
+	char *end;
+	char space[FLEX_ARRAY]; /* more */
+};
+
+struct atom_str
+{
+	struct atom_str *next_atom;
+	unsigned short str_len;
+	char str_dat[FLEX_ARRAY]; /* more */
+};
+
+struct tree_content;
+struct tree_entry
+{
+	struct tree_content *tree;
+	struct atom_str* name;
+	struct tree_entry_ms
+	{
+		uint16_t mode;
+		unsigned char sha1[20];
+	} versions[2];
+};
+
+struct tree_content
+{
+	unsigned int entry_capacity; /* must match avail_tree_content */
+	unsigned int entry_count;
+	unsigned int delta_depth;
+	struct tree_entry *entries[FLEX_ARRAY]; /* more */
+};
+
+struct avail_tree_content
+{
+	unsigned int entry_capacity; /* must match tree_content */
+	struct avail_tree_content *next_avail;
+};
+
+struct branch
+{
+	struct branch *table_next_branch;
+	struct branch *active_next_branch;
+	const char *name;
+	struct tree_entry branch_tree;
+	uintmax_t last_commit;
+	unsigned int pack_id;
+	unsigned char sha1[20];
+};
+
+struct tag
+{
+	struct tag *next_tag;
+	const char *name;
+	unsigned int pack_id;
+	unsigned char sha1[20];
+};
+
+struct dbuf
+{
+	void *buffer;
+	size_t capacity;
+};
+
+struct hash_list
+{
+	struct hash_list *next;
+	unsigned char sha1[20];
+};
+
+typedef enum {
+	WHENSPEC_RAW = 1,
+	WHENSPEC_RFC2822,
+	WHENSPEC_NOW,
+} whenspec_type;
+
+/* Configured limits on output */
+static unsigned long max_depth = 10;
+static unsigned long max_packsize = (1LL << 32) - 1;
+static int force_update;
+
+/* Stats and misc. counters */
+static uintmax_t alloc_count;
+static uintmax_t marks_set_count;
+static uintmax_t object_count_by_type[1 << TYPE_BITS];
+static uintmax_t duplicate_count_by_type[1 << TYPE_BITS];
+static uintmax_t delta_count_by_type[1 << TYPE_BITS];
+static unsigned long object_count;
+static unsigned long branch_count;
+static unsigned long branch_load_count;
+static int failure;
+
+/* Memory pools */
+static size_t mem_pool_alloc = 2*1024*1024 - sizeof(struct mem_pool);
+static size_t total_allocd;
+static struct mem_pool *mem_pool;
+
+/* Atom management */
+static unsigned int atom_table_sz = 4451;
+static unsigned int atom_cnt;
+static struct atom_str **atom_table;
+
+/* The .pack file being generated */
+static unsigned int pack_id;
+static struct packed_git *pack_data;
+static struct packed_git **all_packs;
+static unsigned long pack_size;
+
+/* Table of objects we've written. */
+static unsigned int object_entry_alloc = 5000;
+static struct object_entry_pool *blocks;
+static struct object_entry *object_table[1 << 16];
+static struct mark_set *marks;
+static const char* mark_file;
+
+/* Our last blob */
+static struct last_object last_blob;
+
+/* Tree management */
+static unsigned int tree_entry_alloc = 1000;
+static void *avail_tree_entry;
+static unsigned int avail_tree_table_sz = 100;
+static struct avail_tree_content **avail_tree_table;
+static struct dbuf old_tree;
+static struct dbuf new_tree;
+
+/* Branch data */
+static unsigned long max_active_branches = 5;
+static unsigned long cur_active_branches;
+static unsigned long branch_table_sz = 1039;
+static struct branch **branch_table;
+static struct branch *active_branches;
+
+/* Tag data */
+static struct tag *first_tag;
+static struct tag *last_tag;
+
+/* Input stream parsing */
+static whenspec_type whenspec = WHENSPEC_RAW;
+static struct strbuf command_buf;
+static uintmax_t next_mark;
+static struct dbuf new_data;
+
+
+static void alloc_objects(unsigned int cnt)
+{
+	struct object_entry_pool *b;
+
+	b = xmalloc(sizeof(struct object_entry_pool)
+		+ cnt * sizeof(struct object_entry));
+	b->next_pool = blocks;
+	b->next_free = b->entries;
+	b->end = b->entries + cnt;
+	blocks = b;
+	alloc_count += cnt;
+}
+
+static struct object_entry *new_object(unsigned char *sha1)
+{
+	struct object_entry *e;
+
+	if (blocks->next_free == blocks->end)
+		alloc_objects(object_entry_alloc);
+
+	e = blocks->next_free++;
+	hashcpy(e->sha1, sha1);
+	return e;
+}
+
+static struct object_entry *find_object(unsigned char *sha1)
+{
+	unsigned int h = sha1[0] << 8 | sha1[1];
+	struct object_entry *e;
+	for (e = object_table[h]; e; e = e->next)
+		if (!hashcmp(sha1, e->sha1))
+			return e;
+	return NULL;
+}
+
+static struct object_entry *insert_object(unsigned char *sha1)
+{
+	unsigned int h = sha1[0] << 8 | sha1[1];
+	struct object_entry *e = object_table[h];
+	struct object_entry *p = NULL;
+
+	while (e) {
+		if (!hashcmp(sha1, e->sha1))
+			return e;
+		p = e;
+		e = e->next;
+	}
+
+	e = new_object(sha1);
+	e->next = NULL;
+	e->offset = 0;
+	if (p)
+		p->next = e;
+	else
+		object_table[h] = e;
+	return e;
+}
+
+static unsigned int hc_str(const char *s, size_t len)
+{
+	unsigned int r = 0;
+	while (len-- > 0)
+		r = r * 31 + *s++;
+	return r;
+}
+
+static void *pool_alloc(size_t len)
+{
+	struct mem_pool *p;
+	void *r;
+
+	for (p = mem_pool; p; p = p->next_pool)
+		if ((p->end - p->next_free >= len))
+			break;
+
+	if (!p) {
+		if (len >= (mem_pool_alloc/2)) {
+			total_allocd += len;
+			return xmalloc(len);
+		}
+		total_allocd += sizeof(struct mem_pool) + mem_pool_alloc;
+		p = xmalloc(sizeof(struct mem_pool) + mem_pool_alloc);
+		p->next_pool = mem_pool;
+		p->next_free = p->space;
+		p->end = p->next_free + mem_pool_alloc;
+		mem_pool = p;
+	}
+
+	r = p->next_free;
+	/* round out to a pointer alignment */
+	if (len & (sizeof(void*) - 1))
+		len += sizeof(void*) - (len & (sizeof(void*) - 1));
+	p->next_free += len;
+	return r;
+}
+
+static void *pool_calloc(size_t count, size_t size)
+{
+	size_t len = count * size;
+	void *r = pool_alloc(len);
+	memset(r, 0, len);
+	return r;
+}
+
+static char *pool_strdup(const char *s)
+{
+	char *r = pool_alloc(strlen(s) + 1);
+	strcpy(r, s);
+	return r;
+}
+
+static void size_dbuf(struct dbuf *b, size_t maxlen)
+{
+	if (b->buffer) {
+		if (b->capacity >= maxlen)
+			return;
+		free(b->buffer);
+	}
+	b->capacity = ((maxlen / 1024) + 1) * 1024;
+	b->buffer = xmalloc(b->capacity);
+}
+
+static void insert_mark(uintmax_t idnum, struct object_entry *oe)
+{
+	struct mark_set *s = marks;
+	while ((idnum >> s->shift) >= 1024) {
+		s = pool_calloc(1, sizeof(struct mark_set));
+		s->shift = marks->shift + 10;
+		s->data.sets[0] = marks;
+		marks = s;
+	}
+	while (s->shift) {
+		uintmax_t i = idnum >> s->shift;
+		idnum -= i << s->shift;
+		if (!s->data.sets[i]) {
+			s->data.sets[i] = pool_calloc(1, sizeof(struct mark_set));
+			s->data.sets[i]->shift = s->shift - 10;
+		}
+		s = s->data.sets[i];
+	}
+	if (!s->data.marked[idnum])
+		marks_set_count++;
+	s->data.marked[idnum] = oe;
+}
+
+static struct object_entry *find_mark(uintmax_t idnum)
+{
+	uintmax_t orig_idnum = idnum;
+	struct mark_set *s = marks;
+	struct object_entry *oe = NULL;
+	if ((idnum >> s->shift) < 1024) {
+		while (s && s->shift) {
+			uintmax_t i = idnum >> s->shift;
+			idnum -= i << s->shift;
+			s = s->data.sets[i];
+		}
+		if (s)
+			oe = s->data.marked[idnum];
+	}
+	if (!oe)
+		die("mark :%ju not declared", orig_idnum);
+	return oe;
+}
+
+static struct atom_str *to_atom(const char *s, unsigned short len)
+{
+	unsigned int hc = hc_str(s, len) % atom_table_sz;
+	struct atom_str *c;
+
+	for (c = atom_table[hc]; c; c = c->next_atom)
+		if (c->str_len == len && !strncmp(s, c->str_dat, len))
+			return c;
+
+	c = pool_alloc(sizeof(struct atom_str) + len + 1);
+	c->str_len = len;
+	strncpy(c->str_dat, s, len);
+	c->str_dat[len] = 0;
+	c->next_atom = atom_table[hc];
+	atom_table[hc] = c;
+	atom_cnt++;
+	return c;
+}
+
+static struct branch *lookup_branch(const char *name)
+{
+	unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz;
+	struct branch *b;
+
+	for (b = branch_table[hc]; b; b = b->table_next_branch)
+		if (!strcmp(name, b->name))
+			return b;
+	return NULL;
+}
+
+static struct branch *new_branch(const char *name)
+{
+	unsigned int hc = hc_str(name, strlen(name)) % branch_table_sz;
+	struct branch* b = lookup_branch(name);
+
+	if (b)
+		die("Invalid attempt to create duplicate branch: %s", name);
+	if (check_ref_format(name))
+		die("Branch name doesn't conform to GIT standards: %s", name);
+
+	b = pool_calloc(1, sizeof(struct branch));
+	b->name = pool_strdup(name);
+	b->table_next_branch = branch_table[hc];
+	b->branch_tree.versions[0].mode = S_IFDIR;
+	b->branch_tree.versions[1].mode = S_IFDIR;
+	b->pack_id = MAX_PACK_ID;
+	branch_table[hc] = b;
+	branch_count++;
+	return b;
+}
+
+static unsigned int hc_entries(unsigned int cnt)
+{
+	cnt = cnt & 7 ? (cnt / 8) + 1 : cnt / 8;
+	return cnt < avail_tree_table_sz ? cnt : avail_tree_table_sz - 1;
+}
+
+static struct tree_content *new_tree_content(unsigned int cnt)
+{
+	struct avail_tree_content *f, *l = NULL;
+	struct tree_content *t;
+	unsigned int hc = hc_entries(cnt);
+
+	for (f = avail_tree_table[hc]; f; l = f, f = f->next_avail)
+		if (f->entry_capacity >= cnt)
+			break;
+
+	if (f) {
+		if (l)
+			l->next_avail = f->next_avail;
+		else
+			avail_tree_table[hc] = f->next_avail;
+	} else {
+		cnt = cnt & 7 ? ((cnt / 8) + 1) * 8 : cnt;
+		f = pool_alloc(sizeof(*t) + sizeof(t->entries[0]) * cnt);
+		f->entry_capacity = cnt;
+	}
+
+	t = (struct tree_content*)f;
+	t->entry_count = 0;
+	t->delta_depth = 0;
+	return t;
+}
+
+static void release_tree_entry(struct tree_entry *e);
+static void release_tree_content(struct tree_content *t)
+{
+	struct avail_tree_content *f = (struct avail_tree_content*)t;
+	unsigned int hc = hc_entries(f->entry_capacity);
+	f->next_avail = avail_tree_table[hc];
+	avail_tree_table[hc] = f;
+}
+
+static void release_tree_content_recursive(struct tree_content *t)
+{
+	unsigned int i;
+	for (i = 0; i < t->entry_count; i++)
+		release_tree_entry(t->entries[i]);
+	release_tree_content(t);
+}
+
+static struct tree_content *grow_tree_content(
+	struct tree_content *t,
+	int amt)
+{
+	struct tree_content *r = new_tree_content(t->entry_count + amt);
+	r->entry_count = t->entry_count;
+	r->delta_depth = t->delta_depth;
+	memcpy(r->entries,t->entries,t->entry_count*sizeof(t->entries[0]));
+	release_tree_content(t);
+	return r;
+}
+
+static struct tree_entry *new_tree_entry(void)
+{
+	struct tree_entry *e;
+
+	if (!avail_tree_entry) {
+		unsigned int n = tree_entry_alloc;
+		total_allocd += n * sizeof(struct tree_entry);
+		avail_tree_entry = e = xmalloc(n * sizeof(struct tree_entry));
+		while (n-- > 1) {
+			*((void**)e) = e + 1;
+			e++;
+		}
+		*((void**)e) = NULL;
+	}
+
+	e = avail_tree_entry;
+	avail_tree_entry = *((void**)e);
+	return e;
+}
+
+static void release_tree_entry(struct tree_entry *e)
+{
+	if (e->tree)
+		release_tree_content_recursive(e->tree);
+	*((void**)e) = avail_tree_entry;
+	avail_tree_entry = e;
+}
+
+static void start_packfile(void)
+{
+	static char tmpfile[PATH_MAX];
+	struct packed_git *p;
+	struct pack_header hdr;
+	int pack_fd;
+
+	snprintf(tmpfile, sizeof(tmpfile),
+		"%s/pack_XXXXXX", get_object_directory());
+	pack_fd = mkstemp(tmpfile);
+	if (pack_fd < 0)
+		die("Can't create %s: %s", tmpfile, strerror(errno));
+	p = xcalloc(1, sizeof(*p) + strlen(tmpfile) + 2);
+	strcpy(p->pack_name, tmpfile);
+	p->pack_fd = pack_fd;
+
+	hdr.hdr_signature = htonl(PACK_SIGNATURE);
+	hdr.hdr_version = htonl(2);
+	hdr.hdr_entries = 0;
+	write_or_die(p->pack_fd, &hdr, sizeof(hdr));
+
+	pack_data = p;
+	pack_size = sizeof(hdr);
+	object_count = 0;
+
+	all_packs = xrealloc(all_packs, sizeof(*all_packs) * (pack_id + 1));
+	all_packs[pack_id] = p;
+}
+
+static void fixup_header_footer(void)
+{
+	static const int buf_sz = 128 * 1024;
+	int pack_fd = pack_data->pack_fd;
+	SHA_CTX c;
+	struct pack_header hdr;
+	char *buf;
+
+	if (lseek(pack_fd, 0, SEEK_SET) != 0)
+		die("Failed seeking to start: %s", strerror(errno));
+	if (read_in_full(pack_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
+		die("Unable to reread header of %s", pack_data->pack_name);
+	if (lseek(pack_fd, 0, SEEK_SET) != 0)
+		die("Failed seeking to start: %s", strerror(errno));
+	hdr.hdr_entries = htonl(object_count);
+	write_or_die(pack_fd, &hdr, sizeof(hdr));
+
+	SHA1_Init(&c);
+	SHA1_Update(&c, &hdr, sizeof(hdr));
+
+	buf = xmalloc(buf_sz);
+	for (;;) {
+		size_t n = xread(pack_fd, buf, buf_sz);
+		if (!n)
+			break;
+		if (n < 0)
+			die("Failed to checksum %s", pack_data->pack_name);
+		SHA1_Update(&c, buf, n);
+	}
+	free(buf);
+
+	SHA1_Final(pack_data->sha1, &c);
+	write_or_die(pack_fd, pack_data->sha1, sizeof(pack_data->sha1));
+	close(pack_fd);
+}
+
+static int oecmp (const void *a_, const void *b_)
+{
+	struct object_entry *a = *((struct object_entry**)a_);
+	struct object_entry *b = *((struct object_entry**)b_);
+	return hashcmp(a->sha1, b->sha1);
+}
+
+static char *create_index(void)
+{
+	static char tmpfile[PATH_MAX];
+	SHA_CTX ctx;
+	struct sha1file *f;
+	struct object_entry **idx, **c, **last, *e;
+	struct object_entry_pool *o;
+	uint32_t array[256];
+	int i, idx_fd;
+
+	/* Build the sorted table of object IDs. */
+	idx = xmalloc(object_count * sizeof(struct object_entry*));
+	c = idx;
+	for (o = blocks; o; o = o->next_pool)
+		for (e = o->next_free; e-- != o->entries;)
+			if (pack_id == e->pack_id)
+				*c++ = e;
+	last = idx + object_count;
+	if (c != last)
+		die("internal consistency error creating the index");
+	qsort(idx, object_count, sizeof(struct object_entry*), oecmp);
+
+	/* Generate the fan-out array. */
+	c = idx;
+	for (i = 0; i < 256; i++) {
+		struct object_entry **next = c;;
+		while (next < last) {
+			if ((*next)->sha1[0] != i)
+				break;
+			next++;
+		}
+		array[i] = htonl(next - idx);
+		c = next;
+	}
+
+	snprintf(tmpfile, sizeof(tmpfile),
+		"%s/index_XXXXXX", get_object_directory());
+	idx_fd = mkstemp(tmpfile);
+	if (idx_fd < 0)
+		die("Can't create %s: %s", tmpfile, strerror(errno));
+	f = sha1fd(idx_fd, tmpfile);
+	sha1write(f, array, 256 * sizeof(int));
+	SHA1_Init(&ctx);
+	for (c = idx; c != last; c++) {
+		uint32_t offset = htonl((*c)->offset);
+		sha1write(f, &offset, 4);
+		sha1write(f, (*c)->sha1, sizeof((*c)->sha1));
+		SHA1_Update(&ctx, (*c)->sha1, 20);
+	}
+	sha1write(f, pack_data->sha1, sizeof(pack_data->sha1));
+	sha1close(f, NULL, 1);
+	free(idx);
+	SHA1_Final(pack_data->sha1, &ctx);
+	return tmpfile;
+}
+
+static char *keep_pack(char *curr_index_name)
+{
+	static char name[PATH_MAX];
+	static char *keep_msg = "fast-import";
+	int keep_fd;
+
+	chmod(pack_data->pack_name, 0444);
+	chmod(curr_index_name, 0444);
+
+	snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
+		 get_object_directory(), sha1_to_hex(pack_data->sha1));
+	keep_fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
+	if (keep_fd < 0)
+		die("cannot create keep file");
+	write(keep_fd, keep_msg, strlen(keep_msg));
+	close(keep_fd);
+
+	snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
+		 get_object_directory(), sha1_to_hex(pack_data->sha1));
+	if (move_temp_to_file(pack_data->pack_name, name))
+		die("cannot store pack file");
+
+	snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
+		 get_object_directory(), sha1_to_hex(pack_data->sha1));
+	if (move_temp_to_file(curr_index_name, name))
+		die("cannot store index file");
+	return name;
+}
+
+static void unkeep_all_packs(void)
+{
+	static char name[PATH_MAX];
+	int k;
+
+	for (k = 0; k < pack_id; k++) {
+		struct packed_git *p = all_packs[k];
+		snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
+			 get_object_directory(), sha1_to_hex(p->sha1));
+		unlink(name);
+	}
+}
+
+static void end_packfile(void)
+{
+	struct packed_git *old_p = pack_data, *new_p;
+
+	if (object_count) {
+		char *idx_name;
+		int i;
+		struct branch *b;
+		struct tag *t;
+
+		fixup_header_footer();
+		idx_name = keep_pack(create_index());
+
+		/* Register the packfile with core git's machinary. */
+		new_p = add_packed_git(idx_name, strlen(idx_name), 1);
+		if (!new_p)
+			die("core git rejected index %s", idx_name);
+		new_p->windows = old_p->windows;
+		all_packs[pack_id] = new_p;
+		install_packed_git(new_p);
+
+		/* Print the boundary */
+		fprintf(stdout, "%s:", new_p->pack_name);
+		for (i = 0; i < branch_table_sz; i++) {
+			for (b = branch_table[i]; b; b = b->table_next_branch) {
+				if (b->pack_id == pack_id)
+					fprintf(stdout, " %s", sha1_to_hex(b->sha1));
+			}
+		}
+		for (t = first_tag; t; t = t->next_tag) {
+			if (t->pack_id == pack_id)
+				fprintf(stdout, " %s", sha1_to_hex(t->sha1));
+		}
+		fputc('\n', stdout);
+
+		pack_id++;
+	}
+	else
+		unlink(old_p->pack_name);
+	free(old_p);
+
+	/* We can't carry a delta across packfiles. */
+	free(last_blob.data);
+	last_blob.data = NULL;
+	last_blob.len = 0;
+	last_blob.offset = 0;
+	last_blob.depth = 0;
+}
+
+static void checkpoint(void)
+{
+	end_packfile();
+	start_packfile();
+}
+
+static size_t encode_header(
+	enum object_type type,
+	size_t size,
+	unsigned char *hdr)
+{
+	int n = 1;
+	unsigned char c;
+
+	if (type < OBJ_COMMIT || type > OBJ_REF_DELTA)
+		die("bad type %d", type);
+
+	c = (type << 4) | (size & 15);
+	size >>= 4;
+	while (size) {
+		*hdr++ = c | 0x80;
+		c = size & 0x7f;
+		size >>= 7;
+		n++;
+	}
+	*hdr = c;
+	return n;
+}
+
+static int store_object(
+	enum object_type type,
+	void *dat,
+	size_t datlen,
+	struct last_object *last,
+	unsigned char *sha1out,
+	uintmax_t mark)
+{
+	void *out, *delta;
+	struct object_entry *e;
+	unsigned char hdr[96];
+	unsigned char sha1[20];
+	unsigned long hdrlen, deltalen;
+	SHA_CTX c;
+	z_stream s;
+
+	hdrlen = sprintf((char*)hdr,"%s %lu",type_names[type],datlen) + 1;
+	SHA1_Init(&c);
+	SHA1_Update(&c, hdr, hdrlen);
+	SHA1_Update(&c, dat, datlen);
+	SHA1_Final(sha1, &c);
+	if (sha1out)
+		hashcpy(sha1out, sha1);
+
+	e = insert_object(sha1);
+	if (mark)
+		insert_mark(mark, e);
+	if (e->offset) {
+		duplicate_count_by_type[type]++;
+		return 1;
+	}
+
+	if (last && last->data && last->depth < max_depth) {
+		delta = diff_delta(last->data, last->len,
+			dat, datlen,
+			&deltalen, 0);
+		if (delta && deltalen >= datlen) {
+			free(delta);
+			delta = NULL;
+		}
+	} else
+		delta = NULL;
+
+	memset(&s, 0, sizeof(s));
+	deflateInit(&s, zlib_compression_level);
+	if (delta) {
+		s.next_in = delta;
+		s.avail_in = deltalen;
+	} else {
+		s.next_in = dat;
+		s.avail_in = datlen;
+	}
+	s.avail_out = deflateBound(&s, s.avail_in);
+	s.next_out = out = xmalloc(s.avail_out);
+	while (deflate(&s, Z_FINISH) == Z_OK)
+		/* nothing */;
+	deflateEnd(&s);
+
+	/* Determine if we should auto-checkpoint. */
+	if ((pack_size + 60 + s.total_out) > max_packsize
+		|| (pack_size + 60 + s.total_out) < pack_size) {
+
+		/* This new object needs to *not* have the current pack_id. */
+		e->pack_id = pack_id + 1;
+		checkpoint();
+
+		/* We cannot carry a delta into the new pack. */
+		if (delta) {
+			free(delta);
+			delta = NULL;
+
+			memset(&s, 0, sizeof(s));
+			deflateInit(&s, zlib_compression_level);
+			s.next_in = dat;
+			s.avail_in = datlen;
+			s.avail_out = deflateBound(&s, s.avail_in);
+			s.next_out = out = xrealloc(out, s.avail_out);
+			while (deflate(&s, Z_FINISH) == Z_OK)
+				/* nothing */;
+			deflateEnd(&s);
+		}
+	}
+
+	e->type = type;
+	e->pack_id = pack_id;
+	e->offset = pack_size;
+	object_count++;
+	object_count_by_type[type]++;
+
+	if (delta) {
+		unsigned long ofs = e->offset - last->offset;
+		unsigned pos = sizeof(hdr) - 1;
+
+		delta_count_by_type[type]++;
+		last->depth++;
+
+		hdrlen = encode_header(OBJ_OFS_DELTA, deltalen, hdr);
+		write_or_die(pack_data->pack_fd, hdr, hdrlen);
+		pack_size += hdrlen;
+
+		hdr[pos] = ofs & 127;
+		while (ofs >>= 7)
+			hdr[--pos] = 128 | (--ofs & 127);
+		write_or_die(pack_data->pack_fd, hdr + pos, sizeof(hdr) - pos);
+		pack_size += sizeof(hdr) - pos;
+	} else {
+		if (last)
+			last->depth = 0;
+		hdrlen = encode_header(type, datlen, hdr);
+		write_or_die(pack_data->pack_fd, hdr, hdrlen);
+		pack_size += hdrlen;
+	}
+
+	write_or_die(pack_data->pack_fd, out, s.total_out);
+	pack_size += s.total_out;
+
+	free(out);
+	free(delta);
+	if (last) {
+		if (!last->no_free)
+			free(last->data);
+		last->data = dat;
+		last->offset = e->offset;
+		last->len = datlen;
+	}
+	return 0;
+}
+
+static void *gfi_unpack_entry(
+	struct object_entry *oe,
+	unsigned long *sizep)
+{
+	static char type[20];
+	struct packed_git *p = all_packs[oe->pack_id];
+	if (p == pack_data)
+		p->pack_size = pack_size + 20;
+	return unpack_entry(p, oe->offset, type, sizep);
+}
+
+static const char *get_mode(const char *str, uint16_t *modep)
+{
+	unsigned char c;
+	uint16_t mode = 0;
+
+	while ((c = *str++) != ' ') {
+		if (c < '0' || c > '7')
+			return NULL;
+		mode = (mode << 3) + (c - '0');
+	}
+	*modep = mode;
+	return str;
+}
+
+static void load_tree(struct tree_entry *root)
+{
+	unsigned char* sha1 = root->versions[1].sha1;
+	struct object_entry *myoe;
+	struct tree_content *t;
+	unsigned long size;
+	char *buf;
+	const char *c;
+
+	root->tree = t = new_tree_content(8);
+	if (is_null_sha1(sha1))
+		return;
+
+	myoe = find_object(sha1);
+	if (myoe) {
+		if (myoe->type != OBJ_TREE)
+			die("Not a tree: %s", sha1_to_hex(sha1));
+		t->delta_depth = 0;
+		buf = gfi_unpack_entry(myoe, &size);
+	} else {
+		char type[20];
+		buf = read_sha1_file(sha1, type, &size);
+		if (!buf || strcmp(type, tree_type))
+			die("Can't load tree %s", sha1_to_hex(sha1));
+	}
+
+	c = buf;
+	while (c != (buf + size)) {
+		struct tree_entry *e = new_tree_entry();
+
+		if (t->entry_count == t->entry_capacity)
+			root->tree = t = grow_tree_content(t, 8);
+		t->entries[t->entry_count++] = e;
+
+		e->tree = NULL;
+		c = get_mode(c, &e->versions[1].mode);
+		if (!c)
+			die("Corrupt mode in %s", sha1_to_hex(sha1));
+		e->versions[0].mode = e->versions[1].mode;
+		e->name = to_atom(c, (unsigned short)strlen(c));
+		c += e->name->str_len + 1;
+		hashcpy(e->versions[0].sha1, (unsigned char*)c);
+		hashcpy(e->versions[1].sha1, (unsigned char*)c);
+		c += 20;
+	}
+	free(buf);
+}
+
+static int tecmp0 (const void *_a, const void *_b)
+{
+	struct tree_entry *a = *((struct tree_entry**)_a);
+	struct tree_entry *b = *((struct tree_entry**)_b);
+	return base_name_compare(
+		a->name->str_dat, a->name->str_len, a->versions[0].mode,
+		b->name->str_dat, b->name->str_len, b->versions[0].mode);
+}
+
+static int tecmp1 (const void *_a, const void *_b)
+{
+	struct tree_entry *a = *((struct tree_entry**)_a);
+	struct tree_entry *b = *((struct tree_entry**)_b);
+	return base_name_compare(
+		a->name->str_dat, a->name->str_len, a->versions[1].mode,
+		b->name->str_dat, b->name->str_len, b->versions[1].mode);
+}
+
+static void mktree(struct tree_content *t,
+	int v,
+	unsigned long *szp,
+	struct dbuf *b)
+{
+	size_t maxlen = 0;
+	unsigned int i;
+	char *c;
+
+	if (!v)
+		qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp0);
+	else
+		qsort(t->entries,t->entry_count,sizeof(t->entries[0]),tecmp1);
+
+	for (i = 0; i < t->entry_count; i++) {
+		if (t->entries[i]->versions[v].mode)
+			maxlen += t->entries[i]->name->str_len + 34;
+	}
+
+	size_dbuf(b, maxlen);
+	c = b->buffer;
+	for (i = 0; i < t->entry_count; i++) {
+		struct tree_entry *e = t->entries[i];
+		if (!e->versions[v].mode)
+			continue;
+		c += sprintf(c, "%o", (unsigned int)e->versions[v].mode);
+		*c++ = ' ';
+		strcpy(c, e->name->str_dat);
+		c += e->name->str_len + 1;
+		hashcpy((unsigned char*)c, e->versions[v].sha1);
+		c += 20;
+	}
+	*szp = c - (char*)b->buffer;
+}
+
+static void store_tree(struct tree_entry *root)
+{
+	struct tree_content *t = root->tree;
+	unsigned int i, j, del;
+	unsigned long new_len;
+	struct last_object lo;
+	struct object_entry *le;
+
+	if (!is_null_sha1(root->versions[1].sha1))
+		return;
+
+	for (i = 0; i < t->entry_count; i++) {
+		if (t->entries[i]->tree)
+			store_tree(t->entries[i]);
+	}
+
+	le = find_object(root->versions[0].sha1);
+	if (!S_ISDIR(root->versions[0].mode)
+		|| !le
+		|| le->pack_id != pack_id) {
+		lo.data = NULL;
+		lo.depth = 0;
+	} else {
+		mktree(t, 0, &lo.len, &old_tree);
+		lo.data = old_tree.buffer;
+		lo.offset = le->offset;
+		lo.depth = t->delta_depth;
+		lo.no_free = 1;
+	}
+
+	mktree(t, 1, &new_len, &new_tree);
+	store_object(OBJ_TREE, new_tree.buffer, new_len,
+		&lo, root->versions[1].sha1, 0);
+
+	t->delta_depth = lo.depth;
+	for (i = 0, j = 0, del = 0; i < t->entry_count; i++) {
+		struct tree_entry *e = t->entries[i];
+		if (e->versions[1].mode) {
+			e->versions[0].mode = e->versions[1].mode;
+			hashcpy(e->versions[0].sha1, e->versions[1].sha1);
+			t->entries[j++] = e;
+		} else {
+			release_tree_entry(e);
+			del++;
+		}
+	}
+	t->entry_count -= del;
+}
+
+static int tree_content_set(
+	struct tree_entry *root,
+	const char *p,
+	const unsigned char *sha1,
+	const uint16_t mode)
+{
+	struct tree_content *t = root->tree;
+	const char *slash1;
+	unsigned int i, n;
+	struct tree_entry *e;
+
+	slash1 = strchr(p, '/');
+	if (slash1)
+		n = slash1 - p;
+	else
+		n = strlen(p);
+
+	for (i = 0; i < t->entry_count; i++) {
+		e = t->entries[i];
+		if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+			if (!slash1) {
+				if (e->versions[1].mode == mode
+						&& !hashcmp(e->versions[1].sha1, sha1))
+					return 0;
+				e->versions[1].mode = mode;
+				hashcpy(e->versions[1].sha1, sha1);
+				if (e->tree) {
+					release_tree_content_recursive(e->tree);
+					e->tree = NULL;
+				}
+				hashclr(root->versions[1].sha1);
+				return 1;
+			}
+			if (!S_ISDIR(e->versions[1].mode)) {
+				e->tree = new_tree_content(8);
+				e->versions[1].mode = S_IFDIR;
+			}
+			if (!e->tree)
+				load_tree(e);
+			if (tree_content_set(e, slash1 + 1, sha1, mode)) {
+				hashclr(root->versions[1].sha1);
+				return 1;
+			}
+			return 0;
+		}
+	}
+
+	if (t->entry_count == t->entry_capacity)
+		root->tree = t = grow_tree_content(t, 8);
+	e = new_tree_entry();
+	e->name = to_atom(p, (unsigned short)n);
+	e->versions[0].mode = 0;
+	hashclr(e->versions[0].sha1);
+	t->entries[t->entry_count++] = e;
+	if (slash1) {
+		e->tree = new_tree_content(8);
+		e->versions[1].mode = S_IFDIR;
+		tree_content_set(e, slash1 + 1, sha1, mode);
+	} else {
+		e->tree = NULL;
+		e->versions[1].mode = mode;
+		hashcpy(e->versions[1].sha1, sha1);
+	}
+	hashclr(root->versions[1].sha1);
+	return 1;
+}
+
+static int tree_content_remove(struct tree_entry *root, const char *p)
+{
+	struct tree_content *t = root->tree;
+	const char *slash1;
+	unsigned int i, n;
+	struct tree_entry *e;
+
+	slash1 = strchr(p, '/');
+	if (slash1)
+		n = slash1 - p;
+	else
+		n = strlen(p);
+
+	for (i = 0; i < t->entry_count; i++) {
+		e = t->entries[i];
+		if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+			if (!slash1 || !S_ISDIR(e->versions[1].mode))
+				goto del_entry;
+			if (!e->tree)
+				load_tree(e);
+			if (tree_content_remove(e, slash1 + 1)) {
+				for (n = 0; n < e->tree->entry_count; n++) {
+					if (e->tree->entries[n]->versions[1].mode) {
+						hashclr(root->versions[1].sha1);
+						return 1;
+					}
+				}
+				goto del_entry;
+			}
+			return 0;
+		}
+	}
+	return 0;
+
+del_entry:
+	if (e->tree) {
+		release_tree_content_recursive(e->tree);
+		e->tree = NULL;
+	}
+	e->versions[1].mode = 0;
+	hashclr(e->versions[1].sha1);
+	hashclr(root->versions[1].sha1);
+	return 1;
+}
+
+static int update_branch(struct branch *b)
+{
+	static const char *msg = "fast-import";
+	struct ref_lock *lock;
+	unsigned char old_sha1[20];
+
+	if (read_ref(b->name, old_sha1))
+		hashclr(old_sha1);
+	lock = lock_any_ref_for_update(b->name, old_sha1);
+	if (!lock)
+		return error("Unable to lock %s", b->name);
+	if (!force_update && !is_null_sha1(old_sha1)) {
+		struct commit *old_cmit, *new_cmit;
+
+		old_cmit = lookup_commit_reference_gently(old_sha1, 0);
+		new_cmit = lookup_commit_reference_gently(b->sha1, 0);
+		if (!old_cmit || !new_cmit) {
+			unlock_ref(lock);
+			return error("Branch %s is missing commits.", b->name);
+		}
+
+		if (!in_merge_bases(old_cmit, new_cmit)) {
+			unlock_ref(lock);
+			warn("Not updating %s"
+				" (new tip %s does not contain %s)",
+				b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
+			return -1;
+		}
+	}
+	if (write_ref_sha1(lock, b->sha1, msg) < 0)
+		return error("Unable to update %s", b->name);
+	return 0;
+}
+
+static void dump_branches(void)
+{
+	unsigned int i;
+	struct branch *b;
+
+	for (i = 0; i < branch_table_sz; i++) {
+		for (b = branch_table[i]; b; b = b->table_next_branch)
+			failure |= update_branch(b);
+	}
+}
+
+static void dump_tags(void)
+{
+	static const char *msg = "fast-import";
+	struct tag *t;
+	struct ref_lock *lock;
+	char ref_name[PATH_MAX];
+
+	for (t = first_tag; t; t = t->next_tag) {
+		sprintf(ref_name, "tags/%s", t->name);
+		lock = lock_ref_sha1(ref_name, NULL);
+		if (!lock || write_ref_sha1(lock, t->sha1, msg) < 0)
+			failure |= error("Unable to update %s", ref_name);
+	}
+}
+
+static void dump_marks_helper(FILE *f,
+	uintmax_t base,
+	struct mark_set *m)
+{
+	uintmax_t k;
+	if (m->shift) {
+		for (k = 0; k < 1024; k++) {
+			if (m->data.sets[k])
+				dump_marks_helper(f, (base + k) << m->shift,
+					m->data.sets[k]);
+		}
+	} else {
+		for (k = 0; k < 1024; k++) {
+			if (m->data.marked[k])
+				fprintf(f, ":%ju %s\n", base + k,
+					sha1_to_hex(m->data.marked[k]->sha1));
+		}
+	}
+}
+
+static void dump_marks(void)
+{
+	if (mark_file)
+	{
+		FILE *f = fopen(mark_file, "w");
+		dump_marks_helper(f, 0, marks);
+		fclose(f);
+	}
+}
+
+static void read_next_command(void)
+{
+	read_line(&command_buf, stdin, '\n');
+}
+
+static void cmd_mark(void)
+{
+	if (!strncmp("mark :", command_buf.buf, 6)) {
+		next_mark = strtoumax(command_buf.buf + 6, NULL, 10);
+		read_next_command();
+	}
+	else
+		next_mark = 0;
+}
+
+static void *cmd_data (size_t *size)
+{
+	size_t length;
+	char *buffer;
+
+	if (strncmp("data ", command_buf.buf, 5))
+		die("Expected 'data n' command, found: %s", command_buf.buf);
+
+	if (!strncmp("<<", command_buf.buf + 5, 2)) {
+		char *term = xstrdup(command_buf.buf + 5 + 2);
+		size_t sz = 8192, term_len = command_buf.len - 5 - 2;
+		length = 0;
+		buffer = xmalloc(sz);
+		for (;;) {
+			read_next_command();
+			if (command_buf.eof)
+				die("EOF in data (terminator '%s' not found)", term);
+			if (term_len == command_buf.len
+				&& !strcmp(term, command_buf.buf))
+				break;
+			if (sz < (length + command_buf.len)) {
+				sz = sz * 3 / 2 + 16;
+				if (sz < (length + command_buf.len))
+					sz = length + command_buf.len;
+				buffer = xrealloc(buffer, sz);
+			}
+			memcpy(buffer + length,
+				command_buf.buf,
+				command_buf.len - 1);
+			length += command_buf.len - 1;
+			buffer[length++] = '\n';