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

This commit is contained in:
Johannes Sixt
2008-05-02 22:06:47 +02:00
119 changed files with 2294 additions and 549 deletions

View File

@@ -0,0 +1,44 @@
GIT v1.5.5.1 Release Notes
==========================
Fixes since v1.5.5
------------------
* "git archive --prefix=$path/" mishandled gitattributes.
* "git fetch -v" that fetches into FETCH_HEAD did not report the summary
the same way as done for updating the tracking refs.
* "git svn" misbehaved when the configuration file customized the "git
log" output format using format.pretty.
* "git submodule status" leaked an unnecessary error message.
* "git log --date-order --topo-order" did not override the earlier
date-order with topo-order as expected.
* "git bisect good $this" did not check the validity of the revision
given properly.
* "url.<there>.insteadOf" did not work correctly.
* "git clean" ran inside subdirectory behaved as if the directory was
explicitly specified for removal by the end user from the top level.
* "git bisect" from a detached head leaked an unnecessary error message.
* "git bisect good $a $b" when $a is Ok but $b is bogus should have
atomically failed before marking $a as good.
* "git fmt-merge-msg" did not clean up leading empty lines from commit
log messages like "git log" family does.
* "git am" recorded a commit with empty Subject: line without
complaining.
* when given a commit log message whose first paragraph consists of
multiple lines, "git rebase" squashed it into a single line.
* "git remote add $bogus_name $url" did not complain properly.
Also comes with various documentation updates.

View File

@@ -0,0 +1,59 @@
GIT v1.5.6 Release Notes
========================
Updates since v1.5.5
--------------------
(subsystems)
(portability)
(performance)
* "git rebase --onto $there $from $branch" used to switch to the tip of
$branch only to immediately reset back to $from, smudging work tree
files unnecessarily. This has been optimized.
(usability, bells and whistles)
* "git add -p" (and the "patch" subcommand of "git add -i") can choose to
apply (or not apply) mode changes independently from contents changes.
* "git bisect help" gives longer and more helpful usage information.
* "git diff/log --dirstat" output is consistent between binary and textual
changes.
* "git gc --auto" honors a new pre-aut-gc hook to temporarily disable it.
* "git log --pretty=tformat:<custom format>" gives a LF after each entry,
instead of giving a LF between each pair of entries which is how
"git log --pretty=format:<custom format>" works.
* "git send-email" now can send out messages outside a git repository.
* "git status" can optionally include output from "git submodule
summary".
* "gitweb" can read from a system-wide configuration file.
(internal)
* "git unpack-objects" and "git receive-pack" is now more strict about
detecting breakage in the objects they receive over the wire.
Fixes since v1.5.5
------------------
All of the fixes in v1.5.5 maintenance series are included in
this release, unless otherwise noted.
--
exec >/var/tmp/1
O=v1.5.5-56-g5f0734f
echo O=`git describe refs/heads/master`
git shortlog --no-merges $O..refs/heads/master ^refs/heads/maint

View File

@@ -234,7 +234,13 @@ core.worktree::
used in combination with repositories found automatically in
a .git directory (i.e. $GIT_DIR is not set).
This can be overridden by the GIT_WORK_TREE environment
variable and the '--work-tree' command line option.
variable and the '--work-tree' command line option. It can be
a absolute path or relative path to the directory specified by
--git-dir or GIT_DIR.
Note: If --git-dir or GIT_DIR are specified but none of
--work-tree, GIT_WORK_TREE and core.worktree is specified,
the current working directory is regarded as the top directory
of your working tree.
core.logAllRefUpdates::
Enable the reflog. Updates to a ref <ref> is logged to the file
@@ -261,7 +267,12 @@ 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 linkgit:git-init[1]. False by default.
reported by umask(2). When '0xxx', where '0xxx' is an octal number,
files in the repository will have this mode value. '0xxx' will override
user's umask value, and thus, users with a safe umask (0077) can use
this option. Examples: '0660' is equivalent to 'group'. '0640' is a
repository that is group-readable but not group-writable.
See linkgit:git-init[1]. False by default.
core.warnAmbiguousRefs::
If true, git will warn you if the ref name you passed it is ambiguous
@@ -991,6 +1002,12 @@ imap::
The configuration variables in the 'imap' section are described
in linkgit:git-imap-send[1].
receive.fsckObjects::
If it is set to true, git-receive-pack will check all received
objects. It will abort in the case of a malformed object or a
broken link. The result of an abort are only dangling objects.
Defaults to false.
receive.unpackLimit::
If the number of objects received in a push is below this
limit then the objects will be unpacked into loose object

View File

@@ -535,18 +535,18 @@ with the associated patches use the more complex (and much more
powerful)
----------------
$ git-whatchanged -p --root
$ git-whatchanged -p
----------------
and you will see exactly what has changed in the repository over its
short history.
[NOTE]
The `\--root` flag is a flag to `git-diff-tree` to tell it to
show the initial aka 'root' commit too. Normally you'd probably not
want to see the initial import diff, but since the tutorial project
was started from scratch and is so small, we use it to make the result
a bit more interesting.
When using the above two commands, the initial commit will be shown.
If this is a problem because it is huge, you can hide it by setting
the log.showroot configuration variable to false. Having this, you
can still show it for each command just adding the `\--root` option,
which is a flag for `git-diff-tree` accepted by both commands.
With that, you should now be having some inkling of what git does, and
can explore on your own.

View File

@@ -8,7 +8,8 @@ designating a single shared repository which people can synchronize with;
this document explains how to do that.
Some basic familiarity with git is required. This
link:tutorial.html[tutorial introduction to git] should be sufficient.
link:tutorial.html[tutorial introduction to git] and the
link:glossary.html[git glossary] should be sufficient.
Developing against a shared repository
--------------------------------------

View File

@@ -58,6 +58,14 @@ endif::git-format-patch[]
number of modified files, as well as number of added and deleted
lines.
--dirstat[=limit]::
Output only the sub-directories that are impacted by a diff,
and to what degree they are impacted. You can override the
default cut-off in percent (3) by "--dirstat=limit". If you
want to enable "cumulative" directory statistics, you can use
the "--cumulative" flag, which adds up percentages recursively
even when they have been already reported for a sub-directory.
--summary::
Output a condensed summary of extended header information
such as creations, renames and mode changes.
@@ -75,7 +83,8 @@ endif::git-format-patch[]
Show only names of changed files.
--name-status::
Show only names and status of changed files.
Show only names and status of changed files. See the description
of the `--diff-filter` option on what the status letters mean.
--color::
Show colored diff.

View File

@@ -48,14 +48,12 @@ $ git gc <3>
repository health reasonably well.
<2> check how many loose objects there are and how much
disk space is wasted by not repacking.
<3> repacks the local repository and performs other housekeeping tasks. Running
without `--prune` is a safe operation even while other ones are in progress.
<3> repacks the local repository and performs other housekeeping tasks.
Repack a small project into single pack.::
+
------------
$ git gc <1>
$ git gc --prune
------------
+
<1> pack all the objects reachable from the refs into one pack,
@@ -182,7 +180,7 @@ $ git pull <3>
$ git log -p ORIG_HEAD.. arch/i386 include/asm-i386 <4>
$ git pull git://git.kernel.org/pub/.../jgarzik/libata-dev.git ALL <5>
$ git reset --hard ORIG_HEAD <6>
$ git gc --prune <7>
$ git gc <7>
$ git fetch --tags <8>
------------
+

View File

@@ -15,6 +15,7 @@ DESCRIPTION
The command takes various subcommands, and different options depending
on the subcommand:
git bisect help
git bisect start [<bad> [<good>...]] [--] [<paths>...]
git bisect bad [<rev>]
git bisect good [<rev>...]
@@ -29,6 +30,12 @@ This command uses 'git-rev-list --bisect' option to help drive the
binary search process to find which change introduced a bug, given an
old "good" commit object name and a later "bad" commit object name.
Getting help
~~~~~~~~~~~~
Use "git bisect" to get a short usage description, and "git bisect
help" or "git bisect -h" to get a long usage description.
Basic bisect commands: start, bad, good
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -47,7 +47,7 @@ OPTIONS
by linkgit:git-check-ref-format[1]. Some of these checks
may restrict the characters allowed in a branch name.
--track::
-t, --track::
When creating a new branch, set up configuration so that git-pull
will automatically retrieve data from the start point, which must be
a branch. Use this if you always pull from the same upstream branch

View File

@@ -65,10 +65,13 @@ OPTIONS
+
*NOTE*: this is a possibly dangerous operation; do *not* use
it unless you understand what it does. If you clone your
repository using this option, then delete branches in the
source repository and then run linkgit:git-gc[1] using the
'--prune' option in the source repository, it may remove
objects which are referenced by the cloned repository.
repository using this option and then delete branches (or use any
other git command that makes any existing commit unreferenced) in the
source repository, some objects may become unreferenced (or dangling).
These objects may be removed by normal git operations (such as git-commit[1])
which automatically call git-gc[1]. If these objects are removed and
were referenced by the cloned repository, then the cloned repository
will become corrupt.
@@ -79,6 +82,8 @@ objects which are referenced by the cloned repository.
an already existing repository as an alternate will
require fewer objects to be copied from the repository
being cloned, reducing network and local storage costs.
+
*NOTE*: see NOTE to --shared option.
--quiet::
-q::

View File

@@ -139,6 +139,17 @@ but can be used to amend a merge commit.
as well. This is usually not what you want unless you
are concluding a conflicted merge.
-o|--only::
Make a commit only from the paths specified on the
command line, disregarding any contents that have been
staged so far. This is the default mode of operation of
'git commit' if any paths are given on the command line,
in which case this option can be omitted.
If this option is specified together with '--amend', then
no paths need be specified, which can be used to amend
the last commit without committing changes that have
already been staged.
-u|--untracked-files::
Show all untracked files, also those in uninteresting
directories, in the "Untracked files:" section of commit

View File

@@ -144,6 +144,8 @@ See also <<FILES>>.
"auto". If `stdout-is-tty` is missing, then checks the standard
output of the command itself, and exits with status 0 if color
is to be used, or exits with status 1 otherwise.
When the color setting for `name` is undefined, the command uses
`color.ui` as fallback.
--get-color name default::

View File

