Merge branch 'master' of git://git.kernel.org/pub/scm/git/git

This commit is contained in:
J. Bruce Fields 2007-01-14 19:27:28 -05:00
commit 67583917e9
133 changed files with 3080 additions and 1561 deletions

2
.gitignore vendored
View File

@ -50,6 +50,7 @@ git-http-fetch
git-http-push git-http-push
git-imap-send git-imap-send
git-index-pack git-index-pack
git-init
git-init-db git-init-db
git-instaweb git-instaweb
git-local-fetch git-local-fetch
@ -92,6 +93,7 @@ git-rebase
git-receive-pack git-receive-pack
git-reflog git-reflog
git-relink git-relink
git-remote
git-repack git-repack
git-repo-config git-repo-config
git-request-pull git-request-pull

View File

@ -100,7 +100,7 @@ core.sharedRepository::
group-writable). When 'all' (or 'world' or 'everybody'), the group-writable). When 'all' (or 'world' or 'everybody'), the
repository will be readable by all users, additionally to being repository will be readable by all users, additionally to being
group-shareable. When 'umask' (or 'false'), git will use permissions group-shareable. When 'umask' (or 'false'), git will use permissions
reported by umask(2). See gitlink:git-init-db[1]. False by default. reported by umask(2). See gitlink:git-init[1]. False by default.
core.warnAmbiguousRefs:: core.warnAmbiguousRefs::
If true, git will warn you if the ref name you passed it is ambiguous If true, git will warn you if the ref name you passed it is ambiguous
@ -118,6 +118,34 @@ core.legacyheaders::
database directly (where the "http://" and "rsync://" protocols database directly (where the "http://" and "rsync://" protocols
count as direct access). count as direct access).
core.packedGitWindowSize::
Number of bytes of a pack file to map into memory in a
single mapping operation. Larger window sizes may allow
your system to process a smaller number of large pack files
more quickly. Smaller window sizes will negatively affect
performance due to increased calls to the operating system's
memory manager, but may improve performance when accessing
a large number of large pack files.
+
Default is 1 MiB if NO_MMAP was set at compile time, otherwise 32
MiB on 32 bit platforms and 1 GiB on 64 bit platforms. This should
be reasonable for all users/operating systems. You probably do
not need to adjust this value.
+
Common unit suffixes of 'k', 'm', or 'g' are supported.
core.packedGitLimit::
Maximum number of bytes to map simultaneously into memory
from pack files. If Git needs to access more than this many
bytes at once to complete an operation it will unmap existing
regions to reclaim virtual address space within the process.
+
Default is 256 MiB on 32 bit platforms and 8 GiB on 64 bit platforms.
This should be reasonable for all users/operating systems, except on
the largest projects. You probably do not need to adjust this value.
+
Common unit suffixes of 'k', 'm', or 'g' are supported.
alias.*:: alias.*::
Command aliases for the gitlink:git[1] command wrapper - e.g. Command aliases for the gitlink:git[1] command wrapper - e.g.
after defining "alias.last = cat-file commit HEAD", the invocation after defining "alias.last = cat-file commit HEAD", the invocation

View File

@ -46,12 +46,12 @@ to import into git.
For our first example, we're going to start a totally new repository from For our first example, we're going to start a totally new repository from
scratch, with no pre-existing files, and we'll call it `git-tutorial`. scratch, with no pre-existing files, and we'll call it `git-tutorial`.
To start up, create a subdirectory for it, change into that To start up, create a subdirectory for it, change into that
subdirectory, and initialize the git infrastructure with `git-init-db`: subdirectory, and initialize the git infrastructure with `git-init`:
------------------------------------------------ ------------------------------------------------
$ mkdir git-tutorial $ mkdir git-tutorial
$ cd git-tutorial $ cd git-tutorial
$ git-init-db $ git-init
------------------------------------------------ ------------------------------------------------
to which git will reply to which git will reply
@ -1371,11 +1371,11 @@ $ mkdir my-git.git
------------ ------------
Then, make that directory into a git repository by running Then, make that directory into a git repository by running
`git init-db`, but this time, since its name is not the usual `git init`, but this time, since its name is not the usual
`.git`, we do things slightly differently: `.git`, we do things slightly differently:
------------ ------------
$ GIT_DIR=my-git.git git-init-db $ GIT_DIR=my-git.git git-init
------------ ------------
Make sure this directory is available for others you want your Make sure this directory is available for others you want your
@ -1511,7 +1511,7 @@ A recommended workflow for a "project lead" goes like this:
+ +
If other people are pulling from your repository over dumb If other people are pulling from your repository over dumb
transport protocols (HTTP), you need to keep this repository transport protocols (HTTP), you need to keep this repository
'dumb transport friendly'. After `git init-db`, 'dumb transport friendly'. After `git init`,
`$GIT_DIR/hooks/post-update` copied from the standard templates `$GIT_DIR/hooks/post-update` copied from the standard templates
would contain a call to `git-update-server-info` but the would contain a call to `git-update-server-info` but the
`post-update` hook itself is disabled by default -- enable it `post-update` hook itself is disabled by default -- enable it

View File

@ -80,7 +80,7 @@ it:
------------------------------------------------ ------------------------------------------------
$ mkdir /pub/my-repo.git $ mkdir /pub/my-repo.git
$ cd /pub/my-repo.git $ cd /pub/my-repo.git
$ git --bare init-db --shared $ git --bare init --shared
$ git --bare fetch /home/alice/myproject master:master $ git --bare fetch /home/alice/myproject master:master
------------------------------------------------ ------------------------------------------------

View File

@ -25,7 +25,7 @@ Basic Repository[[Basic Repository]]
Everybody uses these commands to maintain git repositories. Everybody uses these commands to maintain git repositories.
* gitlink:git-init-db[1] or gitlink:git-clone[1] to create a * gitlink:git-init[1] or gitlink:git-clone[1] to create a
new repository. new repository.
* gitlink:git-fsck-objects[1] to check the repository for errors. * gitlink:git-fsck-objects[1] to check the repository for errors.
@ -107,7 +107,7 @@ Use a tarball as a starting point for a new repository.::
------------ ------------
$ tar zxf frotz.tar.gz $ tar zxf frotz.tar.gz
$ cd frotz $ cd frotz
$ git-init-db $ git-init
$ git add . <1> $ git add . <1>
$ git commit -m 'import of frotz source tree.' $ git commit -m 'import of frotz source tree.'
$ git tag v2.43 <2> $ git tag v2.43 <2>

View File

@ -9,7 +9,7 @@ git-am - Apply a series of patches in a mailbox
SYNOPSIS SYNOPSIS
-------- --------
[verse] [verse]
'git-am' [--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] 'git-am' [--signoff] [--dotest=<dir>] [--utf8 | --no-utf8] [--binary] [--3way]
[--interactive] [--whitespace=<option>] <mbox>... [--interactive] [--whitespace=<option>] <mbox>...
'git-am' [--skip | --resolved] 'git-am' [--skip | --resolved]
@ -29,8 +29,21 @@ OPTIONS
Instead of `.dotest` directory, use <dir> as a working Instead of `.dotest` directory, use <dir> as a working
area to store extracted patches. area to store extracted patches.
--utf8, --keep:: --keep::
Pass `-u` and `-k` flags to `git-mailinfo` (see Pass `-k` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
--utf8::
Pass `-u` flag to `git-mailinfo` (see gitlink:git-mailinfo[1]).
The proposed commit log message taken from the e-mail
are re-coded into UTF-8 encoding (configuration variable
`i18n.commitencoding` can be used to specify project's
preferred encoding if it is not UTF-8).
+
This was optional in prior versions of git, but now it is the
default. You could use `--no-utf8` to override this.
--no-utf8::
Do not pass `-u` flag to `git-mailinfo` (see
gitlink:git-mailinfo[1]). gitlink:git-mailinfo[1]).
--binary:: --binary::

View File

@ -42,13 +42,13 @@ OPTIONS
and the current tree. and the current tree.
-u:: -u::
By default, the commit log message, author name and The commit log message, author name and author email are
author email are taken from the e-mail without any taken from the e-mail, and after minimally decoding MIME
charset conversion, after minimally decoding MIME transfer encoding, re-coded in UTF-8 by transliterating
transfer encoding. This flag causes the resulting them. This used to be optional but now it is the default.
commit to be encoded in utf-8 by transliterating them. +
Note that the patch is always used as is without charset Note that the patch is always used as-is without charset
conversion, even with this flag. conversion, even with this flag.
-c .dotest/<num>:: -c .dotest/<num>::
When the patch contained in an e-mail does not cleanly When the patch contained in an e-mail does not cleanly

View File

@ -32,7 +32,8 @@ methods:
4. by using the -a switch with the 'commit' command to automatically "add" 4. by using the -a switch with the 'commit' command to automatically "add"
changes from all known files i.e. files that have already been committed changes from all known files i.e. files that have already been committed
before, and perform the actual commit. before, and to automatically "rm" files that have been
removed from the working tree, and perform the actual commit.
The gitlink:git-status[1] command can be used to obtain a The gitlink:git-status[1] command can be used to obtain a
summary of what is included by any of the above for the next summary of what is included by any of the above for the next
@ -72,12 +73,8 @@ OPTIONS
Add Signed-off-by line at the end of the commit message. Add Signed-off-by line at the end of the commit message.
--no-verify:: --no-verify::
By default, the command looks for suspicious lines the This option bypasses the pre-commit hook.
commit introduces, and aborts committing if there is one. See also link:hooks.html[hooks].
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. This option turns off the check.
-e|--edit:: -e|--edit::
The message taken from file with `-F`, command line with The message taken from file with `-F`, command line with

View File

@ -90,7 +90,8 @@ If you need to pass multiple options, separate them with a comma.
Print a short usage message and exit. Print a short usage message and exit.
-z <fuzz>:: -z <fuzz>::
Pass the timestamp fuzz factor to cvsps. Pass the timestamp fuzz factor to cvsps, in seconds. If unset,
cvsps defaults to 300s.
-s <subst>:: -s <subst>::
Substitute the character "/" in branch names with <subst> Substitute the character "/" in branch names with <subst>
@ -99,6 +100,18 @@ If you need to pass multiple options, separate them with a comma.
CVS by default uses the unix username when writing its CVS by default uses the unix username when writing its
commit logs. Using this option and an author-conv-file commit logs. Using this option and an author-conv-file
in this format in this format
-a::
Import all commits, including recent ones. cvsimport by default
skips commits that have a timestamp less than 10 minutes ago.
-S <regex>::
Skip paths matching the regex.
-L <limit>::
Limit the number of commits imported. Workaround for cases where
cvsimport leaks memory.
+ +
--------- ---------
exon=Andreas Ericsson <ae@op5.se> exon=Andreas Ericsson <ae@op5.se>

View File

@ -11,95 +11,9 @@ SYNOPSIS
'git-init-db' [--template=<template_directory>] [--shared[=<permissions>]] 'git-init-db' [--template=<template_directory>] [--shared[=<permissions>]]
OPTIONS
-------
--
--template=<template_directory>::
Provide the directory from which templates will be used. The default template
directory is `/usr/share/git-core/templates`.
When specified, `<template_directory>` is used as the source of the template
files rather than the default. The template files include some directory
structure, some suggested "exclude patterns", and copies of non-executing
"hook" files. The suggested patterns and hook files are all modifiable and
extensible.
--shared[={false|true|umask|group|all|world|everybody}]::
Specify that the git repository is to be shared amongst several users. This
allows users belonging to the same group to push into that
repository. When specified, the config variable "core.sharedRepository" is
set so that files and directories under `$GIT_DIR` are created with the
requested permissions. When not specified, git will use permissions reported
by umask(2).
The option can have the following values, defaulting to 'group' if no value
is given:
- 'umask' (or 'false'): Use permissions reported by umask(2). The default,
when `--shared` is not specified.
- 'group' (or 'true'): Make the repository group-writable, (and g+sx, since
the git group may be not the primary group of all users).
- 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
readable by all users.
By default, the configuration flag receive.denyNonFastforward is enabled
in shared repositories, so that you cannot force a non fast-forwarding push
into it.
--
DESCRIPTION DESCRIPTION
----------- -----------
This command creates an empty git repository - basically a `.git` directory
with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
template files.
An initial `HEAD` file that references the HEAD of the master branch
is also created.
If the `$GIT_DIR` environment variable is set then it specifies a path This is a synonym for gitlink:git-init[1]. Please refer to the
to use instead of `./.git` for the base of the repository. documentation of that command.
If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
environment variable then the sha1 directories are created underneath -
otherwise the default `$GIT_DIR/objects` directory is used.
Running `git-init-db` in an existing repository is safe. It will not overwrite
things that are already there. The primary reason for rerunning `git-init-db`
is to pick up newly added templates.
EXAMPLES
--------
Start a new git repository for an existing code base::
+
----------------
$ cd /path/to/my/codebase
$ git-init-db <1>
$ git-add . <2>
----------------
+
<1> prepare /path/to/my/codebase/.git directory
<2> add all existing file to the index
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
Documentation
--------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite

111
Documentation/git-init.txt Normal file
View File

@ -0,0 +1,111 @@
git-init(1)
===========
NAME
----
git-init - Creates an empty git repository
SYNOPSIS
--------
'git-init' [--template=<template_directory>] [--shared[=<permissions>]]
OPTIONS
-------
--
--template=<template_directory>::
Provide the directory from which templates will be used. The default template
directory is `/usr/share/git-core/templates`.
When specified, `<template_directory>` is used as the source of the template
files rather than the default. The template files include some directory
structure, some suggested "exclude patterns", and copies of non-executing
"hook" files. The suggested patterns and hook files are all modifiable and
extensible.
--shared[={false|true|umask|group|all|world|everybody}]::
Specify that the git repository is to be shared amongst several users. This
allows users belonging to the same group to push into that
repository. When specified, the config variable "core.sharedRepository" is
set so that files and directories under `$GIT_DIR` are created with the
requested permissions. When not specified, git will use permissions reported
by umask(2).
The option can have the following values, defaulting to 'group' if no value
is given:
- 'umask' (or 'false'): Use permissions reported by umask(2). The default,
when `--shared` is not specified.
- 'group' (or 'true'): Make the repository group-writable, (and g+sx, since
the git group may be not the primary group of all users).
- 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
readable by all users.
By default, the configuration flag receive.denyNonFastforward is enabled
in shared repositories, so that you cannot force a non fast-forwarding push
into it.
--
DESCRIPTION
-----------
This command creates an empty git repository - basically a `.git` directory
with subdirectories for `objects`, `refs/heads`, `refs/tags`, and
template files.
An initial `HEAD` file that references the HEAD of the master branch
is also created.
If the `$GIT_DIR` environment variable is set then it specifies a path
to use instead of `./.git` for the base of the repository.
If the object storage directory is specified via the `$GIT_OBJECT_DIRECTORY`
environment variable then the sha1 directories are created underneath -
otherwise the default `$GIT_DIR/objects` directory is used.
Running `git-init` in an existing repository is safe. It will not overwrite
things that are already there. The primary reason for rerunning `git-init`
is to pick up newly added templates.
Note that `git-init` is the same as `git-init-db`. The command
was primarily meant to initialize the object database, but over
time it has become responsible for setting up the other aspects
of the repository, such as installing the default hooks and
setting the configuration variables. The old name is retained
for backward compatibility reasons.
EXAMPLES
--------
Start a new git repository for an existing code base::
+
----------------
$ cd /path/to/my/codebase
$ git-init <1>
$ git-add . <2>
----------------
+
<1> prepare /path/to/my/codebase/.git directory
<2> add all existing file to the index
Author
------
Written by Linus Torvalds <torvalds@osdl.org>
Documentation
--------------
Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite

View File

@ -33,15 +33,13 @@ OPTIONS
format-patch --mbox' output. format-patch --mbox' output.
-u:: -u::
By default, the commit log message, author name and The commit log message, author name and author email are
author email are taken from the e-mail without any taken from the e-mail, and after minimally decoding MIME
charset conversion, after minimally decoding MIME transfer encoding, re-coded in UTF-8 by transliterating
transfer encoding. This flag causes the resulting them. This used to be optional but now it is the default.
commit to be encoded in the encoding specified by +
i18n.commitencoding configuration (defaults to utf-8) by Note that the patch is always used as-is without charset
transliterating them. conversion, even with this flag.
Note that the patch is always used as is without charset
conversion, even with this flag.
--encoding=<encoding>:: --encoding=<encoding>::
Similar to -u but if the local convention is different Similar to -u but if the local convention is different

View File

@ -93,7 +93,7 @@ perforce branch into a branch named "jammy", like so:
------------ ------------
$ mkdir -p /home/sean/import/jam $ mkdir -p /home/sean/import/jam
$ cd /home/sean/import/jam $ cd /home/sean/import/jam
$ git init-db $ git init
$ git p4import //public/jam jammy $ git p4import //public/jam jammy
------------ ------------

View File

@ -7,7 +7,7 @@ git-pack-refs - Pack heads and tags for efficient repository access
SYNOPSIS SYNOPSIS
-------- --------
'git-pack-refs' [--all] [--prune] 'git-pack-refs' [--all] [--no-prune]
DESCRIPTION DESCRIPTION
----------- -----------
@ -40,10 +40,11 @@ developed and packing their tips does not help performance.
This option causes branch tips to be packed as well. Useful for This option causes branch tips to be packed as well. Useful for
a repository with many branches of historical interests. a repository with many branches of historical interests.
\--prune:: \--no-prune::
The command usually removes loose refs under `$GIT_DIR/refs`
hierarchy after packing them. This option tells it not to.
After packing the refs, remove loose refs under `$GIT_DIR/refs`
hierarchy. This should probably become default.
Author Author
------ ------

View File

@ -9,7 +9,7 @@ residing in a pack file.
SYNOPSIS SYNOPSIS
-------- --------
'git-prune-packed' [-n] 'git-prune-packed' [-n] [-q]
DESCRIPTION DESCRIPTION
@ -32,6 +32,9 @@ OPTIONS
Don't actually remove any objects, only show those that would have been Don't actually remove any objects, only show those that would have been
removed. removed.
-q::
Squelch the progress indicator.
Author Author
------ ------
Written by Linus Torvalds <torvalds@osdl.org> Written by Linus Torvalds <torvalds@osdl.org>

View File

@ -0,0 +1,76 @@
git-remote(1)
============
NAME
----
git-remote - manage set of tracked repositories
SYNOPSIS
--------
[verse]
'git-remote'
'git-remote' add <name> <url>
'git-remote' show <name>
DESCRIPTION
-----------
Manage the set of repositories ("remotes") whose branches you track.
With no arguments, shows a list of existing remotes.
In the second form, adds a remote named <name> for the repository at
<url>. The command `git fetch <name>` can then be used to create and
update remote-tracking branches <name>/<branch>.
In the third form, gives some information about the remote <name>.
The remote configuration is achieved using the `remote.origin.url` and
`remote.origin.fetch` configuration variables. (See
gitlink:git-repo-config[1]).
Examples
--------
Add a new remote, fetch, and check out a branch from it:
------------
$ git remote
origin
$ git branch -r
origin/master
$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
$ git remote
linux-nfs
origin
$ git fetch
* refs/remotes/linux-nfs/master: storing branch 'master' ...
commit: bf81b46
$ git branch -r
origin/master
linux-nfs/master
$ git checkout -b nfs linux-nfs/master
...
------------
See Also
--------
gitlink:git-fetch[1]
gitlink:git-branch[1]
gitlink:git-repo-config[1]
Author
------
Written by Junio Hamano
Documentation
--------------
Documentation by J. Bruce Fields and the git-list <git@vger.kernel.org>.
GIT
---
Part of the gitlink:git[7] suite

View File

