GIT 0.99.9m aka 1.0rc5

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2005-12-11 16:49:45 -08:00
commit a9572072f0
89 changed files with 2606 additions and 1254 deletions

View File

@ -9,6 +9,7 @@ ARTICLES += diffcore
ARTICLES += howto-index ARTICLES += howto-index
ARTICLES += repository-layout ARTICLES += repository-layout
ARTICLES += hooks ARTICLES += hooks
ARTICLES += everyday
# with their own formatting rules. # with their own formatting rules.
SP_ARTICLES = glossary howto/revert-branch-rebase SP_ARTICLES = glossary howto/revert-branch-rebase

View File

@ -187,7 +187,8 @@ you would use git-rev-list and git-diff-tree like this:
We have already talked about the "\--stdin" form of git-diff-tree We have already talked about the "\--stdin" form of git-diff-tree
command that reads the list of commits and compares each commit command that reads the list of commits and compares each commit
with its parents. The git-whatchanged command internally runs with its parents (otherwise you should go back and read the tutorial).
The git-whatchanged command internally runs
the equivalent of the above command, and can be used like this: the equivalent of the above command, and can be used like this:
$ git-whatchanged -p -S'if (frotz) { $ git-whatchanged -p -S'if (frotz) {

138
Documentation/everyday.txt Normal file
View File

@ -0,0 +1,138 @@
Everyday GIT With 20 Commands Or So
===================================
GIT suite has over 100 commands, and the manual page for each of
them discusses what the command does and how it is used in
detail, but until you know what command should be used in order
to achieve what you want to do, you cannot tell which manual
page to look at, and if you know that already you do not need
the manual.
Does that mean you need to know all of them before you can use
git? Not at all. Depending on the role you play, the set of
commands you need to know is slightly different, but in any case
what you need to learn is far smaller than the full set of
commands to carry out your day-to-day work. This document is to
serve as a cheat-sheet and a set of pointers for people playing
various roles.
<<Basic Repository>> commands are needed by people who has a
repository --- that is everybody, because every working tree of
git is a repository.
In addition, <<Individual Developer (Standalone)>> commands are
essential for anybody who makes a commit, even for somebody who
works alone.
If you work with other people, you will need commands listed in
<<Individual Developer (Participant)>> section as well.
People who play <<Integrator>> role need to learn some more
commands in addition to the above.
<<Repository Administration>> commands are for system
administrators who are responsible to care and feed git
repositories to support developers.
Basic Repository[[Basic Repository]]
------------------------------------
Everybody uses these commands to feed and care git repositories.
* gitlink:git-init-db[1] or gitlink:git-clone[1] to create a
new repository.
* gitlink:git-fsck-objects[1] to validate the repository.
* gitlink:git-prune[1] to garbage collect crufts in the
repository.
* gitlink:git-repack[1] to pack loose objects for efficiency.
Individual Developer (Standalone)[[Individual Developer (Standalone)]]
----------------------------------------------------------------------
A standalone individual developer does not exchange patches with
other poeple, and works alone in a single repository, using the
following commands.
* gitlink:git-show-branch[1] to see where you are.
* gitlink:git-diff[1] and gitlink:git-status[1] to see what
you are in the middle of doing.
* gitlink:git-log[1] to see what happened.
* gitlink:git-whatchanged[1] to find out where things have
come from.
* gitlink:git-checkout[1] and gitlink:git-branch[1] to switch
branches.
* gitlink:git-update-index[1] to manage the index file.
* gitlink:git-commit[1] to advance the current branch.
* gitlink:git-reset[1] and gitlink:git-checkout[1] (with
pathname parameters) to undo changes.
* gitlink:git-pull[1] with "." as the remote to merge between
local branches.
* gitlink:git-rebase[1] to maintain topic branches.
Individual Developer (Participant)[[Individual Developer (Participant)]]
------------------------------------------------------------------------
A developer working as a participant in a group project needs to
learn how to communicate with others, and uses these commands in
addition to the ones needed by a standalone developer.
* gitlink:git-pull[1] from "origin" to keep up-to-date with
the upstream.
* gitlink:git-push[1] to shared repository if you adopt CVS
style shared repository workflow.
* gitlink:git-format-patch[1] to prepare e-mail submission, if
you adopt Linux kernel-style public forum workflow.
Integrator[[Integrator]]
------------------------
A fairly central person acting as the integrator in a group
project receives changes made by others, reviews and integrates
them and publishes the result for others to use, using these
commands in addition to the ones needed by participants.
* gitlink:git-am[1] to apply patches e-mailed in from your
contributors.
* gitlink:git-pull[1] to merge from your trusted lieutenants.
* gitlink:git-format-patch[1] to prepare and send suggested
alternative to contributors.
* gitlink:git-revert[1] to undo botched commits.
* gitlink:git-push[1] to publish the bleeding edge.
Repository Administration[[Repository Administration]]
------------------------------------------------------
A repository administrator uses the following tools to set up
and maintain access to the repository by developers.
* gitlink:git-daemon[1] to allow anonymous download from
repository.
* gitlink:git-shell[1] can be used as a 'restricted login shell'
for shared central repository users.
* link:howto/update-hook-example.txt[update hook howto] has a
good example of managing a shared central repository.

View File

@ -28,7 +28,7 @@ OPTIONS
area to store extracted patches. area to store extracted patches.
--utf8, --keep:: --utf8, --keep::
Pass `--utf8` and `--keep` flags to `git-mailinfo` (see Pass `-u` and `-k` flags to `git-mailinfo` (see
gitlink:git-mailinfo[1]). gitlink:git-mailinfo[1]).
--binary:: --binary::

View File

@ -8,16 +8,21 @@ git-bisect - Find the change that introduced a bug
SYNOPSIS SYNOPSIS
-------- --------
'git bisect' start 'git bisect' <subcommand> <options>
'git bisect' bad <rev>
'git bisect' good <rev>
'git bisect' reset [<branch>]
'git bisect' visualize
'git bisect' replay <logfile>
'git bisect' log
DESCRIPTION DESCRIPTION
----------- -----------
The command takes various subcommands, and different options
depending on the subcommand:
git bisect start [<paths>...]
git bisect bad <rev>
git bisect good <rev>
git bisect reset [<branch>]
git bisect visualize
git bisect replay <logfile>
git bisect log
This command uses 'git-rev-list --bisect' option to help drive This command uses 'git-rev-list --bisect' option to help drive
the binary search process to find which change introduced a bug, the binary search process to find which change introduced a bug,
given an old "good" commit object name and a later "bad" commit given an old "good" commit object name and a later "bad" commit
@ -26,10 +31,10 @@ object name.
The way you use it is: The way you use it is:
------------------------------------------------ ------------------------------------------------
git bisect start $ git bisect start
git bisect bad # Current version is bad $ git bisect bad # Current version is bad
git bisect good v2.6.13-rc2 # v2.6.13-rc2 was the last version $ git bisect good v2.6.13-rc2 # v2.6.13-rc2 was the last version
# tested that was good # tested that was good
------------------------------------------------ ------------------------------------------------
When you give at least one bad and one good versions, it will When you give at least one bad and one good versions, it will
@ -43,7 +48,7 @@ and check out the state in the middle. Now, compile that kernel, and boot
it. Now, let's say that this booted kernel works fine, then just do it. Now, let's say that this booted kernel works fine, then just do
------------------------------------------------ ------------------------------------------------
git bisect good # this one is good $ git bisect good # this one is good
------------------------------------------------ ------------------------------------------------
which will now say which will now say
@ -62,7 +67,7 @@ kernel rev in "refs/bisect/bad".
Oh, and then after you want to reset to the original head, do a Oh, and then after you want to reset to the original head, do a
------------------------------------------------ ------------------------------------------------
git bisect reset $ git bisect reset
------------------------------------------------ ------------------------------------------------
to get back to the master branch, instead of being in one of the bisection to get back to the master branch, instead of being in one of the bisection
@ -72,7 +77,9 @@ not using some old bisection branch).
During the bisection process, you can say During the bisection process, you can say
git bisect visualize ------------
$ git bisect visualize
------------
to see the currently remaining suspects in `gitk`. to see the currently remaining suspects in `gitk`.
@ -80,11 +87,40 @@ The good/bad input is logged, and `git bisect
log` shows what you have done so far. You can truncate its log` shows what you have done so far. You can truncate its
output somewhere and save it in a file, and run output somewhere and save it in a file, and run
git bisect replay that-file ------------
$ git bisect replay that-file
------------
if you find later you made a mistake telling good/bad about a if you find later you made a mistake telling good/bad about a
revision. revision.
If in a middle of bisect session, you know what the bisect
suggested to try next is not a good one to test (e.g. the change
the commit introduces is known not to work in your environment
and you know it does not have anything to do with the bug you
are chasing), you may want to find a near-by commit and try that
instead. It goes something like this:
------------
$ git bisect good/bad # previous round was good/bad.
Bisecting: 337 revisions left to test after this
$ git bisect visualize # oops, that is uninteresting.
$ git reset --hard HEAD~3 # try 3 revs before what
# was suggested
------------
Then compile and test the one you chose to try. After that,
tell bisect what the result was as usual.
You can further cut down the number of trials if you know what
part of the tree is involved in the problem you are tracking
down, by giving paths parameters when you say `bisect start`,
like this:
------------
$ git bisect start arch/i386 include/asm-i386
------------
Author Author
------ ------

View File

@ -9,7 +9,7 @@ git-checkout-index - Copy files from the index to the working directory
SYNOPSIS SYNOPSIS
-------- --------
'git-checkout-index' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>] 'git-checkout-index' [-u] [-q] [-a] [-f] [-n] [--prefix=<string>]
[--] <file>... [--stage=<number>] [--] <file>...
DESCRIPTION DESCRIPTION
----------- -----------
@ -40,58 +40,80 @@ OPTIONS
When creating files, prepend <string> (usually a directory When creating files, prepend <string> (usually a directory
including a trailing /) including a trailing /)
--stage=<number>::
Instead of checking out unmerged entries, copy out the
files from named stage. <number> must be between 1 and 3.
--:: --::
Do not interpret any more arguments as options. Do not interpret any more arguments as options.
The order of the flags used to matter, but not anymore. The order of the flags used to matter, but not anymore.
Just doing "git-checkout-index" does nothing. You probably meant Just doing `git-checkout-index` does nothing. You probably meant
"git-checkout-index -a". And if you want to force it, you want `git-checkout-index -a`. And if you want to force it, you want
"git-checkout-index -f -a". `git-checkout-index -f -a`.
Intuitiveness is not the goal here. Repeatability is. The reason for Intuitiveness is not the goal here. Repeatability is. The reason for
the "no arguments means no work" thing is that from scripts you are the "no arguments means no work" behavior is that from scripts you are
supposed to be able to do things like: supposed to be able to do:
find . -name '*.h' -print0 | xargs -0 git-checkout-index -f -- ----------------
$ find . -name '*.h' -print0 | xargs -0 git-checkout-index -f --
----------------
which will force all existing `*.h` files to be replaced with their which will force all existing `*.h` files to be replaced with their
cached copies. If an empty command line implied "all", then this would cached copies. If an empty command line implied "all", then this would
force-refresh everything in the index, which was not the point. force-refresh everything in the index, which was not the point.
To update and refresh only the files already checked out: The `--` is just a good idea when you know the rest will be filenames;
it will prevent problems with a filename of, for example, `-a`.
Using `--` is probably a good policy in scripts.
git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh
Oh, and the "--" is just a good idea when you know the rest will be EXAMPLES
filenames. Just so that you wouldn't have a filename of "-a" causing --------
problems (not possible in the above example, but get used to it in To update and refresh only the files already checked out::
scripting!). +
----------------
$ git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh
----------------
The prefix ability basically makes it trivial to use Using `git-checkout-index` to "export an entire tree"::
git-checkout-index as an "export as tree" function. Just read the The prefix ability basically makes it trivial to use
desired tree into the index, and do a `git-checkout-index` as an "export as tree" function.
Just read the desired tree into the index, and do:
git-checkout-index --prefix=git-export-dir/ -a +
----------------
and git-checkout-index will "export" the index into the specified $ git-checkout-index --prefix=git-export-dir/ -a
----------------
+
`git-checkout-index` will "export" the index into the specified
directory. directory.
+
The final "/" is important. The exported name is literally just
prefixed with the specified string. Contrast this with the
following example.
NOTE The final "/" is important. The exported name is literally just Export files with a prefix::
prefixed with the specified string, so you can also do something like +
----------------
$ git-checkout-index --prefix=.merged- Makefile
----------------
+
This will check out the currently cached copy of `Makefile`
into the file `.merged-Makefile`.
git-checkout-index --prefix=.merged- Makefile
to check out the currently cached copy of `Makefile` into the file
`.merged-Makefile`
Author Author
------ ------
Written by Linus Torvalds <torvalds@osdl.org> Written by Linus Torvalds <torvalds@osdl.org>
Documentation Documentation
-------------- --------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. Documentation by David Greaves,
Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT GIT
--- ---

View File

@ -7,7 +7,7 @@ git-cherry-pick - Apply the change introduced by an existing commit.
SYNOPSIS SYNOPSIS
-------- --------
'git-cherry-pick' [-n] [-r] <commit> 'git-cherry-pick' [--edit] [-n] [-r] <commit>
DESCRIPTION DESCRIPTION
----------- -----------
@ -20,15 +20,19 @@ OPTIONS
<commit>:: <commit>::
Commit to cherry-pick. Commit to cherry-pick.
-r:: -e|--edit::
With this option, `git-cherry-pick` will let you edit the commit
message prior committing.
-r|--replay::
Usually the command appends which commit was Usually the command appends which commit was
cherry-picked after the original commit message when cherry-picked after the original commit message when
making a commit. This option, '--replay', causes it to making a commit. This option, '--replay', causes it to
use the original commit message intact. This is useful use the original commit message intact. This is useful
when you are reordering the patches in your private tree when you are reordering the patches in your private tree
before publishing, and is used by 'git rebase'. before publishing.
-n:: -n|--no-commit::
Usually the command automatically creates a commit with Usually the command automatically creates a commit with
a commit log message stating which commit was a commit log message stating which commit was
cherry-picked. This flag applies the change necessary cherry-picked. This flag applies the change necessary

View File

@ -7,7 +7,7 @@ git-commit - Record your changes
SYNOPSIS SYNOPSIS
-------- --------
'git-commit' [-a] [-s] [-v] [(-c | -C) <commit> | -F <file> | -m <msg>] [-e] <file>... 'git-commit' [-a] [-s] [-v] [(-c | -C) <commit> | -F <file> | -m <msg>] [-e] [--] <file>...
DESCRIPTION DESCRIPTION
----------- -----------
@ -22,7 +22,7 @@ information.
OPTIONS OPTIONS
------- -------
-a:: -a|--all::
Update all paths in the index file. Update all paths in the index file.
-c or -C <commit>:: -c or -C <commit>::
@ -39,23 +39,29 @@ OPTIONS
-m <msg>:: -m <msg>::
Use the given <msg> as the commit message. Use the given <msg> as the commit message.
-s:: -s|--signoff::
Add Signed-off-by line at the end of the commit message. Add Signed-off-by line at the end of the commit message.
-v:: -v|--verify::
Look for suspicious lines the commit introduces, and Look for suspicious lines the commit introduces, and
abort committing if there is one. The definition of abort committing if there is one. The definition of
'suspicious lines' is currently the lines that has 'suspicious lines' is currently the lines that has
trailing whitespaces, and the lines whose indentation trailing whitespaces, and the lines whose indentation
has a SP character immediately followed by a TAB has a SP character immediately followed by a TAB
character. character. This is the default.
-e:: -n|--no-verify::
The opposite of `--verify`.
-e|--edit::
The message taken from file with `-F`, command line with The message taken from file with `-F`, command line with
`-m`, and from file with `-C` are usually used as the `-m`, and from file with `-C` are usually used as the
commit log message unmodified. This option lets you commit log message unmodified. This option lets you
further edit the message taken from these sources. further edit the message taken from these sources.
--::
Do not interpret any more arguments as options.
<file>...:: <file>...::
Update specified paths in the index file before committing. Update specified paths in the index file before committing.

View File

@ -60,7 +60,7 @@ the old cvs2git tool.
+ +
If you need to pass multiple options, separate them with a comma. If you need to pass multiple options, separate them with a comma.
-P:: <cvsps-output-file> -P <cvsps-output-file>::
Instead of calling cvsps, read the provided cvsps output file. Useful Instead of calling cvsps, read the provided cvsps output file. Useful
for debugging or when cvsps is being handled outside cvsimport. for debugging or when cvsps is being handled outside cvsimport.

View File

@ -17,14 +17,16 @@ ent and the index file, or the index file and the working tree.
The combination of what is compared with what is determined by The combination of what is compared with what is determined by
the number of ents given to the command. the number of ents given to the command.
`----------------`--------`-----------------------------`------------------ * When no <ent> is given, the working tree and the index
Number of ents Options What's Compared Underlying command file is compared, using `git-diff-files`.
---------------------------------------------------------------------------
0 - index file and working tree git-diff-files * When one <ent> is given, the working tree and the named
1 --cached ent and index file git-diff-index tree is compared, using `git-diff-index`. The option
1 - ent and working tree git-diff-index `--cached` can be given to compare the index file and
2 - two ents git-diff-tree the named tree.
---------------------------------------------------------------------------
* When two <ent>s are given, these two trees are compared
using `git-diff-tree`.
OPTIONS OPTIONS
------- -------

View File

@ -18,7 +18,7 @@ the objects necessary to complete them.
The ref names and their object names of fetched refs are stored The ref names and their object names of fetched refs are stored
in `.git/FETCH_HEAD`. This information is left for a later merge in `.git/FETCH_HEAD`. This information is left for a later merge
operation done by "git resolve" or "git octopus". operation done by "git merge".
OPTIONS OPTIONS

View File

@ -8,7 +8,7 @@ git-format-patch - Prepare patches for e-mail submission.
SYNOPSIS SYNOPSIS
-------- --------
'git-format-patch' [-n][-o <dir>|--stdout][-k][--mbox][--diff-options] <his> [<mine>] 'git-format-patch' [-n | -k] [-o <dir> | --stdout] [-s] [-c] [--mbox] [--diff-options] <his> [<mine>]
DESCRIPTION DESCRIPTION
----------- -----------
@ -32,23 +32,34 @@ processing with applymbox.
OPTIONS OPTIONS
------- -------
-o <dir>:: -o|--output-directory <dir>::
Use <dir> to store the resulting files, instead of the Use <dir> to store the resulting files, instead of the
current working directory. current working directory.
-n:: -n|--numbered::
Name output in '[PATCH n/m]' format. Name output in '[PATCH n/m]' format.
-k:: -k|--keep-subject::
Do not strip/add '[PATCH]' from the first line of the Do not strip/add '[PATCH]' from the first line of the
commit log message. commit log message.
--author, --date:: -a|--author, -d|--date::
Output From: and Date: headers for commits made by Output From: and Date: headers for commits made by
yourself as well. Usually these are output only for yourself as well. Usually these are output only for
commits made by people other than yourself. commits made by people other than yourself.
--mbox:: -s|--signoff::
Add `Signed-off-by:` line to the commit message, using
the committer identity of yourself.
-c|--check::
Display suspicious lines in the patch. The definition
of 'suspicious lines' is currently the lines that has
trailing whitespaces, and the lines whose indentation
has a SP character immediately followed by a TAB
character.
-m|--mbox::
Format the output files for closer to mbox format by Format the output files for closer to mbox format by
adding a phony Unix "From " line, so they can be adding a phony Unix "From " line, so they can be
concatenated together and fed to `git-applymbox`. concatenated together and fed to `git-applymbox`.

View File

@ -8,7 +8,7 @@ git-hash-object - Computes object ID and optionally creates a blob from a file.
SYNOPSIS SYNOPSIS
-------- --------
'git-hash-object' [-t <type>] [-w] <any-file-on-the-filesystem> 'git-hash-object' [-t <type>] [-w] [--stdin] [--] <file>...
DESCRIPTION DESCRIPTION
----------- -----------
@ -29,6 +29,9 @@ OPTIONS
-w:: -w::
Actually write the object into the object database. Actually write the object into the object database.
--stdin::
Read the object from standard input instead of from a file.
Author Author
------ ------
Written by Junio C Hamano <junkio@cox.net> Written by Junio C Hamano <junkio@cox.net>

View File

@ -14,6 +14,12 @@ DESCRIPTION
----------- -----------
Downloads a remote git repository via HTTP. Downloads a remote git repository via HTTP.
OPTIONS
-------
commit-id::
Either the hash or the filename under [URL]/refs/ to
pull.
-c:: -c::
Get the commit objects. Get the commit objects.
-t:: -t::

View File

@ -8,7 +8,14 @@ git-init-db - Creates an empty git repository
SYNOPSIS SYNOPSIS
-------- --------
'git-init-db' 'git-init-db' [--template=<template_directory>]
OPTIONS
-------
--template=<template_directory>::
Provide the directory in from which templates will be used.
DESCRIPTION DESCRIPTION
----------- -----------
@ -16,14 +23,26 @@ This simply creates an empty git repository - basically a `.git` directory
and `.git/object/??/`, `.git/refs/heads` and `.git/refs/tags` directories, and `.git/object/??/`, `.git/refs/heads` and `.git/refs/tags` directories,
and links `.git/HEAD` symbolically to `.git/refs/heads/master`. and links `.git/HEAD` symbolically to `.git/refs/heads/master`.
If the 'GIT_DIR' environment variable is set then it specifies a path If the `$GIT_DIR` environment variable is set then it specifies a path
to use instead of `./.git` for the base of the repository. to use instead of `./.git` for the base of the repository.
If the object storage directory is specified via the 'GIT_OBJECT_DIRECTORY' If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
environment variable then the sha1 directories are created underneath - environment variable then the sha1 directories are created underneath -
otherwise the default `$GIT_DIR/objects` directory is used. otherwise the default `$GIT_DIR/objects` directory is used.
"git-init-db" won't hurt an existing repository. `git-init-db` won't hurt an existing repository.
EXAMPLES
--------
Start a new git repository for an existing code base::
+
----------------
$ cd /path/to/my/codebase
$ git-init-db
----------------
Author Author

View File

@ -17,7 +17,7 @@ Displays the references other repository has.
OPTIONS OPTIONS
------- -------
--heads --tags:: -h|--heads, -t|--tags::
Limit to only refs/heads and refs/tags, respectively. Limit to only refs/heads and refs/tags, respectively.
These options are _not_ mutually exclusive; when given These options are _not_ mutually exclusive; when given
both, references stored in refs/heads and refs/tags are both, references stored in refs/heads and refs/tags are

View File

@ -8,12 +8,15 @@ git-ls-tree - Lists the contents of a tree object.
SYNOPSIS SYNOPSIS
-------- --------
'git-ls-tree' [-d] [-r] [-z] <tree-ish> [paths...] 'git-ls-tree' [-d] [-r] [-t] [-z] [--name-only] [--name-status] <tree-ish> [paths...]
DESCRIPTION DESCRIPTION
----------- -----------
Lists the contents of a tree object, like what "/bin/ls -a" does Lists the contents of a given tree object, like what "/bin/ls -a" does
in the current working directory. in the current working directory. Note that the usage is subtly different,
though - 'paths' denote just a list of patterns to match, e.g. so specifying
directory name (without '-r') will behave differently, and order of the
arguments does not matter.
OPTIONS OPTIONS
------- -------
@ -21,36 +24,48 @@ OPTIONS
Id of a tree-ish. Id of a tree-ish.
-d:: -d::
show only the named tree entry itself, not its children Show only the named tree entry itself, not its children.
-r:: -r::
recurse into sub-trees Recurse into sub-trees.
-t::
Show tree entries even when going to recurse them. Has no effect
if '-r' was not passed. '-d' implies '-t'.
-z:: -z::
\0 line termination on output \0 line termination on output.
--name-only::
--name-status::
List only filenames (instead of the "long" output), one per line.
paths:: paths::
When paths are given, show them. Otherwise implicitly When paths are given, show them (note that this isn't really raw
uses the root level of the tree as the sole path argument. pathnames, but rather a list of patterns to match). Otherwise
implicitly uses the root level of the tree as the sole path argument.
Output Format Output Format
------------- -------------
<mode> SP <type> SP <object> TAB <file> <mode> SP <type> SP <object> TAB <file>
When `-z` option is not used, TAB, LF, and backslash characters When the `-z` option is not used, TAB, LF, and backslash characters
in pathnames are represented as `\t`, `\n`, and `\\`, in pathnames are represented as `\t`, `\n`, and `\\`, respectively.
respectively.
Author Author
------ ------
Written by Linus Torvalds <torvalds@osdl.org> Written by Petr Baudis <pasky@suse.cz>
Completely rewritten from scratch by Junio C Hamano <junkio@cox.net> Completely rewritten from scratch by Junio C Hamano <junkio@cox.net>,
another major rewrite by Linus Torvalds <torvalds@osdl.org>
Documentation Documentation
-------------- --------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>. Documentation by David Greaves, Junio C Hamano and the git-list
<git@vger.kernel.org>.
This manual page is a stub. You can help the git documentation by expanding it.
GIT GIT
--- ---

View File

@ -20,7 +20,7 @@ files are passed as arguments 5, 6 and 7.
OPTIONS OPTIONS
------- -------
--:: --::
Interpret all following arguments as filenames. Do not interpret any more arguments as options.
-a:: -a::
Run merge against all files in the index that need merging. Run merge against all files in the index that need merging.

View File

@ -8,12 +8,15 @@ git-mv - Script used to move or rename a file, directory or symlink.
SYNOPSIS SYNOPSIS
-------- --------
'git-mv' [-f] [-n] <source> <destination> 'git-mv' <options>... <args>...
'git-mv' [-f] [-n] [-k] <source> ... <destination directory>
DESCRIPTION DESCRIPTION
----------- -----------
This script is used to move or rename a file, directory or symlink. This script is used to move or rename a file, directory or symlink.
git-mv [-f] [-n] <source> <destination>
git-mv [-f] [-n] [-k] <source> ... <destination directory>
In the first form, it renames <source>, which must exist and be either In the first form, it renames <source>, which must exist and be either
a file, symlink or directory, to <destination>. a file, symlink or directory, to <destination>.
In the second form, the last argument has to be an existing In the second form, the last argument has to be an existing

View File

@ -8,7 +8,7 @@ git-pack-objects - Create a packed archive of objects.
SYNOPSIS SYNOPSIS
-------- --------
'git-pack-objects' [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list 'git-pack-objects' [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list
DESCRIPTION DESCRIPTION
@ -70,6 +70,10 @@ base-name::
that are packed and not in the local object store that are packed and not in the local object store
(i.e. borrowed from an alternate). (i.e. borrowed from an alternate).
--non-empty::
Only create a packed archive if it would contain at
least one object.
Author Author
------ ------
Written by Linus Torvalds <torvalds@osdl.org> Written by Linus Torvalds <torvalds@osdl.org>

View File

@ -9,19 +9,22 @@ residing in a pack file.
SYNOPSIS SYNOPSIS
-------- --------
'git-prune-packed' 'git-prune-packed' [-n]
DESCRIPTION DESCRIPTION
----------- -----------
This program search the GIT_OBJECT_DIR for all objects that currently exist in This program search the `$GIT_OBJECT_DIR` for all objects that currently
a pack file as well as the independent object directories. exist in a pack file as well as the independent object directories.
All such extra objects are removed. All such extra objects are removed.
A pack is a collection of objects, individually compressed, with delta A pack is a collection of objects, individually compressed, with delta
compression applied, stored in a single file, with an associated index file. compression applied, stored in a single file, with an associated index file.
Packs are used to reduce the load on mirror systems, backup engines, disk storage, etc. Packs are used to reduce the load on mirror systems, backup engines,
disk storage, etc.
OPTIONS OPTIONS
------- -------

View File

@ -8,15 +8,16 @@ git-prune - Prunes all unreachable objects from the object database
SYNOPSIS SYNOPSIS
-------- --------
'git-prune' [-n] 'git-prune' [-n] [--] [<head>...]
DESCRIPTION DESCRIPTION
----------- -----------
This runs `git-fsck-objects --unreachable` using the heads This runs `git-fsck-objects --unreachable` using all the refs
specified on the command line (or `$GIT_DIR/refs/heads/\*` and available in `$GIT_DIR/refs`, optionally with additional set of
`$GIT_DIR/refs/tags/\*` if none is specified), and prunes all objects specified on the command line, and prunes all
unreachable objects from the object database. In addition, it objects unreachable from any of these head objects from the object database.
In addition, it
prunes the unpacked objects that are also found in packs by prunes the unpacked objects that are also found in packs by
running `git prune-packed`. running `git prune-packed`.
@ -27,6 +28,24 @@ OPTIONS
Do not remove anything; just report what it would Do not remove anything; just report what it would
remove. remove.
--::
Do not interpret any more arguments as options.
<head>...::
In addition to objects
reachable from any of our references, keep objects
reachable from listed <head>s.
EXAMPLE
-------
To prune objects not used by your repository nor another that
borrows from your repository via its
`.git/objects/info/alternates`:
------------
$ git prune $(cd ../another && $(git-rev-parse --all))
------------
Author Author
------ ------

View File

@ -16,6 +16,10 @@ DESCRIPTION
Updates remote refs using local refs, while sending objects Updates remote refs using local refs, while sending objects
necessary to complete the given refs. necessary to complete the given refs.
You can make "interesting" things to happen on the repository
every time you push into it, by setting up 'hooks' there. See
documentation for gitlink:git-receive-pack[1].
OPTIONS OPTIONS
------- -------
@ -31,6 +35,7 @@ include::pull-fetch-param.txt[]
This flag disables the check. What this means is that the This flag disables the check. What this means is that the
local repository can lose commits; use it with care. local repository can lose commits; use it with care.
Author Author
------ ------
Written by Junio C Hamano <junkio@cox.net> Written by Junio C Hamano <junkio@cox.net>

View File

@ -8,22 +8,22 @@ git-read-tree - Reads tree information into the index
SYNOPSIS SYNOPSIS
-------- --------
'git-read-tree' (<tree-ish> | [-m [-u|-i]] <tree-ish1> [<tree-ish2> [<tree-ish3>]]) 'git-read-tree' (<tree-ish> | [[-m | --reset] [-u | -i]] <tree-ish1> [<tree-ish2> [<tree-ish3>]])
DESCRIPTION DESCRIPTION
----------- -----------
Reads the tree information given by <tree-ish> into the index, Reads the tree information given by <tree-ish> into the index,
but does not actually *update* any of the files it "caches". (see: but does not actually *update* any of the files it "caches". (see:
git-checkout-index) gitlink:git-checkout-index[1])
Optionally, it can merge a tree into the index, perform a Optionally, it can merge a tree into the index, perform a
fast-forward (i.e. 2-way) merge, or a 3-way merge, with the -m fast-forward (i.e. 2-way) merge, or a 3-way merge, with the `-m`
flag. When used with -m, the -u flag causes it to also update flag. When used with `-m`, the `-u` flag causes it to also update
the files in the work tree with the result of the merge. the files in the work tree with the result of the merge.
Trivial merges are done by "git-read-tree" itself. Only conflicting paths Trivial merges are done by `git-read-tree` itself. Only conflicting paths
will be in unmerged state when "git-read-tree" returns. will be in unmerged state when `git-read-tree` returns.
OPTIONS OPTIONS
------- -------
@ -56,7 +56,7 @@ OPTIONS
Merging Merging
------- -------
If '-m' is specified, "git-read-tree" can perform 3 kinds of If `-m` is specified, `git-read-tree` can perform 3 kinds of
merge, a single tree merge if only 1 tree is given, a merge, a single tree merge if only 1 tree is given, a
fast-forward merge with 2 trees, or a 3-way merge if 3 trees are fast-forward merge with 2 trees, or a 3-way merge if 3 trees are
provided. provided.
@ -65,23 +65,23 @@ provided.
Single Tree Merge Single Tree Merge
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
If only 1 tree is specified, git-read-tree operates as if the user did not If only 1 tree is specified, git-read-tree operates as if the user did not
specify '-m', except that if the original index has an entry for a specify `-m`, except that if the original index has an entry for a
given pathname, and the contents of the path matches with the tree given pathname, and the contents of the path matches with the tree
being read, the stat info from the index is used. (In other words, the being read, the stat info from the index is used. (In other words, the
index's stat()s take precedence over the merged tree's). index's stat()s take precedence over the merged tree's).
That means that if you do a "git-read-tree -m <newtree>" followed by a That means that if you do a `git-read-tree -m <newtree>` followed by a
"git-checkout-index -f -u -a", the "git-checkout-index" only checks out `git-checkout-index -f -u -a`, the `git-checkout-index` only checks out
the stuff that really changed. the stuff that really changed.
This is used to avoid unnecessary false hits when "git-diff-files" is This is used to avoid unnecessary false hits when `git-diff-files` is
run after git-read-tree. run after `git-read-tree`.
Two Tree Merge Two Tree Merge
~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~
Typically, this is invoked as "git-read-tree -m $H $M", where $H Typically, this is invoked as `git-read-tree -m $H $M`, where $H
is the head commit of the current repository, and $M is the head is the head commit of the current repository, and $M is the head
of a foreign tree, which is simply ahead of $H (i.e. we are in a of a foreign tree, which is simply ahead of $H (i.e. we are in a
fast forward situation). fast forward situation).
@ -94,7 +94,7 @@ the following:
2. The user wants to fast-forward to $M. 2. The user wants to fast-forward to $M.
In this case, the "git-read-tree -m $H $M" command makes sure In this case, the `git-read-tree -m $H $M` command makes sure
that no local change is lost as the result of this "merge". that no local change is lost as the result of this "merge".
Here are the "carry forward" rules: Here are the "carry forward" rules:
@ -141,13 +141,13 @@ operating under the -u flag.
When this form of git-read-tree returns successfully, you can When this form of git-read-tree returns successfully, you can
see what "local changes" you made are carried forward by running see what "local changes" you made are carried forward by running
"git-diff-index --cached $M". Note that this does not `git-diff-index --cached $M`. Note that this does not
necessarily match "git-diff-index --cached $H" would have necessarily match `git-diff-index --cached $H` would have
produced before such a two tree merge. This is because of cases produced before such a two tree merge. This is because of cases
18 and 19 --- if you already had the changes in $M (e.g. maybe 18 and 19 --- if you already had the changes in $M (e.g. maybe
you picked it up via e-mail in a patch form), "git-diff-index you picked it up via e-mail in a patch form), `git-diff-index
--cached $H" would have told you about the change before this --cached $H` would have told you about the change before this
merge, but it would not show in "git-diff-index --cached $M" merge, but it would not show in `git-diff-index --cached $M`
output after two-tree merge. output after two-tree merge.
@ -156,31 +156,39 @@ output after two-tree merge.
Each "index" entry has two bits worth of "stage" state. stage 0 is the Each "index" entry has two bits worth of "stage" state. stage 0 is the
normal one, and is the only one you'd see in any kind of normal use. normal one, and is the only one you'd see in any kind of normal use.
However, when you do "git-read-tree" with three trees, the "stage" However, when you do `git-read-tree` with three trees, the "stage"
starts out at 1. starts out at 1.
This means that you can do This means that you can do
git-read-tree -m <tree1> <tree2> <tree3> ----------------
$ git-read-tree -m <tree1> <tree2> <tree3>
----------------
and you will end up with an index with all of the <tree1> entries in and you will end up with an index with all of the <tree1> entries in
"stage1", all of the <tree2> entries in "stage2" and all of the "stage1", all of the <tree2> entries in "stage2" and all of the
<tree3> entries in "stage3". <tree3> entries in "stage3". When performing a merge of another
branch into the current branch, we use the common ancestor tree
as <tree1>, the current branch head as <tree2>, and the other
branch head as <tree3>.
Furthermore, "git-read-tree" has special-case logic that says: if you see Furthermore, `git-read-tree` has special-case logic that says: if you see
a file that matches in all respects in the following states, it a file that matches in all respects in the following states, it
"collapses" back to "stage0": "collapses" back to "stage0":
- stage 2 and 3 are the same; take one or the other (it makes no - stage 2 and 3 are the same; take one or the other (it makes no
difference - the same work has been done on stage 2 and 3) difference - the same work has been done on our branch in
stage 2 and their branch in stage 3)
- stage 1 and stage 2 are the same and stage 3 is different; take - stage 1 and stage 2 are the same and stage 3 is different; take
stage 3 (some work has been done on stage 3) stage 3 (our branch in stage 2 did not do anything since the
ancestor in stage 1 while their branch in stage 3 worked on
it)
- stage 1 and stage 3 are the same and stage 2 is different take - stage 1 and stage 3 are the same and stage 2 is different take
stage 2 (some work has been done on stage 2) stage 2 (we did something while they did nothing)
The "git-write-tree" command refuses to write a nonsensical tree, and it The `git-write-tree` command refuses to write a nonsensical tree, and it
will complain about unmerged entries if it sees a single entry that is not will complain about unmerged entries if it sees a single entry that is not
stage 0. stage 0.
@ -220,12 +228,10 @@ populated. Here is an outline of how the algorithm works:
matching "stage1" entry if it exists too. .. all the normal matching "stage1" entry if it exists too. .. all the normal
trivial rules .. trivial rules ..
You would normally use "git-merge-index" with supplied You would normally use `git-merge-index` with supplied
"git-merge-one-file" to do this last step. The script `git-merge-one-file` to do this last step. The script updates
does not touch the files in the work tree, and the entire merge the files in the working tree as it merges each path and at the
happens in the index file. In other words, there is no need to end of a successful merge.
worry about what is in the working directory, since it is never
shown and never used.
When you start a 3-way merge with an index file that is already When you start a 3-way merge with an index file that is already
populated, it is assumed that it represents the state of the populated, it is assumed that it represents the state of the
@ -236,33 +242,54 @@ merge refuses to run if it finds an entry in the original index
file that does not match stage 2. file that does not match stage 2.
This is done to prevent you from losing your work-in-progress This is done to prevent you from losing your work-in-progress
changes. To illustrate, suppose you start from what has been changes, and mixing your random changes in an unrelated merge
commit. To illustrate, suppose you start from what has been
commited last to your repository: commited last to your repository:
$ JC=`git-rev-parse --verify "HEAD^0"` ----------------
$ git-checkout-index -f -u -a $JC $ JC=`git-rev-parse --verify "HEAD^0"`
$ git-checkout-index -f -u -a $JC
----------------
You do random edits, without running git-update-index. And then You do random edits, without running git-update-index. And then
you notice that the tip of your "upstream" tree has advanced you notice that the tip of your "upstream" tree has advanced
since you pulled from him: since you pulled from him:
$ git-fetch rsync://.... linus ----------------
$ LT=`cat .git/MERGE_HEAD` $ git-fetch git://.... linus
$ LT=`cat .git/FETCH_HEAD`
----------------
Your work tree is still based on your HEAD ($JC), but you have Your work tree is still based on your HEAD ($JC), but you have
some edits since. Three-way merge makes sure that you have not some edits since. Three-way merge makes sure that you have not
added or modified index entries since $JC, and if you haven't, added or modified index entries since $JC, and if you haven't,
then does the right thing. So with the following sequence: then does the right thing. So with the following sequence:
$ git-read-tree -m -u `git-merge-base $JC $LT` $JC $LT ----------------
$ git-merge-index git-merge-one-file -a $ git-read-tree -m -u `git-merge-base $JC $LT` $JC $LT
$ echo "Merge with Linus" | \ $ git-merge-index git-merge-one-file -a
git-commit-tree `git-write-tree` -p $JC -p $LT $ echo "Merge with Linus" | \
git-commit-tree `git-write-tree` -p $JC -p $LT
----------------
what you would commit is a pure merge between $JC and LT without what you would commit is a pure merge between $JC and $LT without
your work-in-progress changes, and your work tree would be your work-in-progress changes, and your work tree would be
updated to the result of the merge. updated to the result of the merge.
However, if you have local changes in the working tree that
would be overwritten by this merge,`git-read-tree` will refuse
to run to prevent your changes from being lost.
In other words, there is no need to worry about what exists only
in the working tree. When you have local changes in a part of
the project that is not involved in the merge, your changes do
not interfere with the merge, and are kept intact. When they
*do* interfere, the merge does not even start (`git-read-tree`
complains loudly and fails without modifying anything). In such
a case, you can simply continue doing what you were in the
middle of doing, and when your working tree is ready (i.e. you
have finished your work-in-progress), attempt the merge again.
See Also See Also
-------- --------