@@ -110,7 +110,9 @@ cvs -d ":ext;CVS_SERVER=git-cvsserver:user@server/path/repo.git" co <HEAD_name>
------
This has the advantage that it will be saved in your 'CVS/Root' files and
you don't need to worry about always setting the correct environment
variable.
variable. SSH users restricted to git-shell don't need to override the default
with CVS_SERVER (and shouldn't) as git-shell understands `cvs` to mean
git-cvsserver and pretends that the other end runs the real cvs better.
--
2. For each repo that you want accessible from CVS you need to edit config in
the repo and add the following section.
@@ -141,25 +143,29 @@ allowing access over SSH.
enabled=1
------
--
3. On the client machine you need to set the following variables.
CVSROOT should be set as per normal, but the directory should point at the
appropriate git repo. For example:
3. If you didn't specify the CVSROOT/CVS_SERVER directly in the checkout command,
automatically saving it in your 'CVS/Root' files, then you need to set them
explicitly in your environment. CVSROOT should be set as per normal, but the
directory should point at the appropriate git repo. As above, for SSH clients
_not_ restricted to git-shell, CVS_SERVER should be set to git-cvsserver.
+
--
For SSH access, CVS_SERVER should be set to git-cvsserver
Example:
------
export CVSROOT=:ext:user@server:/var/git/project.git
export CVS_SERVER=git-cvsserver
------
--
4. For SSH clients that will make commits, make sure their .bashrc file
sets the GIT_AUTHOR and GIT_COMMITTER variables.
4. For SSH clients that will make commits, make sure their server-side
.ssh/environment files (or .bashrc, etc., according to their specific shell)
export appropriate values for GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL,
GIT_COMMITTER_NAME, and GIT_COMMITTER_EMAIL. For SSH clients whose login
shell is bash, .bashrc may be a reasonable alternative.
5. Clients should now be able to check out the project. Use the CVS 'module'
name to indicate what GIT 'head' you want to check out. Example:
name to indicate what GIT 'head' you want to check out. This also sets the
name of your newly checked-out directory, unless you tell it otherwise with
`-d <dir_name>`. For example, this checks out 'master' branch to the
`project-master` directory:
+
------
cvs co -d project-master master

View File

@@ -22,7 +22,8 @@ OPTIONS
An object to treat as the head of an unreachability trace.
+
If no objects are given, git-fsck defaults to using the
index file and all SHA1 references in .git/refs/* as heads.
index file, all SHA1 references in .git/refs/*, and all reflogs (unless
--no-reflogs is given) as heads.
--unreachable::
Print out objects that exist but that aren't readable from any

View File

@@ -104,6 +104,21 @@ The optional configuration variable 'gc.pruneExpire' controls how old
the unreferenced loose objects have to be before they are pruned. The
default is "2 weeks ago".
Notes
-----
git-gc tries very hard to be safe about the garbage it collects. In
particular, it will keep not only objects referenced by your current set
of branches and tags, but also objects referenced by the index, remote
tracking branches, refs saved by linkgit:git-filter-branch[1] in
refs/original/, or reflogs (which may references commits in branches
that were later amended or rewound).
If you are expecting some objects to be collected and they aren't, check
all of those locations and decide whether it makes sense in your case to
remove those references.
See Also
--------
linkgit:git-prune[1]

View File

@@ -31,7 +31,7 @@ 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}]::
--shared[={false|true|umask|group|all|world|everybody|0xxx}]::
Specify that the git repository is to be shared amongst several users. This
allows users belonging to the same group to push into that
@@ -52,6 +52,12 @@ is given:
- 'all' (or 'world' or 'everybody'): Same as 'group', but make the repository
readable by all users.
- '0xxx': '0xxx' is an octal number and each file will have mode '0xxx'
Any option except 'umask' can be set using this option. '0xxx' will
override users umask(2) value, and thus, users with a safe umask (0077)
can use this option. '0640' will create a repository which is group-readable
but not writable. '0660' is equivalent to 'group'.
By default, the configuration flag receive.denyNonFastForwards is enabled
in shared repositories, so that you cannot force a non fast-forwarding push
into it.

View File

@@ -13,6 +13,9 @@ SYNOPSIS
DESCRIPTION
-----------
NOTE: In most cases, users should run linkgit:git-gc[1], which calls
git-prune. See the section "NOTES", below.
This runs `git-fsck --unreachable` using all the refs
available in `$GIT_DIR/refs`, optionally with additional set of
objects specified on the command line, and prunes all
@@ -50,6 +53,23 @@ borrows from your repository via its
$ git prune $(cd ../another && $(git-rev-parse --all))
------------
Notes
-----
In most cases, users will not need to call git-prune directly, but
should instead call linkgit:git-gc[1], which handles pruning along with
many other housekeeping tasks.
For a description of which objects are considered for pruning, see
git-fsck's --unreachable option.
See Also
--------
linkgit:git-fsck[1],
linkgit:git-gc[1],
linkgit:git-reflog[1]
Author
------
Written by Linus Torvalds <torvalds@osdl.org>

View File

@@ -35,14 +35,15 @@ OPTIONS
by the source ref, followed by a colon `:`, followed by
the destination ref.
+
The <src> side can be an
arbitrary "SHA1 expression" that can be used as an
argument to `git-cat-file -t`. E.g. `master~4` (push
four parents before the current master head).
The <src> side represents the source branch (or arbitrary
"SHA1 expression", such as `master~4` (four parents before the
tip of `master` branch); see linkgit:git-rev-parse[1]) that you
want to push. The <dst> side represents the destination location.
+
The local ref that matches <src> is used
to fast forward the remote ref that matches <dst>. If
the optional plus `+` is used, the remote ref is updated
to fast forward the remote ref that matches <dst> (or, if no <dst> was
specified, the same ref that <src> referred to locally). If
the optional leading plus `+` is used, the remote ref is updated
even if it does not result in a fast forward update.
+
Note: If no explicit refspec is found, (that is neither
@@ -165,7 +166,8 @@ git push origin master::
Find a ref that matches `master` in the source repository
(most likely, it would find `refs/heads/master`), and update
the same ref (e.g. `refs/heads/master`) in `origin` repository
with it.
with it. If `master` did not exist remotely, it would be
created.
git push origin :experimental::
Find a ref that matches `experimental` in the `origin` repository
@@ -179,9 +181,10 @@ git push origin master:satellite/master::
git push origin master:refs/heads/experimental::
Create the branch `experimental` in the `origin` repository
by copying the current `master` branch. This form is usually
needed to create a new branch in the remote repository as
there is no `experimental` branch to match.
by copying the current `master` branch. This form is only
needed to create a new branch or tag in the remote repository when
the local name and the remote name are different; otherwise,
the ref name on its own will work.
Author
------

View File

@@ -24,7 +24,7 @@ OPTIONS
URL to include in the summary.
<end>::
Commit to send at; defaults to HEAD.
Commit to end at; defaults to HEAD.
Author
------

View File

@@ -52,6 +52,11 @@ OPTIONS
The parameter given must be usable as a single, valid
object name. Otherwise barf and abort.
-q, --quiet::
Only meaningful in `--verify` mode. Do not output an error
message if the first argument is not a valid object name;
instead exit with non-zero status silently.
--sq::
Usually the output is made one line per flag and
parameter. This option makes output a single line,

View File

@@ -11,28 +11,37 @@ SYNOPSIS
DESCRIPTION
-----------
Remove files from the working tree and from the index. The
files have to be identical to the tip of the branch, and no
updates to its contents must have been placed in the staging
area (aka index). When --cached is given, the staged content has to
match either the tip of the branch *or* the file on disk.
Remove files from the index, or from the working tree and the index.
`git rm` will not remove a file from just your working directory.
(There is no option to remove a file only from the work tree
and yet keep it in the index; use `/bin/rm` if you want to do that.)
The files being removed have to be identical to the tip of the branch,
and no updates to their contents can be staged in the index,
though that default behavior can be overridden with the `-f` option.
When '--cached' is given, the staged content has to
match either the tip of the branch or the file on disk,
allowing the file to be removed from just the index.
OPTIONS
-------
<file>...::
Files to remove. Fileglobs (e.g. `*.c`) can be given to
remove all matching files. Also a leading directory name
(e.g. `dir` to add `dir/file1` and `dir/file2`) can be
given to remove all files in the directory, recursively,
but this requires `-r` option to be given for safety.
remove all matching files. If you want git to expand
file glob characters, you may need to shell-escape them.
A leading directory name
(e.g. `dir` to remove `dir/file1` and `dir/file2`) can be
given to remove all files in the directory, and recursively
all sub-directories,
but this requires the `-r` option to be explicitly given.
-f::
Override the up-to-date check.
-n, \--dry-run::
Don't actually remove the file(s), just show if they exist in
the index.
Don't actually remove any file(s). Instead, just show
if they exist in the index and would otherwise be removed
by the command.
-r::
Allow recursive removal when a leading directory name is
@@ -44,9 +53,9 @@ OPTIONS
for command-line options).
\--cached::
This option can be used to tell the command to remove
the paths only from the index, leaving working tree
files.
Use this option to unstage and remove paths only from the index.
Working tree files, whether modified or not, will be
left alone.
\--ignore-unmatch::
Exit with a zero status even if no files matched.
@@ -59,11 +68,15 @@ OPTIONS
DISCUSSION
----------
The list of <file> given to the command can be exact pathnames,
file glob patterns, or leading directory name. The command
removes only the paths that is known to git. Giving the name of
The <file> list given to the command can be exact pathnames,
file glob patterns, or leading directory names. The command
removes only the paths that are known to git. Giving the name of
a file that you have not told git about does not remove that file.
File globbing matches across directory boundaries. Thus, given
two directories `d` and `d2`, there is a difference between
using `git rm \'d\*\'` and `git rm \'d/\*\'`, as the former will
also remove all of directory `d2`.
EXAMPLES
--------
@@ -72,11 +85,10 @@ git-rm Documentation/\\*.txt::
`Documentation` directory and any of its subdirectories.
+
Note that the asterisk `\*` is quoted from the shell in this
example; this lets the command include the files from
subdirectories of `Documentation/` directory.
example; this lets git, and not the shell, expand the pathnames
of files and subdirectories under the `Documentation/` directory.
git-rm -f git-*.sh::
Remove all git-*.sh scripts that are in the index.
Because this example lets the shell expand the asterisk
(i.e. you are listing the files explicitly), it
does not remove `subdir/git-foo.sh`.

View File

@@ -8,8 +8,8 @@ git-shortlog - Summarize 'git log' output
SYNOPSIS
--------
[verse]
git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s] [-e]
git-shortlog [-n|--numbered] [-s|--summary] [-e|--email] [<committish>...]
git-log --pretty=short | 'git-shortlog' [-h] [-n] [-s] [-e] [-w]
git-shortlog [-n|--numbered] [-s|--summary] [-e|--email] [-w[<width>[,<indent1>[,<indent2>]]]] [<committish>...]
DESCRIPTION
-----------
@@ -35,6 +35,12 @@ OPTIONS
-e, \--email::
Show the email address of each author.
-w[<width>[,<indent1>[,<indent2>]]]::
Linewrap the output by wrapping each line at `width`. The first
line of each entry is indented by `indent1` spaces, and the second
and subsequent lines are indented by `indent2` spaces. `width`,
`indent1`, and `indent2` default to 76, 6 and 9 respectively.
FILES
-----

View File

@@ -52,6 +52,11 @@ If the config variable `status.relativePaths` is set to false, then all
paths shown are relative to the repository root, not to the current
directory.
If `status.submodulesummary` is set to a non zero number or true (identical
to -1 or an unlimited number), the submodule summary will be enabled and a
summary of commits for modified submodules will be shown (see --summary-limit
option of linkgit:git-submodule[1]).
See Also
--------
linkgit:gitignore[5]

View File

@@ -70,7 +70,7 @@ OPTIONS
-n, --summary-limit::
This option is only valid for the summary command.
Limit the summary size (number of commits shown in total).
Giving 0 will disable the summary; a negative number means unlimted
Giving 0 will disable the summary; a negative number means unlimited
(the default). This limit only applies to modified submodules. The
size is always limited to 1 for added/deleted/typechanged submodules.

View File

@@ -11,7 +11,7 @@ SYNOPSIS
[verse]
'git-tag' [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <name> [<head>]
'git-tag' -d <name>...
'git-tag' [-n [<num>]] -l [<pattern>]
'git-tag' [-n[<num>]] -l [<pattern>]
'git-tag' -v <name>...
DESCRIPTION
@@ -57,7 +57,7 @@ OPTIONS
-v::
Verify the gpg signature of the given tag names.
-n <num>::
-n<num>::
<num> specifies how many lines from the annotation, if any,
are printed when using -l.
The default is not to print any annotation lines.
@@ -233,14 +233,14 @@ the tag object affects, for example, the ordering of tags in the
gitweb interface.
To set the date used in future tag objects, set the environment
variable GIT_AUTHOR_DATE to one or more of the date and time. The
variable GIT_COMMITTER_DATE to one or more of the date and time. The
date and time can be specified in a number of ways; the most common
is "YYYY-MM-DD HH:MM".
An example follows.
------------
$ GIT_AUTHOR_DATE="2006-10-02 10:31" git tag -s v1.0.1
$ GIT_COMMITTER_DATE="2006-10-02 10:31" git tag -s v1.0.1
------------

View File

@@ -8,7 +8,7 @@ git-unpack-objects - Unpack objects from a packed archive
SYNOPSIS
--------
'git-unpack-objects' [-n] [-q] [-r] <pack-file
'git-unpack-objects' [-n] [-q] [-r] [--strict] <pack-file
DESCRIPTION
@@ -40,6 +40,9 @@ OPTIONS
and make the best effort to recover as many objects as
possible.
--strict::
Don't write objects with broken content or links.
Author
------

View File

@@ -46,8 +46,11 @@ Documentation for older releases are available here:
* link:v1.5.5/git.html[documentation for release 1.5.5]
* release notes for
link:RelNotes-1.5.5.1.txt[1.5.5.1],
link:RelNotes-1.5.5.txt[1.5.5].
* link:v1.5.5.1/git.html[documentation for release 1.5.5.1]
* link:v1.5.4.5/git.html[documentation for release 1.5.4.5]
* release notes for
@@ -140,7 +143,8 @@ help ...'.
--git-dir=<path>::
Set the path to the repository. This can also be controlled by
setting the GIT_DIR environment variable.
setting the GIT_DIR environment variable. It can be an absolute
path or relative path to current working directory.
--work-tree=<path>::
Set the path to the working tree. The value will not be
@@ -148,7 +152,12 @@ help ...'.
a .git directory (i.e. $GIT_DIR is not set).
This can also be controlled by setting the GIT_WORK_TREE
environment variable and the core.worktree configuration
variable.
variable. It can be an absolute path or relative path to
the directory specified by --git-dir or GIT_DIR.
Note: If --git-dir or GIT_DIR are specified but none of
--work-tree, GIT_WORK_TREE and core.worktree is specified,
the current working directory is regarded as the top directory
of your working tree.
--bare::
Treat the repository as a bare repository. If GIT_DIR

View File

@@ -41,6 +41,12 @@ frequently used options.
Show all branches.
--merge::
After an attempt to merge stops with conflicts, show the commits on
the history between two branches (i.e. the HEAD and the MERGE_HEAD)
that modify the conflicted files.
<revs>::
Limit the revisions to show. This can be either a single revision
@@ -74,6 +80,11 @@ gitk --max-count=100 --all \-- Makefile::
Show at most 100 changes made to the file 'Makefile'. Instead of only
looking for changes in the current branch look in all branches.
Files
-----
Gitk creates the .gitk file in your $HOME directory to store preferences
such as display options, font, and colors.
See Also
--------
'qgit(1)'::

View File

@@ -276,3 +276,10 @@ probably enable this hook.
Both standard output and standard error output are forwarded to
`git-send-pack` on the other end, so you can simply `echo` messages
for the user.
pre-auto-gc
-----------
This hook is invoked by `git-gc --auto`. It takes no parameter, and
exiting with non-zero status from this script causes the `git-gc --auto`
to abort.

View File

@@ -1,5 +1,5 @@
From: Rutger Nijlunsing <rutger@nospam.com>
Subject: Setting up a git repository which can be pushed into and pulled from over HTTP.
Subject: Setting up a git repository which can be pushed into and pulled from over HTTP(S).
Date: Thu, 10 Aug 2006 22:00:26 +0200
Since Apache is one of those packages people like to compile
@@ -40,9 +40,13 @@ What's needed:
- have permissions to chown a directory
- have git installed at the server _and_ client
- have git installed on the client, and
In effect, this probably means you're going to be root.
- either have git installed on the server or have a webdav client on
the client.
In effect, this means you're going to be root, or that you're using a
preconfigured WebDAV server.
Step 1: setup a bare GIT repository
@@ -50,9 +54,9 @@ Step 1: setup a bare GIT repository
At the time of writing, git-http-push cannot remotely create a GIT
repository. So we have to do that at the server side with git. Another
option would be to generate an empty repository at the client and copy
it to the server with WebDAV. But then you're probably the first to
try that out :)
option is to generate an empty bare repository at the client and copy
it to the server with a WebDAV client (which is the only option if Git
is not installed on the server).
Create the directory under the DocumentRoot of the directories served
by Apache. As an example we take /usr/local/apache2, but try "grep
@@ -169,7 +173,9 @@ On Debian:
Most tests should pass.
A command line tool to test WebDAV is cadaver.
A command line tool to test WebDAV is cadaver. If you prefer GUIs, for
example, konqueror can open WebDAV URLs as "webdav://..." or
"webdavs://...".
If you're into Windows, from XP onwards Internet Explorer supports
WebDAV. For this, do Internet Explorer -> Open Location ->
@@ -179,8 +185,9 @@ http://<servername>/my-new-repo.git [x] Open as webfolder -> login .
Step 3: setup the client
------------------------
Make sure that you have HTTP support, i.e. your git was built with curl.
The easiest way to check is to look for the executable 'git-http-push'.
Make sure that you have HTTP support, i.e. your git was built with
curl (version more recent than 7.10). The command 'git http-push' with
no argument should display a usage message.
Then, add the following to your $HOME/.netrc (you can do without, but will be
asked to input your password a _lot_ of times):
@@ -197,10 +204,10 @@ instead of the server name.
To check whether all is OK, do:
curl --netrc --location -v http://<username>@<servername>/my-new-repo.git/
...this should give a directory listing in HTML of /var/www/my-new-repo.git .
curl --netrc --location -v http://<username>@<servername>/my-new-repo.git/HEAD
...this should give something like 'ref: refs/heads/master', which is
the content of the file HEAD on the server.
Now, add the remote in your existing repository which contains the project
you want to export:
@@ -225,6 +232,15 @@ want to export) to repository called 'upload', which we previously
defined with git-config.
Using a proxy:
--------------
If you have to access the WebDAV server from behind an HTTP(S) proxy,
set the variable 'all_proxy' to 'http://proxy-host.com:port', or
'http://login-on-proxy:passwd-on-proxy@proxy-host.com:port'. See 'man
curl' for details.
Troubleshooting:
----------------
@@ -248,9 +264,14 @@ Reading /usr/local/apache2/logs/error_log is often helpful.
On Debian: Read /var/log/apache2/error.log instead.
If you access HTTPS locations, git may fail verifying the SSL
certificate (this is return code 60). Setting http.sslVerify=false can
help diagnosing the problem, but removes security checks.
Debian References: http://www.debian-administration.org/articles/285
Authors
Johannes Schindelin <Johannes.Schindelin@gmx.de>
Rutger Nijlunsing <git@wingding.demon.nl>
Matthieu Moy <Matthieu.Moy@imag.fr>

View File

@@ -123,3 +123,4 @@ The placeholders are:
- '%Creset': reset color
- '%m': left, right or boundary mark
- '%n': newline
- '%x00': print a byte from a hex code

View File

@@ -1548,22 +1548,7 @@ dangling tree b24c2473f1fd3d91352a624795be026d64c8841f
Dangling objects are not a problem. At worst they may take up a little
extra disk space. They can sometimes provide a last-resort method for
recovering lost work--see <<dangling-objects>> for details. However, if
you wish, you can remove them with linkgit:git-prune[1] or the `--prune`
option to linkgit:git-gc[1]:
-------------------------------------------------
$ git gc --prune
-------------------------------------------------
This may be time-consuming. Unlike most other git operations (including
git-gc when run without any options), it is not safe to prune while
other git operations are in progress in the same repository.
If linkgit:git-fsck[1] complains about sha1 mismatches or missing
objects, you may have a much more serious problem; your best option is
probably restoring from backups. See
<<recovering-from-repository-corruption>> for a detailed discussion.
recovering lost work--see <<dangling-objects>> for details.
[[recovering-lost-changes]]
Recovering lost changes

View File

@@ -189,6 +189,7 @@ ETC_GITCONFIG = $(sysconfdir)/gitconfig
# default configuration for gitweb
GITWEB_CONFIG = gitweb_config.perl
GITWEB_CONFIG_SYSTEM = /etc/gitweb.conf
GITWEB_HOME_LINK_STR = projects
GITWEB_SITENAME =
GITWEB_PROJECTROOT = /pub/git
@@ -364,6 +365,7 @@ LIB_H += refs.h
LIB_H += remote.h
LIB_H += revision.h
LIB_H += run-command.h
LIB_H += sha1-lookup.h
LIB_H += sideband.h
LIB_H += strbuf.h
LIB_H += tag.h
@@ -444,6 +446,7 @@ LIB_OBJS += run-command.o
LIB_OBJS += server-info.o
LIB_OBJS += setup.o
LIB_OBJS += sha1_file.o
LIB_OBJS += sha1-lookup.o
LIB_OBJS += sha1_name.o
LIB_OBJS += shallow.o
LIB_OBJS += sideband.o
@@ -1068,6 +1071,7 @@ gitweb/gitweb.cgi: gitweb/gitweb.perl
-e 's|++GIT_VERSION++|$(GIT_VERSION)|g' \
-e 's|++GIT_BINDIR++|$(bindir)|g' \
-e 's|++GITWEB_CONFIG++|$(GITWEB_CONFIG)|g' \
-e 's|++GITWEB_CONFIG_SYSTEM++|$(GITWEB_CONFIG_SYSTEM)|g' \
-e 's|++GITWEB_HOME_LINK_STR++|$(GITWEB_HOME_LINK_STR)|g' \
-e 's|++GITWEB_SITENAME++|$(GITWEB_SITENAME)|g' \
-e 's|++GITWEB_PROJECTROOT++|$(GITWEB_PROJECTROOT)|g' \

View File

@@ -1 +1 @@
Documentation/RelNotes-1.5.5.txt
Documentation/RelNotes-1.5.6.txt

View File

@@ -17,6 +17,7 @@ static time_t archive_time;
static int tar_umask = 002;
static int verbose;
static const struct commit *commit;
static size_t base_len;
/* writes out the whole block, but only if it is full */
static void write_if_needed(void)
@@ -251,8 +252,8 @@ static int write_tar_entry(const unsigned char *sha1,
buffer = NULL;
size = 0;
} else {
buffer = sha1_file_to_archive(path.buf, sha1, mode, &type,
&size, commit);
buffer = sha1_file_to_archive(path.buf + base_len, sha1, mode,
&type, &size, commit);
if (!buffer)
die("cannot read %s", sha1_to_hex(sha1));
}
@@ -272,6 +273,7 @@ int write_tar_archive(struct archiver_args *args)
archive_time = args->time;
verbose = args->verbose;
commit = args->commit;
base_len = args->base ? strlen(args->base) : 0;
if (args->commit_sha1)
write_global_extended_header(args->commit_sha1);

View File

@@ -13,6 +13,7 @@ static int verbose;
static int zip_date;
static int zip_time;
static const struct commit *commit;
static size_t base_len;
static unsigned char *zip_dir;
static unsigned int zip_dir_size;
@@ -197,8 +198,8 @@ static int write_zip_entry(const unsigned char *sha1,
if (S_ISREG(mode) && zlib_compression_level != 0)
method = 8;
result = 0;
buffer = sha1_file_to_archive(path, sha1, mode, &type, &size,
commit);
buffer = sha1_file_to_archive(path + base_len, sha1, mode,
&type, &size, commit);
if (!buffer)
die("cannot read %s", sha1_to_hex(sha1));
crc = crc32(crc, buffer, size);
@@ -321,6 +322,7 @@ int write_zip_archive(struct archiver_args *args)
zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
verbose = args->verbose;
commit = args->commit;
base_len = args->base ? strlen(args->base) : 0;
if (args->base && plen > 0 && args->base[plen - 1] == '/') {
char *base = xstrdup(args->base);

View File

@@ -16,9 +16,9 @@ static void format_subst(const struct commit *commit,
const char *b, *c;
b = memmem(src, len, "$Format:", 8);
if (!b || src + len < b + 9)
if (!b)
break;
c = memchr(b + 8, '$', len - 8);
c = memchr(b + 8, '$', (src + len) - b - 8);
if (!c)
break;

4
attr.c
View File

@@ -546,7 +546,9 @@ static int path_matches(const char *pathname, int pathlen,
(baselen && pathname[baselen] != '/') ||
strncmp(pathname, base, baselen))
return 0;
return fnmatch(pattern, pathname + baselen + 1, FNM_PATHNAME) == 0;
if (baselen != 0)
baselen++;
return fnmatch(pattern, pathname + baselen, FNM_PATHNAME) == 0;
}
static int fill_one(const char *what, struct match_attr *a, int rem)

View File

@@ -2981,12 +2981,8 @@ static int apply_patch(int fd, const char *filename, int inaccurate_eof)
static int git_apply_config(const char *var, const char *value)
{
if (!strcmp(var, "apply.whitespace")) {
if (!value)
return config_error_nonbool(var);
apply_default_whitespace = xstrdup(value);
return 0;
}
if (!strcmp(var, "apply.whitespace"))
return git_config_string(&apply_default_whitespace, var, value);
return git_default_config(var, value);
}
@@ -3121,7 +3117,7 @@ int cmd_apply(int argc, const char **argv, const char *unused_prefix)
fd = open(arg, O_RDONLY);
if (fd < 0)
usage(apply_usage);
die("can't open patch '%s': %s", arg, strerror(errno));
read_stdin = 0;
set_default_whitespace_mode(whitespace_option);
errs |= apply_patch(fd, arg, inaccurate_eof);

View File

@@ -504,7 +504,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
OPT__QUIET(&opts.quiet),
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
OPT_SET_INT( 0 , "track", &opts.track, "track",
OPT_SET_INT('t', "track", &opts.track, "track",
BRANCH_TRACK_EXPLICIT),
OPT_BOOLEAN('f', NULL, &opts.force, "force"),
OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),

View File

@@ -95,7 +95,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
int len, pos, matches;
int len, pos;
int matches = 0;
struct cache_entry *ce;
struct stat st;
@@ -127,18 +128,18 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (pathspec) {
memset(seen, 0, argc > 0 ? argc : 1);
matches = match_pathspec(pathspec, ent->name, ent->len,
matches = match_pathspec(pathspec, ent->name, len,
baselen, seen);
} else {
matches = 0;
}
if (S_ISDIR(st.st_mode)) {
strbuf_addstr(&directory, ent->name);
qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
if (show_only && (remove_directories || matches)) {
if (show_only && (remove_directories ||
(matches == MATCHED_EXACTLY))) {
printf("Would remove %s\n", qname);
} else if (remove_directories || matches) {
} else if (remove_directories ||
(matches == MATCHED_EXACTLY)) {
if (!quiet)
printf("Removing %s\n", qname);
if (remove_dir_recursively(&directory, 0) != 0) {

View File

@@ -90,7 +90,7 @@ static struct option builtin_commit_options[] = {
OPT_CALLBACK('m', "message", &message, "MESSAGE", "specify commit message", opt_parse_m),
OPT_STRING('c', "reedit-message", &edit_message, "COMMIT", "reuse and edit message from specified commit "),
OPT_STRING('C', "reuse-message", &use_message, "COMMIT", "reuse message from specified commit"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by: header"),
OPT_BOOLEAN('s', "signoff", &signoff, "add Signed-off-by:"),
OPT_STRING('t', "template", &template_file, "FILE", "use specified template file"),
OPT_BOOLEAN('e', "edit", &edit_flag, "force edit of commit"),
@@ -98,7 +98,7 @@ static struct option builtin_commit_options[] = {
OPT_BOOLEAN('a', "all", &all, "commit all changed files"),
OPT_BOOLEAN('i', "include", &also, "add specified files to index for commit"),
OPT_BOOLEAN(0, "interactive", &interactive, "interactively add files"),
OPT_BOOLEAN('o', "only", &only, ""),
OPT_BOOLEAN('o', "only", &only, "commit only specified files"),
OPT_BOOLEAN('n', "no-verify", &no_verify, "bypass pre-commit hook"),
OPT_BOOLEAN(0, "amend", &amend, "amend previous commit"),
OPT_BOOLEAN(0, "untracked-files", &untracked_files, "show all untracked files"),
@@ -745,10 +745,8 @@ static int parse_and_validate_options(int argc, const char *argv[],
die("No paths with --include/--only does not make sense.");
if (argc == 0 && only && amend)
only_include_assumed = "Clever... amending the last one with dirty index.";
if (argc > 0 && !also && !only) {
if (argc > 0 && !also && !only)
only_include_assumed = "Explicit paths specified without -i nor -o; assuming --only paths...";
also = 0;
}
if (!cleanup_arg || !strcmp(cleanup_arg, "default"))
cleanup_mode = use_editor ? CLEANUP_ALL : CLEANUP_SPACE;
else if (!strcmp(cleanup_arg, "verbatim"))
@@ -810,7 +808,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1)
rev.verbose_header = 1;
rev.show_root_diff = 1;
rev.commit_format = get_commit_format("format:%h: %s");
get_commit_format("format:%h: %s", &rev);
rev.always_show_header = 0;
rev.diffopt.detect_rename = 1;
rev.diffopt.rename_limit = 100;

View File

@@ -3,7 +3,7 @@
#include "color.h"
static const char git_config_set_usage[] =
"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]";
"git-config [ --global | --system | [ -f | --file ] config-file ] [ --bool | --int | --bool-or-int ] [ -z | --null ] [--get | --get-all | --get-regexp | --replace-all | --add | --unset | --unset-all] name [value [value_regex]] | --rename-section old_name new_name | --remove-section name | --list | --get-color var [default] | --get-colorbool name [stdout-is-tty]";
static char *key;
static regex_t *key_regexp;
@@ -16,7 +16,7 @@ static int seen;
static char delim = '=';
static char key_delim = ' ';
static char term = '\n';
static enum { T_RAW, T_INT, T_BOOL } type = T_RAW;
static enum { T_RAW, T_INT, T_BOOL, T_BOOL_OR_INT } type = T_RAW;
static int show_all_config(const char *key_, const char *value_)
{
@@ -53,6 +53,14 @@ static int show_config(const char* key_, const char* value_)
sprintf(value, "%d", git_config_int(key_, value_?value_:""));
else if (type == T_BOOL)
vptr = git_config_bool(key_, value_) ? "true" : "false";
else if (type == T_BOOL_OR_INT) {
int is_bool, v;
v = git_config_bool_or_int(key_, value_, &is_bool);
if (is_bool)
vptr = v ? "true" : "false";
else
sprintf(value, "%d", v);
}
else
vptr = value_?value_:"";
seen++;
@@ -157,6 +165,14 @@ char *normalize_value(const char *key, const char *value)
else if (type == T_BOOL)
sprintf(normalized, "%s",
git_config_bool(key, value) ? "true" : "false");
else if (type == T_BOOL_OR_INT) {
int is_bool, v;
v = git_config_bool_or_int(key, value, &is_bool);
if (!is_bool)
sprintf(normalized, "%d", v);
else
sprintf(normalized, "%s", v ? "true" : "false");
}
}
return normalized;
@@ -224,6 +240,10 @@ static int git_get_colorbool_config(const char *var, const char *value)
get_diff_color_found =
git_config_colorbool(var, value, stdout_is_tty);
}
if (!strcmp(var, "color.ui")) {
git_use_color_default = git_config_colorbool(var, value, stdout_is_tty);
return 0;
}
return 0;
}
@@ -251,7 +271,7 @@ static int get_colorbool(int argc, const char **argv)
if (!strcmp(get_color_slot, "color.diff"))
get_colorbool_found = get_diff_color_found;
if (get_colorbool_found < 0)
get_colorbool_found = 0;
get_colorbool_found = git_use_color_default;
}
if (argc == 1) {
@@ -273,6 +293,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
type = T_INT;
else if (!strcmp(argv[1], "--bool"))
type = T_BOOL;
else if (!strcmp(argv[1], "--bool-or-int"))
type = T_BOOL_OR_INT;
else if (!strcmp(argv[1], "--list") || !strcmp(argv[1], "-l")) {
if (argc != 2)
usage(git_config_set_usage);

View File

@@ -117,15 +117,15 @@ static const unsigned char* get_rev(void)
while (commit == NULL) {
unsigned int mark;
struct commit_list *parents = NULL;
struct commit_list *parents;
if (rev_list == NULL || non_common_revs == 0)
return NULL;
commit = rev_list->item;
if (!(commit->object.parsed))
if (!parse_commit(commit))
parents = commit->parents;
if (!commit->object.parsed)
parse_commit(commit);
parents = commit->parents;
commit->object.flags |= POPPED;
if (!(commit->object.flags & COMMON))

View File

@@ -215,13 +215,6 @@ static int update_local_ref(struct ref *ref,
if (type < 0)
die("object %s not found", sha1_to_hex(ref->new_sha1));
if (!*ref->name) {
/* Not storing */
if (verbose)
sprintf(display, "* branch %s -> FETCH_HEAD", remote);
return 0;
}
if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
if (verbose)
sprintf(display, "= %-*s %-*s -> %s", SUMMARY_WIDTH,
@@ -365,16 +358,21 @@ static int store_updated_refs(const char *url, struct ref *ref_map)
rm->merge ? "" : "not-for-merge",
note);
if (ref) {
if (ref)
update_local_ref(ref, what, verbose, note);
if (*note) {
if (!shown_url) {
fprintf(stderr, "From %.*s\n",
url_len, url);
shown_url = 1;
}
fprintf(stderr, " %s\n", note);
else if (verbose)
sprintf(note, "* %-*s %-*s -> FETCH_HEAD",
SUMMARY_WIDTH, *kind ? kind : "branch",
REFCOL_WIDTH, *what ? what : "HEAD");
else
*note = '\0';
if (*note) {
if (!shown_url) {
fprintf(stderr, "From %.*s\n",
url_len, url);
shown_url = 1;
}
fprintf(stderr, " %s\n", note);
}
}
fclose(fp);
@@ -579,8 +577,6 @@ static int do_fetch(struct transport *transport,
free_refs(ref_map);
}
transport_disconnect(transport);
return 0;
}
@@ -601,6 +597,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
int i;
static const char **refs = NULL;
int ref_nr = 0;
int exit_code;
/* Record the command line for the reflog */
strbuf_addstr(&default_rla, "fetch");
@@ -654,6 +651,9 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
signal(SIGINT, unlock_pack_on_signal);
atexit(unlock_pack);
return do_fetch(transport,
exit_code = do_fetch(transport,
parse_fetch_refspec(ref_nr, refs), ref_nr);
transport_disconnect(transport);
transport = NULL;
return exit_code;
}

View File

@@ -201,6 +201,15 @@ static void shortlog(const char *name, unsigned char *sha1,
continue;
bol = strstr(commit->buffer, "\n\n");
if (bol) {
unsigned char c;
do {
c = *++bol;
} while (isspace(c));
if (!c)
bol = NULL;
}
if (!bol) {
append_to_list(&subjects, xstrdup(sha1_to_hex(
commit->object.sha1)),
@@ -208,7 +217,6 @@ static void shortlog(const char *name, unsigned char *sha1,
continue;
}
bol += 2;
eol = strchr(bol, '\n');
if (eol) {
oneline = xmemdupz(bol, eol - bol);

View File

@@ -157,6 +157,34 @@ static int too_many_packs(void)
return gc_auto_pack_limit <= cnt;
}
static int run_hook(void)
{
const char *argv[2];
struct child_process hook;
int ret;
argv[0] = git_path("hooks/pre-auto-gc");
argv[1] = NULL;
if (access(argv[0], X_OK) < 0)
return 0;
memset(&hook, 0, sizeof(hook));
hook.argv = argv;
hook.no_stdin = 1;
hook.stdout_to_stderr = 1;
ret = start_command(&hook);
if (ret) {
warning("Could not spawn %s", argv[0]);
return ret;
}
ret = finish_command(&hook);
if (ret == -ERR_RUN_COMMAND_WAITPID_SIGNAL)
warning("%s exited due to uncaught signal", argv[0]);
return ret;
}
static int need_to_gc(void)
{
/*
@@ -176,6 +204,9 @@ static int need_to_gc(void)
append_option(argv_repack, "-A", MAX_ADD);
else if (!too_many_loose_objects())
return 0;
if (run_hook())
return 0;
return 1;
}

View File

@@ -400,9 +400,16 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
char buf[10];
/* We do not spell "group" and such, so that
* the configuration can be read by older version
* of git.
* of git. Note, we use octal numbers for new share modes,
* and compatibility values for PERM_GROUP and
* PERM_EVERYBODY.
*/
sprintf(buf, "%d", shared_repository);
if (shared_repository == PERM_GROUP)
sprintf(buf, "%d", OLD_PERM_GROUP);
else if (shared_repository == PERM_EVERYBODY)
sprintf(buf, "%d", OLD_PERM_EVERYBODY);
else
sprintf(buf, "0%o", shared_repository);
git_config_set("core.sharedrepository", buf);
git_config_set("receive.denyNonFastforwards", "true");
}

View File

@@ -56,7 +56,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
rev->abbrev = DEFAULT_ABBREV;
rev->commit_format = CMIT_FMT_DEFAULT;
if (fmt_pretty)
rev->commit_format = get_commit_format(fmt_pretty);
get_commit_format(fmt_pretty, rev);
rev->verbose_header = 1;
DIFF_OPT_SET(&rev->diffopt, RECURSIVE);
rev->show_root_diff = default_show_root;
@@ -400,6 +400,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
* allow us to set a different default.
*/
rev.commit_format = CMIT_FMT_ONELINE;
rev.use_terminator = 1;
rev.always_show_header = 1;
/*

View File

@@ -19,6 +19,8 @@ static const char * const builtin_remote_usage[] = {
static int verbose;
static int show_all(void);
static inline int postfixcmp(const char *string, const char *postfix)
{
int len1 = strlen(string), len2 = strlen(postfix);
@@ -88,19 +90,24 @@ static int add(int argc, const char **argv)
strbuf_init(&buf, 0);
strbuf_init(&buf2, 0);
strbuf_addf(&buf2, "refs/heads/test:refs/remotes/%s/test", name);
if (!valid_fetch_refspec(buf2.buf))
die("'%s' is not a valid remote name", name);
strbuf_addf(&buf, "remote.%s.url", name);
if (git_config_set(buf.buf, url))
return 1;
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.fetch", name);
if (track.nr == 0)
path_list_append("*", &track);
for (i = 0; i < track.nr; i++) {
struct path_list_item *item = track.items + i;
strbuf_reset(&buf);
strbuf_addf(&buf, "remote.%s.fetch", name);
strbuf_reset(&buf2);
strbuf_addch(&buf2, '+');
if (mirror)
strbuf_addf(&buf2, "refs/%s:refs/%s",
item->path, item->path);
@@ -380,8 +387,11 @@ static int show_or_prune(int argc, const char **argv, int prune)
argc = parse_options(argc, argv, options, builtin_remote_usage, 0);
if (argc < 1)
if (argc < 1) {
if (!prune)
return show_all();
usage_with_options(builtin_remote_usage, options);
}
memset(&states, 0, sizeof(states));
for (; argc; argc--, argv++) {

View File

@@ -365,9 +365,17 @@ static int cmd_parseopt(int argc, const char **argv, const char *prefix)
return 0;
}
static void die_no_single_rev(int quiet)
{
if (quiet)
exit(1);
else
die("Needed a single revision");
}
int cmd_rev_parse(int argc, const char **argv, const char *prefix)
{
int i, as_is = 0, verify = 0;
int i, as_is = 0, verify = 0, quiet = 0;
unsigned char sha1[20];
if (argc > 1 && !strcmp("--parseopt", argv[1]))
@@ -432,6 +440,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
verify = 1;
continue;
}
if (!strcmp(arg, "--quiet") || !strcmp(arg, "-q")) {
quiet = 1;
continue;
}
if (!strcmp(arg, "--short") ||
!prefixcmp(arg, "--short=")) {
filter &= ~(DO_FLAGS|DO_NOREV);
@@ -549,7 +561,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
continue;
}
if (show_flag(arg) && verify)
die("Needed a single revision");
die_no_single_rev(quiet);
continue;
}
@@ -564,15 +576,15 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
show_rev(REVERSED, sha1, arg+1);
continue;
}
if (verify)
die_no_single_rev(quiet);
as_is = 1;
if (!show_file(arg))
continue;
if (verify)
die("Needed a single revision");
verify_filename(prefix, arg);
}
show_default();
if (verify && revs_count != 1)
die("Needed a single revision");
die_no_single_rev(quiet);
return 0;
}

View File

@@ -9,7 +9,7 @@
#include "shortlog.h"
static const char shortlog_usage[] =
"git-shortlog [-n] [-s] [-e] [<commit-id>... ]";
"git-shortlog [-n] [-s] [-e] [-w] [<commit-id>... ]";
static int compare_by_number(const void *a1, const void *a2)
{

View File

@@ -16,7 +16,7 @@
static const char * const git_tag_usage[] = {
"git-tag [-a|-s|-u <key-id>] [-f] [-m <msg>|-F <file>] <tagname> [<head>]",
"git-tag -d <tagname>...",
"git-tag -l [-n [<num>]] [<pattern>]",
"git-tag -l [-n[<num>]] [<pattern>]",
"git-tag -v <tagname>...",
NULL
};

View File

@@ -7,11 +7,13 @@
#include "commit.h"
#include "tag.h"
#include "tree.h"
#include "tree-walk.h"
#include "progress.h"
#include "decorate.h"
#include "fsck.h"
static int dry_run, quiet, recover, has_errors;
static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] < pack-file";
static int dry_run, quiet, recover, has_errors, strict;
static const char unpack_usage[] = "git-unpack-objects [-n] [-q] [-r] [--strict] < pack-file";
/* We always read in 4kB chunks. */
static unsigned char buffer[4096];
@@ -19,6 +21,11 @@ static unsigned int offset, len;
static off_t consumed_bytes;
static SHA_CTX ctx;
/*
* When running under --strict mode, objects whose reachability are
* suspect are kept in core without getting written in the object
* store.
*/
struct obj_buffer {
char *buffer;
unsigned long size;
@@ -31,6 +38,16 @@ static struct obj_buffer *lookup_object_buffer(struct object *base)
return lookup_decoration(&obj_decorate, base);
}
static void add_object_buffer(struct object *object, char *buffer, unsigned long size)
{
struct obj_buffer *obj;
obj = xcalloc(1, sizeof(struct obj_buffer));
obj->buffer = buffer;
obj->size = size;
if (add_decoration(&obj_decorate, object, obj))
die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
}
/*
* Make sure at least "min" bytes are available in the buffer, and
* return the pointer to the buffer.
@@ -134,19 +151,110 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
struct obj_info {
off_t offset;
unsigned char sha1[20];
struct object *obj;
};
#define FLAG_OPEN (1u<<20)
#define FLAG_WRITTEN (1u<<21)
static struct obj_info *obj_list;
unsigned nr_objects;
/*
* Called only from check_object() after it verified this object
* is Ok.
*/
static void write_cached_object(struct object *obj)
{
unsigned char sha1[20];
struct obj_buffer *obj_buf = lookup_object_buffer(obj);
if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
die("failed to write object %s", sha1_to_hex(obj->sha1));
obj->flags |= FLAG_WRITTEN;
}
/*
* At the very end of the processing, write_rest() scans the objects
* that have reachability requirements and calls this function.
* Verify its reachability and validity recursively and write it out.
*/
static int check_object(struct object *obj, int type, void *data)
{
if (!obj)
return 0;
if (obj->flags & FLAG_WRITTEN)
return 1;
if (type != OBJ_ANY && obj->type != type)
die("object type mismatch");
if (!(obj->flags & FLAG_OPEN)) {
unsigned long size;
int type = sha1_object_info(obj->sha1, &size);
if (type != obj->type || type <= 0)
die("object of unexpected type");
obj->flags |= FLAG_WRITTEN;
return 1;
}
if (fsck_object(obj, 1, fsck_error_function))
die("Error in object");
if (!fsck_walk(obj, check_object, 0))
die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
write_cached_object(obj);
return 1;
}
static void write_rest(void)
{
unsigned i;
for (i = 0; i < nr_objects; i++)
check_object(obj_list[i].obj, OBJ_ANY, 0);
}
static void added_object(unsigned nr, enum object_type type,
void *data, unsigned long size);
/*
* Write out nr-th object from the list, now we know the contents
* of it. Under --strict, this buffers structured objects in-core,
* to be checked at the end.
*/
static void write_object(unsigned nr, enum object_type type,
void *buf, unsigned long size)
{
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
die("failed to write object");
added_object(nr, type, buf, size);
if (!strict) {
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
die("failed to write object");
added_object(nr, type, buf, size);
free(buf);
obj_list[nr].obj = NULL;
} else if (type == OBJ_BLOB) {
struct blob *blob;
if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
die("failed to write object");
added_object(nr, type, buf, size);
free(buf);
blob = lookup_blob(obj_list[nr].sha1);
if (blob)
blob->object.flags |= FLAG_WRITTEN;
else
die("invalid blob object");
obj_list[nr].obj = NULL;
} else {
struct object *obj;
int eaten;
hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
added_object(nr, type, buf, size);
obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
if (!obj)
die("invalid %s", typename(type));
add_object_buffer(obj, buf, size);
obj->flags |= FLAG_OPEN;
obj_list[nr].obj = obj;
}
}
static void resolve_delta(unsigned nr, enum object_type type,
@@ -163,9 +271,12 @@ static void resolve_delta(unsigned nr, enum object_type type,
die("failed to apply delta");
free(delta);
write_object(nr, type, result, result_size);
free(result);
}
/*
* We now know the contents of an object (which is nr-th in the pack);
* resolve all the deltified objects that are based on it.
*/
static void added_object(unsigned nr, enum object_type type,
void *data, unsigned long size)
{
@@ -193,7 +304,24 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
if (!dry_run && buf)
write_object(nr, type, buf, size);
free(buf);
else
free(buf);
}
static int resolve_against_held(unsigned nr, const unsigned char *base,
void *delta_data, unsigned long delta_size)
{
struct object *obj;
struct obj_buffer *obj_buffer;
obj = lookup_object(base);
if (!obj)
return 0;
obj_buffer = lookup_object_buffer(obj);
if (!obj_buffer)
return 0;
resolve_delta(nr, obj->type, obj_buffer->buffer,
obj_buffer->size, delta_data, delta_size);
return 1;
}
static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
@@ -202,7 +330,6 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
void *delta_data, *base;
unsigned long base_size;
unsigned char base_sha1[20];
struct object *obj;
if (type == OBJ_REF_DELTA) {
hashcpy(base_sha1, fill(20));
@@ -212,7 +339,13 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
free(delta_data);
return;
}
if (!has_sha1_file(base_sha1)) {
if (has_sha1_file(base_sha1))
; /* Ok we have this one */
else if (resolve_against_held(nr, base_sha1,
delta_data, delta_size))
return; /* we are done */
else {
/* cannot resolve yet --- queue it */
hashcpy(obj_list[nr].sha1, null_sha1);
add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
return;
@@ -258,22 +391,18 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
}
}
if (!base_found) {
/* The delta base object is itself a delta that
has not been resolved yet. */
/*
* The delta base object is itself a delta that
* has not been resolved yet.
*/
hashcpy(obj_list[nr].sha1, null_sha1);
add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
return;
}
}
obj = lookup_object(base_sha1);
if (obj) {
struct obj_buffer *obj_buf = lookup_object_buffer(obj);
if (obj_buf) {
resolve_delta(nr, obj->type, obj_buf->buffer, obj_buf->size, delta_data, delta_size);
return;
}
}
if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
return;
base = read_sha1_file(base_sha1, &type, &base_size);
if (!base) {
@@ -336,7 +465,8 @@ static void unpack_all(void)
int i;
struct progress *progress = NULL;
struct pack_header *hdr = fill(sizeof(struct pack_header));
unsigned nr_objects = ntohl(hdr->hdr_entries);
nr_objects = ntohl(hdr->hdr_entries);
if (ntohl(hdr->hdr_signature) != PACK_SIGNATURE)
die("bad pack file");
@@ -347,6 +477,7 @@ static void unpack_all(void)
if (!quiet)
progress = start_progress("Unpacking objects", nr_objects);
obj_list = xmalloc(nr_objects * sizeof(*obj_list));
memset(obj_list, 0, nr_objects * sizeof(*obj_list));
for (i = 0; i < nr_objects; i++) {
unpack_one(i);
display_progress(progress, i + 1);
@@ -382,6 +513,10 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
recover = 1;
continue;
}
if (!strcmp(arg, "--strict")) {
strict = 1;
continue;
}
if (!prefixcmp(arg, "--pack_header=")) {
struct pack_header *hdr;
char *c;
@@ -407,6 +542,8 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
unpack_all();
SHA1_Update(&ctx, buffer, offset);
SHA1_Final(sha1, &ctx);
if (strict)
write_rest();
if (hashcmp(fill(20), sha1))
die("final sha1 did not match");
use(20);

View File

@@ -341,8 +341,11 @@ static int update_one(struct cache_tree *it,
if (dryrun)
hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
else
write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
strbuf_release(&buffer);
return -1;
}
strbuf_release(&buffer);
it->entry_count = i;
#if DEBUG

21
cache.h
View File

@@ -474,10 +474,20 @@ static inline void hashclr(unsigned char *hash)
int git_mkstemp(char *path, size_t n, const char *template);
/*
* NOTE NOTE NOTE!!
*
* PERM_UMASK, OLD_PERM_GROUP and OLD_PERM_EVERYBODY enumerations must
* not be changed. Old repositories have core.sharedrepository written in
* numeric format, and therefore these values are preserved for compatibility
* reasons.
*/
enum sharedrepo {
PERM_UMASK = 0,
PERM_GROUP,
PERM_EVERYBODY
PERM_UMASK = 0,
OLD_PERM_GROUP = 1,
OLD_PERM_EVERYBODY = 2,
PERM_GROUP = 0660,
PERM_EVERYBODY = 0664,
};
int git_config_perm(const char *var, const char *value);
int adjust_shared_perm(const char *path);
@@ -692,6 +702,7 @@ extern int git_parse_long(const char *, long *);
extern int git_parse_ulong(const char *, unsigned long *);
extern int git_config_int(const char *, const char *);
extern unsigned long git_config_ulong(const char *, const char *);
extern int git_config_bool_or_int(const char *, const char *, int *);
extern int git_config_bool(const char *, const char *);
extern int git_config_string(const char **, const char *, const char *);
extern int git_config_set(const char *, const char *);
@@ -715,8 +726,8 @@ extern const char *git_log_output_encoding;
extern void maybe_flush_or_die(FILE *, const char *);
extern int copy_fd(int ifd, int ofd);
extern int copy_file(const char *dst, const char *src, int mode);
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 ssize_t read_in_full(int fd, void *buf, size_t count);
extern ssize_t 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);

View File

@@ -718,9 +718,9 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
result = xmalloc(len + 1);
while (sz < len) {
ssize_t done = xread(fd, result+sz, len-sz);
if (done == 0)
break;
if (done < 0)
if (done == 0 && sz != len)
die("early EOF '%s'", elem->path);
else if (done < 0)
die("read error '%s'", elem->path);
sz += done;
}

View File

@@ -63,7 +63,8 @@ enum cmit_fmt {
};
extern int non_ascii(int);
extern enum cmit_fmt get_commit_format(const char *arg);
struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
extern void get_commit_format(const char *arg, struct rev_info *);
extern void format_commit_message(const struct commit *commit,
const void *format, struct strbuf *sb);
extern void pretty_print_commit(enum cmit_fmt fmt, const struct commit*,

View File

@@ -303,8 +303,9 @@ unsigned long git_config_ulong(const char *name, const char *value)
return ret;
}
int git_config_bool(const char *name, const char *value)
int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
{
*is_bool = 1;
if (!value)
return 1;
if (!*value)
@@ -313,7 +314,14 @@ int git_config_bool(const char *name, const char *value)
return 1;
if (!strcasecmp(value, "false") || !strcasecmp(value, "no"))
return 0;
return git_config_int(name, value) != 0;
*is_bool = 0;
return git_config_int(name, value);
}
int git_config_bool(const char *name, const char *value)
{
int discard;
return !!git_config_bool_or_int(name, value, &discard);
}
int git_config_string(const char **dest, const char *var, const char *value)

View File

@@ -152,7 +152,7 @@ __git_heads ()
done
return
fi
for i in $(git-ls-remote "$1" 2>/dev/null); do
for i in $(git ls-remote "$1" 2>/dev/null); do
case "$is_hash,$i" in
y,*) is_hash=n ;;
n,*^{}) is_hash=y ;;
@@ -173,7 +173,7 @@ __git_tags ()
done
return
fi
for i in $(git-ls-remote "$1" 2>/dev/null); do
for i in $(git ls-remote "$1" 2>/dev/null); do
case "$is_hash,$i" in
y,*) is_hash=n ;;
n,*^{}) is_hash=y ;;
@@ -200,7 +200,7 @@ __git_refs ()
done
return
fi
for i in $(git-ls-remote "$dir" 2>/dev/null); do
for i in $(git ls-remote "$dir" 2>/dev/null); do
case "$is_hash,$i" in
y,*) is_hash=n ;;
n,*^{}) is_hash=y ;;
@@ -223,7 +223,7 @@ __git_refs2 ()
__git_refs_remotes ()
{
local cmd i is_hash=y
for i in $(git-ls-remote "$1" 2>/dev/null); do
for i in $(git ls-remote "$1" 2>/dev/null); do
case "$is_hash,$i" in
n,refs/heads/*)
is_hash=y
@@ -641,6 +641,7 @@ _git_diff ()
--ignore-all-space --exit-code --quiet --ext-diff
--no-ext-diff
--no-prefix --src-prefix= --dst-prefix=
--base --ours --theirs
"
return
;;
@@ -1052,6 +1053,7 @@ _git_remote ()
local subcommands="add rm show prune update"
local subcommand="$(__git_find_subcommand "$subcommands")"
if [ -z "$subcommand" ]; then
__gitcomp "$subcommands"
return
fi
@@ -1344,9 +1346,14 @@ _git ()
_gitk ()
{
local cur="${COMP_WORDS[COMP_CWORD]}"
local g="$(git rev-parse --git-dir 2>/dev/null)"
local merge=""
if [ -f $g/MERGE_HEAD ]; then
merge="--merge"
fi
case "$cur" in
--*)
__gitcomp "--not --all"
__gitcomp "--not --all $merge"
return
;;
esac

View File

@@ -232,10 +232,8 @@ and returns the process output as a string, or nil if the git failed."
(defun git-run-command-region (buffer start end env &rest args)
"Run a git command with specified buffer region as input."
(unless (eq 0 (if env
(git-run-process-region
buffer start end "env"
(append (git-get-env-strings env) (list "git") args))
(unless (eq 0 (let ((process-environment (append (git-get-env-strings env)
process-environment)))
(git-run-process-region
buffer start end "git" args)))
(error "Failed to run \"git %s\":\n%s" (mapconcat (lambda (x) x) args " ") (buffer-string))))
@@ -250,9 +248,8 @@ and returns the process output as a string, or nil if the git failed."
(erase-buffer)
(cd dir)
(setq status
(if env
(apply #'call-process "env" nil (list buffer t) nil
(append (git-get-env-strings env) (list hook-name) args))
(let ((process-environment (append (git-get-env-strings env)
process-environment)))
(apply #'call-process hook-name nil (list buffer t) nil args))))
(display-message-or-buffer buffer)
(eq 0 status)))))

View File

@@ -202,11 +202,12 @@ generate_email_header()
generate_email_footer()
{
SPACE=" "
cat <<-EOF
hooks/post-receive
--
--${SPACE}
$projectdesc
EOF
}

View File

@@ -0,0 +1,36 @@
#!/bin/sh
#
# An example hook script to verify if you are on battery, in case you
# are running Linux. Called by git-gc --auto with no arguments. The hook
# should exit with non-zero status after issuing an appropriate message
# if it wants to stop the auto repacking.
#
# This hook is stored in the contrib/hooks directory. Your distribution
# may have put this somewhere else. If you want to use this hook, you
# should make this script executable then link to it in the repository
# you would like to use it in.
#
# For example, if the hook is stored in
# /usr/share/git-core/contrib/hooks/pre-auto-gc-battery:
#
# chmod a+x pre-auto-gc-battery
# cd /path/to/your/repository.git
# ln -sf /usr/share/git-core/contrib/hooks/pre-auto-gc-battery \
# hooks/pre-auto-gc
if test -x /sbin/on_ac_power && /sbin/on_ac_power
then
exit 0
elif test "$(cat /sys/class/power_supply/AC/online 2>/dev/null)" = 1
then
exit 0
elif grep -q 'on-line' /proc/acpi/ac_adapter/AC/state 2>/dev/null
then
exit 0
elif grep -q '0x01$' /proc/apm 2>/dev/null
then
exit 0
fi
echo "Auto packing deferred; not on AC"
exit 1

8
copy.c
View File

@@ -9,8 +9,7 @@ int copy_fd(int ifd, int ofd)
if (!len)
break;
if (len < 0) {
int read_error;
read_error = errno;
int read_error = errno;
close(ifd);
return error("copy-fd: read returned %s",
strerror(read_error));
@@ -25,9 +24,10 @@ int copy_fd(int ifd, int ofd)
close(ifd);
return error("copy-fd: write returned 0");
} else {
int write_error = errno;
close(ifd);
return error("copy-fd: write returned %s",
strerror(errno));
strerror(write_error));
}
}
}
@@ -48,7 +48,7 @@ int copy_file(const char *dst, const char *src, int mode)
}
status = copy_fd(fdi, fdo);
if (close(fdo) != 0)
return error("%s: write error: %s", dst, strerror(errno));
return error("%s: close error: %s", dst, strerror(errno));
if (!status && adjust_shared_perm(dst))
return -1;

View File

@@ -264,6 +264,9 @@ int setup_diff_no_index(struct rev_info *revs,
DIFF_OPT_SET(&revs->diffopt, EXIT_WITH_STATUS);
break;
}
if (nongit && argc != i + 2)
die("git diff [--no-index] takes two paths");
if (argc != i + 2 || (!is_outside_repo(argv[i + 1], nongit, prefix) &&
!is_outside_repo(argv[i], nongit, prefix)))
return -1;

83
diff.c
View File

@@ -991,18 +991,23 @@ static void show_numstat(struct diffstat_t* data, struct diff_options *options)
}
}
struct diffstat_dir {
struct diffstat_file **files;
int nr, percent, cumulative;
struct dirstat_file {
const char *name;
unsigned long changed;
};
static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long changed, const char *base, int baselen)
struct dirstat_dir {
struct dirstat_file *files;
int alloc, nr, percent, cumulative;
};
static long gather_dirstat(FILE *file, struct dirstat_dir *dir, unsigned long changed, const char *base, int baselen)
{
unsigned long this_dir = 0;
unsigned int sources = 0;
while (dir->nr) {
struct diffstat_file *f = *dir->files;
struct dirstat_file *f = dir->files;
int namelen = strlen(f->name);
unsigned long this;
char *slash;
@@ -1017,10 +1022,7 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c
this = gather_dirstat(file, dir, changed, f->name, newbaselen);
sources++;
} else {
if (f->is_unmerged || f->is_binary)
this = 0;
else
this = f->added + f->deleted;
this = f->changed;
dir->files++;
dir->nr--;
sources += 2;
@@ -1048,19 +1050,58 @@ static long gather_dirstat(FILE *file, struct diffstat_dir *dir, unsigned long c
return this_dir;
}
static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
static void show_dirstat(struct diff_options *options)
{
int i;
unsigned long changed;
struct diffstat_dir dir;
struct dirstat_dir dir;
struct diff_queue_struct *q = &diff_queued_diff;
dir.files = NULL;
dir.alloc = 0;
dir.nr = 0;
dir.percent = options->dirstat_percent;
dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
/* Calculate total changes */
changed = 0;
for (i = 0; i < data->nr; i++) {
if (data->files[i]->is_binary || data->files[i]->is_unmerged)
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
const char *name;
unsigned long copied, added, damage;
name = p->one->path ? p->one->path : p->two->path;
if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
diff_populate_filespec(p->one, 0);
diff_populate_filespec(p->two, 0);
diffcore_count_changes(p->one, p->two, NULL, NULL, 0,
&copied, &added);
diff_free_filespec_data(p->one);
diff_free_filespec_data(p->two);
} else if (DIFF_FILE_VALID(p->one)) {
diff_populate_filespec(p->one, 1);
copied = added = 0;
diff_free_filespec_data(p->one);
} else if (DIFF_FILE_VALID(p->two)) {
diff_populate_filespec(p->two, 1);
copied = 0;
added = p->two->size;
diff_free_filespec_data(p->two);
} else
continue;
changed += data->files[i]->added;
changed += data->files[i]->deleted;
/*
* Original minus copied is the removed material,
* added is the new material. They are both damages
* made to the preimage.
*/
damage = (p->one->size - copied) + added;
ALLOC_GROW(dir.files, dir.nr + 1, dir.alloc);
dir.files[dir.nr].name = name;
dir.files[dir.nr].changed = damage;
changed += damage;
dir.nr++;
}
/* This can happen even with many files, if everything was renames */
@@ -1068,10 +1109,6 @@ static void show_dirstat(struct diffstat_t *data, struct diff_options *options)
return;
/* Show all directories with more than x% of the changes */
dir.files = data->files;
dir.nr = data->nr;
dir.percent = options->dirstat_percent;
dir.cumulative = options->output_format & DIFF_FORMAT_CUMULATIVE;
gather_dirstat(options->file, &dir, changed, "", 0);
}
@@ -3095,7 +3132,7 @@ void diff_flush(struct diff_options *options)
separator++;
}
if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT|DIFF_FORMAT_DIRSTAT)) {
if (output_format & (DIFF_FORMAT_DIFFSTAT|DIFF_FORMAT_SHORTSTAT|DIFF_FORMAT_NUMSTAT)) {
struct diffstat_t diffstat;
memset(&diffstat, 0, sizeof(struct diffstat_t));
@@ -3105,8 +3142,6 @@ void diff_flush(struct diff_options *options)
if (check_pair_status(p))
diff_flush_stat(p, options, &diffstat);
}
if (output_format & DIFF_FORMAT_DIRSTAT)
show_dirstat(&diffstat, options);
if (output_format & DIFF_FORMAT_NUMSTAT)
show_numstat(&diffstat, options);
if (output_format & DIFF_FORMAT_DIFFSTAT)
@@ -3116,6 +3151,8 @@ void diff_flush(struct diff_options *options)
free_diffstat_info(&diffstat);
separator++;
}
if (output_format & DIFF_FORMAT_DIRSTAT)
show_dirstat(options);
if (output_format & DIFF_FORMAT_SUMMARY && !is_summary_empty(q)) {
for (i = 0; i < q->nr; i++)

View File

@@ -112,8 +112,8 @@ static int basename_same(struct diff_filespec *src, struct diff_filespec *dst)
struct diff_score {
int src; /* index in rename_src */
int dst; /* index in rename_dst */
int score;
int name_score;
unsigned short score;
short name_score;
};
static int estimate_similarity(struct diff_filespec *src,
@@ -223,6 +223,12 @@ static int score_compare(const void *a_, const void *b_)
{
const struct diff_score *a = a_, *b = b_;
/* sink the unused ones to the bottom */
if (a->dst < 0)
return (0 <= b->dst);
else if (b->dst < 0)
return -1;
if (a->score == b->score)
return b->name_score - a->name_score;
@@ -387,6 +393,22 @@ static int find_exact_renames(void)
return i;
}
#define NUM_CANDIDATE_PER_DST 4
static void record_if_better(struct diff_score m[], struct diff_score *o)
{
int i, worst;
/* find the worst one */
worst = 0;
for (i = 1; i < NUM_CANDIDATE_PER_DST; i++)
if (score_compare(&m[i], &m[worst]) > 0)
worst = i;
/* is it better than the worst one? */
if (score_compare(&m[worst], o) > 0)
m[worst] = *o;
}
void diffcore_rename(struct diff_options *options)
{
int detect_rename = options->detect_rename;
@@ -474,47 +496,61 @@ void diffcore_rename(struct diff_options *options)
goto cleanup;
}
mx = xmalloc(sizeof(*mx) * num_create * num_src);
mx = xcalloc(num_create * NUM_CANDIDATE_PER_DST, sizeof(*mx));
for (dst_cnt = i = 0; i < rename_dst_nr; i++) {
int base = dst_cnt * num_src;
struct diff_filespec *two = rename_dst[i].two;
struct diff_score *m;
if (rename_dst[i].pair)
continue; /* dealt with exact match already. */
m = &mx[dst_cnt * NUM_CANDIDATE_PER_DST];
for (j = 0; j < NUM_CANDIDATE_PER_DST; j++)
m[j].dst = -1;
for (j = 0; j < rename_src_nr; j++) {
struct diff_filespec *one = rename_src[j].one;
struct diff_score *m = &mx[base+j];
m->src = j;
m->dst = i;
m->score = estimate_similarity(one, two,
minimum_score);
m->name_score = basename_same(one, two);
struct diff_score this_src;
this_src.score = estimate_similarity(one, two,
minimum_score);
this_src.name_score = basename_same(one, two);
this_src.dst = i;
this_src.src = j;
record_if_better(m, &this_src);
diff_free_filespec_blob(one);
}
/* We do not need the text anymore */
diff_free_filespec_blob(two);
dst_cnt++;
}
/* cost matrix sorted by most to least similar pair */
qsort(mx, num_create * num_src, sizeof(*mx), score_compare);
for (i = 0; i < num_create * num_src; i++) {
struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
struct diff_filespec *src;
qsort(mx, dst_cnt * NUM_CANDIDATE_PER_DST, sizeof(*mx), score_compare);
for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
struct diff_rename_dst *dst;
if ((mx[i].dst < 0) ||
(mx[i].score < minimum_score))
break; /* there is no more usable pair. */
dst = &rename_dst[mx[i].dst];
if (dst->pair)
continue; /* already done, either exact or fuzzy. */
if (mx[i].score < minimum_score)
break; /* there is no more usable pair. */
src = rename_src[mx[i].src].one;
if (src->rename_used)
if (rename_src[mx[i].src].one->rename_used)
continue;
record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
rename_count++;
}
for (i = 0; i < num_create * num_src; i++) {
struct diff_rename_dst *dst = &rename_dst[mx[i].dst];
for (i = 0; i < dst_cnt * NUM_CANDIDATE_PER_DST; i++) {
struct diff_rename_dst *dst;
if ((mx[i].dst < 0) ||
(mx[i].score < minimum_score))
break; /* there is no more usable pair. */
dst = &rename_dst[mx[i].dst];
if (dst->pair)
continue; /* already done, either exact or fuzzy. */
if (mx[i].score < minimum_score)
break; /* there is no more usable pair. */
record_rename_pair(mx[i].dst, mx[i].src, mx[i].score);
rename_count++;
}

2
dir.c
View File

@@ -80,7 +80,7 @@ static int match_one(const char *match, const char *name, int namelen)
if (strncmp(match, name, matchlen))
return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
if (!name[matchlen])
if (namelen == matchlen)
return MATCHED_EXACTLY;
if (match[matchlen-1] == '/' || name[matchlen] == '/')
return MATCHED_RECURSIVELY;

View File

@@ -550,6 +550,21 @@ sub parse_diff {
return @hunk;
}
sub parse_diff_header {
my $src = shift;
my $head = { TEXT => [], DISPLAY => [] };
my $mode = { TEXT => [], DISPLAY => [] };
for (my $i = 0; $i < @{$src->{TEXT}}; $i++) {
my $dest = $src->{TEXT}->[$i] =~ /^(old|new) mode (\d+)$/ ?
$mode : $head;
push @{$dest->{TEXT}}, $src->{TEXT}->[$i];
push @{$dest->{DISPLAY}}, $src->{DISPLAY}->[$i];
}
return ($head, $mode);
}
sub hunk_splittable {
my ($text) = @_;
@@ -795,9 +810,40 @@ sub patch_update_file {
my ($ix, $num);
my $path = shift;
my ($head, @hunk) = parse_diff($path);
($head, my $mode) = parse_diff_header($head);
for (@{$head->{DISPLAY}}) {
print;
}
if (@{$mode->{TEXT}}) {
while (1) {
print @{$mode->{DISPLAY}};
print colored $prompt_color,
"Stage mode change [y/n/a/d/?]? ";
my $line = <STDIN>;
if ($line =~ /^y/i) {
$mode->{USE} = 1;
last;
}
elsif ($line =~ /^n/i) {
$mode->{USE} = 0;
last;
}
elsif ($line =~ /^a/i) {
$_->{USE} = 1 foreach ($mode, @hunk);
last;
}
elsif ($line =~ /^d/i) {
$_->{USE} = 0 foreach ($mode, @hunk);
last;
}
else {
help_patch_cmd('');
next;
}
}
}
$num = scalar @hunk;
$ix = 0;
@@ -920,6 +966,9 @@ sub patch_update_file {
my $n_lofs = 0;
my @result = ();
if ($mode->{USE}) {
push @result, @{$mode->{TEXT}};
}
for (@hunk) {
my $text = $_->{TEXT};
my ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =

View File

@@ -107,7 +107,7 @@ It does not apply to blobs recorded in its index."
# patch did not touch, so recursive ends up canceling them,
# saying that we reverted all those changes.
eval GITHEAD_$his_tree='"$SUBJECT"'
eval GITHEAD_$his_tree='"$FIRSTLINE"'
export GITHEAD_$his_tree
git-merge-recursive $orig_tree -- HEAD $his_tree || {
git rerere
@@ -117,10 +117,6 @@ It does not apply to blobs recorded in its index."
unset GITHEAD_$his_tree
}
reread_subject () {
git stripspace <"$1" | sed -e 1q
}
prec=4
dotest=".dotest"
sign= utf8=t keep= skip= interactive= resolved= binary= rebasing=
@@ -331,7 +327,20 @@ do
echo "Patch is empty. Was it split wrong?"
stop_here $this
}
git stripspace < "$dotest/msg" > "$dotest/msg-clean"
if test -f "$dotest/rebasing" &&
commit=$(sed -e 's/^From \([0-9a-f]*\) .*/\1/' \
-e q "$dotest/$msgnum") &&
test "$(git cat-file -t "$commit")" = commit
then
git cat-file commit "$commit" |
sed -e '1,/^$/d' >"$dotest/msg-clean"
else
SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
case "$keep_subject" in -k) SUBJECT="[PATCH] $SUBJECT" ;; esac
(printf '%s\n\n' "$SUBJECT"; cat "$dotest/msg") |
git stripspace > "$dotest/msg-clean"
fi
;;
esac
@@ -347,9 +356,6 @@ do
export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
SUBJECT="$(sed -n '/^Subject/ s/Subject: //p' "$dotest/info")"
case "$keep_subject" in -k) SUBJECT="[PATCH] $SUBJECT" ;; esac
case "$resume" in
'')
if test '' != "$SIGNOFF"
@@ -368,10 +374,8 @@ do
ADD_SIGNOFF=
fi
{
printf '%s\n' "$SUBJECT"
if test -s "$dotest/msg-clean"
then
echo
cat "$dotest/msg-clean"
fi
if test '' != "$ADD_SIGNOFF"
@@ -408,7 +412,6 @@ do
[aA]*) action=yes interactive= ;;
[nN]*) action=skip ;;
[eE]*) git_editor "$dotest/final-commit"
SUBJECT=$(reread_subject "$dotest/final-commit")
action=again ;;
[vV]*) action=again
LESS=-S ${PAGER:-less} "$dotest/patch" ;;
@@ -418,6 +421,7 @@ do
else
action=yes
fi
FIRSTLINE=$(head -1 "$dotest/final-commit")
if test $action = skip
then
@@ -431,7 +435,7 @@ do
stop_here $this
fi
printf 'Applying %s\n' "$SUBJECT"
printf 'Applying %s\n' "$FIRSTLINE"
case "$resolved" in
'')
@@ -489,7 +493,7 @@ do
tree=$(git write-tree) &&
parent=$(git rev-parse --verify HEAD) &&
commit=$(git commit-tree $tree -p $parent <"$dotest/final-commit") &&
git update-ref -m "$GIT_REFLOG_ACTION: $SUBJECT" HEAD $commit $parent ||
git update-ref -m "$GIT_REFLOG_ACTION: $FIRSTLINE" HEAD $commit $parent ||
stop_here $this
if test -x "$GIT_DIR"/hooks/post-applypatch

View File

@@ -1,7 +1,9 @@
#!/bin/sh
USAGE='[start|bad|good|skip|next|reset|visualize|replay|log|run]'
LONG_USAGE='git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
USAGE='[help|start|bad|good|skip|next|reset|visualize|replay|log|run]'
LONG_USAGE='git bisect help
print this long help message.
git bisect start [<bad> [<good>...]] [--] [<pathspec>...]
reset bisect state and start bisection.
git bisect bad [<rev>]
mark <rev> a known-bad revision.
@@ -20,7 +22,9 @@ git bisect replay <logfile>
git bisect log
show bisect log.
git bisect run <cmd>...
use <cmd>... to automatically bisect.'
use <cmd>... to automatically bisect.
Please use "git help bisect" to get the full man page.'
OPTIONS_SPEC=
. git-sh-setup
@@ -62,9 +66,10 @@ bisect_start() {
# Verify HEAD. If we were bisecting before this, reset to the
# top-of-line master first!
#
head=$(GIT_DIR="$GIT_DIR" git symbolic-ref HEAD) ||
head=$(GIT_DIR="$GIT_DIR" git symbolic-ref -q HEAD) ||
head=$(GIT_DIR="$GIT_DIR" git rev-parse --verify HEAD) ||
die "Bad HEAD - I need a HEAD"
start_head=''
case "$head" in
refs/heads/bisect)
if [ -s "$GIT_DIR/BISECT_START" ]; then
@@ -78,7 +83,7 @@ bisect_start() {
# This error message should only be triggered by cogito usage,
# and cogito users should understand it relates to cg-seek.
[ -s "$GIT_DIR/head-name" ] && die "won't bisect on seeked tree"
echo "${head#refs/heads/}" >"$GIT_DIR/BISECT_START"
start_head="${head#refs/heads/}"
;;
*)
die "Bad HEAD - strange symbolic ref"
@@ -99,6 +104,7 @@ bisect_start() {
done
orig_args=$(sq "$@")
bad_seen=0
eval=''
while [ $# -gt 0 ]; do
arg="$1"
case "$arg" in
@@ -116,13 +122,15 @@ bisect_start() {
0) state='bad' ; bad_seen=1 ;;
*) state='good' ;;
esac
bisect_write "$state" "$rev" 'nolog'
eval="$eval bisect_write '$state' '$rev' 'nolog'; "
shift
;;
esac
done
sq "$@" >"$GIT_DIR/BISECT_NAMES"
test -n "$start_head" && echo "$start_head" >"$GIT_DIR/BISECT_START"
eval "$eval"
echo "git-bisect start$orig_args" >>"$GIT_DIR/BISECT_LOG"
bisect_auto_next
}
@@ -151,20 +159,18 @@ bisect_state() {
rev=$(git rev-parse --verify HEAD) ||
die "Bad rev input: HEAD"
bisect_write "$state" "$rev" ;;
2,bad)
rev=$(git rev-parse --verify "$2^{commit}") ||
die "Bad rev input: $2"
bisect_write "$state" "$rev" ;;
*,good|*,skip)
2,bad|*,good|*,skip)
shift
revs=$(git rev-parse --revs-only --no-flags "$@") &&
test '' != "$revs" || die "Bad rev input: $@"
for rev in $revs
eval=''
for rev in "$@"
do
rev=$(git rev-parse --verify "$rev^{commit}") ||
die "Bad rev commit: $rev^{commit}"
bisect_write "$state" "$rev"
done ;;
sha=$(git rev-parse --verify "$rev^{commit}") ||
die "Bad rev input: $rev"
eval="$eval bisect_write '$state' '$sha'; "
done
eval "$eval" ;;
*,bad)
die "'git bisect bad' can take only one argument." ;;
*)
usage ;;
esac
@@ -465,6 +471,8 @@ case "$#" in
cmd="$1"
shift
case "$cmd" in
help)
git bisect -h ;;
start)
bisect_start "$@" ;;
bad|good|skip)

