Merge commit 'v1.5.1'

This commit is contained in:
Johannes Sixt
2007-04-06 16:29:46 +02:00
95 changed files with 3406 additions and 1194 deletions

View File

@@ -16,8 +16,9 @@ ARTICLES += repository-layout
ARTICLES += hooks
ARTICLES += everyday
ARTICLES += git-tools
ARTICLES += glossary
# with their own formatting rules.
SP_ARTICLES = glossary howto/revert-branch-rebase user-manual
SP_ARTICLES = howto/revert-branch-rebase user-manual
DOC_HTML += $(patsubst %,%.html,$(ARTICLES) $(SP_ARTICLES))
@@ -106,16 +107,11 @@ user-manual.xml: user-manual.txt user-manual.conf
$(ASCIIDOC) -b docbook -d book $<
XSLT = http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl
XSLTOPTS = --nonet --xinclude --stringparam html.stylesheet docbook-xsl.css
XSLTOPTS = --xinclude --stringparam html.stylesheet docbook-xsl.css
user-manual.html: user-manual.xml
xsltproc $(XSLTOPTS) -o $@ $(XSLT) $<
glossary.html : glossary.txt sort_glossary.pl
cat $< | \
perl sort_glossary.pl | \
$(ASCIIDOC) -b xhtml11 - > glossary.html
howto-index.txt: howto-index.sh $(wildcard howto/*.txt)
rm -f $@+ $@
sh ./howto-index.sh $(wildcard howto/*.txt) >$@+

View File

@@ -0,0 +1,22 @@
GIT v1.5.0.6 Release Notes
==========================
Fixes since v1.5.0.5
--------------------
* Bugfixes
- a handful small fixes to gitweb.
- build procedure for user-manual is fixed not to require locally
installed stylesheets.
- "git commit $paths" on paths whose earlier contents were
already updated in the index were failing out.
* Documentation
- user-manual has better cross references.
- gitweb installation/deployment procedure is now documented.

View File

@@ -0,0 +1,18 @@
GIT v1.5.0.7 Release Notes
==========================
Fixes since v1.5.0.6
--------------------
* Bugfixes
- git-upload-pack failed to close unused pipe ends, resulting
in many zombies to hang around.
- git-rerere was recording the contents of earlier hunks
duplicated in later hunks. This prevented resolving the same
conflict when performing the same merge the other way around.
* Documentation
- a few documentation fixes from Debian package maintainer.

View File

@@ -10,11 +10,15 @@ Updates since v1.5.0
* New commands and options.
- "git log" and friends take --reverse. This makes output
that typically goes reverse order in chronological order.
"git shortlog" usually lists commits in chronological order,
but with "--reverse", they are shown in reverse
chronological order.
- "git log" and friends take --reverse, which instructs them
to give their output in the order opposite from their usual.
They typically output from new to old, but with this option
their output would read from old to new. "git shortlog"
usually lists older commits first, but with this option,
they are shown from new to old.
- "git log --pretty=format:<string>" to allow more flexible
custom log output.
- "git diff" learned --ignore-space-at-eol. This is a weaker
form of --ignore-space-change.
@@ -22,9 +26,6 @@ Updates since v1.5.0
- "git diff --no-index pathA pathB" can be used as diff
replacement with git specific enhancements.
- "git diff --pretty=format:<string>" to allow more flexible
custom log output.
- "git diff --no-index" can read from '-' (standard input).
- "git diff" also learned --exit-code to exit with non-zero
@@ -33,6 +34,17 @@ Updates since v1.5.0
backward incompatible change; it will stay as an option for
now.
- "git diff --quiet" is --exit-code with output turned off,
meant for scripted use to quickly determine if there is any
tree-level difference.
- Textual patch generation with "git diff" without -w/-b
option has been significantly optimized. "git blame" got
faster because of the same change.
- "git log" and "git rev-list" has been optimized
significantly when they are used with pathspecs.
- "git branch --track" can be used to set up configuration
variables to help it easier to base your work on branches
you track from a remote site.
@@ -61,11 +73,33 @@ Updates since v1.5.0
symlinks on filesystems that do not support them; they are
checked out as regular files instead.
- You can name a commit object with its first line of the
message. The syntax to use is ':/message text'. E.g.
* Updated behaviour of existing commands.
$ git show ":/object name: introduce ':/<oneline prefix>' notation"
means the same thing as:
$ git show 28a4d940443806412effa246ecc7768a21553ec7
- "git bisect" learned a new command "run" that takes a script
to run after each revision is checked out to determine if it
is good or bad, to automate the bisection process.
- "git log" family learned a new traversal option --first-parent,
which does what the name suggests.
* Updated behavior of existing commands.
- "git-merge-recursive" used to barf when there are more than
one common ancestors for the merge, and merging them had a
rename/rename conflict. This has been fixed.
- "git fsck" does not barf on corrupt loose objects.
- "git rm" does not remove newly added files without -f.
- "git archimport" allows remapping when coming up with git
branch names from arch names.
@@ -83,10 +117,10 @@ Updates since v1.5.0
allow users to explicitly override this heuristic based on
paths.
- The behaviour of 'git-apply', when run in a subdirectory,
- The behavior of 'git-apply', when run in a subdirectory,
without --index nor --cached were inconsistent with that of
the command with these options. This was fixed to match the
behaviour with --index. A patch that is meant to be applied
behavior with --index. A patch that is meant to be applied
with -p1 from the toplevel of the project tree can be
applied with any custom -p<n> option. A patch that is not
relative to the toplevel needs to be applied with -p<n>
@@ -128,12 +162,17 @@ Updates since v1.5.0
the heaviest parts in C.
- "git mailinfo" which splits an e-mail into a patch and the
metainformation was rewritten, thanks to Don Zickus. It
handles nested multipart better.
meta-information was rewritten, thanks to Don Zickus. It
handles nested multipart better. The command was broken for
a brief period on 'master' branch since 1.5.0 but the
breakage is fixed now.
- send-email learned configurable bcc and chain-reply-to.
- Using objects from packs is now seriouly optimized by clever
- "git remote show $remote" also talks about branches that
would be pushed if you run "git push remote".
- Using objects from packs is now seriously optimized by clever
use of a cache. This should be most noticeable in git-log
family of commands that involve reading many tree objects.
In addition, traversing revisions while filtering changes
@@ -143,23 +182,190 @@ Updates since v1.5.0
* Hooks
- The sample update hook to show how to send out notification
e-mail was updated to show only new commits that appeared in
the repository. Earlier, it showed new commits that appeared
on the branch.
- The part to send out notification e-mails was removed from
the sample update hook, as it was not an appropriate place
to do so. The proper place to do this is the new post-receive
hook. An example hook has been added to contrib/hooks/.
* Others
- git-revert, git-gc and git-cherry-pick are now built-ins.
Fixes since v1.5.0
------------------
--
exec >/var/tmp/1
O=v1.5.0.5-446-g5d86501
echo O=`git describe master`
git shortlog --no-merges $O..master ^maint
These are all in v1.5.0.x series.
# Local Variables:
# mode: text
# End:
* Documentation updates
- Clarifications and corrections to 1.5.0 release notes.
- The main documentation did not link to git-remote documentation.
- Clarified introductory text of git-rebase documentation.
- Converted remaining mentions of update-index on Porcelain
documents to git-add/git-rm.
- Some i18n.* configuration variables were incorrectly
described as core.*; fixed.
- added and clarified core.bare, core.legacyheaders configurations.
- updated "git-clone --depth" documentation.
- user-manual updates.
- Options to 'git remote add' were described insufficiently.
- Configuration format.suffix was not documented.
- Other formatting and spelling fixes.
- user-manual has better cross references.
- gitweb installation/deployment procedure is now documented.
* Bugfixes
- git-upload-pack closes unused pipe ends; earlier this caused
many zombies to hang around.
- git-rerere was recording the contents of earlier hunks
duplicated in later hunks. This prevented resolving the same
conflict when performing the same merge the other way around.
- git-add and git-update-index on a filesystem on which
executable bits are unreliable incorrectly reused st_mode
bits even when the path changed between symlink and regular
file.
- git-daemon marks the listening sockets with FD_CLOEXEC so
that it won't be leaked into the children.
- segfault from git-blame when the mandatory pathname
parameter was missing was fixed; usage() message is given
instead.
- git-rev-list did not read $GIT_DIR/config file, which means
that did not honor i18n.logoutputencoding correctly.
- Automated merge conflict handling when changes to symbolic
links conflicted were completely broken. The merge-resolve
strategy created a regular file with conflict markers in it
in place of the symbolic link. The default strategy,
merge-recursive was even more broken. It removed the path
that was pointed at by the symbolic link. Both of these
problems have been fixed.
- 'git diff maint master next' did not correctly give combined
diff across three trees.
- 'git fast-import' portability fix for Solaris.
- 'git show-ref --verify' without arguments did not error out
but segfaulted.
- 'git diff :tracked-file `pwd`/an-untracked-file' gave an extra
slashes after a/ and b/.
- 'git format-patch' produced too long filenames if the commit
message had too long line at the beginning.
- Running 'make all' and then without changing anything
running 'make install' still rebuilt some files. This
was inconvenient when building as yourself and then
installing as root (especially problematic when the source
directory is on NFS and root is mapped to nobody).
- 'git-rerere' failed to deal with two unconflicted paths that
sorted next to each other.
- 'git-rerere' attempted to open(2) a symlink and failed if
there was a conflict. Since a conflicting change to a
symlink would not benefit from rerere anyway, the command
now ignores conflicting changes to symlinks.
- 'git-repack' did not like to pass more than 64 arguments
internally to underlying 'rev-list' logic, which made it
impossible to repack after accumulating many (small) packs
in the repository.
- 'git-diff' to review the combined diff during a conflicted
merge were not reading the working tree version correctly
when changes to a symbolic link conflicted. It should have
read the data using readlink(2) but read from the regular
file the symbolic link pointed at.
- 'git-remote' did not like period in a remote's name.
- 'git.el' honors the commit coding system from the configuration.
- 'blameview' in contrib/ correctly digs deeper when a line is
clicked.
- 'http-push' correctly makes sure the remote side has leading
path. Earlier it started in the middle of the path, and
incorrectly.
- 'git-merge' did not exit with non-zero status when the
working tree was dirty and cannot fast forward. It does
now.
- 'cvsexportcommit' does not lose yet-to-be-used message file.
- int-vs-size_t typefix when running combined diff on files
over 2GB long.
- 'git apply --whitespace=strip' should not touch unmodified
lines.
- 'git-mailinfo' choke when a logical header line was too long.
- 'git show A..B' did not error out. Negative ref ("not A" in
this example) does not make sense for the purpose of the
command, so now it errors out.
- 'git fmt-merge-msg --file' without file parameter did not
correctly error out.
- 'git archimport' barfed upon encountering a commit without
summary.
- 'git index-pack' did not protect itself from getting a short
read out of pread(2).
- 'git http-push' had a few buffer overruns.
- Build dependency fixes to rebuild fetch.o when other headers
change.
- git.el does not add duplicate sign-off lines.
- git-commit shows the full stat of the resulting commit, not
just about the files in the current directory, when run from
a subdirectory.
- "git-checkout -m '@{8 hours ago}'" had a funny failure from
eval; fixed.
- git-merge (hence git-pull) did not refuse fast-forwarding
when the working tree had local changes that would have
conflicted with it.
- a handful small fixes to gitweb.
- build procedure for user-manual is fixed not to require locally
installed stylesheets.
- "git commit $paths" on paths whose earlier contents were
already updated in the index were failing out.
* Tweaks
- sliding mmap() inefficiently mmaped the same region of a
packfile with an access pattern that used objects in the
reverse order. This has been made more efficient.

View File

@@ -164,5 +164,8 @@
That is, it exits with 1 if there were differences and
0 means no differences.
--quiet::
Disable all output of the program. Implies --exit-code.
For more detailed explanation on these common options, see also
link:diffcore.html[diffcore documentation].

View File

@@ -70,7 +70,7 @@ default. You could use `--no-utf8` to override this.
the patch.
-C<n>, -p<n>::
These flag are passed to the `git-apply` program that applies
These flags are passed to the `git-apply` program that applies
the patch.
--interactive::
@@ -87,6 +87,33 @@ default. You could use `--no-utf8` to override this.
DISCUSSION
----------
The commit author name is taken from the "From: " line of the
message, and commit author time is taken from the "Date: " line
of the message. The "Subject: " line is used as the title of
the commit, after stripping common prefix "[PATCH <anything>]".
It is supposed to describe what the commit is about concisely as
a one line text.
The body of the message (iow, after a blank line that terminates
RFC2822 headers) can begin with "Subject: " and "From: " lines
that are different from those of the mail header, to override
the values of these fields.
The commit message is formed by the title taken from the
"Subject: ", a blank line and the body of the message up to
where the patch begins. Excess whitespaces at the end of the
lines are automatically stripped.
The patch is expected to be inline, directly following the
message. Any line that is of form:
* three-dashes and end-of-line, or
* a line that begins with "diff -", or
* a line that begins with "Index: "
is taken as the beginning of a patch, and the commit log message
is terminated before the first occurrence of such a line.
When initially invoking it, you give it names of the mailboxes
to crunch. Upon seeing the first patch that does not apply, it
aborts in the middle, just like 'git-applymbox' does. You can

View File

@@ -12,8 +12,8 @@ SYNOPSIS
DESCRIPTION
-----------
The command takes various subcommands, and different options
depending on the subcommand:
The command takes various subcommands, and different options depending
on the subcommand:
git bisect start [<paths>...]
git bisect bad <rev>
@@ -22,30 +22,34 @@ depending on the subcommand:
git bisect visualize
git bisect replay <logfile>
git bisect log
git bisect run <cmd>...
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.
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.
Basic bisect commands: start, bad, good
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The way you use it is:
------------------------------------------------
$ git bisect start
$ git bisect bad # Current version is bad
$ git bisect good v2.6.13-rc2 # v2.6.13-rc2 was the last version
# tested that was good
$ git bisect bad # Current version is bad
$ git bisect good v2.6.13-rc2 # v2.6.13-rc2 was the last version
# tested that was good
------------------------------------------------
When you give at least one bad and one good versions, it will
bisect the revision tree and say something like:
When you give at least one bad and one good versions, it will bisect
the revision tree and say something like:
------------------------------------------------
Bisecting: 675 revisions left to test after this
------------------------------------------------
and check out the state in the middle. Now, compile that kernel, and boot
it. Now, let's say that this booted kernel works fine, then just do
and check out the state in the middle. Now, compile that kernel, and
boot it. Now, let's say that this booted kernel works fine, then just
do
------------------------------------------------
$ git bisect good # this one is good
@@ -57,12 +61,15 @@ which will now say
Bisecting: 337 revisions left to test after this
------------------------------------------------
and you continue along, compiling that one, testing it, and depending on
whether it is good or bad, you say "git bisect good" or "git bisect bad",
and ask for the next bisection.
and you continue along, compiling that one, testing it, and depending
on whether it is good or bad, you say "git bisect good" or "git bisect
bad", and ask for the next bisection.
Until you have no more left, and you'll have been left with the first bad
kernel rev in "refs/bisect/bad".
Until you have no more left, and you'll have been left with the first
bad kernel rev in "refs/bisect/bad".
Bisect reset
~~~~~~~~~~~~
Oh, and then after you want to reset to the original head, do a
@@ -70,10 +77,13 @@ Oh, and then after you want to reset to the original head, do a
$ git bisect reset
------------------------------------------------
to get back to the master branch, instead of being in one of the bisection
branches ("git bisect start" will do that for you too, actually: it will
reset the bisection state, and before it does that it checks that you're
not using some old bisection branch).
to get back to the master branch, instead of being in one of the
bisection branches ("git bisect start" will do that for you too,
actually: it will reset the bisection state, and before it does that
it checks that you're not using some old bisection branch).
Bisect visualize
~~~~~~~~~~~~~~~~
During the bisection process, you can say
@@ -83,9 +93,17 @@ $ git bisect visualize
to see the currently remaining suspects in `gitk`.
The good/bad input is logged, and `git bisect
log` shows what you have done so far. You can truncate its
output somewhere and save it in a file, and run
Bisect log and bisect replay
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The good/bad input is logged, and
------------
$ git bisect log
------------
shows what you have done so far. You can truncate its output somewhere
and save it in a file, and run
------------
$ git bisect replay that-file
@@ -94,12 +112,16 @@ $ git bisect replay that-file
if you find later you made a mistake telling good/bad about a
revision.
If in a middle of bisect session, you know what the bisect
suggested to try next is not a good one to test (e.g. the change
the commit introduces is known not to work in your environment
and you know it does not have anything to do with the bug you
are chasing), you may want to find a near-by commit and try that
instead. It goes something like this:
Avoiding to test a commit
~~~~~~~~~~~~~~~~~~~~~~~~~
If in a middle of bisect session, you know what the bisect suggested
to try next is not a good one to test (e.g. the change the commit
introduces is known not to work in your environment and you know it
does not have anything to do with the bug you are chasing), you may
want to find a near-by commit and try that instead.
It goes something like this:
------------
$ git bisect good/bad # previous round was good/bad.
@@ -109,18 +131,52 @@ $ git reset --hard HEAD~3 # try 3 revs before what
# was suggested
------------
Then compile and test the one you chose to try. After that,
tell bisect what the result was as usual.
Then compile and test the one you chose to try. After that, tell
bisect what the result was as usual.
You can further cut down the number of trials if you know what
part of the tree is involved in the problem you are tracking
down, by giving paths parameters when you say `bisect start`,
like this:
Cutting down bisection by giving path parameter to bisect start
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
You can further cut down the number of trials if you know what part of
the tree is involved in the problem you are tracking down, by giving
paths parameters when you say `bisect start`, like this:
------------
$ git bisect start arch/i386 include/asm-i386
------------
Bisect run
~~~~~~~~~~
If you have a script that can tell if the current source code is good
or bad, you can automatically bisect using:
------------
$ git bisect run my_script
------------
Note that the "run" script (`my_script` in the above example) should
exit with code 0 in case the current source code is good and with a
code between 1 and 127 (included) in case the current source code is
bad.
Any other exit code will abort the automatic bisect process. (A
program that does "exit(-1)" leaves $? = 255, see exit(3) manual page,
the value is chopped with "& 0377".)
You may often find that during bisect you want to have near-constant
tweaks (e.g., s/#define DEBUG 0/#define DEBUG 1/ in a header file, or
"revision that does not have this commit needs this patch applied to
work around other problem this bisection is not interested in")
applied to the revision being tested.
To cope with such a situation, after the inner git-bisect finds the
next revision to test, with the "run" script, you can apply that tweak
before compiling, run the real test, and after the test decides if the
revision (possibly with the needed tweaks) passed the test, rewind the
tree to the pristine state. Finally the "run" script can exit with
the status of the real test to let "git bisect run" command loop to
know the outcome.
Author
------

View File

@@ -134,9 +134,11 @@ checkout, diff, status, update, log, add, remove, commit.
Legacy monitoring operations are not supported (edit, watch and related).
Exports and tagging (tags and branches) are not supported at this stage.
The server will set the -k mode to binary when relevant. In proper GIT
tradition, the contents of the files are always respected.
No keyword expansion or newline munging is supported.
The server should set the -k mode to binary when relevant, however,
this is not really implemented yet. For now, you can force the server
to set `-kb` for all files by setting the `gitcvs.allbinary` config
variable. In proper GIT tradition, the contents of the files are
always respected. No keyword expansion or newline munging is supported.
Dependencies
------------

View File

@@ -38,6 +38,11 @@ include::pretty-formats.txt[]
and <until>, see "SPECIFYING REVISIONS" section in
gitlink:git-rev-parse[1].
--first-parent::
Follow only the first parent commit upon seeing a merge
commit. This option gives a better overview of the
evolution of a particular branch.
-p::
Show the change the commit introduces in a patch form.

View File

@@ -26,11 +26,11 @@ OPTIONS
Use the merge resolution program specified by <tool>.
Valid merge tools are:
kdiff3, tkdiff, meld, xxdiff, emerge, and vimdiff.
If a merge resolution program is not specified, 'git mergetool'
will use the configuration variable merge.tool. If the
configuration variable merge.tool is not set, 'git mergetool'
will pick a suitable default.
+
If a merge resolution program is not specified, 'git mergetool'
will use the configuration variable merge.tool. If the
configuration variable merge.tool is not set, 'git mergetool'
will pick a suitable default.
Author
------

View File

@@ -265,14 +265,14 @@ its all parents.
Here are a handful examples:
D A B D
D F A B C D F
^A G B D
^A F B C F
G...I C D F G I
^B G I C D F G I
F^@ A B C
F^! H D F H
D G H D
D F G H I J D F
^G D H D
^D B E I J F B
B...C G H D E B C
^D B C E I J F B C
C^@ I J F
F^! D G H D F
Author
------

View File

@@ -27,7 +27,7 @@ repository, or incrementally import into an existing one.
SVN access is done by the SVN::Perl module.
git-svnimport assumes that SVN repositories are organized into one
"trunk" directory where the main development happens, "branch/FOO"
"trunk" directory where the main development happens, "branches/FOO"
directories for branches, and "/tags/FOO" directories for tags.
Other subdirectories are ignored.

View File

@@ -35,17 +35,23 @@ ifdef::stalenotes[]
You are reading the documentation for the latest version of git.
Documentation for older releases are available here:
* link:v1.5.0.5/git.html[documentation for release 1.5.0.5]
* link:RelNotes-1.5.1.txt[release notes for 1.5.1]
* link:v1.5.0.5/RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
* link:v1.5.0.7/git.html[documentation for release 1.5.0.7]
* link:v1.5.0.3/RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
* link:RelNotes-1.5.0.7.txt[release notes for 1.5.0.7]
* link:v1.5.0.2/RelNotes-1.5.0.2.txt[release notes for 1.5.0.2]
* link:RelNotes-1.5.0.6.txt[release notes for 1.5.0.6]
* link:v1.5.0.1/RelNotes-1.5.0.1.txt[release notes for 1.5.0.1]
* link:RelNotes-1.5.0.5.txt[release notes for 1.5.0.5]
* link:v1.5.0/RelNotes-1.5.0.txt[release notes for 1.5.0]
* link:RelNotes-1.5.0.3.txt[release notes for 1.5.0.3]
* link:RelNotes-1.5.0.2.txt[release notes for 1.5.0.2]
* link:RelNotes-1.5.0.1.txt[release notes for 1.5.0.1]
* link:RelNotes-1.5.0.txt[release notes for 1.5.0]
* link:v1.4.4.4/git.html[documentation for release 1.4.4.4]

View File

@@ -1,365 +1,405 @@
alternate object database::
Via the alternates mechanism, a repository can inherit part of its
object database from another object database, which is called
"alternate".
GIT Glossary
============
bare repository::
A bare repository is normally an appropriately named
directory with a `.git` suffix that does not have a
locally checked-out copy of any of the files under revision
control. That is, all of the `git` administrative and
control files that would normally be present in the
hidden `.git` sub-directory are directly present in
the `repository.git` directory instead, and no other files
are present and checked out. Usually publishers of public
repositories make bare repositories available.
[[def_alternate_object_database]]alternate object database::
Via the alternates mechanism, a <<def_repository,repository>> can
inherit part of its <<def_object_database,object database>> from another
<<def_object_database,object database>>, which is called "alternate".
blob object::
Untyped object, e.g. the contents of a file.
[[def_bare_repository]]bare repository::
A <<def_bare_repository,bare repository>> is normally an appropriately
named <<def_directory,directory>> with a `.git` suffix that does not
have a locally checked-out copy of any of the files under
<<def_revision,revision>> control. That is, all of the `git`
administrative and control files that would normally be present in the
hidden `.git` sub-directory are directly present in the
`repository.git` directory instead,
and no other files are present and checked out. Usually publishers of
public repositories make bare repositories available.
branch::
A non-cyclical graph of revisions, i.e. the complete history of
a particular revision, which is called the branch head. The
branch heads are stored in `$GIT_DIR/refs/heads/`.
[[def_blob_object]]blob object::
Untyped <<def_object,object>>, e.g. the contents of a file.
cache::
Obsolete for: index.
[[def_branch]]branch::
A non-cyclical graph of revisions, i.e. the complete history of a
particular <<def_revision,revision>>, which is called the
branch <<def_head,head>>. The heads
are stored in `$GIT_DIR/refs/heads/`.
chain::
A list of objects, where each object in the list contains a
reference to its successor (for example, the successor of a commit
could be one of its parents).
[[def_cache]]cache::
Obsolete for: <<def_index,index>>.
changeset::
BitKeeper/cvsps speak for "commit". Since git does not store
changes, but states, it really does not make sense to use
the term "changesets" with git.
[[def_chain]]chain::
A list of objects, where each <<def_object,object>> in the list contains
a reference to its successor (for example, the successor of a
<<def_commit,commit>> could be one of its parents).
checkout::
The action of updating the working tree to a revision which was
stored in the object database.
[[def_changeset]]changeset::
BitKeeper/cvsps speak for "<<def_commit,commit>>". Since git does not
store changes, but states, it really does not make sense to use the term
"changesets" with git.
cherry-picking::
In SCM jargon, "cherry pick" means to choose a subset of
changes out of a series of changes (typically commits)
and record them as a new series of changes on top of
different codebase. In GIT, this is performed by
"git cherry-pick" command to extract the change
introduced by an existing commit and to record it based
on the tip of the current branch as a new commit.
[[def_checkout]]checkout::
The action of updating the <<def_working_tree,working tree>> to a
<<def_revision,revision>> which was stored in the
<<def_object_database,object database>>.
clean::
A working tree is clean, if it corresponds to the revision
referenced by the current head. Also see "dirty".
[[def_cherry-picking]]cherry-picking::
In <<def_SCM,SCM>> jargon, "cherry pick" means to choose a subset of
changes out of a series of changes (typically commits) and record them
as a new series of changes on top of different codebase. In GIT, this is
performed by "git cherry-pick" command to extract the change introduced
by an existing <<def_commit,commit>> and to record it based on the tip
of the current <<def_branch,branch>> as a new <<def_commit,commit>>.
commit::
As a verb: The action of storing the current state of the index in the
object database. The result is a revision.
As a noun: Short hand for commit object.
[[def_clean]]clean::
A <<def_working_tree,working tree>> is <<def_clean,clean>>, if it
corresponds to the <<def_revision,revision>> referenced by the current
<<def_head,head>>. Also see "<<def_dirty,dirty>>".
commit object::
An object which contains the information about a particular
revision, such as parents, committer, author, date and the
tree object which corresponds to the top directory of the
stored revision.
[[def_commit]]commit::
As a verb: The action of storing the current state of the
<<def_index,index>> in the <<def_object_database,object database>>. The
result is a <<def_revision,revision>>. As a noun: Short hand for
<<def_commit_object,commit object>>.
core git::
Fundamental data structures and utilities of git. Exposes only
limited source code management tools.
[[def_commit_object]]commit object::
An <<def_object,object>> which contains the information about a
particular <<def_revision,revision>>, such as parents, committer,
author, date and the <<def_tree_object,tree object>> which corresponds
to the top <<def_directory,directory>> of the stored
<<def_revision,revision>>.
DAG::
Directed acyclic graph. The commit objects form a directed acyclic
graph, because they have parents (directed), and the graph of commit
objects is acyclic (there is no chain which begins and ends with the
same object).
[[def_core_git]]core git::
Fundamental data structures and utilities of git. Exposes only limited
source code management tools.
dangling object::
An unreachable object which is not reachable even from other
unreachable objects; a dangling object has no references to it
from any reference or object in the repository.
[[def_DAG]]DAG::
Directed acyclic graph. The <<def_commit,commit>> objects form a
directed acyclic graph, because they have parents (directed), and the
graph of <<def_commit,commit>> objects is acyclic (there is no
<<def_chain,chain>> which begins and ends with the same
<<def_object,object>>).
dircache::
[[def_dangling_object]]dangling object::
An <<def_unreachable_object,unreachable object>> which is not
<<def_reachable,reachable>> even from other unreachable objects; a
<<def_dangling_object,dangling object>> has no references to it from any
reference or <<def_object,object>> in the <<def_repository,repository>>.
[[def_dircache]]dircache::
You are *waaaaay* behind.
dirty::
A working tree is said to be dirty if it contains modifications
which have not been committed to the current branch.
directory::
[[def_directory]]directory::
The list you get with "ls" :-)
ent::
Favorite synonym to "tree-ish" by some total geeks. See
[[def_dirty]]dirty::
A <<def_working_tree,working tree>> is said to be <<def_dirty,dirty>> if
it contains modifications which have not been committed to the current
<<def_branch,branch>>.
[[def_ent]]ent::
Favorite synonym to "<<def_tree-ish,tree-ish>>" by some total geeks. See
`http://en.wikipedia.org/wiki/Ent_(Middle-earth)` for an in-depth
explanation. Avoid this term, not to confuse people.
explanation. Avoid this term, not to confuse people.
fast forward::
A fast-forward is a special type of merge where you have
a revision and you are "merging" another branch's changes
that happen to be a descendant of what you have.
In such these cases, you do not make a new merge commit but
instead just update to his revision. This will happen
frequently on a tracking branch of a remote repository.
[[def_fast_forward]]fast forward::
A fast-forward is a special type of <<def_merge,merge>> where you have a
<<def_revision,revision>> and you are "merging" another
<<def_branch,branch>>'s changes that happen to be a descendant of what
you have. In such these cases, you do not make a new <<def_merge,merge>>
<<def_commit,commit>> but instead just update to his
<<def_revision,revision>>. This will happen frequently on a
<<def_tracking_branch,tracking branch>> of a remote
<<def_repository,repository>>.
fetch::
Fetching a branch means to get the branch's head ref from a
remote repository, to find out which objects are missing from
the local object database, and to get them, too.
[[def_fetch]]fetch::
Fetching a <<def_branch,branch>> means to get the
<<def_branch,branch>>'s <<def_head_ref,head ref>> from a remote
<<def_repository,repository>>, to find out which objects are missing
from the local <<def_object_database,object database>>, and to get them,
too.
file system::
Linus Torvalds originally designed git to be a user space file
system, i.e. the infrastructure to hold files and directories.
That ensured the efficiency and speed of git.
[[def_file_system]]file system::
Linus Torvalds originally designed git to be a user space file system,
i.e. the infrastructure to hold files and directories. That ensured the
efficiency and speed of git.
git archive::
Synonym for repository (for arch people).
[[def_git_archive]]git archive::
Synonym for <<def_repository,repository>> (for arch people).
grafts::
Grafts enables two otherwise different lines of development to be
joined together by recording fake ancestry information for commits.
This way you can make git pretend the set of parents a commit
has is different from what was recorded when the commit was created.
Configured via the `.git/info/grafts` file.
[[def_grafts]]grafts::
Grafts enables two otherwise different lines of development to be joined
together by recording fake ancestry information for commits. This way
you can make git pretend the set of parents a <<def_commit,commit>> has
is different from what was recorded when the <<def_commit,commit>> was
created. Configured via the `.git/info/grafts` file.
hash::
In git's context, synonym to object name.
[[def_hash]]hash::
In git's context, synonym to <<def_object_name,object name>>.
head::
The top of a branch. It contains a ref to the corresponding
commit object.
[[def_head]]head::
The top of a <<def_branch,branch>>. It contains a <<def_ref,ref>> to the
corresponding <<def_commit_object,commit object>>.
head ref::
A ref pointing to a head. Often, this is abbreviated to "head".
Head refs are stored in `$GIT_DIR/refs/heads/`.
[[def_head_ref]]head ref::
A <<def_ref,ref>> pointing to a <<def_head,head>>. Often, this is
abbreviated to "<<def_head,head>>". Head refs are stored in
`$GIT_DIR/refs/heads/`.
hook::
During the normal execution of several git commands,
call-outs are made to optional scripts that allow
a developer to add functionality or checking.
Typically, the hooks allow for a command to be pre-verified
and potentially aborted, and allow for a post-notification
after the operation is done.
The hook scripts are found in the `$GIT_DIR/hooks/` directory,
and are enabled by simply making them executable.
[[def_hook]]hook::
During the normal execution of several git commands, call-outs are made
to optional scripts that allow a developer to add functionality or
checking. Typically, the hooks allow for a command to be pre-verified
and potentially aborted, and allow for a post-notification after the
operation is done. The <<def_hook,hook>> scripts are found in the
`$GIT_DIR/hooks/` <<def_directory,directory>>, and are enabled by simply
making them executable.
index::
A collection of files with stat information, whose contents are
stored as objects. The index is a stored version of your working
tree. Truth be told, it can also contain a second, and even a third
version of a working tree, which are used when merging.
[[def_index]]index::
A collection of files with stat information, whose contents are stored
as objects. The <<def_index,index>> is a stored version of your working
<<def_tree,tree>>. Truth be told, it can also contain a second, and even
a third version of a <<def_working_tree,working tree>>, which are used
when merging.
index entry::
The information regarding a particular file, stored in the index.
An index entry can be unmerged, if a merge was started, but not
yet finished (i.e. if the index contains multiple versions of
that file).
[[def_index_entry]]index entry::
The information regarding a particular file, stored in the
<<def_index,index>>. An <<def_index_entry,index entry>> can be unmerged,
if a <<def_merge,merge>> was started, but not yet finished (i.e. if the
<<def_index,index>> contains multiple versions of that file).
master::
The default development branch. Whenever you create a git
repository, a branch named "master" is created, and becomes
the active branch. In most cases, this contains the local
[[def_master]]master::
The default development <<def_branch,branch>>. Whenever you create a git
<<def_repository,repository>>, a <<def_branch,branch>> named
"<<def_master,master>>" is created, and becomes the active
<<def_branch,branch>>. In most cases, this contains the local
development, though that is purely conventional and not required.
merge::
To merge branches means to try to accumulate the changes since a
common ancestor and apply them to the first branch. An automatic
merge uses heuristics to accomplish that. Evidently, an automatic
merge can fail.
[[def_merge]]merge::
To <<def_merge,merge>> branches means to try to accumulate the changes
since a common ancestor and apply them to the first
<<def_branch,branch>>. An automatic <<def_merge,merge>> uses heuristics
to accomplish that. Evidently, an automatic <<def_merge,merge>> can
fail.
object::
The unit of storage in git. It is uniquely identified by
the SHA1 of its contents. Consequently, an object can not
be changed.
[[def_object]]object::
The unit of storage in git. It is uniquely identified by the
<<def_SHA1,SHA1>> of its contents. Consequently, an
<<def_object,object>> can not be changed.
object database::
Stores a set of "objects", and an individual object is identified
by its object name. The objects usually live in `$GIT_DIR/objects/`.
[[def_object_database]]object database::
Stores a set of "objects", and an individual <<def_object,object>> is
identified by its <<def_object_name,object name>>. The objects usually
live in `$GIT_DIR/objects/`.
object identifier::
Synonym for object name.
[[def_object_identifier]]object identifier::
Synonym for <<def_object_name,object name>>.
object name::
The unique identifier of an object. The hash of the object's contents
using the Secure Hash Algorithm 1 and usually represented by the 40
character hexadecimal encoding of the hash of the object (possibly
followed by a white space).
[[def_object_name]]object name::
The unique identifier of an <<def_object,object>>. The <<def_hash,hash>>
of the <<def_object,object>>'s contents using the Secure Hash Algorithm
1 and usually represented by the 40 character hexadecimal encoding of
the <<def_hash,hash>> of the <<def_object,object>> (possibly followed by
a white space).
object type::
One of the identifiers "commit","tree","tag" and "blob" describing
the type of an object.
[[def_object_type]]object type::
One of the identifiers
"<<def_commit,commit>>","<<def_tree,tree>>","<<def_tag,tag>>" or "<<def_blob_object,blob>>"
describing the type of an <<def_object,object>>.
octopus::
To merge more than two branches. Also denotes an intelligent
predator.
[[def_octopus]]octopus::
To <<def_merge,merge>> more than two branches. Also denotes an
intelligent predator.
origin::
The default upstream repository. Most projects have at
least one upstream project which they track. By default
'origin' is used for that purpose. New upstream updates
[[def_origin]]origin::
The default upstream <<def_repository,repository>>. Most projects have
at least one upstream project which they track. By default
'<<def_origin,origin>>' is used for that purpose. New upstream updates
will be fetched into remote tracking branches named
origin/name-of-upstream-branch, which you can see using
"git branch -r".
"git <<def_branch,branch>> -r".
pack::
A set of objects which have been compressed into one file (to save
space or to transmit them efficiently).
[[def_pack]]pack::
A set of objects which have been compressed into one file (to save space
or to transmit them efficiently).
pack index::
[[def_pack_index]]pack index::
The list of identifiers, and other information, of the objects in a
pack, to assist in efficiently accessing the contents of a pack.
<<def_pack,pack>>, to assist in efficiently accessing the contents of a
<<def_pack,pack>>.
parent::
A commit object contains a (possibly empty) list of the logical
predecessor(s) in the line of development, i.e. its parents.
[[def_parent]]parent::
A <<def_commit_object,commit object>> contains a (possibly empty) list
of the logical predecessor(s) in the line of development, i.e. its
parents.
pickaxe::
The term pickaxe refers to an option to the diffcore routines
that help select changes that add or delete a given text string.
With the --pickaxe-all option, it can be used to view the
full changeset that introduced or removed, say, a particular
line of text. See gitlink:git-diff[1].
[[def_pickaxe]]pickaxe::
The term <<def_pickaxe,pickaxe>> refers to an option to the diffcore
routines that help select changes that add or delete a given text
string. With the --pickaxe-all option, it can be used to view the full
<<def_changeset,changeset>> that introduced or removed, say, a
particular line of text. See gitlink:git-diff[1].
plumbing::
Cute name for core git.
[[def_plumbing]]plumbing::
Cute name for <<def_core_git,core git>>.
porcelain::
Cute name for programs and program suites depending on core git,
presenting a high level access to core git. Porcelains expose
more of a SCM interface than the plumbing.
[[def_porcelain]]porcelain::
Cute name for programs and program suites depending on
<<def_core_git,core git>>, presenting a high level access to
<<def_core_git,core git>>. Porcelains expose more of a <<def_SCM,SCM>>
interface than the <<def_plumbing,plumbing>>.
pull::
Pulling a branch means to fetch it and merge it.
[[def_pull]]pull::
Pulling a <<def_branch,branch>> means to <<def_fetch,fetch>> it and
<<def_merge,merge>> it.
push::
Pushing a branch means to get the branch's head ref from a remote
repository, find out if it is an ancestor to the branch's local
head ref is a direct, and in that case, putting all objects, which
are reachable from the local head ref, and which are missing from
the remote repository, into the remote object database, and updating
the remote head ref. If the remote head is not an ancestor to the
local head, the push fails.
[[def_push]]push::
Pushing a <<def_branch,branch>> means to get the <<def_branch,branch>>'s
<<def_head_ref,head ref>> from a remote <<def_repository,repository>>,
find out if it is an ancestor to the <<def_branch,branch>>'s local
<<def_head_ref,head ref>> is a direct, and in that case, putting all
objects, which are <<def_reachable,reachable>> from the local
<<def_head_ref,head ref>>, and which are missing from the remote
<<def_repository,repository>>, into the remote
<<def_object_database,object database>>, and updating the remote
<<def_head_ref,head ref>>. If the remote <<def_head,head>> is not an
ancestor to the local <<def_head,head>>, the <<def_push,push>> fails.
reachable::
All of the ancestors of a given commit are said to be reachable from
that commit. More generally, one object is reachable from another if
we can reach the one from the other by a chain that follows tags to
whatever they tag, commits to their parents or trees, and trees to the
trees or blobs that they contain.
[[def_reachable]]reachable::
All of the ancestors of a given <<def_commit,commit>> are said to be
<<def_reachable,reachable>> from that <<def_commit,commit>>. More
generally, one <<def_object,object>> is <<def_reachable,reachable>> from
another if we can reach the one from the other by a <<def_chain,chain>>
that follows <<def_tag,tags>> to whatever they tag,
<<def_commit_object,commits>> to their parents or trees, and
<<def_tree_object,trees>> to the trees or <<def_blob_object,blobs>>
that they contain.
rebase::
To clean a branch by starting from the head of the main line of
development ("master"), and reapply the (possibly cherry-picked)
changes from that branch.
[[def_rebase]]rebase::
To reapply a series of changes from a <<def_branch,branch>> to a
different base, and reset the <<def_head,head>> of that branch
to the result.
ref::
A 40-byte hex representation of a SHA1 or a name that denotes
a particular object. These may be stored in `$GIT_DIR/refs/`.
[[def_ref]]ref::
A 40-byte hex representation of a <<def_SHA1,SHA1>> or a name that
denotes a particular <<def_object,object>>. These may be stored in
`$GIT_DIR/refs/`.
refspec::
A refspec is used by fetch and push to describe the mapping
between remote ref and local ref. They are combined with
a colon in the format <src>:<dst>, preceded by an optional
plus sign, +. For example:
`git fetch $URL refs/heads/master:refs/heads/origin`
means "grab the master branch head from the $URL and store
it as my origin branch head".
And `git push $URL refs/heads/master:refs/heads/to-upstream`
means "publish my master branch head as to-upstream branch
at $URL". See also gitlink:git-push[1]
[[def_refspec]]refspec::
A <<def_refspec,refspec>> is used by <<def_fetch,fetch>> and
<<def_push,push>> to describe the mapping between remote <<def_ref,ref>>
and local <<def_ref,ref>>. They are combined with a colon in the format
<src>:<dst>, preceded by an optional plus sign, +. For example: `git
fetch $URL refs/heads/master:refs/heads/origin` means
"grab the master <<def_branch,branch>> <<def_head,head>>
from the $URL and store it as my origin
<<def_branch,branch>> <<def_head,head>>". And `git <<def_push,push>>
$URL refs/heads/master:refs/heads/to-upstream` means
"publish my master <<def_branch,branch>>
<<def_head,head>> as to-upstream <<def_branch,branch>> at $URL". See
also gitlink:git-push[1]
repository::
A collection of refs together with an object database containing
all objects, which are reachable from the refs, possibly accompanied
by meta data from one or more porcelains. A repository can
share an object database with other repositories.
[[def_repository]]repository::
A collection of refs together with an <<def_object_database,object
database>> containing all objects which are <<def_reachable,reachable>>
from the refs, possibly accompanied by meta data from one or more
porcelains. A <<def_repository,repository>> can share an
<<def_object_database,object database>> with other repositories.
resolve::
The action of fixing up manually what a failed automatic merge
left behind.
[[def_resolve]]resolve::
The action of fixing up manually what a failed automatic
<<def_merge,merge>> left behind.
revision::
A particular state of files and directories which was stored in
the object database. It is referenced by a commit object.
[[def_revision]]revision::
A particular state of files and directories which was stored in the
<<def_object_database,object database>>. It is referenced by a
<<def_commit_object,commit object>>.
rewind::
To throw away part of the development, i.e. to assign the head to
an earlier revision.
[[def_rewind]]rewind::
To throw away part of the development, i.e. to assign the
<<def_head,head>> to an earlier <<def_revision,revision>>.
SCM::
[[def_SCM]]SCM::
Source code management (tool).
SHA1::
Synonym for object name.
[[def_SHA1]]SHA1::
Synonym for <<def_object_name,object name>>.
shallow repository::
A shallow repository has an incomplete history some of
whose commits have parents cauterized away (in other
words, git is told to pretend that these commits do not
have the parents, even though they are recorded in the
commit object). This is sometimes useful when you are
interested only in the recent history of a project even
though the real history recorded in the upstream is
much larger. A shallow repository is created by giving
`--depth` option to gitlink:git-clone[1], and its
history can be later deepened with gitlink:git-fetch[1].
[[def_shallow_repository]]shallow repository::
A <<def_shallow_repository,shallow repository>> has an incomplete
history some of whose commits have parents cauterized away (in other
words, git is told to pretend that these commits do not have the
parents, even though they are recorded in the <<def_commit_object,commit
object>>). This is sometimes useful when you are interested only in the
recent history of a project even though the real history recorded in the
upstream is much larger. A <<def_shallow_repository,shallow repository>>
is created by giving the `--depth` option to gitlink:git-clone[1], and
its history can be later deepened with gitlink:git-fetch[1].
symref::
Symbolic reference: instead of containing the SHA1 id itself, it
is of the format 'ref: refs/some/thing' and when referenced, it
recursively dereferences to this reference. 'HEAD' is a prime
example of a symref. Symbolic references are manipulated with
the gitlink:git-symbolic-ref[1] command.
[[def_symref]]symref::
Symbolic reference: instead of containing the <<def_SHA1,SHA1>> id
itself, it is of the format 'ref: refs/some/thing' and when
referenced, it recursively dereferences to this reference. 'HEAD' is a
prime example of a <<def_symref,symref>>. Symbolic references are
manipulated with the gitlink:git-symbolic-ref[1] command.
topic branch::
A regular git branch that is used by a developer to
identify a conceptual line of development. Since branches
are very easy and inexpensive, it is often desirable to
have several small branches that each contain very well
defined concepts or small incremental yet related changes.
[[def_tag]]tag::
A <<def_ref,ref>> pointing to a <<def_tag,tag>> or
<<def_commit_object,commit object>>. In contrast to a <<def_head,head>>,
a tag is not changed by a <<def_commit,commit>>. Tags (not
<<def_tag_object,tag objects>>) are stored in `$GIT_DIR/refs/tags/`. A
git tag has nothing to do with a Lisp tag (which would be
called an <<def_object_type,object type>> in git's context). A
tag is most typically used to mark a particular point in the
<<def_commit,commit>> ancestry <<def_chain,chain>>.
tracking branch::
A regular git branch that is used to follow changes from
another repository. A tracking branch should not contain
direct modifications or have local commits made to it.
A tracking branch can usually be identified as the
right-hand-side ref in a Pull: refspec.
[[def_tag_object]]tag object::
An <<def_object,object>> containing a <<def_ref,ref>> pointing to
another <<def_object,object>>, which can contain a message just like a
<<def_commit_object,commit object>>. It can also contain a (PGP)
signature, in which case it is called a "signed <<def_tag_object,tag
object>>".
tree object::
An object containing a list of file names and modes along with refs
to the associated blob and/or tree objects. A tree is equivalent
to a directory.
[[def_topic_branch]]topic branch::
A regular git <<def_branch,branch>> that is used by a developer to
identify a conceptual line of development. Since branches are very easy
and inexpensive, it is often desirable to have several small branches
that each contain very well defined concepts or small incremental yet
related changes.
tree::
Either a working tree, or a tree object together with the
dependent blob and tree objects (i.e. a stored representation
of a working tree).
[[def_tracking_branch]]tracking branch::
A regular git <<def_branch,branch>> that is used to follow changes from
another <<def_repository,repository>>. A <<def_tracking_branch,tracking
branch>> should not contain direct modifications or have local commits
made to it. A <<def_tracking_branch,tracking branch>> can usually be
identified as the right-hand-side <<def_ref,ref>> in a Pull:
<<def_refspec,refspec>>.
tree-ish::
A ref pointing to either a commit object, a tree object, or a
tag object pointing to a tag or commit or tree object.
[[def_tree]]tree::
Either a <<def_working_tree,working tree>>, or a <<def_tree_object,tree
object>> together with the dependent blob and <<def_tree,tree>> objects
(i.e. a stored representation of a <<def_working_tree,working tree>>).
tag object::
An object containing a ref pointing to another object, which can
contain a message just like a commit object. It can also
contain a (PGP) signature, in which case it is called a "signed
tag object".
[[def_tree_object]]tree object::
An <<def_object,object>> containing a list of file names and modes along
with refs to the associated blob and/or tree objects. A
<<def_tree,tree>> is equivalent to a <<def_directory,directory>>.
tag::
A ref pointing to a tag or commit object. In contrast to a head,
a tag is not changed by a commit. Tags (not tag objects) are
stored in `$GIT_DIR/refs/tags/`. A git tag has nothing to do with
a Lisp tag (which is called object type in git's context).
A tag is most typically used to mark a particular point in the
commit ancestry chain.
[[def_tree-ish]]tree-ish::
A <<def_ref,ref>> pointing to either a <<def_commit_object,commit
object>>, a <<def_tree_object,tree object>>, or a <<def_tag_object,tag
object>> pointing to a <<def_tag,tag>> or <<def_commit,commit>> or
<<def_tree_object,tree object>>.
unmerged index::
An index which contains unmerged index entries.
[[def_unmerged_index]]unmerged index::
An <<def_index,index>> which contains unmerged
<<def_index_entry,index entries>>.
unreachable object::
An object which is not reachable from a branch, tag, or any
other reference.
working tree::
The set of files and directories currently being worked on,
i.e. you can work in your working tree without using git at all.
[[def_unreachable_object]]unreachable object::
An <<def_object,object>> which is not <<def_reachable,reachable>> from a
<<def_branch,branch>>, <<def_tag,tag>>, or any other reference.
[[def_working_tree]]working tree::
The set of files and directories currently being worked on, i.e. you can
work in your <<def_working_tree,working tree>> without using git at all.

View File

@@ -0,0 +1,52 @@
How to use git-daemon
Git can be run in inetd mode and in stand alone mode. But all you want is
let a coworker pull from you, and therefore need to set up a git server
real quick, right?
Note that git-daemon is not really chatty at the moment, especially when
things do not go according to plan (e.g. a socket could not be bound).
Another word of warning: if you run
$ git ls-remote git://127.0.0.1/rule-the-world.git
and you see a message like
fatal: The remote end hung up unexpectedly
it only means that _something_ went wrong. To find out _what_ went wrong,
you have to ask the server. (Git refuses to be more precise for your
security only. Take off your shoes now. You have any coins in your pockets?
Sorry, not allowed -- who knows what you planned to do with them?)
With these two caveats, let's see an example:
$ git daemon --reuseaddr --verbose --base-path=/home/gitte/git \
--export-all -- /home/gitte/git/rule-the-world.git
(Of course, unless your user name is `gitte` _and_ your repository is in
~/rule-the-world.git, you have to adjust the paths. If your repository is
not bare, be aware that you have to type the path to the .git directory!)
This invocation tries to reuse the address if it is already taken
(this can save you some debugging, because otherwise killing and restarting
git-daemon could just silently fail to bind to a socket).
Also, it is (relatively) verbose when somebody actually connects to it.
It also sets the base path, which means that all the projects which can be
accessed using this daemon have to reside in or under that path.
The option `--export-all` just means that you _don't_ have to create a
file named `git-daemon-export-ok` in each exported repository. (Otherwise,
git-daemon would complain loudly, and refuse to cooperate.)
Last of all, the repository which should be exported is specified. It is
a good practice to put the paths after a "--" separator.
Now, test your daemon with
$ git ls-remote git://127.0.0.1/rule-the-world.git
If this does not work, find out why, and submit a patch to this document.

View File

@@ -1,69 +0,0 @@
#!/usr/bin/perl
%terms=();
while(<>) {
if(/^(\S.*)::$/) {
my $term=$1;
if(defined($terms{$term})) {
die "$1 defined twice\n";
}
$terms{$term}="";
LOOP: while(<>) {
if(/^$/) {
last LOOP;
}
if(/^ \S/) {
$terms{$term}.=$_;
} else {
die "Error 1: $_";
}
}
}
}
sub format_tab_80 ($) {
my $text=$_[0];
my $result="";
$text=~s/\s+/ /g;
$text=~s/^\s+//;
while($text=~/^(.{1,72})(|\s+(\S.*)?)$/) {
$result.=" ".$1."\n";
$text=$3;
}
return $result;
}
sub no_spaces ($) {
my $result=$_[0];
$result=~tr/ /_/;
return $result;
}
print 'GIT Glossary
============
This list is sorted alphabetically:
';
@keys=sort {uc($a) cmp uc($b)} keys %terms;
$pattern='(\b(?<!link:git-)'.join('\b|\b(?<!-)',reverse @keys).'\b)';
foreach $key (@keys) {
$terms{$key}=~s/$pattern/sprintf "<<ref_".no_spaces($1).",$1>>";/eg;
print '[[ref_'.no_spaces($key).']]'.$key."::\n"
.format_tab_80($terms{$key})."\n";
}
print '
Author
------
Written by Johannes Schindelin <Johannes.Schindelin@gmx.de> and
the git-list <git@vger.kernel.org>.
GIT
---
Part of the link:git.html[git] suite
';

View File

@@ -21,11 +21,11 @@ GIT pack format
which looks like this:
(undeltified representation)
n-byte type and length (4-bit type, (n-1)*7+4-bit length)
n-byte type and length (3-bit type, (n-1)*7+4-bit length)
compressed data
(deltified representation)
n-byte type and length (4-bit type, (n-1)*7+4-bit length)
n-byte type and length (3-bit type, (n-1)*7+4-bit length)
20-byte base object name
compressed delta data
@@ -102,11 +102,13 @@ trailer | | packfile checksum |
Pack file entry: <+
packed object header:
1-byte type (upper 4-bit)
1-byte size extension bit (MSB)
type (next 3 bit)
size0 (lower 4-bit)
n-byte sizeN (as long as MSB is set, each 7-bit)
size0..sizeN form 4+7+7+..+7 bit integer, size0
is the most significant part.
is the least significant part, and sizeN is the
most significant part.
packed object data:
If it is not DELTA, then deflated bytes (the size above
is the size before compression).

View File

@@ -0,0 +1,49 @@
Def.: Shallow commits do have parents, but not in the shallow
repo, and therefore grafts are introduced pretending that
these commits have no parents.
The basic idea is to write the SHA1s of shallow commits into
$GIT_DIR/shallow, and handle its contents like the contents
of $GIT_DIR/info/grafts (with the difference that shallow
cannot contain parent information).
This information is stored in a new file instead of grafts, or
even the config, since the user should not touch that file
at all (even throughout development of the shallow clone, it
was never manually edited!).
Each line contains exactly one SHA1. When read, a commit_graft
will be constructed, which has nr_parent < 0 to make it easier
to discern from user provided grafts.
Since fsck-objects relies on the library to read the objects,
it honours shallow commits automatically.
There are some unfinished ends of the whole shallow business:
- maybe we have to force non-thin packs when fetching into a
shallow repo (ATM they are forced non-thin).
- A special handling of a shallow upstream is needed. At some
stage, upload-pack has to check if it sends a shallow commit,
and it should send that information early (or fail, if the
client does not support shallow repositories). There is no
support at all for this in this patch series.
- Instead of locking $GIT_DIR/shallow at the start, just
the timestamp of it is noted, and when it comes to writing it,
a check is performed if the mtime is still the same, dying if
it is not.
- It is unclear how "push into/from a shallow repo" should behave.
- If you deepen a history, you'd want to get the tags of the
newly stored (but older!) commits. This does not work right now.
To make a shallow clone, you can call "git-clone --depth 20 repo".
The result contains only commit chains with a length of at most 20.
It also writes an appropriate $GIT_DIR/shallow.
You can deepen a shallow repository with "git-fetch --depth 20
repo branch", which will fetch branch from repo, but stop at depth
20, updating $GIT_DIR/shallow.

View File

@@ -84,7 +84,7 @@ $ git branch -r # list
origin/master
origin/next
...
$ git branch checkout -b masterwork origin/master
$ git checkout -b masterwork origin/master
-----------------------------------------------
Fetch a branch from a different repository, and give it a new
@@ -155,8 +155,8 @@ Make sure git knows who to blame:
------------------------------------------------
$ cat >~/.gitconfig <<\EOF
[user]
name = Your Name Comes Here
email = you@yourdomain.example.com
name = Your Name Comes Here
email = you@yourdomain.example.com
EOF
------------------------------------------------
@@ -195,7 +195,7 @@ Importing or exporting patches:
-----------------------------------------------
$ git format-patch origin..HEAD # format a patch for each commit
# in HEAD but not in origin
$ git-am mbox # import patches from the mailbox "mbox"
$ git am mbox # import patches from the mailbox "mbox"
-----------------------------------------------
Fetch a branch in a different git repository, then merge into the
@@ -288,21 +288,22 @@ collection of files. It stores the history as a compressed
collection of interrelated snapshots (versions) of the project's
contents.
A single git repository may contain multiple branches. Each branch
is a bookmark referencing a particular point in the project history.
The gitlink:git-branch[1] command shows you the list of branches:
A single git repository may contain multiple branches. It keeps track
of them by keeping a list of <<def_head,heads>> which reference the
latest version on each branch; the gitlink:git-branch[1] command shows
you the list of branch heads:
------------------------------------------------
$ git branch
* master
------------------------------------------------
A freshly cloned repository contains a single branch, named "master",
and the working directory contains the version of the project
referred to by the master branch.
A freshly cloned repository contains a single branch head, named
"master", and working directory is initialized to the state of
the project referred to by "master".
Most projects also use tags. Tags, like branches, are references
into the project's history, and can be listed using the
Most projects also use <<def_tag,tags>>. Tags, like heads, are
references into the project's history, and can be listed using the
gitlink:git-tag[1] command:
------------------------------------------------
@@ -320,9 +321,9 @@ v2.6.13
------------------------------------------------
Tags are expected to always point at the same version of a project,
while branches are expected to advance as development progresses.
while heads are expected to advance as development progresses.
Create a new branch pointing to one of these versions and check it
Create a new branch head pointing to one of these versions and check it
out using gitlink:git-checkout[1]:
------------------------------------------------
@@ -346,10 +347,10 @@ the current branch to point at v2.6.17 instead, with
$ git reset --hard v2.6.17
------------------------------------------------
Note that if the current branch was your only reference to a
Note that if the current branch head was your only reference to a
particular point in history, then resetting that branch may leave you
with no way to find the history it used to point to; so use this
command carefully.
with no way to find the history it used to point to; so use this command
carefully.
Understanding History: Commits
------------------------------
@@ -452,17 +453,15 @@ be replaced with another letter or number.
Understanding history: What is a branch?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Though we've been using the word "branch" to mean a kind of reference
to a particular commit, the word branch is also commonly used to
refer to the line of commits leading up to that point. In the
example above, git may think of the branch named "A" as just a
pointer to one particular commit, but we may refer informally to the
line of three commits leading up to that point as all being part of
When we need to be precise, we will use the word "branch" to mean a line
of development, and "branch head" (or just "head") to mean a reference
to the most recent commit on a branch. In the example above, the branch
head named "A" is a pointer to one particular commit, but we refer to
the line of three commits leading up to that point as all being part of
"branch A".
If we need to make it clear that we're just talking about the most
recent commit on the branch, we may refer to that commit as the
"head" of the branch.
However, when no confusion will result, we often just use the term
"branch" both for branches and for branch heads.
Manipulating branches
---------------------
@@ -580,7 +579,7 @@ cloned from, using gitlink:git-remote[1]:
-------------------------------------------------
$ git remote add linux-nfs git://linux-nfs.org/pub/nfs-2.6.git
$ git fetch
$ git fetch linux-nfs
* refs/remotes/linux-nfs/master: storing branch 'master' ...
commit: bf81b46
-------------------------------------------------
@@ -681,7 +680,7 @@ occasionally you may land on a commit that broke something unrelated;
run
-------------------------------------------------
$ git bisect-visualize
$ git bisect visualize
-------------------------------------------------
which will run gitk and label the commit it chose with a marker that
@@ -766,7 +765,7 @@ We can also create a tag to refer to a particular commit; after
running
-------------------------------------------------
$ git-tag stable-1 1b2e1d63ff
$ git tag stable-1 1b2e1d63ff
-------------------------------------------------
You can use stable-1 to refer to the commit 1b2e1d63ff.
@@ -910,7 +909,7 @@ name based on any tag it finds pointing to one of the commit's
descendants:
-------------------------------------------------
$ git name-rev e05db0fd
$ git name-rev --tags e05db0fd
e05db0fd tags/v1.5.0-rc1^0~23
-------------------------------------------------
@@ -919,7 +918,7 @@ revision using a tag on which the given commit is based:
-------------------------------------------------
$ git describe e05db0fd
v1.5.0-rc0-ge05db0f
v1.5.0-rc0-260-ge05db0f
-------------------------------------------------
but that may sometimes help you guess which tags might come after the
@@ -1698,7 +1697,7 @@ If you and maintainer both have accounts on the same machine, then
then you can just pull changes from each other's repositories
directly; note that all of the commands (gitlink:git-clone[1],
git-fetch[1], git-pull[1], etc.) that accept a URL as an argument
will also accept a local file patch; so, for example, you can
will also accept a local directory name; so, for example, you can
use
-------------------------------------------------
@@ -1862,7 +1861,7 @@ Allow web browsing of a repository
The gitweb cgi script provides users an easy way to browse your
project's files and history without having to install git; see the file
gitweb/README in the git source tree for instructions on setting it up.
gitweb/INSTALL in the git source tree for instructions on setting it up.
Examples
--------
@@ -3013,9 +3012,6 @@ confusing and scary messages, but it won't actually do anything bad. In
contrast, running "git prune" while somebody is actively changing the
repository is a *BAD* idea).
Glossary of git terms
=====================
include::glossary.txt[]
Notes and todo list for this manual

View File

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

View File

@@ -984,7 +984,7 @@ dist-doc:
clean:
rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o xdiff/*.o \
$(LIB_FILE) $(XDIFF_LIB)
test-chmtime$X $(LIB_FILE) $(XDIFF_LIB)
rm -f $(ALL_PROGRAMS) $(BUILT_INS) git$X
rm -f *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h TAGS tags
rm -rf autom4te.cache
@@ -1012,7 +1012,7 @@ check-docs::
git-merge-octopus | git-merge-ours | git-merge-recursive | \
git-merge-resolve | git-merge-stupid | \
git-add--interactive | git-fsck-objects | git-init-db | \
git-repo-config | \
git-repo-config | git-fetch--tool | \
git-ssh-pull | git-ssh-push ) continue ;; \
esac ; \
test -f "Documentation/$$v.txt" || \

6
README
View File

@@ -38,3 +38,9 @@ requests, comments and patches to git@vger.kernel.org. To subscribe
to the list, send an email with just "subscribe git" in the body to
majordomo@vger.kernel.org. The mailing list archives are available at
http://marc.theaimsgroup.com/?l=git and other archival sites.
The messages titled "A note from the maintainer", "What's in
git.git (stable)" and "What's cooking in git.git (topics)" and
the discussion following them on the mailing list give a good
reference for project status, development direction and
remaining tasks.

View File

@@ -2355,7 +2355,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
static int try_create_file(const char *path, unsigned int mode, const char *buf, unsigned long size)
{
int fd;
int fd, converted;
char *nbuf;
unsigned long nsize;
@@ -2364,17 +2364,18 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
* terminated.
*/
return symlink(buf, path);
nsize = size;
nbuf = (char *) buf;
if (convert_to_working_tree(path, &nbuf, &nsize)) {
free((char *) buf);
buf = nbuf;
size = nsize;
}
fd = open(path, O_CREAT | O_EXCL | O_WRONLY, (mode & 0100) ? 0777 : 0666);
if (fd < 0)
return -1;
nsize = size;
nbuf = (char *) buf;
converted = convert_to_working_tree(path, &nbuf, &nsize);
if (converted) {
buf = nbuf;
size = nsize;
}
while (size) {
int written = xwrite(fd, buf, size);
if (written < 0)
@@ -2386,6 +2387,8 @@ static int try_create_file(const char *path, unsigned int mode, const char *buf,
}
if (close(fd) < 0)
die("closing file %s: %s", path, strerror(errno));
if (converted)
free(nbuf);
return 0;
}

View File

@@ -180,16 +180,15 @@ struct scoreboard {
int *lineno;
};
static int cmp_suspect(struct origin *a, struct origin *b)
static inline int same_suspect(struct origin *a, struct origin *b)
{
int cmp = hashcmp(a->commit->object.sha1, b->commit->object.sha1);
if (cmp)
return cmp;
return strcmp(a->path, b->path);
if (a == b)
return 1;
if (a->commit != b->commit)
return 0;
return !strcmp(a->path, b->path);
}
#define cmp_suspect(a, b) ( ((a)==(b)) ? 0 : cmp_suspect(a,b) )
static void sanity_check_refcnt(struct scoreboard *);
/*
@@ -202,7 +201,7 @@ static void coalesce(struct scoreboard *sb)
struct blame_entry *ent, *next;
for (ent = sb->ent; ent && (next = ent->next); ent = next) {
if (!cmp_suspect(ent->suspect, next->suspect) &&
if (same_suspect(ent->suspect, next->suspect) &&
ent->guilty == next->guilty &&
ent->s_lno + ent->num_lines == next->s_lno) {
ent->num_lines += next->num_lines;
@@ -775,7 +774,7 @@ static int find_last_in_target(struct scoreboard *sb, struct origin *target)
int last_in_target = -1;
for (e = sb->ent; e; e = e->next) {
if (e->guilty || cmp_suspect(e->suspect, target))
if (e->guilty || !same_suspect(e->suspect, target))
continue;
if (last_in_target < e->s_lno + e->num_lines)
last_in_target = e->s_lno + e->num_lines;
@@ -795,7 +794,7 @@ static void blame_chunk(struct scoreboard *sb,
struct blame_entry *e;
for (e = sb->ent; e; e = e->next) {
if (e->guilty || cmp_suspect(e->suspect, target))
if (e->guilty || !same_suspect(e->suspect, target))
continue;
if (same <= e->s_lno)
continue;
@@ -970,7 +969,7 @@ static int find_move_in_parent(struct scoreboard *sb,
while (made_progress) {
made_progress = 0;
for (e = sb->ent; e; e = e->next) {
if (e->guilty || cmp_suspect(e->suspect, target))
if (e->guilty || !same_suspect(e->suspect, target))
continue;
find_copy_in_blob(sb, e, parent, split, &file_p);
if (split[1].suspect &&
@@ -1002,12 +1001,12 @@ static struct blame_list *setup_blame_list(struct scoreboard *sb,
struct blame_list *blame_list = NULL;
for (e = sb->ent, num_ents = 0; e; e = e->next)
if (!e->guilty && !cmp_suspect(e->suspect, target))
if (!e->guilty && same_suspect(e->suspect, target))
num_ents++;
if (num_ents) {
blame_list = xcalloc(num_ents, sizeof(struct blame_list));
for (e = sb->ent, i = 0; e; e = e->next)
if (!e->guilty && !cmp_suspect(e->suspect, target))
if (!e->guilty && same_suspect(e->suspect, target))
blame_list[i++].ent = e;
}
*num_ents_p = num_ents;
@@ -1137,7 +1136,7 @@ static void pass_whole_blame(struct scoreboard *sb,
origin->file.ptr = NULL;
}
for (e = sb->ent; e; e = e->next) {
if (cmp_suspect(e->suspect, origin))
if (!same_suspect(e->suspect, origin))
continue;
origin_incref(porigin);
origin_decref(e->suspect);
@@ -1443,7 +1442,7 @@ static void assign_blame(struct scoreboard *sb, struct rev_info *revs, int opt)
/* Take responsibility for the remaining entries */
for (ent = sb->ent; ent; ent = ent->next)
if (!cmp_suspect(ent->suspect, suspect))
if (same_suspect(ent->suspect, suspect))
found_guilty_entry(ent);
origin_decref(suspect);

View File

@@ -87,7 +87,7 @@ static int read_header(const char *path, struct bundle_header *header) {
if (buffer[len - 1] == '\n')
buffer[len - 1] = '\0';
if (get_sha1_hex(buffer + offset, sha1)) {
warn("unrecognized header: %s", buffer);
warning("unrecognized header: %s", buffer);
continue;
}
delim = buffer[40 + offset];
@@ -268,7 +268,7 @@ static int create_bundle(struct bundle_header *header, const char *path,
* from getting output.
*/
if (!(e->item->flags & SHOWN)) {
warn("ref '%s' is excluded by the rev-list options",
warning("ref '%s' is excluded by the rev-list options",
e->name);
continue;
}

View File

@@ -227,8 +227,7 @@ static int fsck_tree(struct tree *item)
const char *o_name;
const unsigned char *o_sha1;
desc.buf = item->buffer;
desc.size = item->size;
init_tree_desc(&desc, item->buffer, item->size);
o_mode = 0;
o_name = NULL;
@@ -242,7 +241,7 @@ static int fsck_tree(struct tree *item)
if (strchr(name, '/'))
has_full_path = 1;
has_zero_pad |= *(char *)desc.buf == '0';
has_zero_pad |= *(char *)desc.buffer == '0';
update_tree_entry(&desc);
switch (mode) {

View File

@@ -378,7 +378,7 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
* decide if we want to descend into "abc"
* directory.
*/
strcpy(path_buf + len + entry.pathlen, "/");
strcpy(path_buf + len + tree_entry_len(entry.path, entry.sha1), "/");
if (!pathspec_matches(paths, down))
;
@@ -388,11 +388,13 @@ static int grep_tree(struct grep_opt *opt, const char **paths,
enum object_type type;
struct tree_desc sub;
void *data;
data = read_sha1_file(entry.sha1, &type, &sub.size);
unsigned long size;
data = read_sha1_file(entry.sha1, &type, &size);
if (!data)
die("unable to read tree (%s)",
sha1_to_hex(entry.sha1));
sub.buf = data;
init_tree_desc(&sub, data, size);
hit |= grep_tree(opt, paths, &sub, tree_name, down);
free(data);
}
@@ -408,12 +410,13 @@ static int grep_object(struct grep_opt *opt, const char **paths,
if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
struct tree_desc tree;
void *data;
unsigned long size;
int hit;
data = read_object_with_reference(obj->sha1, tree_type,
&tree.size, NULL);
&size, NULL);
if (!data)
die("unable to read tree (%s)", sha1_to_hex(obj->sha1));
tree.buf = data;
init_tree_desc(&tree, data, size);
hit = grep_tree(opt, paths, &tree, name, "");
free(data);
return hit;

View File

@@ -35,7 +35,7 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
if (!prefixcmp(arg, "--encoding=")) {
arg += 11;
if (strcmp(arg, "none"))
git_log_output_encoding = strdup(arg);
git_log_output_encoding = xstrdup(arg);
else
git_log_output_encoding = "";
}

View File

@@ -294,14 +294,14 @@ static char *header[MAX_HDR_PARSED] = {
"From","Subject","Date",
};
static int check_header(char *line, char **hdr_data)
static int check_header(char *line, char **hdr_data, int overwrite)
{
int i;
/* search for the interesting parts */
for (i = 0; header[i]; i++) {
int len = strlen(header[i]);
if (!hdr_data[i] &&
if ((!hdr_data[i] || overwrite) &&
!strncasecmp(line, header[i], len) &&
line[len] == ':' && isspace(line[len + 1])) {
/* Unwrap inline B and Q encoding, and optionally
@@ -614,6 +614,7 @@ static int find_boundary(void)
static int handle_boundary(void)
{
char newline[]="\n";
again:
if (!memcmp(line+content_top->boundary_len, "--", 2)) {
/* we hit an end boundary */
@@ -628,7 +629,7 @@ again:
"can't recover\n");
exit(1);
}
handle_filter("\n");
handle_filter(newline);
/* skip to the next boundary */
if (!find_boundary())
@@ -643,7 +644,7 @@ again:
/* slurp in this section's info */
while (read_one_header_line(line, sizeof(line), fin))
check_header(line, p_hdr_data);
check_header(line, p_hdr_data, 0);
/* eat the blank line after section info */
return (fgets(line, sizeof(line), fin) != NULL);
@@ -699,10 +700,14 @@ static int handle_commit_msg(char *line)
if (!*cp)
return 0;
}
if ((still_looking = check_header(cp, s_hdr_data)) != 0)
if ((still_looking = check_header(cp, s_hdr_data, 0)) != 0)
return 0;
}
/* normalize the log message to UTF-8. */
if (metainfo_charset)
convert_to_utf8(line, charset);
if (patchbreak(line)) {
fclose(cmitmsg);
cmitmsg = NULL;
@@ -767,12 +772,8 @@ static void handle_body(void)
return;
}
/* Unwrap transfer encoding and optionally
* normalize the log message to UTF-8.
*/
/* Unwrap transfer encoding */
decode_transfer_encoding(line);
if (metainfo_charset)
convert_to_utf8(line, charset);
switch (transfer_encoding) {
case TE_BASE64:
@@ -875,7 +876,7 @@ int mailinfo(FILE *in, FILE *out, int ks, const char *encoding,
/* process the email header */
while (read_one_header_line(line, sizeof(line), fin))
check_header(line, p_hdr_data);
check_header(line, p_hdr_data, 1);
handle_body();
handle_info();

View File

@@ -854,7 +854,7 @@ static void add_pbase_object(struct tree_desc *tree,
unsigned long size;
enum object_type type;
if (entry.pathlen != cmplen ||
if (tree_entry_len(entry.path, entry.sha1) != cmplen ||
memcmp(entry.path, name, cmplen) ||
!has_sha1_file(entry.sha1) ||
(type = sha1_object_info(entry.sha1, &size)) < 0)
@@ -873,8 +873,7 @@ static void add_pbase_object(struct tree_desc *tree,
tree = pbase_tree_get(entry.sha1);
if (!tree)
return;
sub.buf = tree->tree_data;
sub.size = tree->tree_size;
init_tree_desc(&sub, tree->tree_data, tree->tree_size);
add_pbase_object(&sub, down, downlen, fullname);
pbase_tree_put(tree);
@@ -937,8 +936,7 @@ static void add_preferred_base_object(const char *name, unsigned hash)
}
else {
struct tree_desc tree;
tree.buf = it->pcache.tree_data;
tree.size = it->pcache.tree_size;
init_tree_desc(&tree, it->pcache.tree_data, it->pcache.tree_size);
add_pbase_object(&tree, name, cmplen, name);
}
}

View File

@@ -14,10 +14,8 @@ static int prune_object(char *path, const char *filename, const unsigned char *s
enum object_type type = sha1_object_info(sha1, NULL);
printf("%s %s\n", sha1_to_hex(sha1),
(type > 0) ? typename(type) : "unknown");
return 0;
}
unlink(mkpath("%s/%s", path, filename));
rmdir(path);
} else
unlink(mkpath("%s/%s", path, filename));
return 0;
}
@@ -60,6 +58,8 @@ static int prune_dir(int i, char *path)
}
fprintf(stderr, "bad sha1 file: %s/%s\n", path, de->d_name);
}
if (!show_only)
rmdir(path);
closedir(dir);
return 0;
}

View File

@@ -55,8 +55,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
int cnt;
hashcpy(it->sha1, tree->object.sha1);
desc.buf = tree->buffer;
desc.size = tree->size;
init_tree_desc(&desc, tree->buffer, tree->size);
cnt = 0;
while (tree_entry(&desc, &entry)) {
if (!S_ISDIR(entry.mode))
@@ -185,7 +184,7 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
if (opts.dir)
die("more than one --exclude-per-directory are given.");
dir = calloc(1, sizeof(*opts.dir));
dir = xcalloc(1, sizeof(*opts.dir));
dir->show_ignored = 1;
dir->exclude_per_dir = arg + 24;
opts.dir = dir;

View File

@@ -52,18 +52,18 @@ static int tree_is_complete(const unsigned char *sha1)
if (tree->object.flags & INCOMPLETE)
return 0;
desc.buf = tree->buffer;
desc.size = tree->size;
if (!desc.buf) {
if (!tree->buffer) {
enum object_type type;
void *data = read_sha1_file(sha1, &type, &desc.size);
unsigned long size;
void *data = read_sha1_file(sha1, &type, &size);
if (!data) {
tree->object.flags |= INCOMPLETE;
return 0;
}
desc.buf = data;
tree->buffer = data;
tree->size = size;
}
init_tree_desc(&desc, tree->buffer, tree->size);
complete = 1;
while (tree_entry(&desc, &entry)) {
if (!has_sha1_file(entry.sha1) ||

View File

@@ -78,6 +78,13 @@ static void append_line(struct buffer *buffer, const char *line)
buffer->nr += len;
}
static void clear_buffer(struct buffer *buffer)
{
free(buffer->ptr);
buffer->ptr = NULL;
buffer->nr = buffer->alloc = 0;
}
static int handle_file(const char *path,
unsigned char *sha1, const char *output)
{
@@ -131,6 +138,8 @@ static int handle_file(const char *path,
SHA1_Update(&ctx, two->ptr, two->nr);
SHA1_Update(&ctx, "\0", 1);
}
clear_buffer(one);
clear_buffer(two);
} else if (hunk == 1)
append_line(one, buf);
else if (hunk == 2)

View File

@@ -180,7 +180,7 @@ static struct commit_list *find_bisection(struct commit_list *list)
nr++;
p = p->next;
}
closest = 0;
closest = -1;
best = list;
for (p = list; p; p = p->next) {

View File

@@ -294,13 +294,13 @@ static int revert_or_cherry_pick(int argc, const char **argv)
oneline = get_oneline(message);
if (action == REVERT) {
char *oneline_body = strchr(oneline, ' ');
base = commit;
next = commit->parents->item;
add_to_msg("Revert ");
add_to_msg(find_unique_abbrev(commit->object.sha1,
DEFAULT_ABBREV));
add_to_msg(oneline);
add_to_msg("\nThis reverts commit ");
add_to_msg("Revert \"");
add_to_msg(oneline_body + 1);
add_to_msg("\"\n\nThis reverts commit ");
add_to_msg(sha1_to_hex(commit->object.sha1));
add_to_msg(".\n");
} else {

View File

@@ -89,20 +89,10 @@ static int check_local_mod(unsigned char *head)
if (ce_match_stat(ce, &st, 0))
errs = error("'%s' has local modifications "
"(hint: try -f)", ce->name);
if (no_head)
continue;
/*
* It is Ok to remove a newly added path, as long as
* it is cache-clean.
*/
if (get_tree_entry(head, name, sha1, &mode))
continue;
/*
* Otherwise make sure the version from the HEAD
* matches the index.
*/
if (ce->ce_mode != create_ce_mode(mode) ||
hashcmp(ce->sha1, sha1))
if (no_head
|| get_tree_entry(head, name, sha1, &mode)
|| ce->ce_mode != create_ce_mode(mode)
|| hashcmp(ce->sha1, sha1))
errs = error("'%s' has changes staged in the index "
"(hint: try -f)", name);
}

View File

@@ -284,7 +284,7 @@ char *enter_repo(char *path, int strict);
/* Read and unpack a sha1 file into memory, write memory to a sha1 file */
extern int sha1_object_info(const unsigned char *, unsigned long *);
extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size);
extern int hash_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);

View File

@@ -654,6 +654,7 @@ static char *get_header(const struct commit *commit, const char *key)
static char *replace_encoding_header(char *buf, const char *encoding)
{
char *encoding_header = strstr(buf, "\nencoding ");
char *header_end = strstr(buf, "\n\n");
char *end_of_encoding_header;
int encoding_header_pos;
int encoding_header_len;
@@ -661,8 +662,10 @@ static char *replace_encoding_header(char *buf, const char *encoding)
int need_len;
int buflen = strlen(buf) + 1;
if (!encoding_header)
return buf; /* should not happen but be defensive */
if (!header_end)
header_end = buf + buflen;
if (!encoding_header || encoding_header >= header_end)
return buf;
encoding_header++;
end_of_encoding_header = strchr(encoding_header, '\n');
if (!end_of_encoding_header)
@@ -706,7 +709,7 @@ static char *logmsg_reencode(const struct commit *commit,
encoding = get_header(commit, "encoding");
use_encoding = encoding ? encoding : utf8;
if (!strcmp(use_encoding, output_encoding))
out = strdup(commit->buffer);
out = xstrdup(commit->buffer);
else
out = reencode_string(commit->buffer,
output_encoding, use_encoding);
@@ -760,7 +763,7 @@ static void fill_person(struct interp *table, const char *msg, int len)
if (msg + start == ep)
return;
table[5].value = xstrndup(msg + start, ep - msg + start);
table[5].value = xstrndup(msg + start, ep - (msg + start));
/* parse tz */
for (start = ep - msg + 1; start < len && isspace(msg[start]); start++)
@@ -849,19 +852,23 @@ static long format_commit_message(const struct commit *commit,
interp_set_entry(table, ITREE_ABBREV,
find_unique_abbrev(commit->tree->object.sha1,
DEFAULT_ABBREV));
parents[1] = 0;
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
sha1_to_hex(p->item->object.sha1));
interp_set_entry(table, IPARENTS, parents);
interp_set_entry(table, IPARENTS, parents + 1);
parents[1] = 0;
for (i = 0, p = commit->parents;
p && i < sizeof(parents) - 1;
p = p->next)
i += snprintf(parents + i, sizeof(parents) - i - 1, "%s ",
i += snprintf(parents + i, sizeof(parents) - i - 1, " %s",
find_unique_abbrev(p->item->object.sha1,
DEFAULT_ABBREV));
interp_set_entry(table, IPARENTS_ABBREV, parents);
interp_set_entry(table, IPARENTS_ABBREV, parents + 1);
for (i = 0, state = HEADER; msg[i] && state < BODY; i++) {
int eol;
@@ -884,7 +891,8 @@ static long format_commit_message(const struct commit *commit,
fill_person(table + ICOMMITTER_NAME,
msg + i + 10, eol - i - 10);
else if (!prefixcmp(msg + i, "encoding "))
table[IENCODING].value = xstrndup(msg + i, eol - i);
table[IENCODING].value =
xstrndup(msg + i + 9, eol - i - 9);
i = eol;
}
if (msg[i])

View File

@@ -418,6 +418,8 @@ static int git_tcp_connect_sock(char *host)
if (colon) {
*colon = 0;
port = colon + 1;
if (!*port)
port = "<none>";
}
memset(&hints, 0, sizeof(hints));
@@ -426,7 +428,7 @@ static int git_tcp_connect_sock(char *host)
gai = getaddrinfo(host, port, &hints, &ai);
if (gai)
die("Unable to look up %s (%s)", host, gai_strerror(gai));
die("Unable to look up %s (port %s) (%s)", host, port, gai_strerror(gai));
for (ai0 = ai; ai; ai = ai->ai_next) {
sockfd = socket(ai->ai_family,

503
contrib/continuous/cidaemon Normal file
View File

@@ -0,0 +1,503 @@
#!/usr/bin/perl
#
# A daemon that waits for update events sent by its companion
# post-receive-cinotify hook, checks out a new copy of source,
# compiles it, and emails the guilty parties if the compile
# (and optionally test suite) fails.
#
# To use this daemon, configure it and run it. It will disconnect
# from your terminal and fork into the background. The daemon must
# have local filesystem access to the source repositories, as it
# uses objects/info/alternates to avoid copying objects.
#
# Add its companion post-receive-cinotify hook as the post-receive
# hook to each repository that the daemon should monitor. Yes, a
# single daemon can monitor more than one repository.
#
# To use multiple daemons on the same system, give them each a
# unique queue file and tmpdir.
#
# Global Config
# -------------
# Reads from a Git style configuration file. This will be
# ~/.gitconfig by default but can be overridden by setting
# the GIT_CONFIG_FILE environment variable before starting.
#
# cidaemon.smtpHost
# Hostname of the SMTP server the daemon will send email
# through. Defaults to 'localhost'.
#
# cidaemon.smtpUser
# Username to authenticate to the SMTP server as. This
# variable is optional; if it is not supplied then no
# authentication will be performed.
#
# cidaemon.smtpPassword
# Password to authenticate to the SMTP server as. This
# variable is optional. If not supplied but smtpUser was,
# the daemon prompts for the password before forking into
# the background.
#
# cidaemon.smtpAuth
# Type of authentication to perform with the SMTP server.
# If set to 'login' and smtpUser was defined, this will
# use the AUTH LOGIN command, which is suitable for use
# with at least one version of Microsoft Exchange Server.
# If not set the daemon will use whatever auth methods
# are supported by your version of Net::SMTP.
#
# cidaemon.email
# Email address that daemon generated emails will be sent
# from. This should be a useful email address within your
# organization. Required.
#
# cidaemon.name
# Human friendly name that the daemon will send emails as.
# Defaults to 'cidaemon'.
#
# cidaemon.scanDelay
# Number of seconds to sleep between polls of the queue file.
# Defaults to 60.
#
# cidaemon.recentCache
# Number of recent commit SHA-1s per repository to cache and
# skip building if they appear again. This is useful to avoid
# rebuilding the same commit multiple times just because it was
# pushed into more than one branch. Defaults to 100.
#
# cidaemon.tmpdir
# Scratch directory to create the builds within. The daemon
# makes a new subdirectory for each build, then deletes it when
# the build has finished. The pid file is also placed here.
# Defaults to '/tmp'.
#
# cidaemon.queue
# Path to the queue file that the post-receive-cinotify hook
# appends events to. This file is polled by the daemon. It
# must not be on an NFS mount (uses flock). Required.
#
# cidaemon.nocc
# Perl regex patterns to match against author and committer
# lines. If a pattern matches, that author or committer will
# not be notified of a build failure.
#
# Per Repository Config
# ----------------------
# Read from the source repository's config file.
#
# builder.command
# Shell command to execute the build. This command must
# return 0 on "success" and non-zero on failure. If you
# also want to run a test suite, make sure your command
# does that too. Required.
#
# builder.queue
# Queue file to notify the cidaemon through. Should match
# cidaemon.queue. If not set the hook will not notify the
# cidaemon.
#
# builder.skip
# Perl regex patterns of refs that should not be sent to
# cidaemon. Updates of these refs will be ignored.
#
# builder.newBranchBase
# Glob patterns of refs that should be used to form the
# 'old' revions of a newly created ref. This should set
# to be globs that match your 'mainline' branches. This
# way a build failure of a brand new topic branch does not
# attempt to email everyone since the beginning of time;
# instead it only emails those authors of commits not in
# these 'mainline' branches.
local $ENV{PATH} = join ':', qw(
/opt/git/bin
/usr/bin
/bin
);
use strict;
use warnings;
use FindBin qw($RealBin);
use File::Spec;
use lib File::Spec->catfile($RealBin, '..', 'perl5');
use Storable qw(retrieve nstore);
use Fcntl ':flock';
use POSIX qw(strftime);
use Getopt::Long qw(:config no_auto_abbrev auto_help);
sub git_config ($;$)
{
my $var = shift;
my $required = shift || 0;
local *GIT;
open GIT, '-|','git','config','--get',$var;
my $r = <GIT>;
chop $r if $r;
close GIT;
die "error: $var not set.\n" if ($required && !$r);
return $r;
}
package EXCHANGE_NET_SMTP;
# Microsoft Exchange Server requires an 'AUTH LOGIN'
# style of authentication. This is different from
# the default supported by Net::SMTP so we subclass
# and override the auth method to support that.
use Net::SMTP;
use Net::Cmd;
use MIME::Base64 qw(encode_base64);
our @ISA = qw(Net::SMTP);
our $auth_type = ::git_config 'cidaemon.smtpAuth';
sub new
{
my $self = shift;
my $type = ref($self) || $self;
$type->SUPER::new(@_);
}
sub auth
{
my $self = shift;
return $self->SUPER::auth(@_) unless $auth_type eq 'login';
my $user = encode_base64 shift, '';
my $pass = encode_base64 shift, '';
return 0 unless CMD_MORE == $self->command("AUTH LOGIN")->response;
return 0 unless CMD_MORE == $self->command($user)->response;
CMD_OK == $self->command($pass)->response;
}
package main;
my ($debug_flag, %recent);
my $ex_host = git_config('cidaemon.smtpHost') || 'localhost';
my $ex_user = git_config('cidaemon.smtpUser');
my $ex_pass = git_config('cidaemon.smtpPassword');
my $ex_from_addr = git_config('cidaemon.email', 1);
my $ex_from_name = git_config('cidaemon.name') || 'cidaemon';
my $scan_delay = git_config('cidaemon.scanDelay') || 60;
my $recent_size = git_config('cidaemon.recentCache') || 100;
my $tmpdir = git_config('cidaemon.tmpdir') || '/tmp';
my $queue_name = git_config('cidaemon.queue', 1);
my $queue_lock = "$queue_name.lock";
my @nocc_list;
open GIT,'git config --get-all cidaemon.nocc|';
while (<GIT>) {
chop;
push @nocc_list, $_;
}
close GIT;
sub nocc_author ($)
{
local $_ = shift;
foreach my $pat (@nocc_list) {
return 1 if /$pat/;
}
0;
}
sub input_echo ($)
{
my $prompt = shift;
local $| = 1;
print $prompt;
my $input = <STDIN>;
chop $input;
return $input;
}
sub input_noecho ($)
{
my $prompt = shift;
my $end = sub {system('stty','echo');print "\n";exit};
local $SIG{TERM} = $end;
local $SIG{INT} = $end;
system('stty','-echo');
local $| = 1;
print $prompt;
my $input = <STDIN>;
system('stty','echo');
print "\n";
chop $input;
return $input;
}
sub rfc2822_date ()
{
strftime("%a, %d %b %Y %H:%M:%S %Z", localtime);
}
sub send_email ($$$)
{
my ($subj, $body, $to) = @_;
my $now = rfc2822_date;
my $to_str = '';
my @rcpt_to;
foreach (@$to) {
my $s = $_;
$s =~ s/^/"/;
$s =~ s/(\s+<)/"$1/;
$to_str .= ', ' if $to_str;
$to_str .= $s;
push @rcpt_to, $1 if $s =~ /<(.*)>/;
}
die "Nobody to send to.\n" unless @rcpt_to;
my $msg = <<EOF;
From: "$ex_from_name" <$ex_from_addr>
To: $to_str
Date: $now
Subject: $subj
$body
EOF
my $smtp = EXCHANGE_NET_SMTP->new(Host => $ex_host)
or die "Cannot connect to $ex_host: $!\n";
if ($ex_user && $ex_pass) {
$smtp->auth($ex_user,$ex_pass)
or die "$ex_host rejected $ex_user\n";
}
$smtp->mail($ex_from_addr)
or die "$ex_host rejected $ex_from_addr\n";
scalar($smtp->recipient(@rcpt_to, { SkipBad => 1 }))
or die "$ex_host did not accept any addresses.\n";
$smtp->data($msg)
or die "$ex_host rejected message data\n";
$smtp->quit;
}
sub pop_queue ()
{
open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
flock LOCK, LOCK_EX;
my $queue = -f $queue_name ? retrieve $queue_name : [];
my $ent = shift @$queue;
nstore $queue, $queue_name;
flock LOCK, LOCK_UN;
close LOCK;
$ent;
}
sub git_exec (@)
{
system('git',@_) == 0 or die "Cannot git " . join(' ', @_) . "\n";
}
sub git_val (@)
{
open(C, '-|','git',@_);
my $r = <C>;
chop $r if $r;
close C;
$r;
}
sub do_build ($$)
{
my ($git_dir, $new) = @_;
my $tmp = File::Spec->catfile($tmpdir, "builder$$");
system('rm','-rf',$tmp) == 0 or die "Cannot clear $tmp\n";
die "Cannot clear $tmp.\n" if -e $tmp;
my $result = 1;
eval {
my $command;
{
local $ENV{GIT_DIR} = $git_dir;
$command = git_val 'config','builder.command';
}
die "No builder.command for $git_dir.\n" unless $command;
git_exec 'clone','-n','-l','-s',$git_dir,$tmp;
chmod 0700, $tmp or die "Cannot lock $tmp\n";
chdir $tmp or die "Cannot enter $tmp\n";
git_exec 'update-ref','HEAD',$new;
git_exec 'read-tree','-m','-u','HEAD','HEAD';
system $command;
if ($? == -1) {
print STDERR "failed to execute '$command': $!\n";
$result = 1;
} elsif ($? & 127) {
my $sig = $? & 127;
print STDERR "'$command' died from signal $sig\n";
$result = 1;
} else {
my $r = $? >> 8;
print STDERR "'$command' exited with $r\n" if $r;
$result = $r;
}
};
if ($@) {
$result = 2;
print STDERR "$@\n";
}
chdir '/';
system('rm','-rf',$tmp);
rmdir $tmp;
$result;
}
sub build_failed ($$$$$)
{
my ($git_dir, $ref, $old, $new, $msg) = @_;
$git_dir =~ m,/([^/]+)$,;
my $repo_name = $1;
$ref =~ s,^refs/(heads|tags)/,,;
my %authors;
my $shortlog;
my $revstr;
{
local $ENV{GIT_DIR} = $git_dir;
my @revs = ($new);
push @revs, '--not', @$old if @$old;
open LOG,'-|','git','rev-list','--pretty=raw',@revs;
while (<LOG>) {
if (s/^(author|committer) //) {
chomp;
s/>.*$/>/;
$authors{$_} = 1 unless nocc_author $_;
}
}
close LOG;
open LOG,'-|','git','shortlog',@revs;
$shortlog .= $_ while <LOG>;
close LOG;
$revstr = join(' ', @revs);
}
my @to = sort keys %authors;
unless (@to) {
print STDERR "error: No authors in $revstr\n";
return;
}
my $subject = "[$repo_name] $ref : Build Failed";
my $body = <<EOF;
Project: $git_dir
Branch: $ref
Commits: $revstr
$shortlog
Build Output:
--------------------------------------------------------------
$msg
EOF
send_email($subject, $body, \@to);
}
sub run_build ($$$$)
{
my ($git_dir, $ref, $old, $new) = @_;
if ($debug_flag) {
my @revs = ($new);
push @revs, '--not', @$old if @$old;
print "BUILDING $git_dir\n";
print " BRANCH: $ref\n";
print " COMMITS: ", join(' ', @revs), "\n";
}
local(*R, *W);
pipe R, W or die "cannot pipe builder: $!";
my $builder = fork();
if (!defined $builder) {
die "cannot fork builder: $!";
} elsif (0 == $builder) {
close R;
close STDIN;open(STDIN, '/dev/null');
open(STDOUT, '>&W');
open(STDERR, '>&W');
exit do_build $git_dir, $new;
} else {
close W;
my $out = '';
$out .= $_ while <R>;
close R;
waitpid $builder, 0;
build_failed $git_dir, $ref, $old, $new, $out if $?;
}
print "DONE\n\n" if $debug_flag;
}
sub daemon_loop ()
{
my $run = 1;
my $stop_sub = sub {$run = 0};
$SIG{HUP} = $stop_sub;
$SIG{INT} = $stop_sub;
$SIG{TERM} = $stop_sub;
mkdir $tmpdir, 0755;
my $pidfile = File::Spec->catfile($tmpdir, "cidaemon.pid");
open(O, ">$pidfile"); print O "$$\n"; close O;
while ($run) {
my $ent = pop_queue;
if ($ent) {
my ($git_dir, $ref, $old, $new) = @$ent;
$ent = $recent{$git_dir};
$recent{$git_dir} = $ent = [[], {}] unless $ent;
my ($rec_arr, $rec_hash) = @$ent;
next if $rec_hash->{$new}++;
while (@$rec_arr >= $recent_size) {
my $to_kill = shift @$rec_arr;
delete $rec_hash->{$to_kill};
}
push @$rec_arr, $new;
run_build $git_dir, $ref, $old, $new;
} else {
sleep $scan_delay;
}
}
unlink $pidfile;
}
$debug_flag = 0;
GetOptions(
'debug|d' => \$debug_flag,
'smtp-user=s' => \$ex_user,
) or die "usage: $0 [--debug] [--smtp-user=user]\n";
$ex_pass = input_noecho("$ex_user SMTP password: ")
if ($ex_user && !$ex_pass);
if ($debug_flag) {
daemon_loop;
exit 0;
}
my $daemon = fork();
if (!defined $daemon) {
die "cannot fork daemon: $!";
} elsif (0 == $daemon) {
close STDIN;open(STDIN, '/dev/null');
close STDOUT;open(STDOUT, '>/dev/null');
close STDERR;open(STDERR, '>/dev/null');
daemon_loop;
exit 0;
} else {
print "Daemon $daemon running in the background.\n";
}

View File

@@ -0,0 +1,104 @@
#!/usr/bin/perl
#
# A hook that notifies its companion cidaemon through a simple
# queue file that a ref has been updated via a push (actually
# by a receive-pack running on the server).
#
# See cidaemon for per-repository configuration details.
#
# To use this hook, add it as the post-receive hook, make it
# executable, and set its configuration options.
#
local $ENV{PATH} = '/opt/git/bin';
use strict;
use warnings;
use File::Spec;
use Storable qw(retrieve nstore);
use Fcntl ':flock';
my $git_dir = File::Spec->rel2abs($ENV{GIT_DIR});
my $queue_name = `git config --get builder.queue`;chop $queue_name;
$queue_name =~ m,^([^\s]+)$,; $queue_name = $1; # untaint
unless ($queue_name) {
1 while <STDIN>;
print STDERR "\nerror: builder.queue not set. Not enqueing.\n\n";
exit;
}
my $queue_lock = "$queue_name.lock";
my @skip;
open S, "git config --get-all builder.skip|";
while (<S>) {
chop;
push @skip, $_;
}
close S;
my @new_branch_base;
open S, "git config --get-all builder.newBranchBase|";
while (<S>) {
chop;
push @new_branch_base, $_;
}
close S;
sub skip ($)
{
local $_ = shift;
foreach my $p (@skip) {
return 1 if /^$p/;
}
0;
}
open LOCK, ">$queue_lock" or die "Can't open $queue_lock: $!";
flock LOCK, LOCK_EX;
my $queue = -f $queue_name ? retrieve $queue_name : [];
my %existing;
foreach my $r (@$queue) {
my ($gd, $ref) = @$r;
$existing{$gd}{$ref} = $r;
}
my @new_branch_commits;
my $loaded_new_branch_commits = 0;
while (<STDIN>) {
chop;
my ($old, $new, $ref) = split / /, $_, 3;
next if $old eq $new;
next if $new =~ /^0{40}$/;
next if skip $ref;
my $r = $existing{$git_dir}{$ref};
if ($r) {
$r->[3] = $new;
} else {
if ($old =~ /^0{40}$/) {
if (!$loaded_new_branch_commits && @new_branch_base) {
open M,'-|','git','show-ref',@new_branch_base;
while (<M>) {
($_) = split / /, $_;
push @new_branch_commits, $_;
}
close M;
$loaded_new_branch_commits = 1;
}
$old = [@new_branch_commits];
} else {
$old = [$old];
}
$r = [$git_dir, $ref, $old, $new];
$existing{$git_dir}{$ref} = $r;
push @$queue, $r;
}
}
nstore $queue, $queue_name;
flock LOCK, LOCK_UN;
close LOCK;

View File

@@ -263,6 +263,16 @@ and returns the process output as a string."
(locale-charset-to-coding-system repo-config))
'utf-8)))
(defun git-get-logoutput-coding-system ()
"Return the coding system used for git-log output."
(let ((repo-config (or (git-config "i18n.logoutputencoding")
(git-config "i18n.commitencoding"))))
(or git-commits-coding-system
(and repo-config
(fboundp 'locale-charset-to-coding-system)
(locale-charset-to-coding-system repo-config))
'utf-8)))
(defun git-escape-file-name (name)
"Escape a file name if necessary."
(if (string-match "[\n\t\"\\]" name)
@@ -406,6 +416,14 @@ and returns the process output as a string."
(push (match-string 0) heads))))
(nreverse heads)))
(defun git-get-commit-description (commit)
"Get a one-line description of COMMIT."
(let ((coding-system-for-read (git-get-logoutput-coding-system)))
(let ((descr (git-call-process-env-string nil "log" "--max-count=1" "--pretty=oneline" commit)))
(if (and descr (string-match "\\`\\([0-9a-f]\\{40\\}\\) *\\(.*\\)$" descr))
(concat (substring (match-string 1 descr) 0 10) " - " (match-string 2 descr))
descr))))
;;;; File info structure
;;;; ------------------------------------------------------------
@@ -573,7 +591,7 @@ and returns the process output as a string."
"Refresh the ewoc header and footer."
(let ((branch (git-symbolic-ref "HEAD"))
(head (if (git-empty-db-p) "Nothing committed yet"
(substring (git-rev-parse "HEAD") 0 10)))
(git-get-commit-description "HEAD")))
(merge-heads (git-get-merge-heads)))
(ewoc-set-hf status
(format "Directory: %s\nBranch: %s\nHead: %s%s\n"
@@ -584,7 +602,7 @@ and returns the process output as a string."
head
(if merge-heads
(concat "\nMerging: "
(mapconcat (lambda (str) (substring str 0 10)) merge-heads " "))
(mapconcat (lambda (str) (git-get-commit-description str)) merge-heads "\n "))
""))
(if (ewoc-nth status 0) "" " No changes."))))

View File

@@ -0,0 +1,588 @@
#!/bin/sh
#
# Copyright (c) 2007 Andy Parkins
#
# An example hook script to mail out commit update information. This hook sends emails
# listing new revisions to the repository introduced by the change being reported. The
# rule is that (for branch updates) each commit will appear on one email and one email
# only.
#
# This hook is stored in the contrib/hooks directory. Your distribution will have put
# this somewhere standard. You should make this script executable then link to it in
# the repository you would like to use it in. For example, on debian the hook is stored
# in /usr/share/doc/git-core/contrib/hooks/post-receive-email:
#
# chmod a+x post-receive-email
# cd /path/to/your/repository.git
# ln -sf /usr/share/doc/git-core/contrib/hooks/post-receive-email hooks/post-receive
#
# This hook script assumes it is enabled on the central repository of a project, with
# all users pushing only to it and not between each other. It will still work if you
# don't operate in that style, but it would become possible for the email to be from
# someone other than the person doing the push.
#
# Config
# ------
# hooks.mailinglist
# This is the list that all pushes will go to; leave it blank to not send
# emails for every ref update.
# hooks.announcelist
# This is the list that all pushes of annotated tags will go to. Leave it
# blank to default to the mailinglist field. The announce emails lists the
# short log summary of the changes since the last annotated tag.
# hook.envelopesender
# If set then the -f option is passed to sendmail to allow the envelope sender
# address to be set
#
# Notes
# -----
# All emails have their subjects prefixed with "[SCM]" to aid filtering.
# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and
# give information for debugging.
#
# ---------------------------- Functions
#
# Top level email generation function. This decides what type of update
# this is and calls the appropriate body-generation routine after outputting
# the common header
#
# Note this function doesn't actually generate any email output, that is taken
# care of by the functions it calls:
# - generate_email_header
# - generate_create_XXXX_email
# - generate_update_XXXX_email
# - generate_delete_XXXX_email
# - generate_email_footer
#
generate_email()
{
# --- Arguments
oldrev=$(git rev-parse $1)
newrev=$(git rev-parse $2)
refname="$3"
# --- Interpret
# 0000->1234 (create)
# 1234->2345 (update)
# 2345->0000 (delete)
if expr "$oldrev" : '0*$' >/dev/null
then
change_type="create"
else
if expr "$newrev" : '0*$' >/dev/null
then
change_type="delete"
else
change_type="update"
fi
fi
# --- Get the revision types
newrev_type=$(git cat-file -t $newrev 2> /dev/null)
oldrev_type=$(git cat-file -t "$oldrev" 2> /dev/null)
case "$change_type" in
create|update)
rev="$newrev"
rev_type="$newrev_type"
;;
delete)
rev="$oldrev"
rev_type="$oldrev_type"
;;
esac
# The revision type tells us what type the commit is, combined with
# the location of the ref we can decide between
# - working branch
# - tracking branch
# - unannoted tag
# - annotated tag
case "$refname","$rev_type" in
refs/tags/*,commit)
# un-annotated tag
refname_type="tag"
short_refname=${refname##refs/tags/}
;;
refs/tags/*,tag)
# annotated tag
refname_type="annotated tag"
short_refname=${refname##refs/tags/}
# change recipients
if [ -n "$announcerecipients" ]; then
recipients="$announcerecipients"
fi
;;
refs/heads/*,commit)
# branch
refname_type="branch"
short_refname=${refname##refs/heads/}
;;
refs/remotes/*,commit)
# tracking branch
refname_type="tracking branch"
short_refname=${refname##refs/remotes/}
echo >&2 "*** Push-update of tracking branch, $refname"
echo >&2 "*** - no email generated."
exit 0
;;
*)
# Anything else (is there anything else?)
echo >&2 "*** Unknown type of update to $refname ($rev_type)"
echo >&2 "*** - no email generated"
exit 1
;;
esac
# Check if we've got anyone to send to
if [ -z "$recipients" ]; then
echo >&2 "*** hooks.recipients is not set so no email will be sent"
echo >&2 "*** for $refname update $oldrev->$newrev"
exit 0
fi
# Email parameters
# The committer will be obtained from the latest existing rev; so
# for a deletion it will be the oldrev, for the others, then newrev
committer=$(git show --pretty=full -s $rev | sed -ne "s/^Commit: //p" |
sed -ne 's/\(.*\) </"\1" </p')
# The email subject will contain the best description of the ref
# that we can build from the parameters
describe=$(git describe $rev 2>/dev/null)
if [ -z "$describe" ]; then
describe=$rev
fi
generate_email_header
# Call the correct body generation function
fn_name=general
case "$refname_type" in
"tracking branch"|branch)
fn_name=branch
;;
"annotated tag")
fn_name=atag
;;
esac
generate_${change_type}_${fn_name}_email
generate_email_footer
}
generate_email_header()
{
# --- Email (all stdout will be the email)
# Generate header
cat <<-EOF
From: $committer
To: $recipients
Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname, ${change_type}d. $describe
X-Git-Refname: $refname
X-Git-Reftype: $refname_type
X-Git-Oldrev: $oldrev
X-Git-Newrev: $newrev
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "$projectdesc".
The $refname_type, $short_refname has been ${change_type}d
EOF
}
generate_email_footer()
{
cat <<-EOF
hooks/post-receive
--
$projectdesc
EOF
}
# --------------- Branches
#
# Called for the creation of a branch
#
generate_create_branch_email()
{
# This is a new branch and so oldrev is not valid
echo " at $newrev ($newrev_type)"
echo ""
echo $LOGBEGIN
# This shows all log entries that are not already covered by
# another ref - i.e. commits that are now accessible from this
# ref that were previously not accessible (see generate_update_branch_email
# for the explanation of this command)
git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
git rev-list --pretty --stdin $newrev
echo $LOGEND
}
#
# Called for the change of a pre-existing branch
#
generate_update_branch_email()
{
# Consider this:
# 1 --- 2 --- O --- X --- 3 --- 4 --- N
#
# O is $oldrev for $refname
# N is $newrev for $refname
# X is a revision pointed to by some other ref, for which we may
# assume that an email has already been generated.
# In this case we want to issue an email containing only revisions
# 3, 4, and N. Given (almost) by
#
# git-rev-list N ^O --not --all
#
# The reason for the "almost", is that the "--not --all" will take
# precedence over the "N", and effectively will translate to
#
# git-rev-list N ^O ^X ^N
#
# So, we need to build up the list more carefully. git-rev-parse will
# generate a list of revs that may be fed into git-rev-list. We can get
# it to make the "--not --all" part and then filter out the "^N" with:
#
# git-rev-parse --not --all | grep -v N
#
# Then, using the --stdin switch to git-rev-list we have effectively
# manufactured
#
# git-rev-list N ^O ^X
#
# This leaves a problem when someone else updates the repository
# while this script is running. Their new value of the ref we're working
# on would be included in the "--not --all" output; and as our $newrev
# would be an ancestor of that commit, it would exclude all of our
# commits. What we really want is to exclude the current value of
# $refname from the --not list, rather than N itself. So:
#
# git-rev-parse --not --all | grep -v $(git-rev-parse $refname)
#
# Get's us to something pretty safe (apart from the small time between
# refname being read, and git-rev-parse running - for that, I give up)
#
#
# Next problem, consider this:
# * --- B --- * --- O ($oldrev)
# \
# * --- X --- * --- N ($newrev)
#
# That is to say, there is no guarantee that oldrev is a strict subset of
# newrev (it would have required a --force, but that's allowed). So, we
# can't simply say rev-list $oldrev..$newrev. Instead we find the common
# base of the two revs and list from there.
#
# As above, we need to take into account the presence of X; if another
# branch is already in the repository and points at some of the revisions
# that we are about to output - we don't want them. The solution is as
# before: git-rev-parse output filtered.
#
# Finally, tags:
# 1 --- 2 --- O --- T --- 3 --- 4 --- N
#
# Tags pushed into the repository generate nice shortlog emails that
# summarise the commits between them and the previous tag. However,
# those emails don't include the full commit messages that we output
# for a branch update. Therefore we still want to output revisions
# that have been output on a tag email.
#
# Luckily, git-rev-parse includes just the tool. Instead of using "--all"
# we use "--branches"; this has the added benefit that "remotes/" will
# be ignored as well.
# List all of the revisions that were removed by this update, in a fast forward
# update, this list will be empty, because rev-list O ^N is empty. For a non
# fast forward, O ^N is the list of removed revisions
fastforward=""
rev=""
for rev in $(git rev-list $newrev..$oldrev)
do
revtype=$(git cat-file -t "$rev")
echo " discards $rev ($revtype)"
done
if [ -z "$rev" ]; then
fast_forward=1
fi
# List all the revisions from baserev to newrev in a kind of
# "table-of-contents"; note this list can include revisions that have
# already had notification emails and is present to show the full detail
# of the change from rolling back the old revision to the base revision and
# then forward to the new revision
for rev in $(git rev-list $oldrev..$newrev)
do
revtype=$(git cat-file -t "$rev")
echo " via $rev ($revtype)"
done
if [ -z "$fastforward" ]; then
echo " from $oldrev ($oldrev_type)"
else
echo ""
echo "This update added new revisions after undoing old revisions. That is to"
echo "say, the old revision is not a strict subset of the new revision. This"
echo "situation occurs when you --force push a change and generate a"
echo "repository containing something like this:"
echo ""
echo " * -- * -- B -- O -- O -- O ($oldrev)"
echo " \\"
echo " N -- N -- N ($newrev)"
echo ""
echo "When this happens we assume that you've already had alert emails for all"
echo "of the O revisions, and so we here report only the revisions in the N"
echo "branch from the common base, B."
fi
echo ""
echo "Those revisions listed above that are new to this repository have"
echo "not appeared on any other notification email; so we list those"
echo "revisions in full, below."
echo ""
echo $LOGBEGIN
git rev-parse --not --branches | grep -v $(git rev-parse $refname) |
git rev-list --pretty --stdin $oldrev..$newrev
# XXX: Need a way of detecting whether git rev-list actually outputted
# anything, so that we can issue a "no new revisions added by this
# update" message
echo $LOGEND
# The diffstat is shown from the old revision to the new revision. This
# is to show the truth of what happened in this change. There's no point
# showing the stat from the base to the new revision because the base
# is effectively a random revision at this point - the user will be
# interested in what this revision changed - including the undoing of
# previous revisions in the case of non-fast forward updates.
echo ""
echo "Summary of changes:"
git diff-tree --stat --summary --find-copies-harder $oldrev..$newrev
}
#
# Called for the deletion of a branch
#
generate_delete_branch_email()
{
echo " was $oldrev"
echo ""
echo $LOGEND
git show -s --pretty=oneline $oldrev
echo $LOGEND
}
# --------------- Annotated tags
#
# Called for the creation of an annotated tag
#
generate_create_atag_email()
{
echo " at $newrev ($newrev_type)"
generate_atag_email
}
#
# Called for the update of an annotated tag (this is probably a rare event
# and may not even be allowed)
#
generate_update_atag_email()
{
echo " to $newrev ($newrev_type)"
echo " from $oldrev (which is now obsolete)"
generate_atag_email
}
#
# Called when an annotated tag is created or changed
#
generate_atag_email()
{
# Use git-for-each-ref to pull out the individual fields from the tag
eval $(git for-each-ref --shell --format='
tagobject=%(*objectname)
tagtype=%(*objecttype)
tagger=%(taggername)
tagged=%(taggerdate)' $refname
)
echo " tagging $tagobject ($tagtype)"
case "$tagtype" in
commit)
# If the tagged object is a commit, then we assume this is a
# release, and so we calculate which tag this tag is replacing
prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
if [ -n "$prevtag" ]; then
echo " replaces $prevtag"
fi
;;
*)
echo " length $(git cat-file -s $tagobject) bytes"
;;
esac
echo " tagged by $tagger"
echo " on $tagged"
echo ""
echo $LOGBEGIN
# Show the content of the tag message; this might contain a change log
# or release notes so is worth displaying.
git cat-file tag $newrev | sed -e '1,/^$/d'
echo ""
case "$tagtype" in
commit)
# Only commit tags make sense to have rev-list operations performed
# on them
if [ -n "$prevtag" ]; then
# Show changes since the previous release
git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
else
# No previous tag, show all the changes since time began
git rev-list --pretty=short $newrev | git shortlog
fi
;;
*)
# XXX: Is there anything useful we can do for non-commit objects?
;;
esac
echo $LOGEND
}
#
# Called for the deletion of an annotated tag
#
generate_delete_atag_email()
{
echo " was $oldrev"
echo ""
echo $LOGEND
git show -s --pretty=oneline $oldrev
echo $LOGEND
}
# --------------- General references
#
# Called when any other type of reference is created (most likely a
# non-annotated tag)
#
generate_create_general_email()
{
echo " at $newrev ($newrev_type)"
generate_general_email
}
#
# Called when any other type of reference is updated (most likely a
# non-annotated tag)
#
generate_update_general_email()
{
echo " to $newrev ($newrev_type)"
echo " from $oldrev"
generate_general_email
}
#
# Called for creation or update of any other type of reference
#
generate_general_email()
{
# Unannotated tags are more about marking a point than releasing a version;
# therefore we don't do the shortlog summary that we do for annotated tags
# above - we simply show that the point has been marked, and print the log
# message for the marked point for reference purposes
#
# Note this section also catches any other reference type (although there
# aren't any) and deals with them in the same way.
echo ""
if [ "$newrev_type" = "commit" ]; then
echo $LOGBEGIN
git show --no-color --root -s $newrev
echo $LOGEND
else
# What can we do here? The tag marks an object that is not a commit,
# so there is no log for us to display. It's probably not wise to
# output git-cat-file as it could be a binary blob. We'll just say how
# big it is
echo "$newrev is a $newrev_type, and is $(git cat-file -s $newrev) bytes long."
fi
}
#
# Called for the deletion of any other type of reference
#
generate_delete_general_email()
{
echo " was $oldrev"
echo ""
echo $LOGEND
git show -s --pretty=oneline $oldrev
echo $LOGEND
}
# ---------------------------- main()
# --- Constants
EMAILPREFIX="[SCM] "
LOGBEGIN="- Log -----------------------------------------------------------------"
LOGEND="-----------------------------------------------------------------------"
# --- Config
# Set GIT_DIR either from the working directory, or from the environment
# variable.
GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
if [ -z "$GIT_DIR" ]; then
echo >&2 "fatal: post-receive: GIT_DIR not set"
exit 1
fi
projectdesc=$(sed -e '1p' "$GIT_DIR/description")
# Check if the description is unchanged from it's default, and shorten it to a
# more manageable length if it is
if expr "$projectdesc" : "Unnamed repository.*$" >/dev/null
then
projectdesc="UNNAMED PROJECT"
fi
recipients=$(git repo-config hooks.mailinglist)
announcerecipients=$(git repo-config hooks.announcelist)
envelopesender=$(git-repo-config hooks.envelopesender)
# --- Main loop
# Allow dual mode: run from the command line just like the update hook, or if
# no arguments are given then run as a hook script
if [ -n "$1" -a -n "$2" -a -n "$3" ]; then
# Output to the terminal in command line mode - if someone wanted to
# resend an email; they could redirect the output to sendmail themselves
PAGER= generate_email $2 $3 $1
else
if [ -n "$envelopesender" ]; then
envelopesender="-f '$envelopesender'"
fi
while read oldrev newrev refname
do
generate_email $oldrev $newrev $refname |
/usr/sbin/sendmail -t $envelopesender
done
fi

57
contrib/workdir/git-new-workdir Executable file
View File

@@ -0,0 +1,57 @@
#!/bin/sh
usage () {
echo "usage:" $@
exit 127
}
die () {
echo $@
exit 128
}
if test $# -lt 2 || test $# -gt 3
then
usage "$0 <repository> <new_workdir> [<branch>]"
fi
orig_git=$1
new_workdir=$2
branch=$3
# want to make sure that what is pointed to has a .git directory ...
test -d "$orig_git/.git" || die "\"$orig_git\" is not a git repository!"
# don't link to a workdir
if test -L "$orig_git/.git/config"
then
die "\"$orig_git\" is a working directory only, please specify" \
"a complete repository."
fi
# make sure the the links use full paths
orig_git=$(cd "$orig_git"; pwd)
# create the workdir
mkdir -p "$new_workdir/.git" || die "unable to create \"$new_workdir\"!"
# create the links to the original repo. explictly exclude index, HEAD and
# logs/HEAD from the list since they are purely related to the current working
# directory, and should not be shared.
for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache
do
case $x in
*/*)
mkdir -p "$(dirname "$new_workdir/.git/$x")"
;;
esac
ln -s "$orig_git/.git/$x" "$new_workdir/.git/$x"
done
# now setup the workdir
cd "$new_workdir"
# copy the HEAD from the original repository as a default branch
cp "$orig_git/.git/HEAD" .git/HEAD
# checkout the branch (either the same as HEAD from the original repository, or
# the one that was asked for)
git checkout -f $branch

View File

@@ -630,7 +630,7 @@ static void start_packfile(void)
int pack_fd;
snprintf(tmpfile, sizeof(tmpfile),
"%s/pack_XXXXXX", get_object_directory());
"%s/tmp_pack_XXXXXX", get_object_directory());
pack_fd = mkstemp(tmpfile);
if (pack_fd < 0)
die("Can't create %s: %s", tmpfile, strerror(errno));
@@ -730,7 +730,7 @@ static char *create_index(void)
}
snprintf(tmpfile, sizeof(tmpfile),
"%s/index_XXXXXX", get_object_directory());
"%s/tmp_idx_XXXXXX", get_object_directory());
idx_fd = mkstemp(tmpfile);
if (idx_fd < 0)
die("Can't create %s: %s", tmpfile, strerror(errno));
@@ -1312,7 +1312,7 @@ static int update_branch(struct branch *b)
if (!in_merge_bases(old_cmit, &new_cmit, 1)) {
unlock_ref(lock);
warn("Not updating %s"
warning("Not updating %s"
" (new tip %s does not contain %s)",
b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
return -1;

View File

@@ -42,8 +42,7 @@ static int process_tree(struct tree *tree)
if (parse_tree(tree))
return -1;
desc.buf = tree->buffer;
desc.size = tree->size;
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
struct object *obj = NULL;

View File

@@ -408,12 +408,10 @@ do
# trust what the user has in the index file and the
# working tree.
resolved=
changed="$(git-diff-index --cached --name-only HEAD)"
if test '' = "$changed"
then
git-diff-index --quiet --cached HEAD && {
echo "No changes - did you forget to use 'git add'?"
stop_here_user_resolve $this
fi
}
unmerged=$(git-ls-files -u)
if test -n "$unmerged"
then
@@ -435,13 +433,11 @@ do
then
# Applying the patch to an earlier tree and merging the
# result may have produced the same tree as ours.
changed="$(git-diff-index --cached --name-only HEAD)"
if test '' = "$changed"
then
echo No changes -- Patch already applied.
go_next
continue
fi
git-diff-index --quiet --cached HEAD && {
echo No changes -- Patch already applied.
go_next
continue
}
# clear apply_status -- we have successfully merged.
apply_status=0
fi

View File

@@ -1,14 +1,15 @@
#!/bin/sh
USAGE='[start|bad|good|next|reset|visualize|replay|log]'
USAGE='[start|bad|good|next|reset|visualize|replay|log|run]'
LONG_USAGE='git bisect start [<pathspec>] reset bisect state and start bisection.
git bisect bad [<rev>] mark <rev> a known-bad revision.
git bisect good [<rev>...] mark <rev>... known-good revisions.
git bisect next find next bisection to test and check it out.
git bisect reset [<branch>] finish bisection search and go back to branch.
git bisect visualize show bisect status in gitk.
git bisect replay <logfile> replay bisection log
git bisect log show bisect log.'
git bisect replay <logfile> replay bisection log.
git bisect log show bisect log.
git bisect run <cmd>... use <cmd>... to automatically bisect.'
. git-sh-setup
require_work_tree
@@ -49,7 +50,7 @@ bisect_start() {
head=$(GIT_DIR="$GIT_DIR" git-symbolic-ref HEAD) ||
die "Bad HEAD - I need a symbolic ref"
case "$head" in
refs/heads/bisect*)
refs/heads/bisect)
if [ -s "$GIT_DIR/head-name" ]; then
branch=`cat "$GIT_DIR/head-name"`
else
@@ -85,7 +86,7 @@ bisect_bad() {
0)
rev=$(git-rev-parse --verify HEAD) ;;
1)
rev=$(git-rev-parse --verify "$1") ;;
rev=$(git-rev-parse --verify "$1^{commit}") ;;
*)
usage ;;
esac || exit
@@ -104,7 +105,7 @@ bisect_good() {
esac
for rev in $revs
do
rev=$(git-rev-parse --verify "$rev") || exit
rev=$(git-rev-parse --verify "$rev^{commit}") || exit
echo "$rev" >"$GIT_DIR/refs/bisect/good-$rev"
echo "# good: "$(git-show-branch $rev) >>"$GIT_DIR/BISECT_LOG"
echo "git-bisect good $rev" >>"$GIT_DIR/BISECT_LOG"
@@ -122,7 +123,15 @@ bisect_next_check() {
case "$next_ok,$1" in
no,) false ;;
no,fail)
echo >&2 'You need to give me at least one good and one bad revisions.'
THEN=''
test -d "$GIT_DIR/refs/bisect" || {
echo >&2 'You need to start by "git bisect start".'
THEN='then '
}
echo >&2 'You '$THEN'need to give me at least one good' \
'and one bad revisions.'
echo >&2 '(You can use "git bisect bad" and' \
'"git bisect good" for that.)'
exit 1 ;;
*)
true ;;
@@ -140,7 +149,7 @@ bisect_next() {
bad=$(git-rev-parse --verify refs/bisect/bad) &&
good=$(git-rev-parse --sq --revs-only --not \
$(cd "$GIT_DIR" && ls refs/bisect/good-*)) &&
rev=$(eval "git-rev-list --bisect $good $bad -- $(cat $GIT_DIR/BISECT_NAMES)") || exit
rev=$(eval "git-rev-list --bisect $good $bad -- $(cat "$GIT_DIR/BISECT_NAMES")") || exit
if [ -z "$rev" ]; then
echo "$bad was both good and bad"
exit 1
@@ -172,7 +181,7 @@ bisect_reset() {
else
branch=master
fi ;;
1) test -f "$GIT_DIR/refs/heads/$1" || {
1) git-show-ref --verify --quiet -- "refs/heads/$1" || {
echo >&2 "$1 does not seem to be a valid branch"
exit 1
}
@@ -185,6 +194,7 @@ bisect_reset() {
rm -f "$GIT_DIR/refs/heads/bisect" "$GIT_DIR/head-name"
rm -f "$GIT_DIR/BISECT_LOG"
rm -f "$GIT_DIR/BISECT_NAMES"
rm -f "$GIT_DIR/BISECT_RUN"
fi
}
@@ -220,6 +230,52 @@ bisect_replay () {
bisect_auto_next
}
bisect_run () {
bisect_next_check fail
while true
do
echo "running $@"
"$@"
res=$?
# Check for really bad run error.
if [ $res -lt 0 -o $res -ge 128 ]; then
echo >&2 "bisect run failed:"
echo >&2 "exit code $res from '$@' is < 0 or >= 128"
exit $res
fi
# Use "bisect_good" or "bisect_bad"
# depending on run success or failure.
if [ $res -gt 0 ]; then
next_bisect='bisect_bad'
else
next_bisect='bisect_good'
fi
# We have to use a subshell because bisect_good or
# bisect_bad functions can exit.
( $next_bisect > "$GIT_DIR/BISECT_RUN" )
res=$?
cat "$GIT_DIR/BISECT_RUN"
if [ $res -ne 0 ]; then
echo >&2 "bisect run failed:"
echo >&2 "$next_bisect exited with error code $res"
exit $res
fi
if grep "is first bad commit" "$GIT_DIR/BISECT_RUN" > /dev/null; then
echo "bisect run success"
exit 0;
fi
done
}
case "$#" in
0)
usage ;;
@@ -244,6 +300,8 @@ case "$#" in
bisect_replay "$@" ;;
log)
cat "$GIT_DIR/BISECT_LOG" ;;
run)
bisect_run "$@" ;;
*)
usage ;;
esac

