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-imap-send
git-index-pack
git-init
git-init-db
git-instaweb
git-local-fetch
@ -92,6 +93,7 @@ git-rebase
git-receive-pack
git-reflog
git-relink
git-remote
git-repack
git-repo-config
git-request-pull

View File

@ -100,7 +100,7 @@ core.sharedRepository::
group-writable). When 'all' (or 'world' or 'everybody'), the
repository will be readable by all users, additionally to being
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::
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
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.*::
Command aliases for the gitlink:git[1] command wrapper - e.g.
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
scratch, with no pre-existing files, and we'll call it `git-tutorial`.
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
$ cd git-tutorial
$ git-init-db
$ git-init
------------------------------------------------
to which git will reply
@ -1371,11 +1371,11 @@ $ mkdir my-git.git
------------
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_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
@ -1511,7 +1511,7 @@ A recommended workflow for a "project lead" goes like this:
+
If other people are pulling from your repository over dumb
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
would contain a call to `git-update-server-info` but the
`post-update` hook itself is disabled by default -- enable it

View File

@ -80,7 +80,7 @@ it:
------------------------------------------------
$ mkdir /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
------------------------------------------------

View File

@ -25,7 +25,7 @@ Basic Repository[[Basic Repository]]
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.
* 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
$ cd frotz
$ git-init-db
$ git-init
$ git add . <1>
$ git commit -m 'import of frotz source tree.'
$ git tag v2.43 <2>

View File

@ -9,7 +9,7 @@ git-am - Apply a series of patches in a mailbox
SYNOPSIS
--------
[verse]
'git-am' [--signoff] [--dotest=<dir>] [--utf8] [--binary] [--3way]
'git-am' [--signoff] [--dotest=<dir>] [--utf8 | --no-utf8] [--binary] [--3way]
[--interactive] [--whitespace=<option>] <mbox>...
'git-am' [--skip | --resolved]
@ -29,8 +29,21 @@ OPTIONS
Instead of `.dotest` directory, use <dir> as a working
area to store extracted patches.
--utf8, --keep::
Pass `-u` and `-k` flags to `git-mailinfo` (see
--keep::
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]).
--binary::

View File

@ -42,13 +42,13 @@ OPTIONS
and the current tree.
-u::
By default, the commit log message, author name and
author email are taken from the e-mail without any
charset conversion, after minimally decoding MIME
transfer encoding. This flag causes the resulting
commit to be encoded in utf-8 by transliterating them.
Note that the patch is always used as is without charset
conversion, even with this flag.
The commit log message, author name and author email are
taken from the e-mail, and after minimally decoding MIME
transfer encoding, re-coded in UTF-8 by transliterating
them. This used to be optional but now it is the default.
+
Note that the patch is always used as-is without charset
conversion, even with this flag.
-c .dotest/<num>::
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"
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
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.
--no-verify::
By default, the command looks for suspicious lines the
commit introduces, and aborts committing if there is one.
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.
This option bypasses the pre-commit hook.
See also link:hooks.html[hooks].
-e|--edit::
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.
-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>::
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
commit logs. Using this option and an author-conv-file
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>

View File

@ -11,95 +11,9 @@ SYNOPSIS
'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
-----------
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-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
This is a synonym for gitlink:git-init[1]. Please refer to the
documentation of that command.

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.
-u::
By default, the commit log message, author name and
author email are taken from the e-mail without any
charset conversion, after minimally decoding MIME
transfer encoding. This flag causes the resulting
commit to be encoded in the encoding specified by
i18n.commitencoding configuration (defaults to utf-8) by
transliterating them.
Note that the patch is always used as is without charset
conversion, even with this flag.
The commit log message, author name and author email are
taken from the e-mail, and after minimally decoding MIME
transfer encoding, re-coded in UTF-8 by transliterating
them. This used to be optional but now it is the default.
+
Note that the patch is always used as-is without charset
conversion, even with this flag.
--encoding=<encoding>::
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
$ cd /home/sean/import/jam
$ git init-db
$ git init
$ git p4import //public/jam jammy
------------

View File

@ -7,7 +7,7 @@ git-pack-refs - Pack heads and tags for efficient repository access
SYNOPSIS
--------
'git-pack-refs' [--all] [--prune]
'git-pack-refs' [--all] [--no-prune]
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
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
------

View File

