mirror of
https://github.com/git/git.git
synced 2026-03-13 10:23:30 +01:00
Merge commit 'v1.5.1'
This commit is contained in:
@@ -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) >$@+
|
||||
|
||||
22
Documentation/RelNotes-1.5.0.6.txt
Normal file
22
Documentation/RelNotes-1.5.0.6.txt
Normal 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.
|
||||
|
||||
18
Documentation/RelNotes-1.5.0.7.txt
Normal file
18
Documentation/RelNotes-1.5.0.7.txt
Normal 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.
|
||||
@@ -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.
|
||||
|
||||
@@ -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].
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
------
|
||||
|
||||
@@ -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
|
||||
------------
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
------
|
||||
|
||||
@@ -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
|
||||
------
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
52
Documentation/howto/use-git-daemon.txt
Normal file
52
Documentation/howto/use-git-daemon.txt
Normal 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.
|
||||
|
||||
@@ -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
|
||||
';
|
||||
|
||||
@@ -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).
|
||||
|
||||
49
Documentation/technical/shallow.txt
Normal file
49
Documentation/technical/shallow.txt
Normal 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.
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
GVF=GIT-VERSION-FILE
|
||||
DEF_VER=v1.5.1-rc1.GIT
|
||||
DEF_VER=v1.5.1.GIT
|
||||
|
||||
LF='
|
||||
'
|
||||
|
||||
4
Makefile
4
Makefile
@@ -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
6
README
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = "";
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) ||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
18
builtin-rm.c
18
builtin-rm.c
@@ -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);
|
||||
}
|
||||
|
||||
2
cache.h
2
cache.h
@@ -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 *);
|
||||
|
||||
|
||||
26
commit.c
26
commit.c
@@ -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])
|
||||
|
||||
@@ -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
503
contrib/continuous/cidaemon
Normal 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";
|
||||
}
|
||||
104
contrib/continuous/post-receive-cinotify
Normal file
104
contrib/continuous/post-receive-cinotify
Normal 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;
|
||||
@@ -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."))))
|
||||
|
||||
|
||||
588
contrib/hooks/post-receieve-email
Normal file
588
contrib/hooks/post-receieve-email
Normal 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
57
contrib/workdir/git-new-workdir
Executable 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
|
||||
@@ -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;
|
||||
|
||||
3
fetch.c
3
fetch.c
@@ -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;
|
||||
|
||||
|
||||
18
git-am.sh
18
git-am.sh
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" ||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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" &&
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
158
git-mergetool.sh
158
git-mergetool.sh
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
25
git-svn.perl
25
git-svn.perl
@@ -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
2
git.c
@@ -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
6
gitk
@@ -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
225
gitweb/INSTALL
Normal 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>
|
||||
@@ -107,7 +107,7 @@ span.age {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
div.page_body span.signoff {
|
||||
span.signoff {
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
14
help.c
@@ -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;
|
||||
|
||||
37
http-fetch.c
37
http-fetch.c
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
43
index-pack.c
43
index-pack.c
@@ -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 {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
6
object.c
6
object.c
@@ -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)
|
||||
|
||||
@@ -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
21
refs.c
@@ -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);
|
||||
|
||||
30
revision.c
30
revision.c
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
86
sha1_file.c
86
sha1_file.c
@@ -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);
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
150
t/t6006-rev-list-format.sh
Executable 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
|
||||
[31mfoo[32mbar[34mbaz[mxyzzy
|
||||
commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873
|
||||
[31mfoo[32mbar[34mbaz[mxyzzy
|
||||
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
57
t/t6030-bisect-run.sh
Executable 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
|
||||
|
||||
17
templates/hooks--post-receive
Normal file
17
templates/hooks--post-receive
Normal 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
|
||||
|
||||
@@ -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
|
||||
|
||||
108
tree-diff.c
108
tree-diff.c
@@ -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;
|
||||
|
||||
153
tree-walk.c
153
tree-walk.c
@@ -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;
|
||||
|
||||
20
tree-walk.h
20
tree-walk.h
@@ -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
18
tree.c
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
2
usage.c
2
usage.c
@@ -86,7 +86,7 @@ int error(const char *err, ...)
|
||||
return -1;
|
||||
}
|
||||
|
||||
void warn(const char *warn, ...)
|
||||
void warning(const char *warn, ...)
|
||||
{
|
||||
va_list params;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user