View File

@@ -163,6 +163,13 @@ cd_to_toplevel
detached=
detach_warn=
describe_detached_head () {
test -n "$quiet" || {
printf >&2 "$1 "
GIT_PAGER= git log >&2 -1 --pretty=oneline --abbrev-commit "$2"
}
}
if test -z "$branch$newbranch" && test "$new" != "$old"
then
detached="$new"
@@ -173,9 +180,9 @@ If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
git checkout -b <new_branch_name>"
fi
elif test -z "$oldbranch" && test -z "$quiet"
elif test -z "$oldbranch"
then
echo >&2 "Previous HEAD position was $old"
describe_detached_head 'Previous HEAD position was' "$old"
fi
if [ "X$old" = X ]
@@ -257,8 +264,13 @@ if [ "$?" -eq 0 ]; then
if test -n "$branch"
then
GIT_DIR="$GIT_DIR" git-symbolic-ref -m "checkout: moving to $branch" HEAD "refs/heads/$branch"
if test -z "$quiet"
if test -n "$quiet"
then
true # nothing
elif test "refs/heads/$branch" = "$oldbranch"
then
echo >&2 "Already on branch \"$branch\""
else
echo >&2 "Switched to${newbranch:+ a new} branch \"$branch\""
fi
elif test -n "$detached"
@@ -277,6 +289,7 @@ if [ "$?" -eq 0 ]; then
then
echo >&2 "$detach_warn"
fi
describe_detached_head 'HEAD is now at' HEAD
fi
rm -f "$GIT_DIR/MERGE_HEAD"
else