@ -139,6 +139,24 @@ manually joining branches on commit.
where the repository URL ends and where the repository path where the repository URL ends and where the repository path
begins. begins.
-T<trunk_subdir>::
--trunk=<trunk_subdir>::
-t<tags_subdir>::
--tags=<tags_subdir>::
-b<branches_subdir>::
--branches=<branches_subdir>::
These are the command-line options for multi-init. Each of
these flags can point to a relative repository path
(--tags=project/tags') or a full url
(--tags=https://foo.org/project/tags)
--prefix=<prefix>
This allows one to specify a prefix which is prepended to the
names of remotes. The prefix does not automatically include a
trailing slash, so be sure you include one in the argument if
that is what you want. This is useful if you wish to track
multiple projects that share a common repository.
'multi-fetch':: 'multi-fetch'::
This runs fetch on all known SVN branches we're tracking. This This runs fetch on all known SVN branches we're tracking. This
will NOT discover new branches (unlike git-svnimport), so will NOT discover new branches (unlike git-svnimport), so
@ -153,7 +171,7 @@ OPTIONS
--shared:: --shared::
--template=<template_directory>:: --template=<template_directory>::
Only used with the 'init' command. Only used with the 'init' command.
These are passed directly to gitlink:git-init-db[1]. These are passed directly to gitlink:git-init[1].
-r <ARG>:: -r <ARG>::
--revision <ARG>:: --revision <ARG>::
@ -231,8 +249,7 @@ repo-config key: svn.authorsfile
-q:: -q::
--quiet:: --quiet::
Make git-svn less verbose. This only affects git-svn if you Make git-svn less verbose.
have the SVN::* libraries installed and are using them.
--repack[=<n>]:: --repack[=<n>]::
--repack-flags=<flags> --repack-flags=<flags>
@ -303,8 +320,6 @@ for more information on using GIT_SVN_ID.
started tracking a branch and never tracked the trunk it was started tracking a branch and never tracked the trunk it was
descended from. descended from.
This relies on the SVN::* libraries to work.
repo-config key: svn.followparent repo-config key: svn.followparent
--no-metadata:: --no-metadata::
@ -332,25 +347,6 @@ Run this if you used an old version of git-svn that used
"git-svn-HEAD" instead of "remotes/git-svn" as the branch "git-svn-HEAD" instead of "remotes/git-svn" as the branch
for tracking the remote. for tracking the remote.
--no-ignore-externals::
Only used with the 'fetch' and 'rebuild' command.
This command has no effect when you are using the SVN::*
libraries with git, svn:externals are always avoided.
By default, git-svn passes --ignore-externals to svn to avoid
fetching svn:external trees into git. Pass this flag to enable
externals tracking directly via git.
Versions of svn that do not support --ignore-externals are
automatically detected and this flag will be automatically
enabled for them.
Otherwise, do not enable this flag unless you know what you're
doing.
repo-config key: svn.noignoreexternals
--ignore-nodate:: --ignore-nodate::
Only used with the 'fetch' command. Only used with the 'fetch' command.
@ -371,7 +367,7 @@ Basic Examples
Tracking and contributing to a the trunk of a Subversion-managed project: Tracking and contributing to a the trunk of a Subversion-managed project:
------------------------------------------------------------------------ ------------------------------------------------------------------------
# Initialize a repo (like git init-db): # Initialize a repo (like git init):
git-svn init http://svn.foo.org/project/trunk git-svn init http://svn.foo.org/project/trunk
# Fetch remote revisions: # Fetch remote revisions:
git-svn fetch git-svn fetch
@ -392,7 +388,7 @@ See also:
'<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>' '<<tracking-multiple-repos,Tracking Multiple Repositories or Branches>>'
------------------------------------------------------------------------ ------------------------------------------------------------------------
# Initialize a repo (like git init-db): # Initialize a repo (like git init):
git-svn multi-init http://svn.foo.org/project \ git-svn multi-init http://svn.foo.org/project \
-T trunk -b branches -t tags -T trunk -b branches -t tags
# Fetch remote revisions: # Fetch remote revisions:
@ -468,49 +464,18 @@ This allows you to tie unfetched SVN revision 375 to your current HEAD:
git-svn fetch 375=$(git-rev-parse HEAD) git-svn fetch 375=$(git-rev-parse HEAD)
------------------------------------------------ ------------------------------------------------
Advanced Example: Tracking a Reorganized Repository
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Note: this example is now obsolete if you have SVN::* libraries
installed. Simply use --follow-parent when fetching.
If you're tracking a directory that has moved, or otherwise been If you're tracking a directory that has moved, or otherwise been
branched or tagged off of another directory in the repository and you branched or tagged off of another directory in the repository and you
care about the full history of the project, then you can read this care about the full history of the project, then you can use
section. the --follow-parent option.
This is how Yann Dirson tracked the trunk of the ufoai directory when ------------------------------------------------
the /trunk directory of his repository was moved to /ufoai/trunk and git-svn fetch --follow-parent
he needed to continue tracking /ufoai/trunk where /trunk left off. ------------------------------------------------
------------------------------------------------------------------------
# This log message shows when the repository was reorganized:
r166 | ydirson | 2006-03-02 01:36:55 +0100 (Thu, 02 Mar 2006) | 1 line
Changed paths:
D /trunk
A /ufoai/trunk (from /trunk:165)
# First we start tracking the old revisions:
GIT_SVN_ID=git-oldsvn git-svn init \
https://svn.sourceforge.net/svnroot/ufoai/trunk
GIT_SVN_ID=git-oldsvn git-svn fetch -r1:165
# And now, we continue tracking the new revisions:
GIT_SVN_ID=git-newsvn git-svn init \
https://svn.sourceforge.net/svnroot/ufoai/ufoai/trunk
GIT_SVN_ID=git-newsvn git-svn fetch \
166=`git-rev-parse refs/remotes/git-oldsvn`
------------------------------------------------------------------------
BUGS BUGS
---- ----
If you are not using the SVN::* Perl libraries and somebody commits a
conflicting changeset to SVN at a bad moment (right before you commit)
causing a conflict and your commit to fail, your svn working tree
($GIT_DIR/git-svn/tree) may be dirtied. The easiest thing to do is
probably just to rm -rf $GIT_DIR/git-svn/tree and run 'rebuild'. You
can avoid this problem entirely by using 'dcommit'.
We ignore all SVN properties except svn:executable. Too difficult to We ignore all SVN properties except svn:executable. Too difficult to
map them since we rely heavily on git write-tree being _exactly_ the map them since we rely heavily on git write-tree being _exactly_ the
same on both the SVN and git working trees and I prefer not to clutter same on both the SVN and git working trees and I prefer not to clutter

View File

@ -353,8 +353,8 @@ gitlink:git-hash-object[1]::
gitlink:git-index-pack[1]:: gitlink:git-index-pack[1]::
Build pack idx file for an existing packed archive. Build pack idx file for an existing packed archive.
gitlink:git-init-db[1]:: gitlink:git-init[1]::
Creates an empty git object database, or reinitialize an Creates an empty git repository, or reinitialize an
existing one. existing one.
gitlink:git-merge-file[1]:: gitlink:git-merge-file[1]::

View File

@ -235,8 +235,11 @@ push::
local head, the push fails. local head, the push fails.
reachable:: reachable::
An object is reachable from a ref/commit/tree/tag, if there is a All of the ancestors of a given commit are said to be reachable from
chain leading from the latter to the former. that commit. More generally, one object is reachable from another if
we can reach the one from the other by a chain that follows tags to
whatever they tag, commits to their parents or trees, and trees to the
trees or blobs that they contain.
rebase:: rebase::
To clean a branch by starting from the head of the main line of To clean a branch by starting from the head of the main line of
@ -256,7 +259,7 @@ refspec::
means "grab the master branch head from the $URL and store means "grab the master branch head from the $URL and store
it as my origin branch head". it as my origin branch head".
And `git push $URL refs/heads/master:refs/heads/to-upstream` And `git push $URL refs/heads/master:refs/heads/to-upstream`
means "publish my master branch head as to-upstream master head means "publish my master branch head as to-upstream branch
at $URL". See also gitlink:git-push[1] at $URL". See also gitlink:git-push[1]
repository:: repository::

View File

@ -3,7 +3,7 @@ Hooks used by git
Hooks are little scripts you can place in `$GIT_DIR/hooks` Hooks are little scripts you can place in `$GIT_DIR/hooks`
directory to trigger action at certain points. When directory to trigger action at certain points. When
`git-init-db` is run, a handful example hooks are copied in the `git-init` is run, a handful example hooks are copied in the
`hooks` directory of the new repository, but by default they are `hooks` directory of the new repository, but by default they are
all disabled. To enable a hook, make it executable with `chmod +x`. all disabled. To enable a hook, make it executable with `chmod +x`.

View File

@ -70,7 +70,7 @@ DocumentRoot /where/ever/httpd.conf" to find your root:
Initialize a bare repository Initialize a bare repository
$ cd my-new-repo.git $ cd my-new-repo.git
$ git --bare init-db $ git --bare init
Change the ownership to your web-server's credentials. Use "grep ^User Change the ownership to your web-server's credentials. Use "grep ^User

View File

@ -102,7 +102,7 @@ branches::
hooks:: hooks::
Hooks are customization scripts used by various git Hooks are customization scripts used by various git
commands. A handful of sample hooks are installed when commands. A handful of sample hooks are installed when
`git init-db` is run, but all of them are disabled by `git init` is run, but all of them are disabled by
default. To enable, they need to be made executable. default. To enable, they need to be made executable.
Read link:hooks.html[hooks] for more details about Read link:hooks.html[hooks] for more details about
each hook. each hook.

View File

@ -17,7 +17,7 @@ Let's start a new project and create a small amount of history:
------------------------------------------------ ------------------------------------------------
$ mkdir test-project $ mkdir test-project
$ cd test-project $ cd test-project
$ git init-db $ git init
Initialized empty Git repository in .git/ Initialized empty Git repository in .git/
$ echo 'hello world' > file.txt $ echo 'hello world' > file.txt
$ git add . $ git add .

View File

@ -32,7 +32,7 @@ can place it under git revision control as follows.
------------------------------------------------ ------------------------------------------------
$ tar xzf project.tar.gz $ tar xzf project.tar.gz
$ cd project $ cd project
$ git init-db $ git init
------------------------------------------------ ------------------------------------------------
Git will reply Git will reply

View File

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
GVF=GIT-VERSION-FILE GVF=GIT-VERSION-FILE
DEF_VER=v1.4.5-rc0.GIT DEF_VER=v1.5.0-rc1.GIT
LF=' LF='
' '

View File

@ -95,7 +95,7 @@ Issues of note:
repository itself. For example, you could: repository itself. For example, you could:
$ mkdir manual && cd manual $ mkdir manual && cd manual
$ git init-db $ git init
$ git fetch-pack git://git.kernel.org/pub/scm/git/git.git man html | $ git fetch-pack git://git.kernel.org/pub/scm/git/git.git man html |
while read a b while read a b
do do

View File

@ -1,5 +1,5 @@
# The default target of this Makefile is... # The default target of this Makefile is...
all: all::
# Define NO_OPENSSL environment variable if you do not have OpenSSL. # Define NO_OPENSSL environment variable if you do not have OpenSSL.
# This also implies MOZILLA_SHA1. # This also implies MOZILLA_SHA1.
@ -69,6 +69,9 @@ all:
# #
# Define NO_MMAP if you want to avoid mmap. # Define NO_MMAP if you want to avoid mmap.
# #
# Define NO_PREAD if you have a problem with pread() system call (e.g.
# cygwin.dll before v1.5.22).
#
# Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is # Define NO_FAST_WORKING_DIRECTORY if accessing objects in pack files is
# generally faster on your platform than accessing the working directory. # generally faster on your platform than accessing the working directory.
# #
@ -179,7 +182,7 @@ SCRIPT_SH = \
SCRIPT_PERL = \ SCRIPT_PERL = \
git-add--interactive.perl \ git-add--interactive.perl \
git-archimport.perl git-cvsimport.perl git-relink.perl \ git-archimport.perl git-cvsimport.perl git-relink.perl \
git-cvsserver.perl \ git-cvsserver.perl git-remote.perl \
git-svnimport.perl git-cvsexportcommit.perl \ git-svnimport.perl git-cvsexportcommit.perl \
git-send-email.perl git-svn.perl git-send-email.perl git-svn.perl
@ -201,7 +204,7 @@ PROGRAMS = \
git-update-server-info$X \ git-update-server-info$X \
git-upload-pack$X git-verify-pack$X \ git-upload-pack$X git-verify-pack$X \
git-pack-redundant$X git-var$X \ git-pack-redundant$X git-var$X \
git-describe$X git-merge-tree$X git-imap-send$X \ git-merge-tree$X git-imap-send$X \
git-merge-recursive$X \ git-merge-recursive$X \
$(EXTRA_PROGRAMS) $(EXTRA_PROGRAMS)
@ -210,7 +213,7 @@ EXTRA_PROGRAMS =
BUILT_INS = \ BUILT_INS = \
git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \ git-format-patch$X git-show$X git-whatchanged$X git-cherry$X \
git-get-tar-commit-id$X \ git-get-tar-commit-id$X git-init$X \
$(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS)) $(patsubst builtin-%.o,git-%$X,$(BUILTIN_OBJS))
# what 'all' will build and 'install' will install, in gitexecdir # what 'all' will build and 'install' will install, in gitexecdir
@ -251,6 +254,7 @@ LIB_OBJS = \
interpolate.o \ interpolate.o \
lockfile.o \ lockfile.o \
object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \ object.o pack-check.o patch-delta.o path.o pkt-line.o sideband.o \
reachable.o \
quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \ quote.o read-cache.o refs.o run-command.o dir.o object-refs.o \
server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \ server-info.o setup.o sha1_file.o sha1_name.o strbuf.o \
tag.o tree.o usage.o config.o environment.o ctype.o copy.o \ tag.o tree.o usage.o config.o environment.o ctype.o copy.o \
@ -271,6 +275,7 @@ BUILTIN_OBJS = \
builtin-check-ref-format.o \ builtin-check-ref-format.o \
builtin-commit-tree.o \ builtin-commit-tree.o \
builtin-count-objects.o \ builtin-count-objects.o \
builtin-describe.o \
builtin-diff.o \ builtin-diff.o \
builtin-diff-files.o \ builtin-diff-files.o \
builtin-diff-index.o \ builtin-diff-index.o \
@ -522,6 +527,10 @@ ifdef NO_MMAP
COMPAT_CFLAGS += -DNO_MMAP COMPAT_CFLAGS += -DNO_MMAP
COMPAT_OBJS += compat/mmap.o COMPAT_OBJS += compat/mmap.o
endif endif
ifdef NO_PREAD
COMPAT_CFLAGS += -DNO_PREAD
COMPAT_OBJS += compat/pread.o
endif
ifdef NO_FAST_WORKING_DIRECTORY ifdef NO_FAST_WORKING_DIRECTORY
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
endif endif
@ -596,9 +605,12 @@ export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### Build rules ### Build rules
all: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi all:: $(ALL_PROGRAMS) $(BUILT_INS) git$X gitk gitweb/gitweb.cgi
ifneq (,$X)
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$p';)
endif
all: all::
$(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all $(MAKE) -C perl PERL_PATH='$(PERL_PATH_SQ)' prefix='$(prefix_SQ)' all
$(MAKE) -C templates $(MAKE) -C templates
@ -840,6 +852,9 @@ install: all
'$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \ '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \
fi fi
$(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;) $(foreach p,$(BUILT_INS), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' && ln '$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X' '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p' ;)
ifneq (,$X)
$(foreach p,$(patsubst %$X,%,$(filter %$X,$(ALL_PROGRAMS) $(BUILT_INS) git$X)), rm -f '$(DESTDIR_SQ)$(gitexecdir_SQ)/$p';)
endif
install-doc: install-doc:
$(MAKE) -C Documentation install $(MAKE) -C Documentation install

View File

@ -811,7 +811,8 @@ static int find_header(char *line, unsigned long size, int *hdrsize, struct patc
struct fragment dummy; struct fragment dummy;
if (parse_fragment_header(line, len, &dummy) < 0) if (parse_fragment_header(line, len, &dummy) < 0)
continue; continue;
error("patch fragment without header at line %d: %.*s", linenr, (int)len-1, line); die("patch fragment without header at line %d: %.*s",
linenr, (int)len-1, line);
} }
if (size < len + 6) if (size < len + 6)
@ -2238,8 +2239,19 @@ static void remove_file(struct patch *patch)
die("unable to remove %s from index", patch->old_name); die("unable to remove %s from index", patch->old_name);
cache_tree_invalidate_path(active_cache_tree, patch->old_name); cache_tree_invalidate_path(active_cache_tree, patch->old_name);
} }
if (!cached) if (!cached) {
unlink(patch->old_name); if (!unlink(patch->old_name)) {
char *name = xstrdup(patch->old_name);
char *end = strrchr(name, '/');
while (end) {
*end = 0;
if (rmdir(name))
break;
end = strrchr(name, '/');
}
free(name);
}
}
} }
static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size) static void add_index_file(const char *path, unsigned mode, void *buf, unsigned long size)

View File

@ -137,7 +137,6 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
if (err || !S_ISDIR(mode)) if (err || !S_ISDIR(mode))
die("current working directory is untracked"); die("current working directory is untracked");
free(tree);
tree = parse_tree_indirect(tree_sha1); tree = parse_tree_indirect(tree_sha1);
} }
ar_args->tree = tree; ar_args->tree = tree;

View File

@ -275,7 +275,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
} }
} }
static void print_ref_list(int kinds, int verbose, int abbrev) static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
{ {
int i; int i;
struct ref_list ref_list; struct ref_list ref_list;
@ -286,8 +286,20 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
detached = (detached && (kinds & REF_LOCAL_BRANCH));
if (detached) {
struct ref_item item;
item.name = "(no branch)";
item.kind = REF_LOCAL_BRANCH;
hashcpy(item.sha1, head_sha1);
if (strlen(item.name) > ref_list.maxwidth)
ref_list.maxwidth = strlen(item.name);
print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
}
for (i = 0; i < ref_list.index; i++) { for (i = 0; i < ref_list.index; i++) {
int current = (ref_list.list[i].kind == REF_LOCAL_BRANCH) && int current = !detached &&
(ref_list.list[i].kind == REF_LOCAL_BRANCH) &&
!strcmp(ref_list.list[i].name, head); !strcmp(ref_list.list[i].name, head);
print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose, print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
abbrev, current); abbrev, current);
@ -296,7 +308,8 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
free_ref_list(&ref_list); free_ref_list(&ref_list);
} }
static void create_branch(const char *name, const char *start, static void create_branch(const char *name, const char *start_name,
unsigned char *start_sha1,
int force, int reflog) int force, int reflog)
{ {
struct ref_lock *lock; struct ref_lock *lock;
@ -315,9 +328,14 @@ static void create_branch(const char *name, const char *start,
die("Cannot force update the current branch."); die("Cannot force update the current branch.");
} }
if (get_sha1(start, sha1) || if (start_sha1)
(commit = lookup_commit_reference(sha1)) == NULL) /* detached HEAD */
die("Not a valid branch point: '%s'.", start); hashcpy(sha1, start_sha1);
else if (get_sha1(start_name, sha1))
die("Not a valid object name: '%s'.", start_name);
if ((commit = lookup_commit_reference(sha1)) == NULL)
die("Not a valid branch point: '%s'.", start_name);
hashcpy(sha1, commit->object.sha1); hashcpy(sha1, commit->object.sha1);
lock = lock_any_ref_for_update(ref, NULL); lock = lock_any_ref_for_update(ref, NULL);
@ -326,7 +344,8 @@ static void create_branch(const char *name, const char *start,
if (reflog) { if (reflog) {
log_all_ref_updates = 1; log_all_ref_updates = 1;
snprintf(msg, sizeof msg, "branch: Created from %s", start); snprintf(msg, sizeof msg, "branch: Created from %s",
start_name);
} }
if (write_ref_sha1(lock, sha1, msg) < 0) if (write_ref_sha1(lock, sha1, msg) < 0)
@ -338,6 +357,9 @@ static void rename_branch(const char *oldname, const char *newname, int force)
char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100]; char oldref[PATH_MAX], newref[PATH_MAX], logmsg[PATH_MAX*2 + 100];
unsigned char sha1[20]; unsigned char sha1[20];
if (!oldname)
die("cannot rename the curren branch while not on any.");
if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref)) if (snprintf(oldref, sizeof(oldref), "refs/heads/%s", oldname) > sizeof(oldref))
die("Old branchname too long"); die("Old branchname too long");
@ -367,7 +389,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
{ {
int delete = 0, force_delete = 0, force_create = 0; int delete = 0, force_delete = 0, force_create = 0;
int rename = 0, force_rename = 0; int rename = 0, force_rename = 0;
int verbose = 0, abbrev = DEFAULT_ABBREV; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
int reflog = 0; int reflog = 0;
int kinds = REF_LOCAL_BRANCH; int kinds = REF_LOCAL_BRANCH;
int i; int i;
@ -444,22 +466,27 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL)); head = xstrdup(resolve_ref("HEAD", head_sha1, 0, NULL));
if (!head) if (!head)
die("Failed to resolve HEAD as a valid ref."); die("Failed to resolve HEAD as a valid ref.");
if (!strcmp(head, "HEAD")) {
detached = 1;
}
else {
if (strncmp(head, "refs/heads/", 11)) if (strncmp(head, "refs/heads/", 11))
die("HEAD not found below refs/heads!"); die("HEAD not found below refs/heads!");
head += 11; head += 11;
}
if (delete) if (delete)
return delete_branches(argc - i, argv + i, force_delete, kinds); return delete_branches(argc - i, argv + i, force_delete, kinds);
else if (i == argc) else if (i == argc)
print_ref_list(kinds, verbose, abbrev); print_ref_list(kinds, detached, verbose, abbrev);
else if (rename && (i == argc - 1)) else if (rename && (i == argc - 1))
rename_branch(head, argv[i], force_rename); rename_branch(head, argv[i], force_rename);
else if (rename && (i == argc - 2)) else if (rename && (i == argc - 2))
rename_branch(argv[i], argv[i + 1], force_rename); rename_branch(argv[i], argv[i + 1], force_rename);
else if (i == argc - 1) else if (i == argc - 1)
create_branch(argv[i], head, force_create, reflog); create_branch(argv[i], head, head_sha1, force_create, reflog);
else if (i == argc - 2) else if (i == argc - 2)
create_branch(argv[i], argv[i + 1], force_create, reflog); create_branch(argv[i], argv[i+1], NULL, force_create, reflog);
else else
usage(builtin_branch_usage); usage(builtin_branch_usage);

View File

@ -2,8 +2,10 @@
#include "commit.h" #include "commit.h"
#include "tag.h" #include "tag.h"
#include "refs.h" #include "refs.h"
#include "diff.h"
#define SEEN (1u << 0) #include "diffcore.h"
#include "revision.h"
#include "builtin.h"
static const char describe_usage[] = static const char describe_usage[] =
"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*"; "git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
@ -15,7 +17,7 @@ static int abbrev = DEFAULT_ABBREV;
static int names, allocs; static int names, allocs;
static struct commit_name { static struct commit_name {
const struct commit *commit; struct commit *commit;
int prio; /* annotated tag = 2, tag = 1, head = 0 */ int prio; /* annotated tag = 2, tag = 1, head = 0 */
char path[FLEX_ARRAY]; /* more */ char path[FLEX_ARRAY]; /* more */
} **name_array = NULL; } **name_array = NULL;
@ -34,7 +36,7 @@ static struct commit_name *match(struct commit *cmit)
} }
static void add_to_known_names(const char *path, static void add_to_known_names(const char *path,
const struct commit *commit, struct commit *commit,
int prio) int prio)
{ {
int idx; int idx;
@ -97,6 +99,12 @@ static int compare_names(const void *_a, const void *_b)
return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1; return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
} }
struct possible_tag {
struct possible_tag *next;
struct commit_name *name;
unsigned long depth;
};
static void describe(const char *arg, int last_one) static void describe(const char *arg, int last_one)
{ {
unsigned char sha1[20]; unsigned char sha1[20];
@ -104,6 +112,7 @@ static void describe(const char *arg, int last_one)
struct commit_list *list; struct commit_list *list;
static int initialized = 0; static int initialized = 0;
struct commit_name *n; struct commit_name *n;
struct possible_tag *all_matches, *min_match, *cur_match;
if (get_sha1(arg, sha1)) if (get_sha1(arg, sha1))
die("Not a valid object name %s", arg); die("Not a valid object name %s", arg);
@ -124,22 +133,71 @@ static void describe(const char *arg, int last_one)
} }
list = NULL; list = NULL;
all_matches = NULL;
cur_match = NULL;
commit_list_insert(cmit, &list); commit_list_insert(cmit, &list);
while (list) { while (list) {
struct commit *c = pop_most_recent_commit(&list, SEEN); struct commit *c = pop_commit(&list);
n = match(c); n = match(c);
if (n) { if (n) {
printf("%s-g%s\n", n->path, struct possible_tag *p = xmalloc(sizeof(*p));
find_unique_abbrev(cmit->object.sha1, abbrev)); p->name = n;
if (!last_one) p->next = NULL;
clear_commit_marks(cmit, SEEN); if (cur_match)
return; cur_match->next = p;
else
all_matches = p;
cur_match = p;
} else {
struct commit_list *parents = c->parents;
while (parents) {
struct commit *p = parents->item;
parse_commit(p);
if (!(p->object.flags & SEEN)) {
p->object.flags |= SEEN;
insert_by_date(p, &list);
}
parents = parents->next;
} }
} }
}
if (!all_matches)
die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1)); die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
min_match = NULL;
for (cur_match = all_matches; cur_match; cur_match = cur_match->next) {
struct rev_info revs;
struct commit *tagged = cur_match->name->commit;
clear_commit_marks(cmit, -1);
init_revisions(&revs, NULL);
tagged->object.flags |= UNINTERESTING;
add_pending_object(&revs, &tagged->object, NULL);
add_pending_object(&revs, &cmit->object, NULL);
prepare_revision_walk(&revs);
cur_match->depth = 0;
while ((!min_match || cur_match->depth < min_match->depth)
&& get_revision(&revs))
cur_match->depth++;
if (!min_match || cur_match->depth < min_match->depth)
min_match = cur_match;
free_commit_list(revs.commits);
}
printf("%s-g%s\n", min_match->name->path,
find_unique_abbrev(cmit->object.sha1, abbrev));
if (!last_one) {
for (cur_match = all_matches; cur_match; cur_match = min_match) {
min_match = cur_match->next;
free(cur_match);
}
clear_commit_marks(cmit, SEEN);
}
} }
int main(int argc, char **argv) int cmd_describe(int argc, const char **argv, const char *prefix)
{ {
int i; int i;
@ -161,7 +219,7 @@ int main(int argc, char **argv)
usage(describe_usage); usage(describe_usage);
} }
setup_git_directory(); save_commit_buffer = 0;
if (argc <= i) if (argc <= i)
describe("HEAD", 1); describe("HEAD", 1);

View File

