diff --git a/Documentation/Makefile b/Documentation/Makefile index 7c1c9e1918..7db3fb992f 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -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) >$@+ diff --git a/Documentation/RelNotes-1.5.0.6.txt b/Documentation/RelNotes-1.5.0.6.txt new file mode 100644 index 0000000000..e15447ffdb --- /dev/null +++ b/Documentation/RelNotes-1.5.0.6.txt @@ -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. + diff --git a/Documentation/RelNotes-1.5.0.7.txt b/Documentation/RelNotes-1.5.0.7.txt new file mode 100644 index 0000000000..670ad32b85 --- /dev/null +++ b/Documentation/RelNotes-1.5.0.7.txt @@ -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. diff --git a/Documentation/RelNotes-1.5.1.txt b/Documentation/RelNotes-1.5.1.txt index f78cf56bc8..daed367270 100644 --- a/Documentation/RelNotes-1.5.1.txt +++ b/Documentation/RelNotes-1.5.1.txt @@ -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:" 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:" 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 ':/' 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 option. A patch that is not relative to the toplevel needs to be applied with -p @@ -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. diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt index 77a3f78dd7..1689c74817 100644 --- a/Documentation/diff-options.txt +++ b/Documentation/diff-options.txt @@ -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]. diff --git a/Documentation/git-am.txt b/Documentation/git-am.txt index 4fb1d84413..148ce40568 100644 --- a/Documentation/git-am.txt +++ b/Documentation/git-am.txt @@ -70,7 +70,7 @@ default. You could use `--no-utf8` to override this. the patch. -C, -p:: - 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 ]". +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 diff --git a/Documentation/git-bisect.txt b/Documentation/git-bisect.txt index 16ec7269b2..b2bc58d851 100644 --- a/Documentation/git-bisect.txt +++ b/Documentation/git-bisect.txt @@ -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 [...] git bisect bad @@ -22,30 +22,34 @@ depending on the subcommand: git bisect visualize git bisect replay git bisect log + git bisect run ... -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 ------ diff --git a/Documentation/git-cvsserver.txt b/Documentation/git-cvsserver.txt index 1c6f6a7e27..85d0950cf4 100644 --- a/Documentation/git-cvsserver.txt +++ b/Documentation/git-cvsserver.txt @@ -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 ------------ diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 361eaec700..030edaf305 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -38,6 +38,11 @@ include::pretty-formats.txt[] and , 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. diff --git a/Documentation/git-mergetool.txt b/Documentation/git-mergetool.txt index 5baaaca0b5..34288fe08b 100644 --- a/Documentation/git-mergetool.txt +++ b/Documentation/git-mergetool.txt @@ -26,11 +26,11 @@ OPTIONS Use the merge resolution program specified by . 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 ------ diff --git a/Documentation/git-rev-parse.txt b/Documentation/git-rev-parse.txt index ccc66aae7f..a8bf6561e1 100644 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@ -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 ------ diff --git a/Documentation/git-svnimport.txt b/Documentation/git-svnimport.txt index b166cf3327..bdae7d87dc 100644 --- a/Documentation/git-svnimport.txt +++ b/Documentation/git-svnimport.txt @@ -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. diff --git a/Documentation/git.txt b/Documentation/git.txt index 31397dc539..9defc33273 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -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] diff --git a/Documentation/glossary.txt b/Documentation/glossary.txt index 9f446241e2..2465514e46 100644 --- a/Documentation/glossary.txt +++ b/Documentation/glossary.txt @@ -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 <> can + inherit part of its <> from another + <>, which is called "alternate". -blob object:: - Untyped object, e.g. the contents of a file. +[[def_bare_repository]]bare repository:: + A <> is normally an appropriately + named <> with a `.git` suffix that does not + have a locally checked-out copy of any of the files under + <> 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 <>, 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 <>, which is called the + branch <>. 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: <>. -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 <> in the list contains + a reference to its successor (for example, the successor of a + <> 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 "<>". 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 <> to a + <> which was stored in the + <>. -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 <> 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 <> and to record it based on the tip + of the current <> as a new <>. -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 <> is <>, if it + corresponds to the <> referenced by the current + <>. Also see "<>". -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 + <> in the <>. The + result is a <>. As a noun: Short hand for + <>. -core git:: - Fundamental data structures and utilities of git. Exposes only - limited source code management tools. +[[def_commit_object]]commit object:: + An <> which contains the information about a + particular <>, such as parents, committer, + author, date and the <> which corresponds + to the top <> of the stored + <>. -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 <> objects form a + directed acyclic graph, because they have parents (directed), and the + graph of <> objects is acyclic (there is no + <> which begins and ends with the same + <>). -dircache:: +[[def_dangling_object]]dangling object:: + An <> which is not + <> even from other unreachable objects; a + <> has no references to it from any + reference or <> in the <>. + +[[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 <> is said to be <> if + it contains modifications which have not been committed to the current + <>. + +[[def_ent]]ent:: + Favorite synonym to "<>" 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 <> where you have a + <> and you are "merging" another + <>'s changes that happen to be a descendant of what + you have. In such these cases, you do not make a new <> + <> but instead just update to his + <>. This will happen frequently on a + <> of a remote + <>. -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 <> means to get the + <>'s <> from a remote + <>, to find out which objects are missing + from the local <>, 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 <> (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 <> has + is different from what was recorded when the <> 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 <>. -head:: - The top of a branch. It contains a ref to the corresponding - commit object. +[[def_head]]head:: + The top of a <>. It contains a <> to the + corresponding <>. -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 <> pointing to a <>. Often, this is + abbreviated to "<>". 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 <> scripts are found in the + `$GIT_DIR/hooks/` <>, 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 <> is a stored version of your working + <>. Truth be told, it can also contain a second, and even + a third version of a <>, 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 + <>. An <> can be unmerged, + if a <> was started, but not yet finished (i.e. if the + <> 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 <>. Whenever you create a git + <>, a <> named + "<>" is created, and becomes the active + <>. 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 <> branches means to try to accumulate the changes + since a common ancestor and apply them to the first + <>. An automatic <> uses heuristics + to accomplish that. Evidently, an automatic <> 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 + <> of its contents. Consequently, an + <> 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 <> is + identified by its <>. The objects usually + live in `$GIT_DIR/objects/`. -object identifier:: - Synonym for object name. +[[def_object_identifier]]object identifier:: + Synonym for <>. -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 <>. The <> + of the <>'s contents using the Secure Hash Algorithm + 1 and usually represented by the 40 character hexadecimal encoding of + the <> of the <> (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 + "<>","<>","<>" or "<>" + describing the type of an <>. -octopus:: - To merge more than two branches. Also denotes an intelligent - predator. +[[def_octopus]]octopus:: + To <> 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 <>. Most projects have + at least one upstream project which they track. By default + '<>' 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 <> -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. + <>, to assist in efficiently accessing the contents of a + <>. -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 <> 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 <> 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 + <> 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 <>. -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 + <>, presenting a high level access to + <>. Porcelains expose more of a <> + interface than the <>. -pull:: - Pulling a branch means to fetch it and merge it. +[[def_pull]]pull:: + Pulling a <> means to <> it and + <> 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 <> means to get the <>'s + <> from a remote <>, + find out if it is an ancestor to the <>'s local + <> is a direct, and in that case, putting all + objects, which are <> from the local + <>, and which are missing from the remote + <>, into the remote + <>, and updating the remote + <>. If the remote <> is not an + ancestor to the local <>, the <> 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 <> are said to be + <> from that <>. More + generally, one <> is <> from + another if we can reach the one from the other by a <> + that follows <> to whatever they tag, + <> to their parents or trees, and + <> to the trees or <> + 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 <> to a + different base, and reset the <> 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 <> or a name that + denotes a particular <>. 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 :, 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 <> is used by <> and + <> to describe the mapping between remote <> + and local <>. They are combined with a colon in the format + :, preceded by an optional plus sign, +. For example: `git + fetch $URL refs/heads/master:refs/heads/origin` means + "grab the master <> <> + from the $URL and store it as my origin + <> <>". And `git <> + $URL refs/heads/master:refs/heads/to-upstream` means + "publish my master <> + <> as to-upstream <> 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 <> containing all objects which are <> + from the refs, possibly accompanied by meta data from one or more + porcelains. A <> can share an + <> 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 + <> 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 + <>. It is referenced by a + <>. -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 + <> to an earlier <>. -SCM:: +[[def_SCM]]SCM:: Source code management (tool). -SHA1:: - Synonym for object name. +[[def_SHA1]]SHA1:: + Synonym for <>. -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 <> 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 <>). 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 <> + 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 <> 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 <>. 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 <> pointing to a <> or + <>. In contrast to a <>, + a tag is not changed by a <>. Tags (not + <>) are stored in `$GIT_DIR/refs/tags/`. A + git tag has nothing to do with a Lisp tag (which would be + called an <> in git's context). A + tag is most typically used to mark a particular point in the + <> ancestry <>. -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 <> containing a <> pointing to + another <>, which can contain a message just like a + <>. It can also contain a (PGP) + signature, in which case it is called a "signed <>". -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 <> 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 <> that is used to follow changes from + another <>. A <> should not contain direct modifications or have local commits + made to it. A <> can usually be + identified as the right-hand-side <> in a Pull: + <>. -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 <>, or a <> together with the dependent blob and <> objects + (i.e. a stored representation of a <>). -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 <> containing a list of file names and modes along + with refs to the associated blob and/or tree objects. A + <> is equivalent to a <>. -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 <> pointing to either a <>, a <>, or a <> pointing to a <> or <> or + <>. -unmerged index:: - An index which contains unmerged index entries. +[[def_unmerged_index]]unmerged index:: + An <> which contains unmerged + <>. -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 <> which is not <> from a + <>, <>, 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 <> without using git at all. diff --git a/Documentation/howto/use-git-daemon.txt b/Documentation/howto/use-git-daemon.txt new file mode 100644 index 0000000000..1a1eb246bf --- /dev/null +++ b/Documentation/howto/use-git-daemon.txt @@ -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. + diff --git a/Documentation/sort_glossary.pl b/Documentation/sort_glossary.pl deleted file mode 100644 index 05dc7b2c7b..0000000000 --- a/Documentation/sort_glossary.pl +++ /dev/null @@ -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(?>";/eg; - print '[[ref_'.no_spaces($key).']]'.$key."::\n" - .format_tab_80($terms{$key})."\n"; -} - -print ' - -Author ------- -Written by Johannes Schindelin and -the git-list . - -GIT ---- -Part of the link:git.html[git] suite -'; - diff --git a/Documentation/technical/pack-format.txt b/Documentation/technical/pack-format.txt index 0e1ffb2427..9ce3c473ae 100644 --- a/Documentation/technical/pack-format.txt +++ b/Documentation/technical/pack-format.txt @@ -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). diff --git a/Documentation/technical/shallow.txt b/Documentation/technical/shallow.txt new file mode 100644 index 0000000000..559263af48 --- /dev/null +++ b/Documentation/technical/shallow.txt @@ -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. diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt index d7b227e647..574e9c0e50 100644 --- a/Documentation/user-manual.txt +++ b/Documentation/user-manual.txt @@ -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 <> 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 <>. 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 diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN index 39ba8d135c..48715012be 100755 --- a/GIT-VERSION-GEN +++ b/GIT-VERSION-GEN @@ -1,7 +1,7 @@ #!/bin/sh GVF=GIT-VERSION-FILE -DEF_VER=v1.5.1-rc1.GIT +DEF_VER=v1.5.1.GIT LF=' ' diff --git a/Makefile b/Makefile index 18deba2567..87979807fb 100644 --- a/Makefile +++ b/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" || \ diff --git a/README b/README index 441167cb8a..548142c327 100644 --- a/README +++ b/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. diff --git a/builtin-apply.c b/builtin-apply.c index dfa1716796..27a182bfaa 100644 --- a/builtin-apply.c +++ b/builtin-apply.c @@ -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; } diff --git a/builtin-blame.c b/builtin-blame.c index b51cdc71fa..60ec5354f1 100644 --- a/builtin-blame.c +++ b/builtin-blame.c @@ -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); diff --git a/builtin-bundle.c b/builtin-bundle.c index 0a9b73867f..d1635a0a6b 100644 --- a/builtin-bundle.c +++ b/builtin-bundle.c @@ -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; } diff --git a/builtin-fsck.c b/builtin-fsck.c index b8e71b640b..21f1f9e91d 100644 --- a/builtin-fsck.c +++ b/builtin-fsck.c @@ -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) { diff --git a/builtin-grep.c b/builtin-grep.c index 4510d35324..981f3d4d8e 100644 --- a/builtin-grep.c +++ b/builtin-grep.c @@ -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; diff --git a/builtin-log.c b/builtin-log.c index 865832c85e..71df957eaa 100644 --- a/builtin-log.c +++ b/builtin-log.c @@ -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 = ""; } diff --git a/builtin-mailinfo.c b/builtin-mailinfo.c index d94578cb4a..c95e477e83 100644 --- a/builtin-mailinfo.c +++ b/builtin-mailinfo.c @@ -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(); diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c index 73d448b890..b5f9648e80 100644 --- a/builtin-pack-objects.c +++ b/builtin-pack-objects.c @@ -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); } } diff --git a/builtin-prune.c b/builtin-prune.c index 09864b7a6d..44df59e4a7 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -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; } diff --git a/builtin-read-tree.c b/builtin-read-tree.c index e47715538b..793eae0a5f 100644 --- a/builtin-read-tree.c +++ b/builtin-read-tree.c @@ -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; diff --git a/builtin-reflog.c b/builtin-reflog.c index acbcc9b602..d7e4137a0e 100644 --- a/builtin-reflog.c +++ b/builtin-reflog.c @@ -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) || diff --git a/builtin-rerere.c b/builtin-rerere.c index b8867ab4ad..b463c07f04 100644 --- a/builtin-rerere.c +++ b/builtin-rerere.c @@ -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) diff --git a/builtin-rev-list.c b/builtin-rev-list.c index c2db5a5b03..51858e3233 100644 --- a/builtin-rev-list.c +++ b/builtin-rev-list.c @@ -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) { diff --git a/builtin-revert.c b/builtin-revert.c index f3f3f5c6ee..4ba0ee63ab 100644 --- a/builtin-revert.c +++ b/builtin-revert.c @@ -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 { diff --git a/builtin-rm.c b/builtin-rm.c index 00dbe39960..bf42003a7e 100644 --- a/builtin-rm.c +++ b/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); } diff --git a/cache.h b/cache.h index b1b767467e..cd0f88e111 100644 --- a/cache.h +++ b/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 *); diff --git a/commit.c b/commit.c index 8aadd22d84..0737915d20 100644 --- a/commit.c +++ b/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]) diff --git a/connect.c b/connect.c index 7b635ff344..26c5173636 100644 --- a/connect.c +++ b/connect.c @@ -418,6 +418,8 @@ static int git_tcp_connect_sock(char *host) if (colon) { *colon = 0; port = colon + 1; + if (!*port) + port = ""; } 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, diff --git a/contrib/continuous/cidaemon b/contrib/continuous/cidaemon new file mode 100644 index 0000000000..4009a151de --- /dev/null +++ b/contrib/continuous/cidaemon @@ -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 = ; + 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 () { + 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 = ; + 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 = ; + 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 = < +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 = ; + 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 () { + if (s/^(author|committer) //) { + chomp; + s/>.*$/>/; + $authors{$_} = 1 unless nocc_author $_; + } + } + close LOG; + open LOG,'-|','git','shortlog',@revs; + $shortlog .= $_ while ; + 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 = <&W'); + open(STDERR, '>&W'); + exit do_build $git_dir, $new; + } else { + close W; + my $out = ''; + $out .= $_ while ; + 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"; +} diff --git a/contrib/continuous/post-receive-cinotify b/contrib/continuous/post-receive-cinotify new file mode 100644 index 0000000000..b8f5a609af --- /dev/null +++ b/contrib/continuous/post-receive-cinotify @@ -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 ; + 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 () { + chop; + push @skip, $_; +} +close S; + +my @new_branch_base; +open S, "git config --get-all builder.newBranchBase|"; +while () { + 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 () { + 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 () { + ($_) = 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; diff --git a/contrib/emacs/git.el b/contrib/emacs/git.el index 5f22dec5f7..2f9995ea39 100644 --- a/contrib/emacs/git.el +++ b/contrib/emacs/git.el @@ -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.")))) diff --git a/contrib/hooks/post-receieve-email b/contrib/hooks/post-receieve-email new file mode 100644 index 0000000000..65160153ee --- /dev/null +++ b/contrib/hooks/post-receieve-email @@ -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/\(.*\) /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 diff --git a/contrib/workdir/git-new-workdir b/contrib/workdir/git-new-workdir new file mode 100755 index 0000000000..9877b98508 --- /dev/null +++ b/contrib/workdir/git-new-workdir @@ -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 []" +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 diff --git a/fast-import.c b/fast-import.c index 55ffae4fa6..cdd629d6bc 100644 --- a/fast-import.c +++ b/fast-import.c @@ -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; diff --git a/fetch.c b/fetch.c index f69be82f10..8e29d313f8 100644 --- a/fetch.c +++ b/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; diff --git a/git-am.sh b/git-am.sh index 88af8dd256..e69ecbfdb1 100755 --- a/git-am.sh +++ b/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 diff --git a/git-bisect.sh b/git-bisect.sh index b1c3a6b1c1..11313a7949 100755 --- a/git-bisect.sh +++ b/git-bisect.sh @@ -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 [] reset bisect state and start bisection. git bisect bad [] mark a known-bad revision. git bisect good [...] mark ... known-good revisions. git bisect next find next bisection to test and check it out. git bisect reset [] finish bisection search and go back to branch. git bisect visualize show bisect status in gitk. -git bisect replay replay bisection log -git bisect log show bisect log.' +git bisect replay replay bisection log. +git bisect log show bisect log. +git bisect run ... use ... 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 diff --git a/git-checkout.sh b/git-checkout.sh index e67010ac48..9cdeaa445a 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -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 " 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 diff --git a/git-clone.sh b/git-clone.sh index de51983584..513b574d13 100755 --- a/git-clone.sh +++ b/git-clone.sh @@ -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" || diff --git a/git-commit.sh b/git-commit.sh index 3656d607d5..292cf967e3 100755 --- a/git-commit.sh +++ b/git-commit.sh @@ -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 diff --git a/git-compat-util.h b/git-compat-util.h index 428f171379..17cfcea0ad 100644 --- a/git-compat-util.h +++ b/git-compat-util.h @@ -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); diff --git a/git-fetch.sh b/git-fetch.sh index e218042843..fd70696b74 100755 --- a/git-fetch.sh +++ b/git-fetch.sh @@ -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" && diff --git a/git-merge-ours.sh b/git-merge-ours.sh index 4f3d053889..2b6a5c0d10 100755 --- a/git-merge-ours.sh +++ b/git-merge-ours.sh @@ -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 diff --git a/git-merge.sh b/git-merge.sh index ec30da462a..5c62a06a50 100755 --- a/git-merge.sh +++ b/git-merge.sh @@ -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 diff --git a/git-mergetool.sh b/git-mergetool.sh index 7942fd0b64..e62351bcba 100755 --- a/git-mergetool.sh +++ b/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 diff --git a/git-quiltimport.sh b/git-quiltimport.sh index 08ac9bb729..edccd82755 100755 --- a/git-quiltimport.sh +++ b/git-quiltimport.sh @@ -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 diff --git a/git-rebase.sh b/git-rebase.sh index ea117a0c4a..e79ce44f84 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -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 diff --git a/git-remote.perl b/git-remote.perl index bd70bf1ddd..52013fe76d 100755 --- a/git-remote.perl +++ b/git-remote.perl @@ -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 { diff --git a/git-svn.perl b/git-svn.perl index e8457893db..d307d430f3 100755 --- a/git-svn.perl +++ b/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"; diff --git a/git.c b/git.c index 501b482723..e223986fd3 100644 --- a/git.c +++ b/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; diff --git a/gitk b/gitk index 3909445d8a..f36ade0b7b 100755 --- a/gitk +++ b/gitk @@ -720,6 +720,7 @@ proc makewindow {} { bindkey {findnext 0} bindkey ? findprev bindkey f nextfile + bindkey updatecommits bind . doquit bind . dofind bind . {findnext 0} @@ -986,6 +987,7 @@ f Scroll diff view to next file Increase font size Decrease font size Decrease font size + 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 } diff --git a/gitweb/INSTALL b/gitweb/INSTALL new file mode 100644 index 0000000000..6328e26f56 --- /dev/null +++ b/gitweb/INSTALL @@ -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/" + + + Options Indexes FollowSymlinks ExecCGI + AllowOverride None + Order allow,deny + Allow from all + + +- Apache2, gitweb installed as mod_perl legacy script, + under /var/www/perl/ + + Alias /perl "/var/www/perl" + + + SetHandler perl-script + PerlResponseHandler ModPerl::Registry + PerlOptions +ParseHeaders + Options Indexes FollowSymlinks +ExecCGI + AllowOverride None + Order allow,deny + Allow from all + diff --git a/gitweb/gitweb.css b/gitweb/gitweb.css index 7177c6e86b..5e40292404 100644 --- a/gitweb/gitweb.css +++ b/gitweb/gitweb.css @@ -107,7 +107,7 @@ span.age { font-style: italic; } -div.page_body span.signoff { +span.signoff { color: #888888; } diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl index 27b5970bca..3786955fca 100755 --- a/gitweb/gitweb.perl +++ b/gitweb/gitweb.perl @@ -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 "\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"); } diff --git a/help.c b/help.c index 9d1561050e..781fad3543 100644 --- a/help.c +++ b/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; diff --git a/http-fetch.c b/http-fetch.c index e6cd11db73..557b40322f 100644 --- a/http-fetch.c +++ b/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; diff --git a/http-push.c b/http-push.c index cbb02d3bc1..724720c562 100644 --- a/http-push.c +++ b/http-push.c @@ -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)) diff --git a/index-pack.c b/index-pack.c index b405864be9..3c768fbc63 100644 --- a/index-pack.c +++ b/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 { diff --git a/list-objects.c b/list-objects.c index f1fa21c397..2ba2c958e0 100644 --- a/list-objects.c +++ b/list-objects.c @@ -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)) diff --git a/merge-recursive.c b/merge-recursive.c index c96e1a734c..e1aebd7727 100644 --- a/merge-recursive.c +++ b/merge-recursive.c @@ -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; diff --git a/merge-tree.c b/merge-tree.c index b2867ba722..3b8d9e6887 100644 --- a/merge-tree.c +++ b/merge-tree.c @@ -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); diff --git a/object.c b/object.c index 5b46889342..78a44a6ef4 100644 --- a/object.c +++ b/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) diff --git a/reachable.c b/reachable.c index 01760d7046..ff3dd34962 100644 --- a/reachable.c +++ b/reachable.c @@ -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)) diff --git a/refs.c b/refs.c index a96bab57d7..e40baa3c6f 100644 --- a/refs.c +++ b/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); diff --git a/revision.c b/revision.c index bcdb6a1212..486393cb08 100644 --- a/revision.c +++ b/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; diff --git a/revision.h b/revision.h index 6ae39e6bec..55e6b531ce 100644 --- a/revision.h +++ b/revision.h @@ -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, diff --git a/sha1_file.c b/sha1_file.c index 6bcbdcf55c..17886af979 100644 --- a/sha1_file.c +++ b/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); diff --git a/t/t4118-apply-empty-context.sh b/t/t4118-apply-empty-context.sh index 690a182003..27cc6f2b88 100755 --- a/t/t4118-apply-empty-context.sh +++ b/t/t4118-apply-empty-context.sh @@ -23,7 +23,8 @@ test_expect_success setup ' cat file2 >file2.orig git add file1 file2 && sed -e "/^B/d" file1 && - sed -e "/^B/d" file2 && + sed -e "/^[BQ]/d" file2 && + echo Q | tr -d "\\012" >>file2 && cat file1 >file1.mods && cat file2 >file2.mods && git diff | diff --git a/t/t4200-rerere.sh b/t/t4200-rerere.sh index e081b32aff..8b611bbea2 100755 --- a/t/t4200-rerere.sh +++ b/t/t4200-rerere.sh @@ -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' diff --git a/t/t5100/patch0005 b/t/t5100/patch0005 index e7d6f66608..7d24b24af8 100644 --- a/t/t5100/patch0005 +++ b/t/t5100/patch0005 @@ -61,7 +61,7 @@ diff --git a/git-cvsimport-script b/git-cvsimport-script push(@old,$fn); -- -David KÃ¥gedal +David Kågedal - To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@vger.kernel.org diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh index 48c38cfb4d..aafb976a75 100755 --- a/t/t5300-pack-object.sh +++ b/t/t5300-pack-object.sh @@ -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 diff --git a/t/t6006-rev-list-format.sh b/t/t6006-rev-list-format.sh new file mode 100755 index 0000000000..aab17face8 --- /dev/null +++ b/t/t6006-rev-list-format.sh @@ -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 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 + +commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 + +EOF + +test_format subject %s <<'EOF' +commit 131a310eb913d107dd3c09a65d1651175898735d +changed foo +commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +added foo +EOF + +test_format body %b <<'EOF' +commit 131a310eb913d107dd3c09a65d1651175898735d + +commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 + +EOF + +test_format colors %Credfoo%Cgreenbar%Cbluebaz%Cresetxyzzy <<'EOF' +commit 131a310eb913d107dd3c09a65d1651175898735d +foobarbazxyzzy +commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 +foobarbazxyzzy +EOF + +cat >commit-msg <<'EOF' +Test printing of complex bodies + +This commit message is much longer than the others, +and it will be encoded in iso8859-1. We should therefore +include an iso8859 character: ¡bueno! +EOF +test_expect_success 'setup complex body' ' +git-config i18n.commitencoding iso8859-1 && + echo change2 >foo && git-commit -a -F commit-msg +' + +test_format complex-encoding %e <<'EOF' +commit f58db70b055c5718631e5c61528b28b12090cdea +iso8859-1 +commit 131a310eb913d107dd3c09a65d1651175898735d + +commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 + +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 + +commit 86c75cfd708a0e5868dc876ed5b8bb66c80b4873 + +EOF + +test_done diff --git a/t/t6030-bisect-run.sh b/t/t6030-bisect-run.sh new file mode 100755 index 0000000000..39c72283b5 --- /dev/null +++ b/t/t6030-bisect-run.sh @@ -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 + diff --git a/templates/hooks--post-receive b/templates/hooks--post-receive new file mode 100644 index 0000000000..190de2688c --- /dev/null +++ b/templates/hooks--post-receive @@ -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 +# +# 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 + diff --git a/templates/hooks--update b/templates/hooks--update index 8f6c4fea24..0ff03309e6 100644 --- a/templates/hooks--update +++ b/templates/hooks--update @@ -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 diff --git a/tree-diff.c b/tree-diff.c index b2f35dc3d8..852498eb49 100644 --- a/tree-diff.c +++ b/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; diff --git a/tree-walk.c b/tree-walk.c index a4a4e2a989..cbb24eb3f6 100644 --- a/tree-walk.c +++ b/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; diff --git a/tree-walk.h b/tree-walk.h index a0d7afd89b..43458cf8ce 100644 --- a/tree-walk.h +++ b/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 */ diff --git a/tree.c b/tree.c index 24f8fb6766..d188c0fbae 100644 --- a/tree.c +++ b/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; diff --git a/unpack-trees.c b/unpack-trees.c index 2e2232cbb0..ee10eea24c 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -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; diff --git a/upload-pack.c b/upload-pack.c index 84264a888f..20f2bcfe5f 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -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); diff --git a/usage.c b/usage.c index 4dc5c77633..f5e652cc76 100644 --- a/usage.c +++ b/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; diff --git a/xdiff/xutils.c b/xdiff/xutils.c index 3653864e4b..bf91c0f73c 100644 --- a/xdiff/xutils.c +++ b/xdiff/xutils.c @@ -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;