View File

@ -71,6 +71,10 @@ packed and is served via a dumb transport.
#!/bin/sh #!/bin/sh
exec git-update-server-info exec git-update-server-info
There are other real-world examples of using update and
post-update hooks found in the Documentation/howto directory.
OPTIONS OPTIONS
------- -------
<directory>:: <directory>::

View File

@ -9,7 +9,7 @@ objects into pack files.
SYNOPSIS SYNOPSIS
-------- --------
'git-repack' [-a] [-d] 'git-repack' [-a] [-d] [-l] [-n]
DESCRIPTION DESCRIPTION
----------- -----------
@ -39,6 +39,13 @@ OPTIONS
After packing, if the newly created packs make some After packing, if the newly created packs make some
existing packs redundant, remove the redundant packs. existing packs redundant, remove the redundant packs.
-l::
Pass the `--local` option to `git pack-objects`, see
gitlink:git-pack-objects[1].
-n::
Do not update the server information with
`git update-server-info`.
Author Author
------ ------

View File

@ -7,7 +7,7 @@ git-revert - Revert an existing commit.
SYNOPSIS SYNOPSIS
-------- --------
'git-revert' [-n] <commit> 'git-revert' [--edit | --no-edit] [-n] <commit>
DESCRIPTION DESCRIPTION
----------- -----------
@ -20,7 +20,16 @@ OPTIONS
<commit>:: <commit>::
Commit to revert. Commit to revert.
-n:: -e|--edit::
With this option, `git-revert` will let you edit the commit
message prior committing the revert. This is the default if
you run the command from a terminal.
--no-edit::
With this option, `git-revert` will not start the commit
message editor.
-n|--no-commit::
Usually the command automatically creates a commit with Usually the command automatically creates a commit with
a commit log message stating which commit was reverted. a commit log message stating which commit was reverted.
This flag applies the change necessary to revert the This flag applies the change necessary to revert the

View File

@ -7,23 +7,40 @@ git-show-branch - Show branches and their commits.
SYNOPSIS SYNOPSIS
-------- --------
'git-show-branch [--all] [--heads] [--tags] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] <reference>...' 'git-show-branch [--all] [--heads] [--tags] [--topo-order] [--more=<n> | --list | --independent | --merge-base] [--no-name | --sha1-name] [<rev> | <glob>]...'
DESCRIPTION DESCRIPTION
----------- -----------
Shows the head commits from the named <reference> (or all refs under
$GIT_DIR/refs/heads), and displays concise list of commit logs Shows the commit ancestry graph starting from the commits named
to show their relationship semi-visually. with <rev>s or <globs>s (or all refs under $GIT_DIR/refs/heads
and/or $GIT_DIR/refs/tags) semi-visually.
It cannot show more than 29 branches and commits at a time.
OPTIONS OPTIONS
------- -------
<reference>:: <rev>::
Name of the reference under $GIT_DIR/refs/. Arbitrary extended SHA1 expression (see `git-rev-parse`)
that typically names a branch HEAD or a tag.
<glob>::
A glob pattern that matches branch or tag names under
$GIT_DIR/refs. For example, if you have many topic
branches under $GIT_DIR/refs/heads/topic, giving
`topic/*` would show all of them.
--all --heads --tags:: --all --heads --tags::
Show all refs under $GIT_DIR/refs, $GIT_DIR/refs/heads, Show all refs under $GIT_DIR/refs, $GIT_DIR/refs/heads,
and $GIT_DIR/refs/tags, respectively. and $GIT_DIR/refs/tags, respectively.
--topo-order::
By default, the branches and their commits are shown in
reverse chronological order. This option makes them
appear in topological order (i.e., descendant commits
are shown before their parents).
--more=<n>:: --more=<n>::
Usually the command stops output upon showing the commit Usually the command stops output upon showing the commit
that is the common ancestor of all the branches. This that is the common ancestor of all the branches. This

View File

@ -10,26 +10,6 @@ SYNOPSIS
-------- --------
'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <name> [<head>] 'git-tag' [-a | -s | -u <key-id>] [-f | -d] [-m <msg>] <name> [<head>]
OPTIONS
-------
-a::
Make an unsigned, annotated tag object
-s::
Make a GPG-signed tag, using the default e-mail address's key
-u <key-id>::
Make a GPG-signed tag, using the given key
-f::
Replace an existing tag with the given name (instead of failing)
-d::
Delete an existing tag with the given name
-m <msg>::
Use the given tag message (instead of prompting)
DESCRIPTION DESCRIPTION
----------- -----------
Adds a 'tag' reference in .git/refs/tags/ Adds a 'tag' reference in .git/refs/tags/
@ -52,6 +32,26 @@ GnuPG key for signing.
`-d <tag>` deletes the tag. `-d <tag>` deletes the tag.
OPTIONS
-------
-a::
Make an unsigned, annotated tag object
-s::
Make a GPG-signed tag, using the default e-mail address's key
-u <key-id>::
Make a GPG-signed tag, using the given key
-f::
Replace an existing tag with the given name (instead of failing)
-d::
Delete an existing tag with the given name
-m <msg>::
Use the given tag message (instead of prompting)
Author Author
------ ------

View File

@ -123,7 +123,9 @@ merging.
To pretend you have a file with mode and sha1 at path, say: To pretend you have a file with mode and sha1 at path, say:
$ git-update-index --cacheinfo mode sha1 path ----------------
$ git-update-index --cacheinfo mode sha1 path
----------------
'--info-only' is used to register files without placing them in the object '--info-only' is used to register files without placing them in the object
database. This is useful for status-only repositories. database. This is useful for status-only repositories.
@ -134,11 +136,70 @@ in the database but the file isn't available locally. '--info-only' is
useful when the file is available, but you do not wish to update the useful when the file is available, but you do not wish to update the
object database. object database.
Using --index-info
------------------
`--index-info` is a more powerful mechanism that lets you feed
multiple entry definitions from the standard input, and designed
specifically for scripts. It can take inputs of three formats:
. mode SP sha1 TAB path
+
The first format is what "git-apply --index-info"
reports, and used to reconstruct a partial tree
that is used for phony merge base tree when falling
back on 3-way merge.
. mode SP type SP sha1 TAB path
+
The second format is to stuff git-ls-tree output
into the index file.
. mode SP sha1 SP stage TAB path
+
This format is to put higher order stages into the
index file and matches git-ls-files --stage output.
To place a higher stage entry to the index, the path should
first be removed by feeding a mode=0 entry for the path, and
then feeding necessary input lines in the third format.
For example, starting with this index:
------------
$ git ls-files -s
100644 8a1218a1024a212bb3db30becd860315f9f3ac52 0 frotz
------------
you can feed the following input to `--index-info`:
------------
$ git update-index --index-info
0 0000000000000000000000000000000000000000 frotz
100644 8a1218a1024a212bb3db30becd860315f9f3ac52 1 frotz
100755 8a1218a1024a212bb3db30becd860315f9f3ac52 2 frotz
------------
The first line of the input feeds 0 as the mode to remove the
path; the SHA1 does not matter as long as it is well formatted.
Then the second and third line feeds stage 1 and stage 2 entries
for that path. After the above, we would end up with this:
------------
$ git ls-files -s
100644 8a1218a1024a212bb3db30becd860315f9f3ac52 1 frotz
100755 8a1218a1024a212bb3db30becd860315f9f3ac52 2 frotz
------------
Examples Examples
-------- --------
To update and refresh only the files already checked out: To update and refresh only the files already checked out:
git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh ----------------
$ git-checkout-index -n -f -a && git-update-index --ignore-missing --refresh
----------------
Configuration Configuration
@ -146,12 +207,18 @@ Configuration
The command honors `core.filemode` configuration variable. If The command honors `core.filemode` configuration variable. If
your repository is on an filesystem whose executable bits are your repository is on an filesystem whose executable bits are
unreliable, this should be set to 'false'. This causes the unreliable, this should be set to 'false' (see gitlink:git-repo-config[1]).
command to ignore differences in file modes recorded in the This causes the command to ignore differences in file modes recorded
index and the file mode on the filesystem if they differ only on in the index and the file mode on the filesystem if they differ only on
executable bit. On such an unfortunate filesystem, you may executable bit. On such an unfortunate filesystem, you may
need to use `git-update-index --chmod=`. need to use `git-update-index --chmod=`.
See Also
--------
gitlink:git-repo-config[1]
Author Author
------ ------
Written by Linus Torvalds <torvalds@osdl.org> Written by Linus Torvalds <torvalds@osdl.org>

View File

@ -22,7 +22,7 @@ pull decisions. This command generates such auxiliary files.
OPTIONS OPTIONS
------- -------
--force:: -f|--force::
Update the info files from scratch. Update the info files from scratch.

View File

@ -8,7 +8,7 @@ git-verify-pack - Validate packed git archive files.
SYNOPSIS SYNOPSIS
-------- --------
'git-verify-pack' [-v] <pack>.idx ... 'git-verify-pack' [-v] [--] <pack>.idx ...
DESCRIPTION DESCRIPTION
@ -25,6 +25,8 @@ OPTIONS
-v:: -v::
After verifying the pack, show list of objects contained After verifying the pack, show list of objects contained
in the pack. in the pack.
--::
Do not interpret any more arguments as options.
OUTPUT FORMAT OUTPUT FORMAT
------------- -------------

View File

@ -14,19 +14,21 @@ DESCRIPTION
----------- -----------
Creates a tree object using the current index. Creates a tree object using the current index.
The index must be merged. The index must be in a fully merged state.
Conceptually, "git-write-tree" sync()s the current index contents Conceptually, `git-write-tree` sync()s the current index contents
into a set of tree files. into a set of tree files.
In order to have that match what is actually in your directory right In order to have that match what is actually in your directory right
now, you need to have done a "git-update-index" phase before you did the now, you need to have done a `git-update-index` phase before you did the
"git-write-tree". `git-write-tree`.
OPTIONS OPTIONS
------- -------
--missing-ok:: --missing-ok::
Normally "git-write-tree" ensures that the objects referenced by the Normally `git-write-tree` ensures that the objects referenced by the
directory exist in the object database. This option disables this check. directory exist in the object database. This option disables this
check.
Author Author
------ ------

View File

@ -36,31 +36,35 @@ OPTIONS
CORE GIT COMMANDS CORE GIT COMMANDS
----------------- -----------------
Before reading this cover to cover, you may want to take a look Before reading this cover to cover, you may want to take a look
at the link:tutorial.html[tutorial] document. at the link:tutorial.html[tutorial] document. If you are
migrating from CVS, link:cvs-migration.html[cvs migration]
The <<Discussion>> section below contains much useful definition and
clarification info - read that first. And of the commands, I suggest
reading gitlink:git-update-index[1] and
gitlink:git-read-tree[1] first - I wish I had!
If you are migrating from CVS, link:cvs-migration.html[cvs migration]
document may be helpful after you finish the tutorial. document may be helpful after you finish the tutorial.
The <<Discussion>> section below contains much useful definition
and clarification info - read that first. After that, if you
are interested in using git to manage (version control)
projects, use link:everyday.html[Everyday GIT] as a guide to the
minimum set of commands you need to know for day-to-day work.
After you get the general feel from the tutorial and this After you get the general feel from the tutorial and this
overview page, you may want to take a look at the overview page, you may want to take a look at the
link:howto-index.html[howto] documents. link:howto-index.html[howto] documents.
If you are writing your own Porcelain, you need to be familiar
with most of the low level commands --- I suggest starting from
gitlink:git-update-index[1] and gitlink:git-read-tree[1].
David Greaves <david@dgreaves.com> David Greaves <david@dgreaves.com>
08/05/05 08/05/05
Updated by Junio C Hamano <junkio@cox.net> on 2005-05-05 to Updated by Junio C Hamano <junkio@cox.net> on 2005-05-05 and
reflect recent changes. further on 2005-12-07 to reflect recent changes.
Commands Overview Commands Overview
----------------- -----------------
The git commands can helpfully be split into those that manipulate The git commands can helpfully be split into those that manipulate
the repository, the index and the working fileset, those that the repository, the index and the files in the working tree, those that
interrogate and compare them, and those that moves objects and interrogate and compare them, and those that moves objects and
references between repositories. references between repositories.
@ -79,25 +83,26 @@ gitlink:git-apply[1]::
applies it to the working tree. applies it to the working tree.
gitlink:git-checkout-index[1]:: gitlink:git-checkout-index[1]::
Copy files from the index to the working directory Copy files from the index to the working tree.
gitlink:git-commit-tree[1]:: gitlink:git-commit-tree[1]::
Creates a new commit object Creates a new commit object.
gitlink:git-hash-object[1]:: gitlink:git-hash-object[1]::
Computes the object ID from a file. Computes the object ID from a file.
gitlink:git-index-pack[1]:: gitlink:git-index-pack[1]::
Build pack index file for an existing packed archive. Build pack idx file for an existing packed archive.
gitlink:git-init-db[1]:: gitlink:git-init-db[1]::
Creates an empty git object database Creates an empty git object database, or reinitialize an
existing one.
gitlink:git-merge-index[1]:: gitlink:git-merge-index[1]::
Runs a merge for files needing merging Runs a merge for files needing merging.
gitlink:git-mktag[1]:: gitlink:git-mktag[1]::
Creates a tag object Creates a tag object.
gitlink:git-pack-objects[1]:: gitlink:git-pack-objects[1]::
Creates a packed archive of objects. Creates a packed archive of objects.
@ -106,7 +111,7 @@ gitlink:git-prune-packed[1]::
Remove extra objects that are already in pack files. Remove extra objects that are already in pack files.
gitlink:git-read-tree[1]:: gitlink:git-read-tree[1]::
Reads tree information into the directory index Reads tree information into the index.
gitlink:git-repo-config[1]:: gitlink:git-repo-config[1]::
Get and set options in .git/config. Get and set options in .git/config.
@ -115,65 +120,65 @@ gitlink:git-unpack-objects[1]::
Unpacks objects out of a packed archive. Unpacks objects out of a packed archive.
gitlink:git-update-index[1]:: gitlink:git-update-index[1]::
Modifies the index or directory cache Registers files in the working tree to the index.
gitlink:git-write-tree[1]:: gitlink:git-write-tree[1]::
Creates a tree from the current index Creates a tree from the index.
Interrogation commands Interrogation commands
~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~
gitlink:git-cat-file[1]:: gitlink:git-cat-file[1]::
Provide content or type information for repository objects Provide content or type/size information for repository objects.
gitlink:git-diff-index[1]:: gitlink:git-diff-index[1]::
Compares content and mode of blobs between the index and repository Compares content and mode of blobs between the index and repository.
gitlink:git-diff-files[1]:: gitlink:git-diff-files[1]::
Compares files in the working tree and the index Compares files in the working tree and the index.
gitlink:git-diff-stages[1]:: gitlink:git-diff-stages[1]::
Compares two "merge stages" in the index file. Compares two "merge stages" in the index.
gitlink:git-diff-tree[1]:: gitlink:git-diff-tree[1]::
Compares the content and mode of blobs found via two tree objects Compares the content and mode of blobs found via two tree objects.
gitlink:git-fsck-objects[1]:: gitlink:git-fsck-objects[1]::
Verifies the connectivity and validity of the objects in the database Verifies the connectivity and validity of the objects in the database.
gitlink:git-ls-files[1]:: gitlink:git-ls-files[1]::
Information about files in the index/working directory Information about files in the index and the working tree.
gitlink:git-ls-tree[1]:: gitlink:git-ls-tree[1]::
Displays a tree object in human readable form Displays a tree object in human readable form.
gitlink:git-merge-base[1]:: gitlink:git-merge-base[1]::
Finds as good a common ancestor as possible for a merge Finds as good common ancestors as possible for a merge.
gitlink:git-name-rev[1]:: gitlink:git-name-rev[1]::
Find symbolic names for given revs Find symbolic names for given revs.
gitlink:git-rev-list[1]:: gitlink:git-rev-list[1]::
Lists commit objects in reverse chronological order Lists commit objects in reverse chronological order.
gitlink:git-show-index[1]:: gitlink:git-show-index[1]::
Displays contents of a pack idx file. Displays contents of a pack idx file.
gitlink:git-tar-tree[1]:: gitlink:git-tar-tree[1]::
Creates a tar archive of the files in the named tree Creates a tar archive of the files in the named tree object.
gitlink:git-unpack-file[1]:: gitlink:git-unpack-file[1]::
Creates a temporary file with a blob's contents Creates a temporary file with a blob's contents.
gitlink:git-var[1]:: gitlink:git-var[1]::
Displays a git logical variable Displays a git logical variable.
gitlink:git-verify-pack[1]:: gitlink:git-verify-pack[1]::
Validates packed git archive files Validates packed git archive files.
The interrogate commands may create files - and you can force them to In general, the interrogate commands do not touch the files in
touch the working file set - but in general they don't the working tree.
Synching repositories Synching repositories
@ -181,19 +186,24 @@ Synching repositories
gitlink:git-clone-pack[1]:: gitlink:git-clone-pack[1]::
Clones a repository into the current repository (engine Clones a repository into the current repository (engine
for ssh and local transport) for ssh and local transport).
gitlink:git-fetch-pack[1]:: gitlink:git-fetch-pack[1]::
Updates from a remote repository. Updates from a remote repository (engine for ssh and
local transport).
gitlink:git-http-fetch[1]:: gitlink:git-http-fetch[1]::
Downloads a remote git repository via HTTP Downloads a remote git repository via HTTP by walking
commit chain.
gitlink:git-local-fetch[1]:: gitlink:git-local-fetch[1]::
Duplicates another git repository on a local system Duplicates another git repository on a local system by
walking commit chain.
gitlink:git-peek-remote[1]:: gitlink:git-peek-remote[1]::
Lists references on a remote repository using upload-pack protocol. Lists references on a remote repository using
upload-pack protocol (engine for ssh and local
transport).
gitlink:git-receive-pack[1]:: gitlink:git-receive-pack[1]::
Invoked by 'git-send-pack' to receive what is pushed to it. Invoked by 'git-send-pack' to receive what is pushed to it.
@ -205,10 +215,11 @@ gitlink:git-shell[1]::
Restricted shell for GIT-only SSH access. Restricted shell for GIT-only SSH access.
gitlink:git-ssh-fetch[1]:: gitlink:git-ssh-fetch[1]::
Pulls from a remote repository over ssh connection Pulls from a remote repository over ssh connection by
walking commit chain.
gitlink:git-ssh-upload[1]:: gitlink:git-ssh-upload[1]::
Helper "server-side" program used by git-ssh-fetch Helper "server-side" program used by git-ssh-fetch.
gitlink:git-update-server-info[1]:: gitlink:git-update-server-info[1]::
Updates auxiliary information on a dumb server to help Updates auxiliary information on a dumb server to help
@ -223,16 +234,16 @@ Porcelain-ish Commands
---------------------- ----------------------
gitlink:git-add[1]:: gitlink:git-add[1]::
Add paths to the index file. Add paths to the index.
gitlink:git-am[1]:: gitlink:git-am[1]::
Apply patches from a mailbox, but cooler. Apply patches from a mailbox, but cooler.
gitlink:git-applymbox[1]:: gitlink:git-applymbox[1]::
Apply patches from a mailbox. Apply patches from a mailbox, original version by Linus.
gitlink:git-bisect[1]:: gitlink:git-bisect[1]::
Find the change that introduced a bug. Find the change that introduced a bug by binary search.
gitlink:git-branch[1]:: gitlink:git-branch[1]::
Create and Show branches. Create and Show branches.
@ -259,7 +270,7 @@ gitlink:git-format-patch[1]::
Prepare patches for e-mail submission. Prepare patches for e-mail submission.
gitlink:git-grep[1]:: gitlink:git-grep[1]::
Print lines matching a pattern Print lines matching a pattern.
gitlink:git-log[1]:: gitlink:git-log[1]::
Shows commit logs. Shows commit logs.
@ -283,7 +294,7 @@ gitlink:git-push[1]::
Update remote refs along with associated objects. Update remote refs along with associated objects.
gitlink:git-rebase[1]:: gitlink:git-rebase[1]::
Rebase local commits to new upstream head. Rebase local commits to the updated upstream head.
gitlink:git-repack[1]:: gitlink:git-repack[1]::
Pack unpacked objects in a repository. Pack unpacked objects in a repository.
@ -324,7 +335,7 @@ gitlink:git-archimport[1]::
Import an arch repository into git. Import an arch repository into git.
gitlink:git-convert-objects[1]:: gitlink:git-convert-objects[1]::
Converts old-style git repository Converts old-style git repository.
gitlink:git-cvsimport[1]:: gitlink:git-cvsimport[1]::
Salvage your data out of another SCM people love to hate. Salvage your data out of another SCM people love to hate.
@ -333,10 +344,10 @@ gitlink:git-lost-found[1]::
Recover lost refs that luckily have not yet been pruned. Recover lost refs that luckily have not yet been pruned.
gitlink:git-merge-one-file[1]:: gitlink:git-merge-one-file[1]::
The standard helper program to use with "git-merge-index" The standard helper program to use with `git-merge-index`.
gitlink:git-prune[1]:: gitlink:git-prune[1]::
Prunes all unreachable objects from the object database Prunes all unreachable objects from the object database.
gitlink:git-relink[1]:: gitlink:git-relink[1]::
Hardlink common objects in local repositories. Hardlink common objects in local repositories.
@ -348,10 +359,10 @@ gitlink:git-sh-setup[1]::
Common git shell script setup code. Common git shell script setup code.
gitlink:git-symbolic-ref[1]:: gitlink:git-symbolic-ref[1]::
Read and modify symbolic refs Read and modify symbolic refs.
gitlink:git-tag[1]:: gitlink:git-tag[1]::
An example script to create a tag object signed with GPG An example script to create a tag object signed with GPG.
gitlink:git-update-ref[1]:: gitlink:git-update-ref[1]::
Update the object name stored in a ref safely. Update the object name stored in a ref safely.
@ -375,16 +386,19 @@ gitlink:git-get-tar-commit-id[1]::
Extract commit ID from an archive created using git-tar-tree. Extract commit ID from an archive created using git-tar-tree.
gitlink:git-mailinfo[1]:: gitlink:git-mailinfo[1]::
Extracts patch from a single e-mail message. Extracts patch and authorship information from a single
e-mail message, optionally transliterating the commit
message into utf-8.
gitlink:git-mailsplit[1]:: gitlink:git-mailsplit[1]::
git-mailsplit. A stupid program to split UNIX mbox format mailbox into
individual pieces of e-mail.
gitlink:git-patch-id[1]:: gitlink:git-patch-id[1]::
Compute unique ID for a patch. Compute unique ID for a patch.
gitlink:git-parse-remote[1]:: gitlink:git-parse-remote[1]::
Routines to help parsing $GIT_DIR/remotes/ Routines to help parsing `$GIT_DIR/remotes/` files.
gitlink:git-request-pull[1]:: gitlink:git-request-pull[1]::
git-request-pull. git-request-pull.
@ -406,22 +420,20 @@ Commands not yet documented
--------------------------- ---------------------------
gitlink:gitk[1]:: gitlink:gitk[1]::
gitk. The gitk repository browser.
Configuration Mechanism Configuration Mechanism
----------------------- -----------------------
Starting from 0.99.9 (actually mid 0.99.8.GIT), .git/config file Starting from 0.99.9 (actually mid 0.99.8.GIT), `.git/config` file
is used to hold per-repository configuration options. It is a is used to hold per-repository configuration options. It is a
simple text file modelled after `.ini` format familiar to some simple text file modelled after `.ini` format familiar to some
people. Here is an example: people. Here is an example:
------------ ------------
# #
# This is the config file, and # A '#' or ';' character indicates a comment.
# a '#' or ';' character indicates
# a comment
# #
; core variables ; core variables
@ -443,30 +455,30 @@ their operation accordingly.
Identifier Terminology Identifier Terminology
---------------------- ----------------------
<object>:: <object>::
Indicates the sha1 identifier for any type of object Indicates the object name for any type of object.
<blob>:: <blob>::
Indicates a blob object sha1 identifier Indicates a blob object name.
<tree>:: <tree>::
Indicates a tree object sha1 identifier Indicates a tree object name.
<commit>:: <commit>::
Indicates a commit object sha1 identifier Indicates a commit object name.
<tree-ish>:: <tree-ish>::
Indicates a tree, commit or tag object sha1 identifier. A Indicates a tree, commit or tag object name. A
command that takes a <tree-ish> argument ultimately wants to command that takes a <tree-ish> argument ultimately wants to
operate on a <tree> object but automatically dereferences operate on a <tree> object but automatically dereferences
<commit> and <tag> objects that point at a <tree>. <commit> and <tag> objects that point at a <tree>.
<type>:: <type>::
Indicates that an object type is required. Indicates that an object type is required.
Currently one of: blob/tree/commit/tag Currently one of: `blob`, `tree`, `commit`, or `tag`.
<file>:: <file>::
Indicates a filename - always relative to the root of Indicates a filename - almost always relative to the
the tree structure GIT_INDEX_FILE describes. root of the tree structure `GIT_INDEX_FILE` describes.
Symbolic Identifiers Symbolic Identifiers
-------------------- --------------------
@ -474,17 +486,20 @@ Any git command accepting any <object> can also use the following
symbolic notation: symbolic notation:
HEAD:: HEAD::
indicates the head of the repository (ie the contents of indicates the head of the current branch (i.e. the
`$GIT_DIR/HEAD`) contents of `$GIT_DIR/HEAD`).
<tag>:: <tag>::
a valid tag 'name'+ a valid tag 'name'
(ie the contents of `$GIT_DIR/refs/tags/<tag>`) (i.e. the contents of `$GIT_DIR/refs/tags/<tag>`).
<head>:: <head>::
a valid head 'name'+ a valid head 'name'
(ie the contents of `$GIT_DIR/refs/heads/<head>`) (i.e. the contents of `$GIT_DIR/refs/heads/<head>`).
<snap>:: <snap>::
a valid snapshot 'name'+ a valid snapshot 'name'
(ie the contents of `$GIT_DIR/refs/snap/<snap>`) (i.e. the contents of `$GIT_DIR/refs/snap/<snap>`).
File/Directory Structure File/Directory Structure
@ -493,7 +508,7 @@ File/Directory Structure
Please see link:repository-layout.html[repository layout] document. Please see link:repository-layout.html[repository layout] document.
Higher level SCMs may provide and manage additional information in the Higher level SCMs may provide and manage additional information in the
GIT_DIR. `$GIT_DIR`.
Terminology Terminology
@ -509,7 +524,7 @@ The git Repository
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
These environment variables apply to 'all' core git commands. Nb: it These environment variables apply to 'all' core git commands. Nb: it
is worth noting that they may be used/overridden by SCMS sitting above is worth noting that they may be used/overridden by SCMS sitting above
git so take care if using Cogito etc git so take care if using Cogito etc.
'GIT_INDEX_FILE':: 'GIT_INDEX_FILE'::
This environment allows the specification of an alternate This environment allows the specification of an alternate
@ -530,9 +545,9 @@ git so take care if using Cogito etc
written to these directories. written to these directories.
'GIT_DIR':: 'GIT_DIR'::
If the 'GIT_DIR' environment variable is set then it specifies If the 'GIT_DIR' environment variable is set then it
a path to use instead of `./.git` for the base of the specifies a path to use instead of the default `.git`
repository. for the base of the repository.
git Commits git Commits
~~~~~~~~~~~ ~~~~~~~~~~~
@ -559,7 +574,7 @@ include::../README[]
Authors Authors
------- -------
git's founding father is Linus Torvalds <torvalds@osdl.org>. git's founding father is Linus Torvalds <torvalds@osdl.org>.
The current git nurse is Junio C. Hamano <junkio@cox.net>. The current git nurse is Junio C Hamano <junkio@cox.net>.
The git potty was written by Andres Ericsson <ae@op5.se>. The git potty was written by Andres Ericsson <ae@op5.se>.
General upbringing is handled by the git-list <git@vger.kernel.org>. General upbringing is handled by the git-list <git@vger.kernel.org>.