View File

@@ -269,6 +269,7 @@ fi
if test -n "$2"
then
dir="$2"
test $# = 2 || die "excess parameter to git-clone"
else
# Derive one from the repository name
# Try using "humanish" part of source repo if user didn't specify one

View File

@@ -291,6 +291,12 @@ static inline void *xmalloc(size_t size)
return ret;
}
/*
* xmemdupz() allocates (len + 1) bytes of memory, duplicates "len" bytes of
* "data" to the allocated memory, zero terminates the allocated memory,
* and returns a pointer to the allocated memory. If the allocation fails,
* the program dies.
*/
static inline void *xmemdupz(const void *data, size_t len)
{
char *p = xmalloc(len + 1);
@@ -352,6 +358,11 @@ static inline void *xmmap(void *start, size_t length,
return ret;
}
/*
* xread() is the same a read(), but it automatically restarts read()
* operations with a recoverable error (EAGAIN and EINTR). xread()
* DOES NOT GUARANTEE that "len" bytes is read even if the data is available.
*/
static inline ssize_t xread(int fd, void *buf, size_t len)
{
ssize_t nr;
@@ -363,6 +374,11 @@ static inline ssize_t xread(int fd, void *buf, size_t len)
}
}
/*
* xwrite() is the same a write(), but it automatically restarts write()
* operations with a recoverable error (EAGAIN and EINTR). xwrite() DOES NOT
* GUARANTEE that "len" bytes is written even if the operation is successful.
*/
static inline ssize_t xwrite(int fd, const void *buf, size_t len)
{
ssize_t nr;

View File

@@ -772,7 +772,7 @@ sub commit {
waitpid($pid,0);
die "Error running git-commit-tree: $?\n" if $?;
system("git-update-ref $remote/$branch $cid") == 0
system('git-update-ref', "$remote/$branch", $cid) == 0
or die "Cannot write branch $branch for update: $!\n";
if ($tag) {

View File

@@ -309,22 +309,42 @@ then
}
fi
# If the branch to rebase is given, first switch to it.
# If the branch to rebase is given, that is the branch we will rebase
# $branch_name -- branch being rebased, or HEAD (already detached)
# $orig_head -- commit object name of tip of the branch before rebasing
# $head_name -- refs/heads/<that-branch> or "detached HEAD"
switch_to=
case "$#" in
2)
# Is it "rebase other $branchname" or "rebase other $commit"?
branch_name="$2"
git-checkout "$2" || usage
;;
*)
if branch_name=`git symbolic-ref -q HEAD`
switch_to="$2"
if git show-ref --verify --quiet -- "refs/heads/$2" &&
branch=$(git rev-parse --verify "refs/heads/$2" 2>/dev/null)
then
branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
head_name="refs/heads/$2"
elif branch=$(git rev-parse --verify "$2" 2>/dev/null)
then
head_name="detached HEAD"
else
branch_name=HEAD ;# detached
usage
fi
;;
*)
# Do not need to switch branches, we are already on it.
if branch_name=`git symbolic-ref -q HEAD`
then
head_name=$branch_name
branch_name=`expr "z$branch_name" : 'zrefs/heads/\(.*\)'`
else
head_name="detached HEAD"
branch_name=HEAD ;# detached
fi
branch=$(git rev-parse --verify "${branch_name}^0") || exit
;;
esac
branch=$(git rev-parse --verify "${branch_name}^0") || exit
orig_head=$branch
# Now we are rebasing commits $upstream..$branch on top of $onto
@@ -335,6 +355,8 @@ if test "$upstream" = "$onto" && test "$mb" = "$onto" &&
# linear history?
! git rev-list --parents "$onto".."$branch" | grep " .* " > /dev/null
then
# Lazily switch to the target branch if needed...
test -z "$switch_to" || git checkout "$switch_to"
echo >&2 "Current branch $branch_name is up to date."
exit 0
fi
@@ -346,22 +368,11 @@ then
GIT_PAGER='' git diff --stat --summary "$mb" "$onto"
fi
# move to a detached HEAD
orig_head=$(git rev-parse HEAD^0)
head_name=$(git symbolic-ref HEAD 2> /dev/null)
case "$head_name" in
'')
head_name="detached HEAD"
;;
*)
git checkout "$orig_head" > /dev/null 2>&1 ||
die "could not detach HEAD"
;;
esac
# Rewind the head to "$onto"; this saves our current head in ORIG_HEAD.
# Detach HEAD and reset the tree
echo "First, rewinding head to replay your work on top of it..."
git-reset --hard "$onto"
git checkout "$onto^0" >/dev/null 2>&1 ||
die "could not detach HEAD"
# git reset --hard "$onto^0"
# If the $onto is a proper descendant of the tip of the branch, then
# we just fast forwarded.
@@ -374,7 +385,8 @@ fi
if test -z "$do_merge"
then
git format-patch -k --stdout --full-index --ignore-if-in-upstream "$upstream"..ORIG_HEAD |
git format-patch -k --stdout --full-index --ignore-if-in-upstream \
"$upstream..$orig_head" |
git am $git_am_opt --rebasing --resolvemsg="$RESOLVEMSG" &&
move_to_original_branch
ret=$?
@@ -397,7 +409,7 @@ echo "$orig_head" > "$dotest/orig-head"
echo "$head_name" > "$dotest/head-name"
msgnum=0
for cmt in `git rev-list --reverse --no-merges "$upstream"..ORIG_HEAD`
for cmt in `git rev-list --reverse --no-merges "$upstream..$orig_head"`
do
msgnum=$(($msgnum + 1))
echo "$cmt" > "$dotest/cmt.$msgnum"