@ -136,7 +136,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
if (i < 0) if (i < 0)
goto err_ret; goto err_ret;
data = xmalloc(st.st_size + 1); data = xmalloc(st.st_size + 1);
if (st.st_size != xread(i, data, st.st_size)) { if (st.st_size != read_in_full(i, data, st.st_size)) {
error("'%s': short read %s", filename, strerror(errno)); error("'%s': short read %s", filename, strerror(errno));
close(i); close(i);
free(data); free(data);

View File

@ -56,7 +56,7 @@ static void copy_templates_1(char *path, int baselen,
/* Note: if ".git/hooks" file exists in the repository being /* Note: if ".git/hooks" file exists in the repository being
* re-initialized, /etc/core-git/templates/hooks/update would * re-initialized, /etc/core-git/templates/hooks/update would
* cause git-init-db to fail here. I think this is sane but * cause git-init to fail here. I think this is sane but
* it means that the set of templates we ship by default, along * it means that the set of templates we ship by default, along
* with the way the namespace under .git/ is organized, should * with the way the namespace under .git/ is organized, should
* be really carefully chosen. * be really carefully chosen.
@ -252,14 +252,18 @@ static int create_default_files(const char *git_dir, const char *template_path)
} }
git_config_set("core.filemode", filemode ? "true" : "false"); git_config_set("core.filemode", filemode ? "true" : "false");
/* Enable logAllRefUpdates if a working tree is attached */ if (is_bare_repository()) {
if (!is_bare_git_dir(git_dir)) git_config_set("core.bare", "true");
}
else {
git_config_set("core.bare", "false");
git_config_set("core.logallrefupdates", "true"); git_config_set("core.logallrefupdates", "true");
}
return reinit; return reinit;
} }
static const char init_db_usage[] = static const char init_db_usage[] =
"git-init-db [--template=<template-directory>] [--shared]"; "git-init [--template=<template-directory>] [--shared]";
/* /*
* If you want to, you can share the DB area with any number of branches. * If you want to, you can share the DB area with any number of branches.

View File

@ -515,12 +515,9 @@ static void convert_to_utf8(char *line, char *charset)
char *input_charset = *charset ? charset : latin_one; char *input_charset = *charset ? charset : latin_one;
char *out = reencode_string(line, metainfo_charset, input_charset); char *out = reencode_string(line, metainfo_charset, input_charset);
if (!out) { if (!out)
fprintf(stderr, "cannot convert from %s to %s\n", die("cannot convert from %s to %s\n",
input_charset, metainfo_charset); input_charset, metainfo_charset);
*charset = 0;
return;
}
strcpy(line, out); strcpy(line, out);
free(out); free(out);
} }
@ -797,17 +794,23 @@ static const char mailinfo_usage[] =
int cmd_mailinfo(int argc, const char **argv, const char *prefix) int cmd_mailinfo(int argc, const char **argv, const char *prefix)
{ {
const char *def_charset;
/* NEEDSWORK: might want to do the optional .git/ directory /* NEEDSWORK: might want to do the optional .git/ directory
* discovery * discovery
*/ */
git_config(git_default_config); git_config(git_default_config);
def_charset = (git_commit_encoding ? git_commit_encoding : "utf-8");
metainfo_charset = def_charset;
while (1 < argc && argv[1][0] == '-') { while (1 < argc && argv[1][0] == '-') {
if (!strcmp(argv[1], "-k")) if (!strcmp(argv[1], "-k"))
keep_subject = 1; keep_subject = 1;
else if (!strcmp(argv[1], "-u")) else if (!strcmp(argv[1], "-u"))
metainfo_charset = (git_commit_encoding metainfo_charset = def_charset;
? git_commit_encoding : "utf-8"); else if (!strcmp(argv[1], "-n"))
metainfo_charset = NULL;
else if (!strncmp(argv[1], "--encoding=", 11)) else if (!strncmp(argv[1], "--encoding=", 11))
metainfo_charset = argv[1] + 11; metainfo_charset = argv[1] + 11;
else else

View File

@ -276,7 +276,52 @@ static int encode_header(enum object_type type, unsigned long size, unsigned cha
* we are going to reuse the existing object data as is. make * we are going to reuse the existing object data as is. make
* sure it is not corrupt. * sure it is not corrupt.
*/ */
static int check_inflate(unsigned char *data, unsigned long len, unsigned long expect) static int check_pack_inflate(struct packed_git *p,
struct pack_window **w_curs,
unsigned long offset,
unsigned long len,
unsigned long expect)
{
z_stream stream;
unsigned char fakebuf[4096], *in;
int st;
memset(&stream, 0, sizeof(stream));
inflateInit(&stream);
do {
in = use_pack(p, w_curs, offset, &stream.avail_in);
stream.next_in = in;
stream.next_out = fakebuf;
stream.avail_out = sizeof(fakebuf);
st = inflate(&stream, Z_FINISH);
offset += stream.next_in - in;
} while (st == Z_OK || st == Z_BUF_ERROR);
inflateEnd(&stream);
return (st == Z_STREAM_END &&
stream.total_out == expect &&
stream.total_in == len) ? 0 : -1;
}
static void copy_pack_data(struct sha1file *f,
struct packed_git *p,
struct pack_window **w_curs,
unsigned long offset,
unsigned long len)
{
unsigned char *in;
unsigned int avail;
while (len) {
in = use_pack(p, w_curs, offset, &avail);
if (avail > len)
avail = len;
sha1write(f, in, avail);
offset += avail;
len -= avail;
}
}
static int check_loose_inflate(unsigned char *data, unsigned long len, unsigned long expect)
{ {
z_stream stream; z_stream stream;
unsigned char fakebuf[4096]; unsigned char fakebuf[4096];
@ -323,7 +368,7 @@ static int revalidate_loose_object(struct object_entry *entry,
return -1; return -1;
map += used; map += used;
mapsize -= used; mapsize -= used;
return check_inflate(map, mapsize, size); return check_loose_inflate(map, mapsize, size);
} }
static unsigned long write_object(struct sha1file *f, static unsigned long write_object(struct sha1file *f,
@ -416,6 +461,8 @@ static unsigned long write_object(struct sha1file *f,
} }
else { else {
struct packed_git *p = entry->in_pack; struct packed_git *p = entry->in_pack;
struct pack_window *w_curs = NULL;
unsigned long offset;
if (entry->delta) { if (entry->delta) {
obj_type = (allow_ofs_delta && entry->delta->offset) ? obj_type = (allow_ofs_delta && entry->delta->offset) ?
@ -437,16 +484,14 @@ static unsigned long write_object(struct sha1file *f,
hdrlen += 20; hdrlen += 20;
} }
use_packed_git(p); offset = entry->in_pack_offset + entry->in_pack_header_size;
buf = (char *) p->pack_base
+ entry->in_pack_offset
+ entry->in_pack_header_size;
datalen = find_packed_object_size(p, entry->in_pack_offset) datalen = find_packed_object_size(p, entry->in_pack_offset)
- entry->in_pack_header_size; - entry->in_pack_header_size;
if (!pack_to_stdout && check_inflate(buf, datalen, entry->size)) if (!pack_to_stdout && check_pack_inflate(p, &w_curs,
offset, datalen, entry->size))
die("corrupt delta in pack %s", sha1_to_hex(entry->sha1)); die("corrupt delta in pack %s", sha1_to_hex(entry->sha1));
sha1write(f, buf, datalen); copy_pack_data(f, p, &w_curs, offset, datalen);
unuse_packed_git(p); unuse_pack(&w_curs);
reused++; reused++;
} }
if (entry->delta) if (entry->delta)
@ -937,22 +982,19 @@ static void check_object(struct object_entry *entry)
if (entry->in_pack && !entry->preferred_base) { if (entry->in_pack && !entry->preferred_base) {
struct packed_git *p = entry->in_pack; struct packed_git *p = entry->in_pack;
struct pack_window *w_curs = NULL;
unsigned long left = p->pack_size - entry->in_pack_offset; unsigned long left = p->pack_size - entry->in_pack_offset;
unsigned long size, used; unsigned long size, used;
unsigned char *buf; unsigned char *buf;
struct object_entry *base_entry = NULL; struct object_entry *base_entry = NULL;
use_packed_git(p); buf = use_pack(p, &w_curs, entry->in_pack_offset, NULL);
buf = p->pack_base;
buf += entry->in_pack_offset;
/* We want in_pack_type even if we do not reuse delta. /* We want in_pack_type even if we do not reuse delta.
* There is no point not reusing non-delta representations. * There is no point not reusing non-delta representations.
*/ */
used = unpack_object_header_gently(buf, left, used = unpack_object_header_gently(buf, left,
&entry->in_pack_type, &size); &entry->in_pack_type, &size);
if (!used || left - used <= 20)
die("corrupt pack for %s", sha1_to_hex(entry->sha1));
/* Check if it is delta, and the base is also an object /* Check if it is delta, and the base is also an object
* we are going to pack. If so we will reuse the existing * we are going to pack. If so we will reuse the existing
@ -961,21 +1003,26 @@ static void check_object(struct object_entry *entry)
if (!no_reuse_delta) { if (!no_reuse_delta) {
unsigned char c, *base_name; unsigned char c, *base_name;
unsigned long ofs; unsigned long ofs;
unsigned long used_0;
/* there is at least 20 bytes left in the pack */ /* there is at least 20 bytes left in the pack */
switch (entry->in_pack_type) { switch (entry->in_pack_type) {
case OBJ_REF_DELTA: case OBJ_REF_DELTA:
base_name = buf + used; base_name = use_pack(p, &w_curs,
entry->in_pack_offset + used, NULL);
used += 20; used += 20;
break; break;
case OBJ_OFS_DELTA: case OBJ_OFS_DELTA:
c = buf[used++]; buf = use_pack(p, &w_curs,
entry->in_pack_offset + used, NULL);
used_0 = 0;
c = buf[used_0++];
ofs = c & 127; ofs = c & 127;
while (c & 128) { while (c & 128) {
ofs += 1; ofs += 1;
if (!ofs || ofs & ~(~0UL >> 7)) if (!ofs || ofs & ~(~0UL >> 7))
die("delta base offset overflow in pack for %s", die("delta base offset overflow in pack for %s",
sha1_to_hex(entry->sha1)); sha1_to_hex(entry->sha1));
c = buf[used++]; c = buf[used_0++];
ofs = (ofs << 7) + (c & 127); ofs = (ofs << 7) + (c & 127);
} }
if (ofs >= entry->in_pack_offset) if (ofs >= entry->in_pack_offset)
@ -983,6 +1030,7 @@ static void check_object(struct object_entry *entry)
sha1_to_hex(entry->sha1)); sha1_to_hex(entry->sha1));
ofs = entry->in_pack_offset - ofs; ofs = entry->in_pack_offset - ofs;
base_name = find_packed_object_name(p, ofs); base_name = find_packed_object_name(p, ofs);
used += used_0;
break; break;
default: default:
base_name = NULL; base_name = NULL;
@ -990,7 +1038,7 @@ static void check_object(struct object_entry *entry)
if (base_name) if (base_name)
base_entry = locate_object_entry(base_name); base_entry = locate_object_entry(base_name);
} }
unuse_packed_git(p); unuse_pack(&w_curs);
entry->in_pack_header_size = used; entry->in_pack_header_size = used;
if (base_entry) { if (base_entry) {

View File

@ -4,7 +4,7 @@
#include "tag.h" #include "tag.h"
static const char builtin_pack_refs_usage[] = static const char builtin_pack_refs_usage[] =
"git-pack-refs [--all] [--prune]"; "git-pack-refs [--all] [--prune | --no-prune]";
struct ref_to_prune { struct ref_to_prune {
struct ref_to_prune *next; struct ref_to_prune *next;
@ -90,10 +90,15 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
memset(&cbdata, 0, sizeof(cbdata)); memset(&cbdata, 0, sizeof(cbdata));
cbdata.prune = 1;
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
if (!strcmp(arg, "--prune")) { if (!strcmp(arg, "--prune")) {
cbdata.prune = 1; cbdata.prune = 1; /* now the default */
continue;
}
if (!strcmp(arg, "--no-prune")) {
cbdata.prune = 0;
continue; continue;
} }
if (!strcmp(arg, "--all")) { if (!strcmp(arg, "--all")) {

View File

@ -4,7 +4,10 @@
static const char prune_packed_usage[] = static const char prune_packed_usage[] =
"git-prune-packed [-n]"; "git-prune-packed [-n]";
static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun) #define DRY_RUN 01
#define VERBOSE 02
static void prune_dir(int i, DIR *dir, char *pathname, int len, int opts)
{ {
struct dirent *de; struct dirent *de;
char hex[40]; char hex[40];
@ -20,7 +23,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun)
if (!has_sha1_pack(sha1, NULL)) if (!has_sha1_pack(sha1, NULL))
continue; continue;
memcpy(pathname + len, de->d_name, 38); memcpy(pathname + len, de->d_name, 38);
if (dryrun) if (opts & DRY_RUN)
printf("rm -f %s\n", pathname); printf("rm -f %s\n", pathname);
else if (unlink(pathname) < 0) else if (unlink(pathname) < 0)
error("unable to unlink %s", pathname); error("unable to unlink %s", pathname);
@ -29,7 +32,7 @@ static void prune_dir(int i, DIR *dir, char *pathname, int len, int dryrun)
rmdir(pathname); rmdir(pathname);
} }
void prune_packed_objects(int dryrun) void prune_packed_objects(int opts)
{ {
int i; int i;
static char pathname[PATH_MAX]; static char pathname[PATH_MAX];
@ -46,24 +49,31 @@ void prune_packed_objects(int dryrun)
sprintf(pathname + len, "%02x/", i); sprintf(pathname + len, "%02x/", i);
d = opendir(pathname); d = opendir(pathname);
if (opts == VERBOSE && (d || i == 255))
fprintf(stderr, "Removing unused objects %d%%...\015",
((i+1) * 100) / 256);
if (!d) if (!d)
continue; continue;
prune_dir(i, d, pathname, len + 3, dryrun); prune_dir(i, d, pathname, len + 3, opts);
closedir(d); closedir(d);
} }
if (opts == VERBOSE)
fprintf(stderr, "\nDone.\n");
} }
int cmd_prune_packed(int argc, const char **argv, const char *prefix) int cmd_prune_packed(int argc, const char **argv, const char *prefix)
{ {
int i; int i;
int dryrun = 0; int opts = VERBOSE;
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
if (*arg == '-') { if (*arg == '-') {
if (!strcmp(arg, "-n")) if (!strcmp(arg, "-n"))
dryrun = 1; opts |= DRY_RUN;
else if (!strcmp(arg, "-q"))
opts &= ~VERBOSE;
else else
usage(prune_packed_usage); usage(prune_packed_usage);
continue; continue;
@ -72,6 +82,6 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)
usage(prune_packed_usage); usage(prune_packed_usage);
} }
sync(); sync();
prune_packed_objects(dryrun); prune_packed_objects(opts);
return 0; return 0;
} }

View File

@ -1,18 +1,12 @@
#include "cache.h" #include "cache.h"
#include "refs.h"
#include "tag.h"
#include "commit.h" #include "commit.h"
#include "tree.h"
#include "blob.h"
#include "tree-walk.h"
#include "diff.h" #include "diff.h"
#include "revision.h" #include "revision.h"
#include "builtin.h" #include "builtin.h"
#include "cache-tree.h" #include "reachable.h"
static const char prune_usage[] = "git-prune [-n]"; static const char prune_usage[] = "git-prune [-n]";
static int show_only; static int show_only;
static struct rev_info revs;
static int prune_object(char *path, const char *filename, const unsigned char *sha1) static int prune_object(char *path, const char *filename, const unsigned char *sha1)
{ {
@ -85,164 +79,10 @@ static void prune_object_dir(const char *path)
} }
} }
static void process_blob(struct blob *blob,
struct object_array *p,
struct name_path *path,
const char *name)
{
struct object *obj = &blob->object;
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
/* Nothing to do, really .. The blob lookup was the important part */
}
static void process_tree(struct tree *tree,
struct object_array *p,
struct name_path *path,
const char *name)
{
struct object *obj = &tree->object;
struct tree_desc desc;
struct name_entry entry;
struct name_path me;
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
if (parse_tree(tree) < 0)
die("bad tree object %s", sha1_to_hex(obj->sha1));
name = xstrdup(name);
add_object(obj, p, path, name);
me.up = path;
me.elem = name;
me.elem_len = strlen(name);
desc.buf = tree->buffer;
desc.size = tree->size;
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
else
process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
}
free(tree->buffer);
tree->buffer = NULL;
}
static void process_tag(struct tag *tag, struct object_array *p, const char *name)
{
struct object *obj = &tag->object;
struct name_path me;
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
me.up = NULL;
me.elem = "tag:/";
me.elem_len = 5;
if (parse_tag(tag) < 0)
die("bad tag object %s", sha1_to_hex(obj->sha1));
add_object(tag->tagged, p, NULL, name);
}
static void walk_commit_list(struct rev_info *revs)
{
int i;
struct commit *commit;
struct object_array objects = { 0, 0, NULL };
/* Walk all commits, process their trees */
while ((commit = get_revision(revs)) != NULL)
process_tree(commit->tree, &objects, NULL, "");
/* Then walk all the pending objects, recursively processing them too */
for (i = 0; i < revs->pending.nr; i++) {
struct object_array_entry *pending = revs->pending.objects + i;
struct object *obj = pending->item;
const char *name = pending->name;
if (obj->type == OBJ_TAG) {
process_tag((struct tag *) obj, &objects, name);
continue;
}
if (obj->type == OBJ_TREE) {
process_tree((struct tree *)obj, &objects, NULL, name);
continue;
}
if (obj->type == OBJ_BLOB) {
process_blob((struct blob *)obj, &objects, NULL, name);
continue;
}
die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
}
}
static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *datail, void *cb_data)
{
struct object *object;
object = parse_object(osha1);
if (object)
add_pending_object(&revs, object, "");
object = parse_object(nsha1);
if (object)
add_pending_object(&revs, object, "");
return 0;
}
static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *object = parse_object(sha1);
if (!object)
die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
add_pending_object(&revs, object, "");
for_each_reflog_ent(path, add_one_reflog_ent, NULL);
return 0;
}
static void add_one_tree(const unsigned char *sha1)
{
struct tree *tree = lookup_tree(sha1);
add_pending_object(&revs, &tree->object, "");
}
static void add_cache_tree(struct cache_tree *it)
{
int i;
if (it->entry_count >= 0)
add_one_tree(it->sha1);
for (i = 0; i < it->subtree_nr; i++)
add_cache_tree(it->down[i]->cache_tree);
}
static void add_cache_refs(void)
{
int i;
read_cache();
for (i = 0; i < active_nr; i++) {
lookup_blob(active_cache[i]->sha1);
/*
* We could add the blobs to the pending list, but quite
* frankly, we don't care. Once we've looked them up, and
* added them as objects, we've really done everything
* there is to do for a blob
*/
}
if (active_cache_tree)
add_cache_tree(active_cache_tree);
}
int cmd_prune(int argc, const char **argv, const char *prefix) int cmd_prune(int argc, const char **argv, const char *prefix)
{ {
int i; int i;
struct rev_info revs;
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
@ -254,29 +94,8 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
} }
save_commit_buffer = 0; save_commit_buffer = 0;
/*
* Set up revision parsing, and mark us as being interested
* in all object types, not just commits.
*/
init_revisions(&revs, prefix); init_revisions(&revs, prefix);
revs.tag_objects = 1; mark_reachable_objects(&revs, 1);
revs.blob_objects = 1;
revs.tree_objects = 1;
/* Add all external refs */
for_each_ref(add_one_ref, NULL);
/* Add all refs from the index file */
add_cache_refs();
/*
* Set up the revision walk - this will move all commits
* from the pending list to the commit walking list.
*/
prepare_revision_walk(&revs);
walk_commit_list(&revs);
prune_object_dir(get_object_directory()); prune_object_dir(get_object_directory());

View File

@ -4,106 +4,239 @@
#include "refs.h" #include "refs.h"
#include "dir.h" #include "dir.h"
#include "tree-walk.h" #include "tree-walk.h"
#include "diff.h"
#include "revision.h"
#include "reachable.h"
/*
* reflog expire
*/
static const char reflog_expire_usage[] =
"git-reflog expire [--verbose] [--dry-run] [--fix-stale] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
static unsigned long default_reflog_expire; static unsigned long default_reflog_expire;
static unsigned long default_reflog_expire_unreachable; static unsigned long default_reflog_expire_unreachable;
struct cmd_reflog_expire_cb {
struct rev_info revs;
int dry_run;
int stalefix;
int verbose;
unsigned long expire_total;
unsigned long expire_unreachable;
};
struct expire_reflog_cb { struct expire_reflog_cb {
FILE *newlog; FILE *newlog;
const char *ref; const char *ref;
struct commit *ref_commit; struct commit *ref_commit;
unsigned long expire_total; struct cmd_reflog_expire_cb *cmd;
unsigned long expire_unreachable;
}; };
#define INCOMPLETE (1u<<10)
#define STUDYING (1u<<11)
static int tree_is_complete(const unsigned char *sha1) static int tree_is_complete(const unsigned char *sha1)
{ {
struct tree_desc desc; struct tree_desc desc;
void *buf; struct name_entry entry;
char type[20]; int complete;
struct tree *tree;
buf = read_sha1_file(sha1, type, &desc.size); tree = lookup_tree(sha1);
if (!buf) if (!tree)
return 0; return 0;
desc.buf = buf; if (tree->object.flags & SEEN)
while (desc.size) {
const unsigned char *elem;
const char *name;
unsigned mode;
elem = tree_entry_extract(&desc, &name, &mode);
if (!has_sha1_file(elem) ||
(S_ISDIR(mode) && !tree_is_complete(elem))) {
free(buf);
return 0;
}
update_tree_entry(&desc);
}
free(buf);
return 1; return 1;
if (tree->object.flags & INCOMPLETE)
return 0;
desc.buf = tree->buffer;
desc.size = tree->size;
if (!desc.buf) {
char type[20];
void *data = read_sha1_file(sha1, type, &desc.size);
if (!data) {
tree->object.flags |= INCOMPLETE;
return 0;
}
desc.buf = data;
tree->buffer = data;
}
complete = 1;
while (tree_entry(&desc, &entry)) {
if (!has_sha1_file(entry.sha1) ||
(S_ISDIR(entry.mode) && !tree_is_complete(entry.sha1))) {
tree->object.flags |= INCOMPLETE;
complete = 0;
}
}
free(tree->buffer);
tree->buffer = NULL;
if (complete)
tree->object.flags |= SEEN;
return complete;
}
static int commit_is_complete(struct commit *commit)
{
struct object_array study;
struct object_array found;
int is_incomplete = 0;
int i;
/* early return */
if (commit->object.flags & SEEN)
return 1;
if (commit->object.flags & INCOMPLETE)
return 0;
/*
* Find all commits that are reachable and are not marked as
* SEEN. Then make sure the trees and blobs contained are
* complete. After that, mark these commits also as SEEN.
* If some of the objects that are needed to complete this
* commit are missing, mark this commit as INCOMPLETE.
*/
memset(&study, 0, sizeof(study));
memset(&found, 0, sizeof(found));
add_object_array(&commit->object, NULL, &study);
add_object_array(&commit->object, NULL, &found);
commit->object.flags |= STUDYING;
while (study.nr) {
struct commit *c;
struct commit_list *parent;
c = (struct commit *)study.objects[--study.nr].item;
if (!c->object.parsed && !parse_object(c->object.sha1))
c->object.flags |= INCOMPLETE;
if (c->object.flags & INCOMPLETE) {
is_incomplete = 1;
break;
}
else if (c->object.flags & SEEN)
continue;
for (parent = c->parents; parent; parent = parent->next) {
struct commit *p = parent->item;
if (p->object.flags & STUDYING)
continue;
p->object.flags |= STUDYING;
add_object_array(&p->object, NULL, &study);
add_object_array(&p->object, NULL, &found);
}
}
if (!is_incomplete) {
/*
* make sure all commits in "found" array have all the
* necessary objects.
*/
for (i = 0; i < found.nr; i++) {
struct commit *c =
(struct commit *)found.objects[i].item;
if (!tree_is_complete(c->tree->object.sha1)) {
is_incomplete = 1;
c->object.flags |= INCOMPLETE;
}
}
if (!is_incomplete) {
/* mark all found commits as complete, iow SEEN */
for (i = 0; i < found.nr; i++)
found.objects[i].item->flags |= SEEN;
}
}
/* clear flags from the objects we traversed */
for (i = 0; i < found.nr; i++)
found.objects[i].item->flags &= ~STUDYING;
if (is_incomplete)
commit->object.flags |= INCOMPLETE;
else {
/*
* If we come here, we have (1) traversed the ancestry chain
* from the "commit" until we reach SEEN commits (which are
* known to be complete), and (2) made sure that the commits
* encountered during the above traversal refer to trees that
* are complete. Which means that we know *all* the commits
* we have seen during this process are complete.
*/
for (i = 0; i < found.nr; i++)
found.objects[i].item->flags |= SEEN;
}
/* free object arrays */
free(study.objects);
free(found.objects);
return !is_incomplete;
} }
static int keep_entry(struct commit **it, unsigned char *sha1) static int keep_entry(struct commit **it, unsigned char *sha1)
{ {
struct commit *commit; struct commit *commit;
*it = NULL;
if (is_null_sha1(sha1)) if (is_null_sha1(sha1))
return 1; return 1;
commit = lookup_commit_reference_gently(sha1, 1); commit = lookup_commit_reference_gently(sha1, 1);
if (!commit) if (!commit)
return 0; return 0;
/* Make sure everything in this commit exists. */ /*
parse_object(commit->object.sha1); * Make sure everything in this commit exists.
if (!tree_is_complete(commit->tree->object.sha1)) *
* We have walked all the objects reachable from the refs
* and cache earlier. The commits reachable by this commit
* must meet SEEN commits -- and then we should mark them as
* SEEN as well.
*/
if (!commit_is_complete(commit))
return 0; return 0;
*it = commit; *it = commit;
return 1; return 1;
} }
static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1, static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
char *data, void *cb_data) const char *email, unsigned long timestamp, int tz,
const char *message, void *cb_data)
{ {
struct expire_reflog_cb *cb = cb_data; struct expire_reflog_cb *cb = cb_data;
unsigned long timestamp;
char *cp, *ep;
struct commit *old, *new; struct commit *old, *new;
cp = strchr(data, '>'); if (timestamp < cb->cmd->expire_total)
if (!cp || *++cp != ' ')
goto prune;
timestamp = strtoul(cp, &ep, 10);
if (*ep != ' ')
goto prune;
if (timestamp < cb->expire_total)
goto prune; goto prune;
if (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)) old = new = NULL;
if (cb->cmd->stalefix &&
(!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
goto prune; goto prune;
if ((timestamp < cb->expire_unreachable) && if (timestamp < cb->cmd->expire_unreachable) {
(!cb->ref_commit || if (!cb->ref_commit)
(old && !in_merge_bases(old, cb->ref_commit)) ||
(new && !in_merge_bases(new, cb->ref_commit))))
goto prune; goto prune;
if (!old && !is_null_sha1(osha1))
old = lookup_commit_reference_gently(osha1, 1);
if (!new && !is_null_sha1(nsha1))
new = lookup_commit_reference_gently(nsha1, 1);
if ((old && !in_merge_bases(old, cb->ref_commit)) ||
(new && !in_merge_bases(new, cb->ref_commit)))
goto prune;
}
if (cb->newlog) if (cb->newlog) {
fprintf(cb->newlog, "%s %s %s", char sign = (tz < 0) ? '-' : '+';
sha1_to_hex(osha1), sha1_to_hex(nsha1), data); int zone = (tz < 0) ? (-tz) : tz;
fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
sha1_to_hex(osha1), sha1_to_hex(nsha1),
email, timestamp, sign, zone,
message);
}
if (cb->cmd->verbose)
printf("keep %s", message);
return 0; return 0;
prune: prune:
if (!cb->newlog) if (!cb->newlog || cb->cmd->verbose)
fprintf(stderr, "would prune %s", data); printf("%sprune %s", cb->newlog ? "" : "would ", message);
return 0; return 0;
} }
struct cmd_reflog_expire_cb {
int dry_run;
unsigned long expire_total;
unsigned long expire_unreachable;
};
static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data) static int expire_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
{ {
struct cmd_reflog_expire_cb *cmd = cb_data; struct cmd_reflog_expire_cb *cmd = cb_data;
@ -134,8 +267,7 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
fprintf(stderr, fprintf(stderr,
"warning: ref '%s' does not point at a commit\n", ref); "warning: ref '%s' does not point at a commit\n", ref);
cb.ref = ref; cb.ref = ref;
cb.expire_total = cmd->expire_total; cb.cmd = cmd;
cb.expire_unreachable = cmd->expire_unreachable;
for_each_reflog_ent(ref, expire_reflog_ent, &cb); for_each_reflog_ent(ref, expire_reflog_ent, &cb);
finish: finish:
if (cb.newlog) { if (cb.newlog) {
@ -164,9 +296,6 @@ static int reflog_expire_config(const char *var, const char *value)
return 0; return 0;
} }
static const char reflog_expire_usage[] =
"git-reflog expire [--dry-run] [--expire=<time>] [--expire-unreachable=<time>] [--all] <refs>...";
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix) static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{ {
struct cmd_reflog_expire_cb cb; struct cmd_reflog_expire_cb cb;
@ -186,6 +315,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
cb.expire_total = default_reflog_expire; cb.expire_total = default_reflog_expire;
cb.expire_unreachable = default_reflog_expire_unreachable; cb.expire_unreachable = default_reflog_expire_unreachable;
/*
* We can trust the commits and objects reachable from refs
* even in older repository. We cannot trust what's reachable
* from reflog if the repository was pruned with older git.
*/
for (i = 1; i < argc; i++) { for (i = 1; i < argc; i++) {
const char *arg = argv[i]; const char *arg = argv[i];
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n")) if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
@ -194,8 +329,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
cb.expire_total = approxidate(arg + 9); cb.expire_total = approxidate(arg + 9);
else if (!strncmp(arg, "--expire-unreachable=", 21)) else if (!strncmp(arg, "--expire-unreachable=", 21))
cb.expire_unreachable = approxidate(arg + 21); cb.expire_unreachable = approxidate(arg + 21);
else if (!strcmp(arg, "--stale-fix"))
cb.stalefix = 1;
else if (!strcmp(arg, "--all")) else if (!strcmp(arg, "--all"))
do_all = 1; do_all = 1;
else if (!strcmp(arg, "--verbose"))
cb.verbose = 1;
else if (!strcmp(arg, "--")) { else if (!strcmp(arg, "--")) {
i++; i++;
break; break;
@ -205,6 +344,15 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
else else
break; break;
} }
if (cb.stalefix) {
init_revisions(&cb.revs, prefix);
if (cb.verbose)
printf("Marking reachable objects...");
mark_reachable_objects(&cb.revs, 0);
if (cb.verbose)
putchar('\n');
}
if (do_all) if (do_all)
status |= for_each_ref(expire_reflog, &cb); status |= for_each_ref(expire_reflog, &cb);
while (i < argc) { while (i < argc) {
@ -219,6 +367,10 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
return status; return status;
} }
/*
* main "reflog"
*/
static const char reflog_usage[] = static const char reflog_usage[] =
"git-reflog (expire | ...)"; "git-reflog (expire | ...)";

View File

@ -51,9 +51,11 @@ static int write_rr(struct path_list *rr, int out_fd)
int i; int i;
for (i = 0; i < rr->nr; i++) { for (i = 0; i < rr->nr; i++) {
const char *path = rr->items[i].path; const char *path = rr->items[i].path;
write(out_fd, rr->items[i].util, 40); int length = strlen(path) + 1;
write(out_fd, "\t", 1); if (write_in_full(out_fd, rr->items[i].util, 40) != 40 ||
write(out_fd, path, strlen(path) + 1); write_in_full(out_fd, "\t", 1) != 1 ||
write_in_full(out_fd, path, length) != length)
die("unable to write rerere record");
} }
close(out_fd); close(out_fd);
return commit_lock_file(&write_lock); return commit_lock_file(&write_lock);
@ -244,7 +246,8 @@ static int outf(void *dummy, mmbuffer_t *ptr, int nbuf)
{ {
int i; int i;
for (i = 0; i < nbuf; i++) for (i = 0; i < nbuf; i++)
write(1, ptr[i].ptr, ptr[i].size); if (write_in_full(1, ptr[i].ptr, ptr[i].size) != ptr[i].size)
return -1;
return 0; return 0;
} }

View File

@ -32,6 +32,10 @@ static int remove_file(const char *name)
char *slash; char *slash;
ret = unlink(name); ret = unlink(name);
if (ret && errno == ENOENT)
/* The user has removed it from the filesystem by hand */
ret = errno = 0;
if (!ret && (slash = strrchr(name, '/'))) { if (!ret && (slash = strrchr(name, '/'))) {
char *n = xstrdup(name); char *n = xstrdup(name);
do { do {
@ -204,7 +208,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
return 0; return 0;
/* /*
* Then, unless we used "--cache", remove the filenames from * Then, unless we used "--cached", remove the filenames from
* the workspace. If we fail to remove the first one, we * the workspace. If we fail to remove the first one, we
* abort the "git rm" (but once we've successfully removed * abort the "git rm" (but once we've successfully removed
* any file at all, we'll go ahead and commit to it all: * any file at all, we'll go ahead and commit to it all:

View File

@ -74,7 +74,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
char *content = buffer + RECORDSIZE; char *content = buffer + RECORDSIZE;
ssize_t n; ssize_t n;
n = xread(0, buffer, HEADERSIZE); n = read_in_full(0, buffer, HEADERSIZE);
if (n < HEADERSIZE) if (n < HEADERSIZE)
die("git-get-tar-commit-id: read error"); die("git-get-tar-commit-id: read error");
if (header->typeflag[0] != 'g') if (header->typeflag[0] != 'g')
@ -82,7 +82,7 @@ int cmd_get_tar_commit_id(int argc, const char **argv, const char *prefix)
if (memcmp(content, "52 comment=", 11)) if (memcmp(content, "52 comment=", 11))
return 1; return 1;
n = xwrite(1, content + 11, 41); n = write_in_full(1, content + 11, 41);
if (n < 41) if (n < 41)
die("git-get-tar-commit-id: write error"); die("git-get-tar-commit-id: write error");

View File

@ -91,7 +91,7 @@ static void process_input(int child_fd, int band)
char buf[16384]; char buf[16384];
ssize_t sz = read(child_fd, buf, sizeof(buf)); ssize_t sz = read(child_fd, buf, sizeof(buf));
if (sz < 0) { if (sz < 0) {
if (errno != EINTR) if (errno != EAGAIN && errno != EINTR)
error_clnt("read error: %s\n", strerror(errno)); error_clnt("read error: %s\n", strerror(errno));
return; return;
} }

View File

@ -55,6 +55,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)
int no_more_options = 0; int no_more_options = 0;
int nothing_done = 1; int nothing_done = 1;
git_config(git_default_config);
while (1 < argc) { while (1 < argc) {
if (!no_more_options && argv[1][0] == '-') { if (!no_more_options && argv[1][0] == '-') {
if (!strcmp("-v", argv[1])) if (!strcmp("-v", argv[1]))

View File

@ -25,6 +25,7 @@ extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
extern int cmd_cherry(int argc, const char **argv, const char *prefix); extern int cmd_cherry(int argc, const char **argv, const char *prefix);
extern int cmd_commit_tree(int argc, const char **argv, const char *prefix); extern int cmd_commit_tree(int argc, const char **argv, const char *prefix);
extern int cmd_count_objects(int argc, const char **argv, const char *prefix); extern int cmd_count_objects(int argc, const char **argv, const char *prefix);
extern int cmd_describe(int argc, const char **argv, const char *prefix);
extern int cmd_diff_files(int argc, const char **argv, const char *prefix); extern int cmd_diff_files(int argc, const char **argv, const char *prefix);
extern int cmd_diff_index(int argc, const char **argv, const char *prefix); extern int cmd_diff_index(int argc, const char **argv, const char *prefix);
extern int cmd_diff(int argc, const char **argv, const char *prefix); extern int cmd_diff(int argc, const char **argv, const char *prefix);

36
cache.h
View File

@ -127,7 +127,8 @@ extern int cache_errno;
#define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL" #define CONFIG_LOCAL_ENVIRONMENT "GIT_CONFIG_LOCAL"
#define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH" #define EXEC_PATH_ENVIRONMENT "GIT_EXEC_PATH"
extern int is_bare_git_dir(const char *dir); extern int is_bare_repository_cfg;
extern int is_bare_repository(void);
extern const char *get_git_dir(void); extern const char *get_git_dir(void);
extern char *get_object_directory(void); extern char *get_object_directory(void);
extern char *get_refs_directory(void); extern char *get_refs_directory(void);
@ -197,6 +198,8 @@ extern int warn_ambiguous_refs;
extern int shared_repository; extern int shared_repository;
extern const char *apply_default_whitespace; extern const char *apply_default_whitespace;
extern int zlib_compression_level; extern int zlib_compression_level;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
#define GIT_REPO_VERSION 0 #define GIT_REPO_VERSION 0
extern int repository_format_version; extern int repository_format_version;
@ -297,7 +300,7 @@ extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */
extern int read_ref(const char *filename, unsigned char *sha1); extern int read_ref(const char *filename, unsigned char *sha1);
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
extern int create_symref(const char *ref, const char *refs_heads_master); extern int create_symref(const char *ref, const char *refs_heads_master);
extern int validate_symref(const char *ref); extern int validate_headref(const char *ref);
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);
@ -336,14 +339,22 @@ extern struct alternate_object_database {
} *alt_odb_list; } *alt_odb_list;
extern void prepare_alt_odb(void); extern void prepare_alt_odb(void);
struct pack_window {
struct pack_window *next;
unsigned char *base;
off_t offset;
size_t len;
unsigned int last_used;
unsigned int inuse_cnt;
};
extern struct packed_git { extern struct packed_git {
struct packed_git *next; struct packed_git *next;
unsigned long index_size; struct pack_window *windows;
unsigned long pack_size;
unsigned int *index_base; unsigned int *index_base;
void *pack_base; off_t index_size;
unsigned int pack_last_used; off_t pack_size;
unsigned int pack_use_cnt; int pack_fd;
int pack_local; int pack_local;
unsigned char sha1[20]; unsigned char sha1[20];
/* something like ".git/objects/pack/xxxxx.pack" */ /* something like ".git/objects/pack/xxxxx.pack" */
@ -389,13 +400,14 @@ extern void install_packed_git(struct packed_git *pack);
extern struct packed_git *find_sha1_pack(const unsigned char *sha1, extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
struct packed_git *packs); struct packed_git *packs);
extern int use_packed_git(struct packed_git *); extern void pack_report();
extern void unuse_packed_git(struct packed_git *); extern unsigned char* use_pack(struct packed_git *, struct pack_window **, unsigned long, unsigned int *);
extern void unuse_pack(struct pack_window **);
extern struct packed_git *add_packed_git(char *, int, int); extern struct packed_git *add_packed_git(char *, int, int);
extern int num_packed_objects(const struct packed_git *p); extern int num_packed_objects(const struct packed_git *p);
extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*); extern int nth_packed_object_sha1(const struct packed_git *, int, unsigned char*);
extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *); extern unsigned long find_pack_entry_one(const unsigned char *, struct packed_git *);
extern void *unpack_entry_gently(struct packed_git *, unsigned long, char *, unsigned long *); extern void *unpack_entry(struct packed_git *, unsigned long, char *, unsigned long *);
extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep); extern unsigned long unpack_object_header_gently(const unsigned char *buf, unsigned long len, enum object_type *type, unsigned long *sizep);
extern void packed_object_info_detail(struct packed_git *, unsigned long, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *); extern void packed_object_info_detail(struct packed_git *, unsigned long, char *, unsigned long *, unsigned long *, unsigned int *, unsigned char *);
@ -421,9 +433,11 @@ extern char *git_commit_encoding;
extern char *git_log_output_encoding; extern char *git_log_output_encoding;
extern int copy_fd(int ifd, int ofd); extern int copy_fd(int ifd, int ofd);
extern int write_in_full(int fd, const void *buf, size_t count, const char *); extern int read_in_full(int fd, void *buf, size_t count);
extern int write_in_full(int fd, const void *buf, size_t count);
extern void write_or_die(int fd, const void *buf, size_t count); extern void write_or_die(int fd, const void *buf, size_t count);
extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg); extern int write_or_whine(int fd, const void *buf, size_t count, const char *msg);
extern int write_or_whine_pipe(int fd, const void *buf, size_t count, const char *msg);
/* pager.c */ /* pager.c */
extern void setup_pager(void); extern void setup_pager(void);

View File

@ -249,8 +249,10 @@ int write_shallow_commits(int fd, int use_pack_protocol)
if (use_pack_protocol) if (use_pack_protocol)
packet_write(fd, "shallow %s", hex); packet_write(fd, "shallow %s", hex);
else { else {
write(fd, hex, 40); if (write_in_full(fd, hex, 40) != 40)
write(fd, "\n", 1); break;
if (write_in_full(fd, "\n", 1) != 1)
break;
} }
} }
return count; return count;
@ -462,20 +464,29 @@ static int get_one_line(const char *msg, unsigned long len)
return ret; return ret;
} }
static int is_rfc2047_special(char ch) /* High bit set, or ISO-2022-INT */
static int non_ascii(int ch)
{ {
return ((ch & 0x80) || (ch == '=') || (ch == '?') || (ch == '_')); ch = (ch & 0xff);
return ((ch & 0x80) || (ch == 0x1b));
} }
static int add_rfc2047(char *buf, const char *line, int len) static int is_rfc2047_special(char ch)
{
return (non_ascii(ch) || (ch == '=') || (ch == '?') || (ch == '_'));
}
static int add_rfc2047(char *buf, const char *line, int len,
const char *encoding)
{ {
char *bp = buf; char *bp = buf;
int i, needquote; int i, needquote;
static const char q_utf8[] = "=?utf-8?q?"; char q_encoding[128];
const char *q_encoding_fmt = "=?%s?q?";
for (i = needquote = 0; !needquote && i < len; i++) { for (i = needquote = 0; !needquote && i < len; i++) {
unsigned ch = line[i]; int ch = line[i];
if (ch & 0x80) if (non_ascii(ch))
needquote++; needquote++;
if ((i + 1 < len) && if ((i + 1 < len) &&
(ch == '=' && line[i+1] == '?')) (ch == '=' && line[i+1] == '?'))
@ -484,8 +495,11 @@ static int add_rfc2047(char *buf, const char *line, int len)
if (!needquote) if (!needquote)
return sprintf(buf, "%.*s", len, line); return sprintf(buf, "%.*s", len, line);
memcpy(bp, q_utf8, sizeof(q_utf8)-1); i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding);
bp += sizeof(q_utf8)-1; if (sizeof(q_encoding) < i)
die("Insanely long encoding name %s", encoding);
memcpy(bp, q_encoding, i);
bp += i;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
unsigned ch = line[i] & 0xFF; unsigned ch = line[i] & 0xFF;
if (is_rfc2047_special(ch)) { if (is_rfc2047_special(ch)) {
@ -503,7 +517,8 @@ static int add_rfc2047(char *buf, const char *line, int len)
} }
static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf, static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
const char *line, int relative_date) const char *line, int relative_date,
const char *encoding)
{ {
char *date; char *date;
int namelen; int namelen;
@ -531,7 +546,8 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
filler = ""; filler = "";
strcpy(buf, "From: "); strcpy(buf, "From: ");
ret = strlen(buf); ret = strlen(buf);
ret += add_rfc2047(buf + ret, line, display_name_length); ret += add_rfc2047(buf + ret, line, display_name_length,
encoding);
memcpy(buf + ret, name_tail, namelen - display_name_length); memcpy(buf + ret, name_tail, namelen - display_name_length);
ret += namelen - display_name_length; ret += namelen - display_name_length;
buf[ret++] = '\n'; buf[ret++] = '\n';
@ -666,21 +682,18 @@ static char *replace_encoding_header(char *buf, char *encoding)
return buf; return buf;
} }
static char *logmsg_reencode(const struct commit *commit) static char *logmsg_reencode(const struct commit *commit,
char *output_encoding)
{ {
char *encoding; char *encoding;
char *out; char *out;
char *output_encoding = (git_log_output_encoding char *utf8 = "utf-8";
? git_log_output_encoding
: git_commit_encoding);
if (!output_encoding) if (!*output_encoding)
output_encoding = "utf-8";
else if (!*output_encoding)
return NULL; return NULL;
encoding = get_header(commit, "encoding"); encoding = get_header(commit, "encoding");
if (!encoding) if (!encoding)
return NULL; encoding = utf8;
if (!strcmp(encoding, output_encoding)) if (!strcmp(encoding, output_encoding))
out = strdup(commit->buffer); out = strdup(commit->buffer);
else else
@ -689,6 +702,7 @@ static char *logmsg_reencode(const struct commit *commit)
if (out) if (out)
out = replace_encoding_header(out, output_encoding); out = replace_encoding_header(out, output_encoding);
if (encoding != utf8)
free(encoding); free(encoding);
if (!out) if (!out)
return NULL; return NULL;
@ -709,8 +723,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
int parents_shown = 0; int parents_shown = 0;
const char *msg = commit->buffer; const char *msg = commit->buffer;
int plain_non_ascii = 0; int plain_non_ascii = 0;
char *reencoded = logmsg_reencode(commit); char *reencoded;
char *encoding;
encoding = (git_log_output_encoding
? git_log_output_encoding
: git_commit_encoding);
if (!encoding)
encoding = "utf-8";
reencoded = logmsg_reencode(commit, encoding);
if (reencoded) if (reencoded)
msg = reencoded; msg = reencoded;
@ -736,7 +757,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
i + 1 < len && msg[i+1] == '\n') i + 1 < len && msg[i+1] == '\n')
in_body = 1; in_body = 1;
} }
else if (ch & 0x80) { else if (non_ascii(ch)) {
plain_non_ascii = 1; plain_non_ascii = 1;
break; break;
} }
@ -795,13 +816,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
offset += add_user_info("Author", fmt, offset += add_user_info("Author", fmt,
buf + offset, buf + offset,
line + 7, line + 7,
relative_date); relative_date,
encoding);
if (!memcmp(line, "committer ", 10) && if (!memcmp(line, "committer ", 10) &&
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER)) (fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
offset += add_user_info("Commit", fmt, offset += add_user_info("Commit", fmt,
buf + offset, buf + offset,
line + 10, line + 10,
relative_date); relative_date,
encoding);
continue; continue;
} }
@ -824,7 +847,8 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
int slen = strlen(subject); int slen = strlen(subject);
memcpy(buf + offset, subject, slen); memcpy(buf + offset, subject, slen);
offset += slen; offset += slen;
offset += add_rfc2047(buf + offset, line, linelen); offset += add_rfc2047(buf + offset, line, linelen,
encoding);
} }
else { else {
memset(buf + offset, ' ', indent); memset(buf + offset, ' ', indent);
@ -835,11 +859,17 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
if (fmt == CMIT_FMT_ONELINE) if (fmt == CMIT_FMT_ONELINE)
break; break;
if (subject && plain_non_ascii) { if (subject && plain_non_ascii) {
static const char header[] = int sz;
"Content-Type: text/plain; charset=UTF-8\n" char header[512];
const char *header_fmt =
"Content-Type: text/plain; charset=%s\n"
"Content-Transfer-Encoding: 8bit\n"; "Content-Transfer-Encoding: 8bit\n";
memcpy(buf + offset, header, sizeof(header)-1); sz = snprintf(header, sizeof(header), header_fmt,
offset += sizeof(header)-1; encoding);
if (sizeof(header) < sz)
die("Encoding name %s too long", encoding);
memcpy(buf + offset, header, sz);
offset += sz;
} }
if (after_subject) { if (after_subject) {
int slen = strlen(after_subject); int slen = strlen(after_subject);
@ -1010,7 +1040,7 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
free(nodes); free(nodes);
} }
/* merge-rebase stuff */ /* merge-base stuff */
/* bits #0..15 in revision.h */ /* bits #0..15 in revision.h */
#define PARENT1 (1u<<16) #define PARENT1 (1u<<16)
@ -1018,6 +1048,8 @@ void sort_in_topological_order_fn(struct commit_list ** list, int lifo,
#define STALE (1u<<18) #define STALE (1u<<18)
#define RESULT (1u<<19) #define RESULT (1u<<19)
static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
static struct commit *interesting(struct commit_list *list) static struct commit *interesting(struct commit_list *list)
{ {
while (list) { while (list) {
@ -1082,6 +1114,7 @@ static struct commit_list *merge_bases(struct commit *one, struct commit *two)
} }
/* Clean up the result to remove stale ones */ /* Clean up the result to remove stale ones */
free_commit_list(list);
list = result; result = NULL; list = result; result = NULL;
while (list) { while (list) {
struct commit_list *n = list->next; struct commit_list *n = list->next;
@ -1097,7 +1130,6 @@ struct commit_list *get_merge_bases(struct commit *one,
struct commit *two, struct commit *two,
int cleanup) int cleanup)
{ {
const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
struct commit_list *list; struct commit_list *list;
struct commit **rslt; struct commit **rslt;
struct commit_list *result; struct commit_list *result;

18
compat/pread.c Normal file
View File

@ -0,0 +1,18 @@
#include "../git-compat-util.h"
ssize_t git_pread(int fd, void *buf, size_t count, off_t offset)
{
off_t current_offset;
ssize_t rc;
current_offset = lseek(fd, 0, SEEK_CUR);
if (lseek(fd, offset, SEEK_SET) < 0)
return -1;
rc = read_in_full(fd, buf, count);
if (current_offset != lseek(fd, current_offset, SEEK_SET))
return -1;
return rc;
}

153
config.c
View File

@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
return 0; return 0;
} }
if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.ignorestat")) { if (!strcmp(var, "core.ignorestat")) {
assume_unchanged = git_config_bool(var, value); assume_unchanged = git_config_bool(var, value);
return 0; return 0;
@ -304,6 +309,21 @@ int git_default_config(const char *var, const char *value)
return 0; return 0;
} }
if (!strcmp(var, "core.packedgitwindowsize")) {
int pgsz = getpagesize();
packed_git_window_size = git_config_int(var, value);
packed_git_window_size /= pgsz;
if (packed_git_window_size < 2)
packed_git_window_size = 2;
packed_git_window_size *= pgsz;
return 0;
}
if (!strcmp(var, "core.packedgitlimit")) {
packed_git_limit = git_config_int(var, value);
return 0;
}
if (!strcmp(var, "user.name")) { if (!strcmp(var, "user.name")) {
strlcpy(git_default_name, value, sizeof(git_default_name)); strlcpy(git_default_name, value, sizeof(git_default_name));
return 0; return 0;
@ -449,7 +469,15 @@ static int store_aux(const char* key, const char* value)
return 0; return 0;
} }
static void store_write_section(int fd, const char* key) static int write_error()
{
fprintf(stderr, "Failed to write new configuration file\n");
/* Same error code as "failed to rename". */
return 4;
}
static int store_write_section(int fd, const char* key)
{ {
const char *dot = strchr(key, '.'); const char *dot = strchr(key, '.');
int len1 = store.baselen, len2 = -1; int len1 = store.baselen, len2 = -1;
@ -463,37 +491,74 @@ static void store_write_section(int fd, const char* key)
} }
} }
write(fd, "[", 1); if (write_in_full(fd, "[", 1) != 1 ||
write(fd, key, len1); write_in_full(fd, key, len1) != len1)
return 0;
if (len2 >= 0) { if (len2 >= 0) {
write(fd, " \"", 2); if (write_in_full(fd, " \"", 2) != 2)
return 0;
while (--len2 >= 0) { while (--len2 >= 0) {
unsigned char c = *++dot; unsigned char c = *++dot;
if (c == '"') if (c == '"')
write(fd, "\\", 1); if (write_in_full(fd, "\\", 1) != 1)
write(fd, &c, 1); return 0;
if (write_in_full(fd, &c, 1) != 1)
return 0;
} }
write(fd, "\"", 1); if (write_in_full(fd, "\"", 1) != 1)
return 0;
} }
write(fd, "]\n", 2); if (write_in_full(fd, "]\n", 2) != 2)
return 0;
return 1;
} }
static void store_write_pair(int fd, const char* key, const char* value) static int store_write_pair(int fd, const char* key, const char* value)
{ {
int i; int i;
int length = strlen(key+store.baselen+1);
int quote = 0;
write(fd, "\t", 1); /* Check to see if the value needs to be quoted. */
write(fd, key+store.baselen+1, if (value[0] == ' ')
strlen(key+store.baselen+1)); quote = 1;
write(fd, " = ", 3); for (i = 0; value[i]; i++)
if (value[i] == ';' || value[i] == '#')
quote = 1;
if (value[i-1] == ' ')
quote = 1;
if (write_in_full(fd, "\t", 1) != 1 ||
write_in_full(fd, key+store.baselen+1, length) != length ||
write_in_full(fd, " = ", 3) != 3)
return 0;
if (quote && write_in_full(fd, "\"", 1) != 1)
return 0;
for (i = 0; value[i]; i++) for (i = 0; value[i]; i++)
switch (value[i]) { switch (value[i]) {
case '\n': write(fd, "\\n", 2); break; case '\n':
case '\t': write(fd, "\\t", 2); break; if (write_in_full(fd, "\\n", 2) != 2)
case '"': case '\\': write(fd, "\\", 1); return 0;
default: write(fd, value+i, 1); break;
case '\t':
if (write_in_full(fd, "\\t", 2) != 2)
return 0;
break;
case '"':
case '\\':
if (write_in_full(fd, "\\", 1) != 1)
return 0;
default:
if (write_in_full(fd, value+i, 1) != 1)
return 0;
break;
} }
write(fd, "\n", 1); if (quote && write_in_full(fd, "\"", 1) != 1)
return 0;
if (write_in_full(fd, "\n", 1) != 1)
return 0;
return 1;
} }
static int find_beginning_of_line(const char* contents, int size, static int find_beginning_of_line(const char* contents, int size,
@ -633,9 +698,10 @@ int git_config_set_multivar(const char* key, const char* value,
} }
store.key = (char*)key; store.key = (char*)key;
store_write_section(fd, key); if (!store_write_section(fd, key) ||
store_write_pair(fd, key, value); !store_write_pair(fd, key, value))
} else{ goto write_err_out;
} else {
struct stat st; struct stat st;
char* contents; char* contents;
int i, copy_begin, copy_end, new_line = 0; int i, copy_begin, copy_end, new_line = 0;
@ -695,7 +761,7 @@ int git_config_set_multivar(const char* key, const char* value,
} }
fstat(in_fd, &st); fstat(in_fd, &st);
contents = mmap(NULL, st.st_size, PROT_READ, contents = xmmap(NULL, st.st_size, PROT_READ,
MAP_PRIVATE, in_fd, 0); MAP_PRIVATE, in_fd, 0);
close(in_fd); close(in_fd);
@ -714,25 +780,33 @@ int git_config_set_multivar(const char* key, const char* value,
/* write the first part of the config */ /* write the first part of the config */
if (copy_end > copy_begin) { if (copy_end > copy_begin) {
write(fd, contents + copy_begin, if (write_in_full(fd, contents + copy_begin,
copy_end - copy_begin); copy_end - copy_begin) <
if (new_line) copy_end - copy_begin)
write(fd, "\n", 1); goto write_err_out;
if (new_line &&
write_in_full(fd, "\n", 1) != 1)
goto write_err_out;
} }
copy_begin = store.offset[i]; copy_begin = store.offset[i];
} }
/* write the pair (value == NULL means unset) */ /* write the pair (value == NULL means unset) */
if (value != NULL) { if (value != NULL) {
if (store.state == START) if (store.state == START) {
store_write_section(fd, key); if (!store_write_section(fd, key))
store_write_pair(fd, key, value); goto write_err_out;
}
if (!store_write_pair(fd, key, value))
goto write_err_out;
} }
/* write the rest of the config */ /* write the rest of the config */
if (copy_begin < st.st_size) if (copy_begin < st.st_size)
write(fd, contents + copy_begin, if (write_in_full(fd, contents + copy_begin,
st.st_size - copy_begin); st.st_size - copy_begin) <
st.st_size - copy_begin)
goto write_err_out;
munmap(contents, st.st_size); munmap(contents, st.st_size);
unlink(config_filename); unlink(config_filename);
@ -755,6 +829,11 @@ out_free:
free(lock_file); free(lock_file);
} }
return ret; return ret;
write_err_out:
ret = write_error();
goto out_free;
} }
int git_config_rename_section(const char *old_name, const char *new_name) int git_config_rename_section(const char *old_name, const char *new_name)
@ -785,6 +864,7 @@ int git_config_rename_section(const char *old_name, const char *new_name)
while (fgets(buf, sizeof(buf), config_file)) { while (fgets(buf, sizeof(buf), config_file)) {
int i; int i;
int length;
for (i = 0; buf[i] && isspace(buf[i]); i++) for (i = 0; buf[i] && isspace(buf[i]); i++)
; /* do nothing */ ; /* do nothing */
if (buf[i] == '[') { if (buf[i] == '[') {
@ -815,11 +895,18 @@ int git_config_rename_section(const char *old_name, const char *new_name)
/* old_name matches */ /* old_name matches */
ret++; ret++;
store.baselen = strlen(new_name); store.baselen = strlen(new_name);
store_write_section(out_fd, new_name); if (!store_write_section(out_fd, new_name)) {
ret = write_error();
goto out;
}
continue; continue;
} }
} }
write(out_fd, buf, strlen(buf)); length = strlen(buf);
if (write_in_full(out_fd, buf, length) != length) {
ret = write_error();
goto out;
}
} }
fclose(config_file); fclose(config_file);
if (close(out_fd) || commit_lock_file(lock) < 0) if (close(out_fd) || commit_lock_file(lock) < 0)