View File

@ -20,7 +20,7 @@ hash::
object database:: object database::
Stores a set of "objects", and an individial object is identified Stores a set of "objects", and an individial object is identified
by its object name. The object usually live in $GIT_DIR/objects/. by its object name. The objects usually live in `$GIT_DIR/objects/`.
blob object:: blob object::
Untyped object, e.g. the contents of a file. Untyped object, e.g. the contents of a file.
@ -109,15 +109,15 @@ head::
branch:: branch::
A non-cyclical graph of revisions, i.e. the complete history of A non-cyclical graph of revisions, i.e. the complete history of
a particular revision, which is called the branch head. The a particular revision, which is called the branch head. The
branch heads are stored in $GIT_DIR/refs/heads/. branch heads are stored in `$GIT_DIR/refs/heads/`.
ref:: ref::
A 40-byte hex representation of a SHA1 pointing to a particular A 40-byte hex representation of a SHA1 pointing to a particular
object. These may be stored in $GIT_DIR/refs/. object. These may be stored in `$GIT_DIR/refs/`.
head ref:: head ref::
A ref pointing to a head. Often, this is abbreviated to "head". A ref pointing to a head. Often, this is abbreviated to "head".
Head refs are stored in $GIT_DIR/refs/heads/. Head refs are stored in `$GIT_DIR/refs/heads/`.
tree-ish:: tree-ish::
A ref pointing to either a commit object, a tree object, or a A ref pointing to either a commit object, a tree object, or a
@ -125,7 +125,7 @@ tree-ish::
ent:: ent::
Favorite synonym to "tree-ish" by some total geeks. See Favorite synonym to "tree-ish" by some total geeks. See
http://en.wikipedia.org/wiki/Ent_(Middle-earth) for an in-depth `http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
explanation. explanation.
tag object:: tag object::
@ -137,7 +137,7 @@ tag object::
tag:: tag::
A ref pointing to a tag or commit object. In contrast to a head, A ref pointing to a tag or commit object. In contrast to a head,
a tag is not changed by a commit. Tags (not tag objects) are a tag is not changed by a commit. Tags (not tag objects) are
stored in $GIT_DIR/refs/tags/. A git tag has nothing to do with stored in `$GIT_DIR/refs/tags/`. A git tag has nothing to do with
a Lisp tag (which is called object type in git's context). a Lisp tag (which is called object type in git's context).
A tag is most typically used to mark a particular point in the A tag is most typically used to mark a particular point in the
commit ancestry chain. commit ancestry chain.

View File

