diff options
-rw-r--r-- | .mailmap | 185 | ||||
-rw-r--r-- | Documentation/git-cat-file.txt | 79 | ||||
-rw-r--r-- | Documentation/git-log.txt | 2 | ||||
-rw-r--r-- | builtin/cat-file.c | 210 | ||||
-rw-r--r-- | builtin/clone.c | 21 | ||||
-rw-r--r-- | cache.h | 1 | ||||
-rw-r--r-- | commit.c | 38 | ||||
-rw-r--r-- | contrib/mw-to-git/Git/Mediawiki.pm | 100 | ||||
-rw-r--r-- | contrib/mw-to-git/Makefile | 33 | ||||
-rwxr-xr-x | contrib/mw-to-git/bin-wrapper/git | 14 | ||||
-rwxr-xr-x | contrib/mw-to-git/git-mw.perl | 368 | ||||
-rwxr-xr-x | contrib/mw-to-git/git-remote-mediawiki.perl | 85 | ||||
-rwxr-xr-x | contrib/mw-to-git/t/test-gitmw-lib.sh | 8 | ||||
-rw-r--r-- | git-rebase--interactive.sh | 6 | ||||
-rwxr-xr-x | git-rebase.sh | 8 | ||||
-rw-r--r-- | line-log.c | 3 | ||||
-rw-r--r-- | pack-revindex.c | 108 | ||||
-rw-r--r-- | remote.c | 27 | ||||
-rw-r--r-- | sha1_file.c | 22 | ||||
-rw-r--r-- | streaming.c | 2 | ||||
-rwxr-xr-x | t/t1006-cat-file.sh | 74 | ||||
-rwxr-xr-x | t/t3404-rebase-interactive.sh | 15 | ||||
-rwxr-xr-x | t/t3900-i18n-commit.sh | 36 | ||||
-rw-r--r-- | t/t4211/expect.multiple-superset | 134 | ||||
-rwxr-xr-x | t/t5710-info-alternate.sh | 8 | ||||
-rw-r--r-- | t/test-lib.sh | 1 | ||||
-rw-r--r-- | wrap-for-bin.sh | 2 |
27 files changed, 1341 insertions, 249 deletions
@@ -5,99 +5,216 @@ # same person appearing not to be so. # +<nico@fluxnic.net> <nico@cam.org> +Alejandro R. Sedeño <asedeno@MIT.EDU> <asedeno@mit.edu> Alex Bennée <kernel-hacker@bennee.com> +Alex Riesen <raa.lkml@gmail.com> <fork0@t-online.de> +Alex Riesen <raa.lkml@gmail.com> <raa@limbo.localdomain> +Alex Riesen <raa.lkml@gmail.com> <raa@steel.home> +Alex Vandiver <alex@chmrr.net> <alexmv@MIT.EDU> Alexander Gavrilov <angavrilov@gmail.com> +Alexey Shumkin <alex.crezoff@gmail.com> <zapped@mail.ru> +Anders Kaseorg <andersk@MIT.EDU> <andersk@ksplice.com> +Anders Kaseorg <andersk@MIT.EDU> <andersk@mit.edu> Aneesh Kumar K.V <aneesh.kumar@gmail.com> +Ben Walton <bdwalton@gmail.com> <bwalton@artsci.utoronto.ca> +Bernt Hansen <bernt@norang.ca> <bernt@alumni.uwaterloo.ca> +Brandon Casey <drafnel@gmail.com> <casey@nrlssc.navy.mil> Brian M. Carlson <sandals@crustytoothpaste.ath.cx> +Bryan Larsen <bryan@larsen.st> <bryan.larsen@gmail.com> +Bryan Larsen <bryan@larsen.st> <bryanlarsen@yahoo.com> Cheng Renquan <crquan@gmail.com> Chris Shoemaker <c.shoemaker@cox.net> +Chris Wright <chrisw@sous-sol.org> <chrisw@osdl.org> +Csaba Henk <csaba@gluster.com> <csaba@lowlife.hu> Dan Johnson <computerdruid@gmail.com> -Dana L. How <danahow@gmail.com> -Dana L. How <how@deathvalley.cswitch.com> +Dana L. How <danahow@gmail.com> <how@deathvalley.cswitch.com> Daniel Barkalow <barkalow@iabervon.org> +David Brown <git@davidb.org> <davidb@quicinc.com> David D. Kilzer <ddkilzer@kilzer.net> David Kågedal <davidk@lysator.liu.se> +David Reiss <dreiss@facebook.com> <dreiss@dreiss-vmware.(none)> David S. Miller <davem@davemloft.net> Deskin Miller <deskinm@umich.edu> Dirk Süsserott <newsletter@dirk.my1.cc> +Eric Blake <eblake@redhat.com> <ebb9@byu.net> +Eric Hanchrow <eric.hanchrow@gmail.com> <offby1@blarg.net> Eric S. Raymond <esr@thyrsus.com> Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com> -Fredrik Kuivinen <freku045@student.liu.se> +Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com> <eyvind-git@orakel.ntnu.no> +Florian Achleitner <florian.achleitner.2.6.31@gmail.com> <florian.achleitner2.6.31@gmail.com> +Franck Bui-Huu <vagabon.xyz@gmail.com> <fbuihuu@gmail.com> +Frank Lichtenheld <frank@lichtenheld.de> <djpig@debian.org> +Frank Lichtenheld <frank@lichtenheld.de> <flichtenheld@astaro.com> +Fredrik Kuivinen <frekui@gmail.com> <freku045@student.liu.se> Frédéric Heitzmann <frederic.heitzmann@gmail.com> +Garry Dolley <gdolley@ucla.edu> <gdolley@arpnetworks.com> +Greg Price <price@mit.edu> <price@MIT.EDU> +Greg Price <price@mit.edu> <price@ksplice.com> +Heiko Voigt <hvoigt@hvoigt.net> <git-list@hvoigt.net> H. Merijn Brand <h.m.brand@xs4all.nl> H.Merijn Brand <h.m.brand@xs4all.nl> -H. Peter Anvin <hpa@bonde.sc.orionmulti.com> -H. Peter Anvin <hpa@tazenda.sc.orionmulti.com> -H. Peter Anvin <hpa@trantor.hos.anvin.org> +H. Peter Anvin <hpa@zytor.com> <hpa@bonde.sc.orionmulti.com> +H. Peter Anvin <hpa@zytor.com> <hpa@smyrno.hos.anvin.org> +H. Peter Anvin <hpa@zytor.com> <hpa@tazenda.sc.orionmulti.com> +H. Peter Anvin <hpa@zytor.com> <hpa@trantor.hos.anvin.org> +Han-Wen Nienhuys <hanwen@google.com> Han-Wen Nienhuys <hanwen@xs4all.nl> Horst H. von Brand <vonbrand@inf.utfsm.cl> -İsmail Dönmez <ismail@pardus.org.tr> +J. Bruce Fields <bfields@citi.umich.edu> <bfields@fieldses.org> +J. Bruce Fields <bfields@citi.umich.edu> <bfields@pig.linuxdev.us.dell.com> +J. Bruce Fields <bfields@citi.umich.edu> <bfields@puzzle.fieldses.org> Jakub Narębski <jnareb@gmail.com> -Jay Soffian <jaysoffian+git@gmail.com> +Jason Riedy <ejr@eecs.berkeley.edu> <ejr@EECS.Berkeley.EDU> +Jason Riedy <ejr@eecs.berkeley.edu> <ejr@cs.berkeley.edu> +Jay Soffian <jaysoffian@gmail.com> <jaysoffian+git@gmail.com> Jeff King <peff@peff.net> <peff@github.com> +Jeff Muizelaar <jmuizelaar@mozilla.com> <jeff@infidigm.net> +Jim Meyering <jim@meyering.net> <meyering@redhat.com> Joachim Berdal Haga <cjhaga@fys.uio.no> -Johannes Sixt <j6t@kdbg.org> <johannes.sixt@telecom.at> -Johannes Sixt <j6t@kdbg.org> <j.sixt@viscovery.net> +Johannes Schindelin <Johannes.Schindelin@gmx.de> <johannes.schindelin@gmx.de> Johannes Sixt <j6t@kdbg.org> <J.Sixt@eudaptics.com> -Jon Loeliger <jdl@freescale.com> -Jon Seymour <jon@blackcubes.dyndns.org> -Jonathan Nieder <jrnieder@uchicago.edu> +Johannes Sixt <j6t@kdbg.org> <j.sixt@viscovery.net> +Johannes Sixt <j6t@kdbg.org> <johannes.sixt@telecom.at> +Jon Loeliger <jdl@jdl.com> <jdl@freescale.com> +Jon Loeliger <jdl@jdl.com> <jdl@freescale.org> +Jon Seymour <jon.seymour@gmail.com> <jon@blackcubes.dyndns.org> +Jonathan Nieder <jrnieder@gmail.com> <jrnieder@uchicago.edu> +Jonathan del Strother <jon.delStrother@bestbefore.tv> <maillist@steelskies.com> +Josh Triplett <josh@joshtriplett.org> <josh@freedesktop.org> +Josh Triplett <josh@joshtriplett.org> <josht@us.ibm.com> +Julian Phillips <julian@quantumfyre.co.uk> <jp3@quantumfyre.co.uk> Junio C Hamano <gitster@pobox.com> <gitster@pobox.com> -Junio C Hamano <gitster@pobox.com> <junio@pobox.com> -Junio C Hamano <gitster@pobox.com> <junio@twinsun.com> -Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com> Junio C Hamano <gitster@pobox.com> <junio@hera.kernel.org> Junio C Hamano <gitster@pobox.com> <junio@kernel.org> +Junio C Hamano <gitster@pobox.com> <junio@pobox.com> +Junio C Hamano <gitster@pobox.com> <junio@twinsun.com> Junio C Hamano <gitster@pobox.com> <junkio@cox.net> -Karl Hasselström <kha@treskal.com> -Kevin Leung <kevinlsk@gmail.com> +Junio C Hamano <gitster@pobox.com> <junkio@twinsun.com> +Karl Wiberg <kha@treskal.com> Karl Hasselström <kha@treskal.com> +Karl Wiberg <kha@treskal.com> Karl Hasselström <kha@yoghurt.hemma.treskal.com> +Karsten Blees <blees@dcon.de> <karsten.blees@dcon.de> +Karsten Blees <blees@dcon.de> <karsten.blees@gmail.com> +Kay Sievers <kay.sievers@vrfy.org> <kay.sievers@suse.de> +Kay Sievers <kay.sievers@vrfy.org> <kay@mam.(none)> +Keith Cascio <keith@CS.UCLA.EDU> <keith@cs.ucla.edu> Kent Engstrom <kent@lysator.liu.se> +Kevin Leung <kevinlsk@gmail.com> +Kirill Smelkov <kirr@navytux.spb.ru> <kirr@landau.phys.spbu.ru> +Kirill Smelkov <kirr@navytux.spb.ru> <kirr@mns.spb.ru> +Knut Franke <Knut.Franke@gmx.de> <k.franke@science-computing.de> Lars Doelle <lars.doelle@on-line ! de> Lars Doelle <lars.doelle@on-line.de> +Lars Noschinski <lars@public.noschinski.de> <lars.noschinski@rwth-aachen.de> Li Hong <leehong@pku.edu.cn> -Linus Torvalds <torvalds@linux-foundation.org> <torvalds@woody.linux-foundation.org> -Linus Torvalds <torvalds@linux-foundation.org> <torvalds@osdl.org> -Linus Torvalds <torvalds@linux-foundation.org> <torvalds@g5.osdl.org> Linus Torvalds <torvalds@linux-foundation.org> <torvalds@evo.osdl.org> -Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org> +Linus Torvalds <torvalds@linux-foundation.org> <torvalds@g5.osdl.org> +Linus Torvalds <torvalds@linux-foundation.org> <torvalds@osdl.org> Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org.(none)> -Lukas Sandström <lukass@etek.chalmers.se> +Linus Torvalds <torvalds@linux-foundation.org> <torvalds@ppc970.osdl.org> +Linus Torvalds <torvalds@linux-foundation.org> <torvalds@woody.linux-foundation.org> +Lukas Sandström <luksan@gmail.com> <lukass@etek.chalmers.se> +Marc Khouzam <marc.khouzam@ericsson.com> <marc.khouzam@gmail.com> Marc-André Lureau <marcandre.lureau@gmail.com> +Marco Costalba <mcostalba@gmail.com> <mcostalba@yahoo.it> +Mark Levedahl <mdl123@verizon.net> <mlevedahl@gmail.com> Mark Rada <marada@uwaterloo.ca> Martin Langhoff <martin@laptop.org> <martin@catalyst.net.nz> Martin von Zweigbergk <martinvonz@gmail.com> <martin.von.zweigbergk@gmail.com> +Matt Draisey <matt@draisey.ca> <mattdraisey@sympatico.ca> +Matt Kraai <kraai@ftbfs.org> <matt.kraai@amo.abbott.com> +Matt McCutchen <matt@mattmccutchen.net> <hashproduct@gmail.com> +Matthias Kestenholz <matthias@spinlock.ch> <mk@spinlock.ch> +Matthias Urlichs <matthias@urlichs.de> <smurf@kiste.(none)> +Matthias Urlichs <matthias@urlichs.de> <smurf@smurf.noris.de> Michael Coleman <tutufan@gmail.com> Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm> Michael W. Olson <mwolson@gnu.org> +Michael Witten <mfwitten@gmail.com> <mfwitten@MIT.EDU> +Michael Witten <mfwitten@gmail.com> <mfwitten@mit.edu> +Michal Rokos <michal.rokos@nextsoft.cz> <rokos@nextsoft.cz> Michele Ballabio <barra_cuda@katamail.com> +Miklos Vajna <vmiklos@frugalware.org> <vmiklos@suse.cz> +Namhyung Kim <namhyung@gmail.com> <namhyung.kim@lge.com> +Namhyung Kim <namhyung@gmail.com> <namhyung@kernel.org> Nanako Shiraishi <nanako3@bluebottle.com> Nanako Shiraishi <nanako3@lavabit.com> +Nelson Elhage <nelhage@mit.edu> <nelhage@MIT.EDU> +Nelson Elhage <nelhage@mit.edu> <nelhage@ksplice.com> Nguyễn Thái Ngọc Duy <pclouds@gmail.com> -<nico@fluxnic.net> <nico@cam.org> -Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se> +Nick Stokoe <nick@noodlefactory.co.uk> Nick Woolley <nick@noodlefactory.co.uk> +Nick Stokoe <nick@noodlefactory.co.uk> Nick Woolley <nickwoolley@yahoo.co.uk> +Nicolas Morey-Chaisemartin <devel-git@morey-chaisemartin.com> <nicolas.morey@free.fr> +Nicolas Morey-Chaisemartin <devel-git@morey-chaisemartin.com> <nmorey@kalray.eu> +Nicolas Sebrecht <nicolas.s.dev@gmx.fr> <ni.s@laposte.net> +Paolo Bonzini <bonzini@gnu.org> <paolo.bonzini@lu.unisi.ch> +Pascal Obry <pascal@obry.net> <pascal.obry@gmail.com> +Pascal Obry <pascal@obry.net> <pascal.obry@wanadoo.fr> +Pat Notz <patnotz@gmail.com> <pknotz@sandia.gov> +Paul Mackerras <paulus@samba.org> <paulus@dorrigo.(none)> +Paul Mackerras <paulus@samba.org> <paulus@pogo.(none)> +Peter Baumann <waste.manager@gmx.de> <Peter.B.Baumann@stud.informatik.uni-erlangen.de> +Peter Baumann <waste.manager@gmx.de> <siprbaum@stud.informatik.uni-erlangen.de> Peter Krefting <peter@softwolves.pp.se> <peter@softwolves.pp.se> +Peter Krefting <peter@softwolves.pp.se> <peter@svarten.intern.softwolves.pp.se> Petr Baudis <pasky@ucw.cz> <pasky@suse.cz> +Petr Baudis <pasky@ucw.cz> <xpasky@machine> +Phil Hord <hordp@cisco.com> <phil.hord@gmail.com> +Philip Jägenstedt <philip@foolip.org> <philip.jagenstedt@gmail.com> +Philipp A. Hartmann <pah@qo.cx> <ph@sorgh.de> Philippe Bruhat <book@cpan.org> Ralf Thielow <ralf.thielow@gmail.com> <ralf.thielow@googlemail.com> Ramsay Allan Jones <ramsay@ramsay1.demon.co.uk> René Scharfe <l.s.r@web.de> <rene.scharfe@lsrfire.ath.cx> Robert Fitzsimons <robfitz@273k.net> +Robert Shearman <robertshearman@gmail.com> <rob@codeweavers.com> Robert Zeh <robert.a.zeh@gmail.com> -Sam Vilain <sam@vilain.net> -Santi Béjar <sbejar@gmail.com> +Robin Rosenberg <robin.rosenberg@dewire.com> <robin.rosenberg.lists@dewire.com> +Ryan Anderson <ryan@michonline.com> <rda@google.com> +Salikh Zakirov <salikh.zakirov@gmail.com> <Salikh.Zakirov@Intel.com> +Sam Vilain <sam@vilain.net> <sam.vilain@catalyst.net.nz> +Santi Béjar <santi@agolina.net> <sbejar@gmail.com> Sean Estabrooks <seanlkml@sympatico.ca> +Sebastian Schuberth <sschuberth@gmail.com> <sschuberth@visageimaging.com> +Seth Falcon <seth@userprimary.net> <sfalcon@fhcrc.org> Shawn O. Pearce <spearce@spearce.org> -Steven Grimm <koreth@midwinter.com> +Simon Hausmann <hausmann@kde.org> <simon@lst.de> +Simon Hausmann <hausmann@kde.org> <shausman@trolltech.com> +Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@atlas-elektronik.com> +Stefan Naewe <stefan.naewe@gmail.com> <stefan.naewe@googlemail.com> +Stefan Sperling <stsp@elego.de> <stsp@stsp.name> +Stephen Boyd <bebarino@gmail.com> <sboyd@codeaurora.org> +Steven Drake <sdrake@xnet.co.nz> <sdrake@ihug.co.nz> +Steven Grimm <koreth@midwinter.com> <sgrimm@sgrimm-mbp.local> +Steven Walter <stevenrwalter@gmail.com> <swalter@lexmark.com> +Steven Walter <stevenrwalter@gmail.com> <swalter@lpdev.prtdev.lexmark.com> +Sven Verdoolaege <skimo@kotnet.org> <Sven.Verdoolaege@cs.kuleuven.ac.be> +Sven Verdoolaege <skimo@kotnet.org> <skimo@liacs.nl> Tay Ray Chuan <rctay89@gmail.com> +Ted Percival <ted@midg3t.net> <ted.percival@quest.com> Theodore Ts'o <tytso@mit.edu> +Thomas Ackermann <th.acker@arcor.de> <th.acker66@arcor.de> Thomas Rast <trast@inf.ethz.ch> <trast@student.ethz.ch> +Timo Hirvonen <tihirvon@gmail.com> <tihirvon@ee.oulu.fi> +Toby Allsopp <Toby.Allsopp@navman.co.nz> <toby.allsopp@navman.co.nz> +Tom Grennan <tmgrennan@gmail.com> <tgrennan@redback.com> +Tommi Virtanen <tv@debian.org> <tv@eagain.net> +Tommi Virtanen <tv@debian.org> <tv@inoi.fi> +Tommy Thorn <tommy-git@thorn.ws> <tt1729@yahoo.com> Tony Luck <tony.luck@intel.com> -Uwe Kleine-König <Uwe_Zeisberger@digi.com> -Uwe Kleine-König <Uwe.Kleine-Koenig@digi.com> -Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de> -Uwe Kleine-König <uzeisberger@io.fsforth.de> -Uwe Kleine-König <zeisberg@informatik.uni-freiburg.de> -Ville Skyttä <scop@xemacs.org> +Tor Arne Vestbø <torarnv@gmail.com> <tavestbo@trolltech.com> +Trent Piepho <tpiepho@gmail.com> <tpiepho@freescale.com> +Trent Piepho <tpiepho@gmail.com> <xyzzy@speakeasy.org> +Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <Uwe.Kleine-Koenig@digi.com> +Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <ukleinek@informatik.uni-freiburg.de> +Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <uzeisberger@io.fsforth.de> +Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <zeisberg@informatik.uni-freiburg.de> +Ville Skyttä <ville.skytta@iki.fi> <scop@xemacs.org> Vitaly "_Vi" Shukela <public_vi@tut.by> +W. Trevor King <wking@tremily.us> <wking@drexel.edu> William Pursell <bill.pursell@gmail.com> +YONETANI Tomokazu <y0n3t4n1@gmail.com> <qhwt+git@les.ath.cx> +YONETANI Tomokazu <y0n3t4n1@gmail.com> <y0netan1@dragonflybsd.org> YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org> +# the two anonymous contributors are different persons: anonymous <linux@horizon.com> anonymous <linux@horizon.net> +İsmail Dönmez <ismail@pardus.org.tr> diff --git a/Documentation/git-cat-file.txt b/Documentation/git-cat-file.txt index 30d585af5d..3ddec0b65b 100644 --- a/Documentation/git-cat-file.txt +++ b/Documentation/git-cat-file.txt @@ -58,12 +58,16 @@ OPTIONS to apply the filter to the content recorded in the index at <path>. --batch:: - Print the SHA-1, type, size, and contents of each object provided on - stdin. May not be combined with any other options or arguments. +--batch=<format>:: + Print object information and contents for each object provided + on stdin. May not be combined with any other options or arguments. + See the section `BATCH OUTPUT` below for details. --batch-check:: - Print the SHA-1, type, and size of each object provided on stdin. May not - be combined with any other options or arguments. +--batch-check=<format>:: + Print object information for each object provided on stdin. May + not be combined with any other options or arguments. See the + section `BATCH OUTPUT` below for details. OUTPUT ------ @@ -78,28 +82,81 @@ If '-p' is specified, the contents of <object> are pretty-printed. If <type> is specified, the raw (though uncompressed) contents of the <object> will be returned. -If '--batch' is specified, output of the following form is printed for each -object specified on stdin: +BATCH OUTPUT +------------ + +If `--batch` or `--batch-check` is given, `cat-file` will read objects +from stdin, one per line, and print information about them. + +Each line is split at the first whitespace boundary. All characters +before that whitespace are considered as a whole object name, and are +parsed as if given to linkgit:git-rev-parse[1]. Characters after that +whitespace can be accessed using the `%(rest)` atom (see below). + +You can specify the information shown for each object by using a custom +`<format>`. The `<format>` is copied literally to stdout for each +object, with placeholders of the form `%(atom)` expanded, followed by a +newline. The available atoms are: + +`objectname`:: + The 40-hex object name of the object. + +`objecttype`:: + The type of of the object (the same as `cat-file -t` reports). + +`objectsize`:: + The size, in bytes, of the object (the same as `cat-file -s` + reports). + +`objectsize:disk`:: + The size, in bytes, that the object takes up on disk. See the + note about on-disk sizes in the `CAVEATS` section below. + +`rest`:: + The text (if any) found after the first run of whitespace on the + input line (i.e., the "rest" of the line). + +If no format is specified, the default format is `%(objectname) +%(objecttype) %(objectsize)`. + +If `--batch` is specified, the object information is followed by the +object contents (consisting of `%(objectsize)` bytes), followed by a +newline. + +For example, `--batch` without a custom format would produce: ------------ <sha1> SP <type> SP <size> LF <contents> LF ------------ -If '--batch-check' is specified, output of the following form is printed for -each object specified on stdin: +Whereas `--batch-check='%(objectname) %(objecttype)'` would produce: ------------ -<sha1> SP <type> SP <size> LF +<sha1> SP <type> LF ------------ -For both '--batch' and '--batch-check', output of the following form is printed -for each object specified on stdin that does not exist in the repository: +If a name is specified on stdin that cannot be resolved to an object in +the repository, then `cat-file` will ignore any custom format and print: ------------ <object> SP missing LF ------------ + +CAVEATS +------- + +Note that the sizes of objects on disk are reported accurately, but care +should be taken in drawing conclusions about which refs or objects are +responsible for disk usage. The size of a packed non-delta object may be +much larger than the size of objects which delta against it, but the +choice of which object is the base and which is the delta is arbitrary +and is subject to change during a repack. Note also that multiple copies +of an object may be present in the object database; in this case, it is +undefined which copy's size will be reported. + + GIT --- Part of the linkgit:git[1] suite diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index 2ea79ba168..2ee6962a72 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -153,7 +153,7 @@ Examples This makes sense only when following a strict policy of merging all topic branches when staying on a single integration branch. -git log -L '/int main/',/^}/:main.c:: +`git log -L '/int main/',/^}/:main.c`:: Shows how the function `main()` in the file 'main.c' evolved over time. diff --git a/builtin/cat-file.c b/builtin/cat-file.c index 045cee7bce..0e64b4159c 100644 --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@ -13,9 +13,6 @@ #include "userdiff.h" #include "streaming.h" -#define BATCH 1 -#define BATCH_CHECK 2 - static int cat_one_file(int opt, const char *exp_type, const char *obj_name) { unsigned char sha1[20]; @@ -117,54 +114,174 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name) return 0; } -static int batch_one_object(const char *obj_name, int print_contents) -{ +struct expand_data { unsigned char sha1[20]; - enum object_type type = 0; + enum object_type type; unsigned long size; - void *contents = NULL; + unsigned long disk_size; + const char *rest; + + /* + * If mark_query is true, we do not expand anything, but rather + * just mark the object_info with items we wish to query. + */ + int mark_query; + + /* + * After a mark_query run, this object_info is set up to be + * passed to sha1_object_info_extended. It will point to the data + * elements above, so you can retrieve the response from there. + */ + struct object_info info; +}; + +static int is_atom(const char *atom, const char *s, int slen) +{ + int alen = strlen(atom); + return alen == slen && !memcmp(atom, s, alen); +} + +static void expand_atom(struct strbuf *sb, const char *atom, int len, + void *vdata) +{ + struct expand_data *data = vdata; + + if (is_atom("objectname", atom, len)) { + if (!data->mark_query) + strbuf_addstr(sb, sha1_to_hex(data->sha1)); + } else if (is_atom("objecttype", atom, len)) { + if (!data->mark_query) + strbuf_addstr(sb, typename(data->type)); + } else if (is_atom("objectsize", atom, len)) { + if (data->mark_query) + data->info.sizep = &data->size; + else + strbuf_addf(sb, "%lu", data->size); + } else if (is_atom("objectsize:disk", atom, len)) { + if (data->mark_query) + data->info.disk_sizep = &data->disk_size; + else + strbuf_addf(sb, "%lu", data->disk_size); + } else if (is_atom("rest", atom, len)) { + if (!data->mark_query && data->rest) + strbuf_addstr(sb, data->rest); + } else + die("unknown format element: %.*s", len, atom); +} + +static size_t expand_format(struct strbuf *sb, const char *start, void *data) +{ + const char *end; + + if (*start != '(') + return 0; + end = strchr(start + 1, ')'); + if (!end) + die("format element '%s' does not end in ')'", start); + + expand_atom(sb, start + 1, end - start - 1, data); + + return end - start + 1; +} + +static void print_object_or_die(int fd, const unsigned char *sha1, + enum object_type type, unsigned long size) +{ + if (type == OBJ_BLOB) { + if (stream_blob_to_fd(fd, sha1, NULL, 0) < 0) + die("unable to stream %s to stdout", sha1_to_hex(sha1)); + } + else { + enum object_type rtype; + unsigned long rsize; + void *contents; + + contents = read_sha1_file(sha1, &rtype, &rsize); + if (!contents) + die("object %s disappeared", sha1_to_hex(sha1)); + if (rtype != type) + die("object %s changed type!?", sha1_to_hex(sha1)); + if (rsize != size) + die("object %s change size!?", sha1_to_hex(sha1)); + + write_or_die(fd, contents, size); + free(contents); + } +} + +struct batch_options { + int enabled; + int print_contents; + const char *format; +}; + +static int batch_one_object(const char *obj_name, struct batch_options *opt, + struct expand_data *data) +{ + struct strbuf buf = STRBUF_INIT; if (!obj_name) return 1; - if (get_sha1(obj_name, sha1)) { + if (get_sha1(obj_name, data->sha1)) { printf("%s missing\n", obj_name); fflush(stdout); return 0; } - if (print_contents == BATCH) - contents = read_sha1_file(sha1, &type, &size); - else - type = sha1_object_info(sha1, &size); - - if (type <= 0) { + data->type = sha1_object_info_extended(data->sha1, &data->info); + if (data->type <= 0) { printf("%s missing\n", obj_name); fflush(stdout); - if (print_contents == BATCH) - free(contents); return 0; } - printf("%s %s %lu\n", sha1_to_hex(sha1), typename(type), size); - fflush(stdout); + strbuf_expand(&buf, opt->format, expand_format, data); + strbuf_addch(&buf, '\n'); + write_or_die(1, buf.buf, buf.len); + strbuf_release(&buf); - if (print_contents == BATCH) { - write_or_die(1, contents, size); - printf("\n"); - fflush(stdout); - free(contents); + if (opt->print_contents) { + print_object_or_die(1, data->sha1, data->type, data->size); + write_or_die(1, "\n", 1); } - return 0; } -static int batch_objects(int print_contents) +static int batch_objects(struct batch_options *opt) { struct strbuf buf = STRBUF_INIT; + struct expand_data data; + + if (!opt->format) + opt->format = "%(objectname) %(objecttype) %(objectsize)"; + + /* + * Expand once with our special mark_query flag, which will prime the + * object_info to be handed to sha1_object_info_extended for each + * object. + */ + memset(&data, 0, sizeof(data)); + data.mark_query = 1; + strbuf_expand(&buf, opt->format, expand_format, &data); + data.mark_query = 0; while (strbuf_getline(&buf, stdin, '\n') != EOF) { - int error = batch_one_object(buf.buf, print_contents); + char *p; + int error; + + /* + * Split at first whitespace, tying off the beginning of the + * string and saving the remainder (or NULL) in data.rest. + */ + p = strpbrk(buf.buf, " \t"); + if (p) { + while (*p && strchr(" \t", *p)) + *p++ = '\0'; + } + data.rest = p; + + error = batch_one_object(buf.buf, opt, &data); if (error) return error; } @@ -186,10 +303,29 @@ static int git_cat_file_config(const char *var, const char *value, void *cb) return git_default_config(var, value, cb); } +static int batch_option_callback(const struct option *opt, + const char *arg, + int unset) +{ + struct batch_options *bo = opt->value; + + if (unset) { + memset(bo, 0, sizeof(*bo)); + return 0; + } + + bo->enabled = 1; + bo->print_contents = !strcmp(opt->long_name, "batch"); + bo->format = arg; + + return 0; +} + int cmd_cat_file(int argc, const char **argv, const char *prefix) { - int opt = 0, batch = 0; + int opt = 0; const char *exp_type = NULL, *obj_name = NULL; + struct batch_options batch = {0}; const struct option options[] = { OPT_GROUP(N_("<type> can be one of: blob, tree, commit, tag")), @@ -200,12 +336,12 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) OPT_SET_INT('p', NULL, &opt, N_("pretty-print object's content"), 'p'), OPT_SET_INT(0, "textconv", &opt, N_("for blob objects, run textconv on object's content"), 'c'), - OPT_SET_INT(0, "batch", &batch, - N_("show info and content of objects fed from the standard input"), - BATCH), - OPT_SET_INT(0, "batch-check", &batch, - N_("show info about objects fed from the standard input"), - BATCH_CHECK), + { OPTION_CALLBACK, 0, "batch", &batch, "format", + N_("show info and content of objects fed from the standard input"), + PARSE_OPT_OPTARG, batch_option_callback }, + { OPTION_CALLBACK, 0, "batch-check", &batch, "format", + N_("show info about objects fed from the standard input"), + PARSE_OPT_OPTARG, batch_option_callback }, OPT_END() }; @@ -222,19 +358,19 @@ int cmd_cat_file(int argc, const char **argv, const char *prefix) else usage_with_options(cat_file_usage, options); } - if (!opt && !batch) { + if (!opt && !batch.enabled) { if (argc == 2) { exp_type = argv[0]; obj_name = argv[1]; } else usage_with_options(cat_file_usage, options); } - if (batch && (opt || argc)) { + if (batch.enabled && (opt || argc)) { usage_with_options(cat_file_usage, options); } - if (batch) - return batch_objects(batch); + if (batch.enabled) + return batch_objects(&batch); return cat_one_file(opt, exp_type, obj_name); } diff --git a/builtin/clone.c b/builtin/clone.c index 17f57cdf29..430307b298 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -545,17 +545,20 @@ static void update_remote_refs(const struct ref *refs, const struct ref *remote_head_points_at, const char *branch_top, const char *msg, - struct transport *transport) + struct transport *transport, + int check_connectivity) { const struct ref *rm = mapped_refs; - if (0 <= option_verbosity) - printf(_("Checking connectivity... ")); - if (check_everything_connected_with_transport(iterate_ref_map, - 0, &rm, transport)) - die(_("remote did not send all necessary objects")); - if (0 <= option_verbosity) - printf(_("done\n")); + if (check_connectivity) { + if (0 <= option_verbosity) + printf(_("Checking connectivity... ")); + if (check_everything_connected_with_transport(iterate_ref_map, + 0, &rm, transport)) + die(_("remote did not send all necessary objects")); + if (0 <= option_verbosity) + printf(_("done\n")); + } if (refs) { write_remote_refs(mapped_refs); @@ -963,7 +966,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) transport_fetch_refs(transport, mapped_refs); update_remote_refs(refs, mapped_refs, remote_head_points_at, - branch_top.buf, reflog_msg.buf, transport); + branch_top.buf, reflog_msg.buf, transport, !is_local); update_head(our_head_points_at, remote_head, reflog_msg.buf); @@ -1130,6 +1130,7 @@ extern int unpack_object_header(struct packed_git *, struct pack_window **, off_ struct object_info { /* Request */ unsigned long *sizep; + unsigned long *disk_sizep; /* Response */ enum { @@ -1350,10 +1350,15 @@ int commit_tree(const struct strbuf *msg, unsigned char *tree, static int find_invalid_utf8(const char *buf, int len) { int offset = 0; + static const unsigned int max_codepoint[] = { + 0x7f, 0x7ff, 0xffff, 0x10ffff + }; while (len) { unsigned char c = *buf++; int bytes, bad_offset; + unsigned int codepoint; + unsigned int min_val, max_val; len--; offset++; @@ -1374,24 +1379,48 @@ static int find_invalid_utf8(const char *buf, int len) bytes++; } - /* Must be between 1 and 5 more bytes */ - if (bytes < 1 || bytes > 5) + /* + * Must be between 1 and 3 more bytes. Longer sequences result in + * codepoints beyond U+10FFFF, which are guaranteed never to exist. + */ + if (bytes < 1 || 3 < bytes) return bad_offset; /* Do we *have* that many bytes? */ if (len < bytes) return bad_offset; + /* + * Place the encoded bits at the bottom of the value and compute the + * valid range. + */ + codepoint = (c & 0x7f) >> bytes; + min_val = max_codepoint[bytes-1] + 1; + max_val = max_codepoint[bytes]; + offset += bytes; len -= bytes; /* And verify that they are good continuation bytes */ do { + codepoint <<= 6; + codepoint |= *buf & 0x3f; if ((*buf++ & 0xc0) != 0x80) return bad_offset; } while (--bytes); - /* We could/should check the value and length here too */ + /* Reject codepoints that are out of range for the sequence length. */ + if (codepoint < min_val || codepoint > max_val) + return bad_offset; + /* Surrogates are only for UTF-16 and cannot be encoded in UTF-8. */ + if ((codepoint & 0x1ff800) == 0xd800) + return bad_offset; + /* U+xxFFFE and U+xxFFFF are guaranteed non-characters. */ + if ((codepoint & 0xffffe) == 0xfffe) + return bad_offset; + /* So are anything in the range U+FDD0..U+FDEF. */ + if (codepoint >= 0xfdd0 && codepoint <= 0xfdef) + return bad_offset; } return -1; } @@ -1401,9 +1430,6 @@ static int find_invalid_utf8(const char *buf, int len) * * If it isn't, it assumes any non-utf8 characters are Latin1, * and does the conversion. - * - * Fixme: we should probably also disallow overlong forms and - * invalid characters. But we don't do that currently. */ static int verify_utf8(struct strbuf *buf) { diff --git a/contrib/mw-to-git/Git/Mediawiki.pm b/contrib/mw-to-git/Git/Mediawiki.pm new file mode 100644 index 0000000000..d13c4dfa7d --- /dev/null +++ b/contrib/mw-to-git/Git/Mediawiki.pm @@ -0,0 +1,100 @@ +package Git::Mediawiki; + +use 5.008; +use strict; +use Git; + +BEGIN { + +our ($VERSION, @ISA, @EXPORT, @EXPORT_OK); + +# Totally unstable API. +$VERSION = '0.01'; + +require Exporter; + +@ISA = qw(Exporter); + +@EXPORT = (); + +# Methods which can be called as standalone functions as well: +@EXPORT_OK = qw(clean_filename smudge_filename connect_maybe + EMPTY HTTP_CODE_OK HTTP_CODE_PAGE_NOT_FOUND); +} + +# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced +use constant SLASH_REPLACEMENT => '%2F'; + +# Used to test for empty strings +use constant EMPTY => q{}; + +# HTTP codes +use constant HTTP_CODE_OK => 200; +use constant HTTP_CODE_PAGE_NOT_FOUND => 404; + +sub clean_filename { + my $filename = shift; + $filename =~ s{@{[SLASH_REPLACEMENT]}}{/}g; + # [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded. + # Do a variant of URL-encoding, i.e. looks like URL-encoding, + # but with _ added to prevent MediaWiki from thinking this is + # an actual special character. + $filename =~ s/[\[\]\{\}\|]/sprintf("_%%_%x", ord($&))/ge; + # If we use the uri escape before + # we should unescape here, before anything + + return $filename; +} + +sub smudge_filename { + my $filename = shift; + $filename =~ s{/}{@{[SLASH_REPLACEMENT]}}g; + $filename =~ s/ /_/g; + # Decode forbidden characters encoded in clean_filename + $filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge; + return $filename; +} + +sub connect_maybe { + my $wiki = shift; + if ($wiki) { + return $wiki; + } + + my $remote_name = shift; + my $remote_url = shift; + my ($wiki_login, $wiki_password, $wiki_domain); + + $wiki_login = Git::config("remote.${remote_name}.mwLogin"); + $wiki_password = Git::config("remote.${remote_name}.mwPassword"); + $wiki_domain = Git::config("remote.${remote_name}.mwDomain"); + + $wiki = MediaWiki::API->new; + $wiki->{config}->{api_url} = "${remote_url}/api.php"; + if ($wiki_login) { + my %credential = ( + 'url' => $remote_url, + 'username' => $wiki_login, + 'password' => $wiki_password + ); + Git::credential(\%credential); + my $request = {lgname => $credential{username}, + lgpassword => $credential{password}, + lgdomain => $wiki_domain}; + if ($wiki->login($request)) { + Git::credential(\%credential, 'approve'); + print {*STDERR} qq(Logged in mediawiki user "$credential{username}".\n); + } else { + print {*STDERR} qq(Failed to log in mediawiki user "$credential{username}" on ${remote_url}\n); + print {*STDERR} ' (error ' . + $wiki->{error}->{code} . ': ' . + $wiki->{error}->{details} . ")\n"; + Git::credential(\%credential, 'reject'); + exit 1; + } + } + + return $wiki; +} + +1; # Famous last words diff --git a/contrib/mw-to-git/Makefile b/contrib/mw-to-git/Makefile index 1fb2424481..76fcd4defc 100644 --- a/contrib/mw-to-git/Makefile +++ b/contrib/mw-to-git/Makefile @@ -2,18 +2,43 @@ # Copyright (C) 2013 # Matthieu Moy <Matthieu.Moy@imag.fr> # -## Build git-remote-mediawiki +# To build and test: +# +# make +# bin-wrapper/git mw preview Some_page.mw +# bin-wrapper/git clone mediawiki::http://example.com/wiki/ +# +# To install, run Git's toplevel 'make install' then run: +# +# make install +GIT_MEDIAWIKI_PM=Git/Mediawiki.pm SCRIPT_PERL=git-remote-mediawiki.perl +SCRIPT_PERL+=git-mw.perl GIT_ROOT_DIR=../.. HERE=contrib/mw-to-git/ SCRIPT_PERL_FULL=$(patsubst %,$(HERE)/%,$(SCRIPT_PERL)) +INSTLIBDIR=$(shell $(MAKE) -C $(GIT_ROOT_DIR)/perl \ + -s --no-print-directory instlibdir) all: build -build install clean: - $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL=$(SCRIPT_PERL_FULL) \ - $@-perl-script +install_pm: + install $(GIT_MEDIAWIKI_PM) $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM) + +build: + $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \ + build-perl-script + +install: install_pm + $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \ + install-perl-script + +clean: + $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \ + clean-perl-script + rm $(INSTLIBDIR)/$(GIT_MEDIAWIKI_PM) + perlcritic: perlcritic -2 *.perl diff --git a/contrib/mw-to-git/bin-wrapper/git b/contrib/mw-to-git/bin-wrapper/git new file mode 100755 index 0000000000..6663ae57e8 --- /dev/null +++ b/contrib/mw-to-git/bin-wrapper/git @@ -0,0 +1,14 @@ +#!/bin/sh + +# git executable wrapper script for Git-Mediawiki to run tests without +# installing all the scripts and perl packages. + +GIT_ROOT_DIR=../../.. +GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd ${GIT_ROOT_DIR} && pwd) + +GITPERLLIB="$GIT_EXEC_PATH"'/contrib/mw-to-git'"${GITPERLLIB:+:$GITPERLLIB}" +PATH="$GIT_EXEC_PATH"'/contrib/mw-to-git:'"$PATH" + +export GITPERLLIB PATH + +exec "${GIT_EXEC_PATH}/bin-wrappers/git" "$@" diff --git a/contrib/mw-to-git/git-mw.perl b/contrib/mw-to-git/git-mw.perl new file mode 100755 index 0000000000..28df3ee321 --- /dev/null +++ b/contrib/mw-to-git/git-mw.perl @@ -0,0 +1,368 @@ +#!/usr/bin/perl + +# Copyright (C) 2013 +# Benoit Person <benoit.person@ensimag.imag.fr> +# Celestin Matte <celestin.matte@ensimag.imag.fr> +# License: GPL v2 or later + +# Set of tools for git repo with a mediawiki remote. +# Documentation & bugtracker: https://github.com/moy/Git-Mediawiki/ + +use strict; +use warnings; + +use Getopt::Long; +use URI::URL qw(url); +use LWP::UserAgent; +use HTML::TreeBuilder; + +use Git; +use MediaWiki::API; +use Git::Mediawiki qw(clean_filename connect_maybe + EMPTY HTTP_CODE_PAGE_NOT_FOUND); + +# By default, use UTF-8 to communicate with Git and the user +binmode STDERR, ':encoding(UTF-8)'; +binmode STDOUT, ':encoding(UTF-8)'; + +# Global parameters +my $verbose = 0; +sub v_print { + if ($verbose) { + return print {*STDERR} @_; + } + return; +} + +# Preview parameters +my $file_name = EMPTY; +my $remote_name = EMPTY; +my $preview_file_name = EMPTY; +my $autoload = 0; +sub file { + $file_name = shift; + return $file_name; +} + +my %commands = ( + 'help' => + [\&help, {}, \&help], + 'preview' => + [\&preview, { + '<>' => \&file, + 'output|o=s' => \$preview_file_name, + 'remote|r=s' => \$remote_name, + 'autoload|a' => \$autoload + }, \&preview_help] +); + +# Search for sub-command +my $cmd = $commands{'help'}; +for (0..@ARGV-1) { + if (defined $commands{$ARGV[$_]}) { + $cmd = $commands{$ARGV[$_]}; + splice @ARGV, $_, 1; + last; + } +}; +GetOptions( %{$cmd->[1]}, + 'help|h' => \&{$cmd->[2]}, + 'verbose|v' => \$verbose); + +# Launch command +&{$cmd->[0]}; + +############################# Preview Functions ################################ + +sub preview_help { + print {*STDOUT} <<'END'; +USAGE: git mw preview [--remote|-r <remote name>] [--autoload|-a] + [--output|-o <output filename>] [--verbose|-v] + <blob> | <filename> + +DESCRIPTION: +Preview is an utiliy to preview local content of a mediawiki repo as if it was +pushed on the remote. + +For that, preview searches for the remote name of the current branch's +upstream if --remote is not set. If that remote is not found or if it +is not a mediawiki, it lists all mediawiki remotes configured and asks +you to replay your command with the --remote option set properly. + +Then, it searches for a file named 'filename'. If it's not found in +the current dir, it will assume it's a blob. + +The content retrieved in the file (or in the blob) will then be parsed +by the remote mediawiki and combined with a template retrieved from +the mediawiki. + +Finally, preview will save the HTML result in a file. and autoload it +in your default web browser if the option --autoload is present. + +OPTIONS: + -r <remote name>, --remote <remote name> + If the remote is a mediawiki, the template and the parse engine + used for the preview will be those of that remote. + If not, a list of valid remotes will be shown. + + -a, --autoload + Try to load the HTML output in a new tab (or new window) of your + default web browser. + + -o <output filename>, --output <output filename> + Change the HTML output filename. Default filename is based on the + input filename with its extension replaced by '.html'. + + -v, --verbose + Show more information on what's going on under the hood. +END + exit; +} + +sub preview { + my $wiki; + my ($remote_url, $wiki_page_name); + my ($new_content, $template); + my $file_content; + + if ($file_name eq EMPTY) { + die "Missing file argument, see `git mw help`\n"; + } + + v_print("### Selecting remote\n"); + if ($remote_name eq EMPTY) { + $remote_name = find_upstream_remote_name(); + if ($remote_name) { + $remote_url = mediawiki_remote_url_maybe($remote_name); + } + + if (! $remote_url) { + my @valid_remotes = find_mediawiki_remotes(); + + if ($#valid_remotes == 0) { + print {*STDERR} "No mediawiki remote in this repo. \n"; + exit 1; + } else { + my $remotes_list = join("\n\t", @valid_remotes); + print {*STDERR} <<"MESSAGE"; +There are multiple mediawiki remotes, which of: + ${remotes_list} +do you want ? Use the -r option to specify the remote. +MESSAGE + } + + exit 1; + } + } else { + if (!is_valid_remote($remote_name)) { + die "${remote_name} is not a remote\n"; + } + + $remote_url = mediawiki_remote_url_maybe($remote_name); + if (! $remote_url) { + die "${remote_name} is not a mediawiki remote\n"; + } + } + v_print("selected remote:\n\tname: ${remote_name}\n\turl: ${remote_url}\n"); + + $wiki = connect_maybe($wiki, $remote_name, $remote_url); + + # Read file content + if (! -e $file_name) { + $file_content = git_cmd_try { + Git::command('cat-file', 'blob', $file_name); } + "%s failed w/ code %d"; + + if ($file_name =~ /(.+):(.+)/) { + $file_name = $2; + } + } else { + open my $read_fh, "<", $file_name + or die "could not open ${file_name}: $!\n"; + $file_content = do { local $/ = undef; <$read_fh> }; + close $read_fh + or die "unable to close: $!\n"; + } + + v_print("### Retrieving template\n"); + ($wiki_page_name = clean_filename($file_name)) =~ s/\.[^.]+$//; + $template = get_template($remote_url, $wiki_page_name); + + v_print("### Parsing local content\n"); + $new_content = $wiki->api({ + action => 'parse', + text => $file_content, + title => $wiki_page_name + }, { + skip_encoding => 1 + }) or die "No response from remote mediawiki\n"; + $new_content = $new_content->{'parse'}->{'text'}->{'*'}; + + v_print("### Merging contents\n"); + if ($preview_file_name eq EMPTY) { + ($preview_file_name = $file_name) =~ s/\.[^.]+$/.html/; + } + open(my $save_fh, '>:encoding(UTF-8)', $preview_file_name) + or die "Could not open: $!\n"; + print {$save_fh} merge_contents($template, $new_content, $remote_url); + close($save_fh) + or die "Could not close: $!\n"; + + v_print("### Results\n"); + if ($autoload) { + v_print("Launching browser w/ file: ${preview_file_name}"); + system('git', 'web--browse', $preview_file_name); + } else { + print {*STDERR} "Preview file saved as: ${preview_file_name}\n"; + } + + exit; +} + +# uses global scope variable: $remote_name +sub merge_contents { + my $template = shift; + my $content = shift; + my $remote_url = shift; + my ($content_tree, $html_tree, $mw_content_text); + my $template_content_id = 'bodyContent'; + + $html_tree = HTML::TreeBuilder->new; + $html_tree->parse($template); + + $content_tree = HTML::TreeBuilder->new; + $content_tree->parse($content); + + $template_content_id = Git::config("remote.${remote_name}.mwIDcontent") + || $template_content_id; + v_print("Using '${template_content_id}' as the content ID\n"); + + $mw_content_text = $html_tree->look_down('id', $template_content_id); + if (!defined $mw_content_text) { + print {*STDERR} <<"CONFIG"; +Could not combine the new content with the template. You might want to +configure `mediawiki.IDContent` in your config: + git config --add remote.${remote_name}.mwIDcontent <id> +and re-run the command afterward. +CONFIG + exit 1; + } + $mw_content_text->delete_content(); + $mw_content_text->push_content($content_tree); + + make_links_absolute($html_tree, $remote_url); + + return $html_tree->as_HTML; +} + +sub make_links_absolute { + my $html_tree = shift; + my $remote_url = shift; + for (@{ $html_tree->extract_links() }) { + my ($link, $element, $attr) = @{ $_ }; + my $url = url($link)->canonical; + if ($url !~ /#/) { + $element->attr($attr, URI->new_abs($url, $remote_url)); + } + } + return $html_tree; +} + +sub is_valid_remote { + my $remote = shift; + my @remotes = git_cmd_try { + Git::command('remote') } + "%s failed w/ code %d"; + my $found_remote = 0; + foreach my $remote (@remotes) { + if ($remote eq $remote) { + $found_remote = 1; + last; + } + } + return $found_remote; +} + +sub find_mediawiki_remotes { + my @remotes = git_cmd_try { + Git::command('remote'); } + "%s failed w/ code %d"; + my $remote_url; + my @valid_remotes = (); + foreach my $remote (@remotes) { + $remote_url = mediawiki_remote_url_maybe($remote); + if ($remote_url) { + push(@valid_remotes, $remote); + } + } + return @valid_remotes; +} + +sub find_upstream_remote_name { + my $current_branch = git_cmd_try { + Git::command_oneline('symbolic-ref', '--short', 'HEAD') } + "%s failed w/ code %d"; + return Git::config("branch.${current_branch}.remote"); +} + +sub mediawiki_remote_url_maybe { + my $remote = shift; + + # Find remote url + my $remote_url = Git::config("remote.${remote}.url"); + if ($remote_url =~ s/mediawiki::(.*)/$1/) { + return url($remote_url)->canonical; + } + + return; +} + +sub get_template { + my $url = shift; + my $page_name = shift; + my ($req, $res, $code, $url_after); + + $req = LWP::UserAgent->new; + if ($verbose) { + $req->show_progress(1); + } + + $res = $req->get("${url}/index.php?title=${page_name}"); + if (!$res->is_success) { + $code = $res->code; + $url_after = $res->request()->uri(); # resolve all redirections + if ($code == HTTP_CODE_PAGE_NOT_FOUND) { + if ($verbose) { + print {*STDERR} <<"WARNING"; +Warning: Failed to retrieve '$page_name'. Create it on the mediawiki if you want +all the links to work properly. +Trying to use the mediawiki homepage as a fallback template ... +WARNING + } + + # LWP automatically redirects GET request + $res = $req->get("${url}/index.php"); + if (!$res->is_success) { + $url_after = $res->request()->uri(); # resolve all redirections + die "Failed to get homepage @ ${url_after} w/ code ${code}\n"; + } + } else { + die "Failed to get '${page_name}' @ ${url_after} w/ code ${code}\n"; + } + } + + return $res->decoded_content; +} + +############################## Help Functions ################################## + +sub help { + print {*STDOUT} <<'END'; +usage: git mw <command> <args> + +git mw commands are: + help Display help information about git mw + preview Parse and render local file into HTML +END + exit; +} diff --git a/contrib/mw-to-git/git-remote-mediawiki.perl b/contrib/mw-to-git/git-remote-mediawiki.perl index d09f5da668..f8d7d2ca6c 100755 --- a/contrib/mw-to-git/git-remote-mediawiki.perl +++ b/contrib/mw-to-git/git-remote-mediawiki.perl @@ -14,6 +14,8 @@ use strict; use MediaWiki::API; use Git; +use Git::Mediawiki qw(clean_filename smudge_filename connect_maybe + EMPTY HTTP_CODE_OK); use DateTime::Format::ISO8601; use warnings; @@ -23,9 +25,6 @@ binmode STDOUT, ':encoding(UTF-8)'; use URI::Escape; -# Mediawiki filenames can contain forward slashes. This variable decides by which pattern they should be replaced -use constant SLASH_REPLACEMENT => '%2F'; - # It's not always possible to delete pages (may require some # privileges). Deleted pages are replaced with this content. use constant DELETED_CONTENT => "[[Category:Deleted]]\n"; @@ -40,8 +39,6 @@ use constant NULL_SHA1 => '0000000000000000000000000000000000000000'; # Used on Git's side to reflect empty edit messages on the wiki use constant EMPTY_MESSAGE => '*Empty MediaWiki Message*'; -use constant EMPTY => q{}; - # Number of pages taken into account at once in submodule get_mw_page_list use constant SLICE_SIZE => 50; @@ -50,8 +47,6 @@ use constant SLICE_SIZE => 50; # the number of links to be returned (500 links max). use constant BATCH_SIZE => 10; -use constant HTTP_CODE_OK => 200; - if (@ARGV != 2) { exit_error_usage(); } @@ -199,37 +194,6 @@ sub parse_command { # MediaWiki API instance, created lazily. my $mediawiki; -sub mw_connect_maybe { - if ($mediawiki) { - return; - } - $mediawiki = MediaWiki::API->new; - $mediawiki->{config}->{api_url} = "${url}/api.php"; - if ($wiki_login) { - my %credential = ( - 'url' => $url, - 'username' => $wiki_login, - 'password' => $wiki_passwd - ); - Git::credential(\%credential); - my $request = {lgname => $credential{username}, - lgpassword => $credential{password}, - lgdomain => $wiki_domain}; - if ($mediawiki->login($request)) { - Git::credential(\%credential, 'approve'); - print {*STDERR} qq(Logged in mediawiki user "$credential{username}".\n); - } else { - print {*STDERR} qq(Failed to log in mediawiki user "$credential{username}" on ${url}\n); - print {*STDERR} ' (error ' . - $mediawiki->{error}->{code} . ': ' . - $mediawiki->{error}->{details} . ")\n"; - Git::credential(\%credential, 'reject'); - exit 1; - } - } - return; -} - sub fatal_mw_error { my $action = shift; print STDERR "fatal: could not $action.\n"; @@ -339,7 +303,7 @@ sub get_mw_first_pages { # Get the list of pages to be fetched according to configuration. sub get_mw_pages { - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); print {*STDERR} "Listing pages on remote wiki...\n"; @@ -529,7 +493,7 @@ sub get_last_local_revision { # avoid a loop onto all tracked pages. This is useful for the fetch-by-rev # option. sub get_last_global_remote_rev { - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); my $query = { action => 'query', @@ -545,7 +509,7 @@ sub get_last_global_remote_rev { # Get the last remote revision concerning the tracked pages and the tracked # categories. sub get_last_remote_revision { - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); my %pages_hash = get_mw_pages(); my @pages = values(%pages_hash); @@ -601,29 +565,6 @@ sub mediawiki_smudge { return "${string}\n"; } -sub mediawiki_clean_filename { - my $filename = shift; - $filename =~ s{@{[SLASH_REPLACEMENT]}}{/}g; - # [, ], |, {, and } are forbidden by MediaWiki, even URL-encoded. - # Do a variant of URL-encoding, i.e. looks like URL-encoding, - # but with _ added to prevent MediaWiki from thinking this is - # an actual special character. - $filename =~ s/[\[\]\{\}\|]/sprintf("_%%_%x", ord($&))/ge; - # If we use the uri escape before - # we should unescape here, before anything - - return $filename; -} - -sub mediawiki_smudge_filename { - my $filename = shift; - $filename =~ s{/}{@{[SLASH_REPLACEMENT]}}g; - $filename =~ s/ /_/g; - # Decode forbidden characters encoded in mediawiki_clean_filename - $filename =~ s/_%_([0-9a-fA-F][0-9a-fA-F])/sprintf('%c', hex($1))/ge; - return $filename; -} - sub literal_data { my ($content) = @_; print {*STDOUT} 'data ', bytes::length($content), "\n", $content; @@ -831,7 +772,7 @@ sub mw_import_ref { return; } - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); print {*STDERR} "Searching revisions...\n"; my $last_local = get_last_local_revision(); @@ -945,7 +886,7 @@ sub mw_import_revids { my %commit; $commit{author} = $rev->{user} || 'Anonymous'; $commit{comment} = $rev->{comment} || EMPTY_MESSAGE; - $commit{title} = mediawiki_smudge_filename($page_title); + $commit{title} = smudge_filename($page_title); $commit{mw_revision} = $rev->{revid}; $commit{content} = mediawiki_smudge($rev->{'*'}); @@ -1006,7 +947,7 @@ sub mw_upload_file { } # Deleting and uploading a file requires a priviledged user if ($file_deleted) { - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); my $query = { action => 'delete', title => $path, @@ -1022,7 +963,7 @@ sub mw_upload_file { # Don't let perl try to interpret file content as UTF-8 => use "raw" my $content = run_git("cat-file blob ${new_sha1}", 'raw'); if ($content ne EMPTY) { - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); $mediawiki->{config}->{upload_url} = "${url}/index.php/Special:Upload"; $mediawiki->edit({ @@ -1070,7 +1011,7 @@ sub mw_push_file { my $old_sha1 = $diff_info_split[2]; my $page_created = ($old_sha1 eq NULL_SHA1); my $page_deleted = ($new_sha1 eq NULL_SHA1); - $complete_file_name = mediawiki_clean_filename($complete_file_name); + $complete_file_name = clean_filename($complete_file_name); my ($title, $extension) = $complete_file_name =~ /^(.*)\.([^\.]*)$/; if (!defined($extension)) { @@ -1093,7 +1034,7 @@ sub mw_push_file { $file_content = run_git("cat-file blob ${new_sha1}"); } - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); my $result = $mediawiki->edit( { action => 'edit', @@ -1279,7 +1220,7 @@ sub mw_push_revision { } sub get_allowed_file_extensions { - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); my $query = { action => 'query', @@ -1303,7 +1244,7 @@ my %cached_mw_namespace_id; # Return MediaWiki id for a canonical namespace name. # Ex.: "File", "Project". sub get_mw_namespace_id { - mw_connect_maybe(); + $mediawiki = connect_maybe($mediawiki, $remotename, $url); my $name = shift; if (!exists $namespace_id{$name}) { diff --git a/contrib/mw-to-git/t/test-gitmw-lib.sh b/contrib/mw-to-git/t/test-gitmw-lib.sh index bb76cee379..ca6860ff30 100755 --- a/contrib/mw-to-git/t/test-gitmw-lib.sh +++ b/contrib/mw-to-git/t/test-gitmw-lib.sh @@ -62,12 +62,8 @@ test_check_precond () { test_done fi - if [ ! -f "$GIT_BUILD_DIR"/git-remote-mediawiki ]; - then - echo "No remote mediawiki for git found. Copying it in git" - echo "cp $GIT_BUILD_DIR/contrib/mw-to-git/git-remote-mediawiki $GIT_BUILD_DIR/" - ln -s "$GIT_BUILD_DIR"/contrib/mw-to-git/git-remote-mediawiki "$GIT_BUILD_DIR" - fi + GIT_EXEC_PATH=$(cd "$(dirname "$0")" && cd "../.." && pwd) + PATH="$GIT_EXEC_PATH"'/bin-wrapper:'"$PATH" if [ ! -d "$WIKI_DIR_INST/$WIKI_DIR_NAME" ]; then diff --git a/git-rebase--interactive.sh b/git-rebase--interactive.sh index 157690b685..83d6d4676b 100644 --- a/git-rebase--interactive.sh +++ b/git-rebase--interactive.sh @@ -864,8 +864,11 @@ comment_for_reflog start if test ! -z "$switch_to" then + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" output git checkout "$switch_to" -- || - die "Could not checkout $switch_to" + die "Could not checkout $switch_to" + + comment_for_reflog start fi orig_head=$(git rev-parse --verify HEAD) || die "No HEAD?" @@ -1007,6 +1010,7 @@ has_action "$todo" || test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks +GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" output git checkout $onto || die_abort "could not detach HEAD" git update-ref ORIG_HEAD $orig_head do_rest diff --git a/git-rebase.sh b/git-rebase.sh index 81b0346a5d..0039ecfb40 100755 --- a/git-rebase.sh +++ b/git-rebase.sh @@ -542,7 +542,9 @@ then if test -z "$force_rebase" then # Lazily switch to the target branch if needed... - test -z "$switch_to" || git checkout "$switch_to" -- + test -z "$switch_to" || + GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \ + git checkout "$switch_to" -- say "$(eval_gettext "Current branch \$branch_name is up to date.")" finish_rebase exit 0 @@ -568,7 +570,9 @@ test "$type" = interactive && run_specific_rebase # Detach HEAD and reset the tree say "$(gettext "First, rewinding head to replay your work on top of it...")" -git checkout -q "$onto^0" || die "could not detach HEAD" + +GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \ + git checkout -q "$onto^0" || die "could not detach HEAD" git update-ref ORIG_HEAD $orig_head # If the $onto is a proper descendant of the tip of the branch, then diff --git a/line-log.c b/line-log.c index 4bbb09be59..8cc29a0000 100644 --- a/line-log.c +++ b/line-log.c @@ -116,7 +116,8 @@ static void sort_and_merge_range_set(struct range_set *rs) for (i = 1; i < rs->nr; i++) { if (rs->ranges[i].start <= rs->ranges[o-1].end) { - rs->ranges[o-1].end = rs->ranges[i].end; + if (rs->ranges[o-1].end < rs->ranges[i].end) + rs->ranges[o-1].end = rs->ranges[i].end; } else { rs->ranges[o].start = rs->ranges[i].start; rs->ranges[o].end = rs->ranges[i].end; diff --git a/pack-revindex.c b/pack-revindex.c index 77a0465be6..b4d2b35bb3 100644 --- a/pack-revindex.c +++ b/pack-revindex.c @@ -59,11 +59,101 @@ static void init_pack_revindex(void) /* revindex elements are lazily initialized */ } -static int cmp_offset(const void *a_, const void *b_) +/* + * This is a least-significant-digit radix sort. + * + * It sorts each of the "n" items in "entries" by its offset field. The "max" + * parameter must be at least as large as the largest offset in the array, + * and lets us quit the sort early. + */ +static void sort_revindex(struct revindex_entry *entries, unsigned n, off_t max) { - const struct revindex_entry *a = a_; - const struct revindex_entry *b = b_; - return (a->offset < b->offset) ? -1 : (a->offset > b->offset) ? 1 : 0; + /* + * We use a "digit" size of 16 bits. That keeps our memory + * usage reasonable, and we can generally (for a 4G or smaller + * packfile) quit after two rounds of radix-sorting. + */ +#define DIGIT_SIZE (16) +#define BUCKETS (1 << DIGIT_SIZE) + /* + * We want to know the bucket that a[i] will go into when we are using + * the digit that is N bits from the (least significant) end. + */ +#define BUCKET_FOR(a, i, bits) (((a)[(i)].offset >> (bits)) & (BUCKETS-1)) + + /* + * We need O(n) temporary storage. Rather than do an extra copy of the + * partial results into "entries", we sort back and forth between the + * real array and temporary storage. In each iteration of the loop, we + * keep track of them with alias pointers, always sorting from "from" + * to "to". + */ + struct revindex_entry *tmp = xmalloc(n * sizeof(*tmp)); + struct revindex_entry *from = entries, *to = tmp; + int bits; + unsigned *pos = xmalloc(BUCKETS * sizeof(*pos)); + + /* + * If (max >> bits) is zero, then we know that the radix digit we are + * on (and any higher) will be zero for all entries, and our loop will + * be a no-op, as everybody lands in the same zero-th bucket. + */ + for (bits = 0; max >> bits; bits += DIGIT_SIZE) { + struct revindex_entry *swap; + unsigned i; + + memset(pos, 0, BUCKETS * sizeof(*pos)); + + /* + * We want pos[i] to store the index of the last element that + * will go in bucket "i" (actually one past the last element). + * To do this, we first count the items that will go in each + * bucket, which gives us a relative offset from the last + * bucket. We can then cumulatively add the index from the + * previous bucket to get the true index. + */ + for (i = 0; i < n; i++) + pos[BUCKET_FOR(from, i, bits)]++; + for (i = 1; i < BUCKETS; i++) + pos[i] += pos[i-1]; + + /* + * Now we can drop the elements into their correct buckets (in + * our temporary array). We iterate the pos counter backwards + * to avoid using an extra index to count up. And since we are + * going backwards there, we must also go backwards through the + * array itself, to keep the sort stable. + * + * Note that we use an unsigned iterator to make sure we can + * handle 2^32-1 objects, even on a 32-bit system. But this + * means we cannot use the more obvious "i >= 0" loop condition + * for counting backwards, and must instead check for + * wrap-around with UINT_MAX. + */ + for (i = n - 1; i != UINT_MAX; i--) + to[--pos[BUCKET_FOR(from, i, bits)]] = from[i]; + + /* + * Now "to" contains the most sorted list, so we swap "from" and + * "to" for the next iteration. + */ + swap = from; + from = to; + to = swap; + } + + /* + * If we ended with our data in the original array, great. If not, + * we have to move it back from the temporary storage. + */ + if (from != entries) + memcpy(entries, tmp, n * sizeof(*entries)); + free(tmp); + free(pos); + +#undef BUCKET_FOR +#undef BUCKETS +#undef DIGIT_SIZE } /* @@ -72,8 +162,8 @@ static int cmp_offset(const void *a_, const void *b_) static void create_pack_revindex(struct pack_revindex *rix) { struct packed_git *p = rix->p; - int num_ent = p->num_objects; - int i; + unsigned num_ent = p->num_objects; + unsigned i; const char *index = p->index_data; rix->revindex = xmalloc(sizeof(*rix->revindex) * (num_ent + 1)); @@ -108,13 +198,13 @@ static void create_pack_revindex(struct pack_revindex *rix) */ rix->revindex[num_ent].offset = p->pack_size - 20; rix->revindex[num_ent].nr = -1; - qsort(rix->revindex, num_ent, sizeof(*rix->revindex), cmp_offset); + sort_revindex(rix->revindex, num_ent, p->pack_size); } struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs) { int num; - int lo, hi; + unsigned lo, hi; struct pack_revindex *rix; struct revindex_entry *revindex; @@ -132,7 +222,7 @@ struct revindex_entry *find_pack_revindex(struct packed_git *p, off_t ofs) lo = 0; hi = p->num_objects + 1; do { - int mi = (lo + hi) / 2; + unsigned mi = lo + (hi - lo) / 2; if (revindex[mi].offset == ofs) { return revindex + mi; } else if (ofs < revindex[mi].offset) @@ -1302,6 +1302,14 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds free(sent_tips.tip); } +static void prepare_ref_index(struct string_list *ref_index, struct ref *ref) +{ + for ( ; ref; ref = ref->next) + string_list_append_nodup(ref_index, ref->name)->util = ref; + + sort_string_list(ref_index); +} + /* * Given the set of refs the local repository has, the set of refs the * remote repository has, and the refspec used for push, determine @@ -1320,6 +1328,7 @@ int match_push_refs(struct ref *src, struct ref **dst, int errs; static const char *default_refspec[] = { ":", NULL }; struct ref *ref, **dst_tail = tail_ref(dst); + struct string_list dst_ref_index = STRING_LIST_INIT_NODUP; if (!nr_refspec) { nr_refspec = 1; @@ -1330,6 +1339,7 @@ int match_push_refs(struct ref *src, struct ref **dst, /* pick the remainder */ for (ref = src; ref; ref = ref->next) { + struct string_list_item *dst_item; struct ref *dst_peer; const struct refspec *pat = NULL; char *dst_name; @@ -1338,7 +1348,11 @@ int match_push_refs(struct ref *src, struct ref **dst, if (!dst_name) continue; - dst_peer = find_ref_by_name(*dst, dst_name); + if (!dst_ref_index.nr) + prepare_ref_index(&dst_ref_index, *dst); + + dst_item = string_list_lookup(&dst_ref_index, dst_name); + dst_peer = dst_item ? dst_item->util : NULL; if (dst_peer) { if (dst_peer->peer_ref) /* We're already sending something to this ref. */ @@ -1355,6 +1369,8 @@ int match_push_refs(struct ref *src, struct ref **dst, /* Create a new one and link it */ dst_peer = make_linked_ref(dst_name, &dst_tail); hashcpy(dst_peer->new_sha1, ref->new_sha1); + string_list_insert(&dst_ref_index, + dst_peer->name)->util = dst_peer; } dst_peer->peer_ref = copy_ref(ref); dst_peer->force = pat->force; @@ -1362,10 +1378,13 @@ int match_push_refs(struct ref *src, struct ref **dst, free(dst_name); } + string_list_clear(&dst_ref_index, 0); + if (flags & MATCH_REFS_FOLLOW_TAGS) add_missing_tags(src, dst, &dst_tail); if (send_prune) { + struct string_list src_ref_index = STRING_LIST_INIT_NODUP; /* check for missing refs on the remote */ for (ref = *dst; ref; ref = ref->next) { char *src_name; @@ -1376,11 +1395,15 @@ int match_push_refs(struct ref *src, struct ref **dst, src_name = get_ref_match(rs, nr_refspec, ref, send_mirror, FROM_DST, NULL); if (src_name) { - if (!find_ref_by_name(src, src_name)) + if (!src_ref_index.nr) + prepare_ref_index(&src_ref_index, src); + if (!string_list_has_string(&src_ref_index, + src_name)) ref->peer_ref = alloc_delete_ref(); free(src_name); } } + string_list_clear(&src_ref_index, 0); } if (errs) return -1; diff --git a/sha1_file.c b/sha1_file.c index 0af19c00f1..4c2365f48f 100644 --- a/sha1_file.c +++ b/sha1_file.c @@ -1697,7 +1697,8 @@ static int retry_bad_packed_offset(struct packed_git *p, off_t obj_offset) #define POI_STACK_PREALLOC 64 static int packed_object_info(struct packed_git *p, off_t obj_offset, - unsigned long *sizep, int *rtype) + unsigned long *sizep, int *rtype, + unsigned long *disk_sizep) { struct pack_window *w_curs = NULL; unsigned long size; @@ -1731,6 +1732,11 @@ static int packed_object_info(struct packed_git *p, off_t obj_offset, } } + if (disk_sizep) { + struct revindex_entry *revidx = find_pack_revindex(p, obj_offset); + *disk_sizep = revidx[1].offset - obj_offset; + } + while (type == OBJ_OFS_DELTA || type == OBJ_REF_DELTA) { off_t base_offset; /* Push the object we're going to leave behind */ @@ -2357,7 +2363,8 @@ struct packed_git *find_sha1_pack(const unsigned char *sha1, } -static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *sizep) +static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *sizep, + unsigned long *disk_sizep) { int status; unsigned long mapsize, size; @@ -2368,6 +2375,8 @@ static int sha1_loose_object_info(const unsigned char *sha1, unsigned long *size map = map_sha1_file(sha1, &mapsize); if (!map) return -1; + if (disk_sizep) + *disk_sizep = mapsize; if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0) status = error("unable to unpack %s header", sha1_to_hex(sha1)); @@ -2391,13 +2400,15 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) if (co) { if (oi->sizep) *(oi->sizep) = co->size; + if (oi->disk_sizep) + *(oi->disk_sizep) = 0; oi->whence = OI_CACHED; return co->type; } if (!find_pack_entry(sha1, &e)) { /* Most likely it's a loose object. */ - status = sha1_loose_object_info(sha1, oi->sizep); + status = sha1_loose_object_info(sha1, oi->sizep, oi->disk_sizep); if (status >= 0) { oi->whence = OI_LOOSE; return status; @@ -2409,7 +2420,8 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) return status; } - status = packed_object_info(e.p, e.offset, oi->sizep, &rtype); + status = packed_object_info(e.p, e.offset, oi->sizep, &rtype, + oi->disk_sizep); if (status < 0) { mark_bad_packed_object(e.p, sha1); status = sha1_object_info_extended(sha1, oi); @@ -2428,7 +2440,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi) int sha1_object_info(const unsigned char *sha1, unsigned long *sizep) { - struct object_info oi; + struct object_info oi = {0}; oi.sizep = sizep; return sha1_object_info_extended(sha1, &oi); diff --git a/streaming.c b/streaming.c index cabcd9d157..cac282f06b 100644 --- a/streaming.c +++ b/streaming.c @@ -135,7 +135,7 @@ struct git_istream *open_istream(const unsigned char *sha1, struct stream_filter *filter) { struct git_istream *st; - struct object_info oi; + struct object_info oi = {0}; const unsigned char *real = lookup_replace_object(sha1); enum input_source src = istream_source(real, type, &oi); diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 9cc5c6bf4d..d499d02a29 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -36,66 +36,54 @@ $content" ' test_expect_success "Type of $type is correct" ' - test $type = "$(git cat-file -t $sha1)" + echo $type >expect && + git cat-file -t $sha1 >actual && + test_cmp expect actual ' test_expect_success "Size of $type is correct" ' - test $size = "$(git cat-file -s $sha1)" + echo $size >expect && + git cat-file -s $sha1 >actual && + test_cmp expect actual ' test -z "$content" || test_expect_success "Content of $type is correct" ' - expect="$(maybe_remove_timestamp "$content" $no_ts)" - actual="$(maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts)" - - if test "z$expect" = "z$actual" - then - : happy - else - echo "Oops: expected $expect" - echo "but got $actual" - false - fi + maybe_remove_timestamp "$content" $no_ts >expect && + maybe_remove_timestamp "$(git cat-file $type $sha1)" $no_ts >actual && + test_cmp expect actual ' test_expect_success "Pretty content of $type is correct" ' - expect="$(maybe_remove_timestamp "$pretty_content" $no_ts)" - actual="$(maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts)" - if test "z$expect" = "z$actual" - then - : happy - else - echo "Oops: expected $expect" - echo "but got $actual" - false - fi + maybe_remove_timestamp "$pretty_content" $no_ts >expect && + maybe_remove_timestamp "$(git cat-file -p $sha1)" $no_ts >actual && + test_cmp expect actual ' test -z "$content" || test_expect_success "--batch output of $type is correct" ' - expect="$(maybe_remove_timestamp "$batch_output" $no_ts)" - actual="$(maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts)" - if test "z$expect" = "z$actual" - then - : happy - else - echo "Oops: expected $expect" - echo "but got $actual" - false - fi + maybe_remove_timestamp "$batch_output" $no_ts >expect && + maybe_remove_timestamp "$(echo $sha1 | git cat-file --batch)" $no_ts >actual && + test_cmp expect actual ' test_expect_success "--batch-check output of $type is correct" ' - expect="$sha1 $type $size" - actual="$(echo_without_newline $sha1 | git cat-file --batch-check)" - if test "z$expect" = "z$actual" - then - : happy - else - echo "Oops: expected $expect" - echo "but got $actual" - false - fi + echo "$sha1 $type $size" >expect && + echo_without_newline $sha1 | git cat-file --batch-check >actual && + test_cmp expect actual + ' + + test_expect_success "custom --batch-check format" ' + echo "$type $sha1" >expect && + echo $sha1 | git cat-file --batch-check="%(objecttype) %(objectname)" >actual && + test_cmp expect actual + ' + + test_expect_success '--batch-check with %(rest)' ' + echo "$type this is some extra content" >expect && + echo "$sha1 this is some extra content" | + git cat-file --batch-check="%(objecttype) %(rest)" >actual && + test_cmp expect actual ' } diff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh index 8a6ec039fe..49ccb38f88 100755 --- a/t/t3404-rebase-interactive.sh +++ b/t/t3404-rebase-interactive.sh @@ -926,6 +926,21 @@ test_expect_success 'rebase --edit-todo can be used to modify todo' ' test L = $(git cat-file commit HEAD | sed -ne \$p) ' +test_expect_success 'rebase -i produces readable reflog' ' + git reset --hard && + git branch -f branch-reflog-test H && + git rebase -i --onto I F branch-reflog-test && + cat >expect <<-\EOF && + rebase -i (start): checkout I + rebase -i (pick): G + rebase -i (pick): H + rebase -i (finish): returning to refs/heads/branch-reflog-test + EOF + tail -n 4 .git/logs/HEAD | + sed -e "s/.* //" >actual && + test_cmp expect actual +' + test_expect_success 'rebase -i respects core.commentchar' ' git reset --hard && git checkout E^0 && diff --git a/t/t3900-i18n-commit.sh b/t/t3900-i18n-commit.sh index 37ddabba2d..38b00c37b0 100755 --- a/t/t3900-i18n-commit.sh +++ b/t/t3900-i18n-commit.sh @@ -39,6 +39,42 @@ test_expect_failure 'UTF-16 refused because of NULs' ' git commit -a -F "$TEST_DIRECTORY"/t3900/UTF-16.txt ' +test_expect_success 'UTF-8 invalid characters refused' ' + test_when_finished "rm -f $HOME/stderr $HOME/invalid" && + echo "UTF-8 characters" >F && + printf "Commit message\n\nInvalid surrogate:\355\240\200\n" \ + >"$HOME/invalid" && + git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr && + grep "did not conform" "$HOME"/stderr +' + +test_expect_success 'UTF-8 overlong sequences rejected' ' + test_when_finished "rm -f $HOME/stderr $HOME/invalid" && + rm -f "$HOME/stderr" "$HOME/invalid" && + echo "UTF-8 overlong" >F && + printf "\340\202\251ommit message\n\nThis is not a space:\300\240\n" \ + >"$HOME/invalid" && + git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr && + grep "did not conform" "$HOME"/stderr +' + +test_expect_success 'UTF-8 non-characters refused' ' + test_when_finished "rm -f $HOME/stderr $HOME/invalid" && + echo "UTF-8 non-character 1" >F && + printf "Commit message\n\nNon-character:\364\217\277\276\n" \ + >"$HOME/invalid" && + git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr && + grep "did not conform" "$HOME"/stderr +' + +test_expect_success 'UTF-8 non-characters refused' ' + test_when_finished "rm -f $HOME/stderr $HOME/invalid" && + echo "UTF-8 non-character 2." >F && + printf "Commit message\n\nNon-character:\357\267\220\n" \ + >"$HOME/invalid" && + git commit -a -F "$HOME/invalid" 2>"$HOME"/stderr && + grep "did not conform" "$HOME"/stderr +' for H in ISO8859-1 eucJP ISO-2022-JP do diff --git a/t/t4211/expect.multiple-superset b/t/t4211/expect.multiple-superset index a1f5bc49c8..d930b6eec4 100644 --- a/t/t4211/expect.multiple-superset +++ b/t/t4211/expect.multiple-superset @@ -1,3 +1,100 @@ +commit 4659538844daa2849b1a9e7d6fadb96fcd26fc83 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:43 2013 +0100 + + change back to complete line + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -4,19 +4,21 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; +-} +\ No newline at end of file ++} ++ ++/* incomplete lines are bad! */ + +commit 100b61a6f2f720f812620a9d10afb3a960ccb73c +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:48:10 2013 +0100 + + change to an incomplete line at end + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -4,19 +4,19 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* + * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; +-} ++} +\ No newline at end of file + +commit 39b6eb2d5b706d3322184a169f666f25ed3fbd00 +Author: Thomas Rast <trast@student.ethz.ch> +Date: Thu Feb 28 10:45:41 2013 +0100 + + touch comment + +diff --git a/a.c b/a.c +--- a/a.c ++++ b/a.c +@@ -3,19 +3,19 @@ + long f(long x) + { + int s = 0; + while (x) { + x >>= 1; + s++; + } + return s; + } + + /* +- * A comment. ++ * This is only an example! + */ + + int main () + { + printf("%ld\n", f(15)); + return 0; + } + commit a6eb82647d5d67f893da442f8f9375fd89a3b1e2 Author: Thomas Rast <trast@student.ethz.ch> Date: Thu Feb 28 10:45:16 2013 +0100 @@ -7,7 +104,7 @@ Date: Thu Feb 28 10:45:16 2013 +0100 diff --git a/a.c b/a.c --- a/a.c +++ b/a.c -@@ -3,9 +3,9 @@ +@@ -3,19 +3,19 @@ -int f(int x) +long f(long x) { @@ -18,6 +115,17 @@ diff --git a/a.c b/a.c } return s; } + + /* + * A comment. + */ + + int main () + { +- printf("%d\n", f(15)); ++ printf("%ld\n", f(15)); + return 0; + } commit f04fb20f2c77850996cba739709acc6faecc58f7 Author: Thomas Rast <trast@student.ethz.ch> @@ -28,7 +136,7 @@ Date: Thu Feb 28 10:44:55 2013 +0100 diff --git a/a.c b/a.c --- a/a.c +++ b/a.c -@@ -3,8 +3,9 @@ +@@ -3,18 +3,19 @@ int f(int x) { int s = 0; @@ -38,6 +146,16 @@ diff --git a/a.c b/a.c } + return s; } + + /* + * A comment. + */ + + int main () + { + printf("%d\n", f(15)); + return 0; + } commit de4c48ae814792c02a49c4c3c0c757ae69c55f6a Author: Thomas Rast <trast@student.ethz.ch> @@ -48,7 +166,7 @@ Date: Thu Feb 28 10:44:48 2013 +0100 diff --git a/a.c b/a.c --- /dev/null +++ b/a.c -@@ -0,0 +3,8 @@ +@@ -0,0 +3,18 @@ +int f(int x) +{ + int s = 0; @@ -57,3 +175,13 @@ diff --git a/a.c b/a.c + s++; + } +} ++ ++/* ++ * A comment. ++ */ ++ ++int main () ++{ ++ printf("%d\n", f(15)); ++ return 0; ++} diff --git a/t/t5710-info-alternate.sh b/t/t5710-info-alternate.sh index 8956c21617..5a6e49d18d 100755 --- a/t/t5710-info-alternate.sh +++ b/t/t5710-info-alternate.sh @@ -58,7 +58,13 @@ test_expect_success 'creating too deep nesting' \ git clone -l -s D E && git clone -l -s E F && git clone -l -s F G && -test_must_fail git clone --bare -l -s G H' +git clone --bare -l -s G H' + +test_expect_success 'invalidity of deepest repository' \ +'cd H && { + test_valid_repo + test $? -ne 0 +}' cd "$base_dir" diff --git a/t/test-lib.sh b/t/test-lib.sh index 2d63307351..b490283182 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -92,6 +92,7 @@ unset VISUAL EMAIL LANGUAGE COLUMNS $("$PERL_PATH" -e ' print join("\n", @vars); ') unset XDG_CONFIG_HOME +unset GITPERLLIB GIT_AUTHOR_EMAIL=author@example.com GIT_AUTHOR_NAME='A U Thor' GIT_COMMITTER_EMAIL=committer@example.com diff --git a/wrap-for-bin.sh b/wrap-for-bin.sh index 53a8dd0a3f..701d2339b9 100644 --- a/wrap-for-bin.sh +++ b/wrap-for-bin.sh @@ -14,7 +14,7 @@ else GIT_TEMPLATE_DIR='@@BUILD_DIR@@/templates/blt' export GIT_TEMPLATE_DIR fi -GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib' +GITPERLLIB='@@BUILD_DIR@@/perl/blib/lib'"${GITPERLLIB:+:$GITPERLLIB}" GIT_TEXTDOMAINDIR='@@BUILD_DIR@@/po/build/locale' PATH='@@BUILD_DIR@@/bin-wrappers:'"$PATH" export GIT_EXEC_PATH GITPERLLIB PATH GIT_TEXTDOMAINDIR |