@ -9,7 +9,7 @@ residing in a pack file.
SYNOPSIS
--------
'git-prune-packed' [-n]
'git-prune-packed' [-n] [-q]
DESCRIPTION
@ -32,6 +32,9 @@ OPTIONS
Don't actually remove any objects, only show those that would have been
removed.
-q::
Squelch the progress indicator.
Author
------
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
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'::
This runs fetch on all known SVN branches we're tracking. This
will NOT discover new branches (unlike git-svnimport), so
@ -153,7 +171,7 @@ OPTIONS
--shared::
--template=<template_directory>::
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>::
--revision <ARG>::
@ -231,8 +249,7 @@ repo-config key: svn.authorsfile
-q::
--quiet::
Make git-svn less verbose. This only affects git-svn if you
have the SVN::* libraries installed and are using them.
Make git-svn less verbose.
--repack[=<n>]::
--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
descended from.
This relies on the SVN::* libraries to work.
repo-config key: svn.followparent
--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
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::
Only used with the 'fetch' command.
@ -371,7 +367,7 @@ Basic Examples
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
# Fetch remote revisions:
git-svn fetch
@ -392,7 +388,7 @@ See also:
'<<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 \
-T trunk -b branches -t tags
# 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)
------------------------------------------------
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
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
section.
care about the full history of the project, then you can use
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
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`
------------------------------------------------------------------------
------------------------------------------------
git-svn fetch --follow-parent
------------------------------------------------
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
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

View File

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

View File

@ -235,8 +235,11 @@ push::
local head, the push fails.
reachable::
An object is reachable from a ref/commit/tree/tag, if there is a
chain leading from the latter to the former.
All of the ancestors of a given commit are said to be reachable from
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::
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
it as my origin branch head".
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]
repository::

View File

@ -3,7 +3,7 @@ Hooks used by git
Hooks are little scripts you can place in `$GIT_DIR/hooks`
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
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
$ cd my-new-repo.git
$ git --bare init-db
$ git --bare init
Change the ownership to your web-server's credentials. Use "grep ^User

View File

@ -102,7 +102,7 @@ branches::
hooks::
Hooks are customization scripts used by various git
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.
Read link:hooks.html[hooks] for more details about
each hook.

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
# The default target of this Makefile is...
all:
all::
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
# This also implies MOZILLA_SHA1.
@ -69,6 +69,9 @@ all:
#
# 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
# generally faster on your platform than accessing the working directory.
#
@ -179,7 +182,7 @@ SCRIPT_SH = \
SCRIPT_PERL = \
git-add--interactive.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-send-email.perl git-svn.perl
@ -201,7 +204,7 @@ PROGRAMS = \
git-update-server-info$X \
git-upload-pack$X git-verify-pack$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 \
$(EXTRA_PROGRAMS)
@ -210,7 +213,7 @@ EXTRA_PROGRAMS =
BUILT_INS = \
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))
# what 'all' will build and 'install' will install, in gitexecdir
@ -251,6 +254,7 @@ LIB_OBJS = \
interpolate.o \
lockfile.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 \
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 \
@ -271,6 +275,7 @@ BUILTIN_OBJS = \
builtin-check-ref-format.o \
builtin-commit-tree.o \
builtin-count-objects.o \
builtin-describe.o \
builtin-diff.o \
builtin-diff-files.o \
builtin-diff-index.o \
@ -522,6 +527,10 @@ ifdef NO_MMAP
COMPAT_CFLAGS += -DNO_MMAP
COMPAT_OBJS += compat/mmap.o
endif
ifdef NO_PREAD
COMPAT_CFLAGS += -DNO_PREAD
COMPAT_OBJS += compat/pread.o
endif
ifdef NO_FAST_WORKING_DIRECTORY
BASIC_CFLAGS += -DNO_FAST_WORKING_DIRECTORY
endif
@ -596,9 +605,12 @@ export prefix TAR INSTALL DESTDIR SHELL_PATH template_dir
### 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 templates
@ -840,6 +852,9 @@ install: all
'$(DESTDIR_SQ)$(gitexecdir_SQ)/git$X'; \
fi
$(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:
$(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;
if (parse_fragment_header(line, len, &dummy) < 0)
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)
@ -2238,8 +2239,19 @@ static void remove_file(struct patch *patch)
die("unable to remove %s from index", patch->old_name);
cache_tree_invalidate_path(active_cache_tree, patch->old_name);
}
if (!cached)
unlink(patch->old_name);
if (!cached) {
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)

View File

@ -137,7 +137,6 @@ void parse_treeish_arg(const char **argv, struct archiver_args *ar_args,
if (err || !S_ISDIR(mode))
die("current working directory is untracked");
free(tree);
tree = parse_tree_indirect(tree_sha1);
}
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;
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);
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++) {
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);
print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose,
abbrev, current);
@ -296,7 +308,8 @@ static void print_ref_list(int kinds, int verbose, int abbrev)
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)
{
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.");
}
if (get_sha1(start, sha1) ||
(commit = lookup_commit_reference(sha1)) == NULL)
die("Not a valid branch point: '%s'.", start);
if (start_sha1)
/* detached HEAD */
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);
lock = lock_any_ref_for_update(ref, NULL);
@ -326,7 +344,8 @@ static void create_branch(const char *name, const char *start,
if (reflog) {
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)
@ -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];
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))
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 rename = 0, force_rename = 0;
int verbose = 0, abbrev = DEFAULT_ABBREV;
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
int reflog = 0;
int kinds = REF_LOCAL_BRANCH;
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));
if (!head)
die("Failed to resolve HEAD as a valid ref.");
if (strncmp(head, "refs/heads/", 11))
die("HEAD not found below refs/heads!");
head += 11;
if (!strcmp(head, "HEAD")) {
detached = 1;
}
else {
if (strncmp(head, "refs/heads/", 11))
die("HEAD not found below refs/heads!");
head += 11;
}
if (delete)
return delete_branches(argc - i, argv + i, force_delete, kinds);
else if (i == argc)
print_ref_list(kinds, verbose, abbrev);
print_ref_list(kinds, detached, verbose, abbrev);
else if (rename && (i == argc - 1))
rename_branch(head, argv[i], force_rename);
else if (rename && (i == argc - 2))
rename_branch(argv[i], argv[i + 1], force_rename);
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)
create_branch(argv[i], argv[i + 1], force_create, reflog);
create_branch(argv[i], argv[i+1], NULL, force_create, reflog);
else
usage(builtin_branch_usage);

View File

@ -2,8 +2,10 @@
#include "commit.h"
#include "tag.h"
#include "refs.h"
#define SEEN (1u << 0)
#include "diff.h"
#include "diffcore.h"
#include "revision.h"
#include "builtin.h"
static const char describe_usage[] =
"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
@ -15,7 +17,7 @@ static int abbrev = DEFAULT_ABBREV;
static int names, allocs;
static struct commit_name {
const struct commit *commit;
struct commit *commit;
int prio; /* annotated tag = 2, tag = 1, head = 0 */
char path[FLEX_ARRAY]; /* more */
} **name_array = NULL;
@ -34,7 +36,7 @@ static struct commit_name *match(struct commit *cmit)
}
static void add_to_known_names(const char *path,
const struct commit *commit,
struct commit *commit,
int prio)
{
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;
}
struct possible_tag {
struct possible_tag *next;
struct commit_name *name;
unsigned long depth;
};
static void describe(const char *arg, int last_one)
{
unsigned char sha1[20];
@ -104,6 +112,7 @@ static void describe(const char *arg, int last_one)
struct commit_list *list;
static int initialized = 0;
struct commit_name *n;
struct possible_tag *all_matches, *min_match, *cur_match;
if (get_sha1(arg, sha1))
die("Not a valid object name %s", arg);
@ -124,22 +133,71 @@ static void describe(const char *arg, int last_one)
}
list = NULL;
all_matches = NULL;
cur_match = NULL;
commit_list_insert(cmit, &list);
while (list) {
struct commit *c = pop_most_recent_commit(&list, SEEN);
struct commit *c = pop_commit(&list);
n = match(c);
if (n) {
printf("%s-g%s\n", n->path,
find_unique_abbrev(cmit->object.sha1, abbrev));
if (!last_one)
clear_commit_marks(cmit, SEEN);
return;
struct possible_tag *p = xmalloc(sizeof(*p));
p->name = n;
p->next = NULL;
if (cur_match)
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;
}
}
}
die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
if (!all_matches)
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;
@ -161,7 +219,7 @@ int main(int argc, char **argv)
usage(describe_usage);
}
setup_git_directory();
save_commit_buffer = 0;
if (argc <= i)
describe("HEAD", 1);

View File

@ -136,7 +136,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
if (i < 0)
goto err_ret;
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));
close(i);
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
* 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
* with the way the namespace under .git/ is organized, should
* 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");
/* Enable logAllRefUpdates if a working tree is attached */
if (!is_bare_git_dir(git_dir))
if (is_bare_repository()) {
git_config_set("core.bare", "true");
}
else {
git_config_set("core.bare", "false");
git_config_set("core.logallrefupdates", "true");
}
return reinit;
}
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.

View File

@ -515,12 +515,9 @@ static void convert_to_utf8(char *line, char *charset)
char *input_charset = *charset ? charset : latin_one;
char *out = reencode_string(line, metainfo_charset, input_charset);
if (!out) {
fprintf(stderr, "cannot convert from %s to %s\n",
input_charset, metainfo_charset);
*charset = 0;
return;
}
if (!out)
die("cannot convert from %s to %s\n",
input_charset, metainfo_charset);
strcpy(line, out);
free(out);
}
@ -797,17 +794,23 @@ static const char mailinfo_usage[] =
int cmd_mailinfo(int argc, const char **argv, const char *prefix)
{
const char *def_charset;
/* NEEDSWORK: might want to do the optional .git/ directory
* discovery
*/
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] == '-') {
if (!strcmp(argv[1], "-k"))
keep_subject = 1;
else if (!strcmp(argv[1], "-u"))
metainfo_charset = (git_commit_encoding
? git_commit_encoding : "utf-8");
metainfo_charset = def_charset;
else if (!strcmp(argv[1], "-n"))
metainfo_charset = NULL;
else if (!strncmp(argv[1], "--encoding=", 11))
metainfo_charset = argv[1] + 11;
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
* 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;
unsigned char fakebuf[4096];
@ -323,7 +368,7 @@ static int revalidate_loose_object(struct object_entry *entry,
return -1;
map += used;
mapsize -= used;
return check_inflate(map, mapsize, size);
return check_loose_inflate(map, mapsize, size);
}
static unsigned long write_object(struct sha1file *f,
@ -416,6 +461,8 @@ static unsigned long write_object(struct sha1file *f,
}
else {
struct packed_git *p = entry->in_pack;
struct pack_window *w_curs = NULL;
unsigned long offset;
if (entry->delta) {
obj_type = (allow_ofs_delta && entry->delta->offset) ?
@ -437,16 +484,14 @@ static unsigned long write_object(struct sha1file *f,
hdrlen += 20;
}
use_packed_git(p);
buf = (char *) p->pack_base
+ entry->in_pack_offset
+ entry->in_pack_header_size;
offset = entry->in_pack_offset + entry->in_pack_header_size;
datalen = find_packed_object_size(p, entry->in_pack_offset)
- 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));
sha1write(f, buf, datalen);
unuse_packed_git(p);
copy_pack_data(f, p, &w_curs, offset, datalen);
unuse_pack(&w_curs);
reused++;
}
if (entry->delta)
@ -937,22 +982,19 @@ static void check_object(struct object_entry *entry)
if (entry->in_pack && !entry->preferred_base) {
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 size, used;
unsigned char *buf;
struct object_entry *base_entry = NULL;
use_packed_git(p);
buf = p->pack_base;
buf += entry->in_pack_offset;
buf = use_pack(p, &w_curs, entry->in_pack_offset, NULL);
/* We want in_pack_type even if we do not reuse delta.
* There is no point not reusing non-delta representations.
*/
used = unpack_object_header_gently(buf, left,
&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
* 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) {
unsigned char c, *base_name;
unsigned long ofs;
unsigned long used_0;
/* there is at least 20 bytes left in the pack */
switch (entry->in_pack_type) {
case OBJ_REF_DELTA:
base_name = buf + used;
base_name = use_pack(p, &w_curs,
entry->in_pack_offset + used, NULL);
used += 20;
break;
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;
while (c & 128) {
ofs += 1;
if (!ofs || ofs & ~(~0UL >> 7))
die("delta base offset overflow in pack for %s",
sha1_to_hex(entry->sha1));
c = buf[used++];
c = buf[used_0++];
ofs = (ofs << 7) + (c & 127);
}
if (ofs >= entry->in_pack_offset)
@ -983,6 +1030,7 @@ static void check_object(struct object_entry *entry)
sha1_to_hex(entry->sha1));
ofs = entry->in_pack_offset - ofs;
base_name = find_packed_object_name(p, ofs);
used += used_0;
break;
default:
base_name = NULL;
@ -990,7 +1038,7 @@ static void check_object(struct object_entry *entry)
if (base_name)
base_entry = locate_object_entry(base_name);
}
unuse_packed_git(p);
unuse_pack(&w_curs);
entry->in_pack_header_size = used;
if (base_entry) {

View File

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

View File

@ -4,7 +4,10 @@
static const char prune_packed_usage[] =
"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;
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))
continue;
memcpy(pathname + len, de->d_name, 38);
if (dryrun)
if (opts & DRY_RUN)
printf("rm -f %s\n", pathname);
else if (unlink(pathname) < 0)
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);
}
void prune_packed_objects(int dryrun)
void prune_packed_objects(int opts)
{
int i;
static char pathname[PATH_MAX];
@ -46,24 +49,31 @@ void prune_packed_objects(int dryrun)
sprintf(pathname + len, "%02x/", i);
d = opendir(pathname);
if (opts == VERBOSE && (d || i == 255))
fprintf(stderr, "Removing unused objects %d%%...\015",
((i+1) * 100) / 256);
if (!d)
continue;
prune_dir(i, d, pathname, len + 3, dryrun);
prune_dir(i, d, pathname, len + 3, opts);
closedir(d);
}
if (opts == VERBOSE)
fprintf(stderr, "\nDone.\n");
}
int cmd_prune_packed(int argc, const char **argv, const char *prefix)
{
int i;
int dryrun = 0;
int opts = VERBOSE;
for (i = 1; i < argc; i++) {
const char *arg = argv[i];
if (*arg == '-') {
if (!strcmp(arg, "-n"))
dryrun = 1;
opts |= DRY_RUN;
else if (!strcmp(arg, "-q"))
opts &= ~VERBOSE;
else
usage(prune_packed_usage);
continue;
@ -72,6 +82,6 @@ int cmd_prune_packed(int argc, const char **argv, const char *prefix)
usage(prune_packed_usage);
}
sync();
prune_packed_objects(dryrun);
prune_packed_objects(opts);
return 0;
}

View File

@ -1,18 +1,12 @@
#include "cache.h"
#include "refs.h"
#include "tag.h"
#include "commit.h"
#include "tree.h"
#include "blob.h"
#include "tree-walk.h"
#include "diff.h"
#include "revision.h"
#include "builtin.h"
#include "cache-tree.h"
#include "reachable.h"
static const char prune_usage[] = "git-prune [-n]";
static int show_only;
static struct rev_info revs;
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 i;
struct rev_info revs;
for (i = 1; i < argc; 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;
/*
* Set up revision parsing, and mark us as being interested
* in all object types, not just commits.
*/
init_revisions(&revs, prefix);
revs.tag_objects = 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);
mark_reachable_objects(&revs, 1);
prune_object_dir(get_object_directory());

View File

@ -4,106 +4,239 @@
#include "refs.h"
#include "dir.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_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 {
FILE *newlog;
const char *ref;
struct commit *ref_commit;
unsigned long expire_total;
unsigned long expire_unreachable;
struct cmd_reflog_expire_cb *cmd;
};
#define INCOMPLETE (1u<<10)
#define STUDYING (1u<<11)
static int tree_is_complete(const unsigned char *sha1)
{
struct tree_desc desc;
void *buf;
char type[20];
struct name_entry entry;
int complete;
struct tree *tree;
buf = read_sha1_file(sha1, type, &desc.size);
if (!buf)
tree = lookup_tree(sha1);
if (!tree)
return 0;
if (tree->object.flags & SEEN)
return 1;
if (tree->object.flags & INCOMPLETE)
return 0;
desc.buf = buf;
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);
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;
}
update_tree_entry(&desc);
desc.buf = data;
tree->buffer = data;
}
free(buf);
return 1;
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)
{
struct commit *commit;
*it = NULL;
if (is_null_sha1(sha1))
return 1;
commit = lookup_commit_reference_gently(sha1, 1);
if (!commit)
return 0;
/* Make sure everything in this commit exists. */
parse_object(commit->object.sha1);
if (!tree_is_complete(commit->tree->object.sha1))
/*
* Make sure everything in this commit exists.
*
* 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;
*it = commit;
return 1;
}
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;
unsigned long timestamp;
char *cp, *ep;
struct commit *old, *new;
cp = strchr(data, '>');
if (!cp || *++cp != ' ')
goto prune;
timestamp = strtoul(cp, &ep, 10);
if (*ep != ' ')
goto prune;
if (timestamp < cb->expire_total)
if (timestamp < cb->cmd->expire_total)
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;
if ((timestamp < cb->expire_unreachable) &&
(!cb->ref_commit ||
(old && !in_merge_bases(old, cb->ref_commit)) ||
(new && !in_merge_bases(new, cb->ref_commit))))
goto prune;
if (timestamp < cb->cmd->expire_unreachable) {
if (!cb->ref_commit)
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)
fprintf(cb->newlog, "%s %s %s",
sha1_to_hex(osha1), sha1_to_hex(nsha1), data);
if (cb->newlog) {
char sign = (tz < 0) ? '-' : '+';
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;
prune:
if (!cb->newlog)
fprintf(stderr, "would prune %s", data);
if (!cb->newlog || cb->cmd->verbose)
printf("%sprune %s", cb->newlog ? "" : "would ", message);
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)
{
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,
"warning: ref '%s' does not point at a commit\n", ref);
cb.ref = ref;
cb.expire_total = cmd->expire_total;
cb.expire_unreachable = cmd->expire_unreachable;
cb.cmd = cmd;
for_each_reflog_ent(ref, expire_reflog_ent, &cb);
finish:
if (cb.newlog) {
@ -164,9 +296,6 @@ static int reflog_expire_config(const char *var, const char *value)
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)
{
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_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++) {
const char *arg = argv[i];
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);
else if (!strncmp(arg, "--expire-unreachable=", 21))
cb.expire_unreachable = approxidate(arg + 21);
else if (!strcmp(arg, "--stale-fix"))
cb.stalefix = 1;
else if (!strcmp(arg, "--all"))
do_all = 1;
else if (!strcmp(arg, "--verbose"))
cb.verbose = 1;
else if (!strcmp(arg, "--")) {
i++;
break;
@ -205,6 +344,15 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
else
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)
status |= for_each_ref(expire_reflog, &cb);
while (i < argc) {
@ -219,6 +367,10 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
return status;
}
/*
* main "reflog"
*/
static const char reflog_usage[] =
"git-reflog (expire | ...)";

View File

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

View File

@ -32,6 +32,10 @@ static int remove_file(const char *name)
char *slash;
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, '/'))) {
char *n = xstrdup(name);
do {
@ -204,7 +208,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
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
* abort the "git rm" (but once we've successfully removed
* 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;
ssize_t n;
n = xread(0, buffer, HEADERSIZE);
n = read_in_full(0, buffer, HEADERSIZE);
if (n < HEADERSIZE)
die("git-get-tar-commit-id: read error");
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))
return 1;
n = xwrite(1, content + 11, 41);
n = write_in_full(1, content + 11, 41);
if (n < 41)
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];
ssize_t sz = read(child_fd, buf, sizeof(buf));
if (sz < 0) {
if (errno != EINTR)
if (errno != EAGAIN && errno != EINTR)
error_clnt("read error: %s\n", strerror(errno));
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 nothing_done = 1;
git_config(git_default_config);
while (1 < argc) {
if (!no_more_options && argv[1][0] == '-') {
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_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_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_index(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 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 char *get_object_directory(void);
extern char *get_refs_directory(void);
@ -197,6 +198,8 @@ extern int warn_ambiguous_refs;
extern int shared_repository;
extern const char *apply_default_whitespace;
extern int zlib_compression_level;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
#define GIT_REPO_VERSION 0
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 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 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 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;
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 {
struct packed_git *next;
unsigned long index_size;
unsigned long pack_size;
struct pack_window *windows;
unsigned int *index_base;
void *pack_base;
unsigned int pack_last_used;
unsigned int pack_use_cnt;
off_t index_size;
off_t pack_size;
int pack_fd;
int pack_local;
unsigned char sha1[20];
/* 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,
struct packed_git *packs);
extern int use_packed_git(struct packed_git *);
extern void unuse_packed_git(struct packed_git *);
extern void pack_report();
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 int num_packed_objects(const struct packed_git *p);
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 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 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 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 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 */
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)
packet_write(fd, "shallow %s", hex);
else {
write(fd, hex, 40);
write(fd, "\n", 1);
if (write_in_full(fd, hex, 40) != 40)
break;
if (write_in_full(fd, "\n", 1) != 1)
break;
}
}
return count;
@ -462,20 +464,29 @@ static int get_one_line(const char *msg, unsigned long len)
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;
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++) {
unsigned ch = line[i];
if (ch & 0x80)
int ch = line[i];
if (non_ascii(ch))
needquote++;
if ((i + 1 < len) &&
(ch == '=' && line[i+1] == '?'))
@ -484,8 +495,11 @@ static int add_rfc2047(char *buf, const char *line, int len)
if (!needquote)
return sprintf(buf, "%.*s", len, line);
memcpy(bp, q_utf8, sizeof(q_utf8)-1);
bp += sizeof(q_utf8)-1;
i = snprintf(q_encoding, sizeof(q_encoding), q_encoding_fmt, encoding);
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++) {
unsigned ch = line[i] & 0xFF;
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,
const char *line, int relative_date)
const char *line, int relative_date,
const char *encoding)
{
char *date;
int namelen;
@ -531,7 +546,8 @@ static int add_user_info(const char *what, enum cmit_fmt fmt, char *buf,
filler = "";
strcpy(buf, "From: ");
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);
ret += namelen - display_name_length;
buf[ret++] = '\n';
@ -666,21 +682,18 @@ static char *replace_encoding_header(char *buf, char *encoding)
return buf;
}
static char *logmsg_reencode(const struct commit *commit)
static char *logmsg_reencode(const struct commit *commit,
char *output_encoding)
{
char *encoding;
char *out;
char *output_encoding = (git_log_output_encoding
? git_log_output_encoding
: git_commit_encoding);
char *utf8 = "utf-8";
if (!output_encoding)
output_encoding = "utf-8";
else if (!*output_encoding)
if (!*output_encoding)
return NULL;
encoding = get_header(commit, "encoding");
if (!encoding)
return NULL;
encoding = utf8;
if (!strcmp(encoding, output_encoding))
out = strdup(commit->buffer);
else
@ -689,7 +702,8 @@ static char *logmsg_reencode(const struct commit *commit)
if (out)
out = replace_encoding_header(out, output_encoding);
free(encoding);
if (encoding != utf8)
free(encoding);
if (!out)
return NULL;
return out;
@ -709,8 +723,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
int parents_shown = 0;
const char *msg = commit->buffer;
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)
msg = reencoded;
@ -736,7 +757,7 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
i + 1 < len && msg[i+1] == '\n')
in_body = 1;
}
else if (ch & 0x80) {
else if (non_ascii(ch)) {
plain_non_ascii = 1;
break;
}
@ -795,13 +816,15 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
offset += add_user_info("Author", fmt,
buf + offset,
line + 7,
relative_date);
relative_date,
encoding);
if (!memcmp(line, "committer ", 10) &&
(fmt == CMIT_FMT_FULL || fmt == CMIT_FMT_FULLER))
offset += add_user_info("Commit", fmt,
buf + offset,
line + 10,
relative_date);
relative_date,
encoding);
continue;
}
@ -824,7 +847,8 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
int slen = strlen(subject);
memcpy(buf + offset, subject, slen);
offset += slen;
offset += add_rfc2047(buf + offset, line, linelen);
offset += add_rfc2047(buf + offset, line, linelen,
encoding);
}
else {
memset(buf + offset, ' ', indent);
@ -835,11 +859,17 @@ unsigned long pretty_print_commit(enum cmit_fmt fmt,
if (fmt == CMIT_FMT_ONELINE)
break;
if (subject && plain_non_ascii) {
static const char header[] =
"Content-Type: text/plain; charset=UTF-8\n"
int sz;
char header[512];
const char *header_fmt =
"Content-Type: text/plain; charset=%s\n"
"Content-Transfer-Encoding: 8bit\n";
memcpy(buf + offset, header, sizeof(header)-1);
offset += sizeof(header)-1;
sz = snprintf(header, sizeof(header), header_fmt,
encoding);
if (sizeof(header) < sz)
die("Encoding name %s too long", encoding);
memcpy(buf + offset, header, sz);
offset += sz;
}
if (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);
}
/* merge-rebase stuff */
/* merge-base stuff */
/* bits #0..15 in revision.h */
#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 RESULT (1u<<19)
static const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
static struct commit *interesting(struct commit_list *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 */
free_commit_list(list);
list = result; result = NULL;
while (list) {
struct commit_list *n = list->next;
@ -1097,7 +1130,6 @@ struct commit_list *get_merge_bases(struct commit *one,
struct commit *two,
int cleanup)
{
const unsigned all_flags = (PARENT1 | PARENT2 | STALE | RESULT);
struct commit_list *list;
struct commit **rslt;
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;
}

157
config.c
View File

@ -269,6 +269,11 @@ int git_default_config(const char *var, const char *value)
return 0;
}
if (!strcmp(var, "core.bare")) {
is_bare_repository_cfg = git_config_bool(var, value);
return 0;
}
if (!strcmp(var, "core.ignorestat")) {
assume_unchanged = git_config_bool(var, value);
return 0;
@ -304,6 +309,21 @@ int git_default_config(const char *var, const char *value)
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")) {
strlcpy(git_default_name, value, sizeof(git_default_name));
return 0;
@ -449,7 +469,15 @@ static int store_aux(const char* key, const char* value)
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, '.');
int len1 = store.baselen, len2 = -1;
@ -463,37 +491,74 @@ static void store_write_section(int fd, const char* key)
}
}
write(fd, "[", 1);
write(fd, key, len1);
if (write_in_full(fd, "[", 1) != 1 ||
write_in_full(fd, key, len1) != len1)
return 0;
if (len2 >= 0) {
write(fd, " \"", 2);
if (write_in_full(fd, " \"", 2) != 2)
return 0;
while (--len2 >= 0) {
unsigned char c = *++dot;
if (c == '"')
write(fd, "\\", 1);
write(fd, &c, 1);
if (write_in_full(fd, "\\", 1) != 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 length = strlen(key+store.baselen+1);
int quote = 0;
write(fd, "\t", 1);
write(fd, key+store.baselen+1,
strlen(key+store.baselen+1));
write(fd, " = ", 3);
/* Check to see if the value needs to be quoted. */
if (value[0] == ' ')
quote = 1;
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++)
switch (value[i]) {
case '\n': write(fd, "\\n", 2); break;
case '\t': write(fd, "\\t", 2); break;
case '"': case '\\': write(fd, "\\", 1);
default: write(fd, value+i, 1);
}
write(fd, "\n", 1);
case '\n':
if (write_in_full(fd, "\\n", 2) != 2)
return 0;
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;
}
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,
@ -633,9 +698,10 @@ int git_config_set_multivar(const char* key, const char* value,
}
store.key = (char*)key;
store_write_section(fd, key);
store_write_pair(fd, key, value);
} else{
if (!store_write_section(fd, key) ||
!store_write_pair(fd, key, value))
goto write_err_out;
} else {
struct stat st;
char* contents;
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);
contents = mmap(NULL, st.st_size, PROT_READ,
contents = xmmap(NULL, st.st_size, PROT_READ,
MAP_PRIVATE, in_fd, 0);
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 */
if (copy_end > copy_begin) {
write(fd, contents + copy_begin,
copy_end - copy_begin);
if (new_line)
write(fd, "\n", 1);
if (write_in_full(fd, contents + copy_begin,
copy_end - copy_begin) <
copy_end - copy_begin)
goto write_err_out;
if (new_line &&
write_in_full(fd, "\n", 1) != 1)
goto write_err_out;
}
copy_begin = store.offset[i];
}
/* write the pair (value == NULL means unset) */
if (value != NULL) {
if (store.state == START)
store_write_section(fd, key);
store_write_pair(fd, key, value);
if (store.state == START) {
if (!store_write_section(fd, key))
goto write_err_out;
}
if (!store_write_pair(fd, key, value))
goto write_err_out;
}
/* write the rest of the config */
if (copy_begin < st.st_size)
write(fd, contents + copy_begin,
st.st_size - copy_begin);
if (write_in_full(fd, contents + copy_begin,
st.st_size - copy_begin) <
st.st_size - copy_begin)
goto write_err_out;
munmap(contents, st.st_size);
unlink(config_filename);
@ -755,6 +829,11 @@ out_free:
free(lock_file);
}
return ret;
write_err_out:
ret = write_error();
goto out_free;
}
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)) {
int i;
int length;
for (i = 0; buf[i] && isspace(buf[i]); i++)
; /* do nothing */
if (buf[i] == '[') {
@ -815,15 +895,22 @@ int git_config_rename_section(const char *old_name, const char *new_name)
/* old_name matches */
ret++;
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;
}
}
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);
if (close(out_fd) || commit_lock_file(lock) < 0)
ret = error("Cannot commit config file!");
ret = error("Cannot commit config file!");
out:
free(config_filename);
return ret;

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-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
;;;; ------------------------------------------------------------
@ -448,11 +457,10 @@ and returns the process output as a string."
(defun git-fileinfo-prettyprint (info)
"Pretty-printer for the git-fileinfo structure."
(insert (format " %s %s %s %s%s"
(if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ")
(git-status-code-as-string (git-fileinfo->state info))
(git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info))
(git-escape-file-name (git-fileinfo->name info))
(insert (concat " " (if (git-fileinfo->marked info) (propertize "*" 'face 'git-mark-face) " ")
" " (git-status-code-as-string (git-fileinfo->state info))
" " (git-permissions-as-string (git-fileinfo->old-perm info) (git-fileinfo->new-perm info))
" " (git-escape-file-name (git-fileinfo->name info))
(git-rename-as-string info))))
(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] = '\0';
write(2, buf, buflen);
write_in_full(2, buf, buflen);
}
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
* 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)
continue;
}
@ -297,9 +297,12 @@ static int diff_cache(struct rev_info *revs,
!show_modified(revs, ce, ac[1], 0,
cached, match_missing))
break;
/* fallthru */
diff_unmerge(&revs->diffopt, ce->name,
ntohl(ce->ce_mode), ce->sha1);
break;
case 3:
diff_unmerge(&revs->diffopt, ce->name);
diff_unmerge(&revs->diffopt, ce->name,
0, null_sha1);
break;
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);
if (fd < 0)
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);
if (s->data == MAP_FAILED)
goto err_empty;
s->should_munmap = 1;
}
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");
if (fd < 0)
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");
close(fd);
temp->name = temp->tmp_path;
@ -2875,10 +2873,12 @@ void diff_change(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;
one = 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);
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);

View File

@ -54,9 +54,9 @@ struct diff_filepair {
unsigned source_stays : 1; /* all of R/C are copies */
unsigned broken_pair : 1;
unsigned renamed_pair : 1;
unsigned is_unmerged : 1;
};
#define DIFF_PAIR_UNMERGED(p) \
(!DIFF_FILE_VALID((p)->one) && !DIFF_FILE_VALID((p)->two))
#define DIFF_PAIR_UNMERGED(p) ((p)->is_unmerged)
#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;
}
buf = xmalloc(size+1);
if (read(fd, buf, size) != size)
if (read_in_full(fd, buf, size) != size)
goto err;
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)",
path, strerror(errno));
}
wrote = write(fd, new, size);
wrote = write_in_full(fd, new, size);
close(fd);
free(new);
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 "
"file %s (%s)", path, strerror(errno));
}
wrote = write(fd, new, size);
wrote = write_in_full(fd, new, size);
close(fd);
free(new);
if (wrote != size)

View File

@ -15,7 +15,8 @@ int use_legacy_headers = 1;
int trust_executable_bit = 1;
int assume_unchanged;
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 repository_format_version;
char *git_commit_encoding;
@ -23,6 +24,8 @@ char *git_log_output_encoding;
int shared_repository = PERM_UMASK;
const char *apply_default_whitespace;
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_use_color = 1;
@ -49,12 +52,15 @@ static void setup_git_env(void)
git_graft_file = getenv(GRAFT_ENVIRONMENT);
if (!git_graft_file)
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))
return 0;
s = strrchr(dir, '/');

View File

@ -290,7 +290,7 @@ static int fsck_sha1(unsigned char *sha1)
{
struct object *obj = parse_object(sha1);
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)
return 0;
obj->flags |= SEEN;
@ -399,7 +399,9 @@ static void fsck_dir(int i, char *path)
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;

View File

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

View File

@ -2,11 +2,12 @@
#
# 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>...
or, when resuming [--skip | --resolved]'
. git-sh-setup
set_reflog_action am
require_work_tree
git var GIT_COMMITTER_IDENT >/dev/null || exit
@ -105,7 +106,7 @@ It does not apply to blobs recorded in its index."
}
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
do
@ -128,7 +129,9 @@ do
-s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
sign=t; shift ;;
-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)
keep=t; shift ;;
@ -226,6 +229,8 @@ fi
if test "$(cat "$dotest/utf8")" = t
then
utf8=-u
else
utf8=-n
fi
if test "$(cat "$dotest/keep")" = t
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
keep_subject= query_apply= continue= utf8= resume=t
keep_subject= query_apply= continue= utf8=-u resume=t
while case "$#" in 0) break ;; esac
do
case "$1" in
-u) utf8=-u ;;
-n) utf8=-n ;;
-k) keep_subject=-k ;;
-q) query_apply=t ;;
-c) continue="$2"; resume=f; shift ;;

View File

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

View File

@ -3,9 +3,11 @@
USAGE='[-f] [-b <new_branch>] [-m] [<branch>] [<paths>...]'
SUBDIRECTORY_OK=Sometimes
. git-sh-setup
require_work_tree
old_name=HEAD
old=$(git-rev-parse --verify $old_name 2>/dev/null)
oldbranch=$(git-symbolic-ref $old_name 2>/dev/null)
new=
new_name=
force=
@ -13,6 +15,8 @@ branch=
newbranch=
newbranch_log=
merge=
LF='
'
while [ "$#" != "0" ]; do
arg="$1"
shift
@ -50,7 +54,7 @@ while [ "$#" != "0" ]; do
exit 1
fi
new="$rev"
new_name="$arg^0"
new_name="$arg"
if git-show-ref --verify --quiet -- "refs/heads/$arg"
then
branch="$arg"
@ -131,31 +135,53 @@ fi
# We are switching branches and checking out trees, so
# we *NEED* to be at the toplevel.
cdup=$(git-rev-parse --show-cdup)
if test ! -z "$cdup"
then
cd "$cdup"
fi
cd_to_toplevel
[ -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
# are switching to, then we'd better just be checking out
# what we already had
# are switching to, then we are detaching our HEAD from any
# 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" ] &&
[ "$new" != "$old" ] &&
die "git checkout: provided reference cannot be checked out directly
detached=
detach_warn=
You need -b to associate a new branch with the wanted checkout. Example:
git checkout -b <new_branch_name> $arg
"
if test -z "$branch$newbranch" && test "$new" != "$old"
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 ]
then
echo "warning: You do not appear to currently be on a branch." >&2
echo "warning: Forcing checkout of $new_name." >&2
echo >&2 "warning: You appear to be on a branch yet to be born."
echo >&2 "warning: Forcing checkout of $new_name."
force=1
fi
@ -226,8 +252,25 @@ if [ "$?" -eq 0 ]; then
git-update-ref -m "checkout: Created from $new_name" "refs/heads/$newbranch" $new || exit
branch="$newbranch"
fi
[ "$branch" ] &&
GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD "refs/heads/$branch"
if test -n "$branch"
then
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"
else
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.'
SUBDIRECTORY_OK=Yes
. git-sh-setup
require_work_tree
ignored=
ignoredonly=

View File

@ -214,7 +214,7 @@ yes)
GIT_DIR="$D" ;;
*)
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"
then
@ -355,7 +355,7 @@ then
# The name under $remote_top the remote HEAD seems to point at.
head_points_at=$(
(
echo "master"
test -f "$GIT_DIR/$remote_top/master" && echo "master"
cd "$GIT_DIR/$remote_top" &&
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>...]'
SUBDIRECTORY_OK=Yes
. git-sh-setup
require_work_tree
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
TOP=`git-rev-parse --show-cdup`
if test -z "$TOP"
then
TOP=./
fi
case "$all,$also" in
t,)
save_index &&
(
cd "$TOP"
GIT_INDEX_FILE="$NEXT_INDEX"
export GIT_INDEX_FILE
cd_to_toplevel &&
GIT_INDEX_FILE="$NEXT_INDEX" &&
export GIT_INDEX_FILE &&
git-diff-files --name-only -z |
git-update-index --remove -z --stdin
)
) || exit
;;
,t)
save_index &&
@ -338,11 +333,11 @@ t,)
git-diff-files --name-only -z -- "$@" |
(
cd "$TOP"
GIT_INDEX_FILE="$NEXT_INDEX"
export GIT_INDEX_FILE
cd_to_toplevel &&
GIT_INDEX_FILE="$NEXT_INDEX" &&
export GIT_INDEX_FILE &&
git-update-index --remove -z --stdin
)
) || exit
;;
,)
case "$#" in
@ -434,7 +429,9 @@ then
fi
elif test "$use_commit" != ""
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"
then
cat "$GIT_DIR/MERGE_MSG"
@ -496,7 +493,8 @@ then
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"`
eval "$set_author_env"
export GIT_AUTHOR_NAME
@ -628,7 +626,7 @@ then
if test -z "$quiet"
then
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

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 int git_munmap(void *start, size_t length);
#define DEFAULT_PACKED_GIT_WINDOW_SIZE (1 * 1024 * 1024)
#else /* NO_MMAP */
#include <sys/mman.h>
#define DEFAULT_PACKED_GIT_WINDOW_SIZE \
(sizeof(void*) >= 8 \
? 1 * 1024 * 1024 * 1024 \
: 32 * 1024 * 1024)
#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
#define setenv gitsetenv
extern int gitsetenv(const char *, const char *, int);
@ -118,11 +132,17 @@ extern char *gitstrcasestr(const char *haystack, const char *needle);
extern size_t gitstrlcpy(char *, const char *, size_t);
#endif
extern void release_pack_memory(size_t);
static inline char* xstrdup(const char *str)
{
char *ret = strdup(str);
if (!ret)
die("Out of memory, strdup failed");
if (!ret) {
release_pack_memory(strlen(str) + 1);
ret = strdup(str);
if (!ret)
die("Out of memory, strdup failed");
}
return ret;
}
@ -131,8 +151,14 @@ static inline void *xmalloc(size_t size)
void *ret = malloc(size);
if (!ret && !size)
ret = malloc(1);
if (!ret)
die("Out of memory, malloc failed");
if (!ret) {
release_pack_memory(size);
ret = malloc(size);
if (!ret && !size)
ret = malloc(1);
if (!ret)
die("Out of memory, malloc failed");
}
#ifdef XMALLOC_POISON
memset(ret, 0xA5, size);
#endif
@ -144,8 +170,14 @@ static inline void *xrealloc(void *ptr, size_t size)
void *ret = realloc(ptr, size);
if (!ret && !size)
ret = realloc(ptr, 1);
if (!ret)
die("Out of memory, realloc failed");
if (!ret) {
release_pack_memory(size);
ret = realloc(ptr, size);
if (!ret && !size)
ret = realloc(ptr, 1);
if (!ret)
die("Out of memory, realloc failed");
}
return ret;
}
@ -154,8 +186,29 @@ static inline void *xcalloc(size_t nmemb, size_t size)
void *ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
if (!ret)
die("Out of memory, calloc failed");
if (!ret) {
release_pack_memory(nmemb * size);
ret = calloc(nmemb, size);
if (!ret && (!nmemb || !size))
ret = calloc(1, 1);
if (!ret)
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;
}