View File

@ -280,6 +280,15 @@ and returns the process output as a string."
(git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name))) (git-run-command nil nil "update-index" "--info-only" "--add" "--" (file-relative-name ignore-name)))
(git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name)))) (git-add-status-file (if created 'added 'modified) (file-relative-name ignore-name))))
; propertize definition for XEmacs, stolen from erc-compat
(eval-when-compile
(unless (fboundp 'propertize)
(defun propertize (string &rest props)
(let ((string (copy-sequence string)))
(while props
(put-text-property 0 (length string) (nth 0 props) (nth 1 props) string)
(setq props (cddr props)))
string))))
;;;; Wrappers for basic git commands ;;;; Wrappers for basic git commands
;;;; ------------------------------------------------------------ ;;;; ------------------------------------------------------------
@ -448,11 +457,10 @@ and returns the process output as a string."
(defun git-fileinfo-prettyprint (info) (defun git-fileinfo-prettyprint (info)
"Pretty-printer for the git-fileinfo structure." "Pretty-printer for the git-fileinfo structure."
(insert (format " %s %s %s %s%s" (insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ")
(if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ") " " (git-status-code-as-string (git-fileinfo->state info))
(git-status-code-as-string (git-fileinfo->state info)) " " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info))
(git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info)) " " (git-escape-file-name (git-fileinfo->name info))
(git-escape-file-name (git-fileinfo->name info))
(git-rename-as-string info)))) (git-rename-as-string info))))
(defun git-parse-status (status) (defun git-parse-status (status)

View File

@ -102,7 +102,7 @@ static void logreport(int priority, const char *err, va_list params)
buf[buflen++] = '\n'; buf[buflen++] = '\n';
buf[buflen] = '\0'; buf[buflen] = '\0';
write(2, buf, buflen); write_in_full(2, buf, buflen);
} }
static void logerror(const char *err, ...) static void logerror(const char *err, ...)