View File

@@ -168,7 +168,8 @@ my $envelope_sender;
# Example reply to:
#$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
my $repo = Git->repository();
my $repo = eval { Git->repository() };
my @repo = $repo ? ($repo) : ();
my $term = eval {
$ENV{"GIT_SEND_EMAIL_NOTTY"}
? new Term::ReadLine 'git-send-email', \*STDIN, \*STDOUT
@@ -271,25 +272,25 @@ sub read_config {
foreach my $setting (keys %config_bool_settings) {
my $target = $config_bool_settings{$setting}->[0];
$$target = $repo->config_bool("$prefix.$setting") unless (defined $$target);
$$target = Git::config_bool(@repo, "$prefix.$setting") unless (defined $$target);
}
foreach my $setting (keys %config_settings) {
my $target = $config_settings{$setting};
if (ref($target) eq "ARRAY") {
unless (@$target) {
my @values = $repo->config("$prefix.$setting");
my @values = Git::config(@repo, "$prefix.$setting");
@$target = @values if (@values && defined $values[0]);
}
}
else {
$$target = $repo->config("$prefix.$setting") unless (defined $$target);
$$target = Git::config(@repo, "$prefix.$setting") unless (defined $$target);
}
}
}
# read configuration from [sendemail "$identity"], fall back on [sendemail]
$identity = $repo->config("sendemail.identity") unless (defined $identity);
$identity = Git::config(@repo, "sendemail.identity") unless (defined $identity);
read_config("sendemail.$identity") if (defined $identity);
read_config("sendemail");
@@ -327,8 +328,9 @@ if (0) {
}
}
my ($repoauthor) = $repo->ident_person('author');
my ($repocommitter) = $repo->ident_person('committer');
my ($repoauthor, $repocommitter);
($repoauthor) = Git::ident_person(@repo, 'author');
($repocommitter) = Git::ident_person(@repo, 'committer');
# Verify the user input
@@ -415,7 +417,7 @@ if (@files) {
my $prompting = 0;
if (!defined $sender) {
$sender = $repoauthor || $repocommitter;
$sender = $repoauthor || $repocommitter || '';
while (1) {
$_ = $term->readline("Who should the emails appear to be from? [$sender] ");
@@ -509,7 +511,7 @@ GIT: for the patch you are writing.
EOT
close(C);
my $editor = $ENV{GIT_EDITOR} || $repo->config("core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
my $editor = $ENV{GIT_EDITOR} || Git::config(@repo, "core.editor") || $ENV{VISUAL} || $ENV{EDITOR} || "vi";
system('sh', '-c', '$0 $@', $editor, $compose_filename);
open(C2,">",$compose_filename . ".final")

View File

@@ -327,7 +327,8 @@ set_name_rev () {
cd "$1" && {
git describe "$2" 2>/dev/null ||
git describe --tags "$2" 2>/dev/null ||
git describe --contains --tags "$2"
git describe --contains "$2" 2>/dev/null ||
git describe --all --always "$2"
}
) )
test -z "$revname" || revname=" ($revname)"
@@ -342,6 +343,7 @@ set_name_rev () {
#
cmd_summary() {
summary_limit=-1
for_status=
# parse $args after "submodule ... summary".
while test $# -ne 0
@@ -350,6 +352,9 @@ cmd_summary() {
--cached)
cached="$1"
;;
--for-status)
for_status="$1"
;;
-n|--summary-limit)
if summary_limit=$(($2 + 0)) 2>/dev/null && test "$summary_limit" = "$2"
then
@@ -397,7 +402,8 @@ cmd_summary() {
done
)
test -n "$modules" &&
test -z "$modules" && return
git diff-index $cached --raw $head -- $modules |
grep -e '^:160000' -e '^:[0-7]* 160000' |
cut -c2- |
@@ -499,7 +505,14 @@ cmd_summary() {
echo
fi
echo
done
done |
if test -n "$for_status"; then
echo "# Modified submodules:"
echo "#"
sed -e 's|^|# |' -e 's|^# $|#|'
else
cat
fi
}
#
# List all submodules, prefixed with:

View File

@@ -410,10 +410,12 @@ sub cmd_dcommit {
$head ||= 'HEAD';
my @refs;
my ($url, $rev, $uuid, $gs) = working_head_info($head, \@refs);
print "Committing to $url ...\n";
if ($url) {
print "Committing to $url ...\n";
}
unless ($gs) {
die "Unable to determine upstream SVN information from ",
"$head history\n";
"$head history.\nPerhaps the repository is empty.";
}
my $last_rev;
my ($linear_refs, $parents) = linearize_history($gs, \@refs);
@@ -1120,7 +1122,7 @@ sub cmt_metadata {
sub working_head_info {
my ($head, $refs) = @_;
my @args = ('log', '--no-color', '--first-parent');
my @args = ('log', '--no-color', '--first-parent', '--pretty=medium');
my ($fh, $ctx) = command_output_pipe(@args, $head);
my $hash;
my %max;
@@ -2375,8 +2377,7 @@ sub check_author {
my ($author) = @_;
if (!defined $author || length $author == 0) {
$author = '(no author)';
}
if (defined $::_authors && ! defined $::users{$author}) {
} elsif (defined $::_authors && ! defined $::users{$author}) {
die "Author: $author not defined in $::_authors file\n";
}
$author;
@@ -2519,6 +2520,7 @@ sub rebuild_from_rev_db {
my ($self, $path) = @_;
my $r = -1;
open my $fh, '<', $path or croak "open: $!";
binmode $fh or croak "binmode: $!";
while (<$fh>) {
length($_) == 41 or croak "inconsistent size in ($_) != 41";
chomp($_);
@@ -2616,6 +2618,7 @@ sub rebuild {
sub _rev_map_set {
my ($fh, $rev, $commit) = @_;
binmode $fh or croak "binmode: $!";
my $size = (stat($fh))[7];
($size % 24) == 0 or croak "inconsistent size: $size";
@@ -2719,6 +2722,7 @@ sub rev_map_max {
my $map_path = $self->map_path;
stat $map_path or return $want_commit ? (0, undef) : 0;
sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
binmode $fh or croak "binmode: $!";
my $size = (stat($fh))[7];
($size % 24) == 0 or croak "inconsistent size: $size";
@@ -2751,6 +2755,7 @@ sub rev_map_get {
return undef unless -e $map_path;
sysopen(my $fh, $map_path, O_RDONLY) or croak "open: $!";
binmode $fh or croak "binmode: $!";
my $size = (stat($fh))[7];
($size % 24) == 0 or croak "inconsistent size: $size";

2
git.c
View File

@@ -347,7 +347,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "shortlog", cmd_shortlog, USE_PAGER },
{ "show-branch", cmd_show_branch, RUN_SETUP },
{ "show", cmd_show, RUN_SETUP | USE_PAGER },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
{ "status", cmd_status, RUN_SETUP | NEED_WORK_TREE | USE_PAGER },
{ "stripspace", cmd_stripspace },
{ "symbolic-ref", cmd_symbolic_ref, RUN_SETUP },
{ "tag", cmd_tag, RUN_SETUP },

View File

@@ -29,40 +29,40 @@ Build time configuration
See also "How to configure gitweb for your local system" in README
file for gitweb (in gitweb/README).
- There are many configuration variables which affects building of
- There are many configuration variables which affect building of
gitweb.cgi; see "default configuration for gitweb" section in main
(top dir) Makefile, and instructions for building gitweb/gitweb.cgi
target.
One of most important is where to find git wrapper binary. Gitweb
tries to find git wrapper at $(bindir)/git, so you have to set $bindir
One of the most important is where to find the git wrapper binary. Gitweb
tries to find the git wrapper at $(bindir)/git, so you have to set $bindir
when building gitweb.cgi, or $prefix from which $bindir is derived. If
you build and install gitweb together with the rest of git suite,
you build and install gitweb together with the rest of the git suite,
there should be no problems. Otherwise, if git was for example
installed from a binary package, you have to set $prefix (or $bindir)
accordingly.
- Another important issue is where are git repositories you want to make
available to gitweb. By default gitweb search for repositories under
available to gitweb. By default gitweb searches for repositories under
/pub/git; if you want to have projects somewhere else, like /home/git,
use GITWEB_PROJECTROOT build configuration variable.
By default all git repositories under projectroot are visible and
available to gitweb. List of projects is generated by default by
available to gitweb. The list of projects is generated by default by
scanning the projectroot directory for git repositories. This can be
changed (configured) as described in "Gitweb repositories" section
below.
Note that gitweb deals directly with object database, and does not
need working directory; the name of the project is the name of its
Note that gitweb deals directly with the object database, and does not
need a working directory; the name of the project is the name of its
repository object database, usually projectname.git for bare
repositories. If you want to provide gitweb access to non-bare (live)
repository, you can make projectname.git symbolic link under
repositories, you can make projectname.git a symbolic link under
projectroot linking to projectname/.git (but it is just
a suggestion).
- You can control where gitweb tries to find its main CSS style file,
its favicon and logo with GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
its favicon and logo with the GITWEB_CSS, GITWEB_FAVICON and GITWEB_LOGO
build configuration variables. By default gitweb tries to find them
in the same directory as gitweb.cgi script.
@@ -91,13 +91,17 @@ Gitweb config file
See also "Runtime gitweb configuration" section in README file
for gitweb (in gitweb/README).
- You can configure gitweb further using gitweb configuration file;
by default it is file named gitweb_config.perl in the same place as
gitweb.cgi script. You can control default place for config file
using GITWEB_CONFIG build configuration variable, and you can set it
using GITWEB_CONFIG environmental variable.
- You can configure gitweb further using the gitweb configuration file;
by default this is a file named gitweb_config.perl in the same place as
gitweb.cgi script. You can control the default place for the config file
using the GITWEB_CONFIG build configuration variable, and you can set it
using the GITWEB_CONFIG environment variable. If this file does not
exist, gitweb looks for a system-wide configuration file, normally
/etc/gitweb.conf. You can change the default using the
GITWEB_CONFIG_SYSTEM build configuration variable, and override it
through the GITWEB_CONFIG_SYSTEM environment variable.
- Gitweb config file is [fragment] of perl code. You can set variables
- The gitweb config file is a fragment of perl code. You can set variables
using "our $variable = value"; text from "#" character until the end
of a line is ignored. See perlsyn(1) for details.
@@ -124,36 +128,37 @@ Gitweb repositories
-------------------
- By default all git repositories under projectroot are visible and
available to gitweb. List of projects is generated by default by
available to gitweb. The list of projects is generated by default by
scanning the projectroot directory for git repositories (for object
databases to be more exact).
You can provide pre-generated list of [visible] repositories,
You can provide a pre-generated list of [visible] repositories,
together with information about their owners (the project ownership
is taken from owner of repository directory otherwise), by setting
GITWEB_LIST build configuration variable (or $projects_list variable
in gitweb config file) to point to a plain file.
defaults to the owner of the repository directory otherwise), by setting
the GITWEB_LIST build configuration variable (or the $projects_list
variable in the gitweb config file) to point to a plain file.
Each line of projects list file should consist of url-encoded path
to project repository database (relative to projectroot) separated
by space from url-encoded project owner; spaces in both project path
and project owner have to be encoded as either '%20' or '+'.
Each line of the projects list file should consist of the url-encoded path
to the project repository database (relative to projectroot), followed
by the url-encoded project owner on the same line (separated by a space).
Spaces in both project path and project owner have to be encoded as either
'%20' or '+'.
You can generate projects list index file using project_index action
(the 'TXT' link on projects list page) directly from gitweb.
You can generate the projects list index file using the project_index
action (the 'TXT' link on projects list page) directly from gitweb.
- By default even if project is not visible on projects list page, you
can view it nevertheless by hand-crafting gitweb URL. You can set
GITWEB_STRICT_EXPORT build configuration variable (or $strict_export
variable in gitweb config file) to only allow viewing of
- By default, even if a project is not visible on projects list page, you
can view it nevertheless by hand-crafting a gitweb URL. You can set the
GITWEB_STRICT_EXPORT build configuration variable (or the $strict_export
variable in the gitweb config file) to only allow viewing of
repositories also shown on the overview page.
- Alternatively, you can configure gitweb to only list and allow
viewing of the explicitly exported repositories, via
GITWEB_EXPORT_OK build configuration variable (or $export_ok
viewing of the explicitly exported repositories, via the
GITWEB_EXPORT_OK build configuration variable (or the $export_ok
variable in gitweb config file). If it evaluates to true, gitweb
show repository only if this file exists in its object database
(if directory has the magic file $export_ok).
shows repositories only if this file exists in its object database
(if directory has the magic file named $export_ok).
Generating projects list using gitweb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@@ -2,7 +2,7 @@ GIT web Interface
=================
The one working on:
http://www.kernel.org/git/
http://git.kernel.org/
From the git version 1.4.0 gitweb is bundled with git.
@@ -10,13 +10,13 @@ From the git version 1.4.0 gitweb is bundled with git.
How to configure gitweb for your local system
---------------------------------------------
See also "Build time configuration" section in INSTALL
See also the "Build time configuration" section in the INSTALL
file for gitweb (in gitweb/INSTALL).
You can specify the following configuration variables when building GIT:
* GIT_BINDIR
Points out where to find git executable. You should set up it to
the place where git binary was installed (usually /usr/bin) if you
Points where to find the git executable. You should set it up to
the place where the git binary was installed (usually /usr/bin) if you
don't install git from sources together with gitweb. [Default: $(bindir)]
* GITWEB_SITENAME
Shown in the title of all generated pages, defaults to the server name
@@ -24,13 +24,13 @@ You can specify the following configuration variables when building GIT:
* GITWEB_PROJECTROOT
The root directory for all projects shown by gitweb. Must be set
correctly for gitweb to find repositories to display. See also
"Gitweb repositories" in INSTALL file for gitweb. [Default: /pub/git]
"Gitweb repositories" in the INSTALL file for gitweb. [Default: /pub/git]
* GITWEB_PROJECT_MAXDEPTH
The filesystem traversing limit for getting projects list; the number
The filesystem traversing limit for getting the project list; the number
is taken as depth relative to the projectroot. It is used when
GITWEB_LIST is a directory (or is not set; then project root is used).
Is is meant to speed up project listing on large work trees by limiting
find depth. [Default: 2007]
search depth. [Default: 2007]
* GITWEB_LIST
Points to a directory to scan for projects (defaults to project root
if not set / if empty) or to a file with explicit listing of projects
@@ -72,15 +72,15 @@ You can specify the following configuration variables when building GIT:
Git base URLs used for URL to where fetch project from, i.e. full
URL is "$git_base_url/$project". Shown on projects summary page.
Repository URL for project can be also configured per repository; this
takes precendence over URL composed from base URL and project name.
takes precedence over URLs composed from base URL and a project name.
Note that you can setup multiple base URLs (for example one for
git:// protocol access, one for http:// access) from gitweb config
file. [No default]
git:// protocol access, another for http:// access) from the gitweb
config file. [No default]
* GITWEB_CSS
Points to the location where you put gitweb.css on your web server
(or to be more generic URI of gitweb stylesheet). Relative to base
URI of gitweb. Note that you can setup multiple stylesheets from
gitweb config file. [Default: gitweb.css]
(or to be more generic, the URI of gitweb stylesheet). Relative to the
base URI of gitweb. Note that you can setup multiple stylesheets from
the gitweb config file. [Default: gitweb.css]
* GITWEB_LOGO
Points to the location where you put git-logo.png on your web server
(or to be more generic URI of logo, 72x27 size, displayed in top right
@@ -100,13 +100,20 @@ You can specify the following configuration variables when building GIT:
is set when gitweb.cgi is executed, then the file specified in the
environment variable will be loaded instead of the file specified
when gitweb.cgi was created. [Default: gitweb_config.perl]
* GITWEB_CONFIG_SYSTEM
This Perl file will be loaded using 'do' as a fallback if GITWEB_CONFIG
does not exist. If the environment variable GITWEB_CONFIG_SYSTEM is set
when gitweb.cgi is executed, then the file specified in the environment
variable will be loaded instead of the file specified when gitweb.cgi was
created. [Default: /etc/gitweb.conf]
Runtime gitweb configuration
----------------------------
You can adjust gitweb behaviour using the file specified in `GITWEB_CONFIG`
(defaults to 'gitweb_config.perl' in the same directory as the CGI).
(defaults to 'gitweb_config.perl' in the same directory as the CGI), and
as a fallback `GITWEB_CONFIG_SYSTEM` (defaults to /etc/gitweb.conf).
The most notable thing that is not configurable at compile time are the
optional features, stored in the '%features' variable.
@@ -114,15 +121,15 @@ Ultimate description on how to reconfigure the default features setting
in your `GITWEB_CONFIG` or per-project in `project.git/config` can be found
as comments inside 'gitweb.cgi'.
See also "Gitweb config file" (with example of gitweb config file), and
"Gitweb repositories" sections in INSTALL file for gitweb.
See also the "Gitweb config file" (with an example of config file), and
the "Gitweb repositories" sections in INSTALL file for gitweb.
Gitweb config file is [fragment] of perl code. You can set variables
The gitweb config file is a fragment of perl code. You can set variables
using "our $variable = value"; text from "#" character until the end
of a line is ignored. See perlsyn(1) man page for details.
Below there is list of vaiables which you might want to set in gitweb config.
Below is the list of variables which you might want to set in gitweb config.
See the top of 'gitweb.cgi' for the full list of variables and their
descriptions.
@@ -133,7 +140,7 @@ You can set, among others, the following variables in gitweb config files
(with the exception of $projectroot and $projects_list this list does
not include variables usually directly set during build):
* $GIT
Cure git executable to use. By default set to "$GIT_BINDIR/git", which
Core git executable to use. By default set to "$GIT_BINDIR/git", which
in turn is by default set to "$(bindir)/git". If you use git from binary
package, set this to "/usr/bin/git". This can just be "git" if your
webserver has a sensible PATH. If you have multiple git versions
@@ -169,7 +176,7 @@ not include variables usually directly set during build):
to make it easier to upgrade gitweb. You can add 'site' stylesheet
for example by using
push @stylesheets, "gitweb-site.css";
in gitweb config file.
in the gitweb config file.
* $logo_url, $logo_label
URI and label (title) of GIT logo link (or your site logo, if you choose
to use different logo image). By default they point to git homepage;
@@ -191,12 +198,12 @@ not include variables usually directly set during build):
Default mimetype for blob_plain (raw) view, if mimetype checking
doesn't result in some other type; by default 'text/plain'.
* $default_text_plain_charset
Default charset for text files. If not set, web serwer configuration
Default charset for text files. If not set, web server configuration
would be used.
* $mimetypes_file
File to use for (filename extension based) guessing of MIME types before
trying /etc/mime.types. Path, if relative, is taken currently as taken
relative to current git repositoy.
trying /etc/mime.types. Path, if relative, is taken currently as
relative to the current git repository.
* $fallback_encoding
Gitweb assumes this charset if line contains non-UTF-8 characters.
Fallback decoding is used without error checking, so it can be even
@@ -225,14 +232,14 @@ You can use the following files in repository:
single line description of a project (of a repository). Plain text file;
HTML will be escaped. By default set to
Unnamed repository; edit this file to name it for gitweb.
from the template during creating repository. You can use
from the template during repository creation. You can use the
gitweb.description repo configuration variable, but the file takes
precendence.
precedence.
* cloneurl (or multiple-valued gitweb.url)
File with repository URL (used for clone and fetch), one per line.
Displayed in the project summary page. You can use multiple-valued
gitweb.url repository configuration variable for that, but the file
takes precendence.
takes precedence.
* gitweb.owner
You can use the gitweb.owner repository configuration variable to set
repository's owner. It is displayed in the project list and summary

View File

@@ -369,7 +369,12 @@ sub filter_snapshot_fmts {
}
our $GITWEB_CONFIG = $ENV{'GITWEB_CONFIG'} || "++GITWEB_CONFIG++";
do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
if (-e $GITWEB_CONFIG) {
do $GITWEB_CONFIG;
} else {
our $GITWEB_CONFIG_SYSTEM = $ENV{'GITWEB_CONFIG_SYSTEM'} || "++GITWEB_CONFIG_SYSTEM++";
do $GITWEB_CONFIG_SYSTEM if -e $GITWEB_CONFIG_SYSTEM;
}
# version of the core git binary
our $git_version = qx($GIT --version) =~ m/git version (.*)$/ ? $1 : "unknown";
@@ -506,7 +511,7 @@ sub evaluate_path_info {
}
# do not change any parameters if an action is given using the query string
return if $action;
$path_info =~ s,^$project/*,,;
$path_info =~ s,^\Q$project\E/*,,;
my ($refname, $pathname) = split(/:/, $path_info, 2);
if (defined $pathname) {
# we got "project.git/branch:filename" or "project.git/branch:dir/"
@@ -628,7 +633,7 @@ sub href(%) {
my ($use_pathinfo) = gitweb_check_feature('pathinfo');
if ($use_pathinfo) {
# use PATH_INFO for project name
$href .= "/$params{'project'}" if defined $params{'project'};
$href .= "/".esc_url($params{'project'}) if defined $params{'project'};
delete $params{'project'};
# Summary just uses the project path URL
@@ -2570,7 +2575,7 @@ EOF
my $action = $my_uri;
my ($use_pathinfo) = gitweb_check_feature('pathinfo');
if ($use_pathinfo) {
$action .= "/$project";
$action .= "/".esc_url($project);
} else {
$cgi->param("p", $project);
}
@@ -5171,14 +5176,26 @@ sub git_history {
my $refs = git_get_references();
my $limit = sprintf("--max-count=%i", (100 * ($page+1)));
my @commitlist = parse_commits($hash_base, 101, (100 * $page),
$file_name, "--full-history");
if (!@commitlist) {
die_error('404 Not Found', "No such file or directory on given branch");
}
if (!defined $hash && defined $file_name) {
$hash = git_get_hash_by_path($hash_base, $file_name);
# some commits could have deleted file in question,
# and not have it in tree, but one of them has to have it
for (my $i = 0; $i <= @commitlist; $i++) {
$hash = git_get_hash_by_path($commitlist[$i]{'id'}, $file_name);
last if defined $hash;
}
}
if (defined $hash) {
$ftype = git_get_type($hash);
}
my @commitlist = parse_commits($hash_base, 101, (100 * $page), $file_name, "--full-history");
if (!defined $ftype) {
die_error(undef, "Unknown type of object");
}
my $paging_nav = '';
if ($page > 0) {

View File

@@ -1303,8 +1303,11 @@ main(int argc, char **argv)
return 1;
}
if (!server.host) {
fprintf( stderr, "no imap host specified\n" );
return 1;
if (!server.tunnel) {
fprintf( stderr, "no imap host specified\n" );
return 1;
}
server.host = "tunnel";
}
/* read the messages */

View File

@@ -249,9 +249,9 @@ void show_log(struct rev_info *opt, const char *sep)
* not have an empty line between entries.
*/
extra = "";
if (*sep != '\n' && opt->commit_format == CMIT_FMT_ONELINE)
if (*sep != '\n' && opt->use_terminator)
extra = "\n";
if (opt->shown_one && opt->commit_format != CMIT_FMT_ONELINE)
if (opt->shown_one && !opt->use_terminator)
putchar(opt->diffopt.line_termination);
opt->shown_one = 1;
@@ -317,8 +317,10 @@ void show_log(struct rev_info *opt, const char *sep)
if (opt->show_log_size)
printf("log size %i\n", (int)msgbuf.len);
if (msgbuf.len)
printf("%s%s%s", msgbuf.buf, extra, sep);
if (msgbuf.len) {
fwrite(msgbuf.buf, sizeof(char), msgbuf.len, stdout);
printf("%s%s", extra, sep);
}
strbuf_release(&msgbuf);
}

View File

@@ -344,7 +344,7 @@ void usage_with_options_internal(const char * const *usagestr,
break;
case OPTION_INTEGER:
if (opts->flags & PARSE_OPT_OPTARG)
pos += fprintf(stderr, " [<n>]");
pos += fprintf(stderr, "[<n>]");
else
pos += fprintf(stderr, " <n>");
break;

34
path.c
View File

@@ -98,7 +98,8 @@ int validate_headref(const char *path)
struct stat st;
char *buf, buffer[256];
unsigned char sha1[20];
int len, fd;
int fd;
ssize_t len;
if (lstat(path, &st) < 0)
return -1;
@@ -273,24 +274,25 @@ int adjust_shared_perm(const char *path)
if (lstat(path, &st) < 0)
return -1;
mode = st.st_mode;
if (mode & S_IRUSR)
mode |= (shared_repository == PERM_GROUP
? S_IRGRP
: (shared_repository == PERM_EVERYBODY
? (S_IRGRP|S_IROTH)
: 0));
if (mode & S_IWUSR)
mode |= S_IWGRP;
if (shared_repository) {
int tweak = shared_repository;
if (!(mode & S_IWUSR))
tweak &= ~0222;
mode = (mode & ~0777) | tweak;
} else {
/* Preserve old PERM_UMASK behaviour */
if (mode & S_IWUSR)
mode |= S_IWGRP;
}
if (mode & S_IXUSR)
mode |= (shared_repository == PERM_GROUP
? S_IXGRP
: (shared_repository == PERM_EVERYBODY
? (S_IXGRP|S_IXOTH)
: 0));
if (S_ISDIR(mode))
if (S_ISDIR(mode)) {
mode |= FORCE_DIR_SET_GID;
/* Copy read bits to execute bits */
mode |= (shared_repository & 0444) >> 2;
}
if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
return -2;
return 0;

View File

@@ -487,22 +487,20 @@ does. In scalar context requires the variable to be set only one time
(exception is thrown otherwise), in array context returns allows the
variable to be set multiple times and returns all the values.
Must be called on a repository instance.
This currently wraps command('config') so it is not so fast.
=cut
sub config {
my ($self, $var) = @_;
$self->repo_path()
or throw Error::Simple("not a repository");
my ($self, $var) = _maybe_self(@_);
try {
my @cmd = ('config');
unshift @cmd, $self if $self;
if (wantarray) {
return $self->command('config', '--get-all', $var);
return command(@cmd, '--get-all', $var);
} else {
return $self->command_oneline('config', '--get', $var);
return command_oneline(@cmd, '--get', $var);
}
} catch Git::Error::Command with {
my $E = shift;
@@ -522,20 +520,17 @@ Retrieve the bool configuration C<VARIABLE>. The return value
is usable as a boolean in perl (and C<undef> if it's not defined,
of course).
Must be called on a repository instance.
This currently wraps command('config') so it is not so fast.
=cut
sub config_bool {
my ($self, $var) = @_;
$self->repo_path()
or throw Error::Simple("not a repository");
my ($self, $var) = _maybe_self(@_);
try {
my $val = $self->command_oneline('config', '--bool', '--get',
$var);
my @cmd = ('config', '--bool', '--get', $var);
unshift @cmd, $self if $self;
my $val = command_oneline(@cmd);
return undef unless defined $val;
return $val eq 'true';
} catch Git::Error::Command with {
@@ -557,19 +552,17 @@ or 'g' in the config file will cause the value to be multiplied
by 1024, 1048576 (1024^2), or 1073741824 (1024^3) prior to output.
It would return C<undef> if configuration variable is not defined,
Must be called on a repository instance.
This currently wraps command('config') so it is not so fast.
=cut
sub config_int {
my ($self, $var) = @_;
$self->repo_path()
or throw Error::Simple("not a repository");
my ($self, $var) = _maybe_self(@_);
try {
return $self->command_oneline('config', '--int', '--get', $var);
my @cmd = ('config', '--int', '--get', $var);
unshift @cmd, $self if $self;
return command_oneline(@cmd);
} catch Git::Error::Command with {
my $E = shift;
if ($E->value() == 1) {
@@ -639,15 +632,15 @@ The synopsis is like:
"$name <$email>" eq ident_person($name);
$time_tz =~ /^\d+ [+-]\d{4}$/;
Both methods must be called on a repository instance.
=cut
sub ident {
my ($self, $type) = @_;
my ($self, $type) = _maybe_self(@_);
my $identstr;
if (lc $type eq lc 'committer' or lc $type eq lc 'author') {
$identstr = $self->command_oneline('var', 'GIT_'.uc($type).'_IDENT');
my @cmd = ('var', 'GIT_'.uc($type).'_IDENT');
unshift @cmd, $self if $self;
$identstr = command_oneline(@cmd);
} else {
$identstr = $type;
}
@@ -659,8 +652,8 @@ sub ident {
}
sub ident_person {
my ($self, @ident) = @_;
$#ident == 0 and @ident = $self->ident($ident[0]);
my ($self, @ident) = _maybe_self(@_);
$#ident == 0 and @ident = $self ? $self->ident($ident[0]) : ident($ident[0]);
return "$ident[0] <$ident[1]>";
}

View File

@@ -4,40 +4,49 @@
#include "diff.h"
#include "revision.h"
static struct cmt_fmt_map {
const char *n;
size_t cmp_len;
enum cmit_fmt v;
} cmt_fmts[] = {
{ "raw", 1, CMIT_FMT_RAW },
{ "medium", 1, CMIT_FMT_MEDIUM },
{ "short", 1, CMIT_FMT_SHORT },
{ "email", 1, CMIT_FMT_EMAIL },
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
{ "format:", 7, CMIT_FMT_USERFORMAT},
};
static char *user_format;
enum cmit_fmt get_commit_format(const char *arg)
void get_commit_format(const char *arg, struct rev_info *rev)
{
int i;
static struct cmt_fmt_map {
const char *n;
size_t cmp_len;
enum cmit_fmt v;
} cmt_fmts[] = {
{ "raw", 1, CMIT_FMT_RAW },
{ "medium", 1, CMIT_FMT_MEDIUM },
{ "short", 1, CMIT_FMT_SHORT },
{ "email", 1, CMIT_FMT_EMAIL },
{ "full", 5, CMIT_FMT_FULL },
{ "fuller", 5, CMIT_FMT_FULLER },
{ "oneline", 1, CMIT_FMT_ONELINE },
};
if (!arg || !*arg)
return CMIT_FMT_DEFAULT;
rev->use_terminator = 0;
if (!arg || !*arg) {
rev->commit_format = CMIT_FMT_DEFAULT;
return;
}
if (*arg == '=')
arg++;
if (!prefixcmp(arg, "format:")) {
if (!prefixcmp(arg, "format:") || !prefixcmp(arg, "tformat:")) {
const char *cp = strchr(arg, ':') + 1;
free(user_format);
user_format = xstrdup(arg + 7);
return CMIT_FMT_USERFORMAT;
user_format = xstrdup(cp);
if (arg[0] == 't')
rev->use_terminator = 1;
rev->commit_format = CMIT_FMT_USERFORMAT;
return;
}
for (i = 0; i < ARRAY_SIZE(cmt_fmts); i++) {
if (!strncmp(arg, cmt_fmts[i].n, cmt_fmts[i].cmp_len) &&
!strncmp(arg, cmt_fmts[i].n, strlen(arg)))
return cmt_fmts[i].v;
!strncmp(arg, cmt_fmts[i].n, strlen(arg))) {
if (cmt_fmts[i].v == CMIT_FMT_ONELINE)
rev->use_terminator = 1;
rev->commit_format = cmt_fmts[i].v;
return;
}
}
die("invalid --pretty format: %s", arg);
@@ -457,6 +466,7 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
const struct commit *commit = c->commit;
const char *msg = commit->buffer;
struct commit_list *p;
int h1, h2;
/* these are independent of the commit */
switch (placeholder[0]) {
@@ -478,6 +488,16 @@ static size_t format_commit_item(struct strbuf *sb, const char *placeholder,
case 'n': /* newline */
strbuf_addch(sb, '\n');
return 1;
case 'x':
/* %x00 == NUL, %x0a == LF, etc. */
if (0 <= (h1 = hexval_table[0xff & placeholder[1]]) &&
h1 <= 16 &&
0 <= (h2 = hexval_table[0xff & placeholder[2]]) &&
h2 <= 16) {
strbuf_addch(sb, (h1<<4)|h2);
return 3;
} else
return 0;
}
/* these depend on the commit */

View File

@@ -10,6 +10,7 @@
static const char receive_pack_usage[] = "git-receive-pack <git-dir>";
static int deny_non_fast_forwards = 0;
static int receive_fsck_objects;
static int receive_unpack_limit = -1;
static int transfer_unpack_limit = -1;
static int unpack_limit = 100;
@@ -35,6 +36,11 @@ static int receive_pack_config(const char *var, const char *value)
return 0;
}
if (strcmp(var, "receive.fsckobjects") == 0) {
receive_fsck_objects = git_config_bool(var, value);
return 0;
}
return git_default_config(var, value);
}
@@ -368,11 +374,13 @@ static const char *unpack(void)
ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
if (ntohl(hdr.hdr_entries) < unpack_limit) {
int code;
const char *unpacker[3];
unpacker[0] = "unpack-objects";
unpacker[1] = hdr_arg;
unpacker[2] = NULL;
int code, i = 0;
const char *unpacker[4];
unpacker[i++] = "unpack-objects";
if (receive_fsck_objects)
unpacker[i++] = "--strict";
unpacker[i++] = hdr_arg;
unpacker[i++] = NULL;
code = run_command_v_opt(unpacker, RUN_GIT_CMD);
switch (code) {
case 0:
@@ -393,8 +401,8 @@ static const char *unpack(void)
return "unpacker exited with error code";
}
} else {
const char *keeper[6];
int s, status;
const char *keeper[7];
int s, status, i = 0;
char keep_arg[256];
struct child_process ip;
@@ -402,12 +410,14 @@ static const char *unpack(void)
if (gethostname(keep_arg + s, sizeof(keep_arg) - s))
strcpy(keep_arg + s, "localhost");
keeper[0] = "index-pack";
keeper[1] = "--stdin";
keeper[2] = "--fix-thin";
keeper[3] = hdr_arg;
keeper[4] = keep_arg;
keeper[5] = NULL;
keeper[i++] = "index-pack";
keeper[i++] = "--stdin";
if (receive_fsck_objects)
keeper[i++] = "--strict";
keeper[i++] = "--fix-thin";
keeper[i++] = hdr_arg;
keeper[i++] = keep_arg;
keeper[i++] = NULL;
memset(&ip, 0, sizeof(ip));
ip.argv = keeper;
ip.out = -1;

3
refs.c
View File

@@ -368,7 +368,8 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
{
int depth = MAXDEPTH, len;
int depth = MAXDEPTH;
ssize_t len;
char buffer[256];
static char ref_buffer[256];

View File

@@ -315,7 +315,7 @@ static int handle_config(const char *key, const char *value)
}
if (!prefixcmp(key, "url.")) {
struct rewrite *rewrite;
name = key + 5;
name = key + 4;
subkey = strrchr(name, '.');
if (!subkey)
return 0;
@@ -409,7 +409,7 @@ static void read_config(void)
alias_all_urls();
}
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
{
int i;
int st;
@@ -519,17 +519,32 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
return rs;
invalid:
if (verify) {
free(rs);
return NULL;
}
die("Invalid refspec '%s'", refspec[i]);
}
int valid_fetch_refspec(const char *fetch_refspec_str)
{
const char *fetch_refspec[] = { fetch_refspec_str };
struct refspec *refspec;
refspec = parse_refspec_internal(1, fetch_refspec, 1, 1);
if (refspec)
free(refspec);
return !!refspec;
}
struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
{
return parse_refspec_internal(nr_refspec, refspec, 1);
return parse_refspec_internal(nr_refspec, refspec, 1, 0);
}
struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
{
return parse_refspec_internal(nr_refspec, refspec, 0);
return parse_refspec_internal(nr_refspec, refspec, 0, 0);
}
static int valid_remote_nick(const char *name)
@@ -797,6 +812,26 @@ static struct ref *make_linked_ref(const char *name, struct ref ***tail)
return ret;
}
static char *guess_ref(const char *name, struct ref *peer)
{
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
const char *r = resolve_ref(peer->name, sha1, 1, NULL);
if (!r)
return NULL;
if (!prefixcmp(r, "refs/heads/"))
strbuf_addstr(&buf, "refs/heads/");
else if (!prefixcmp(r, "refs/tags/"))
strbuf_addstr(&buf, "refs/tags/");
else
return NULL;
strbuf_addstr(&buf, name);
return strbuf_detach(&buf, NULL);
}
static int match_explicit(struct ref *src, struct ref *dst,
struct ref ***dst_tail,
struct refspec *rs,
@@ -805,6 +840,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
struct ref *matched_src, *matched_dst;
const char *dst_value = rs->dst;
char *dst_guess;
if (rs->pattern)
return errs;
@@ -851,10 +887,15 @@ static int match_explicit(struct ref *src, struct ref *dst,
case 0:
if (!memcmp(dst_value, "refs/", 5))
matched_dst = make_linked_ref(dst_value, dst_tail);
else if((dst_guess = guess_ref(dst_value, matched_src)))
matched_dst = make_linked_ref(dst_guess, dst_tail);
else
error("dst refspec %s does not match any "
"existing ref on the remote and does "
"not start with refs/.", dst_value);
error("unable to push to unqualified destination: %s\n"
"The destination refspec neither matches an "
"existing ref on the remote nor\n"
"begins with refs/, and we are unable to "
"guess a prefix based on the source ref.",
dst_value);
break;
default:
matched_dst = NULL;

View File

@@ -67,6 +67,7 @@ void free_refs(struct ref *ref);
*/
void ref_remove_duplicates(struct ref *ref_map);
int valid_fetch_refspec(const char *refspec);
struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);

View File

@@ -1083,6 +1083,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
continue;
}
if (!strcmp(arg, "--topo-order")) {
revs->lifo = 1;
revs->topo_order = 1;
continue;
}
@@ -1198,7 +1199,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
}
if (!prefixcmp(arg, "--pretty")) {
revs->verbose_header = 1;
revs->commit_format = get_commit_format(arg+8);
get_commit_format(arg+8, revs);
continue;
}
if (!strcmp(arg, "--root")) {

View File

@@ -64,7 +64,8 @@ struct rev_info {
/* Format info */
unsigned int shown_one:1,
abbrev_commit:1;
abbrev_commit:1,
use_terminator:1;
enum date_mode date_mode;
const char **ignore_packed; /* pretend objects in these are unpacked */

60
setup.c
View File

@@ -448,21 +448,53 @@ const char *setup_git_directory_gently(int *nongit_ok)
int git_config_perm(const char *var, const char *value)
{
if (value) {
int i;
if (!strcmp(value, "umask"))
return PERM_UMASK;
if (!strcmp(value, "group"))
return PERM_GROUP;
if (!strcmp(value, "all") ||
!strcmp(value, "world") ||
!strcmp(value, "everybody"))
return PERM_EVERYBODY;
i = atoi(value);
if (i > 1)
return i;
int i;
char *endptr;
if (value == NULL)
return PERM_GROUP;
if (!strcmp(value, "umask"))
return PERM_UMASK;
if (!strcmp(value, "group"))
return PERM_GROUP;
if (!strcmp(value, "all") ||
!strcmp(value, "world") ||
!strcmp(value, "everybody"))
return PERM_EVERYBODY;
/* Parse octal numbers */
i = strtol(value, &endptr, 8);
/* If not an octal number, maybe true/false? */
if (*endptr != 0)
return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK;
/*
* Treat values 0, 1 and 2 as compatibility cases, otherwise it is
* a chmod value.
*/
switch (i) {
case PERM_UMASK: /* 0 */
return PERM_UMASK;
case OLD_PERM_GROUP: /* 1 */
return PERM_GROUP;
case OLD_PERM_EVERYBODY: /* 2 */
return PERM_EVERYBODY;
}
return git_config_bool(var, value);
/* A filemode value was given: 0xxx */
if ((i & 0600) != 0600)
die("Problem with core.sharedRepository filemode value "
"(0%.3o).\nThe owner of files must always have "
"read and write permissions.", i);
/*
* Mask filemode value. Others can not get write permission.
* x flags for directories are handled separately.
*/
return i & 0666;
}
int check_repository_format_version(const char *var, const char *value)

171
sha1-lookup.c Normal file
View File

@@ -0,0 +1,171 @@
#include "cache.h"
#include "sha1-lookup.h"
/*
* Conventional binary search loop looks like this:
*
* unsigned lo, hi;
* do {
* unsigned mi = (lo + hi) / 2;
* int cmp = "entry pointed at by mi" minus "target";
* if (!cmp)
* return (mi is the wanted one)
* if (cmp > 0)
* hi = mi; "mi is larger than target"
* else
* lo = mi+1; "mi is smaller than target"
* } while (lo < hi);
*
* The invariants are:
*
* - When entering the loop, lo points at a slot that is never
* above the target (it could be at the target), hi points at a
* slot that is guaranteed to be above the target (it can never
* be at the target).
*
* - We find a point 'mi' between lo and hi (mi could be the same
* as lo, but never can be as same as hi), and check if it hits
* the target. There are three cases:
*
* - if it is a hit, we are happy.
*
* - if it is strictly higher than the target, we set it to hi,
* and repeat the search.
*
* - if it is strictly lower than the target, we update lo to
* one slot after it, because we allow lo to be at the target.
*
* If the loop exits, there is no matching entry.
*
* When choosing 'mi', we do not have to take the "middle" but
* anywhere in between lo and hi, as long as lo <= mi < hi is
* satisfied. When we somehow know that the distance between the
* target and lo is much shorter than the target and hi, we could
* pick mi that is much closer to lo than the midway.
*
* Now, we can take advantage of the fact that SHA-1 is a good hash
* function, and as long as there are enough entries in the table, we
* can expect uniform distribution. An entry that begins with for
* example "deadbeef..." is much likely to appear much later than in
* the midway of the table. It can reasonably be expected to be near
* 87% (222/256) from the top of the table.
*
* However, we do not want to pick "mi" too precisely. If the entry at
* the 87% in the above example turns out to be higher than the target
* we are looking for, we would end up narrowing the search space down
* only by 13%, instead of 50% we would get if we did a simple binary
* search. So we would want to hedge our bets by being less aggressive.
*
* The table at "table" holds at least "nr" entries of "elem_size"
* bytes each. Each entry has the SHA-1 key at "key_offset". The
* table is sorted by the SHA-1 key of the entries. The caller wants
* to find the entry with "key", and knows that the entry at "lo" is
* not higher than the entry it is looking for, and that the entry at
* "hi" is higher than the entry it is looking for.
*/
int sha1_entry_pos(const void *table,
size_t elem_size,
size_t key_offset,
unsigned lo, unsigned hi, unsigned nr,
const unsigned char *key)
{
const unsigned char *base = table;
const unsigned char *hi_key, *lo_key;
unsigned ofs_0;
static int debug_lookup = -1;
if (debug_lookup < 0)
debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
if (!nr || lo >= hi)
return -1;
if (nr == hi)
hi_key = NULL;
else
hi_key = base + elem_size * hi + key_offset;
lo_key = base + elem_size * lo + key_offset;
ofs_0 = 0;
do {
int cmp;
unsigned ofs, mi, range;
unsigned lov, hiv, kyv;
const unsigned char *mi_key;
range = hi - lo;
if (hi_key) {
for (ofs = ofs_0; ofs < 20; ofs++)
if (lo_key[ofs] != hi_key[ofs])
break;
ofs_0 = ofs;
/*
* byte 0 thru (ofs-1) are the same between
* lo and hi; ofs is the first byte that is
* different.
*/
hiv = hi_key[ofs_0];
if (ofs_0 < 19)
hiv = (hiv << 8) | hi_key[ofs_0+1];
} else {
hiv = 256;
if (ofs_0 < 19)
hiv <<= 8;
}
lov = lo_key[ofs_0];
kyv = key[ofs_0];
if (ofs_0 < 19) {
lov = (lov << 8) | lo_key[ofs_0+1];
kyv = (kyv << 8) | key[ofs_0+1];
}
assert(lov < hiv);
if (kyv < lov)
return -1 - lo;
if (hiv < kyv)
return -1 - hi;
/*
* Even if we know the target is much closer to 'hi'
* than 'lo', if we pick too precisely and overshoot
* (e.g. when we know 'mi' is closer to 'hi' than to
* 'lo', pick 'mi' that is higher than the target), we
* end up narrowing the search space by a smaller
* amount (i.e. the distance between 'mi' and 'hi')
* than what we would have (i.e. about half of 'lo'
* and 'hi'). Hedge our bets to pick 'mi' less
* aggressively, i.e. make 'mi' a bit closer to the
* middle than we would otherwise pick.
*/
kyv = (kyv * 6 + lov + hiv) / 8;
if (lov < hiv - 1) {
if (kyv == lov)
kyv++;
else if (kyv == hiv)
kyv--;
}
mi = (range - 1) * (kyv - lov) / (hiv - lov) + lo;
if (debug_lookup) {
printf("lo %u hi %u rg %u mi %u ", lo, hi, range, mi);
printf("ofs %u lov %x, hiv %x, kyv %x\n",
ofs_0, lov, hiv, kyv);
}
if (!(lo <= mi && mi < hi))
die("assertion failure lo %u mi %u hi %u %s",
lo, mi, hi, sha1_to_hex(key));
mi_key = base + elem_size * mi + key_offset;
cmp = memcmp(mi_key + ofs_0, key + ofs_0, 20 - ofs_0);
if (!cmp)
return mi;
if (cmp > 0) {
hi = mi;
hi_key = mi_key;
} else {
lo = mi + 1;
lo_key = mi_key + elem_size;
}
} while (lo < hi);
return -lo-1;
}

9
sha1-lookup.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef SHA1_LOOKUP_H
#define SHA1_LOOKUP_H
extern int sha1_entry_pos(const void *table,
size_t elem_size,
size_t key_offset,
unsigned lo, unsigned hi, unsigned nr,
const unsigned char *key);
#endif

View File

@@ -15,6 +15,7 @@
#include "tree.h"
#include "refs.h"
#include "pack-revindex.h"
#include "sha1-lookup.h"
#ifndef O_NOATIME
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
@@ -1679,7 +1680,12 @@ off_t find_pack_entry_one(const unsigned char *sha1,
{
const uint32_t *level1_ofs = p->index_data;
const unsigned char *index = p->index_data;
unsigned hi, lo;
unsigned hi, lo, stride;
static int use_lookup = -1;
static int debug_lookup = -1;
if (debug_lookup < 0)
debug_lookup = !!getenv("GIT_DEBUG_LOOKUP");
if (!index) {
if (open_pack_index(p))
@@ -1694,11 +1700,34 @@ off_t find_pack_entry_one(const unsigned char *sha1,
index += 4 * 256;
hi = ntohl(level1_ofs[*sha1]);
lo = ((*sha1 == 0x0) ? 0 : ntohl(level1_ofs[*sha1 - 1]));
if (p->index_version > 1) {
stride = 20;
} else {
stride = 24;
index += 4;
}
if (debug_lookup)
printf("%02x%02x%02x... lo %u hi %u nr %u\n",
sha1[0], sha1[1], sha1[2], lo, hi, p->num_objects);
if (use_lookup < 0)
use_lookup = !!getenv("GIT_USE_LOOKUP");
if (use_lookup) {
int pos = sha1_entry_pos(index, stride, 0,
lo, hi, p->num_objects, sha1);
if (pos < 0)
return 0;
return nth_packed_object_offset(p, pos);
}
do {
unsigned mi = (lo + hi) / 2;
unsigned x = (p->index_version > 1) ? (mi * 20) : (mi * 24 + 4);
int cmp = hashcmp(index + x, sha1);
int cmp = hashcmp(index + mi * stride, sha1);
if (debug_lookup)
printf("lo %u hi %u rg %u mi %u\n",
lo, hi, hi - lo, mi);
if (!cmp)
return nth_packed_object_offset(p, mi);
if (cmp > 0)

View File

@@ -351,8 +351,11 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
}
if (0 <= nth)
at_time = 0;
else
at_time = approxidate(str + at + 2);
else {
char *tmp = xstrndup(str + at + 2, reflog_len);
at_time = approxidate(tmp);
free(tmp);
}
if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
&co_time, &co_tz, &co_cnt)) {
if (at_time)

View File

@@ -21,6 +21,7 @@ test_expect_success 'setup' '
mkdir -p a/b/d a/c &&
(
echo "f test=f"
echo "a/i test=a/i"
) >.gitattributes &&
(
echo "g test=a/g" &&
@@ -46,4 +47,11 @@ test_expect_success 'attribute test' '
'
test_expect_success 'root subdir attribute test' '
attr_check a/i a/i &&
attr_check subdir/a/i unspecified
'
test_done

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