View File

@@ -42,6 +42,7 @@ clone_dumb_http () {
http_fetch "$1/info/refs" "$clone_tmp/refs" ||
die "Cannot get remote repository information.
Perhaps git-update-server-info needs to be run there?"
test "z$quiet" = z && v=-v || v=
while read sha1 refname
do
name=`expr "z$refname" : 'zrefs/\(.*\)'` &&
@@ -59,7 +60,7 @@ Perhaps git-update-server-info needs to be run there?"
else
tname=$name
fi
git-http-fetch -v -a -w "$tname" "$name" "$1/" || exit 1
git-http-fetch $v -a -w "$tname" "$name" "$1" || exit 1
done <"$clone_tmp/refs"
rm -fr "$clone_tmp"
http_fetch "$1/HEAD" "$GIT_DIR/REMOTE_HEAD" ||

View File

@@ -371,7 +371,7 @@ t,)
if test -z "$initial_commit"
then
cp "$THIS_INDEX" "$TMP_INDEX"
GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -m HEAD
GIT_INDEX_FILE="$TMP_INDEX" git-read-tree -i -m HEAD
else
rm -f "$TMP_INDEX"
fi || exit

View File

@@ -94,7 +94,7 @@
extern void usage(const char *err) NORETURN;
extern void die(const char *err, ...) NORETURN __attribute__((format (printf, 1, 2)));
extern int error(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void warn(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void warning(const char *err, ...) __attribute__((format (printf, 1, 2)));
extern void set_usage_routine(void (*routine)(const char *err) NORETURN);
extern void set_die_routine(void (*routine)(const char *err, va_list params) NORETURN);

View File

@@ -110,8 +110,8 @@ ls_remote_result=$(git ls-remote $exec "$remote") ||
append_fetch_head () {
flags=
test -n "$verbose" && flags="$flags -v"
test -n "$force" && flags="$flags -f"
test -n "$verbose" && flags="$flags$LF-v"
test -n "$force$single_force" && flags="$flags$LF-f"
GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION" \
git-fetch--tool $flags append-fetch-head "$@"
}
@@ -248,7 +248,7 @@ fetch_per_ref () {
expr "z$head" : "z$_x40\$" >/dev/null ||
die "No such ref $remote_name at $remote"
echo >&2 "Fetching $remote_name from $remote using $proto"
git-http-fetch -v -a "$head" "$remote/" || exit
git-http-fetch -v -a "$head" "$remote" || exit
;;
rsync://*)
test -n "$shallow_depth" &&

View File

@@ -9,6 +9,6 @@
# because the current index is what we will be committing as the
# merge result.
test "$(git-diff-index --cached --name-status HEAD)" = "" || exit 2
git-diff-index --quiet --cached HEAD || exit 2
exit 0

View File

@@ -153,6 +153,10 @@ merge_name () {
git-show-ref -q --verify "refs/heads/$truname" 2>/dev/null
then
echo "$rh branch '$truname' (early part) of ."
elif test "$remote" = "FETCH_HEAD" -a -r "$GIT_DIR/FETCH_HEAD"
then
sed -e 's/ not-for-merge / /' -e 1q \
"$GIT_DIR/FETCH_HEAD"
else
echo "$rh commit '$remote'"
fi

View File

@@ -14,19 +14,19 @@ SUBDIRECTORY_OK=Yes
require_work_tree
# Returns true if the mode reflects a symlink
function is_symlink () {
is_symlink () {
test "$1" = 120000
}
function local_present () {
local_present () {
test -n "$local_mode"
}
function remote_present () {
remote_present () {
test -n "$remote_mode"
}
function base_present () {
base_present () {
test -n "$base_mode"
}
@@ -39,32 +39,29 @@ cleanup_temp_files () {
fi
}
function describe_file () {
describe_file () {
mode="$1"
branch="$2"
file="$3"
echo -n " "
printf " {%s}: " "$branch"
if test -z "$mode"; then
echo -n "'$path' was deleted"
echo "deleted"
elif is_symlink "$mode" ; then
echo -n "'$path' is a symlink containing '"
cat "$file"
echo -n "'"
echo "a symbolic link -> '$(cat "$file")'"
else
if base_present; then
echo -n "'$path' was created"
echo "modified"
else
echo -n "'$path' was modified"
echo "created"
fi
fi
echo " in the $branch branch"
}
resolve_symlink_merge () {
while /bin/true; do
echo -n "Use (r)emote or (l)ocal, or (a)bort? "
while true; do
printf "Use (l)ocal or (r)emote, or (a)bort? "
read ans
case "$ans" in
[lL]*)
@@ -73,13 +70,13 @@ resolve_symlink_merge () {
cleanup_temp_files --save-backup
return
;;
[rR]*)
[rR]*)
git-checkout-index -f --stage=3 -- "$path"
git-add -- "$path"
cleanup_temp_files --save-backup
return
;;
[qQ]*)
[aA]*)
exit 1
;;
esac
@@ -87,38 +84,69 @@ resolve_symlink_merge () {
}
resolve_deleted_merge () {
while /bin/true; do
echo -n "Use (m)odified or (d)eleted file, or (a)bort? "
while true; do
if base_present; then
printf "Use (m)odified or (d)eleted file, or (a)bort? "
else
printf "Use (c)reated or (d)eleted file, or (a)bort? "
fi
read ans
case "$ans" in
[mM]*)
[mMcC]*)
git-add -- "$path"
cleanup_temp_files --save-backup
return
;;
[dD]*)
git-rm -- "$path"
[dD]*)
git-rm -- "$path" > /dev/null
cleanup_temp_files
return
;;
[qQ]*)
[aA]*)
exit 1
;;
esac
done
}
check_unchanged () {
if test "$path" -nt "$BACKUP" ; then
status=0;
else
while true; do
echo "$path seems unchanged."
printf "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
y*|Y*) status=0; break ;;
n*|N*) status=1; break ;;
esac
done
fi
}
save_backup () {
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
fi
}
remove_backup () {
if test "$status" -eq 0; then
rm "$BACKUP"
fi
}
merge_file () {
path="$1"
if test ! -f "$path" ; then
echo "$path: file not found"
exit 1
fi
f=`git-ls-files -u -- "$path"`
if test -z "$f" ; then
echo "$path: file does not need merging"
if test ! -f "$path" ; then
echo "$path: file not found"
else
echo "$path: file does not need merging"
fi
exit 1
fi
@@ -139,7 +167,7 @@ merge_file () {
remote_present && git cat-file blob ":3:$path" > "$REMOTE" 2>/dev/null
if test -z "$local_mode" -o -z "$remote_mode"; then
echo "Deleted merge conflict for $path:"
echo "Deleted merge conflict for '$path':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_deleted_merge
@@ -147,17 +175,17 @@ merge_file () {
fi
if is_symlink "$local_mode" || is_symlink "$remote_mode"; then
echo "Symlink merge conflict for $path:"
echo "Symbolic link merge conflict for '$path':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
resolve_symlink_merge
return
fi
echo "Normal merge conflict for $path:"
echo "Normal merge conflict for '$path':"
describe_file "$local_mode" "local" "$LOCAL"
describe_file "$remote_mode" "remote" "$REMOTE"
echo -n "Hit return to start merge resolution tool ($merge_tool): "
printf "Hit return to start merge resolution tool (%s): " "$merge_tool"
read ans
case "$merge_tool" in
@@ -170,9 +198,7 @@ merge_file () {
-o "$path" -- "$LOCAL" "$REMOTE" > /dev/null 2>&1)
fi
status=$?
if test "$status" -eq 0; then
rm "$BACKUP"
fi
remove_backup
;;
tkdiff)
if base_present ; then
@@ -181,29 +207,13 @@ merge_file () {
tkdiff -o "$path" -- "$LOCAL" "$REMOTE"
fi
status=$?
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
fi
save_backup
;;
meld|vimdiff)
touch "$BACKUP"
$merge_tool -- "$LOCAL" "$path" "$REMOTE"
if test "$path" -nt "$BACKUP" ; then
status=0;
else
while true; do
echo "$path seems unchanged."
echo -n "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
y*|Y*) status=0; break ;;
n*|N*) status=1; break ;;
esac
done
fi
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
fi
check_unchanged
save_backup
;;
xxdiff)
touch "$BACKUP"
@@ -220,22 +230,18 @@ merge_file () {
-R 'Accel.SearchForward: "Ctrl-G"' \
--merged-file "$path" -- "$LOCAL" "$REMOTE"
fi
if test "$path" -nt "$BACKUP" ; then
status=0;
check_unchanged
save_backup
;;
opendiff)
touch "$BACKUP"
if base_present; then
opendiff "$LOCAL" "$REMOTE" -ancestor "$BASE" -merge "$path" | cat
else
while true; do
echo "$path seems unchanged."
echo -n "Was the merge successful? [y/n] "
read answer < /dev/tty
case "$answer" in
y*|Y*) status=0; break ;;
n*|N*) status=1; break ;;
esac
done
fi
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
opendiff "$LOCAL" "$REMOTE" -merge "$path" | cat
fi
check_unchanged
save_backup
;;
emerge)
if base_present ; then
@@ -244,9 +250,7 @@ merge_file () {
emacs -f emerge-files-command "$LOCAL" "$REMOTE" "$path"
fi
status=$?
if test "$status" -eq 0; then
mv -- "$BACKUP" "$path.orig"
fi
save_backup
;;
esac
if test "$status" -ne 0; then
@@ -289,7 +293,7 @@ done
if test -z "$merge_tool"; then
merge_tool=`git-config merge.tool`
case "$merge_tool" in
kdiff3 | tkdiff | xxdiff | meld | emerge | vimdiff)
kdiff3 | tkdiff | xxdiff | meld | opendiff | emerge | vimdiff | "")
;; # happy
*)
echo >&2 "git config option merge.tool set to unknown tool: $merge_tool"
@@ -308,6 +312,8 @@ if test -z "$merge_tool" ; then
merge_tool=xxdiff
elif type meld >/dev/null 2>&1 && test -n "$DISPLAY"; then
merge_tool=meld
elif type opendiff >/dev/null 2>&1; then
merge_tool=opendiff
elif type emacs >/dev/null 2>&1; then
merge_tool=emerge
elif type vimdiff >/dev/null 2>&1; then
@@ -319,7 +325,7 @@ if test -z "$merge_tool" ; then
fi
case "$merge_tool" in
kdiff3|tkdiff|meld|xxdiff|vimdiff)
kdiff3|tkdiff|meld|xxdiff|vimdiff|opendiff)
if ! type "$merge_tool" > /dev/null 2>&1; then
echo "The merge tool $merge_tool is not available"
exit 1
@@ -346,12 +352,12 @@ if test $# -eq 0 ; then
echo Merging the files: $files
git ls-files -u | sed -e 's/^[^ ]* //' | sort -u | while read i
do
echo ""
printf "\n"
merge_file "$i" < /dev/tty > /dev/tty
done
else
while test $# -gt 0; do
echo ""
printf "\n"
merge_file "$1"
shift
done