@ -1,4 +1,4 @@
From: Junio C Hamano <junkio@cox.net> From: Junio C Hamano <junkio@cox.net> and Carl Baldwin <cnb@fc.hp.com>
Subject: control access to branches. Subject: control access to branches.
Date: Thu, 17 Nov 2005 23:55:32 -0800 Date: Thu, 17 Nov 2005 23:55:32 -0800
Message-ID: <7vfypumlu3.fsf@assigned-by-dhcp.cox.net> Message-ID: <7vfypumlu3.fsf@assigned-by-dhcp.cox.net>
@ -26,63 +26,137 @@ section of the documentation:
So if your policy is (1) always require fast-forward push So if your policy is (1) always require fast-forward push
(i.e. never allow "git-push repo +branch:branch"), (2) you (i.e. never allow "git-push repo +branch:branch"), (2) you
have a list of users allowed to update each branch, and (3) you have a list of users allowed to update each branch, and (3) you
do not let tags to be overwritten, then: do not let tags to be overwritten, then you can use something
like this as your hooks/update script.
#!/bin/sh [jc: editorial note. This is a much improved version by Carl
# This is a sample hooks/update script, written by JC since I posted the original outline]
# in his e-mail buffer, so naturally it is not tested
# but hopefully would convey the idea.
umask 002 -- >8 -- beginning of script -- >8 --
case "$1" in
refs/tags/*)
# No overwriting an existing tag
if test -f "$GIT_DIR/$1"
then
exit 1
fi
;;
refs/heads/*)
# No rebasing or rewinding
if expr "$2" : '0*$' >/dev/null
then
# creating a new branch
;
else
# updating -- make sure it is a fast forward
mb=`git-merge-base "$2" "$3"`
case "$mb,$2" in
"$2,$mb")
;; # fast forward -- happy
*)
exit 1 ;; # unhappy
esac
fi
;;
*)
# No funny refs allowed
exit 1
;;
esac
# Is the user allowed to update it? #!/bin/bash
me=`id -u -n` ;# e.g. "junio"
while read head_pattern users
do
if expr "$1" : "$head_pattern" >/dev/null
then
case " $users " in
*" $me "*)
exit 0 ;; # happy
' * ')
exit 0 ;; # anybody
esac
fi
done
exit 1
For the sake of simplicity, I assumed that you keep something umask 002
like this in $GIT_DIR/info/allowed-pushers file:
# If you are having trouble with this access control hook script
# you can try setting this to true. It will tell you exactly
# why a user is being allowed/denied access.
verbose=false
# Default shell globbing messes things up downstream
GLOBIGNORE=*
function grant {
$verbose && echo >&2 "-Grant- $1"
echo grant
exit 0
}
function deny {
$verbose && echo >&2 "-Deny- $1"
echo deny
exit 1
}
function info {
$verbose && echo >&2 "-Info- $1"
}
# Implement generic branch and tag policies.
# - Tags should not be updated once created.
# - Branches should only be fast-forwarded.
case "$1" in
refs/tags/*)
[ -f "$GIT_DIR/$1" ] &&
deny >/dev/null "You can't overwrite an existing tag"
;;
refs/heads/*)
# No rebasing or rewinding
if expr "$2" : '0*$' >/dev/null; then
info "The branch '$1' is new..."
else
# updating -- make sure it is a fast forward
mb=$(git-merge-base "$2" "$3")
case "$mb,$2" in
"$2,$mb") info "Update is fast-forward" ;;
*) deny >/dev/null "This is not a fast-forward update." ;;
esac
fi
;;
*)
deny >/dev/null \
"Branch is not under refs/heads or refs/tags. What are you trying to do?"
;;
esac
# Implement per-branch controls based on username
allowed_users_file=$GIT_DIR/info/allowed-users
username=$(id -u -n)
info "The user is: '$username'"
if [ -f "$allowed_users_file" ]; then
rc=$(cat $allowed_users_file | grep -v '^#' | grep -v '^$' |
while read head_pattern user_patterns; do
matchlen=$(expr "$1" : "$head_pattern")
if [ "$matchlen" == "${#1}" ]; then
info "Found matching head pattern: '$head_pattern'"
for user_pattern in $user_patterns; do
info "Checking user: '$username' against pattern: '$user_pattern'"
matchlen=$(expr "$username" : "$user_pattern")
if [ "$matchlen" == "${#username}" ]; then
grant "Allowing user: '$username' with pattern: '$user_pattern'"
fi
done
deny "The user is not in the access list for this branch"
fi
done
)
case "$rc" in
grant) grant >/dev/null "Granting access based on $allowed_users_file" ;;
deny) deny >/dev/null "Denying access based on $allowed_users_file" ;;
*) ;;
esac
fi
allowed_groups_file=$GIT_DIR/info/allowed-groups
groups=$(id -G -n)
info "The user belongs to the following groups:"
info "'$groups'"
if [ -f "$allowed_groups_file" ]; then
rc=$(cat $allowed_groups_file | grep -v '^#' | grep -v '^$' |
while read head_pattern group_patterns; do
matchlen=$(expr "$1" : "$head_pattern")
if [ "$matchlen" == "${#1}" ]; then
info "Found matching head pattern: '$head_pattern'"
for group_pattern in $group_patterns; do
for groupname in $groups; do
info "Checking group: '$groupname' against pattern: '$group_pattern'"
matchlen=$(expr "$groupname" : "$group_pattern")
if [ "$matchlen" == "${#groupname}" ]; then
grant "Allowing group: '$groupname' with pattern: '$group_pattern'"
fi
done
done
deny "None of the user's groups are in the access list for this branch"
fi
done
)
case "$rc" in
grant) grant >/dev/null "Granting access based on $allowed_groups_file" ;;
deny) deny >/dev/null "Denying access based on $allowed_groups_file" ;;
*) ;;
esac
fi
deny >/dev/null "There are no more rules to check. Denying access"
-- >8 -- end of script -- >8 --
This uses two files, $GIT_DIR/info/allowed-users and
allowed-groups, to describe which heads can be pushed into by
whom. The format of each file would look like this:
refs/heads/master junio refs/heads/master junio
refs/heads/cogito$ pasky refs/heads/cogito$ pasky
@ -91,15 +165,8 @@ like this in $GIT_DIR/info/allowed-pushers file:
refs/tags/v[0-9]* junio refs/tags/v[0-9]* junio
With this, Linus can push or create "bw/penguin" or "bw/zebra" With this, Linus can push or create "bw/penguin" or "bw/zebra"
or "bw/panda" branches, Pasky can do only "cogito", and I can do or "bw/panda" branches, Pasky can do only "cogito", and JC can
master branch and make versioned tags. And anybody can do do master branch and make versioned tags. And anybody can do
tmp/blah branches. This assumes all the users are in a single tmp/blah branches.
group that can write into $GIT_DIR/ and underneath.
------------

View File

@ -11,6 +11,6 @@
Use the given merge strategy; can be supplied more than Use the given merge strategy; can be supplied more than
once to specify them in the order they should be tried. once to specify them in the order they should be tried.
If there is no `-s` option, a built-in list of strategies If there is no `-s` option, a built-in list of strategies
is used instead (`git-merge-resolve` when merging a single is used instead (`git-merge-recursive` when merging a single
head, `git-merge-octopus` otherwise). head, `git-merge-octopus` otherwise).

View File

@ -6,27 +6,27 @@ resolve::
and another branch you pulled from) using 3-way merge and another branch you pulled from) using 3-way merge
algorithm. It tries to carefully detect criss-cross algorithm. It tries to carefully detect criss-cross
merge ambiguities and is considered generally safe and merge ambiguities and is considered generally safe and
fast. This is the default merge strategy when pulling fast.
one branch.
recursive:: recursive::
This can only resolve two heads using 3-way merge This can only resolve two heads using 3-way merge
algorithm. When there are more than one common algorithm. When there are more than one common
ancestors that can be used for 3-way merge, it creates a ancestors that can be used for 3-way merge, it creates a
merged tree of the common ancestores and uses that as merged tree of the common ancestors and uses that as
the reference tree for the 3-way merge. This has been the reference tree for the 3-way merge. This has been
reported to result in fewer merge conflicts without reported to result in fewer merge conflicts without
causing mis-merges by tests done on actual merge commits causing mis-merges by tests done on actual merge commits
taken from Linux 2.6 kernel development history. taken from Linux 2.6 kernel development history.
Additionally this can detect and handle merges involving Additionally this can detect and handle merges involving
renames. renames. This is the default merge strategy when
pulling or merging one branch.
octopus:: octopus::
This resolves more than two-head case, but refuses to do This resolves more than two-head case, but refuses to do
complex merge that needs manual resolution. It is complex merge that needs manual resolution. It is
primarily meant to be used for bundling topic branch primarily meant to be used for bundling topic branch
heads together. This is the default merge strategy when heads together. This is the default merge strategy when
pulling more than one branch. pulling or merging more than one branches.
ours:: ours::
This resolves any number of heads, but the result of the This resolves any number of heads, but the result of the

View File

@ -42,7 +42,6 @@ sub no_spaces ($) {
print 'GIT Glossary print 'GIT Glossary
============ ============
Aug 2005
This list is sorted alphabetically: This list is sorted alphabetically:

View File

@ -1282,26 +1282,27 @@ fatal: merge program failed
`git-merge-one-file` script is called with parameters to `git-merge-one-file` script is called with parameters to
describe those three versions, and is responsible to leave the describe those three versions, and is responsible to leave the
merge results in the working tree and register it in the index merge results in the working tree.
file. It is a fairly straightforward shell script, and It is a fairly straightforward shell script, and
eventually calls `merge` program from RCS suite to perform the eventually calls `merge` program from RCS suite to perform a
file-level 3-way merge. In this case, `merge` detects file-level 3-way merge. In this case, `merge` detects
conflicts, and the merge result with conflict marks is left in conflicts, and the merge result with conflict marks is left in
the working tree, while the index file is updated with the the working tree.. This can be seen if you run `ls-files
version from the current branch (this is to make `git diff`
useful after this step). This can be seen if you run `ls-files
--stage` again at this point: --stage` again at this point:
------------ ------------
$ git-ls-files --stage $ git-ls-files --stage
100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example 100644 7f8b141b65fdcee47321e399a2598a235a032422 0 example
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 0 hello 100644 263414f423d0e4d70dae8fe53fa34614ff3e2860 1 hello
100644 06fa6a24256dc7e560efa5687fa84b51f0263c3a 2 hello
100644 cc44c73eb783565da5831b4d820c962954019b69 3 hello
------------ ------------
As you can see, there is no unmerged paths in the index file.
This is the state of the index file and the working file after This is the state of the index file and the working file after
`git merge` returns control back to you, leaving the conflicting `git merge` returns control back to you, leaving the conflicting
merge for you to resolve. merge for you to resolve. Notice that the path `hello` is still
unmerged, and what you see with `git diff` at this point is
differences since stage 2 (i.e. your version).
Publishing your work Publishing your work
@ -1636,14 +1637,49 @@ fast forward. You need to pull and merge those other changes
back before you push your work when it happens. back before you push your work when it happens.
Advanced Shared Repository Management
-------------------------------------
Being able to push into a shared repository means being able to
write into it. If your developers are coming over the network,
this means you, as the repository administrator, need to give
each of them an SSH access to the shared repository machine.
In some cases, though, you may not want to give a normal shell
account to them, but want to restrict them to be able to only
do `git push` into the repository and nothing else.
You can achieve this by setting the login shell of your
developers on the shared repository host to `git-shell` program.
[NOTE]
Most likely you would also need to list `git-shell` program in
`/etc/shells` file.
This restricts the set of commands that can be run from incoming
SSH connection for these users to only `receive-pack` and
`upload-pack`, so the only thing they can do are `git fetch` and
`git push`.
You still need to create UNIX user accounts for each developer,
and put them in the same group. Make sure that the repository
shared among these developers is writable by that group.
You can implement finer grained branch policies using update
hooks. There is a document ("control access to branches") in
Documentation/howto by Carl Baldwin and JC outlining how to (1)
limit access to branch per user, (2) forbid overwriting existing
tags.
Bundling your work together Bundling your work together
--------------------------- ---------------------------
It is likely that you will be working on more than one thing at It is likely that you will be working on more than one thing at
a time. It is easy to use those more-or-less independent tasks a time. It is easy to manage those more-or-less independent tasks
using branches with git. using branches with git.
We have already seen how branches work in a previous example, We have already seen how branches work previously,
with "fun and work" example using two branches. The idea is the with "fun and work" example using two branches. The idea is the
same if there are more than two branches. Let's say you started same if there are more than two branches. Let's say you started
out from "master" head, and have some new code in the "master" out from "master" head, and have some new code in the "master"

View File

@ -87,3 +87,6 @@ Issues of note:
have all the libraries/tools needed, or you may have have all the libraries/tools needed, or you may have
necessary libraries at unusual locations. Please look at the necessary libraries at unusual locations. Please look at the
top of the Makefile to see what can be adjusted for your needs. top of the Makefile to see what can be adjusted for your needs.
You can place local settings in config.mak and the Makefile
will include them. Note that config.mak is not distributed;
the name is reserved for local settings.

View File

@ -55,7 +55,7 @@ all:
# Define USE_STDEV below if you want git to care about the underlying device # Define USE_STDEV below if you want git to care about the underlying device
# change being considered an inode change from the update-cache perspective. # change being considered an inode change from the update-cache perspective.
GIT_VERSION = 0.99.9l GIT_VERSION = 0.99.9m
# CFLAGS and LDFLAGS are for the users to override from the command line. # CFLAGS and LDFLAGS are for the users to override from the command line.
@ -162,7 +162,7 @@ LIB_FILE=libgit.a
LIB_H = \ LIB_H = \
blob.h cache.h commit.h count-delta.h csum-file.h delta.h \ blob.h cache.h commit.h count-delta.h csum-file.h delta.h \
diff.h epoch.h object.h pack.h pkt-line.h quote.h refs.h \ diff.h epoch.h object.h pack.h pkt-line.h quote.h refs.h \
run-command.h strbuf.h tag.h tree.h run-command.h strbuf.h tag.h tree.h git-compat-util.h
DIFF_OBJS = \ DIFF_OBJS = \
diff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \ diff.o diffcore-break.o diffcore-order.o diffcore-pathspec.o \
@ -243,6 +243,10 @@ ifeq ($(uname_S),NetBSD)
ALL_CFLAGS += -I/usr/pkg/include ALL_CFLAGS += -I/usr/pkg/include
ALL_LDFLAGS += -L/usr/pkg/lib -Wl,-rpath,/usr/pkg/lib ALL_LDFLAGS += -L/usr/pkg/lib -Wl,-rpath,/usr/pkg/lib
endif endif
ifeq ($(uname_S),AIX)
NO_STRCASESTR=YesPlease
NEEDS_LIBICONV=YesPlease
endif
ifneq (,$(findstring arm,$(uname_M))) ifneq (,$(findstring arm,$(uname_M)))
ARM_SHA1 = YesPlease ARM_SHA1 = YesPlease
endif endif
@ -320,15 +324,15 @@ ifdef NEEDS_NSL
SIMPLE_LIB += -lnsl SIMPLE_LIB += -lnsl
endif endif
ifdef NO_STRCASESTR ifdef NO_STRCASESTR
COMPAT_CFLAGS += -Dstrcasestr=gitstrcasestr -DNO_STRCASESTR=1 COMPAT_CFLAGS += -DNO_STRCASESTR
COMPAT_OBJS += compat/strcasestr.o COMPAT_OBJS += compat/strcasestr.o
endif endif
ifdef NO_SETENV ifdef NO_SETENV
COMPAT_CFLAGS += -Dsetenv=gitsetenv -DNO_SETENV=1 COMPAT_CFLAGS += -DNO_SETENV
COMPAT_OBJS += compat/setenv.o COMPAT_OBJS += compat/setenv.o
endif endif
ifdef NO_MMAP ifdef NO_MMAP
COMPAT_CFLAGS += -Dmmap=gitfakemmap -Dmunmap=gitfakemunmap -DNO_MMAP COMPAT_CFLAGS += -DNO_MMAP
COMPAT_OBJS += compat/mmap.o COMPAT_OBJS += compat/mmap.o
endif endif
ifdef NO_IPV6 ifdef NO_IPV6
@ -363,9 +367,9 @@ all: $(ALL_PROGRAMS)
all: all:
$(MAKE) -C templates $(MAKE) -C templates
git$(X): git.c $(COMPAT_OBJS) Makefile git$X: git.c $(LIB_FILE) Makefile
$(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \ $(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
$(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(filter %.o,$^) $(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE)
$(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh $(patsubst %.sh,%,$(SCRIPT_SH)) : % : %.sh
rm -f $@ rm -f $@

17
README
View File

@ -35,7 +35,7 @@ the object (i.e. how it is used, and how it can refer to other
objects). There are currently four different object types: "blob", objects). There are currently four different object types: "blob",
"tree", "commit" and "tag". "tree", "commit" and "tag".
A "blob" object cannot refer to any other object, and is, like the tag A "blob" object cannot refer to any other object, and is, like the type
implies, a pure storage object containing some user data. It is used to implies, a pure storage object containing some user data. It is used to
actually store the file data, i.e. a blob object is associated with some actually store the file data, i.e. a blob object is associated with some
particular version of some file. particular version of some file.
@ -64,7 +64,7 @@ symbolic name (of course!) and, optionally, a signature.
Regardless of object type, all objects share the following Regardless of object type, all objects share the following
characteristics: they are all deflated with zlib, and have a header characteristics: they are all deflated with zlib, and have a header
that not only specifies their tag, but also provides size information that not only specifies their type, but also provides size information
about the data in the object. It's worth noting that the SHA1 hash about the data in the object. It's worth noting that the SHA1 hash
that is used to name the object is the hash of the original data that is used to name the object is the hash of the original data
plus this header, so `sha1sum` 'file' does not match the object name plus this header, so `sha1sum` 'file' does not match the object name
@ -76,7 +76,7 @@ As a result, the general consistency of an object can always be tested
independently of the contents or the type of the object: all objects can independently of the contents or the type of the object: all objects can
be validated by verifying that (a) their hashes match the content of the be validated by verifying that (a) their hashes match the content of the
file and (b) the object successfully inflates to a stream of bytes that file and (b) the object successfully inflates to a stream of bytes that
forms a sequence of <ascii tag without space> + <space> + <ascii decimal forms a sequence of <ascii type without space> + <space> + <ascii decimal
size> + <byte\0> + <binary object data>. size> + <byte\0> + <binary object data>.
The structured objects can further have their structure and The structured objects can further have their structure and
@ -297,7 +297,7 @@ will not normally add totally new entries or remove old entries,
i.e. it will normally just update existing cache entries. i.e. it will normally just update existing cache entries.
To tell git that yes, you really do realize that certain files no To tell git that yes, you really do realize that certain files no
longer exist in the archive, or that new files should be added, you longer exist, or that new files should be added, you
should use the `--remove` and `--add` flags respectively. should use the `--remove` and `--add` flags respectively.
NOTE! A `--remove` flag does 'not' mean that subsequent filenames will NOTE! A `--remove` flag does 'not' mean that subsequent filenames will
@ -515,8 +515,11 @@ index file, and you can just write the result out with
Historical note. We did not have `-u` facility when this Historical note. We did not have `-u` facility when this
section was first written, so we used to warn that section was first written, so we used to warn that
the merge is done in the index file, not in your the merge is done in the index file, not in your
working directory, and your working directory will no longer match your working tree, and your working tree will not match your
index. index after this step.
This is no longer true. The above command, thanks to `-u`
option, updates your working tree with the merge results for
paths that have been trivially merged.
8) Merging multiple trees, continued 8) Merging multiple trees, continued
@ -579,7 +582,7 @@ The above is the description of a git merge at the lowest level,
to help you understand what conceptually happens under the hood. to help you understand what conceptually happens under the hood.
In practice, nobody, not even git itself, uses three `git-cat-file` In practice, nobody, not even git itself, uses three `git-cat-file`
for this. There is `git-merge-index` program that extracts the for this. There is `git-merge-index` program that extracts the
stages to temporary files and calls a `merge` script on it stages to temporary files and calls a "merge" script on it:
git-merge-index git-merge-one-file hello.c git-merge-index git-merge-one-file hello.c

97
cache.h
View File

@ -1,23 +1,7 @@
#ifndef CACHE_H #ifndef CACHE_H
#define CACHE_H #define CACHE_H
#include <unistd.h> #include "git-compat-util.h"
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#ifndef NO_MMAP
#include <sys/mman.h>
#endif
#include <sys/param.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <dirent.h>
#include SHA1_HEADER #include SHA1_HEADER
#include <zlib.h> #include <zlib.h>
@ -36,15 +20,6 @@
#define DTYPE(de) DT_UNKNOWN #define DTYPE(de) DT_UNKNOWN
#endif #endif
#ifdef __GNUC__
#define NORETURN __attribute__((__noreturn__))
#else
#define NORETURN
#ifndef __attribute__
#define __attribute__(x)
#endif
#endif
/* /*
* Intensive research over the course of many years has shown that * Intensive research over the course of many years has shown that
* port 9418 is totally unused by anything else. Or * port 9418 is totally unused by anything else. Or
@ -169,6 +144,7 @@ extern int ce_match_stat(struct cache_entry *ce, struct stat *st);
extern int ce_modified(struct cache_entry *ce, struct stat *st); extern int ce_modified(struct cache_entry *ce, struct stat *st);
extern int ce_path_match(const struct cache_entry *ce, const char **pathspec); extern int ce_path_match(const struct cache_entry *ce, const char **pathspec);
extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type); extern int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type);
extern int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object);
extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object); extern int index_path(unsigned char *sha1, const char *path, struct stat *st, int write_object);
extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st); extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
@ -250,11 +226,6 @@ extern const char *resolve_ref(const char *path, unsigned char *sha1, int);
extern int create_symref(const char *git_HEAD, const char *refs_heads_master); extern int create_symref(const char *git_HEAD, const char *refs_heads_master);
extern int validate_symref(const char *git_HEAD); extern int validate_symref(const char *git_HEAD);
/* General helper functions */
extern void usage(const char *err) NORETURN;
extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2); extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);
extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2); extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);
@ -272,30 +243,6 @@ extern int setup_ident(void);
extern const char *git_author_info(void); extern const char *git_author_info(void);
extern const char *git_committer_info(void); extern const char *git_committer_info(void);
static inline void *xmalloc(size_t size)
{
void *ret = malloc(size);
if (!ret)
die("Out of memory, malloc failed");
return ret;
}
static inline void *xrealloc(void *ptr, size_t size)
{
void *ret = realloc(ptr, size);
if (!ret)
die("Out of memory, realloc failed");
return ret;
}
static inline void *xcalloc(size_t nmemb, size_t size)
{
void *ret = calloc(nmemb, size);
if (!ret)
die("Out of memory, calloc failed");
return ret;
}
struct checkout { struct checkout {
const char *base_dir; const char *base_dir;
int base_dir_len; int base_dir_len;
@ -373,20 +320,6 @@ extern void packed_object_info_detail(struct pack_entry *, char *, unsigned long
/* Dumb servers support */ /* Dumb servers support */
extern int update_server_info(int); extern int update_server_info(int);
#ifdef NO_MMAP
#ifndef PROT_READ
#define PROT_READ 1
#define PROT_WRITE 2
#define MAP_PRIVATE 1
#define MAP_FAILED ((void*)-1)
#endif
extern void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
extern int gitfakemunmap(void *start, size_t length);
#endif
typedef int (*config_fn_t)(const char *, const char *); typedef int (*config_fn_t)(const char *, const char *);
extern int git_default_config(const char *, const char *); extern int git_default_config(const char *, const char *);
extern int git_config_from_file(config_fn_t fn, const char *); extern int git_config_from_file(config_fn_t fn, const char *);
@ -404,31 +337,5 @@ extern char git_default_name[MAX_GITNAME];
#define MAX_ENCODING_LENGTH 64 #define MAX_ENCODING_LENGTH 64
extern char git_commit_encoding[MAX_ENCODING_LENGTH]; extern char git_commit_encoding[MAX_ENCODING_LENGTH];
/* Sane ctype - no locale, and works with signed chars */
#undef isspace
#undef isdigit
#undef isalpha
#undef isalnum
#undef tolower
#undef toupper
extern unsigned char sane_ctype[256];
#define GIT_SPACE 0x01
#define GIT_DIGIT 0x02
#define GIT_ALPHA 0x04
#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
#define isspace(x) sane_istest(x,GIT_SPACE)
#define isdigit(x) sane_istest(x,GIT_DIGIT)
#define isalpha(x) sane_istest(x,GIT_ALPHA)
#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
#define tolower(x) sane_case((unsigned char)(x), 0x20)
#define toupper(x) sane_case((unsigned char)(x), 0)
static inline int sane_case(int x, int high)
{
if (sane_istest(x, GIT_ALPHA))
x = (x & ~0x20) | high;
return x;
}
extern int copy_fd(int ifd, int ofd); extern int copy_fd(int ifd, int ofd);
#endif /* CACHE_H */ #endif /* CACHE_H */

View File

@ -36,6 +36,7 @@
static const char *prefix; static const char *prefix;
static int prefix_length; static int prefix_length;
static int checkout_stage; /* default to checkout stage0 */
static struct checkout state = { static struct checkout state = {
.base_dir = "", .base_dir = "",
@ -48,20 +49,36 @@ static struct checkout state = {
static int checkout_file(const char *name) static int checkout_file(const char *name)
{ {
int pos = cache_name_pos(name, strlen(name)); int namelen = strlen(name);
if (pos < 0) { int pos = cache_name_pos(name, namelen);
if (!state.quiet) { int has_same_name = 0;
pos = -pos - 1;
fprintf(stderr, if (pos < 0)
"git-checkout-index: %s is %s.\n", pos = -pos - 1;
name,
(pos < active_nr && while (pos < active_nr) {
!strcmp(active_cache[pos]->name, name)) ? struct cache_entry *ce = active_cache[pos];
"unmerged" : "not in the cache"); if (ce_namelen(ce) != namelen &&
} memcmp(ce->name, name, namelen))
return -1; break;
has_same_name = 1;
if (checkout_stage == ce_stage(ce))
return checkout_entry(ce, &state);
pos++;
} }
return checkout_entry(active_cache[pos], &state);
if (!state.quiet) {
fprintf(stderr, "git-checkout-index: %s ", name);
if (!has_same_name)
fprintf(stderr, "is not in the cache");
else if (checkout_stage)
fprintf(stderr, "does not exist at stage %d",
checkout_stage);
else
fprintf(stderr, "is unmerged");
fputc('\n', stderr);
}
return -1;
} }
static int checkout_all(void) static int checkout_all(void)
@ -70,11 +87,11 @@ static int checkout_all(void)
for (i = 0; i < active_nr ; i++) { for (i = 0; i < active_nr ; i++) {
struct cache_entry *ce = active_cache[i]; struct cache_entry *ce = active_cache[i];
if (ce_stage(ce)) if (ce_stage(ce) != checkout_stage)
continue; continue;
if (prefix && *prefix && if (prefix && *prefix &&
( ce_namelen(ce) <= prefix_length || (ce_namelen(ce) <= prefix_length ||
memcmp(prefix, ce->name, prefix_length) )) memcmp(prefix, ce->name, prefix_length)))
continue; continue;
if (checkout_entry(ce, &state) < 0) if (checkout_entry(ce, &state) < 0)
errs++; errs++;
@ -88,7 +105,7 @@ static int checkout_all(void)
} }
static const char checkout_cache_usage[] = static const char checkout_cache_usage[] =
"git-checkout-index [-u] [-q] [-a] [-f] [-n] [--prefix=<string>] [--] <file>..."; "git-checkout-index [-u] [-q] [-a] [-f] [-n] [--stage=[123]] [--prefix=<string>] [--] <file>...";
static struct cache_file cache_file; static struct cache_file cache_file;
@ -138,11 +155,19 @@ int main(int argc, char **argv)
die("cannot open index.lock file."); die("cannot open index.lock file.");
continue; continue;
} }
if (!memcmp(arg, "--prefix=", 9)) { if (!strncmp(arg, "--prefix=", 9)) {
state.base_dir = arg+9; state.base_dir = arg+9;
state.base_dir_len = strlen(state.base_dir); state.base_dir_len = strlen(state.base_dir);
continue; continue;
} }
if (!strncmp(arg, "--stage=", 8)) {
int ch = arg[8];
if ('1' <= ch && ch <= '3')
checkout_stage = arg[8] - '0';
else
die("stage should be between 1 and 3");
continue;
}
if (arg[0] == '-') if (arg[0] == '-')
usage(checkout_cache_usage); usage(checkout_cache_usage);
break; break;

View File

@ -2,7 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include "../cache.h" #include "../git-compat-util.h"
void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset) void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset)
{ {

View File

@ -16,7 +16,7 @@ int gitsetenv(const char *name, const char *value, int replace)
namelen = strlen(name); namelen = strlen(name);
valuelen = strlen(value); valuelen = strlen(value);
envstr = malloc((namelen + valuelen + 2) * sizeof(char)); envstr = malloc((namelen + valuelen + 2));
if (!envstr) return -1; if (!envstr) return -1;
memcpy(envstr, name, namelen); memcpy(envstr, name, namelen);
@ -25,7 +25,11 @@ int gitsetenv(const char *name, const char *value, int replace)
envstr[namelen + valuelen + 1] = 0; envstr[namelen + valuelen + 1] = 0;
out = putenv(envstr); out = putenv(envstr);
/* putenv(3) makes the argument string part of the environment,
* and changing that string modifies the environment --- which
* means we do not own that storage anymore. Do not free
* envstr.
*/
free(envstr);
return out; return out;
} }

View File

@ -1,5 +1,4 @@
#include <string.h> #include "../git-compat-util.h"
#include <ctype.h>
char *gitstrcasestr(const char *haystack, const char *needle) char *gitstrcasestr(const char *haystack, const char *needle)
{ {

View File

@ -458,12 +458,6 @@ int git_config_set_multivar(const char* key, const char* value,
* If .git/config does not exist yet, write a minimal version. * If .git/config does not exist yet, write a minimal version.
*/ */
if (stat(config_filename, &st)) { if (stat(config_filename, &st)) {
static const char contents[] =
"#\n"
"# This is the config file\n"
"#\n"
"\n";
free(store.key); free(store.key);
/* if nothing to unset, error out */ /* if nothing to unset, error out */
@ -474,8 +468,6 @@ int git_config_set_multivar(const char* key, const char* value,
} }
store.key = (char*)key; store.key = (char*)key;
write(fd, contents, sizeof(contents)-1);
store_write_section(fd, key); store_write_section(fd, key);
store_write_pair(fd, key, value); store_write_pair(fd, key, value);
} else{ } else{

View File

@ -1,4 +1,5 @@
#define _XOPEN_SOURCE /* glibc2 needs this */ #define _XOPEN_SOURCE 500 /* glibc2 and AIX 5.3L need this */
#define _XOPEN_SOURCE_EXTENDED 1 /* AIX 5.3L needs this */
#include <time.h> #include <time.h>
#include "cache.h" #include "cache.h"

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
git-core (0.99.9m-0) unstable; urgency=low
* GIT 0.99.9m aka 1.0rc5
-- Junio C Hamano <junkio@cox.net> Sun, 11 Dec 2005 16:48:06 -0800
git-core (0.99.9l-0) unstable; urgency=low git-core (0.99.9l-0) unstable; urgency=low
* GIT 0.99.9l aka 1.0rc4 * GIT 0.99.9l aka 1.0rc4

View File

@ -312,7 +312,7 @@ do
echo "--------------------------" echo "--------------------------"
cat "$dotest/final-commit" cat "$dotest/final-commit"
echo "--------------------------" echo "--------------------------"
echo -n "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all " printf "Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all "
read reply read reply
case "$reply" in case "$reply" in
[yY]*) action=yes ;; [yY]*) action=yes ;;

View File

@ -83,7 +83,7 @@ while [ "$interactive" = yes ]; do
echo "--------------------------" echo "--------------------------"
cat "$final" cat "$final"
echo "--------------------------" echo "--------------------------"
echo -n "Apply? [y]es/[n]o/[e]dit/[a]ccept all " printf "Apply? [y]es/[n]o/[e]dit/[a]ccept all "
read reply read reply
case "$reply" in case "$reply" in
y|Y) interactive=no;; y|Y) interactive=no;;

View File

@ -25,6 +25,9 @@ See man (1) git-archimport for more details.
- audit shell-escaping of filenames - audit shell-escaping of filenames
- hide our private tags somewhere smarter - hide our private tags somewhere smarter
- find a way to make "cat *patches | patch" safe even when patchfiles are missing newlines - find a way to make "cat *patches | patch" safe even when patchfiles are missing newlines
- sort and apply patches by graphing ancestry relations instead of just
relying in dates supplied in the changeset itself.
tla ancestry-graph -m could be helpful here...
=head1 Devel tricks =head1 Devel tricks
@ -53,15 +56,9 @@ and can contain multiple, unrelated branches.
use strict; use strict;
use warnings; use warnings;
use Getopt::Std; use Getopt::Std;
use File::Spec; use File::Temp qw(tempdir);
use File::Temp qw(tempfile tempdir); use File::Path qw(mkpath rmtree);
use File::Path qw(mkpath);
use File::Basename qw(basename dirname); use File::Basename qw(basename dirname);
use String::ShellQuote;
use Time::Local;
use IO::Socket;
use IO::Pipe;
use POSIX qw(strftime dup2);
use Data::Dumper qw/ Dumper /; use Data::Dumper qw/ Dumper /;
use IPC::Open2; use IPC::Open2;
@ -72,124 +69,150 @@ my $git_dir = $ENV{"GIT_DIR"} || ".git";
$ENV{"GIT_DIR"} = $git_dir; $ENV{"GIT_DIR"} = $git_dir;
my $ptag_dir = "$git_dir/archimport/tags"; my $ptag_dir = "$git_dir/archimport/tags";
our($opt_h,$opt_v, $opt_T,$opt_t,$opt_o); our($opt_h,$opt_f,$opt_v,$opt_T,$opt_t,$opt_D,$opt_a,$opt_o);
sub usage() { sub usage() {
print STDERR <<END; print STDERR <<END;
Usage: ${\basename $0} # fetch/update GIT from Arch Usage: ${\basename $0} # fetch/update GIT from Arch
[ -o ] [ -h ] [ -v ] [ -T ] [ -t tempdir ] [ -f ] [ -o ] [ -h ] [ -v ] [ -T ] [ -a ] [ -D depth ] [ -t tempdir ]
repository/arch-branch [ repository/arch-branch] ... repository/arch-branch [ repository/arch-branch] ...
END END
exit(1); exit(1);
} }
getopts("Thvt:") or usage(); getopts("fThvat:D:") or usage();
usage if $opt_h; usage if $opt_h;
@ARGV >= 1 or usage(); @ARGV >= 1 or usage();
my @arch_roots = @ARGV; # $arch_branches:
# values associated with keys:
# =1 - Arch version / git 'branch' detected via abrowse on a limit
# >1 - Arch version / git 'branch' of an auxilliary branch we've merged
my %arch_branches = map { $_ => 1 } @ARGV;
my ($tmpdir, $tmpdirname) = tempdir('git-archimport-XXXXXX', TMPDIR => 1, CLEANUP => 1); $ENV{'TMPDIR'} = $opt_t if $opt_t; # $ENV{TMPDIR} will affect tempdir() calls:
my $tmp = $opt_t || 1; my $tmp = tempdir('git-archimport-XXXXXX', TMPDIR => 1, CLEANUP => 1);
$tmp = tempdir('git-archimport-XXXXXX', TMPDIR => 1, CLEANUP => 1);
$opt_v && print "+ Using $tmp as temporary directory\n"; $opt_v && print "+ Using $tmp as temporary directory\n";
my %reachable = (); # Arch repositories we can access
my %unreachable = (); # Arch repositories we can't access :<
my @psets = (); # the collection my @psets = (); # the collection
my %psets = (); # the collection, by name my %psets = (); # the collection, by name
my %stats = ( # Track which strategy we used to import:
get_tag => 0, replay => 0, get_new => 0, get_delta => 0,
simple_changeset => 0, import_or_tag => 0
);
my %rptags = (); # my reverse private tags my %rptags = (); # my reverse private tags
# to map a SHA1 to a commitid # to map a SHA1 to a commitid
my $TLA = $ENV{'ARCH_CLIENT'} || 'tla';
foreach my $root (@arch_roots) { sub do_abrowse {
my ($arepo, $abranch) = split(m!/!, $root); my $stage = shift;
open ABROWSE, "tla abrowse -f -A $arepo --desc --merges $abranch |" while (my ($limit, $level) = each %arch_branches) {
or die "Problems with tla abrowse: $!"; next unless $level == $stage;
my %ps = (); # the current one
my $mode = '';
my $lastseen = '';
while (<ABROWSE>) {
chomp;
# first record padded w 8 spaces open ABROWSE, "$TLA abrowse -fkD --merges $limit |"
if (s/^\s{8}\b//) { or die "Problems with tla abrowse: $!";
my %ps = (); # the current one
my $lastseen = '';
while (<ABROWSE>) {
chomp;
# store the record we just captured # first record padded w 8 spaces
if (%ps) { if (s/^\s{8}\b//) {
my %temp = %ps; # break references my ($id, $type) = split(m/\s+/, $_, 2);
push (@psets, \%temp);
$psets{$temp{id}} = \%temp;
%ps = ();
}
my ($id, $type) = split(m/\s{3}/, $_);
$ps{id} = $id;
$ps{repo} = $arepo;
# deal with types my %last_ps;
if ($type =~ m/^\(simple changeset\)/) { # store the record we just captured
$ps{type} = 's'; if (%ps && !exists $psets{ $ps{id} }) {
} elsif ($type eq '(initial import)') { %last_ps = %ps; # break references
$ps{type} = 'i'; push (@psets, \%last_ps);
} elsif ($type =~ m/^\(tag revision of (.+)\)/) { $psets{ $last_ps{id} } = \%last_ps;
$ps{type} = 't';
$ps{tag} = $1;
} else {
warn "Unknown type $type";
}
$lastseen = 'id';
}
if (s/^\s{10}//) {
# 10 leading spaces or more
# indicate commit metadata
# date & author
if ($lastseen eq 'id' && m/^\d{4}-\d{2}-\d{2}/) {
my ($date, $authoremail) = split(m/\s{2,}/, $_);
$ps{date} = $date;
$ps{date} =~ s/\bGMT$//; # strip off trailign GMT
if ($ps{date} =~ m/\b\w+$/) {
warn 'Arch dates not in GMT?! - imported dates will be wrong';
} }
$authoremail =~ m/^(.+)\s(\S+)$/; my $branch = extract_versionname($id);
$ps{author} = $1; %ps = ( id => $id, branch => $branch );
$ps{email} = $2; if (%last_ps && ($last_ps{branch} eq $branch)) {
$ps{parent_id} = $last_ps{id};
$lastseen = 'date'; }
} elsif ($lastseen eq 'date') { $arch_branches{$branch} = 1;
# the only hint is position $lastseen = 'id';
# subject is after date
$ps{subj} = $_;
$lastseen = 'subj';
} elsif ($lastseen eq 'subj' && $_ eq 'merges in:') {
$ps{merges} = [];
$lastseen = 'merges';
} elsif ($lastseen eq 'merges' && s/^\s{2}//) {
push (@{$ps{merges}}, $_);
} else {
warn 'more metadata after merges!?';
}
}
}
if (%ps) { # deal with types (should work with baz or tla):
my %temp = %ps; # break references if ($type =~ m/\(.*changeset\)/) {
push (@psets, \%temp); $ps{type} = 's';
$psets{ $temp{id} } = \%temp; } elsif ($type =~ /\(.*import\)/) {
%ps = (); $ps{type} = 'i';
} } elsif ($type =~ m/\(tag.*?(\S+\@\S+).*?\)/) {
close ABROWSE; $ps{type} = 't';
# read which revision we've tagged when we parse the log
$ps{tag} = $1;
} else {
warn "Unknown type $type";
}
$arch_branches{$branch} = 1;
$lastseen = 'id';
} elsif (s/^\s{10}//) {
# 10 leading spaces or more
# indicate commit metadata
# date
if ($lastseen eq 'id' && m/^(\d{4}-\d\d-\d\d \d\d:\d\d:\d\d)/){
$ps{date} = $1;
$lastseen = 'date';
} elsif ($_ eq 'merges in:') {
$ps{merges} = [];
$lastseen = 'merges';
} elsif ($lastseen eq 'merges' && s/^\s{2}//) {
my $id = $_;
push (@{$ps{merges}}, $id);
# aggressive branch finding:
if ($opt_D) {
my $branch = extract_versionname($id);
my $repo = extract_reponame($branch);
if (archive_reachable($repo) &&
!defined $arch_branches{$branch}) {
$arch_branches{$branch} = $stage + 1;
}
}
} else {
warn "more metadata after merges!?: $_\n" unless /^\s*$/;
}
}
}
if (%ps && !exists $psets{ $ps{id} }) {
my %temp = %ps; # break references
if (@psets && $psets[$#psets]{branch} eq $ps{branch}) {
$temp{parent_id} = $psets[$#psets]{id};
}
push (@psets, \%temp);
$psets{ $temp{id} } = \%temp;
}
close ABROWSE or die "$TLA abrowse failed on $limit\n";
}
} # end foreach $root } # end foreach $root
do_abrowse(1);
my $depth = 2;
$opt_D ||= 0;
while ($depth <= $opt_D) {
do_abrowse($depth);
$depth++;
}
## Order patches by time ## Order patches by time
# FIXME see if we can find a more optimal way to do this by graphing
# the ancestry data and walking it, that way we won't have to rely on
# client-supplied dates
@psets = sort {$a->{date}.$b->{id} cmp $b->{date}.$b->{id}} @psets; @psets = sort {$a->{date}.$b->{id} cmp $b->{date}.$b->{id}} @psets;
#print Dumper \@psets; #print Dumper \@psets;
@ -210,7 +233,7 @@ unless (-d $git_dir) { # initial import
} }
} else { # progressing an import } else { # progressing an import
# load the rptags # load the rptags
opendir(DIR, "$git_dir/archimport/tags") opendir(DIR, $ptag_dir)
|| die "can't opendir: $!"; || die "can't opendir: $!";
while (my $file = readdir(DIR)) { while (my $file = readdir(DIR)) {
# skip non-interesting-files # skip non-interesting-files
@ -272,29 +295,69 @@ sub old_style_branchname {
*git_branchname = $opt_o ? *old_style_branchname : *tree_dirname; *git_branchname = $opt_o ? *old_style_branchname : *tree_dirname;
# process patchsets sub process_patchset_accurate {
foreach my $ps (@psets) { my $ps = shift;
$ps->{branch} = git_branchname($ps->{id});
# switch to that branch if we're not already in that branch:
if (-e "$git_dir/refs/heads/$ps->{branch}") {
system('git-checkout','-f',$ps->{branch}) == 0 or die "$! $?\n";
# # remove any old stuff that got leftover:
# ensure we have a clean state my $rm = safe_pipe_capture('git-ls-files','--others','-z');
# rmtree(split(/\0/,$rm)) if $rm;
if (`git diff-files`) {
die "Unclean tree when about to process $ps->{id} " .
" - did we fail to commit cleanly before?";
} }
die $! if $?;
# Apply the import/changeset/merge into the working tree
my $dir = sync_to_ps($ps);
# read the new log entry:
my @commitlog = safe_pipe_capture($TLA,'cat-log','-d',$dir,$ps->{id});
die "Error in cat-log: $!" if $?;
chomp @commitlog;
# # grab variables we want from the log, new fields get added to $ps:
# skip commits already in repo # (author, date, email, summary, message body ...)
# parselog($ps, \@commitlog);
if (ptag($ps->{id})) {
$opt_v && print " * Skipping already imported: $ps->{id}\n";
next;
}
print " * Starting to work on $ps->{id}\n"; if ($ps->{id} =~ /--base-0$/ && $ps->{id} ne $psets[0]{id}) {
# this should work when importing continuations
if ($ps->{tag} && (my $branchpoint = eval { ptag($ps->{tag}) })) {
# find where we are supposed to branch from
system('git-checkout','-f','-b',$ps->{branch},
$branchpoint) == 0 or die "$! $?\n";
# remove any old stuff that got leftover:
my $rm = safe_pipe_capture('git-ls-files','--others','-z');
rmtree(split(/\0/,$rm)) if $rm;
# If we trust Arch with the fact that this is just
# a tag, and it does not affect the state of the tree
# then we just tag and move on
tag($ps->{id}, $branchpoint);
ptag($ps->{id}, $branchpoint);
print " * Tagged $ps->{id} at $branchpoint\n";
return 0;
} else {
warn "Tagging from unknown id unsupported\n" if $ps->{tag};
}
# allow multiple bases/imports here since Arch supports cherry-picks
# from unrelated trees
}
# update the index with all the changes we got
system('git-ls-files --others -z | '.
'git-update-index --add -z --stdin') == 0 or die "$! $?\n";
system('git-ls-files --deleted -z | '.
'git-update-index --remove -z --stdin') == 0 or die "$! $?\n";
system('git-ls-files -z | '.
'git-update-index -z --stdin') == 0 or die "$! $?\n";
return 1;
}
# the native changeset processing strategy. This is very fast, but
# does not handle permissions or any renames involving directories
sub process_patchset_fast {
my $ps = shift;
# #
# create the branch if needed # create the branch if needed
# #
@ -305,7 +368,7 @@ foreach my $ps (@psets) {
unless ($import) { # skip for import unless ($import) { # skip for import
if ( -e "$git_dir/refs/heads/$ps->{branch}") { if ( -e "$git_dir/refs/heads/$ps->{branch}") {
# we know about this branch # we know about this branch
`git checkout $ps->{branch}`; system('git-checkout',$ps->{branch});
} else { } else {
# new branch! we need to verify a few things # new branch! we need to verify a few things
die "Branch on a non-tag!" unless $ps->{type} eq 't'; die "Branch on a non-tag!" unless $ps->{type} eq 't';
@ -314,7 +377,7 @@ foreach my $ps (@psets) {
unless $branchpoint; unless $branchpoint;
# find where we are supposed to branch from # find where we are supposed to branch from
`git checkout -b $ps->{branch} $branchpoint`; system('git-checkout','-b',$ps->{branch},$branchpoint);
# If we trust Arch with the fact that this is just # If we trust Arch with the fact that this is just
# a tag, and it does not affect the state of the tree # a tag, and it does not affect the state of the tree
@ -322,7 +385,7 @@ foreach my $ps (@psets) {
tag($ps->{id}, $branchpoint); tag($ps->{id}, $branchpoint);
ptag($ps->{id}, $branchpoint); ptag($ps->{id}, $branchpoint);
print " * Tagged $ps->{id} at $branchpoint\n"; print " * Tagged $ps->{id} at $branchpoint\n";
next; return 0;
} }
die $! if $?; die $! if $?;
} }
@ -332,96 +395,128 @@ foreach my $ps (@psets) {
# #
if ($ps->{type} eq 'i' || $ps->{type} eq 't') { if ($ps->{type} eq 'i' || $ps->{type} eq 't') {
apply_import($ps) or die $!; apply_import($ps) or die $!;
$stats{import_or_tag}++;
$import=0; $import=0;
} elsif ($ps->{type} eq 's') { } elsif ($ps->{type} eq 's') {
apply_cset($ps); apply_cset($ps);
$stats{simple_changeset}++;
} }
# #
# prepare update git's index, based on what arch knows # prepare update git's index, based on what arch knows
# about the pset, resolve parents, etc # about the pset, resolve parents, etc
# #
my $tree;
my $commitlog = `tla cat-archive-log -A $ps->{repo} $ps->{id}`; my @commitlog = safe_pipe_capture($TLA,'cat-archive-log',$ps->{id});
die "Error in cat-archive-log: $!" if $?; die "Error in cat-archive-log: $!" if $?;
# parselog will git-add/rm files parselog($ps,\@commitlog);
# and generally prepare things for the commit
# NOTE: parselog will shell-quote filenames!
my ($sum, $msg, $add, $del, $mod, $ren) = parselog($commitlog);
my $logmessage = "$sum\n$msg";
# imports don't give us good info # imports don't give us good info
# on added files. Shame on them # on added files. Shame on them
if ($ps->{type} eq 'i' || $ps->{type} eq 't') { if ($ps->{type} eq 'i' || $ps->{type} eq 't') {
`find . -type f -print0 | grep -zv '^./$git_dir' | xargs -0 -l100 git-update-index --add`; system('git-ls-files --others -z | '.
`git-ls-files --deleted -z | xargs --no-run-if-empty -0 -l100 git-update-index --remove`; 'git-update-index --add -z --stdin') == 0 or die "$! $?\n";
system('git-ls-files --deleted -z | '.
'git-update-index --remove -z --stdin') == 0 or die "$! $?\n";
} }
if (@$add) { # TODO: handle removed_directories and renamed_directories:
if (my $add = $ps->{new_files}) {
while (@$add) { while (@$add) {
my @slice = splice(@$add, 0, 100); my @slice = splice(@$add, 0, 100);
my $slice = join(' ', @slice); system('git-update-index','--add','--',@slice) == 0 or
`git-update-index --add $slice`; die "Error in git-update-index --add: $! $?\n";
die "Error in git-update-index --add: $!" if $?;
} }
} }
if (@$del) {
foreach my $file (@$del) { if (my $del = $ps->{removed_files}) {
unlink $file or die "Problems deleting $file : $!"; unlink @$del;
}
while (@$del) { while (@$del) {
my @slice = splice(@$del, 0, 100); my @slice = splice(@$del, 0, 100);
my $slice = join(' ', @slice); system('git-update-index','--remove','--',@slice) == 0 or
`git-update-index --remove $slice`; die "Error in git-update-index --remove: $! $?\n";
die "Error in git-update-index --remove: $!" if $?;
} }
} }
if (@$ren) { # renamed
if (my $ren = $ps->{renamed_files}) { # renamed
if (@$ren % 2) { if (@$ren % 2) {
die "Odd number of entries in rename!?"; die "Odd number of entries in rename!?";
} }
;
while (@$ren) { while (@$ren) {
my $from = pop @$ren; my $from = shift @$ren;
my $to = pop @$ren; my $to = shift @$ren;
unless (-d dirname($to)) { unless (-d dirname($to)) {
mkpath(dirname($to)); # will die on err mkpath(dirname($to)); # will die on err
} }
#print "moving $from $to"; # print "moving $from $to";
`mv $from $to`; rename($from, $to) or die "Error renaming '$from' '$to': $!\n";
die "Error renaming $from $to : $!" if $?; system('git-update-index','--remove','--',$from) == 0 or
`git-update-index --remove $from`; die "Error in git-update-index --remove: $! $?\n";
die "Error in git-update-index --remove: $!" if $?; system('git-update-index','--add','--',$to) == 0 or
`git-update-index --add $to`; die "Error in git-update-index --add: $! $?\n";
die "Error in git-update-index --add: $!" if $?;
} }
} }
if (@$mod) { # must be _after_ renames
if (my $mod = $ps->{modified_files}) {
while (@$mod) { while (@$mod) {
my @slice = splice(@$mod, 0, 100); my @slice = splice(@$mod, 0, 100);
my $slice = join(' ', @slice); system('git-update-index','--',@slice) == 0 or
`git-update-index $slice`; die "Error in git-update-index: $! $?\n";
die "Error in git-update-index: $!" if $?;
} }
} }
return 1; # we successfully applied the changeset
}
if ($opt_f) {
print "Will import patchsets using the fast strategy\n",
"Renamed directories and permission changes will be missed\n";
*process_patchset = *process_patchset_fast;
} else {
print "Using the default (accurate) import strategy.\n",
"Things may be a bit slow\n";
*process_patchset = *process_patchset_accurate;
}
foreach my $ps (@psets) {
# process patchsets
$ps->{branch} = git_branchname($ps->{id});
#
# ensure we have a clean state
#
if (my $dirty = `git-diff-files`) {
die "Unclean tree when about to process $ps->{id} " .
" - did we fail to commit cleanly before?\n$dirty";
}
die $! if $?;
#
# skip commits already in repo
#
if (ptag($ps->{id})) {
$opt_v && print " * Skipping already imported: $ps->{id}\n";
next;
}
print " * Starting to work on $ps->{id}\n";
process_patchset($ps) or next;
# warn "errors when running git-update-index! $!"; # warn "errors when running git-update-index! $!";
$tree = `git-write-tree`; my $tree = `git-write-tree`;
die "cannot write tree $!" if $?; die "cannot write tree $!" if $?;
chomp $tree; chomp $tree;
# #
# Who's your daddy? # Who's your daddy?
# #
my @par; my @par;
if ( -e "$git_dir/refs/heads/$ps->{branch}") { if ( -e "$git_dir/refs/heads/$ps->{branch}") {
if (open HEAD, "<$git_dir/refs/heads/$ps->{branch}") { if (open HEAD, "<","$git_dir/refs/heads/$ps->{branch}") {
my $p = <HEAD>; my $p = <HEAD>;
close HEAD; close HEAD;
chomp $p; chomp $p;
@ -436,7 +531,6 @@ foreach my $ps (@psets) {
if ($ps->{merges}) { if ($ps->{merges}) {
push @par, find_parents($ps); push @par, find_parents($ps);
} }
my $par = join (' ', @par);
# #
# Commit, tag and clean state # Commit, tag and clean state
@ -449,13 +543,14 @@ foreach my $ps (@psets) {
$ENV{GIT_COMMITTER_EMAIL} = $ps->{email}; $ENV{GIT_COMMITTER_EMAIL} = $ps->{email};
$ENV{GIT_COMMITTER_DATE} = $ps->{date}; $ENV{GIT_COMMITTER_DATE} = $ps->{date};
my ($pid, $commit_rh, $commit_wh); my $pid = open2(*READER, *WRITER,'git-commit-tree',$tree,@par)
$commit_rh = 'commit_rh';
$commit_wh = 'commit_wh';
$pid = open2(*READER, *WRITER, "git-commit-tree $tree $par")
or die $!; or die $!;
print WRITER $logmessage; # write print WRITER $ps->{summary},"\n";
print WRITER $ps->{message},"\n";
# make it easy to backtrack and figure out which Arch revision this was:
print WRITER 'git-archimport-id: ',$ps->{id},"\n";
close WRITER; close WRITER;
my $commitid = <READER>; # read my $commitid = <READER>; # read
chomp $commitid; chomp $commitid;
@ -468,7 +563,7 @@ foreach my $ps (@psets) {
# #
# Update the branch # Update the branch
# #
open HEAD, ">$git_dir/refs/heads/$ps->{branch}"; open HEAD, ">","$git_dir/refs/heads/$ps->{branch}";
print HEAD $commitid; print HEAD $commitid;
close HEAD; close HEAD;
system('git-update-ref', 'HEAD', "$ps->{branch}"); system('git-update-ref', 'HEAD', "$ps->{branch}");
@ -482,21 +577,78 @@ foreach my $ps (@psets) {
print " + tree $tree\n"; print " + tree $tree\n";
print " + commit $commitid\n"; print " + commit $commitid\n";
$opt_v && print " + commit date is $ps->{date} \n"; $opt_v && print " + commit date is $ps->{date} \n";
$opt_v && print " + parents: $par \n"; $opt_v && print " + parents: ",join(' ',@par),"\n";
}
if ($opt_v) {
foreach (sort keys %stats) {
print" $_: $stats{$_}\n";
}
}
exit 0;
# used by the accurate strategy:
sub sync_to_ps {
my $ps = shift;
my $tree_dir = $tmp.'/'.tree_dirname($ps->{id});
$opt_v && print "sync_to_ps($ps->{id}) method: ";
if (-d $tree_dir) {
if ($ps->{type} eq 't') {
$opt_v && print "get (tag)\n";
# looks like a tag-only or (worse,) a mixed tags/changeset branch,
# can't rely on replay to work correctly on these
rmtree($tree_dir);
safe_pipe_capture($TLA,'get','--no-pristine',$ps->{id},$tree_dir);
$stats{get_tag}++;
} else {
my $tree_id = arch_tree_id($tree_dir);
if ($ps->{parent_id} && ($ps->{parent_id} eq $tree_id)) {
# the common case (hopefully)
$opt_v && print "replay\n";
safe_pipe_capture($TLA,'replay','-d',$tree_dir,$ps->{id});
$stats{replay}++;
} else {
# getting one tree is usually faster than getting two trees
# and applying the delta ...
rmtree($tree_dir);
$opt_v && print "apply-delta\n";
safe_pipe_capture($TLA,'get','--no-pristine',
$ps->{id},$tree_dir);
$stats{get_delta}++;
}
}
} else {
# new branch work
$opt_v && print "get (new tree)\n";
safe_pipe_capture($TLA,'get','--no-pristine',$ps->{id},$tree_dir);
$stats{get_new}++;
}
# added -I flag to rsync since we're going to fast! AIEEEEE!!!!
system('rsync','-aI','--delete','--exclude',$git_dir,
# '--exclude','.arch-inventory',
'--exclude','.arch-ids','--exclude','{arch}',
'--exclude','+*','--exclude',',*',
"$tree_dir/",'./') == 0 or die "Cannot rsync $tree_dir: $! $?";
return $tree_dir;
} }
sub apply_import { sub apply_import {
my $ps = shift; my $ps = shift;
my $bname = git_branchname($ps->{id}); my $bname = git_branchname($ps->{id});
`mkdir -p $tmp`; mkpath($tmp);
`tla get -s --no-pristine -A $ps->{repo} $ps->{id} $tmp/import`; safe_pipe_capture($TLA,'get','-s','--no-pristine',$ps->{id},"$tmp/import");
die "Cannot get import: $!" if $?; die "Cannot get import: $!" if $?;
`rsync -v --archive --delete --exclude '$git_dir' --exclude '.arch-ids' --exclude '{arch}' $tmp/import/* ./`; system('rsync','-aI','--delete', '--exclude',$git_dir,
'--exclude','.arch-ids','--exclude','{arch}',
"$tmp/import/", './');
die "Cannot rsync import:$!" if $?; die "Cannot rsync import:$!" if $?;
`rm -fr $tmp/import`; rmtree("$tmp/import");
die "Cannot remove tempdir: $!" if $?; die "Cannot remove tempdir: $!" if $?;
@ -506,10 +658,10 @@ sub apply_import {
sub apply_cset { sub apply_cset {
my $ps = shift; my $ps = shift;
`mkdir -p $tmp`; mkpath($tmp);
# get the changeset # get the changeset
`tla get-changeset -A $ps->{repo} $ps->{id} $tmp/changeset`; safe_pipe_capture($TLA,'get-changeset',$ps->{id},"$tmp/changeset");
die "Cannot get changeset: $!" if $?; die "Cannot get changeset: $!" if $?;
# apply patches # apply patches
@ -533,22 +685,27 @@ sub apply_cset {
$orig =~ s/\.modified$//; # lazy $orig =~ s/\.modified$//; # lazy
$orig =~ s!^\Q$tmp\E/changeset/patches/!!; $orig =~ s!^\Q$tmp\E/changeset/patches/!!;
#print "rsync -p '$mod' '$orig'"; #print "rsync -p '$mod' '$orig'";
`rsync -p $mod ./$orig`; system('rsync','-p',$mod,"./$orig");
die "Problem applying binary changes! $!" if $?; die "Problem applying binary changes! $!" if $?;
} }
} }
# bring in new files # bring in new files
`rsync --archive --exclude '$git_dir' --exclude '.arch-ids' --exclude '{arch}' $tmp/changeset/new-files-archive/* ./`; system('rsync','-aI','--exclude',$git_dir,
'--exclude','.arch-ids',
'--exclude', '{arch}',
"$tmp/changeset/new-files-archive/",'./');
# deleted files are hinted from the commitlog processing # deleted files are hinted from the commitlog processing
`rm -fr $tmp/changeset`; rmtree("$tmp/changeset");
} }
# =for reference # =for reference
# A log entry looks like # notes: *-files/-directories keys cannot have spaces, they're always
# pika-escaped. Everything after the first newline
# A log entry looks like:
# Revision: moodle-org--moodle--1.3.3--patch-15 # Revision: moodle-org--moodle--1.3.3--patch-15
# Archive: arch-eduforge@catalyst.net.nz--2004 # Archive: arch-eduforge@catalyst.net.nz--2004
# Creator: Penny Leach <penny@catalyst.net.nz> # Creator: Penny Leach <penny@catalyst.net.nz>
@ -566,70 +723,85 @@ sub apply_cset {
# admin/editor.html backup/lib.php backup/restore.php # admin/editor.html backup/lib.php backup/restore.php
# New-patches: arch-eduforge@catalyst.net.nz--2004/moodle-org--moodle--1.3.3--patch-15 # New-patches: arch-eduforge@catalyst.net.nz--2004/moodle-org--moodle--1.3.3--patch-15
# Summary: Updating to latest from MOODLE_14_STABLE (1.4.5+) # Summary: Updating to latest from MOODLE_14_STABLE (1.4.5+)
# summary can be multiline with a leading space just like the above fields
# Keywords: # Keywords:
# #
# Updating yadda tadda tadda madda # Updating yadda tadda tadda madda
sub parselog { sub parselog {
my $log = shift; my ($ps, $log) = @_;
#print $log; my $key = undef;
my (@add, @del, @mod, @ren, @kw, $sum, $msg ); # headers we want that contain filenames:
my %want_headers = (
if ($log =~ m/(?:\n|^)New-files:(.*?)(?=\n\w)/s ) { new_files => 1,
my $files = $1; modified_files => 1,
@add = split(m/\s+/s, $files); renamed_files => 1,
} renamed_directories => 1,
removed_files => 1,
if ($log =~ m/(?:\n|^)Removed-files:(.*?)(?=\n\w)/s ) { removed_directories => 1,
my $files = $1; );
@del = split(m/\s+/s, $files);
}
if ($log =~ m/(?:\n|^)Modified-files:(.*?)(?=\n\w)/s ) { chomp (@$log);
my $files = $1; while ($_ = shift @$log) {
@mod = split(m/\s+/s, $files); if (/^Continuation-of:\s*(.*)/) {
$ps->{tag} = $1;
$key = undef;
} elsif (/^Summary:\s*(.*)$/ ) {
# summary can be multiline as long as it has a leading space
$ps->{summary} = [ $1 ];
$key = 'summary';
} elsif (/^Creator: (.*)\s*<([^\>]+)>/) {
$ps->{author} = $1;
$ps->{email} = $2;
$key = undef;
# any *-files or *-directories can be read here:
} elsif (/^([A-Z][a-z\-]+):\s*(.*)$/) {
my $val = $2;
$key = lc $1;
$key =~ tr/-/_/; # too lazy to quote :P
if ($want_headers{$key}) {
push @{$ps->{$key}}, split(/\s+/, $val);
} else {
$key = undef;
}
} elsif (/^$/) {
last; # remainder of @$log that didn't get shifted off is message
} elsif ($key) {
if (/^\s+(.*)$/) {
if ($key eq 'summary') {
push @{$ps->{$key}}, $1;
} else { # files/directories:
push @{$ps->{$key}}, split(/\s+/, $1);
}
} else {
$key = undef;
}
}
} }
# post-processing:
$ps->{summary} = join("\n",@{$ps->{summary}})."\n";
$ps->{message} = join("\n",@$log);
if ($log =~ m/(?:\n|^)Renamed-files:(.*?)(?=\n\w)/s ) { # skip Arch control files, unescape pika-escaped files
my $files = $1; foreach my $k (keys %want_headers) {
@ren = split(m/\s+/s, $files); next unless (defined $ps->{$k});
}
$sum ='';
if ($log =~ m/^Summary:(.+?)$/m ) {
$sum = $1;
$sum =~ s/^\s+//;
$sum =~ s/\s+$//;
}
$msg = '';
if ($log =~ m/\n\n(.+)$/s) {
$msg = $1;
$msg =~ s/^\s+//;
$msg =~ s/\s+$//;
}
# cleanup the arrays
foreach my $ref ( (\@add, \@del, \@mod, \@ren) ) {
my @tmp = (); my @tmp = ();
while (my $t = pop @$ref) { foreach my $t (@{$ps->{$k}}) {
next unless length ($t); next unless length ($t);
next if $t =~ m!\{arch\}/!; next if $t =~ m!\{arch\}/!;
next if $t =~ m!\.arch-ids/!; next if $t =~ m!\.arch-ids/!;
next if $t =~ m!\.arch-inventory$!; # should we skip this?
next if $t =~ m!\.arch-inventory$!;
# tla cat-archive-log will give us filenames with spaces as file\(sp)name - why? # tla cat-archive-log will give us filenames with spaces as file\(sp)name - why?
# we can assume that any filename with \ indicates some pika escaping that we want to get rid of. # we can assume that any filename with \ indicates some pika escaping that we want to get rid of.
if ($t =~ /\\/ ){ if ($t =~ /\\/ ){
$t = `tla escape --unescaped '$t'`; $t = (safe_pipe_capture($TLA,'escape','--unescaped',$t))[0];
} }
push (@tmp, shell_quote($t)); push @tmp, $t;
} }
@$ref = @tmp; $ps->{$k} = \@tmp;
} }
#print Dumper [$sum, $msg, \@add, \@del, \@mod, \@ren];
return ($sum, $msg, \@add, \@del, \@mod, \@ren);
} }
# write/read a tag # write/read a tag
@ -826,8 +998,11 @@ sub find_parents {
} }
} }
} }
@parents = keys %parents;
@parents = map { " -p " . ptag($_) } @parents; @parents = ();
foreach (keys %parents) {
push @parents, '-p', ptag($_);
}
return @parents; return @parents;
} }
@ -850,3 +1025,45 @@ sub commitid2pset {
|| (print Dumper(sort keys %psets)) && die "Cannot find patchset for $name"; || (print Dumper(sort keys %psets)) && die "Cannot find patchset for $name";
return $ps; return $ps;
} }
# an alterative to `command` that allows input to be passed as an array
# to work around shell problems with weird characters in arguments
sub safe_pipe_capture {
my @output;
if (my $pid = open my $child, '-|') {
@output = (<$child>);
close $child or die join(' ',@_).": $! $?";
} else {
exec(@_) or die "$! $?"; # exec() can fail the executable can't be found
}
return wantarray ? @output : join('',@output);
}
# `tla logs -rf -d <dir> | head -n1` or `baz tree-id <dir>`
sub arch_tree_id {
my $dir = shift;
chomp( my $ret = (safe_pipe_capture($TLA,'logs','-rf','-d',$dir))[0] );
return $ret;
}
sub archive_reachable {
my $archive = shift;
return 1 if $reachable{$archive};
return 0 if $unreachable{$archive};
if (system "$TLA whereis-archive $archive >/dev/null") {
if ($opt_a && (system($TLA,'register-archive',
"http://mirrors.sourcecontrol.net/$archive") == 0)) {
$reachable{$archive} = 1;
return 1;
}
print STDERR "Archive is unreachable: $archive\n";
$unreachable{$archive} = 1;
return 0;
} else {
$reachable{$archive} = 1;
return 1;
}
}

View File

@ -67,7 +67,7 @@ bisect_start() {
rm -rf "$GIT_DIR/refs/bisect/" rm -rf "$GIT_DIR/refs/bisect/"
mkdir "$GIT_DIR/refs/bisect" mkdir "$GIT_DIR/refs/bisect"
{ {
echo -n "git-bisect start" printf "git-bisect start"
sq "$@" sq "$@"
} >"$GIT_DIR/BISECT_LOG" } >"$GIT_DIR/BISECT_LOG"
sq "$@" >"$GIT_DIR/BISECT_NAMES" sq "$@" >"$GIT_DIR/BISECT_NAMES"

113
git-compat-util.h Normal file
View File

@ -0,0 +1,113 @@
#ifndef GIT_COMPAT_UTIL_H
#define GIT_COMPAT_UTIL_H
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <dirent.h>
#ifdef __GNUC__
#define NORETURN __attribute__((__noreturn__))
#else
#define NORETURN
#ifndef __attribute__
#define __attribute__(x)
#endif
#endif
/* General helper functions */
extern void usage(const char *err) NORETURN;
extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
#ifdef NO_MMAP
#ifndef PROT_READ
#define PROT_READ 1
#define PROT_WRITE 2
#define MAP_PRIVATE 1
#define MAP_FAILED ((void*)-1)
#endif
#define mmap gitfakemmap
#define munmap gitfakemunmap
extern void *gitfakemmap(void *start, size_t length, int prot , int flags, int fd, off_t offset);
extern int gitfakemunmap(void *start, size_t length);
#else /* NO_MMAP */
#include <sys/mman.h>
#endif /* NO_MMAP */
#ifdef NO_SETENV
#define setenv gitsetenv
extern int gitsetenv(const char *, const char *, int);
#endif
#ifdef NO_STRCASESTR
#define strcasestr gitstrcasestr
extern char *gitstrcasestr(const char *haystack, const char *needle);
#endif
static inline void *xmalloc(size_t size)
{
void *ret = malloc(size);
if (!ret)
die("Out of memory, malloc failed");
return ret;
}
static inline void *xrealloc(void *ptr, size_t size)
{
void *ret = realloc(ptr, size);
if (!ret)
die("Out of memory, realloc failed");
return ret;
}
static inline void *xcalloc(size_t nmemb, size_t size)
{
void *ret = calloc(nmemb, size);
if (!ret)
die("Out of memory, calloc failed");
return ret;
}
/* Sane ctype - no locale, and works with signed chars */
#undef isspace
#undef isdigit
#undef isalpha
#undef isalnum
#undef tolower
#undef toupper
extern unsigned char sane_ctype[256];
#define GIT_SPACE 0x01
#define GIT_DIGIT 0x02
#define GIT_ALPHA 0x04
#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
#define isspace(x) sane_istest(x,GIT_SPACE)
#define isdigit(x) sane_istest(x,GIT_DIGIT)
#define isalpha(x) sane_istest(x,GIT_ALPHA)
#define isalnum(x) sane_istest(x,GIT_ALPHA | GIT_DIGIT)
#define tolower(x) sane_case((unsigned char)(x), 0x20)
#define toupper(x) sane_case((unsigned char)(x), 0)
static inline int sane_case(int x, int high)
{
if (sane_istest(x, GIT_ALPHA))
x = (x & ~0x20) | high;
return x;
}
#endif

View File

@ -254,6 +254,15 @@ Date: '"$ad"
} }
total=`wc -l <$series | tr -dc "[0-9]"` total=`wc -l <$series | tr -dc "[0-9]"`
case "$total,$numbered" in
1,*)
numfmt='' ;;
*,t)
numfmt=`echo "$total" | wc -c`
numfmt=$(($numfmt-1))
numfmt=" %0${numfmt}d/$total"
esac
i=1 i=1
while read commit while read commit
do do
@ -262,10 +271,7 @@ do
case "$numbered" in case "$numbered" in
'') num= ;; '') num= ;;
*) *)
case $total in num=`printf "$numfmt" $i` ;;
1) num= ;;
*) num=' '`printf "%d/%d" $i $total` ;;
esac
esac esac
file=`printf '%04d-%stxt' $i "$title"` file=`printf '%04d-%stxt' $i "$title"`