View File

@ -97,7 +97,7 @@ int run_diff_files(struct rev_info *revs, int silent_on_removed)
* Show the diff for the 'ce' if we found the one * Show the diff for the 'ce' if we found the one
* from the desired stage. * from the desired stage.
*/ */
diff_unmerge(&revs->diffopt, ce->name); diff_unmerge(&revs->diffopt, ce->name, 0, null_sha1);
if (ce_stage(ce) != diff_unmerged_stage) if (ce_stage(ce) != diff_unmerged_stage)
continue; continue;
} }
@ -297,9 +297,12 @@ static int diff_cache(struct rev_info *revs,
!show_modified(revs, ce, ac[1], 0, !show_modified(revs, ce, ac[1], 0,
cached, match_missing)) cached, match_missing))
break; break;
/* fallthru */ diff_unmerge(&revs->diffopt, ce->name,
ntohl(ce->ce_mode), ce->sha1);
break;
case 3: case 3:
diff_unmerge(&revs->diffopt, ce->name); diff_unmerge(&revs->diffopt, ce->name,
0, null_sha1);
break; break;
default: default:

12
diff.c
View File

@ -1341,10 +1341,8 @@ int diff_populate_filespec(struct diff_filespec *s, int size_only)
fd = open(s->path, O_RDONLY); fd = open(s->path, O_RDONLY);
if (fd < 0) if (fd < 0)
goto err_empty; goto err_empty;
s->data = mmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0); s->data = xmmap(NULL, s->size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd); close(fd);
if (s->data == MAP_FAILED)
goto err_empty;
s->should_munmap = 1; s->should_munmap = 1;
} }
else { else {
@ -1391,7 +1389,7 @@ static void prep_temp_blob(struct diff_tempfile *temp,
fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX"); fd = git_mkstemp(temp->tmp_path, TEMPFILE_PATH_LEN, ".diff_XXXXXX");
if (fd < 0) if (fd < 0)
die("unable to create temp-file"); die("unable to create temp-file");
if (write(fd, blob, size) != size) if (write_in_full(fd, blob, size) != size)
die("unable to write temp-file"); die("unable to write temp-file");
close(fd); close(fd);
temp->name = temp->tmp_path; temp->name = temp->tmp_path;
@ -2875,10 +2873,12 @@ void diff_change(struct diff_options *options,
} }
void diff_unmerge(struct diff_options *options, void diff_unmerge(struct diff_options *options,
const char *path) const char *path,
unsigned mode, const unsigned char *sha1)
{ {
struct diff_filespec *one, *two; struct diff_filespec *one, *two;
one = alloc_filespec(path); one = alloc_filespec(path);
two = alloc_filespec(path); two = alloc_filespec(path);
diff_queue(&diff_queued_diff, one, two); fill_filespec(one, sha1, mode);
diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
} }

4
diff.h
View File

@ -144,7 +144,9 @@ extern void diff_change(struct diff_options *,
const char *base, const char *path); const char *base, const char *path);
extern void diff_unmerge(struct diff_options *, extern void diff_unmerge(struct diff_options *,
const char *path); const char *path,
unsigned mode,
const unsigned char *sha1);
extern int diff_scoreopt_parse(const char *opt); extern int diff_scoreopt_parse(const char *opt);

View File

@ -54,9 +54,9 @@ struct diff_filepair {
unsigned source_stays : 1; /* all of R/C are copies */ unsigned source_stays : 1; /* all of R/C are copies */
unsigned broken_pair : 1; unsigned broken_pair : 1;
unsigned renamed_pair : 1; unsigned renamed_pair : 1;
unsigned is_unmerged : 1;
}; };
#define DIFF_PAIR_UNMERGED(p) \ #define DIFF_PAIR_UNMERGED(p) ((p)->is_unmerged)
(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
#define DIFF_PAIR_RENAME(p) ((p)->renamed_pair) #define DIFF_PAIR_RENAME(p) ((p)->renamed_pair)

2
dir.c
View File

@ -142,7 +142,7 @@ static int add_excludes_from_file_1(const char *fname,
return 0; return 0;
} }
buf = xmalloc(size+1); buf = xmalloc(size+1);
if (read(fd, buf, size) != size) if (read_in_full(fd, buf, size) != size)
goto err; goto err;
close(fd); close(fd);

View File

@ -89,7 +89,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
return error("git-checkout-index: unable to create file %s (%s)", return error("git-checkout-index: unable to create file %s (%s)",
path, strerror(errno)); path, strerror(errno));
} }
wrote = write(fd, new, size); wrote = write_in_full(fd, new, size);
close(fd); close(fd);
free(new); free(new);
if (wrote != size) if (wrote != size)
@ -104,7 +104,7 @@ static int write_entry(struct cache_entry *ce, char *path, struct checkout *stat
return error("git-checkout-index: unable to create " return error("git-checkout-index: unable to create "
"file %s (%s)", path, strerror(errno)); "file %s (%s)", path, strerror(errno));
} }
wrote = write(fd, new, size); wrote = write_in_full(fd, new, size);
close(fd); close(fd);
free(new); free(new);
if (wrote != size) if (wrote != size)

View File

@ -15,7 +15,8 @@ int use_legacy_headers = 1;
int trust_executable_bit = 1; int trust_executable_bit = 1;
int assume_unchanged; int assume_unchanged;
int prefer_symlink_refs; int prefer_symlink_refs;
int log_all_ref_updates; int is_bare_repository_cfg = -1; /* unspecified */
int log_all_ref_updates = -1; /* unspecified */
int warn_ambiguous_refs = 1; int warn_ambiguous_refs = 1;
int repository_format_version; int repository_format_version;
char *git_commit_encoding; char *git_commit_encoding;
@ -23,6 +24,8 @@ char *git_log_output_encoding;
int shared_repository = PERM_UMASK; int shared_repository = PERM_UMASK;
const char *apply_default_whitespace; const char *apply_default_whitespace;
int zlib_compression_level = Z_DEFAULT_COMPRESSION; int zlib_compression_level = Z_DEFAULT_COMPRESSION;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
int pager_in_use; int pager_in_use;
int pager_use_color = 1; int pager_use_color = 1;
@ -49,12 +52,15 @@ static void setup_git_env(void)
git_graft_file = getenv(GRAFT_ENVIRONMENT); git_graft_file = getenv(GRAFT_ENVIRONMENT);
if (!git_graft_file) if (!git_graft_file)
git_graft_file = xstrdup(git_path("info/grafts")); git_graft_file = xstrdup(git_path("info/grafts"));
log_all_ref_updates = !is_bare_git_dir(git_dir);
} }
int is_bare_git_dir (const char *dir) int is_bare_repository(void)
{ {
const char *s; const char *dir, *s;
if (0 <= is_bare_repository_cfg)
return is_bare_repository_cfg;
dir = get_git_dir();
if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT)) if (!strcmp(dir, DEFAULT_GIT_DIR_ENVIRONMENT))
return 0; return 0;
s = strrchr(dir, '/'); s = strrchr(dir, '/');

View File

@ -290,7 +290,7 @@ static int fsck_sha1(unsigned char *sha1)
{ {
struct object *obj = parse_object(sha1); struct object *obj = parse_object(sha1);
if (!obj) if (!obj)
return error("%s: object not found", sha1_to_hex(sha1)); return error("%s: object corrupt or missing", sha1_to_hex(sha1));
if (obj->flags & SEEN) if (obj->flags & SEEN)
return 0; return 0;
obj->flags |= SEEN; obj->flags |= SEEN;
@ -399,7 +399,9 @@ static void fsck_dir(int i, char *path)
static int default_refs; static int default_refs;
static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1, char *datail, void *cb_data) static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
const char *email, unsigned long timestamp, int tz,
const char *message, void *cb_data)
{ {
struct object *obj; struct object *obj;

View File

@ -22,7 +22,7 @@ commit
diff diff
fetch fetch
grep grep
init-db init
log log
merge merge
mv mv

View File

@ -2,11 +2,12 @@
# #
# Copyright (c) 2005, 2006 Junio C Hamano # Copyright (c) 2005, 2006 Junio C Hamano
USAGE='[--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way] USAGE='[--signoff] [--dotest=<dir>] [--utf8 | --no-utf8] [--binary] [--3way]
[--interactive] [--whitespace=<option>] <mbox>... [--interactive] [--whitespace=<option>] <mbox>...
or, when resuming [--skip | --resolved]' or, when resuming [--skip | --resolved]'
. git-sh-setup . git-sh-setup
set_reflog_action am set_reflog_action am
require_work_tree
git var GIT_COMMITTER_IDENT >/dev/null || exit git var GIT_COMMITTER_IDENT >/dev/null || exit
@ -105,7 +106,7 @@ It does not apply to blobs recorded in its index."
} }
prec=4 prec=4
dotest=.dotest sign= utf8= keep= skip= interactive= resolved= binary= ws= resolvemsg= dotest=.dotest sign= utf8=t keep= skip= interactive= resolved= binary= ws= resolvemsg=
while case "$#" in 0) break;; esac while case "$#" in 0) break;; esac
do do
@ -128,7 +129,9 @@ do
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff) -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
sign=t; shift ;; sign=t; shift ;;
-u|--u|--ut|--utf|--utf8) -u|--u|--ut|--utf|--utf8)
utf8=t; shift ;; utf8=t; shift ;; # this is now default
--no-u|--no-ut|--no-utf|--no-utf8)
utf8=; shift ;;
-k|--k|--ke|--kee|--keep) -k|--k|--ke|--kee|--keep)
keep=t; shift ;; keep=t; shift ;;
@ -226,6 +229,8 @@ fi
if test "$(cat "$dotest/utf8")" = t if test "$(cat "$dotest/utf8")" = t
then then
utf8=-u utf8=-u
else
utf8=-n
fi fi
if test "$(cat "$dotest/keep")" = t if test "$(cat "$dotest/keep")" = t
then then

View File

@ -23,11 +23,12 @@ USAGE='[-u] [-k] [-q] [-m] (-c .dotest/<num> | mbox) [signoff]'
git var GIT_COMMITTER_IDENT >/dev/null || exit git var GIT_COMMITTER_IDENT >/dev/null || exit
keep_subject= query_apply= continue= utf8= resume=t keep_subject= query_apply= continue= utf8=-u resume=t
while case "$#" in 0) break ;; esac while case "$#" in 0) break ;; esac
do do
case "$1" in case "$1" in
-u) utf8=-u ;; -u) utf8=-u ;;
-n) utf8=-n ;;
-k) keep_subject=-k ;; -k) keep_subject=-k ;;
-q) query_apply=t ;; -q) query_apply=t ;;
-c) continue="$2"; resume=f; shift ;; -c) continue="$2"; resume=f; shift ;;

View File

@ -226,7 +226,7 @@ my $import = 0;
unless (-d $git_dir) { # initial import unless (-d $git_dir) { # initial import
if ($psets[0]{type} eq 'i' || $psets[0]{type} eq 't') { if ($psets[0]{type} eq 'i' || $psets[0]{type} eq 't') {
print "Starting import from $psets[0]{id}\n"; print "Starting import from $psets[0]{id}\n";
`git-init-db`; `git-init`;
die $! if $?; die $! if $?;
$import = 1; $import = 1;
} else { } else {

View File

@ -3,9 +3,11 @@
USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]' USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
SUBDIRECTORY_OK=Sometimes SUBDIRECTORY_OK=Sometimes
. git-sh-setup . git-sh-setup
require_work_tree
old_name=HEAD old_name=HEAD
old=$(git-rev-parse --verify $old_name 2>/dev/null) old=$(git-rev-parse --verify $old_name 2>/dev/null)
oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
new= new=
new_name= new_name=
force= force=
@ -13,6 +15,8 @@ branch=
newbranch= newbranch=
newbranch_log= newbranch_log=
merge= merge=
LF='
'
while [ "$#" != "0" ]; do while [ "$#" != "0" ]; do
arg="$1" arg="$1"
shift shift
@ -50,7 +54,7 @@ while [ "$#" != "0" ]; do
exit 1 exit 1
fi fi
new="$rev" new="$rev"
new_name="$arg^0" new_name="$arg"
if git-show-ref --verify --quiet -- "refs/heads/$arg" if git-show-ref --verify --quiet -- "refs/heads/$arg"
then then
branch="$arg" branch="$arg"
@ -131,31 +135,53 @@ fi
# We are switching branches and checking out trees, so # We are switching branches and checking out trees, so
# we *NEED* to be at the toplevel. # we *NEED* to be at the toplevel.
cdup=$(git-rev-parse --show-cdup) cd_to_toplevel
if test ! -z "$cdup"
then
cd "$cdup"
fi
[ -z "$new" ] && new=$old && new_name="$old_name" [ -z "$new" ] && new=$old && new_name="$old_name"
# If we don't have an old branch that we're switching to, # If we don't have an existing branch that we're switching to,
# and we don't have a new branch name for the target we # and we don't have a new branch name for the target we
# are switching to, then we'd better just be checking out # are switching to, then we are detaching our HEAD from any
# what we already had # branch. However, if "git checkout HEAD" detaches the HEAD
# from the current branch, even though that may be logically
# correct, it feels somewhat funny. More importantly, we do not
# want "git checkout" nor "git checkout -f" to detach HEAD.
[ -z "$branch$newbranch" ] && detached=
[ "$new" != "$old" ] && detach_warn=
die "git checkout: provided reference cannot be checked out directly
You need -b to associate a new branch with the wanted checkout. Example: if test -z "$branch$newbranch" && test "$new" != "$old"
git checkout -b <new_branch_name> $arg then
" detached="$new"
if test -n "$oldbranch"
then
detach_warn="warning: you are not on ANY branch anymore.
If you meant to create a new branch from the commit, you need -b to
associate a new branch with the wanted checkout. Example:
git checkout -b <new_branch_name> $arg"
fi
elif test -z "$oldbranch" && test -n "$branch"
then
# Coming back...
if test -z "$force"
then
git show-ref -d -s | grep "$old" >/dev/null || {
echo >&2 \
"You are not on any branch and switching to branch '$new_name'
may lose your changes. At this point, you can do one of two things:
(1) Decide it is Ok and say 'git checkout -f $new_name';
(2) Start a new branch from the current commit, by saying
'git checkout -b <branch-name>'.
Leaving your HEAD detached; not switching to branch '$new_name'."
exit 1;
}
fi
fi
if [ "X$old" = X ] if [ "X$old" = X ]
then then
echo "warning: You do not appear to currently be on a branch." >&2 echo >&2 "warning: You appear to be on a branch yet to be born."
echo "warning: Forcing checkout of $new_name." >&2 echo >&2 "warning: Forcing checkout of $new_name."
force=1 force=1
fi fi
@ -226,8 +252,25 @@ if [ "$?" -eq 0 ]; then
git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
branch="$newbranch" branch="$newbranch"
fi fi
[ "$branch" ] && if test -n "$branch"
then
GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch" GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
elif test -n "$detached"
then
# NEEDSWORK: we would want a command to detach the HEAD
# atomically, instead of this handcrafted command sequence.
# Perhaps:
# git update-ref --detach HEAD $new
# or something like that...
#
echo "$detached" >"$GIT_DIR/HEAD.new" &&
mv "$GIT_DIR/HEAD.new" "$GIT_DIR/HEAD" ||
die "Cannot detach HEAD"
if test -n "$detach_warn"
then
echo >&2 "$detach_warn"
fi
fi
rm -f "$GIT_DIR/MERGE_HEAD" rm -f "$GIT_DIR/MERGE_HEAD"
else else
exit 1 exit 1

View File

@ -14,6 +14,7 @@ When optional <paths>... arguments are given, the paths
affected are further limited to those that match them.' affected are further limited to those that match them.'
SUBDIRECTORY_OK=Yes SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
require_work_tree
ignored= ignored=
ignoredonly= ignoredonly=

View File

@ -214,7 +214,7 @@ yes)
GIT_DIR="$D" ;; GIT_DIR="$D" ;;
*) *)
GIT_DIR="$D/.git" ;; GIT_DIR="$D/.git" ;;
esac && export GIT_DIR && git-init-db ${template+"$template"} || usage esac && export GIT_DIR && git-init ${template+"$template"} || usage
if test -n "$reference" if test -n "$reference"
then then
@ -355,7 +355,7 @@ then
# The name under $remote_top the remote HEAD seems to point at. # The name under $remote_top the remote HEAD seems to point at.
head_points_at=$( head_points_at=$(
( (
echo "master" test -f "$GIT_DIR/$remote_top/master" && echo "master"
cd "$GIT_DIR/$remote_top" && cd "$GIT_DIR/$remote_top" &&
find . -type f -print | sed -e 's/^\.\///' find . -type f -print | sed -e 's/^\.\///'
) | ( ) | (

View File

@ -6,6 +6,7 @@
USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-u] [--amend] [-e] [--author <author>] [[-i | -o] <path>...]' USAGE='[-a] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit>] [-u] [--amend] [-e] [--author <author>] [[-i | -o] <path>...]'
SUBDIRECTORY_OK=Yes SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
require_work_tree
git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t git-rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
@ -315,22 +316,16 @@ esac
################################################################ ################################################################
# Prepare index to have a tree to be committed # Prepare index to have a tree to be committed
TOP=`git-rev-parse --show-cdup`
if test -z "$TOP"
then
TOP=./
fi
case "$all,$also" in case "$all,$also" in
t,) t,)
save_index && save_index &&
( (
cd "$TOP" cd_to_toplevel &&
GIT_INDEX_FILE="$NEXT_INDEX" GIT_INDEX_FILE="$NEXT_INDEX" &&
export GIT_INDEX_FILE export GIT_INDEX_FILE &&
git-diff-files --name-only -z | git-diff-files --name-only -z |
git-update-index --remove -z --stdin git-update-index --remove -z --stdin
) ) || exit
;; ;;
,t) ,t)
save_index && save_index &&
@ -338,11 +333,11 @@ t,)
git-diff-files --name-only -z -- "$@" | git-diff-files --name-only -z -- "$@" |
( (
cd "$TOP" cd_to_toplevel &&
GIT_INDEX_FILE="$NEXT_INDEX" GIT_INDEX_FILE="$NEXT_INDEX" &&
export GIT_INDEX_FILE export GIT_INDEX_FILE &&
git-update-index --remove -z --stdin git-update-index --remove -z --stdin
) ) || exit
;; ;;
,) ,)
case "$#" in case "$#" in
@ -434,7 +429,9 @@ then
fi fi
elif test "$use_commit" != "" elif test "$use_commit" != ""
then then
git-cat-file commit "$use_commit" | sed -e '1,/^$/d' encoding=$(git repo-config i18n.commitencoding || echo UTF-8)
git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
sed -e '1,/^$/d' -e 's/^ //'
elif test -f "$GIT_DIR/MERGE_MSG" elif test -f "$GIT_DIR/MERGE_MSG"
then then
cat "$GIT_DIR/MERGE_MSG" cat "$GIT_DIR/MERGE_MSG"
@ -496,7 +493,8 @@ then
q q
} }
' '
set_author_env=`git-cat-file commit "$use_commit" | encoding=$(git repo-config i18n.commitencoding || echo UTF-8)
set_author_env=`git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
LANG=C LC_ALL=C sed -ne "$pick_author_script"` LANG=C LC_ALL=C sed -ne "$pick_author_script"`
eval "$set_author_env" eval "$set_author_env"
export GIT_AUTHOR_NAME export GIT_AUTHOR_NAME
@ -628,7 +626,7 @@ then
if test -z "$quiet" if test -z "$quiet"
then then
echo "Created${initial_commit:+ initial} commit $commit" echo "Created${initial_commit:+ initial} commit $commit"
git-diff-tree --shortstat --summary --root --no-commit-id HEAD git-diff-tree --shortstat --summary --root --no-commit-id HEAD --
fi fi
fi fi

View File

@ -92,12 +92,26 @@ extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); extern void *git_mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
extern int git_munmap(void *start, size_t length); extern int git_munmap(void *start, size_t length);
#define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024)
#else /* NO_MMAP */ #else /* NO_MMAP */
#include <sys/mman.h> #include <sys/mman.h>
#define DEFAULT_PACKED_GIT_WINDOW_SIZE \
(sizeof(void*) >= 8 \
? 1 * 1024 * 1024 * 1024 \
: 32 * 1024 * 1024)
#endif /* NO_MMAP */ #endif /* NO_MMAP */
#define DEFAULT_PACKED_GIT_LIMIT \
((1024L * 1024L) * (sizeof(void*) >= 8 ? 8192 : 256))
#ifdef NO_PREAD
#define pread git_pread
extern ssize_t git_pread(int fd, void *buf, size_t count, off_t offset);
#endif
#ifdef NO_SETENV #ifdef NO_SETENV
#define setenv gitsetenv #define setenv gitsetenv
extern int gitsetenv(const char *, const char *, int); extern int gitsetenv(const char *, const char *, int);
@ -118,21 +132,33 @@ extern char *gitstrcasestr(const char *haystack, const char *needle);
extern size_t gitstrlcpy(char *, const char *, size_t); extern size_t gitstrlcpy(char *, const char *, size_t);
#endif #endif
extern void release_pack_memory(size_t);
static inline char* xstrdup(const char *str) static inline char* xstrdup(const char *str)
{ {
char *ret = strdup(str); char *ret = strdup(str);
if (!ret) {
release_pack_memory(strlen(str) + 1);
ret = strdup(str);
if (!ret) if (!ret)
die("Out of memory, strdup failed"); die("Out of memory, strdup failed");
}
return ret; return ret;
} }
static inline void *xmalloc(size_t size) static inline void *xmalloc(size_t size)
{ {
void *ret = malloc(size); void *ret = malloc(size);
if (!ret && !size)
ret = malloc(1);
if (!ret) {
release_pack_memory(size);
ret = malloc(size);
if (!ret && !size) if (!ret && !size)
ret = malloc(1); ret = malloc(1);
if (!ret) if (!ret)
die("Out of memory, malloc failed"); die("Out of memory, malloc failed");
}
#ifdef XMALLOC_POISON #ifdef XMALLOC_POISON
memset(ret, 0xA5, size); memset(ret, 0xA5, size);
#endif #endif
@ -142,20 +168,47 @@ static inline void *xmalloc(size_t size)
static inline void *xrealloc(void *ptr, size_t size) static inline void *xrealloc(void *ptr, size_t size)
{ {
void *ret = realloc(ptr, size); void *ret = realloc(ptr, size);
if (!ret && !size)
ret = realloc(ptr, 1);
if (!ret) {
release_pack_memory(size);
ret = realloc(ptr, size);
if (!ret && !size) if (!ret && !size)
ret = realloc(ptr, 1); ret = realloc(ptr, 1);
if (!ret) if (!ret)
die("Out of memory, realloc failed"); die("Out of memory, realloc failed");
}
return ret; return ret;
} }
static inline void *xcalloc(size_t nmemb, size_t size) static inline void *xcalloc(size_t nmemb, size_t size)
{ {
void *ret = calloc(nmemb, size); void *ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
if (!ret) {
release_pack_memory(nmemb * size);
ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size)) if (!ret && (!nmemb || !size))
ret = calloc(1, 1); ret = calloc(1, 1);
if (!ret) if (!ret)
die("Out of memory, calloc failed"); die("Out of memory, calloc failed");
}
return ret;
}
static inline void *xmmap(void *start, size_t length,
int prot, int flags, int fd, off_t offset)
{
void *ret = mmap(start, length, prot, flags, fd, offset);
if (ret == MAP_FAILED) {
if (!length)
return NULL;
release_pack_memory(length);
ret = mmap(start, length, prot, flags, fd, offset);
if (ret == MAP_FAILED)
die("Out of memory? mmap failed: %s", strerror(errno));
}
return ret; return ret;
} }

View File