View File

@@ -115,7 +115,7 @@ for patch_name in $(cat "$QUILT_PATCHES/series" | grep -v '^#'); do
if [ -z "$dry_run" ] ; then
git-apply --index -C1 "$tmp_patch" &&
tree=$(git-write-tree) &&
commit=$((echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) &&
commit=$( (echo "$SUBJECT"; echo; cat "$tmp_msg") | git-commit-tree $tree -p $commit) &&
git-update-ref -m "quiltimport: $patch_name" HEAD $commit || exit 4
fi
done

View File

@@ -59,7 +59,7 @@ continue_merge () {
die "$RESOLVEMSG"
fi
if test -n "`git-diff-index HEAD`"
if ! git-diff-index --quiet HEAD
then
if ! git-commit -C "`cat $dotest/current`"
then
@@ -124,13 +124,11 @@ while case "$#" in 0) break ;; esac
do
case "$1" in
--continue)
diff=$(git-diff-files)
case "$diff" in
?*) echo "You must edit all merge conflicts and then"
git-diff-files --quiet || {
echo "You must edit all merge conflicts and then"
echo "mark them as resolved using git update-index"
exit 1
;;
esac
}
if test -d "$dotest"
then
prev_head="`cat $dotest/prev_head`"
@@ -265,6 +263,10 @@ upstream_name="$1"
upstream=`git rev-parse --verify "${upstream_name}^0"` ||
die "invalid upstream $upstream_name"
# Make sure the branch to rebase onto is valid.
onto_name=${newbase-"$upstream_name"}
onto=$(git-rev-parse --verify "${onto_name}^0") || exit
# If a hook exists, give it a chance to interrupt
if test -x "$GIT_DIR/hooks/pre-rebase"
then
@@ -291,10 +293,6 @@ case "$#" in
esac
branch=$(git-rev-parse --verify "${branch_name}^0") || exit
# Make sure the branch to rebase onto is valid.
onto_name=${newbase-"$upstream_name"}
onto=$(git-rev-parse --verify "${onto_name}^0") || exit
# Now we are rebasing commits $upstream..$branch on top of $onto
# Check if we are already based on $onto, but this should be

