summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--.mailmap187
-rw-r--r--Documentation/RelNotes/1.8.3.3.txt21
-rw-r--r--Documentation/RelNotes/1.8.3.4.txt20
-rw-r--r--Documentation/RelNotes/1.8.4.txt124
-rw-r--r--Documentation/blame-options.txt10
-rw-r--r--Documentation/config.txt25
-rw-r--r--Documentation/diff-options.txt5
-rw-r--r--Documentation/git-cat-file.txt79
-rw-r--r--Documentation/git-check-mailmap.txt47
-rw-r--r--Documentation/git-clean.txt71
-rw-r--r--Documentation/git-clone.txt12
-rw-r--r--Documentation/git-config.txt7
-rw-r--r--Documentation/git-format-patch.txt15
-rw-r--r--Documentation/git-log.txt10
-rw-r--r--Documentation/git-reset.txt2
-rw-r--r--Documentation/git-send-email.txt6
-rw-r--r--Documentation/git-show-ref.txt10
-rw-r--r--Documentation/git-show.txt9
-rw-r--r--Documentation/git-submodule.txt14
-rw-r--r--Documentation/git.txt4
-rw-r--r--Documentation/rev-list-options.txt3
-rw-r--r--Documentation/user-manual.txt4
-rw-r--r--Makefile1
-rw-r--r--advice.h1
-rw-r--r--argv-array.h1
-rw-r--r--builtin.h1
-rw-r--r--builtin/add.c1
-rw-r--r--builtin/apply.c19
-rw-r--r--builtin/cat-file.c210
-rw-r--r--builtin/check-mailmap.c66
-rw-r--r--builtin/checkout.c8
-rw-r--r--builtin/clean.c780
-rw-r--r--builtin/clone.c21
-rw-r--r--builtin/commit.c3
-rw-r--r--builtin/config.c31
-rw-r--r--builtin/describe.c31
-rw-r--r--builtin/grep.c7
-rw-r--r--builtin/log.c24
-rw-r--r--builtin/ls-files.c30
-rw-r--r--builtin/merge-index.c4
-rw-r--r--builtin/merge.c57
-rw-r--r--builtin/name-rev.c119
-rw-r--r--builtin/revert.c2
-rw-r--r--builtin/rm.c6
-rw-r--r--builtin/show-branch.c4
-rw-r--r--builtin/show-ref.c8
-rw-r--r--builtin/update-index.c14
-rw-r--r--cache-tree.c19
-rw-r--r--cache-tree.h2
-rw-r--r--cache.h13
-rw-r--r--command-list.txt1
-rw-r--r--commit.c40
-rw-r--r--commit.h23
-rw-r--r--config.c217
-rw-r--r--contrib/completion/git-completion.bash1
-rw-r--r--contrib/mw-to-git/Git/Mediawiki.pm100
-rw-r--r--contrib/mw-to-git/Makefile33
-rwxr-xr-xcontrib/mw-to-git/bin-wrapper/git14
-rwxr-xr-xcontrib/mw-to-git/git-mw.perl368
-rwxr-xr-xcontrib/mw-to-git/git-remote-mediawiki.perl85
-rwxr-xr-xcontrib/mw-to-git/t/test-gitmw-lib.sh8
-rw-r--r--daemon.c14
-rw-r--r--diff.c36
-rw-r--r--dir.c6
-rw-r--r--entry.c16
-rw-r--r--exec_cmd.h1
-rw-r--r--fetch-pack.c16
-rw-r--r--git-compat-util.h7
-rw-r--r--git-rebase--interactive.sh6
-rwxr-xr-xgit-rebase.sh8
-rwxr-xr-xgit-request-pull.sh14
-rwxr-xr-xgit-send-email.perl47
-rwxr-xr-xgit-submodule.sh30
-rw-r--r--git.c8
-rw-r--r--http-push.c2
-rw-r--r--line-log.c12
-rw-r--r--line-range.c7
-rw-r--r--log-tree.c2
-rw-r--r--mailmap.c67
-rw-r--r--merge-recursive.c7
-rw-r--r--object.c7
-rw-r--r--pack-revindex.c108
-rw-r--r--path.c112
-rw-r--r--pathspec.c4
-rw-r--r--pretty.c48
-rw-r--r--quote.c65
-rw-r--r--quote.h7
-rw-r--r--read-cache.c2
-rw-r--r--refs.c9
-rw-r--r--remote.c27
-rw-r--r--rerere.c12
-rw-r--r--resolve-undo.c6
-rw-r--r--revision.c2
-rw-r--r--revision.h1
-rw-r--r--run-command.c5
-rw-r--r--run-command.h1
-rw-r--r--sequencer.c7
-rw-r--r--setup.c17
-rw-r--r--sha1_file.c22
-rw-r--r--sha1_name.c4
-rw-r--r--shallow.c17
-rw-r--r--shell.c12
-rw-r--r--streaming.c4
-rw-r--r--submodule.c2
-rw-r--r--t/annotate-tests.sh363
-rwxr-xr-xt/t0008-ignores.sh12
-rwxr-xr-xt/t0060-path-utils.sh72
-rwxr-xr-xt/t1006-cat-file.sh74
-rwxr-xr-xt/t1307-config-blob.sh70
-rwxr-xr-xt/t1403-show-ref.sh167
-rwxr-xr-xt/t2202-add-addremove.sh10
-rwxr-xr-xt/t3404-rebase-interactive.sh15
-rwxr-xr-xt/t3900-i18n-commit.sh36
-rwxr-xr-xt/t4000-diff-format.sh48
-rwxr-xr-xt/t4014-format-patch.sh43
-rwxr-xr-xt/t4203-mailmap.sh61
-rwxr-xr-xt/t4211-line-log.sh13
-rw-r--r--t/t4211/expect.multiple-superset134
-rwxr-xr-xt/t5710-info-alternate.sh8
-rwxr-xr-xt/t6021-merge-criss-cross.sh2
-rwxr-xr-xt/t6120-describe.sh24
-rwxr-xr-xt/t7301-clean-interactive.sh439
-rwxr-xr-xt/t7400-submodule-basic.sh16
-rwxr-xr-xt/t7406-submodule-update.sh53
-rwxr-xr-xt/t7600-merge.sh12
-rwxr-xr-xt/t8001-annotate.sh6
-rwxr-xr-xt/t8002-blame.sh12
-rwxr-xr-xt/t9801-git-p4-branch.sh23
-rw-r--r--t/test-lib.sh1
-rw-r--r--test-dump-cache-tree.c4
-rw-r--r--test-path-utils.c32
-rw-r--r--trace.c1
-rw-r--r--transport-helper.c3
-rw-r--r--tree.c2
-rw-r--r--unpack-trees.c14
-rw-r--r--utf8.h1
-rw-r--r--wrap-for-bin.sh2
-rw-r--r--wrapper.c2
-rw-r--r--wt-status.c21
-rw-r--r--wt-status.h8
141 files changed, 4730 insertions, 940 deletions
diff --git a/.gitignore b/.gitignore
index efa8db0035..6b1fd1bfb0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -23,6 +23,7 @@
/git-cat-file
/git-check-attr
/git-check-ignore
+/git-check-mailmap
/git-check-ref-format
/git-checkout
/git-checkout-index
diff --git a/.mailmap b/.mailmap
index 345cce698a..57070b50d1 100644
--- a/.mailmap
+++ b/.mailmap
@@ -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 <rene.scharfe@lsrfire.ath.cx>
+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/RelNotes/1.8.3.3.txt b/Documentation/RelNotes/1.8.3.3.txt
index 04289e793c..9ba4f4da0f 100644
--- a/Documentation/RelNotes/1.8.3.3.txt
+++ b/Documentation/RelNotes/1.8.3.3.txt
@@ -4,6 +4,27 @@ Git v1.8.3.3 Release Notes
Fixes since v1.8.3.2
--------------------
+ * "git apply" parsed patches that add new files, generated by programs
+ other than Git, incorrectly. This is an old breakage in v1.7.11.
+
+ * Older cURL wanted piece of memory we call it with to be stable, but
+ we updated the auth material after handing it to a call.
+
+ * "git pull" into nothing trashed "local changes" that were in the
+ index.
+
+ * Many "git submodule" operations did not work on a submodule at a
+ path whose name is not in ASCII.
+
+ * "cherry-pick" had a small leak in its error codepath.
+
+ * Logic used by git-send-email to suppress cc mishandled names like
+ "A U. Thor" <author@example.xz>, where the human readable part
+ needs to be quoted (the user input may not have the double quotes
+ around the name, and comparison was done between quoted and
+ unquoted strings). It also mishandled names that need RFC2047
+ quoting.
+
* "gitweb" forgot to clear a global variable $search_regexp upon each
request, mistakenly carrying over the previous search to a new one
when used as a persistent CGI.
diff --git a/Documentation/RelNotes/1.8.3.4.txt b/Documentation/RelNotes/1.8.3.4.txt
new file mode 100644
index 0000000000..56f106e262
--- /dev/null
+++ b/Documentation/RelNotes/1.8.3.4.txt
@@ -0,0 +1,20 @@
+Git v1.8.3.4 Release Notes
+==========================
+
+This update is mostly to propagate documentation fixes and test
+updates from the master front back to the maintenance track.
+
+Fixes since v1.8.3.3
+--------------------
+
+ * The bisect log listed incorrect commits when bisection ends with
+ only skipped ones.
+
+ * The test coverage framework was left broken for some time.
+
+ * The test suite for HTTP transport did not run with Apache 2.4.
+
+ * "git diff" used to fail when core.safecrlf is set and the working
+ tree contents had mixed CRLF/LF line endings. Committing such a
+ content must be prohibited, but "git diff" should help the user to
+ locate and fix such problems without failing.
diff --git a/Documentation/RelNotes/1.8.4.txt b/Documentation/RelNotes/1.8.4.txt
index 4250e5a3a9..ad58ac6dbd 100644
--- a/Documentation/RelNotes/1.8.4.txt
+++ b/Documentation/RelNotes/1.8.4.txt
@@ -76,9 +76,64 @@ Foreign interfaces, subsystems and ports.
* git-remote-mw (in contrib/) hints users to check the certificate,
when https:// connection failed.
+ * git-remote-mw (in contrib/) adds a command to allow previewing the
+ contents locally before pushing it out, when working with a
+ MediaWiki remote.
+
UI, Workflows & Features
+ * "git clean" command learned an interactive mode.
+
+ * The "--head" option to "git show-ref" was only to add "HEAD" to the
+ list of candidate refs to be filtered by the usual rules
+ (e.g. "--heads" that only show refs under refs/heads). The meaning
+ of the option has been changed to always show "HEAD" regardless of
+ what filtering will be applied to any other ref.
+
+ This is a backward incompatible change and might cause breakages to
+ people's existing scripts.
+
+ * "git show -s" was less discoverable than it should have been. It
+ now has a natural synonym "git show --no-patch".
+
+ * "git check-mailmap" is a new command that lets you map usernames
+ and e-mail addresses through the mailmap mechanism, just like many
+ built-in commands do.
+
+ * "git name-rev" learned to name an annotated tag object back to its
+ tagname; "git name-rev $(git rev-parse v1.0.0)" gives "tags/v1.0.0",
+ for example.
+
+ * "git cat-file --batch-check=<format>" is added, primarily to allow
+ on-disk footprint of objects in packfiles (often they are a lot
+ smaller than their true size, when expressed as deltas) to be
+ reported.
+
+ * "git rebase [-i]" used to leave just "rebase" as its reflog messages
+ for some operations. They have been reworded to be more informative.
+
+ * In addition to the choice from "rebase, merge, or checkout-detach",
+ "submodule update" can allow a custom command to be used in to
+ update the working tree of submodules via the "submodule.*.update"
+ configuration variable.
+
+ * "git submodule update" can optionally clone the submodule
+ repositories shallowly.
+
+ * "git format-patch" learned "--from[=whom]" option, which sets the
+ "From: " header to the specified person (or the person who runs the
+ command, if "=whom" part is missing) and move the original author
+ information to an in-body From: header as necessary.
+
+ * The configuration variable "merge.ff" was cleary a tri-state to
+ choose one from "favor fast-forward when possible", "always create
+ a merge even when the history could fast-forward" and "do not
+ create any merge, only update when the history fast-forwards", but
+ the command line parser did not implement the usual convention of
+ "last one wins, and command line overrides the configuration"
+ correctly.
+
* "gitweb" learned to optionally place extra links that point at the
levels higher than the Gitweb pages themselves in the breadcrumbs,
so that it can be used as part of a larger installation.
@@ -179,6 +234,17 @@ UI, Workflows & Features
Performance, Internal Implementation, etc.
+ * The function attributes extensions are used to catch mistakes in
+ use of our own variadic functions that use NULL sentinel at the end
+ (i.e. like execl(3)) and format strings (i.e. like printf(3)).
+
+ * The code to allow configuration data to be read from in-tree blob
+ objects is in. This may help working in a bare repository and
+ submodule updates.
+
+ * Fetching between repositories with many refs employed O(n^2)
+ algorithm to match up the common objects, which has been corrected.
+
* The original way to specify remote repository using .git/branches/
used to have a nifty feature. The code to support the feature was
still in a function but the caller was changed not to call it 5
@@ -244,6 +310,35 @@ Unless otherwise noted, all the fixes since v1.8.3 in the maintenance
track are contained in this release (see release notes to them for
details).
+ * Newer Net::SMTP::SSL module does not want the user programs to use
+ the default behaviour to let server certificate go without
+ verification, so by default enable the verification with a
+ mechanism to turn it off if needed.
+ (merge 35035bb rr/send-email-ssl-verify later to maint).
+
+ * When "git" is spawned in such a way that any of the low 3 file
+ descriptors is closed, our first open() may yield file descriptor 2,
+ and writing error message to it would screw things up in a big way.
+ (merge a11c396 tr/protect-low-3-fds later to maint).
+
+ * The mailmap mechanism unnecessarily downcased the e-mail addresses
+ in the output, and also ignored the human name when it is a single
+ character name.
+ (merge bd23794 jc/mailmap-case-insensitivity later to maint).
+
+ * In two places we did not check return value (expected to be a file
+ descriptor) correctly.
+ (merge a77f106 tr/fd-gotcha-fixes later to maint).
+
+ * Logic to auto-detect character encodings in the commit log message
+ did not reject overlong and invalid UTF-8 characters.
+ (merge 81050ac bc/commit-invalid-utf8 later to maint).
+
+ * Pass port number as a separate argument when "send-email" initializes
+ Net::SMTP, instead of as a part of the hostname, i.e. host:port.
+ This allows GSSAPI codepath to match with the hostname given.
+ (merge 1a741bf bc/send-email-use-port-as-separate-param later to maint).
+
* "git diff" refused to even show difference when core.safecrlf is
set to true (i.e. error out) and there are offending lines in the
working tree files.
@@ -279,22 +374,17 @@ details).
* "git apply" parsed patches that add new files, generated by
programs other than Git, incorrectly. This is an old breakage in
v1.7.11 and will need to be merged down to the maintanance tracks.
- (merge 212eb96 tr/maint-apply-non-git-patch-parsefix later to maint).
* Older cURL wanted piece of memory we call it with to be stable, but
we updated the auth material after handing it to a call.
- (merge a94cf2c bc/http-keep-memory-given-to-curl later to maint).
* "git pull" into nothing trashed "local changes" that were in the
index, and this avoids it.
- (merge b4dc085 jk/pull-into-dirty-unborn later to maint).
* Many "git submodule" operations do not work on a submodule at a
path whose name is not in ASCII.
- (merge bed9470 fg/submodule-non-ascii-path later to maint).
* "cherry-pick" had a small leak in an error codepath.
- (merge 706728a fc/sequencer-plug-leak later to maint).
* Logic used by git-send-email to suppress cc mishandled names like
"A U. Thor" <author@example.xz>, where the human readable part
@@ -302,7 +392,6 @@ details).
around the name, and comparison was done between quoted and
unquoted strings). It also mishandled names that need RFC2047
quoting.
- (merge 1495266 mt/send-email-cc-match-fix later to maint).
* Call to discard_cache/discard_index (used when we use different
contents of the index in-core, in many operations like commit,
@@ -313,102 +402,79 @@ details).
* "gitweb" forgot to clear a global variable $search_regexp upon each
request, mistakenly carrying over the previous search to a new one
when used as a persistent CGI.
- (merge ca7a5dc cm/gitweb-project-list-persistent-cgi-fix later to maint).
* The wildmatch engine did not honor WM_CASEFOLD option correctly.
- (merge b79c0c3 ar/wildmatch-foldcase later to maint).
* "git log -c --follow $path" segfaulted upon hitting the commit that
renamed the $path being followed.
- (merge 46ec510 cb/log-follow-with-combined later to maint).
* When a reflog notation is used for implicit "current branch", we
did not say which branch and worse said "branch ''".
- (merge 305ebea rr/die-on-missing-upstream later to maint).
* "difftool --dir-diff" did not copy back changes made by the
end-user in the diff tool backend to the working tree in some
cases.
- (merge 32eaf1d ks/difftool-dir-diff-copy-fix later to maint).
* "git push $there HEAD:branch" did not resolve HEAD early enough, so
it was easy to flip it around while push is still going on and push
out a branch that the user did not originally intended when the
command was started.
- (merge 0f075b2 rr/push-head later to maint).
* The bash prompt code (in contrib/) displayed the name of the branch
being rebased when "rebase -i/-m/-p" modes are in use, but not the
plain vanilla "rebase".
- (merge 1306321 fc/show-branch-in-rebase-am later to maint).
* Handling of negative exclude pattern for directories "!dir" was
broken in the update to v1.8.3.
- (merge c3c327d kb/status-ignored-optim-2 later to maint).
* zsh prompt script that borrowed from bash prompt script did not
work due to slight differences in array variable notation between
these two shells.
- (merge d0583da tg/maint-zsh-svn-remote-prompt later to maint).
* An entry for "file://" scheme in the enumeration of URL types Git
can take in the HTML documentation was made into a clickable link
by mistake.
- (merge 4c32e36 nd/urls-doc-no-file-hyperlink-fix later to maint).
* "git push --[no-]verify" was not documented.
- (merge 90d32d1 tr/push-no-verify-doc later to maint).
* Stop installing the git-remote-testpy script that is only used for
testing.
- (merge 416fda6 fc/makefile later to maint).
* "git commit --allow-empty-message -m ''" should not start an
editor.
- (merge 2520677 rs/commit-m-no-edit later to maint).
* "git merge @{-1}~22" was rewritten to "git merge frotz@{1}~22"
incorrectly when your previous branch was "frotz" (it should be
rewritten to "git merge frotz~22" instead).
- (merge 84cf246 jc/strbuf-branchname-fix later to maint).
* "git diff -c -p" was not showing a deleted line from a hunk when
another hunk immediately begins where the earlier one ends.
- (merge aac3857 mk/combine-diff-context-horizon-fix later to maint).
* "git log --ancestry-path A...B" did not work as expected, as it did
not pay attention to the fact that the merge base between A and B
was the bottom of the range being specified.
- (merge a765499 kb/ancestry-path-threedots later to maint).
* Mac OS X does not like to write(2) more than INT_MAX number of
bytes; work it around by chopping write(2) into smaller pieces.
- (merge 6c642a8 fc/macos-x-clipped-write later to maint).
* Newer MacOS X encourages the programs to compile and link with
their CommonCrypto, not with OpenSSL.
- (merge be4c828 da/darwin later to maint).
* "git clone foo/bar:baz" cannot be a request to clone from a remote
over git-over-ssh specified in the scp style. This case is now
detected and clones from a local repository at "foo/bar:baz".
- (merge 6000334 nd/clone-local-with-colon later to maint).
* When $HOME is misconfigured to point at an unreadable directory, we
used to complain and die. Loosen the check.
- (merge 4698c8f jn/config-ignore-inaccessible later to maint).
* "git subtree" (in contrib/) had one codepath with loose error
checks to lose data at the remote side.
- (merge 3212d56 jk/subtree-do-not-push-if-split-fails later to maint).
* "git fetch" into a shallow repository from a repository that does
not know about the shallow boundary commits (e.g. a different fork
from the repository the current shallow repository was cloned from)
did not work correctly.
- (merge 71d5f93 mh/fetch-into-shallow later to maint).
* "git checkout foo" DWIMs the intended "upstream" and turns it into
"git checkout -t -b foo remotes/origin/foo". This codepath has been
updated to correctly take existing remote definitions into account.
- (merge 229177a jh/checkout-auto-tracking later to maint).
diff --git a/Documentation/blame-options.txt b/Documentation/blame-options.txt
index e9f984ba01..4e55b1564e 100644
--- a/Documentation/blame-options.txt
+++ b/Documentation/blame-options.txt
@@ -9,9 +9,13 @@
--show-stats::
Include additional statistics at the end of blame output.
--L <start>,<end>, -L :<regex>::
- Annotate only the given line range. <start> and <end> can take
- one of these forms:
+-L <start>,<end>::
+-L :<regex>::
+ Annotate only the given line range. <start> and <end> are optional.
+ ``-L <start>'' or ``-L <start>,'' spans from <start> to end of file.
+ ``-L ,<end>'' spans from start of file to <end>.
++
+<start> and <end> can take one of these forms:
include::line-range-format.txt[]
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 81856dd5a2..e0b923f428 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -879,16 +879,17 @@ The values of these variables may be specified as in color.branch.<slot>.
color.interactive::
When set to `always`, always use colors for interactive prompts
- and displays (such as those used by "git-add --interactive").
- When false (or `never`), never. When set to `true` or `auto`, use
- colors only when the output is to the terminal. Defaults to false.
+ and displays (such as those used by "git-add --interactive" and
+ "git-clean --interactive"). When false (or `never`), never.
+ When set to `true` or `auto`, use colors only when the output is
+ to the terminal. Defaults to false.
color.interactive.<slot>::
- Use customized color for 'git add --interactive'
- output. `<slot>` may be `prompt`, `header`, `help` or `error`, for
- four distinct types of normal output from interactive
- commands. The values of these variables may be specified as
- in color.branch.<slot>.
+ Use customized color for 'git add --interactive' and 'git clean
+ --interactive' output. `<slot>` may be `prompt`, `header`, `help`
+ or `error`, for four distinct types of normal output from
+ interactive commands. The values of these variables may be
+ specified as in color.branch.<slot>.
color.pager::
A boolean to enable/disable colored output when the pager is in
@@ -973,6 +974,10 @@ column.branch::
Specify whether to output branch listing in `git branch` in columns.
See `column.ui` for details.
+column.clean::
+ Specify the layout when list items in `git clean -i`, which always
+ shows files and directories in columns. See `column.ui` for details.
+
column.status::
Specify whether to output untracked files in `git status` in columns.
See `column.ui` for details.
@@ -2068,6 +2073,10 @@ sendemail.smtpencryption::
sendemail.smtpssl::
Deprecated alias for 'sendemail.smtpencryption = ssl'.
+sendemail.smtpsslcertpath::
+ Path to ca-certificates (either a directory or a single file).
+ Set it to an empty string to disable certificate verification.
+
sendemail.<identity>.*::
Identity-specific versions of the 'sendemail.*' parameters
found below, taking precedence over those when the this
diff --git a/Documentation/diff-options.txt b/Documentation/diff-options.txt
index 87e92d6537..bbed2cd79c 100644
--- a/Documentation/diff-options.txt
+++ b/Documentation/diff-options.txt
@@ -26,6 +26,11 @@ ifndef::git-format-patch[]
{git-diff? This is the default.}
endif::git-format-patch[]
+-s::
+--no-patch::
+ Suppress diff output. Useful for commands like `git show` that
+ show the patch by default, or to cancel the effect of `--patch`.
+
-U<n>::
--unified=<n>::
Generate diffs with <n> lines of context instead of
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-check-mailmap.txt b/Documentation/git-check-mailmap.txt
new file mode 100644
index 0000000000..39028ee1a3
--- /dev/null
+++ b/Documentation/git-check-mailmap.txt
@@ -0,0 +1,47 @@
+git-check-mailmap(1)
+====================
+
+NAME
+----
+git-check-mailmap - Show canonical names and email addresses of contacts
+
+
+SYNOPSIS
+--------
+[verse]
+'git check-mailmap' [options] <contact>...
+
+
+DESCRIPTION
+-----------
+
+For each ``Name $$<user@host>$$'' or ``$$<user@host>$$'' from the command-line
+or standard input (when using `--stdin`), look up the person's canonical name
+and email address (see "Mapping Authors" below). If found, print them;
+otherwise print the input as-is.
+
+
+OPTIONS
+-------
+--stdin::
+ Read contacts, one per line, from the standard input after exhausting
+ contacts provided on the command-line.
+
+
+OUTPUT
+------
+
+For each contact, a single line is output, terminated by a newline. If the
+name is provided or known to the 'mailmap', ``Name $$<user@host>$$'' is
+printed; otherwise only ``$$<user@host>$$'' is printed.
+
+
+MAPPING AUTHORS
+---------------
+
+include::mailmap.txt[]
+
+
+GIT
+---
+Part of the linkgit:git[1] suite
diff --git a/Documentation/git-clean.txt b/Documentation/git-clean.txt
index bdc3ab80c7..75fb543393 100644
--- a/Documentation/git-clean.txt
+++ b/Documentation/git-clean.txt
@@ -8,7 +8,7 @@ git-clean - Remove untracked files from the working tree
SYNOPSIS
--------
[verse]
-'git clean' [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
+'git clean' [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <path>...
DESCRIPTION
-----------
@@ -34,7 +34,13 @@ OPTIONS
-f::
--force::
If the Git configuration variable clean.requireForce is not set
- to false, 'git clean' will refuse to run unless given -f or -n.
+ to false, 'git clean' will refuse to run unless given -f, -n or
+ -i.
+
+-i::
+--interactive::
+ Show what would be done and clean files interactively. See
+ ``Interactive mode'' for details.
-n::
--dry-run::
@@ -63,6 +69,67 @@ OPTIONS
Remove only files ignored by Git. This may be useful to rebuild
everything from scratch, but keep manually created files.
+Interactive mode
+----------------
+When the command enters the interactive mode, it shows the
+files and directories to be cleaned, and goes into its
+interactive command loop.
+
+The command loop shows the list of subcommands available, and
+gives a prompt "What now> ". In general, when the prompt ends
+with a single '>', you can pick only one of the choices given
+and type return, like this:
+
+------------
+ *** Commands ***
+ 1: clean 2: filter by pattern 3: select by numbers
+ 4: ask each 5: quit 6: help
+ What now> 1
+------------
+
+You also could say `c` or `clean` above as long as the choice is unique.
+
+The main command loop has 6 subcommands.
+
+clean::
+
+ Start cleaning files and directories, and then quit.
+
+filter by pattern::
+
+ This shows the files and directories to be deleted and issues an
+ "Input ignore patterns>>" prompt. You can input space-seperated
+ patterns to exclude files and directories from deletion.
+ E.g. "*.c *.h" will excludes files end with ".c" and ".h" from
+ deletion. When you are satisfied with the filtered result, press
+ ENTER (empty) back to the main menu.
+
+select by numbers::
+
+ This shows the files and directories to be deleted and issues an
+ "Select items to delete>>" prompt. When the prompt ends with double
+ '>>' like this, you can make more than one selection, concatenated
+ with whitespace or comma. Also you can say ranges. E.g. "2-5 7,9"
+ to choose 2,3,4,5,7,9 from the list. If the second number in a
+ range is omitted, all remaining patches are taken. E.g. "7-" to
+ choose 7,8,9 from the list. You can say '*' to choose everything.
+ Also when you are satisfied with the filtered result, press ENTER
+ (empty) back to the main menu.
+
+ask each::
+
+ This will start to clean, and you must confirm one by one in order
+ to delete items. Please note that this action is not as efficient
+ as the above two actions.
+
+quit::
+
+ This lets you quit without do cleaning.
+
+help::
+
+ Show brief usage of interactive git-clean.
+
SEE ALSO
--------
linkgit:gitignore[5]
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 85769b884e..450f158779 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -182,13 +182,11 @@ objects from the source repository into a pack in the cloned repository.
--depth <depth>::
Create a 'shallow' clone with a history truncated to the
specified number of revisions. A shallow repository has a
- number of limitations (you cannot clone or fetch from it, nor
- push into it), but is adequate if you are only interested in
- the recent history of a large project with a long history.
-+
-Pushing from a shallow clone should be avoided if the git version on
-the receiver end is older than v1.7.10, or any other git
-implementation that does not perform connectivity check.
+ number of limitations (you cannot clone or fetch from
+ it, nor push from nor into it), but is adequate if you
+ are only interested in the recent history of a large project
+ with a long history, and would want to send in fixes
+ as patches.
--[no-]single-branch::
Clone only the history leading to the tip of a single branch,
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 99dc497b6f..34b0894646 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -127,6 +127,13 @@ See also <<FILES>>.
--file config-file::
Use the given config file instead of the one specified by GIT_CONFIG.
+--blob blob::
+ Similar to '--file' but use the given blob instead of a file. E.g.
+ you can use 'master:.gitmodules' to read values from the file
+ '.gitmodules' in the master branch. See "SPECIFYING REVISIONS"
+ section in linkgit:gitrevisions[7] for a more complete list of
+ ways to spell blob names.
+
--remove-section::
Remove the given section from the configuration file.
diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
index 39118774af..e394276b1a 100644
--- a/Documentation/git-format-patch.txt
+++ b/Documentation/git-format-patch.txt
@@ -187,6 +187,21 @@ will want to ensure that threading is disabled for `git send-email`.
The negated form `--no-cc` discards all `Cc:` headers added so
far (from config or command line).
+--from::
+--from=<ident>::
+ Use `ident` in the `From:` header of each commit email. If the
+ author ident of the commit is not textually identical to the
+ provided `ident`, place a `From:` header in the body of the
+ message with the original author. If no `ident` is given, use
+ the committer ident.
++
+Note that this option is only useful if you are actually sending the
+emails and want to identify yourself as the sender, but retain the
+original author (and `git am` will correctly pick up the in-body
+header). Note also that `git send-email` already handles this
+transformation for you, and this option should not be used if you are
+feeding the result to `git send-email`.
+
--add-header=<header>::
Add an arbitrary header to the email headers. This is in addition
to any configured headers, and may be used multiple times.
diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 2ea79ba168..ac2694d04c 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -97,7 +97,7 @@ include::rev-list-options.txt[]
include::pretty-formats.txt[]
-Common diff options
+COMMON DIFF OPTIONS
-------------------
:git-log: 1
@@ -105,7 +105,7 @@ include::diff-options.txt[]
include::diff-generate-patch.txt[]
-Examples
+EXAMPLES
--------
`git log --no-merges`::
@@ -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.
@@ -161,12 +161,12 @@ git log -L '/int main/',/^}/:main.c::
`git log -3`::
Limits the number of commits to show to 3.
-Discussion
+DISCUSSION
----------
include::i18n.txt[]
-Configuration
+CONFIGURATION
-------------
See linkgit:git-config[1] for core variables and linkgit:git-diff[1]
diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index a404b47b7b..f445cb38fa 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -9,7 +9,7 @@ SYNOPSIS
--------
[verse]
'git reset' [-q] [<tree-ish>] [--] <paths>...
-'git reset' (--patch | -p) [<tree-sh>] [--] [<paths>...]
+'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
'git reset' [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
DESCRIPTION
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 40a9a9abc1..f0e57a597b 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -198,6 +198,12 @@ must be used for each option.
--smtp-ssl::
Legacy alias for '--smtp-encryption ssl'.
+--smtp-ssl-cert-path::
+ Path to ca-certificates (either a directory or a single file).
+ Set it to an empty string to disable certificate verification.
+ Defaults to the value set to the 'sendemail.smtpsslcertpath'
+ configuration variable, if set, or `/etc/ssl/certs` otherwise.
+
--smtp-user=<user>::
Username for SMTP-AUTH. Default is the value of 'sendemail.smtpuser';
if a username is not specified (with '--smtp-user' or 'sendemail.smtpuser'),
diff --git a/Documentation/git-show-ref.txt b/Documentation/git-show-ref.txt
index de4d352da2..b0a309b117 100644
--- a/Documentation/git-show-ref.txt
+++ b/Documentation/git-show-ref.txt
@@ -21,6 +21,8 @@ commit IDs. Results can be filtered using a pattern and tags can be
dereferenced into object IDs. Additionally, it can be used to test whether a
particular ref exists.
+By default, shows the tags, heads, and remote refs.
+
The --exclude-existing form is a filter that does the inverse, it shows the
refs from stdin that don't exist in the local repository.
@@ -32,14 +34,14 @@ OPTIONS
--head::
- Show the HEAD reference.
+ Show the HEAD reference, even if it would normally be filtered out.
--tags::
--heads::
- Limit to only "refs/heads" and "refs/tags", respectively. These
- options are not mutually exclusive; when given both, references stored
- in "refs/heads" and "refs/tags" are displayed.
+ Limit to "refs/heads" and "refs/tags", respectively. These options
+ are not mutually exclusive; when given both, references stored in
+ "refs/heads" and "refs/tags" are displayed.
-d::
--dereference::
diff --git a/Documentation/git-show.txt b/Documentation/git-show.txt
index ae4edcccfb..4e617e6979 100644
--- a/Documentation/git-show.txt
+++ b/Documentation/git-show.txt
@@ -45,6 +45,15 @@ include::pretty-options.txt[]
include::pretty-formats.txt[]
+COMMON DIFF OPTIONS
+-------------------
+
+:git-log: 1
+include::diff-options.txt[]
+
+include::diff-generate-patch.txt[]
+
+
EXAMPLES
--------
diff --git a/Documentation/git-submodule.txt b/Documentation/git-submodule.txt
index bfff09062d..bfef8a0c62 100644
--- a/Documentation/git-submodule.txt
+++ b/Documentation/git-submodule.txt
@@ -10,12 +10,12 @@ SYNOPSIS
--------
[verse]
'git submodule' [--quiet] add [-b <branch>] [-f|--force] [--name <name>]
- [--reference <repository>] [--] <repository> [<path>]
+ [--reference <repository>] [--depth <depth>] [--] <repository> [<path>]
'git submodule' [--quiet] status [--cached] [--recursive] [--] [<path>...]
'git submodule' [--quiet] init [--] [<path>...]
'git submodule' [--quiet] deinit [-f|--force] [--] <path>...
'git submodule' [--quiet] update [--init] [--remote] [-N|--no-fetch]
- [-f|--force] [--rebase] [--reference <repository>]
+ [-f|--force] [--rebase] [--reference <repository>] [--depth <depth>]
[--merge] [--recursive] [--] [<path>...]
'git submodule' [--quiet] summary [--cached|--files] [(-n|--summary-limit) <n>]
[commit] [--] [<path>...]
@@ -159,7 +159,9 @@ update::
This will make the submodules HEAD be detached unless `--rebase` or
`--merge` is specified or the key `submodule.$name.update` is set to
`rebase`, `merge` or `none`. `none` can be overridden by specifying
- `--checkout`.
+ `--checkout`. Setting the key `submodule.$name.update` to `!command`
+ will cause `command` to be run. `command` can be any arbitrary shell
+ command that takes a single argument, namely the sha1 to update to.
+
If the submodule is not yet initialized, and you just want to use the
setting as stored in .gitmodules, you can automatically initialize the
@@ -328,6 +330,12 @@ for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
only in the submodules of the current repo, but also
in any nested submodules inside those submodules (and so on).
+--depth::
+ This option is valid for add and update commands. Create a 'shallow'
+ clone with a history truncated to the specified number of revisions.
+ See linkgit:git-clone[1]
+
+
<path>...::
Paths to submodule(s). When specified this will restrict the command
to only operate on the submodules found at the specified paths.
diff --git a/Documentation/git.txt b/Documentation/git.txt
index b738a40e6b..3bdd56e8f4 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -43,9 +43,11 @@ unreleased) version of Git, that is available from 'master'
branch of the `git.git` repository.
Documentation for older releases are available here:
-* link:v1.8.3.2/git.html[documentation for release 1.8.3.2]
+* link:v1.8.3.4/git.html[documentation for release 1.8.3.4]
* release notes for
+ link:RelNotes/1.8.3.4.txt[1.8.3.4],
+ link:RelNotes/1.8.3.3.txt[1.8.3.3],
link:RelNotes/1.8.3.2.txt[1.8.3.2],
link:RelNotes/1.8.3.1.txt[1.8.3.1],
link:RelNotes/1.8.3.txt[1.8.3].
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index e157ec3fe7..e632e8576e 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -849,7 +849,4 @@ options may be given. See linkgit:git-diff-files[1] for more options.
-t::
Show the tree objects in the diff output. This implies '-r'.
-
--s::
- Suppress diff output.
endif::git-rev-list[]
diff --git a/Documentation/user-manual.txt b/Documentation/user-manual.txt
index e364007d7c..fe723e4722 100644
--- a/Documentation/user-manual.txt
+++ b/Documentation/user-manual.txt
@@ -4675,5 +4675,5 @@ Write a chapter on using plumbing and writing scripts.
Alternates, clone -reference, etc.
More on recovery from repository corruption. See:
- http://marc.theaimsgroup.com/?l=git&m=117263864820799&w=2
- http://marc.theaimsgroup.com/?l=git&m=117147855503798&w=2
+ http://marc.info/?l=git&m=117263864820799&w=2
+ http://marc.info/?l=git&m=117147855503798&w=2
diff --git a/Makefile b/Makefile
index 0600eb46db..ef442ebad0 100644
--- a/Makefile
+++ b/Makefile
@@ -912,6 +912,7 @@ BUILTIN_OBJS += builtin/bundle.o
BUILTIN_OBJS += builtin/cat-file.o
BUILTIN_OBJS += builtin/check-attr.o
BUILTIN_OBJS += builtin/check-ignore.o
+BUILTIN_OBJS += builtin/check-mailmap.o
BUILTIN_OBJS += builtin/check-ref-format.o
BUILTIN_OBJS += builtin/checkout-index.o
BUILTIN_OBJS += builtin/checkout.o
diff --git a/advice.h b/advice.h
index 93a7d110ea..08fbc8ee3c 100644
--- a/advice.h
+++ b/advice.h
@@ -21,6 +21,7 @@ extern int advice_object_name_warning;
extern int advice_rm_hints;
int git_default_advice_config(const char *var, const char *value);
+__attribute__((format (printf, 1, 2)))
void advise(const char *advice, ...);
int error_resolve_conflict(const char *me);
extern void NORETURN die_resolve_conflict(const char *me);
diff --git a/argv-array.h b/argv-array.h
index 40248d424c..85ba438ac1 100644
--- a/argv-array.h
+++ b/argv-array.h
@@ -15,6 +15,7 @@ void argv_array_init(struct argv_array *);
void argv_array_push(struct argv_array *, const char *);
__attribute__((format (printf,2,3)))
void argv_array_pushf(struct argv_array *, const char *fmt, ...);
+LAST_ARG_MUST_BE_NULL
void argv_array_pushl(struct argv_array *, ...);
void argv_array_pop(struct argv_array *);
void argv_array_clear(struct argv_array *);
diff --git a/builtin.h b/builtin.h
index 1ed8edb0cb..8afa2def49 100644
--- a/builtin.h
+++ b/builtin.h
@@ -40,6 +40,7 @@ extern int cmd_checkout(int argc, const char **argv, const char *prefix);
extern int cmd_checkout_index(int argc, const char **argv, const char *prefix);
extern int cmd_check_attr(int argc, const char **argv, const char *prefix);
extern int cmd_check_ignore(int argc, const char **argv, const char *prefix);
+extern int cmd_check_mailmap(int argc, const char **argv, const char *prefix);
extern int cmd_check_ref_format(int argc, const char **argv, const char *prefix);
extern int cmd_cherry(int argc, const char **argv, const char *prefix);
extern int cmd_cherry_pick(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index f45d9d4865..8266a9cb70 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -343,6 +343,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
argc = setup_revisions(argc, argv, &rev, NULL);
rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+ rev.diffopt.use_color = 0;
DIFF_OPT_SET(&rev.diffopt, IGNORE_DIRTY_SUBMODULES);
out = open(file, O_CREAT | O_WRONLY, 0666);
if (out < 0)
diff --git a/builtin/apply.c b/builtin/apply.c
index 0e9b631db6..50912c928f 100644
--- a/builtin/apply.c
+++ b/builtin/apply.c
@@ -473,7 +473,7 @@ static char *find_name_gnu(const char *line, const char *def, int p_value)
/*
* Proposed "new-style" GNU patch/diff format; see
- * http://marc.theaimsgroup.com/?l=git&m=112927316408690&w=2
+ * http://marc.info/?l=git&m=112927316408690&w=2
*/
if (unquote_c_style(&name, line, NULL)) {
strbuf_release(&name);
@@ -722,7 +722,7 @@ static char *find_name(const char *line, char *def, int p_value, int terminate)
static char *find_name_traditional(const char *line, char *def, int p_value)
{
- size_t len = strlen(line);
+ size_t len;
size_t date_len;
if (*line == '"') {
@@ -2999,7 +2999,7 @@ static int read_blob_object(struct strbuf *buf, const unsigned char *sha1, unsig
return 0;
}
-static int read_file_or_gitlink(struct cache_entry *ce, struct strbuf *buf)
+static int read_file_or_gitlink(const struct cache_entry *ce, struct strbuf *buf)
{
if (!ce)
return 0;
@@ -3117,7 +3117,7 @@ static struct patch *previous_patch(struct patch *patch, int *gone)
return previous;
}
-static int verify_index_match(struct cache_entry *ce, struct stat *st)
+static int verify_index_match(const struct cache_entry *ce, struct stat *st)
{
if (S_ISGITLINK(ce->ce_mode)) {
if (!S_ISDIR(st->st_mode))
@@ -3130,7 +3130,7 @@ static int verify_index_match(struct cache_entry *ce, struct stat *st)
#define SUBMODULE_PATCH_WITHOUT_INDEX 1
static int load_patch_target(struct strbuf *buf,
- struct cache_entry *ce,
+ const struct cache_entry *ce,
struct stat *st,
const char *name,
unsigned expected_mode)
@@ -3160,7 +3160,8 @@ static int load_patch_target(struct strbuf *buf,
* we read from the result of a previous diff.
*/
static int load_preimage(struct image *image,
- struct patch *patch, struct stat *st, struct cache_entry *ce)
+ struct patch *patch, struct stat *st,
+ const struct cache_entry *ce)
{
struct strbuf buf = STRBUF_INIT;
size_t len;
@@ -3273,7 +3274,7 @@ static int load_current(struct image *image, struct patch *patch)
}
static int try_threeway(struct image *image, struct patch *patch,
- struct stat *st, struct cache_entry *ce)
+ struct stat *st, const struct cache_entry *ce)
{
unsigned char pre_sha1[20], post_sha1[20], our_sha1[20];
struct strbuf buf = STRBUF_INIT;
@@ -3343,7 +3344,7 @@ static int try_threeway(struct image *image, struct patch *patch,
return 0;
}
-static int apply_data(struct patch *patch, struct stat *st, struct cache_entry *ce)
+static int apply_data(struct patch *patch, struct stat *st, const struct cache_entry *ce)
{
struct image image;
@@ -3847,7 +3848,7 @@ static void add_index_file(const char *path, unsigned mode, void *buf, unsigned
const char *s = buf;
if (get_sha1_hex(s + strlen("Subproject commit "), ce->sha1))
- die(_("corrupt patch for subproject %s"), path);
+ die(_("corrupt patch for submodule %s"), path);
} else {
if (!cached) {
if (lstat(path, &st) < 0)
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/check-mailmap.c b/builtin/check-mailmap.c
new file mode 100644
index 0000000000..8f4d809bd8
--- /dev/null
+++ b/builtin/check-mailmap.c
@@ -0,0 +1,66 @@
+#include "builtin.h"
+#include "mailmap.h"
+#include "parse-options.h"
+#include "string-list.h"
+
+static int use_stdin;
+static const char * const check_mailmap_usage[] = {
+N_("git check-mailmap [options] <contact>..."),
+NULL
+};
+
+static const struct option check_mailmap_options[] = {
+ OPT_BOOL(0, "stdin", &use_stdin, N_("also read contacts from stdin")),
+ OPT_END()
+};
+
+static void check_mailmap(struct string_list *mailmap, const char *contact)
+{
+ const char *name, *mail;
+ size_t namelen, maillen;
+ struct ident_split ident;
+
+ if (split_ident_line(&ident, contact, strlen(contact)))
+ die(_("unable to parse contact: %s"), contact);
+
+ name = ident.name_begin;
+ namelen = ident.name_end - ident.name_begin;
+ mail = ident.mail_begin;
+ maillen = ident.mail_end - ident.mail_begin;
+
+ map_user(mailmap, &mail, &maillen, &name, &namelen);
+
+ if (namelen)
+ printf("%.*s ", (int)namelen, name);
+ printf("<%.*s>\n", (int)maillen, mail);
+}
+
+int cmd_check_mailmap(int argc, const char **argv, const char *prefix)
+{
+ int i;
+ struct string_list mailmap = STRING_LIST_INIT_NODUP;
+
+ git_config(git_default_config, NULL);
+ argc = parse_options(argc, argv, prefix, check_mailmap_options,
+ check_mailmap_usage, 0);
+ if (argc == 0 && !use_stdin)
+ die(_("no contacts specified"));
+
+ read_mailmap(&mailmap, NULL);
+
+ for (i = 0; i < argc; ++i)
+ check_mailmap(&mailmap, argv[i]);
+ maybe_flush_or_die(stdout, "stdout");
+
+ if (use_stdin) {
+ struct strbuf buf = STRBUF_INIT;
+ while (strbuf_getline(&buf, stdin, '\n') != EOF) {
+ check_mailmap(&mailmap, buf.buf);
+ maybe_flush_or_die(stdout, "stdout");
+ }
+ strbuf_release(&buf);
+ }
+
+ clear_mailmap(&mailmap);
+ return 0;
+}
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 7fe0bffaf8..7025938ae3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -97,7 +97,7 @@ static int read_tree_some(struct tree *tree, const char **pathspec)
return 0;
}
-static int skip_same_name(struct cache_entry *ce, int pos)
+static int skip_same_name(const struct cache_entry *ce, int pos)
{
while (++pos < active_nr &&
!strcmp(active_cache[pos]->name, ce->name))
@@ -105,7 +105,7 @@ static int skip_same_name(struct cache_entry *ce, int pos)
return pos;
}
-static int check_stage(int stage, struct cache_entry *ce, int pos)
+static int check_stage(int stage, const struct cache_entry *ce, int pos)
{
while (pos < active_nr &&
!strcmp(active_cache[pos]->name, ce->name)) {
@@ -119,7 +119,7 @@ static int check_stage(int stage, struct cache_entry *ce, int pos)
return error(_("path '%s' does not have their version"), ce->name);
}
-static int check_stages(unsigned stages, struct cache_entry *ce, int pos)
+static int check_stages(unsigned stages, const struct cache_entry *ce, int pos)
{
unsigned seen = 0;
const char *name = ce->name;
@@ -321,7 +321,7 @@ static int checkout_paths(const struct checkout_opts *opts,
/* Any unmerged paths? */
for (pos = 0; pos < active_nr; pos++) {
- struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = active_cache[pos];
if (ce->ce_flags & CE_MATCHED) {
if (!ce_stage(ce))
continue;
diff --git a/builtin/clean.c b/builtin/clean.c
index 04e396b17a..dba8387747 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -13,11 +13,16 @@
#include "refs.h"
#include "string-list.h"
#include "quote.h"
+#include "column.h"
+#include "color.h"
static int force = -1; /* unset */
+static int interactive;
+static struct string_list del_list = STRING_LIST_INIT_DUP;
+static unsigned int colopts;
static const char *const builtin_clean_usage[] = {
- N_("git clean [-d] [-f] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
+ N_("git clean [-d] [-f] [-i] [-n] [-q] [-e <pattern>] [-x | -X] [--] <paths>..."),
NULL
};
@@ -27,11 +32,112 @@ static const char *msg_skip_git_dir = N_("Skipping repository %s\n");
static const char *msg_would_skip_git_dir = N_("Would skip repository %s\n");
static const char *msg_warn_remove_failed = N_("failed to remove %s");
+static int clean_use_color = -1;
+static char clean_colors[][COLOR_MAXLEN] = {
+ GIT_COLOR_RESET,
+ GIT_COLOR_NORMAL, /* PLAIN */
+ GIT_COLOR_BOLD_BLUE, /* PROMPT */
+ GIT_COLOR_BOLD, /* HEADER */
+ GIT_COLOR_BOLD_RED, /* HELP */
+ GIT_COLOR_BOLD_RED, /* ERROR */
+};
+enum color_clean {
+ CLEAN_COLOR_RESET = 0,
+ CLEAN_COLOR_PLAIN = 1,
+ CLEAN_COLOR_PROMPT = 2,
+ CLEAN_COLOR_HEADER = 3,
+ CLEAN_COLOR_HELP = 4,
+ CLEAN_COLOR_ERROR = 5,
+};
+
+#define MENU_OPTS_SINGLETON 01
+#define MENU_OPTS_IMMEDIATE 02
+#define MENU_OPTS_LIST_ONLY 04
+
+struct menu_opts {
+ const char *header;
+ const char *prompt;
+ int flags;
+};
+
+#define MENU_RETURN_NO_LOOP 10
+
+struct menu_item {
+ char hotkey;
+ const char *title;
+ int selected;
+ int (*fn)();
+};
+
+enum menu_stuff_type {
+ MENU_STUFF_TYPE_STRING_LIST = 1,
+ MENU_STUFF_TYPE_MENU_ITEM
+};
+
+struct menu_stuff {
+ enum menu_stuff_type type;
+ int nr;
+ void *stuff;
+};
+
+static int parse_clean_color_slot(const char *var)
+{
+ if (!strcasecmp(var, "reset"))
+ return CLEAN_COLOR_RESET;
+ if (!strcasecmp(var, "plain"))
+ return CLEAN_COLOR_PLAIN;
+ if (!strcasecmp(var, "prompt"))
+ return CLEAN_COLOR_PROMPT;
+ if (!strcasecmp(var, "header"))
+ return CLEAN_COLOR_HEADER;
+ if (!strcasecmp(var, "help"))
+ return CLEAN_COLOR_HELP;
+ if (!strcasecmp(var, "error"))
+ return CLEAN_COLOR_ERROR;
+ return -1;
+}
+
static int git_clean_config(const char *var, const char *value, void *cb)
{
- if (!strcmp(var, "clean.requireforce"))
+ if (!prefixcmp(var, "column."))
+ return git_column_config(var, value, "clean", &colopts);
+
+ /* honors the color.interactive* config variables which also
+ applied in git-add--interactive and git-stash */
+ if (!strcmp(var, "color.interactive")) {
+ clean_use_color = git_config_colorbool(var, value);
+ return 0;
+ }
+ if (!prefixcmp(var, "color.interactive.")) {
+ int slot = parse_clean_color_slot(var +
+ strlen("color.interactive."));
+ if (slot < 0)
+ return 0;
+ if (!value)
+ return config_error_nonbool(var);
+ color_parse(value, var, clean_colors[slot]);
+ return 0;
+ }
+
+ if (!strcmp(var, "clean.requireforce")) {
force = !git_config_bool(var, value);
- return git_default_config(var, value, cb);
+ return 0;
+ }
+
+ /* inspect the color.ui config variable and others */
+ return git_color_default_config(var, value, cb);
+}
+
+static const char *clean_get_color(enum color_clean ix)
+{
+ if (want_color(clean_use_color))
+ return clean_colors[ix];
+ return "";
+}
+
+static void clean_print_color(enum color_clean ix)
+{
+ printf("%s", clean_get_color(ix));
}
static int exclude_cb(const struct option *opt, const char *arg, int unset)
@@ -56,7 +162,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
if ((force_flag & REMOVE_DIR_KEEP_NESTED_GIT) &&
!resolve_gitlink_ref(path->buf, "HEAD", submodule_head)) {
if (!quiet) {
- quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+ quote_path_relative(path->buf, prefix, &quoted);
printf(dry_run ? _(msg_would_skip_git_dir) : _(msg_skip_git_dir),
quoted.buf);
}
@@ -70,7 +176,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
/* an empty dir could be removed even if it is unreadble */
res = dry_run ? 0 : rmdir(path->buf);
if (res) {
- quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+ quote_path_relative(path->buf, prefix, &quoted);
warning(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
}
@@ -94,7 +200,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
if (remove_dirs(path, prefix, force_flag, dry_run, quiet, &gone))
ret = 1;
if (gone) {
- quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+ quote_path_relative(path->buf, prefix, &quoted);
string_list_append(&dels, quoted.buf);
} else
*dir_gone = 0;
@@ -102,10 +208,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
} else {
res = dry_run ? 0 : unlink(path->buf);
if (!res) {
- quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+ quote_path_relative(path->buf, prefix, &quoted);
string_list_append(&dels, quoted.buf);
} else {
- quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+ quote_path_relative(path->buf, prefix, &quoted);
warning(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
ret = 1;
@@ -127,7 +233,7 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
if (!res)
*dir_gone = 1;
else {
- quote_path_relative(path->buf, strlen(path->buf), &quoted, prefix);
+ quote_path_relative(path->buf, prefix, &quoted);
warning(_(msg_warn_remove_failed), quoted.buf);
*dir_gone = 0;
ret = 1;
@@ -142,24 +248,609 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
return ret;
}
+static void pretty_print_dels(void)
+{
+ struct string_list list = STRING_LIST_INIT_DUP;
+ struct string_list_item *item;
+ struct strbuf buf = STRBUF_INIT;
+ const char *qname;
+ struct column_options copts;
+
+ for_each_string_list_item(item, &del_list) {
+ qname = quote_path_relative(item->string, NULL, &buf);
+ string_list_append(&list, qname);
+ }
+
+ /*
+ * always enable column display, we only consult column.*
+ * about layout strategy and stuff
+ */
+ colopts = (colopts & ~COL_ENABLE_MASK) | COL_ENABLED;
+ memset(&copts, 0, sizeof(copts));
+ copts.indent = " ";
+ copts.padding = 2;
+ print_columns(&list, colopts, &copts);
+ strbuf_release(&buf);
+ string_list_clear(&list, 0);
+}
+
+static void pretty_print_menus(struct string_list *menu_list)
+{
+ unsigned int local_colopts = 0;
+ struct column_options copts;
+
+ local_colopts = COL_ENABLED | COL_ROW;
+ memset(&copts, 0, sizeof(copts));
+ copts.indent = " ";
+ copts.padding = 2;
+ print_columns(menu_list, local_colopts, &copts);
+}
+
+static void prompt_help_cmd(int singleton)
+{
+ clean_print_color(CLEAN_COLOR_HELP);
+ printf_ln(singleton ?
+ _("Prompt help:\n"
+ "1 - select a numbered item\n"
+ "foo - select item based on unique prefix\n"
+ " - (empty) select nothing") :
+ _("Prompt help:\n"
+ "1 - select a single item\n"
+ "3-5 - select a range of items\n"
+ "2-3,6-9 - select multiple ranges\n"
+ "foo - select item based on unique prefix\n"
+ "-... - unselect specified items\n"
+ "* - choose all items\n"
+ " - (empty) finish selecting"));
+ clean_print_color(CLEAN_COLOR_RESET);
+}
+
+/*
+ * display menu stuff with number prefix and hotkey highlight
+ */
+static void print_highlight_menu_stuff(struct menu_stuff *stuff, int **chosen)
+{
+ struct string_list menu_list = STRING_LIST_INIT_DUP;
+ struct strbuf menu = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ struct menu_item *menu_item;
+ struct string_list_item *string_list_item;
+ int i;
+
+ switch (stuff->type) {
+ default:
+ die("Bad type of menu_staff when print menu");
+ case MENU_STUFF_TYPE_MENU_ITEM:
+ menu_item = (struct menu_item *)stuff->stuff;
+ for (i = 0; i < stuff->nr; i++, menu_item++) {
+ const char *p;
+ int highlighted = 0;
+
+ p = menu_item->title;
+ if ((*chosen)[i] < 0)
+ (*chosen)[i] = menu_item->selected ? 1 : 0;
+ strbuf_addf(&menu, "%s%2d: ", (*chosen)[i] ? "*" : " ", i+1);
+ for (; *p; p++) {
+ if (!highlighted && *p == menu_item->hotkey) {
+ strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_PROMPT));
+ strbuf_addch(&menu, *p);
+ strbuf_addstr(&menu, clean_get_color(CLEAN_COLOR_RESET));
+ highlighted = 1;
+ } else {
+ strbuf_addch(&menu, *p);
+ }
+ }
+ string_list_append(&menu_list, menu.buf);
+ strbuf_reset(&menu);
+ }
+ break;
+ case MENU_STUFF_TYPE_STRING_LIST:
+ i = 0;
+ for_each_string_list_item(string_list_item, (struct string_list *)stuff->stuff) {
+ if ((*chosen)[i] < 0)
+ (*chosen)[i] = 0;
+ strbuf_addf(&menu, "%s%2d: %s",
+ (*chosen)[i] ? "*" : " ", i+1, string_list_item->string);
+ string_list_append(&menu_list, menu.buf);
+ strbuf_reset(&menu);
+ i++;
+ }
+ break;
+ }
+
+ pretty_print_menus(&menu_list);
+
+ strbuf_release(&menu);
+ strbuf_release(&buf);
+ string_list_clear(&menu_list, 0);
+}
+
+/*
+ * Parse user input, and return choice(s) for menu (menu_stuff).
+ *
+ * Input
+ * (for single choice)
+ * 1 - select a numbered item
+ * foo - select item based on menu title
+ * - (empty) select nothing
+ *
+ * (for multiple choice)
+ * 1 - select a single item
+ * 3-5 - select a range of items
+ * 2-3,6-9 - select multiple ranges
+ * foo - select item based on menu title
+ * -... - unselect specified items
+ * * - choose all items
+ * - (empty) finish selecting
+ *
+ * The parse result will be saved in array **chosen, and
+ * return number of total selections.
+ */
+static int parse_choice(struct menu_stuff *menu_stuff,
+ int is_single,
+ struct strbuf input,
+ int **chosen)
+{
+ struct strbuf **choice_list, **ptr;
+ struct menu_item *menu_item;
+ struct string_list_item *string_list_item;
+ int nr = 0;
+ int i;
+
+ if (is_single) {
+ choice_list = strbuf_split_max(&input, '\n', 0);
+ } else {
+ char *p = input.buf;
+ do {
+ if (*p == ',')
+ *p = ' ';
+ } while (*p++);
+ choice_list = strbuf_split_max(&input, ' ', 0);
+ }
+
+ for (ptr = choice_list; *ptr; ptr++) {
+ char *p;
+ int choose = 1;
+ int bottom = 0, top = 0;
+ int is_range, is_number;
+
+ strbuf_trim(*ptr);
+ if (!(*ptr)->len)
+ continue;
+
+ /* Input that begins with '-'; unchoose */
+ if (*(*ptr)->buf == '-') {
+ choose = 0;
+ strbuf_remove((*ptr), 0, 1);
+ }
+
+ is_range = 0;
+ is_number = 1;
+ for (p = (*ptr)->buf; *p; p++) {
+ if ('-' == *p) {
+ if (!is_range) {
+ is_range = 1;
+ is_number = 0;
+ } else {
+ is_number = 0;
+ is_range = 0;
+ break;
+ }
+ } else if (!isdigit(*p)) {
+ is_number = 0;
+ is_range = 0;
+ break;
+ }
+ }
+
+ if (is_number) {
+ bottom = atoi((*ptr)->buf);
+ top = bottom;
+ } else if (is_range) {
+ bottom = atoi((*ptr)->buf);
+ /* a range can be specified like 5-7 or 5- */
+ if (!*(strchr((*ptr)->buf, '-') + 1))
+ top = menu_stuff->nr;
+ else
+ top = atoi(strchr((*ptr)->buf, '-') + 1);
+ } else if (!strcmp((*ptr)->buf, "*")) {
+ bottom = 1;
+ top = menu_stuff->nr;
+ } else {
+ switch (menu_stuff->type) {
+ default:
+ die("Bad type of menu_stuff when parse choice");
+ case MENU_STUFF_TYPE_MENU_ITEM:
+ menu_item = (struct menu_item *)menu_stuff->stuff;
+ for (i = 0; i < menu_stuff->nr; i++, menu_item++) {
+ if (((*ptr)->len == 1 &&
+ *(*ptr)->buf == menu_item->hotkey) ||
+ !strcasecmp((*ptr)->buf, menu_item->title)) {
+ bottom = i + 1;
+ top = bottom;
+ break;
+ }
+ }
+ break;
+ case MENU_STUFF_TYPE_STRING_LIST:
+ string_list_item = ((struct string_list *)menu_stuff->stuff)->items;
+ for (i = 0; i < menu_stuff->nr; i++, string_list_item++) {
+ if (!strcasecmp((*ptr)->buf, string_list_item->string)) {
+ bottom = i + 1;
+ top = bottom;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if (top <= 0 || bottom <= 0 || top > menu_stuff->nr || bottom > top ||
+ (is_single && bottom != top)) {
+ clean_print_color(CLEAN_COLOR_ERROR);
+ printf_ln(_("Huh (%s)?"), (*ptr)->buf);
+ clean_print_color(CLEAN_COLOR_RESET);
+ continue;
+ }
+
+ for (i = bottom; i <= top; i++)
+ (*chosen)[i-1] = choose;
+ }
+
+ strbuf_list_free(choice_list);
+
+ for (i = 0; i < menu_stuff->nr; i++)
+ nr += (*chosen)[i];
+ return nr;
+}
+
+/*
+ * Implement a git-add-interactive compatible UI, which is borrowed
+ * from git-add--interactive.perl.
+ *
+ * Return value:
+ *
+ * - Return an array of integers
+ * - , and it is up to you to free the allocated memory.
+ * - The array ends with EOF.
+ * - If user pressed CTRL-D (i.e. EOF), no selection returned.
+ */
+static int *list_and_choose(struct menu_opts *opts, struct menu_stuff *stuff)
+{
+ struct strbuf choice = STRBUF_INIT;
+ int *chosen, *result;
+ int nr = 0;
+ int eof = 0;
+ int i;
+
+ chosen = xmalloc(sizeof(int) * stuff->nr);
+ /* set chosen as uninitialized */
+ for (i = 0; i < stuff->nr; i++)
+ chosen[i] = -1;
+
+ for (;;) {
+ if (opts->header) {
+ printf_ln("%s%s%s",
+ clean_get_color(CLEAN_COLOR_HEADER),
+ _(opts->header),
+ clean_get_color(CLEAN_COLOR_RESET));
+ }
+
+ /* chosen will be initialized by print_highlight_menu_stuff */
+ print_highlight_menu_stuff(stuff, &chosen);
+
+ if (opts->flags & MENU_OPTS_LIST_ONLY)
+ break;
+
+ if (opts->prompt) {
+ printf("%s%s%s%s",
+ clean_get_color(CLEAN_COLOR_PROMPT),
+ _(opts->prompt),
+ opts->flags & MENU_OPTS_SINGLETON ? "> " : ">> ",
+ clean_get_color(CLEAN_COLOR_RESET));
+ }
+
+ if (strbuf_getline(&choice, stdin, '\n') != EOF) {
+ strbuf_trim(&choice);
+ } else {
+ eof = 1;
+ break;
+ }
+
+ /* help for prompt */
+ if (!strcmp(choice.buf, "?")) {
+ prompt_help_cmd(opts->flags & MENU_OPTS_SINGLETON);
+ continue;
+ }
+
+ /* for a multiple-choice menu, press ENTER (empty) will return back */
+ if (!(opts->flags & MENU_OPTS_SINGLETON) && !choice.len)
+ break;
+
+ nr = parse_choice(stuff,
+ opts->flags & MENU_OPTS_SINGLETON,
+ choice,
+ &chosen);
+
+ if (opts->flags & MENU_OPTS_SINGLETON) {
+ if (nr)
+ break;
+ } else if (opts->flags & MENU_OPTS_IMMEDIATE) {
+ break;
+ }
+ }
+
+ if (eof) {
+ result = xmalloc(sizeof(int));
+ *result = EOF;
+ } else {
+ int j = 0;
+
+ /*
+ * recalculate nr, if return back from menu directly with
+ * default selections.
+ */
+ if (!nr) {
+ for (i = 0; i < stuff->nr; i++)
+ nr += chosen[i];
+ }
+
+ result = xmalloc(sizeof(int) * (nr + 1));
+ memset(result, 0, sizeof(int) * (nr + 1));
+ for (i = 0; i < stuff->nr && j < nr; i++) {
+ if (chosen[i])
+ result[j++] = i;
+ }
+ result[j] = EOF;
+ }
+
+ free(chosen);
+ strbuf_release(&choice);
+ return result;
+}
+
+static int clean_cmd(void)
+{
+ return MENU_RETURN_NO_LOOP;
+}
+
+static int filter_by_patterns_cmd(void)
+{
+ struct dir_struct dir;
+ struct strbuf confirm = STRBUF_INIT;
+ struct strbuf **ignore_list;
+ struct string_list_item *item;
+ struct exclude_list *el;
+ int changed = -1, i;
+
+ for (;;) {
+ if (!del_list.nr)
+ break;
+
+ if (changed)
+ pretty_print_dels();
+
+ clean_print_color(CLEAN_COLOR_PROMPT);
+ printf(_("Input ignore patterns>> "));
+ clean_print_color(CLEAN_COLOR_RESET);
+ if (strbuf_getline(&confirm, stdin, '\n') != EOF)
+ strbuf_trim(&confirm);
+ else
+ putchar('\n');
+
+ /* quit filter_by_pattern mode if press ENTER or Ctrl-D */
+ if (!confirm.len)
+ break;
+
+ memset(&dir, 0, sizeof(dir));
+ el = add_exclude_list(&dir, EXC_CMDL, "manual exclude");
+ ignore_list = strbuf_split_max(&confirm, ' ', 0);
+
+ for (i = 0; ignore_list[i]; i++) {
+ strbuf_trim(ignore_list[i]);
+ if (!ignore_list[i]->len)
+ continue;
+
+ add_exclude(ignore_list[i]->buf, "", 0, el, -(i+1));
+ }
+
+ changed = 0;
+ for_each_string_list_item(item, &del_list) {
+ int dtype = DT_UNKNOWN;
+
+ if (is_excluded(&dir, item->string, &dtype)) {
+ *item->string = '\0';
+ changed++;
+ }
+ }
+
+ if (changed) {
+ string_list_remove_empty_items(&del_list, 0);
+ } else {
+ clean_print_color(CLEAN_COLOR_ERROR);
+ printf_ln(_("WARNING: Cannot find items matched by: %s"), confirm.buf);
+ clean_print_color(CLEAN_COLOR_RESET);
+ }
+
+ strbuf_list_free(ignore_list);
+ clear_directory(&dir);
+ }
+
+ strbuf_release(&confirm);
+ return 0;
+}
+
+static int select_by_numbers_cmd(void)
+{
+ struct menu_opts menu_opts;
+ struct menu_stuff menu_stuff;
+ struct string_list_item *items;
+ int *chosen;
+ int i, j;
+
+ menu_opts.header = NULL;
+ menu_opts.prompt = N_("Select items to delete");
+ menu_opts.flags = 0;
+
+ menu_stuff.type = MENU_STUFF_TYPE_STRING_LIST;
+ menu_stuff.stuff = &del_list;
+ menu_stuff.nr = del_list.nr;
+
+ chosen = list_and_choose(&menu_opts, &menu_stuff);
+ items = del_list.items;
+ for (i = 0, j = 0; i < del_list.nr; i++) {
+ if (i < chosen[j]) {
+ *(items[i].string) = '\0';
+ } else if (i == chosen[j]) {
+ /* delete selected item */
+ j++;
+ continue;
+ } else {
+ /* end of chosen (chosen[j] == EOF), won't delete */
+ *(items[i].string) = '\0';
+ }
+ }
+
+ string_list_remove_empty_items(&del_list, 0);
+
+ free(chosen);
+ return 0;
+}
+
+static int ask_each_cmd(void)
+{
+ struct strbuf confirm = STRBUF_INIT;
+ struct strbuf buf = STRBUF_INIT;
+ struct string_list_item *item;
+ const char *qname;
+ int changed = 0, eof = 0;
+
+ for_each_string_list_item(item, &del_list) {
+ /* Ctrl-D should stop removing files */
+ if (!eof) {
+ qname = quote_path_relative(item->string, NULL, &buf);
+ printf(_("remove %s? "), qname);
+ if (strbuf_getline(&confirm, stdin, '\n') != EOF) {
+ strbuf_trim(&confirm);
+ } else {
+ putchar('\n');
+ eof = 1;
+ }
+ }
+ if (!confirm.len || strncasecmp(confirm.buf, "yes", confirm.len)) {
+ *item->string = '\0';
+ changed++;
+ }
+ }
+
+ if (changed)
+ string_list_remove_empty_items(&del_list, 0);
+
+ strbuf_release(&buf);
+ strbuf_release(&confirm);
+ return MENU_RETURN_NO_LOOP;
+}
+
+static int quit_cmd(void)
+{
+ string_list_clear(&del_list, 0);
+ printf_ln(_("Bye."));
+ return MENU_RETURN_NO_LOOP;
+}
+
+static int help_cmd(void)
+{
+ clean_print_color(CLEAN_COLOR_HELP);
+ printf_ln(_(
+ "clean - start cleaning\n"
+ "filter by pattern - exclude items from deletion\n"
+ "select by numbers - select items to be deleted by numbers\n"
+ "ask each - confirm each deletion (like \"rm -i\")\n"
+ "quit - stop cleaning\n"
+ "help - this screen\n"
+ "? - help for prompt selection"
+ ));
+ clean_print_color(CLEAN_COLOR_RESET);
+ return 0;
+}
+
+static void interactive_main_loop(void)
+{
+ while (del_list.nr) {
+ struct menu_opts menu_opts;
+ struct menu_stuff menu_stuff;
+ struct menu_item menus[] = {
+ {'c', "clean", 0, clean_cmd},
+ {'f', "filter by pattern", 0, filter_by_patterns_cmd},
+ {'s', "select by numbers", 0, select_by_numbers_cmd},
+ {'a', "ask each", 0, ask_each_cmd},
+ {'q', "quit", 0, quit_cmd},
+ {'h', "help", 0, help_cmd},
+ };
+ int *chosen;
+
+ menu_opts.header = N_("*** Commands ***");
+ menu_opts.prompt = N_("What now");
+ menu_opts.flags = MENU_OPTS_SINGLETON;
+
+ menu_stuff.type = MENU_STUFF_TYPE_MENU_ITEM;
+ menu_stuff.stuff = menus;
+ menu_stuff.nr = sizeof(menus) / sizeof(struct menu_item);
+
+ clean_print_color(CLEAN_COLOR_HEADER);
+ printf_ln(Q_("Would remove the following item:",
+ "Would remove the following items:",
+ del_list.nr));
+ clean_print_color(CLEAN_COLOR_RESET);
+
+ pretty_print_dels();
+
+ chosen = list_and_choose(&menu_opts, &menu_stuff);
+
+ if (*chosen != EOF) {
+ int ret;
+ ret = menus[*chosen].fn();
+ if (ret != MENU_RETURN_NO_LOOP) {
+ free(chosen);
+ chosen = NULL;
+ if (!del_list.nr) {
+ clean_print_color(CLEAN_COLOR_ERROR);
+ printf_ln(_("No more files to clean, exiting."));
+ clean_print_color(CLEAN_COLOR_RESET);
+ break;
+ }
+ continue;
+ }
+ } else {
+ quit_cmd();
+ }
+
+ free(chosen);
+ chosen = NULL;
+ break;
+ }
+}
+
int cmd_clean(int argc, const char **argv, const char *prefix)
{
int i, res;
int dry_run = 0, remove_directories = 0, quiet = 0, ignored = 0;
int ignored_only = 0, config_set = 0, errors = 0, gone = 1;
int rm_flags = REMOVE_DIR_KEEP_NESTED_GIT;
- struct strbuf directory = STRBUF_INIT;
+ struct strbuf abs_path = STRBUF_INIT;
struct dir_struct dir;
static const char **pathspec;
struct strbuf buf = STRBUF_INIT;
struct string_list exclude_list = STRING_LIST_INIT_NODUP;
struct exclude_list *el;
+ struct string_list_item *item;
const char *qname;
char *seen = NULL;
struct option options[] = {
OPT__QUIET(&quiet, N_("do not print names of files removed")),
OPT__DRY_RUN(&dry_run, N_("dry run")),
OPT__FORCE(&force, N_("force")),
+ OPT_BOOL('i', "interactive", &interactive, N_("interactive cleaning")),
OPT_BOOLEAN('d', NULL, &remove_directories,
N_("remove whole directories")),
{ OPTION_CALLBACK, 'e', "exclude", &exclude_list, N_("pattern"),
@@ -186,12 +877,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
if (ignored && ignored_only)
die(_("-x and -X cannot be used together"));
- if (!dry_run && !force) {
+ if (!interactive && !dry_run && !force) {
if (config_set)
- die(_("clean.requireForce set to true and neither -n nor -f given; "
+ die(_("clean.requireForce set to true and neither -i, -n nor -f given; "
"refusing to clean"));
else
- die(_("clean.requireForce defaults to true and neither -n nor -f given; "
+ die(_("clean.requireForce defaults to true and neither -i, -n nor -f given; "
"refusing to clean"));
}
@@ -221,8 +912,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
struct dir_entry *ent = dir.entries[i];
int len, pos;
int matches = 0;
- struct cache_entry *ce;
+ const struct cache_entry *ce;
struct stat st;
+ const char *rel;
/*
* Remove the '/' at the end that directory
@@ -242,13 +934,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
continue; /* Yup, this one exists unmerged */
}
- /*
- * we might have removed this as part of earlier
- * recursive directory removal, so lstat() here could
- * fail with ENOENT.
- */
if (lstat(ent->name, &st))
- continue;
+ die_errno("Cannot lstat '%s'", ent->name);
if (pathspec) {
memset(seen, 0, argc > 0 ? argc : 1);
@@ -257,33 +944,62 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
}
if (S_ISDIR(st.st_mode)) {
- strbuf_addstr(&directory, ent->name);
if (remove_directories || (matches == MATCHED_EXACTLY)) {
- if (remove_dirs(&directory, prefix, rm_flags, dry_run, quiet, &gone))
- errors++;
- if (gone && !quiet) {
- qname = quote_path_relative(directory.buf, directory.len, &buf, prefix);
- printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
- }
+ rel = relative_path(ent->name, prefix, &buf);
+ string_list_append(&del_list, rel);
}
- strbuf_reset(&directory);
} else {
if (pathspec && !matches)
continue;
- res = dry_run ? 0 : unlink(ent->name);
+ rel = relative_path(ent->name, prefix, &buf);
+ string_list_append(&del_list, rel);
+ }
+ }
+
+ if (interactive && del_list.nr > 0)
+ interactive_main_loop();
+
+ for_each_string_list_item(item, &del_list) {
+ struct stat st;
+
+ if (prefix)
+ strbuf_addstr(&abs_path, prefix);
+
+ strbuf_addstr(&abs_path, item->string);
+
+ /*
+ * we might have removed this as part of earlier
+ * recursive directory removal, so lstat() here could
+ * fail with ENOENT.
+ */
+ if (lstat(abs_path.buf, &st))
+ continue;
+
+ if (S_ISDIR(st.st_mode)) {
+ if (remove_dirs(&abs_path, prefix, rm_flags, dry_run, quiet, &gone))
+ errors++;
+ if (gone && !quiet) {
+ qname = quote_path_relative(item->string, NULL, &buf);
+ printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
+ }
+ } else {
+ res = dry_run ? 0 : unlink(abs_path.buf);
if (res) {
- qname = quote_path_relative(ent->name, -1, &buf, prefix);
+ qname = quote_path_relative(item->string, NULL, &buf);
warning(_(msg_warn_remove_failed), qname);
errors++;
} else if (!quiet) {
- qname = quote_path_relative(ent->name, -1, &buf, prefix);
+ qname = quote_path_relative(item->string, NULL, &buf);
printf(dry_run ? _(msg_would_remove) : _(msg_remove), qname);
}
}
+ strbuf_reset(&abs_path);
}
free(seen);
- strbuf_release(&directory);
+ strbuf_release(&abs_path);
+ strbuf_release(&buf);
+ string_list_clear(&del_list, 0);
string_list_clear(&exclude_list, 0);
return (errors != 0);
}
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);
diff --git a/builtin/commit.c b/builtin/commit.c
index 790e5ab5cf..003bd7dbec 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -207,7 +207,7 @@ static int list_paths(struct string_list *list, const char *with_tree,
}
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
struct string_list_item *item;
if (ce->ce_flags & CE_UPDATE)
@@ -534,7 +534,6 @@ static void determine_author_info(struct strbuf *author_ident)
(lb - strlen(" ") -
(a + strlen("\nauthor "))));
email = xmemdupz(lb + strlen("<"), rb - (lb + strlen("<")));
- date = xmemdupz(rb + strlen("> "), eol - (rb + strlen("> ")));
len = eol - (rb + strlen("> "));
date = xmalloc(len + 2);
*date = '@';
diff --git a/builtin/config.c b/builtin/config.c
index 7759671eb8..4010c4320a 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -21,6 +21,7 @@ static char term = '\n';
static int use_global_config, use_system_config, use_local_config;
static const char *given_config_file;
+static const char *given_config_blob;
static int actions, types;
static const char *get_color_slot, *get_colorbool_slot;
static int end_null;
@@ -53,6 +54,7 @@ static struct option builtin_config_options[] = {
OPT_BOOLEAN(0, "system", &use_system_config, N_("use system config file")),
OPT_BOOLEAN(0, "local", &use_local_config, N_("use repository config file")),
OPT_STRING('f', "file", &given_config_file, N_("file"), N_("use given config file")),
+ OPT_STRING(0, "blob", &given_config_blob, N_("blob-id"), N_("read config from given blob object")),
OPT_GROUP(N_("Action")),
OPT_BIT(0, "get", &actions, N_("get value: name [value-regex]"), ACTION_GET),
OPT_BIT(0, "get-all", &actions, N_("get all values: key [value-regex]"), ACTION_GET_ALL),
@@ -218,7 +220,8 @@ static int get_value(const char *key_, const char *regex_)
}
git_config_with_options(collect_config, &values,
- given_config_file, respect_includes);
+ given_config_file, given_config_blob,
+ respect_includes);
ret = !values.nr;
@@ -302,7 +305,8 @@ static void get_color(const char *def_color)
get_color_found = 0;
parsed_color[0] = '\0';
git_config_with_options(git_get_color_config, NULL,
- given_config_file, respect_includes);
+ given_config_file, given_config_blob,
+ respect_includes);
if (!get_color_found && def_color)
color_parse(def_color, "command line", parsed_color);
@@ -331,7 +335,8 @@ static int get_colorbool(int print)
get_diff_color_found = -1;
get_color_ui_found = -1;
git_config_with_options(git_get_colorbool_config, NULL,
- given_config_file, respect_includes);
+ given_config_file, given_config_blob,
+ respect_includes);
if (get_colorbool_found < 0) {
if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -353,6 +358,12 @@ static int get_colorbool(int print)
return get_colorbool_found ? 0 : 1;
}
+static void check_blob_write(void)
+{
+ if (given_config_blob)
+ die("writing config blobs is not supported");
+}
+
int cmd_config(int argc, const char **argv, const char *prefix)
{
int nongit = !startup_info->have_repository;
@@ -364,7 +375,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
builtin_config_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
- if (use_global_config + use_system_config + use_local_config + !!given_config_file > 1) {
+ if (use_global_config + use_system_config + use_local_config +
+ !!given_config_file + !!given_config_blob > 1) {
error("only one config file at a time.");
usage_with_options(builtin_config_usage, builtin_config_options);
}
@@ -443,6 +455,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_argc(argc, 0, 0);
if (git_config_with_options(show_all_config, NULL,
given_config_file,
+ given_config_blob,
respect_includes) < 0) {
if (given_config_file)
die_errno("unable to read config file '%s'",
@@ -455,6 +468,8 @@ int cmd_config(int argc, const char **argv, const char *prefix)
check_argc(argc, 0, 0);
if (!given_config_file && nongit)
die("not in a git directory");
+ if (given_config_blob)
+ die("editing blobs is not supported");
git_config(git_default_config, NULL);
launch_editor(given_config_file ?
given_config_file : git_path("config"),
@@ -462,6 +477,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
else if (actions == ACTION_SET) {
int ret;
+ check_blob_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
ret = git_config_set_in_file(given_config_file, argv[0], value);
@@ -471,18 +487,21 @@ int cmd_config(int argc, const char **argv, const char *prefix)
return ret;
}
else if (actions == ACTION_SET_ALL) {
+ check_blob_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
return git_config_set_multivar_in_file(given_config_file,
argv[0], value, argv[2], 0);
}
else if (actions == ACTION_ADD) {
+ check_blob_write();
check_argc(argc, 2, 2);
value = normalize_value(argv[0], argv[1]);
return git_config_set_multivar_in_file(given_config_file,
argv[0], value, "^$", 0);
}
else if (actions == ACTION_REPLACE_ALL) {
+ check_blob_write();
check_argc(argc, 2, 3);
value = normalize_value(argv[0], argv[1]);
return git_config_set_multivar_in_file(given_config_file,
@@ -505,6 +524,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
return get_value(argv[0], argv[1]);
}
else if (actions == ACTION_UNSET) {
+ check_blob_write();
check_argc(argc, 1, 2);
if (argc == 2)
return git_config_set_multivar_in_file(given_config_file,
@@ -514,12 +534,14 @@ int cmd_config(int argc, const char **argv, const char *prefix)
argv[0], NULL);
}
else if (actions == ACTION_UNSET_ALL) {
+ check_blob_write();
check_argc(argc, 1, 2);
return git_config_set_multivar_in_file(given_config_file,
argv[0], NULL, argv[1], 1);
}
else if (actions == ACTION_RENAME_SECTION) {
int ret;
+ check_blob_write();
check_argc(argc, 2, 2);
ret = git_config_rename_section_in_file(given_config_file,
argv[0], argv[1]);
@@ -530,6 +552,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
}
else if (actions == ACTION_REMOVE_SECTION) {
int ret;
+ check_blob_write();
check_argc(argc, 1, 1);
ret = git_config_rename_section_in_file(given_config_file,
argv[0], NULL);
diff --git a/builtin/describe.c b/builtin/describe.c
index 4e675c3d0d..7d73722f59 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -7,6 +7,7 @@
#include "parse-options.h"
#include "diff.h"
#include "hash.h"
+#include "argv-array.h"
#define SEEN (1u<<0)
#define MAX_TAGS (FLAG_BITS - 1)
@@ -442,24 +443,24 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
die(_("--long is incompatible with --abbrev=0"));
if (contains) {
- const char **args = xmalloc((7 + argc) * sizeof(char *));
- int i = 0;
- args[i++] = "name-rev";
- args[i++] = "--name-only";
- args[i++] = "--no-undefined";
+ struct argv_array args;
+
+ argv_array_init(&args);
+ argv_array_pushl(&args, "name-rev",
+ "--peel-tag", "--name-only", "--no-undefined",
+ NULL);
if (always)
- args[i++] = "--always";
+ argv_array_push(&args, "--always");
if (!all) {
- args[i++] = "--tags";
- if (pattern) {
- char *s = xmalloc(strlen("--refs=refs/tags/") + strlen(pattern) + 1);
- sprintf(s, "--refs=refs/tags/%s", pattern);
- args[i++] = s;
- }
+ argv_array_push(&args, "--tags");
+ if (pattern)
+ argv_array_pushf(&args, "--refs=refs/tags/%s", pattern);
+ }
+ while (*argv) {
+ argv_array_push(&args, *argv);
+ argv++;
}
- memcpy(args + i, argv, argc * sizeof(char *));
- args[i + argc] = NULL;
- return cmd_name_rev(i + argc, args, prefix);
+ return cmd_name_rev(args.argc, args.argv, prefix);
}
init_hash(&names);
diff --git a/builtin/grep.c b/builtin/grep.c
index 159e65d47a..d3b3b1db11 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -286,8 +286,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
struct strbuf pathbuf = STRBUF_INIT;
if (opt->relative && opt->prefix_length) {
- quote_path_relative(filename + tree_name_len, -1, &pathbuf,
- opt->prefix);
+ quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
strbuf_insert(&pathbuf, 0, filename, tree_name_len);
} else {
strbuf_addstr(&pathbuf, filename);
@@ -318,7 +317,7 @@ static int grep_file(struct grep_opt *opt, const char *filename)
struct strbuf buf = STRBUF_INIT;
if (opt->relative && opt->prefix_length)
- quote_path_relative(filename, -1, &buf, opt->prefix);
+ quote_path_relative(filename, opt->prefix, &buf);
else
strbuf_addstr(&buf, filename);
@@ -376,7 +375,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec, int
read_cache();
for (nr = 0; nr < active_nr; nr++) {
- struct cache_entry *ce = active_cache[nr];
+ const struct cache_entry *ce = active_cache[nr];
if (!S_ISREG(ce->ce_mode))
continue;
if (!match_pathspec_depth(pathspec, ce->name, ce_namelen(ce), 0, NULL))
diff --git a/builtin/log.c b/builtin/log.c
index e3222ed9f9..2625f9881a 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1112,6 +1112,21 @@ static int cc_callback(const struct option *opt, const char *arg, int unset)
return 0;
}
+static int from_callback(const struct option *opt, const char *arg, int unset)
+{
+ char **from = opt->value;
+
+ free(*from);
+
+ if (unset)
+ *from = NULL;
+ else if (arg)
+ *from = xstrdup(arg);
+ else
+ *from = xstrdup(git_committer_info(IDENT_NO_DATE));
+ return 0;
+}
+
int cmd_format_patch(int argc, const char **argv, const char *prefix)
{
struct commit *commit;
@@ -1134,6 +1149,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
int quiet = 0;
int reroll_count = -1;
char *branch_name = NULL;
+ char *from = NULL;
const struct option builtin_format_patch_options[] = {
{ OPTION_CALLBACK, 'n', "numbered", &numbered, NULL,
N_("use [PATCH n/m] even with a single patch"),
@@ -1177,6 +1193,9 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
0, to_callback },
{ OPTION_CALLBACK, 0, "cc", NULL, N_("email"), N_("add Cc: header"),
0, cc_callback },
+ { OPTION_CALLBACK, 0, "from", &from, N_("ident"),
+ N_("set From address to <ident> (or committer ident if absent)"),
+ PARSE_OPT_OPTARG, from_callback },
OPT_STRING(0, "in-reply-to", &in_reply_to, N_("message-id"),
N_("make first mail a reply to <message-id>")),
{ OPTION_CALLBACK, 0, "attach", &rev, N_("boundary"),
@@ -1264,6 +1283,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
rev.extra_headers = strbuf_detach(&buf, NULL);
+ if (from) {
+ if (split_ident_line(&rev.from_ident, from, strlen(from)))
+ die(_("invalid ident line: %s"), from);
+ }
+
if (start_number < 0)
start_number = 1;
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index 3a410c35d9..5cf3e31370 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -46,10 +46,14 @@ static const char *tag_modified = "";
static const char *tag_skip_worktree = "";
static const char *tag_resolve_undo = "";
-static void write_name(const char* name, size_t len)
+static void write_name(const char *name)
{
- write_name_quoted_relative(name, len, prefix, prefix_len, stdout,
- line_terminator);
+ /*
+ * With "--full-name", prefix_len=0; this caller needs to pass
+ * an empty string in that case (a NULL is good for "").
+ */
+ write_name_quoted_relative(name, prefix_len ? prefix : NULL,
+ stdout, line_terminator);
}
static void show_dir_entry(const char *tag, struct dir_entry *ent)
@@ -63,7 +67,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
return;
fputs(tag, stdout);
- write_name(ent->name, ent->len);
+ write_name(ent->name);
}
static void show_other_files(struct dir_struct *dir)
@@ -127,7 +131,7 @@ static void show_killed_files(struct dir_struct *dir)
}
}
-static void show_ce_entry(const char *tag, struct cache_entry *ce)
+static void show_ce_entry(const char *tag, const struct cache_entry *ce)
{
int len = max_prefix_len;
@@ -163,9 +167,9 @@ static void show_ce_entry(const char *tag, struct cache_entry *ce)
find_unique_abbrev(ce->sha1,abbrev),
ce_stage(ce));
}
- write_name(ce->name, ce_namelen(ce));
+ write_name(ce->name);
if (debug_mode) {
- struct stat_data *sd = &ce->ce_stat_data;
+ const struct stat_data *sd = &ce->ce_stat_data;
printf(" ctime: %d:%d\n", sd->sd_ctime.sec, sd->sd_ctime.nsec);
printf(" mtime: %d:%d\n", sd->sd_mtime.sec, sd->sd_mtime.nsec);
@@ -198,12 +202,12 @@ static void show_ru_info(void)
printf("%s%06o %s %d\t", tag_resolve_undo, ui->mode[i],
find_unique_abbrev(ui->sha1[i], abbrev),
i + 1);
- write_name(path, len);
+ write_name(path);
}
}
}
-static int ce_excluded(struct dir_struct *dir, struct cache_entry *ce)
+static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce)
{
int dtype = ce_to_dtype(ce);
return is_excluded(dir, ce->name, &dtype);
@@ -223,7 +227,7 @@ static void show_files(struct dir_struct *dir)
}
if (show_cached || show_stage) {
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if ((dir->flags & DIR_SHOW_IGNORED) &&
!ce_excluded(dir, ce))
continue;
@@ -237,7 +241,7 @@ static void show_files(struct dir_struct *dir)
}
if (show_deleted || show_modified) {
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
struct stat st;
int err;
if ((dir->flags & DIR_SHOW_IGNORED) &&
@@ -273,7 +277,7 @@ static void prune_cache(const char *prefix)
last = active_nr;
while (last > first) {
int next = (last + first) >> 1;
- struct cache_entry *ce = active_cache[next];
+ const struct cache_entry *ce = active_cache[next];
if (!strncmp(ce->name, prefix, max_prefix_len)) {
first = next+1;
continue;
@@ -391,7 +395,7 @@ int report_path_error(const char *ps_matched, const char **pathspec, const char
if (found_dup)
continue;
- name = quote_path_relative(pathspec[num], -1, &sb, prefix);
+ name = quote_path_relative(pathspec[num], prefix, &sb);
error("pathspec '%s' did not match any file(s) known to git.",
name);
errors++;
diff --git a/builtin/merge-index.c b/builtin/merge-index.c
index be5e514324..b416d92849 100644
--- a/builtin/merge-index.c
+++ b/builtin/merge-index.c
@@ -16,7 +16,7 @@ static int merge_entry(int pos, const char *path)
die("git merge-index: %s not in the cache", path);
found = 0;
do {
- struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = active_cache[pos];
int stage = ce_stage(ce);
if (strcmp(ce->name, path))
@@ -58,7 +58,7 @@ static void merge_all(void)
{
int i;
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if (!ce_stage(ce))
continue;
i += merge_entry(i, ce->name)-1;
diff --git a/builtin/merge.c b/builtin/merge.c
index bad4536a87..34a6166b52 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -47,8 +47,8 @@ static const char * const builtin_merge_usage[] = {
};
static int show_diffstat = 1, shortlog_len = -1, squash;
-static int option_commit = 1, allow_fast_forward = 1;
-static int fast_forward_only, option_edit = -1;
+static int option_commit = 1;
+static int option_edit = -1;
static int allow_trivial = 1, have_message, verify_signatures;
static int overwrite_ignore = 1;
static struct strbuf merge_msg = STRBUF_INIT;
@@ -76,6 +76,14 @@ static struct strategy all_strategy[] = {
static const char *pull_twohead, *pull_octopus;
+enum ff_type {
+ FF_NO,
+ FF_ALLOW,
+ FF_ONLY
+};
+
+static enum ff_type fast_forward = FF_ALLOW;
+
static int option_parse_message(const struct option *opt,
const char *arg, int unset)
{
@@ -178,6 +186,13 @@ static int option_parse_n(const struct option *opt,
return 0;
}
+static int option_parse_ff_only(const struct option *opt,
+ const char *arg, int unset)
+{
+ fast_forward = FF_ONLY;
+ return 0;
+}
+
static struct option builtin_merge_options[] = {
{ OPTION_CALLBACK, 'n', NULL, NULL, NULL,
N_("do not show a diffstat at the end of the merge"),
@@ -194,10 +209,10 @@ static struct option builtin_merge_options[] = {
N_("perform a commit if the merge succeeds (default)")),
OPT_BOOL('e', "edit", &option_edit,
N_("edit message before committing")),
- OPT_BOOLEAN(0, "ff", &allow_fast_forward,
- N_("allow fast-forward (default)")),
- OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
- N_("abort if fast-forward is not possible")),
+ OPT_SET_INT(0, "ff", &fast_forward, N_("allow fast-forward (default)"), FF_ALLOW),
+ { OPTION_CALLBACK, 0, "ff-only", NULL, NULL,
+ N_("abort if fast-forward is not possible"),
+ PARSE_OPT_NOARG | PARSE_OPT_NONEG, option_parse_ff_only },
OPT_RERERE_AUTOUPDATE(&allow_rerere_auto),
OPT_BOOL(0, "verify-signatures", &verify_signatures,
N_("Verify that the named commit has a valid GPG signature")),
@@ -581,10 +596,9 @@ static int git_merge_config(const char *k, const char *v, void *cb)
else if (!strcmp(k, "merge.ff")) {
int boolval = git_config_maybe_bool(k, v);
if (0 <= boolval) {
- allow_fast_forward = boolval;
+ fast_forward = boolval ? FF_ALLOW : FF_NO;
} else if (v && !strcmp(v, "only")) {
- allow_fast_forward = 1;
- fast_forward_only = 1;
+ fast_forward = FF_ONLY;
} /* do not barf on values from future versions of git */
return 0;
} else if (!strcmp(k, "merge.defaulttoupstream")) {
@@ -863,7 +877,7 @@ static int finish_automerge(struct commit *head,
free_commit_list(common);
parents = remoteheads;
- if (!head_subsumed || !allow_fast_forward)
+ if (!head_subsumed || fast_forward == FF_NO)
commit_list_insert(head, &parents);
strbuf_addch(&merge_msg, '\n');
prepare_to_commit(remoteheads);
@@ -889,7 +903,7 @@ static int suggest_conflicts(int renormalizing)
die_errno(_("Could not open '%s' for writing"), filename);
fprintf(fp, "\nConflicts:\n");
for (pos = 0; pos < active_nr; pos++) {
- struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = active_cache[pos];
if (ce_stage(ce)) {
fprintf(fp, "\t%s\n", ce->name);
@@ -1008,7 +1022,7 @@ static void write_merge_state(struct commit_list *remoteheads)
if (fd < 0)
die_errno(_("Could not open '%s' for writing"), filename);
strbuf_reset(&buf);
- if (!allow_fast_forward)
+ if (fast_forward == FF_NO)
strbuf_addf(&buf, "no-ff");
if (write_in_full(fd, buf.buf, buf.len) != buf.len)
die_errno(_("Could not write to '%s'"), filename);
@@ -1157,14 +1171,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
show_diffstat = 0;
if (squash) {
- if (!allow_fast_forward)
+ if (fast_forward == FF_NO)
die(_("You cannot combine --squash with --no-ff."));
option_commit = 0;
}
- if (!allow_fast_forward && fast_forward_only)
- die(_("You cannot combine --no-ff with --ff-only."));
-
if (!abort_current_merge) {
if (!argc) {
if (default_to_upstream)
@@ -1206,7 +1217,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
"empty head"));
if (squash)
die(_("Squash commit into empty head not supported yet"));
- if (!allow_fast_forward)
+ if (fast_forward == FF_NO)
die(_("Non-fast-forward commit does not make sense into "
"an empty head"));
remoteheads = collect_parents(head_commit, &head_subsumed, argc, argv);
@@ -1294,11 +1305,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
sha1_to_hex(commit->object.sha1));
setenv(buf.buf, merge_remote_util(commit)->name, 1);
strbuf_reset(&buf);
- if (!fast_forward_only &&
+ if (fast_forward != FF_ONLY &&
merge_remote_util(commit) &&
merge_remote_util(commit)->obj &&
merge_remote_util(commit)->obj->type == OBJ_TAG)
- allow_fast_forward = 0;
+ fast_forward = FF_NO;
}
if (option_edit < 0)
@@ -1315,7 +1326,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
for (i = 0; i < use_strategies_nr; i++) {
if (use_strategies[i]->attr & NO_FAST_FORWARD)
- allow_fast_forward = 0;
+ fast_forward = FF_NO;
if (use_strategies[i]->attr & NO_TRIVIAL)
allow_trivial = 0;
}
@@ -1345,7 +1356,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
*/
finish_up_to_date("Already up-to-date.");
goto done;
- } else if (allow_fast_forward && !remoteheads->next &&
+ } else if (fast_forward != FF_NO && !remoteheads->next &&
!common->next &&
!hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
/* Again the most common case of merging one remote. */
@@ -1392,7 +1403,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
* only one common.
*/
refresh_cache(REFRESH_QUIET);
- if (allow_trivial && !fast_forward_only) {
+ if (allow_trivial && fast_forward != FF_ONLY) {
/* See if it is really trivial. */
git_committer_info(IDENT_STRICT);
printf(_("Trying really trivial in-index merge...\n"));
@@ -1433,7 +1444,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
}
}
- if (fast_forward_only)
+ if (fast_forward == FF_ONLY)
die(_("Not possible to fast-forward, aborting."));
/* We are going to make a new commit. */
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index 87d485496f..0aaa19e4ab 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -4,6 +4,7 @@
#include "tag.h"
#include "refs.h"
#include "parse-options.h"
+#include "sha1-lookup.h"
#define CUTOFF_DATE_SLOP 86400 /* one day */
@@ -96,12 +97,51 @@ static int subpath_matches(const char *path, const char *filter)
return -1;
}
+static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
+{
+ if (shorten_unambiguous)
+ refname = shorten_unambiguous_ref(refname, 0);
+ else if (!prefixcmp(refname, "refs/heads/"))
+ refname = refname + 11;
+ else if (!prefixcmp(refname, "refs/"))
+ refname = refname + 5;
+ return refname;
+}
+
struct name_ref_data {
int tags_only;
int name_only;
const char *ref_filter;
};
+static struct tip_table {
+ struct tip_table_entry {
+ unsigned char sha1[20];
+ const char *refname;
+ } *table;
+ int nr;
+ int alloc;
+ int sorted;
+} tip_table;
+
+static void add_to_tip_table(const unsigned char *sha1, const char *refname,
+ int shorten_unambiguous)
+{
+ refname = name_ref_abbrev(refname, shorten_unambiguous);
+
+ ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
+ hashcpy(tip_table.table[tip_table.nr].sha1, sha1);
+ tip_table.table[tip_table.nr].refname = xstrdup(refname);
+ tip_table.nr++;
+ tip_table.sorted = 0;
+}
+
+static int tipcmp(const void *a_, const void *b_)
+{
+ const struct tip_table_entry *a = a_, *b = b_;
+ return hashcmp(a->sha1, b->sha1);
+}
+
static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)
{
struct object *o = parse_object(sha1);
@@ -124,6 +164,8 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
}
}
+ add_to_tip_table(sha1, path, can_abbreviate_output);
+
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
if (!t->tagged)
@@ -134,18 +176,38 @@ static int name_ref(const char *path, const unsigned char *sha1, int flags, void
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
- if (can_abbreviate_output)
- path = shorten_unambiguous_ref(path, 0);
- else if (!prefixcmp(path, "refs/heads/"))
- path = path + 11;
- else if (!prefixcmp(path, "refs/"))
- path = path + 5;
-
+ path = name_ref_abbrev(path, can_abbreviate_output);
name_rev(commit, xstrdup(path), 0, 0, deref);
}
return 0;
}
+static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
+{
+ struct tip_table_entry *table = table_;
+ return table[ix].sha1;
+}
+
+static const char *get_exact_ref_match(const struct object *o)
+{
+ int found;
+
+ if (!tip_table.table || !tip_table.nr)
+ return NULL;
+
+ if (!tip_table.sorted) {
+ qsort(tip_table.table, tip_table.nr, sizeof(*tip_table.table),
+ tipcmp);
+ tip_table.sorted = 1;
+ }
+
+ found = sha1_pos(o->sha1, tip_table.table, tip_table.nr,
+ nth_tip_table_ent);
+ if (0 <= found)
+ return tip_table.table[found].refname;
+ return NULL;
+}
+
/* returns a static buffer */
static const char *get_rev_name(const struct object *o)
{
@@ -154,7 +216,7 @@ static const char *get_rev_name(const struct object *o)
struct commit *c;
if (o->type != OBJ_COMMIT)
- return NULL;
+ return get_exact_ref_match(o);
c = (struct commit *) o;
n = c->util;
if (!n)
@@ -245,7 +307,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = OBJECT_ARRAY_INIT;
- int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0;
+ int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
struct name_ref_data data = { 0, 0, NULL };
struct option opts[] = {
OPT_BOOLEAN(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
@@ -258,6 +320,12 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
OPT_BOOLEAN(0, "undefined", &allow_undefined, N_("allow to print `undefined` names")),
OPT_BOOLEAN(0, "always", &always,
N_("show abbreviated commit object as fallback")),
+ {
+ /* A Hidden OPT_BOOL */
+ OPTION_SET_INT, 0, "peel-tag", &peel_tag, NULL,
+ N_("dereference tags in the input (internal use)"),
+ PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, NULL, 1,
+ },
OPT_END(),
};
@@ -272,7 +340,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
for (; argc; argc--, argv++) {
unsigned char sha1[20];
- struct object *o;
+ struct object *object;
struct commit *commit;
if (get_sha1(*argv, sha1)) {
@@ -281,17 +349,34 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
continue;
}
- o = deref_tag(parse_object(sha1), *argv, 0);
- if (!o || o->type != OBJ_COMMIT) {
- fprintf(stderr, "Could not get commit for %s. Skipping.\n",
+ commit = NULL;
+ object = parse_object(sha1);
+ if (object) {
+ struct object *peeled = deref_tag(object, *argv, 0);
+ if (peeled && peeled->type == OBJ_COMMIT)
+ commit = (struct commit *)peeled;
+ }
+
+ if (!object) {
+ fprintf(stderr, "Could not get object for %s. Skipping.\n",
*argv);
continue;
}
- commit = (struct commit *)o;
- if (cutoff > commit->date)
- cutoff = commit->date;
- add_object_array((struct object *)commit, *argv, &revs);
+ if (commit) {
+ if (cutoff > commit->date)
+ cutoff = commit->date;
+ }
+
+ if (peel_tag) {
+ if (!commit) {
+ fprintf(stderr, "Could not get commit for %s. Skipping.\n",
+ *argv);
+ continue;
+ }
+ object = (struct object *)commit;
+ }
+ add_object_array(object, *argv, &revs);
}
if (cutoff)
diff --git a/builtin/revert.c b/builtin/revert.c
index 0401fdb02c..1d2648b756 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -54,6 +54,7 @@ static int option_parse_x(const struct option *opt,
return 0;
}
+LAST_ARG_MUST_BE_NULL
static void verify_opt_compatible(const char *me, const char *base_opt, ...)
{
const char *this_opt;
@@ -70,6 +71,7 @@ static void verify_opt_compatible(const char *me, const char *base_opt, ...)
die(_("%s: %s cannot be used with %s"), me, this_opt, base_opt);
}
+LAST_ARG_MUST_BE_NULL
static void verify_opt_mutually_compatible(const char *me, ...)
{
const char *opt1, *opt2 = NULL;
diff --git a/builtin/rm.c b/builtin/rm.c
index 06025a2e75..18916e022a 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -67,7 +67,7 @@ static int check_submodules_use_gitfiles(void)
for (i = 0; i < list.nr; i++) {
const char *name = list.entry[i].name;
int pos;
- struct cache_entry *ce;
+ const struct cache_entry *ce;
struct stat st;
pos = cache_name_pos(name, strlen(name));
@@ -120,7 +120,7 @@ static int check_local_mod(unsigned char *head, int index_only)
for (i = 0; i < list.nr; i++) {
struct stat st;
int pos;
- struct cache_entry *ce;
+ const struct cache_entry *ce;
const char *name = list.entry[i].name;
unsigned char sha1[20];
unsigned mode;
@@ -321,7 +321,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
seen = xcalloc(i, 1);
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen))
continue;
ALLOC_GROW(list.entry, list.nr + 1, list.alloc);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 99ec4af224..9788eb115b 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -673,8 +673,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
OPT_SET_INT(0, "sparse", &dense,
N_("show merges reachable from only one tip"), 0),
OPT_SET_INT(0, "date-order", &sort_order,
- N_("show commits where no parent comes before its "
- "children"),
+ N_("topologically sort, maintaining date order "
+ "where possible"),
REV_SORT_BY_COMMIT_DATE),
{ OPTION_CALLBACK, 'g', "reflog", &reflog_base, N_("<n>[,<base>]"),
N_("show <n> most recent ref-log entries starting at "
diff --git a/builtin/show-ref.c b/builtin/show-ref.c
index 4a0310da37..87806ad5b0 100644
--- a/builtin/show-ref.c
+++ b/builtin/show-ref.c
@@ -31,6 +31,9 @@ static int show_ref(const char *refname, const unsigned char *sha1, int flag, vo
const char *hex;
unsigned char peeled[20];
+ if (show_head && !strcmp(refname, "HEAD"))
+ goto match;
+
if (tags_only || heads_only) {
int match;
@@ -167,9 +170,10 @@ static const struct option show_ref_options[] = {
OPT_BOOLEAN(0, "verify", &verify, N_("stricter reference checking, "
"requires exact ref path")),
{ OPTION_BOOLEAN, 'h', NULL, &show_head, NULL,
- N_("show the HEAD reference"),
+ N_("show the HEAD reference, even if it would be filtered out"),
PARSE_OPT_NOARG | PARSE_OPT_HIDDEN },
- OPT_BOOLEAN(0, "head", &show_head, N_("show the HEAD reference")),
+ OPT_BOOLEAN(0, "head", &show_head,
+ N_("show the HEAD reference, even if it would be filtered out")),
OPT_BOOLEAN('d', "dereference", &deref_tags,
N_("dereference tags into object IDs")),
{ OPTION_CALLBACK, 's', "hash", &abbrev, N_("n"),
diff --git a/builtin/update-index.c b/builtin/update-index.c
index 5c7762eef4..c317981516 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -83,7 +83,7 @@ static int process_lstat_error(const char *path, int err)
return error("lstat(\"%s\"): %s", path, strerror(errno));
}
-static int add_one_path(struct cache_entry *old, const char *path, int len, struct stat *st)
+static int add_one_path(const struct cache_entry *old, const char *path, int len, struct stat *st)
{
int option, size;
struct cache_entry *ce;
@@ -142,7 +142,7 @@ static int process_directory(const char *path, int len, struct stat *st)
/* Exact match: file or existing gitlink */
if (pos >= 0) {
- struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = active_cache[pos];
if (S_ISGITLINK(ce->ce_mode)) {
/* Do nothing to the index if there is no HEAD! */
@@ -158,7 +158,7 @@ static int process_directory(const char *path, int len, struct stat *st)
/* Inexact match: is there perhaps a subdirectory match? */
pos = -pos-1;
while (pos < active_nr) {
- struct cache_entry *ce = active_cache[pos++];
+ const struct cache_entry *ce = active_cache[pos++];
if (strncmp(ce->name, path, len))
break;
@@ -183,7 +183,7 @@ static int process_path(const char *path)
{
int pos, len;
struct stat st;
- struct cache_entry *ce;
+ const struct cache_entry *ce;
len = strlen(path);
if (has_symlink_leading_path(path, len))
@@ -448,7 +448,7 @@ static int unresolve_one(const char *path)
/* already merged */
pos = unmerge_cache_entry_at(pos);
if (pos < active_nr) {
- struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = active_cache[pos];
if (ce_stage(ce) &&
ce_namelen(ce) == namelen &&
!memcmp(ce->name, path, namelen))
@@ -462,7 +462,7 @@ static int unresolve_one(const char *path)
*/
pos = -pos-1;
if (pos < active_nr) {
- struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = active_cache[pos];
if (ce_namelen(ce) == namelen &&
!memcmp(ce->name, path, namelen)) {
fprintf(stderr,
@@ -558,7 +558,7 @@ static int do_reupdate(int ac, const char **av,
has_head = 0;
redo:
for (pos = 0; pos < active_nr; pos++) {
- struct cache_entry *ce = active_cache[pos];
+ const struct cache_entry *ce = active_cache[pos];
struct cache_entry *old = NULL;
int save_nr;
diff --git a/cache-tree.c b/cache-tree.c
index 37e4d008b5..0bbec43216 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -149,7 +149,7 @@ void cache_tree_invalidate_path(struct cache_tree *it, const char *path)
cache_tree_invalidate_path(down->cache_tree, slash + 1);
}
-static int verify_cache(struct cache_entry **cache,
+static int verify_cache(const struct cache_entry * const *cache,
int entries, int flags)
{
int i, funny;
@@ -158,7 +158,7 @@ static int verify_cache(struct cache_entry **cache,
/* Verify that the tree is merged */
funny = 0;
for (i = 0; i < entries; i++) {
- struct cache_entry *ce = cache[i];
+ const struct cache_entry *ce = cache[i];
if (ce_stage(ce)) {
if (silent)
return -1;
@@ -234,7 +234,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
}
static int update_one(struct cache_tree *it,
- struct cache_entry **cache,
+ const struct cache_entry * const *cache,
int entries,
const char *base,
int baselen,
@@ -265,7 +265,7 @@ static int update_one(struct cache_tree *it,
*/
i = 0;
while (i < entries) {
- struct cache_entry *ce = cache[i];
+ const struct cache_entry *ce = cache[i];
struct cache_tree_sub *sub;
const char *path, *slash;
int pathlen, sublen, subcnt, subskip;
@@ -312,7 +312,7 @@ static int update_one(struct cache_tree *it,
i = 0;
while (i < entries) {
- struct cache_entry *ce = cache[i];
+ const struct cache_entry *ce = cache[i];
struct cache_tree_sub *sub;
const char *path, *slash;
int pathlen, entlen;
@@ -397,7 +397,7 @@ static int update_one(struct cache_tree *it,
}
int cache_tree_update(struct cache_tree *it,
- struct cache_entry **cache,
+ const struct cache_entry * const *cache,
int entries,
int flags)
{
@@ -599,8 +599,8 @@ int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix)
was_valid = cache_tree_fully_valid(active_cache_tree);
if (!was_valid) {
if (cache_tree_update(active_cache_tree,
- active_cache, active_nr,
- flags) < 0)
+ (const struct cache_entry * const *)active_cache,
+ active_nr, flags) < 0)
return WRITE_TREE_UNMERGED_INDEX;
if (0 <= newfd) {
if (!write_cache(newfd, active_cache, active_nr) &&
@@ -701,5 +701,6 @@ int update_main_cache_tree(int flags)
if (!the_index.cache_tree)
the_index.cache_tree = cache_tree();
return cache_tree_update(the_index.cache_tree,
- the_index.cache, the_index.cache_nr, flags);
+ (const struct cache_entry * const *)the_index.cache,
+ the_index.cache_nr, flags);
}
diff --git a/cache-tree.h b/cache-tree.h
index 55d0f59f2b..f1923ad1e9 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -30,7 +30,7 @@ void cache_tree_write(struct strbuf *, struct cache_tree *root);
struct cache_tree *cache_tree_read(const char *buffer, unsigned long size);
int cache_tree_fully_valid(struct cache_tree *);
-int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int);
+int cache_tree_update(struct cache_tree *, const struct cache_entry * const *, int, int);
int update_main_cache_tree(int);
diff --git a/cache.h b/cache.h
index dd0fb33a15..4c606ce28b 100644
--- a/cache.h
+++ b/cache.h
@@ -425,6 +425,8 @@ extern int path_inside_repo(const char *prefix, const char *path);
extern int set_git_dir_init(const char *git_dir, const char *real_git_dir, int);
extern int init_db(const char *template_dir, unsigned int flags);
+extern void sanitize_stdfds(void);
+
#define alloc_nr(x) (((x)+16)*3/2)
/*
@@ -476,7 +478,7 @@ extern int remove_file_from_index(struct index_state *, const char *path);
extern int add_to_index(struct index_state *, const char *path, struct stat *, int flags);
extern int add_file_to_index(struct index_state *, const char *path, int flags);
extern struct cache_entry *make_cache_entry(unsigned int mode, const unsigned char *sha1, const char *path, int stage, int refresh);
-extern int ce_same_name(struct cache_entry *a, struct cache_entry *b);
+extern int ce_same_name(const struct cache_entry *a, const struct cache_entry *b);
extern int index_name_is_other(const struct index_state *, const char *, int);
extern void *read_blob_data_from_index(struct index_state *, const char *, unsigned long *);
@@ -758,7 +760,7 @@ int is_directory(const char *);
const char *real_path(const char *path);
const char *real_path_if_valid(const char *path);
const char *absolute_path(const char *path);
-const char *relative_path(const char *abs, const char *base);
+const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
int normalize_path_copy(char *dst, const char *src);
int longest_ancestor_length(const char *path, struct string_list *prefixes);
char *strip_path_suffix(const char *path, const char *suffix);
@@ -1130,6 +1132,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 {
@@ -1173,11 +1176,15 @@ extern int update_server_info(int);
typedef int (*config_fn_t)(const char *, const char *, void *);
extern int git_default_config(const char *, const char *, void *);
extern int git_config_from_file(config_fn_t fn, const char *, void *);
+extern int git_config_from_buf(config_fn_t fn, const char *name,
+ const char *buf, size_t len, void *data);
extern void git_config_push_parameter(const char *text);
extern int git_config_from_parameters(config_fn_t fn, void *data);
extern int git_config(config_fn_t fn, void *);
extern int git_config_with_options(config_fn_t fn, void *,
- const char *filename, int respect_includes);
+ const char *filename,
+ const char *blob_ref,
+ int respect_includes);
extern int git_config_early(config_fn_t fn, void *, const char *repo_config);
extern int git_parse_ulong(const char *, unsigned long *);
extern int git_config_int(const char *, const char *);
diff --git a/command-list.txt b/command-list.txt
index bf83303c48..08b04e2c66 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -13,6 +13,7 @@ git-bundle mainporcelain
git-cat-file plumbinginterrogators
git-check-attr purehelpers
git-check-ignore purehelpers
+git-check-mailmap purehelpers
git-checkout mainporcelain common
git-checkout-index plumbingmanipulators
git-check-ref-format purehelpers
diff --git a/commit.c b/commit.c
index 521e49c309..e5862f6d7c 100644
--- a/commit.c
+++ b/commit.c
@@ -581,7 +581,7 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
return 0;
}
-static int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
+int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused)
{
const struct commit *a = a_, *b = b_;
/* newer commits with larger date first */
@@ -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/commit.h b/commit.h
index 4d452dc96d..35cc4e266b 100644
--- a/commit.h
+++ b/commit.h
@@ -6,6 +6,7 @@
#include "strbuf.h"
#include "decorate.h"
#include "gpg-interface.h"
+#include "string-list.h"
struct commit_list {
struct commit *item;
@@ -79,6 +80,9 @@ enum cmit_fmt {
};
struct pretty_print_context {
+ /*
+ * Callers should tweak these to change the behavior of pp_* functions.
+ */
enum cmit_fmt fmt;
int abbrev;
const char *subject;
@@ -92,6 +96,15 @@ struct pretty_print_context {
const char *output_encoding;
struct string_list *mailmap;
int color;
+ struct ident_split *from_ident;
+
+ /*
+ * Fields below here are manipulated internally by pp_* functions and
+ * should not be counted on by callers.
+ */
+
+ /* Manipulated by the pp_* functions internally. */
+ struct string_list in_body_headers;
};
struct userformat_want {
@@ -111,20 +124,20 @@ extern void userformat_find_requirements(const char *fmt, struct userformat_want
extern void format_commit_message(const struct commit *commit,
const char *format, struct strbuf *sb,
const struct pretty_print_context *context);
-extern void pretty_print_commit(const struct pretty_print_context *pp,
+extern void pretty_print_commit(struct pretty_print_context *pp,
const struct commit *commit,
struct strbuf *sb);
extern void pp_commit_easy(enum cmit_fmt fmt, const struct commit *commit,
struct strbuf *sb);
-void pp_user_info(const struct pretty_print_context *pp,
+void pp_user_info(struct pretty_print_context *pp,
const char *what, struct strbuf *sb,
const char *line, const char *encoding);
-void pp_title_line(const struct pretty_print_context *pp,
+void pp_title_line(struct pretty_print_context *pp,
const char **msg_p,
struct strbuf *sb,
const char *encoding,
int need_8bit_cte);
-void pp_remainder(const struct pretty_print_context *pp,
+void pp_remainder(struct pretty_print_context *pp,
const char **msg_p,
struct strbuf *sb,
int indent);
@@ -254,4 +267,6 @@ extern void print_commit_list(struct commit_list *list,
*/
extern void check_commit_signature(const struct commit* commit, struct signature_check *sigc);
+int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
+
#endif /* COMMIT_H */
diff --git a/config.c b/config.c
index d04e8157ab..e13a7b65e7 100644
--- a/config.c
+++ b/config.c
@@ -10,20 +10,69 @@
#include "strbuf.h"
#include "quote.h"
-typedef struct config_file {
- struct config_file *prev;
- FILE *f;
+struct config_source {
+ struct config_source *prev;
+ union {
+ FILE *file;
+ struct config_buf {
+ const char *buf;
+ size_t len;
+ size_t pos;
+ } buf;
+ } u;
const char *name;
+ int die_on_error;
int linenr;
int eof;
struct strbuf value;
struct strbuf var;
-} config_file;
-static config_file *cf;
+ int (*fgetc)(struct config_source *c);
+ int (*ungetc)(int c, struct config_source *conf);
+ long (*ftell)(struct config_source *c);
+};
+
+static struct config_source *cf;
static int zlib_compression_seen;
+static int config_file_fgetc(struct config_source *conf)
+{
+ return fgetc(conf->u.file);
+}
+
+static int config_file_ungetc(int c, struct config_source *conf)
+{
+ return ungetc(c, conf->u.file);
+}
+
+static long config_file_ftell(struct config_source *conf)
+{
+ return ftell(conf->u.file);
+}
+
+
+static int config_buf_fgetc(struct config_source *conf)
+{
+ if (conf->u.buf.pos < conf->u.buf.len)
+ return conf->u.buf.buf[conf->u.buf.pos++];
+
+ return EOF;
+}
+
+static int config_buf_ungetc(int c, struct config_source *conf)
+{
+ if (conf->u.buf.pos > 0)
+ return conf->u.buf.buf[--conf->u.buf.pos];
+
+ return EOF;
+}
+
+static long config_buf_ftell(struct config_source *conf)
+{
+ return conf->u.buf.pos;
+}
+
#define MAX_INCLUDE_DEPTH 10
static const char include_depth_advice[] =
"exceeded maximum include depth (%d) while including\n"
@@ -168,27 +217,22 @@ int git_config_from_parameters(config_fn_t fn, void *data)
static int get_next_char(void)
{
- int c;
- FILE *f;
-
- c = '\n';
- if (cf && ((f = cf->f) != NULL)) {
- c = fgetc(f);
- if (c == '\r') {
- /* DOS like systems */
- c = fgetc(f);
- if (c != '\n') {
- ungetc(c, f);
- c = '\r';
- }
- }
- if (c == '\n')
- cf->linenr++;
- if (c == EOF) {
- cf->eof = 1;
- c = '\n';
+ int c = cf->fgetc(cf);
+
+ if (c == '\r') {
+ /* DOS like systems */
+ c = cf->fgetc(cf);
+ if (c != '\n') {
+ cf->ungetc(c, cf);
+ c = '\r';
}
}
+ if (c == '\n')
+ cf->linenr++;
+ if (c == EOF) {
+ cf->eof = 1;
+ c = '\n';
+ }
return c;
}
@@ -339,7 +383,7 @@ static int get_base_var(struct strbuf *name)
}
}
-static int git_parse_file(config_fn_t fn, void *data)
+static int git_parse_source(config_fn_t fn, void *data)
{
int comment = 0;
int baselen = 0;
@@ -399,7 +443,10 @@ static int git_parse_file(config_fn_t fn, void *data)
if (get_value(fn, data, var) < 0)
break;
}
- die("bad config file line %d in %s", cf->linenr, cf->name);
+ if (cf->die_on_error)
+ die("bad config file line %d in %s", cf->linenr, cf->name);
+ else
+ return error("bad config file line %d in %s", cf->linenr, cf->name);
}
static int parse_unit_factor(const char *end, uintmax_t *val)
@@ -906,6 +953,33 @@ int git_default_config(const char *var, const char *value, void *dummy)
return 0;
}
+/*
+ * All source specific fields in the union, die_on_error, name and the callbacks
+ * fgetc, ungetc, ftell of top need to be initialized before calling
+ * this function.
+ */
+static int do_config_from(struct config_source *top, config_fn_t fn, void *data)
+{
+ int ret;
+
+ /* push config-file parsing state stack */
+ top->prev = cf;
+ top->linenr = 1;
+ top->eof = 0;
+ strbuf_init(&top->value, 1024);
+ strbuf_init(&top->var, 1024);
+ cf = top;
+
+ ret = git_parse_source(fn, data);
+
+ /* pop config-file parsing state stack */
+ strbuf_release(&top->value);
+ strbuf_release(&top->var);
+ cf = top->prev;
+
+ return ret;
+}
+
int git_config_from_file(config_fn_t fn, const char *filename, void *data)
{
int ret;
@@ -913,30 +987,74 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
ret = -1;
if (f) {
- config_file top;
+ struct config_source top;
- /* push config-file parsing state stack */
- top.prev = cf;
- top.f = f;
+ top.u.file = f;
top.name = filename;
- top.linenr = 1;
- top.eof = 0;
- strbuf_init(&top.value, 1024);
- strbuf_init(&top.var, 1024);
- cf = &top;
-
- ret = git_parse_file(fn, data);
+ top.die_on_error = 1;
+ top.fgetc = config_file_fgetc;
+ top.ungetc = config_file_ungetc;
+ top.ftell = config_file_ftell;
- /* pop config-file parsing state stack */
- strbuf_release(&top.value);
- strbuf_release(&top.var);
- cf = top.prev;
+ ret = do_config_from(&top, fn, data);
fclose(f);
}
return ret;
}
+int git_config_from_buf(config_fn_t fn, const char *name, const char *buf,
+ size_t len, void *data)
+{
+ struct config_source top;
+
+ top.u.buf.buf = buf;
+ top.u.buf.len = len;
+ top.u.buf.pos = 0;
+ top.name = name;
+ top.die_on_error = 0;
+ top.fgetc = config_buf_fgetc;
+ top.ungetc = config_buf_ungetc;
+ top.ftell = config_buf_ftell;
+
+ return do_config_from(&top, fn, data);
+}
+
+static int git_config_from_blob_sha1(config_fn_t fn,
+ const char *name,
+ const unsigned char *sha1,
+ void *data)
+{
+ enum object_type type;
+ char *buf;
+ unsigned long size;
+ int ret;
+
+ buf = read_sha1_file(sha1, &type, &size);
+ if (!buf)
+ return error("unable to load config blob object '%s'", name);
+ if (type != OBJ_BLOB) {
+ free(buf);
+ return error("reference '%s' does not point to a blob", name);
+ }
+
+ ret = git_config_from_buf(fn, name, buf, size, data);
+ free(buf);
+
+ return ret;
+}
+
+static int git_config_from_blob_ref(config_fn_t fn,
+ const char *name,
+ void *data)
+{
+ unsigned char sha1[20];
+
+ if (get_sha1(name, sha1) < 0)
+ return error("unable to resolve config blob '%s'", name);
+ return git_config_from_blob_sha1(fn, name, sha1, data);
+}
+
const char *git_etc_gitconfig(void)
{
static const char *system_wide;
@@ -1002,7 +1120,9 @@ int git_config_early(config_fn_t fn, void *data, const char *repo_config)
}
int git_config_with_options(config_fn_t fn, void *data,
- const char *filename, int respect_includes)
+ const char *filename,
+ const char *blob_ref,
+ int respect_includes)
{
char *repo_config = NULL;
int ret;
@@ -1021,6 +1141,8 @@ int git_config_with_options(config_fn_t fn, void *data,
*/
if (filename)
return git_config_from_file(fn, filename, data);
+ else if (blob_ref)
+ return git_config_from_blob_ref(fn, blob_ref, data);
repo_config = git_pathdup("config");
ret = git_config_early(fn, data, repo_config);
@@ -1031,7 +1153,7 @@ int git_config_with_options(config_fn_t fn, void *data,
int git_config(config_fn_t fn, void *data)
{
- return git_config_with_options(fn, data, NULL, 1);
+ return git_config_with_options(fn, data, NULL, NULL, 1);
}
/*
@@ -1063,7 +1185,6 @@ static int store_aux(const char *key, const char *value, void *cb)
{
const char *ep;
size_t section_len;
- FILE *f = cf->f;
switch (store.state) {
case KEY_SEEN:
@@ -1075,7 +1196,7 @@ static int store_aux(const char *key, const char *value, void *cb)
return 1;
}
- store.offset[store.seen] = ftell(f);
+ store.offset[store.seen] = cf->ftell(cf);
store.seen++;
}
break;
@@ -1102,19 +1223,19 @@ static int store_aux(const char *key, const char *value, void *cb)
* Do not increment matches: this is no match, but we
* just made sure we are in the desired section.
*/
- store.offset[store.seen] = ftell(f);
+ store.offset[store.seen] = cf->ftell(cf);
/* fallthru */
case SECTION_END_SEEN:
case START:
if (matches(key, value)) {
- store.offset[store.seen] = ftell(f);
+ store.offset[store.seen] = cf->ftell(cf);
store.state = KEY_SEEN;
store.seen++;
} else {
if (strrchr(key, '.') - key == store.baselen &&
!strncmp(key, store.key, store.baselen)) {
store.state = SECTION_SEEN;
- store.offset[store.seen] = ftell(f);
+ store.offset[store.seen] = cf->ftell(cf);
}
}
}
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index fa3ae1e853..cd509a5d63 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -648,6 +648,7 @@ __git_list_porcelain_commands ()
cat-file) : plumbing;;
check-attr) : plumbing;;
check-ignore) : plumbing;;
+ check-mailmap) : plumbing;;
check-ref-format) : plumbing;;
checkout-index) : plumbing;;
commit-tree) : plumbing;;
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/daemon.c b/daemon.c
index 6aeddcb98d..34916c5e10 100644
--- a/daemon.c
+++ b/daemon.c
@@ -760,7 +760,7 @@ static void handle(int incoming, struct sockaddr *addr, socklen_t addrlen)
snprintf(portbuf, sizeof(portbuf), "REMOTE_PORT=%d",
ntohs(sin_addr->sin_port));
#ifndef NO_IPV6
- } else if (addr && addr->sa_family == AF_INET6) {
+ } else if (addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin6_addr = (void *) addr;
char *buf = addrbuf + 12;
@@ -1047,18 +1047,6 @@ static int service_loop(struct socketlist *socklist)
}
}
-/* if any standard file descriptor is missing open it to /dev/null */
-static void sanitize_stdfds(void)
-{
- int fd = open("/dev/null", O_RDWR, 0);
- while (fd != -1 && fd < 2)
- fd = dup(fd);
- if (fd == -1)
- die_errno("open /dev/null or dup failed");
- if (fd > 2)
- close(fd);
-}
-
#ifdef NO_POSIX_GOODIES
struct credentials;
diff --git a/diff.c b/diff.c
index e53ddad7d4..4629de2092 100644
--- a/diff.c
+++ b/diff.c
@@ -1683,9 +1683,7 @@ static void show_stats(struct diffstat_t *data, struct diff_options *options)
del = deleted;
if (graph_width <= max_change) {
- int total = add + del;
-
- total = scale_linear(add + del, graph_width, max_change);
+ int total = scale_linear(add + del, graph_width, max_change);
if (total < 2 && add && del)
/* width >= 2 due to the sanity check */
total = 2;
@@ -2586,7 +2584,7 @@ void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
*/
static int reuse_worktree_file(const char *name, const unsigned char *sha1, int want_file)
{
- struct cache_entry *ce;
+ const struct cache_entry *ce;
struct stat st;
int pos, len;
@@ -3505,6 +3503,11 @@ static int parse_submodule_opt(struct diff_options *options, const char *value)
return 1;
}
+static void enable_patch_output(int *fmt) {
+ *fmt &= ~DIFF_FORMAT_NO_OUTPUT;
+ *fmt |= DIFF_FORMAT_PATCH;
+}
+
int diff_opt_parse(struct diff_options *options, const char **av, int ac)
{
const char *arg = av[0];
@@ -3512,15 +3515,15 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
int argcount;
/* Output format options */
- if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch"))
- options->output_format |= DIFF_FORMAT_PATCH;
- else if (opt_arg(arg, 'U', "unified", &options->context))
- options->output_format |= DIFF_FORMAT_PATCH;
+ if (!strcmp(arg, "-p") || !strcmp(arg, "-u") || !strcmp(arg, "--patch")
+ || opt_arg(arg, 'U', "unified", &options->context))
+ enable_patch_output(&options->output_format);
else if (!strcmp(arg, "--raw"))
options->output_format |= DIFF_FORMAT_RAW;
- else if (!strcmp(arg, "--patch-with-raw"))
- options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_RAW;
- else if (!strcmp(arg, "--numstat"))
+ else if (!strcmp(arg, "--patch-with-raw")) {
+ enable_patch_output(&options->output_format);
+ options->output_format |= DIFF_FORMAT_RAW;
+ } else if (!strcmp(arg, "--numstat"))
options->output_format |= DIFF_FORMAT_NUMSTAT;
else if (!strcmp(arg, "--shortstat"))
options->output_format |= DIFF_FORMAT_SHORTSTAT;
@@ -3542,13 +3545,14 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
options->output_format |= DIFF_FORMAT_CHECKDIFF;
else if (!strcmp(arg, "--summary"))
options->output_format |= DIFF_FORMAT_SUMMARY;
- else if (!strcmp(arg, "--patch-with-stat"))
- options->output_format |= DIFF_FORMAT_PATCH | DIFF_FORMAT_DIFFSTAT;
- else if (!strcmp(arg, "--name-only"))
+ else if (!strcmp(arg, "--patch-with-stat")) {
+ enable_patch_output(&options->output_format);
+ options->output_format |= DIFF_FORMAT_DIFFSTAT;
+ } else if (!strcmp(arg, "--name-only"))
options->output_format |= DIFF_FORMAT_NAME;
else if (!strcmp(arg, "--name-status"))
options->output_format |= DIFF_FORMAT_NAME_STATUS;
- else if (!strcmp(arg, "-s"))
+ else if (!strcmp(arg, "-s") || !strcmp(arg, "--no-patch"))
options->output_format |= DIFF_FORMAT_NO_OUTPUT;
else if (!prefixcmp(arg, "--stat"))
/* --stat, --stat-width, --stat-name-width, or --stat-count */
@@ -3621,7 +3625,7 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
/* flags options */
else if (!strcmp(arg, "--binary")) {
- options->output_format |= DIFF_FORMAT_PATCH;
+ enable_patch_output(&options->output_format);
DIFF_OPT_SET(options, BINARY);
}
else if (!strcmp(arg, "--full-index"))
diff --git a/dir.c b/dir.c
index 0480419544..910bfcde4e 100644
--- a/dir.c
+++ b/dir.c
@@ -933,7 +933,7 @@ enum exist_status {
*/
static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
{
- struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+ const struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
unsigned char endchar;
if (!ce)
@@ -977,7 +977,7 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
if (pos < 0)
pos = -pos-1;
while (pos < active_nr) {
- struct cache_entry *ce = active_cache[pos++];
+ const struct cache_entry *ce = active_cache[pos++];
unsigned char endchar;
if (strncmp(ce->name, dirname, len))
@@ -1113,7 +1113,7 @@ static int exclude_matches_pathspec(const char *path, int len,
static int get_index_dtype(const char *path, int len)
{
int pos;
- struct cache_entry *ce;
+ const struct cache_entry *ce;
ce = cache_name_exists(path, len, 0);
if (ce) {
diff --git a/entry.c b/entry.c
index d7c131d453..acc892f9a6 100644
--- a/entry.c
+++ b/entry.c
@@ -79,7 +79,7 @@ static int create_file(const char *path, unsigned int mode)
return open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
}
-static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
+static void *read_blob_entry(const struct cache_entry *ce, unsigned long *size)
{
enum object_type type;
void *new = read_sha1_file(ce->sha1, &type, size);
@@ -92,7 +92,7 @@ static void *read_blob_entry(struct cache_entry *ce, unsigned long *size)
return NULL;
}
-static int open_output_fd(char *path, struct cache_entry *ce, int to_tempfile)
+static int open_output_fd(char *path, const struct cache_entry *ce, int to_tempfile)
{
int symlink = (ce->ce_mode & S_IFMT) != S_IFREG;
if (to_tempfile) {
@@ -115,7 +115,7 @@ static int fstat_output(int fd, const struct checkout *state, struct stat *st)
return 0;
}
-static int streaming_write_entry(struct cache_entry *ce, char *path,
+static int streaming_write_entry(const struct cache_entry *ce, char *path,
struct stream_filter *filter,
const struct checkout *state, int to_tempfile,
int *fstat_done, struct stat *statbuf)
@@ -136,7 +136,8 @@ static int streaming_write_entry(struct cache_entry *ce, char *path,
return result;
}
-static int write_entry(struct cache_entry *ce, char *path, const struct checkout *state, int to_tempfile)
+static int write_entry(struct cache_entry *ce,
+ char *path, const struct checkout *state, int to_tempfile)
{
unsigned int ce_mode_s_ifmt = ce->ce_mode & S_IFMT;
int fd, ret, fstat_done = 0;
@@ -199,9 +200,9 @@ static int write_entry(struct cache_entry *ce, char *path, const struct checkout
break;
case S_IFGITLINK:
if (to_tempfile)
- return error("cannot create temporary subproject %s", path);
+ return error("cannot create temporary submodule %s", path);
if (mkdir(path, 0777) < 0)
- return error("cannot create subproject directory %s", path);
+ return error("cannot create submodule directory %s", path);
break;
default:
return error("unknown file mode for %s in index", path);
@@ -233,7 +234,8 @@ static int check_path(const char *path, int len, struct stat *st, int skiplen)
return lstat(path, st);
}
-int checkout_entry(struct cache_entry *ce, const struct checkout *state, char *topath)
+int checkout_entry(struct cache_entry *ce,
+ const struct checkout *state, char *topath)
{
static char path[PATH_MAX + 1];
struct stat st;
diff --git a/exec_cmd.h b/exec_cmd.h
index e2b546b615..e4c9702f02 100644
--- a/exec_cmd.h
+++ b/exec_cmd.h
@@ -7,6 +7,7 @@ extern const char *git_exec_path(void);
extern void setup_path(void);
extern const char **prepare_git_cmd(const char **argv);
extern int execv_git_cmd(const char **argv); /* NULL terminated */
+LAST_ARG_MUST_BE_NULL
extern int execl_git_cmd(const char *cmd, ...);
extern const char *system_path(const char *path);
diff --git a/fetch-pack.c b/fetch-pack.c
index abe5ffbba5..6684348c0e 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -11,6 +11,7 @@
#include "run-command.h"
#include "transport.h"
#include "version.h"
+#include "prio-queue.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
@@ -37,7 +38,7 @@ static int marked;
*/
#define MAX_IN_VAIN 256
-static struct commit_list *rev_list;
+static struct prio_queue rev_list = { compare_commits_by_commit_date };
static int non_common_revs, multi_ack, use_sideband, allow_tip_sha1_in_want;
static void rev_list_push(struct commit *commit, int mark)
@@ -49,7 +50,7 @@ static void rev_list_push(struct commit *commit, int mark)
if (parse_commit(commit))
return;
- commit_list_insert_by_date(commit, &rev_list);
+ prio_queue_put(&rev_list, commit);
if (!(commit->object.flags & COMMON))
non_common_revs++;
@@ -122,10 +123,10 @@ static const unsigned char *get_rev(void)
unsigned int mark;
struct commit_list *parents;
- if (rev_list == NULL || non_common_revs == 0)
+ if (rev_list.nr == 0 || non_common_revs == 0)
return NULL;
- commit = rev_list->item;
+ commit = prio_queue_get(&rev_list);
if (!commit->object.parsed)
parse_commit(commit);
parents = commit->parents;
@@ -152,8 +153,6 @@ static const unsigned char *get_rev(void)
mark_common(parents->item, 1, 0);
parents = parents->next;
}
-
- rev_list = rev_list->next;
}
return commit->object.sha1;
@@ -442,7 +441,7 @@ static int find_common(struct fetch_pack_args *args,
in_vain = 0;
got_continue = 1;
if (ack == ACK_ready) {
- rev_list = NULL;
+ clear_prio_queue(&rev_list);
got_ready = 1;
}
break;
@@ -505,7 +504,7 @@ static int mark_complete(const char *refname, const unsigned char *sha1, int fla
struct commit *commit = (struct commit *)o;
if (!(commit->object.flags & COMPLETE)) {
commit->object.flags |= COMPLETE;
- commit_list_insert_by_date(commit, &complete);
+ commit_list_insert(commit, &complete);
}
}
return 0;
@@ -622,6 +621,7 @@ static int everything_local(struct fetch_pack_args *args,
if (!args->depth) {
for_each_ref(mark_complete, NULL);
for_each_alternate_ref(mark_alternate_complete, NULL);
+ commit_list_sort_by_date(&complete);
if (cutoff)
mark_recent_complete_commits(args, cutoff);
}
diff --git a/git-compat-util.h b/git-compat-util.h
index ff193f4aa2..cc4ba4d18f 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -303,6 +303,13 @@ extern char *gitbasename(char *);
#endif
#endif
+/* The sentinel attribute is valid from gcc version 4.0 */
+#if defined(__GNUC__) && (__GNUC__ >= 4)
+#define LAST_ARG_MUST_BE_NULL __attribute__((sentinel))
+#else
+#define LAST_ARG_MUST_BE_NULL
+#endif
+
#include "compat/bswap.h"
#ifdef USE_WILDMATCH
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/git-request-pull.sh b/git-request-pull.sh
index d566015975..ebf1269d29 100755
--- a/git-request-pull.sh
+++ b/git-request-pull.sh
@@ -51,8 +51,18 @@ fi
tag_name=$(git describe --exact "$head^0" 2>/dev/null)
test -n "$base" && test -n "$url" || usage
-baserev=$(git rev-parse --verify "$base"^0) &&
-headrev=$(git rev-parse --verify "$head"^0) || exit
+
+baserev=$(git rev-parse --verify --quiet "$base"^0)
+if test -z "$baserev"
+then
+ die "fatal: Not a valid revision: $base"
+fi
+
+headrev=$(git rev-parse --verify --quiet "$head"^0)
+if test -z "$headrev"
+then
+ die "fatal: Not a valid revision: $head"
+fi
merge_base=$(git merge-base $baserev $headrev) ||
die "fatal: No commits in common between $base and $head"
diff --git a/git-send-email.perl b/git-send-email.perl
index ecbf56f693..2162478392 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -69,6 +69,9 @@ git send-email [options] <file | directory | rev-list options >
--smtp-pass <str> * Password for SMTP-AUTH; not necessary.
--smtp-encryption <str> * tls or ssl; anything else disables.
--smtp-ssl * Deprecated. Use '--smtp-encryption ssl'.
+ --smtp-ssl-cert-path <str> * Path to ca-certificates (either directory or file).
+ Pass an empty string to disable certificate
+ verification.
--smtp-domain <str> * The domain name sent to HELO/EHLO handshake
--smtp-debug <0|1> * Disable, enable Net::SMTP debug.
@@ -194,7 +197,7 @@ sub do_edit {
my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc);
my ($to_cmd, $cc_cmd);
my ($smtp_server, $smtp_server_port, @smtp_server_options);
-my ($smtp_authuser, $smtp_encryption);
+my ($smtp_authuser, $smtp_encryption, $smtp_ssl_cert_path);
my ($identity, $aliasfiletype, @alias_files, $smtp_domain);
my ($validate, $confirm);
my (@suppress_cc);
@@ -220,6 +223,7 @@ my %config_settings = (
"smtpserveroption" => \@smtp_server_options,
"smtpuser" => \$smtp_authuser,
"smtppass" => \$smtp_authpass,
+ "smtpsslcertpath" => \$smtp_ssl_cert_path,
"smtpdomain" => \$smtp_domain,
"to" => \@initial_to,
"tocmd" => \$to_cmd,
@@ -287,6 +291,7 @@ my $rc = GetOptions("h" => \$help,
"smtp-pass:s" => \$smtp_authpass,
"smtp-ssl" => sub { $smtp_encryption = 'ssl' },
"smtp-encryption=s" => \$smtp_encryption,
+ "smtp-ssl-cert-path" => \$smtp_ssl_cert_path,
"smtp-debug:i" => \$debug_net_smtp,
"smtp-domain:s" => \$smtp_domain,
"identity=s" => \$identity,
@@ -1079,6 +1084,34 @@ sub smtp_auth_maybe {
return $auth;
}
+sub ssl_verify_params {
+ eval {
+ require IO::Socket::SSL;
+ IO::Socket::SSL->import(qw/SSL_VERIFY_PEER SSL_VERIFY_NONE/);
+ };
+ if ($@) {
+ print STDERR "Not using SSL_VERIFY_PEER due to out-of-date IO::Socket::SSL.\n";
+ return;
+ }
+
+ if (!defined $smtp_ssl_cert_path) {
+ $smtp_ssl_cert_path = "/etc/ssl/certs";
+ }
+
+ if ($smtp_ssl_cert_path eq "") {
+ return (SSL_verify_mode => SSL_VERIFY_NONE());
+ } elsif (-d $smtp_ssl_cert_path) {
+ return (SSL_verify_mode => SSL_VERIFY_PEER(),
+ SSL_ca_path => $smtp_ssl_cert_path);
+ } elsif (-f $smtp_ssl_cert_path) {
+ return (SSL_verify_mode => SSL_VERIFY_PEER(),
+ SSL_ca_file => $smtp_ssl_cert_path);
+ } else {
+ print STDERR "Not using SSL_VERIFY_PEER because the CA path does not exist.\n";
+ return (SSL_verify_mode => SSL_VERIFY_NONE());
+ }
+}
+
# Returns 1 if the message was sent, and 0 otherwise.
# In actuality, the whole program dies when there
# is an error sending a message.
@@ -1183,20 +1216,24 @@ X-Mailer: git-send-email $gitversion
$smtp_domain ||= maildomain();
$smtp ||= Net::SMTP::SSL->new($smtp_server,
Hello => $smtp_domain,
- Port => $smtp_server_port);
+ Port => $smtp_server_port,
+ ssl_verify_params());
}
else {
require Net::SMTP;
$smtp_domain ||= maildomain();
- $smtp ||= Net::SMTP->new(smtp_host_string(),
+ $smtp_server_port ||= 25;
+ $smtp ||= Net::SMTP->new($smtp_server,
Hello => $smtp_domain,
- Debug => $debug_net_smtp);
+ Debug => $debug_net_smtp,
+ Port => $smtp_server_port);
if ($smtp_encryption eq 'tls' && $smtp) {
require Net::SMTP::SSL;
$smtp->command('STARTTLS');
$smtp->response();
if ($smtp->code == 220) {
- $smtp = Net::SMTP::SSL->start_SSL($smtp)
+ $smtp = Net::SMTP::SSL->start_SSL($smtp,
+ ssl_verify_params())
or die "STARTTLS failed! ".$smtp->message;
$smtp_encryption = '';
# Send EHLO again to receive fresh
diff --git a/git-submodule.sh b/git-submodule.sh
index 945e296d30..2979197087 100755
--- a/git-submodule.sh
+++ b/git-submodule.sh
@@ -35,6 +35,7 @@ nofetch=
update=
prefix=
custom_name=
+depth=
# The function takes at most 2 arguments. The first argument is the
# URL that navigates to the submodule origin repo. When relative, this URL
@@ -251,6 +252,7 @@ module_clone()
name=$2
url=$3
reference="$4"
+ depth="$5"
quiet=
if test -n "$GIT_QUIET"
then
@@ -273,7 +275,7 @@ module_clone()
mkdir -p "$gitdir_base"
(
clear_local_git_env
- git clone $quiet -n ${reference:+"$reference"} \
+ git clone $quiet ${depth:+"$depth"} -n ${reference:+"$reference"} \
--separate-git-dir "$gitdir" "$url" "$sm_path"
) ||
die "$(eval_gettext "Clone of '\$url' into submodule path '\$sm_path' failed")"
@@ -350,6 +352,14 @@ cmd_add()
custom_name=$2
shift
;;
+ --depth)
+ case "$2" in '') usage ;; esac
+ depth="--depth=$2"
+ shift
+ ;;
+ --depth=*)
+ depth=$1
+ ;;
--)
shift
break
@@ -459,7 +469,7 @@ Use -f if you really want to add it." >&2
echo "$(eval_gettext "Reactivating local git directory for submodule '\$sm_name'.")"
fi
fi
- module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" || exit
+ module_clone "$sm_path" "$sm_name" "$realrepo" "$reference" "$depth" || exit
(
clear_local_git_env
cd "$sm_path" &&
@@ -736,6 +746,14 @@ cmd_update()
--checkout)
update="checkout"
;;
+ --depth)
+ case "$2" in '') usage ;; esac
+ depth="--depth=$2"
+ shift
+ ;;
+ --depth=*)
+ depth=$1
+ ;;
--)
shift
break
@@ -797,7 +815,7 @@ Maybe you want to use 'update --init'?")"
if ! test -d "$sm_path"/.git -o -f "$sm_path"/.git
then
- module_clone "$sm_path" "$name" "$url" "$reference" || exit
+ module_clone "$sm_path" "$name" "$url" "$reference" "$depth" || exit
cloned_modules="$cloned_modules;$name"
subsha1=
else
@@ -860,6 +878,12 @@ Maybe you want to use 'update --init'?")"
say_msg="$(eval_gettext "Submodule path '\$displaypath': merged in '\$sha1'")"
must_die_on_failure=yes
;;
+ !*)
+ command="${update_module#!}"
+ die_msg="$(eval_gettext "Execution of '\$command \$sha1' failed in submodule path '\$prefix\$sm_path'")"
+ say_msg="$(eval_gettext "Submodule path '\$prefix\$sm_path': '\$command \$sha1'")"
+ must_die_on_failure=yes
+ ;;
*)
command="git checkout $subforce -q"
die_msg="$(eval_gettext "Unable to checkout '\$sha1' in submodule path '\$displaypath'")"
diff --git a/git.c b/git.c
index 4359086fd6..2025f77d01 100644
--- a/git.c
+++ b/git.c
@@ -324,6 +324,7 @@ static void handle_internal_command(int argc, const char **argv)
{ "cat-file", cmd_cat_file, RUN_SETUP },
{ "check-attr", cmd_check_attr, RUN_SETUP },
{ "check-ignore", cmd_check_ignore, RUN_SETUP | NEED_WORK_TREE },
+ { "check-mailmap", cmd_check_mailmap, RUN_SETUP },
{ "check-ref-format", cmd_check_ref_format },
{ "checkout", cmd_checkout, RUN_SETUP | NEED_WORK_TREE },
{ "checkout-index", cmd_checkout_index,
@@ -525,6 +526,13 @@ int main(int argc, char **av)
if (!cmd)
cmd = "git-help";
+ /*
+ * Always open file descriptors 0/1/2 to avoid clobbering files
+ * in die(). It also avoids messing up when the pipes are dup'ed
+ * onto stdin/stdout/stderr in the child processes we spawn.
+ */
+ sanitize_stdfds();
+
git_setup_gettext();
/*
diff --git a/http-push.c b/http-push.c
index 395a8cfc10..6dad188b5f 100644
--- a/http-push.c
+++ b/http-push.c
@@ -663,7 +663,7 @@ static void add_fetch_request(struct object *obj)
static int add_send_request(struct object *obj, struct remote_lock *lock)
{
- struct transfer_request *request = request_queue_head;
+ struct transfer_request *request;
struct packed_git *target;
/* Keep locks active */
diff --git a/line-log.c b/line-log.c
index 4bbb09be59..c2d01dccc2 100644
--- a/line-log.c
+++ b/line-log.c
@@ -110,13 +110,16 @@ static void range_set_check_invariants(struct range_set *rs)
static void sort_and_merge_range_set(struct range_set *rs)
{
int i;
- int o = 1; /* output cursor */
+ int o = 0; /* output cursor */
qsort(rs->ranges, rs->nr, sizeof(struct range), range_cmp);
- 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;
+ for (i = 0; i < rs->nr; i++) {
+ if (rs->ranges[i].start == rs->ranges[i].end)
+ continue;
+ if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].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;
@@ -296,6 +299,7 @@ static void line_log_data_insert(struct line_log_data **list,
p = xcalloc(1, sizeof(struct line_log_data));
p->path = path;
range_set_append(&p->ranges, begin, end);
+ sort_and_merge_range_set(&p->ranges);
if (ip) {
p->next = ip->next;
ip->next = p;
diff --git a/line-range.c b/line-range.c
index 8faf943745..3942475c2f 100644
--- a/line-range.c
+++ b/line-range.c
@@ -211,6 +211,8 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
void *cb_data, long lines, long *begin, long *end,
const char *path)
{
+ *begin = *end = 0;
+
if (*arg == ':') {
arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, begin, end, path);
if (!arg || *arg)
@@ -226,6 +228,11 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
if (*arg)
return -1;
+ if (*begin && *end && *end < *begin) {
+ long tmp;
+ tmp = *end; *end = *begin; *begin = tmp;
+ }
+
return 0;
}
diff --git a/log-tree.c b/log-tree.c
index 60f32a3965..a49d8e895d 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -618,6 +618,8 @@ void show_log(struct rev_info *opt)
ctx.mailmap = opt->mailmap;
ctx.color = opt->diffopt.use_color;
ctx.output_encoding = get_log_output_encoding();
+ if (opt->from_ident.mail_begin && opt->from_ident.name_begin)
+ ctx.from_ident = &opt->from_ident;
pretty_print_commit(&ctx, commit, &msgbuf);
if (opt->add_signoff)
diff --git a/mailmap.c b/mailmap.c
index 2a7b36628c..44614fc411 100644
--- a/mailmap.c
+++ b/mailmap.c
@@ -5,8 +5,10 @@
#define DEBUG_MAILMAP 0
#if DEBUG_MAILMAP
#define debug_mm(...) fprintf(stderr, __VA_ARGS__)
+#define debug_str(X) ((X) ? (X) : "(none)")
#else
static inline void debug_mm(const char *format, ...) {}
+static inline const char *debug_str(const char *s) { return s; }
#endif
const char *git_mailmap_file;
@@ -29,7 +31,8 @@ struct mailmap_entry {
static void free_mailmap_info(void *p, const char *s)
{
struct mailmap_info *mi = (struct mailmap_info *)p;
- debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n", s, mi->name, mi->email);
+ debug_mm("mailmap: -- complex: '%s' -> '%s' <%s>\n",
+ s, debug_str(mi->name), debug_str(mi->email));
free(mi->name);
free(mi->email);
}
@@ -37,8 +40,11 @@ static void free_mailmap_info(void *p, const char *s)
static void free_mailmap_entry(void *p, const char *s)
{
struct mailmap_entry *me = (struct mailmap_entry *)p;
- debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n", s, me->namemap.nr);
- debug_mm("mailmap: - simple: '%s' <%s>\n", me->name, me->email);
+ debug_mm("mailmap: removing entries for <%s>, with %d sub-entries\n",
+ s, me->namemap.nr);
+ debug_mm("mailmap: - simple: '%s' <%s>\n",
+ debug_str(me->name), debug_str(me->email));
+
free(me->name);
free(me->email);
@@ -47,18 +53,11 @@ static void free_mailmap_entry(void *p, const char *s)
}
static void add_mapping(struct string_list *map,
- char *new_name, char *new_email, char *old_name, char *old_email)
+ char *new_name, char *new_email,
+ char *old_name, char *old_email)
{
struct mailmap_entry *me;
int index;
- char *p;
-
- if (old_email)
- for (p = old_email; *p; p++)
- *p = tolower(*p);
- if (new_email)
- for (p = new_email; *p; p++)
- *p = tolower(*p);
if (old_email == NULL) {
old_email = new_email;
@@ -68,16 +67,21 @@ static void add_mapping(struct string_list *map,
if ((index = string_list_find_insert_index(map, old_email, 1)) < 0) {
/* mailmap entry exists, invert index value */
index = -1 - index;
+ me = (struct mailmap_entry *)map->items[index].util;
} else {
/* create mailmap entry */
- struct string_list_item *item = string_list_insert_at_index(map, index, old_email);
- item->util = xcalloc(1, sizeof(struct mailmap_entry));
- ((struct mailmap_entry *)item->util)->namemap.strdup_strings = 1;
+ struct string_list_item *item;
+
+ item = string_list_insert_at_index(map, index, old_email);
+ me = xcalloc(1, sizeof(struct mailmap_entry));
+ me->namemap.strdup_strings = 1;
+ me->namemap.cmp = strcasecmp;
+ item->util = me;
}
- me = (struct mailmap_entry *)map->items[index].util;
if (old_name == NULL) {
- debug_mm("mailmap: adding (simple) entry for %s at index %d\n", old_email, index);
+ debug_mm("mailmap: adding (simple) entry for %s at index %d\n",
+ old_email, index);
/* Replace current name and new email for simple entry */
if (new_name) {
free(me->name);
@@ -89,7 +93,8 @@ static void add_mapping(struct string_list *map,
}
} else {
struct mailmap_info *mi = xcalloc(1, sizeof(struct mailmap_info));
- debug_mm("mailmap: adding (complex) entry for %s at index %d\n", old_email, index);
+ debug_mm("mailmap: adding (complex) entry for %s at index %d\n",
+ old_email, index);
if (new_name)
mi->name = xstrdup(new_name);
if (new_email)
@@ -98,11 +103,12 @@ static void add_mapping(struct string_list *map,
}
debug_mm("mailmap: '%s' <%s> -> '%s' <%s>\n",
- old_name, old_email, new_name, new_email);
+ debug_str(old_name), old_email,
+ debug_str(new_name), debug_str(new_email));
}
static char *parse_name_and_email(char *buffer, char **name,
- char **email, int allow_empty_email)
+ char **email, int allow_empty_email)
{
char *left, *right, *nstart, *nend;
*name = *email = NULL;
@@ -122,7 +128,7 @@ static char *parse_name_and_email(char *buffer, char **name,
while (nend > nstart && isspace(*nend))
--nend;
- *name = (nstart < nend ? nstart : NULL);
+ *name = (nstart <= nend ? nstart : NULL);
*email = left+1;
*(nend+1) = '\0';
*right++ = '\0';
@@ -306,21 +312,25 @@ static struct string_list_item *lookup_prefix(struct string_list *map,
}
int map_user(struct string_list *map,
- const char **email, size_t *emaillen,
- const char **name, size_t *namelen)
+ const char **email, size_t *emaillen,
+ const char **name, size_t *namelen)
{
struct string_list_item *item;
struct mailmap_entry *me;
debug_mm("map_user: map '%.*s' <%.*s>\n",
- *name, *namelen, *emaillen, *email);
+ (int)*namelen, debug_str(*name),
+ (int)*emaillen, debug_str(*email));
item = lookup_prefix(map, *email, *emaillen);
if (item != NULL) {
me = (struct mailmap_entry *)item->util;
if (me->namemap.nr) {
- /* The item has multiple items, so we'll look up on name too */
- /* If the name is not found, we choose the simple entry */
+ /*
+ * The item has multiple items, so we'll look up on
+ * name too. If the name is not found, we choose the
+ * simple entry.
+ */
struct string_list_item *subitem;
subitem = lookup_prefix(&me->namemap, *name, *namelen);
if (subitem)
@@ -341,8 +351,9 @@ int map_user(struct string_list *map,
*name = mi->name;
*namelen = strlen(*name);
}
- debug_mm("map_user: to '%.*s' <.*%s>\n", *namelen, *name,
- *emaillen, *email);
+ debug_mm("map_user: to '%.*s' <%.*s>\n",
+ (int)*namelen, debug_str(*name),
+ (int)*emaillen, debug_str(*email));
return 1;
}
debug_mm("map_user: --\n");
diff --git a/merge-recursive.c b/merge-recursive.c
index ea9dbd307c..f95933b0aa 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -251,7 +251,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
int i;
fprintf(stderr, "BUG: There are unmerged index entries:\n");
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if (ce_stage(ce))
fprintf(stderr, "BUG: %d %.*s\n", ce_stage(ce),
(int)ce_namelen(ce), ce->name);
@@ -264,7 +264,8 @@ struct tree *write_tree_from_memory(struct merge_options *o)
if (!cache_tree_fully_valid(active_cache_tree) &&
cache_tree_update(active_cache_tree,
- active_cache, active_nr, 0) < 0)
+ (const struct cache_entry * const *)active_cache,
+ active_nr, 0) < 0)
die(_("error building trees"));
result = lookup_tree(active_cache_tree->sha1);
@@ -339,7 +340,7 @@ static struct string_list *get_unmerged(void)
for (i = 0; i < active_nr; i++) {
struct string_list_item *item;
struct stage_data *e;
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if (!ce_stage(ce))
continue;
diff --git a/object.c b/object.c
index cbc7333a7e..d8a4b1ffbe 100644
--- a/object.c
+++ b/object.c
@@ -145,7 +145,7 @@ struct object *lookup_unknown_object(const unsigned char *sha1)
struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
{
struct object *obj;
- int eaten = 0;
+ *eaten_p = 0;
obj = NULL;
if (type == OBJ_BLOB) {
@@ -164,7 +164,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
if (!tree->object.parsed) {
if (parse_tree_buffer(tree, buffer, size))
return NULL;
- eaten = 1;
+ *eaten_p = 1;
}
}
} else if (type == OBJ_COMMIT) {
@@ -174,7 +174,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
return NULL;
if (!commit->buffer) {
commit->buffer = buffer;
- eaten = 1;
+ *eaten_p = 1;
}
obj = &commit->object;
}
@@ -191,7 +191,6 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
}
if (obj && obj->type == OBJ_NONE)
obj->type = type;
- *eaten_p = eaten;
return obj;
}
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)
diff --git a/path.c b/path.c
index 04ff1487ed..7f3324aeea 100644
--- a/path.c
+++ b/path.c
@@ -441,42 +441,100 @@ int adjust_shared_perm(const char *path)
return 0;
}
-const char *relative_path(const char *abs, const char *base)
+/*
+ * Give path as relative to prefix.
+ *
+ * The strbuf may or may not be used, so do not assume it contains the
+ * returned path.
+ */
+const char *relative_path(const char *in, const char *prefix,
+ struct strbuf *sb)
{
- static char buf[PATH_MAX + 1];
+ int in_len = in ? strlen(in) : 0;
+ int prefix_len = prefix ? strlen(prefix) : 0;
+ int in_off = 0;
+ int prefix_off = 0;
int i = 0, j = 0;
- if (!base || !base[0])
- return abs;
- while (base[i]) {
- if (is_dir_sep(base[i])) {
- if (!is_dir_sep(abs[j]))
- return abs;
- while (is_dir_sep(base[i]))
+ if (!in_len)
+ return "./";
+ else if (!prefix_len)
+ return in;
+
+ while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
+ if (is_dir_sep(prefix[i])) {
+ while (is_dir_sep(prefix[i]))
i++;
- while (is_dir_sep(abs[j]))
+ while (is_dir_sep(in[j]))
j++;
+ prefix_off = i;
+ in_off = j;
+ } else {
+ i++;
+ j++;
+ }
+ }
+
+ if (
+ /* "prefix" seems like prefix of "in" */
+ i >= prefix_len &&
+ /*
+ * but "/foo" is not a prefix of "/foobar"
+ * (i.e. prefix not end with '/')
+ */
+ prefix_off < prefix_len) {
+ if (j >= in_len) {
+ /* in="/a/b", prefix="/a/b" */
+ in_off = in_len;
+ } else if (is_dir_sep(in[j])) {
+ /* in="/a/b/c", prefix="/a/b" */
+ while (is_dir_sep(in[j]))
+ j++;
+ in_off = j;
+ } else {
+ /* in="/a/bbb/c", prefix="/a/b" */
+ i = prefix_off;
+ }
+ } else if (
+ /* "in" is short than "prefix" */
+ j >= in_len &&
+ /* "in" not end with '/' */
+ in_off < in_len) {
+ if (is_dir_sep(prefix[i])) {
+ /* in="/a/b", prefix="/a/b/c/" */
+ while (is_dir_sep(prefix[i]))
+ i++;
+ in_off = in_len;
+ }
+ }
+ in += in_off;
+ in_len -= in_off;
+
+ if (i >= prefix_len) {
+ if (!in_len)
+ return "./";
+ else
+ return in;
+ }
+
+ strbuf_reset(sb);
+ strbuf_grow(sb, in_len);
+
+ while (i < prefix_len) {
+ if (is_dir_sep(prefix[i])) {
+ strbuf_addstr(sb, "../");
+ while (is_dir_sep(prefix[i]))
+ i++;
continue;
- } else if (abs[j] != base[i]) {
- return abs;
}
i++;
- j++;
}
- if (
- /* "/foo" is a prefix of "/foo" */
- abs[j] &&
- /* "/foo" is not a prefix of "/foobar" */
- !is_dir_sep(base[i-1]) && !is_dir_sep(abs[j])
- )
- return abs;
- while (is_dir_sep(abs[j]))
- j++;
- if (!abs[j])
- strcpy(buf, ".");
- else
- strcpy(buf, abs + j);
- return buf;
+ if (!is_dir_sep(prefix[prefix_len - 1]))
+ strbuf_addstr(sb, "../");
+
+ strbuf_addstr(sb, in);
+
+ return sb->buf;
}
/*
diff --git a/pathspec.c b/pathspec.c
index 284f3970a3..6ea0867493 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -32,7 +32,7 @@ void add_pathspec_matches_against_index(const char **pathspec,
if (!num_unmatched)
return;
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, seen);
}
}
@@ -67,7 +67,7 @@ const char *check_path_for_gitlink(const char *path)
{
int i, path_len = strlen(path);
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if (S_ISGITLINK(ce->ce_mode)) {
int ce_len = ce_namelen(ce);
if (path_len <= ce_len || path[ce_len] != '/' ||
diff --git a/pretty.c b/pretty.c
index 9e431545d8..74563c92b4 100644
--- a/pretty.c
+++ b/pretty.c
@@ -406,7 +406,7 @@ static const char *show_ident_date(const struct ident_split *ident,
return show_date(date, tz, mode);
}
-void pp_user_info(const struct pretty_print_context *pp,
+void pp_user_info(struct pretty_print_context *pp,
const char *what, struct strbuf *sb,
const char *line, const char *encoding)
{
@@ -432,6 +432,23 @@ void pp_user_info(const struct pretty_print_context *pp,
map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
if (pp->fmt == CMIT_FMT_EMAIL) {
+ if (pp->from_ident) {
+ struct strbuf buf = STRBUF_INIT;
+
+ strbuf_addstr(&buf, "From: ");
+ strbuf_add(&buf, namebuf, namelen);
+ strbuf_addstr(&buf, " <");
+ strbuf_add(&buf, mailbuf, maillen);
+ strbuf_addstr(&buf, ">\n");
+ string_list_append(&pp->in_body_headers,
+ strbuf_detach(&buf, NULL));
+
+ mailbuf = pp->from_ident->mail_begin;
+ maillen = pp->from_ident->mail_end - mailbuf;
+ namebuf = pp->from_ident->name_begin;
+ namelen = pp->from_ident->name_end - namebuf;
+ }
+
strbuf_addstr(sb, "From: ");
if (needs_rfc2047_encoding(namebuf, namelen, RFC2047_ADDRESS)) {
add_rfc2047(sb, namebuf, namelen,
@@ -1514,7 +1531,7 @@ void format_commit_message(const struct commit *commit,
free(context.signature_check.signer);
}
-static void pp_header(const struct pretty_print_context *pp,
+static void pp_header(struct pretty_print_context *pp,
const char *encoding,
const struct commit *commit,
const char **msg_p,
@@ -1575,7 +1592,7 @@ static void pp_header(const struct pretty_print_context *pp,
}
}
-void pp_title_line(const struct pretty_print_context *pp,
+void pp_title_line(struct pretty_print_context *pp,
const char **msg_p,
struct strbuf *sb,
const char *encoding,
@@ -1602,6 +1619,16 @@ void pp_title_line(const struct pretty_print_context *pp,
}
strbuf_addch(sb, '\n');
+ if (need_8bit_cte == 0) {
+ int i;
+ for (i = 0; i < pp->in_body_headers.nr; i++) {
+ if (has_non_ascii(pp->in_body_headers.items[i].string)) {
+ need_8bit_cte = 1;
+ break;
+ }
+ }
+ }
+
if (need_8bit_cte > 0) {
const char *header_fmt =
"MIME-Version: 1.0\n"
@@ -1615,10 +1642,21 @@ void pp_title_line(const struct pretty_print_context *pp,
if (pp->fmt == CMIT_FMT_EMAIL) {
strbuf_addch(sb, '\n');
}
+
+ if (pp->in_body_headers.nr) {
+ int i;
+ for (i = 0; i < pp->in_body_headers.nr; i++) {
+ strbuf_addstr(sb, pp->in_body_headers.items[i].string);
+ free(pp->in_body_headers.items[i].string);
+ }
+ string_list_clear(&pp->in_body_headers, 0);
+ strbuf_addch(sb, '\n');
+ }
+
strbuf_release(&title);
}
-void pp_remainder(const struct pretty_print_context *pp,
+void pp_remainder(struct pretty_print_context *pp,
const char **msg_p,
struct strbuf *sb,
int indent)
@@ -1650,7 +1688,7 @@ void pp_remainder(const struct pretty_print_context *pp,
}
}
-void pretty_print_commit(const struct pretty_print_context *pp,
+void pretty_print_commit(struct pretty_print_context *pp,
const struct commit *commit,
struct strbuf *sb)
{
diff --git a/quote.c b/quote.c
index 911229fdf3..5c8808160e 100644
--- a/quote.c
+++ b/quote.c
@@ -312,82 +312,27 @@ void write_name_quotedpfx(const char *pfx, size_t pfxlen,
fputc(terminator, fp);
}
-static const char *path_relative(const char *in, int len,
- struct strbuf *sb, const char *prefix,
- int prefix_len);
-
-void write_name_quoted_relative(const char *name, size_t len,
- const char *prefix, size_t prefix_len,
+void write_name_quoted_relative(const char *name, const char *prefix,
FILE *fp, int terminator)
{
struct strbuf sb = STRBUF_INIT;
- name = path_relative(name, len, &sb, prefix, prefix_len);
+ name = relative_path(name, prefix, &sb);
write_name_quoted(name, fp, terminator);
strbuf_release(&sb);
}
-/*
- * Give path as relative to prefix.
- *
- * The strbuf may or may not be used, so do not assume it contains the
- * returned path.
- */
-static const char *path_relative(const char *in, int len,
- struct strbuf *sb, const char *prefix,
- int prefix_len)
-{
- int off, i;
-
- if (len < 0)
- len = strlen(in);
- if (prefix_len < 0) {
- if (prefix)
- prefix_len = strlen(prefix);
- else
- prefix_len = 0;
- }
-
- off = 0;
- i = 0;
- while (i < prefix_len && i < len && prefix[i] == in[i]) {
- if (prefix[i] == '/')
- off = i + 1;
- i++;
- }
- in += off;
- len -= off;
-
- if (i >= prefix_len)
- return in;
-
- strbuf_reset(sb);
- strbuf_grow(sb, len);
-
- while (i < prefix_len) {
- if (prefix[i] == '/')
- strbuf_addstr(sb, "../");
- i++;
- }
- strbuf_add(sb, in, len);
-
- return sb->buf;
-}
-
/* quote path as relative to the given prefix */
-char *quote_path_relative(const char *in, int len,
- struct strbuf *out, const char *prefix)
+char *quote_path_relative(const char *in, const char *prefix,
+ struct strbuf *out)
{
struct strbuf sb = STRBUF_INIT;
- const char *rel = path_relative(in, len, &sb, prefix, -1);
+ const char *rel = relative_path(in, prefix, &sb);
strbuf_reset(out);
quote_c_style_counted(rel, strlen(rel), out, NULL, 0);
strbuf_release(&sb);
- if (!out->len)
- strbuf_addstr(out, "./");
-
return out->buf;
}
diff --git a/quote.h b/quote.h
index 133155a48b..ed110a5d8d 100644
--- a/quote.h
+++ b/quote.h
@@ -60,13 +60,12 @@ extern void quote_two_c_style(struct strbuf *, const char *, const char *, int);
extern void write_name_quoted(const char *name, FILE *, int terminator);
extern void write_name_quotedpfx(const char *pfx, size_t pfxlen,
const char *name, FILE *, int terminator);
-extern void write_name_quoted_relative(const char *name, size_t len,
- const char *prefix, size_t prefix_len,
+extern void write_name_quoted_relative(const char *name, const char *prefix,
FILE *fp, int terminator);
/* quote path as relative to the given prefix */
-extern char *quote_path_relative(const char *in, int len,
- struct strbuf *out, const char *prefix);
+extern char *quote_path_relative(const char *in, const char *prefix,
+ struct strbuf *out);
/* quoting as a string literal for other languages */
extern void perl_quote_print(FILE *stream, const char *src);
diff --git a/read-cache.c b/read-cache.c
index d5201f9b06..d4474eab7b 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -722,7 +722,7 @@ struct cache_entry *make_cache_entry(unsigned int mode,
return ce;
}
-int ce_same_name(struct cache_entry *a, struct cache_entry *b)
+int ce_same_name(const struct cache_entry *a, const struct cache_entry *b)
{
int len = ce_namelen(a);
return ce_namelen(b) == len && !memcmp(a->name, b->name, len);
diff --git a/refs.c b/refs.c
index 4302206649..a3f2302292 100644
--- a/refs.c
+++ b/refs.c
@@ -2174,11 +2174,14 @@ int lock_packed_refs(int flags)
{
struct packed_ref_cache *packed_ref_cache;
- /* Discard the old cache because it might be invalid: */
- clear_packed_ref_cache(&ref_cache);
if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
return -1;
- /* Read the current packed-refs while holding the lock: */
+ /*
+ * Get the current packed-refs while holding the lock. If the
+ * packed-refs file has been modified since we last read it,
+ * this will automatically invalidate the cache and re-read
+ * the packed-refs file.
+ */
packed_ref_cache = get_packed_ref_cache(&ref_cache);
packed_ref_cache->lock = &packlock;
/* Increment the reference count to prevent it from being freed: */
diff --git a/remote.c b/remote.c
index 6f57830b64..efcba931ec 100644
--- a/remote.c
+++ b/remote.c
@@ -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/rerere.c b/rerere.c
index 98e3e294d0..6fde8f94c2 100644
--- a/rerere.c
+++ b/rerere.c
@@ -299,7 +299,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
{
mmfile_t mmfile[3] = {{NULL}};
mmbuffer_t result = {NULL, 0};
- struct cache_entry *ce;
+ const struct cache_entry *ce;
int pos, len, i, hunk_no;
struct rerere_io_mem io;
int marker_size = ll_merge_marker_size(path);
@@ -359,7 +359,7 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
static int check_one_conflict(int i, int *type)
{
- struct cache_entry *e = active_cache[i];
+ const struct cache_entry *e = active_cache[i];
if (!ce_stage(e)) {
*type = RESOLVED;
@@ -374,8 +374,8 @@ static int check_one_conflict(int i, int *type)
/* Only handle regular files with both stages #2 and #3 */
if (i + 1 < active_nr) {
- struct cache_entry *e2 = active_cache[i];
- struct cache_entry *e3 = active_cache[i + 1];
+ const struct cache_entry *e2 = active_cache[i];
+ const struct cache_entry *e3 = active_cache[i + 1];
if (ce_stage(e2) == 2 &&
ce_stage(e3) == 3 &&
ce_same_name(e, e3) &&
@@ -398,7 +398,7 @@ static int find_conflict(struct string_list *conflict)
for (i = 0; i < active_nr;) {
int conflict_type;
- struct cache_entry *e = active_cache[i];
+ const struct cache_entry *e = active_cache[i];
i = check_one_conflict(i, &conflict_type);
if (conflict_type == THREE_STAGED)
string_list_insert(conflict, (const char *)e->name);
@@ -414,7 +414,7 @@ int rerere_remaining(struct string_list *merge_rr)
for (i = 0; i < active_nr;) {
int conflict_type;
- struct cache_entry *e = active_cache[i];
+ const struct cache_entry *e = active_cache[i];
i = check_one_conflict(i, &conflict_type);
if (conflict_type == PUNTED)
string_list_insert(merge_rr, (const char *)e->name);
diff --git a/resolve-undo.c b/resolve-undo.c
index 639eb9c59f..77101f51c1 100644
--- a/resolve-undo.c
+++ b/resolve-undo.c
@@ -115,7 +115,7 @@ void resolve_undo_clear_index(struct index_state *istate)
int unmerge_index_entry_at(struct index_state *istate, int pos)
{
- struct cache_entry *ce;
+ const struct cache_entry *ce;
struct string_list_item *item;
struct resolve_undo_info *ru;
int i, err = 0, matched;
@@ -167,7 +167,7 @@ void unmerge_marked_index(struct index_state *istate)
return;
for (i = 0; i < istate->cache_nr; i++) {
- struct cache_entry *ce = istate->cache[i];
+ const struct cache_entry *ce = istate->cache[i];
if (ce->ce_flags & CE_MATCHED)
i = unmerge_index_entry_at(istate, i);
}
@@ -181,7 +181,7 @@ void unmerge_index(struct index_state *istate, const char **pathspec)
return;
for (i = 0; i < istate->cache_nr; i++) {
- struct cache_entry *ce = istate->cache[i];
+ const struct cache_entry *ce = istate->cache[i];
if (!match_pathspec(pathspec, ce->name, ce_namelen(ce), 0, NULL))
continue;
i = unmerge_index_entry_at(istate, i);
diff --git a/revision.c b/revision.c
index 2f0142f22e..84ccc0529b 100644
--- a/revision.c
+++ b/revision.c
@@ -1358,7 +1358,7 @@ static void prepare_show_merge(struct rev_info *revs)
if (!active_nr)
read_cache();
for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if (!ce_stage(ce))
continue;
if (ce_path_match(ce, &revs->prune_data)) {
diff --git a/revision.h b/revision.h
index 92d6614af6..95859ba119 100644
--- a/revision.h
+++ b/revision.h
@@ -144,6 +144,7 @@ struct rev_info {
int numbered_files;
int reroll_count;
char *message_id;
+ struct ident_split from_ident;
struct string_list *ref_message_ids;
int add_signoff;
const char *extra_headers;
diff --git a/run-command.c b/run-command.c
index aece872e33..1b7f88eeb1 100644
--- a/run-command.c
+++ b/run-command.c
@@ -76,7 +76,10 @@ static inline void close_pair(int fd[2])
static inline void dup_devnull(int to)
{
int fd = open("/dev/null", O_RDWR);
- dup2(fd, to);
+ if (fd < 0)
+ die_errno(_("open /dev/null failed"));
+ if (dup2(fd, to) < 0)
+ die_errno(_("dup2(%d,%d) failed"), fd, to);
close(fd);
}
#endif
diff --git a/run-command.h b/run-command.h
index 221ce33140..6b985afd07 100644
--- a/run-command.h
+++ b/run-command.h
@@ -46,6 +46,7 @@ int finish_command(struct child_process *);
int run_command(struct child_process *);
extern char *find_hook(const char *name);
+LAST_ARG_MUST_BE_NULL
extern int run_hook(const char *index_file, const char *name, ...);
#define RUN_COMMAND_NO_STDIN 1
diff --git a/sequencer.c b/sequencer.c
index 425207ad5f..351548f57d 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -331,7 +331,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
int i;
strbuf_addstr(msgbuf, "\nConflicts:\n");
for (i = 0; i < active_nr;) {
- struct cache_entry *ce = active_cache[i++];
+ const struct cache_entry *ce = active_cache[i++];
if (ce_stage(ce)) {
strbuf_addch(msgbuf, '\t');
strbuf_addstr(msgbuf, ce->name);
@@ -371,8 +371,9 @@ static int is_index_unchanged(void)
active_cache_tree = cache_tree();
if (!cache_tree_fully_valid(active_cache_tree))
- if (cache_tree_update(active_cache_tree, active_cache,
- active_nr, 0))
+ if (cache_tree_update(active_cache_tree,
+ (const struct cache_entry * const *)active_cache,
+ active_nr, 0))
return error(_("Unable to update cache tree\n"));
return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
diff --git a/setup.c b/setup.c
index 94c1e61bda..5262319be6 100644
--- a/setup.c
+++ b/setup.c
@@ -360,6 +360,7 @@ int is_inside_work_tree(void)
void setup_work_tree(void)
{
+ struct strbuf sb = STRBUF_INIT;
const char *work_tree, *git_dir;
static int initialized = 0;
@@ -379,8 +380,10 @@ void setup_work_tree(void)
if (getenv(GIT_WORK_TREE_ENVIRONMENT))
setenv(GIT_WORK_TREE_ENVIRONMENT, ".", 1);
- set_git_dir(relative_path(git_dir, work_tree));
+ set_git_dir(relative_path(git_dir, work_tree, &sb));
initialized = 1;
+
+ strbuf_release(&sb);
}
static int check_repository_format_gently(const char *gitdir, int *nongit_ok)
@@ -908,3 +911,15 @@ const char *resolve_gitdir(const char *suspect)
return suspect;
return read_gitfile(suspect);
}
+
+/* if any standard file descriptor is missing open it to /dev/null */
+void sanitize_stdfds(void)
+{
+ int fd = open("/dev/null", O_RDWR, 0);
+ while (fd != -1 && fd < 2)
+ fd = dup(fd);
+ if (fd == -1)
+ die_errno("open /dev/null or dup failed");
+ if (fd > 2)
+ close(fd);
+}
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/sha1_name.c b/sha1_name.c
index 1a75fac05f..543bf9d9ec 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -1234,7 +1234,7 @@ static void diagnose_invalid_index_path(int stage,
const char *filename)
{
struct stat st;
- struct cache_entry *ce;
+ const struct cache_entry *ce;
int pos;
unsigned namelen = strlen(filename);
unsigned fullnamelen;
@@ -1328,7 +1328,7 @@ static int get_sha1_with_context_1(const char *name,
*/
if (name[0] == ':') {
int stage = 0;
- struct cache_entry *ce;
+ const struct cache_entry *ce;
char *new_path = NULL;
int pos;
if (!only_to_die && namelen > 2 && name[1] == '/') {
diff --git a/shallow.c b/shallow.c
index cbe2526d8c..8a9c96d019 100644
--- a/shallow.c
+++ b/shallow.c
@@ -110,17 +110,12 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
continue;
*pointer = cur_depth;
}
- if (cur_depth < depth) {
- if (p->next)
- add_object_array(&p->item->object,
- NULL, &stack);
- else {
- commit = p->item;
- cur_depth = *(int *)commit->util;
- }
- } else {
- commit_list_insert(p->item, &result);
- p->item->object.flags |= shallow_flag;
+ if (p->next)
+ add_object_array(&p->item->object,
+ NULL, &stack);
+ else {
+ commit = p->item;
+ cur_depth = *(int *)commit->util;
}
}
}
diff --git a/shell.c b/shell.c
index 1429870a8f..66350b220c 100644
--- a/shell.c
+++ b/shell.c
@@ -147,7 +147,6 @@ int main(int argc, char **argv)
char *prog;
const char **user_argv;
struct commands *cmd;
- int devnull_fd;
int count;
git_setup_gettext();
@@ -156,15 +155,10 @@ int main(int argc, char **argv)
/*
* Always open file descriptors 0/1/2 to avoid clobbering files
- * in die(). It also avoids not messing up when the pipes are
- * dup'ed onto stdin/stdout/stderr in the child processes we spawn.
+ * in die(). It also avoids messing up when the pipes are dup'ed
+ * onto stdin/stdout/stderr in the child processes we spawn.
*/
- devnull_fd = open("/dev/null", O_RDWR);
- while (devnull_fd >= 0 && devnull_fd <= 2)
- devnull_fd = dup(devnull_fd);
- if (devnull_fd == -1)
- die_errno("opening /dev/null failed");
- close (devnull_fd);
+ sanitize_stdfds();
/*
* Special hack to pretend to be a CVS server
diff --git a/streaming.c b/streaming.c
index cabcd9d157..efbc3babf1 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);
@@ -149,7 +149,7 @@ struct git_istream *open_istream(const unsigned char *sha1,
return NULL;
}
}
- if (st && filter) {
+ if (filter) {
/* Add "&& !is_null_stream_filter(filter)" for performance */
struct git_istream *nst = attach_stream_filter(st, filter);
if (!nst)
diff --git a/submodule.c b/submodule.c
index 85415d0057..3f0a3f9419 100644
--- a/submodule.c
+++ b/submodule.c
@@ -624,7 +624,7 @@ int fetch_populated_submodules(const struct argv_array *options,
struct strbuf submodule_path = STRBUF_INIT;
struct strbuf submodule_git_dir = STRBUF_INIT;
struct strbuf submodule_prefix = STRBUF_INIT;
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
const char *git_dir, *name, *default_argv;
if (!S_ISGITLINK(ce->ce_mode))
diff --git a/t/annotate-tests.sh b/t/annotate-tests.sh
index c56a77d237..0bfee001b4 100644
--- a/t/annotate-tests.sh
+++ b/t/annotate-tests.sh
@@ -2,11 +2,21 @@
# sourced from t8001-annotate.sh and t8002-blame.sh.
check_count () {
- head=
- case "$1" in -h) head="$2"; shift; shift ;; esac
- echo "$PROG file $head" >&4
- $PROG file $head >.result || return 1
- cat .result | perl -e '
+ head= &&
+ file='file' &&
+ options= &&
+ while :
+ do
+ case "$1" in
+ -h) head="$2"; shift; shift ;;
+ -f) file="$2"; shift; shift ;;
+ -*) options="$options $1"; shift ;;
+ *) break ;;
+ esac
+ done &&
+ echo "$PROG $options $file $head" >&4 &&
+ $PROG $options $file $head >actual &&
+ perl -e '
my %expect = (@ARGV);
my %count = map { $_ => 0 } keys %expect;
while (<STDIN>) {
@@ -31,107 +41,246 @@ check_count () {
print STDERR "Author $author (expected $value, attributed $count) $ok\n";
}
exit($bad);
- ' "$@"
+ ' "$@" <actual
}
-test_expect_success \
- 'prepare reference tree' \
- 'echo "1A quick brown fox jumps over the" >file &&
- echo "lazy dog" >>file &&
- git add file &&
- GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" git commit -a -m "Initial."'
-
-test_expect_success \
- 'check all lines blamed on A' \
- 'check_count A 2'
-
-test_expect_success \
- 'Setup new lines blamed on B' \
- 'echo "2A quick brown fox jumps over the" >>file &&
- echo "lazy dog" >> file &&
- GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" git commit -a -m "Second."'
-
-test_expect_success \
- 'Two lines blamed on A, two on B' \
- 'check_count A 2 B 2'
-
-test_expect_success \
- 'merge-setup part 1' \
- 'git checkout -b branch1 master &&
- echo "3A slow green fox jumps into the" >> file &&
- echo "well." >> file &&
- GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" git commit -a -m "Branch1-1"'
-
-test_expect_success \
- 'Two lines blamed on A, two on B, two on B1' \
- 'check_count A 2 B 2 B1 2'
-
-test_expect_success \
- 'merge-setup part 2' \
- 'git checkout -b branch2 master &&
- sed -e "s/2A quick brown/4A quick brown lazy dog/" < file > file.new &&
- mv file.new file &&
- GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" git commit -a -m "Branch2-1"'
-
-test_expect_success \
- 'Two lines blamed on A, one on B, one on B2' \
- 'check_count A 2 B 1 B2 1'
-
-test_expect_success \
- 'merge-setup part 3' \
- 'git pull . branch1'
-
-test_expect_success \
- 'Two lines blamed on A, one on B, two on B1, one on B2' \
- 'check_count A 2 B 1 B1 2 B2 1'
-
-test_expect_success \
- 'Annotating an old revision works' \
- 'check_count -h master A 2 B 2'
-
-test_expect_success \
- 'Annotating an old revision works' \
- 'check_count -h master^ A 2'
-
-test_expect_success \
- 'merge-setup part 4' \
- 'echo "evil merge." >>file &&
- git commit -a --amend'
-
-test_expect_success \
- 'Two lines blamed on A, one on B, two on B1, one on B2, one on A U Thor' \
- 'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1'
-
-test_expect_success \
- 'an incomplete line added' \
- 'echo "incomplete" | tr -d "\\012" >>file &&
- GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" git commit -a -m "Incomplete"'
-
-test_expect_success \
- 'With incomplete lines.' \
- 'check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1'
-
-test_expect_success \
- 'some edit' \
- 'mv file file.orig &&
- {
- cat file.orig &&
- echo
- } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" > file &&
- echo "incomplete" | tr -d "\\012" >>file &&
- GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" git commit -a -m "edit"'
-
-test_expect_success \
- 'some edit' \
- 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1'
-
-test_expect_success \
- 'an obfuscated email added' \
- 'echo "No robots allowed" > file.new &&
- cat file >> file.new &&
- mv file.new file &&
- GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" git commit -a -m "norobots"'
-
-test_expect_success \
- 'obfuscated email parsed' \
- 'check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1'
+test_expect_success 'setup A lines' '
+ echo "1A quick brown fox jumps over the" >file &&
+ echo "lazy dog" >>file &&
+ git add file &&
+ GIT_AUTHOR_NAME="A" GIT_AUTHOR_EMAIL="A@test.git" \
+ git commit -a -m "Initial."
+'
+
+test_expect_success 'blame 1 author' '
+ check_count A 2
+'
+
+test_expect_success 'setup B lines' '
+ echo "2A quick brown fox jumps over the" >>file &&
+ echo "lazy dog" >>file &&
+ GIT_AUTHOR_NAME="B" GIT_AUTHOR_EMAIL="B@test.git" \
+ git commit -a -m "Second."
+'
+
+test_expect_success 'blame 2 authors' '
+ check_count A 2 B 2
+'
+
+test_expect_success 'setup B1 lines (branch1)' '
+ git checkout -b branch1 master &&
+ echo "3A slow green fox jumps into the" >>file &&
+ echo "well." >>file &&
+ GIT_AUTHOR_NAME="B1" GIT_AUTHOR_EMAIL="B1@test.git" \
+ git commit -a -m "Branch1-1"
+'
+
+test_expect_success 'blame 2 authors + 1 branch1 author' '
+ check_count A 2 B 2 B1 2
+'
+
+test_expect_success 'setup B2 lines (branch2)' '
+ git checkout -b branch2 master &&
+ sed -e "s/2A quick brown/4A quick brown lazy dog/" <file >file.new &&
+ mv file.new file &&
+ GIT_AUTHOR_NAME="B2" GIT_AUTHOR_EMAIL="B2@test.git" \
+ git commit -a -m "Branch2-1"
+'
+
+test_expect_success 'blame 2 authors + 1 branch2 author' '
+ check_count A 2 B 1 B2 1
+'
+
+test_expect_success 'merge branch1 & branch2' '
+ git pull . branch1
+'
+
+test_expect_success 'blame 2 authors + 2 merged-in authors' '
+ check_count A 2 B 1 B1 2 B2 1
+'
+
+test_expect_success 'blame ancestor' '
+ check_count -h master A 2 B 2
+'
+
+test_expect_success 'blame great-ancestor' '
+ check_count -h master^ A 2
+'
+
+test_expect_success 'setup evil merge' '
+ echo "evil merge." >>file &&
+ git commit -a --amend
+'
+
+test_expect_success 'blame evil merge' '
+ check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1
+'
+
+test_expect_success 'setup incomplete line' '
+ echo "incomplete" | tr -d "\\012" >>file &&
+ GIT_AUTHOR_NAME="C" GIT_AUTHOR_EMAIL="C@test.git" \
+ git commit -a -m "Incomplete"
+'
+
+test_expect_success 'blame incomplete line' '
+ check_count A 2 B 1 B1 2 B2 1 "A U Thor" 1 C 1
+'
+
+test_expect_success 'setup edits' '
+ mv file file.orig &&
+ {
+ cat file.orig &&
+ echo
+ } | sed -e "s/^3A/99/" -e "/^1A/d" -e "/^incomplete/d" >file &&
+ echo "incomplete" | tr -d "\\012" >>file &&
+ GIT_AUTHOR_NAME="D" GIT_AUTHOR_EMAIL="D@test.git" \
+ git commit -a -m "edit"
+'
+
+test_expect_success 'blame edits' '
+ check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1
+'
+
+test_expect_success 'setup obfuscated email' '
+ echo "No robots allowed" >file.new &&
+ cat file >>file.new &&
+ mv file.new file &&
+ GIT_AUTHOR_NAME="E" GIT_AUTHOR_EMAIL="E at test dot git" \
+ git commit -a -m "norobots"
+'
+
+test_expect_success 'blame obfuscated email' '
+ check_count A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L 1 (all)' '
+ check_count -L1 A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L , (all)' '
+ check_count -L, A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
+'
+
+test_expect_success 'blame -L X (X to end)' '
+ check_count -L5 B1 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L X, (X to end)' '
+ check_count -L5, B1 1 C 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L ,Y (up to Y)' '
+ check_count -L,3 A 1 B2 1 E 1
+'
+
+test_expect_success 'blame -L X,X' '
+ check_count -L3,3 B2 1
+'
+
+test_expect_success 'blame -L X,Y' '
+ check_count -L3,6 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L Y,X (undocumented)' '
+ check_count -L6,3 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X,+1' '
+ check_count -L3,+1 B2 1
+'
+
+test_expect_success 'blame -L X,+N' '
+ check_count -L3,+4 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X,-1' '
+ check_count -L3,-1 B2 1
+'
+
+test_expect_success 'blame -L X,-N' '
+ check_count -L6,-4 B 1 B1 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L /RE/ (RE to end)' '
+ check_count -L/evil/ C 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,/RE2/' '
+ check_count -L/robot/,/green/ A 1 B 1 B2 1 D 1 E 1
+'
+
+test_expect_success 'blame -L X,/RE/' '
+ check_count -L5,/evil/ B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,Y' '
+ check_count -L/99/,7 B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,+N' '
+ check_count -L/99/,+3 B1 1 D 1 "A U Thor" 1
+'
+
+test_expect_success 'blame -L /RE/,-N' '
+ check_count -L/99/,-3 B 1 B2 1 D 1
+'
+
+test_expect_success 'blame -L X (X > nlines)' '
+ test_must_fail $PROG -L12345 file
+'
+
+test_expect_success 'blame -L ,Y (Y > nlines)' '
+ test_must_fail $PROG -L,12345 file
+'
+
+test_expect_success 'setup -L :regex' '
+ tr Q "\\t" >hello.c <<-\EOF &&
+ int main(int argc, const char *argv[])
+ {
+ Qputs("hello");
+ }
+ EOF
+ git add hello.c &&
+ GIT_AUTHOR_NAME="F" GIT_AUTHOR_EMAIL="F@test.git" \
+ git commit -m "hello" &&
+
+ mv hello.c hello.orig &&
+ sed -e "/}/i\\
+ Qputs(\"goodbye\");" <hello.orig | tr Q "\\t" >hello.c &&
+ GIT_AUTHOR_NAME="G" GIT_AUTHOR_EMAIL="G@test.git" \
+ git commit -a -m "goodbye" &&
+
+ mv hello.c hello.orig &&
+ echo "#include <stdio.h>" >hello.c &&
+ cat hello.orig >>hello.c &&
+ tr Q "\\t" >>hello.c <<-\EOF
+ void mail()
+ {
+ Qputs("mail");
+ }
+ EOF
+ GIT_AUTHOR_NAME="H" GIT_AUTHOR_EMAIL="H@test.git" \
+ git commit -a -m "mail"
+'
+
+test_expect_success 'blame -L :literal' '
+ check_count -f hello.c -L:main F 4 G 1
+'
+
+test_expect_success 'blame -L :regex' '
+ check_count -f hello.c "-L:m[a-z][a-z]l" H 4
+'
+
+test_expect_success 'blame -L :nomatch' '
+ test_must_fail $PROG -L:nomatch hello.c
+'
+
+test_expect_success 'blame -L bogus' '
+ test_must_fail $PROG -L file &&
+ test_must_fail $PROG -L1,+ file &&
+ test_must_fail $PROG -L1,- file &&
+ test_must_fail $PROG -LX file &&
+ test_must_fail $PROG -L1,X file &&
+ test_must_fail $PROG -L1,+N file &&
+ test_must_fail $PROG -L1,-N file
+'
diff --git a/t/t0008-ignores.sh b/t/t0008-ignores.sh
index a56db804cb..c29342d6bc 100755
--- a/t/t0008-ignores.sh
+++ b/t/t0008-ignores.sh
@@ -697,13 +697,21 @@ test_expect_success PIPE 'streaming support for --stdin' '
# shell, and then echo to the fd. We make sure to close it at
# the end, so that the subprocess does get EOF and dies
# properly.
+ #
+ # Similarly, we must keep "out" open so that check-ignore does
+ # not ever get SIGPIPE trying to write to us. Not only would that
+ # produce incorrect results, but then there would be no writer on the
+ # other end of the pipe, and we would potentially block forever trying
+ # to open it.
exec 9>in &&
+ exec 8<out &&
test_when_finished "exec 9>&-" &&
+ test_when_finished "exec 8<&-" &&
echo >&9 one &&
- read response <out &&
+ read response <&8 &&
echo "$response" | grep "^\.gitignore:1:one one" &&
echo >&9 two &&
- read response <out &&
+ read response <&8 &&
echo "$response" | grep "^:: two"
'
diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh
index 09a42a428e..3a48de20d8 100755
--- a/t/t0060-path-utils.sh
+++ b/t/t0060-path-utils.sh
@@ -8,8 +8,15 @@ test_description='Test various path utilities'
. ./test-lib.sh
norm_path() {
+ expected=$(test-path-utils mingw_path "$2")
test_expect_success $3 "normalize path: $1 => $2" \
- "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$2'"
+ "test \"\$(test-path-utils normalize_path_copy '$1')\" = '$expected'"
+}
+
+relative_path() {
+ expected=$(test-path-utils mingw_path "$3")
+ test_expect_success $4 "relative path: $1 $2 => $3" \
+ "test \"\$(test-path-utils relative_path '$1' '$2')\" = '$expected'"
}
# On Windows, we are using MSYS's bash, which mangles the paths.
@@ -34,8 +41,8 @@ ancestor() {
test \"\$actual\" = '$expected'"
}
-# Absolute path tests must be skipped on Windows because due to path mangling
-# the test program never sees a POSIX-style absolute path
+# Some absolute path tests should be skipped on Windows due to path mangling
+# on POSIX-style absolute paths
case $(uname -s) in
*MINGW*)
;;
@@ -68,30 +75,30 @@ norm_path d1/s1//../s2/../../d2 d2
norm_path d1/.../d2 d1/.../d2
norm_path d1/..././../d2 d1/d2
-norm_path / / POSIX
+norm_path / /
norm_path // / POSIX
norm_path /// / POSIX
-norm_path /. / POSIX
+norm_path /. /
norm_path /./ / POSIX
norm_path /./.. ++failed++ POSIX
-norm_path /../. ++failed++ POSIX
+norm_path /../. ++failed++
norm_path /./../.// ++failed++ POSIX
norm_path /dir/.. / POSIX
norm_path /dir/sub/../.. / POSIX
norm_path /dir/sub/../../.. ++failed++ POSIX
-norm_path /dir /dir POSIX
-norm_path /dir// /dir/ POSIX
-norm_path /./dir /dir POSIX
-norm_path /dir/. /dir/ POSIX
-norm_path /dir///./ /dir/ POSIX
-norm_path /dir//sub/.. /dir/ POSIX
-norm_path /dir/sub/../ /dir/ POSIX
+norm_path /dir /dir
+norm_path /dir// /dir/
+norm_path /./dir /dir
+norm_path /dir/. /dir/
+norm_path /dir///./ /dir/
+norm_path /dir//sub/.. /dir/
+norm_path /dir/sub/../ /dir/
norm_path //dir/sub/../. /dir/ POSIX
-norm_path /dir/s1/../s2/ /dir/s2/ POSIX
-norm_path /d1/s1///s2/..//../s3/ /d1/s3/ POSIX
-norm_path /d1/s1//../s2/../../d2 /d2 POSIX
-norm_path /d1/.../d2 /d1/.../d2 POSIX
-norm_path /d1/..././../d2 /d1/d2 POSIX
+norm_path /dir/s1/../s2/ /dir/s2/
+norm_path /d1/s1///s2/..//../s3/ /d1/s3/
+norm_path /d1/s1//../s2/../../d2 /d2
+norm_path /d1/.../d2 /d1/.../d2
+norm_path /d1/..././../d2 /d1/d2
ancestor / / -1
ancestor /foo / 0
@@ -183,4 +190,33 @@ test_expect_success SYMLINKS 'real path works on symlinks' '
test "$sym" = "$(test-path-utils real_path "$dir2/syml")"
'
+relative_path /a/b/c/ /a/b/ c/
+relative_path /a/b/c/ /a/b c/
+relative_path /a//b//c/ //a/b// c/ POSIX
+relative_path /a/b /a/b ./
+relative_path /a/b/ /a/b ./
+relative_path /a /a/b ../
+relative_path / /a/b/ ../../
+relative_path /a/c /a/b/ ../c
+relative_path /a/c /a/b ../c
+relative_path /x/y /a/b/ ../../x/y
+relative_path /a/b "<empty>" /a/b
+relative_path /a/b "<null>" /a/b
+relative_path a/b/c/ a/b/ c/
+relative_path a/b/c/ a/b c/
+relative_path a/b//c a//b c
+relative_path a/b/ a/b/ ./
+relative_path a/b/ a/b ./
+relative_path a a/b ../
+relative_path x/y a/b ../../x/y
+relative_path a/c a/b ../c
+relative_path a/b "<empty>" a/b
+relative_path a/b "<null>" a/b
+relative_path "<empty>" /a/b ./
+relative_path "<empty>" "<empty>" ./
+relative_path "<empty>" "<null>" ./
+relative_path "<null>" "<empty>" ./
+relative_path "<null>" "<null>" ./
+relative_path "<null>" /a/b ./
+
test_done
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/t1307-config-blob.sh b/t/t1307-config-blob.sh
new file mode 100755
index 0000000000..fdc257e66f
--- /dev/null
+++ b/t/t1307-config-blob.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+test_description='support for reading config from a blob'
+. ./test-lib.sh
+
+test_expect_success 'create config blob' '
+ cat >config <<-\EOF &&
+ [some]
+ value = 1
+ EOF
+ git add config &&
+ git commit -m foo
+'
+
+test_expect_success 'list config blob contents' '
+ echo some.value=1 >expect &&
+ git config --blob=HEAD:config --list >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'fetch value from blob' '
+ echo true >expect &&
+ git config --blob=HEAD:config --bool some.value >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'reading non-existing value from blob is an error' '
+ test_must_fail git config --blob=HEAD:config non.existing
+'
+
+test_expect_success 'reading from blob and file is an error' '
+ test_must_fail git config --blob=HEAD:config --system --list
+'
+
+test_expect_success 'reading from missing ref is an error' '
+ test_must_fail git config --blob=HEAD:doesnotexist --list
+'
+
+test_expect_success 'reading from non-blob is an error' '
+ test_must_fail git config --blob=HEAD --list
+'
+
+test_expect_success 'setting a value in a blob is an error' '
+ test_must_fail git config --blob=HEAD:config some.value foo
+'
+
+test_expect_success 'deleting a value in a blob is an error' '
+ test_must_fail git config --blob=HEAD:config --unset some.value
+'
+
+test_expect_success 'editing a blob is an error' '
+ test_must_fail git config --blob=HEAD:config --edit
+'
+
+test_expect_success 'parse errors in blobs are properly attributed' '
+ cat >config <<-\EOF &&
+ [some]
+ value = "
+ EOF
+ git add config &&
+ git commit -m broken &&
+
+ test_must_fail git config --blob=HEAD:config some.value 2>err &&
+
+ # just grep for our token as the exact error message is likely to
+ # change or be internationalized
+ grep "HEAD:config" err
+'
+
+test_done
diff --git a/t/t1403-show-ref.sh b/t/t1403-show-ref.sh
new file mode 100755
index 0000000000..3e500ed7da
--- /dev/null
+++ b/t/t1403-show-ref.sh
@@ -0,0 +1,167 @@
+#!/bin/sh
+
+test_description='show-ref'
+. ./test-lib.sh
+
+test_expect_success setup '
+ test_commit A &&
+ git tag -f -a -m "annotated A" A &&
+ git checkout -b side &&
+ test_commit B &&
+ git tag -f -a -m "annotated B" B &&
+ git checkout master &&
+ test_commit C &&
+ git branch B A^0
+'
+
+test_expect_success 'show-ref' '
+ echo $(git rev-parse refs/tags/A) refs/tags/A >expect &&
+
+ git show-ref A >actual &&
+ test_cmp expect actual &&
+
+ git show-ref tags/A >actual &&
+ test_cmp expect actual &&
+
+ git show-ref refs/tags/A >actual &&
+ test_cmp expect actual &&
+
+ >expect &&
+
+ test_must_fail git show-ref D >actual
+ test_cmp expect actual
+'
+
+test_expect_success 'show-ref -q' '
+ >expect &&
+
+ git show-ref -q A >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -q tags/A >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -q refs/tags/A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref -q D >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'show-ref --verify' '
+ echo $(git rev-parse refs/tags/A) refs/tags/A >expect &&
+
+ git show-ref --verify refs/tags/A >actual &&
+ test_cmp expect actual &&
+
+ >expect &&
+
+ test_must_fail git show-ref --verify A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify tags/A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify D >actual
+ test_cmp expect actual
+'
+
+test_expect_success 'show-ref --verify -q' '
+ >expect &&
+
+ git show-ref --verify -q refs/tags/A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify -q A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify -q tags/A >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref --verify -q D >actual
+ test_cmp expect actual
+'
+
+test_expect_success 'show-ref -d' '
+ {
+ echo $(git rev-parse refs/tags/A) refs/tags/A &&
+ echo $(git rev-parse refs/tags/A^0) "refs/tags/A^{}"
+ echo $(git rev-parse refs/tags/C) refs/tags/C
+ } >expect &&
+ git show-ref -d A C >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d tags/A tags/C >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d refs/tags/A refs/tags/C >actual &&
+ test_cmp expect actual &&
+
+ echo $(git rev-parse refs/heads/master) refs/heads/master >expect &&
+ git show-ref -d master >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d heads/master >actual &&
+ test_cmp expect actual &&
+
+ git show-ref -d refs/heads/master >actual &&
+ test_cmp expect actual
+
+ git show-ref -d --verify refs/heads/master >actual &&
+ test_cmp expect actual
+
+ >expect &&
+
+ test_must_fail git show-ref -d --verify master >actual &&
+ test_cmp expect actual &&
+
+ test_must_fail git show-ref -d --verify heads/master >actual &&
+ test_cmp expect actual
+
+'
+
+test_expect_success 'show-ref --heads, --tags, --head, pattern' '
+ for branch in B master side
+ do
+ echo $(git rev-parse refs/heads/$branch) refs/heads/$branch
+ done >expect.heads &&
+ git show-ref --heads >actual &&
+ test_cmp expect.heads actual &&
+
+ for tag in A B C
+ do
+ echo $(git rev-parse refs/tags/$tag) refs/tags/$tag
+ done >expect.tags &&
+ git show-ref --tags >actual &&
+ test_cmp expect.tags actual &&
+
+ cat expect.heads expect.tags >expect &&
+ git show-ref --heads --tags >actual &&
+ test_cmp expect actual &&
+
+ {
+ echo $(git rev-parse HEAD) HEAD &&
+ cat expect.heads expect.tags
+ } >expect &&
+ git show-ref --heads --tags --head >actual &&
+ test_cmp expect actual &&
+
+ {
+ echo $(git rev-parse HEAD) HEAD &&
+ echo $(git rev-parse refs/heads/B) refs/heads/B
+ echo $(git rev-parse refs/tags/B) refs/tags/B
+ } >expect &&
+ git show-ref --head B >actual &&
+ test_cmp expect actual &&
+
+ {
+ echo $(git rev-parse HEAD) HEAD &&
+ echo $(git rev-parse refs/heads/B) refs/heads/B
+ echo $(git rev-parse refs/tags/B) refs/tags/B
+ echo $(git rev-parse refs/tags/B^0) "refs/tags/B^{}"
+ } >expect &&
+ git show-ref --head -d B >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/t/t2202-add-addremove.sh b/t/t2202-add-addremove.sh
index 6a8151064c..fc8b59e7f7 100755
--- a/t/t2202-add-addremove.sh
+++ b/t/t2202-add-addremove.sh
@@ -41,4 +41,14 @@ test_expect_success 'git add --all' '
test_cmp expect actual
'
+test_expect_success 'Just "git add" is a no-op' '
+ git reset --hard &&
+ echo >will-remove &&
+ >will-not-be-added &&
+ git add &&
+ git diff-index --name-status --cached HEAD >actual &&
+ >expect &&
+ test_cmp expect actual
+'
+
test_done
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/t4000-diff-format.sh b/t/t4000-diff-format.sh
index 6ddd46915d..8de36b7d12 100755
--- a/t/t4000-diff-format.sh
+++ b/t/t4000-diff-format.sh
@@ -15,17 +15,17 @@ line 3'
cat path0 >path1
chmod +x path1
-test_expect_success \
- 'update-index --add two files with and without +x.' \
- 'git update-index --add path0 path1'
+test_expect_success 'update-index --add two files with and without +x.' '
+ git update-index --add path0 path1
+'
mv path0 path0-
sed -e 's/line/Line/' <path0- >path0
chmod +x path0
rm -f path1
-test_expect_success \
- 'git diff-files -p after editing work tree.' \
- 'git diff-files -p >current'
+test_expect_success 'git diff-files -p after editing work tree.' '
+ git diff-files -p >actual
+'
# that's as far as it comes
if [ "$(git config --get core.filemode)" = false ]
@@ -55,8 +55,38 @@ deleted file mode 100755
-line 3
EOF
-test_expect_success \
- 'validate git diff-files -p output.' \
- 'compare_diff_patch current expected'
+test_expect_success 'validate git diff-files -p output.' '
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'git diff-files -s after editing work tree' '
+ git diff-files -s >actual 2>err &&
+ test_must_be_empty actual &&
+ test_must_be_empty err
+'
+
+test_expect_success 'git diff-files --no-patch as synonym for -s' '
+ git diff-files --no-patch >actual 2>err &&
+ test_must_be_empty actual &&
+ test_must_be_empty err
+'
+
+test_expect_success 'git diff-files --no-patch --patch shows the patch' '
+ git diff-files --no-patch --patch >actual &&
+ compare_diff_patch expected actual
+'
+
+test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' '
+ git diff-files --no-patch --patch-with-raw >actual &&
+ grep -q "^:100644 100755 .* 0000000000000000000000000000000000000000 M path0\$" actual &&
+ tail -n +4 actual >actual-patch &&
+ compare_diff_patch expected actual-patch
+'
+
+test_expect_success 'git diff-files --patch --no-patch does not show the patch' '
+ git diff-files --patch --no-patch >actual 2>err &&
+ test_must_be_empty actual &&
+ test_must_be_empty err
+'
test_done
diff --git a/t/t4014-format-patch.sh b/t/t4014-format-patch.sh
index 58d418098d..668933bfb2 100755
--- a/t/t4014-format-patch.sh
+++ b/t/t4014-format-patch.sh
@@ -972,6 +972,49 @@ test_expect_success 'empty subject prefix does not have extra space' '
test_cmp expect actual
'
+test_expect_success '--from=ident notices bogus ident' '
+ test_must_fail git format-patch -1 --stdout --from=foo >patch
+'
+
+test_expect_success '--from=ident replaces author' '
+ git format-patch -1 --stdout --from="Me <me@example.com>" >patch &&
+ cat >expect <<-\EOF &&
+ From: Me <me@example.com>
+
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success '--from uses committer ident' '
+ git format-patch -1 --stdout --from >patch &&
+ cat >expect <<-\EOF &&
+ From: C O Mitter <committer@example.com>
+
+ From: A U Thor <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+ test_cmp expect patch.head
+'
+
+test_expect_success 'in-body headers trigger content encoding' '
+ GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
+ test_when_finished "git reset --hard HEAD^" &&
+ git format-patch -1 --stdout --from >patch &&
+ cat >expect <<-\EOF &&
+ From: C O Mitter <committer@example.com>
+ Content-Type: text/plain; charset=UTF-8
+
+ From: éxötìc <author@example.com>
+
+ EOF
+ sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
+ test_cmp expect patch.head
+'
+
append_signoff()
{
C=$(git commit-tree HEAD^^{tree} -p HEAD) &&
diff --git a/t/t4203-mailmap.sh b/t/t4203-mailmap.sh
index 842b7549ec..baa4685dcc 100755
--- a/t/t4203-mailmap.sh
+++ b/t/t4203-mailmap.sh
@@ -13,6 +13,11 @@ fuzz_blame () {
}
test_expect_success setup '
+ cat >contacts <<-\EOF &&
+ A U Thor <author@example.com>
+ nick1 <bugs@company.xx>
+ EOF
+
echo one >one &&
git add one &&
test_tick &&
@@ -23,6 +28,44 @@ test_expect_success setup '
git commit --author "nick1 <bugs@company.xx>" -m second
'
+test_expect_success 'check-mailmap no arguments' '
+ test_must_fail git check-mailmap
+'
+
+test_expect_success 'check-mailmap arguments' '
+ cat >expect <<-\EOF &&
+ A U Thor <author@example.com>
+ nick1 <bugs@company.xx>
+ EOF
+ git check-mailmap \
+ "A U Thor <author@example.com>" \
+ "nick1 <bugs@company.xx>" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin' '
+ cat >expect <<-\EOF &&
+ A U Thor <author@example.com>
+ nick1 <bugs@company.xx>
+ EOF
+ git check-mailmap --stdin <contacts >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap --stdin arguments' '
+ cat >expect <<-\EOF &&
+ Internal Guy <bugs@company.xy>
+ EOF
+ cat <contacts >>expect &&
+ git check-mailmap --stdin "Internal Guy <bugs@company.xy>" \
+ <contacts >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'check-mailmap bogus contact' '
+ test_must_fail git check-mailmap bogus
+'
+
cat >expect <<\EOF
A U Thor (1):
initial
@@ -247,6 +290,24 @@ test_expect_success 'cleanup after mailmap.blob tests' '
rm -f .mailmap
'
+test_expect_success 'single-character name' '
+ echo " 1 A <author@example.com>" >expect &&
+ echo " 1 nick1 <bugs@company.xx>" >>expect &&
+ echo "A <author@example.com>" >.mailmap &&
+ test_when_finished "rm .mailmap" &&
+ git shortlog -es HEAD >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'preserve canonical email case' '
+ echo " 1 A U Thor <AUTHOR@example.com>" >expect &&
+ echo " 1 nick1 <bugs@company.xx>" >>expect &&
+ echo "<AUTHOR@example.com> <author@example.com>" >.mailmap &&
+ test_when_finished "rm .mailmap" &&
+ git shortlog -es HEAD >actual &&
+ test_cmp expect actual
+'
+
# Extended mailmap configurations should give us the following output for shortlog
cat >expect <<\EOF
A U Thor <author@example.com> (1):
diff --git a/t/t4211-line-log.sh b/t/t4211-line-log.sh
index 7776f93e3d..7665d6785c 100755
--- a/t/t4211-line-log.sh
+++ b/t/t4211-line-log.sh
@@ -64,4 +64,17 @@ test_bad_opts "-L 1,1000:b.c" "has only.*lines"
test_bad_opts "-L :b.c" "argument.*not of the form"
test_bad_opts "-L :foo:b.c" "no match"
+# There is a separate bug when an empty -L range is the first -L encountered,
+# thus to demonstrate this particular bug, the empty -L range must follow a
+# non-empty -L range.
+test_expect_success '-L {empty-range} (any -L)' '
+ n=$(expr $(wc -l <b.c) + 1) &&
+ git log -L1,1:b.c -L$n:b.c
+'
+
+test_expect_success '-L {empty-range} (first -L)' '
+ n=$(expr $(wc -l <b.c) + 1) &&
+ git log -L$n:b.c
+'
+
test_done
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/t6021-merge-criss-cross.sh b/t/t6021-merge-criss-cross.sh
index 331b9b07d4..d15b313d4b 100755
--- a/t/t6021-merge-criss-cross.sh
+++ b/t/t6021-merge-criss-cross.sh
@@ -3,7 +3,7 @@
# Copyright (c) 2005 Fredrik Kuivinen
#
-# See http://marc.theaimsgroup.com/?l=git&m=111463358500362&w=2 for a
+# See http://marc.info/?l=git&m=111463358500362&w=2 for a
# nice description of what this is about.
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index a25729f2a7..c0e5b2a627 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -174,4 +174,28 @@ check_describe "test2-lightweight-*" --tags --match="test2-*"
check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
+test_expect_success 'name-rev with exact tags' '
+ echo A >expect &&
+ tag_object=$(git rev-parse refs/tags/A) &&
+ git name-rev --tags --name-only $tag_object >actual &&
+ test_cmp expect actual &&
+
+ echo "A^0" >expect &&
+ tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+ git name-rev --tags --name-only $tagged_commit >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'describe --contains with the exact tags' '
+ echo "A^0" >expect &&
+ tag_object=$(git rev-parse refs/tags/A) &&
+ git describe --contains $tag_object >actual &&
+ test_cmp expect actual &&
+
+ echo "A^0" >expect &&
+ tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+ git describe --contains $tagged_commit >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t7301-clean-interactive.sh b/t/t7301-clean-interactive.sh
new file mode 100755
index 0000000000..4e6055d06a
--- /dev/null
+++ b/t/t7301-clean-interactive.sh
@@ -0,0 +1,439 @@
+#!/bin/sh
+
+test_description='git clean -i basic tests'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+
+ mkdir -p src &&
+ touch src/part1.c Makefile &&
+ echo build >.gitignore &&
+ echo \*.o >>.gitignore &&
+ git add . &&
+ git commit -m setup &&
+ touch src/part2.c README &&
+ git add .
+
+'
+
+test_expect_success 'git clean -i (clean)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ echo c | git clean -i &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (quit)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ echo q | git clean -i &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -i (Ctrl+D)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ echo "\04" | git clean -i &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter all)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo f; echo "*"; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo f; echo "part3.* *.out"; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (filter patterns 2)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo f; echo "* !*.out"; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - all)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo "*"; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - none)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 3; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 2)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 2 3; echo 5; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - number 3)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 3,4 5; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - range)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 1,3-4; echo 2; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test ! -f docs/manual.txt &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (select - range 2)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo 4- 1; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test ! -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (inverse select)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo s; echo "*"; echo -5- 1 -2; echo; echo c) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id (ask - Ctrl+D)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (echo a; echo Y; echo no; echo yes; echo "\04") | \
+ git clean -id &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (filter)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (cd build/ && \
+ (echo f; echo "docs"; echo "*.h"; echo ; echo c) | \
+ git clean -id ..) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (select by name)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (cd build/ && \
+ (echo s; echo "../docs/"; echo "../src/part3.c"; \
+ echo "../src/part4.c"; echo; echo c) | \
+ git clean -id ..) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test -f a.out &&
+ test ! -f docs/manual.txt &&
+ test ! -f src/part3.c &&
+ test -f src/part3.h &&
+ test ! -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_expect_success 'git clean -id with prefix and path (ask)' '
+
+ mkdir -p build docs &&
+ touch a.out src/part3.c src/part3.h src/part4.c src/part4.h \
+ docs/manual.txt obj.o build/lib.so &&
+ (cd build/ && \
+ (echo a; echo Y; echo y; echo no; echo yes; echo bad; echo) | \
+ git clean -id ..) &&
+ test -f Makefile &&
+ test -f README &&
+ test -f src/part1.c &&
+ test -f src/part2.c &&
+ test ! -f a.out &&
+ test ! -f docs/manual.txt &&
+ test -f src/part3.c &&
+ test ! -f src/part3.h &&
+ test -f src/part4.c &&
+ test -f src/part4.h &&
+ test -f obj.o &&
+ test -f build/lib.so
+
+'
+
+test_done
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index 50e6ad7458..5ee97b003a 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -963,4 +963,20 @@ test_expect_success 'submodule with UTF-8 name' '
git submodule >&2 &&
test -n "$(git submodule | grep "$svname")"
'
+
+test_expect_success 'submodule add clone shallow submodule' '
+ mkdir super &&
+ pwd=$(pwd)
+ (
+ cd super &&
+ git init &&
+ git submodule add --depth=1 file://"$pwd"/example2 submodule &&
+ (
+ cd submodule &&
+ test 1 = $(git log --oneline | wc -l)
+ )
+ )
+'
+
+
test_done
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index cdb0538392..b192f936bc 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -294,6 +294,35 @@ test_expect_success 'submodule update - checkout in .git/config' '
)
'
+test_expect_success 'submodule update - command in .git/config' '
+ (cd super &&
+ git config submodule.submodule.update "!git checkout"
+ ) &&
+ (cd super/submodule &&
+ git reset --hard HEAD^
+ ) &&
+ (cd super &&
+ (cd submodule &&
+ compare_head
+ ) &&
+ git submodule update submodule &&
+ cd submodule &&
+ ! compare_head
+ )
+'
+
+test_expect_success 'submodule update - command in .git/config catches failure' '
+ (cd super &&
+ git config submodule.submodule.update "!false"
+ ) &&
+ (cd super/submodule &&
+ git reset --hard HEAD^
+ ) &&
+ (cd super &&
+ test_must_fail git submodule update submodule
+ )
+'
+
test_expect_success 'submodule init picks up rebase' '
(cd super &&
git config -f .gitmodules submodule.rebasing.update rebase &&
@@ -700,14 +729,24 @@ test_expect_success 'submodule update properly revives a moved submodule' '
test_expect_success SYMLINKS 'submodule update can handle symbolic links in pwd' '
mkdir -p linked/dir &&
ln -s linked/dir linkto &&
- (
- cd linkto &&
- git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
- (
- cd super &&
- git submodule update --init --recursive
- )
+ (cd linkto &&
+ git clone "$TRASH_DIRECTORY"/super_update_r2 super &&
+ (cd super &&
+ git submodule update --init --recursive
+ )
)
'
+test_expect_success 'submodule update clone shallow submodule' '
+ git clone cloned super3 &&
+ pwd=$(pwd)
+ (cd super3 &&
+ sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
+ mv -f .gitmodules.tmp .gitmodules &&
+ git submodule update --init --depth=3
+ (cd submodule &&
+ test 1 = $(git log --oneline | wc -l)
+ )
+ )
+'
test_done
diff --git a/t/t7600-merge.sh b/t/t7600-merge.sh
index 460d8ebf48..10aa028017 100755
--- a/t/t7600-merge.sh
+++ b/t/t7600-merge.sh
@@ -497,9 +497,15 @@ test_expect_success 'combining --squash and --no-ff is refused' '
test_must_fail git merge --no-ff --squash c1
'
-test_expect_success 'combining --ff-only and --no-ff is refused' '
- test_must_fail git merge --ff-only --no-ff c1 &&
- test_must_fail git merge --no-ff --ff-only c1
+test_expect_success 'option --ff-only overwrites --no-ff' '
+ git merge --no-ff --ff-only c1 &&
+ test_must_fail git merge --no-ff --ff-only c2
+'
+
+test_expect_success 'option --no-ff overrides merge.ff=only config' '
+ git reset --hard c0 &&
+ test_config merge.ff only &&
+ git merge --no-ff c1
'
test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
diff --git a/t/t8001-annotate.sh b/t/t8001-annotate.sh
index 41962f04a7..72176e42c1 100755
--- a/t/t8001-annotate.sh
+++ b/t/t8001-annotate.sh
@@ -6,9 +6,9 @@ test_description='git annotate'
PROG='git annotate'
. "$TEST_DIRECTORY"/annotate-tests.sh
-test_expect_success 'Annotating an old revision works' '
- git annotate file master >result &&
- awk "{ print \$3; }" <result >authors &&
+test_expect_success 'annotate old revision' '
+ git annotate file master >actual &&
+ awk "{ print \$3; }" <actual >authors &&
test 2 = $(grep A <authors | wc -l) &&
test 2 = $(grep B <authors | wc -l)
'
diff --git a/t/t8002-blame.sh b/t/t8002-blame.sh
index e2896cffc1..5cdf3f178e 100755
--- a/t/t8002-blame.sh
+++ b/t/t8002-blame.sh
@@ -7,8 +7,16 @@ PROG='git blame -c'
. "$TEST_DIRECTORY"/annotate-tests.sh
PROG='git blame -c -e'
-test_expect_success 'Blame --show-email works' '
- check_count "<A@test.git>" 1 "<B@test.git>" 1 "<B1@test.git>" 1 "<B2@test.git>" 1 "<author@example.com>" 1 "<C@test.git>" 1 "<D@test.git>" 1 "<E at test dot git>" 1
+test_expect_success 'blame --show-email' '
+ check_count \
+ "<A@test.git>" 1 \
+ "<B@test.git>" 1 \
+ "<B1@test.git>" 1 \
+ "<B2@test.git>" 1 \
+ "<author@example.com>" 1 \
+ "<C@test.git>" 1 \
+ "<D@test.git>" 1 \
+ "<E at test dot git>" 1
'
test_done
diff --git a/t/t9801-git-p4-branch.sh b/t/t9801-git-p4-branch.sh
index 9730821c30..2bf142d09c 100755
--- a/t/t9801-git-p4-branch.sh
+++ b/t/t9801-git-p4-branch.sh
@@ -469,9 +469,11 @@ test_expect_success 'use-client-spec detect-branches skips branches setup' '
View: //depot/usecs/b1/... //depot/usecs/b3/...
EOF
- echo b3/b3-file3 >b3/b3-file3 &&
- p4 add b3/b3-file3 &&
- p4 submit -d "b3/b3-file3"
+ echo b3/b3-file3_1 >b3/b3-file3_1 &&
+ echo b3/b3-file3_2 >b3/b3-file3_2 &&
+ p4 add b3/b3-file3_1 &&
+ p4 add b3/b3-file3_2 &&
+ p4 submit -d "b3/b3-file3_1 b3/b3-file3_2"
)
'
@@ -487,6 +489,21 @@ test_expect_success 'use-client-spec detect-branches skips branches' '
)
'
+test_expect_success 'use-client-spec detect-branches skips files in branches' '
+ client_view "//depot/usecs/... //client/..." \
+ "-//depot/usecs/b3/b3-file3_1 //client/b3/b3-file3_1" &&
+ test_when_finished cleanup_git &&
+ test_create_repo "$git" &&
+ (
+ cd "$git" &&
+ git p4 sync --detect-branches --use-client-spec //depot/usecs@all &&
+ git checkout -b master p4/usecs/b3 &&
+ test_path_is_file b1-file1 &&
+ test_path_is_file b3-file3_2 &&
+ test_path_is_missing b3-file3_1
+ )
+'
+
test_expect_success 'kill p4d' '
kill_p4d
'
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/test-dump-cache-tree.c b/test-dump-cache-tree.c
index a6ffdf39d5..47eab9765f 100644
--- a/test-dump-cache-tree.c
+++ b/test-dump-cache-tree.c
@@ -59,6 +59,8 @@ int main(int ac, char **av)
struct cache_tree *another = cache_tree();
if (read_cache() < 0)
die("unable to read index file");
- cache_tree_update(another, active_cache, active_nr, WRITE_TREE_DRY_RUN);
+ cache_tree_update(another,
+ (const struct cache_entry * const *)active_cache,
+ active_nr, WRITE_TREE_DRY_RUN);
return dump_cache_tree(active_cache_tree, another, "");
}
diff --git a/test-path-utils.c b/test-path-utils.c
index 0092cbf354..bb975e4d3e 100644
--- a/test-path-utils.c
+++ b/test-path-utils.c
@@ -28,6 +28,19 @@ static int normalize_ceiling_entry(struct string_list_item *item, void *unused)
return 1;
}
+static void normalize_argv_string(const char **var, const char *input)
+{
+ if (!strcmp(input, "<null>"))
+ *var = NULL;
+ else if (!strcmp(input, "<empty>"))
+ *var = "";
+ else
+ *var = input;
+
+ if (*var && (**var == '<' || **var == '('))
+ die("Bad value: %s\n", input);
+}
+
int main(int argc, char **argv)
{
if (argc == 3 && !strcmp(argv[1], "normalize_path_copy")) {
@@ -103,6 +116,25 @@ int main(int argc, char **argv)
return 0;
}
+ if (argc == 3 && !strcmp(argv[1], "mingw_path")) {
+ puts(argv[2]);
+ return 0;
+ }
+
+ if (argc == 4 && !strcmp(argv[1], "relative_path")) {
+ struct strbuf sb = STRBUF_INIT;
+ const char *in, *prefix, *rel;
+ normalize_argv_string(&in, argv[2]);
+ normalize_argv_string(&prefix, argv[3]);
+ rel = relative_path(in, prefix, &sb);
+ if (!rel)
+ puts("(null)");
+ else
+ puts(strlen(rel) > 0 ? rel : "(empty)");
+ strbuf_release(&sb);
+ return 0;
+ }
+
fprintf(stderr, "%s: unknown function name: %s\n", argv[0],
argv[1] ? argv[1] : "(there was none)");
return 1;
diff --git a/trace.c b/trace.c
index 5ec0e3bd16..3d744d1d4d 100644
--- a/trace.c
+++ b/trace.c
@@ -75,6 +75,7 @@ static void trace_vprintf(const char *key, const char *fmt, va_list ap)
strbuf_release(&buf);
}
+__attribute__((format (printf, 2, 3)))
static void trace_printf_key(const char *key, const char *fmt, ...)
{
va_list ap;
diff --git a/transport-helper.c b/transport-helper.c
index db9bd18298..45a35df66e 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -982,6 +982,7 @@ int transport_helper_init(struct transport *transport, const char *name)
#define PBUFFERSIZE 8192
/* Print bidirectional transfer loop debug message. */
+__attribute__((format (printf, 1, 2)))
static void transfer_debug(const char *fmt, ...)
{
va_list args;
@@ -1067,7 +1068,7 @@ static int udt_do_read(struct unidirectional_transfer *t)
return -1;
} else if (bytes == 0) {
transfer_debug("%s EOF (with %i bytes in buffer)",
- t->src_name, t->bufuse);
+ t->src_name, (int)t->bufuse);
t->state = SSTATE_FLUSHING;
} else if (bytes > 0) {
t->bufuse += bytes;
diff --git a/tree.c b/tree.c
index 62fed632d8..c0e568c08f 100644
--- a/tree.c
+++ b/tree.c
@@ -159,7 +159,7 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match)
* sort at the end.
*/
for (i = 0; !fn && i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if (ce_stage(ce) == stage)
fn = read_one_entry;
}
diff --git a/unpack-trees.c b/unpack-trees.c
index b27f2a62e8..bf01717015 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -179,7 +179,7 @@ static void display_error_msgs(struct unpack_trees_options *o)
* Unlink the last component and schedule the leading directories for
* removal, such that empty directories get removed.
*/
-static void unlink_entry(struct cache_entry *ce)
+static void unlink_entry(const struct cache_entry *ce)
{
if (!check_leading_path(ce->name, ce_namelen(ce)))
return;
@@ -199,7 +199,7 @@ static int check_updates(struct unpack_trees_options *o)
if (o->update && o->verbose_update) {
for (total = cnt = 0; cnt < index->cache_nr; cnt++) {
- struct cache_entry *ce = index->cache[cnt];
+ const struct cache_entry *ce = index->cache[cnt];
if (ce->ce_flags & (CE_UPDATE | CE_WT_REMOVE))
total++;
}
@@ -212,7 +212,7 @@ static int check_updates(struct unpack_trees_options *o)
if (o->update)
git_attr_set_direction(GIT_ATTR_CHECKOUT, &o->result);
for (i = 0; i < index->cache_nr; i++) {
- struct cache_entry *ce = index->cache[i];
+ const struct cache_entry *ce = index->cache[i];
if (ce->ce_flags & CE_WT_REMOVE) {
display_progress(progress, ++cnt);
@@ -376,7 +376,7 @@ static struct cache_entry *next_cache_entry(struct unpack_trees_options *o)
return NULL;
}
-static void add_same_unmerged(struct cache_entry *ce,
+static void add_same_unmerged(const struct cache_entry *ce,
struct unpack_trees_options *o)
{
struct index_state *index = o->src_index;
@@ -650,7 +650,7 @@ static int find_cache_pos(struct traverse_info *info,
int p_len = tree_entry_len(p);
for (pos = o->cache_bottom; pos < index->cache_nr; pos++) {
- struct cache_entry *ce = index->cache[pos];
+ const struct cache_entry *ce = index->cache[pos];
const char *ce_name, *ce_slash;
int cmp, ce_len;
@@ -1353,7 +1353,7 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
*/
static int icase_exists(struct unpack_trees_options *o, const char *name, int len, struct stat *st)
{
- struct cache_entry *src;
+ const struct cache_entry *src;
src = index_name_exists(o->src_index, name, len, 1);
return src && !ie_match_stat(o->src_index, src, st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
@@ -1364,7 +1364,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
enum unpack_trees_error_types error_type,
struct unpack_trees_options *o)
{
- struct cache_entry *result;
+ const struct cache_entry *result;
/*
* It may be that the 'lstat()' succeeded even though
diff --git a/utf8.h b/utf8.h
index 32a7bfb987..65d0e42b96 100644
--- a/utf8.h
+++ b/utf8.h
@@ -10,6 +10,7 @@ int utf8_strwidth(const char *string);
int is_utf8(const char *text);
int is_encoding_utf8(const char *name);
int same_encoding(const char *, const char *);
+__attribute__((format (printf, 2, 3)))
int utf8_fprintf(FILE *, const char *, ...);
void strbuf_add_wrapped_text(struct strbuf *buf,
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
diff --git a/wrapper.c b/wrapper.c
index dd7ecbb115..6a015de5f0 100644
--- a/wrapper.c
+++ b/wrapper.c
@@ -322,7 +322,7 @@ int git_mkstemps_mode(char *pattern, int suffix_len, int mode)
template[5] = letters[v % num_letters]; v /= num_letters;
fd = open(pattern, O_CREAT | O_EXCL | O_RDWR, mode);
- if (fd > 0)
+ if (fd >= 0)
return fd;
/*
* Fatal error (EPERM, ENOSPC etc).
diff --git a/wt-status.c b/wt-status.c
index 9c4d086b51..cb24f1fa9b 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -244,7 +244,7 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
struct strbuf onebuf = STRBUF_INIT;
const char *one, *how = _("bug");
- one = quote_path(it->string, -1, &onebuf, s->prefix);
+ one = quote_path(it->string, s->prefix, &onebuf);
status_printf(s, color(WT_STATUS_HEADER, s), "\t");
switch (d->stagemask) {
case 1: how = _("both deleted:"); break;
@@ -298,8 +298,8 @@ static void wt_status_print_change_data(struct wt_status *s,
change_type);
}
- one = quote_path(one_name, -1, &onebuf, s->prefix);
- two = quote_path(two_name, -1, &twobuf, s->prefix);
+ one = quote_path(one_name, s->prefix, &onebuf);
+ two = quote_path(two_name, s->prefix, &twobuf);
status_printf(s, color(WT_STATUS_HEADER, s), "\t");
switch (status) {
@@ -372,7 +372,7 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
static int unmerged_mask(const char *path)
{
int pos, mask;
- struct cache_entry *ce;
+ const struct cache_entry *ce;
pos = cache_name_pos(path, strlen(path));
if (0 <= pos)
@@ -476,7 +476,7 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
for (i = 0; i < active_nr; i++) {
struct string_list_item *it;
struct wt_status_change_data *d;
- struct cache_entry *ce = active_cache[i];
+ const struct cache_entry *ce = active_cache[i];
if (!ce_path_match(ce, &pathspec))
continue;
@@ -707,8 +707,7 @@ static void wt_status_print_other(struct wt_status *s,
struct string_list_item *it;
const char *path;
it = &(l->items[i]);
- path = quote_path(it->string, strlen(it->string),
- &buf, s->prefix);
+ path = quote_path(it->string, s->prefix, &buf);
if (column_active(s->colopts)) {
string_list_append(&output, path);
continue;
@@ -1293,7 +1292,7 @@ static void wt_shortstatus_unmerged(struct string_list_item *it,
} else {
struct strbuf onebuf = STRBUF_INIT;
const char *one;
- one = quote_path(it->string, -1, &onebuf, s->prefix);
+ one = quote_path(it->string, s->prefix, &onebuf);
printf(" %s\n", one);
strbuf_release(&onebuf);
}
@@ -1321,7 +1320,7 @@ static void wt_shortstatus_status(struct string_list_item *it,
struct strbuf onebuf = STRBUF_INIT;
const char *one;
if (d->head_path) {
- one = quote_path(d->head_path, -1, &onebuf, s->prefix);
+ one = quote_path(d->head_path, s->prefix, &onebuf);
if (*one != '"' && strchr(one, ' ') != NULL) {
putchar('"');
strbuf_addch(&onebuf, '"');
@@ -1330,7 +1329,7 @@ static void wt_shortstatus_status(struct string_list_item *it,
printf("%s -> ", one);
strbuf_release(&onebuf);
}
- one = quote_path(it->string, -1, &onebuf, s->prefix);
+ one = quote_path(it->string, s->prefix, &onebuf);
if (*one != '"' && strchr(one, ' ') != NULL) {
putchar('"');
strbuf_addch(&onebuf, '"');
@@ -1349,7 +1348,7 @@ static void wt_shortstatus_other(struct string_list_item *it,
} else {
struct strbuf onebuf = STRBUF_INIT;
const char *one;
- one = quote_path(it->string, -1, &onebuf, s->prefix);
+ one = quote_path(it->string, s->prefix, &onebuf);
color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign);
printf(" %s\n", one);
strbuf_release(&onebuf);
diff --git a/wt-status.h b/wt-status.h
index 4121bc208d..fb7152e187 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -96,9 +96,9 @@ void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
void wt_shortstatus_print(struct wt_status *s);
void wt_porcelain_print(struct wt_status *s);
-void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...)
- ;
-void status_printf(struct wt_status *s, const char *color, const char *fmt, ...)
- ;
+__attribute__((format (printf, 3, 4)))
+void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...);
+__attribute__((format (printf, 3, 4)))
+void status_printf(struct wt_status *s, const char *color, const char *fmt, ...);
#endif /* STATUS_H */