View File

@ -87,15 +87,16 @@ case "${1:-.}${2:-.}${3:-.}" in
;; ;;
esac esac
# Create the working tree file, with the correct permission bits. # Be careful for funny filename such as "-L" in "$4", which
# we can not rely on the fact that our tree has the path, because # would confuse "merge" greatly.
# we allow the merge to be done in an unchecked-out working tree. src1=`git-unpack-file $2`
rm -f "$4" && merge "$src1" "$orig" "$src2"
git-cat-file blob "$2" >"$4" &&
case "$6" in *7??) chmod +x "$4" ;; esac &&
merge "$4" "$orig" "$src2"
ret=$? ret=$?
rm -f -- "$orig" "$src2"
# Create the working tree file, using "our tree" version from the
# index, and then store the result of the merge.
git-checkout-index -f --stage=2 -- "$4" && cat "$src1" >"$4"
rm -f -- "$orig" "$src1" "$src2"
if [ "$6" != "$7" ]; then if [ "$6" != "$7" ]; then
echo "ERROR: Permissions conflict: $5->$6,$7." echo "ERROR: Permissions conflict: $5->$6,$7."

View File

@ -280,6 +280,16 @@ def updateFileExt(sha, mode, path, updateCache, updateWd):
runProgram(['git-update-index', '--add', '--cacheinfo', runProgram(['git-update-index', '--add', '--cacheinfo',
'0%o' % mode, sha, path]) '0%o' % mode, sha, path])
def setIndexStages(path,
oSHA1, oMode,
aSHA1, aMode,
bSHA1, bMode):
runProgram(['git-update-index', '-z', '--index-info'],
input="0 " + ("0" * 40) + "\t" + path + "\0" + \
"%o %s %d\t%s\0" % (oMode, oSHA1, 1, path) + \
"%o %s %d\t%s\0" % (aMode, aSHA1, 2, path) + \
"%o %s %d\t%s\0" % (bMode, bSHA1, 3, path))
def removeFile(clean, path): def removeFile(clean, path):
updateCache = cacheOnly or clean updateCache = cacheOnly or clean
updateWd = not cacheOnly updateWd = not cacheOnly
@ -590,6 +600,8 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
else: else:
dstName2 = ren1.dstName dstName2 = ren1.dstName
# NEEDSWORK: place dstNameA at stage 2 and dstNameB at stage 3
# What about other stages???
updateFile(False, ren1.dstSha, ren1.dstMode, dstName1) updateFile(False, ren1.dstSha, ren1.dstMode, dstName1)
updateFile(False, ren2.dstSha, ren2.dstMode, dstName2) updateFile(False, ren2.dstSha, ren2.dstMode, dstName2)
else: else:
@ -611,8 +623,11 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
cleanMerge = False cleanMerge = False
if not cacheOnly: if not cacheOnly:
updateFileExt(ren1.dstSha, ren1.dstMode, ren1.dstName, setIndexStages(ren1.dstName,
updateCache=True, updateWd=False) ren1.srcSha, ren1.srcMode,
ren1.dstSha, ren1.dstMode,
ren2.dstSha, ren2.dstMode)
updateFile(clean, resSha, resMode, ren1.dstName) updateFile(clean, resSha, resMode, ren1.dstName)
else: else:
# Renamed in 1, maybe changed in 2 # Renamed in 1, maybe changed in 2
@ -672,11 +687,24 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
tryMerge = True tryMerge = True
if tryMerge: if tryMerge:
oName, oSHA1, oMode = ren1.srcName, ren1.srcSha, ren1.srcMode
aName, bName = ren1.dstName, ren1.srcName
aSHA1, bSHA1 = ren1.dstSha, srcShaOtherBranch
aMode, bMode = ren1.dstMode, srcModeOtherBranch
aBranch, bBranch = branchName1, branchName2
if renamesA != renames1:
aName, bName = bName, aName
aSHA1, bSHA1 = bSHA1, aSHA1
aMode, bMode = bMode, aMode
aBranch, bBranch = bBranch, aBranch
[resSha, resMode, clean, merge] = \ [resSha, resMode, clean, merge] = \
mergeFile(ren1.srcName, ren1.srcSha, ren1.srcMode, mergeFile(oName, oSHA1, oMode,
ren1.dstName, ren1.dstSha, ren1.dstMode, aName, aSHA1, aMode,
ren1.srcName, srcShaOtherBranch, srcModeOtherBranch, bName, bSHA1, bMode,
branchName1, branchName2) aBranch, bBranch);
if merge or not clean: if merge or not clean:
output('Renaming', fmtRename(ren1.srcName, ren1.dstName)) output('Renaming', fmtRename(ren1.srcName, ren1.dstName))
@ -690,8 +718,11 @@ def processRenames(renamesA, renamesB, branchNameA, branchNameB):
cleanMerge = False cleanMerge = False
if not cacheOnly: if not cacheOnly:
updateFileExt(ren1.dstSha, ren1.dstMode, ren1.dstName, setIndexStages(ren1.dstName,
updateCache=True, updateWd=False) oSHA1, oMode,
aSHA1, aMode,
bSHA1, bMode)
updateFile(clean, resSha, resMode, ren1.dstName) updateFile(clean, resSha, resMode, ren1.dstName)
return cleanMerge return cleanMerge

View File

@ -62,7 +62,7 @@ my $safesrc;
my (%overwritten, %srcForDst); my (%overwritten, %srcForDst);
$/ = "\0"; $/ = "\0";
open(F,"-|","git-ls-files","-z") open(F, 'git-ls-files -z |')
or die "Failed to open pipe from git-ls-files: " . $!; or die "Failed to open pipe from git-ls-files: " . $!;
@allfiles = map { chomp; $_; } <F>; @allfiles = map { chomp; $_; } <F>;

View File

@ -16,7 +16,11 @@ do
done done
sync sync
git-fsck-objects --full --cache --unreachable "$@" | case "$#" in
0) git-fsck-objects --full --cache --unreachable ;;
*) git-fsck-objects --full --cache --unreachable $(git-rev-parse --all) "$@" ;;
esac |
sed -ne '/unreachable /{ sed -ne '/unreachable /{
s/unreachable [^ ][^ ]* // s/unreachable [^ ][^ ]* //
s|\(..\)|\1/|p s|\(..\)|\1/|p

View File

@ -19,10 +19,10 @@ esac
usage () { usage () {
case "$me" in case "$me" in
cherry-pick) cherry-pick)
die "usage git $me [-n] [-r] <commit-ish>" die "usage git $me [--edit] [-n] [-r] <commit-ish>"
;; ;;
revert) revert)
die "usage git $me [-n] <commit-ish>" die "usage git $me [--edit | --no-edit] [-n] <commit-ish>"
;; ;;
esac esac
} }
@ -38,7 +38,7 @@ do
-e|--e|--ed|--edi|--edit) -e|--e|--ed|--edi|--edit)
edit=-e edit=-e
;; ;;
-n|--n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit) --n|--no|--no-|--no-e|--no-ed|--no-edi|--no-edit)
edit= edit=
;; ;;
-r|--r|--re|--rep|--repl|--repla|--replay) -r|--r|--re|--rep|--repl|--repla|--replay)

View File

@ -13,7 +13,7 @@ report () {
trailer="" trailer=""
while read status name newname while read status name newname
do do
echo -n "$header" printf '%s' "$header"
header="" header=""
trailer="# trailer="#
" "
@ -27,7 +27,7 @@ report () {
U ) echo "# unmerged: $name";; U ) echo "# unmerged: $name";;
esac esac
done done
echo -n "$trailer" printf '%s' "$trailer"
[ "$header" ] [ "$header" ]
} }

18
git.c
View File