View File

@@ -15,6 +15,10 @@ sub add_remote_config {
$hash->{$name}{'FETCH'} ||= [];
push @{$hash->{$name}{'FETCH'}}, $value;
}
elsif ($what eq 'push') {
$hash->{$name}{'PUSH'} ||= [];
push @{$hash->{$name}{'PUSH'}}, $value;
}
if (!exists $hash->{$name}{'SOURCE'}) {
$hash->{$name}{'SOURCE'} = 'config';
}
@@ -44,7 +48,8 @@ sub add_remote_remotes {
}
}
elsif (/^Push:\s*(.*)$/) {
; # later
$it->{'PUSH'} ||= [];
push @{$it->{'PUSH'}}, $1;
}
elsif (/^Pull:\s*(.*)$/) {
$it->{'FETCH'} ||= [];
@@ -250,6 +255,15 @@ sub show_remote {
if ($info->{'LS_REMOTE'}) {
show_mapping($name, $info);
}
if ($info->{'PUSH'}) {
my @pushed = map {
s|^refs/heads/||;
s|:refs/heads/|:|;
$_;
} @{$info->{'PUSH'}};
print " Local branch(es) pushed with 'git push'\n";
print " @pushed\n";
}
}
sub add_remote {

View File

@@ -435,6 +435,9 @@ sub cmd_rebase {
}
my $gs = Git::SVN->find_by_url($url);
unless ($gs) {
die "Unable to determine remote information from URL: $url\n";
}
if (command(qw/diff-index HEAD --/)) {
print STDERR "Cannot rebase with uncommited changes:\n";
command_noisy('status');
@@ -1327,8 +1330,10 @@ sub rel_path {
my ($self) = @_;
my $repos_root = $self->ra->{repos_root};
return $self->{path} if ($self->{url} eq $repos_root);
die "BUG: rel_path failed! repos_root: $repos_root, Ra URL: ",
$self->ra->{url}, " path: $self->{path}, URL: $self->{url}\n";
my $url = $self->{url} .
(length $self->{path} ? "/$self->{path}" : $self->{path});
$url =~ s!^\Q$repos_root\E(?:/+|$)!!g;
$url;
}
sub traverse_ignore {
@@ -2838,8 +2843,7 @@ package Git::SVN::Ra;
use vars qw/@ISA $config_dir $_log_window_size/;
use strict;
use warnings;
my ($can_do_switch);
my $RA;
my ($can_do_switch, %ignored_err, $RA);
BEGIN {
# enforce temporary pool usage for some simple functions
@@ -3211,9 +3215,16 @@ sub skip_unknown_revs {
# 175007 - http(s):// (this repo required authorization, too...)
# More codes may be discovered later...
if ($errno == 175007 || $errno == 175002 || $errno == 160013) {
warn "W: Ignoring error from SVN, path probably ",
"does not exist: ($errno): ",
$err->expanded_message,"\n";
my $err_key = $err->expanded_message;
# revision numbers change every time, filter them out
$err_key =~ s/\d+/\0/g;
$err_key = "$errno\0$err_key";
unless ($ignored_err{$err_key}) {
warn "W: Ignoring error from SVN, path probably ",
"does not exist: ($errno): ",
$err->expanded_message,"\n";
$ignored_err{$err_key} = 1;
}
return;
}
die "Error from SVN, ($errno): ", $err->expanded_message,"\n";

2
git.c
View File

@@ -103,7 +103,7 @@ static int split_cmdline(char *cmdline, const char ***argv)
int src, dst, count = 0, size = 16;
char quoted = 0;
*argv = malloc(sizeof(char*) * size);
*argv = xmalloc(sizeof(char*) * size);
/* split alias_string */
(*argv)[count++] = cmdline;

6
gitk
View File

@@ -720,6 +720,7 @@ proc makewindow {} {
bindkey <Key-Return> {findnext 0}
bindkey ? findprev
bindkey f nextfile
bindkey <F5> updatecommits
bind . <Control-q> doquit
bind . <Control-f> dofind
bind . <Control-g> {findnext 0}
@@ -986,6 +987,7 @@ f Scroll diff view to next file
<Ctrl-plus> Increase font size
<Ctrl-KP-> Decrease font size
<Ctrl-minus> Decrease font size
<F5> Update
} \
-justify left -bg white -border 2 -relief sunken
pack $w.m -side top -fill both
@@ -1905,7 +1907,7 @@ proc do_file_hl {serial} {
} else {
set gdtargs [list "-S$highlight_files"]
}
set cmd [concat | git-diff-tree -r -s --stdin $gdtargs]
set cmd [concat | git diff-tree -r -s --stdin $gdtargs]
set filehighlight [open $cmd r+]
fconfigure $filehighlight -blocking 0
fileevent $filehighlight readable readfhighlight
@@ -1957,7 +1959,7 @@ proc readfhighlight {} {
}
if {[eof $filehighlight]} {
# strange...
puts "oops, git-diff-tree died"
puts "oops, git diff-tree died"
catch {close $filehighlight}
unset filehighlight
}

225
gitweb/INSTALL Normal file
View File

@@ -0,0 +1,225 @@
GIT web Interface (gitweb) Installation
=======================================
First you have to generate gitweb.cgi from gitweb.perl using
"make gitweb/gitweb.cgi", then copy appropriate files (gitweb.cgi,
gitweb.css, git-logo.png and git-favicon.png) to their destination.
For example if git was (or is) installed with /usr prefix, you can do
$ make prefix=/usr gitweb/gitweb.cgi ;# as yourself
# cp gitweb/git* /var/www/cgi-bin/ ;# as root
Alternatively you can use autoconf generated ./configure script to
set up path to git binaries (via config.mak.autogen), so you can write
instead
$ make configure ;# as yourself
$ ./configure --prefix=/usr ;# as yourself
$ make gitweb/gitweb.cgi ;# as yourself
# cp gitweb/git* /var/www/cgi-bin/ ;# as root
The above example assumes that your web server is configured to run
[executable] files in /var/www/cgi-bin/ as server scripts (as CGI
scripts).
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
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
when building gitweb.cgi, or $prefix from which $bindir is derived. If
you build and install gitweb together with the rest of 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
/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
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
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
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
build configuration variables. By default gitweb tries to find them
in the same directory as gitweb.cgi script.
Build example
~~~~~~~~~~~~~
- To install gitweb to /var/www/cgi-bin/gitweb/ when git wrapper
is installed at /usr/local/bin/git and the repositories (projects)
we want to display are under /home/local/scm, you can do
make GITWEB_PROJECTROOT="/home/local/scm" \
GITWEB_CSS="/gitweb/gitweb.css" \
GITWEB_LOGO="/gitweb/git-logo.png" \
GITWEB_FAVICON="/gitweb/git-favicon.png" \
bindir=/usr/local/bin \
gitweb/gitweb.cgi
cp -fv ~/git/gitweb/gitweb.{cgi,css} \
~/git/gitweb/git-{favicon,logo}.png \
/var/www/cgi-bin/gitweb/
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.
- Gitweb config file is [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.
See the top of gitweb.perl file for examples of customizable options.
Config file example
~~~~~~~~~~~~~~~~~~~
To enable blame, pickaxe search, and snapshot support, while allowing
individual projects to turn them off, put the following in your
GITWEB_CONFIG file:
$feature{'blame'}{'default'} = [1];
$feature{'blame'}{'override'} = 1;
$feature{'pickaxe'}{'default'} = [1];
$feature{'pickaxe'}{'override'} = 1;
$feature{'snapshot'}{'default'} = ['x-gzip', 'gz', 'gzip'];
$feature{'snapshot'}{'override'} = 1;
Gitweb repositories
-------------------
- By default all git repositories under projectroot are visible and
available to gitweb. 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,
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.
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 '+'.
You can generate projects list index file using 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
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
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).
Generating projects list using gitweb
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
We assume that GITWEB_CONFIG has its default Makefile value, namely
gitweb_config.perl. Put the following in gitweb_make_index.perl file:
$GITWEB_CONFIG = "gitweb_config.perl";
do $GITWEB_CONFIG if -e $GITWEB_CONFIG;
$projects_list = $projectroot;
Then create the following script to get list of project in the format
suitable for GITWEB_LIST build configuration variable (or
$projects_list variable in gitweb config):
#!/bin/sh
export GITWEB_CONFIG="gitweb_make_index.perl"
export GATEWAY_INTERFACE="CGI/1.1"
export HTTP_ACCEPT="*/*"
export REQUEST_METHOD="GET"
export QUERY_STRING="a=project_index"
perl -- /var/www/cgi-bin/gitweb.cgi
Requirements
------------
- Core git tools
- Perl
- Perl modules: CGI, Encode, Fcntl, File::Find, File::Basename.
- web server
Example web server configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See also "Webserver configuration" section in README file for gitweb
(in gitweb/README).
- Apache2, gitweb installed as CGI script,
under /var/www/cgi-bin/
ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
<Directory "/var/www/cgi-bin">
Options Indexes FollowSymlinks ExecCGI
AllowOverride None
Order allow,deny
Allow from all
</Directory>
- Apache2, gitweb installed as mod_perl legacy script,
under /var/www/perl/
Alias /perl "/var/www/perl"
<Directory "/var/www/perl">
SetHandler perl-script
PerlResponseHandler ModPerl::Registry
PerlOptions +ParseHeaders
Options Indexes FollowSymlinks +ExecCGI
AllowOverride None
Order allow,deny
Allow from all
</Directory>

View File

@@ -107,7 +107,7 @@ span.age {
font-style: italic;
}
div.page_body span.signoff {
span.signoff {
color: #888888;
}

View File

@@ -3154,7 +3154,7 @@ sub git_blame2 {
}
$ftype = git_get_type($hash);
if ($ftype !~ "blob") {
die_error("400 Bad Request", "Object is not a blob");
die_error('400 Bad Request', "Object is not a blob");
}
open ($fd, "-|", git_cmd(), "blame", '-p', '--',
$file_name, $hash_base)
@@ -3220,7 +3220,7 @@ HTML
print "</td>\n";
}
open (my $dd, "-|", git_cmd(), "rev-parse", "$full_rev^")
or die_error("could not open git-rev-parse");
or die_error(undef, "Open git-rev-parse failed");
my $parent_commit = <$dd>;
close $dd;
chomp($parent_commit);
@@ -3622,7 +3622,7 @@ sub git_snapshot {
$name =~ s/\047/\047\\\047\047/g;
open my $fd, "-|",
"$git archive --format=tar --prefix=\'$name\'/ $hash | $command"
or die_error(undef, "Execute git-tar-tree failed.");
or die_error(undef, "Execute git-tar-tree failed");
binmode STDOUT, ':raw';
print <$fd>;
binmode STDOUT, ':utf8'; # as set at the beginning of gitweb.cgi
@@ -3719,7 +3719,7 @@ sub git_commit {
$formats_nav .=
'(merge: ' .
join(' ', map {
$cgi->a({-href => href(action=>"commitdiff",
$cgi->a({-href => href(action=>"commit",
hash=>$_)},
esc_html(substr($_, 0, 7)));
} @$parents ) .
@@ -3885,7 +3885,7 @@ sub git_blobdiff {
# read raw output
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
$hash_parent_base, $hash_base,
"--", $file_name
"--", (defined $file_parent ? $file_parent : ()), $file_name
or die_error(undef, "Open git-diff-tree failed");
@difftree = map { chomp; $_ } <$fd>;
close $fd
@@ -3935,7 +3935,7 @@ sub git_blobdiff {
# open patch output
open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
'-p', $hash_parent_base, $hash_base,
"--", $file_name
"--", (defined $file_parent ? $file_parent : ()), $file_name
or die_error(undef, "Open git-diff-tree failed");
}

14
help.c
View File

@@ -31,12 +31,6 @@ static int term_columns(void)
return 80;
}
static void oom(void)
{
fprintf(stderr, "git: out of memory\n");
exit(1);
}
static inline void mput_char(char c, unsigned int num)
{
while(num--)
@@ -54,13 +48,9 @@ static void add_cmdname(const char *name, int len)
struct cmdname *ent;
if (cmdname_alloc <= cmdname_cnt) {
cmdname_alloc = cmdname_alloc + 200;
cmdname = realloc(cmdname, cmdname_alloc * sizeof(*cmdname));
if (!cmdname)
oom();
cmdname = xrealloc(cmdname, cmdname_alloc * sizeof(*cmdname));
}
ent = malloc(sizeof(*ent) + len);
if (!ent)
oom();
ent = xmalloc(sizeof(*ent) + len);
ent->len = len;
memcpy(ent->name, name, len);
ent->name[len] = 0;

View File

@@ -16,8 +16,7 @@ static struct curl_slist *no_pragma_header;
struct alt_base
{
const char *base;
int path_len;
char *base;
int got_indices;
struct packed_git *packs;
struct alt_base *next;
@@ -158,12 +157,12 @@ static void start_object_request(struct object_request *obj_req)
SHA1_Init(&obj_req->c);
url = xmalloc(strlen(obj_req->repo->base) + 50);
obj_req->url = xmalloc(strlen(obj_req->repo->base) + 50);
url = xmalloc(strlen(obj_req->repo->base) + 51);
obj_req->url = xmalloc(strlen(obj_req->repo->base) + 51);
strcpy(url, obj_req->repo->base);
posn = url + strlen(obj_req->repo->base);
strcpy(posn, "objects/");
posn += 8;
strcpy(posn, "/objects/");
posn += 9;
memcpy(posn, hex, 2);
posn += 2;
*(posn++) = '/';
@@ -515,7 +514,6 @@ static void process_alternates_response(void *callback_data)
int serverlen = 0;
struct alt_base *newalt;
char *target = NULL;
char *path;
if (data[i] == '/') {
/* This counts
* http://git.host/pub/scm/linux.git/
@@ -583,12 +581,6 @@ static void process_alternates_response(void *callback_data)
newalt->base = target;
newalt->got_indices = 0;
newalt->packs = NULL;
path = strstr(target, "//");
if (path) {
path = strchr(path+2, '/');
if (path)
newalt->path_len = strlen(path);
}
while (tail->next != NULL)
tail = tail->next;
@@ -938,14 +930,14 @@ static char *quote_ref_url(const char *base, const char *ref)
int len, baselen, ch;
baselen = strlen(base);
len = baselen + 6; /* "refs/" + NUL */
len = baselen + 7; /* "/refs/" + NUL */
for (cp = ref; (ch = *cp) != 0; cp++, len++)
if (needs_quote(ch))
len += 2; /* extra two hex plus replacement % */
qref = xmalloc(len);
memcpy(qref, base, baselen);
memcpy(qref + baselen, "refs/", 5);
for (cp = ref, dp = qref + baselen + 5; (ch = *cp) != 0; cp++) {
memcpy(qref + baselen, "/refs/", 6);
for (cp = ref, dp = qref + baselen + 6; (ch = *cp) != 0; cp++) {
if (needs_quote(ch)) {
*dp++ = '%';
*dp++ = hex((ch >> 4) & 0xF);
@@ -999,7 +991,7 @@ int main(int argc, const char **argv)
const char **write_ref = NULL;
char **commit_id;
const char *url;
char *path;
char *s;
int arg = 1;
int rc = 0;
@@ -1044,16 +1036,13 @@ int main(int argc, const char **argv)
no_pragma_header = curl_slist_append(no_pragma_header, "Pragma:");
alt = xmalloc(sizeof(*alt));
alt->base = url;
alt->base = xmalloc(strlen(url) + 1);
strcpy(alt->base, url);
for (s = alt->base + strlen(alt->base) - 1; *s == '/'; --s)
*s = 0;
alt->got_indices = 0;
alt->packs = NULL;
alt->next = NULL;
path = strstr(url, "//");
if (path) {
path = strchr(path+2, '/');
if (path)
alt->path_len = strlen(path);
}
if (pull(commits, commit_id, write_ref, url))
rc = 1;

View File

@@ -1750,8 +1750,7 @@ static struct object_list **process_tree(struct tree *tree,
me.elem = name;
me.elem_len = strlen(name);
desc.buf = tree->buffer;
desc.size = tree->size;
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))

View File

@@ -139,7 +139,7 @@ static const char *open_pack_file(const char *pack_name)
if (!pack_name) {
static char tmpfile[PATH_MAX];
snprintf(tmpfile, sizeof(tmpfile),
"%s/pack_XXXXXX", get_object_directory());
"%s/tmp_pack_XXXXXX", get_object_directory());
output_fd = mkstemp(tmpfile);
pack_name = xstrdup(tmpfile);
} else
@@ -347,26 +347,19 @@ static int find_delta_children(const union delta_base *base,
static void sha1_object(const void *data, unsigned long size,
enum object_type type, unsigned char *sha1)
{
SHA_CTX ctx;
char header[50];
int header_size;
const char *type_str;
switch (type) {
case OBJ_COMMIT: type_str = commit_type; break;
case OBJ_TREE: type_str = tree_type; break;
case OBJ_BLOB: type_str = blob_type; break;
case OBJ_TAG: type_str = tag_type; break;
default:
die("bad type %d", type);
hash_sha1_file(data, size, typename(type), sha1);
if (has_sha1_file(sha1)) {
void *has_data;
enum object_type has_type;
unsigned long has_size;
has_data = read_sha1_file(sha1, &has_type, &has_size);
if (!has_data)
die("cannot read existing object %s", sha1_to_hex(sha1));
if (size != has_size || type != has_type ||
memcmp(data, has_data, size) != 0)
die("SHA1 COLLISION FOUND WITH %s !", sha1_to_hex(sha1));
free(has_data);
}
header_size = sprintf(header, "%s %lu", type_str, size) + 1;
SHA1_Init(&ctx);
SHA1_Update(&ctx, header, header_size);
SHA1_Update(&ctx, data, size);
SHA1_Final(sha1, &ctx);
}
static void resolve_delta(struct object_entry *delta_obj, void *base_data,
@@ -547,7 +540,7 @@ static int write_compressed(int fd, void *in, unsigned int size)
return size;
}
static void append_obj_to_pack(void *buf,
static void append_obj_to_pack(const unsigned char *sha1, void *buf,
unsigned long size, enum object_type type)
{
struct object_entry *obj = &objects[nr_objects++];
@@ -565,7 +558,7 @@ static void append_obj_to_pack(void *buf,
write_or_die(output_fd, header, n);
obj[1].offset = obj[0].offset + n;
obj[1].offset += write_compressed(output_fd, buf, size);
sha1_object(buf, size, type, obj->sha1);
hashcpy(obj->sha1, sha1);
}
static int delta_pos_compare(const void *_a, const void *_b)
@@ -618,7 +611,9 @@ static void fix_unresolved_deltas(int nr_unresolved)
resolve_delta(child, data, size, type);
}
append_obj_to_pack(data, size, type);
if (check_sha1_signature(d->base.sha1, data, size, typename(type)))
die("local object %s is corrupt", sha1_to_hex(d->base.sha1));
append_obj_to_pack(d->base.sha1, data, size, type);
free(data);
if (verbose)
percent = display_progress(nr_resolved_deltas,
@@ -696,7 +691,7 @@ static const char *write_index_file(const char *index_name, unsigned char *sha1)
if (!index_name) {
static char tmpfile[PATH_MAX];
snprintf(tmpfile, sizeof(tmpfile),
"%s/index_XXXXXX", get_object_directory());
"%s/tmp_idx_XXXXXX", get_object_directory());
fd = mkstemp(tmpfile);
index_name = xstrdup(tmpfile);
} else {

View File

@@ -49,8 +49,7 @@ static void process_tree(struct rev_info *revs,
me.elem = name;
me.elem_len = strlen(name);
desc.buf = tree->buffer;
desc.size = tree->size;
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))

View File

@@ -278,8 +278,16 @@ static struct tree *git_write_tree(void)
{
struct tree *result = NULL;
if (unmerged_index())
if (unmerged_index()) {
int i;
output(0, "There are unmerged index entries:");
for (i = 0; i < active_nr; i++) {
struct cache_entry *ce = active_cache[i];
if (ce_stage(ce))
output(0, "%d %.*s", ce_stage(ce), ce_namelen(ce), ce->name);
}
return NULL;
}
if (!active_cache_tree)
active_cache_tree = cache_tree();
@@ -735,8 +743,19 @@ static void conflict_rename_rename(struct rename *ren1,
ren2_dst, branch1, dst_name2);
remove_file(0, ren2_dst, 0);
}
update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
if (index_only) {
remove_file_from_cache(dst_name1);
remove_file_from_cache(dst_name2);
/*
* Uncomment to leave the conflicting names in the resulting tree
*
* update_file(0, ren1->pair->two->sha1, ren1->pair->two->mode, dst_name1);
* update_file(0, ren2->pair->two->sha1, ren2->pair->two->mode, dst_name2);
*/
} else {
update_stages(dst_name1, NULL, ren1->pair->two, NULL, 1);
update_stages(dst_name2, NULL, NULL, ren2->pair->two, 1);
}
while (delp--)
free(del[delp]);
}
@@ -852,10 +871,16 @@ static int process_renames(struct path_list *a_renames,
if (strcmp(ren1_dst, ren2_dst) != 0) {
clean_merge = 0;
output(1, "CONFLICT (rename/rename): "
"Rename %s->%s in branch %s "
"rename %s->%s in %s",
"Rename \"%s\"->\"%s\" in branch \"%s\" "
"rename \"%s\"->\"%s\" in \"%s\"%s",
src, ren1_dst, branch1,
src, ren2_dst, branch2);
src, ren2_dst, branch2,
index_only ? " (left unresolved)": "");
if (index_only) {
remove_file_from_cache(src);
update_file(0, ren1->pair->one->sha1,
ren1->pair->one->mode, src);
}
conflict_rename_rename(ren1, branch1, ren2, branch2);
} else {
struct merge_file_info mfi;

View File

@@ -188,7 +188,7 @@ static void resolve(const char *base, struct name_entry *branch1, struct name_en
static int unresolved_directory(const char *base, struct name_entry n[3])
{
int baselen;
int baselen, pathlen;
char *newbase;
struct name_entry *p;
struct tree_desc t[3];
@@ -205,10 +205,11 @@ static int unresolved_directory(const char *base, struct name_entry n[3])
if (!S_ISDIR(p->mode))
return 0;
baselen = strlen(base);
newbase = xmalloc(baselen + p->pathlen + 2);
pathlen = tree_entry_len(p->path, p->sha1);
newbase = xmalloc(baselen + pathlen + 2);
memcpy(newbase, base, baselen);
memcpy(newbase + baselen, p->path, p->pathlen);
memcpy(newbase + baselen + p->pathlen, "/", 2);
memcpy(newbase + baselen, p->path, pathlen);
memcpy(newbase + baselen + pathlen, "/", 2);
buf0 = fill_tree_descriptor(t+0, n[0].sha1);
buf1 = fill_tree_descriptor(t+1, n[1].sha1);

View File

@@ -184,8 +184,10 @@ struct object *parse_object(const unsigned char *sha1)
if (buffer) {
struct object *obj;
if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0)
printf("sha1 mismatch %s\n", sha1_to_hex(sha1));
if (check_sha1_signature(sha1, buffer, size, typename(type)) < 0) {
error("sha1 mismatch %s\n", sha1_to_hex(sha1));
return NULL;
}
obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
if (!eaten)

View File

@@ -42,8 +42,7 @@ static void process_tree(struct tree *tree,
me.elem = name;
me.elem_len = strlen(name);
desc.buf = tree->buffer;
desc.size = tree->size;
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))

21
refs.c
View File

@@ -998,6 +998,27 @@ int write_ref_sha1(struct ref_lock *lock,
unlock_ref(lock);
return -1;
}
if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
/*
* Special hack: If a branch is updated directly and HEAD
* points to it (may happen on the remote side of a push
* for example) then logically the HEAD reflog should be
* updated too.
* A generic solution implies reverse symref information,
* but finding all symrefs pointing to the given branch
* would be rather costly for this rare event (the direct
* update of a branch) to be worth it. So let's cheat and
* check with HEAD only which should cover 99% of all usage
* scenarios (even 100% of the default ones).
*/
unsigned char head_sha1[20];
int head_flag;
const char *head_ref;
head_ref = resolve_ref("HEAD", head_sha1, 1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
}
if (commit_lock_file(lock->lk)) {
error("Couldn't set %s", lock->ref_name);
unlock_ref(lock);

View File

@@ -62,8 +62,7 @@ void mark_tree_uninteresting(struct tree *tree)
if (parse_tree(tree) < 0)
die("bad tree %s", sha1_to_hex(obj->sha1));
desc.buf = tree->buffer;
desc.size = tree->size;
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (S_ISDIR(entry.mode))
mark_tree_uninteresting(lookup_tree(entry.sha1));
@@ -275,18 +274,17 @@ int rev_same_tree_as_empty(struct rev_info *revs, struct tree *t1)
{
int retval;
void *tree;
unsigned long size;
struct tree_desc empty, real;
if (!t1)
return 0;
tree = read_object_with_reference(t1->object.sha1, tree_type, &real.size, NULL);
tree = read_object_with_reference(t1->object.sha1, tree_type, &size, NULL);
if (!tree)
return 0;
real.buf = tree;
empty.buf = "";
empty.size = 0;
init_tree_desc(&real, tree, size);
init_tree_desc(&empty, "", 0);
tree_difference = REV_TREE_SAME;
revs->pruning.has_changes = 0;
@@ -362,6 +360,7 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
{
struct commit_list *parent = commit->parents;
unsigned left_flag;
int add, rest;
if (commit->object.flags & ADDED)
return;
@@ -407,18 +406,19 @@ static void add_parents_to_list(struct rev_info *revs, struct commit *commit, st
return;
left_flag = (commit->object.flags & SYMMETRIC_LEFT);
parent = commit->parents;
while (parent) {
rest = !revs->first_parent_only;
for (parent = commit->parents, add = 1; parent; add = rest) {
struct commit *p = parent->item;
parent = parent->next;
parse_commit(p);
p->object.flags |= left_flag;
if (p->object.flags & SEEN)
continue;
p->object.flags |= SEEN;
insert_by_date(p, list);
if (add)
insert_by_date(p, list);
}
}
@@ -486,7 +486,7 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
add_pending_object(cb->all_revs, o, "");
}
else if (!cb->warned_bad_reflog) {
warn("reflog of '%s' references pruned commits",
warning("reflog of '%s' references pruned commits",
cb->name_for_errormsg);
cb->warned_bad_reflog = 1;
}
@@ -849,6 +849,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
handle_all(revs, flags);
continue;
}
if (!strcmp(arg, "--first-parent")) {
revs->first_parent_only = 1;
continue;
}
if (!strcmp(arg, "--reflog")) {
handle_reflog(revs, flags);
continue;
@@ -1038,7 +1042,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
if (!prefixcmp(arg, "--encoding=")) {
arg += 11;
if (strcmp(arg, "none"))
git_log_output_encoding = strdup(arg);
git_log_output_encoding = xstrdup(arg);
else
git_log_output_encoding = "";
continue;

View File

@@ -46,7 +46,8 @@ struct rev_info {
boundary:2,
left_right:1,
parents:1,
reverse:1;
reverse:1,
first_parent_only:1;
/* Diff flags */
unsigned int diff:1,

View File

@@ -1040,14 +1040,27 @@ static void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size
n = size;
memcpy(buf, (char *) buffer + bytes, n);
bytes = n;
if (bytes < size) {
if (bytes <= size) {
/*
* The above condition must be (bytes <= size), not
* (bytes < size). In other words, even though we
* expect no more output and set avail_out to zer0,
* the input zlib stream may have bytes that express
* "this concludes the stream", and we *do* want to
* eat that input.
*
* Otherwise we would not be able to test that we
* consumed all the input to reach the expected size;
* we also want to check that zlib tells us that all
* went well with status == Z_STREAM_END at the end.
*/
stream->next_out = buf + bytes;
stream->avail_out = size - bytes;
while (status == Z_OK)
status = inflate(stream, Z_FINISH);
}
buf[size] = 0;
if ((status == Z_OK || status == Z_STREAM_END) && !stream->avail_in) {
if (status == Z_STREAM_END && !stream->avail_in) {
inflateEnd(stream);
return buf;
}
@@ -1365,11 +1378,18 @@ static void *unpack_compressed_entry(struct packed_git *p,
#define MAX_DELTA_CACHE (256)
static size_t delta_base_cached;
static struct delta_base_cache_lru_list {
struct delta_base_cache_lru_list *prev;
struct delta_base_cache_lru_list *next;
} delta_base_cache_lru = { &delta_base_cache_lru, &delta_base_cache_lru };
static struct delta_base_cache_entry {
struct delta_base_cache_lru_list lru;
void *data;
struct packed_git *p;
off_t base_offset;
unsigned long size;
void *data;
enum object_type type;
} delta_base_cache[MAX_DELTA_CACHE];
@@ -1379,7 +1399,7 @@ static unsigned long pack_entry_hash(struct packed_git *p, off_t base_offset)
hash = (unsigned long)p + (unsigned long)base_offset;
hash += (hash >> 8) + (hash >> 16);
return hash & 0xff;
return hash % MAX_DELTA_CACHE;
}
static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
@@ -1397,6 +1417,8 @@ static void *cache_or_unpack_entry(struct packed_git *p, off_t base_offset,
found_cache_entry:
if (!keep_cache) {
ent->data = NULL;
ent->lru.next->prev = ent->lru.prev;
ent->lru.prev->next = ent->lru.next;
delta_base_cached -= ent->size;
}
else {
@@ -1414,6 +1436,8 @@ static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
if (ent->data) {
free(ent->data);
ent->data = NULL;
ent->lru.next->prev = ent->lru.prev;
ent->lru.prev->next = ent->lru.next;
delta_base_cached -= ent->size;
}
}
@@ -1421,26 +1445,38 @@ static inline void release_delta_base_cache(struct delta_base_cache_entry *ent)
static void add_delta_base_cache(struct packed_git *p, off_t base_offset,
void *base, unsigned long base_size, enum object_type type)
{
unsigned long i, hash = pack_entry_hash(p, base_offset);
unsigned long hash = pack_entry_hash(p, base_offset);
struct delta_base_cache_entry *ent = delta_base_cache + hash;
struct delta_base_cache_lru_list *lru;
release_delta_base_cache(ent);
delta_base_cached += base_size;
for (i = 0; delta_base_cached > delta_base_cache_limit
&& i < ARRAY_SIZE(delta_base_cache); i++) {
struct delta_base_cache_entry *f = delta_base_cache + i;
for (lru = delta_base_cache_lru.next;
delta_base_cached > delta_base_cache_limit
&& lru != &delta_base_cache_lru;
lru = lru->next) {
struct delta_base_cache_entry *f = (void *)lru;
if (f->type == OBJ_BLOB)
release_delta_base_cache(f);
}
for (i = 0; delta_base_cached > delta_base_cache_limit
&& i < ARRAY_SIZE(delta_base_cache); i++)
release_delta_base_cache(delta_base_cache + i);
for (lru = delta_base_cache_lru.next;
delta_base_cached > delta_base_cache_limit
&& lru != &delta_base_cache_lru;
lru = lru->next) {
struct delta_base_cache_entry *f = (void *)lru;
release_delta_base_cache(f);
}
ent->p = p;
ent->base_offset = base_offset;
ent->type = type;
ent->data = base;
ent->size = base_size;
ent->lru.next = &delta_base_cache_lru;
ent->lru.prev = delta_base_cache_lru.prev;
delta_base_cache_lru.prev->next = &ent->lru;
delta_base_cache_lru.prev = &ent->lru;
}
static void *unpack_delta_entry(struct packed_git *p,
@@ -1782,7 +1818,7 @@ void *read_object_with_reference(const unsigned char *sha1,
}
}
static void write_sha1_file_prepare(void *buf, unsigned long len,
static void write_sha1_file_prepare(const void *buf, unsigned long len,
const char *type, unsigned char *sha1,
char *hdr, int *hdrlen)
{
@@ -1910,7 +1946,7 @@ static void setup_object_header(z_stream *stream, const char *type, unsigned lon
stream->avail_out -= hdrlen;
}
int hash_sha1_file(void *buf, unsigned long len, const char *type,
int hash_sha1_file(const void *buf, unsigned long len, const char *type,
unsigned char *sha1)
{
char hdr[32];
@@ -1921,7 +1957,7 @@ int hash_sha1_file(void *buf, unsigned long len, const char *type,
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
{
int size;
int size, ret;
unsigned char *compressed;
z_stream stream;
unsigned char sha1[20];
@@ -1953,7 +1989,7 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
return error("sha1 file %s: %s\n", filename, strerror(errno));
}
snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
fd = mkstemp(tmpfile);
if (fd < 0) {
@@ -1981,15 +2017,21 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
/* Then the data itself.. */
stream.next_in = buf;
stream.avail_in = len;
while (deflate(&stream, Z_FINISH) == Z_OK)
/* nothing */;
deflateEnd(&stream);
ret = deflate(&stream, Z_FINISH);
if (ret != Z_STREAM_END)
die("unable to deflate new object %s (%d)", sha1_to_hex(sha1), ret);
ret = deflateEnd(&stream);
if (ret != Z_OK)
die("deflateEnd on object %s failed (%d)", sha1_to_hex(sha1), ret);
size = stream.total_out;
if (write_buffer(fd, compressed, size) < 0)
die("unable to write sha1 file");
fchmod(fd, 0444);
close(fd);
if (close(fd))
die("unable to write sha1 file");
free(compressed);
return move_temp_to_file(tmpfile, filename);
@@ -2074,7 +2116,7 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
int ret;
SHA_CTX c;
snprintf(tmpfile, sizeof(tmpfile), "%s/obj_XXXXXX", get_object_directory());
snprintf(tmpfile, sizeof(tmpfile), "%s/tmp_obj_XXXXXX", get_object_directory());
local = mkstemp(tmpfile);
if (local < 0) {
@@ -2123,7 +2165,9 @@ int write_sha1_from_fd(const unsigned char *sha1, int fd, char *buffer,
} while (1);
inflateEnd(&stream);
close(local);
fchmod(local, 0444);
if (close(local) != 0)
die("unable to write sha1 file");
SHA1_Final(real_sha1, &c);
if (ret != Z_STREAM_END) {
unlink(tmpfile);

View File

@@ -23,7 +23,8 @@ test_expect_success setup '
cat file2 >file2.orig
git add file1 file2 &&
sed -e "/^B/d" <file1.orig >file1 &&
sed -e "/^B/d" <file2.orig >file2 &&
sed -e "/^[BQ]/d" <file2.orig >file2 &&
echo Q | tr -d "\\012" >>file2 &&
cat file1 >file1.mods &&
cat file2 >file2.mods &&
git diff |

View File

@@ -34,7 +34,8 @@ EOF
git commit -q -a -m first
git checkout -b second master
git show first:a1 | sed 's/To die, t/To die! T/' > a1
git show first:a1 |
sed -e 's/To die, t/To die! T/' -e 's/life;$/life./' > a1
git commit -q -a -m second
# activate rerere
@@ -42,19 +43,26 @@ mkdir .git/rr-cache
test_expect_failure 'conflicting merge' 'git pull . first'
sha1=4f58849a60b4f969a2848966b6d02893b783e8fb
sha1=$(sed -e 's/\t.*//' .git/rr-cache/MERGE_RR)
rr=.git/rr-cache/$sha1
test_expect_success 'recorded preimage' "grep ======= $rr/preimage"
test_expect_success 'no postimage or thisimage yet' \
"test ! -f $rr/postimage -a ! -f $rr/thisimage"
test_expect_success 'preimage have right number of lines' '
cnt=$(sed -ne "/^<<<<<<</,/^>>>>>>>/p" $rr/preimage | wc -l) &&
test "$cnt" = 10
'
git show first:a1 > a1
cat > expect << EOF
--- a/a1
+++ b/a1
@@ -6,11 +6,7 @@
@@ -6,17 +6,9 @@
The heart-ache and the thousand natural shocks
That flesh is heir to, 'tis a consummation
Devoutly to be wish'd.
@@ -66,8 +74,13 @@ cat > expect << EOF
To sleep: perchance to dream: ay, there's the rub;
For in that sleep of death what dreams may come
When we have shuffled off this mortal coil,
Must give us pause: there's the respect
-<<<<<<<
-That makes calamity of so long life.
-=======
That makes calamity of so long life;
->>>>>>>
EOF
git rerere diff > out
test_expect_success 'rerere diff' 'git diff expect out'

View File

@@ -61,7 +61,7 @@ diff --git a/git-cvsimport-script b/git-cvsimport-script
push(@old,$fn);
--
David Kågedal
David K<EFBFBD>gedal
-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org

View File

@@ -275,4 +275,14 @@ test_expect_success \
:'
test_expect_success \
'fake a SHA1 hash collision' \
'test -f .git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67 &&
cp -f .git/objects/9d/235ed07cd19811a6ceb342de82f190e49c9f68 \
.git/objects/c8/2de19312b6c3695c0c18f70709a6c535682a67'
test_expect_failure \
'make sure index-pack detects the SHA1 collision' \
'git-index-pack -o bad.idx test-3.pack'
test_done

150
t/t6006-rev-list-format.sh Executable file
View File

@@ -0,0 +1,150 @@
#!/bin/sh
test_description='git-rev-list --pretty=format test'
. ./test-lib.sh
test_tick
test_expect_success 'setup' '
touch foo && git-add foo && git-commit -m "added foo" &&
echo changed >foo && git-commit -a -m "changed foo"
'
# usage: test_format name format_string <expected_output
test_format() {
cat >expect.$1
test_expect_success "format $1" "
git-rev-list --pretty=format:$2 master >output.$1 &&
git-diff expect.$1 output.$1
"
}
test_format hash %H%n%h <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
131a310eb913d107dd3c09a65d1651175898735d
131a310
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
86c75cfd708a0e5868dc876ed5b8bb66c80b4873
86c75cf
EOF
test_format tree %T%n%t <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
fe722612f26da5064c32ca3843aa154bdb0b08a0
fe72261
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
4d5fcadc293a348e88f777dc0920f11e7d71441c
4d5fcad
EOF
test_format parents %P%n%p <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
86c75cfd708a0e5868dc876ed5b8bb66c80b4873
86c75cf
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
EOF
# we don't test relative here
test_format author %an%n%ae%n%ad%n%aD%n%at <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
A U Thor
author@example.com
Thu Apr 7 15:13:13 2005 -0700
Thu, 7 Apr 2005 15:13:13 -0700
1112911993
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
A U Thor
author@example.com
Thu Apr 7 15:13:13 2005 -0700
Thu, 7 Apr 2005 15:13:13 -0700
1112911993
EOF
test_format committer %cn%n%ce%n%cd%n%cD%n%ct <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
C O Mitter
committer@example.com
Thu Apr 7 15:13:13 2005 -0700
Thu, 7 Apr 2005 15:13:13 -0700
1112911993
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
C O Mitter
committer@example.com
Thu Apr 7 15:13:13 2005 -0700
Thu, 7 Apr 2005 15:13:13 -0700
1112911993
EOF
test_format encoding %e <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
<unknown>
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
<unknown>
EOF
test_format subject %s <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
changed foo
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
added foo
EOF
test_format body %b <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
<unknown>
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
<unknown>
EOF
test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF'
commit 131a310eb913d107dd3c09a65d1651175898735d
foobarbazxyzzy
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
foobarbazxyzzy
EOF
cat >commit-msg <<'EOF'
Test printing of complex bodies
This commit message is much longer than the others,
and it will be encoded in iso8859-1. We should therefore
include an iso8859 character: ¡bueno!
EOF
test_expect_success 'setup complex body' '
git-config i18n.commitencoding iso8859-1 &&
echo change2 >foo && git-commit -a -F commit-msg
'
test_format complex-encoding %e <<'EOF'
commit f58db70b055c5718631e5c61528b28b12090cdea
iso8859-1
commit 131a310eb913d107dd3c09a65d1651175898735d
<unknown>
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
<unknown>
EOF
test_format complex-subject %s <<'EOF'
commit f58db70b055c5718631e5c61528b28b12090cdea
Test printing of complex bodies
commit 131a310eb913d107dd3c09a65d1651175898735d
changed foo
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
added foo
EOF
test_format complex-body %b <<'EOF'
commit f58db70b055c5718631e5c61528b28b12090cdea
This commit message is much longer than the others,
and it will be encoded in iso8859-1. We should therefore
include an iso8859 character: ¡bueno!
commit 131a310eb913d107dd3c09a65d1651175898735d
<unknown>
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
<unknown>
EOF
test_done

57
t/t6030-bisect-run.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/bin/sh
#
# Copyright (c) 2007 Christian Couder
#
test_description='Tests git-bisect run functionality'
. ./test-lib.sh
add_line_into_file()
{
_line=$1
_file=$2
if [ -f "$_file" ]; then
echo "$_line" >> $_file || return $?
MSG="Add <$_line> into <$_file>."
else
echo "$_line" > $_file || return $?
git add $_file || return $?
MSG="Create file <$_file> with <$_line> inside."
fi
git-commit -m "$MSG" $_file
}
HASH1=
HASH3=
HASH4=
test_expect_success \
'set up basic repo with 1 file (hello) and 4 commits' \
'add_line_into_file "1: Hello World" hello &&
add_line_into_file "2: A new day for git" hello &&
add_line_into_file "3: Another new day for git" hello &&
add_line_into_file "4: Ciao for now" hello &&
HASH1=$(git rev-list HEAD | tail -1) &&
HASH3=$(git rev-list HEAD | head -2 | tail -1) &&
HASH4=$(git rev-list HEAD | head -1)'
# We want to automatically find the commit that
# introduced "Another" into hello.
test_expect_success \
'git bisect run simple case' \
'echo "#!/bin/sh" > test_script.sh &&
echo "grep Another hello > /dev/null" >> test_script.sh &&
echo "test \$? -ne 0" >> test_script.sh &&
chmod +x test_script.sh &&
git bisect start &&
git bisect good $HASH1 &&
git bisect bad $HASH4 &&
git bisect run ./test_script.sh > my_bisect_log.txt &&
grep "$HASH3 is first bad commit" my_bisect_log.txt'
#
#
test_done

View File

@@ -0,0 +1,17 @@
#!/bin/sh
#
# An example hook script for the post-receive event
#
# This script is run after receive-pack has accepted a pack and the
# repository has been updated. It is passed arguments in through stdin
# in the form
# <oldrev> <newrev> <refname>
# For example:
# aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
#
# see contrib/hooks/ for an sample, or uncomment the next line (on debian)
#
#. /usr/share/doc/git-core/contrib/hooks/post-receive-email

View File

@@ -1,36 +1,16 @@
#!/bin/sh
#
# An example hook script to mail out commit update information.
# It can also blocks tags that aren't annotated.
# An example hook script to blocks unannotated tags from entering.
# Called by git-receive-pack with arguments: refname sha1-old sha1-new
#
# To enable this hook, make this file executable by "chmod +x update".
#
# Config
# ------
# hooks.mailinglist
# This is the list that all pushes will go to; leave it blank to not send
# emails frequently. The log email will list every log entry in full between
# the old ref value and the new ref value.
# hooks.announcelist
# This is the list that all pushes of annotated tags will go to. Leave it
# blank to just use the mailinglist field. The announce emails list the
# short log summary of the changes since the last annotated tag
# hooks.allowunannotated
# This boolean sets whether unannotated tags will be allowed into the
# repository. By default they won't be.
#
# Notes
# -----
# All emails have their subjects prefixed with "[SCM]" to aid filtering.
# All emails include the headers "X-Git-Refname", "X-Git-Oldrev",
# "X-Git-Newrev", and "X-Git-Reftype" to enable fine tuned filtering and info.
# --- Constants
EMAILPREFIX="[SCM] "
LOGBEGIN="- Log -----------------------------------------------------------------"
LOGEND="-----------------------------------------------------------------------"
DATEFORMAT="%F %R %z"
# --- Command line
refname="$1"
@@ -51,235 +31,42 @@ if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
fi
# --- Config
projectdesc=$(cat $GIT_DIR/description)
recipients=$(git-repo-config hooks.mailinglist)
announcerecipients=$(git-repo-config hooks.announcelist)
allowunannotated=$(git-repo-config --bool hooks.allowunannotated)
# check for no description
if [ -z "$projectdesc" -o "$projectdesc" = "Unnamed repository; edit this file to name it for gitweb" ]; then
echo "*** Project description file hasn't been set" >&2
exit 1
fi
# --- Check types
newrev_type=$(git-cat-file -t $newrev)
case "$refname","$newrev_type" in
refs/tags/*,commit)
# un-annotated tag
refname_type="tag"
short_refname=${refname##refs/tags/}
if [ "$allowunannotated" != "true" ]; then
echo "*** The un-annotated tag, $short_refname is not allowed in this repository" >&2
echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
exit 1
fi
;;
refs/tags/*,tag)
# annotated tag
refname_type="annotated tag"
short_refname=${refname##refs/tags/}
# change recipients
if [ -n "$announcerecipients" ]; then
recipients="$announcerecipients"
fi
;;
refs/heads/*,commit)
# branch
refname_type="branch"
short_refname=${refname##refs/heads/}
;;
refs/remotes/*,commit)
# tracking branch
refname_type="tracking branch"
short_refname=${refname##refs/remotes/}
# Should this even be allowed?
echo "*** Push-update of tracking branch, $refname. No email generated." >&2
exit 0
;;
*)
# Anything else (is there anything else?)
echo "*** Update hook: unknown type of update, \"$newrev_type\", to ref $refname" >&2
echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
exit 1
;;
esac
# Check if we've got anyone to send to
if [ -z "$recipients" ]; then
# If the email isn't sent, then at least give the user some idea of what command
# would generate the email at a later date
echo "*** No recipients found - no email will be sent, but the push will continue" >&2
echo "*** for $0 $1 $2 $3" >&2
exit 0
fi
# --- Email parameters
committer=$(git show --pretty=full -s $newrev | grep "^Commit: " | sed -e "s/^Commit: //")
describe=$(git describe $newrev 2>/dev/null)
if [ -z "$describe" ]; then
describe=$newrev
fi
# --- Email (all stdout will be the email)
(
# Generate header
cat <<-EOF
From: $committer
To: $recipients
Subject: ${EMAILPREFIX}$projectdesc $refname_type, $short_refname now at $describe
X-Git-Refname: $refname
X-Git-Reftype: $refname_type
X-Git-Oldrev: $oldrev
X-Git-Newrev: $newrev
Hello,
This is an automated email from the git hooks/update script, it was
generated because a ref change was pushed to the repository.
Updating $refname_type, $short_refname,
EOF
case "$refname_type" in
"tracking branch"|branch)
if expr "$oldrev" : '0*$' >/dev/null
then
# If the old reference is "0000..0000" then this is a new branch
# and so oldrev is not valid
echo " as a new $refname_type"
echo " to $newrev ($newrev_type)"
echo ""
echo $LOGBEGIN
# This shows all log entries that are not already covered by
# another ref - i.e. commits that are now accessible from this
# ref that were previously not accessible
git log $newrev --not --all
echo $LOGEND
else
# oldrev is valid
oldrev_type=$(git-cat-file -t "$oldrev")
# Now the problem is for cases like this:
# * --- * --- * --- * (oldrev)
# \
# * --- * --- * (newrev)
# i.e. there is no guarantee that newrev is a strict subset
# of oldrev - (would have required a force, but that's allowed).
# So, we can't simply say rev-list $oldrev..$newrev. Instead
# we find the common base of the two revs and list from there
baserev=$(git-merge-base $oldrev $newrev)
# Commit with a parent
for rev in $(git-rev-list $newrev --not $baserev --all)
do
revtype=$(git-cat-file -t "$rev")
echo " via $rev ($revtype)"
done
if [ "$baserev" = "$oldrev" ]; then
echo " from $oldrev ($oldrev_type)"
else
echo " based on $baserev"
echo " from $oldrev ($oldrev_type)"
echo ""
echo "This ref update crossed a branch point; i.e. the old rev is not a strict subset"
echo "of the new rev. This occurs, when you --force push a change in a situation"
echo "like this:"
echo ""
echo " * -- * -- B -- O -- O -- O ($oldrev)"
echo " \\"
echo " N -- N -- N ($newrev)"
echo ""
echo "Therefore, we assume that you've already had alert emails for all of the O"
echo "revisions, and now give you all the revisions in the N branch from the common"
echo "base, B ($baserev), up to the new revision."
fi
echo ""
echo $LOGBEGIN
git log $newrev --not $baserev --all
echo $LOGEND
echo ""
echo "Diffstat:"
git-diff-tree --no-color --stat -M -C --find-copies-harder $baserev..$newrev
fi
;;
"annotated tag")
# Should we allow changes to annotated tags?
if expr "$oldrev" : '0*$' >/dev/null
then
# If the old reference is "0000..0000" then this is a new atag
# and so oldrev is not valid
echo " to $newrev ($newrev_type)"
else
echo " to $newrev ($newrev_type)"
echo " from $oldrev"
fi
# If this tag succeeds another, then show which tag it replaces
prevtag=$(git describe --abbrev=0 $newrev^ 2>/dev/null)
if [ -n "$prevtag" ]; then
echo " replaces $prevtag"
fi
# Read the tag details
eval $(git cat-file tag $newrev | \
sed -n '4s/tagger \([^>]*>\)[^0-9]*\([0-9]*\).*/tagger="\1" ts="\2"/p')
tagged=$(date --date="1970-01-01 00:00:00 +0000 $ts seconds" +"$DATEFORMAT")
echo " tagged by $tagger"
echo " on $tagged"
echo ""
echo $LOGBEGIN
echo ""
if [ -n "$prevtag" ]; then
git rev-list --pretty=short "$prevtag..$newrev" | git shortlog
else
git rev-list --pretty=short $newrev | git shortlog
fi
echo $LOGEND
echo ""
;;
*)
# By default, unannotated tags aren't allowed in; if
# they are though, it's debatable whether we would even want an
# email to be generated; however, I don't want to add another config
# option just for that.
#
# Unannotated tags are more about marking a point than releasing
# a version; therefore we don't do the shortlog summary that we
# do for annotated tags above - we simply show that the point has
# been marked, and print the log message for the marked point for
# reference purposes
#
# Note this section also catches any other reference type (although
# there aren't any) and deals with them in the same way.
if expr "$oldrev" : '0*$' >/dev/null
then
# If the old reference is "0000..0000" then this is a new tag
# and so oldrev is not valid
echo " as a new $refname_type"
echo " to $newrev ($newrev_type)"
else
echo " to $newrev ($newrev_type)"
echo " from $oldrev"
fi
echo ""
echo $LOGBEGIN
git-show --no-color --root -s $newrev
echo $LOGEND
echo ""
;;
esac
# Footer
cat <<-EOF
hooks/update
---
Git Source Code Management System
$0 $1 \\
$2 \\
$3
EOF
#) | cat >&2
) | /usr/sbin/sendmail -t
# --- Finished
exit 0

View File

@@ -70,7 +70,8 @@ static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const
* Is a tree entry interesting given the pathspec we have?
*
* Return:
* - positive for yes
* - 2 for "yes, and all subsequent entries will be"
* - 1 for yes
* - zero for no
* - negative for "no, and no subsequent entries will be either"
*/
@@ -81,6 +82,7 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
unsigned mode;
int i;
int pathlen;
int never_interesting = -1;
if (!opt->nr_paths)
return 1;
@@ -89,17 +91,21 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
pathlen = tree_entry_len(path, sha1);
for (i=0; i < opt->nr_paths; i++) {
for (i = 0; i < opt->nr_paths; i++) {
const char *match = opt->paths[i];
int matchlen = opt->pathlens[i];
int m = -1; /* signals that we haven't called strncmp() */
if (baselen >= matchlen) {
/* If it doesn't match, move along... */
if (strncmp(base, match, matchlen))
continue;
/* The base is a subdirectory of a path which was specified. */
return 1;
/*
* The base is a subdirectory of a path which
* was specified, so all of them are interesting.
*/
return 2;
}
/* Does the base match? */
@@ -109,6 +115,37 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
match += baselen;
matchlen -= baselen;
if (never_interesting) {
/*
* We have not seen any match that sorts later
* than the current path.
*/
/*
* Does match sort strictly earlier than path
* with their common parts?
*/
m = strncmp(match, path,
(matchlen < pathlen) ? matchlen : pathlen);
if (m < 0)
continue;
/*
* If we come here even once, that means there is at
* least one pathspec that would sort equal to or
* later than the path we are currently looking at.
* In other words, if we have never reached this point
* after iterating all pathspecs, it means all
* pathspecs are either outside of base, or inside the
* base but sorts strictly earlier than the current
* one. In either case, they will never match the
* subsequent entries. In such a case, we initialized
* the variable to -1 and that is what will be
* returned, allowing the caller to terminate early.
*/
never_interesting = 0;
}
if (pathlen > matchlen)
continue;
@@ -119,19 +156,39 @@ static int tree_entry_interesting(struct tree_desc *desc, const char *base, int
continue;
}
if (strncmp(path, match, pathlen))
continue;
if (m == -1)
/*
* we cheated and did not do strncmp(), so we do
* that here.
*/
m = strncmp(match, path, pathlen);
return 1;
/*
* If common part matched earlier then it is a hit,
* because we rejected the case where path is not a
* leading directory and is shorter than match.
*/
if (!m)
return 1;
}
return 0; /* No matches */
return never_interesting; /* No matches */
}
/* A whole sub-tree went away or appeared */
static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base, int baselen)
{
int all_interesting = 0;
while (desc->size) {
int show = tree_entry_interesting(desc, base, baselen, opt);
int show;
if (all_interesting)
show = 1;
else {
show = tree_entry_interesting(desc, base, baselen,
opt);
if (show == 2)
all_interesting = 1;
}
if (show < 0)
break;
if (show)
@@ -154,12 +211,13 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
char *newbase = malloc_base(base, baselen, path, pathlen);
struct tree_desc inner;
void *tree;
unsigned long size;
tree = read_sha1_file(sha1, &type, &inner.size);
tree = read_sha1_file(sha1, &type, &size);
if (!tree || type != OBJ_TREE)
die("corrupt tree sha %s", sha1_to_hex(sha1));
inner.buf = tree;
init_tree_desc(&inner, tree, size);
show_tree(opt, prefix, &inner, newbase, baselen + 1 + pathlen);
free(tree);
@@ -171,8 +229,17 @@ static void show_entry(struct diff_options *opt, const char *prefix, struct tree
static void skip_uninteresting(struct tree_desc *t, const char *base, int baselen, struct diff_options *opt)
{
int all_interesting = 0;
while (t->size) {
int show = tree_entry_interesting(t, base, baselen, opt);
int show;
if (all_interesting)
show = 1;
else {
show = tree_entry_interesting(t, base, baselen, opt);
if (show == 2)
all_interesting = 1;
}
if (!show) {
update_tree_entry(t);
continue;
@@ -227,16 +294,17 @@ int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const cha
{
void *tree1, *tree2;
struct tree_desc t1, t2;
unsigned long size1, size2;
int retval;
tree1 = read_object_with_reference(old, tree_type, &t1.size, NULL);
tree1 = read_object_with_reference(old, tree_type, &size1, NULL);
if (!tree1)
die("unable to read source tree (%s)", sha1_to_hex(old));
tree2 = read_object_with_reference(new, tree_type, &t2.size, NULL);
tree2 = read_object_with_reference(new, tree_type, &size2, NULL);
if (!tree2)
die("unable to read destination tree (%s)", sha1_to_hex(new));
t1.buf = tree1;
t2.buf = tree2;
init_tree_desc(&t1, tree1, size1);
init_tree_desc(&t2, tree2, size2);
retval = diff_tree(&t1, &t2, base, opt);
free(tree1);
free(tree2);
@@ -247,15 +315,15 @@ int diff_root_tree_sha1(const unsigned char *new, const char *base, struct diff_
{
int retval;
void *tree;
unsigned long size;
struct tree_desc empty, real;
tree = read_object_with_reference(new, tree_type, &real.size, NULL);
tree = read_object_with_reference(new, tree_type, &size, NULL);
if (!tree)
die("unable to read root tree (%s)", sha1_to_hex(new));
real.buf = tree;
init_tree_desc(&real, tree, size);
empty.size = 0;
empty.buf = "";
init_tree_desc(&empty, "", 0);
retval = diff_tree(&empty, &real, base, opt);
free(tree);
return retval;

View File

@@ -2,51 +2,6 @@
#include "tree-walk.h"
#include "tree.h"
void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
{
unsigned long size = 0;
void *buf = NULL;
if (sha1) {
buf = read_object_with_reference(sha1, tree_type, &size, NULL);
if (!buf)
die("unable to read tree %s", sha1_to_hex(sha1));
}
desc->size = size;
desc->buf = buf;
return buf;
}
static int entry_compare(struct name_entry *a, struct name_entry *b)
{
return base_name_compare(
a->path, a->pathlen, a->mode,
b->path, b->pathlen, b->mode);
}
static void entry_clear(struct name_entry *a)
{
memset(a, 0, sizeof(*a));
}
static void entry_extract(struct tree_desc *t, struct name_entry *a)
{
a->sha1 = tree_entry_extract(t, &a->path, &a->mode);
a->pathlen = tree_entry_len(a->path, a->sha1);
}
void update_tree_entry(struct tree_desc *desc)
{
const void *buf = desc->buf;
unsigned long size = desc->size;
int len = strlen(buf) + 1 + 20;
if (size < len)
die("corrupt tree file");
desc->buf = (char *) buf + len;
desc->size = size - len;
}
static const char *get_mode(const char *str, unsigned int *modep)
{
unsigned char c;
@@ -61,50 +16,85 @@ static const char *get_mode(const char *str, unsigned int *modep)
return str;
}
const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
static void decode_tree_entry(struct tree_desc *desc, const void *buf, unsigned long size)
{
const void *tree = desc->buf;
unsigned long size = desc->size;
int len = strlen(tree)+1;
const unsigned char *sha1 = (unsigned char *) tree + len;
const char *path;
unsigned int mode;
unsigned int mode, len;
path = get_mode(tree, &mode);
if (!path || size < len + 20)
path = get_mode(buf, &mode);
if (!path)
die("corrupt tree file");
*pathp = path;
*modep = canon_mode(mode);
return sha1;
len = strlen(path) + 1;
/* Initialize the descriptor entry */
desc->entry.path = path;
desc->entry.mode = mode;
desc->entry.sha1 = (const unsigned char *)(path + len);
}
void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
{
desc->buffer = buffer;
desc->size = size;
if (size)
decode_tree_entry(desc, buffer, size);
}
void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
{
unsigned long size = 0;
void *buf = NULL;
if (sha1) {
buf = read_object_with_reference(sha1, tree_type, &size, NULL);
if (!buf)
die("unable to read tree %s", sha1_to_hex(sha1));
}
init_tree_desc(desc, buf, size);
return buf;
}
static int entry_compare(struct name_entry *a, struct name_entry *b)
{
return base_name_compare(
a->path, tree_entry_len(a->path, a->sha1), a->mode,
b->path, tree_entry_len(b->path, b->sha1), b->mode);
}
static void entry_clear(struct name_entry *a)
{
memset(a, 0, sizeof(*a));
}
static void entry_extract(struct tree_desc *t, struct name_entry *a)
{
*a = t->entry;
}
void update_tree_entry(struct tree_desc *desc)
{
const void *buf = desc->buffer;
const unsigned char *end = desc->entry.sha1 + 20;
unsigned long size = desc->size;
unsigned long len = end - (const unsigned char *)buf;
if (size < len)
die("corrupt tree file");
buf = end;
size -= len;
desc->buffer = buf;
desc->size = size;
if (size)
decode_tree_entry(desc, buf, size);
}
int tree_entry(struct tree_desc *desc, struct name_entry *entry)
{
const void *tree = desc->buf;
const char *path;
unsigned long len, size = desc->size;
if (!size)
if (!desc->size)
return 0;
path = get_mode(tree, &entry->mode);
if (!path)
die("corrupt tree file");
entry->path = path;
len = strlen(path);
entry->pathlen = len;
path += len + 1;
entry->sha1 = (const unsigned char *) path;
path += 20;
len = path - (char *) tree;
if (len > size)
die("corrupt tree file");
desc->buf = path;
desc->size = size - len;
*entry = desc->entry;
update_tree_entry(desc);
return 1;
}
@@ -198,10 +188,11 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
{
int retval;
void *tree;
unsigned long size;
struct tree_desc t;
unsigned char root[20];
tree = read_object_with_reference(tree_sha1, tree_type, &t.size, root);
tree = read_object_with_reference(tree_sha1, tree_type, &size, root);
if (!tree)
return -1;
@@ -210,7 +201,7 @@ int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned ch
return 0;
}
t.buf = tree;
init_tree_desc(&t, tree, size);
retval = find_tree_entry(&t, name, sha1, mode);
free(tree);
return retval;

View File

@@ -1,24 +1,32 @@
#ifndef TREE_WALK_H
#define TREE_WALK_H
struct tree_desc {
const void *buf;
unsigned long size;
};
struct name_entry {
const unsigned char *sha1;
const char *path;
unsigned int mode;
int pathlen;
};
struct tree_desc {
const void *buffer;
struct name_entry entry;
unsigned int size;
};
static inline const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
{
*pathp = desc->entry.path;
*modep = canon_mode(desc->entry.mode);
return desc->entry.sha1;
}
static inline int tree_entry_len(const char *name, const unsigned char *sha1)
{
return (char *)sha1 - (char *)name - 1;
}
void update_tree_entry(struct tree_desc *);
void init_tree_desc(struct tree_desc *desc, const void *buf, unsigned long size);
const unsigned char *tree_entry_extract(struct tree_desc *, const char **, unsigned int *);
/* Helper function that does both of the above and returns true for success */

18
tree.c
View File

@@ -83,8 +83,7 @@ int read_tree_recursive(struct tree *tree,
if (parse_tree(tree))
return -1;
desc.buf = tree->buffer;
desc.size = tree->size;
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &entry)) {
if (!match_tree_entry(base, baselen, entry.path, entry.mode, match))
@@ -101,14 +100,15 @@ int read_tree_recursive(struct tree *tree,
if (S_ISDIR(entry.mode)) {
int retval;
char *newbase;
unsigned int pathlen = tree_entry_len(entry.path, entry.sha1);
newbase = xmalloc(baselen + 1 + entry.pathlen);
newbase = xmalloc(baselen + 1 + pathlen);
memcpy(newbase, base, baselen);
memcpy(newbase + baselen, entry.path, entry.pathlen);
newbase[baselen + entry.pathlen] = '/';
memcpy(newbase + baselen, entry.path, pathlen);
newbase[baselen + pathlen] = '/';
retval = read_tree_recursive(lookup_tree(entry.sha1),
newbase,
baselen + entry.pathlen + 1,
baselen + pathlen + 1,
stage, match, fn);
free(newbase);
if (retval)
@@ -151,16 +151,14 @@ static void track_tree_refs(struct tree *item)
struct name_entry entry;
/* Count how many entries there are.. */
desc.buf = item->buffer;
desc.size = item->size;
init_tree_desc(&desc, item->buffer, item->size);
while (tree_entry(&desc, &entry))
n_refs++;
/* Allocate object refs and walk it again.. */
i = 0;
refs = alloc_object_refs(n_refs);
desc.buf = item->buffer;
desc.size = item->size;
init_tree_desc(&desc, item->buffer, item->size);
while (tree_entry(&desc, &entry)) {
struct object *obj;

View File

@@ -27,8 +27,7 @@ static struct tree_entry_list *create_tree_entry_list(struct tree *tree)
if (!tree->object.parsed)
parse_tree(tree);
desc.buf = tree->buffer;
desc.size = tree->size;
init_tree_desc(&desc, tree->buffer, tree->size);
while (tree_entry(&desc, &one)) {
struct tree_entry_list *entry;

View File

@@ -155,6 +155,7 @@ static void create_pack_file(void)
die("git-upload-pack: unable to fork git-rev-list");
if (!pid_rev_list) {
close(lp_pipe[0]);
pack_pipe = fdopen(lp_pipe[1], "w");
do_rev_list(create_full_pack);
exit(0);

View File

@@ -86,7 +86,7 @@ int error(const char *err, ...)
return -1;
}
void warn(const char *warn, ...)
void warning(const char *warn, ...)
{
va_list params;

View File

@@ -236,12 +236,13 @@ int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
return 0;
}
unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
static unsigned long xdl_hash_record_with_whitespace(char const **data,
char const *top, long flags) {
unsigned long ha = 5381;
char const *ptr = *data;
for (; ptr < top && *ptr != '\n'; ptr++) {
if (isspace(*ptr) && (flags & XDF_WHITESPACE_FLAGS)) {
if (isspace(*ptr)) {
const char *ptr2 = ptr;
while (ptr + 1 < top && isspace(ptr[1])
&& ptr[1] != '\n')
@@ -270,6 +271,23 @@ unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
}
unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
unsigned long ha = 5381;
char const *ptr = *data;
if (flags & XDF_WHITESPACE_FLAGS)
return xdl_hash_record_with_whitespace(data, top, flags);
for (; ptr < top && *ptr != '\n'; ptr++) {
ha += (ha << 5);
ha ^= (unsigned long) *ptr;
}
*data = ptr < top ? ptr + 1: ptr;
return ha;
}
unsigned int xdl_hashbits(unsigned int size) {
unsigned int val = 1, bits = 0;