@ -29,7 +29,7 @@ use IPC::Open2;
$SIG{'PIPE'}="IGNORE"; $SIG{'PIPE'}="IGNORE";
$ENV{'TZ'}="UTC"; $ENV{'TZ'}="UTC";
our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L); our ($opt_h,$opt_o,$opt_v,$opt_k,$opt_u,$opt_d,$opt_p,$opt_C,$opt_z,$opt_i,$opt_P, $opt_s,$opt_m,$opt_M,$opt_A,$opt_S,$opt_L, $opt_a);
my (%conv_author_name, %conv_author_email); my (%conv_author_name, %conv_author_email);
sub usage() { sub usage() {
@ -37,7 +37,7 @@ sub usage() {
Usage: ${\basename $0} # fetch/update GIT from CVS Usage: ${\basename $0} # fetch/update GIT from CVS
[-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file] [-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
[-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] [-i] [-k] [-u] [-p opts-for-cvsps] [-C GIT_repository] [-z fuzz] [-i] [-k] [-u]
[-s subst] [-m] [-M regex] [-S regex] [CVS_module] [-s subst] [-a] [-m] [-M regex] [-S regex] [CVS_module]
END END
exit(1); exit(1);
} }
@ -105,6 +105,8 @@ if ($opt_d) {
} }
$opt_o ||= "origin"; $opt_o ||= "origin";
$opt_s ||= "-"; $opt_s ||= "-";
$opt_a ||= 0;
my $git_tree = $opt_C; my $git_tree = $opt_C;
$git_tree ||= "."; $git_tree ||= ".";
@ -129,6 +131,11 @@ if ($opt_M) {
push (@mergerx, qr/$opt_M/); push (@mergerx, qr/$opt_M/);
} }
# Remember UTC of our starting time
# we'll want to avoid importing commits
# that are too recent
our $starttime = time();
select(STDERR); $|=1; select(STDOUT); select(STDERR); $|=1; select(STDOUT);
@ -513,7 +520,7 @@ $orig_git_index = $ENV{GIT_INDEX_FILE} if exists $ENV{GIT_INDEX_FILE};
my %index; # holds filenames of one index per branch my %index; # holds filenames of one index per branch
unless (-d $git_dir) { unless (-d $git_dir) {
system("git-init-db"); system("git-init");
die "Cannot init the GIT db at $git_tree: $?\n" if $?; die "Cannot init the GIT db at $git_tree: $?\n" if $?;
system("git-read-tree"); system("git-read-tree");
die "Cannot init an empty tree: $?\n" if $?; die "Cannot init an empty tree: $?\n" if $?;
@ -568,9 +575,11 @@ if ($opt_A) {
# run cvsps into a file unless we are getting # run cvsps into a file unless we are getting
# it passed as a file via $opt_P # it passed as a file via $opt_P
# #
my $cvspsfile;
unless ($opt_P) { unless ($opt_P) {
print "Running cvsps...\n" if $opt_v; print "Running cvsps...\n" if $opt_v;
my $pid = open(CVSPS,"-|"); my $pid = open(CVSPS,"-|");
my $cvspsfh;
die "Cannot fork: $!\n" unless defined $pid; die "Cannot fork: $!\n" unless defined $pid;
unless ($pid) { unless ($pid) {
my @opt; my @opt;
@ -583,18 +592,18 @@ unless ($opt_P) {
exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree); exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
die "Could not start cvsps: $!\n"; die "Could not start cvsps: $!\n";
} }
my ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps', ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
DIR => File::Spec->tmpdir()); DIR => File::Spec->tmpdir());
while (<CVSPS>) { while (<CVSPS>) {
print $cvspsfh $_; print $cvspsfh $_;
} }
close CVSPS; close CVSPS;
close $cvspsfh; close $cvspsfh;
$opt_P = $cvspsfile; } else {
$cvspsfile = $opt_P;
} }
open(CVS, "<$cvspsfile") or die $!;
open(CVS, "<$opt_P") or die $!;
## cvsps output: ## cvsps output:
#--------------------- #---------------------
@ -651,7 +660,7 @@ $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
sub commit { sub commit {
if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) { if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
# looks like an initial commit # looks like an initial commit
# use the index primed by git-init-db # use the index primed by git-init
$ENV{GIT_INDEX_FILE} = '.git/index'; $ENV{GIT_INDEX_FILE} = '.git/index';
$index{$branch} = '.git/index'; $index{$branch} = '.git/index';
} else { } else {
@ -824,6 +833,15 @@ while (<CVS>) {
$state = 11; $state = 11;
next; next;
} }
if (!$opt_a && $starttime - 300 - (defined $opt_z ? $opt_z : 300) <= $date) {
# skip if the commit is too recent
# that the cvsps default fuzz is 300s, we give ourselves another
# 300s just in case -- this also prevents skipping commits
# due to server clock drift
print "skip patchset $patchset: $date too recent\n" if $opt_v;
$state = 11;
next;
}
if (exists $ignorebranch{$branch}) { if (exists $ignorebranch{$branch}) {
print STDERR "Skipping $branch\n"; print STDERR "Skipping $branch\n";
$state = 11; $state = 11;
@ -920,6 +938,10 @@ while (<CVS>) {
} }
commit() if $branch and $state != 11; commit() if $branch and $state != 11;
unless ($opt_P) {
unlink($cvspsfile);
}
# The heuristic of repacking every 1024 commits can leave a # The heuristic of repacking every 1024 commits can leave a
# lot of unpacked data. If there is more than 1MB worth of # lot of unpacked data. If there is more than 1MB worth of
# not-packed objects, repack once more. # not-packed objects, repack once more.

View File

@ -1181,12 +1181,15 @@ sub req_ci
$filename = filecleanup($filename); $filename = filecleanup($filename);
my $meta = $updater->getmeta($filename); my $meta = $updater->getmeta($filename);
unless (defined $meta->{revision}) {
$meta->{revision} = 1;
}
my ( $filepart, $dirpart ) = filenamesplit($filename, 1); my ( $filepart, $dirpart ) = filenamesplit($filename, 1);
$log->debug("Checked-in $dirpart : $filename"); $log->debug("Checked-in $dirpart : $filename");
if ( $meta->{filehash} eq "deleted" ) if ( defined $meta->{filehash} && $meta->{filehash} eq "deleted" )
{ {
print "Remove-entry $dirpart\n"; print "Remove-entry $dirpart\n";
print "$filename\n"; print "$filename\n";
@ -2184,7 +2187,10 @@ sub update
# first lets get the commit list # first lets get the commit list
$ENV{GIT_DIR} = $self->{git_path}; $ENV{GIT_DIR} = $self->{git_path};
my $commitinfo = `git-cat-file commit $self->{module} 2>&1`; my $commitsha1 = `git rev-parse $self->{module}`;
chomp $commitsha1;
my $commitinfo = `git cat-file commit $self->{module} 2>&1`;
unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ ) unless ( $commitinfo =~ /tree\s+[a-zA-Z0-9]{40}/ )
{ {
die("Invalid module '$self->{module}'"); die("Invalid module '$self->{module}'");
@ -2194,6 +2200,10 @@ sub update
my $git_log; my $git_log;
my $lastcommit = $self->_get_prop("last_commit"); my $lastcommit = $self->_get_prop("last_commit");
if (defined $lastcommit && $lastcommit eq $commitsha1) { # up-to-date
return 1;
}
# Start exclusive lock here... # Start exclusive lock here...
$self->{dbh}->begin_work() or die "Cannot lock database for BEGIN"; $self->{dbh}->begin_work() or die "Cannot lock database for BEGIN";

View File

@ -5,12 +5,8 @@ USAGE='<fetch-options> <repository> <refspec>...'
SUBDIRECTORY_OK=Yes SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
set_reflog_action "fetch $*" set_reflog_action "fetch $*"
cd_to_toplevel ;# probably unnecessary...
TOP=$(git-rev-parse --show-cdup)
if test ! -z "$TOP"
then
cd "$TOP"
fi
. git-parse-remote . git-parse-remote
_x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]' _x40='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
_x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40" _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
@ -231,11 +227,12 @@ update_local_ref () {
esac esac
} }
case "$update_head_ok" in # updating the current HEAD with git-fetch in a bare
'') # repository is always fine.
if test -z "$update_head_ok" && test $(is_bare_repository) = false
then
orig_head=$(git-rev-parse --verify HEAD 2>/dev/null) orig_head=$(git-rev-parse --verify HEAD 2>/dev/null)
;; fi
esac
# If --tags (and later --heads or --all) is specified, then we are # If --tags (and later --heads or --all) is specified, then we are
# not talking about defaults stored in Pull: line of remotes or # not talking about defaults stored in Pull: line of remotes or

View File

@ -5,11 +5,14 @@
USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+' USAGE='[-n] [--no-commit] [--squash] [-s <strategy>] [-m=<merge-message>] <commit>+'
SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
set_reflog_action "merge $*" set_reflog_action "merge $*"
require_work_tree
cd_to_toplevel
test -z "$(git ls-files -u)" || test -z "$(git ls-files -u)" ||
die "You are in a middle of conflicted merge." die "You are in the middle of a conflicted merge."
LF=' LF='
' '
@ -298,11 +301,16 @@ f,*)
;; ;;
?,1,*,) ?,1,*,)
# We are not doing octopus, not fast forward, and have only # We are not doing octopus, not fast forward, and have only
# one common. See if it is really trivial. # one common.
git var GIT_COMMITTER_IDENT >/dev/null || exit
echo "Trying really trivial in-index merge..."
git-update-index --refresh 2>/dev/null git-update-index --refresh 2>/dev/null
case " $use_strategies " in
*' recursive '*|*' recur '*)
: run merge later
;;
*)
# See if it is really trivial.
git var GIT_COMMITTER_IDENT >/dev/null || exit
echo "Trying really trivial in-index merge..."
if git-read-tree --trivial -m -u -v $common $head "$1" && if git-read-tree --trivial -m -u -v $common $head "$1" &&
result_tree=$(git-write-tree) result_tree=$(git-write-tree)
then then
@ -316,6 +324,7 @@ f,*)
exit 0 exit 0
fi fi
echo "Nope." echo "Nope."
esac
;; ;;
*) *)
# An octopus. If we can reach all the remote we are up to date. # An octopus. If we can reach all the remote we are up to date.

View File

@ -163,7 +163,7 @@ class git_command:
self.gitdir = self.get_single("rev-parse --git-dir") self.gitdir = self.get_single("rev-parse --git-dir")
report(2, "gdir:", self.gitdir) report(2, "gdir:", self.gitdir)
except: except:
die("Not a git repository... did you forget to \"git init-db\" ?") die("Not a git repository... did you forget to \"git init\" ?")
try: try:
self.cdup = self.get_single("rev-parse --show-cdup") self.cdup = self.get_single("rev-parse --show-cdup")
if self.cdup != "": if self.cdup != "":

View File

@ -6,11 +6,14 @@
USAGE='[-n | --no-summary] [--no-commit] [-s strategy]... [<fetch-options>] <repo> <head>...' USAGE='[-n | --no-summary] [--no-commit] [-s strategy]... [<fetch-options>] <repo> <head>...'
LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.' LONG_USAGE='Fetch one or more remote refs and merge it/them into the current HEAD.'
SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
set_reflog_action "pull $*" set_reflog_action "pull $*"
require_work_tree
cd_to_toplevel
test -z "$(git ls-files -u)" || test -z "$(git ls-files -u)" ||
die "You are in a middle of conflicted merge." die "You are in the middle of a conflicted merge."
strategy_args= no_summary= no_commit= squash= strategy_args= no_summary= no_commit= squash=
while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac while case "$#,$1" in 0) break ;; *,-*) ;; *) break ;; esac

View File

@ -27,8 +27,12 @@ Example: git-rebase master~1 topic
/ --> / / --> /
D---E---F---G master D---E---F---G master D---E---F---G master D---E---F---G master
' '
SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
set_reflog_action rebase set_reflog_action rebase
require_work_tree
cd_to_toplevel
RESOLVEMSG=" RESOLVEMSG="
When you have resolved this problem run \"git rebase --continue\". When you have resolved this problem run \"git rebase --continue\".

282
git-remote.perl Executable file
View File

@ -0,0 +1,282 @@
#!/usr/bin/perl -w
use Git;
my $git = Git->repository();
sub add_remote_config {
my ($hash, $name, $what, $value) = @_;
if ($what eq 'url') {
if (exists $hash->{$name}{'URL'}) {
print STDERR "Warning: more than one remote.$name.url\n";
}
$hash->{$name}{'URL'} = $value;
}
elsif ($what eq 'fetch') {
$hash->{$name}{'FETCH'} ||= [];
push @{$hash->{$name}{'FETCH'}}, $value;
}
if (!exists $hash->{$name}{'SOURCE'}) {
$hash->{$name}{'SOURCE'} = 'config';
}
}
sub add_remote_remotes {
my ($hash, $file, $name) = @_;
if (exists $hash->{$name}) {
$hash->{$name}{'WARNING'} = 'ignored due to config';
return;
}
my $fh;
if (!open($fh, '<', $file)) {
print STDERR "Warning: cannot open $file\n";
return;
}
my $it = { 'SOURCE' => 'remotes' };
$hash->{$name} = $it;
while (<$fh>) {
chomp;
if (/^URL:\s*(.*)$/) {
# Having more than one is Ok -- it is used for push.
if (! exists $it->{'URL'}) {
$it->{'URL'} = $1;
}
}
elsif (/^Push:\s*(.*)$/) {
; # later
}
elsif (/^Pull:\s*(.*)$/) {
$it->{'FETCH'} ||= [];
push @{$it->{'FETCH'}}, $1;
}
elsif (/^\#/) {
; # ignore
}
else {
print STDERR "Warning: funny line in $file: $_\n";
}
}
close($fh);
}
sub list_remote {
my ($git) = @_;
my %seen = ();
my @remotes = eval {
$git->command(qw(repo-config --get-regexp), '^remote\.');
};
for (@remotes) {
if (/^remote\.([^.]*)\.(\S*)\s+(.*)$/) {
add_remote_config(\%seen, $1, $2, $3);
}
}
my $dir = $git->repo_path() . "/remotes";
if (opendir(my $dh, $dir)) {
local $_;
while ($_ = readdir($dh)) {
chomp;
next if (! -f "$dir/$_" || ! -r _);
add_remote_remotes(\%seen, "$dir/$_", $_);
}
}
return \%seen;
}
sub add_branch_config {
my ($hash, $name, $what, $value) = @_;
if ($what eq 'remote') {
if (exists $hash->{$name}{'REMOTE'}) {
print STDERR "Warning: more than one branch.$name.remote\n";
}
$hash->{$name}{'REMOTE'} = $value;
}
elsif ($what eq 'merge') {
$hash->{$name}{'MERGE'} ||= [];
push @{$hash->{$name}{'MERGE'}}, $value;
}
}
sub list_branch {
my ($git) = @_;
my %seen = ();
my @branches = eval {
$git->command(qw(repo-config --get-regexp), '^branch\.');
};
for (@branches) {
if (/^branch\.([^.]*)\.(\S*)\s+(.*)$/) {
add_branch_config(\%seen, $1, $2, $3);
}
}
return \%seen;
}
my $remote = list_remote($git);
my $branch = list_branch($git);
sub update_ls_remote {
my ($harder, $info) = @_;
return if (($harder == 0) ||
(($harder == 1) && exists $info->{'LS_REMOTE'}));
my @ref = map {
s|^[0-9a-f]{40}\s+refs/heads/||;
$_;
} $git->command(qw(ls-remote --heads), $info->{'URL'});
$info->{'LS_REMOTE'} = \@ref;
}
sub show_wildcard_mapping {
my ($forced, $ours, $ls) = @_;
my %refs;
for (@$ls) {
$refs{$_} = 01; # bit #0 to say "they have"
}
for ($git->command('for-each-ref', "refs/remotes/$ours")) {
chomp;
next unless (s|^[0-9a-f]{40}\s[a-z]+\srefs/remotes/$ours/||);
next if ($_ eq 'HEAD');
$refs{$_} ||= 0;
$refs{$_} |= 02; # bit #1 to say "we have"
}
my (@new, @stale, @tracked);
for (sort keys %refs) {
my $have = $refs{$_};
if ($have == 1) {
push @new, $_;
}
elsif ($have == 2) {
push @stale, $_;
}
elsif ($have == 3) {
push @tracked, $_;
}
}
if (@new) {
print " New remote branches (next fetch will store in remotes/$ours)\n";
print " @new\n";
}
if (@stale) {
print " Stale tracking branches in remotes/$ours (you'd better remove them)\n";
print " @stale\n";
}
if (@tracked) {
print " Tracked remote branches\n";
print " @tracked\n";
}
}
sub show_mapping {
my ($name, $info) = @_;
my $fetch = $info->{'FETCH'};
my $ls = $info->{'LS_REMOTE'};
my (@stale, @tracked);
for (@$fetch) {
next unless (/(\+)?([^:]+):(.*)/);
my ($forced, $theirs, $ours) = ($1, $2, $3);
if ($theirs eq 'refs/heads/*' &&
$ours =~ /^refs\/remotes\/(.*)\/\*$/) {
# wildcard mapping
show_wildcard_mapping($forced, $1, $ls);
}
elsif ($theirs =~ /\*/ || $ours =~ /\*/) {
print STDERR "Warning: unrecognized mapping in remotes.$name.fetch: $_\n";
}
elsif ($theirs =~ s|^refs/heads/||) {
if (!grep { $_ eq $theirs } @$ls) {
push @stale, $theirs;
}
elsif ($ours ne '') {
push @tracked, $theirs;
}
}
}
if (@stale) {
print " Stale tracking branches in remotes/$name (you'd better remove them)\n";
print " @stale\n";
}
if (@tracked) {
print " Tracked remote branches\n";
print " @tracked\n";
}
}
sub show_remote {
my ($name, $ls_remote) = @_;
if (!exists $remote->{$name}) {
print STDERR "No such remote $name\n";
return;
}
my $info = $remote->{$name};
update_ls_remote($ls_remote, $info);
print "* remote $name\n";
print " URL: $info->{'URL'}\n";
for my $branchname (sort keys %$branch) {
next if ($branch->{$branchname}{'REMOTE'} ne $name);
my @merged = map {
s|^refs/heads/||;
$_;
} split(' ',"@{$branch->{$branchname}{'MERGE'}}");
next unless (@merged);
print " Remote branch(es) merged with 'git pull' while on branch $branchname\n";
print " @merged\n";
}
if ($info->{'LS_REMOTE'}) {
show_mapping($name, $info);
}
}
sub add_remote {
my ($name, $url) = @_;
if (exists $remote->{$name}) {
print STDERR "remote $name already exists.\n";
exit(1);
}
$git->command('repo-config', "remote.$name.url", $url);
$git->command('repo-config', "remote.$name.fetch",
"+refs/heads/*:refs/remotes/$name/*");
}
if (!@ARGV) {
for (sort keys %$remote) {
print "$_\n";
}
}
elsif ($ARGV[0] eq 'show') {
my $ls_remote = 1;
my $i;
for ($i = 1; $i < @ARGV; $i++) {
if ($ARGV[$i] eq '-n') {
$ls_remote = 0;
}
else {
last;
}
}
if ($i >= @ARGV) {
print STDERR "Usage: git remote show <remote>\n";
exit(1);
}
for (; $i < @ARGV; $i++) {
show_remote($ARGV[$i], $ls_remote);
}
}
elsif ($ARGV[0] eq 'add') {
if (@ARGV != 3) {
print STDERR "Usage: git remote add <name> <url>\n";
exit(1);
}
add_remote($ARGV[1], $ARGV[2]);
}
else {
print STDERR "Usage: git remote\n";
print STDERR " git remote add <name> <url>\n";
print STDERR " git remote show <name>\n";
exit(1);
}

View File

@ -110,7 +110,7 @@ then
done done
) )
fi fi
git-prune-packed git-prune-packed $quiet
fi fi
case "$no_update_info" in case "$no_update_info" in

View File

@ -1,284 +0,0 @@
#!/usr/bin/perl
#
# REuse REcorded REsolve. This tool records a conflicted automerge
# result and its hand resolution, and helps to resolve future
# automerge that results in the same conflict.
#
# To enable this feature, create a directory 'rr-cache' under your
# .git/ directory.
use Digest;
use File::Path;
use File::Copy;
my $git_dir = $::ENV{GIT_DIR} || ".git";
my $rr_dir = "$git_dir/rr-cache";
my $merge_rr = "$git_dir/rr-cache/MERGE_RR";
my %merge_rr = ();
sub read_rr {
if (!-f $merge_rr) {
%merge_rr = ();
return;
}
my $in;
local $/ = "\0";
open $in, "<$merge_rr" or die "$!: $merge_rr";
while (<$in>) {
chomp;
my ($name, $path) = /^([0-9a-f]{40})\t(.*)$/s;
$merge_rr{$path} = $name;
}
close $in;
}
sub write_rr {
my $out;
open $out, ">$merge_rr" or die "$!: $merge_rr";
for my $path (sort keys %merge_rr) {
my $name = $merge_rr{$path};
print $out "$name\t$path\0";
}
close $out;
}
sub compute_conflict_name {
my ($path) = @_;
my @side = ();
my $in;
open $in, "<$path" or die "$!: $path";
my $sha1 = Digest->new("SHA-1");
my $hunk = 0;
while (<$in>) {
if (/^<<<<<<< .*/) {
$hunk++;
@side = ([], undef);
}
elsif (/^=======$/) {
$side[1] = [];
}
elsif (/^>>>>>>> .*/) {
my ($one, $two);
$one = join('', @{$side[0]});
$two = join('', @{$side[1]});
if ($two le $one) {
($one, $two) = ($two, $one);
}
$sha1->add($one);
$sha1->add("\0");
$sha1->add($two);
$sha1->add("\0");
@side = ();
}
elsif (@side == 0) {
next;
}
elsif (defined $side[1]) {
push @{$side[1]}, $_;
}
else {
push @{$side[0]}, $_;
}
}
close $in;
return ($sha1->hexdigest, $hunk);
}
sub record_preimage {
my ($path, $name) = @_;
my @side = ();
my ($in, $out);
open $in, "<$path" or die "$!: $path";
open $out, ">$name" or die "$!: $name";
while (<$in>) {
if (/^<<<<<<< .*/) {
@side = ([], undef);
}
elsif (/^=======$/) {
$side[1] = [];
}
elsif (/^>>>>>>> .*/) {
my ($one, $two);
$one = join('', @{$side[0]});
$two = join('', @{$side[1]});
if ($two le $one) {
($one, $two) = ($two, $one);
}
print $out "<<<<<<<\n";
print $out $one;
print $out "=======\n";
print $out $two;
print $out ">>>>>>>\n";
@side = ();
}
elsif (@side == 0) {
print $out $_;
}
elsif (defined $side[1]) {
push @{$side[1]}, $_;
}
else {
push @{$side[0]}, $_;
}
}
close $out;
close $in;
}
sub find_conflict {
my $in;
local $/ = "\0";
my $pid = open($in, '-|');
die "$!" unless defined $pid;
if (!$pid) {
exec(qw(git ls-files -z -u)) or die "$!: ls-files";
}
my %path = ();
my @path = ();
while (<$in>) {
chomp;
my ($mode, $sha1, $stage, $path) =
/^([0-7]+) ([0-9a-f]{40}) ([123])\t(.*)$/s;
$path{$path} |= (1 << $stage);
}
close $in;
while (my ($path, $status) = each %path) {
if ($status == 14) { push @path, $path; }
}
return @path;
}
sub merge {
my ($name, $path) = @_;
record_preimage($path, "$rr_dir/$name/thisimage");
unless (system('git', 'merge-file', map { "$rr_dir/$name/${_}image" }
qw(this pre post))) {
my $in;
open $in, "<$rr_dir/$name/thisimage" or
die "$!: $name/thisimage";
my $out;
open $out, ">$path" or die "$!: $path";
while (<$in>) { print $out $_; }
close $in;
close $out;
return 1;
}
return 0;
}
sub garbage_collect_rerere {
# We should allow specifying these from the command line and
# that is why the caller gives @ARGV to us, but I am lazy.
my $cutoff_noresolve = 15; # two weeks
my $cutoff_resolve = 60; # two months
my @to_remove;
while (<$rr_dir/*/preimage>) {
my ($dir) = /^(.*)\/preimage$/;
my $cutoff = ((-f "$dir/postimage")
? $cutoff_resolve
: $cutoff_noresolve);
my $age = -M "$_";
if ($cutoff <= $age) {
push @to_remove, $dir;
}
}
if (@to_remove) {
rmtree(\@to_remove);
}
}
-d "$rr_dir" || exit(0);
read_rr();
if (@ARGV) {
my $arg = shift @ARGV;
if ($arg eq 'clear') {
for my $path (keys %merge_rr) {
my $name = $merge_rr{$path};
if (-d "$rr_dir/$name" &&
! -f "$rr_dir/$name/postimage") {
rmtree(["$rr_dir/$name"]);
}
}
unlink $merge_rr;
}
elsif ($arg eq 'status') {
for my $path (keys %merge_rr) {
print $path, "\n";
}
}
elsif ($arg eq 'diff') {
for my $path (keys %merge_rr) {
my $name = $merge_rr{$path};
system('diff', ((@ARGV == 0) ? ('-u') : @ARGV),
'-L', "a/$path", '-L', "b/$path",
"$rr_dir/$name/preimage", $path);
}
}
elsif ($arg eq 'gc') {
garbage_collect_rerere(@ARGV);
}
else {
die "$0 unknown command: $arg\n";
}
exit 0;
}
my %conflict = map { $_ => 1 } find_conflict();
# MERGE_RR records paths with conflicts immediately after merge
# failed. Some of the conflicted paths might have been hand resolved
# in the working tree since then, but the initial run would catch all
# and register their preimages.
for my $path (keys %conflict) {
# This path has conflict. If it is not recorded yet,
# record the pre-image.
if (!exists $merge_rr{$path}) {
my ($name, $hunk) = compute_conflict_name($path);
next unless ($hunk);
$merge_rr{$path} = $name;
if (! -d "$rr_dir/$name") {
mkpath("$rr_dir/$name", 0, 0777);
print STDERR "Recorded preimage for '$path'\n";
record_preimage($path, "$rr_dir/$name/preimage");
}
}
}
# Now some of the paths that had conflicts earlier might have been
# hand resolved. Others may be similar to a conflict already that
# was resolved before.
for my $path (keys %merge_rr) {
my $name = $merge_rr{$path};
# We could resolve this automatically if we have images.
if (-f "$rr_dir/$name/preimage" &&
-f "$rr_dir/$name/postimage") {
if (merge($name, $path)) {
print STDERR "Resolved '$path' using previous resolution.\n";
# Then we do not have to worry about this path
# anymore.
delete $merge_rr{$path};
next;
}
}
# Let's see if we have resolved it.
(undef, my $hunk) = compute_conflict_name($path);
next if ($hunk);
print STDERR "Recorded resolution for '$path'.\n";
copy($path, "$rr_dir/$name/postimage");
# And we do not have to worry about this path anymore.
delete $merge_rr{$path};
}
# Write out the rest.
write_rr();

View File

@ -6,6 +6,7 @@ USAGE='[--mixed | --soft | --hard] [<commit-ish>] [ [--] <paths>...]'
SUBDIRECTORY_OK=Yes SUBDIRECTORY_OK=Yes
. git-sh-setup . git-sh-setup
set_reflog_action "reset $*" set_reflog_action "reset $*"
require_work_tree
update= reset_type=--mixed update= reset_type=--mixed
unset rev unset rev
@ -44,17 +45,15 @@ if test $# != 0
then then
test "$reset_type" == "--mixed" || test "$reset_type" == "--mixed" ||
die "Cannot do partial $reset_type reset." die "Cannot do partial $reset_type reset."
git ls-tree -r --full-name $rev -- "$@" |
git update-index --add --index-info || exit git-diff-index --cached $rev -- "$@" |
sed -e 's/^:\([0-7][0-7]*\) [0-7][0-7]* \([0-9a-f][0-9a-f]*\) [0-9a-f][0-9a-f]* [A-Z] \(.*\)$/\1 \2 \3/' |
git update-index --add --remove --index-info || exit
git update-index --refresh git update-index --refresh
exit exit
fi fi
TOP=$(git-rev-parse --show-cdup) cd_to_toplevel
if test ! -z "$TOP"
then
cd "$TOP"
fi
if test "$reset_type" = "--hard" if test "$reset_type" = "--hard"
then then

View File

@ -16,9 +16,14 @@ case "$0" in
me=cherry-pick me=cherry-pick
USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;; USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;;
* ) * )
die "What are you talking about?" ;; echo >&2 "What are you talking about?"
exit 1 ;;
esac esac
SUBDIRECTORY_OK=Yes ;# we will cd up
. git-sh-setup . git-sh-setup
require_work_tree
cd_to_toplevel
no_commit= no_commit=
while case "$#" in 0) break ;; esac while case "$#" in 0) break ;; esac
@ -76,6 +81,8 @@ prev=$(git-rev-parse --verify "$commit^1" 2>/dev/null) ||
git-rev-parse --verify "$commit^2" >/dev/null 2>&1 && git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
die "Cannot run $me a multi-parent commit." die "Cannot run $me a multi-parent commit."
encoding=$(git repo-config i18n.commitencoding || echo UTF-8)
# "commit" is an existing commit. We would want to apply # "commit" is an existing commit. We would want to apply
# the difference it introduces since its first parent "prev" # the difference it introduces since its first parent "prev"
# on top of the current HEAD if we are cherry-pick. Or the # on top of the current HEAD if we are cherry-pick. Or the
@ -83,10 +90,11 @@ git-rev-parse --verify "$commit^2" >/dev/null 2>&1 &&
case "$me" in case "$me" in
revert) revert)
git-rev-list --pretty=oneline --max-count=1 $commit | git show -s --pretty=oneline --encoding="$encoding" $commit |
sed -e ' sed -e '
s/^[^ ]* /Revert "/ s/^[^ ]* /Revert "/
s/$/"/' s/$/"/
'
echo echo
echo "This reverts commit $commit." echo "This reverts commit $commit."
test "$rev" = "$commit" || test "$rev" = "$commit" ||
@ -115,14 +123,17 @@ cherry-pick)
q q
}' }'
set_author_env=`git-cat-file commit "$commit" |
logmsg=`git show -s --pretty=raw --encoding="$encoding" "$commit"`
set_author_env=`echo "$logmsg" |
LANG=C LC_ALL=C sed -ne "$pick_author_script"` LANG=C LC_ALL=C sed -ne "$pick_author_script"`
eval "$set_author_env" eval "$set_author_env"
export GIT_AUTHOR_NAME export GIT_AUTHOR_NAME
export GIT_AUTHOR_EMAIL export GIT_AUTHOR_EMAIL
export GIT_AUTHOR_DATE export GIT_AUTHOR_DATE
git-cat-file commit $commit | sed -e '1,/^$/d' echo "$logmsg" |
sed -e '1,/^$/d' -e 's/^ //'
case "$replay" in case "$replay" in
'') '')
echo "(cherry picked from commit $commit)" echo "(cherry picked from commit $commit)"

View File

@ -402,6 +402,15 @@ sub make_message_id
$cc = ""; $cc = "";
$time = time - scalar $#files; $time = time - scalar $#files;
sub unquote_rfc2047 {
local ($_) = @_;
if (s/=\?utf-8\?q\?(.*)\?=/$1/g) {
s/_/ /g;
s/=([0-9A-F]{2})/chr(hex($1))/eg;
}
return "$_ - unquoted";
}
sub send_message sub send_message
{ {
my @recipients = unique_email_list(@to); my @recipients = unique_email_list(@to);
@ -555,6 +564,7 @@ foreach my $t (@files) {
} }
close F; close F;
if (defined $author_not_sender) { if (defined $author_not_sender) {
$author_not_sender = unquote_rfc2047($author_not_sender);
$message = "From: $author_not_sender\n\n$message"; $message = "From: $author_not_sender\n\n$message";
} }

View File

@ -28,6 +28,30 @@ set_reflog_action() {
fi fi
} }
is_bare_repository () {
git-repo-config --bool --get core.bare ||
case "$GIT_DIR" in
.git | */.git) echo false ;;
*) echo true ;;
esac
}
cd_to_toplevel () {
cdup=$(git-rev-parse --show-cdup)
if test ! -z "$cdup"
then
cd "$cdup" || {
echo >&2 "Cannot chdir to $cdup, the toplevel of the working tree"
exit 1
}
fi
}
require_work_tree () {
test $(is_bare_repository) = false ||
die "fatal: $0 cannot be used without a working tree."
}
if [ -z "$LONG_USAGE" ] if [ -z "$LONG_USAGE" ]
then then
LONG_USAGE="Usage: $0 $USAGE" LONG_USAGE="Usage: $0 $USAGE"
@ -47,7 +71,11 @@ esac
if [ -z "$SUBDIRECTORY_OK" ] if [ -z "$SUBDIRECTORY_OK" ]
then then
: ${GIT_DIR=.git} : ${GIT_DIR=.git}
GIT_DIR=$(GIT_DIR="$GIT_DIR" git-rev-parse --git-dir) || exit GIT_DIR=$(GIT_DIR="$GIT_DIR" git-rev-parse --git-dir) || {
exit=$?
echo >&2 "You need to run this command from the toplevel of the working tree."
exit $exit
}
else else
GIT_DIR=$(git-rev-parse --git-dir) || exit GIT_DIR=$(git-rev-parse --git-dir) || exit
fi fi