@ -8,15 +8,12 @@
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <stdarg.h> #include <stdarg.h>
#include "git-compat-util.h"
#ifndef PATH_MAX #ifndef PATH_MAX
# define PATH_MAX 4096 # define PATH_MAX 4096
#endif #endif
#ifdef NO_SETENV
extern int gitsetenv(char *name, char *value, int overwrite);
#endif
static const char git_usage[] = static const char git_usage[] =
"Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]"; "Usage: git [--version] [--exec-path[=GIT_EXEC_PATH]] [--help] COMMAND [ ARGS ]";
@ -156,10 +153,10 @@ static void list_commands(const char *exec_path, const char *pattern)
} }
#ifdef __GNUC__ #ifdef __GNUC__
static void usage(const char *exec_path, const char *fmt, ...) static void cmd_usage(const char *exec_path, const char *fmt, ...)
__attribute__((__format__(__printf__, 2, 3), __noreturn__)); __attribute__((__format__(__printf__, 2, 3), __noreturn__));
#endif #endif
static void usage(const char *exec_path, const char *fmt, ...) static void cmd_usage(const char *exec_path, const char *fmt, ...)
{ {
if (fmt) { if (fmt) {
va_list ap; va_list ap;
@ -192,7 +189,6 @@ static void prepend_to_path(const char *dir, int len)
path_len = len + strlen(old_path) + 1; path_len = len + strlen(old_path) + 1;
path = malloc(path_len + 1); path = malloc(path_len + 1);
path[path_len + 1] = '\0';
memcpy(path, dir, len); memcpy(path, dir, len);
path[len] = ':'; path[len] = ':';
@ -255,12 +251,12 @@ int main(int argc, char **argv, char **envp)
else if (!strcmp(arg, "help")) else if (!strcmp(arg, "help"))
show_help = 1; show_help = 1;
else if (!show_help) else if (!show_help)
usage(NULL, NULL); cmd_usage(NULL, NULL);
} }
if (i >= argc || show_help) { if (i >= argc || show_help) {
if (i >= argc) if (i >= argc)
usage(exec_path, NULL); cmd_usage(exec_path, NULL);
show_man_page(argv[i]); show_man_page(argv[i]);
} }
@ -290,7 +286,7 @@ int main(int argc, char **argv, char **envp)
len += snprintf(git_command + len, sizeof(git_command) - len, len += snprintf(git_command + len, sizeof(git_command) - len,
"/git-%s", argv[i]); "/git-%s", argv[i]);
if (sizeof(git_command) <= len) { if (sizeof(git_command) <= len) {
fprintf(stderr, "git: command name given is too long (%d)\n", len); fprintf(stderr, "git: command name given is too long.\n");
exit(1); exit(1);
} }
@ -298,7 +294,7 @@ int main(int argc, char **argv, char **envp)
execve(git_command, &argv[i], envp); execve(git_command, &argv[i], envp);
if (errno == ENOENT) if (errno == ENOENT)
usage(exec_path, "'%s' is not a git-command", argv[i]); cmd_usage(exec_path, "'%s' is not a git-command", argv[i]);
fprintf(stderr, "Failed to run command '%s': %s\n", fprintf(stderr, "Failed to run command '%s': %s\n",
git_command, strerror(errno)); git_command, strerror(errno));

538
gitk
View File

@ -16,22 +16,9 @@ proc gitdir {} {
} }
} }
proc getcommits {rargs} { proc parse_args {rargs} {
global commits commfd phase canv mainfont env global parsed_args
global startmsecs nextupdate ncmupdate
global ctext maincursor textcursor leftover gitencoding
# check that we can find a .git directory somewhere...
set gitdir [gitdir]
if {![file isdirectory $gitdir]} {
error_popup "Cannot find the git directory \"$gitdir\"."
exit 1
}
set commits {}
set phase getcommits
set startmsecs [clock clicks -milliseconds]
set nextupdate [expr {$startmsecs + 100}]
set ncmupdate 1
if [catch { if [catch {
set parse_args [concat --default HEAD $rargs] set parse_args [concat --default HEAD $rargs]
set parsed_args [split [eval exec git-rev-parse $parse_args] "\n"] set parsed_args [split [eval exec git-rev-parse $parse_args] "\n"]
@ -42,26 +29,56 @@ proc getcommits {rargs} {
} }
set parsed_args $rargs set parsed_args $rargs
} }
return $parsed_args
}
proc start_rev_list {rlargs} {
global startmsecs nextupdate ncmupdate
global commfd leftover tclencoding
set startmsecs [clock clicks -milliseconds]
set nextupdate [expr {$startmsecs + 100}]
set ncmupdate 1
if [catch { if [catch {
set commfd [open "|git-rev-list --header --topo-order --parents $parsed_args" r] set commfd [open [concat | git-rev-list --header --topo-order \
--parents $rlargs] r]
} err] { } err] {
puts stderr "Error executing git-rev-list: $err" puts stderr "Error executing git-rev-list: $err"
exit 1 exit 1
} }
set leftover {} set leftover {}
fconfigure $commfd -blocking 0 -translation lf -encoding $gitencoding fconfigure $commfd -blocking 0 -translation lf
if {$tclencoding != {}} {
fconfigure $commfd -encoding $tclencoding
}
fileevent $commfd readable [list getcommitlines $commfd] fileevent $commfd readable [list getcommitlines $commfd]
$canv delete all
$canv create text 3 3 -anchor nw -text "Reading commits..." \
-font $mainfont -tags textitems
. config -cursor watch . config -cursor watch
settextcursor watch settextcursor watch
} }
proc getcommits {rargs} {
global oldcommits commits phase canv mainfont env
# check that we can find a .git directory somewhere...
set gitdir [gitdir]
if {![file isdirectory $gitdir]} {
error_popup "Cannot find the git directory \"$gitdir\"."
exit 1
}
set oldcommits {}
set commits {}
set phase getcommits
start_rev_list [parse_args $rargs]
$canv delete all
$canv create text 3 3 -anchor nw -text "Reading commits..." \
-font $mainfont -tags textitems
}
proc getcommitlines {commfd} { proc getcommitlines {commfd} {
global commits parents cdate children global oldcommits commits parents cdate children nchildren
global commitlisted phase nextupdate global commitlisted phase nextupdate
global stopped redisplaying leftover global stopped redisplaying leftover
global canv
set stuff [read $commfd] set stuff [read $commfd]
if {$stuff == {}} { if {$stuff == {}} {
@ -122,7 +139,7 @@ proc getcommitlines {commfd} {
lappend commits $id lappend commits $id
set commitlisted($id) 1 set commitlisted($id) 1
parsecommit $id $cmit 1 [lrange $ids 1 end] parsecommit $id $cmit 1 [lrange $ids 1 end]
drawcommit $id drawcommit $id 1
if {[clock clicks -milliseconds] >= $nextupdate} { if {[clock clicks -milliseconds] >= $nextupdate} {
doupdate 1 doupdate 1
} }
@ -132,7 +149,7 @@ proc getcommitlines {commfd} {
set stopped 0 set stopped 0
set phase "getcommits" set phase "getcommits"
foreach id $commits { foreach id $commits {
drawcommit $id drawcommit $id 1
if {$stopped} break if {$stopped} break
if {[clock clicks -milliseconds] >= $nextupdate} { if {[clock clicks -milliseconds] >= $nextupdate} {
doupdate 1 doupdate 1
@ -168,16 +185,99 @@ proc readcommit {id} {
parsecommit $id $contents 0 {} parsecommit $id $contents 0 {}
} }
proc parsecommit {id contents listed olds} { proc updatecommits {rargs} {
global commitinfo children nchildren parents nparents cdate ncleft global commitlisted commfd phase
global startmsecs nextupdate ncmupdate
global idtags idheads idotherrefs
global leftover
global parsed_args
global canv mainfont
global oldcommits commits
global parents nchildren children ncleft
set old_args $parsed_args
parse_args $rargs
if {$phase == "getcommits" || $phase == "incrdraw"} {
# havent read all the old commits, just start again from scratch
stopfindproc
set oldcommits {}
set commits {}
foreach v {children nchildren parents commitlisted commitinfo
selectedline matchinglines treediffs
mergefilelist currentid rowtextx} {
global $v
catch {unset $v}
}
readrefs
if {$phase == "incrdraw"} {
allcanvs delete all
$canv create text 3 3 -anchor nw -text "Reading commits..." \
-font $mainfont -tags textitems
set phase getcommits
}
start_rev_list $parsed_args
return
}
foreach id $old_args {
if {![regexp {^[0-9a-f]{40}$} $id]} continue
if {[info exists oldref($id)]} continue
set oldref($id) $id
lappend ignoreold "^$id"
}
foreach id $parsed_args {
if {![regexp {^[0-9a-f]{40}$} $id]} continue
if {[info exists ref($id)]} continue
set ref($id) $id
lappend ignorenew "^$id"
}
foreach a $old_args {
if {![info exists ref($a)]} {
lappend ignorenew $a
}
}
set phase updatecommits
set oldcommits $commits
set commits {}
set removed_commits [split [eval exec git-rev-list $ignorenew] "\n" ]
if {[llength $removed_commits] > 0} {
allcanvs delete all
foreach c $removed_commits {
set i [lsearch -exact $oldcommits $c]
if {$i >= 0} {
set oldcommits [lreplace $oldcommits $i $i]
unset commitlisted($c)
foreach p $parents($c) {
if {[info exists nchildren($p)]} {
set j [lsearch -exact $children($p) $c]
if {$j >= 0} {
set children($p) [lreplace $children($p) $j $j]
incr nchildren($p) -1
}
}
}
}
}
set phase removecommits
}
set args {}
foreach a $parsed_args {
if {![info exists oldref($a)]} {
lappend args $a
}
}
readrefs
start_rev_list [concat $ignoreold $args]
}
proc updatechildren {id olds} {
global children nchildren parents nparents ncleft
set inhdr 1
set comment {}
set headline {}
set auname {}
set audate {}
set comname {}
set comdate {}
if {![info exists nchildren($id)]} { if {![info exists nchildren($id)]} {
set children($id) {} set children($id) {}
set nchildren($id) 0 set nchildren($id) 0
@ -196,6 +296,19 @@ proc parsecommit {id contents listed olds} {
incr ncleft($p) incr ncleft($p)
} }
} }
}
proc parsecommit {id contents listed olds} {
global commitinfo cdate
set inhdr 1
set comment {}
set headline {}
set auname {}
set audate {}
set comname {}
set comdate {}
updatechildren $id $olds
set hdrend [string first "\n\n" $contents] set hdrend [string first "\n\n" $contents]
if {$hdrend < 0} { if {$hdrend < 0} {
# should never happen... # should never happen...
@ -243,6 +356,9 @@ proc readrefs {} {
global tagids idtags headids idheads tagcontents global tagids idtags headids idheads tagcontents
global otherrefids idotherrefs global otherrefids idotherrefs
foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
catch {unset $v}
}
set refd [open [list | git-ls-remote [gitdir]] r] set refd [open [list | git-ls-remote [gitdir]] r]
while {0 <= [set n [gets $refd line]]} { while {0 <= [set n [gets $refd line]]} {
if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \ if {![regexp {^([0-9a-f]{40}) refs/([^^]*)$} $line \
@ -292,7 +408,7 @@ proc error_popup msg {
tkwait window $w tkwait window $w
} }
proc makewindow {} { proc makewindow {rargs} {
global canv canv2 canv3 linespc charspc ctext cflist textfont global canv canv2 canv3 linespc charspc ctext cflist textfont
global findtype findtypemenu findloc findstring fstring geometry global findtype findtypemenu findloc findstring fstring geometry
global entries sha1entry sha1string sha1but global entries sha1entry sha1string sha1but
@ -302,6 +418,7 @@ proc makewindow {} {
menu .bar menu .bar
.bar add cascade -label "File" -menu .bar.file .bar add cascade -label "File" -menu .bar.file
menu .bar.file menu .bar.file
.bar.file add command -label "Update" -command [list updatecommits $rargs]
.bar.file add command -label "Reread references" -command rereadrefs .bar.file add command -label "Reread references" -command rereadrefs
.bar.file add command -label "Quit" -command doquit .bar.file add command -label "Quit" -command doquit
menu .bar.edit menu .bar.edit
@ -719,7 +836,6 @@ proc assigncolor {id} {
proc initgraph {} { proc initgraph {} {
global canvy canvy0 lineno numcommits nextcolor linespc global canvy canvy0 lineno numcommits nextcolor linespc
global mainline mainlinearrow sidelines
global nchildren ncleft global nchildren ncleft
global displist nhyperspace global displist nhyperspace
@ -728,9 +844,11 @@ proc initgraph {} {
set canvy $canvy0 set canvy $canvy0
set lineno -1 set lineno -1
set numcommits 0 set numcommits 0
catch {unset mainline} foreach v {mainline mainlinearrow sidelines colormap cornercrossings
catch {unset mainlinearrow} crossings idline lineid} {
catch {unset sidelines} global $v
catch {unset $v}
}
foreach id [array names nchildren] { foreach id [array names nchildren] {
set ncleft($id) $nchildren($id) set ncleft($id) $nchildren($id)
} }
@ -1399,29 +1517,23 @@ proc decidenext {{noread 0}} {
} }
} }
} }
if {$level < 0} {
if {$todo != {}} {
puts "ERROR: none of the pending commits can be done yet:"
foreach p $todo {
puts " $p ($ncleft($p))"
}
}
return -1
}
return $level return $level
} }
proc drawcommit {id} { proc drawcommit {id reading} {
global phase todo nchildren datemode nextupdate revlistorder global phase todo nchildren datemode nextupdate revlistorder ncleft
global numcommits ncmupdate displayorder todo onscreen parents global numcommits ncmupdate displayorder todo onscreen parents
global commitlisted commitordered
if {$phase != "incrdraw"} { if {$phase != "incrdraw"} {
set phase incrdraw set phase incrdraw
set displayorder {} set displayorder {}
set todo {} set todo {}
initgraph initgraph
catch {unset commitordered}
} }
set commitordered($id) 1
if {$nchildren($id) == 0} { if {$nchildren($id) == 0} {
lappend todo $id lappend todo $id
set onscreen($id) 0 set onscreen($id) 0
@ -1436,35 +1548,44 @@ proc drawcommit {id} {
updatetodo $level 0 updatetodo $level 0
} else { } else {
set level [decidenext 1] set level [decidenext 1]
if {$level == {} || $id != [lindex $todo $level]} { if {$level == {} || $level < 0} return
return
}
while 1 { while 1 {
set id [lindex $todo $level]
if {![info exists commitordered($id)]} {
break
}
lappend displayorder [lindex $todo $level] lappend displayorder [lindex $todo $level]
if {[updatetodo $level $datemode]} { if {[updatetodo $level $datemode]} {
set level [decidenext 1] set level [decidenext 1]
if {$level == {}} break if {$level == {} || $level < 0} break
}
set id [lindex $todo $level]
if {![info exists commitlisted($id)]} {
break
} }
} }
} }
drawmore 1 drawmore $reading
} }
proc finishcommits {} { proc finishcommits {} {
global phase global phase oldcommits commits
global canv mainfont ctext maincursor textcursor global canv mainfont ctext maincursor textcursor
global parents displayorder todo
if {$phase != "incrdraw"} { if {$phase == "incrdraw" || $phase == "removecommits"} {
foreach id $oldcommits {
lappend commits $id
drawcommit $id 0
}
set oldcommits {}
drawrest
} elseif {$phase == "updatecommits"} {
# there were no new commits, in fact
set commits $oldcommits
set oldcommits {}
set phase {}
} else {
$canv delete all $canv delete all
$canv create text 3 3 -anchor nw -text "No commits selected" \ $canv create text 3 3 -anchor nw -text "No commits selected" \
-font $mainfont -tags textitems -font $mainfont -tags textitems
set phase {} set phase {}
} else {
drawrest
} }
. config -cursor $maincursor . config -cursor $maincursor
settextcursor $textcursor settextcursor $textcursor
@ -1498,7 +1619,7 @@ proc drawgraph {} {
proc drawrest {} { proc drawrest {} {
global phase stopped redisplaying selectedline global phase stopped redisplaying selectedline
global datemode todo displayorder global datemode todo displayorder ncleft
global numcommits ncmupdate global numcommits ncmupdate
global nextupdate startmsecs revlistorder global nextupdate startmsecs revlistorder
@ -1514,6 +1635,13 @@ proc drawrest {} {
} }
} }
} }
if {$todo != {}} {
puts "ERROR: none of the pending commits can be done yet:"
foreach p $todo {
puts " $p ($ncleft($p))"
}
}
drawmore 0 drawmore 0
set phase {} set phase {}
set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}] set drawmsecs [expr {[clock clicks -milliseconds] - $startmsecs}]
@ -3578,9 +3706,6 @@ proc rereadrefs {} {
set ref($id) [listrefs $id] set ref($id) [listrefs $id]
} }
} }
foreach v {tagids idtags headids idheads otherrefids idotherrefs} {
catch {unset $v}
}
readrefs readrefs
set refids [lsort -unique [concat $refids [array names idtags] \ set refids [lsort -unique [concat $refids [array names idtags] \
[array names idheads] [array names idotherrefs]]] [array names idheads] [array names idotherrefs]]]
@ -3689,17 +3814,294 @@ proc formatdate {d} {
return [clock format $d -format "%Y-%m-%d %H:%M:%S"] return [clock format $d -format "%Y-%m-%d %H:%M:%S"]
} }
# This list of encoding names and aliases is distilled from
# http://www.iana.org/assignments/character-sets.
# Not all of them are supported by Tcl.
set encoding_aliases {
{ ANSI_X3.4-1968 iso-ir-6 ANSI_X3.4-1986 ISO_646.irv:1991 ASCII
ISO646-US US-ASCII us IBM367 cp367 csASCII }
{ ISO-10646-UTF-1 csISO10646UTF1 }
{ ISO_646.basic:1983 ref csISO646basic1983 }
{ INVARIANT csINVARIANT }
{ ISO_646.irv:1983 iso-ir-2 irv csISO2IntlRefVersion }
{ BS_4730 iso-ir-4 ISO646-GB gb uk csISO4UnitedKingdom }
{ NATS-SEFI iso-ir-8-1 csNATSSEFI }
{ NATS-SEFI-ADD iso-ir-8-2 csNATSSEFIADD }
{ NATS-DANO iso-ir-9-1 csNATSDANO }
{ NATS-DANO-ADD iso-ir-9-2 csNATSDANOADD }
{ SEN_850200_B iso-ir-10 FI ISO646-FI ISO646-SE se csISO10Swedish }
{ SEN_850200_C iso-ir-11 ISO646-SE2 se2 csISO11SwedishForNames }
{ KS_C_5601-1987 iso-ir-149 KS_C_5601-1989 KSC_5601 korean csKSC56011987 }
{ ISO-2022-KR csISO2022KR }
{ EUC-KR csEUCKR }
{ ISO-2022-JP csISO2022JP }
{ ISO-2022-JP-2 csISO2022JP2 }
{ JIS_C6220-1969-jp JIS_C6220-1969 iso-ir-13 katakana x0201-7
csISO13JISC6220jp }
{ JIS_C6220-1969-ro iso-ir-14 jp ISO646-JP csISO14JISC6220ro }
{ IT iso-ir-15 ISO646-IT csISO15Italian }
{ PT iso-ir-16 ISO646-PT csISO16Portuguese }
{ ES iso-ir-17 ISO646-ES csISO17Spanish }
{ greek7-old iso-ir-18 csISO18Greek7Old }
{ latin-greek iso-ir-19 csISO19LatinGreek }
{ DIN_66003 iso-ir-21 de ISO646-DE csISO21German }
{ NF_Z_62-010_(1973) iso-ir-25 ISO646-FR1 csISO25French }
{ Latin-greek-1 iso-ir-27 csISO27LatinGreek1 }
{ ISO_5427 iso-ir-37 csISO5427Cyrillic }
{ JIS_C6226-1978 iso-ir-42 csISO42JISC62261978 }
{ BS_viewdata iso-ir-47 csISO47BSViewdata }
{ INIS iso-ir-49 csISO49INIS }
{ INIS-8 iso-ir-50 csISO50INIS8 }
{ INIS-cyrillic iso-ir-51 csISO51INISCyrillic }
{ ISO_5427:1981 iso-ir-54 ISO5427Cyrillic1981 }
{ ISO_5428:1980 iso-ir-55 csISO5428Greek }
{ GB_1988-80 iso-ir-57 cn ISO646-CN csISO57GB1988 }
{ GB_2312-80 iso-ir-58 chinese csISO58GB231280 }
{ NS_4551-1 iso-ir-60 ISO646-NO no csISO60DanishNorwegian
csISO60Norwegian1 }
{ NS_4551-2 ISO646-NO2 iso-ir-61 no2 csISO61Norwegian2 }
{ NF_Z_62-010 iso-ir-69 ISO646-FR fr csISO69French }
{ videotex-suppl iso-ir-70 csISO70VideotexSupp1 }
{ PT2 iso-ir-84 ISO646-PT2 csISO84Portuguese2 }
{ ES2 iso-ir-85 ISO646-ES2 csISO85Spanish2 }
{ MSZ_7795.3 iso-ir-86 ISO646-HU hu csISO86Hungarian }
{ JIS_C6226-1983 iso-ir-87 x0208 JIS_X0208-1983 csISO87JISX0208 }
{ greek7 iso-ir-88 csISO88Greek7 }
{ ASMO_449 ISO_9036 arabic7 iso-ir-89 csISO89ASMO449 }
{ iso-ir-90 csISO90 }
{ JIS_C6229-1984-a iso-ir-91 jp-ocr-a csISO91JISC62291984a }
{ JIS_C6229-1984-b iso-ir-92 ISO646-JP-OCR-B jp-ocr-b
csISO92JISC62991984b }
{ JIS_C6229-1984-b-add iso-ir-93 jp-ocr-b-add csISO93JIS62291984badd }
{ JIS_C6229-1984-hand iso-ir-94 jp-ocr-hand csISO94JIS62291984hand }
{ JIS_C6229-1984-hand-add iso-ir-95 jp-ocr-hand-add
csISO95JIS62291984handadd }
{ JIS_C6229-1984-kana iso-ir-96 csISO96JISC62291984kana }
{ ISO_2033-1983 iso-ir-98 e13b csISO2033 }
{ ANSI_X3.110-1983 iso-ir-99 CSA_T500-1983 NAPLPS csISO99NAPLPS }
{ ISO_8859-1:1987 iso-ir-100 ISO_8859-1 ISO-8859-1 latin1 l1 IBM819
CP819 csISOLatin1 }
{ ISO_8859-2:1987 iso-ir-101 ISO_8859-2 ISO-8859-2 latin2 l2 csISOLatin2 }
{ T.61-7bit iso-ir-102 csISO102T617bit }
{ T.61-8bit T.61 iso-ir-103 csISO103T618bit }
{ ISO_8859-3:1988 iso-ir-109 ISO_8859-3 ISO-8859-3 latin3 l3 csISOLatin3 }
{ ISO_8859-4:1988 iso-ir-110 ISO_8859-4 ISO-8859-4 latin4 l4 csISOLatin4 }
{ ECMA-cyrillic iso-ir-111 KOI8-E csISO111ECMACyrillic }
{ CSA_Z243.4-1985-1 iso-ir-121 ISO646-CA csa7-1 ca csISO121Canadian1 }
{ CSA_Z243.4-1985-2 iso-ir-122 ISO646-CA2 csa7-2 csISO122Canadian2 }
{ CSA_Z243.4-1985-gr iso-ir-123 csISO123CSAZ24341985gr }
{ ISO_8859-6:1987 iso-ir-127 ISO_8859-6 ISO-8859-6 ECMA-114 ASMO-708
arabic csISOLatinArabic }
{ ISO_8859-6-E csISO88596E ISO-8859-6-E }
{ ISO_8859-6-I csISO88596I ISO-8859-6-I }
{ ISO_8859-7:1987 iso-ir-126 ISO_8859-7 ISO-8859-7 ELOT_928 ECMA-118
greek greek8 csISOLatinGreek }
{ T.101-G2 iso-ir-128 csISO128T101G2 }
{ ISO_8859-8:1988 iso-ir-138 ISO_8859-8 ISO-8859-8 hebrew
csISOLatinHebrew }
{ ISO_8859-8-E csISO88598E ISO-8859-8-E }
{ ISO_8859-8-I csISO88598I ISO-8859-8-I }
{ CSN_369103 iso-ir-139 csISO139CSN369103 }
{ JUS_I.B1.002 iso-ir-141 ISO646-YU js yu csISO141JUSIB1002 }
{ ISO_6937-2-add iso-ir-142 csISOTextComm }
{ IEC_P27-1 iso-ir-143 csISO143IECP271 }
{ ISO_8859-5:1988 iso-ir-144 ISO_8859-5 ISO-8859-5 cyrillic
csISOLatinCyrillic }
{ JUS_I.B1.003-serb iso-ir-146 serbian csISO146Serbian }
{ JUS_I.B1.003-mac macedonian iso-ir-147 csISO147Macedonian }
{ ISO_8859-9:1989 iso-ir-148 ISO_8859-9 ISO-8859-9 latin5 l5 csISOLatin5 }
{ greek-ccitt iso-ir-150 csISO150 csISO150GreekCCITT }
{ NC_NC00-10:81 cuba iso-ir-151 ISO646-CU csISO151Cuba }
{ ISO_6937-2-25 iso-ir-152 csISO6937Add }
{ GOST_19768-74 ST_SEV_358-88 iso-ir-153 csISO153GOST1976874 }
{ ISO_8859-supp iso-ir-154 latin1-2-5 csISO8859Supp }
{ ISO_10367-box iso-ir-155 csISO10367Box }
{ ISO-8859-10 iso-ir-157 l6 ISO_8859-10:1992 csISOLatin6 latin6 }
{ latin-lap lap iso-ir-158 csISO158Lap }
{ JIS_X0212-1990 x0212 iso-ir-159 csISO159JISX02121990 }
{ DS_2089 DS2089 ISO646-DK dk csISO646Danish }
{ us-dk csUSDK }
{ dk-us csDKUS }
{ JIS_X0201 X0201 csHalfWidthKatakana }
{ KSC5636 ISO646-KR csKSC5636 }
{ ISO-10646-UCS-2 csUnicode }
{ ISO-10646-UCS-4 csUCS4 }
{ DEC-MCS dec csDECMCS }
{ hp-roman8 roman8 r8 csHPRoman8 }
{ macintosh mac csMacintosh }
{ IBM037 cp037 ebcdic-cp-us ebcdic-cp-ca ebcdic-cp-wt ebcdic-cp-nl
csIBM037 }
{ IBM038 EBCDIC-INT cp038 csIBM038 }
{ IBM273 CP273 csIBM273 }
{ IBM274 EBCDIC-BE CP274 csIBM274 }
{ IBM275 EBCDIC-BR cp275 csIBM275 }
{ IBM277 EBCDIC-CP-DK EBCDIC-CP-NO csIBM277 }
{ IBM278 CP278 ebcdic-cp-fi ebcdic-cp-se csIBM278 }
{ IBM280 CP280 ebcdic-cp-it csIBM280 }
{ IBM281 EBCDIC-JP-E cp281 csIBM281 }
{ IBM284 CP284 ebcdic-cp-es csIBM284 }
{ IBM285 CP285 ebcdic-cp-gb csIBM285 }
{ IBM290 cp290 EBCDIC-JP-kana csIBM290 }
{ IBM297 cp297 ebcdic-cp-fr csIBM297 }
{ IBM420 cp420 ebcdic-cp-ar1 csIBM420 }
{ IBM423 cp423 ebcdic-cp-gr csIBM423 }
{ IBM424 cp424 ebcdic-cp-he csIBM424 }
{ IBM437 cp437 437 csPC8CodePage437 }
{ IBM500 CP500 ebcdic-cp-be ebcdic-cp-ch csIBM500 }
{ IBM775 cp775 csPC775Baltic }
{ IBM850 cp850 850 csPC850Multilingual }
{ IBM851 cp851 851 csIBM851 }
{ IBM852 cp852 852 csPCp852 }
{ IBM855 cp855 855 csIBM855 }
{ IBM857 cp857 857 csIBM857 }
{ IBM860 cp860 860 csIBM860 }
{ IBM861 cp861 861 cp-is csIBM861 }
{ IBM862 cp862 862 csPC862LatinHebrew }
{ IBM863 cp863 863 csIBM863 }
{ IBM864 cp864 csIBM864 }
{ IBM865 cp865 865 csIBM865 }
{ IBM866 cp866 866 csIBM866 }
{ IBM868 CP868 cp-ar csIBM868 }
{ IBM869 cp869 869 cp-gr csIBM869 }
{ IBM870 CP870 ebcdic-cp-roece ebcdic-cp-yu csIBM870 }
{ IBM871 CP871 ebcdic-cp-is csIBM871 }
{ IBM880 cp880 EBCDIC-Cyrillic csIBM880 }
{ IBM891 cp891 csIBM891 }
{ IBM903 cp903 csIBM903 }
{ IBM904 cp904 904 csIBBM904 }
{ IBM905 CP905 ebcdic-cp-tr csIBM905 }
{ IBM918 CP918 ebcdic-cp-ar2 csIBM918 }
{ IBM1026 CP1026 csIBM1026 }
{ EBCDIC-AT-DE csIBMEBCDICATDE }
{ EBCDIC-AT-DE-A csEBCDICATDEA }
{ EBCDIC-CA-FR csEBCDICCAFR }
{ EBCDIC-DK-NO csEBCDICDKNO }
{ EBCDIC-DK-NO-A csEBCDICDKNOA }
{ EBCDIC-FI-SE csEBCDICFISE }
{ EBCDIC-FI-SE-A csEBCDICFISEA }
{ EBCDIC-FR csEBCDICFR }
{ EBCDIC-IT csEBCDICIT }
{ EBCDIC-PT csEBCDICPT }
{ EBCDIC-ES csEBCDICES }
{ EBCDIC-ES-A csEBCDICESA }
{ EBCDIC-ES-S csEBCDICESS }
{ EBCDIC-UK csEBCDICUK }
{ EBCDIC-US csEBCDICUS }
{ UNKNOWN-8BIT csUnknown8BiT }
{ MNEMONIC csMnemonic }
{ MNEM csMnem }
{ VISCII csVISCII }
{ VIQR csVIQR }
{ KOI8-R csKOI8R }
{ IBM00858 CCSID00858 CP00858 PC-Multilingual-850+euro }
{ IBM00924 CCSID00924 CP00924 ebcdic-Latin9--euro }
{ IBM01140 CCSID01140 CP01140 ebcdic-us-37+euro }
{ IBM01141 CCSID01141 CP01141 ebcdic-de-273+euro }
{ IBM01142 CCSID01142 CP01142 ebcdic-dk-277+euro ebcdic-no-277+euro }
{ IBM01143 CCSID01143 CP01143 ebcdic-fi-278+euro ebcdic-se-278+euro }
{ IBM01144 CCSID01144 CP01144 ebcdic-it-280+euro }
{ IBM01145 CCSID01145 CP01145 ebcdic-es-284+euro }
{ IBM01146 CCSID01146 CP01146 ebcdic-gb-285+euro }
{ IBM01147 CCSID01147 CP01147 ebcdic-fr-297+euro }
{ IBM01148 CCSID01148 CP01148 ebcdic-international-500+euro }
{ IBM01149 CCSID01149 CP01149 ebcdic-is-871+euro }
{ IBM1047 IBM-1047 }
{ PTCP154 csPTCP154 PT154 CP154 Cyrillic-Asian }
{ Amiga-1251 Ami1251 Amiga1251 Ami-1251 }
{ UNICODE-1-1 csUnicode11 }
{ CESU-8 csCESU-8 }
{ BOCU-1 csBOCU-1 }
{ UNICODE-1-1-UTF-7 csUnicode11UTF7 }
{ ISO-8859-14 iso-ir-199 ISO_8859-14:1998 ISO_8859-14 latin8 iso-celtic
l8 }
{ ISO-8859-15 ISO_8859-15 Latin-9 }
{ ISO-8859-16 iso-ir-226 ISO_8859-16:2001 ISO_8859-16 latin10 l10 }
{ GBK CP936 MS936 windows-936 }
{ JIS_Encoding csJISEncoding }
{ Shift_JIS MS_Kanji csShiftJIS }
{ Extended_UNIX_Code_Packed_Format_for_Japanese csEUCPkdFmtJapanese
EUC-JP }
{ Extended_UNIX_Code_Fixed_Width_for_Japanese csEUCFixWidJapanese }
{ ISO-10646-UCS-Basic csUnicodeASCII }
{ ISO-10646-Unicode-Latin1 csUnicodeLatin1 ISO-10646 }
{ ISO-Unicode-IBM-1261 csUnicodeIBM1261 }
{ ISO-Unicode-IBM-1268 csUnicodeIBM1268 }
{ ISO-Unicode-IBM-1276 csUnicodeIBM1276 }
{ ISO-Unicode-IBM-1264 csUnicodeIBM1264 }
{ ISO-Unicode-IBM-1265 csUnicodeIBM1265 }
{ ISO-8859-1-Windows-3.0-Latin-1 csWindows30Latin1 }
{ ISO-8859-1-Windows-3.1-Latin-1 csWindows31Latin1 }
{ ISO-8859-2-Windows-Latin-2 csWindows31Latin2 }
{ ISO-8859-9-Windows-Latin-5 csWindows31Latin5 }
{ Adobe-Standard-Encoding csAdobeStandardEncoding }
{ Ventura-US csVenturaUS }
{ Ventura-International csVenturaInternational }
{ PC8-Danish-Norwegian csPC8DanishNorwegian }
{ PC8-Turkish csPC8Turkish }
{ IBM-Symbols csIBMSymbols }
{ IBM-Thai csIBMThai }
{ HP-Legal csHPLegal }
{ HP-Pi-font csHPPiFont }
{ HP-Math8 csHPMath8 }
{ Adobe-Symbol-Encoding csHPPSMath }
{ HP-DeskTop csHPDesktop }
{ Ventura-Math csVenturaMath }
{ Microsoft-Publishing csMicrosoftPublishing }
{ Windows-31J csWindows31J }
{ GB2312 csGB2312 }
{ Big5 csBig5 }
}
proc tcl_encoding {enc} {
global encoding_aliases
set names [encoding names]
set lcnames [string tolower $names]
set enc [string tolower $enc]
set i [lsearch -exact $lcnames $enc]
if {$i < 0} {
# look for "isonnn" instead of "iso-nnn" or "iso_nnn"
if {[regsub {^iso[-_]} $enc iso encx]} {
set i [lsearch -exact $lcnames $encx]
}
}
if {$i < 0} {
foreach l $encoding_aliases {
set ll [string tolower $l]
if {[lsearch -exact $ll $enc] < 0} continue
# look through the aliases for one that tcl knows about
foreach e $ll {
set i [lsearch -exact $lcnames $e]
if {$i < 0} {
if {[regsub {^iso[-_]} $e iso ex]} {
set i [lsearch -exact $lcnames $ex]
}
}
if {$i >= 0} break
}
break
}
}
if {$i >= 0} {
return [lindex $names $i]
}
return {}
}
# defaults... # defaults...
set datemode 0 set datemode 0
set diffopts "-U 5 -p" set diffopts "-U 5 -p"
set wrcomcmd "git-diff-tree --stdin -p --pretty" set wrcomcmd "git-diff-tree --stdin -p --pretty"
set gitencoding "" set gitencoding {}
catch { catch {
set gitencoding [exec git-repo-config --get i18n.commitencoding] set gitencoding [exec git-repo-config --get i18n.commitencoding]
} }
if {$gitencoding == ""} { if {$gitencoding == ""} {
set gitencoding "utf-8" set gitencoding "utf-8"
}
set tclencoding [tcl_encoding $gitencoding]
if {$tclencoding == {}} {
puts stderr "Warning: encoding $gitencoding is not supported by Tcl/Tk"
} }
set mainfont {Helvetica 9} set mainfont {Helvetica 9}
@ -3738,6 +4140,6 @@ set redisplaying 0
set stuffsaved 0 set stuffsaved 0
set patchnum 0 set patchnum 0
setcoords setcoords
makewindow makewindow $revtreeargs
readrefs readrefs
getcommits $revtreeargs getcommits $revtreeargs

View File

@ -21,8 +21,16 @@ static void hash_object(const char *path, const char *type, int write_object)
printf("%s\n", sha1_to_hex(sha1)); printf("%s\n", sha1_to_hex(sha1));
} }
static void hash_stdin(const char *type, int write_object)
{
unsigned char sha1[20];
if (index_pipe(sha1, 0, type, write_object))
die("Unable to add stdin to database");
printf("%s\n", sha1_to_hex(sha1));
}
static const char hash_object_usage[] = static const char hash_object_usage[] =
"git-hash-object [-t <type>] [-w] <file>..."; "git-hash-object [-t <type>] [-w] [--stdin] <file>...";
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -31,26 +39,41 @@ int main(int argc, char **argv)
int write_object = 0; int write_object = 0;
const char *prefix = NULL; const char *prefix = NULL;
int prefix_length = -1; int prefix_length = -1;
int no_more_flags = 0;
for (i = 1 ; i < argc; i++) { for (i = 1 ; i < argc; i++) {
if (!strcmp(argv[i], "-t")) { if (!no_more_flags && argv[i][0] == '-') {
if (argc <= ++i) if (!strcmp(argv[i], "-t")) {
die(hash_object_usage); if (argc <= ++i)
type = argv[i]; die(hash_object_usage);
} type = argv[i];
else if (!strcmp(argv[i], "-w")) {
if (prefix_length < 0) {
prefix = setup_git_directory();
prefix_length = prefix ? strlen(prefix) : 0;
} }
write_object = 1; else if (!strcmp(argv[i], "-w")) {
} if (prefix_length < 0) {
prefix = setup_git_directory();
prefix_length =
prefix ? strlen(prefix) : 0;
}
write_object = 1;
}
else if (!strcmp(argv[i], "--")) {
no_more_flags = 1;
}
else if (!strcmp(argv[i], "--help"))
usage(hash_object_usage);
else if (!strcmp(argv[i], "--stdin")) {
hash_stdin(type, write_object);
}
else
die(hash_object_usage);
}
else { else {
const char *arg = argv[i]; const char *arg = argv[i];
if (0 <= prefix_length) if (0 <= prefix_length)
arg = prefix_filename(prefix, prefix_length, arg = prefix_filename(prefix, prefix_length,
arg); arg);
hash_object(arg, type, write_object); hash_object(arg, type, write_object);
no_more_flags = 1;
} }
} }
return 0; return 0;

View File

@ -237,9 +237,7 @@ int main(int argc, char **argv)
for (i = 1; i < argc; i++, argv++) { for (i = 1; i < argc; i++, argv++) {
char *arg = argv[1]; char *arg = argv[1];
if (arg[0] != '-') if (!strncmp(arg, "--template=", 11))
break;
else if (!strncmp(arg, "--template=", 11))
template_dir = arg+11; template_dir = arg+11;
else else
die(init_db_usage); die(init_db_usage);

View File

@ -8,12 +8,9 @@
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <iconv.h> #include <iconv.h>
#include "git-compat-util.h"
#include "cache.h" #include "cache.h"
#ifdef NO_STRCASESTR
extern char *gitstrcasestr(const char *haystack, const char *needle);
#endif
static FILE *cmitmsg, *patchfile; static FILE *cmitmsg, *patchfile;
static int keep_subject = 0; static int keep_subject = 0;

View File

@ -82,7 +82,12 @@ static int compare_object_pointers(const void *a, const void *b)
{ {
const struct object * const *pa = a; const struct object * const *pa = a;
const struct object * const *pb = b; const struct object * const *pb = b;
return *pa - *pb; if (*pa == *pb)
return 0;
else if (*pa < *pb)
return -1;
else
return 1;
} }
void set_object_refs(struct object *obj, struct object_refs *refs) void set_object_refs(struct object *obj, struct object_refs *refs)

View File

@ -4,7 +4,7 @@
#include "pack.h" #include "pack.h"
#include "csum-file.h" #include "csum-file.h"
static const char pack_usage[] = "git-pack-objects [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list"; static const char pack_usage[] = "git-pack-objects [--non-empty] [--local] [--incremental] [--window=N] [--depth=N] {--stdout | base-name} < object-list";
struct object_entry { struct object_entry {
unsigned char sha1[20]; unsigned char sha1[20];

View File

@ -173,7 +173,7 @@ static void run_update_post_hook(struct command *cmd)
argc++; argc++;
} }
argv[argc] = NULL; argv[argc] = NULL;
run_command_v(argc, argv); run_command_v_opt(argc, argv, RUN_COMMAND_NO_STDIO);
} }
/* /*

4
refs.c
View File

@ -292,6 +292,8 @@ int write_ref_sha1(const char *ref, int fd, const unsigned char *sha1)
return -1; return -1;
filename = ref_file_name(ref); filename = ref_file_name(ref);
lock_filename = ref_lock_file_name(ref); lock_filename = ref_lock_file_name(ref);
if (safe_create_leading_directories(filename))
die("unable to create leading directory for %s", filename);
retval = write_ref_file(filename, lock_filename, fd, sha1); retval = write_ref_file(filename, lock_filename, fd, sha1);
free(filename); free(filename);
free(lock_filename); free(lock_filename);
@ -358,6 +360,8 @@ int write_ref_sha1_unlocked(const char *ref, const unsigned char *sha1)
return -1; return -1;
filename = ref_file_name(ref); filename = ref_file_name(ref);
lock_filename = ref_lock_file_name(ref); lock_filename = ref_lock_file_name(ref);
if (safe_create_leading_directories(filename))
die("unable to create leading directory for %s", filename);
fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666); fd = open(lock_filename, O_WRONLY | O_CREAT | O_EXCL, 0666);
if (fd < 0) { if (fd < 0) {
error("Writing %s", lock_filename); error("Writing %s", lock_filename);

View File

@ -2,13 +2,19 @@
#include "run-command.h" #include "run-command.h"
#include <sys/wait.h> #include <sys/wait.h>
int run_command_v(int argc, char **argv) int run_command_v_opt(int argc, char **argv, int flags)
{ {
pid_t pid = fork(); pid_t pid = fork();
if (pid < 0) if (pid < 0)
return -ERR_RUN_COMMAND_FORK; return -ERR_RUN_COMMAND_FORK;
if (!pid) { if (!pid) {
if (flags & RUN_COMMAND_NO_STDIO) {
int fd = open("/dev/null", O_RDWR);
dup2(fd, 0);
dup2(fd, 1);
close(fd);
}
execvp(argv[0], (char *const*) argv); execvp(argv[0], (char *const*) argv);
die("exec %s failed.", argv[0]); die("exec %s failed.", argv[0]);
} }
@ -36,6 +42,11 @@ int run_command_v(int argc, char **argv)
} }
} }
int run_command_v(int argc, char **argv)
{
return run_command_v_opt(argc, argv, 0);
}
int run_command(const char *cmd, ...) int run_command(const char *cmd, ...)
{ {
int argc; int argc;
@ -54,5 +65,5 @@ int run_command(const char *cmd, ...)
va_end(param); va_end(param);
if (MAX_RUN_COMMAND_ARGS <= argc) if (MAX_RUN_COMMAND_ARGS <= argc)
return error("too many args to run %s", cmd); return error("too many args to run %s", cmd);
return run_command_v(argc, argv); return run_command_v_opt(argc, argv, 0);
} }

View File

@ -11,6 +11,9 @@ enum {
ERR_RUN_COMMAND_WAITPID_NOEXIT, ERR_RUN_COMMAND_WAITPID_NOEXIT,
}; };
#define RUN_COMMAND_NO_STDIO 1
int run_command_v_opt(int argc, char **argv, int opt);
int run_command_v(int argc, char **argv); int run_command_v(int argc, char **argv);
int run_command(const char *cmd, ...); int run_command(const char *cmd, ...);

View File

@ -190,6 +190,12 @@ static int send_pack(int in, int out, int nr_refspec, char **refspec)
if (match_refs(local_refs, remote_refs, &remote_tail, if (match_refs(local_refs, remote_refs, &remote_tail,
nr_refspec, refspec, send_all)) nr_refspec, refspec, send_all))
return -1; return -1;
if (!remote_refs) {
fprintf(stderr, "No refs in common and none specified; doing nothing.\n");
return 0;
}
/* /*
* Finally, tell the other end! * Finally, tell the other end!
*/ */

View File

@ -44,43 +44,17 @@ static int update_info_refs(int force)
/* packs */ /* packs */
static struct pack_info { static struct pack_info {
unsigned long latest;
struct packed_git *p; struct packed_git *p;
int old_num; int old_num;
int new_num; int new_num;
int nr_alloc; int nr_alloc;
int nr_heads; int nr_heads;
unsigned char (*head)[20]; unsigned char (*head)[20];
char dep[0]; /* more */
} **info; } **info;
static int num_pack; static int num_pack;
static const char *objdir; static const char *objdir;
static int objdirlen; static int objdirlen;
static struct object *parse_object_cheap(const unsigned char *sha1)
{
struct object *o;
if ((o = parse_object(sha1)) == NULL)
return NULL;
if (o->type == commit_type) {
struct commit *commit = (struct commit *)o;
free(commit->buffer);
commit->buffer = NULL;
} else if (o->type == tree_type) {
struct tree *tree = (struct tree *)o;
struct tree_entry_list *e, *n;
for (e = tree->entries; e; e = n) {
free(e->name);
e->name = NULL;
n = e->next;
free(e);
}
tree->entries = NULL;
}
return o;
}
static struct pack_info *find_pack_by_name(const char *name) static struct pack_info *find_pack_by_name(const char *name)
{ {
int i; int i;
@ -93,25 +67,6 @@ static struct pack_info *find_pack_by_name(const char *name)
return NULL; return NULL;
} }
static struct pack_info *find_pack_by_old_num(int old_num)
{
int i;
for (i = 0; i < num_pack; i++)
if (info[i]->old_num == old_num)
return info[i];
return NULL;
}
static int add_head_def(struct pack_info *this, unsigned char *sha1)
{
if (this->nr_alloc <= this->nr_heads) {
this->nr_alloc = alloc_nr(this->nr_alloc);
this->head = xrealloc(this->head, this->nr_alloc * 20);
}
memcpy(this->head[this->nr_heads++], sha1, 20);
return 0;
}
/* Returns non-zero when we detect that the info in the /* Returns non-zero when we detect that the info in the
* old file is useless. * old file is useless.
*/ */
@ -123,72 +78,11 @@ static int parse_pack_def(const char *line, int old_cnt)
return 0; return 0;
} }
else { else {
/* The file describes a pack that is no longer here; /* The file describes a pack that is no longer here */
* dependencies between packs needs to be recalculated.
*/
return 1; return 1;
} }
} }
/* Returns non-zero when we detect that the info in the
* old file is useless.
*/
static int parse_depend_def(char *line)
{
unsigned long num;
char *cp, *ep;
struct pack_info *this, *that;
cp = line + 2;
num = strtoul(cp, &ep, 10);
if (ep == cp)
return error("invalid input %s", line);
this = find_pack_by_old_num(num);
if (!this)
return 0;
while (ep && *(cp = ep)) {
num = strtoul(cp, &ep, 10);
if (ep == cp)
break;
that = find_pack_by_old_num(num);
if (!that)
/* The pack this one depends on does not
* exist; this should not happen because
* we write out the list of packs first and
* then dependency information, but it means
* the file is useless anyway.
*/
return 1;
this->dep[that->new_num] = 1;
}
return 0;
}
/* Returns non-zero when we detect that the info in the
* old file is useless.
*/
static int parse_head_def(char *line)
{
unsigned char sha1[20];
unsigned long num;
char *cp, *ep;
struct pack_info *this;
struct object *o;
cp = line + 2;
num = strtoul(cp, &ep, 10);
if (ep == cp || *ep++ != ' ')
return error("invalid input ix %s", line);
this = find_pack_by_old_num(num);
if (!this)
return 1; /* You know the drill. */
if (get_sha1_hex(ep, sha1) || ep[40] != ' ')
return error("invalid input sha1 %s (%s)", line, ep);
if ((o = parse_object_cheap(sha1)) == NULL)
return error("no such object: %s", line);
return add_head_def(this, sha1);
}
/* Returns non-zero when we detect that the info in the /* Returns non-zero when we detect that the info in the
* old file is useless. * old file is useless.
*/ */
@ -212,13 +106,11 @@ static int read_pack_info_file(const char *infofile)
if (parse_pack_def(line, old_cnt++)) if (parse_pack_def(line, old_cnt++))
goto out_stale; goto out_stale;
break; break;
case 'D': /* D ix dep-ix1 dep-ix2... */ case 'D': /* we used to emit D but that was misguided. */
if (parse_depend_def(line)) goto out_stale;
goto out_stale;
break; break;
case 'T': /* T ix sha1 type */ case 'T': /* we used to emit T but nobody uses it. */
if (parse_head_def(line)) goto out_stale;
goto out_stale;
break; break;
default: default:
error("unrecognized: %s", line); error("unrecognized: %s", line);
@ -232,32 +124,6 @@ static int read_pack_info_file(const char *infofile)
return 1; return 1;
} }
/* We sort the packs according to the date of the latest commit. That
* in turn indicates how young the pack is, and in general we would
* want to depend on younger packs.
*/
static unsigned long get_latest_commit_date(struct packed_git *p)
{
unsigned char sha1[20];
struct object *o;
int num = num_packed_objects(p);
int i;
unsigned long latest = 0;
for (i = 0; i < num; i++) {
if (nth_packed_object_sha1(p, i, sha1))
die("corrupt pack file %s?", p->pack_name);
if ((o = parse_object_cheap(sha1)) == NULL)
die("cannot parse %s", sha1_to_hex(sha1));
if (o->type == commit_type) {
struct commit *commit = (struct commit *)o;
if (latest < commit->date)
latest = commit->date;
}
}
return latest;
}
static int compare_info(const void *a_, const void *b_) static int compare_info(const void *a_, const void *b_)
{ {
struct pack_info * const* a = a_; struct pack_info * const* a = a_;
@ -273,10 +139,11 @@ static int compare_info(const void *a_, const void *b_)
/* The other way around. */ /* The other way around. */
return 1; return 1;
if ((*a)->latest < (*b)->latest) /* then it does not matter but at least keep the comparison stable */
return -1; if ((*a)->p == (*b)->p)
else if ((*a)->latest == (*b)->latest)
return 0; return 0;
else if ((*a)->p < (*b)->p)
return -1;
else else
return 1; return 1;
} }
@ -286,7 +153,6 @@ static void init_pack_info(const char *infofile, int force)
struct packed_git *p; struct packed_git *p;
int stale; int stale;
int i = 0; int i = 0;
char *dep_temp;
objdir = get_object_directory(); objdir = get_object_directory();
objdirlen = strlen(objdir); objdirlen = strlen(objdir);
@ -296,18 +162,16 @@ static void init_pack_info(const char *infofile, int force)
/* we ignore things on alternate path since they are /* we ignore things on alternate path since they are
* not available to the pullers in general. * not available to the pullers in general.
*/ */
if (strncmp(p->pack_name, objdir, objdirlen) || if (!p->pack_local)
strncmp(p->pack_name + objdirlen, "/pack/", 6))
continue; continue;
i++; i++;
} }
num_pack = i; num_pack = i;
info = xcalloc(num_pack, sizeof(struct pack_info *)); info = xcalloc(num_pack, sizeof(struct pack_info *));
for (i = 0, p = packed_git; p; p = p->next) { for (i = 0, p = packed_git; p; p = p->next) {
if (strncmp(p->pack_name, objdir, objdirlen) || if (!p->pack_local)
p->pack_name[objdirlen] != '/')
continue; continue;
info[i] = xcalloc(1, sizeof(struct pack_info) + num_pack); info[i] = xcalloc(1, sizeof(struct pack_info));
info[i]->p = p; info[i]->p = p;
info[i]->old_num = -1; info[i]->old_num = -1;
i++; i++;
@ -321,177 +185,21 @@ static void init_pack_info(const char *infofile, int force)
for (i = 0; i < num_pack; i++) { for (i = 0; i < num_pack; i++) {
if (stale) { if (stale) {
info[i]->old_num = -1; info[i]->old_num = -1;
memset(info[i]->dep, 0, num_pack);
info[i]->nr_heads = 0; info[i]->nr_heads = 0;
} }
if (info[i]->old_num < 0)
info[i]->latest = get_latest_commit_date(info[i]->p);
} }
/* renumber them */
qsort(info, num_pack, sizeof(info[0]), compare_info); qsort(info, num_pack, sizeof(info[0]), compare_info);
for (i = 0; i < num_pack; i++) for (i = 0; i < num_pack; i++)
info[i]->new_num = i; info[i]->new_num = i;
/* we need to fix up the dependency information
* for the old ones.
*/
dep_temp = NULL;
for (i = 0; i < num_pack; i++) {
int old;
if (info[i]->old_num < 0)
continue;
if (! dep_temp)
dep_temp = xmalloc(num_pack);
memset(dep_temp, 0, num_pack);
for (old = 0; old < num_pack; old++) {
struct pack_info *base;
if (!info[i]->dep[old])
continue;
base = find_pack_by_old_num(old);
if (!base)
die("internal error renumbering");
dep_temp[base->new_num] = 1;
}
memcpy(info[i]->dep, dep_temp, num_pack);
}
free(dep_temp);
} }
static void write_pack_info_file(FILE *fp) static void write_pack_info_file(FILE *fp)
{ {
int i, j; int i;
for (i = 0; i < num_pack; i++) for (i = 0; i < num_pack; i++)
fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6); fprintf(fp, "P %s\n", info[i]->p->pack_name + objdirlen + 6);
for (i = 0; i < num_pack; i++) {
fprintf(fp, "D %1d", i);
for (j = 0; j < num_pack; j++) {
if ((i == j) || !(info[i]->dep[j]))
continue;
fprintf(fp, " %1d", j);
}
fputc('\n', fp);
}
for (i = 0; i < num_pack; i++) {
struct pack_info *this = info[i];
for (j = 0; j < this->nr_heads; j++) {
struct object *o = lookup_object(this->head[j]);
fprintf(fp, "T %1d %s %s\n",
i, sha1_to_hex(this->head[j]), o->type);
}
}
}
#define REFERENCED 01
#define INTERNAL 02
#define EMITTED 04
static void show(struct object *o, int pack_ix)
{
/*
* We are interested in objects that are not referenced,
* and objects that are referenced but not internal.
*/
if (o->flags & EMITTED)
return;
if (!(o->flags & REFERENCED))
add_head_def(info[pack_ix], o->sha1);
else if ((o->flags & REFERENCED) && !(o->flags & INTERNAL)) {
int i;
/* Which pack contains this object? That is what
* pack_ix can depend on. We earlier sorted info
* array from youngest to oldest, so try newer packs
* first to favor them here.
*/
for (i = num_pack - 1; 0 <= i; i--) {
struct packed_git *p = info[i]->p;
struct pack_entry ent;
if (find_pack_entry_one(o->sha1, &ent, p)) {
info[pack_ix]->dep[i] = 1;
break;
}
}
}
o->flags |= EMITTED;
}
static void find_pack_info_one(int pack_ix)
{
unsigned char sha1[20];
struct object *o;
int i;
struct packed_git *p = info[pack_ix]->p;
int num = num_packed_objects(p);
/* Scan objects, clear flags from all the edge ones and
* internal ones, possibly marked in the previous round.
*/
for (i = 0; i < num; i++) {
if (nth_packed_object_sha1(p, i, sha1))
die("corrupt pack file %s?", p->pack_name);
if ((o = lookup_object(sha1)) == NULL)
die("cannot parse %s", sha1_to_hex(sha1));
if (o->refs) {
struct object_refs *refs = o->refs;
int j;
for (j = 0; j < refs->count; j++)
refs->ref[j]->flags = 0;
}
o->flags = 0;
}
/* Mark all the internal ones */
for (i = 0; i < num; i++) {
if (nth_packed_object_sha1(p, i, sha1))
die("corrupt pack file %s?", p->pack_name);
if ((o = lookup_object(sha1)) == NULL)
die("cannot find %s", sha1_to_hex(sha1));
if (o->refs) {
struct object_refs *refs = o->refs;
int j;
for (j = 0; j < refs->count; j++)
refs->ref[j]->flags |= REFERENCED;
}
o->flags |= INTERNAL;
}
for (i = 0; i < num; i++) {
if (nth_packed_object_sha1(p, i, sha1))
die("corrupt pack file %s?", p->pack_name);
if ((o = lookup_object(sha1)) == NULL)
die("cannot find %s", sha1_to_hex(sha1));
show(o, pack_ix);
if (o->refs) {
struct object_refs *refs = o->refs;
int j;
for (j = 0; j < refs->count; j++)
show(refs->ref[j], pack_ix);
}
}
}
static void find_pack_info(void)
{
int i;
for (i = 0; i < num_pack; i++) {
/* The packed objects are cast in stone, and a head
* in a pack will stay as head, so is the set of missing
* objects. If the repo has been reorganized and we
* are missing some packs available back then, we have
* already discarded the info read from the file, so
* we will find (old_num < 0) in that case.
*/
if (0 <= info[i]->old_num)
continue;
find_pack_info_one(i);
}
} }
static int update_info_packs(int force) static int update_info_packs(int force)
@ -506,7 +214,6 @@ static int update_info_packs(int force)
strcpy(name + namelen, "+"); strcpy(name + namelen, "+");
init_pack_info(infofile, force); init_pack_info(infofile, force);
find_pack_info();
safe_create_leading_directories(name); safe_create_leading_directories(name);
fp = fopen(name, "w"); fp = fopen(name, "w");
@ -530,5 +237,8 @@ int update_server_info(int force)
errs = errs | update_info_refs(force); errs = errs | update_info_refs(force);
errs = errs | update_info_packs(force); errs = errs | update_info_packs(force);
/* remove leftover rev-cache file if there is any */
unlink(git_path("info/rev-cache"));
return errs; return errs;
} }