View File

@ -29,7 +29,7 @@ use IPC::Open2;
$SIG{'PIPE'}="IGNORE";
$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);
sub usage() {
@ -37,7 +37,7 @@ sub usage() {
Usage: ${\basename $0} # fetch/update GIT from CVS
[-o branch-for-HEAD] [-h] [-v] [-d CVSROOT] [-A author-conv-file]
[-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
exit(1);
}
@ -105,6 +105,8 @@ if ($opt_d) {
}
$opt_o ||= "origin";
$opt_s ||= "-";
$opt_a ||= 0;
my $git_tree = $opt_C;
$git_tree ||= ".";
@ -129,6 +131,11 @@ if ($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);
@ -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
unless (-d $git_dir) {
system("git-init-db");
system("git-init");
die "Cannot init the GIT db at $git_tree: $?\n" if $?;
system("git-read-tree");
die "Cannot init an empty tree: $?\n" if $?;
@ -568,9 +575,11 @@ if ($opt_A) {
# run cvsps into a file unless we are getting
# it passed as a file via $opt_P
#
my $cvspsfile;
unless ($opt_P) {
print "Running cvsps...\n" if $opt_v;
my $pid = open(CVSPS,"-|");
my $cvspsfh;
die "Cannot fork: $!\n" unless defined $pid;
unless ($pid) {
my @opt;
@ -583,18 +592,18 @@ unless ($opt_P) {
exec("cvsps","--norc",@opt,"-u","-A",'--root',$opt_d,$cvs_tree);
die "Could not start cvsps: $!\n";
}
my ($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
DIR => File::Spec->tmpdir());
($cvspsfh, $cvspsfile) = tempfile('gitXXXXXX', SUFFIX => '.cvsps',
DIR => File::Spec->tmpdir());
while (<CVSPS>) {
print $cvspsfh $_;
}
close CVSPS;
close $cvspsfh;
$opt_P = $cvspsfile;
} else {
$cvspsfile = $opt_P;
}
open(CVS, "<$opt_P") or die $!;
open(CVS, "<$cvspsfile") or die $!;
## cvsps output:
#---------------------
@ -651,7 +660,7 @@ $ignorebranch{'#CVSPS_NO_BRANCH'} = 1;
sub commit {
if ($branch eq $opt_o && !$index{branch} && !get_headref($branch, $git_dir)) {
# 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';
$index{$branch} = '.git/index';
} else {
@ -824,6 +833,15 @@ while (<CVS>) {
$state = 11;
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}) {
print STDERR "Skipping $branch\n";
$state = 11;
@ -920,6 +938,10 @@ while (<CVS>) {
}
commit() if $branch and $state != 11;
unless ($opt_P) {
unlink($cvspsfile);
}
# The heuristic of repacking every 1024 commits can leave a
# lot of unpacked data. If there is more than 1MB worth of
# not-packed objects, repack once more.

View File

@ -1181,12 +1181,15 @@ sub req_ci
$filename = filecleanup($filename);
my $meta = $updater->getmeta($filename);
unless (defined $meta->{revision}) {
$meta->{revision} = 1;
}
my ( $filepart, $dirpart ) = filenamesplit($filename, 1);
$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 "$filename\n";
@ -2184,7 +2187,10 @@ sub update
# first lets get the commit list
$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}/ )
{
die("Invalid module '$self->{module}'");
@ -2194,6 +2200,10 @@ sub update
my $git_log;
my $lastcommit = $self->_get_prop("last_commit");
if (defined $lastcommit && $lastcommit eq $commitsha1) { # up-to-date
return 1;
}
# Start exclusive lock here...
$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
. git-sh-setup
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
_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"
@ -231,11 +227,12 @@ update_local_ref () {
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)
;;
esac
fi
# If --tags (and later --heads or --all) is specified, then we are
# 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>+'
SUBDIRECTORY_OK=Yes
. git-sh-setup
set_reflog_action "merge $*"
require_work_tree
cd_to_toplevel
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='
'
@ -298,24 +301,30 @@ f,*)
;;
?,1,*,)
# We are not doing octopus, not fast forward, and have only
# one common. See if it is really trivial.
git var GIT_COMMITTER_IDENT >/dev/null || exit
echo "Trying really trivial in-index merge..."
# one common.
git-update-index --refresh 2>/dev/null
if git-read-tree --trivial -m -u -v $common $head "$1" &&
result_tree=$(git-write-tree)
then
echo "Wonderful."
result_commit=$(
echo "$merge_msg" |
git-commit-tree $result_tree -p HEAD -p "$1"
) || exit
finish "$result_commit" "In-index merge"
dropsave
exit 0
fi
echo "Nope."
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" &&
result_tree=$(git-write-tree)
then
echo "Wonderful."
result_commit=$(
echo "$merge_msg" |
git-commit-tree $result_tree -p HEAD -p "$1"
) || exit
finish "$result_commit" "In-index merge"
dropsave
exit 0
fi
echo "Nope."
esac
;;
*)
# 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")
report(2, "gdir:", self.gitdir)
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:
self.cdup = self.get_single("rev-parse --show-cdup")
if self.cdup != "":

View File

@ -6,11 +6,14 @@
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.'
SUBDIRECTORY_OK=Yes
. git-sh-setup
set_reflog_action "pull $*"
require_work_tree
cd_to_toplevel
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=
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
'
SUBDIRECTORY_OK=Yes
. git-sh-setup
set_reflog_action rebase
require_work_tree
cd_to_toplevel
RESOLVEMSG="
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
)
fi
git-prune-packed
git-prune-packed $quiet
fi
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
. git-sh-setup
set_reflog_action "reset $*"
require_work_tree
update= reset_type=--mixed
unset rev
@ -44,17 +45,15 @@ if test $# != 0
then
test "$reset_type" == "--mixed" ||
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
exit
fi
TOP=$(git-rev-parse --show-cdup)
if test ! -z "$TOP"
then
cd "$TOP"
fi
cd_to_toplevel
if test "$reset_type" = "--hard"
then

View File

@ -16,9 +16,14 @@ case "$0" in
me=cherry-pick
USAGE='[--edit] [-n] [-r] [-x] <commit-ish>' ;;
* )
die "What are you talking about?" ;;
echo >&2 "What are you talking about?"
exit 1 ;;
esac
SUBDIRECTORY_OK=Yes ;# we will cd up
. git-sh-setup
require_work_tree
cd_to_toplevel
no_commit=
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 &&
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
# the difference it introduces since its first parent "prev"
# 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
revert)
git-rev-list --pretty=oneline --max-count=1 $commit |
git show -s --pretty=oneline --encoding="$encoding" $commit |
sed -e '
s/^[^ ]* /Revert "/
s/$/"/'
s/$/"/
'
echo
echo "This reverts commit $commit."
test "$rev" = "$commit" ||
@ -115,14 +123,17 @@ cherry-pick)
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"`
eval "$set_author_env"
export GIT_AUTHOR_NAME
export GIT_AUTHOR_EMAIL
export GIT_AUTHOR_DATE
git-cat-file commit $commit | sed -e '1,/^$/d'
echo "$logmsg" |
sed -e '1,/^$/d' -e 's/^ //'
case "$replay" in
'')
echo "(cherry picked from commit $commit)"