View File

@ -70,7 +70,7 @@ my ($_revision,$_stdin,$_no_ignore_ext,$_no_stop_copy,$_help,$_rmdir,$_edit,
$_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m, $_version, $_upgrade, $_authors, $_branch_all_refs, @_opt_m,
$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive, $_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
$_username, $_config_dir, $_no_auth_cache, $_username, $_config_dir, $_no_auth_cache,
$_pager, $_color); $_pager, $_color, $_prefix);
my (@_branch_from, %tree_map, %users, %rusers, %equiv); my (@_branch_from, %tree_map, %users, %rusers, %equiv);
my ($_svn_can_do_switch); my ($_svn_can_do_switch);
my @repo_path_split_cache; my @repo_path_split_cache;
@ -134,6 +134,7 @@ my %cmd = (
'username=s' => \$_username, 'username=s' => \$_username,
'config-dir=s' => \$_config_dir, 'config-dir=s' => \$_config_dir,
'no-auth-cache' => \$_no_auth_cache, 'no-auth-cache' => \$_no_auth_cache,
'prefix=s' => \$_prefix,
} ], } ],
'multi-fetch' => [ \&multi_fetch, 'multi-fetch' => [ \&multi_fetch,
'Fetch multiple trees (like git-svnimport)', 'Fetch multiple trees (like git-svnimport)',
@ -285,7 +286,7 @@ sub init {
$SVN_URL = $url; $SVN_URL = $url;
unless (-d $GIT_DIR) { unless (-d $GIT_DIR) {
my @init_db = ('init-db'); my @init_db = ('init');
push @init_db, "--template=$_template" if defined $_template; push @init_db, "--template=$_template" if defined $_template;
push @init_db, "--shared" if defined $_shared; push @init_db, "--shared" if defined $_shared;
command_noisy(@init_db); command_noisy(@init_db);
@ -595,8 +596,9 @@ sub multi_init {
command_noisy('repo-config', 'svn.trunk', $trunk_url); command_noisy('repo-config', 'svn.trunk', $trunk_url);
} }
} }
complete_url_ls_init($url, $_branches, '--branches/-b', ''); $_prefix = '' unless defined $_prefix;
complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/'); complete_url_ls_init($url, $_branches, '--branches/-b', $_prefix);
complete_url_ls_init($url, $_tags, '--tags/-t', $_prefix . 'tags/');
} }
sub multi_fetch { sub multi_fetch {
@ -1084,7 +1086,7 @@ sub graft_merge_msg {
my ($grafts, $l_map, $u, $p, @re) = @_; my ($grafts, $l_map, $u, $p, @re) = @_;
my $x = $l_map->{$u}->{$p}; my $x = $l_map->{$u}->{$p};
my $rl = rev_list_raw($x); my $rl = rev_list_raw("refs/remotes/$x");
while (my $c = next_rev_list_entry($rl)) { while (my $c = next_rev_list_entry($rl)) {
foreach my $re (@re) { foreach my $re (@re) {
my (@br) = ($c->{m} =~ /$re/g); my (@br) = ($c->{m} =~ /$re/g);

View File

@ -285,7 +285,7 @@ my $last_rev = "";
my $last_branch; my $last_branch;
my $current_rev = $opt_s || 1; my $current_rev = $opt_s || 1;
unless(-d $git_dir) { unless(-d $git_dir) {
system("git-init-db"); system("git-init");
die "Cannot init the GIT db at $git_tree: $?\n" if $?; die "Cannot init the GIT db at $git_tree: $?\n" if $?;
system("git-read-tree"); system("git-read-tree");
die "Cannot init an empty tree: $?\n" if $?; die "Cannot init an empty tree: $?\n" if $?;
@ -943,10 +943,10 @@ if ($opt_l < $current_rev) {
print "Processing from $current_rev to $opt_l ...\n" if $opt_v; print "Processing from $current_rev to $opt_l ...\n" if $opt_v;
my $from_rev; my $from_rev;
my $to_rev = $current_rev; my $to_rev = $current_rev - 1;
while ($to_rev < $opt_l) { while ($to_rev < $opt_l) {
$from_rev = $to_rev; $from_rev = $to_rev + 1;
$to_rev = $from_rev + $repack_after; $to_rev = $from_rev + $repack_after;
$to_rev = $opt_l if $opt_l < $to_rev; $to_rev = $opt_l if $opt_l < $to_rev;
print "Fetching from $from_rev to $to_rev ...\n" if $opt_v; print "Fetching from $from_rev to $to_rev ...\n" if $opt_v;

17
git.c
View File

@ -199,6 +199,11 @@ const char git_version_string[] = GIT_VERSION;
#define RUN_SETUP (1<<0) #define RUN_SETUP (1<<0)
#define USE_PAGER (1<<1) #define USE_PAGER (1<<1)
/*
* require working tree to be present -- anything uses this needs
* RUN_SETUP for reading from the configuration file.
*/
#define NOT_BARE (1<<2)
static void handle_internal_command(int argc, const char **argv, char **envp) static void handle_internal_command(int argc, const char **argv, char **envp)
{ {
@ -208,7 +213,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
int (*fn)(int, const char **, const char *); int (*fn)(int, const char **, const char *);
int option; int option;
} commands[] = { } commands[] = {
{ "add", cmd_add, RUN_SETUP }, { "add", cmd_add, RUN_SETUP | NOT_BARE },
{ "annotate", cmd_annotate, }, { "annotate", cmd_annotate, },
{ "apply", cmd_apply }, { "apply", cmd_apply },
{ "archive", cmd_archive }, { "archive", cmd_archive },
@ -220,6 +225,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "cherry", cmd_cherry, RUN_SETUP }, { "cherry", cmd_cherry, RUN_SETUP },
{ "commit-tree", cmd_commit_tree, RUN_SETUP }, { "commit-tree", cmd_commit_tree, RUN_SETUP },
{ "count-objects", cmd_count_objects, RUN_SETUP }, { "count-objects", cmd_count_objects, RUN_SETUP },
{ "describe", cmd_describe, RUN_SETUP },
{ "diff", cmd_diff, RUN_SETUP | USE_PAGER }, { "diff", cmd_diff, RUN_SETUP | USE_PAGER },
{ "diff-files", cmd_diff_files, RUN_SETUP }, { "diff-files", cmd_diff_files, RUN_SETUP },
{ "diff-index", cmd_diff_index, RUN_SETUP }, { "diff-index", cmd_diff_index, RUN_SETUP },
@ -231,6 +237,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "get-tar-commit-id", cmd_get_tar_commit_id }, { "get-tar-commit-id", cmd_get_tar_commit_id },
{ "grep", cmd_grep, RUN_SETUP }, { "grep", cmd_grep, RUN_SETUP },
{ "help", cmd_help }, { "help", cmd_help },
{ "init", cmd_init_db },
{ "init-db", cmd_init_db }, { "init-db", cmd_init_db },
{ "log", cmd_log, RUN_SETUP | USE_PAGER }, { "log", cmd_log, RUN_SETUP | USE_PAGER },
{ "ls-files", cmd_ls_files, RUN_SETUP }, { "ls-files", cmd_ls_files, RUN_SETUP },
@ -238,7 +245,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "mailinfo", cmd_mailinfo }, { "mailinfo", cmd_mailinfo },
{ "mailsplit", cmd_mailsplit }, { "mailsplit", cmd_mailsplit },
{ "merge-file", cmd_merge_file }, { "merge-file", cmd_merge_file },
{ "mv", cmd_mv, RUN_SETUP }, { "mv", cmd_mv, RUN_SETUP | NOT_BARE },
{ "name-rev", cmd_name_rev, RUN_SETUP }, { "name-rev", cmd_name_rev, RUN_SETUP },
{ "pack-objects", cmd_pack_objects, RUN_SETUP }, { "pack-objects", cmd_pack_objects, RUN_SETUP },
{ "pickaxe", cmd_blame, RUN_SETUP | USE_PAGER }, { "pickaxe", cmd_blame, RUN_SETUP | USE_PAGER },
@ -251,8 +258,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "rerere", cmd_rerere, RUN_SETUP }, { "rerere", cmd_rerere, RUN_SETUP },
{ "rev-list", cmd_rev_list, RUN_SETUP }, { "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse, RUN_SETUP }, { "rev-parse", cmd_rev_parse, RUN_SETUP },
{ "rm", cmd_rm, RUN_SETUP }, { "rm", cmd_rm, RUN_SETUP | NOT_BARE },
{ "runstatus", cmd_runstatus, RUN_SETUP }, { "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER }, { "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP }, { "show-branch", cmd_show_branch, RUN_SETUP },
{ "show", cmd_show, RUN_SETUP | USE_PAGER }, { "show", cmd_show, RUN_SETUP | USE_PAGER },
@ -289,6 +296,8 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
prefix = setup_git_directory(); prefix = setup_git_directory();
if (p->option & USE_PAGER) if (p->option & USE_PAGER)
setup_pager(); setup_pager();
if ((p->option & NOT_BARE) && is_bare_repository())
die("%s cannot be used in a bare git directory", cmd);
trace_argv_printf(argv, argc, "trace: built-in: git"); trace_argv_printf(argv, argc, "trace: built-in: git");
exit(p->fn(argc, argv, prefix)); exit(p->fn(argc, argv, prefix));

View File

@ -2239,7 +2239,7 @@ sub git_difftree_body {
} }
print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'}, print $cgi->a({-href => href(action=>"blob", hash=>$diff{'to_id'},
hash_base=>$hash, file_name=>$diff{'file'})}, hash_base=>$hash, file_name=>$diff{'file'})},
"blob") . " | "; "blob");
print "</td>\n"; print "</td>\n";
} elsif ($diff{'status'} eq "D") { # deleted } elsif ($diff{'status'} eq "D") { # deleted
@ -2412,7 +2412,6 @@ sub git_patchset_body {
push @diff_header, $patch_line; push @diff_header, $patch_line;
} }
#last PATCH unless $patch_line;
my $last_patch_line = $patch_line; my $last_patch_line = $patch_line;
# check if current patch belong to current raw line # check if current patch belong to current raw line
@ -2522,7 +2521,10 @@ sub git_patchset_body {
# from-file/to-file diff header # from-file/to-file diff header
$patch_line = $last_patch_line; $patch_line = $last_patch_line;
last PATCH unless $patch_line; if (! $patch_line) {
print "</div>\n"; # class="patch"
last PATCH;
}
next PATCH if ($patch_line =~ m/^diff /); next PATCH if ($patch_line =~ m/^diff /);
#assert($patch_line =~ m/^---/) if DEBUG; #assert($patch_line =~ m/^---/) if DEBUG;
if ($from{'href'} && $patch_line =~ m!^--- "?a/!) { if ($from{'href'} && $patch_line =~ m!^--- "?a/!) {
@ -2533,7 +2535,6 @@ sub git_patchset_body {
print "<div class=\"diff from_file\">$patch_line</div>\n"; print "<div class=\"diff from_file\">$patch_line</div>\n";
$patch_line = <$fd>; $patch_line = <$fd>;
#last PATCH unless $patch_line;
chomp $patch_line; chomp $patch_line;
#assert($patch_line =~ m/^+++/) if DEBUG; #assert($patch_line =~ m/^+++/) if DEBUG;

View File

@ -71,7 +71,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
int posn = 0; int posn = 0;
struct object_request *obj_req = (struct object_request *)data; struct object_request *obj_req = (struct object_request *)data;
do { do {
ssize_t retval = write(obj_req->local, ssize_t retval = xwrite(obj_req->local,
(char *) ptr + posn, size - posn); (char *) ptr + posn, size - posn);
if (retval < 0) if (retval < 0)
return posn; return posn;
@ -175,7 +175,7 @@ static void start_object_request(struct object_request *obj_req)
prevlocal = open(prevfile, O_RDONLY); prevlocal = open(prevfile, O_RDONLY);
if (prevlocal != -1) { if (prevlocal != -1) {
do { do {
prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE); prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
if (prev_read>0) { if (prev_read>0) {
if (fwrite_sha1_file(prev_buf, if (fwrite_sha1_file(prev_buf,
1, 1,
@ -809,6 +809,7 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
return error("Unable to start request"); return error("Unable to start request");
} }
target->pack_size = ftell(packfile);
fclose(packfile); fclose(packfile);
ret = move_temp_to_file(tmpfile, filename); ret = move_temp_to_file(tmpfile, filename);

View File

@ -195,7 +195,7 @@ static size_t fwrite_sha1_file(void *ptr, size_t eltsize, size_t nmemb,
int posn = 0; int posn = 0;
struct transfer_request *request = (struct transfer_request *)data; struct transfer_request *request = (struct transfer_request *)data;
do { do {
ssize_t retval = write(request->local_fileno, ssize_t retval = xwrite(request->local_fileno,
(char *) ptr + posn, size - posn); (char *) ptr + posn, size - posn);
if (retval < 0) if (retval < 0)
return posn; return posn;
@ -288,7 +288,7 @@ static void start_fetch_loose(struct transfer_request *request)
prevlocal = open(prevfile, O_RDONLY); prevlocal = open(prevfile, O_RDONLY);
if (prevlocal != -1) { if (prevlocal != -1) {
do { do {
prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE); prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
if (prev_read>0) { if (prev_read>0) {
if (fwrite_sha1_file(prev_buf, if (fwrite_sha1_file(prev_buf,
1, 1,
@ -770,11 +770,14 @@ static void finish_request(struct transfer_request *request)
request->url, curl_errorstr); request->url, curl_errorstr);
remote->can_update_info_refs = 0; remote->can_update_info_refs = 0;
} else { } else {
off_t pack_size = ftell(request->local_stream);
fclose(request->local_stream); fclose(request->local_stream);
request->local_stream = NULL; request->local_stream = NULL;
if (!move_temp_to_file(request->tmpfile, if (!move_temp_to_file(request->tmpfile,
request->filename)) { request->filename)) {
target = (struct packed_git *)request->userData; target = (struct packed_git *)request->userData;
target->pack_size = pack_size;
lst = &remote->packs; lst = &remote->packs;
while (*lst != target) while (*lst != target)
lst = &((*lst)->next); lst = &((*lst)->next);

View File

@ -224,7 +224,7 @@ socket_perror( const char *func, Socket_t *sock, int ret )
static int static int
socket_read( Socket_t *sock, char *buf, int len ) socket_read( Socket_t *sock, char *buf, int len )
{ {
int n = read( sock->fd, buf, len ); int n = xread( sock->fd, buf, len );
if (n <= 0) { if (n <= 0) {
socket_perror( "read", sock, n ); socket_perror( "read", sock, n );
close( sock->fd ); close( sock->fd );
@ -236,7 +236,7 @@ socket_read( Socket_t *sock, char *buf, int len )
static int static int
socket_write( Socket_t *sock, const char *buf, int len ) socket_write( Socket_t *sock, const char *buf, int len )
{ {
int n = write( sock->fd, buf, len ); int n = write_in_full( sock->fd, buf, len );
if (n != len) { if (n != len) {
socket_perror( "write", sock, n ); socket_perror( "write", sock, n );
close( sock->fd ); close( sock->fd );
@ -390,7 +390,7 @@ arc4_init( void )
fprintf( stderr, "Fatal: no random number source available.\n" ); fprintf( stderr, "Fatal: no random number source available.\n" );
exit( 3 ); exit( 3 );
} }
if (read( fd, dat, 128 ) != 128) { if (read_in_full( fd, dat, 128 ) != 128) {
fprintf( stderr, "Fatal: cannot read random number source.\n" ); fprintf( stderr, "Fatal: cannot read random number source.\n" );
exit( 3 ); exit( 3 );
} }

View File

@ -638,7 +638,7 @@ static void readjust_pack_header_and_sha1(unsigned char *sha1)
/* Rewrite pack header with updated object number */ /* Rewrite pack header with updated object number */
if (lseek(output_fd, 0, SEEK_SET) != 0) if (lseek(output_fd, 0, SEEK_SET) != 0)
die("cannot seek back: %s", strerror(errno)); die("cannot seek back: %s", strerror(errno));
if (xread(output_fd, &hdr, sizeof(hdr)) != sizeof(hdr)) if (read_in_full(output_fd, &hdr, sizeof(hdr)) != sizeof(hdr))
die("cannot read pack header back: %s", strerror(errno)); die("cannot read pack header back: %s", strerror(errno));
hdr.hdr_entries = htonl(nr_objects); hdr.hdr_entries = htonl(nr_objects);
if (lseek(output_fd, 0, SEEK_SET) != 0) if (lseek(output_fd, 0, SEEK_SET) != 0)
@ -814,7 +814,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
char buf[48]; char buf[48];
int len = snprintf(buf, sizeof(buf), "%s\t%s\n", int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
report, sha1_to_hex(sha1)); report, sha1_to_hex(sha1));
xwrite(1, buf, len); write_or_die(1, buf, len);
/* /*
* Let's just mimic git-unpack-objects here and write * Let's just mimic git-unpack-objects here and write

View File

@ -184,7 +184,7 @@ int fetch_ref(char *ref, unsigned char *sha1)
fprintf(stderr, "cannot open %s\n", filename); fprintf(stderr, "cannot open %s\n", filename);
return -1; return -1;
} }
if (read(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) { if (read_in_full(ifd, hex, 40) != 40 || get_sha1_hex(hex, sha1)) {
close(ifd); close(ifd);
fprintf(stderr, "cannot read from %s\n", filename); fprintf(stderr, "cannot read from %s\n", filename);
return -1; return -1;

View File

@ -110,35 +110,6 @@ static void output_commit_title(struct commit *commit)
} }
} }
static const char *current_index_file = NULL;
static const char *original_index_file;
static const char *temporary_index_file;
static int cache_dirty = 0;
static int flush_cache(void)
{
/* flush temporary index */
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int fd = hold_lock_file_for_update(lock, current_index_file, 1);
if (write_cache(fd, active_cache, active_nr) ||
close(fd) || commit_lock_file(lock))
die ("unable to write %s", current_index_file);
discard_cache();
cache_dirty = 0;
return 0;
}
static void setup_index(int temp)
{
current_index_file = temp ? temporary_index_file: original_index_file;
if (cache_dirty) {
discard_cache();
cache_dirty = 0;
}
unlink(temporary_index_file);
discard_cache();
}
static struct cache_entry *make_cache_entry(unsigned int mode, static struct cache_entry *make_cache_entry(unsigned int mode,
const unsigned char *sha1, const char *path, int stage, int refresh) const unsigned char *sha1, const char *path, int stage, int refresh)
{ {
@ -167,9 +138,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
const char *path, int stage, int refresh, int options) const char *path, int stage, int refresh, int options)
{ {
struct cache_entry *ce; struct cache_entry *ce;
if (!cache_dirty)
read_cache_from(current_index_file);
cache_dirty++;
ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh); ce = make_cache_entry(mode, sha1 ? sha1 : null_sha1, path, stage, refresh);
if (!ce) if (!ce)
return error("cache_addinfo failed: %s", strerror(cache_errno)); return error("cache_addinfo failed: %s", strerror(cache_errno));
@ -187,26 +155,6 @@ static int add_cacheinfo(unsigned int mode, const unsigned char *sha1,
*/ */
static int index_only = 0; static int index_only = 0;
static int git_read_tree(struct tree *tree)
{
int rc;
struct object_list *trees = NULL;
struct unpack_trees_options opts;
if (cache_dirty)
die("read-tree with dirty cache");
memset(&opts, 0, sizeof(opts));
object_list_append(&tree->object, &trees);
rc = unpack_trees(trees, &opts);
cache_tree_free(&active_cache_tree);
if (rc == 0)
cache_dirty = 1;
return rc;
}
static int git_merge_trees(int index_only, static int git_merge_trees(int index_only,
struct tree *common, struct tree *common,
struct tree *head, struct tree *head,
@ -216,11 +164,6 @@ static int git_merge_trees(int index_only,
struct object_list *trees = NULL; struct object_list *trees = NULL;
struct unpack_trees_options opts; struct unpack_trees_options opts;
if (!cache_dirty) {
read_cache_from(current_index_file);
cache_dirty = 1;
}
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
if (index_only) if (index_only)
opts.index_only = 1; opts.index_only = 1;
@ -236,25 +179,26 @@ static int git_merge_trees(int index_only,
rc = unpack_trees(trees, &opts); rc = unpack_trees(trees, &opts);
cache_tree_free(&active_cache_tree); cache_tree_free(&active_cache_tree);
cache_dirty = 1;
return rc; return rc;
} }
static int unmerged_index(void)
{
int i;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce))
return 1;
}
return 0;
}
static struct tree *git_write_tree(void) static struct tree *git_write_tree(void)
{ {
struct tree *result = NULL; struct tree *result = NULL;
if (cache_dirty) { if (unmerged_index())
unsigned i;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce))
return NULL; return NULL;
}
} else
read_cache_from(current_index_file);
if (!active_cache_tree) if (!active_cache_tree)
active_cache_tree = cache_tree(); active_cache_tree = cache_tree();
@ -266,9 +210,6 @@ static struct tree *git_write_tree(void)
result = lookup_tree(active_cache_tree->sha1); result = lookup_tree(active_cache_tree->sha1);
flush_cache();
cache_dirty = 0;
return result; return result;
} }
@ -331,10 +272,7 @@ static struct path_list *get_unmerged(void)
int i; int i;
unmerged->strdup_paths = 1; unmerged->strdup_paths = 1;
if (!cache_dirty) {
read_cache_from(current_index_file);
cache_dirty++;
}
for (i = 0; i < active_nr; i++) { for (i = 0; i < active_nr; i++) {
struct path_list_item *item; struct path_list_item *item;
struct stage_data *e; struct stage_data *e;
@ -469,9 +407,6 @@ static int remove_file(int clean, const char *path, int no_wd)
int update_working_directory = !index_only && !no_wd; int update_working_directory = !index_only && !no_wd;
if (update_cache) { if (update_cache) {
if (!cache_dirty)
read_cache_from(current_index_file);
cache_dirty++;
if (remove_file_from_cache(path)) if (remove_file_from_cache(path))
return -1; return -1;
} }
@ -517,7 +452,7 @@ static int mkdir_p(const char *path, unsigned long mode)
static void flush_buffer(int fd, const char *buf, unsigned long size) static void flush_buffer(int fd, const char *buf, unsigned long size)
{ {
while (size > 0) { while (size > 0) {
long ret = xwrite(fd, buf, size); long ret = write_in_full(fd, buf, size);
if (ret < 0) { if (ret < 0) {
/* Ignore epipe */ /* Ignore epipe */
if (errno == EPIPE) if (errno == EPIPE)
@ -954,8 +889,6 @@ static int process_renames(struct path_list *a_renames,
path_list_clear(&a_by_dst, 0); path_list_clear(&a_by_dst, 0);
path_list_clear(&b_by_dst, 0); path_list_clear(&b_by_dst, 0);
if (cache_dirty)
flush_cache();
return clean_merge; return clean_merge;
} }
@ -1083,9 +1016,6 @@ static int process_entry(const char *path, struct stage_data *entry,
} else } else
die("Fatal merge failure, shouldn't happen."); die("Fatal merge failure, shouldn't happen.");
if (cache_dirty)
flush_cache();
return clean_merge; return clean_merge;
} }
@ -1110,9 +1040,7 @@ static int merge_trees(struct tree *head,
sha1_to_hex(head->object.sha1), sha1_to_hex(head->object.sha1),
sha1_to_hex(merge->object.sha1)); sha1_to_hex(merge->object.sha1));
*result = git_write_tree(); if (unmerged_index()) {
if (!*result) {
struct path_list *entries, *re_head, *re_merge; struct path_list *entries, *re_head, *re_merge;
int i; int i;
path_list_clear(&current_file_set, 1); path_list_clear(&current_file_set, 1);
@ -1138,17 +1066,12 @@ static int merge_trees(struct tree *head,
path_list_clear(re_head, 0); path_list_clear(re_head, 0);
path_list_clear(entries, 1); path_list_clear(entries, 1);
if (clean || index_only)
*result = git_write_tree();
else
*result = NULL;
} else {
clean = 1;
printf("merging of trees %s and %s resulted in %s\n",
sha1_to_hex(head->object.sha1),
sha1_to_hex(merge->object.sha1),
sha1_to_hex((*result)->object.sha1));
} }
else
clean = 1;
if (index_only)
*result = git_write_tree();
return clean; return clean;
} }
@ -1173,10 +1096,10 @@ static int merge(struct commit *h1,
const char *branch1, const char *branch1,
const char *branch2, const char *branch2,
int call_depth /* =0 */, int call_depth /* =0 */,
struct commit *ancestor /* =None */, struct commit_list *ca,
struct commit **result) struct commit **result)
{ {
struct commit_list *ca = NULL, *iter; struct commit_list *iter;
struct commit *merged_common_ancestors; struct commit *merged_common_ancestors;
struct tree *mrtree; struct tree *mrtree;
int clean; int clean;
@ -1185,10 +1108,10 @@ static int merge(struct commit *h1,
output_commit_title(h1); output_commit_title(h1);
output_commit_title(h2); output_commit_title(h2);
if (ancestor) if (!ca) {
commit_list_insert(ancestor, &ca); ca = get_merge_bases(h1, h2, 1);
else ca = reverse_commit_list(ca);
ca = reverse_commit_list(get_merge_bases(h1, h2, 1)); }
output("found %u common ancestor(s):", commit_list_count(ca)); output("found %u common ancestor(s):", commit_list_count(ca));
for (iter = ca; iter; iter = iter->next) for (iter = ca; iter; iter = iter->next)
@ -1214,6 +1137,7 @@ static int merge(struct commit *h1,
* merge_trees has always overwritten it: the commited * merge_trees has always overwritten it: the commited
* "conflicts" were already resolved. * "conflicts" were already resolved.
*/ */
discard_cache();
merge(merged_common_ancestors, iter->item, merge(merged_common_ancestors, iter->item,
"Temporary merge branch 1", "Temporary merge branch 1",
"Temporary merge branch 2", "Temporary merge branch 2",
@ -1226,25 +1150,21 @@ static int merge(struct commit *h1,
die("merge returned no commit"); die("merge returned no commit");
} }
discard_cache();
if (call_depth == 0) { if (call_depth == 0) {
setup_index(0 /* $GIT_DIR/index */); read_cache();
index_only = 0; index_only = 0;
} else { } else
setup_index(1 /* temporary index */);
git_read_tree(h1->tree);
index_only = 1; index_only = 1;
}
clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree, clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree,
branch1, branch2, &mrtree); branch1, branch2, &mrtree);
if (!ancestor && (clean || index_only)) { if (index_only) {
*result = make_virtual_commit(mrtree, "merged tree"); *result = make_virtual_commit(mrtree, "merged tree");
commit_list_insert(h1, &(*result)->parents); commit_list_insert(h1, &(*result)->parents);
commit_list_insert(h2, &(*result)->parents->next); commit_list_insert(h2, &(*result)->parents->next);
} else }
*result = NULL;
return clean; return clean;
} }
@ -1280,19 +1200,16 @@ static struct commit *get_ref(const char *ref)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
static const char *bases[2]; static const char *bases[20];
static unsigned bases_count = 0; static unsigned bases_count = 0;
int i, clean; int i, clean;
const char *branch1, *branch2; const char *branch1, *branch2;
struct commit *result, *h1, *h2; struct commit *result, *h1, *h2;
struct commit_list *ca = NULL;
struct lock_file *lock = xcalloc(1, sizeof(struct lock_file));
int index_fd;
git_config(git_default_config); /* core.filemode */ git_config(git_default_config); /* core.filemode */
original_index_file = getenv(INDEX_ENVIRONMENT);
if (!original_index_file)
original_index_file = xstrdup(git_path("index"));
temporary_index_file = xstrdup(git_path("mrg-rcrsv-tmp-idx"));
if (argc < 4) if (argc < 4)
die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]); die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
@ -1316,18 +1233,18 @@ int main(int argc, char *argv[])
branch2 = better_branch_name(branch2); branch2 = better_branch_name(branch2);
printf("Merging %s with %s\n", branch1, branch2); printf("Merging %s with %s\n", branch1, branch2);
if (bases_count == 1) { index_fd = hold_lock_file_for_update(lock, get_index_file(), 1);
struct commit *ancestor = get_ref(bases[0]);
clean = merge(h1, h2, branch1, branch2, 0, ancestor, &result);
} else
clean = merge(h1, h2, branch1, branch2, 0, NULL, &result);
if (cache_dirty) for (i = 0; i < bases_count; i++) {
flush_cache(); struct commit *ancestor = get_ref(bases[i]);
ca = commit_list_insert(ancestor, &ca);
}
clean = merge(h1, h2, branch1, branch2, 0, ca, &result);
if (active_cache_changed &&
(write_cache(index_fd, active_cache, active_nr) ||
close(index_fd) || commit_lock_file(lock)))
die ("unable to write %s", get_index_file());
return clean ? 0: 1; return clean ? 0: 1;
} }
/*
vim: sw=8 noet
*/

View File

@ -1,55 +1,45 @@
#include "cache.h" #include "cache.h"
#include "pack.h" #include "pack.h"
#define BATCH (1u<<20) static int verify_packfile(struct packed_git *p,
struct pack_window **w_curs)
static int verify_packfile(struct packed_git *p)
{ {
unsigned long index_size = p->index_size; unsigned long index_size = p->index_size;
void *index_base = p->index_base; void *index_base = p->index_base;
SHA_CTX ctx; SHA_CTX ctx;
unsigned char sha1[20]; unsigned char sha1[20];
struct pack_header *hdr; unsigned long offset = 0, pack_sig = p->pack_size - 20;
int nr_objects, err, i; int nr_objects, err, i;
unsigned char *packdata;
unsigned long datasize;
/* Header consistency check */ /* Note that the pack header checks are actually performed by
hdr = p->pack_base; * use_pack when it first opens the pack file. If anything
if (hdr->hdr_signature != htonl(PACK_SIGNATURE)) * goes wrong during those checks then the call will die out
return error("Packfile %s signature mismatch", p->pack_name); * immediately.
if (!pack_version_ok(hdr->hdr_version)) */
return error("Packfile version %d unsupported",
ntohl(hdr->hdr_version));
nr_objects = ntohl(hdr->hdr_entries);
if (num_packed_objects(p) != nr_objects)
return error("Packfile claims to have %d objects, "
"while idx size expects %d", nr_objects,
num_packed_objects(p));
/* Check integrity of pack data with its SHA-1 checksum */
SHA1_Init(&ctx); SHA1_Init(&ctx);
packdata = p->pack_base; while (offset < pack_sig) {
datasize = p->pack_size - 20; unsigned int remaining;
while (datasize) { unsigned char *in = use_pack(p, w_curs, offset, &remaining);
unsigned long batch = (datasize < BATCH) ? datasize : BATCH; offset += remaining;
SHA1_Update(&ctx, packdata, batch); if (offset > pack_sig)
datasize -= batch; remaining -= offset - pack_sig;
packdata += batch; SHA1_Update(&ctx, in, remaining);
} }
SHA1_Final(sha1, &ctx); SHA1_Final(sha1, &ctx);
if (hashcmp(sha1, use_pack(p, w_curs, pack_sig, NULL)))
if (hashcmp(sha1, (unsigned char *)(p->pack_base) + p->pack_size - 20))
return error("Packfile %s SHA1 mismatch with itself", return error("Packfile %s SHA1 mismatch with itself",
p->pack_name); p->pack_name);
if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40)) if (hashcmp(sha1, (unsigned char *)index_base + index_size - 40))
return error("Packfile %s SHA1 mismatch with idx", return error("Packfile %s SHA1 mismatch with idx",
p->pack_name); p->pack_name);
unuse_pack(w_curs);
/* Make sure everything reachable from idx is valid. Since we /* Make sure everything reachable from idx is valid. Since we
* have verified that nr_objects matches between idx and pack, * have verified that nr_objects matches between idx and pack,
* we do not do scan-streaming check on the pack file. * we do not do scan-streaming check on the pack file.
*/ */
nr_objects = num_packed_objects(p);
for (i = err = 0; i < nr_objects; i++) { for (i = err = 0; i < nr_objects; i++) {
unsigned char sha1[20]; unsigned char sha1[20];
void *data; void *data;
@ -61,7 +51,7 @@ static int verify_packfile(struct packed_git *p)
offset = find_pack_entry_one(sha1, p); offset = find_pack_entry_one(sha1, p);
if (!offset) if (!offset)
die("internal error pack-check find-pack-entry-one"); die("internal error pack-check find-pack-entry-one");
data = unpack_entry_gently(p, offset, type, &size); data = unpack_entry(p, offset, type, &size);
if (!data) { if (!data) {
err = error("cannot unpack %s from %s", err = error("cannot unpack %s from %s",
sha1_to_hex(sha1), p->pack_name); sha1_to_hex(sha1), p->pack_name);
@ -84,12 +74,10 @@ static int verify_packfile(struct packed_git *p)
static void show_pack_info(struct packed_git *p) static void show_pack_info(struct packed_git *p)
{ {
struct pack_header *hdr;
int nr_objects, i; int nr_objects, i;
unsigned int chain_histogram[MAX_CHAIN]; unsigned int chain_histogram[MAX_CHAIN];
hdr = p->pack_base; nr_objects = num_packed_objects(p);
nr_objects = ntohl(hdr->hdr_entries);
memset(chain_histogram, 0, sizeof(chain_histogram)); memset(chain_histogram, 0, sizeof(chain_histogram));
for (i = 0; i < nr_objects; i++) { for (i = 0; i < nr_objects; i++) {
@ -152,18 +140,16 @@ int verify_pack(struct packed_git *p, int verbose)
if (!ret) { if (!ret) {
/* Verify pack file */ /* Verify pack file */
use_packed_git(p); struct pack_window *w_curs = NULL;
ret = verify_packfile(p); ret = verify_packfile(p, &w_curs);
unuse_packed_git(p); unuse_pack(&w_curs);
} }
if (verbose) { if (verbose) {
if (ret) if (ret)
printf("%s: bad\n", p->pack_name); printf("%s: bad\n", p->pack_name);
else { else {
use_packed_git(p);
show_pack_info(p); show_pack_info(p);
unuse_packed_git(p);
printf("%s: ok\n", p->pack_name); printf("%s: ok\n", p->pack_name);
} }
} }

18
path.c
View File

@ -90,10 +90,11 @@ int git_mkstemp(char *path, size_t len, const char *template)
} }
int validate_symref(const char *path) int validate_headref(const char *path)
{ {
struct stat st; struct stat st;
char *buf, buffer[256]; char *buf, buffer[256];
unsigned char sha1[20];
int len, fd; int len, fd;
if (lstat(path, &st) < 0) if (lstat(path, &st) < 0)
@ -113,20 +114,29 @@ int validate_symref(const char *path)
fd = open(path, O_RDONLY); fd = open(path, O_RDONLY);
if (fd < 0) if (fd < 0)
return -1; return -1;
len = read(fd, buffer, sizeof(buffer)-1); len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd); close(fd);
/* /*
* Is it a symbolic ref? * Is it a symbolic ref?
*/ */
if (len < 4 || memcmp("ref:", buffer, 4)) if (len < 4)
return -1; return -1;
if (!memcmp("ref:", buffer, 4)) {
buf = buffer + 4; buf = buffer + 4;
len -= 4; len -= 4;
while (len && isspace(*buf)) while (len && isspace(*buf))
buf++, len--; buf++, len--;
if (len >= 5 && !memcmp("refs/", buf, 5)) if (len >= 5 && !memcmp("refs/", buf, 5))
return 0; return 0;
}
/*
* Is this a detached HEAD?
*/
if (!get_sha1_hex(buffer, sha1))
return 0;
return -1; return -1;
} }
@ -241,7 +251,7 @@ char *enter_repo(char *path, int strict)
return NULL; return NULL;
if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 && if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
validate_symref("HEAD") == 0) { validate_headref("HEAD") == 0) {
putenv("GIT_DIR=."); putenv("GIT_DIR=.");
check_repository_format(); check_repository_format();
return path; return path;

View File

@ -63,7 +63,7 @@ for doing easily operations which are not totally trivial to do over
the generic command interface. the generic command interface.
While some commands can be executed outside of any context (e.g. 'version' While some commands can be executed outside of any context (e.g. 'version'
or 'init-db'), most operations require a repository context, which in practice or 'init'), most operations require a repository context, which in practice
means getting an instance of the Git object using the repository() constructor. means getting an instance of the Git object using the repository() constructor.
(In the future, we will also get a new_repository() constructor.) All commands (In the future, we will also get a new_repository() constructor.) All commands
called as methods of the object are then executed in the context of the called as methods of the object are then executed in the context of the

201
reachable.c Normal file
View File

@ -0,0 +1,201 @@
#include "cache.h"
#include "refs.h"
#include "tag.h"
#include "commit.h"
#include "blob.h"
#include "diff.h"
#include "revision.h"
#include "reachable.h"
#include "cache-tree.h"
static void process_blob(struct blob *blob,
struct object_array *p,
struct name_path *path,
const char *name)
{
struct object *obj = &blob->object;
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
/* Nothing to do, really .. The blob lookup was the important part */
}
static void process_tree(struct tree *tree,
struct object_array *p,
struct name_path *path,
const char *name)
{
struct object *obj = &tree->object;
struct tree_desc desc;
struct name_entry entry;
struct name_path me;
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
if (parse_tree(tree) < 0)
die("bad tree object %s", sha1_to_hex(obj->sha1));
name = xstrdup(name);
add_object(obj, p, path, name);
me.up = path;
me.elem = name;
me.elem_len = strlen(name);
desc.buf = tree->buffer;
desc.size = tree->size;
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
process_tree(lookup_tree(entry.sha1), p, &me, entry.path);
else
process_blob(lookup_blob(entry.sha1), p, &me, entry.path);
}
free(tree->buffer);
tree->buffer = NULL;
}
static void process_tag(struct tag *tag, struct object_array *p, const char *name)
{
struct object *obj = &tag->object;
struct name_path me;
if (obj->flags & SEEN)
return;
obj->flags |= SEEN;
me.up = NULL;
me.elem = "tag:/";
me.elem_len = 5;
if (parse_tag(tag) < 0)
die("bad tag object %s", sha1_to_hex(obj->sha1));
add_object(tag->tagged, p, NULL, name);
}
static void walk_commit_list(struct rev_info *revs)
{
int i;
struct commit *commit;
struct object_array objects = { 0, 0, NULL };
/* Walk all commits, process their trees */
while ((commit = get_revision(revs)) != NULL)
process_tree(commit->tree, &objects, NULL, "");
/* Then walk all the pending objects, recursively processing them too */
for (i = 0; i < revs->pending.nr; i++) {
struct object_array_entry *pending = revs->pending.objects + i;
struct object *obj = pending->item;
const char *name = pending->name;
if (obj->type == OBJ_TAG) {
process_tag((struct tag *) obj, &objects, name);
continue;
}
if (obj->type == OBJ_TREE) {
process_tree((struct tree *)obj, &objects, NULL, name);
continue;
}
if (obj->type == OBJ_BLOB) {
process_blob((struct blob *)obj, &objects, NULL, name);
continue;
}
die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
}
}
static int add_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
const char *email, unsigned long timestamp, int tz,
const char *message, void *cb_data)
{
struct object *object;
struct rev_info *revs = (struct rev_info *)cb_data;
object = parse_object(osha1);
if (object)
add_pending_object(revs, object, "");
object = parse_object(nsha1);
if (object)
add_pending_object(revs, object, "");
return 0;
}
static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
struct object *object = parse_object(sha1);
struct rev_info *revs = (struct rev_info *)cb_data;
if (!object)
die("bad object ref: %s:%s", path, sha1_to_hex(sha1));
add_pending_object(revs, object, "");
return 0;
}
static int add_one_reflog(const char *path, const unsigned char *sha1, int flag, void *cb_data)
{
for_each_reflog_ent(path, add_one_reflog_ent, cb_data);
return 0;
}
static void add_one_tree(const unsigned char *sha1, struct rev_info *revs)
{
struct tree *tree = lookup_tree(sha1);
add_pending_object(revs, &tree->object, "");
}
static void add_cache_tree(struct cache_tree *it, struct rev_info *revs)
{
int i;
if (it->entry_count >= 0)
add_one_tree(it->sha1, revs);
for (i = 0; i < it->subtree_nr; i++)
add_cache_tree(it->down[i]->cache_tree, revs);
}
static void add_cache_refs(struct rev_info *revs)
{
int i;
read_cache();
for (i = 0; i < active_nr; i++) {
lookup_blob(active_cache[i]->sha1);
/*
* We could add the blobs to the pending list, but quite
* frankly, we don't care. Once we've looked them up, and
* added them as objects, we've really done everything
* there is to do for a blob
*/
}
if (active_cache_tree)
add_cache_tree(active_cache_tree, revs);
}
void mark_reachable_objects(struct rev_info *revs, int mark_reflog)
{
/*
* Set up revision parsing, and mark us as being interested
* in all object types, not just commits.
*/
revs->tag_objects = 1;
revs->blob_objects = 1;
revs->tree_objects = 1;
/* Add all refs from the index file */
add_cache_refs(revs);
/* Add all external refs */
for_each_ref(add_one_ref, revs);
/* Add all reflog info from refs */
if (mark_reflog)
for_each_ref(add_one_reflog, revs);
/*
* Set up the revision walk - this will move all commits
* from the pending list to the commit walking list.
*/
prepare_revision_walk(revs);
walk_commit_list(revs);
}

6
reachable.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef REACHEABLE_H
#define REACHEABLE_H
extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog);
#endif

View File

@ -793,16 +793,16 @@ int read_cache_from(const char *path)
die("index file open failed (%s)", strerror(errno)); die("index file open failed (%s)", strerror(errno));
} }
cache_mmap = MAP_FAILED;
if (!fstat(fd, &st)) { if (!fstat(fd, &st)) {
cache_mmap_size = st.st_size; cache_mmap_size = st.st_size;
errno = EINVAL; errno = EINVAL;
if (cache_mmap_size >= sizeof(struct cache_header) + 20) if (cache_mmap_size >= sizeof(struct cache_header) + 20)
cache_mmap = mmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); cache_mmap = xmmap(NULL, cache_mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
} else
die("index file smaller than expected");
} else
die("cannot stat the open index (%s)", strerror(errno));
close(fd); close(fd);
if (cache_mmap == MAP_FAILED)
die("index file mmap failed (%s)", strerror(errno));
hdr = cache_mmap; hdr = cache_mmap;
if (verify_hdr(hdr, cache_mmap_size) < 0) if (verify_hdr(hdr, cache_mmap_size) < 0)
@ -870,7 +870,7 @@ static int ce_write_flush(SHA_CTX *context, int fd)
unsigned int buffered = write_buffer_len; unsigned int buffered = write_buffer_len;
if (buffered) { if (buffered) {
SHA1_Update(context, write_buffer, buffered); SHA1_Update(context, write_buffer, buffered);
if (write(fd, write_buffer, buffered) != buffered) if (write_in_full(fd, write_buffer, buffered) != buffered)
return -1; return -1;
write_buffer_len = 0; write_buffer_len = 0;
} }
@ -919,7 +919,7 @@ static int ce_flush(SHA_CTX *context, int fd)
/* Flush first if not enough space for SHA1 signature */ /* Flush first if not enough space for SHA1 signature */
if (left + 20 > WRITE_BUFFER_SIZE) { if (left + 20 > WRITE_BUFFER_SIZE) {
if (write(fd, write_buffer, left) != left) if (write_in_full(fd, write_buffer, left) != left)
return -1; return -1;
left = 0; left = 0;
} }
@ -927,7 +927,7 @@ static int ce_flush(SHA_CTX *context, int fd)
/* Append the SHA1 signature at the end */ /* Append the SHA1 signature at the end */
SHA1_Final(write_buffer + left, context); SHA1_Final(write_buffer + left, context);
left += 20; left += 20;
return (write(fd, write_buffer, left) != left) ? -1 : 0; return (write_in_full(fd, write_buffer, left) != left) ? -1 : 0;
} }
static void ce_smudge_racily_clean_entry(struct cache_entry *ce) static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
@ -1010,7 +1010,7 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
if (data && if (data &&
!write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) && !write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
!ce_write(&c, newfd, data, sz)) !ce_write(&c, newfd, data, sz))
; free(data);
else { else {
free(data); free(data);
return -1; return -1;

41
refs.c
View File

@ -284,7 +284,7 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
fd = open(path, O_RDONLY); fd = open(path, O_RDONLY);
if (fd < 0) if (fd < 0)
return NULL; return NULL;
len = read(fd, buffer, sizeof(buffer)-1); len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd); close(fd);
/* /*
@ -332,7 +332,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master)
} }
lockpath = mkpath("%s.lock", git_HEAD); lockpath = mkpath("%s.lock", git_HEAD);
fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666); fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
written = write(fd, ref, len); written = write_in_full(fd, ref, len);
close(fd); close(fd);
if (written != len) { if (written != len) {
unlink(lockpath); unlink(lockpath);
@ -923,6 +923,9 @@ static int log_ref_write(struct ref_lock *lock,
char *logrec; char *logrec;
const char *committer; const char *committer;
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
if (log_all_ref_updates && if (log_all_ref_updates &&
(!strncmp(lock->ref_name, "refs/heads/", 11) || (!strncmp(lock->ref_name, "refs/heads/", 11) ||
!strncmp(lock->ref_name, "refs/remotes/", 13))) { !strncmp(lock->ref_name, "refs/remotes/", 13))) {
@ -968,7 +971,7 @@ static int log_ref_write(struct ref_lock *lock,
sha1_to_hex(sha1), sha1_to_hex(sha1),
committer); committer);
} }
written = len <= maxlen ? write(logfd, logrec, len) : -1; written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
free(logrec); free(logrec);
close(logfd); close(logfd);
if (written != len) if (written != len)
@ -987,8 +990,8 @@ int write_ref_sha1(struct ref_lock *lock,
unlock_ref(lock); unlock_ref(lock);
return 0; return 0;
} }
if (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 || if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
write(lock->lock_fd, &term, 1) != 1 write_in_full(lock->lock_fd, &term, 1) != 1
|| close(lock->lock_fd) < 0) { || close(lock->lock_fd) < 0) {
error("Couldn't write %s", lock->lk->filename); error("Couldn't write %s", lock->lk->filename);
unlock_ref(lock); unlock_ref(lock);
@ -1025,7 +1028,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
fstat(logfd, &st); fstat(logfd, &st);
if (!st.st_size) if (!st.st_size)
die("Log %s is empty.", logfile); die("Log %s is empty.", logfile);
logdata = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0); logdata = xmmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, logfd, 0);
close(logfd); close(logfd);
lastrec = NULL; lastrec = NULL;
@ -1097,7 +1100,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
return 0; return 0;
} }
void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data) int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
{ {
const char *logfile; const char *logfile;
FILE *logfp; FILE *logfp;
@ -1106,19 +1109,35 @@ void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data)
logfile = git_path("logs/%s", ref); logfile = git_path("logs/%s", ref);
logfp = fopen(logfile, "r"); logfp = fopen(logfile, "r");
if (!logfp) if (!logfp)
return; return -1;
while (fgets(buf, sizeof(buf), logfp)) { while (fgets(buf, sizeof(buf), logfp)) {
unsigned char osha1[20], nsha1[20]; unsigned char osha1[20], nsha1[20];
int len; char *email_end, *message;
unsigned long timestamp;
int len, ret, tz;
/* old SP new SP name <email> SP time TAB msg LF */ /* old SP new SP name <email> SP time TAB msg LF */
len = strlen(buf); len = strlen(buf);
if (len < 83 || buf[len-1] != '\n' || if (len < 83 || buf[len-1] != '\n' ||
get_sha1_hex(buf, osha1) || buf[40] != ' ' || get_sha1_hex(buf, osha1) || buf[40] != ' ' ||
get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ') get_sha1_hex(buf + 41, nsha1) || buf[81] != ' ' ||
!(email_end = strchr(buf + 82, '>')) ||
email_end[1] != ' ' ||
!(timestamp = strtoul(email_end + 2, &message, 10)) ||
!message || message[0] != ' ' ||
(message[1] != '+' && message[1] != '-') ||
!isdigit(message[2]) || !isdigit(message[3]) ||
!isdigit(message[4]) || !isdigit(message[5]) ||
message[6] != '\t')
continue; /* corrupt? */ continue; /* corrupt? */
fn(osha1, nsha1, buf+82, cb_data); email_end[1] = '\0';
tz = strtol(message + 1, NULL, 10);
message += 7;
ret = fn(osha1, nsha1, buf+82, timestamp, tz, message, cb_data);
if (ret)
return ret;
} }
fclose(logfp); fclose(logfp);
return 0;
} }

4
refs.h
View File

@ -45,8 +45,8 @@ extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, cons
extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1); extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1);
/* iterate over reflog entries */ /* iterate over reflog entries */
typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, char *, void *); typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data); int for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
/** Returns 0 if target has the right format for a ref. **/ /** Returns 0 if target has the right format for a ref. **/
extern int check_ref_format(const char *target); extern int check_ref_format(const char *target);

Some files were not shown because too many files have changed in this diff Show More