View File

@ -184,8 +184,8 @@ static struct alternate_object_database **alt_odb_tail;
* alternate_object_database. The elements on this list come from * alternate_object_database. The elements on this list come from
* non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT * non-empty elements from colon separated ALTERNATE_DB_ENVIRONMENT
* environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates, * environment variable, and $GIT_OBJECT_DIRECTORY/info/alternates,
* whose contents is exactly in the same format as that environment * whose contents is similar to that environment variable but can be
* variable. Its base points at a statically allocated buffer that * LF separated. Its base points at a statically allocated buffer that
* contains "/the/directory/corresponding/to/.git/objects/...", while * contains "/the/directory/corresponding/to/.git/objects/...", while
* its name points just after the slash at the end of ".git/objects/" * its name points just after the slash at the end of ".git/objects/"
* in the example above, and has enough space to hold 40-byte hex * in the example above, and has enough space to hold 40-byte hex
@ -197,6 +197,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
{ {
const char *cp, *last; const char *cp, *last;
struct alternate_object_database *ent; struct alternate_object_database *ent;
const char *objdir = get_object_directory();
int base_len = -1; int base_len = -1;
last = alt; last = alt;
@ -211,6 +212,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
for ( ; cp < ep && *cp != sep; cp++) for ( ; cp < ep && *cp != sep; cp++)
; ;
if (last != cp) { if (last != cp) {
struct alternate_object_database *alt;
/* 43 = 40-byte + 2 '/' + terminating NUL */ /* 43 = 40-byte + 2 '/' + terminating NUL */
int pfxlen = cp - last; int pfxlen = cp - last;
int entlen = pfxlen + 43; int entlen = pfxlen + 43;
@ -223,9 +225,7 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
pfxlen += base_len; pfxlen += base_len;
} }
ent = xmalloc(sizeof(*ent) + entlen); ent = xmalloc(sizeof(*ent) + entlen);
*alt_odb_tail = ent;
alt_odb_tail = &(ent->next);
ent->next = NULL;
if (*last != '/' && relative_base) { if (*last != '/' && relative_base) {
memcpy(ent->base, relative_base, base_len - 1); memcpy(ent->base, relative_base, base_len - 1);
ent->base[base_len - 1] = '/'; ent->base[base_len - 1] = '/';
@ -237,6 +237,22 @@ static void link_alt_odb_entries(const char *alt, const char *ep, int sep,
ent->name = ent->base + pfxlen + 1; ent->name = ent->base + pfxlen + 1;
ent->base[pfxlen] = ent->base[pfxlen + 3] = '/'; ent->base[pfxlen] = ent->base[pfxlen + 3] = '/';
ent->base[entlen-1] = 0; ent->base[entlen-1] = 0;
/* Prevent the common mistake of listing the same
* thing twice, or object directory itself.
*/
for (alt = alt_odb_list; alt; alt = alt->next)
if (!memcmp(ent->base, alt->base, pfxlen))
goto bad;
if (!memcmp(ent->base, objdir, pfxlen)) {
bad:
free(ent);
}
else {
*alt_odb_tail = ent;
alt_odb_tail = &(ent->next);
ent->next = NULL;
}
} }
while (cp < ep && *cp == sep) while (cp < ep && *cp == sep)
cp++; cp++;
@ -531,8 +547,9 @@ void prepare_packed_git(void)
prepare_packed_git_one(get_object_directory(), 1); prepare_packed_git_one(get_object_directory(), 1);
prepare_alt_odb(); prepare_alt_odb();
for (alt = alt_odb_list; alt; alt = alt->next) { for (alt = alt_odb_list; alt; alt = alt->next) {
alt->name[0] = 0; alt->name[-1] = 0;
prepare_packed_git_one(alt->base, 0); prepare_packed_git_one(alt->base, 0);
alt->name[-1] = '/';
} }
run_once = 1; run_once = 1;
} }
@ -1511,6 +1528,40 @@ int has_sha1_file(const unsigned char *sha1)
return find_sha1_file(sha1, &st) ? 1 : 0; return find_sha1_file(sha1, &st) ? 1 : 0;
} }
int index_pipe(unsigned char *sha1, int fd, const char *type, int write_object)
{
unsigned long size = 4096;
char *buf = malloc(size);
int iret, ret;
unsigned long off = 0;
unsigned char hdr[50];
int hdrlen;
do {
iret = read(fd, buf + off, size - off);
if (iret > 0) {
off += iret;
if (off == size) {
size *= 2;
buf = realloc(buf, size);
}
}
} while (iret > 0);
if (iret < 0) {
free(buf);
return -1;
}
if (!type)
type = "blob";
if (write_object)
ret = write_sha1_file(buf, off, type, sha1);
else {
write_sha1_file_prepare(buf, off, type, sha1, hdr, &hdrlen);
ret = 0;
}
free(buf);
return ret;
}
int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type) int index_fd(unsigned char *sha1, int fd, struct stat *st, int write_object, const char *type)
{ {
unsigned long size = st->st_size; unsigned long size = st->st_size;

View File

@ -1,10 +1,11 @@
#include <stdlib.h> #include <stdlib.h>
#include <fnmatch.h>
#include "cache.h" #include "cache.h"
#include "commit.h" #include "commit.h"
#include "refs.h" #include "refs.h"
static const char show_branch_usage[] = static const char show_branch_usage[] =
"git-show-branch [--all] [--heads] [--tags] [--more=count | --list | --independent | --merge-base ] [<refs>...]"; "git-show-branch [--all] [--heads] [--tags] [--topo-order] [--more=count | --list | --independent | --merge-base ] [<refs>...]";
#define UNINTERESTING 01 #define UNINTERESTING 01
@ -54,7 +55,7 @@ static void name_commit(struct commit *commit, const char *head_name, int nth)
/* Parent is the first parent of the commit. We may name it /* Parent is the first parent of the commit. We may name it
* as (n+1)th generation ancestor of the same head_name as * as (n+1)th generation ancestor of the same head_name as
* commit is nth generation ancestore of, if that generation * commit is nth generation ancestor of, if that generation
* number is better than the name it already has. * number is better than the name it already has.
*/ */
static void name_parent(struct commit *commit, struct commit *parent) static void name_parent(struct commit *commit, struct commit *parent)
@ -332,6 +333,39 @@ static int append_tag_ref(const char *refname, const unsigned char *sha1)
return append_ref(refname + 5, sha1); return append_ref(refname + 5, sha1);
} }
static const char *match_ref_pattern = NULL;
static int match_ref_slash = 0;
static int count_slash(const char *s)
{
int cnt = 0;
while (*s)
if (*s++ == '/')
cnt++;
return cnt;
}
static int append_matching_ref(const char *refname, const unsigned char *sha1)
{
/* we want to allow pattern hold/<asterisk> to show all
* branches under refs/heads/hold/, and v0.99.9? to show
* refs/tags/v0.99.9a and friends.
*/
const char *tail;
int slash = count_slash(refname);
for (tail = refname; *tail && match_ref_slash < slash; )
if (*tail++ == '/')
slash--;
if (!*tail)
return 0;
if (fnmatch(match_ref_pattern, tail, 0))
return 0;
if (!strncmp("refs/heads/", refname, 11))
return append_head_ref(refname, sha1);
if (!strncmp("refs/tags/", refname, 10))
return append_tag_ref(refname, sha1);
return append_ref(refname, sha1);
}
static void snarf_refs(int head, int tag) static void snarf_refs(int head, int tag)
{ {
if (head) { if (head) {
@ -400,6 +434,27 @@ static int show_independent(struct commit **rev,
return 0; return 0;
} }
static void append_one_rev(const char *av)
{
unsigned char revkey[20];
if (!get_sha1(av, revkey)) {
append_ref(av, revkey);
return;
}
if (strchr(av, '*') || strchr(av, '?')) {
/* glob style match */
int saved_matches = ref_name_cnt;
match_ref_pattern = av;
match_ref_slash = count_slash(av);
for_each_ref(append_matching_ref);
if (saved_matches == ref_name_cnt &&
ref_name_cnt < MAX_REVS)
error("no matching refs with %s", av);
return;
}
die("bad sha1 reference %s", av);
}
int main(int ac, char **av) int main(int ac, char **av)
{ {
struct commit *rev[MAX_REVS], *commit; struct commit *rev[MAX_REVS], *commit;
@ -458,17 +513,20 @@ int main(int ac, char **av)
if (all_heads + all_tags) if (all_heads + all_tags)
snarf_refs(all_heads, all_tags); snarf_refs(all_heads, all_tags);
while (0 < ac) { if (ac) {
unsigned char revkey[20]; while (0 < ac) {
if (get_sha1(*av, revkey)) append_one_rev(*av);
die("bad sha1 reference %s", *av); ac--; av++;
append_ref(*av, revkey); }
ac--; av++;
} }
else {
/* If still no revs, then add heads */ /* If no revs given, then add heads */
if (!ref_name_cnt)
snarf_refs(1, 0); snarf_refs(1, 0);
}
if (!ref_name_cnt) {
fprintf(stderr, "No revs to be shown.\n");
exit(0);
}
for (num_rev = 0; ref_name[num_rev]; num_rev++) { for (num_rev = 0; ref_name[num_rev]; num_rev++) {
unsigned char revkey[20]; unsigned char revkey[20];

View File

@ -17,8 +17,37 @@ IDs. When object ID computation changes, like in the previous case of
swapping compression and hashing order, the person who is making the swapping compression and hashing order, the person who is making the
modification *should* take notice and update the test vectors here. modification *should* take notice and update the test vectors here.
' '
################################################################
# It appears that people are getting bitten by not installing
# 'merge' (usually part of RCS package in binary distributions)
# or have too old python without subprocess. Check them and error
# out before running any tests. Also catch the bogosity of trying
# to run tests without building while we are at it.
../git >/dev/null
if test $? != 1
then
echo >&2 'You do not seem to have built git yet.'
exit 1
fi
merge >/dev/null 2>/dev/null
if test $? == 127
then
echo >&2 'You do not seem to have "merge" installed.
Please check INSTALL document.'
exit 1
fi
. ./test-lib.sh . ./test-lib.sh
"$PYTHON" -c 'import subprocess' || {
echo >&2 'Your python seem to lack "subprocess" module.
Please check INSTALL document.'
exit 1
}
################################################################ ################################################################
# init-db has been done in an empty repository. # init-db has been done in an empty repository.
# make sure it is empty. # make sure it is empty.

View File

@ -12,10 +12,6 @@ test -f .git/config && rm .git/config
git-repo-config core.penguin "little blue" git-repo-config core.penguin "little blue"
cat > expect << EOF cat > expect << EOF
#
# This is the config file
#
[core] [core]
penguin = little blue penguin = little blue
EOF EOF
@ -25,10 +21,6 @@ test_expect_success 'initial' 'cmp .git/config expect'
git-repo-config Core.Movie BadPhysics git-repo-config Core.Movie BadPhysics
cat > expect << EOF cat > expect << EOF
#
# This is the config file
#
[core] [core]
penguin = little blue penguin = little blue
Movie = BadPhysics Movie = BadPhysics
@ -39,10 +31,6 @@ test_expect_success 'mixed case' 'cmp .git/config expect'
git-repo-config Cores.WhatEver Second git-repo-config Cores.WhatEver Second
cat > expect << EOF cat > expect << EOF
#
# This is the config file
#
[core] [core]
penguin = little blue penguin = little blue
Movie = BadPhysics Movie = BadPhysics
@ -55,10 +43,6 @@ test_expect_success 'similar section' 'cmp .git/config expect'
git-repo-config CORE.UPPERCASE true git-repo-config CORE.UPPERCASE true
cat > expect << EOF cat > expect << EOF
#
# This is the config file
#
[core] [core]
penguin = little blue penguin = little blue
Movie = BadPhysics Movie = BadPhysics
@ -76,10 +60,6 @@ test_expect_success 'replace with non-match (actually matching)' \
'git-repo-config core.penguin "very blue" !kingpin' 'git-repo-config core.penguin "very blue" !kingpin'
cat > expect << EOF cat > expect << EOF
#
# This is the config file
#
[core] [core]
penguin = very blue penguin = very blue
Movie = BadPhysics Movie = BadPhysics

View File

@ -15,7 +15,7 @@ test "$(uname -o 2>/dev/null)" = Cygwin && exit 0
. ./test-lib.sh . ./test-lib.sh
p0='no-funny' p0='no-funny'
p1='tabs and spaces' p1='tabs ," (dq) and spaces'
p2='just space' p2='just space'
cat >"$p0" <<\EOF cat >"$p0" <<\EOF
@ -39,7 +39,7 @@ echo "$t0" >t0
echo 'just space echo 'just space
no-funny no-funny
"tabs\tand spaces"' >expected "tabs\t,\" (dq) and spaces"' >expected
test_expect_success 'git-ls-files with-funny' \ test_expect_success 'git-ls-files with-funny' \
'git-update-index --add "$p1" && 'git-update-index --add "$p1" &&
git-ls-files >current && git-ls-files >current &&
@ -47,7 +47,7 @@ test_expect_success 'git-ls-files with-funny' \
echo 'just space echo 'just space
no-funny no-funny
tabs and spaces' >expected tabs ," (dq) and spaces' >expected
test_expect_success 'git-ls-files -z with-funny' \ test_expect_success 'git-ls-files -z with-funny' \
'git-ls-files -z | tr \\0 \\012 >current && 'git-ls-files -z | tr \\0 \\012 >current &&
diff -u expected current' diff -u expected current'
@ -57,12 +57,12 @@ echo "$t1" >t1
echo 'just space echo 'just space
no-funny no-funny
"tabs\tand spaces"' >expected "tabs\t,\" (dq) and spaces"' >expected
test_expect_success 'git-ls-tree with funny' \ test_expect_success 'git-ls-tree with funny' \
'git-ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current && 'git-ls-tree -r $t1 | sed -e "s/^[^ ]* //" >current &&
diff -u expected current' diff -u expected current'
echo 'A "tabs\tand spaces"' >expected echo 'A "tabs\t,\" (dq) and spaces"' >expected
test_expect_success 'git-diff-index with-funny' \ test_expect_success 'git-diff-index with-funny' \
'git-diff-index --name-status $t0 >current && 'git-diff-index --name-status $t0 >current &&
diff -u expected current' diff -u expected current'
@ -72,7 +72,7 @@ test_expect_success 'git-diff-tree with-funny' \
diff -u expected current' diff -u expected current'
echo 'A echo 'A
tabs and spaces' >expected tabs ," (dq) and spaces' >expected
test_expect_success 'git-diff-index -z with-funny' \ test_expect_success 'git-diff-index -z with-funny' \
'git-diff-index -z --name-status $t0 | tr \\0 \\012 >current && 'git-diff-index -z --name-status $t0 | tr \\0 \\012 >current &&
diff -u expected current' diff -u expected current'
@ -81,23 +81,23 @@ test_expect_success 'git-diff-tree -z with-funny' \
'git-diff-tree -z --name-status $t0 $t1 | tr \\0 \\012 >current && 'git-diff-tree -z --name-status $t0 $t1 | tr \\0 \\012 >current &&
diff -u expected current' diff -u expected current'
echo 'CNUM no-funny "tabs\tand spaces"' >expected echo 'CNUM no-funny "tabs\t,\" (dq) and spaces"' >expected
test_expect_success 'git-diff-tree -C with-funny' \ test_expect_success 'git-diff-tree -C with-funny' \
'git-diff-tree -C --find-copies-harder --name-status \ 'git-diff-tree -C --find-copies-harder --name-status \
$t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current && $t0 $t1 | sed -e 's/^C[0-9]*/CNUM/' >current &&
diff -u expected current' diff -u expected current'
echo 'RNUM no-funny "tabs\tand spaces"' >expected echo 'RNUM no-funny "tabs\t,\" (dq) and spaces"' >expected
test_expect_success 'git-diff-tree delete with-funny' \ test_expect_success 'git-diff-tree delete with-funny' \
'git-update-index --force-remove "$p0" && 'git-update-index --force-remove "$p0" &&
git-diff-index -M --name-status \ git-diff-index -M --name-status \
$t0 | sed -e 's/^R[0-9]*/RNUM/' >current && $t0 | sed -e 's/^R[0-9]*/RNUM/' >current &&
diff -u expected current' diff -u expected current'
echo 'diff --git a/no-funny "b/tabs\tand spaces" echo 'diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
similarity index NUM% similarity index NUM%
rename from no-funny rename from no-funny
rename to "tabs\tand spaces"' >expected rename to "tabs\t,\" (dq) and spaces"' >expected
test_expect_success 'git-diff-tree delete with-funny' \ test_expect_success 'git-diff-tree delete with-funny' \
'git-diff-index -M -p $t0 | 'git-diff-index -M -p $t0 |
@ -105,19 +105,19 @@ test_expect_success 'git-diff-tree delete with-funny' \
diff -u expected current' diff -u expected current'
chmod +x "$p1" chmod +x "$p1"
echo 'diff --git a/no-funny "b/tabs\tand spaces" echo 'diff --git a/no-funny "b/tabs\t,\" (dq) and spaces"
old mode 100644 old mode 100644
new mode 100755 new mode 100755
similarity index NUM% similarity index NUM%
rename from no-funny rename from no-funny
rename to "tabs\tand spaces"' >expected rename to "tabs\t,\" (dq) and spaces"' >expected
test_expect_success 'git-diff-tree delete with-funny' \ test_expect_success 'git-diff-tree delete with-funny' \
'git-diff-index -M -p $t0 | 'git-diff-index -M -p $t0 |
sed -e "s/index [0-9]*%/index NUM%/" >current && sed -e "s/index [0-9]*%/index NUM%/" >current &&
diff -u expected current' diff -u expected current'
echo >expected ' "tabs\tand spaces" echo >expected ' "tabs\t,\" (dq) and spaces"
1 files changed, 0 insertions(+), 0 deletions(-)' 1 files changed, 0 insertions(+), 0 deletions(-)'
test_expect_success 'git-diff-tree rename with-funny applied' \ test_expect_success 'git-diff-tree rename with-funny applied' \
'git-diff-index -M -p $t0 | 'git-diff-index -M -p $t0 |
@ -125,7 +125,7 @@ test_expect_success 'git-diff-tree rename with-funny applied' \
diff -u expected current' diff -u expected current'
echo >expected ' no-funny echo >expected ' no-funny
"tabs\tand spaces" "tabs\t,\" (dq) and spaces"
2 files changed, 3 insertions(+), 3 deletions(-)' 2 files changed, 3 insertions(+), 3 deletions(-)'
test_expect_success 'git-diff-tree delete with-funny applied' \ test_expect_success 'git-diff-tree delete with-funny applied' \

View File

@ -7,8 +7,9 @@
# an old counterpart # an old counterpart
cd $(dirname $0) || exit 1 cd $(dirname $0) || exit 1
: ${SHELL_PATH=/bin/sh}
tmp=$(mktemp /tmp/tmp-XXXXXXXX) tmp=`pwd`/.tmp$$
retval=0 retval=0
@ -25,13 +26,17 @@ for i in $list; do
both) pgm="old-git-upload-pack"; replace="old-git-fetch-pack --exec=$pgm";; both) pgm="old-git-upload-pack"; replace="old-git-fetch-pack --exec=$pgm";;
esac esac
if which $pgm 2>/dev/null; then if where=`LANG=C LC_ALL=C which "$pgm" 2>/dev/null` &&
case "$where" in
"no "*) (exit 1) ;;
esac
then
echo "Testing with $pgm" echo "Testing with $pgm"
sed -e "s/git-fetch-pack/$replace/g" \ sed -e "s/git-fetch-pack/$replace/g" \
-e "s/# old fails/warn/" < t5500-fetch-pack.sh > $tmp -e "s/# old fails/warn/" < t5500-fetch-pack.sh > $tmp
sh $tmp || retval=$? "$SHELL_PATH" "$tmp" || retval=$?
rm $tmp rm -f "$tmp"
test $retval != 0 && exit $retval test $retval != 0 && exit $retval
else else

164
t/t6022-merge-rename.sh Executable file
View File

@ -0,0 +1,164 @@
#!/bin/sh
test_description='Merge-recursive merging renames'
. ./test-lib.sh
test_expect_success setup \
'
cat >A <<\EOF &&
a aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
b bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
c cccccccccccccccccccccccccccccccccccccccccccccccc
d dddddddddddddddddddddddddddddddddddddddddddddddd
e eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
f ffffffffffffffffffffffffffffffffffffffffffffffff
g gggggggggggggggggggggggggggggggggggggggggggggggg
h hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
i iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
j jjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj
k kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk
l llllllllllllllllllllllllllllllllllllllllllllllll
m mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm
n nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn
o oooooooooooooooooooooooooooooooooooooooooooooooo
EOF
cat >M <<\EOF &&
A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
B BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
C CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
D DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
E EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
F FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
G GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
H HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
I IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
J JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
K KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
L LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
M MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
N NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN
O OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
EOF
git add A M &&
git commit -m initial &&
git branch white &&
git branch red &&
git branch blue &&
sed -e "/^g /s/.*/g : master changes a line/" <A >A+ &&
mv A+ A &&
git commit -a -m "master updates A" &&
git checkout white &&
sed -e "/^g /s/.*/g : white changes a line/" <A >B &&
sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
rm -f A M &&
git update-index --add --remove A B M N &&
git commit -m "white renames A->B, M->N" &&
git checkout red &&
sed -e "/^g /s/.*/g : red changes a line/" <A >B &&
sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
rm -f A M &&
git update-index --add --remove A B M N &&
git commit -m "red renames A->B, M->N" &&
git checkout blue &&
sed -e "/^g /s/.*/g : blue changes a line/" <A >C &&
sed -e "/^G /s/.*/G : colored branch changes a line/" <M >N &&
rm -f A M &&
git update-index --add --remove A C M N &&
git commit -m "blue renames A->C, M->N" &&
git checkout master'
test_expect_success 'pull renaming branch into unrenaming one' \
'
git show-branch
git pull . white && {
echo "BAD: should have conflicted"
exit 1
}
git ls-files -s
test "$(git ls-files -u B | wc -l)" -eq 3 || {
echo "BAD: should have left stages for B"
exit 1
}
test "$(git ls-files -s N | wc -l)" -eq 1 || {
echo "BAD: should have merged N"
exit 1
}
sed -ne "/^g/{
p
q
}" B | grep master || {
echo "BAD: should have listed our change first"
exit 1
}
test "$(git diff white N | wc -l)" -eq 0 || {
echo "BAD: should have taken colored branch"
exit 1
}
'
test_expect_success 'pull renaming branch into another renaming one' \
'
git reset --hard
git checkout red
git pull . white && {
echo "BAD: should have conflicted"
exit 1
}
test "$(git ls-files -u B | wc -l)" -eq 3 || {
echo "BAD: should have left stages"
exit 1
}
test "$(git ls-files -s N | wc -l)" -eq 1 || {
echo "BAD: should have merged N"
exit 1
}
sed -ne "/^g/{
p
q
}" B | grep red || {
echo "BAD: should have listed our change first"
exit 1
}
test "$(git diff white N | wc -l)" -eq 0 || {
echo "BAD: should have taken colored branch"
exit 1
}
'
test_expect_success 'pull unrenaming branch into renaming one' \
'
git reset --hard
git show-branch
git pull . master && {
echo "BAD: should have conflicted"
exit 1
}
test "$(git ls-files -u B | wc -l)" -eq 3 || {
echo "BAD: should have left stages"
exit 1
}
test "$(git ls-files -s N | wc -l)" -eq 1 || {
echo "BAD: should have merged N"
exit 1
}
sed -ne "/^g/{
p
q
}" B | grep red || {
echo "BAD: should have listed our change first"
exit 1
}
test "$(git diff white N | wc -l)" -eq 0 || {
echo "BAD: should have taken colored branch"
exit 1
}
'
test_done