View File

@ -402,6 +402,15 @@ sub make_message_id
$cc = "";
$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
{
my @recipients = unique_email_list(@to);
@ -555,6 +564,7 @@ foreach my $t (@files) {
}
close F;
if (defined $author_not_sender) {
$author_not_sender = unquote_rfc2047($author_not_sender);
$message = "From: $author_not_sender\n\n$message";
}

View File

@ -28,6 +28,30 @@ set_reflog_action() {
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" ]
then
LONG_USAGE="Usage: $0 $USAGE"
@ -47,7 +71,11 @@ esac
if [ -z "$SUBDIRECTORY_OK" ]
then
: ${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
GIT_DIR=$(git-rev-parse --git-dir) || exit
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,
$_merge, $_strategy, $_dry_run, $_ignore_nodate, $_non_recursive,
$_username, $_config_dir, $_no_auth_cache,
$_pager, $_color);
$_pager, $_color, $_prefix);
my (@_branch_from, %tree_map, %users, %rusers, %equiv);
my ($_svn_can_do_switch);
my @repo_path_split_cache;
@ -134,6 +134,7 @@ my %cmd = (
'username=s' => \$_username,
'config-dir=s' => \$_config_dir,
'no-auth-cache' => \$_no_auth_cache,
'prefix=s' => \$_prefix,
} ],
'multi-fetch' => [ \&multi_fetch,
'Fetch multiple trees (like git-svnimport)',
@ -285,7 +286,7 @@ sub init {
$SVN_URL = $url;
unless (-d $GIT_DIR) {
my @init_db = ('init-db');
my @init_db = ('init');
push @init_db, "--template=$_template" if defined $_template;
push @init_db, "--shared" if defined $_shared;
command_noisy(@init_db);
@ -595,8 +596,9 @@ sub multi_init {
command_noisy('repo-config', 'svn.trunk', $trunk_url);
}
}
complete_url_ls_init($url, $_branches, '--branches/-b', '');
complete_url_ls_init($url, $_tags, '--tags/-t', 'tags/');
$_prefix = '' unless defined $_prefix;
complete_url_ls_init($url, $_branches, '--branches/-b', $_prefix);
complete_url_ls_init($url, $_tags, '--tags/-t', $_prefix . 'tags/');
}
sub multi_fetch {
@ -1084,7 +1086,7 @@ sub graft_merge_msg {
my ($grafts, $l_map, $u, $p, @re) = @_;
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)) {
foreach my $re (@re) {
my (@br) = ($c->{m} =~ /$re/g);

View File

@ -285,7 +285,7 @@ my $last_rev = "";
my $last_branch;
my $current_rev = $opt_s || 1;
unless(-d $git_dir) {
system("git-init-db");
system("git-init");
die "Cannot init the GIT db at $git_tree: $?\n" if $?;
system("git-read-tree");
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;
my $from_rev;
my $to_rev = $current_rev;
my $to_rev = $current_rev - 1;
while ($to_rev < $opt_l) {
$from_rev = $to_rev;
$from_rev = $to_rev + 1;
$to_rev = $from_rev + $repack_after;
$to_rev = $opt_l if $opt_l < $to_rev;
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 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)
{
@ -208,7 +213,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
int (*fn)(int, const char **, const char *);
int option;
} commands[] = {
{ "add", cmd_add, RUN_SETUP },
{ "add", cmd_add, RUN_SETUP | NOT_BARE },
{ "annotate", cmd_annotate, },
{ "apply", cmd_apply },
{ "archive", cmd_archive },
@ -220,6 +225,7 @@ static void handle_internal_command(int argc, const char **argv, char **envp)
{ "cherry", cmd_cherry, RUN_SETUP },
{ "commit-tree", cmd_commit_tree, RUN_SETUP },
{ "count-objects", cmd_count_objects, RUN_SETUP },
{ "describe", cmd_describe, RUN_SETUP },
{ "diff", cmd_diff, RUN_SETUP | USE_PAGER },
{ "diff-files", cmd_diff_files, 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 },
{ "grep", cmd_grep, RUN_SETUP },
{ "help", cmd_help },
{ "init", cmd_init_db },
{ "init-db", cmd_init_db },
{ "log", cmd_log, RUN_SETUP | USE_PAGER },
{ "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 },
{ "mailsplit", cmd_mailsplit },
{ "merge-file", cmd_merge_file },
{ "mv", cmd_mv, RUN_SETUP },
{ "mv", cmd_mv, RUN_SETUP | NOT_BARE },
{ "name-rev", cmd_name_rev, RUN_SETUP },
{ "pack-objects", cmd_pack_objects, RUN_SETUP },
{ "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 },
{ "rev-list", cmd_rev_list, RUN_SETUP },
{ "rev-parse", cmd_rev_parse, RUN_SETUP },
{ "rm", cmd_rm, RUN_SETUP },
{ "runstatus", cmd_runstatus, RUN_SETUP },
{ "rm", cmd_rm, RUN_SETUP | NOT_BARE },
{ "runstatus", cmd_runstatus, RUN_SETUP | NOT_BARE },
{ "shortlog", cmd_shortlog, RUN_SETUP | USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "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();
if (p->option & USE_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");
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'},
hash_base=>$hash, file_name=>$diff{'file'})},
"blob") . " | ";
"blob");
print "</td>\n";
} elsif ($diff{'status'} eq "D") { # deleted
@ -2412,7 +2412,6 @@ sub git_patchset_body {
push @diff_header, $patch_line;
}
#last PATCH unless $patch_line;
my $last_patch_line = $patch_line;
# check if current patch belong to current raw line
@ -2522,7 +2521,10 @@ sub git_patchset_body {
# from-file/to-file diff header
$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 /);
#assert($patch_line =~ m/^---/) if DEBUG;
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";
$patch_line = <$fd>;
#last PATCH unless $patch_line;
chomp $patch_line;
#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;
struct object_request *obj_req = (struct object_request *)data;
do {
ssize_t retval = write(obj_req->local,
ssize_t retval = xwrite(obj_req->local,
(char *) ptr + posn, size - posn);
if (retval < 0)
return posn;
@ -175,7 +175,7 @@ static void start_object_request(struct object_request *obj_req)
prevlocal = open(prevfile, O_RDONLY);
if (prevlocal != -1) {
do {
prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
if (prev_read>0) {
if (fwrite_sha1_file(prev_buf,
1,
@ -809,6 +809,7 @@ static int fetch_pack(struct alt_base *repo, unsigned char *sha1)
return error("Unable to start request");
}
target->pack_size = ftell(packfile);
fclose(packfile);
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;
struct transfer_request *request = (struct transfer_request *)data;
do {
ssize_t retval = write(request->local_fileno,
ssize_t retval = xwrite(request->local_fileno,
(char *) ptr + posn, size - posn);
if (retval < 0)
return posn;
@ -288,7 +288,7 @@ static void start_fetch_loose(struct transfer_request *request)
prevlocal = open(prevfile, O_RDONLY);
if (prevlocal != -1) {
do {
prev_read = read(prevlocal, prev_buf, PREV_BUF_SIZE);
prev_read = xread(prevlocal, prev_buf, PREV_BUF_SIZE);
if (prev_read>0) {
if (fwrite_sha1_file(prev_buf,
1,
@ -770,11 +770,14 @@ static void finish_request(struct transfer_request *request)
request->url, curl_errorstr);
remote->can_update_info_refs = 0;
} else {
off_t pack_size = ftell(request->local_stream);
fclose(request->local_stream);
request->local_stream = NULL;
if (!move_temp_to_file(request->tmpfile,
request->filename)) {
target = (struct packed_git *)request->userData;
target->pack_size = pack_size;
lst = &remote->packs;
while (*lst != target)
lst = &((*lst)->next);

View File

@ -224,7 +224,7 @@ socket_perror( const char *func, Socket_t *sock, int ret )
static int
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) {
socket_perror( "read", sock, n );
close( sock->fd );
@ -236,7 +236,7 @@ socket_read( Socket_t *sock, char *buf, int len )
static int
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) {
socket_perror( "write", sock, n );
close( sock->fd );
@ -390,7 +390,7 @@ arc4_init( void )
fprintf( stderr, "Fatal: no random number source available.\n" );
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" );
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 */
if (lseek(output_fd, 0, SEEK_SET) != 0)
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));
hdr.hdr_entries = htonl(nr_objects);
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];
int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
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

View File

@ -184,7 +184,7 @@ int fetch_ref(char *ref, unsigned char *sha1)
fprintf(stderr, "cannot open %s\n", filename);
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);
fprintf(stderr, "cannot read from %s\n", filename);
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,
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)
{
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);
if (!ce)
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 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,
struct tree *common,
struct tree *head,
@ -216,11 +164,6 @@ static int git_merge_trees(int index_only,
struct object_list *trees = NULL;
struct unpack_trees_options opts;
if (!cache_dirty) {
read_cache_from(current_index_file);
cache_dirty = 1;
}
memset(&opts, 0, sizeof(opts));
if (index_only)
opts.index_only = 1;
@ -236,39 +179,37 @@ static int git_merge_trees(int index_only,
rc = unpack_trees(trees, &opts);
cache_tree_free(&active_cache_tree);
cache_dirty = 1;
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)
{
struct tree *result = NULL;
if (cache_dirty) {
unsigned i;
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce))
return NULL;
}
} else
read_cache_from(current_index_file);
if (unmerged_index())
return NULL;
if (!active_cache_tree)
active_cache_tree = cache_tree();
if (!cache_tree_fully_valid(active_cache_tree) &&
cache_tree_update(active_cache_tree,
active_cache, active_nr, 0, 0) < 0)
cache_tree_update(active_cache_tree,
active_cache, active_nr, 0, 0) < 0)
die("error building trees");
result = lookup_tree(active_cache_tree->sha1);
flush_cache();
cache_dirty = 0;
return result;
}
@ -331,10 +272,7 @@ static struct path_list *get_unmerged(void)
int i;
unmerged->strdup_paths = 1;
if (!cache_dirty) {
read_cache_from(current_index_file);
cache_dirty++;
}
for (i = 0; i < active_nr; i++) {
struct path_list_item *item;
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;
if (update_cache) {
if (!cache_dirty)
read_cache_from(current_index_file);
cache_dirty++;
if (remove_file_from_cache(path))
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)
{
while (size > 0) {
long ret = xwrite(fd, buf, size);
long ret = write_in_full(fd, buf, size);
if (ret < 0) {
/* Ignore 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(&b_by_dst, 0);
if (cache_dirty)
flush_cache();
return clean_merge;
}
@ -1083,9 +1016,6 @@ static int process_entry(const char *path, struct stage_data *entry,
} else
die("Fatal merge failure, shouldn't happen.");
if (cache_dirty)
flush_cache();
return clean_merge;
}
@ -1110,9 +1040,7 @@ static int merge_trees(struct tree *head,
sha1_to_hex(head->object.sha1),
sha1_to_hex(merge->object.sha1));
*result = git_write_tree();
if (!*result) {
if (unmerged_index()) {
struct path_list *entries, *re_head, *re_merge;
int i;
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(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;
}
@ -1173,10 +1096,10 @@ static int merge(struct commit *h1,
const char *branch1,
const char *branch2,
int call_depth /* =0 */,
struct commit *ancestor /* =None */,
struct commit_list *ca,
struct commit **result)
{
struct commit_list *ca = NULL, *iter;
struct commit_list *iter;
struct commit *merged_common_ancestors;
struct tree *mrtree;
int clean;
@ -1185,10 +1108,10 @@ static int merge(struct commit *h1,
output_commit_title(h1);
output_commit_title(h2);
if (ancestor)
commit_list_insert(ancestor, &ca);
else
ca = reverse_commit_list(get_merge_bases(h1, h2, 1));
if (!ca) {
ca = get_merge_bases(h1, h2, 1);
ca = reverse_commit_list(ca);
}
output("found %u common ancestor(s):", commit_list_count(ca));
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
* "conflicts" were already resolved.
*/
discard_cache();
merge(merged_common_ancestors, iter->item,
"Temporary merge branch 1",
"Temporary merge branch 2",
@ -1226,25 +1150,21 @@ static int merge(struct commit *h1,
die("merge returned no commit");
}
discard_cache();
if (call_depth == 0) {
setup_index(0 /* $GIT_DIR/index */);
read_cache();
index_only = 0;
} else {
setup_index(1 /* temporary index */);
git_read_tree(h1->tree);
} else
index_only = 1;
}
clean = merge_trees(h1->tree, h2->tree, merged_common_ancestors->tree,
branch1, branch2, &mrtree);
if (!ancestor && (clean || index_only)) {
if (index_only) {
*result = make_virtual_commit(mrtree, "merged tree");
commit_list_insert(h1, &(*result)->parents);
commit_list_insert(h2, &(*result)->parents->next);
} else
*result = NULL;
}
return clean;
}
@ -1280,19 +1200,16 @@ static struct commit *get_ref(const char *ref)
int main(int argc, char *argv[])
{
static const char *bases[2];
static const char *bases[20];
static unsigned bases_count = 0;
int i, clean;
const char *branch1, *branch2;
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 */
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)
die("Usage: %s <base>... -- <head> <remote> ...\n", argv[0]);
@ -1316,18 +1233,18 @@ int main(int argc, char *argv[])
branch2 = better_branch_name(branch2);
printf("Merging %s with %s\n", branch1, branch2);
if (bases_count == 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);
index_fd = hold_lock_file_for_update(lock, get_index_file(), 1);
if (cache_dirty)
flush_cache();
for (i = 0; i < bases_count; i++) {
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;
}
/*
vim: sw=8 noet
*/

View File

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

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

View File

@ -63,7 +63,7 @@ for doing easily operations which are not totally trivial to do over
the generic command interface.
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.
(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

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));
}
cache_mmap = MAP_FAILED;
if (!fstat(fd, &st)) {
cache_mmap_size = st.st_size;
errno = EINVAL;
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);
if (cache_mmap == MAP_FAILED)
die("index file mmap failed (%s)", strerror(errno));
hdr = cache_mmap;
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;
if (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;
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 */
if (left + 20 > WRITE_BUFFER_SIZE) {
if (write(fd, write_buffer, left) != left)
if (write_in_full(fd, write_buffer, left) != left)
return -1;
left = 0;
}
@ -927,7 +927,7 @@ static int ce_flush(SHA_CTX *context, int fd)
/* Append the SHA1 signature at the end */
SHA1_Final(write_buffer + left, context);
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)
@ -1010,7 +1010,7 @@ int write_cache(int newfd, struct cache_entry **cache, int entries)
if (data &&
!write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sz) &&
!ce_write(&c, newfd, data, sz))
;
free(data);
else {
free(data);
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);
if (fd < 0)
return NULL;
len = read(fd, buffer, sizeof(buffer)-1);
len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd);
/*
@ -332,7 +332,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master)
}
lockpath = mkpath("%s.lock", git_HEAD);
fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
written = write(fd, ref, len);
written = write_in_full(fd, ref, len);
close(fd);
if (written != len) {
unlink(lockpath);
@ -923,6 +923,9 @@ static int log_ref_write(struct ref_lock *lock,
char *logrec;
const char *committer;
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
if (log_all_ref_updates &&
(!strncmp(lock->ref_name, "refs/heads/", 11) ||
!strncmp(lock->ref_name, "refs/remotes/", 13))) {
@ -968,7 +971,7 @@ static int log_ref_write(struct ref_lock *lock,
sha1_to_hex(sha1),
committer);
}
written = len <= maxlen ? write(logfd, logrec, len) : -1;
written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
free(logrec);
close(logfd);
if (written != len)
@ -987,8 +990,8 @@ int write_ref_sha1(struct ref_lock *lock,
unlock_ref(lock);
return 0;
}
if (write(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
write(lock->lock_fd, &term, 1) != 1
if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
write_in_full(lock->lock_fd, &term, 1) != 1
|| close(lock->lock_fd) < 0) {
error("Couldn't write %s", lock->lk->filename);
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);
if (!st.st_size)
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);
lastrec = NULL;
@ -1097,7 +1100,7 @@ int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *
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;
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);
logfp = fopen(logfile, "r");
if (!logfp)
return;
return -1;
while (fgets(buf, sizeof(buf), logfp)) {
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 */
len = strlen(buf);
if (len < 83 || buf[len-1] != '\n' ||
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? */
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);
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);
/* iterate over reflog entries */
typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, char *, void *);
void for_each_reflog_ent(const char *ref, each_reflog_ent_fn fn, void *cb_data);
typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
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. **/
extern int check_ref_format(const char *target);

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