View File

@ -149,7 +149,7 @@ test_expect_code () {
test_done () { test_done () {
trap - exit trap - exit
case "$test_failure" in case "$test_failure" in
0) 0)
# We could: # We could:
# cd .. && rm -fr trash # cd .. && rm -fr trash
# but that means we forbid any tests that use their own # but that means we forbid any tests that use their own
@ -172,15 +172,30 @@ test_done () {
# t/ subdirectory and are run in trash subdirectory. # t/ subdirectory and are run in trash subdirectory.
PATH=$(pwd)/..:$PATH PATH=$(pwd)/..:$PATH
GIT_EXEC_PATH=$(pwd)/.. GIT_EXEC_PATH=$(pwd)/..
export GIT_EXEC_PATH export PATH GIT_EXEC_PATH
# Similarly use ../compat/subprocess.py if our python does not
# have subprocess.py on its own.
PYTHON=`sed -e '1{
s/^#!//
q
}' ../git-merge-recursive` || {
error "You haven't built things yet, have you?"
}
"$PYTHON" -c 'import subprocess' 2>/dev/null || {
PYTHONPATH=$(pwd)/../compat
export PYTHONPATH
}
test -d ../templates/blt || {
error "You haven't built things yet, have you?"
}
# Test repository # Test repository
test=trash test=trash
rm -fr "$test" rm -fr "$test"
mkdir "$test" mkdir "$test"
cd "$test" cd "$test"
git-init-db --template=../../templates/blt/ 2>/dev/null || "$GIT_EXEC_PATH/git" init-db --template=../../templates/blt/ 2>/dev/null ||
error "cannot run git-init-db" error "cannot run git init-db -- have you built things yet?"
mv .git/hooks .git/hooks-disabled mv .git/hooks .git/hooks-disabled

1
tree.c
View File

@ -200,7 +200,6 @@ int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size)
} }
if (obj) if (obj)
n_refs++; n_refs++;
entry->parent = NULL; /* needs to be filled by the user */
*list_p = entry; *list_p = entry;
list_p = &entry->next; list_p = &entry->next;
} }

1
tree.h
View File

@ -18,7 +18,6 @@ struct tree_entry_list {
struct tree *tree; struct tree *tree;
struct blob *blob; struct blob *blob;
} item; } item;
struct tree_entry_list *parent;
}; };
struct tree { struct tree {

View File

@ -256,35 +256,30 @@ inside:
} }
} }
static int add_cacheinfo(const char *arg1, const char *arg2, const char *arg3) static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
const char *path, int stage)
{ {
int size, len, option; int size, len, option;
unsigned int mode;
unsigned char sha1[20];
struct cache_entry *ce; struct cache_entry *ce;
if (sscanf(arg1, "%o", &mode) != 1) if (!verify_path(path))
return -1;
if (get_sha1_hex(arg2, sha1))
return -1;
if (!verify_path(arg3))
return -1; return -1;
len = strlen(arg3); len = strlen(path);
size = cache_entry_size(len); size = cache_entry_size(len);
ce = xmalloc(size); ce = xmalloc(size);
memset(ce, 0, size); memset(ce, 0, size);
memcpy(ce->sha1, sha1, 20); memcpy(ce->sha1, sha1, 20);
memcpy(ce->name, arg3, len); memcpy(ce->name, path, len);
ce->ce_flags = htons(len); ce->ce_flags = create_ce_flags(len, stage);
ce->ce_mode = create_ce_mode(mode); ce->ce_mode = create_ce_mode(mode);
option = allow_add ? ADD_CACHE_OK_TO_ADD : 0; option = allow_add ? ADD_CACHE_OK_TO_ADD : 0;
option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0; option |= allow_replace ? ADD_CACHE_OK_TO_REPLACE : 0;
if (add_cache_entry(ce, option)) if (add_cache_entry(ce, option))
return error("%s: cannot add to the index - missing --add option?", return error("%s: cannot add to the index - missing --add option?",
arg3); path);
report("add '%s'", arg3); report("add '%s'", path);
return 0; return 0;
} }
@ -342,7 +337,24 @@ static void read_index_info(int line_termination)
char *path_name; char *path_name;
unsigned char sha1[20]; unsigned char sha1[20];
unsigned int mode; unsigned int mode;
int stage;
/* This reads lines formatted in one of three formats:
*
* (1) mode SP sha1 TAB path
* The first format is what "git-apply --index-info"
* reports, and used to reconstruct a partial tree
* that is used for phony merge base tree when falling
* back on 3-way merge.
*
* (2) mode SP type SP sha1 TAB path
* The second format is to stuff git-ls-tree output
* into the index file.
*
* (3) mode SP sha1 SP stage TAB path
* This format is to put higher order stages into the
* index file and matches git-ls-files --stage output.
*/
read_line(&buf, stdin, line_termination); read_line(&buf, stdin, line_termination);
if (buf.eof) if (buf.eof)
break; break;
@ -354,9 +366,19 @@ static void read_index_info(int line_termination)
tab = strchr(ptr, '\t'); tab = strchr(ptr, '\t');
if (!tab || tab - ptr < 41) if (!tab || tab - ptr < 41)
goto bad_line; goto bad_line;
if (tab[-2] == ' ' && '1' <= tab[-1] && tab[-1] <= '3') {
stage = tab[-1] - '0';
ptr = tab + 1; /* point at the head of path */
tab = tab - 2; /* point at tail of sha1 */
}
else {
stage = 0;
ptr = tab + 1; /* point at the head of path */
}
if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ') if (get_sha1_hex(tab - 40, sha1) || tab[-41] != ' ')
goto bad_line; goto bad_line;
ptr = tab + 1;
if (line_termination && ptr[0] == '"') if (line_termination && ptr[0] == '"')
path_name = unquote_c_style(ptr, NULL); path_name = unquote_c_style(ptr, NULL);
@ -382,7 +404,7 @@ static void read_index_info(int line_termination)
* ptr[-41] is at the beginning of sha1 * ptr[-41] is at the beginning of sha1
*/ */
ptr[-42] = ptr[-1] = 0; ptr[-42] = ptr[-1] = 0;
if (add_cacheinfo(buf.buf, ptr-41, path_name)) if (add_cacheinfo(mode, sha1, path_name, stage))
die("git-update-index: unable to update %s", die("git-update-index: unable to update %s",
path_name); path_name);
} }
@ -449,10 +471,17 @@ int main(int argc, const char **argv)
continue; continue;
} }
if (!strcmp(path, "--cacheinfo")) { if (!strcmp(path, "--cacheinfo")) {
unsigned char sha1[20];
unsigned int mode;
if (i+3 >= argc) if (i+3 >= argc)
die("git-update-index: --cacheinfo <mode> <sha1> <path>"); die("git-update-index: --cacheinfo <mode> <sha1> <path>");
if (add_cacheinfo(argv[i+1], argv[i+2], argv[i+3]))
die("git-update-index: --cacheinfo cannot add %s", argv[i+3]); if ((sscanf(argv[i+1], "%o", &mode) != 1) ||
get_sha1_hex(argv[i+2], sha1) ||
add_cacheinfo(mode, sha1, argv[i+3], 0))
die("git-update-index: --cacheinfo"
" cannot add %s", argv[i+3]);
i += 3; i += 3;
continue; continue;
} }

View File

@ -3,7 +3,7 @@
* *
* Copyright (C) Linus Torvalds, 2005 * Copyright (C) Linus Torvalds, 2005
*/ */
#include "cache.h" #include "git-compat-util.h"
static void report(const char *prefix, const char *err, va_list params) static void report(const char *prefix, const char *err, va_list params)
{ {

View File

@ -83,6 +83,8 @@ static int write_tree(struct cache_entry **cachep, int maxentries, const char *b
return nr; return nr;
} }
static const char write_tree_usage[] = "git-write-tree [--missing-ok]";
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int i, funny; int i, funny;
@ -96,7 +98,7 @@ int main(int argc, char **argv)
if (!strcmp(argv[1], "--missing-ok")) if (!strcmp(argv[1], "--missing-ok"))
missing_ok = 1; missing_ok = 1;
else else
die("unknown option %s", argv[1]); die(write_tree_usage);
} }
if (argc > 2) if (argc > 2)