]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'sb/object-store-lookup'
authorJunio C Hamano <gitster@pobox.com>
Thu, 2 Aug 2018 22:30:42 +0000 (15:30 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 2 Aug 2018 22:30:42 +0000 (15:30 -0700)
lookup_commit_reference() and friends have been updated to find
in-core object for a specific in-core repository instance.

* sb/object-store-lookup: (32 commits)
  commit.c: allow lookup_commit_reference to handle arbitrary repositories
  commit.c: allow lookup_commit_reference_gently to handle arbitrary repositories
  tag.c: allow deref_tag to handle arbitrary repositories
  object.c: allow parse_object to handle arbitrary repositories
  object.c: allow parse_object_buffer to handle arbitrary repositories
  commit.c: allow get_cached_commit_buffer to handle arbitrary repositories
  commit.c: allow set_commit_buffer to handle arbitrary repositories
  commit.c: migrate the commit buffer to the parsed object store
  commit-slabs: remove realloc counter outside of slab struct
  commit.c: allow parse_commit_buffer to handle arbitrary repositories
  tag: allow parse_tag_buffer to handle arbitrary repositories
  tag: allow lookup_tag to handle arbitrary repositories
  commit: allow lookup_commit to handle arbitrary repositories
  tree: allow lookup_tree to handle arbitrary repositories
  blob: allow lookup_blob to handle arbitrary repositories
  object: allow lookup_object to handle arbitrary repositories
  object: allow object_as_type to handle arbitrary repositories
  tag: add repository argument to deref_tag
  tag: add repository argument to parse_tag_buffer
  tag: add repository argument to lookup_tag
  ...

292 files changed:
.mailmap
Documentation/RelNotes/2.19.0.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-commit-graph.txt
Documentation/git-fsck.txt
Documentation/git-gc.txt
Documentation/git-grep.txt
Documentation/git-imap-send.txt
Documentation/git-notes.txt
Documentation/git-rebase.txt
Documentation/git-send-email.txt
Documentation/git-status.txt
Documentation/git-svn.txt
Documentation/git-worktree.txt
Documentation/giteveryday.txt
Documentation/gitignore.txt
Documentation/gitsubmodules.txt
Documentation/glossary-content.txt
Documentation/revisions.txt
Documentation/technical/api-directory-listing.txt
Documentation/technical/api-gitattributes.txt
Documentation/technical/commit-graph-format.txt
Documentation/technical/commit-graph.txt
Documentation/technical/directory-rename-detection.txt [new file with mode: 0644]
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-v2.txt
Makefile
advice.c
advice.h
apply.c
builtin/blame.c
builtin/branch.c
builtin/checkout.c
builtin/clone.c
builtin/commit-graph.c
builtin/config.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/log.c
builtin/merge-recursive.c
builtin/merge.c
builtin/pack-objects.c
builtin/receive-pack.c
builtin/rev-list.c
builtin/submodule--helper.c
builtin/update-index.c
builtin/worktree.c
cache.h
checkout.c
checkout.h
commit-graph.c
commit-graph.h
commit.c
commit.h
compat/vcbuild/README
config.c
config.h
connect.h
connected.c
connected.h
contrib/coccinelle/commit.cocci
contrib/git-jump/README
contrib/git-jump/git-jump
convert.c
diff-lib.c
diff.c
diff.h
dir.c
ewah/bitmap.c
ewah/ewah_bitmap.c
ewah/ewah_io.c
ewah/ewah_rlw.c
ewah/ewok.h
ewah/ewok_rlw.h
fast-import.c
fetch-negotiator.c [new file with mode: 0644]
fetch-negotiator.h [new file with mode: 0644]
fetch-object.c
fetch-pack.c
fetch-pack.h
fsck.c
git-filter-branch.sh
git-p4.py
git-rebase--merge.sh
git-rebase--preserve-merges.sh
git-rebase.sh
git-send-email.perl
git-submodule.sh
gpg-interface.c
gpg-interface.h
grep.c
grep.h
hex.c
line-log.c
line-range.c
list-objects.c
log-tree.c
merge-recursive.c
negotiator/default.c [new file with mode: 0644]
negotiator/default.h [new file with mode: 0644]
object.h
pack-bitmap-write.c
pack-bitmap.c
pack-bitmap.h
path.h
pretty.c
ref-filter.c
refs.c
refs/files-backend.c
refs/refs-internal.h
remote.c
remote.h
revision.c
revision.h
sequencer.c
sha1-file.c
sha1-name.c
strbuf.c
string-list.c
submodule-config.c
submodule-config.h
submodule.c
submodule.h
t/annotate-tests.sh
t/check-non-portable-shell.pl
t/helper/test-drop-caches.c
t/helper/test-pkt-line.c
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/lib-httpd/apply-one-time-sed.sh [new file with mode: 0644]
t/lib-submodule-update.sh
t/t0000-basic.sh
t/t0001-init.sh
t/t0003-attributes.sh
t/t0021-conversion.sh
t/t0090-cache-tree.sh
t/t1004-read-tree-m-u-wf.sh
t/t1005-read-tree-reset.sh
t/t1008-read-tree-overlay.sh
t/t1020-subdirectory.sh
t/t1050-large.sh
t/t1300-config.sh
t/t1410-reflog.sh
t/t1411-reflog-show.sh
t/t1507-rev-parse-upstream.sh
t/t1512-rev-parse-disambiguation.sh
t/t1700-split-index.sh
t/t2016-checkout-patch.sh
t/t2024-checkout-dwim.sh
t/t2025-worktree-add.sh
t/t2103-update-index-ignore-missing.sh
t/t2202-add-addremove.sh
t/t3000-ls-files-others.sh
t/t3006-ls-files-long.sh
t/t3008-ls-files-lazy-init-name-hash.sh
t/t3030-merge-recursive.sh
t/t3035-merge-sparse.sh [new file with mode: 0755]
t/t3050-subprojects-fetch.sh
t/t3102-ls-tree-wildcards.sh
t/t3200-branch.sh
t/t3210-pack-refs.sh
t/t3301-notes.sh
t/t3400-rebase.sh
t/t3401-rebase-and-am-rename.sh [new file with mode: 0755]
t/t3402-rebase-merge.sh
t/t3404-rebase-interactive.sh
t/t3405-rebase-malformed.sh
t/t3418-rebase-continue.sh
t/t3422-rebase-incompatible-options.sh [new file with mode: 0755]
t/t3423-rebase-reword.sh [new file with mode: 0755]
t/t3510-cherry-pick-sequence.sh
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t3904-stash-patch.sh
t/t4001-diff-rename.sh
t/t4010-diff-pathspec.sh
t/t4012-diff-binary.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4018/php-abstract-class [new file with mode: 0644]
t/t4018/php-class [new file with mode: 0644]
t/t4018/php-final-class [new file with mode: 0644]
t/t4018/php-function [new file with mode: 0644]
t/t4018/php-interface [new file with mode: 0644]
t/t4018/php-method [new file with mode: 0644]
t/t4018/php-trait [new file with mode: 0644]
t/t4024-diff-optimize-common.sh
t/t4025-hunk-header.sh
t/t4041-diff-submodule-option.sh
t/t4060-diff-submodule-option-diff-format.sh
t/t4121-apply-diffs.sh
t/t4208-log-magic-pathspec.sh
t/t4211-line-log.sh
t/t5300-pack-object.sh
t/t5302-pack-index.sh
t/t5317-pack-objects-filter-objects.sh
t/t5318-commit-graph.sh
t/t5400-send-pack.sh
t/t5401-update-hooks.sh
t/t5405-send-pack-rewind.sh
t/t5406-remote-rejects.sh
t/t5407-post-rewrite-hook.sh
t/t5500-fetch-pack.sh
t/t5505-remote.sh
t/t5512-ls-remote.sh
t/t5516-fetch-push.sh
t/t5517-push-mirror.sh
t/t5520-pull.sh
t/t5526-fetch-submodules.sh
t/t5531-deep-submodule-push.sh
t/t5537-fetch-shallow.sh
t/t5541-http-push-smart.sh
t/t5543-atomic-push.sh
t/t5551-http-fetch-smart.sh
t/t5561-http-backend.sh
t/t5601-clone.sh
t/t5605-clone-local.sh
t/t5608-clone-2gb.sh
t/t5616-partial-clone.sh
t/t5703-upload-pack-ref-in-want.sh [new file with mode: 0755]
t/t5801-remote-helpers.sh
t/t6010-merge-base.sh
t/t6029-merge-subtree.sh
t/t6036-recursive-corner-cases.sh
t/t6042-merge-rename-corner-cases.sh
t/t6043-merge-rename-directories.sh
t/t6046-merge-skip-unneeded-updates.sh
t/t6300-for-each-ref.sh
t/t7001-mv.sh
t/t7003-filter-branch.sh
t/t7105-reset-patch.sh
t/t7201-co.sh
t/t7301-clean-interactive.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7408-submodule-reference.sh
t/t7415-submodule-names.sh
t/t7501-commit.sh
t/t7506-status-submodule.sh
t/t7610-mergetool.sh
t/t7810-grep.sh
t/t8003-blame-corner-cases.sh
t/t9001-send-email.sh
t/t9100-git-svn-basic.sh
t/t9101-git-svn-props.sh
t/t9119-git-svn-info.sh
t/t9122-git-svn-author.sh
t/t9129-git-svn-i18n-commitencoding.sh
t/t9130-git-svn-authors-file.sh
t/t9134-git-svn-ignore-paths.sh
t/t9137-git-svn-dcommit-clobber-series.sh
t/t9138-git-svn-authors-prog.sh
t/t9146-git-svn-empty-dirs.sh
t/t9147-git-svn-include-paths.sh
t/t9152-svn-empty-dirs-after-gc.sh
t/t9164-git-svn-dcommit-concurrent.sh
t/t9165-git-svn-fetch-merge-branch-of-branch.sh
t/t9200-git-cvsexportcommit.sh
t/t9302-fast-import-unpack-limit.sh
t/t9400-git-cvsserver-server.sh
t/t9600-cvsimport.sh
t/t9806-git-p4-options.sh
t/t9810-git-p4-rcs.sh
t/t9811-git-p4-label-import.sh
t/t9814-git-p4-rename.sh
t/t9815-git-p4-submit-fail.sh
t/t9830-git-p4-symlink-dir.sh
t/t9831-git-p4-triggers.sh
t/t9833-errors.sh
t/t9902-completion.sh
t/t9903-bash-prompt.sh
transport-helper.c
transport-internal.h
transport.c
transport.h
tree-walk.c
unicode-width.h
unpack-trees.c
upload-pack.c
userdiff.c
utf8.c
wt-status.c
xdiff/xdiff.h
xdiff/xdiffi.c

index df7cf6313c7dd0c5c065e448fd7c725ff537a08b..f165222a7821e982174adc8e1c3678275ae80753 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -35,11 +35,13 @@ Chris Wright <chrisw@sous-sol.org> <chrisw@osdl.org>
 Cord Seele <cowose@gmail.com> <cowose@googlemail.com>
 Christian Couder <chriscool@tuxfamily.org> <christian.couder@gmail.com>
 Christian Stimming <stimming@tuhh.de> <chs@ckiste.goetheallee>
+Christopher Díaz Riveros <chrisadr@gentoo.org> Christopher Diaz Riveros
 Csaba Henk <csaba@gluster.com> <csaba@lowlife.hu>
 Dan Johnson <computerdruid@gmail.com>
 Dana L. How <danahow@gmail.com> <how@deathvalley.cswitch.com>
 Dana L. How <danahow@gmail.com> Dana How
 Daniel Barkalow <barkalow@iabervon.org>
+Daniel Knittl-Frank <knittl89@googlemail.com> knittl
 Daniel Trstenjak <daniel.trstenjak@gmail.com> <daniel.trstenjak@online.de>
 Daniel Trstenjak <daniel.trstenjak@gmail.com> <trsten@science-computing.de>
 David Brown <git@davidb.org> <davidb@quicinc.com>
@@ -57,6 +59,7 @@ Eric S. Raymond <esr@thyrsus.com>
 Eric Wong <e@80x24.org> <normalperson@yhbt.net>
 Erik Faye-Lund <kusmabite@gmail.com> <kusmabite@googlemail.com>
 Eyvind Bernhardsen <eyvind.bernhardsen@gmail.com> <eyvind-git@orakel.ntnu.no>
+Fangyi Zhou <fangyi.zhou@yuriko.moe> Zhou Fangyi
 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>
@@ -86,6 +89,8 @@ Jason McMullan <mcmullan@netapp.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>
+Jean-Noël Avila <jn.avila@free.fr> Jean-Noel Avila
+Jean-Noël Avila <jn.avila@free.fr> Jean-Noël AVILA
 Jeff King <peff@peff.net> <peff@github.com>
 Jeff Muizelaar <jmuizelaar@mozilla.com> <jeff@infidigm.net>
 Jens Axboe <axboe@kernel.dk> <axboe@suse.de>
@@ -149,6 +154,7 @@ 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 Rüster <matthias.ruester@gmail.com> Matthias Ruester
 Matthias Urlichs <matthias@urlichs.de> <smurf@kiste.(none)>
 Matthias Urlichs <matthias@urlichs.de> <smurf@smurf.noris.de>
 Michael Coleman <tutufan@gmail.com>
@@ -213,6 +219,8 @@ 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>
+Wei Shuyu <wsy@dogben.com> Shuyu Wei
+Sidhant Sharma <tigerkid001@gmail.com> Sidhant Sharma [:tk]
 Simon Hausmann <hausmann@kde.org> <simon@lst.de>
 Simon Hausmann <hausmann@kde.org> <shausman@trolltech.com>
 Stefan Beller <stefanbeller@gmail.com> <stefanbeller@googlemail.com>
@@ -253,7 +261,8 @@ Uwe Kleine-König <u.kleine-koenig@pengutronix.de> <ukleinek@informatik.uni-frei
 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>
+Vitaly "_Vi" Shukela <vi0oss@gmail.com> <public_vi@tut.by>
+Vitaly "_Vi" Shukela <vi0oss@gmail.com> Vitaly _Vi Shukela
 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>
index 2b45b6ff5cb3bf2980ad911b8c84179f27d8d72c..7a5c5b82d4c5b08dcd5b9b1fbec4976bfa3a1794 100644 (file)
@@ -15,6 +15,23 @@ UI, Workflows & Features
    otherwise working-tree-only application of a patch will add new
    paths to the index marked with the "intent-to-add" bit.
 
+ * "git grep" learned the "--column" option that gives not just the
+   line number but the column number of the hit.
+
+ * The "-l" option in "git branch -l" is an unfortunate short-hand for
+   "--create-reflog", but many users, both old and new, somehow expect
+   it to be something else, perhaps "--list".  This step warns when "-l"
+   is used as a short-hand for "--create-reflog" and warns about the
+   future repurposing of the it when it is used.
+
+ * The userdiff pattern for .php has been updated.
+
+ * The content-transfer-encoding of the message "git send-email" sends
+   out by default was 8bit, which can cause trouble when there is an
+   overlong line to bust RFC 5322/2822 limit.  A new option 'auto' to
+   automatically switch to quoted-printable when there is such a line
+   in the payload has been introduced and is made the default.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -51,6 +68,32 @@ Performance, Internal Implementation, Development Support etc.
  * Build and test procedure for netrc credential helper (in contrib/)
    has been updated.
 
+ * The conversion to pass "the_repository" and then "a_repository"
+   throughout the object access API continues.
+
+ * Remove unused function definitions and declarations from ewah
+   bitmap subsystem.
+
+ * Code preparation to make "git p4" closer to be usable with Python 3.
+
+ * Tighten the API to make it harder to misuse in-tree .gitmodules
+   file, even though it shares the same syntax with configuration
+   files, to read random configuration items from it.
+
+ * "git fast-import" has been updated to avoid attempting to create
+   delta against a zero-byte-long string, which is pointless.
+
+ * The codebase has been updated to compile cleanly with -pedantic
+   option.
+   (merge 2b647a05d7 bb/pedantic later to maint).
+
+ * The character display width table has been updated to match the
+   latest Unicode standard.
+   (merge 570951eea2 bb/unicode-11-width later to maint).
+
+ * test-lint now looks for broken use of "VAR=VAL shell_func" in test
+   scripts.
+
 
 Fixes since v2.18
 -----------------
@@ -96,6 +139,101 @@ Fixes since v2.18
    to the submodule was changed in the range of commits in the
    superproject, sometimes showing "(null)".  This has been corrected.
 
- * Code cleanup.
+ * "git submodule" did not correctly adjust core.worktree setting that
+   indicates whether/where a submodule repository has its associated
+   working tree across various state transitions, which has been
+   corrected.
+   (merge 984cd77ddb sb/submodule-core-worktree later to maint).
+
+ * Bugfix for "rebase -i" corner case regression.
+   (merge a9279c6785 pw/rebase-i-keep-reword-after-conflict later to maint).
+
+ * Recently added "--base" option to "git format-patch" command did
+   not correctly generate prereq patch ids.
+   (merge 15b76c1fb3 xy/format-patch-prereq-patch-id-fix later to maint).
+
+ * POSIX portability fix in Makefile to fix a glitch introduced a few
+   releases ago.
+   (merge 6600054e9b dj/runtime-prefix later to maint).
+
+ * "git filter-branch" when used with the "--state-branch" option
+   still attempted to rewrite the commits whose filtered result is
+   known from the previous attempt (which is recorded on the state
+   branch); the command has been corrected not to waste cycles doing
+   so.
+   (merge 709cfe848a mb/filter-branch-optim later to maint).
+
+ * Clarify that setting core.ignoreCase to deviate from reality would
+   not turn a case-incapable filesystem into a case-capable one.
+   (merge 48294b512a ms/core-icase-doc later to maint).
+
+ * "fsck.skipList" did not prevent a blob object listed there from
+   being inspected for is contents (e.g. we recently started to
+   inspect the contents of ".gitmodules" for certain malicious
+   patterns), which has been corrected.
+   (merge fb16287719 rj/submodule-fsck-skip later to maint).
+
+ * "git checkout --recurse-submodules another-branch" did not report
+   in which submodule it failed to update the working tree, which
+   resulted in an unhelpful error message.
+   (merge ba95d4e4bd sb/submodule-move-head-error-msg later to maint).
+
+ * "git rebase" behaved slightly differently depending on which one of
+   the three backends gets used; this has been documented and an
+   effort to make them more uniform has begun.
+   (merge b00bf1c9a8 en/rebase-consistency later to maint).
+
+ * The "--ignore-case" option of "git for-each-ref" (and its friends)
+   did not work correctly, which has been fixed.
+   (merge e674eb2528 jk/for-each-ref-icase later to maint).
+
+ * "git fetch" failed to correctly validate the set of objects it
+   received when making a shallow history deeper, which has been
+   corrected.
+   (merge cf1e7c0770 jt/connectivity-check-after-unshallow later to maint).
+
+ * Partial clone support of "git clone" has been updated to correctly
+   validate the objects it receives from the other side.  The server
+   side has been corrected to send objects that are directly
+   requested, even if they may match the filtering criteria (e.g. when
+   doing a "lazy blob" partial clone).
+   (merge a7e67c11b8 jt/partial-clone-fsck-connectivity later to maint).
+
+ * Handling of an empty range by "git cherry-pick" was inconsistent
+   depending on how the range ended up to be empty, which has been
+   corrected.
+   (merge c5e358d073 jk/empty-pick-fix later to maint).
+
+ * "git reset --merge" (hence "git merge ---abort") and "git reset --hard"
+   had trouble working correctly in a sparsely checked out working
+   tree after a conflict, which has been corrected.
+   (merge b33fdfc34c mk/merge-in-sparse-checkout later to maint).
+
+ * Correct a broken use of "VAR=VAL shell_func" in a test.
+   (merge 650161a277 jc/t3404-one-shot-export-fix later to maint).
+
+ * "git rev-parse ':/substring'" did not consider the history leading
+   only to HEAD when looking for a commit with the given substring,
+   when the HEAD is detached.  This has been fixed.
+   (merge 6b3351e799 wc/find-commit-with-pattern-on-detached-head later to maint).
+
+ * Build doc update for Windows.
+   (merge ede8d89bb1 nd/command-list later to maint).
+
+ * core.commentchar is now honored when preparing the list of commits
+   to replay in "rebase -i".
+
+ * Code cleanup, docfix, build fix, etc.
    (merge aee9be2ebe sg/update-ref-stdin-cleanup later to maint).
    (merge 037714252f jc/clean-after-sanity-tests later to maint).
+   (merge 5b26c3c941 en/merge-recursive-cleanup later to maint).
+   (merge 0dcbc0392e bw/config-refer-to-gitsubmodules-doc later to maint).
+   (merge bb4d000e87 bw/protocol-v2 later to maint).
+   (merge 928f0ab4ba vs/typofixes later to maint).
+   (merge d7f590be84 en/rebase-i-microfixes later to maint).
+   (merge 81d395cc85 js/rebase-recreate-merge later to maint).
+   (merge 51d1863168 tz/exclude-doc-smallfixes later to maint).
+   (merge a9aa3c0927 ds/commit-graph later to maint).
+   (merge 5cf8e06474 js/enhanced-version-info later to maint).
+   (merge 6aaded5509 tb/config-default later to maint).
+   (merge 022d2ac1f3 sb/blame-color later to maint).
index 24885444078b04cfe70cf9fd0b81c972f72b91bd..b44fd51f275b105b62a1f5c9d23ca616e2d87e7d 100644 (file)
@@ -298,7 +298,7 @@ smaller project it is a good discipline to follow it.
 
 The sign-off is a simple line at the end of the explanation for
 the patch, which certifies that you wrote it or otherwise have
-the right to pass it on as a open-source patch.  The rules are
+the right to pass it on as an open-source patch.  The rules are
 pretty simple: if you can certify the below D-C-O:
 
 [[dco]]
@@ -403,7 +403,7 @@ don't demand).  +git log -p {litdd} _$area_you_are_modifying_+ would
 help you find out who they are.
 
 . You get comments and suggestions for improvements.  You may
-  even get them in a "on top of your change" patch form.
+  even get them in an "on top of your change" patch form.
 
 . Polish, refine, and re-send to the list and the people who
   spend their time to improve your patch.  Go back to step (2).
index 1cc18a828ca63bc489726dc3f1489a5e5e5c8b2b..44df580a81c3076a89483c34f07b40192894c7fa 100644 (file)
@@ -344,6 +344,16 @@ advice.*::
                Advice shown when you used linkgit:git-checkout[1] to
                move to the detach HEAD state, to instruct how to create
                a local branch after the fact.
+       checkoutAmbiguousRemoteBranchName::
+               Advice shown when the argument to
+               linkgit:git-checkout[1] ambiguously resolves to a
+               remote tracking branch on more than one remote in
+               situations where an unambiguous argument would have
+               otherwise caused a remote-tracking branch to be
+               checked out. See the `checkout.defaultRemote`
+               configuration variable for how to set a given remote
+               to used by default in some situations where this
+               advice would be printed.
        amWorkDir::
                Advice that shows the location of the patch file when
                linkgit:git-am[1] fails to apply it.
@@ -354,7 +364,7 @@ advice.*::
                Advice on what to do when you've accidentally added one
                git repo inside of another.
        ignoredHook::
-               Advice shown if an hook is ignored because the hook is not
+               Advice shown if a hook is ignored because the hook is not
                set as executable.
        waitingForEditor::
                Print a message to the terminal whenever Git is waiting for
@@ -390,16 +400,19 @@ core.hideDotFiles::
        default mode is 'dotGitOnly'.
 
 core.ignoreCase::
-       If true, this option enables various workarounds to enable
+       Internal variable which enables various workarounds to enable
        Git to work better on filesystems that are not case sensitive,
-       like FAT. For example, if a directory listing finds
-       "makefile" when Git expects "Makefile", Git will assume
+       like APFS, HFS+, FAT, NTFS, etc. For example, if a directory listing
+       finds "makefile" when Git expects "Makefile", Git will assume
        it is really the same file, and continue to remember it as
        "Makefile".
 +
 The default is false, except linkgit:git-clone[1] or linkgit:git-init[1]
 will probe and set core.ignoreCase true if appropriate when the repository
 is created.
++
+Git relies on the proper configuration of this variable for your operating
+and file system. Modifying this value may result in unexpected behavior.
 
 core.precomposeUnicode::
        This option is only used by Mac OS implementation of Git.
@@ -904,9 +917,12 @@ core.notesRef::
 This setting defaults to "refs/notes/commits", and it can be overridden by
 the `GIT_NOTES_REF` environment variable.  See linkgit:git-notes[1].
 
-core.commitGraph::
-       Enable git commit graph feature. Allows reading from the
-       commit-graph file.
+gc.commitGraph::
+       If true, then gc will rewrite the commit-graph file when
+       linkgit:git-gc[1] is run. When using linkgit:git-gc[1]
+       '--auto' the commit-graph will be updated if housekeeping is
+       required. Default is false. See linkgit:git-commit-graph[1]
+       for details.
 
 core.sparseCheckout::
        Enable "sparse checkout" feature. See section "Sparse checkout" in
@@ -1098,6 +1114,22 @@ browser.<tool>.path::
        browse HTML help (see `-w` option in linkgit:git-help[1]) or a
        working repository in gitweb (see linkgit:git-instaweb[1]).
 
+checkout.defaultRemote::
+       When you run 'git checkout <something>' and only have one
+       remote, it may implicitly fall back on checking out and
+       tracking e.g. 'origin/<something>'. This stops working as soon
+       as you have more than one remote with a '<something>'
+       reference. This setting allows for setting the name of a
+       preferred remote that should always win when it comes to
+       disambiguation. The typical use-case is to set this to
+       `origin`.
++
+Currently this is used by linkgit:git-checkout[1] when 'git checkout
+<something>' will checkout the '<something>' branch on another remote,
+and by linkgit:git-worktree[1] when 'git worktree add' refers to a
+remote branch. This setting might be used for other checkout-like
+commands or functionality in the future.
+
 clean.requireForce::
        A boolean to make git-clean do nothing unless given -f,
        -i or -n.   Defaults to true.
@@ -1146,6 +1178,11 @@ diff.colorMoved::
        true the default color mode will be used. When set to false,
        moved lines are not colored.
 
+diff.colorMovedWS::
+       When moved lines are colored using e.g. the `diff.colorMoved` setting,
+       this option controls the `<mode>` how spaces are treated
+       for details of valid modes see '--color-moved-ws' in linkgit:git-diff[1].
+
 color.diff.<slot>::
        Use customized color for diff colorization.  `<slot>` specifies
        which part of the patch to use the specified color, and is one
@@ -1182,8 +1219,10 @@ color.grep.<slot>::
        filename prefix (when not using `-h`)
 `function`;;
        function name lines (when using `-p`)
-`linenumber`;;
+`lineNumber`;;
        line number prefix (when using `-n`)
+`column`;;
+       column number prefix (when using `--column`)
 `match`;;
        matching text (same as setting `matchContext` and `matchSelected`)
 `matchContext`;;
@@ -1798,6 +1837,9 @@ gitweb.snapshot::
 grep.lineNumber::
        If set to true, enable `-n` option by default.
 
+grep.column::
+       If set to true, enable the `--column` option by default.
+
 grep.patternType::
        Set the default matching behavior. Using a value of 'basic', 'extended',
        'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`,
@@ -3328,12 +3370,13 @@ submodule.<name>.ignore::
 submodule.<name>.active::
        Boolean value indicating if the submodule is of interest to git
        commands.  This config option takes precedence over the
-       submodule.active config option.
+       submodule.active config option. See linkgit:gitsubmodules[7] for
+       details.
 
 submodule.active::
        A repeated field which contains a pathspec used to match against a
        submodule's path to determine if the submodule is of interest to git
-       commands.
+       commands. See linkgit:gitsubmodules[7] for details.
 
 submodule.recurse::
        Specifies if commands recurse into submodules by default. This
@@ -3480,6 +3523,13 @@ Note that this configuration variable is ignored if it is seen in the
 repository-level config (this is a safety measure against fetching from
 untrusted repositories).
 
+uploadpack.allowRefInWant::
+       If this option is set, `upload-pack` will support the `ref-in-want`
+       feature of the protocol version 2 `fetch` command.  This feature
+       is intended for the benefit of load-balanced servers which may
+       not have the same view of what OIDs their refs point to due to
+       replication delay.
+
 url.<base>.insteadOf::
        Any URL that starts with this value will be rewritten to
        start, instead, with <base>. In cases where some site serves a
index 41064909ee42bca9a5decd375bc62ad658ae3c81..f394608b42c268f0f187dafabe50b718fde2a8bf 100644 (file)
@@ -276,10 +276,14 @@ plain::
        that are added somewhere else in the diff. This mode picks up any
        moved line, but it is not very useful in a review to determine
        if a block of code was moved without permutation.
-zebra::
+blocks::
        Blocks of moved text of at least 20 alphanumeric characters
        are detected greedily. The detected blocks are
-       painted using either the 'color.diff.{old,new}Moved' color or
+       painted using either the 'color.diff.{old,new}Moved' color.
+       Adjacent blocks cannot be told apart.
+zebra::
+       Blocks of moved text are detected as in 'blocks' mode. The blocks
+       are painted using either the 'color.diff.{old,new}Moved' color or
        'color.diff.{old,new}MovedAlternative'. The change between
        the two colors indicates that a new block was detected.
 dimmed_zebra::
@@ -288,6 +292,31 @@ dimmed_zebra::
        blocks are considered interesting, the rest is uninteresting.
 --
 
+--color-moved-ws=<modes>::
+       This configures how white spaces are ignored when performing the
+       move detection for `--color-moved`.
+ifdef::git-diff[]
+       It can be set by the `diff.colorMovedWS` configuration setting.
+endif::git-diff[]
+       These modes can be given as a comma separated list:
++
+--
+ignore-space-at-eol::
+       Ignore changes in whitespace at EOL.
+ignore-space-change::
+       Ignore changes in amount of whitespace.  This ignores whitespace
+       at line end, and considers all other sequences of one or
+       more whitespace characters to be equivalent.
+ignore-all-space::
+       Ignore whitespace when comparing lines. This ignores differences
+       even if one line has whitespace where the other line has none.
+allow-indentation-change::
+       Initially ignore any white spaces in the move detection, then
+       group the moved code blocks only into a block if the change in
+       whitespace is the same per line. This is incompatible with the
+       other modes.
+--
+
 --word-diff[=<mode>]::
        Show a word diff, using the <mode> to delimit changed words.
        By default, words are delimited by whitespace; see
index 78479b003eb3a340da99c533aece8ee9fc19360c..0f9ef2f25e039e2a9ce72988962377439e84590c 100644 (file)
@@ -1103,7 +1103,7 @@ _____________
 Combining test suites, git bisect and other systems together
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-We have seen that test suites an git bisect are very powerful when
+We have seen that test suites and git bisect are very powerful when
 used together. It can be even more powerful if you can combine them
 with other systems.
 
index 02eccbb931eb1ffe6e4c018927eebce8eb98f124..1072ca0eb61e45f5049fedfd24a7512ff81ee957 100644 (file)
@@ -91,7 +91,6 @@ OPTIONS
 -D::
        Shortcut for `--delete --force`.
 
--l::
 --create-reflog::
        Create the branch's reflog.  This activates recording of
        all changes made to the branch ref, enabling use of date
@@ -101,6 +100,8 @@ OPTIONS
        The negated form `--no-create-reflog` only overrides an earlier
        `--create-reflog`, but currently does not negate the setting of
        `core.logAllRefUpdates`.
++
+The `-l` option is a deprecated synonym for `--create-reflog`.
 
 -f::
 --force::
index ca5fc9c79887652e38e3d11dc86dba657fe585c0..9db02928c4634ea07c3950b1431bdf9981ec1ab9 100644 (file)
@@ -38,6 +38,15 @@ equivalent to
 $ git checkout -b <branch> --track <remote>/<branch>
 ------------
 +
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
 You could omit <branch>, in which case the command degenerates to
 "check out the current branch", which is a glorified no-op with
 rather expensive side-effects to show only the tracking information,
index 4c97b555cca5c1daa66c1f44c097ef6a640e0914..dececb79d772447e5ca0d3ea8253f20ea288ddf6 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git commit-graph read' [--object-dir <dir>]
+'git commit-graph verify' [--object-dir <dir>]
 'git commit-graph write' <options> [--object-dir <dir>]
 
 
@@ -37,12 +38,16 @@ Write a commit graph file based on the commits found in packfiles.
 +
 With the `--stdin-packs` option, generate the new commit graph by
 walking objects only in the specified pack-indexes. (Cannot be combined
-with --stdin-commits.)
+with `--stdin-commits` or `--reachable`.)
 +
 With the `--stdin-commits` option, generate the new commit graph by
 walking commits starting at the commits specified in stdin as a list
 of OIDs in hex, one OID per line. (Cannot be combined with
---stdin-packs.)
+`--stdin-packs` or `--reachable`.)
++
+With the `--reachable` option, generate the new commit graph by walking
+commits starting at all refs. (Cannot be combined with `--stdin-commits`
+or `--stdin-packs`.)
 +
 With the `--append` option, include all commits that are present in the
 existing commit-graph file.
@@ -52,6 +57,11 @@ existing commit-graph file.
 Read a graph file given by the commit-graph file and output basic
 details about the graph file. Used for debugging purposes.
 
+'verify'::
+
+Read the commit-graph file and verify its contents against the object
+database. Used to check for corrupted data.
+
 
 EXAMPLES
 --------
index b9f060e3b207f981932957d8148dfcfcd912e33c..ab9a93fb9b8fb7291362cc419a7aca8b464d3d76 100644 (file)
@@ -110,6 +110,9 @@ Any corrupt objects you will have to find in backups or other archives
 (i.e., you can just remove them and do an 'rsync' with some other site in
 the hopes that somebody else has the object you have corrupted).
 
+If core.commitGraph is true, the commit-graph file will also be inspected
+using 'git commit-graph verify'. See linkgit:git-commit-graph[1].
+
 Extracted Diagnostics
 ---------------------
 
index 24b2dd44fe445a66121fa957f0af8e2209a85676..f5bc98ccb3673079fa2e1205b57bc87acb3c1e90 100644 (file)
@@ -136,6 +136,10 @@ The optional configuration variable `gc.packRefs` determines if
 it within all non-bare repos or it can be set to a boolean value.
 This defaults to true.
 
+The optional configuration variable `gc.commitGraph` determines if
+'git gc' should run 'git commit-graph write'. This can be set to a
+boolean value. This defaults to false.
+
 The optional configuration variable `gc.aggressiveWindow` controls how
 much time is spent optimizing the delta compression of the objects in
 the repository when the --aggressive option is specified.  The larger
index 312409a607231d66bd296c1d955bb62c47ecffcf..0de3493b804b3f71fb5c9fc66dbf2a75c6ab0f0c 100644 (file)
@@ -13,7 +13,7 @@ SYNOPSIS
           [-v | --invert-match] [-h|-H] [--full-name]
           [-E | --extended-regexp] [-G | --basic-regexp]
           [-P | --perl-regexp]
-          [-F | --fixed-strings] [-n | --line-number]
+          [-F | --fixed-strings] [-n | --line-number] [--column]
           [-l | --files-with-matches] [-L | --files-without-match]
           [(-O | --open-files-in-pager) [<pager>]]
           [-z | --null]
@@ -44,6 +44,9 @@ CONFIGURATION
 grep.lineNumber::
        If set to true, enable `-n` option by default.
 
+grep.column::
+       If set to true, enable the `--column` option by default.
+
 grep.patternType::
        Set the default matching behavior. Using a value of 'basic', 'extended',
        'fixed', or 'perl' will enable the `--basic-regexp`, `--extended-regexp`,
@@ -169,6 +172,10 @@ providing this option will cause it to die.
 --line-number::
        Prefix the line number to matching lines.
 
+--column::
+       Prefix the 1-indexed byte-offset of the first match from the start of the
+       matching line.
+
 -l::
 --files-with-matches::
 --name-only::
index 032613c420cf3cbcf75e82fc89abed6ec8d85355..7b157441eb3ee9e84e98f36f74a6083f071f36e1 100644 (file)
@@ -68,8 +68,8 @@ imap.tunnel::
        to the server. Required when imap.host is not set.
 
 imap.host::
-       A URL identifying the server. Use a `imap://` prefix for non-secure
-       connections and a `imaps://` prefix for secure connections.
+       A URL identifying the server. Use an `imap://` prefix for non-secure
+       connections and an `imaps://` prefix for secure connections.
        Ignored when imap.tunnel is set, but required otherwise.
 
 imap.user::
index e8dec1b3c89a66dc2f9e78737fa7d243bee85d37..df2b64dbb618e3cf7c538798f5f18ebc41e7eff6 100644 (file)
@@ -199,7 +199,7 @@ OPTIONS
        .git/NOTES_MERGE_REF symref is updated to the resulting commit.
 
 --abort::
-       Abort/reset a in-progress 'git notes merge', i.e. a notes merge
+       Abort/reset an in-progress 'git notes merge', i.e. a notes merge
        with conflicts. This simply removes all files related to the
        notes merge.
 
index 0e20a66e73cf420c83b156d1a0aaa8f60cd4702f..a7850415b1a831eb3d1530dbc71e194b27bd8d59 100644 (file)
@@ -243,11 +243,15 @@ leave out at most one of A and B, in which case it defaults to HEAD.
 --keep-empty::
        Keep the commits that do not change anything from its
        parents in the result.
++
+See also INCOMPATIBLE OPTIONS below.
 
 --allow-empty-message::
        By default, rebasing commits with an empty message will fail.
        This option overrides that behavior, allowing commits with empty
        messages to be rebased.
++
+See also INCOMPATIBLE OPTIONS below.
 
 --skip::
        Restart the rebasing process by skipping the current patch.
@@ -271,6 +275,8 @@ branch on top of the <upstream> branch.  Because of this, when a merge
 conflict happens, the side reported as 'ours' is the so-far rebased
 series, starting with <upstream>, and 'theirs' is the working branch.  In
 other words, the sides are swapped.
++
+See also INCOMPATIBLE OPTIONS below.
 
 -s <strategy>::
 --strategy=<strategy>::
@@ -280,8 +286,10 @@ other words, the sides are swapped.
 +
 Because 'git rebase' replays each commit from the working branch
 on top of the <upstream> branch using the given strategy, using
-the 'ours' strategy simply discards all patches from the <branch>,
+the 'ours' strategy simply empties all patches from the <branch>,
 which makes little sense.
++
+See also INCOMPATIBLE OPTIONS below.
 
 -X <strategy-option>::
 --strategy-option=<strategy-option>::
@@ -289,6 +297,8 @@ which makes little sense.
        This implies `--merge` and, if no strategy has been
        specified, `-s recursive`.  Note the reversal of 'ours' and
        'theirs' as noted above for the `-m` option.
++
+See also INCOMPATIBLE OPTIONS below.
 
 -S[<keyid>]::
 --gpg-sign[=<keyid>]::
@@ -324,17 +334,21 @@ which makes little sense.
        and after each change.  When fewer lines of surrounding
        context exist they all must match.  By default no context is
        ever ignored.
++
+See also INCOMPATIBLE OPTIONS below.
 
--f::
+--no-ff::
 --force-rebase::
-       Force a rebase even if the current branch is up to date and
-       the command without `--force` would return without doing anything.
+-f::
+       Individually replay all rebased commits instead of fast-forwarding
+       over the unchanged ones.  This ensures that the entire history of
+       the rebased branch is composed of new commits.
 +
-You may find this (or --no-ff with an interactive rebase) helpful after
-reverting a topic branch merge, as this option recreates the topic branch with
-fresh commits so it can be remerged successfully without needing to "revert
-the reversion" (see the
-link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details).
+You may find this helpful after reverting a topic branch merge, as this option
+recreates the topic branch with fresh commits so it can be remerged
+successfully without needing to "revert the reversion" (see the
+link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for
+details).
 
 --fork-point::
 --no-fork-point::
@@ -355,19 +369,22 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
 --whitespace=<option>::
        These flag are passed to the 'git apply' program
        (see linkgit:git-apply[1]) that applies the patch.
-       Incompatible with the --interactive option.
++
+See also INCOMPATIBLE OPTIONS below.
 
 --committer-date-is-author-date::
 --ignore-date::
        These flags are passed to 'git am' to easily change the dates
        of the rebased commits (see linkgit:git-am[1]).
-       Incompatible with the --interactive option.
++
+See also INCOMPATIBLE OPTIONS below.
 
 --signoff::
        Add a Signed-off-by: trailer to all the rebased commits. Note
        that if `--interactive` is given then only commits marked to be
-       picked, edited or reworded will have the trailer added. Incompatible
-       with the `--preserve-merges` option.
+       picked, edited or reworded will have the trailer added.
++
+See also INCOMPATIBLE OPTIONS below.
 
 -i::
 --interactive::
@@ -378,6 +395,8 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
 The commit list format can be changed by setting the configuration option
 rebase.instructionFormat.  A customized instruction format will automatically
 have the long commit hash prepended to the format.
++
+See also INCOMPATIBLE OPTIONS below.
 
 -r::
 --rebase-merges[=(rebase-cousins|no-rebase-cousins)]::
@@ -404,7 +423,7 @@ It is currently only possible to recreate the merge commits using the
 `recursive` merge strategy; Different merge strategies can be used only via
 explicit `exec git merge -s <strategy> [...]` commands.
 +
-See also REBASING MERGES below.
+See also REBASING MERGES and INCOMPATIBLE OPTIONS below.
 
 -p::
 --preserve-merges::
@@ -415,6 +434,8 @@ See also REBASING MERGES below.
 This uses the `--interactive` machinery internally, but combining it
 with the `--interactive` option explicitly is generally not a good
 idea unless you know what you are doing (see BUGS below).
++
+See also INCOMPATIBLE OPTIONS below.
 
 -x <cmd>::
 --exec <cmd>::
@@ -437,6 +458,8 @@ squash/fixup series.
 +
 This uses the `--interactive` machinery internally, but it can be run
 without an explicit `--interactive`.
++
+See also INCOMPATIBLE OPTIONS below.
 
 --root::
        Rebase all commits reachable from <branch>, instead of
@@ -447,6 +470,8 @@ without an explicit `--interactive`.
        When used together with both --onto and --preserve-merges,
        'all' root commits will be rewritten to have <newbase> as parent
        instead.
++
+See also INCOMPATIBLE OPTIONS below.
 
 --autosquash::
 --no-autosquash::
@@ -461,11 +486,11 @@ without an explicit `--interactive`.
        too.  The recommended way to create fixup/squash commits is by using
        the `--fixup`/`--squash` options of linkgit:git-commit[1].
 +
-This option is only valid when the `--interactive` option is used.
-+
 If the `--autosquash` option is enabled by default using the
 configuration variable `rebase.autoSquash`, this option can be
 used to override and disable this setting.
++
+See also INCOMPATIBLE OPTIONS below.
 
 --autostash::
 --no-autostash::
@@ -475,17 +500,73 @@ used to override and disable this setting.
        with care: the final stash application after a successful
        rebase might result in non-trivial conflicts.
 
---no-ff::
-       With --interactive, cherry-pick all rebased commits instead of
-       fast-forwarding over the unchanged ones.  This ensures that the
-       entire history of the rebased branch is composed of new commits.
-+
-Without --interactive, this is a synonym for --force-rebase.
-+
-You may find this helpful after reverting a topic branch merge, as this option
-recreates the topic branch with fresh commits so it can be remerged
-successfully without needing to "revert the reversion" (see the
-link:howto/revert-a-faulty-merge.html[revert-a-faulty-merge How-To] for details).
+INCOMPATIBLE OPTIONS
+--------------------
+
+git-rebase has many flags that are incompatible with each other,
+predominantly due to the fact that it has three different underlying
+implementations:
+
+ * one based on linkgit:git-am[1] (the default)
+ * one based on git-merge-recursive (merge backend)
+ * one based on linkgit:git-cherry-pick[1] (interactive backend)
+
+Flags only understood by the am backend:
+
+ * --committer-date-is-author-date
+ * --ignore-date
+ * --whitespace
+ * --ignore-whitespace
+ * -C
+
+Flags understood by both merge and interactive backends:
+
+ * --merge
+ * --strategy
+ * --strategy-option
+ * --allow-empty-message
+
+Flags only understood by the interactive backend:
+
+ * --[no-]autosquash
+ * --rebase-merges
+ * --preserve-merges
+ * --interactive
+ * --exec
+ * --keep-empty
+ * --autosquash
+ * --edit-todo
+ * --root when used in combination with --onto
+
+Other incompatible flag pairs:
+
+ * --preserve-merges and --interactive
+ * --preserve-merges and --signoff
+ * --preserve-merges and --rebase-merges
+ * --rebase-merges and --strategy
+ * --rebase-merges and --strategy-option
+
+BEHAVIORAL DIFFERENCES
+-----------------------
+
+ * empty commits:
+
+    am-based rebase will drop any "empty" commits, whether the
+    commit started empty (had no changes relative to its parent to
+    start with) or ended empty (all changes were already applied
+    upstream in other commits).
+
+    merge-based rebase does the same.
+
+    interactive-based rebase will by default drop commits that
+    started empty and halt if it hits a commit that ended up empty.
+    The `--keep-empty` option exists for interactive rebases to allow
+    it to keep commits that started empty.
+
+  * directory rename detection:
+
+    merge-based and interactive-based rebases work fine with
+    directory rename detection.  am-based rebases sometimes do not.
 
 include::merge-strategies.txt[]
 
@@ -804,7 +885,7 @@ The ripple effect of a "hard case" recovery is especially bad:
 case" recovery too!
 
 REBASING MERGES
------------------
+---------------
 
 The interactive rebase command was originally designed to handle
 individual patch series. As such, it makes sense to exclude merge
index 4f3efde80cd8e4f0b1a5b6699ad3e569f19ca516..465a4ecbeddaa20189bce6e223df8bbfa0319b9a 100644 (file)
@@ -137,15 +137,17 @@ Note that no attempts whatsoever are made to validate the encoding.
        Specify encoding of compose message. Default is the value of the
        'sendemail.composeencoding'; if that is unspecified, UTF-8 is assumed.
 
---transfer-encoding=(7bit|8bit|quoted-printable|base64)::
+--transfer-encoding=(7bit|8bit|quoted-printable|base64|auto)::
        Specify the transfer encoding to be used to send the message over SMTP.
        7bit will fail upon encountering a non-ASCII message.  quoted-printable
        can be useful when the repository contains files that contain carriage
        returns, but makes the raw patch email file (as saved from a MUA) much
        harder to inspect manually.  base64 is even more fool proof, but also
-       even more opaque.  Default is the value of the `sendemail.transferEncoding`
-       configuration value; if that is unspecified, git will use 8bit and not
-       add a Content-Transfer-Encoding header.
+       even more opaque.  auto will use 8bit when possible, and quoted-printable
+       otherwise.
++
+Default is the value of the `sendemail.transferEncoding` configuration
+value; if that is unspecified, default to `auto`.
 
 --xmailer::
 --no-xmailer::
@@ -398,8 +400,11 @@ have been specified, in which case default to 'compose'.
 +
 --
                *       Invoke the sendemail-validate hook if present (see linkgit:githooks[5]).
-               *       Warn of patches that contain lines longer than 998 characters; this
-                       is due to SMTP limits as described by http://www.ietf.org/rfc/rfc2821.txt.
+               *       Warn of patches that contain lines longer than
+                       998 characters unless a suitable transfer encoding
+                       ('auto', 'base64', or 'quoted-printable') is used;
+                       this is due to SMTP limits as described by
+                       http://www.ietf.org/rfc/rfc5322.txt.
 --
 +
 Default is the value of `sendemail.validate`; if this is not set,
index c4467ffb98a95fa6bb5dc86a92dee3317be9c0ea..d9f422d5600af6b112eba692d5c5607cdb4169ef 100644 (file)
@@ -106,7 +106,7 @@ It is optional: it defaults to 'traditional'.
 The possible options are:
 +
        - 'traditional' - Shows ignored files and directories, unless
-                         --untracked-files=all is specifed, in which case
+                         --untracked-files=all is specified, in which case
                          individual files in ignored directories are
                          displayed.
        - 'no'          - Show no ignored files.
index 7ea24fc942c18c032048edc10dfe00b56bff991e..b99029520d9b17e76ab65b4333a79fe20ed22eff 100644 (file)
@@ -452,7 +452,7 @@ Any other arguments are passed directly to 'git log'
 
 'commit-diff'::
        Commits the diff of two tree-ish arguments from the
-       command-line.  This command does not rely on being inside an `git svn
+       command-line.  This command does not rely on being inside a `git svn
        init`-ed repository.  This command takes three arguments, (a) the
        original tree to diff against, (b) the new tree result, (c) the
        URL of the target Subversion repository.  The final argument
index afc6576a14d56ea49e37d1251a5665bf77457f89..9c26be40f4412b5f0a9c478236b9d3c38fcb14ec 100644 (file)
@@ -60,6 +60,15 @@ with a matching name, treat as equivalent to:
 $ git worktree add --track -b <branch> <path> <remote>/<branch>
 ------------
 +
+If the branch exists in multiple remotes and one of them is named by
+the `checkout.defaultRemote` configuration variable, we'll use that
+one for the purposes of disambiguation, even if the `<branch>` isn't
+unique across all remotes. Set it to
+e.g. `checkout.defaultRemote=origin` to always checkout remote
+branches from there if `<branch>` is ambiguous but exists on the
+'origin' remote. See also `checkout.defaultRemote` in
+linkgit:git-config[1].
++
 If `<commit-ish>` is omitted and neither `-b` nor `-B` nor `--detach` used,
 then, as a convenience, the new worktree is associated with a branch
 (call it `<branch>`) named after `$(basename <path>)`.  If `<branch>`
index 10c8ff93c0b9f55ff19a8b54cdeed2fdad93b92b..9f2528fc8c81622c237f7bc80289137d8f4f750b 100644 (file)
@@ -244,7 +244,7 @@ commands in addition to the ones needed by participants.
 
 This section can also be used by those who respond to `git
 request-pull` or pull-request on GitHub (www.github.com) to
-integrate the work of others into their history. An sub-area
+integrate the work of others into their history. A sub-area
 lieutenant for a repository will act both as a participant and
 as an integrator.
 
index ff5d7f9ed6f089646807f93d59e35a10763e46c2..d107daaffd4e84921c47951cf743d1eace11c525 100644 (file)
@@ -7,7 +7,7 @@ gitignore - Specifies intentionally untracked files to ignore
 
 SYNOPSIS
 --------
-$HOME/.config/git/ignore, $GIT_DIR/info/exclude, .gitignore
+$XDG_CONFIG_HOME/git/ignore, $GIT_DIR/info/exclude, .gitignore
 
 DESCRIPTION
 -----------
index 3b9faabdbb9cbc45893dff67fd40b0ed7552f287..504c5f1a885dfb20c37a9f87bdbdbd452cd216bd 100644 (file)
@@ -194,7 +194,7 @@ In the above config only the submodule 'bar' and 'baz' are active,
 
 Note that (c) is a historical artefact and will be ignored if the
 (a) and (b) specify that the submodule is not active. In other words,
-if we have an `submodule.<name>.active` set to `false` or if the
+if we have a `submodule.<name>.active` set to `false` or if the
 submodule's path is excluded in the pathspec in `submodule.active`, the
 url doesn't matter whether it is present or not. This is illustrated in
 the example that follows.
index 6c2d23dc489474958d39d8053fc799b8616aafee..0d2aa48c63856aba78d559445186a9b9e6bddd7d 100644 (file)
@@ -334,7 +334,7 @@ The optional colon that terminates the "magic signature" can be
 omitted if the pattern begins with a character that does not belong to
 "magic signature" symbol set and is not a colon.
 +
-In the long form, the leading colon `:` is followed by a open
+In the long form, the leading colon `:` is followed by an open
 parenthesis `(`, a comma-separated list of zero or more "magic words",
 and a close parentheses `)`, and the remainder is the pattern to match
 against the path.
index 7d1bd440944149bb8de03a9a202e46c85ec646f2..72daa20e76fa0a0c10feaf37f927797ad1445934 100644 (file)
@@ -184,7 +184,8 @@ existing tag object.
   A colon, followed by a slash, followed by a text, names
   a commit whose commit message matches the specified regular expression.
   This name returns the youngest matching commit which is
-  reachable from any ref. The regular expression can match any part of the
+  reachable from any ref, including HEAD.
+  The regular expression can match any part of the
   commit message. To match messages starting with a string, one can use
   e.g. ':/^foo'. The special sequence ':/!' is reserved for modifiers to what
   is matched. ':/!-foo' performs a negative match, while ':/!!foo' matches a
index 4f44ca24f6457e6acf208d7068dfcd02039cf41b..5abb8e8b1fdb37f9948ea2e09bae6380cb11041b 100644 (file)
@@ -54,7 +54,7 @@ The notable options are:
        this case, the contents are returned as individual entries.
 +
 If this is set, files and directories that explicitly match an ignore
-pattern are reported. Implicity ignored directories (directories that
+pattern are reported. Implicitly ignored directories (directories that
 do not match an ignore pattern, but whose contents are all ignored)
 are not reported, instead all of the contents are reported.
 
index e7cbb7c13adf2f7ea3b15279a17f1a06da859cbc..45f0df600fab616688154df39775cbbc0f91ebd3 100644 (file)
@@ -146,7 +146,7 @@ To get the values of all attributes associated with a file:
 
 * Iterate over the `attr_check.items[]` array to examine
   the attribute names and values.  The name of the attribute
-  described by a  `attr_check.items[]` object can be retrieved via
+  described by an `attr_check.items[]` object can be retrieved via
   `git_attr_name(check->items[i].attr)`.  (Please note that no items
   will be returned for unset attributes, so `ATTR_UNSET()` will return
   false for all returned `attr_check.items[]` objects.)
index ad6af8105cf68e9e607cf67032e445094e53daf9..cc0474ba3ee4b04153752340328233437d5c7b05 100644 (file)
@@ -18,9 +18,9 @@ metadata, including:
   the graph file.
 
 These positional references are stored as unsigned 32-bit integers
-corresponding to the array position withing the list of commit OIDs. We
-use the most-significant bit for special purposes, so we can store at most
-(1 << 31) - 1 (around 2 billion) commits.
+corresponding to the array position within the list of commit OIDs. Due
+to some special constants we use to track parents, we can store at most
+(1 << 30) + (1 << 29) + (1 << 28) - 1 (around 1.8 billion) commits.
 
 == Commit graph files have the following format:
 
@@ -70,10 +70,10 @@ CHUNK DATA:
   OID Lookup (ID: {'O', 'I', 'D', 'L'}) (N * H bytes)
       The OIDs for all commits in the graph, sorted in ascending order.
 
-  Commit Data (ID: {'C', 'G', 'E', 'T' }) (N * (H + 16) bytes)
+  Commit Data (ID: {'C', 'D', 'A', 'T' }) (N * (H + 16) bytes)
     * The first H bytes are for the OID of the root tree.
     * The next 8 bytes are for the positions of the first two parents
-      of the ith commit. Stores value 0xffffffff if no parent in that
+      of the ith commit. Stores value 0x7000000 if no parent in that
       position. If there are more than two parents, the second value
       has its most-significant bit on and the other bits store an array
       position into the Large Edge List chunk.
index e1a883eb462cd9bddbc192a315464282146ac433..c664acbd765d06f000b2feeef1d7e98f1616ff62 100644 (file)
@@ -118,9 +118,6 @@ Future Work
 - The commit graph feature currently does not honor commit grafts. This can
   be remedied by duplicating or refactoring the current graft logic.
 
-- The 'commit-graph' subcommand does not have a "verify" mode that is
-  necessary for integration with fsck.
-
 - After computing and storing generation numbers, we must make graph
   walks aware of generation numbers to gain the performance benefits they
   enable. This will mostly be accomplished by swapping a commit-date-ordered
@@ -130,25 +127,6 @@ Future Work
     - 'log --topo-order'
     - 'tag --merged'
 
-- Currently, parse_commit_gently() requires filling in the root tree
-  object for a commit. This passes through lookup_tree() and consequently
-  lookup_object(). Also, it calls lookup_commit() when loading the parents.
-  These method calls check the ODB for object existence, even if the
-  consumer does not need the content. For example, we do not need the
-  tree contents when computing merge bases. Now that commit parsing is
-  removed from the computation time, these lookup operations are the
-  slowest operations keeping graph walks from being fast. Consider
-  loading these objects without verifying their existence in the ODB and
-  only loading them fully when consumers need them. Consider a method
-  such as "ensure_tree_loaded(commit)" that fully loads a tree before
-  using commit->tree.
-
-- The current design uses the 'commit-graph' subcommand to generate the graph.
-  When this feature stabilizes enough to recommend to most users, we should
-  add automatic graph writes to common operations that create many commits.
-  For example, one could compute a graph on 'clone', 'fetch', or 'repack'
-  commands.
-
 - A server could provide a commit graph file as part of the network protocol
   to avoid extra calculations by clients. This feature is only of benefit if
   the user is willing to trust the file, because verifying the file is correct
diff --git a/Documentation/technical/directory-rename-detection.txt b/Documentation/technical/directory-rename-detection.txt
new file mode 100644 (file)
index 0000000..1c0086e
--- /dev/null
@@ -0,0 +1,115 @@
+Directory rename detection
+==========================
+
+Rename detection logic in diffcore-rename that checks for renames of
+individual files is aggregated and analyzed in merge-recursive for cases
+where combinations of renames indicate that a full directory has been
+renamed.
+
+Scope of abilities
+------------------
+
+It is perhaps easiest to start with an example:
+
+  * When all of x/a, x/b and x/c have moved to z/a, z/b and z/c, it is
+    likely that x/d added in the meantime would also want to move to z/d by
+    taking the hint that the entire directory 'x' moved to 'z'.
+
+More interesting possibilities exist, though, such as:
+
+  * one side of history renames x -> z, and the other renames some file to
+    x/e, causing the need for the merge to do a transitive rename.
+
+  * one side of history renames x -> z, but also renames all files within
+    x.  For example, x/a -> z/alpha, x/b -> z/bravo, etc.
+
+  * both 'x' and 'y' being merged into a single directory 'z', with a
+    directory rename being detected for both x->z and y->z.
+
+  * not all files in a directory being renamed to the same location;
+    i.e. perhaps most the files in 'x' are now found under 'z', but a few
+    are found under 'w'.
+
+  * a directory being renamed, which also contained a subdirectory that was
+    renamed to some entirely different location.  (And perhaps the inner
+    directory itself contained inner directories that were renamed to yet
+    other locations).
+
+  * combinations of the above; see t/t6043-merge-rename-directories.sh for
+    various interesting cases.
+
+Limitations -- applicability of directory renames
+-------------------------------------------------
+
+In order to prevent edge and corner cases resulting in either conflicts
+that cannot be represented in the index or which might be too complex for
+users to try to understand and resolve, a couple basic rules limit when
+directory rename detection applies:
+
+  1) If a given directory still exists on both sides of a merge, we do
+     not consider it to have been renamed.
+
+  2) If a subset of to-be-renamed files have a file or directory in the
+     way (or would be in the way of each other), "turn off" the directory
+     rename for those specific sub-paths and report the conflict to the
+     user.
+
+  3) If the other side of history did a directory rename to a path that
+     your side of history renamed away, then ignore that particular
+     rename from the other side of history for any implicit directory
+     renames (but warn the user).
+
+Limitations -- detailed rules and testcases
+-------------------------------------------
+
+t/t6043-merge-rename-directories.sh contains extensive tests and commentary
+which generate and explore the rules listed above.  It also lists a few
+additional rules:
+
+  a) If renames split a directory into two or more others, the directory
+     with the most renames, "wins".
+
+  b) Avoid directory-rename-detection for a path, if that path is the
+     source of a rename on either side of a merge.
+
+  c) Only apply implicit directory renames to directories if the other side
+     of history is the one doing the renaming.
+
+Limitations -- support in different commands
+--------------------------------------------
+
+Directory rename detection is supported by 'merge' and 'cherry-pick'.
+Other git commands which users might be surprised to see limited or no
+directory rename detection support in:
+
+  * diff
+
+    Folks have requested in the past that `git diff` detect directory
+    renames and somehow simplify its output.  It is not clear whether this
+    would be desirable or how the output should be simplified, so this was
+    simply not implemented.  Further, to implement this, directory rename
+    detection logic would need to move from merge-recursive to
+    diffcore-rename.
+
+  * am
+
+    git-am tries to avoid a full three way merge, instead calling
+    git-apply.  That prevents us from detecting renames at all, which may
+    defeat the directory rename detection.  There is a fallback, though; if
+    the initial git-apply fails and the user has specified the -3 option,
+    git-am will fall back to a three way merge.  However, git-am lacks the
+    necessary information to do a "real" three way merge.  Instead, it has
+    to use build_fake_ancestor() to get a merge base that is missing files
+    whose rename may have been important to detect for directory rename
+    detection to function.
+
+  * rebase
+
+    Since am-based rebases work by first generating a bunch of patches
+    (which no longer record what the original commits were and thus don't
+    have the necessary info from which we can find a real merge-base), and
+    then calling git-am, this implies that am-based rebases will not always
+    successfully detect directory renames either (see the 'am' section
+    above).  merged-based rebases (rebase -m) and cherry-pick-based rebases
+    (rebase -i) are not affected by this shortcoming, and fully support
+    directory rename detection.
index 7fee6b780a869fcb9af979fb95a54587002b92f2..508a344cf19fa702219c62b61ef5e04960c246b9 100644 (file)
@@ -284,7 +284,9 @@ information is sent back to the client in the next step.
 The client can optionally request that pack-objects omit various
 objects from the packfile using one of several filtering techniques.
 These are intended for use with partial clone and partial fetch
-operations.  See `rev-list` for possible "filter-spec" values.
+operations. An object that does not meet a filter-spec value is
+omitted unless explicitly requested in a 'want' line. See `rev-list`
+for possible filter-spec values.
 
 Once all the 'want's and 'shallow's (and optional 'deepen') are
 transferred, clients MUST send a flush-pkt, to tell the server side
index 49bda76d231d08173a48593d0910b3cbc934f01c..09e4e0273fd515254b41cf2d36e3b6083de1d4ee 100644 (file)
@@ -64,9 +64,8 @@ When using the http:// or https:// transport a client makes a "smart"
 info/refs request as described in `http-protocol.txt` and requests that
 v2 be used by supplying "version=2" in the `Git-Protocol` header.
 
-   C: Git-Protocol: version=2
-   C:
    C: GET $GIT_URL/info/refs?service=git-upload-pack HTTP/1.0
+   C: Git-Protocol: version=2
 
 A v2 server would reply:
 
@@ -299,12 +298,21 @@ included in the client's request:
        for use with partial clone and partial fetch operations. See
        `rev-list` for possible "filter-spec" values.
 
+If the 'ref-in-want' feature is advertised, the following argument can
+be included in the client's request as well as the potential addition of
+the 'wanted-refs' section in the server's response as explained below.
+
+    want-ref <ref>
+       Indicates to the server that the client wants to retrieve a
+       particular ref, where <ref> is the full name of a ref on the
+       server.
+
 The response of `fetch` is broken into a number of sections separated by
 delimiter packets (0001), with each section beginning with its section
 header.
 
     output = *section
-    section = (acknowledgments | shallow-info | packfile)
+    section = (acknowledgments | shallow-info | wanted-refs | packfile)
              (flush-pkt | delim-pkt)
 
     acknowledgments = PKT-LINE("acknowledgments" LF)
@@ -319,6 +327,10 @@ header.
     shallow = "shallow" SP obj-id
     unshallow = "unshallow" SP obj-id
 
+    wanted-refs = PKT-LINE("wanted-refs" LF)
+                 *PKT-LINE(wanted-ref LF)
+    wanted-ref = obj-id SP refname
+
     packfile = PKT-LINE("packfile" LF)
               *PKT-LINE(%x01-03 *%x00-ff)
 
@@ -379,6 +391,19 @@ header.
        * This section is only included if a packfile section is also
          included in the response.
 
+    wanted-refs section
+       * This section is only included if the client has requested a
+         ref using a 'want-ref' line and if a packfile section is also
+         included in the response.
+
+       * Always begins with the section header "wanted-refs".
+
+       * The server will send a ref listing ("<oid> <refname>") for
+         each reference requested using 'want-ref' lines.
+
+       * The server MUST NOT send any refs which were not requested
+         using 'want-ref' lines.
+
     packfile section
        * This section is only included if the client has sent 'want'
          lines in its request and either requested that no more
index 0cb6590f24acdaec89fa85815e7af584c48ed620..ab5f6037980bfdd92a077f4c196144684da305fe 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -859,6 +859,7 @@ LIB_OBJS += ewah/ewah_bitmap.o
 LIB_OBJS += ewah/ewah_io.o
 LIB_OBJS += ewah/ewah_rlw.o
 LIB_OBJS += exec-cmd.o
+LIB_OBJS += fetch-negotiator.o
 LIB_OBJS += fetch-object.o
 LIB_OBJS += fetch-pack.o
 LIB_OBJS += fsck.o
@@ -891,6 +892,7 @@ LIB_OBJS += merge-blobs.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += mergesort.o
 LIB_OBJS += name-hash.o
+LIB_OBJS += negotiator/default.o
 LIB_OBJS += notes.o
 LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
@@ -2021,8 +2023,9 @@ version.sp version.s version.o: GIT-VERSION-FILE GIT-USER-AGENT
 version.sp version.s version.o: EXTRA_CPPFLAGS = \
        '-DGIT_VERSION="$(GIT_VERSION)"' \
        '-DGIT_USER_AGENT=$(GIT_USER_AGENT_CQ_SQ)' \
-       '-DGIT_BUILT_FROM_COMMIT="$(shell GIT_CEILING_DIRECTORIES=\"$(CURDIR)/..\" \
-               git rev-parse -q --verify HEAD || :)"'
+       '-DGIT_BUILT_FROM_COMMIT="$(shell \
+               GIT_CEILING_DIRECTORIES="$(CURDIR)/.." \
+               git rev-parse -q --verify HEAD 2>/dev/null)"'
 
 $(BUILT_INS): git$X
        $(QUIET_BUILT_IN)$(RM) $@ && \
@@ -2110,7 +2113,7 @@ $(SCRIPT_PERL_GEN): % : %.perl GIT-PERL-DEFINES GIT-PERL-HEADER GIT-VERSION-FILE
        $(QUIET_GEN)$(RM) $@ $@+ && \
        sed -e '1{' \
            -e '        s|#!.*perl|#!$(PERL_PATH_SQ)|' \
-           -e '        rGIT-PERL-HEADER' \
+           -e '        r GIT-PERL-HEADER' \
            -e '        G' \
            -e '}' \
            -e 's/@@GIT_VERSION@@/$(GIT_VERSION)/g' \
index 52aa85bdfd9e9054c24445f259125bc4b81be038..3561cd64e9dab0a5b0c52d117253f37a5926f9c7 100644 (file)
--- a/advice.c
+++ b/advice.c
@@ -23,6 +23,7 @@ int advice_add_embedded_repo = 1;
 int advice_ignored_hook = 1;
 int advice_waiting_for_editor = 1;
 int advice_graft_file_deprecated = 1;
+int advice_checkout_ambiguous_remote_branch_name = 1;
 
 static int advice_use_color = -1;
 static char advice_colors[][COLOR_MAXLEN] = {
@@ -75,6 +76,7 @@ static struct {
        { "ignoredHook", &advice_ignored_hook },
        { "waitingForEditor", &advice_waiting_for_editor },
        { "graftFileDeprecated", &advice_graft_file_deprecated },
+       { "checkoutAmbiguousRemoteBranchName", &advice_checkout_ambiguous_remote_branch_name },
 
        /* make this an alias for backward compatibility */
        { "pushNonFastForward", &advice_push_update_rejected }
index 7e9377864f8fca1051ce3fd27f3def62b8d234eb..ab24df0fd0d0c739f6f58bb2650bb4162ef4c7f2 100644 (file)
--- a/advice.h
+++ b/advice.h
@@ -23,6 +23,7 @@ extern int advice_add_embedded_repo;
 extern int advice_ignored_hook;
 extern int advice_waiting_for_editor;
 extern int advice_graft_file_deprecated;
+extern int advice_checkout_ambiguous_remote_branch_name;
 
 int git_default_advice_config(const char *var, const char *value);
 __attribute__((format (printf, 1, 2)))
diff --git a/apply.c b/apply.c
index 23a0f25ded853c29c3a6e891399dc5c1c2d8a753..49752cde44df6f0853051abacf8546ab92ee4d7a 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -4056,7 +4056,7 @@ static int preimage_oid_in_gitlink_patch(struct patch *p, struct object_id *oid)
        return get_oid_hex(p->old_sha1_prefix, oid);
 }
 
-/* Build an index that contains the just the files needed for a 3way merge */
+/* Build an index that contains just the files needed for a 3way merge */
 static int build_fake_ancestor(struct apply_state *state, struct patch *list)
 {
        struct patch *patch;
index 921d127f29257facb5942e7b03304a69bafff175..5c93d169dd431b666b25086ac4bb5303e8b26309 100644 (file)
@@ -1002,13 +1002,13 @@ parse_done:
                                    nth_line_cb, &sb, lno, anchor,
                                    &bottom, &top, sb.path))
                        usage(blame_usage);
-               if (lno < top || ((lno || bottom) && lno < bottom))
+               if ((!lno && (top || bottom)) || lno < bottom)
                        die(Q_("file %s has only %lu line",
                               "file %s has only %lu lines",
                               lno), path, lno);
                if (bottom < 1)
                        bottom = 1;
-               if (top < 1)
+               if (top < 1 || lno < top)
                        top = lno;
                bottom--;
                range_set_append_unsafe(&ranges, bottom, top);
@@ -1071,7 +1071,9 @@ parse_done:
                find_alignment(&sb, &output_option);
                if (!*repeated_meta_color &&
                    (output_option & OUTPUT_COLOR_LINE))
-                       strcpy(repeated_meta_color, GIT_COLOR_CYAN);
+                       xsnprintf(repeated_meta_color,
+                                 sizeof(repeated_meta_color),
+                                 "%s", GIT_COLOR_CYAN);
        }
        if (output_option & OUTPUT_ANNOTATE_COMPAT)
                output_option &= ~(OUTPUT_COLOR_LINE | OUTPUT_SHOW_AGE_WITH_COLOR);
index a50632fb23f52e3bad6de8a8e225498f4071fc58..4fc55c3508c2fff3e92e6235ed35ba582caf4b15 100644 (file)
@@ -37,6 +37,7 @@ static const char * const builtin_branch_usage[] = {
 
 static const char *head;
 static struct object_id head_oid;
+static int used_deprecated_reflog_option;
 
 static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
@@ -569,6 +570,14 @@ static int edit_branch_description(const char *branch_name)
        return 0;
 }
 
+static int deprecated_reflog_option_cb(const struct option *opt,
+                                      const char *arg, int unset)
+{
+       used_deprecated_reflog_option = 1;
+       *(int *)opt->value = !unset;
+       return 0;
+}
+
 int cmd_branch(int argc, const char **argv, const char *prefix)
 {
        int delete = 0, rename = 0, copy = 0, force = 0, list = 0;
@@ -611,7 +620,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_BIT('c', "copy", &copy, N_("copy a branch and its reflog"), 1),
                OPT_BIT('C', NULL, &copy, N_("copy a branch, even if target exists"), 2),
                OPT_BOOL(0, "list", &list, N_("list branch names")),
-               OPT_BOOL('l', "create-reflog", &reflog, N_("create the branch's reflog")),
+               OPT_BOOL(0, "create-reflog", &reflog, N_("create the branch's reflog")),
+               {
+                       OPTION_CALLBACK, 'l', NULL, &reflog, NULL,
+                       N_("deprecated synonym for --create-reflog"),
+                       PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
+                       deprecated_reflog_option_cb
+               },
                OPT_BOOL(0, "edit-description", &edit_description,
                         N_("edit the description for the branch")),
                OPT__FORCE(&force, N_("force creation, move/rename, deletion"), PARSE_OPT_NOCOMPLETE),
@@ -684,6 +699,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (list)
                setup_auto_pager("branch", 1);
 
+       if (used_deprecated_reflog_option && !list) {
+               warning("the '-l' alias for '--create-reflog' is deprecated;");
+               warning("it will be removed in a future version of Git");
+       }
+
        if (delete) {
                if (!argc)
                        die(_("branch name required"));
index 40c27bf54d7049e830f1628bd83dddfd86dd137a..923b8cbd8240e16bad5adace670b3b63b9c02768 100644 (file)
@@ -23,6 +23,7 @@
 #include "resolve-undo.h"
 #include "submodule-config.h"
 #include "submodule.h"
+#include "advice.h"
 
 static const char * const checkout_usage[] = {
        N_("git checkout [<options>] <branch>"),
@@ -879,7 +880,8 @@ static int parse_branchname_arg(int argc, const char **argv,
                                int dwim_new_local_branch_ok,
                                struct branch_info *new_branch_info,
                                struct checkout_opts *opts,
-                               struct object_id *rev)
+                               struct object_id *rev,
+                               int *dwim_remotes_matched)
 {
        struct tree **source_tree = &opts->source_tree;
        const char **new_branch = &opts->new_branch;
@@ -911,8 +913,10 @@ static int parse_branchname_arg(int argc, const char **argv,
         *   (b) If <something> is _not_ a commit, either "--" is present
         *       or <something> is not a path, no -t or -b was given, and
         *       and there is a tracking branch whose name is <something>
-        *       in one and only one remote, then this is a short-hand to
-        *       fork local <something> from that remote-tracking branch.
+        *       in one and only one remote (or if the branch exists on the
+        *       remote named in checkout.defaultRemote), then this is a
+        *       short-hand to fork local <something> from that
+        *       remote-tracking branch.
         *
         *   (c) Otherwise, if "--" is present, treat it like case (1).
         *
@@ -973,7 +977,8 @@ static int parse_branchname_arg(int argc, const char **argv,
                        recover_with_dwim = 0;
 
                if (recover_with_dwim) {
-                       const char *remote = unique_tracking_name(arg, rev);
+                       const char *remote = unique_tracking_name(arg, rev,
+                                                                 dwim_remotes_matched);
                        if (remote) {
                                *new_branch = arg;
                                arg = remote;
@@ -1110,6 +1115,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        struct branch_info new_branch_info;
        char *conflict_style = NULL;
        int dwim_new_local_branch = 1;
+       int dwim_remotes_matched = 0;
        struct option options[] = {
                OPT__QUIET(&opts.quiet, N_("suppress progress reporting")),
                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -1222,7 +1228,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                        opts.track == BRANCH_TRACK_UNSPECIFIED &&
                        !opts.new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
-                                            &new_branch_info, &opts, &rev);
+                                            &new_branch_info, &opts, &rev,
+                                            &dwim_remotes_matched);
                argv += n;
                argc -= n;
        }
@@ -1264,8 +1271,26 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
        }
 
        UNLEAK(opts);
-       if (opts.patch_mode || opts.pathspec.nr)
-               return checkout_paths(&opts, new_branch_info.name);
-       else
+       if (opts.patch_mode || opts.pathspec.nr) {
+               int ret = checkout_paths(&opts, new_branch_info.name);
+               if (ret && dwim_remotes_matched > 1 &&
+                   advice_checkout_ambiguous_remote_branch_name)
+                       advise(_("'%s' matched more than one remote tracking branch.\n"
+                                "We found %d remotes with a reference that matched. So we fell back\n"
+                                "on trying to resolve the argument as a path, but failed there too!\n"
+                                "\n"
+                                "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
+                                "you can do so by fully qualifying the name with the --track option:\n"
+                                "\n"
+                                "    git checkout --track origin/<name>\n"
+                                "\n"
+                                "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
+                                "one remote, e.g. the 'origin' remote, consider setting\n"
+                                "checkout.defaultRemote=origin in your config."),
+                              argv[0],
+                              dwim_remotes_matched);
+               return ret;
+       } else {
                return checkout_branch(&opts, &new_branch_info);
+       }
 }
index 4b3b48ee84ac59a546038a0dd9a84521e96e3cd7..9ebb5acf56c5e03f8acafda4d806d6e3a41ee91f 100644 (file)
@@ -1157,7 +1157,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        }
 
                if (!is_local && !complete_refs_before_fetch)
-                       transport_fetch_refs(transport, mapped_refs);
+                       transport_fetch_refs(transport, mapped_refs, NULL);
 
                remote_head = find_ref_by_name(refs, "HEAD");
                remote_head_points_at =
@@ -1199,11 +1199,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        if (is_local)
                clone_local(path, git_dir);
        else if (refs && complete_refs_before_fetch)
-               transport_fetch_refs(transport, mapped_refs);
+               transport_fetch_refs(transport, mapped_refs, NULL);
 
        update_remote_refs(refs, mapped_refs, remote_head_points_at,
                           branch_top.buf, reflog_msg.buf, transport,
-                          !is_local && !filter_options.choice);
+                          !is_local);
 
        update_head(our_head_points_at, remote_head, reflog_msg.buf);
 
index 37420ae0fde81ea2e957bedd4949a2eba0cd6ff9..c7d0db5ab4b668618720eb30cffad904f0c57b14 100644 (file)
@@ -3,12 +3,19 @@
 #include "dir.h"
 #include "lockfile.h"
 #include "parse-options.h"
+#include "repository.h"
 #include "commit-graph.h"
 
 static char const * const builtin_commit_graph_usage[] = {
        N_("git commit-graph [--object-dir <objdir>]"),
        N_("git commit-graph read [--object-dir <objdir>]"),
-       N_("git commit-graph write [--object-dir <objdir>] [--append] [--stdin-packs|--stdin-commits]"),
+       N_("git commit-graph verify [--object-dir <objdir>]"),
+       N_("git commit-graph write [--object-dir <objdir>] [--append] [--reachable|--stdin-packs|--stdin-commits]"),
+       NULL
+};
+
+static const char * const builtin_commit_graph_verify_usage[] = {
+       N_("git commit-graph verify [--object-dir <objdir>]"),
        NULL
 };
 
@@ -18,17 +25,48 @@ static const char * const builtin_commit_graph_read_usage[] = {
 };
 
 static const char * const builtin_commit_graph_write_usage[] = {
-       N_("git commit-graph write [--object-dir <objdir>] [--append] [--stdin-packs|--stdin-commits]"),
+       N_("git commit-graph write [--object-dir <objdir>] [--append] [--reachable|--stdin-packs|--stdin-commits]"),
        NULL
 };
 
 static struct opts_commit_graph {
        const char *obj_dir;
+       int reachable;
        int stdin_packs;
        int stdin_commits;
        int append;
 } opts;
 
+
+static int graph_verify(int argc, const char **argv)
+{
+       struct commit_graph *graph = NULL;
+       char *graph_name;
+
+       static struct option builtin_commit_graph_verify_options[] = {
+               OPT_STRING(0, "object-dir", &opts.obj_dir,
+                          N_("dir"),
+                          N_("The object directory to store the graph")),
+               OPT_END(),
+       };
+
+       argc = parse_options(argc, argv, NULL,
+                            builtin_commit_graph_verify_options,
+                            builtin_commit_graph_verify_usage, 0);
+
+       if (!opts.obj_dir)
+               opts.obj_dir = get_object_directory();
+
+       graph_name = get_commit_graph_filename(opts.obj_dir);
+       graph = load_commit_graph_one(graph_name);
+       FREE_AND_NULL(graph_name);
+
+       if (!graph)
+               return 0;
+
+       return verify_commit_graph(the_repository, graph);
+}
+
 static int graph_read(int argc, const char **argv)
 {
        struct commit_graph *graph = NULL;
@@ -51,8 +89,11 @@ static int graph_read(int argc, const char **argv)
        graph_name = get_commit_graph_filename(opts.obj_dir);
        graph = load_commit_graph_one(graph_name);
 
-       if (!graph)
+       if (!graph) {
+               UNLEAK(graph_name);
                die("graph file %s does not exist", graph_name);
+       }
+
        FREE_AND_NULL(graph_name);
 
        printf("header: %08x %d %d %d %d\n",
@@ -79,18 +120,16 @@ static int graph_read(int argc, const char **argv)
 
 static int graph_write(int argc, const char **argv)
 {
-       const char **pack_indexes = NULL;
-       int packs_nr = 0;
-       const char **commit_hex = NULL;
-       int commits_nr = 0;
-       const char **lines = NULL;
-       int lines_nr = 0;
-       int lines_alloc = 0;
+       struct string_list *pack_indexes = NULL;
+       struct string_list *commit_hex = NULL;
+       struct string_list lines;
 
        static struct option builtin_commit_graph_write_options[] = {
                OPT_STRING(0, "object-dir", &opts.obj_dir,
                        N_("dir"),
                        N_("The object directory to store the graph")),
+               OPT_BOOL(0, "reachable", &opts.reachable,
+                       N_("start walk at all refs")),
                OPT_BOOL(0, "stdin-packs", &opts.stdin_packs,
                        N_("scan pack-indexes listed by stdin for commits")),
                OPT_BOOL(0, "stdin-commits", &opts.stdin_commits,
@@ -104,39 +143,35 @@ static int graph_write(int argc, const char **argv)
                             builtin_commit_graph_write_options,
                             builtin_commit_graph_write_usage, 0);
 
-       if (opts.stdin_packs && opts.stdin_commits)
-               die(_("cannot use both --stdin-commits and --stdin-packs"));
+       if (opts.reachable + opts.stdin_packs + opts.stdin_commits > 1)
+               die(_("use at most one of --reachable, --stdin-commits, or --stdin-packs"));
        if (!opts.obj_dir)
                opts.obj_dir = get_object_directory();
 
+       if (opts.reachable) {
+               write_commit_graph_reachable(opts.obj_dir, opts.append);
+               return 0;
+       }
+
+       string_list_init(&lines, 0);
        if (opts.stdin_packs || opts.stdin_commits) {
                struct strbuf buf = STRBUF_INIT;
-               lines_nr = 0;
-               lines_alloc = 128;
-               ALLOC_ARRAY(lines, lines_alloc);
-
-               while (strbuf_getline(&buf, stdin) != EOF) {
-                       ALLOC_GROW(lines, lines_nr + 1, lines_alloc);
-                       lines[lines_nr++] = strbuf_detach(&buf, NULL);
-               }
-
-               if (opts.stdin_packs) {
-                       pack_indexes = lines;
-                       packs_nr = lines_nr;
-               }
-               if (opts.stdin_commits) {
-                       commit_hex = lines;
-                       commits_nr = lines_nr;
-               }
+
+               while (strbuf_getline(&buf, stdin) != EOF)
+                       string_list_append(&lines, strbuf_detach(&buf, NULL));
+
+               if (opts.stdin_packs)
+                       pack_indexes = &lines;
+               if (opts.stdin_commits)
+                       commit_hex = &lines;
        }
 
        write_commit_graph(opts.obj_dir,
                           pack_indexes,
-                          packs_nr,
                           commit_hex,
-                          commits_nr,
                           opts.append);
 
+       string_list_clear(&lines, 0);
        return 0;
 }
 
@@ -162,6 +197,8 @@ int cmd_commit_graph(int argc, const char **argv, const char *prefix)
        if (argc > 0) {
                if (!strcmp(argv[0], "read"))
                        return graph_read(argc, argv);
+               if (!strcmp(argv[0], "verify"))
+                       return graph_verify(argc, argv);
                if (!strcmp(argv[0], "write"))
                        return graph_write(argc, argv);
        }
index b29d26dede79b9a5c46bdc4f951b22e7b88310b3..2c93a289a7255c69260a806cc064f00170453531 100644 (file)
@@ -67,7 +67,7 @@ static int show_origin;
        { OPTION_CALLBACK, (s), (l), (v), NULL, (h), PARSE_OPT_NOARG | \
        PARSE_OPT_NONEG, option_parse_type, (i) }
 
-static struct option builtin_config_options[];
+static NORETURN void usage_builtin_config(void);
 
 static int option_parse_type(const struct option *opt, const char *arg,
                             int unset)
@@ -111,8 +111,7 @@ static int option_parse_type(const struct option *opt, const char *arg,
                 * --type=int'.
                 */
                error("only one type at a time.");
-               usage_with_options(builtin_config_usage,
-                       builtin_config_options);
+               usage_builtin_config();
        }
        *to_type = new_type;
 
@@ -157,11 +156,16 @@ static struct option builtin_config_options[] = {
        OPT_END(),
 };
 
+static NORETURN void usage_builtin_config(void)
+{
+       usage_with_options(builtin_config_usage, builtin_config_options);
+}
+
 static void check_argc(int argc, int min, int max) {
        if (argc >= min && argc <= max)
                return;
        error("wrong number of arguments");
-       usage_with_options(builtin_config_usage, builtin_config_options);
+       usage_builtin_config();
 }
 
 static void show_config_origin(struct strbuf *buf)
@@ -596,7 +600,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        if (use_global_config + use_system_config + use_local_config +
            !!given_config_source.file + !!given_config_source.blob > 1) {
                error("only one config file at a time.");
-               usage_with_options(builtin_config_usage, builtin_config_options);
+               usage_builtin_config();
        }
 
        if (use_local_config && nongit)
@@ -660,12 +664,12 @@ int cmd_config(int argc, const char **argv, const char *prefix)
 
        if ((actions & (ACTION_GET_COLOR|ACTION_GET_COLORBOOL)) && type) {
                error("--get-color and variable type are incoherent");
-               usage_with_options(builtin_config_usage, builtin_config_options);
+               usage_builtin_config();
        }
 
        if (HAS_MULTI_BITS(actions)) {
                error("only one action at a time.");
-               usage_with_options(builtin_config_usage, builtin_config_options);
+               usage_builtin_config();
        }
        if (actions == 0)
                switch (argc) {
@@ -673,25 +677,24 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                case 2: actions = ACTION_SET; break;
                case 3: actions = ACTION_SET_ALL; break;
                default:
-                       usage_with_options(builtin_config_usage, builtin_config_options);
+                       usage_builtin_config();
                }
        if (omit_values &&
            !(actions == ACTION_LIST || actions == ACTION_GET_REGEXP)) {
                error("--name-only is only applicable to --list or --get-regexp");
-               usage_with_options(builtin_config_usage, builtin_config_options);
+               usage_builtin_config();
        }
 
        if (show_origin && !(actions &
                (ACTION_GET|ACTION_GET_ALL|ACTION_GET_REGEXP|ACTION_LIST))) {
                error("--show-origin is only applicable to --get, --get-all, "
                          "--get-regexp, and --list.");
-               usage_with_options(builtin_config_usage, builtin_config_options);
+               usage_builtin_config();
        }
 
        if (default_value && !(actions & ACTION_GET)) {
                error("--default is only applicable to --get");
-               usage_with_options(builtin_config_usage,
-                       builtin_config_options);
+               usage_builtin_config();
        }
 
        if (actions & PAGING_ACTIONS)
index f5d960baecf6c901ca656370308ebebd8dbcdb76..c3c58a545349e33d73aa9f65c60f414a06c06ee4 100644 (file)
@@ -94,19 +94,6 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
        return git_default_config(k, v, cb);
 }
 
-static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
-{
-       if (!strcmp(var, "submodule.fetchjobs")) {
-               max_children = parse_submodule_fetchjobs(var, value);
-               return 0;
-       } else if (!strcmp(var, "fetch.recursesubmodules")) {
-               recurse_submodules = parse_fetch_recurse_submodules_arg(var, value);
-               return 0;
-       }
-
-       return 0;
-}
-
 static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
 {
        /*
@@ -255,9 +242,9 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
        return 0;
 }
 
-static void find_non_local_tags(struct transport *transport,
-                       struct ref **head,
-                       struct ref ***tail)
+static void find_non_local_tags(const struct ref *refs,
+                               struct ref **head,
+                               struct ref ***tail)
 {
        struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct string_list remote_refs = STRING_LIST_INIT_NODUP;
@@ -265,7 +252,7 @@ static void find_non_local_tags(struct transport *transport,
        struct string_list_item *item = NULL;
 
        for_each_ref(add_existing, &existing_refs);
-       for (ref = transport_get_remote_refs(transport, NULL); ref; ref = ref->next) {
+       for (ref = refs; ref; ref = ref->next) {
                if (!starts_with(ref->name, "refs/tags/"))
                        continue;
 
@@ -339,7 +326,8 @@ static void find_non_local_tags(struct transport *transport,
        string_list_clear(&remote_refs, 0);
 }
 
-static struct ref *get_ref_map(struct transport *transport,
+static struct ref *get_ref_map(struct remote *remote,
+                              const struct ref *remote_refs,
                               struct refspec *rs,
                               int tags, int *autotags)
 {
@@ -347,26 +335,11 @@ static struct ref *get_ref_map(struct transport *transport,
        struct ref *rm;
        struct ref *ref_map = NULL;
        struct ref **tail = &ref_map;
-       struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
 
        /* opportunistically-updated references: */
        struct ref *orefs = NULL, **oref_tail = &orefs;
 
-       const struct ref *remote_refs;
-
-       if (rs->nr)
-               refspec_ref_prefixes(rs, &ref_prefixes);
-       else if (transport->remote && transport->remote->fetch.nr)
-               refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
-
-       if (ref_prefixes.argc &&
-           (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) {
-               argv_array_push(&ref_prefixes, "refs/tags/");
-       }
-
-       remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
-
-       argv_array_clear(&ref_prefixes);
+       struct string_list existing_refs = STRING_LIST_INIT_DUP;
 
        if (rs->nr) {
                struct refspec *fetch_refspec;
@@ -403,7 +376,7 @@ static struct ref *get_ref_map(struct transport *transport,
                if (refmap.nr)
                        fetch_refspec = &refmap;
                else
-                       fetch_refspec = &transport->remote->fetch;
+                       fetch_refspec = &remote->fetch;
 
                for (i = 0; i < fetch_refspec->nr; i++)
                        get_fetch_map(ref_map, &fetch_refspec->items[i], &oref_tail, 1);
@@ -411,7 +384,6 @@ static struct ref *get_ref_map(struct transport *transport,
                die("--refmap option is only meaningful with command-line refspec(s).");
        } else {
                /* Use the defaults */
-               struct remote *remote = transport->remote;
                struct branch *branch = branch_get(NULL);
                int has_merge = branch_has_merge_config(branch);
                if (remote &&
@@ -450,7 +422,7 @@ static struct ref *get_ref_map(struct transport *transport,
                /* also fetch all tags */
                get_fetch_map(remote_refs, tag_refspec, &tail, 0);
        else if (tags == TAGS_DEFAULT && *autotags)
-               find_non_local_tags(transport, &ref_map, &tail);
+               find_non_local_tags(remote_refs, &ref_map, &tail);
 
        /* Now append any refs to be updated opportunistically: */
        *tail = orefs;
@@ -459,7 +431,23 @@ static struct ref *get_ref_map(struct transport *transport,
                tail = &rm->next;
        }
 
-       return ref_remove_duplicates(ref_map);
+       ref_map = ref_remove_duplicates(ref_map);
+
+       for_each_ref(add_existing, &existing_refs);
+       for (rm = ref_map; rm; rm = rm->next) {
+               if (rm->peer_ref) {
+                       struct string_list_item *peer_item =
+                               string_list_lookup(&existing_refs,
+                                                  rm->peer_ref->name);
+                       if (peer_item) {
+                               struct object_id *old_oid = peer_item->util;
+                               oidcpy(&rm->peer_ref->old_oid, old_oid);
+                       }
+               }
+       }
+       string_list_clear(&existing_refs, 1);
+
+       return ref_map;
 }
 
 #define STORE_REF_ERROR_OTHER 1
@@ -771,7 +759,7 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
 }
 
 static int store_updated_refs(const char *raw_url, const char *remote_name,
-               struct ref *ref_map)
+                             int connectivity_checked, struct ref *ref_map)
 {
        FILE *fp;
        struct commit *commit;
@@ -793,10 +781,12 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        else
                url = xstrdup("foreign");
 
-       rm = ref_map;
-       if (check_connected(iterate_ref_map, &rm, NULL)) {
-               rc = error(_("%s did not send all necessary objects\n"), url);
-               goto abort;
+       if (!connectivity_checked) {
+               rm = ref_map;
+               if (check_connected(iterate_ref_map, &rm, NULL)) {
+                       rc = error(_("%s did not send all necessary objects\n"), url);
+                       goto abort;
+               }
        }
 
        prepare_format_display(ref_map);
@@ -949,15 +939,32 @@ static int quickfetch(struct ref *ref_map)
        return check_connected(iterate_ref_map, &rm, &opt);
 }
 
-static int fetch_refs(struct transport *transport, struct ref *ref_map)
+static int fetch_refs(struct transport *transport, struct ref *ref_map,
+                     struct ref **updated_remote_refs)
 {
        int ret = quickfetch(ref_map);
        if (ret)
-               ret = transport_fetch_refs(transport, ref_map);
+               ret = transport_fetch_refs(transport, ref_map,
+                                          updated_remote_refs);
        if (!ret)
-               ret |= store_updated_refs(transport->url,
-                               transport->remote->name,
-                               ref_map);
+               /*
+                * Keep the new pack's ".keep" file around to allow the caller
+                * time to update refs to reference the new objects.
+                */
+               return 0;
+       transport_unlock_pack(transport);
+       return ret;
+}
+
+/* Update local refs based on the ref values fetched from a remote */
+static int consume_refs(struct transport *transport, struct ref *ref_map)
+{
+       int connectivity_checked = transport->smart_options
+               ? transport->smart_options->connectivity_checked : 0;
+       int ret = store_updated_refs(transport->url,
+                                    transport->remote->name,
+                                    connectivity_checked,
+                                    ref_map);
        transport_unlock_pack(transport);
        return ret;
 }
@@ -1103,7 +1110,8 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
        transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, NULL);
        transport_set_option(transport, TRANS_OPT_DEPTH, "0");
        transport_set_option(transport, TRANS_OPT_DEEPEN_RELATIVE, NULL);
-       fetch_refs(transport, ref_map);
+       if (!fetch_refs(transport, ref_map, NULL))
+               consume_refs(transport, ref_map);
 
        if (gsecondary) {
                transport_disconnect(gsecondary);
@@ -1114,13 +1122,12 @@ static void backfill_tags(struct transport *transport, struct ref *ref_map)
 static int do_fetch(struct transport *transport,
                    struct refspec *rs)
 {
-       struct string_list existing_refs = STRING_LIST_INIT_DUP;
        struct ref *ref_map;
-       struct ref *rm;
        int autotags = (transport->remote->fetch_tags == 1);
        int retcode = 0;
-
-       for_each_ref(add_existing, &existing_refs);
+       const struct ref *remote_refs;
+       struct ref *updated_remote_refs = NULL;
+       struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
 
        if (tags == TAGS_DEFAULT) {
                if (transport->remote->fetch_tags == 2)
@@ -1136,22 +1143,24 @@ static int do_fetch(struct transport *transport,
                        goto cleanup;
        }
 
-       ref_map = get_ref_map(transport, rs, tags, &autotags);
-       if (!update_head_ok)
-               check_not_current_branch(ref_map);
+       if (rs->nr)
+               refspec_ref_prefixes(rs, &ref_prefixes);
+       else if (transport->remote && transport->remote->fetch.nr)
+               refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
 
-       for (rm = ref_map; rm; rm = rm->next) {
-               if (rm->peer_ref) {
-                       struct string_list_item *peer_item =
-                               string_list_lookup(&existing_refs,
-                                                  rm->peer_ref->name);
-                       if (peer_item) {
-                               struct object_id *old_oid = peer_item->util;
-                               oidcpy(&rm->peer_ref->old_oid, old_oid);
-                       }
-               }
+       if (ref_prefixes.argc &&
+           (tags == TAGS_SET || (tags == TAGS_DEFAULT && !rs->nr))) {
+               argv_array_push(&ref_prefixes, "refs/tags/");
        }
 
+       remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+       argv_array_clear(&ref_prefixes);
+
+       ref_map = get_ref_map(transport->remote, remote_refs, rs,
+                             tags, &autotags);
+       if (!update_head_ok)
+               check_not_current_branch(ref_map);
+
        if (tags == TAGS_DEFAULT && autotags)
                transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
        if (prune) {
@@ -1168,7 +1177,24 @@ static int do_fetch(struct transport *transport,
                                   transport->url);
                }
        }
-       if (fetch_refs(transport, ref_map)) {
+
+       if (fetch_refs(transport, ref_map, &updated_remote_refs)) {
+               free_refs(ref_map);
+               retcode = 1;
+               goto cleanup;
+       }
+       if (updated_remote_refs) {
+               /*
+                * Regenerate ref_map using the updated remote refs.  This is
+                * to account for additional information which may be provided
+                * by the transport (e.g. shallow info).
+                */
+               free_refs(ref_map);
+               ref_map = get_ref_map(transport->remote, updated_remote_refs, rs,
+                                     tags, &autotags);
+               free_refs(updated_remote_refs);
+       }
+       if (consume_refs(transport, ref_map)) {
                free_refs(ref_map);
                retcode = 1;
                goto cleanup;
@@ -1180,14 +1206,13 @@ static int do_fetch(struct transport *transport,
        if (tags == TAGS_DEFAULT && autotags) {
                struct ref **tail = &ref_map;
                ref_map = NULL;
-               find_non_local_tags(transport, &ref_map, &tail);
+               find_non_local_tags(remote_refs, &ref_map, &tail);
                if (ref_map)
                        backfill_tags(transport, ref_map);
                free_refs(ref_map);
        }
 
  cleanup:
-       string_list_clear(&existing_refs, 1);
        return retcode;
 }
 
@@ -1437,7 +1462,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        for (i = 1; i < argc; i++)
                strbuf_addf(&default_rla, " %s", argv[i]);
 
-       config_from_gitmodules(gitmodules_fetch_config, NULL);
+       fetch_config_from_gitmodules(&max_children, &recurse_submodules);
        git_config(git_fetch_config, NULL);
 
        argc = parse_options(argc, argv, prefix,
index ff165c0fcd209f1bab596905a61bdfa6b491208a..ca9206fbbe1c2d9da706111a8de31396b3bf5909 100644 (file)
@@ -110,14 +110,15 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
        struct string_list_item *item;
        int pulling_head = 0;
        struct object_id oid;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
-       if (len < GIT_SHA1_HEXSZ + 3 || line[GIT_SHA1_HEXSZ] != '\t')
+       if (len < hexsz + 3 || line[hexsz] != '\t')
                return 1;
 
-       if (starts_with(line + GIT_SHA1_HEXSZ + 1, "not-for-merge"))
+       if (starts_with(line + hexsz + 1, "not-for-merge"))
                return 0;
 
-       if (line[GIT_SHA1_HEXSZ + 1] != '\t')
+       if (line[hexsz + 1] != '\t')
                return 2;
 
        i = get_oid_hex(line, &oid);
@@ -132,7 +133,7 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
 
        if (line[len - 1] == '\n')
                line[len - 1] = 0;
-       line += GIT_SHA1_HEXSZ + 2;
+       line += hexsz + 2;
 
        /*
         * At this point, line points at the beginning of comment e.g.
@@ -346,7 +347,7 @@ static void shortlog(const char *name,
 
        branch = deref_tag(the_repository, parse_object(the_repository, oid),
                           oid_to_hex(oid),
-                          GIT_SHA1_HEXSZ);
+                          the_hash_algo->hexsz);
        if (!branch || branch->type != OBJ_COMMIT)
                return;
 
@@ -549,6 +550,7 @@ static void find_merge_parents(struct merge_parents *result,
                int len;
                char *p = in->buf + pos;
                char *newline = strchr(p, '\n');
+               const char *q;
                struct object_id oid;
                struct commit *parent;
                struct object *obj;
@@ -556,10 +558,9 @@ static void find_merge_parents(struct merge_parents *result,
                len = newline ? newline - p : strlen(p);
                pos += len + !!newline;
 
-               if (len < GIT_SHA1_HEXSZ + 3 ||
-                   get_oid_hex(p, &oid) ||
-                   p[GIT_SHA1_HEXSZ] != '\t' ||
-                   p[GIT_SHA1_HEXSZ + 1] != '\t')
+               if (parse_oid_hex(p, &oid, &q) ||
+                   q[0] != '\t' ||
+                   q[1] != '\t')
                        continue; /* skip not-for-merge */
                /*
                 * Do not use get_merge_parent() here; we do not have
index 263191942d4c6f785961fd0ed36cbac9b5748206..ea5e2a03e6c882c674dbef9b3f6e99e93b552f64 100644 (file)
@@ -18,6 +18,7 @@
 #include "decorate.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "run-command.h"
 
 #define REACHABLE 0x0001
 #define SEEN      0x0002
@@ -47,6 +48,7 @@ static int name_objects;
 #define ERROR_REACHABLE 02
 #define ERROR_PACK 04
 #define ERROR_REFS 010
+#define ERROR_COMMIT_GRAPH 020
 
 static const char *describe_object(struct object *obj)
 {
@@ -827,5 +829,24 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        }
 
        check_connectivity();
+
+       if (core_commit_graph) {
+               struct child_process commit_graph_verify = CHILD_PROCESS_INIT;
+               const char *verify_argv[] = { "commit-graph", "verify", NULL, NULL, NULL };
+
+               commit_graph_verify.argv = verify_argv;
+               commit_graph_verify.git_cmd = 1;
+               if (run_command(&commit_graph_verify))
+                       errors_found |= ERROR_COMMIT_GRAPH;
+
+               prepare_alt_odb(the_repository);
+               for (alt =  the_repository->objects->alt_odb_list; alt; alt = alt->next) {
+                       verify_argv[2] = "--object-dir";
+                       verify_argv[3] = alt->path;
+                       if (run_command(&commit_graph_verify))
+                               errors_found |= ERROR_COMMIT_GRAPH;
+               }
+       }
+
        return errors_found;
 }
index ccfb1ceaeb3eb9c6a8cbe9297bceac94fa54bcac..e103f0f85d7995999fbd7e52057e563005ae819e 100644 (file)
@@ -20,6 +20,7 @@
 #include "sigchain.h"
 #include "argv-array.h"
 #include "commit.h"
+#include "commit-graph.h"
 #include "packfile.h"
 #include "object-store.h"
 #include "pack.h"
@@ -40,6 +41,7 @@ static int aggressive_depth = 50;
 static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
+static int gc_write_commit_graph;
 static int detach_auto = 1;
 static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
@@ -129,6 +131,7 @@ static void gc_config(void)
        git_config_get_int("gc.aggressivedepth", &aggressive_depth);
        git_config_get_int("gc.auto", &gc_auto_threshold);
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
+       git_config_get_bool("gc.writecommitgraph", &gc_write_commit_graph);
        git_config_get_bool("gc.autodetach", &detach_auto);
        git_config_get_expiry("gc.pruneexpire", &prune_expire);
        git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
@@ -641,6 +644,9 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (pack_garbage.nr > 0)
                clean_pack_garbage();
 
+       if (gc_write_commit_graph)
+               write_commit_graph_reachable(get_object_directory(), 0);
+
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
                        "run 'git prune' to remove them."));
index 538a818e6dd63cd0825bb6c1b195e49bad9442b9..f9678f19e4aff52ae82ee1ff5d9842950b943f43 100644 (file)
@@ -829,6 +829,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                            GREP_PATTERN_TYPE_PCRE),
                OPT_GROUP(""),
                OPT_BOOL('n', "line-number", &opt.linenum, N_("show line numbers")),
+               OPT_BOOL(0, "column", &opt.columnnum, N_("show column number of first match")),
                OPT_NEGBIT('h', NULL, &opt.pathname, N_("don't show filenames"), 1),
                OPT_BIT('H', NULL, &opt.pathname, N_("show filenames"), 1),
                OPT_NEGBIT(0, "full-name", &opt.relative,
index 55a6286d7f4a954db80a1564f15d0db465f9b1d6..574595132af4a799da3665543d19787e670b2791 100644 (file)
@@ -1757,6 +1757,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
        if (base_commit || base_auto) {
                struct commit *base = get_base_commit(base_commit, list, nr);
                reset_revision_walk();
+               clear_object_flags(UNINTERESTING);
                prepare_bases(&bases, base, list, nr);
        }
 
index 0dd902195878aedef76559ad67ad6dc9384141b3..9b2f707c291cf903b9151c2f3673b6fe0e0ca6d5 100644 (file)
@@ -9,10 +9,10 @@ static const char builtin_merge_recursive_usage[] =
 
 static const char *better_branch_name(const char *branch)
 {
-       static char githead_env[8 + GIT_SHA1_HEXSZ + 1];
+       static char githead_env[8 + GIT_MAX_HEXSZ + 1];
        char *name;
 
-       if (strlen(branch) != GIT_SHA1_HEXSZ)
+       if (strlen(branch) != the_hash_algo->hexsz)
                return branch;
        xsnprintf(githead_env, sizeof(githead_env), "GITHEAD_%s", branch);
        name = getenv(githead_env);
index d1b547d97376d74e02e8f3dae3a1afb64dd2b780..299a75425900a10b16dcba1e1f3cadba50eb791b 100644 (file)
@@ -1035,6 +1035,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
        const char *filename;
        int fd, pos, npos;
        struct strbuf fetch_head_file = STRBUF_INIT;
+       const unsigned hexsz = the_hash_algo->hexsz;
 
        if (!merge_names)
                merge_names = &fetch_head_file;
@@ -1060,16 +1061,16 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
                else
                        npos = merge_names->len;
 
-               if (npos - pos < GIT_SHA1_HEXSZ + 2 ||
+               if (npos - pos < hexsz + 2 ||
                    get_oid_hex(merge_names->buf + pos, &oid))
                        commit = NULL; /* bad */
-               else if (memcmp(merge_names->buf + pos + GIT_SHA1_HEXSZ, "\t\t", 2))
+               else if (memcmp(merge_names->buf + pos + hexsz, "\t\t", 2))
                        continue; /* not-for-merge */
                else {
-                       char saved = merge_names->buf[pos + GIT_SHA1_HEXSZ];
-                       merge_names->buf[pos + GIT_SHA1_HEXSZ] = '\0';
+                       char saved = merge_names->buf[pos + hexsz];
+                       merge_names->buf[pos + hexsz] = '\0';
                        commit = get_merge_parent(merge_names->buf + pos);
-                       merge_names->buf[pos + GIT_SHA1_HEXSZ] = saved;
+                       merge_names->buf[pos + hexsz] = saved;
                }
                if (!commit) {
                        if (ptr)
index 6565c800ac3865f9325c21bf28707cce0506195e..4391504a91367bc8c4897e66921fe9a91263f0d0 100644 (file)
@@ -2929,11 +2929,13 @@ static int pack_options_allow_reuse(void)
 
 static int get_object_list_from_bitmap(struct rev_info *revs)
 {
-       if (prepare_bitmap_walk(revs) < 0)
+       struct bitmap_index *bitmap_git;
+       if (!(bitmap_git = prepare_bitmap_walk(revs)))
                return -1;
 
        if (pack_options_allow_reuse() &&
            !reuse_partial_packfile_from_bitmap(
+                       bitmap_git,
                        &reuse_packfile,
                        &reuse_packfile_objects,
                        &reuse_packfile_offset)) {
@@ -2942,7 +2944,8 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
                display_progress(progress_state, nr_result);
        }
 
-       traverse_bitmap_commit_list(&add_object_entry_from_bitmap);
+       traverse_bitmap_commit_list(bitmap_git, &add_object_entry_from_bitmap);
+       free_bitmap_index(bitmap_git);
        return 0;
 }
 
index 400d31c18ccd97a896f686c05a646147aa07ca2c..c17ce94e12ee34c5b822b0e09fcd6d7264e759ad 100644 (file)
@@ -630,8 +630,6 @@ static void prepare_push_cert_sha1(struct child_process *proc)
                return;
 
        if (!already_done) {
-               struct strbuf gpg_output = STRBUF_INIT;
-               struct strbuf gpg_status = STRBUF_INIT;
                int bogs /* beginning_of_gpg_sig */;
 
                already_done = 1;
@@ -640,22 +638,11 @@ static void prepare_push_cert_sha1(struct child_process *proc)
                        oidclr(&push_cert_oid);
 
                memset(&sigcheck, '\0', sizeof(sigcheck));
-               sigcheck.result = 'N';
 
                bogs = parse_signature(push_cert.buf, push_cert.len);
-               if (verify_signed_buffer(push_cert.buf, bogs,
-                                        push_cert.buf + bogs, push_cert.len - bogs,
-                                        &gpg_output, &gpg_status) < 0) {
-                       ; /* error running gpg */
-               } else {
-                       sigcheck.payload = push_cert.buf;
-                       sigcheck.gpg_output = gpg_output.buf;
-                       sigcheck.gpg_status = gpg_status.buf;
-                       parse_gpg_output(&sigcheck);
-               }
+               check_signature(push_cert.buf, bogs, push_cert.buf + bogs,
+                               push_cert.len - bogs, &sigcheck);
 
-               strbuf_release(&gpg_output);
-               strbuf_release(&gpg_status);
                nonce_status = check_nonce(push_cert.buf, bogs);
        }
        if (!is_null_oid(&push_cert_oid)) {
index cbaaae83eaed295c49608954e23f3cd62e78d55f..5b07f3f4a2cf6cd5f73f20a930934ca2277c8391 100644 (file)
@@ -17,6 +17,7 @@
 #include "reflog-walk.h"
 #include "oidset.h"
 #include "packfile.h"
+#include "object-store.h"
 
 static const char rev_list_usage[] =
 "git rev-list [OPTION] <commit-id>... [ -- paths... ]\n"
@@ -515,17 +516,21 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                if (revs.count && !revs.left_right && !revs.cherry_mark) {
                        uint32_t commit_count;
                        int max_count = revs.max_count;
-                       if (!prepare_bitmap_walk(&revs)) {
-                               count_bitmap_commit_list(&commit_count, NULL, NULL, NULL);
+                       struct bitmap_index *bitmap_git;
+                       if ((bitmap_git = prepare_bitmap_walk(&revs))) {
+                               count_bitmap_commit_list(bitmap_git, &commit_count, NULL, NULL, NULL);
                                if (max_count >= 0 && max_count < commit_count)
                                        commit_count = max_count;
                                printf("%d\n", commit_count);
+                               free_bitmap_index(bitmap_git);
                                return 0;
                        }
                } else if (revs.max_count < 0 &&
                           revs.tag_objects && revs.tree_objects && revs.blob_objects) {
-                       if (!prepare_bitmap_walk(&revs)) {
-                               traverse_bitmap_commit_list(&show_object_fast);
+                       struct bitmap_index *bitmap_git;
+                       if ((bitmap_git = prepare_bitmap_walk(&revs))) {
+                               traverse_bitmap_commit_list(bitmap_git, &show_object_fast);
+                               free_bitmap_index(bitmap_git);
                                return 0;
                        }
                }
index 20ae9191ca376841ca2540502d85a3dc150d3417..a3c4564c6c87255449a0a4fc800e81d2915954e2 100644 (file)
@@ -1124,6 +1124,8 @@ static void deinit_submodule(const char *path, const char *prefix,
                if (!(flags & OPT_QUIET))
                        printf(format, displaypath);
 
+               submodule_unset_core_worktree(sub);
+
                strbuf_release(&sb_rm);
        }
 
@@ -1706,8 +1708,8 @@ static int update_clone_task_finished(int result,
        return 0;
 }
 
-static int gitmodules_update_clone_config(const char *var, const char *value,
-                                         void *cb)
+static int git_update_clone_config(const char *var, const char *value,
+                                  void *cb)
 {
        int *max_jobs = cb;
        if (!strcmp(var, "submodule.fetchjobs"))
@@ -1757,8 +1759,8 @@ static int update_clone(int argc, const char **argv, const char *prefix)
        };
        suc.prefix = prefix;
 
-       config_from_gitmodules(gitmodules_update_clone_config, &max_jobs);
-       git_config(gitmodules_update_clone_config, &max_jobs);
+       update_clone_config_from_gitmodules(&max_jobs);
+       git_config(git_update_clone_config, &max_jobs);
 
        argc = parse_options(argc, argv, prefix, module_update_clone_options,
                             git_submodule_helper_usage, 0);
@@ -2004,6 +2006,29 @@ static int check_name(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static int connect_gitdir_workingtree(int argc, const char **argv, const char *prefix)
+{
+       struct strbuf sb = STRBUF_INIT;
+       const char *name, *path;
+       char *sm_gitdir;
+
+       if (argc != 3)
+               BUG("submodule--helper connect-gitdir-workingtree <name> <path>");
+
+       name = argv[1];
+       path = argv[2];
+
+       strbuf_addf(&sb, "%s/modules/%s", get_git_dir(), name);
+       sm_gitdir = absolute_pathdup(sb.buf);
+
+       connect_work_tree_and_git_dir(path, sm_gitdir, 0);
+
+       strbuf_release(&sb);
+       free(sm_gitdir);
+
+       return 0;
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -2017,6 +2042,7 @@ static struct cmd_struct commands[] = {
        {"name", module_name, 0},
        {"clone", module_clone, 0},
        {"update-clone", update_clone, 0},
+       {"connect-gitdir-workingtree", connect_gitdir_workingtree, 0},
        {"relative-path", resolve_relative_path, 0},
        {"resolve-relative-url", resolve_relative_url, 0},
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
index a8709a26ec4b8bb79cbb20bfa4a7892e063c804d..3206c5ad45910643f8e75d16b6b0af08f9ac1b00 100644 (file)
@@ -492,6 +492,7 @@ static void update_one(const char *path)
 
 static void read_index_info(int nul_term_line)
 {
+       const int hexsz = the_hash_algo->hexsz;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf uq = STRBUF_INIT;
        strbuf_getline_fn getline_fn;
@@ -529,7 +530,7 @@ static void read_index_info(int nul_term_line)
                mode = ul;
 
                tab = strchr(ptr, '\t');
-               if (!tab || tab - ptr < GIT_SHA1_HEXSZ + 1)
+               if (!tab || tab - ptr < hexsz + 1)
                        goto bad_line;
 
                if (tab[-2] == ' ' && '0' <= tab[-1] && tab[-1] <= '3') {
@@ -542,8 +543,8 @@ static void read_index_info(int nul_term_line)
                        ptr = tab + 1; /* point at the head of path */
                }
 
-               if (get_oid_hex(tab - GIT_SHA1_HEXSZ, &oid) ||
-                       tab[-(GIT_SHA1_HEXSZ + 1)] != ' ')
+               if (get_oid_hex(tab - hexsz, &oid) ||
+                       tab[-(hexsz + 1)] != ' ')
                        goto bad_line;
 
                path_name = ptr;
@@ -571,7 +572,7 @@ static void read_index_info(int nul_term_line)
                         * ptr[-1] points at tab,
                         * ptr[-41] is at the beginning of sha1
                         */
-                       ptr[-(GIT_SHA1_HEXSZ + 2)] = ptr[-1] = 0;
+                       ptr[-(hexsz + 2)] = ptr[-1] = 0;
                        if (add_cacheinfo(mode, &oid, path_name, stage))
                                die("git update-index: unable to update %s",
                                    path_name);
@@ -826,6 +827,7 @@ static int parse_new_style_cacheinfo(const char *arg,
 {
        unsigned long ul;
        char *endp;
+       const char *p;
 
        if (!arg)
                return -1;
@@ -836,9 +838,9 @@ static int parse_new_style_cacheinfo(const char *arg,
                return -1; /* not a new-style cacheinfo */
        *mode = ul;
        endp++;
-       if (get_oid_hex(endp, oid) || endp[GIT_SHA1_HEXSZ] != ',')
+       if (parse_oid_hex(endp, oid, &p) || *p != ',')
                return -1;
-       *path = endp + GIT_SHA1_HEXSZ + 1;
+       *path = p + 1;
        return 0;
 }
 
index 5c7d2bb1807f942139b3ec41b426320e4b0cfc2a..a763dbdccb490ab6707c247d618203ffd37d4052 100644 (file)
@@ -412,7 +412,7 @@ static const char *dwim_branch(const char *path, const char **new_branch)
        if (guess_remote) {
                struct object_id oid;
                const char *remote =
-                       unique_tracking_name(*new_branch, &oid);
+                       unique_tracking_name(*new_branch, &oid, NULL);
                return remote;
        }
        return NULL;
@@ -484,7 +484,7 @@ static int add(int ac, const char **av, const char *prefix)
 
                commit = lookup_commit_reference_by_name(branch);
                if (!commit) {
-                       remote = unique_tracking_name(branch, &oid);
+                       remote = unique_tracking_name(branch, &oid, NULL);
                        if (remote) {
                                new_branch = branch;
                                branch = remote;
diff --git a/cache.h b/cache.h
index 8b447652a7e94ad31fd28134e0f548baa37265db..ebfe59d01c8ef0e41f9ec228dde124ea9b9015f2 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -972,7 +972,7 @@ extern const struct object_id null_oid;
 
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
 {
-       return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);
+       return memcmp(sha1, sha2, the_hash_algo->rawsz);
 }
 
 static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)
@@ -992,7 +992,7 @@ static inline int is_null_oid(const struct object_id *oid)
 
 static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)
 {
-       memcpy(sha_dst, sha_src, GIT_SHA1_RAWSZ);
+       memcpy(sha_dst, sha_src, the_hash_algo->rawsz);
 }
 
 static inline void oidcpy(struct object_id *dst, const struct object_id *src)
@@ -1009,7 +1009,7 @@ static inline struct object_id *oiddup(const struct object_id *src)
 
 static inline void hashclr(unsigned char *hash)
 {
-       memset(hash, 0, GIT_SHA1_RAWSZ);
+       memset(hash, 0, the_hash_algo->rawsz);
 }
 
 static inline void oidclr(struct object_id *oid)
index bdefc888bae1516f0ddcf22264807d031af7c991..c72e9f9773e173c0dbcb5de88bde54d5434cf808 100644 (file)
@@ -2,14 +2,20 @@
 #include "remote.h"
 #include "refspec.h"
 #include "checkout.h"
+#include "config.h"
 
 struct tracking_name_data {
        /* const */ char *src_ref;
        char *dst_ref;
        struct object_id *dst_oid;
-       int unique;
+       int num_matches;
+       const char *default_remote;
+       char *default_dst_ref;
+       struct object_id *default_dst_oid;
 };
 
+#define TRACKING_NAME_DATA_INIT { NULL, NULL, NULL, 0, NULL, NULL, NULL }
+
 static int check_tracking_name(struct remote *remote, void *cb_data)
 {
        struct tracking_name_data *cb = cb_data;
@@ -21,24 +27,45 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
                free(query.dst);
                return 0;
        }
+       cb->num_matches++;
+       if (cb->default_remote && !strcmp(remote->name, cb->default_remote)) {
+               struct object_id *dst = xmalloc(sizeof(*cb->default_dst_oid));
+               cb->default_dst_ref = xstrdup(query.dst);
+               oidcpy(dst, cb->dst_oid);
+               cb->default_dst_oid = dst;
+       }
        if (cb->dst_ref) {
                free(query.dst);
-               cb->unique = 0;
                return 0;
        }
        cb->dst_ref = query.dst;
        return 0;
 }
 
-const char *unique_tracking_name(const char *name, struct object_id *oid)
+const char *unique_tracking_name(const char *name, struct object_id *oid,
+                                int *dwim_remotes_matched)
 {
-       struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
+       struct tracking_name_data cb_data = TRACKING_NAME_DATA_INIT;
+       const char *default_remote = NULL;
+       if (!git_config_get_string_const("checkout.defaultremote", &default_remote))
+               cb_data.default_remote = default_remote;
        cb_data.src_ref = xstrfmt("refs/heads/%s", name);
        cb_data.dst_oid = oid;
        for_each_remote(check_tracking_name, &cb_data);
+       if (dwim_remotes_matched)
+               *dwim_remotes_matched = cb_data.num_matches;
        free(cb_data.src_ref);
-       if (cb_data.unique)
+       free((char *)default_remote);
+       if (cb_data.num_matches == 1) {
+               free(cb_data.default_dst_ref);
+               free(cb_data.default_dst_oid);
                return cb_data.dst_ref;
+       }
        free(cb_data.dst_ref);
+       if (cb_data.default_dst_ref) {
+               oidcpy(oid, cb_data.default_dst_oid);
+               free(cb_data.default_dst_oid);
+               return cb_data.default_dst_ref;
+       }
        return NULL;
 }
index 998071117952de27f2ec0c16d7b3d49abaa952bc..6b2073310c4a6a735a557ab0883c7bb045c2c458 100644 (file)
@@ -8,6 +8,8 @@
  * tracking branch.  Return the name of the remote if such a branch
  * exists, NULL otherwise.
  */
-extern const char *unique_tracking_name(const char *name, struct object_id *oid);
+extern const char *unique_tracking_name(const char *name,
+                                       struct object_id *oid,
+                                       int *dwim_remotes_matched);
 
 #endif /* CHECKOUT_H */
index 7f907b4bfb2ff722e93e13ec3d03c2c4452bc8c9..41a0133ff7506131d4966bd6bdee9cf6ea7614a2 100644 (file)
@@ -7,10 +7,12 @@
 #include "packfile.h"
 #include "commit.h"
 #include "object.h"
+#include "refs.h"
 #include "revision.h"
 #include "sha1-lookup.h"
 #include "commit-graph.h"
 #include "object-store.h"
+#include "alloc.h"
 
 #define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 
 #define GRAPH_LAST_EDGE 0x80000000
 
+#define GRAPH_HEADER_SIZE 8
 #define GRAPH_FANOUT_SIZE (4 * 256)
 #define GRAPH_CHUNKLOOKUP_WIDTH 12
-#define GRAPH_MIN_SIZE (5 * GRAPH_CHUNKLOOKUP_WIDTH + GRAPH_FANOUT_SIZE + \
-                       GRAPH_OID_LEN + 8)
+#define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
+                       + GRAPH_FANOUT_SIZE + GRAPH_OID_LEN)
 
 char *get_commit_graph_filename(const char *obj_dir)
 {
@@ -241,6 +244,10 @@ static struct commit_list **insert_parent_or_die(struct commit_graph *g,
 {
        struct commit *c;
        struct object_id oid;
+
+       if (pos >= g->num_commits)
+               die("invalid parent position %"PRIu64, pos);
+
        hashcpy(oid.hash, g->chunk_oid_lookup + g->hash_len * pos);
        c = lookup_commit(the_repository, &oid);
        if (!c)
@@ -313,7 +320,7 @@ static int find_commit_in_graph(struct commit *item, struct commit_graph *g, uin
        }
 }
 
-int parse_commit_in_graph(struct commit *item)
+static int parse_commit_in_graph_one(struct commit_graph *g, struct commit *item)
 {
        uint32_t pos;
 
@@ -321,9 +328,21 @@ int parse_commit_in_graph(struct commit *item)
                return 0;
        if (item->object.parsed)
                return 1;
+
+       if (find_commit_in_graph(item, g, &pos))
+               return fill_commit_in_graph(item, g, pos);
+
+       return 0;
+}
+
+int parse_commit_in_graph(struct commit *item)
+{
+       if (!core_commit_graph)
+               return 0;
+
        prepare_commit_graph();
-       if (commit_graph && find_commit_in_graph(item, commit_graph, &pos))
-               return fill_commit_in_graph(item, commit_graph, pos);
+       if (commit_graph)
+               return parse_commit_in_graph_one(commit_graph, item);
        return 0;
 }
 
@@ -349,14 +368,20 @@ static struct tree *load_tree_for_commit(struct commit_graph *g, struct commit *
        return c->maybe_tree;
 }
 
-struct tree *get_commit_tree_in_graph(const struct commit *c)
+static struct tree *get_commit_tree_in_graph_one(struct commit_graph *g,
+                                                const struct commit *c)
 {
        if (c->maybe_tree)
                return c->maybe_tree;
        if (c->graph_pos == COMMIT_NOT_FROM_GRAPH)
-               BUG("get_commit_tree_in_graph called from non-commit-graph commit");
+               BUG("get_commit_tree_in_graph_one called from non-commit-graph commit");
+
+       return load_tree_for_commit(g, (struct commit *)c);
+}
 
-       return load_tree_for_commit(commit_graph, (struct commit *)c);
+struct tree *get_commit_tree_in_graph(const struct commit *c)
+{
+       return get_commit_tree_in_graph_one(commit_graph, c);
 }
 
 static void write_graph_chunk_fanout(struct hashfile *f,
@@ -632,11 +657,28 @@ static void compute_generation_numbers(struct packed_commit_list* commits)
        }
 }
 
+static int add_ref_to_list(const char *refname,
+                          const struct object_id *oid,
+                          int flags, void *cb_data)
+{
+       struct string_list *list = (struct string_list *)cb_data;
+
+       string_list_append(list, oid_to_hex(oid));
+       return 0;
+}
+
+void write_commit_graph_reachable(const char *obj_dir, int append)
+{
+       struct string_list list;
+
+       string_list_init(&list, 1);
+       for_each_ref(add_ref_to_list, &list);
+       write_commit_graph(obj_dir, NULL, &list, append);
+}
+
 void write_commit_graph(const char *obj_dir,
-                       const char **pack_indexes,
-                       int nr_packs,
-                       const char **commit_hex,
-                       int nr_commits,
+                       struct string_list *pack_indexes,
+                       struct string_list *commit_hex,
                        int append)
 {
        struct packed_oid_list oids;
@@ -677,10 +719,10 @@ void write_commit_graph(const char *obj_dir,
                int dirlen;
                strbuf_addf(&packname, "%s/pack/", obj_dir);
                dirlen = packname.len;
-               for (i = 0; i < nr_packs; i++) {
+               for (i = 0; i < pack_indexes->nr; i++) {
                        struct packed_git *p;
                        strbuf_setlen(&packname, dirlen);
-                       strbuf_addstr(&packname, pack_indexes[i]);
+                       strbuf_addstr(&packname, pack_indexes->items[i].string);
                        p = add_packed_git(packname.buf, packname.len, 1);
                        if (!p)
                                die("error adding pack %s", packname.buf);
@@ -693,12 +735,13 @@ void write_commit_graph(const char *obj_dir,
        }
 
        if (commit_hex) {
-               for (i = 0; i < nr_commits; i++) {
+               for (i = 0; i < commit_hex->nr; i++) {
                        const char *end;
                        struct object_id oid;
                        struct commit *result;
 
-                       if (commit_hex[i] && parse_oid_hex(commit_hex[i], &oid, &end))
+                       if (commit_hex->items[i].string &&
+                           parse_oid_hex(commit_hex->items[i].string, &oid, &end))
                                continue;
 
                        result = lookup_commit_reference_gently(the_repository, &oid, 1);
@@ -808,3 +851,179 @@ void write_commit_graph(const char *obj_dir,
        oids.alloc = 0;
        oids.nr = 0;
 }
+
+#define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
+static int verify_commit_graph_error;
+
+static void graph_report(const char *fmt, ...)
+{
+       va_list ap;
+
+       verify_commit_graph_error = 1;
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       fprintf(stderr, "\n");
+       va_end(ap);
+}
+
+#define GENERATION_ZERO_EXISTS 1
+#define GENERATION_NUMBER_EXISTS 2
+
+int verify_commit_graph(struct repository *r, struct commit_graph *g)
+{
+       uint32_t i, cur_fanout_pos = 0;
+       struct object_id prev_oid, cur_oid, checksum;
+       int generation_zero = 0;
+       struct hashfile *f;
+       int devnull;
+
+       if (!g) {
+               graph_report("no commit-graph file loaded");
+               return 1;
+       }
+
+       verify_commit_graph_error = 0;
+
+       if (!g->chunk_oid_fanout)
+               graph_report("commit-graph is missing the OID Fanout chunk");
+       if (!g->chunk_oid_lookup)
+               graph_report("commit-graph is missing the OID Lookup chunk");
+       if (!g->chunk_commit_data)
+               graph_report("commit-graph is missing the Commit Data chunk");
+
+       if (verify_commit_graph_error)
+               return verify_commit_graph_error;
+
+       devnull = open("/dev/null", O_WRONLY);
+       f = hashfd(devnull, NULL);
+       hashwrite(f, g->data, g->data_len - g->hash_len);
+       finalize_hashfile(f, checksum.hash, CSUM_CLOSE);
+       if (hashcmp(checksum.hash, g->data + g->data_len - g->hash_len)) {
+               graph_report(_("the commit-graph file has incorrect checksum and is likely corrupt"));
+               verify_commit_graph_error = VERIFY_COMMIT_GRAPH_ERROR_HASH;
+       }
+
+       for (i = 0; i < g->num_commits; i++) {
+               struct commit *graph_commit;
+
+               hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
+
+               if (i && oidcmp(&prev_oid, &cur_oid) >= 0)
+                       graph_report("commit-graph has incorrect OID order: %s then %s",
+                                    oid_to_hex(&prev_oid),
+                                    oid_to_hex(&cur_oid));
+
+               oidcpy(&prev_oid, &cur_oid);
+
+               while (cur_oid.hash[0] > cur_fanout_pos) {
+                       uint32_t fanout_value = get_be32(g->chunk_oid_fanout + cur_fanout_pos);
+
+                       if (i != fanout_value)
+                               graph_report("commit-graph has incorrect fanout value: fanout[%d] = %u != %u",
+                                            cur_fanout_pos, fanout_value, i);
+                       cur_fanout_pos++;
+               }
+
+               graph_commit = lookup_commit(r, &cur_oid);
+               if (!parse_commit_in_graph_one(g, graph_commit))
+                       graph_report("failed to parse %s from commit-graph",
+                                    oid_to_hex(&cur_oid));
+       }
+
+       while (cur_fanout_pos < 256) {
+               uint32_t fanout_value = get_be32(g->chunk_oid_fanout + cur_fanout_pos);
+
+               if (g->num_commits != fanout_value)
+                       graph_report("commit-graph has incorrect fanout value: fanout[%d] = %u != %u",
+                                    cur_fanout_pos, fanout_value, i);
+
+               cur_fanout_pos++;
+       }
+
+       if (verify_commit_graph_error & ~VERIFY_COMMIT_GRAPH_ERROR_HASH)
+               return verify_commit_graph_error;
+
+       for (i = 0; i < g->num_commits; i++) {
+               struct commit *graph_commit, *odb_commit;
+               struct commit_list *graph_parents, *odb_parents;
+               uint32_t max_generation = 0;
+
+               hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
+
+               graph_commit = lookup_commit(r, &cur_oid);
+               odb_commit = (struct commit *)create_object(r, cur_oid.hash, alloc_commit_node(r));
+               if (parse_commit_internal(odb_commit, 0, 0)) {
+                       graph_report("failed to parse %s from object database",
+                                    oid_to_hex(&cur_oid));
+                       continue;
+               }
+
+               if (oidcmp(&get_commit_tree_in_graph_one(g, graph_commit)->object.oid,
+                          get_commit_tree_oid(odb_commit)))
+                       graph_report("root tree OID for commit %s in commit-graph is %s != %s",
+                                    oid_to_hex(&cur_oid),
+                                    oid_to_hex(get_commit_tree_oid(graph_commit)),
+                                    oid_to_hex(get_commit_tree_oid(odb_commit)));
+
+               graph_parents = graph_commit->parents;
+               odb_parents = odb_commit->parents;
+
+               while (graph_parents) {
+                       if (odb_parents == NULL) {
+                               graph_report("commit-graph parent list for commit %s is too long",
+                                            oid_to_hex(&cur_oid));
+                               break;
+                       }
+
+                       if (oidcmp(&graph_parents->item->object.oid, &odb_parents->item->object.oid))
+                               graph_report("commit-graph parent for %s is %s != %s",
+                                            oid_to_hex(&cur_oid),
+                                            oid_to_hex(&graph_parents->item->object.oid),
+                                            oid_to_hex(&odb_parents->item->object.oid));
+
+                       if (graph_parents->item->generation > max_generation)
+                               max_generation = graph_parents->item->generation;
+
+                       graph_parents = graph_parents->next;
+                       odb_parents = odb_parents->next;
+               }
+
+               if (odb_parents != NULL)
+                       graph_report("commit-graph parent list for commit %s terminates early",
+                                    oid_to_hex(&cur_oid));
+
+               if (!graph_commit->generation) {
+                       if (generation_zero == GENERATION_NUMBER_EXISTS)
+                               graph_report("commit-graph has generation number zero for commit %s, but non-zero elsewhere",
+                                            oid_to_hex(&cur_oid));
+                       generation_zero = GENERATION_ZERO_EXISTS;
+               } else if (generation_zero == GENERATION_ZERO_EXISTS)
+                       graph_report("commit-graph has non-zero generation number for commit %s, but zero elsewhere",
+                                    oid_to_hex(&cur_oid));
+
+               if (generation_zero == GENERATION_ZERO_EXISTS)
+                       continue;
+
+               /*
+                * If one of our parents has generation GENERATION_NUMBER_MAX, then
+                * our generation is also GENERATION_NUMBER_MAX. Decrement to avoid
+                * extra logic in the following condition.
+                */
+               if (max_generation == GENERATION_NUMBER_MAX)
+                       max_generation--;
+
+               if (graph_commit->generation != max_generation + 1)
+                       graph_report("commit-graph generation for commit %s is %u != %u",
+                                    oid_to_hex(&cur_oid),
+                                    graph_commit->generation,
+                                    max_generation + 1);
+
+               if (graph_commit->date != odb_commit->date)
+                       graph_report("commit date for commit %s in commit-graph is %"PRItime" != %"PRItime,
+                                    oid_to_hex(&cur_oid),
+                                    graph_commit->date,
+                                    odb_commit->date);
+       }
+
+       return verify_commit_graph_error;
+}
index 96cccb10f3d53a6da77f4ad06a971dbaa152f70c..506cb45fb1178256b2d6c9752c28e300fc0791b2 100644 (file)
@@ -2,6 +2,8 @@
 #define COMMIT_GRAPH_H
 
 #include "git-compat-util.h"
+#include "repository.h"
+#include "string-list.h"
 
 char *get_commit_graph_filename(const char *obj_dir);
 
@@ -46,11 +48,12 @@ struct commit_graph {
 
 struct commit_graph *load_commit_graph_one(const char *graph_file);
 
+void write_commit_graph_reachable(const char *obj_dir, int append);
 void write_commit_graph(const char *obj_dir,
-                       const char **pack_indexes,
-                       int nr_packs,
-                       const char **commit_hex,
-                       int nr_commits,
+                       struct string_list *pack_indexes,
+                       struct string_list *commit_hex,
                        int append);
 
+int verify_commit_graph(struct repository *r, struct commit_graph *g);
+
 #endif
index b88ced5b0265f705ad1dbd0c5b8dfb8eed6427a7..add310d4231d4cc71068b64827dc041aae0bb011 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -388,8 +388,8 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        struct object_id parent;
        struct commit_list **pptr;
        struct commit_graft *graft;
-       const int tree_entry_len = GIT_SHA1_HEXSZ + 5;
-       const int parent_entry_len = GIT_SHA1_HEXSZ + 7;
+       const int tree_entry_len = the_hash_algo->hexsz + 5;
+       const int parent_entry_len = the_hash_algo->hexsz + 7;
 
        if (item->object.parsed)
                return 0;
@@ -443,7 +443,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
        return 0;
 }
 
-int parse_commit_gently(struct commit *item, int quiet_on_missing)
+int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_commit_graph)
 {
        enum object_type type;
        void *buffer;
@@ -454,7 +454,7 @@ int parse_commit_gently(struct commit *item, int quiet_on_missing)
                return -1;
        if (item->object.parsed)
                return 0;
-       if (parse_commit_in_graph(item))
+       if (use_commit_graph && parse_commit_in_graph(item))
                return 0;
        buffer = read_object_file(&item->object.oid, &type, &size);
        if (!buffer)
@@ -466,6 +466,7 @@ int parse_commit_gently(struct commit *item, int quiet_on_missing)
                return error("Object %s not a commit",
                             oid_to_hex(&item->object.oid));
        }
+
        ret = parse_commit_buffer(the_repository, item, buffer, size, 0);
        if (save_commit_buffer && !ret) {
                set_commit_buffer(the_repository, item, buffer, size);
@@ -475,6 +476,11 @@ int parse_commit_gently(struct commit *item, int quiet_on_missing)
        return ret;
 }
 
+int parse_commit_gently(struct commit *item, int quiet_on_missing)
+{
+       return parse_commit_internal(item, quiet_on_missing, 1);
+}
+
 void parse_commit_or_die(struct commit *item)
 {
        if (parse_commit(item))
index 8b2cf9692dec335c75074736038b4ebfe0ac822e..da0db36eba2bf16277dbb7aadcb3384e29191ecc 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -79,6 +79,7 @@ struct commit *lookup_commit_reference_by_name(const char *name);
 struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name);
 
 int parse_commit_buffer(struct repository *r, struct commit *item, const void *buffer, unsigned long size, int check_graph);
+int parse_commit_internal(struct commit *item, int quiet_on_missing, int use_commit_graph);
 int parse_commit_gently(struct commit *item, int quiet_on_missing);
 static inline int parse_commit(struct commit *item)
 {
index df8a6574c9ac24722aec9baf046dab785d210cc4..60fd873fe801da608cdef1ac2bafd371a0855db0 100644 (file)
@@ -30,8 +30,8 @@ The Steps of Build Git with VS2008
    the git operations.
 
 3. Inside Git's directory run the command:
-       make common-cmds.h
-   to generate the common-cmds.h file needed to compile git.
+       make command-list.h
+   to generate the command-list.h file needed to compile git.
 
 4. Then either build Git with the GNU Make Makefile in the Git projects
    root
index 139c903f6b3f1f35f2a443ebcd8b09b74ffcb7b5..35a9bc79555ade317e965b031a7a8a402bddf43f 100644 (file)
--- a/config.c
+++ b/config.c
@@ -32,7 +32,7 @@ struct config_source {
        enum config_origin_type origin_type;
        const char *name;
        const char *path;
-       int die_on_error;
+       enum config_error_action default_error_action;
        int linenr;
        int eof;
        struct strbuf value;
@@ -810,10 +810,21 @@ static int git_parse_source(config_fn_t fn, void *data,
                                      cf->linenr, cf->name);
        }
 
-       if (cf->die_on_error)
+       switch (opts && opts->error_action ?
+               opts->error_action :
+               cf->default_error_action) {
+       case CONFIG_ERROR_DIE:
                die("%s", error_msg);
-       else
+               break;
+       case CONFIG_ERROR_ERROR:
                error_return = error("%s", error_msg);
+               break;
+       case CONFIG_ERROR_SILENT:
+               error_return = -1;
+               break;
+       case CONFIG_ERROR_UNSET:
+               BUG("config error action unset");
+       }
 
        free(error_msg);
        return error_return;
@@ -1521,7 +1532,7 @@ static int do_config_from_file(config_fn_t fn,
        top.origin_type = origin_type;
        top.name = name;
        top.path = path;
-       top.die_on_error = 1;
+       top.default_error_action = CONFIG_ERROR_DIE;
        top.do_fgetc = config_file_fgetc;
        top.do_ungetc = config_file_ungetc;
        top.do_ftell = config_file_ftell;
@@ -1559,8 +1570,10 @@ int git_config_from_file(config_fn_t fn, const char *filename, void *data)
        return git_config_from_file_with_options(fn, filename, data, NULL);
 }
 
-int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_type,
-                       const char *name, const char *buf, size_t len, void *data)
+int git_config_from_mem(config_fn_t fn,
+                       const enum config_origin_type origin_type,
+                       const char *name, const char *buf, size_t len,
+                       void *data, const struct config_options *opts)
 {
        struct config_source top;
 
@@ -1570,12 +1583,12 @@ int git_config_from_mem(config_fn_t fn, const enum config_origin_type origin_typ
        top.origin_type = origin_type;
        top.name = name;
        top.path = NULL;
-       top.die_on_error = 0;
+       top.default_error_action = CONFIG_ERROR_ERROR;
        top.do_fgetc = config_buf_fgetc;
        top.do_ungetc = config_buf_ungetc;
        top.do_ftell = config_buf_ftell;
 
-       return do_config_from(&top, fn, data, NULL);
+       return do_config_from(&top, fn, data, opts);
 }
 
 int git_config_from_blob_oid(config_fn_t fn,
@@ -1596,7 +1609,8 @@ int git_config_from_blob_oid(config_fn_t fn,
                return error("reference '%s' does not point to a blob", name);
        }
 
-       ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size, data);
+       ret = git_config_from_mem(fn, CONFIG_ORIGIN_BLOB, name, buf, size,
+                                 data, NULL);
        free(buf);
 
        return ret;
@@ -2173,23 +2187,6 @@ int git_config_get_pathname(const char *key, const char **dest)
        return repo_config_get_pathname(the_repository, key, dest);
 }
 
-/*
- * Note: This function exists solely to maintain backward compatibility with
- * 'fetch' and 'update_clone' storing configuration in '.gitmodules' and should
- * NOT be used anywhere else.
- *
- * Runs the provided config function on the '.gitmodules' file found in the
- * working directory.
- */
-void config_from_gitmodules(config_fn_t fn, void *data)
-{
-       if (the_repository->worktree) {
-               char *file = repo_worktree_path(the_repository, GITMODULES_FILE);
-               git_config_from_file(fn, file, data);
-               free(file);
-       }
-}
-
 int git_config_get_expiry(const char *key, const char **output)
 {
        int ret = git_config_get_string_const(key, output);
index 626d4654bd6f98771be903cca208fdb368920bf4..bb2f506b27137e8d66ab4cd96439143dced94780 100644 (file)
--- a/config.h
+++ b/config.h
@@ -54,6 +54,12 @@ struct config_options {
        const char *git_dir;
        config_parser_event_fn_t event_fn;
        void *event_fn_data;
+       enum config_error_action {
+               CONFIG_ERROR_UNSET = 0, /* use source-specific default */
+               CONFIG_ERROR_DIE, /* die() on error */
+               CONFIG_ERROR_ERROR, /* error() on error, return -1 */
+               CONFIG_ERROR_SILENT, /* return -1 */
+       } error_action;
 };
 
 typedef int (*config_fn_t)(const char *, const char *, void *);
@@ -62,8 +68,11 @@ extern int git_config_from_file(config_fn_t fn, const char *, void *);
 extern int git_config_from_file_with_options(config_fn_t fn, const char *,
                                             void *,
                                             const struct config_options *);
-extern int git_config_from_mem(config_fn_t fn, const enum config_origin_type,
-                                       const char *name, const char *buf, size_t len, void *data);
+extern int git_config_from_mem(config_fn_t fn,
+                              const enum config_origin_type,
+                              const char *name,
+                              const char *buf, size_t len,
+                              void *data, const struct config_options *opts);
 extern int git_config_from_blob_oid(config_fn_t fn, const char *name,
                                    const struct object_id *oid, void *data);
 extern void git_config_push_parameter(const char *text);
@@ -215,16 +224,6 @@ extern int repo_config_get_maybe_bool(struct repository *repo,
 extern int repo_config_get_pathname(struct repository *repo,
                                    const char *key, const char **dest);
 
-/*
- * Note: This function exists solely to maintain backward compatibility with
- * 'fetch' and 'update_clone' storing configuration in '.gitmodules' and should
- * NOT be used anywhere else.
- *
- * Runs the provided config function on the '.gitmodules' file found in the
- * working directory.
- */
-extern void config_from_gitmodules(config_fn_t fn, void *data);
-
 extern int git_config_get_value(const char *key, const char **value);
 extern const struct string_list *git_config_get_value_multi(const char *key);
 extern void git_config_clear(void);
index 0e69c6709c9fdb83b4888443f490d79ef504c8a3..32aff60979512b7f1382482435a26c51882b9f16 100644 (file)
--- a/connect.h
+++ b/connect.h
@@ -1,6 +1,8 @@
 #ifndef CONNECT_H
 #define CONNECT_H
 
+#include "protocol.h"
+
 #define CONNECT_VERBOSE       (1u << 0)
 #define CONNECT_DIAG_URL      (1u << 1)
 #define CONNECT_IPV4          (1u << 2)
index 91feb7881545f4143f45b9eb3393de65739cfa92..1bba888eff90a23e586807fd02b274b8e7fac097 100644 (file)
@@ -58,8 +58,10 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
        argv_array_push(&rev_list.args, "--stdin");
        if (repository_format_partial_clone)
                argv_array_push(&rev_list.args, "--exclude-promisor-objects");
-       argv_array_push(&rev_list.args, "--not");
-       argv_array_push(&rev_list.args, "--all");
+       if (!opt->is_deepening_fetch) {
+               argv_array_push(&rev_list.args, "--not");
+               argv_array_push(&rev_list.args, "--all");
+       }
        argv_array_push(&rev_list.args, "--quiet");
        if (opt->progress)
                argv_array_pushf(&rev_list.args, "--progress=%s",
index a53f03a61aca4871be5ab75db9bf63ee895668e1..322dc7637263630712e5ab3e875720c5e39eda22 100644 (file)
@@ -38,6 +38,13 @@ struct check_connected_options {
         * Insert these variables into the environment of the child process.
         */
        const char **env;
+
+       /*
+        * If non-zero, check the ancestry chain completely, not stopping at
+        * any existing ref. This is necessary when deepening existing refs
+        * during a fetch.
+        */
+       unsigned is_deepening_fetch : 1;
 };
 
 #define CHECK_CONNECTED_INIT { 0 }
index a7e9215ffc370d945ba2ac28b7eff9e8cb1d0679..aec3345adb4f0fb83b511335e0727f1097f97e29 100644 (file)
@@ -12,7 +12,7 @@ expression c;
 
 // These excluded functions must access c->maybe_tree direcly.
 @@
-identifier f !~ "^(get_commit_tree|get_commit_tree_in_graph|load_tree_for_commit)$";
+identifier f !~ "^(get_commit_tree|get_commit_tree_in_graph_one|load_tree_for_commit)$";
 expression c;
 @@
   f(...) {...
index 4484bda41065af6f6e23c667a7bab59a8a1a61cc..2f618a7f978822a0453114df0ed0045d2cf8ace5 100644 (file)
@@ -25,6 +25,13 @@ git-jump will feed this to the editor:
 foo.c:2: printf("hello word!\n");
 -----------------------------------
 
+Or, when running 'git jump grep', column numbers will also be emitted,
+e.g. `git jump grep "hello"` would return:
+
+-----------------------------------
+foo.c:2:9: printf("hello word!\n");
+-----------------------------------
+
 Obviously this trivial case isn't that interesting; you could just open
 `foo.c` yourself. But when you have many changes scattered across a
 project, you can use the editor's support to "jump" from point to point.
@@ -35,7 +42,8 @@ Git-jump can generate four types of interesting lists:
 
   2. The beginning of any merge conflict markers.
 
-  3. Any grep matches.
+  3. Any grep matches, including the column of the first match on a
+     line.
 
   4. Any whitespace errors detected by `git diff --check`.
 
@@ -82,7 +90,7 @@ which does something similar to `git jump grep`. However, it is limited
 to positioning the cursor to the correct line in only the first file,
 leaving you to locate subsequent hits in that file or other files using
 the editor or pager. By contrast, git-jump provides the editor with a
-complete list of files and line numbers for each match.
+complete list of files, lines, and a column number for each match.
 
 
 Limitations
index 80ab0590bc92221e2b2f8f35b76f5a0cbb970f8b..931b0fe3a9483ea7394d6b30d36f92ebade09110 100755 (executable)
@@ -52,7 +52,7 @@ mode_merge() {
 # editor shows them to us in the status bar.
 mode_grep() {
        cmd=$(git config jump.grepCmd)
-       test -n "$cmd" || cmd="git grep -n"
+       test -n "$cmd" || cmd="git grep -n --column"
        $cmd "$@" |
        perl -pe '
        s/[ \t]+/ /g;
index 56cfe31ec5363c6f1e9b3d51ec700c7d46730080..7907efd16f279df81ca18f2f370bb89380ccf921 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -335,7 +335,7 @@ static void trace_encoding(const char *context, const char *path,
        strbuf_addf(&trace, "%s (%s, considered %s):\n", context, path, encoding);
        for (i = 0; i < len && buf; ++i) {
                strbuf_addf(
-                       &trace,"| \e[2m%2i:\e[0m %2x \e[2m%c\e[0m%c",
+                       &trace, "| \033[2m%2i:\033[0m %2x \033[2m%c\033[0m%c",
                        i,
                        (unsigned char) buf[i],
                        (buf[i] > 32 && buf[i] < 127 ? buf[i] : ' '),
index a9f38eb5a3e0e17d111301b581d06876ce5fd510..732f684a49c54cdbc3b9708e91c064b7845d7716 100644 (file)
@@ -520,6 +520,9 @@ int run_diff_index(struct rev_info *revs, int cached)
        struct object_array_entry *ent;
        uint64_t start = getnanotime();
 
+       if (revs->pending.nr != 1)
+               BUG("run_diff_index must be passed exactly one tree");
+
        ent = revs->pending.objects;
        if (diff_cache(revs, &ent->item->oid, ent->name, cached))
                exit(128);
diff --git a/diff.c b/diff.c
index dc53a19bab609eac8de52e57f437c79da7fbb1fa..04d044bbb67b77a9992a499d4f7728bb85cfe94f 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -37,6 +37,7 @@ static int diff_rename_limit_default = 400;
 static int diff_suppress_blank_empty;
 static int diff_use_color_default = -1;
 static int diff_color_moved_default;
+static int diff_color_moved_ws_default;
 static int diff_context_default = 3;
 static int diff_interhunk_context_default;
 static const char *diff_word_regex_cfg;
@@ -264,6 +265,8 @@ static int parse_color_moved(const char *arg)
                return COLOR_MOVED_NO;
        else if (!strcmp(arg, "plain"))
                return COLOR_MOVED_PLAIN;
+       else if (!strcmp(arg, "blocks"))
+               return COLOR_MOVED_BLOCKS;
        else if (!strcmp(arg, "zebra"))
                return COLOR_MOVED_ZEBRA;
        else if (!strcmp(arg, "default"))
@@ -271,7 +274,43 @@ static int parse_color_moved(const char *arg)
        else if (!strcmp(arg, "dimmed_zebra"))
                return COLOR_MOVED_ZEBRA_DIM;
        else
-               return error(_("color moved setting must be one of 'no', 'default', 'zebra', 'dimmed_zebra', 'plain'"));
+               return error(_("color moved setting must be one of 'no', 'default', 'blocks', 'zebra', 'dimmed_zebra', 'plain'"));
+}
+
+static int parse_color_moved_ws(const char *arg)
+{
+       int ret = 0;
+       struct string_list l = STRING_LIST_INIT_DUP;
+       struct string_list_item *i;
+
+       string_list_split(&l, arg, ',', -1);
+
+       for_each_string_list_item(i, &l) {
+               struct strbuf sb = STRBUF_INIT;
+               strbuf_addstr(&sb, i->string);
+               strbuf_trim(&sb);
+
+               if (!strcmp(sb.buf, "ignore-space-change"))
+                       ret |= XDF_IGNORE_WHITESPACE_CHANGE;
+               else if (!strcmp(sb.buf, "ignore-space-at-eol"))
+                       ret |= XDF_IGNORE_WHITESPACE_AT_EOL;
+               else if (!strcmp(sb.buf, "ignore-all-space"))
+                       ret |= XDF_IGNORE_WHITESPACE;
+               else if (!strcmp(sb.buf, "allow-indentation-change"))
+                       ret |= COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE;
+               else
+                       error(_("ignoring unknown color-moved-ws mode '%s'"), sb.buf);
+
+               strbuf_release(&sb);
+       }
+
+       if ((ret & COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) &&
+           (ret & XDF_WHITESPACE_FLAGS))
+               die(_("color-moved-ws: allow-indentation-change cannot be combined with other white space modes"));
+
+       string_list_clear(&l, 0);
+
+       return ret;
 }
 
 int git_diff_ui_config(const char *var, const char *value, void *cb)
@@ -287,6 +326,13 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
                diff_color_moved_default = cm;
                return 0;
        }
+       if (!strcmp(var, "diff.colormovedws")) {
+               int cm = parse_color_moved_ws(value);
+               if (cm < 0)
+                       return -1;
+               diff_color_moved_ws_default = cm;
+               return 0;
+       }
        if (!strcmp(var, "diff.context")) {
                diff_context_default = git_config_int(var, value);
                if (diff_context_default < 0)
@@ -698,16 +744,116 @@ struct moved_entry {
        struct hashmap_entry ent;
        const struct emitted_diff_symbol *es;
        struct moved_entry *next_line;
+       struct ws_delta *wsd;
 };
 
-static int moved_entry_cmp(const struct diff_options *diffopt,
-                          const struct moved_entry *a,
-                          const struct moved_entry *b,
+/**
+ * The struct ws_delta holds white space differences between moved lines, i.e.
+ * between '+' and '-' lines that have been detected to be a move.
+ * The string contains the difference in leading white spaces, before the
+ * rest of the line is compared using the white space config for move
+ * coloring. The current_longer indicates if the first string in the
+ * comparision is longer than the second.
+ */
+struct ws_delta {
+       char *string;
+       unsigned int current_longer : 1;
+};
+#define WS_DELTA_INIT { NULL, 0 }
+
+static int compute_ws_delta(const struct emitted_diff_symbol *a,
+                            const struct emitted_diff_symbol *b,
+                            struct ws_delta *out)
+{
+       const struct emitted_diff_symbol *longer =  a->len > b->len ? a : b;
+       const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
+       int d = longer->len - shorter->len;
+
+       out->string = xmemdupz(longer->line, d);
+       out->current_longer = (a == longer);
+
+       return !strncmp(longer->line + d, shorter->line, shorter->len);
+}
+
+static int cmp_in_block_with_wsd(const struct diff_options *o,
+                                const struct moved_entry *cur,
+                                const struct moved_entry *match,
+                                struct moved_entry *pmb,
+                                int n)
+{
+       struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
+       int al = cur->es->len, cl = l->len;
+       const char *a = cur->es->line,
+                  *b = match->es->line,
+                  *c = l->line;
+
+       int wslen;
+
+       /*
+        * We need to check if 'cur' is equal to 'match'.
+        * As those are from the same (+/-) side, we do not need to adjust for
+        * indent changes. However these were found using fuzzy matching
+        * so we do have to check if they are equal.
+        */
+       if (strcmp(a, b))
+               return 1;
+
+       if (!pmb->wsd)
+               /*
+                * No white space delta was carried forward? This can happen
+                * when we exit early in this function and do not carry
+                * forward ws.
+                */
+               return 1;
+
+       /*
+        * The indent changes of the block are known and carried forward in
+        * pmb->wsd; however we need to check if the indent changes of the
+        * current line are still the same as before.
+        *
+        * To do so we need to compare 'l' to 'cur', adjusting the
+        * one of them for the white spaces, depending which was longer.
+        */
+
+       wslen = strlen(pmb->wsd->string);
+       if (pmb->wsd->current_longer) {
+               c += wslen;
+               cl -= wslen;
+       } else {
+               a += wslen;
+               al -= wslen;
+       }
+
+       if (strcmp(a, c))
+               return 1;
+
+       return 0;
+}
+
+static int moved_entry_cmp(const void *hashmap_cmp_fn_data,
+                          const void *entry,
+                          const void *entry_or_key,
                           const void *keydata)
 {
+       const struct diff_options *diffopt = hashmap_cmp_fn_data;
+       const struct moved_entry *a = entry;
+       const struct moved_entry *b = entry_or_key;
+       unsigned flags = diffopt->color_moved_ws_handling
+                        & XDF_WHITESPACE_FLAGS;
+
+       if (diffopt->color_moved_ws_handling &
+           COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+               /*
+                * As there is not specific white space config given,
+                * we'd need to check for a new block, so ignore all
+                * white space. The setup of the white space
+                * configuration for the next block is done else where
+                */
+               flags |= XDF_IGNORE_WHITESPACE;
+
        return !xdiff_compare_lines(a->es->line, a->es->len,
                                    b->es->line, b->es->len,
-                                   diffopt->xdl_opts);
+                                   flags);
 }
 
 static struct moved_entry *prepare_entry(struct diff_options *o,
@@ -715,10 +861,12 @@ static struct moved_entry *prepare_entry(struct diff_options *o,
 {
        struct moved_entry *ret = xmalloc(sizeof(*ret));
        struct emitted_diff_symbol *l = &o->emitted_symbols->buf[line_no];
+       unsigned flags = o->color_moved_ws_handling & XDF_WHITESPACE_FLAGS;
 
-       ret->ent.hash = xdiff_hash_string(l->line, l->len, o->xdl_opts);
+       ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
        ret->es = l;
        ret->next_line = NULL;
+       ret->wsd = NULL;
 
        return ret;
 }
@@ -755,6 +903,56 @@ static void add_lines_to_move_detection(struct diff_options *o,
        }
 }
 
+static void pmb_advance_or_null(struct diff_options *o,
+                               struct moved_entry *match,
+                               struct hashmap *hm,
+                               struct moved_entry **pmb,
+                               int pmb_nr)
+{
+       int i;
+       for (i = 0; i < pmb_nr; i++) {
+               struct moved_entry *prev = pmb[i];
+               struct moved_entry *cur = (prev && prev->next_line) ?
+                               prev->next_line : NULL;
+               if (cur && !hm->cmpfn(o, cur, match, NULL)) {
+                       pmb[i] = cur;
+               } else {
+                       pmb[i] = NULL;
+               }
+       }
+}
+
+static void pmb_advance_or_null_multi_match(struct diff_options *o,
+                                           struct moved_entry *match,
+                                           struct hashmap *hm,
+                                           struct moved_entry **pmb,
+                                           int pmb_nr, int n)
+{
+       int i;
+       char *got_match = xcalloc(1, pmb_nr);
+
+       for (; match; match = hashmap_get_next(hm, match)) {
+               for (i = 0; i < pmb_nr; i++) {
+                       struct moved_entry *prev = pmb[i];
+                       struct moved_entry *cur = (prev && prev->next_line) ?
+                                       prev->next_line : NULL;
+                       if (!cur)
+                               continue;
+                       if (!cmp_in_block_with_wsd(o, cur, match, pmb[i], n))
+                               got_match[i] |= 1;
+               }
+       }
+
+       for (i = 0; i < pmb_nr; i++) {
+               if (got_match[i]) {
+                       /* Carry the white space delta forward */
+                       pmb[i]->next_line->wsd = pmb[i]->wsd;
+                       pmb[i] = pmb[i]->next_line;
+               } else
+                       pmb[i] = NULL;
+       }
+}
+
 static int shrink_potential_moved_blocks(struct moved_entry **pmb,
                                         int pmb_nr)
 {
@@ -772,6 +970,10 @@ static int shrink_potential_moved_blocks(struct moved_entry **pmb,
 
                if (lp < pmb_nr && rp > -1 && lp < rp) {
                        pmb[lp] = pmb[rp];
+                       if (pmb[rp]->wsd) {
+                               free(pmb[rp]->wsd->string);
+                               FREE_AND_NULL(pmb[rp]->wsd);
+                       }
                        pmb[rp] = NULL;
                        rp--;
                        lp++;
@@ -829,19 +1031,18 @@ static void mark_color_as_moved(struct diff_options *o,
                struct moved_entry *key;
                struct moved_entry *match = NULL;
                struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
-               int i;
 
                switch (l->s) {
                case DIFF_SYMBOL_PLUS:
                        hm = del_lines;
                        key = prepare_entry(o, n);
-                       match = hashmap_get(hm, key, o);
+                       match = hashmap_get(hm, key, NULL);
                        free(key);
                        break;
                case DIFF_SYMBOL_MINUS:
                        hm = add_lines;
                        key = prepare_entry(o, n);
-                       match = hashmap_get(hm, key, o);
+                       match = hashmap_get(hm, key, NULL);
                        free(key);
                        break;
                default:
@@ -860,17 +1061,11 @@ static void mark_color_as_moved(struct diff_options *o,
                if (o->color_moved == COLOR_MOVED_PLAIN)
                        continue;
 
-               /* Check any potential block runs, advance each or nullify */
-               for (i = 0; i < pmb_nr; i++) {
-                       struct moved_entry *p = pmb[i];
-                       struct moved_entry *pnext = (p && p->next_line) ?
-                                       p->next_line : NULL;
-                       if (pnext && !hm->cmpfn(o, pnext, match, NULL)) {
-                               pmb[i] = p->next_line;
-                       } else {
-                               pmb[i] = NULL;
-                       }
-               }
+               if (o->color_moved_ws_handling &
+                   COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+                       pmb_advance_or_null_multi_match(o, match, hm, pmb, pmb_nr, n);
+               else
+                       pmb_advance_or_null(o, match, hm, pmb, pmb_nr);
 
                pmb_nr = shrink_potential_moved_blocks(pmb, pmb_nr);
 
@@ -881,7 +1076,17 @@ static void mark_color_as_moved(struct diff_options *o,
                         */
                        for (; match; match = hashmap_get_next(hm, match)) {
                                ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
-                               pmb[pmb_nr++] = match;
+                               if (o->color_moved_ws_handling &
+                                   COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
+                                       struct ws_delta *wsd = xmalloc(sizeof(*match->wsd));
+                                       if (compute_ws_delta(l, match->es, wsd)) {
+                                               match->wsd = wsd;
+                                               pmb[pmb_nr++] = match;
+                                       } else
+                                               free(wsd);
+                               } else {
+                                       pmb[pmb_nr++] = match;
+                               }
                        }
 
                        flipped_block = (flipped_block + 1) % 2;
@@ -892,7 +1097,7 @@ static void mark_color_as_moved(struct diff_options *o,
 
                block_length++;
 
-               if (flipped_block)
+               if (flipped_block && o->color_moved != COLOR_MOVED_BLOCKS)
                        l->flags |= DIFF_SYMBOL_MOVED_LINE_ALT;
        }
        adjust_last_block(o, n, block_length);
@@ -3833,7 +4038,7 @@ static const char *diff_abbrev_oid(const struct object_id *oid, int abbrev)
                char *hex = oid_to_hex(oid);
                if (abbrev < 0)
                        abbrev = FALLBACK_DEFAULT_ABBREV;
-               if (abbrev > GIT_SHA1_HEXSZ)
+               if (abbrev > the_hash_algo->hexsz)
                        BUG("oid abbreviation out of range: %d", abbrev);
                if (abbrev)
                        hex[abbrev] = '\0';
@@ -4125,6 +4330,7 @@ void diff_setup(struct diff_options *options)
        }
 
        options->color_moved = diff_color_moved_default;
+       options->color_moved_ws_handling = diff_color_moved_ws_default;
 }
 
 void diff_setup_done(struct diff_options *options)
@@ -4704,6 +4910,8 @@ int diff_opt_parse(struct diff_options *options,
                if (cm < 0)
                        die("bad --color-moved argument: %s", arg);
                options->color_moved = cm;
+       } else if (skip_prefix(arg, "--color-moved-ws=", &arg)) {
+               options->color_moved_ws_handling = parse_color_moved_ws(arg);
        } else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
                options->use_color = 1;
                options->word_diff = DIFF_WORDS_COLOR;
@@ -4948,7 +5156,7 @@ const char *diff_aligned_abbrev(const struct object_id *oid, int len)
        const char *abbrev;
 
        /* Do we want all 40 hex characters? */
-       if (len == GIT_SHA1_HEXSZ)
+       if (len == the_hash_algo->hexsz)
                return oid_to_hex(oid);
 
        /* An abbreviated value is fine, possibly followed by an ellipsis. */
@@ -4978,7 +5186,7 @@ const char *diff_aligned_abbrev(const struct object_id *oid, int len)
         * the automatic sizing is supposed to give abblen that ensures
         * uniqueness across all objects (statistically speaking).
         */
-       if (abblen < GIT_SHA1_HEXSZ - 3) {
+       if (abblen < the_hash_algo->hexsz - 3) {
                static char hex[GIT_MAX_HEXSZ + 1];
                if (len < abblen && abblen <= len + 2)
                        xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
@@ -5534,10 +5742,12 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
                if (o->color_moved) {
                        struct hashmap add_lines, del_lines;
 
-                       hashmap_init(&del_lines,
-                                    (hashmap_cmp_fn)moved_entry_cmp, o, 0);
-                       hashmap_init(&add_lines,
-                                    (hashmap_cmp_fn)moved_entry_cmp, o, 0);
+                       if (o->color_moved_ws_handling &
+                           COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE)
+                               o->color_moved_ws_handling |= XDF_IGNORE_WHITESPACE;
+
+                       hashmap_init(&del_lines, moved_entry_cmp, o, 0);
+                       hashmap_init(&add_lines, moved_entry_cmp, o, 0);
 
                        add_lines_to_move_detection(o, &add_lines, &del_lines);
                        mark_color_as_moved(o, &add_lines, &del_lines);
diff --git a/diff.h b/diff.h
index dedac472ca5959bb5ff17888a54042ac75678631..a14895bb824f2c844f8d2382b42d6c6f738e3835 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -208,11 +208,16 @@ struct diff_options {
        enum {
                COLOR_MOVED_NO = 0,
                COLOR_MOVED_PLAIN = 1,
-               COLOR_MOVED_ZEBRA = 2,
-               COLOR_MOVED_ZEBRA_DIM = 3,
+               COLOR_MOVED_BLOCKS = 2,
+               COLOR_MOVED_ZEBRA = 3,
+               COLOR_MOVED_ZEBRA_DIM = 4,
        } color_moved;
        #define COLOR_MOVED_DEFAULT COLOR_MOVED_ZEBRA
        #define COLOR_MOVED_MIN_ALNUM_COUNT 20
+
+       /* XDF_WHITESPACE_FLAGS regarding block detection are set at 2, 3, 4 */
+       #define COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE (1<<5)
+       int color_moved_ws_handling;
 };
 
 void diff_emit_submodule_del(struct diff_options *o, const char *line);
diff --git a/dir.c b/dir.c
index a1fe57e925f8ceede7780fc2542ade4f55b15bca..21e6f2520a487ec9167e7d1f79101f82444af832 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -2498,7 +2498,7 @@ void setup_standard_excludes(struct dir_struct *dir)
 {
        dir->exclude_per_dir = ".gitignore";
 
-       /* core.excludefile defaulting to $XDG_HOME/git/ignore */
+       /* core.excludesfile defaulting to $XDG_CONFIG_HOME/git/ignore */
        if (!excludes_file)
                excludes_file = xdg_config_home("ignore");
        if (excludes_file && !access_or_warn(excludes_file, R_OK, 0))
index 756bdd050e6df9e90bb08edc73617351d1c1f9b1..52f1178db4ce35103545a3e1ee00a3000b08eb90 100644 (file)
@@ -45,14 +45,6 @@ void bitmap_set(struct bitmap *self, size_t pos)
        self->words[block] |= EWAH_MASK(pos);
 }
 
-void bitmap_clear(struct bitmap *self, size_t pos)
-{
-       size_t block = EWAH_BLOCK(pos);
-
-       if (block < self->word_alloc)
-               self->words[block] &= ~EWAH_MASK(pos);
-}
-
 int bitmap_get(struct bitmap *self, size_t pos)
 {
        size_t block = EWAH_BLOCK(pos);
@@ -137,30 +129,6 @@ void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other)
                self->words[i++] |= word;
 }
 
-void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data)
-{
-       size_t pos = 0, i;
-
-       for (i = 0; i < self->word_alloc; ++i) {
-               eword_t word = self->words[i];
-               uint32_t offset;
-
-               if (word == (eword_t)~0) {
-                       for (offset = 0; offset < BITS_IN_EWORD; ++offset)
-                               callback(pos++, data);
-               } else {
-                       for (offset = 0; offset < BITS_IN_EWORD; ++offset) {
-                               if ((word >> offset) == 0)
-                                       break;
-
-                               offset += ewah_bit_ctz64(word >> offset);
-                               callback(pos + offset, data);
-                       }
-                       pos += BITS_IN_EWORD;
-               }
-       }
-}
-
 size_t bitmap_popcount(struct bitmap *self)
 {
        size_t i, count = 0;
index b9fdda1d3d2e033ddcfa7c1b31730d4188c75306..d59b1afe3d6a83b89dc30e19e61f8a3e9b1cc0aa 100644 (file)
@@ -276,6 +276,18 @@ void ewah_each_bit(struct ewah_bitmap *self, void (*callback)(size_t, void*), vo
        }
 }
 
+/**
+ * Clear all the bits in the bitmap. Does not free or resize
+ * memory.
+ */
+static void ewah_clear(struct ewah_bitmap *self)
+{
+       self->buffer_size = 1;
+       self->buffer[0] = 0;
+       self->bit_size = 0;
+       self->rlw = self->buffer;
+}
+
 struct ewah_bitmap *ewah_new(void)
 {
        struct ewah_bitmap *self;
@@ -288,14 +300,6 @@ struct ewah_bitmap *ewah_new(void)
        return self;
 }
 
-void ewah_clear(struct ewah_bitmap *self)
-{
-       self->buffer_size = 1;
-       self->buffer[0] = 0;
-       self->bit_size = 0;
-       self->rlw = self->buffer;
-}
-
 void ewah_free(struct ewah_bitmap *self)
 {
        if (!self)
@@ -376,25 +380,6 @@ void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent)
                read_new_rlw(it);
 }
 
-void ewah_not(struct ewah_bitmap *self)
-{
-       size_t pointer = 0;
-
-       while (pointer < self->buffer_size) {
-               eword_t *word = &self->buffer[pointer];
-               size_t literals, k;
-
-               rlw_xor_run_bit(word);
-               ++pointer;
-
-               literals = rlw_get_literal_words(word);
-               for (k = 0; k < literals; ++k) {
-                       self->buffer[pointer] = ~self->buffer[pointer];
-                       ++pointer;
-               }
-       }
-}
-
 void ewah_xor(
        struct ewah_bitmap *ewah_i,
        struct ewah_bitmap *ewah_j,
@@ -459,216 +444,6 @@ void ewah_xor(
        out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
 }
 
-void ewah_and(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out)
-{
-       struct rlw_iterator rlw_i;
-       struct rlw_iterator rlw_j;
-       size_t literals;
-
-       rlwit_init(&rlw_i, ewah_i);
-       rlwit_init(&rlw_j, ewah_j);
-
-       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
-               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
-                       struct rlw_iterator *prey, *predator;
-
-                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
-                               prey = &rlw_i;
-                               predator = &rlw_j;
-                       } else {
-                               prey = &rlw_j;
-                               predator = &rlw_i;
-                       }
-
-                       if (predator->rlw.running_bit == 0) {
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(prey,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       } else {
-                               size_t index = rlwit_discharge(prey, out,
-                                       predator->rlw.running_len, 0);
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len - index);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       }
-               }
-
-               literals = min_size(
-                       rlw_i.rlw.literal_words,
-                       rlw_j.rlw.literal_words);
-
-               if (literals) {
-                       size_t k;
-
-                       for (k = 0; k < literals; ++k) {
-                               ewah_add(out,
-                                       rlw_i.buffer[rlw_i.literal_word_start + k] &
-                                       rlw_j.buffer[rlw_j.literal_word_start + k]
-                               );
-                       }
-
-                       rlwit_discard_first_words(&rlw_i, literals);
-                       rlwit_discard_first_words(&rlw_j, literals);
-               }
-       }
-
-       if (rlwit_word_size(&rlw_i) > 0)
-               rlwit_discharge_empty(&rlw_i, out);
-       else
-               rlwit_discharge_empty(&rlw_j, out);
-
-       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
-}
-
-void ewah_and_not(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out)
-{
-       struct rlw_iterator rlw_i;
-       struct rlw_iterator rlw_j;
-       size_t literals;
-
-       rlwit_init(&rlw_i, ewah_i);
-       rlwit_init(&rlw_j, ewah_j);
-
-       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
-               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
-                       struct rlw_iterator *prey, *predator;
-
-                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
-                               prey = &rlw_i;
-                               predator = &rlw_j;
-                       } else {
-                               prey = &rlw_j;
-                               predator = &rlw_i;
-                       }
-
-                       if ((predator->rlw.running_bit && prey == &rlw_i) ||
-                               (!predator->rlw.running_bit && prey != &rlw_i)) {
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(prey,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       } else {
-                               size_t index;
-                               int negate_words;
-
-                               negate_words = (&rlw_i != prey);
-                               index = rlwit_discharge(prey, out,
-                                       predator->rlw.running_len, negate_words);
-                               ewah_add_empty_words(out, negate_words,
-                                       predator->rlw.running_len - index);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       }
-               }
-
-               literals = min_size(
-                       rlw_i.rlw.literal_words,
-                       rlw_j.rlw.literal_words);
-
-               if (literals) {
-                       size_t k;
-
-                       for (k = 0; k < literals; ++k) {
-                               ewah_add(out,
-                                       rlw_i.buffer[rlw_i.literal_word_start + k] &
-                                       ~(rlw_j.buffer[rlw_j.literal_word_start + k])
-                               );
-                       }
-
-                       rlwit_discard_first_words(&rlw_i, literals);
-                       rlwit_discard_first_words(&rlw_j, literals);
-               }
-       }
-
-       if (rlwit_word_size(&rlw_i) > 0)
-               rlwit_discharge(&rlw_i, out, ~0, 0);
-       else
-               rlwit_discharge_empty(&rlw_j, out);
-
-       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
-}
-
-void ewah_or(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out)
-{
-       struct rlw_iterator rlw_i;
-       struct rlw_iterator rlw_j;
-       size_t literals;
-
-       rlwit_init(&rlw_i, ewah_i);
-       rlwit_init(&rlw_j, ewah_j);
-
-       while (rlwit_word_size(&rlw_i) > 0 && rlwit_word_size(&rlw_j) > 0) {
-               while (rlw_i.rlw.running_len > 0 || rlw_j.rlw.running_len > 0) {
-                       struct rlw_iterator *prey, *predator;
-
-                       if (rlw_i.rlw.running_len < rlw_j.rlw.running_len) {
-                               prey = &rlw_i;
-                               predator = &rlw_j;
-                       } else {
-                               prey = &rlw_j;
-                               predator = &rlw_i;
-                       }
-
-                       if (predator->rlw.running_bit) {
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(prey,
-                                       predator->rlw.running_len);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       } else {
-                               size_t index = rlwit_discharge(prey, out,
-                                       predator->rlw.running_len, 0);
-                               ewah_add_empty_words(out, 0,
-                                       predator->rlw.running_len - index);
-                               rlwit_discard_first_words(predator,
-                                       predator->rlw.running_len);
-                       }
-               }
-
-               literals = min_size(
-                       rlw_i.rlw.literal_words,
-                       rlw_j.rlw.literal_words);
-
-               if (literals) {
-                       size_t k;
-
-                       for (k = 0; k < literals; ++k) {
-                               ewah_add(out,
-                                       rlw_i.buffer[rlw_i.literal_word_start + k] |
-                                       rlw_j.buffer[rlw_j.literal_word_start + k]
-                               );
-                       }
-
-                       rlwit_discard_first_words(&rlw_i, literals);
-                       rlwit_discard_first_words(&rlw_j, literals);
-               }
-       }
-
-       if (rlwit_word_size(&rlw_i) > 0)
-               rlwit_discharge(&rlw_i, out, ~0, 0);
-       else
-               rlwit_discharge(&rlw_j, out, ~0, 0);
-
-       out->bit_size = max_size(ewah_i->bit_size, ewah_j->bit_size);
-}
-
-
 #define BITMAP_POOL_MAX 16
 static struct ewah_bitmap *bitmap_pool[BITMAP_POOL_MAX];
 static size_t bitmap_pool_size;
index 33c08c40f896f32d9a1c251c1456755e949d91bd..9035ee65ea8db67c373492fcb1755f8b1499679e 100644 (file)
 #include "ewok.h"
 #include "strbuf.h"
 
-int ewah_serialize_native(struct ewah_bitmap *self, int fd)
-{
-       uint32_t write32;
-       size_t to_write = self->buffer_size * 8;
-
-       /* 32 bit -- bit size for the map */
-       write32 = (uint32_t)self->bit_size;
-       if (write(fd, &write32, 4) != 4)
-               return -1;
-
-       /** 32 bit -- number of compressed 64-bit words */
-       write32 = (uint32_t)self->buffer_size;
-       if (write(fd, &write32, 4) != 4)
-               return -1;
-
-       if (write(fd, self->buffer, to_write) != to_write)
-               return -1;
-
-       /** 32 bit -- position for the RLW */
-       write32 = self->rlw - self->buffer;
-       if (write(fd, &write32, 4) != 4)
-               return -1;
-
-       return (3 * 4) + to_write;
-}
-
 int ewah_serialize_to(struct ewah_bitmap *self,
                      int (*write_fun)(void *, const void *, size_t),
                      void *data)
@@ -100,16 +74,6 @@ int ewah_serialize_to(struct ewah_bitmap *self,
        return (3 * 4) + (self->buffer_size * 8);
 }
 
-static int write_helper(void *fd, const void *buf, size_t len)
-{
-       return write((intptr_t)fd, buf, len);
-}
-
-int ewah_serialize(struct ewah_bitmap *self, int fd)
-{
-       return ewah_serialize_to(self, write_helper, (void *)(intptr_t)fd);
-}
-
 static int write_strbuf(void *user_data, const void *data, size_t len)
 {
        struct strbuf *sb = user_data;
@@ -168,58 +132,3 @@ ssize_t ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len)
 
        return ptr - (const uint8_t *)map;
 }
-
-int ewah_deserialize(struct ewah_bitmap *self, int fd)
-{
-       size_t i;
-       eword_t dump[2048];
-       const size_t words_per_dump = sizeof(dump) / sizeof(eword_t);
-       uint32_t bitsize, word_count, rlw_pos;
-
-       eword_t *buffer = NULL;
-       size_t words_left;
-
-       ewah_clear(self);
-
-       /* 32 bit -- bit size for the map */
-       if (read(fd, &bitsize, 4) != 4)
-               return -1;
-
-       self->bit_size = (size_t)ntohl(bitsize);
-
-       /** 32 bit -- number of compressed 64-bit words */
-       if (read(fd, &word_count, 4) != 4)
-               return -1;
-
-       self->buffer_size = self->alloc_size = (size_t)ntohl(word_count);
-       REALLOC_ARRAY(self->buffer, self->alloc_size);
-
-       /** 64 bit x N -- compressed words */
-       buffer = self->buffer;
-       words_left = self->buffer_size;
-
-       while (words_left >= words_per_dump) {
-               if (read(fd, dump, sizeof(dump)) != sizeof(dump))
-                       return -1;
-
-               for (i = 0; i < words_per_dump; ++i, ++buffer)
-                       *buffer = ntohll(dump[i]);
-
-               words_left -= words_per_dump;
-       }
-
-       if (words_left) {
-               if (read(fd, dump, words_left * 8) != words_left * 8)
-                       return -1;
-
-               for (i = 0; i < words_left; ++i, ++buffer)
-                       *buffer = ntohll(dump[i]);
-       }
-
-       /** 32 bit -- position for the RLW */
-       if (read(fd, &rlw_pos, 4) != 4)
-               return -1;
-
-       self->rlw = self->buffer + ntohl(rlw_pos);
-       return 0;
-}
index b9643b7d0f4420b0ebbd7315a3308407bdfafa53..5093d43e2f00e2bec4290c1fe918d8a0593e2e8c 100644 (file)
@@ -104,11 +104,3 @@ size_t rlwit_discharge(
 
        return index;
 }
-
-void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out)
-{
-       while (rlwit_word_size(it) > 0) {
-               ewah_add_empty_words(out, 0, rlwit_word_size(it));
-               rlwit_discard_first_words(it, rlwit_word_size(it));
-       }
-}
index 357fd93c84edbe79f9bcb10e2947ed935ac9f0df..84b2a29faa0c57410168ad5afb2dbaa7a49ddd60 100644 (file)
@@ -72,12 +72,6 @@ void ewah_pool_free(struct ewah_bitmap *self);
  */
 struct ewah_bitmap *ewah_new(void);
 
-/**
- * Clear all the bits in the bitmap. Does not free or resize
- * memory.
- */
-void ewah_clear(struct ewah_bitmap *self);
-
 /**
  * Free all the memory of the bitmap
  */
@@ -86,22 +80,12 @@ void ewah_free(struct ewah_bitmap *self);
 int ewah_serialize_to(struct ewah_bitmap *self,
                      int (*write_fun)(void *out, const void *buf, size_t len),
                      void *out);
-int ewah_serialize(struct ewah_bitmap *self, int fd);
-int ewah_serialize_native(struct ewah_bitmap *self, int fd);
 int ewah_serialize_strbuf(struct ewah_bitmap *self, struct strbuf *);
 
-int ewah_deserialize(struct ewah_bitmap *self, int fd);
 ssize_t ewah_read_mmap(struct ewah_bitmap *self, const void *map, size_t len);
 
 uint32_t ewah_checksum(struct ewah_bitmap *self);
 
-/**
- * Logical not (bitwise negation) in-place on the bitmap
- *
- * This operation is linear time based on the size of the bitmap.
- */
-void ewah_not(struct ewah_bitmap *self);
-
 /**
  * Call the given callback with the position of every single bit
  * that has been set on the bitmap.
@@ -164,26 +148,11 @@ void ewah_iterator_init(struct ewah_iterator *it, struct ewah_bitmap *parent);
  */
 int ewah_iterator_next(eword_t *next, struct ewah_iterator *it);
 
-void ewah_or(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out);
-
-void ewah_and_not(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out);
-
 void ewah_xor(
        struct ewah_bitmap *ewah_i,
        struct ewah_bitmap *ewah_j,
        struct ewah_bitmap *out);
 
-void ewah_and(
-       struct ewah_bitmap *ewah_i,
-       struct ewah_bitmap *ewah_j,
-       struct ewah_bitmap *out);
-
 /**
  * Direct word access
  */
@@ -204,7 +173,6 @@ struct bitmap {
 
 struct bitmap *bitmap_new(void);
 void bitmap_set(struct bitmap *self, size_t pos);
-void bitmap_clear(struct bitmap *self, size_t pos);
 int bitmap_get(struct bitmap *self, size_t pos);
 void bitmap_reset(struct bitmap *self);
 void bitmap_free(struct bitmap *self);
@@ -218,7 +186,6 @@ void bitmap_and_not(struct bitmap *self, struct bitmap *other);
 void bitmap_or_ewah(struct bitmap *self, struct ewah_bitmap *other);
 void bitmap_or(struct bitmap *self, const struct bitmap *other);
 
-void bitmap_each_bit(struct bitmap *self, ewah_callback callback, void *data);
 size_t bitmap_popcount(struct bitmap *self);
 
 #endif
index bb3c6ff7e05c0e3cb51a2ae5a065baa372e1d7f4..7cdfdd0c020e37abbc30624b2ea1bcf4ef0ddfee 100644 (file)
@@ -98,7 +98,6 @@ void rlwit_init(struct rlw_iterator *it, struct ewah_bitmap *bitmap);
 void rlwit_discard_first_words(struct rlw_iterator *it, size_t x);
 size_t rlwit_discharge(
        struct rlw_iterator *it, struct ewah_bitmap *out, size_t max, int negate);
-void rlwit_discharge_empty(struct rlw_iterator *it, struct ewah_bitmap *out);
 
 static inline size_t rlwit_word_size(struct rlw_iterator *it)
 {
index 3ea5781029de661fb083fcd99acbae7c39c22904..89bb0c9db3de9b380aad53709d882cb04f7d054a 100644 (file)
@@ -1076,7 +1076,7 @@ static int store_object(
                return 1;
        }
 
-       if (last && last->data.buf && last->depth < max_depth
+       if (last && last->data.len && last->data.buf && last->depth < max_depth
                && dat->len > the_hash_algo->rawsz) {
 
                delta_count_attempts_by_type[type]++;
diff --git a/fetch-negotiator.c b/fetch-negotiator.c
new file mode 100644 (file)
index 0000000..2675d12
--- /dev/null
@@ -0,0 +1,8 @@
+#include "git-compat-util.h"
+#include "fetch-negotiator.h"
+#include "negotiator/default.h"
+
+void fetch_negotiator_init(struct fetch_negotiator *negotiator)
+{
+       default_negotiator_init(negotiator);
+}
diff --git a/fetch-negotiator.h b/fetch-negotiator.h
new file mode 100644 (file)
index 0000000..b1290aa
--- /dev/null
@@ -0,0 +1,57 @@
+#ifndef FETCH_NEGOTIATOR
+#define FETCH_NEGOTIATOR
+
+struct commit;
+
+/*
+ * An object that supplies the information needed to negotiate the contents of
+ * the to-be-sent packfile during a fetch.
+ *
+ * To set up the negotiator, call fetch_negotiator_init(), then known_common()
+ * (0 or more times), then add_tip() (0 or more times).
+ *
+ * Then, when "have" lines are required, call next(). Call ack() to report what
+ * the server tells us.
+ *
+ * Once negotiation is done, call release(). The negotiator then cannot be used
+ * (unless reinitialized with fetch_negotiator_init()).
+ */
+struct fetch_negotiator {
+       /*
+        * Before negotiation starts, indicate that the server is known to have
+        * this commit.
+        */
+       void (*known_common)(struct fetch_negotiator *, struct commit *);
+
+       /*
+        * Once this function is invoked, known_common() cannot be invoked any
+        * more.
+        *
+        * Indicate that this commit and all its ancestors are to be checked
+        * for commonality with the server.
+        */
+       void (*add_tip)(struct fetch_negotiator *, struct commit *);
+
+       /*
+        * Once this function is invoked, known_common() and add_tip() cannot
+        * be invoked any more.
+        *
+        * Return the next commit that the client should send as a "have" line.
+        */
+       const struct object_id *(*next)(struct fetch_negotiator *);
+
+       /*
+        * Inform the negotiator that the server has the given commit. This
+        * method must only be called on commits returned by next().
+        */
+       int (*ack)(struct fetch_negotiator *, struct commit *);
+
+       void (*release)(struct fetch_negotiator *);
+
+       /* internal use */
+       void *data;
+};
+
+void fetch_negotiator_init(struct fetch_negotiator *negotiator);
+
+#endif
index 853624f811c59c17af88814ebeecf4154095a19c..48fe63dd6cf935d02724a0d862f43c070083bbf2 100644 (file)
@@ -19,7 +19,7 @@ static void fetch_refs(const char *remote_name, struct ref *ref)
 
        transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
        transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1");
-       transport_fetch_refs(transport, ref);
+       transport_fetch_refs(transport, ref, NULL);
        fetch_if_missing = original_fetch_if_missing;
 }
 
index d60d83f174be139aefbbcaa8fce662b3c10d8169..5056e22e6d01751eddb17391e5cb68051a5ff9ff 100644 (file)
 #include "connect.h"
 #include "transport.h"
 #include "version.h"
-#include "prio-queue.h"
 #include "sha1-array.h"
 #include "oidset.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "connected.h"
+#include "fetch-negotiator.h"
 
 static int transfer_unpack_limit = -1;
 static int fetch_unpack_limit = -1;
@@ -37,13 +38,7 @@ static const char *alternate_shallow_file;
 
 /* Remember to update object flag allocation in object.h */
 #define COMPLETE       (1U << 0)
-#define COMMON         (1U << 1)
-#define COMMON_REF     (1U << 2)
-#define SEEN           (1U << 3)
-#define POPPED         (1U << 4)
-#define ALTERNATE      (1U << 5)
-
-static int marked;
+#define ALTERNATE      (1U << 1)
 
 /*
  * After sending this many "have"s if we do not get any new ACK , we
@@ -51,8 +46,7 @@ static int marked;
  */
 #define MAX_IN_VAIN 256
 
-static struct prio_queue rev_list = { compare_commits_by_commit_date };
-static int non_common_revs, multi_ack, use_sideband;
+static int multi_ack, use_sideband;
 /* Allow specifying sha1 if it is a ref tip. */
 #define ALLOW_TIP_SHA1 01
 /* Allow request of a sha1 if it is reachable from a ref (possibly hidden ref). */
@@ -94,7 +88,9 @@ static void cache_one_alternate(const char *refname,
        cache->items[cache->nr++] = obj;
 }
 
-static void for_each_cached_alternate(void (*cb)(struct object *))
+static void for_each_cached_alternate(struct fetch_negotiator *negotiator,
+                                     void (*cb)(struct fetch_negotiator *,
+                                                struct object *))
 {
        static int initialized;
        static struct alternate_object_cache cache;
@@ -106,32 +102,19 @@ static void for_each_cached_alternate(void (*cb)(struct object *))
        }
 
        for (i = 0; i < cache.nr; i++)
-               cb(cache.items[i]);
+               cb(negotiator, cache.items[i]);
 }
 
-static void rev_list_push(struct commit *commit, int mark)
-{
-       if (!(commit->object.flags & mark)) {
-               commit->object.flags |= mark;
-
-               if (parse_commit(commit))
-                       return;
-
-               prio_queue_put(&rev_list, commit);
-
-               if (!(commit->object.flags & COMMON))
-                       non_common_revs++;
-       }
-}
-
-static int rev_list_insert_ref(const char *refname, const struct object_id *oid)
+static int rev_list_insert_ref(struct fetch_negotiator *negotiator,
+                              const char *refname,
+                              const struct object_id *oid)
 {
        struct object *o = deref_tag(the_repository,
                                     parse_object(the_repository, oid),
                                     refname, 0);
 
        if (o && o->type == OBJ_COMMIT)
-               rev_list_push((struct commit *)o, SEEN);
+               negotiator->add_tip(negotiator, (struct commit *)o);
 
        return 0;
 }
@@ -139,100 +122,7 @@ static int rev_list_insert_ref(const char *refname, const struct object_id *oid)
 static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid,
                                   int flag, void *cb_data)
 {
-       return rev_list_insert_ref(refname, oid);
-}
-
-static int clear_marks(const char *refname, const struct object_id *oid,
-                      int flag, void *cb_data)
-{
-       struct object *o = deref_tag(the_repository,
-                                    parse_object(the_repository, oid),
-                                    refname, 0);
-
-       if (o && o->type == OBJ_COMMIT)
-               clear_commit_marks((struct commit *)o,
-                                  COMMON | COMMON_REF | SEEN | POPPED);
-       return 0;
-}
-
-/*
-   This function marks a rev and its ancestors as common.
-   In some cases, it is desirable to mark only the ancestors (for example
-   when only the server does not yet know that they are common).
-*/
-
-static void mark_common(struct commit *commit,
-               int ancestors_only, int dont_parse)
-{
-       if (commit != NULL && !(commit->object.flags & COMMON)) {
-               struct object *o = (struct object *)commit;
-
-               if (!ancestors_only)
-                       o->flags |= COMMON;
-
-               if (!(o->flags & SEEN))
-                       rev_list_push(commit, SEEN);
-               else {
-                       struct commit_list *parents;
-
-                       if (!ancestors_only && !(o->flags & POPPED))
-                               non_common_revs--;
-                       if (!o->parsed && !dont_parse)
-                               if (parse_commit(commit))
-                                       return;
-
-                       for (parents = commit->parents;
-                                       parents;
-                                       parents = parents->next)
-                               mark_common(parents->item, 0, dont_parse);
-               }
-       }
-}
-
-/*
-  Get the next rev to send, ignoring the common.
-*/
-
-static const struct object_id *get_rev(void)
-{
-       struct commit *commit = NULL;
-
-       while (commit == NULL) {
-               unsigned int mark;
-               struct commit_list *parents;
-
-               if (rev_list.nr == 0 || non_common_revs == 0)
-                       return NULL;
-
-               commit = prio_queue_get(&rev_list);
-               parse_commit(commit);
-               parents = commit->parents;
-
-               commit->object.flags |= POPPED;
-               if (!(commit->object.flags & COMMON))
-                       non_common_revs--;
-
-               if (commit->object.flags & COMMON) {
-                       /* do not send "have", and ignore ancestors */
-                       commit = NULL;
-                       mark = COMMON | SEEN;
-               } else if (commit->object.flags & COMMON_REF)
-                       /* send "have", and ignore ancestors */
-                       mark = COMMON | SEEN;
-               else
-                       /* send "have", also for its ancestors */
-                       mark = SEEN;
-
-               while (parents) {
-                       if (!(parents->item->object.flags & SEEN))
-                               rev_list_push(parents->item, mark);
-                       if (mark & COMMON)
-                               mark_common(parents->item, 1, 0);
-                       parents = parents->next;
-               }
-       }
-
-       return &commit->object.oid;
+       return rev_list_insert_ref(cb_data, refname, oid);
 }
 
 enum ack_type {
@@ -301,9 +191,10 @@ static void send_request(struct fetch_pack_args *args,
                write_or_die(fd, buf->buf, buf->len);
 }
 
-static void insert_one_alternate_object(struct object *obj)
+static void insert_one_alternate_object(struct fetch_negotiator *negotiator,
+                                       struct object *obj)
 {
-       rev_list_insert_ref(NULL, &obj->oid);
+       rev_list_insert_ref(negotiator, NULL, &obj->oid);
 }
 
 #define INITIAL_FLUSH 16
@@ -326,7 +217,8 @@ static int next_flush(int stateless_rpc, int count)
        return count;
 }
 
-static int find_common(struct fetch_pack_args *args,
+static int find_common(struct fetch_negotiator *negotiator,
+                      struct fetch_pack_args *args,
                       int fd[2], struct object_id *result_oid,
                       struct ref *refs)
 {
@@ -341,12 +233,9 @@ static int find_common(struct fetch_pack_args *args,
 
        if (args->stateless_rpc && multi_ack == 1)
                die(_("--stateless-rpc requires multi_ack_detailed"));
-       if (marked)
-               for_each_ref(clear_marks, NULL);
-       marked = 1;
 
-       for_each_ref(rev_list_insert_ref_oid, NULL);
-       for_each_cached_alternate(insert_one_alternate_object);
+       for_each_ref(rev_list_insert_ref_oid, negotiator);
+       for_each_cached_alternate(negotiator, insert_one_alternate_object);
 
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
@@ -464,7 +353,7 @@ static int find_common(struct fetch_pack_args *args,
        retval = -1;
        if (args->no_dependents)
                goto done;
-       while ((oid = get_rev())) {
+       while ((oid = negotiator->next(negotiator))) {
                packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid));
                print_verbose(args, "have %s", oid_to_hex(oid));
                in_vain++;
@@ -502,11 +391,14 @@ static int find_common(struct fetch_pack_args *args,
                                        struct commit *commit =
                                                lookup_commit(the_repository,
                                                              result_oid);
+                                       int was_common;
+
                                        if (!commit)
                                                die(_("invalid commit %s"), oid_to_hex(result_oid));
+                                       was_common = negotiator->ack(negotiator, commit);
                                        if (args->stateless_rpc
                                         && ack == ACK_common
-                                        && !(commit->object.flags & COMMON)) {
+                                        && !was_common) {
                                                /* We need to replay the have for this object
                                                 * on the next RPC request so the peer knows
                                                 * it is in common with us.
@@ -523,13 +415,10 @@ static int find_common(struct fetch_pack_args *args,
                                        } else if (!args->stateless_rpc
                                                   || ack != ACK_common)
                                                in_vain = 0;
-                                       mark_common(commit, 0, 1);
                                        retval = 0;
                                        got_continue = 1;
-                                       if (ack == ACK_ready) {
-                                               clear_prio_queue(&rev_list);
+                                       if (ack == ACK_ready)
                                                got_ready = 1;
-                                       }
                                        break;
                                        }
                                }
@@ -539,6 +428,8 @@ static int find_common(struct fetch_pack_args *args,
                                print_verbose(args, _("giving up"));
                                break; /* give up */
                        }
+                       if (got_ready)
+                               break;
                }
        }
 done:
@@ -713,7 +604,8 @@ static void filter_refs(struct fetch_pack_args *args,
        *refs = newlist;
 }
 
-static void mark_alternate_complete(struct object *obj)
+static void mark_alternate_complete(struct fetch_negotiator *unused,
+                                   struct object *obj)
 {
        mark_complete(&obj->oid);
 }
@@ -740,12 +632,21 @@ static int add_loose_objects_to_set(const struct object_id *oid,
        return 0;
 }
 
-static int everything_local(struct fetch_pack_args *args,
-                           struct ref **refs,
-                           struct ref **sought, int nr_sought)
+/*
+ * Mark recent commits available locally and reachable from a local ref as
+ * COMPLETE. If args->no_dependents is false, also mark COMPLETE remote refs as
+ * COMMON_REF (otherwise, we are not planning to participate in negotiation, and
+ * thus do not need COMMON_REF marks).
+ *
+ * The cutoff time for recency is determined by this heuristic: it is the
+ * earliest commit time of the objects in refs that are commits and that we know
+ * the commit time of.
+ */
+static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
+                                        struct fetch_pack_args *args,
+                                        struct ref **refs)
 {
        struct ref *ref;
-       int retval;
        int old_save_commit_buffer = save_commit_buffer;
        timestamp_t cutoff = 0;
        struct oidset loose_oid_set = OIDSET_INIT;
@@ -793,7 +694,7 @@ static int everything_local(struct fetch_pack_args *args,
        if (!args->no_dependents) {
                if (!args->deepen) {
                        for_each_ref(mark_complete_oid, NULL);
-                       for_each_cached_alternate(mark_alternate_complete);
+                       for_each_cached_alternate(NULL, mark_alternate_complete);
                        commit_list_sort_by_date(&complete);
                        if (cutoff)
                                mark_recent_complete_commits(args, cutoff);
@@ -812,15 +713,23 @@ static int everything_local(struct fetch_pack_args *args,
                        if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
                                continue;
 
-                       if (!(o->flags & SEEN)) {
-                               rev_list_push((struct commit *)o, COMMON_REF | SEEN);
-
-                               mark_common((struct commit *)o, 1, 1);
-                       }
+                       negotiator->known_common(negotiator,
+                                                (struct commit *)o);
                }
        }
 
-       filter_refs(args, refs, sought, nr_sought);
+       save_commit_buffer = old_save_commit_buffer;
+}
+
+/*
+ * Returns 1 if every object pointed to by the given remote refs is available
+ * locally and reachable from a local ref, and 0 otherwise.
+ */
+static int everything_local(struct fetch_pack_args *args,
+                           struct ref **refs)
+{
+       struct ref *ref;
+       int retval;
 
        for (retval = 1, ref = *refs; ref ; ref = ref->next) {
                const struct object_id *remote = &ref->old_oid;
@@ -837,8 +746,6 @@ static int everything_local(struct fetch_pack_args *args,
                              ref->name);
        }
 
-       save_commit_buffer = old_save_commit_buffer;
-
        return retval;
 }
 
@@ -989,6 +896,8 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        struct object_id oid;
        const char *agent_feature;
        int agent_len;
+       struct fetch_negotiator negotiator;
+       fetch_negotiator_init(&negotiator);
 
        sort_ref_list(&ref, ref_compare_name);
        QSORT(sought, nr_sought, cmp_ref_by_name);
@@ -1061,11 +970,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        if (!server_supports("deepen-relative") && args->deepen_relative)
                die(_("Server does not support --deepen"));
 
-       if (everything_local(args, &ref, sought, nr_sought)) {
+       mark_complete_and_common_ref(&negotiator, args, &ref);
+       filter_refs(args, &ref, sought, nr_sought);
+       if (everything_local(args, &ref)) {
                packet_flush(fd[1]);
                goto all_done;
        }
-       if (find_common(args, fd, &oid, ref) < 0)
+       if (find_common(&negotiator, args, fd, &oid, ref) < 0)
                if (!args->keep_pack)
                        /* When cloning, it is not unusual to have
                         * no common commit.
@@ -1085,6 +996,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                die(_("git fetch-pack: fetch failed."));
 
  all_done:
+       negotiator.release(&negotiator);
        return ref;
 }
 
@@ -1110,9 +1022,10 @@ static void add_shallow_requests(struct strbuf *req_buf,
 
 static void add_wants(const struct ref *wants, struct strbuf *req_buf)
 {
+       int use_ref_in_want = server_supports_feature("fetch", "ref-in-want", 0);
+
        for ( ; wants ; wants = wants->next) {
                const struct object_id *remote = &wants->old_oid;
-               const char *remote_hex;
                struct object *o;
 
                /*
@@ -1130,8 +1043,10 @@ static void add_wants(const struct ref *wants, struct strbuf *req_buf)
                        continue;
                }
 
-               remote_hex = oid_to_hex(remote);
-               packet_buf_write(req_buf, "want %s\n", remote_hex);
+               if (!use_ref_in_want || wants->exact_oid)
+                       packet_buf_write(req_buf, "want %s\n", oid_to_hex(remote));
+               else
+                       packet_buf_write(req_buf, "want-ref %s\n", wants->name);
        }
 }
 
@@ -1146,13 +1061,15 @@ static void add_common(struct strbuf *req_buf, struct oidset *common)
        }
 }
 
-static int add_haves(struct strbuf *req_buf, int *haves_to_send, int *in_vain)
+static int add_haves(struct fetch_negotiator *negotiator,
+                    struct strbuf *req_buf,
+                    int *haves_to_send, int *in_vain)
 {
        int ret = 0;
        int haves_added = 0;
        const struct object_id *oid;
 
-       while ((oid = get_rev())) {
+       while ((oid = negotiator->next(negotiator))) {
                packet_buf_write(req_buf, "have %s\n", oid_to_hex(oid));
                if (++haves_added >= *haves_to_send)
                        break;
@@ -1171,7 +1088,8 @@ static int add_haves(struct strbuf *req_buf, int *haves_to_send, int *in_vain)
        return ret;
 }
 
-static int send_fetch_request(int fd_out, const struct fetch_pack_args *args,
+static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
+                             const struct fetch_pack_args *args,
                              const struct ref *wants, struct oidset *common,
                              int *haves_to_send, int *in_vain)
 {
@@ -1227,7 +1145,7 @@ static int send_fetch_request(int fd_out, const struct fetch_pack_args *args,
                add_common(&req_buf, common);
 
                /* Add initial haves */
-               ret = add_haves(&req_buf, haves_to_send, in_vain);
+               ret = add_haves(negotiator, &req_buf, haves_to_send, in_vain);
        }
 
        /* Send request */
@@ -1264,7 +1182,9 @@ static int process_section_header(struct packet_reader *reader,
        return ret;
 }
 
-static int process_acks(struct packet_reader *reader, struct oidset *common)
+static int process_acks(struct fetch_negotiator *negotiator,
+                       struct packet_reader *reader,
+                       struct oidset *common)
 {
        /* received */
        int received_ready = 0;
@@ -1283,13 +1203,12 @@ static int process_acks(struct packet_reader *reader, struct oidset *common)
                                struct commit *commit;
                                oidset_insert(common, &oid);
                                commit = lookup_commit(the_repository, &oid);
-                               mark_common(commit, 0, 1);
+                               negotiator->ack(negotiator, commit);
                        }
                        continue;
                }
 
                if (!strcmp(reader->line, "ready")) {
-                       clear_prio_queue(&rev_list);
                        received_ready = 1;
                        continue;
                }
@@ -1342,6 +1261,32 @@ static void receive_shallow_info(struct fetch_pack_args *args,
        args->deepen = 1;
 }
 
+static void receive_wanted_refs(struct packet_reader *reader, struct ref *refs)
+{
+       process_section_header(reader, "wanted-refs", 0);
+       while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
+               struct object_id oid;
+               const char *end;
+               struct ref *r = NULL;
+
+               if (parse_oid_hex(reader->line, &oid, &end) || *end++ != ' ')
+                       die("expected wanted-ref, got '%s'", reader->line);
+
+               for (r = refs; r; r = r->next) {
+                       if (!strcmp(end, r->name)) {
+                               oidcpy(&r->old_oid, &oid);
+                               break;
+                       }
+               }
+
+               if (!r)
+                       die("unexpected wanted-ref: '%s'", reader->line);
+       }
+
+       if (reader->status != PACKET_READ_DELIM)
+               die("error processing wanted refs: %d", reader->status);
+}
+
 enum fetch_state {
        FETCH_CHECK_LOCAL = 0,
        FETCH_SEND_REQUEST,
@@ -1362,6 +1307,8 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
        struct packet_reader reader;
        int in_vain = 0;
        int haves_to_send = INITIAL_FLUSH;
+       struct fetch_negotiator negotiator;
+       fetch_negotiator_init(&negotiator);
        packet_reader_init(&reader, fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE);
 
@@ -1377,21 +1324,21 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                        if (args->depth > 0 || args->deepen_since || args->deepen_not)
                                args->deepen = 1;
 
-                       if (marked)
-                               for_each_ref(clear_marks, NULL);
-                       marked = 1;
-
-                       for_each_ref(rev_list_insert_ref_oid, NULL);
-                       for_each_cached_alternate(insert_one_alternate_object);
-
                        /* Filter 'ref' by 'sought' and those that aren't local */
-                       if (everything_local(args, &ref, sought, nr_sought))
+                       mark_complete_and_common_ref(&negotiator, args, &ref);
+                       filter_refs(args, &ref, sought, nr_sought);
+                       if (everything_local(args, &ref))
                                state = FETCH_DONE;
                        else
                                state = FETCH_SEND_REQUEST;
+
+                       for_each_ref(rev_list_insert_ref_oid, &negotiator);
+                       for_each_cached_alternate(&negotiator,
+                                                 insert_one_alternate_object);
                        break;
                case FETCH_SEND_REQUEST:
-                       if (send_fetch_request(fd[1], args, ref, &common,
+                       if (send_fetch_request(&negotiator, fd[1], args, ref,
+                                              &common,
                                               &haves_to_send, &in_vain))
                                state = FETCH_GET_PACK;
                        else
@@ -1399,7 +1346,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                        break;
                case FETCH_PROCESS_ACKS:
                        /* Process ACKs/NAKs */
-                       switch (process_acks(&reader, &common)) {
+                       switch (process_acks(&negotiator, &reader, &common)) {
                        case 2:
                                state = FETCH_GET_PACK;
                                break;
@@ -1416,6 +1363,9 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                        if (process_section_header(&reader, "shallow-info", 1))
                                receive_shallow_info(args, &reader);
 
+                       if (process_section_header(&reader, "wanted-refs", 1))
+                               receive_wanted_refs(&reader, ref);
+
                        /* get the pack */
                        process_section_header(&reader, "packfile", 0);
                        if (get_pack(args, fd, pack_lockfile))
@@ -1428,6 +1378,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                }
        }
 
+       negotiator.release(&negotiator);
        oidset_clear(&common);
        return ref;
 }
@@ -1478,12 +1429,13 @@ static int remove_duplicates_in_refs(struct ref **ref, int nr)
 }
 
 static void update_shallow(struct fetch_pack_args *args,
-                          struct ref **sought, int nr_sought,
+                          struct ref *refs,
                           struct shallow_info *si)
 {
        struct oid_array ref = OID_ARRAY_INIT;
        int *status;
        int i;
+       struct ref *r;
 
        if (args->deepen && alternate_shallow_file) {
                if (*alternate_shallow_file == '\0') { /* --unshallow */
@@ -1525,8 +1477,8 @@ static void update_shallow(struct fetch_pack_args *args,
        remove_nonexistent_theirs_shallow(si);
        if (!si->nr_ours && !si->nr_theirs)
                return;
-       for (i = 0; i < nr_sought; i++)
-               oid_array_append(&ref, &sought[i]->old_oid);
+       for (r = refs; r; r = r->next)
+               oid_array_append(&ref, &r->old_oid);
        si->ref = &ref;
 
        if (args->update_shallow) {
@@ -1560,17 +1512,29 @@ static void update_shallow(struct fetch_pack_args *args,
         * remote is also shallow, check what ref is safe to update
         * without updating .git/shallow
         */
-       status = xcalloc(nr_sought, sizeof(*status));
+       status = xcalloc(ref.nr, sizeof(*status));
        assign_shallow_commits_to_refs(si, NULL, status);
        if (si->nr_ours || si->nr_theirs) {
-               for (i = 0; i < nr_sought; i++)
+               for (r = refs, i = 0; r; r = r->next, i++)
                        if (status[i])
-                               sought[i]->status = REF_STATUS_REJECT_SHALLOW;
+                               r->status = REF_STATUS_REJECT_SHALLOW;
        }
        free(status);
        oid_array_clear(&ref);
 }
 
+static int iterate_ref_map(void *cb_data, struct object_id *oid)
+{
+       struct ref **rm = cb_data;
+       struct ref *ref = *rm;
+
+       if (!ref)
+               return -1; /* end of the list */
+       *rm = ref->next;
+       oidcpy(oid, &ref->old_oid);
+       return 0;
+}
+
 struct ref *fetch_pack(struct fetch_pack_args *args,
                       int fd[], struct child_process *conn,
                       const struct ref *ref,
@@ -1599,7 +1563,25 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                ref_cpy = do_fetch_pack(args, fd, ref, sought, nr_sought,
                                        &si, pack_lockfile);
        reprepare_packed_git(the_repository);
-       update_shallow(args, sought, nr_sought, &si);
+
+       if (!args->cloning && args->deepen) {
+               struct check_connected_options opt = CHECK_CONNECTED_INIT;
+               struct ref *iterator = ref_cpy;
+               opt.shallow_file = alternate_shallow_file;
+               if (args->deepen)
+                       opt.is_deepening_fetch = 1;
+               if (check_connected(iterate_ref_map, &iterator, &opt)) {
+                       error(_("remote did not send all necessary objects"));
+                       free_refs(ref_cpy);
+                       ref_cpy = NULL;
+                       rollback_lock_file(&shallow_lock);
+                       goto cleanup;
+               }
+               args->connectivity_checked = 1;
+       }
+
+       update_shallow(args, ref_cpy, &si);
+cleanup:
        clear_shallow_info(&si);
        return ref_cpy;
 }
index bb45a366a82a4a7dae2524e0845ac33128076c57..2160be9164b4df0f46f4a604c7c1715371cc8977 100644 (file)
@@ -41,6 +41,21 @@ struct fetch_pack_args {
         * regardless of which object flags it uses (if any).
         */
        unsigned no_dependents:1;
+
+       /*
+        * Because fetch_pack() overwrites the shallow file upon a
+        * successful deepening non-clone fetch, if this struct
+        * specifies such a fetch, fetch_pack() needs to perform a
+        * connectivity check before deciding if a fetch is successful
+        * (and overwriting the shallow file). fetch_pack() sets this
+        * field to 1 if such a connectivity check was performed.
+        *
+        * This is different from check_self_contained_and_connected
+        * in that the former allows existing objects in the
+        * repository to satisfy connectivity needs, whereas the
+        * latter doesn't.
+        */
+       unsigned connectivity_checked:1;
 };
 
 /*
diff --git a/fsck.c b/fsck.c
index 93459798bcb2144081b829c3a860b294ba5a3be3..a0cee0be590020e4ff4b42da86c322ba4d0010ae 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -64,7 +64,7 @@ static struct oidset gitmodules_done = OIDSET_INIT;
        FUNC(ZERO_PADDED_DATE, ERROR) \
        FUNC(GITMODULES_MISSING, ERROR) \
        FUNC(GITMODULES_BLOB, ERROR) \
-       FUNC(GITMODULES_PARSE, ERROR) \
+       FUNC(GITMODULES_LARGE, ERROR) \
        FUNC(GITMODULES_NAME, ERROR) \
        FUNC(GITMODULES_SYMLINK, ERROR) \
        /* warnings */ \
@@ -78,6 +78,7 @@ static struct oidset gitmodules_done = OIDSET_INIT;
        FUNC(ZERO_PADDED_FILEMODE, WARN) \
        FUNC(NUL_IN_COMMIT, WARN) \
        /* infos (reported as warnings, but ignored by default) */ \
+       FUNC(GITMODULES_PARSE, INFO) \
        FUNC(BAD_TAG_NAME, INFO) \
        FUNC(MISSING_TAGGER_ENTRY, INFO)
 
@@ -317,6 +318,13 @@ static void append_msg_id(struct strbuf *sb, const char *msg_id)
        strbuf_addstr(sb, ": ");
 }
 
+static int object_on_skiplist(struct fsck_options *opts, struct object *obj)
+{
+       if (opts && opts->skiplist && obj)
+               return oid_array_lookup(opts->skiplist, &obj->oid) >= 0;
+       return 0;
+}
+
 __attribute__((format (printf, 4, 5)))
 static int report(struct fsck_options *options, struct object *object,
        enum fsck_msg_id id, const char *fmt, ...)
@@ -328,8 +336,7 @@ static int report(struct fsck_options *options, struct object *object,
        if (msg_type == FSCK_IGNORE)
                return 0;
 
-       if (options->skiplist && object &&
-                       oid_array_lookup(options->skiplist, &object->oid) >= 0)
+       if (object_on_skiplist(options, object))
                return 0;
 
        if (msg_type == FSCK_FATAL)
@@ -994,11 +1001,15 @@ static int fsck_blob(struct blob *blob, const char *buf,
                     unsigned long size, struct fsck_options *options)
 {
        struct fsck_gitmodules_data data;
+       struct config_options config_opts = { 0 };
 
        if (!oidset_contains(&gitmodules_found, &blob->object.oid))
                return 0;
        oidset_insert(&gitmodules_done, &blob->object.oid);
 
+       if (object_on_skiplist(options, &blob->object))
+               return 0;
+
        if (!buf) {
                /*
                 * A missing buffer here is a sign that the caller found the
@@ -1006,15 +1017,16 @@ static int fsck_blob(struct blob *blob, const char *buf,
                 * that an error.
                 */
                return report(options, &blob->object,
-                             FSCK_MSG_GITMODULES_PARSE,
+                             FSCK_MSG_GITMODULES_LARGE,
                              ".gitmodules too large to parse");
        }
 
        data.obj = &blob->object;
        data.options = options;
        data.ret = 0;
+       config_opts.error_action = CONFIG_ERROR_SILENT;
        if (git_config_from_mem(fsck_gitmodules_fn, CONFIG_ORIGIN_BLOB,
-                               ".gitmodules", buf, size, &data))
+                               ".gitmodules", buf, size, &data, &config_opts))
                data.ret |= report(options, &blob->object,
                                   FSCK_MSG_GITMODULES_PARSE,
                                   "could not parse gitmodules blob");
index ccceaf19a74373bb23b2a2b1aa71b7f0bb909d54..5c5afa2b985e2e524601518bbb8fb8d3c8efda61 100755 (executable)
@@ -372,6 +372,7 @@ while read commit parents; do
        git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
 
        report_progress
+       test -f "$workdir"/../map/$commit && continue
 
        case "$filter_subdir" in
        "")
index 0354d4df5cadd5145245d755a8868608b849719f..b449db1cc9020ad1d7a5e66418d6c97f088419b2 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -27,6 +27,22 @@ import zlib
 import ctypes
 import errno
 
+# support basestring in python3
+try:
+    unicode = unicode
+except NameError:
+    # 'unicode' is undefined, must be Python 3
+    str = str
+    unicode = str
+    bytes = bytes
+    basestring = (str,bytes)
+else:
+    # 'unicode' exists, must be Python 2
+    str = str
+    unicode = unicode
+    bytes = str
+    basestring = basestring
+
 try:
     from subprocess import CalledProcessError
 except ImportError:
@@ -767,7 +783,7 @@ def gitDeleteRef(ref):
 _gitConfig = {}
 
 def gitConfig(key, typeSpecifier=None):
-    if not _gitConfig.has_key(key):
+    if key not in _gitConfig:
         cmd = [ "git", "config" ]
         if typeSpecifier:
             cmd += [ typeSpecifier ]
@@ -781,12 +797,12 @@ def gitConfigBool(key):
        variable is set to true, and False if set to false or not present
        in the config."""
 
-    if not _gitConfig.has_key(key):
+    if key not in _gitConfig:
         _gitConfig[key] = gitConfig(key, '--bool') == "true"
     return _gitConfig[key]
 
 def gitConfigInt(key):
-    if not _gitConfig.has_key(key):
+    if key not in _gitConfig:
         cmd = [ "git", "config", "--int", key ]
         s = read_pipe(cmd, ignore_error=True)
         v = s.strip()
@@ -797,7 +813,7 @@ def gitConfigInt(key):
     return _gitConfig[key]
 
 def gitConfigList(key):
-    if not _gitConfig.has_key(key):
+    if key not in _gitConfig:
         s = read_pipe(["git", "config", "--get-all", key], ignore_error=True)
         _gitConfig[key] = s.strip().splitlines()
         if _gitConfig[key] == ['']:
@@ -855,7 +871,7 @@ def findUpstreamBranchPoint(head = "HEAD"):
         tip = branches[branch]
         log = extractLogMessageFromGitCommit(tip)
         settings = extractSettingsGitLog(log)
-        if settings.has_key("depot-paths"):
+        if "depot-paths" in settings:
             paths = ",".join(settings["depot-paths"])
             branchByDepotPath[paths] = "remotes/p4/" + branch
 
@@ -865,9 +881,9 @@ def findUpstreamBranchPoint(head = "HEAD"):
         commit = head + "~%s" % parent
         log = extractLogMessageFromGitCommit(commit)
         settings = extractSettingsGitLog(log)
-        if settings.has_key("depot-paths"):
+        if "depot-paths" in settings:
             paths = ",".join(settings["depot-paths"])
-            if branchByDepotPath.has_key(paths):
+            if paths in branchByDepotPath:
                 return [branchByDepotPath[paths], settings]
 
         parent = parent + 1
@@ -876,7 +892,7 @@ def findUpstreamBranchPoint(head = "HEAD"):
 
 def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent=True):
     if not silent:
-        print ("Creating/updating branch(es) in %s based on origin branch(es)"
+        print("Creating/updating branch(es) in %s based on origin branch(es)"
                % localRefPrefix)
 
     originPrefix = "origin/p4/"
@@ -891,29 +907,29 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
         originHead = line
 
         original = extractSettingsGitLog(extractLogMessageFromGitCommit(originHead))
-        if (not original.has_key('depot-paths')
-            or not original.has_key('change')):
+        if ('depot-paths' not in original
+            or 'change' not in original):
             continue
 
         update = False
         if not gitBranchExists(remoteHead):
             if verbose:
-                print "creating %s" % remoteHead
+                print("creating %s" % remoteHead)
             update = True
         else:
             settings = extractSettingsGitLog(extractLogMessageFromGitCommit(remoteHead))
-            if settings.has_key('change') > 0:
+            if 'change' in settings:
                 if settings['depot-paths'] == original['depot-paths']:
                     originP4Change = int(original['change'])
                     p4Change = int(settings['change'])
                     if originP4Change > p4Change:
-                        print ("%s (%s) is newer than %s (%s). "
+                        print("%s (%s) is newer than %s (%s). "
                                "Updating p4 branch from origin."
                                % (originHead, originP4Change,
                                   remoteHead, p4Change))
                         update = True
                 else:
-                    print ("Ignoring: %s was imported from %s while "
+                    print("Ignoring: %s was imported from %s while "
                            "%s was imported from %s"
                            % (originHead, ','.join(original['depot-paths']),
                               remoteHead, ','.join(settings['depot-paths'])))
@@ -1002,7 +1018,7 @@ def p4ChangesForPaths(depotPaths, changeRange, requestedBlockSize):
 
         # Insert changes in chronological order
         for entry in reversed(result):
-            if not entry.has_key('change'):
+            if 'change' not in entry:
                 continue
             changes.add(int(entry['change']))
 
@@ -1312,7 +1328,7 @@ class P4UserMap:
 
         results = p4CmdList("user -o")
         for r in results:
-            if r.has_key('User'):
+            if 'User' in r:
                 self.myP4UserId = r['User']
                 return r['User']
         die("Could not find your p4 user id")
@@ -1336,7 +1352,7 @@ class P4UserMap:
         self.emails = {}
 
         for output in p4CmdList("users"):
-            if not output.has_key("User"):
+            if "User" not in output:
                 continue
             self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
             self.emails[output["Email"]] = output["User"]
@@ -1381,9 +1397,9 @@ class P4Debug(Command):
     def run(self, args):
         j = 0
         for output in p4CmdList(args):
-            print 'Element: %d' % j
+            print('Element: %d' % j)
             j += 1
-            print output
+            print(output)
         return True
 
 class P4RollBack(Command):
@@ -1424,14 +1440,14 @@ class P4RollBack(Command):
 
                 if len(p4Cmd("changes -m 1 "  + ' '.join (['%s...@%s' % (p, maxChange)
                                                            for p in depotPaths]))) == 0:
-                    print "Branch %s did not exist at change %s, deleting." % (ref, maxChange)
+                    print("Branch %s did not exist at change %s, deleting." % (ref, maxChange))
                     system("git update-ref -d %s `git rev-parse %s`" % (ref, ref))
                     continue
 
                 while change and int(change) > maxChange:
                     changed = True
                     if self.verbose:
-                        print "%s is at %s ; rewinding towards %s" % (ref, change, maxChange)
+                        print("%s is at %s ; rewinding towards %s" % (ref, change, maxChange))
                     system("git update-ref %s \"%s^\"" % (ref, ref))
                     log = extractLogMessageFromGitCommit(ref)
                     settings =  extractSettingsGitLog(log)
@@ -1441,7 +1457,7 @@ class P4RollBack(Command):
                     change = settings['change']
 
                 if changed:
-                    print "%s rewound to %s" % (ref, change)
+                    print("%s rewound to %s" % (ref, change))
 
         return True
 
@@ -1577,10 +1593,10 @@ class P4Submit(Command, P4UserMap):
         except:
             # cleanup our temporary file
             os.unlink(outFileName)
-            print "Failed to strip RCS keywords in %s" % file
+            print("Failed to strip RCS keywords in %s" % file)
             raise
 
-        print "Patched up RCS keywords in %s" % file
+        print("Patched up RCS keywords in %s" % file)
 
     def p4UserForCommit(self,id):
         # Return the tuple (perforce user,git email) for a given git commit id
@@ -1588,7 +1604,7 @@ class P4Submit(Command, P4UserMap):
         gitEmail = read_pipe(["git", "log", "--max-count=1",
                               "--format=%ae", id])
         gitEmail = gitEmail.strip()
-        if not self.emails.has_key(gitEmail):
+        if gitEmail not in self.emails:
             return (None,gitEmail)
         else:
             return (self.emails[gitEmail],gitEmail)
@@ -1600,7 +1616,7 @@ class P4Submit(Command, P4UserMap):
             if not user:
                 msg = "Cannot find p4 user for email %s in commit %s." % (email, id)
                 if gitConfigBool("git-p4.allowMissingP4Users"):
-                    print "%s" % msg
+                    print("%s" % msg)
                 else:
                     die("Error: %s\nSet git-p4.allowMissingP4Users to true to allow this." % msg)
 
@@ -1612,14 +1628,14 @@ class P4Submit(Command, P4UserMap):
         results = p4CmdList("client -o")        # find the current client
         client = None
         for r in results:
-            if r.has_key('Client'):
+            if 'Client' in r:
                 client = r['Client']
                 break
         if not client:
             die("could not get client spec")
         results = p4CmdList(["changes", "-c", client, "-m", "1"])
         for r in results:
-            if r.has_key('change'):
+            if 'change' in r:
                 return r['change']
         die("Could not get changelist number for last submit - cannot patch up user details")
 
@@ -1637,10 +1653,10 @@ class P4Submit(Command, P4UserMap):
 
         result = p4CmdList("change -f -i", stdin=input)
         for r in result:
-            if r.has_key('code'):
+            if 'code' in r:
                 if r['code'] == 'error':
                     die("Could not modify user field of changelist %s to %s:%s" % (changelist, newUser, r['data']))
-            if r.has_key('data'):
+            if 'data' in r:
                 print("Updated user field for changelist %s to %s" % (changelist, newUser))
                 return
         die("Could not modify user field of changelist %s to %s" % (changelist, newUser))
@@ -1650,7 +1666,7 @@ class P4Submit(Command, P4UserMap):
         # which are required to modify changelists.
         results = p4CmdList(["protects", self.depotPath])
         for r in results:
-            if r.has_key('perm'):
+            if 'perm' in r:
                 if r['perm'] == 'admin':
                     return 1
                 if r['perm'] == 'super':
@@ -1690,7 +1706,7 @@ class P4Submit(Command, P4UserMap):
         if changelist:
             args.append(str(changelist))
         for entry in p4CmdList(args):
-            if not entry.has_key('code'):
+            if 'code' not in entry:
                 continue
             if entry['code'] == 'stat':
                 change_entry = entry
@@ -1699,7 +1715,7 @@ class P4Submit(Command, P4UserMap):
             die('Failed to decode output of p4 change -o')
         for key, value in change_entry.iteritems():
             if key.startswith('File'):
-                if settings.has_key('depot-paths'):
+                if 'depot-paths' in settings:
                     if not [p for p in settings['depot-paths']
                             if p4PathStartsWith(value, p)]:
                         continue
@@ -1710,7 +1726,7 @@ class P4Submit(Command, P4UserMap):
                 continue
         # Output in the order expected by prepareLogMessage
         for key in ['Change', 'Client', 'User', 'Status', 'Description', 'Jobs']:
-            if not change_entry.has_key(key):
+            if key not in change_entry:
                 continue
             template += '\n'
             template += key + ':'
@@ -1738,7 +1754,7 @@ class P4Submit(Command, P4UserMap):
         mtime = os.stat(template_file).st_mtime
 
         # invoke the editor
-        if os.environ.has_key("P4EDITOR") and (os.environ.get("P4EDITOR") != ""):
+        if "P4EDITOR" in os.environ and (os.environ.get("P4EDITOR") != ""):
             editor = os.environ.get("P4EDITOR")
         else:
             editor = read_pipe("git var GIT_EDITOR").strip()
@@ -1762,7 +1778,7 @@ class P4Submit(Command, P4UserMap):
 
     def get_diff_description(self, editedFiles, filesToAdd, symlinks):
         # diff
-        if os.environ.has_key("P4DIFF"):
+        if "P4DIFF" in os.environ:
             del(os.environ["P4DIFF"])
         diff = ""
         for editedFile in editedFiles:
@@ -1792,8 +1808,8 @@ class P4Submit(Command, P4UserMap):
     def applyCommit(self, id):
         """Apply one commit, return True if it succeeded."""
 
-        print "Applying", read_pipe(["git", "show", "-s",
-                                     "--format=format:%h %s", id])
+        print("Applying", read_pipe(["git", "show", "-s",
+                                     "--format=format:%h %s", id]))
 
         (p4User, gitEmail) = self.p4UserForCommit(id)
 
@@ -1825,7 +1841,7 @@ class P4Submit(Command, P4UserMap):
                     filesToDelete.remove(path)
 
                 dst_mode = int(diff['dst_mode'], 8)
-                if dst_mode == 0120000:
+                if dst_mode == 0o120000:
                     symlinks.add(path)
 
             elif modifier == "D":
@@ -1883,7 +1899,7 @@ class P4Submit(Command, P4UserMap):
         if os.system(tryPatchCmd) != 0:
             fixed_rcs_keywords = False
             patch_succeeded = False
-            print "Unfortunately applying the change failed!"
+            print("Unfortunately applying the change failed!")
 
             # Patch failed, maybe it's just RCS keyword woes. Look through
             # the patch to see if that's possible.
@@ -1901,13 +1917,13 @@ class P4Submit(Command, P4UserMap):
                         for line in read_pipe_lines(["git", "diff", "%s^..%s" % (id, id), file]):
                             if regexp.search(line):
                                 if verbose:
-                                    print "got keyword match on %s in %s in %s" % (pattern, line, file)
+                                    print("got keyword match on %s in %s in %s" % (pattern, line, file))
                                 kwfiles[file] = pattern
                                 break
 
                 for file in kwfiles:
                     if verbose:
-                        print "zapping %s with %s" % (line,pattern)
+                        print("zapping %s with %s" % (line,pattern))
                     # File is being deleted, so not open in p4.  Must
                     # disable the read-only bit on windows.
                     if self.isWindows and file not in editedFiles:
@@ -1916,7 +1932,7 @@ class P4Submit(Command, P4UserMap):
                     fixed_rcs_keywords = True
 
             if fixed_rcs_keywords:
-                print "Retrying the patch with RCS keywords cleaned up"
+                print("Retrying the patch with RCS keywords cleaned up")
                 if os.system(tryPatchCmd) == 0:
                     patch_succeeded = True
 
@@ -1984,34 +2000,34 @@ class P4Submit(Command, P4UserMap):
             # Leave the p4 tree prepared, and the submit template around
             # and let the user decide what to do next
             #
-            print
-            print "P4 workspace prepared for submission."
-            print "To submit or revert, go to client workspace"
-            print "  " + self.clientPath
-            print
-            print "To submit, use \"p4 submit\" to write a new description,"
-            print "or \"p4 submit -i <%s\" to use the one prepared by" \
-                  " \"git p4\"." % fileName
-            print "You can delete the file \"%s\" when finished." % fileName
+            print()
+            print("P4 workspace prepared for submission.")
+            print("To submit or revert, go to client workspace")
+            print("  " + self.clientPath)
+            print()
+            print("To submit, use \"p4 submit\" to write a new description,")
+            print("or \"p4 submit -i <%s\" to use the one prepared by" \
+                  " \"git p4\"." % fileName)
+            print("You can delete the file \"%s\" when finished." % fileName)
 
             if self.preserveUser and p4User and not self.p4UserIsMe(p4User):
-                print "To preserve change ownership by user %s, you must\n" \
+                print("To preserve change ownership by user %s, you must\n" \
                       "do \"p4 change -f <change>\" after submitting and\n" \
-                      "edit the User field."
+                      "edit the User field.")
             if pureRenameCopy:
-                print "After submitting, renamed files must be re-synced."
-                print "Invoke \"p4 sync -f\" on each of these files:"
+                print("After submitting, renamed files must be re-synced.")
+                print("Invoke \"p4 sync -f\" on each of these files:")
                 for f in pureRenameCopy:
-                    print "  " + f
+                    print("  " + f)
 
-            print
-            print "To revert the changes, use \"p4 revert ...\", and delete"
-            print "the submit template file \"%s\"" % fileName
+            print()
+            print("To revert the changes, use \"p4 revert ...\", and delete")
+            print("the submit template file \"%s\"" % fileName)
             if filesToAdd:
-                print "Since the commit adds new files, they must be deleted:"
+                print("Since the commit adds new files, they must be deleted:")
                 for f in filesToAdd:
-                    print "  " + f
-            print
+                    print("  " + f)
+            print()
             return True
 
         #
@@ -2078,17 +2094,17 @@ class P4Submit(Command, P4UserMap):
 
             if not m.match(name):
                 if verbose:
-                    print "tag %s does not match regexp %s" % (name, validLabelRegexp)
+                    print("tag %s does not match regexp %s" % (name, validLabelRegexp))
                 continue
 
             # Get the p4 commit this corresponds to
             logMessage = extractLogMessageFromGitCommit(name)
             values = extractSettingsGitLog(logMessage)
 
-            if not values.has_key('change'):
+            if 'change' not in values:
                 # a tag pointing to something not sent to p4; ignore
                 if verbose:
-                    print "git tag %s does not give a p4 commit" % name
+                    print("git tag %s does not give a p4 commit" % name)
                 continue
             else:
                 changelist = values['change']
@@ -2123,10 +2139,10 @@ class P4Submit(Command, P4UserMap):
                 labelTemplate += "\t%s\n" % depot_side
 
             if self.dry_run:
-                print "Would create p4 label %s for tag" % name
+                print("Would create p4 label %s for tag" % name)
             elif self.prepare_p4_only:
-                print "Not creating p4 label %s for tag due to option" \
-                      " --prepare-p4-only" % name
+                print("Not creating p4 label %s for tag due to option" \
+                      " --prepare-p4-only" % name)
             else:
                 p4_write_pipe(["label", "-i"], labelTemplate)
 
@@ -2135,7 +2151,7 @@ class P4Submit(Command, P4UserMap):
                           ["%s@%s" % (depot_side, changelist) for depot_side in clientSpec.mappings])
 
                 if verbose:
-                    print "created p4 label for tag %s" % name
+                    print("created p4 label for tag %s" % name)
 
     def run(self, args):
         if len(args) == 0:
@@ -2179,10 +2195,10 @@ class P4Submit(Command, P4UserMap):
             self.conflict_behavior = val
 
         if self.verbose:
-            print "Origin branch is " + self.origin
+            print("Origin branch is " + self.origin)
 
         if len(self.depotPath) == 0:
-            print "Internal error: cannot locate perforce depot path from existing branches"
+            print("Internal error: cannot locate perforce depot path from existing branches")
             sys.exit(128)
 
         self.useClientSpec = False
@@ -2203,7 +2219,7 @@ class P4Submit(Command, P4UserMap):
         if self.clientPath == "":
             die("Error: Cannot locate perforce checkout of %s in client view" % self.depotPath)
 
-        print "Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath)
+        print("Perforce checkout for depot path %s located at %s" % (self.depotPath, self.clientPath))
         self.oldWorkingDirectory = os.getcwd()
 
         # ensure the clientPath exists
@@ -2214,9 +2230,9 @@ class P4Submit(Command, P4UserMap):
 
         chdir(self.clientPath, is_client_path=True)
         if self.dry_run:
-            print "Would synchronize p4 checkout in %s" % self.clientPath
+            print("Would synchronize p4 checkout in %s" % self.clientPath)
         else:
-            print "Synchronizing p4 checkout..."
+            print("Synchronizing p4 checkout...")
             if new_client_dir:
                 # old one was destroyed, and maybe nobody told p4
                 p4_sync("...", "-f")
@@ -2292,13 +2308,13 @@ class P4Submit(Command, P4UserMap):
         # continue to try the rest of the patches, or quit.
         #
         if self.dry_run:
-            print "Would apply"
+            print("Would apply")
         applied = []
         last = len(commits) - 1
         for i, commit in enumerate(commits):
             if self.dry_run:
-                print " ", read_pipe(["git", "show", "-s",
-                                      "--format=format:%h %s", commit])
+                print(" ", read_pipe(["git", "show", "-s",
+                                      "--format=format:%h %s", commit]))
                 ok = True
             else:
                 ok = self.applyCommit(commit)
@@ -2306,15 +2322,15 @@ class P4Submit(Command, P4UserMap):
                 applied.append(commit)
             else:
                 if self.prepare_p4_only and i < last:
-                    print "Processing only the first commit due to option" \
-                          " --prepare-p4-only"
+                    print("Processing only the first commit due to option" \
+                          " --prepare-p4-only")
                     break
                 if i < last:
                     quit = False
                     while True:
                         # prompt for what to do, or use the option/variable
                         if self.conflict_behavior == "ask":
-                            print "What do you want to do?"
+                            print("What do you want to do?")
                             response = raw_input("[s]kip this commit but apply"
                                                  " the rest, or [q]uit? ")
                             if not response:
@@ -2328,10 +2344,10 @@ class P4Submit(Command, P4UserMap):
                                 self.conflict_behavior)
 
                         if response[0] == "s":
-                            print "Skipping this commit, but applying the rest"
+                            print("Skipping this commit, but applying the rest")
                             break
                         if response[0] == "q":
-                            print "Quitting"
+                            print("Quitting")
                             quit = True
                             break
                     if quit:
@@ -2344,7 +2360,7 @@ class P4Submit(Command, P4UserMap):
         elif self.prepare_p4_only:
             pass
         elif len(commits) == len(applied):
-            print ("All commits {0}!".format(shelved_applied))
+            print("All commits {0}!".format(shelved_applied))
 
             sync = P4Sync()
             if self.branch:
@@ -2360,17 +2376,17 @@ class P4Submit(Command, P4UserMap):
 
         else:
             if len(applied) == 0:
-                print ("No commits {0}.".format(shelved_applied))
+                print("No commits {0}.".format(shelved_applied))
             else:
-                print ("{0} only the commits marked with '*':".format(shelved_applied.capitalize()))
+                print("{0} only the commits marked with '*':".format(shelved_applied.capitalize()))
                 for c in commits:
                     if c in applied:
                         star = "*"
                     else:
                         star = " "
-                    print star, read_pipe(["git", "show", "-s",
-                                           "--format=format:%h %s",  c])
-                print "You will have to do 'git p4 sync' and rebase."
+                    print(star, read_pipe(["git", "show", "-s",
+                                           "--format=format:%h %s",  c]))
+                print("You will have to do 'git p4 sync' and rebase.")
 
         if gitConfigBool("git-p4.exportLabels"):
             self.exportLabels = True
@@ -2580,7 +2596,7 @@ class P4Sync(Command, P4UserMap):
         self.gitStream.write("progress checkpoint\n\n")
         out = self.gitOutput.readline()
         if self.verbose:
-            print "checkpoint finished: " + out
+            print("checkpoint finished: " + out)
 
     def cmp_shelved(self, path, filerev, revision):
         """ Determine if a path at revision #filerev is the same as the file
@@ -2600,7 +2616,7 @@ class P4Sync(Command, P4UserMap):
                              for path in self.cloneExclude]
         files = []
         fnum = 0
-        while commit.has_key("depotFile%s" % fnum):
+        while "depotFile%s" % fnum in commit:
             path =  commit["depotFile%s" % fnum]
 
             if [p for p in self.cloneExclude
@@ -2638,7 +2654,7 @@ class P4Sync(Command, P4UserMap):
     def extractJobsFromCommit(self, commit):
         jobs = []
         jnum = 0
-        while commit.has_key("job%s" % jnum):
+        while "job%s" % jnum in commit:
             job = commit["job%s" % jnum]
             jobs.append(job)
             jnum = jnum + 1
@@ -2686,7 +2702,7 @@ class P4Sync(Command, P4UserMap):
 
         branches = {}
         fnum = 0
-        while commit.has_key("depotFile%s" % fnum):
+        while "depotFile%s" % fnum in commit:
             path =  commit["depotFile%s" % fnum]
             found = [p for p in self.depotPaths
                      if p4PathStartsWith(path, p)]
@@ -2735,7 +2751,7 @@ class P4Sync(Command, P4UserMap):
                 encoding = gitConfig('git-p4.pathEncoding')
             path = path.decode(encoding, 'replace').encode('utf8', 'replace')
             if self.verbose:
-                print 'Path with non-ASCII characters detected. Used %s to encode: %s ' % (encoding, path)
+                print('Path with non-ASCII characters detected. Used %s to encode: %s ' % (encoding, path))
         return path
 
     # output one file from the P4 stream
@@ -2764,7 +2780,7 @@ class P4Sync(Command, P4UserMap):
                 # to nothing.  This causes p4 errors when checking out such
                 # a change, and errors here too.  Work around it by ignoring
                 # the bad symlink; hopefully a future change fixes it.
-                print "\nIgnoring empty symlink in %s" % file['depotFile']
+                print("\nIgnoring empty symlink in %s" % file['depotFile'])
                 return
             elif data[-1] == '\n':
                 contents = [data[:-1]]
@@ -2804,7 +2820,7 @@ class P4Sync(Command, P4UserMap):
             # Ideally, someday, this script can learn how to generate
             # appledouble files directly and import those to git, but
             # non-mac machines can never find a use for apple filetype.
-            print "\nIgnoring apple filetype file %s" % file['depotFile']
+            print("\nIgnoring apple filetype file %s" % file['depotFile'])
             return
 
         # Note that we do not try to de-mangle keywords on utf16 files,
@@ -2866,7 +2882,7 @@ class P4Sync(Command, P4UserMap):
             else:
                 die("Error from p4 print: %s" % err)
 
-        if marshalled.has_key('depotFile') and self.stream_have_file_info:
+        if 'depotFile' in marshalled and self.stream_have_file_info:
             # start of a new file - output the old one first
             self.streamOneP4File(self.stream_file, self.stream_contents)
             self.stream_file = {}
@@ -2938,7 +2954,7 @@ class P4Sync(Command, P4UserMap):
                       cb=streamP4FilesCbSelf)
 
             # do the last chunk
-            if self.stream_file.has_key('depotFile'):
+            if 'depotFile' in self.stream_file:
                 self.streamOneP4File(self.stream_file, self.stream_contents)
 
     def make_email(self, userid):
@@ -2953,11 +2969,11 @@ class P4Sync(Command, P4UserMap):
         """
 
         if verbose:
-            print "writing tag %s for commit %s" % (labelName, commit)
+            print("writing tag %s for commit %s" % (labelName, commit))
         gitStream.write("tag %s\n" % labelName)
         gitStream.write("from %s\n" % commit)
 
-        if labelDetails.has_key('Owner'):
+        if 'Owner' in labelDetails:
             owner = labelDetails["Owner"]
         else:
             owner = None
@@ -2972,8 +2988,8 @@ class P4Sync(Command, P4UserMap):
 
         gitStream.write("tagger %s\n" % tagger)
 
-        print "labelDetails=",labelDetails
-        if labelDetails.has_key('Description'):
+        print("labelDetails=",labelDetails)
+        if 'Description' in labelDetails:
             description = labelDetails['Description']
         else:
             description = 'Label from git p4'
@@ -3044,7 +3060,7 @@ class P4Sync(Command, P4UserMap):
 
         if len(parent) > 0:
             if self.verbose:
-                print "parent %s" % parent
+                print("parent %s" % parent)
             self.gitStream.write("from %s\n" % parent)
 
         self.streamP4Files(files)
@@ -3052,12 +3068,12 @@ class P4Sync(Command, P4UserMap):
 
         change = int(details["change"])
 
-        if self.labels.has_key(change):
+        if change in self.labels:
             label = self.labels[change]
             labelDetails = label[0]
             labelRevisions = label[1]
             if self.verbose:
-                print "Change %s is labelled %s" % (change, labelDetails)
+                print("Change %s is labelled %s" % (change, labelDetails))
 
             files = p4CmdList(["files"] + ["%s...@%s" % (p, change)
                                                 for p in self.branchPrefixes])
@@ -3075,12 +3091,12 @@ class P4Sync(Command, P4UserMap):
 
                 else:
                     if not self.silent:
-                        print ("Tag %s does not match with change %s: files do not match."
+                        print("Tag %s does not match with change %s: files do not match."
                                % (labelDetails["label"], change))
 
             else:
                 if not self.silent:
-                    print ("Tag %s does not match with change %s: file count is different."
+                    print("Tag %s does not match with change %s: file count is different."
                            % (labelDetails["label"], change))
 
     # Build a dictionary of changelists and labels, for "detect-labels" option.
@@ -3089,14 +3105,14 @@ class P4Sync(Command, P4UserMap):
 
         l = p4CmdList(["labels"] + ["%s..." % p for p in self.depotPaths])
         if len(l) > 0 and not self.silent:
-            print "Finding files belonging to labels in %s" % `self.depotPaths`
+            print("Finding files belonging to labels in %s" % self.depotPaths)
 
         for output in l:
             label = output["label"]
             revisions = {}
             newestChange = 0
             if self.verbose:
-                print "Querying files for label %s" % label
+                print("Querying files for label %s" % label)
             for file in p4CmdList(["files"] +
                                       ["%s...@%s" % (p, label)
                                           for p in self.depotPaths]):
@@ -3108,7 +3124,7 @@ class P4Sync(Command, P4UserMap):
             self.labels[newestChange] = [output, revisions]
 
         if self.verbose:
-            print "Label changes: %s" % self.labels.keys()
+            print("Label changes: %s" % self.labels.keys())
 
     # Import p4 labels as git tags. A direct mapping does not
     # exist, so assume that if all the files are at the same revision
@@ -3116,7 +3132,7 @@ class P4Sync(Command, P4UserMap):
     # just ignore.
     def importP4Labels(self, stream, p4Labels):
         if verbose:
-            print "import p4 labels: " + ' '.join(p4Labels)
+            print("import p4 labels: " + ' '.join(p4Labels))
 
         ignoredP4Labels = gitConfigList("git-p4.ignoredP4Labels")
         validLabelRegexp = gitConfig("git-p4.labelImportRegexp")
@@ -3129,7 +3145,7 @@ class P4Sync(Command, P4UserMap):
 
             if not m.match(name):
                 if verbose:
-                    print "label %s does not match regexp %s" % (name,validLabelRegexp)
+                    print("label %s does not match regexp %s" % (name,validLabelRegexp))
                 continue
 
             if name in ignoredP4Labels:
@@ -3141,7 +3157,7 @@ class P4Sync(Command, P4UserMap):
             change = p4Cmd(["changes", "-m", "1"] + ["%s...@%s" % (p, name)
                                 for p in self.depotPaths])
 
-            if change.has_key('change'):
+            if 'change' in change:
                 # find the corresponding git commit; take the oldest commit
                 changelist = int(change['change'])
                 if changelist in self.committedChanges:
@@ -3151,7 +3167,7 @@ class P4Sync(Command, P4UserMap):
                     gitCommit = read_pipe(["git", "rev-list", "--max-count=1",
                         "--reverse", ":/\[git-p4:.*change = %d\]" % changelist], ignore_error=True)
                     if len(gitCommit) == 0:
-                        print "importing label %s: could not find git commit for changelist %d" % (name, changelist)
+                        print("importing label %s: could not find git commit for changelist %d" % (name, changelist))
                     else:
                         commitFound = True
                         gitCommit = gitCommit.strip()
@@ -3161,16 +3177,16 @@ class P4Sync(Command, P4UserMap):
                     try:
                         tmwhen = time.strptime(labelDetails['Update'], "%Y/%m/%d %H:%M:%S")
                     except ValueError:
-                        print "Could not convert label time %s" % labelDetails['Update']
+                        print("Could not convert label time %s" % labelDetails['Update'])
                         tmwhen = 1
 
                     when = int(time.mktime(tmwhen))
                     self.streamTag(stream, name, labelDetails, gitCommit, when)
                     if verbose:
-                        print "p4 label %s mapped to git commit %s" % (name, gitCommit)
+                        print("p4 label %s mapped to git commit %s" % (name, gitCommit))
             else:
                 if verbose:
-                    print "Label %s has no changelists - possibly deleted?" % name
+                    print("Label %s has no changelists - possibly deleted?" % name)
 
             if not commitFound:
                 # We can't import this label; don't try again as it will get very
@@ -3200,7 +3216,7 @@ class P4Sync(Command, P4UserMap):
         for info in p4CmdList(command):
             details = p4Cmd(["branch", "-o", info["branch"]])
             viewIdx = 0
-            while details.has_key("View%s" % viewIdx):
+            while "View%s" % viewIdx in details:
                 paths = details["View%s" % viewIdx].split(" ")
                 viewIdx = viewIdx + 1
                 # require standard //depot/foo/... //depot/bar/... mapping
@@ -3215,8 +3231,8 @@ class P4Sync(Command, P4UserMap):
 
                     if destination in self.knownBranches:
                         if not self.silent:
-                            print "p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination)
-                            print "but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination)
+                            print("p4 branch %s defines a mapping from %s to %s" % (info["branch"], source, destination))
+                            print("but there exists another mapping from %s to %s already!" % (self.knownBranches[destination], destination))
                         continue
 
                     self.knownBranches[destination] = source
@@ -3266,7 +3282,7 @@ class P4Sync(Command, P4UserMap):
         d["options"] = ' '.join(sorted(option_keys.keys()))
 
     def readOptions(self, d):
-        self.keepRepoPath = (d.has_key('options')
+        self.keepRepoPath = ('options' in d
                              and ('keepRepoPath' in d['options']))
 
     def gitRefForBranch(self, branch):
@@ -3280,28 +3296,28 @@ class P4Sync(Command, P4UserMap):
 
     def gitCommitByP4Change(self, ref, change):
         if self.verbose:
-            print "looking in ref " + ref + " for change %s using bisect..." % change
+            print("looking in ref " + ref + " for change %s using bisect..." % change)
 
         earliestCommit = ""
         latestCommit = parseRevision(ref)
 
         while True:
             if self.verbose:
-                print "trying: earliest %s latest %s" % (earliestCommit, latestCommit)
+                print("trying: earliest %s latest %s" % (earliestCommit, latestCommit))
             next = read_pipe("git rev-list --bisect %s %s" % (latestCommit, earliestCommit)).strip()
             if len(next) == 0:
                 if self.verbose:
-                    print "argh"
+                    print("argh")
                 return ""
             log = extractLogMessageFromGitCommit(next)
             settings = extractSettingsGitLog(log)
             currentChange = int(settings['change'])
             if self.verbose:
-                print "current change %s" % currentChange
+                print("current change %s" % currentChange)
 
             if currentChange == change:
                 if self.verbose:
-                    print "found %s" % next
+                    print("found %s" % next)
                 return next
 
             if currentChange < change:
@@ -3347,7 +3363,7 @@ class P4Sync(Command, P4UserMap):
             if len(read_pipe(["git", "diff-tree", blob, target])) == 0:
                 parentFound = True
                 if self.verbose:
-                    print "Found parent of %s in commit %s" % (branch, blob)
+                    print("Found parent of %s in commit %s" % (branch, blob))
                 break
         if parentFound:
             return blob
@@ -3378,7 +3394,7 @@ class P4Sync(Command, P4UserMap):
                         filesForCommit = branches[branch]
 
                         if self.verbose:
-                            print "branch is %s" % branch
+                            print("branch is %s" % branch)
 
                         self.updatedBranches.add(branch)
 
@@ -3399,13 +3415,13 @@ class P4Sync(Command, P4UserMap):
                                         print("\n    Resuming with change %s" % change);
 
                                 if self.verbose:
-                                    print "parent determined through known branches: %s" % parent
+                                    print("parent determined through known branches: %s" % parent)
 
                         branch = self.gitRefForBranch(branch)
                         parent = self.gitRefForBranch(parent)
 
                         if self.verbose:
-                            print "looking for initial parent for %s; current parent is %s" % (branch, parent)
+                            print("looking for initial parent for %s; current parent is %s" % (branch, parent))
 
                         if len(parent) == 0 and branch in self.initialParents:
                             parent = self.initialParents[branch]
@@ -3415,7 +3431,7 @@ class P4Sync(Command, P4UserMap):
                         if len(parent) > 0:
                             tempBranch = "%s/%d" % (self.tempBranchLocation, change)
                             if self.verbose:
-                                print "Creating temporary branch: " + tempBranch
+                                print("Creating temporary branch: " + tempBranch)
                             self.commit(description, filesForCommit, tempBranch)
                             self.tempBranches.append(tempBranch)
                             self.checkpoint()
@@ -3424,7 +3440,7 @@ class P4Sync(Command, P4UserMap):
                             self.commit(description, filesForCommit, branch, blob)
                         else:
                             if self.verbose:
-                                print "Parent of %s not found. Committing into head of %s" % (branch, parent)
+                                print("Parent of %s not found. Committing into head of %s" % (branch, parent))
                             self.commit(description, filesForCommit, branch, parent)
                 else:
                     files = self.extractFilesFromCommit(description, shelved, change, origin_revision)
@@ -3433,7 +3449,7 @@ class P4Sync(Command, P4UserMap):
                     # only needed once, to connect to the previous commit
                     self.initialParent = ""
             except IOError:
-                print self.gitError.read()
+                print(self.gitError.read())
                 sys.exit(1)
 
     def sync_origin_only(self):
@@ -3441,11 +3457,11 @@ class P4Sync(Command, P4UserMap):
             self.hasOrigin = originP4BranchesExist()
             if self.hasOrigin:
                 if not self.silent:
-                    print 'Syncing with origin first, using "git fetch origin"'
+                    print('Syncing with origin first, using "git fetch origin"')
                 system("git fetch origin")
 
     def importHeadRevision(self, revision):
-        print "Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch)
+        print("Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch))
 
         details = {}
         details["user"] = "git perforce import user"
@@ -3497,8 +3513,8 @@ class P4Sync(Command, P4UserMap):
         try:
             self.commit(details, self.extractFilesFromCommit(details), self.branch)
         except IOError:
-            print "IO error with git fast-import. Is your git version recent enough?"
-            print self.gitError.read()
+            print("IO error with git fast-import. Is your git version recent enough?")
+            print(self.gitError.read())
 
     def openStreams(self):
         self.importProcess = subprocess.Popen(["git", "fast-import"],
@@ -3560,14 +3576,14 @@ class P4Sync(Command, P4UserMap):
 
             if len(self.p4BranchesInGit) > 1:
                 if not self.silent:
-                    print "Importing from/into multiple branches"
+                    print("Importing from/into multiple branches")
                 self.detectBranches = True
                 for branch in branches.keys():
                     self.initialParents[self.refPrefix + branch] = \
                         branches[branch]
 
             if self.verbose:
-                print "branches: %s" % self.p4BranchesInGit
+                print("branches: %s" % self.p4BranchesInGit)
 
             p4Change = 0
             for branch in self.p4BranchesInGit:
@@ -3576,8 +3592,8 @@ class P4Sync(Command, P4UserMap):
                 settings = extractSettingsGitLog(logMsg)
 
                 self.readOptions(settings)
-                if (settings.has_key('depot-paths')
-                    and settings.has_key ('change')):
+                if ('depot-paths' in settings
+                    and 'change' in settings):
                     change = int(settings['change']) + 1
                     p4Change = max(p4Change, change)
 
@@ -3590,7 +3606,7 @@ class P4Sync(Command, P4UserMap):
                             prev_list = prev.split("/")
                             cur_list = cur.split("/")
                             for i in range(0, min(len(cur_list), len(prev_list))):
-                                if cur_list[i] <> prev_list[i]:
+                                if cur_list[i] != prev_list[i]:
                                     i = i - 1
                                     break
 
@@ -3602,7 +3618,7 @@ class P4Sync(Command, P4UserMap):
                 self.depotPaths = sorted(self.previousDepotPaths)
                 self.changeRange = "@%s,#head" % p4Change
                 if not self.silent and not self.detectBranches:
-                    print "Performing incremental import into %s git branch" % self.branch
+                    print("Performing incremental import into %s git branch" % self.branch)
 
         # accept multiple ref name abbreviations:
         #    refs/foo/bar/branch -> use it exactly
@@ -3619,10 +3635,10 @@ class P4Sync(Command, P4UserMap):
 
         if len(args) == 0 and self.depotPaths:
             if not self.silent:
-                print "Depot paths: %s" % ' '.join(self.depotPaths)
+                print("Depot paths: %s" % ' '.join(self.depotPaths))
         else:
             if self.depotPaths and self.depotPaths != args:
-                print ("previous import used depot path %s and now %s was specified. "
+                print("previous import used depot path %s and now %s was specified. "
                        "This doesn't work!" % (' '.join (self.depotPaths),
                                                ' '.join (args)))
                 sys.exit(1)
@@ -3689,8 +3705,8 @@ class P4Sync(Command, P4UserMap):
             else:
                 self.getBranchMapping()
             if self.verbose:
-                print "p4-git branches: %s" % self.p4BranchesInGit
-                print "initial parents: %s" % self.initialParents
+                print("p4-git branches: %s" % self.p4BranchesInGit)
+                print("initial parents: %s" % self.initialParents)
             for b in self.p4BranchesInGit:
                 if b != "master":
 
@@ -3734,8 +3750,8 @@ class P4Sync(Command, P4UserMap):
                                     self.branch)
 
                 if self.verbose:
-                    print "Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
-                                                              self.changeRange)
+                    print("Getting p4 changes for %s...%s" % (', '.join(self.depotPaths),
+                                                              self.changeRange))
                 changes = p4ChangesForPaths(self.depotPaths, self.changeRange, self.changes_block_size)
 
                 if len(self.maxChanges) > 0:
@@ -3743,10 +3759,10 @@ class P4Sync(Command, P4UserMap):
 
             if len(changes) == 0:
                 if not self.silent:
-                    print "No changes to import!"
+                    print("No changes to import!")
             else:
                 if not self.silent and not self.detectBranches:
-                    print "Import destination: %s" % self.branch
+                    print("Import destination: %s" % self.branch)
 
                 self.updatedBranches = set()
 
@@ -3761,7 +3777,7 @@ class P4Sync(Command, P4UserMap):
                 self.importChanges(changes)
 
                 if not self.silent:
-                    print ""
+                    print("")
                     if len(self.updatedBranches) > 0:
                         sys.stdout.write("Updated branches: ")
                         for b in self.updatedBranches:
@@ -3825,7 +3841,7 @@ class P4Rebase(Command):
         # the branchpoint may be p4/foo~3, so strip off the parent
         upstream = re.sub("~[0-9]+$", "", upstream)
 
-        print "Rebasing the current branch onto %s" % upstream
+        print("Rebasing the current branch onto %s" % upstream)
         oldHead = read_pipe("git rev-parse HEAD").strip()
         system("git rebase %s" % upstream)
         system("git diff-tree --stat --summary -M %s HEAD --" % oldHead)
@@ -3879,7 +3895,7 @@ class P4Clone(P4Sync):
         if not self.cloneDestination:
             self.cloneDestination = self.defaultDestination(args)
 
-        print "Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination)
+        print("Importing from %s into %s" % (', '.join(depotPaths), self.cloneDestination))
 
         if not os.path.exists(self.cloneDestination):
             os.makedirs(self.cloneDestination)
@@ -3901,8 +3917,8 @@ class P4Clone(P4Sync):
             if not self.cloneBare:
                 system([ "git", "checkout", "-f" ])
         else:
-            print 'Not checking out any branch, use ' \
-                  '"git checkout -q -b master <branch>"'
+            print('Not checking out any branch, use ' \
+                  '"git checkout -q -b master <branch>"')
 
         # auto-set this variable if invoked with --use-client-spec
         if self.useClientSpec_from_options:
@@ -3950,7 +3966,7 @@ class P4Unshelve(Command):
         for parent in (range(65535)):
             log = extractLogMessageFromGitCommit("{0}^{1}".format(starting_point, parent))
             settings = extractSettingsGitLog(log)
-            if settings.has_key('change'):
+            if 'change' in settings:
                 return settings
 
         sys.exit("could not find git-p4 commits in {0}".format(self.origin))
@@ -4018,7 +4034,7 @@ class P4Branches(Command):
             log = extractLogMessageFromGitCommit("refs/remotes/%s" % branch)
             settings = extractSettingsGitLog(log)
 
-            print "%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"])
+            print("%s <= %s (%s)" % (branch, ",".join(settings["depot-paths"]), settings["change"]))
         return True
 
 class HelpFormatter(optparse.IndentedHelpFormatter):
@@ -4032,12 +4048,12 @@ class HelpFormatter(optparse.IndentedHelpFormatter):
             return ""
 
 def printUsage(commands):
-    print "usage: %s <command> [options]" % sys.argv[0]
-    print ""
-    print "valid commands: %s" % ", ".join(commands)
-    print ""
-    print "Try %s <command> --help for command specific help." % sys.argv[0]
-    print ""
+    print("usage: %s <command> [options]" % sys.argv[0])
+    print("")
+    print("valid commands: %s" % ", ".join(commands))
+    print("")
+    print("Try %s <command> --help for command specific help." % sys.argv[0])
+    print("")
 
 commands = {
     "debug" : P4Debug,
@@ -4062,8 +4078,8 @@ def main():
         klass = commands[cmdName]
         cmd = klass()
     except KeyError:
-        print "unknown command %s" % cmdName
-        print ""
+        print("unknown command %s" % cmdName)
+        print("")
         printUsage(commands.keys())
         sys.exit(2)
 
index cf4c0422148935906ad939c5351652a1531e5f0d..aa2f2f08728edbbfd4beb28a882b744170b426ba 100644 (file)
@@ -71,7 +71,7 @@ call_merge () {
        test -z "$strategy" && strategy=recursive
        # If cmt doesn't have a parent, don't include it as a base
        base=$(git rev-parse --verify --quiet $cmt^)
-       eval 'git-merge-$strategy' $strategy_opts $base ' -- "$hd" "$cmt"'
+       eval 'git merge-$strategy' $strategy_opts $base ' -- "$hd" "$cmt"'
        rv=$?
        case "$rv" in
        0)
@@ -88,7 +88,7 @@ call_merge () {
                ;;
        *)
                die "Unknown exit code ($rv) from command:" \
-                       "git-merge-$strategy $cmt^ -- HEAD $cmt"
+                       "git merge-$strategy $cmt^ -- HEAD $cmt"
                ;;
        esac
 }
index c51c7828e7b331710006ac9a15f3d7443dcf3816..c214c5e4d6ce21996f16031ec14dd624b75a9939 100644 (file)
@@ -891,9 +891,9 @@ $comment_char $(eval_ngettext \
 EOF
        append_todo_help
        gettext "
-       However, if you remove everything, the rebase will be aborted.
+However, if you remove everything, the rebase will be aborted.
 
-       " | git stripspace --comment-lines >>"$todo"
+" | git stripspace --comment-lines >>"$todo"
 
        if test -z "$keep_empty"
        then
index 19bdebb48025e236c596d554768012951d348632..797344764510e0a2b09bf91af08c528411a9025b 100755 (executable)
@@ -20,23 +20,23 @@ onto=!             rebase onto given branch instead of upstream
 r,rebase-merges?   try to rebase merges instead of skipping them
 p,preserve-merges! try to recreate merges instead of ignoring them
 s,strategy=!       use the given merge strategy
+X,strategy-option=! pass the argument through to the merge strategy
 no-ff!             cherry-pick all commits, even if unchanged
+f,force-rebase!    cherry-pick all commits, even if unchanged
 m,merge!           use merging strategies to rebase
 i,interactive!     let the user edit the list of commits to rebase
 x,exec=!           add exec lines after each commit of the editable list
 k,keep-empty      preserve empty commits during rebase
 allow-empty-message allow rebasing commits with empty messages
-f,force-rebase!    force rebase even if branch is up to date
-X,strategy-option=! pass the argument through to the merge strategy
 stat!              display a diffstat of what changed upstream
 n,no-stat!         do not show diffstat of what changed upstream
 verify             allow pre-rebase hook to run
 rerere-autoupdate  allow rerere to update index with resolved conflicts
 root!              rebase all reachable commits up to the root(s)
 autosquash         move commits that begin with squash!/fixup! under -i
+signoff            add a Signed-off-by: line to each commit
 committer-date-is-author-date! passed to 'git am'
 ignore-date!       passed to 'git am'
-signoff            passed to 'git am'
 whitespace=!       passed to 'git apply'
 ignore-whitespace! passed to 'git apply'
 C=!                passed to 'git apply'
@@ -95,7 +95,7 @@ rebase_cousins=
 preserve_merges=
 autosquash=
 keep_empty=
-allow_empty_message=
+allow_empty_message=--allow-empty-message
 signoff=
 test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
 case "$(git config --bool commit.gpgsign)" in
@@ -328,7 +328,7 @@ do
                do_merge=t
                ;;
        --strategy-option=*)
-               strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}")"
+               strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}" | sed -e s/^.//)"
                do_merge=t
                test -z "$strategy" && strategy=recursive
                ;;
@@ -521,6 +521,24 @@ then
        git_format_patch_opt="$git_format_patch_opt --progress"
 fi
 
+if test -n "$git_am_opt"; then
+       incompatible_opts=$(echo " $git_am_opt " | \
+                           sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/')
+       if test -n "$interactive_rebase"
+       then
+               if test -n "$incompatible_opts"
+               then
+                       die "$(gettext "error: cannot combine interactive options (--interactive, --exec, --rebase-merges, --preserve-merges, --keep-empty, --root + --onto) with am options ($incompatible_opts)")"
+               fi
+       fi
+       if test -n "$do_merge"; then
+               if test -n "$incompatible_opts"
+               then
+                       die "$(gettext "error: cannot combine merge options (--merge, --strategy, --strategy-option) with am options ($incompatible_opts)")"
+               fi
+       fi
+fi
+
 if test -n "$signoff"
 then
        test -n "$preserve_merges" &&
@@ -529,6 +547,23 @@ then
        force_rebase=t
 fi
 
+if test -n "$preserve_merges"
+then
+       # Note: incompatibility with --signoff handled in signoff block above
+       # Note: incompatibility with --interactive is just a strong warning;
+       #       git-rebase.txt caveats with "unless you know what you are doing"
+       test -n "$rebase_merges" &&
+               die "$(gettext "error: cannot combine '--preserve_merges' with '--rebase-merges'")"
+fi
+
+if test -n "$rebase_merges"
+then
+       test -n "$strategy_opts" &&
+               die "$(gettext "error: cannot combine '--rebase_merges' with '--strategy-option'")"
+       test -n "$strategy" &&
+               die "$(gettext "error: cannot combine '--rebase_merges' with '--strategy'")"
+fi
+
 if test -z "$rebase_root"
 then
        case "$#" in
index 8ec70e58ed5c4caace7701549038b63f9a3e9aad..f4c07908d2737203031bdcab7f4f30404e64d4e5 100755 (executable)
@@ -231,7 +231,7 @@ my ($validate, $confirm);
 my (@suppress_cc);
 my ($auto_8bit_encoding);
 my ($compose_encoding);
-my ($target_xfer_encoding);
+my $target_xfer_encoding = 'auto';
 
 my ($debug_net_smtp) = 0;              # Net::SMTP, see send_message()
 
@@ -645,7 +645,7 @@ if (@rev_list_opts) {
 if ($validate) {
        foreach my $f (@files) {
                unless (-p $f) {
-                       my $error = validate_patch($f);
+                       my $error = validate_patch($f, $target_xfer_encoding);
                        $error and die sprintf(__("fatal: %s: %s\nwarning: no patches were sent\n"),
                                                  $f, $error);
                }
@@ -1737,18 +1737,11 @@ sub process_file {
                        }
                }
        }
-       if (defined $target_xfer_encoding) {
-               $xfer_encoding = '8bit' if not defined $xfer_encoding;
-               $message = apply_transfer_encoding(
-                       $message, $xfer_encoding, $target_xfer_encoding);
-               $xfer_encoding = $target_xfer_encoding;
-       }
-       if (defined $xfer_encoding) {
-               push @xh, "Content-Transfer-Encoding: $xfer_encoding";
-       }
-       if (defined $xfer_encoding or $has_content_type) {
-               unshift @xh, 'MIME-Version: 1.0' unless $has_mime_version;
-       }
+       $xfer_encoding = '8bit' if not defined $xfer_encoding;
+       ($message, $xfer_encoding) = apply_transfer_encoding(
+               $message, $xfer_encoding, $target_xfer_encoding);
+       push @xh, "Content-Transfer-Encoding: $xfer_encoding";
+       unshift @xh, 'MIME-Version: 1.0' unless $has_mime_version;
 
        $needs_confirm = (
                $confirm eq "always" or
@@ -1852,13 +1845,16 @@ sub apply_transfer_encoding {
        $message = MIME::Base64::decode($message)
                if ($from eq 'base64');
 
+       $to = ($message =~ /.{999,}/) ? 'quoted-printable' : '8bit'
+               if $to eq 'auto';
+
        die __("cannot send message as 7bit")
                if ($to eq '7bit' and $message =~ /[^[:ascii:]]/);
-       return $message
+       return ($message, $to)
                if ($to eq '7bit' or $to eq '8bit');
-       return MIME::QuotedPrint::encode($message, "\n", 0)
+       return (MIME::QuotedPrint::encode($message, "\n", 0), $to)
                if ($to eq 'quoted-printable');
-       return MIME::Base64::encode($message, "\n")
+       return (MIME::Base64::encode($message, "\n"), $to)
                if ($to eq 'base64');
        die __("invalid transfer encoding");
 }
@@ -1877,7 +1873,7 @@ sub unique_email_list {
 }
 
 sub validate_patch {
-       my $fn = shift;
+       my ($fn, $xfer_encoding) = @_;
 
        if ($repo) {
                my $validate_hook = catfile(catdir($repo->repo_path(), 'hooks'),
@@ -1897,11 +1893,15 @@ sub validate_patch {
                return $hook_error if $hook_error;
        }
 
-       open(my $fh, '<', $fn)
-               or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
-       while (my $line = <$fh>) {
-               if (length($line) > 998) {
-                       return sprintf(__("%s: patch contains a line longer than 998 characters"), $.);
+       # Any long lines will be automatically fixed if we use a suitable transfer
+       # encoding.
+       unless ($xfer_encoding =~ /^(?:auto|quoted-printable|base64)$/) {
+               open(my $fh, '<', $fn)
+                       or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
+               while (my $line = <$fh>) {
+                       if (length($line) > 998) {
+                               return sprintf(__("%s: patch contains a line longer than 998 characters"), $.);
+                       }
                }
        }
        return;
index 5f9d9f6ea3713be458c006501c503d8d660e9ddf..8b5ad59bdee39eba4fc56b28d6771a291dac55f0 100755 (executable)
@@ -577,6 +577,11 @@ cmd_update()
                        die "$(eval_gettext "Unable to find current \${remote_name}/\${branch} revision in submodule path '\$sm_path'")"
                fi
 
+               if ! $(git config -f "$(git rev-parse --git-common-dir)/modules/$name/config" core.worktree) 2>/dev/null
+               then
+                       git submodule--helper connect-gitdir-workingtree "$name" "$sm_path"
+               fi
+
                if test "$subsha1" != "$sha1" || test -n "$force"
                then
                        subforce=$force
index 0647bd6348cdedd0e32f30ed23934d390eae4122..09ddfbc267e3bee3edf54e0166d92c1e6e6d331c 100644 (file)
@@ -35,7 +35,7 @@ static struct {
        { 'R', "\n[GNUPG:] REVKEYSIG "},
 };
 
-void parse_gpg_output(struct signature_check *sigc)
+static void parse_gpg_output(struct signature_check *sigc)
 {
        const char *buf = sigc->gpg_status;
        int i;
index a5e6517ae67ea5fa3f79265517381d58d99fba18..5ecff4aa0c062d8a33290d39456e24c073fd48e4 100644 (file)
@@ -33,8 +33,6 @@ void signature_check_clear(struct signature_check *sigc);
  */
 size_t parse_signature(const char *buf, size_t size);
 
-void parse_gpg_output(struct signature_check *);
-
 /*
  * Create a detached signature for the contents of "buffer" and append
  * it after "signature"; "buffer" and "signature" can be the same
diff --git a/grep.c b/grep.c
index 7e2f440955a01ae305664df37f77ed87a55c2b06..cd7fc6f66cadfcb38ad73f171b97a419610aad37 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -20,6 +20,7 @@ static const char *color_grep_slots[] = {
        [GREP_COLOR_FILENAME]       = "filename",
        [GREP_COLOR_FUNCTION]       = "function",
        [GREP_COLOR_LINENO]         = "lineNumber",
+       [GREP_COLOR_COLUMNNO]       = "column",
        [GREP_COLOR_MATCH_CONTEXT]  = "matchContext",
        [GREP_COLOR_MATCH_SELECTED] = "matchSelected",
        [GREP_COLOR_SELECTED]       = "selected",
@@ -59,6 +60,7 @@ void init_grep_defaults(void)
        color_set(opt->colors[GREP_COLOR_FILENAME], "");
        color_set(opt->colors[GREP_COLOR_FUNCTION], "");
        color_set(opt->colors[GREP_COLOR_LINENO], "");
+       color_set(opt->colors[GREP_COLOR_COLUMNNO], "");
        color_set(opt->colors[GREP_COLOR_MATCH_CONTEXT], GIT_COLOR_BOLD_RED);
        color_set(opt->colors[GREP_COLOR_MATCH_SELECTED], GIT_COLOR_BOLD_RED);
        color_set(opt->colors[GREP_COLOR_SELECTED], "");
@@ -110,6 +112,10 @@ int grep_config(const char *var, const char *value, void *cb)
                opt->linenum = git_config_bool(var, value);
                return 0;
        }
+       if (!strcmp(var, "grep.column")) {
+               opt->columnnum = git_config_bool(var, value);
+               return 0;
+       }
 
        if (!strcmp(var, "grep.fullname")) {
                opt->relative = !git_config_bool(var, value);
@@ -157,6 +163,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
        opt->extended_regexp_option = def->extended_regexp_option;
        opt->pattern_type_option = def->pattern_type_option;
        opt->linenum = def->linenum;
+       opt->columnnum = def->columnnum;
        opt->max_depth = def->max_depth;
        opt->pathname = def->pathname;
        opt->relative = def->relative;
@@ -1244,11 +1251,11 @@ static int match_one_pattern(struct grep_pat *p, char *bol, char *eol,
        return hit;
 }
 
-static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
-                          enum grep_context ctx, int collect_hits)
+static int match_expr_eval(struct grep_opt *opt, struct grep_expr *x, char *bol,
+                          char *eol, enum grep_context ctx, ssize_t *col,
+                          ssize_t *icol, int collect_hits)
 {
        int h = 0;
-       regmatch_t match;
 
        if (!x)
                die("Not a valid grep expression");
@@ -1257,25 +1264,52 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
                h = 1;
                break;
        case GREP_NODE_ATOM:
-               h = match_one_pattern(x->u.atom, bol, eol, ctx, &match, 0);
+               {
+                       regmatch_t tmp;
+                       h = match_one_pattern(x->u.atom, bol, eol, ctx,
+                                             &tmp, 0);
+                       if (h && (*col < 0 || tmp.rm_so < *col))
+                               *col = tmp.rm_so;
+               }
                break;
        case GREP_NODE_NOT:
-               h = !match_expr_eval(x->u.unary, bol, eol, ctx, 0);
+               /*
+                * Upon visiting a GREP_NODE_NOT, col and icol become swapped.
+                */
+               h = !match_expr_eval(opt, x->u.unary, bol, eol, ctx, icol, col,
+                                    0);
                break;
        case GREP_NODE_AND:
-               if (!match_expr_eval(x->u.binary.left, bol, eol, ctx, 0))
-                       return 0;
-               h = match_expr_eval(x->u.binary.right, bol, eol, ctx, 0);
+               h = match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col,
+                                   icol, 0);
+               if (h || opt->columnnum) {
+                       /*
+                        * Don't short-circuit AND when given --column, since a
+                        * NOT earlier in the tree may turn this into an OR. In
+                        * this case, see the below comment.
+                        */
+                       h &= match_expr_eval(opt, x->u.binary.right, bol, eol,
+                                            ctx, col, icol, 0);
+               }
                break;
        case GREP_NODE_OR:
-               if (!collect_hits)
-                       return (match_expr_eval(x->u.binary.left,
-                                               bol, eol, ctx, 0) ||
-                               match_expr_eval(x->u.binary.right,
-                                               bol, eol, ctx, 0));
-               h = match_expr_eval(x->u.binary.left, bol, eol, ctx, 0);
-               x->u.binary.left->hit |= h;
-               h |= match_expr_eval(x->u.binary.right, bol, eol, ctx, 1);
+               if (!(collect_hits || opt->columnnum)) {
+                       /*
+                        * Don't short-circuit OR when given --column (or
+                        * collecting hits) to ensure we don't skip a later
+                        * child that would produce an earlier match.
+                        */
+                       return (match_expr_eval(opt, x->u.binary.left, bol, eol,
+                                               ctx, col, icol, 0) ||
+                               match_expr_eval(opt, x->u.binary.right, bol,
+                                               eol, ctx, col, icol, 0));
+               }
+               h = match_expr_eval(opt, x->u.binary.left, bol, eol, ctx, col,
+                                   icol, 0);
+               if (collect_hits)
+                       x->u.binary.left->hit |= h;
+               h |= match_expr_eval(opt, x->u.binary.right, bol, eol, ctx, col,
+                                    icol, collect_hits);
                break;
        default:
                die("Unexpected node type (internal error) %d", x->node);
@@ -1286,27 +1320,43 @@ static int match_expr_eval(struct grep_expr *x, char *bol, char *eol,
 }
 
 static int match_expr(struct grep_opt *opt, char *bol, char *eol,
-                     enum grep_context ctx, int collect_hits)
+                     enum grep_context ctx, ssize_t *col,
+                     ssize_t *icol, int collect_hits)
 {
        struct grep_expr *x = opt->pattern_expression;
-       return match_expr_eval(x, bol, eol, ctx, collect_hits);
+       return match_expr_eval(opt, x, bol, eol, ctx, col, icol, collect_hits);
 }
 
 static int match_line(struct grep_opt *opt, char *bol, char *eol,
+                     ssize_t *col, ssize_t *icol,
                      enum grep_context ctx, int collect_hits)
 {
        struct grep_pat *p;
-       regmatch_t match;
+       int hit = 0;
 
        if (opt->extended)
-               return match_expr(opt, bol, eol, ctx, collect_hits);
+               return match_expr(opt, bol, eol, ctx, col, icol,
+                                 collect_hits);
 
        /* we do not call with collect_hits without being extended */
        for (p = opt->pattern_list; p; p = p->next) {
-               if (match_one_pattern(p, bol, eol, ctx, &match, 0))
-                       return 1;
+               regmatch_t tmp;
+               if (match_one_pattern(p, bol, eol, ctx, &tmp, 0)) {
+                       hit |= 1;
+                       if (!opt->columnnum) {
+                               /*
+                                * Without --column, any single match on a line
+                                * is enough to know that it needs to be
+                                * printed. With --column, scan _all_ patterns
+                                * to find the earliest.
+                                */
+                               break;
+                       }
+                       if (*col < 0 || tmp.rm_so < *col)
+                               *col = tmp.rm_so;
+               }
        }
-       return 0;
+       return hit;
 }
 
 static int match_next_pattern(struct grep_pat *p, char *bol, char *eol,
@@ -1355,7 +1405,7 @@ static int next_match(struct grep_opt *opt, char *bol, char *eol,
 }
 
 static void show_line(struct grep_opt *opt, char *bol, char *eol,
-                     const char *name, unsigned lno, char sign)
+                     const char *name, unsigned lno, ssize_t cno, char sign)
 {
        int rest = eol - bol;
        const char *match_color, *line_color = NULL;
@@ -1390,6 +1440,17 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
                output_color(opt, buf, strlen(buf), opt->colors[GREP_COLOR_LINENO]);
                output_sep(opt, sign);
        }
+       /*
+        * Treat 'cno' as the 1-indexed offset from the start of a non-context
+        * line to its first match. Otherwise, 'cno' is 0 indicating that we are
+        * being called with a context line.
+        */
+       if (opt->columnnum && cno) {
+               char buf[32];
+               xsnprintf(buf, sizeof(buf), "%"PRIuMAX, (uintmax_t)cno);
+               output_color(opt, buf, strlen(buf), opt->colors[GREP_COLOR_COLUMNNO]);
+               output_sep(opt, sign);
+       }
        if (opt->color) {
                regmatch_t match;
                enum grep_context ctx = GREP_CONTEXT_BODY;
@@ -1495,7 +1556,7 @@ static void show_funcname_line(struct grep_opt *opt, struct grep_source *gs,
                        break;
 
                if (match_funcname(opt, gs, bol, eol)) {
-                       show_line(opt, bol, eol, gs->name, lno, '=');
+                       show_line(opt, bol, eol, gs->name, lno, 0, '=');
                        break;
                }
        }
@@ -1560,7 +1621,7 @@ static void show_pre_context(struct grep_opt *opt, struct grep_source *gs,
 
                while (*eol != '\n')
                        eol++;
-               show_line(opt, bol, eol, gs->name, cur, sign);
+               show_line(opt, bol, eol, gs->name, cur, 0, sign);
                bol = eol + 1;
                cur++;
        }
@@ -1759,6 +1820,8 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
        while (left) {
                char *eol, ch;
                int hit;
+               ssize_t cno;
+               ssize_t col = -1, icol = -1;
 
                /*
                 * look_ahead() skips quickly to the line that possibly
@@ -1782,7 +1845,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                if ((ctx == GREP_CONTEXT_HEAD) && (eol == bol))
                        ctx = GREP_CONTEXT_BODY;
 
-               hit = match_line(opt, bol, eol, ctx, collect_hits);
+               hit = match_line(opt, bol, eol, &col, &icol, ctx, collect_hits);
                *eol = ch;
 
                if (collect_hits)
@@ -1823,7 +1886,18 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                                show_pre_context(opt, gs, bol, eol, lno);
                        else if (opt->funcname)
                                show_funcname_line(opt, gs, bol, lno);
-                       show_line(opt, bol, eol, gs->name, lno, ':');
+                       cno = opt->invert ? icol : col;
+                       if (cno < 0) {
+                               /*
+                                * A negative cno indicates that there was no
+                                * match on the line. We are thus inverted and
+                                * being asked to show all lines that _don't_
+                                * match a given expression. Therefore, set cno
+                                * to 0 to suggest the whole line matches.
+                                */
+                               cno = 0;
+                       }
+                       show_line(opt, bol, eol, gs->name, lno, cno + 1, ':');
                        last_hit = lno;
                        if (opt->funcbody)
                                show_function = 1;
@@ -1852,7 +1926,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                        /* If the last hit is within the post context,
                         * we need to show this line.
                         */
-                       show_line(opt, bol, eol, gs->name, lno, '-');
+                       show_line(opt, bol, eol, gs->name, lno, col + 1, '-');
                }
 
        next_line:
diff --git a/grep.h b/grep.h
index ed25be271b5b6a002fdb582fd5c11a499a4f0c94..01d2cba6f80921538d0269aefcad6f11218392b0 100644 (file)
--- a/grep.h
+++ b/grep.h
@@ -67,6 +67,7 @@ enum grep_color {
        GREP_COLOR_FILENAME,
        GREP_COLOR_FUNCTION,
        GREP_COLOR_LINENO,
+       GREP_COLOR_COLUMNNO,
        GREP_COLOR_MATCH_CONTEXT,
        GREP_COLOR_MATCH_SELECTED,
        GREP_COLOR_SELECTED,
@@ -139,6 +140,7 @@ struct grep_opt {
        int prefix_length;
        regex_t regexp;
        int linenum;
+       int columnnum;
        int invert;
        int ignore_case;
        int status_only;
diff --git a/hex.c b/hex.c
index 8df2d63728f0405a9b1dc70aa404954dd921b6bc..10af1a29e80f903fb95cbeb71be00e5fdd705e66 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -50,7 +50,7 @@ int hex_to_bytes(unsigned char *binary, const char *hex, size_t len)
 int get_sha1_hex(const char *hex, unsigned char *sha1)
 {
        int i;
-       for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
+       for (i = 0; i < the_hash_algo->rawsz; i++) {
                int val = hex2chr(hex);
                if (val < 0)
                        return -1;
@@ -69,7 +69,7 @@ int parse_oid_hex(const char *hex, struct object_id *oid, const char **end)
 {
        int ret = get_oid_hex(hex, oid);
        if (!ret)
-               *end = hex + GIT_SHA1_HEXSZ;
+               *end = hex + the_hash_algo->hexsz;
        return ret;
 }
 
@@ -79,7 +79,7 @@ char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
        char *buf = buffer;
        int i;
 
-       for (i = 0; i < GIT_SHA1_RAWSZ; i++) {
+       for (i = 0; i < the_hash_algo->rawsz; i++) {
                unsigned int val = *sha1++;
                *buf++ = hex[val >> 4];
                *buf++ = hex[val & 0xf];
index 7fa0f16eba6bbb851be38dd266d597cf450a0275..72a5fed661ca0a0f0eb1fe726e91acc8cd47512a 100644 (file)
@@ -598,11 +598,11 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
                                    lines, anchor, &begin, &end,
                                    full_name))
                        die("malformed -L argument '%s'", range_part);
-               if (lines < end || ((lines || begin) && lines < begin))
+               if ((!lines && (begin || end)) || lines < begin)
                        die("file %s has only %lu lines", name_part, lines);
                if (begin < 1)
                        begin = 1;
-               if (end < 1)
+               if (end < 1 || lines < end)
                        end = lines;
                begin--;
                line_log_data_insert(&ranges, full_name, begin, end);
index 323399d16cfa828adce913c9ff994e860ce2e7a5..232c3909ec27d3079e9228d0fcf561ce6a349754 100644 (file)
@@ -47,7 +47,7 @@ static const char *parse_loc(const char *spec, nth_line_fn_t nth_line,
                        else if (!num)
                                *ret = begin;
                        else
-                               *ret = begin + num;
+                               *ret = begin + num > 0 ? begin + num : 1;
                        return term;
                }
                return spec;
index 782c0e189f933869d3df4f1ba1e17398e95b215b..c99c47ac181612db5122a313557090e1957fea4f 100644 (file)
@@ -48,7 +48,7 @@ static void process_blob(struct rev_info *revs,
 
        pathlen = path->len;
        strbuf_addstr(path, name);
-       if (filter_fn)
+       if (!(obj->flags & USER_GIVEN) && filter_fn)
                r = filter_fn(LOFS_BLOB, obj,
                              path->buf, &path->buf[pathlen],
                              filter_data);
@@ -133,7 +133,7 @@ static void process_tree(struct rev_info *revs,
        }
 
        strbuf_addstr(base, name);
-       if (filter_fn)
+       if (!(obj->flags & USER_GIVEN) && filter_fn)
                r = filter_fn(LOFS_BEGIN_TREE, obj,
                              base->buf, &base->buf[baselen],
                              filter_data);
@@ -172,7 +172,7 @@ static void process_tree(struct rev_info *revs,
                                     cb_data, filter_fn, filter_data);
        }
 
-       if (filter_fn) {
+       if (!(obj->flags & USER_GIVEN) && filter_fn) {
                r = filter_fn(LOFS_END_TREE, obj,
                              base->buf, &base->buf[baselen],
                              filter_data);
index 76475d0136b60d1f19ae1267014bed8485dc2898..c0ac7af7cb6bffea85447ba3af151ee6b8ab65e3 100644 (file)
@@ -547,7 +547,7 @@ void show_log(struct rev_info *opt)
        struct strbuf msgbuf = STRBUF_INIT;
        struct log_info *log = opt->loginfo;
        struct commit *commit = log->commit, *parent = log->parent;
-       int abbrev_commit = opt->abbrev_commit ? opt->abbrev : GIT_SHA1_HEXSZ;
+       int abbrev_commit = opt->abbrev_commit ? opt->abbrev : the_hash_algo->hexsz;
        const char *extra_headers = opt->extra_headers;
        struct pretty_print_context ctx = {0};
 
index 1dd6ec384de132d24f96b2ee89639bb765de9589..3d6b34b4f7438d2408780d73095e9a1bd0cd4338 100644 (file)
@@ -184,7 +184,7 @@ static int oid_eq(const struct object_id *a, const struct object_id *b)
 
 enum rename_type {
        RENAME_NORMAL = 0,
-       RENAME_DIR,
+       RENAME_VIA_DIR,
        RENAME_DELETE,
        RENAME_ONE_FILE_TO_ONE,
        RENAME_ONE_FILE_TO_TWO,
@@ -314,8 +314,8 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
 }
 
 static int add_cacheinfo(struct merge_options *o,
-               unsigned int mode, const struct object_id *oid,
-               const char *path, int stage, int refresh, int options)
+                        unsigned int mode, const struct object_id *oid,
+                        const char *path, int stage, int refresh, int options)
 {
        struct cache_entry *ce;
        int ret;
@@ -422,8 +422,8 @@ struct tree *write_tree_from_memory(struct merge_options *o)
 }
 
 static int save_files_dirs(const struct object_id *oid,
-               struct strbuf *base, const char *path,
-               unsigned int mode, int stage, void *context)
+                          struct strbuf *base, const char *path,
+                          unsigned int mode, int stage, void *context)
 {
        struct path_hashmap_entry *entry;
        int baselen = base->len;
@@ -544,7 +544,7 @@ static void record_df_conflict_files(struct merge_options *o,
                                     struct string_list *entries)
 {
        /* If there is a D/F conflict and the file for such a conflict
-        * currently exist in the working tree, we want to allow it to be
+        * currently exists in the working tree, we want to allow it to be
         * removed to make room for the corresponding directory if needed.
         * The files underneath the directories of such D/F conflicts will
         * be processed before the corresponding file involved in the D/F
@@ -918,7 +918,7 @@ static int make_room_for_path(struct merge_options *o, const char *path)
         */
        if (would_lose_untracked(path))
                return err(o, _("refusing to lose untracked file at '%s'"),
-                            path);
+                          path);
 
        /* Successful unlink is good.. */
        if (!unlink(path))
@@ -997,16 +997,16 @@ static int update_file_flags(struct merge_options *o,
                        unlink(path);
                        if (symlink(lnk, path))
                                ret = err(o, _("failed to symlink '%s': %s"),
-                                       path, strerror(errno));
+                                         path, strerror(errno));
                        free(lnk);
                } else
                        ret = err(o,
                                  _("do not know what to do with %06o %s '%s'"),
                                  mode, oid_to_hex(oid), path);
- free_buf:
      free_buf:
                free(buf);
        }
- update_index:
+update_index:
        if (!ret && update_cache)
                if (add_cacheinfo(o, mode, oid, path, 0, update_wd,
                                  ADD_CACHE_OK_TO_ADD))
@@ -1095,7 +1095,7 @@ static int merge_3way(struct merge_options *o,
 }
 
 static int find_first_merges(struct object_array *result, const char *path,
-               struct commit *a, struct commit *b)
+                            struct commit *a, struct commit *b)
 {
        int i, j;
        struct object_array merges = OBJECT_ARRAY_INIT;
@@ -1113,7 +1113,7 @@ static int find_first_merges(struct object_array *result, const char *path,
 
        /* get all revisions that merge commit a */
        xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
-                       oid_to_hex(&a->object.oid));
+                 oid_to_hex(&a->object.oid));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
        /* FIXME: can't handle linked worktrees in submodules yet */
@@ -1255,12 +1255,12 @@ static int merge_submodule(struct merge_options *o,
                output(o, 2, _("Found a possible merge resolution for the submodule:\n"));
                print_commit((struct commit *) merges.objects[0].item);
                output(o, 2, _(
-                       "If this is correct simply add it to the index "
-                       "for example\n"
-                       "by using:\n\n"
-                       "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
-                       "which will accept this suggestion.\n"),
-                       oid_to_hex(&merges.objects[0].item->oid), path);
+                      "If this is correct simply add it to the index "
+                      "for example\n"
+                      "by using:\n\n"
+                      "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
+                      "which will accept this suggestion.\n"),
+                      oid_to_hex(&merges.objects[0].item->oid), path);
                break;
 
        default:
@@ -1337,10 +1337,10 @@ static int merge_file_1(struct merge_options *o,
                        result->clean = (merge_status == 0);
                } else if (S_ISGITLINK(a->mode)) {
                        result->clean = merge_submodule(o, &result->oid,
-                                                      one->path,
-                                                      &one->oid,
-                                                      &a->oid,
-                                                      &b->oid);
+                                                       one->path,
+                                                       &one->oid,
+                                                       &a->oid,
+                                                       &b->oid);
                } else if (S_ISLNK(a->mode)) {
                        switch (o->recursive_variant) {
                        case MERGE_RECURSIVE_NORMAL:
@@ -1415,11 +1415,17 @@ static int merge_file_one(struct merge_options *o,
        return merge_file_1(o, &one, &a, &b, path, branch1, branch2, mfi);
 }
 
-static int conflict_rename_dir(struct merge_options *o,
-                              struct diff_filepair *pair,
-                              const char *rename_branch,
-                              const char *other_branch)
+static int handle_rename_via_dir(struct merge_options *o,
+                                struct diff_filepair *pair,
+                                const char *rename_branch,
+                                const char *other_branch)
 {
+       /*
+        * Handle file adds that need to be renamed due to directory rename
+        * detection.  This differs from handle_rename_normal, because
+        * there is no content merge to do; just move the file into the
+        * desired final location.
+        */
        const struct diff_filespec *dest = pair->two;
 
        if (!o->call_depth && would_lose_untracked(dest->path)) {
@@ -1448,13 +1454,13 @@ static int conflict_rename_dir(struct merge_options *o,
 }
 
 static int handle_change_delete(struct merge_options *o,
-                                const char *path, const char *old_path,
-                                const struct object_id *o_oid, int o_mode,
-                                const struct object_id *changed_oid,
-                                int changed_mode,
-                                const char *change_branch,
-                                const char *delete_branch,
-                                const char *change, const char *change_past)
+                               const char *path, const char *old_path,
+                               const struct object_id *o_oid, int o_mode,
+                               const struct object_id *changed_oid,
+                               int changed_mode,
+                               const char *change_branch,
+                               const char *delete_branch,
+                               const char *change, const char *change_past)
 {
        char *alt_path = NULL;
        const char *update_path = path;
@@ -1475,6 +1481,21 @@ static int handle_change_delete(struct merge_options *o,
                if (!ret)
                        ret = update_file(o, 0, o_oid, o_mode, update_path);
        } else {
+               /*
+                * Despite the four nearly duplicate messages and argument
+                * lists below and the ugliness of the nested if-statements,
+                * having complete messages makes the job easier for
+                * translators.
+                *
+                * The slight variance among the cases is due to the fact
+                * that:
+                *   1) directory/file conflicts (in effect if
+                *      !alt_path) could cause us to need to write the
+                *      file to a different path.
+                *   2) renames (in effect if !old_path) could mean that
+                *      there are two names for the path that the user
+                *      may know the file by.
+                */
                if (!alt_path) {
                        if (!old_path) {
                                output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
@@ -1514,10 +1535,10 @@ static int handle_change_delete(struct merge_options *o,
        return ret;
 }
 
-static int conflict_rename_delete(struct merge_options *o,
-                                  struct diff_filepair *pair,
-                                  const char *rename_branch,
-                                  const char *delete_branch)
+static int handle_rename_delete(struct merge_options *o,
+                               struct diff_filepair *pair,
+                               const char *rename_branch,
+                               const char *delete_branch)
 {
        const struct diff_filespec *orig = pair->one;
        const struct diff_filespec *dest = pair->two;
@@ -1619,8 +1640,8 @@ static int handle_file(struct merge_options *o,
        return ret;
 }
 
-static int conflict_rename_rename_1to2(struct merge_options *o,
-                                       struct rename_conflict_info *ci)
+static int handle_rename_rename_1to2(struct merge_options *o,
+                                    struct rename_conflict_info *ci)
 {
        /* One file was renamed in both branches, but to different names. */
        struct diff_filespec *one = ci->pair1->one;
@@ -1681,8 +1702,8 @@ static int conflict_rename_rename_1to2(struct merge_options *o,
        return 0;
 }
 
-static int conflict_rename_rename_2to1(struct merge_options *o,
-                                       struct rename_conflict_info *ci)
+static int handle_rename_rename_2to1(struct merge_options *o,
+                                    struct rename_conflict_info *ci)
 {
        /* Two files, a & b, were renamed to the same thing, c. */
        struct diff_filespec *a = ci->pair1->one;
@@ -2424,7 +2445,7 @@ static void apply_directory_rename_modifications(struct merge_options *o,
         * "NOTE" in update_stages(), doing so will modify the current
         * in-memory index which will break calls to would_lose_untracked()
         * that we need to make.  Instead, we need to just make sure that
-        * the various conflict_rename_*() functions update the index
+        * the various handle_rename_*() functions update the index
         * explicitly rather than relying on unpack_trees() to have done it.
         */
        get_tree_entry(&tree->object.oid,
@@ -2697,7 +2718,7 @@ static int process_renames(struct merge_options *o,
 
                        if (oid_eq(&src_other.oid, &null_oid) &&
                            ren1->add_turned_into_rename) {
-                               setup_rename_conflict_info(RENAME_DIR,
+                               setup_rename_conflict_info(RENAME_VIA_DIR,
                                                           ren1->pair,
                                                           NULL,
                                                           branch1,
@@ -2828,12 +2849,12 @@ static void initial_cleanup_rename(struct diff_queue_struct *pairs,
        free(pairs);
 }
 
-static int handle_renames(struct merge_options *o,
-                         struct tree *common,
-                         struct tree *head,
-                         struct tree *merge,
-                         struct string_list *entries,
-                         struct rename_info *ri)
+static int detect_and_process_renames(struct merge_options *o,
+                                     struct tree *common,
+                                     struct tree *head,
+                                     struct tree *merge,
+                                     struct string_list *entries,
+                                     struct rename_info *ri)
 {
        struct diff_queue_struct *head_pairs, *merge_pairs;
        struct hashmap *dir_re_head, *dir_re_merge;
@@ -2909,7 +2930,8 @@ static struct object_id *stage_oid(const struct object_id *oid, unsigned mode)
 }
 
 static int read_oid_strbuf(struct merge_options *o,
-       const struct object_id *oid, struct strbuf *dst)
+                          const struct object_id *oid,
+                          struct strbuf *dst)
 {
        void *buf;
        enum object_type type;
@@ -2962,10 +2984,10 @@ error_return:
 }
 
 static int handle_modify_delete(struct merge_options *o,
-                                const char *path,
-                                struct object_id *o_oid, int o_mode,
-                                struct object_id *a_oid, int a_mode,
-                                struct object_id *b_oid, int b_mode)
+                               const char *path,
+                               struct object_id *o_oid, int o_mode,
+                               struct object_id *a_oid, int a_mode,
+                               struct object_id *b_oid, int b_mode)
 {
        const char *modify_branch, *delete_branch;
        struct object_id *changed_oid;
@@ -3103,12 +3125,12 @@ static int merge_content(struct merge_options *o,
        return !is_dirty && mfi.clean;
 }
 
-static int conflict_rename_normal(struct merge_options *o,
-                                 const char *path,
-                                 struct object_id *o_oid, unsigned int o_mode,
-                                 struct object_id *a_oid, unsigned int a_mode,
-                                 struct object_id *b_oid, unsigned int b_mode,
-                                 struct rename_conflict_info *ci)
+static int handle_rename_normal(struct merge_options *o,
+                               const char *path,
+                               struct object_id *o_oid, unsigned int o_mode,
+                               struct object_id *a_oid, unsigned int a_mode,
+                               struct object_id *b_oid, unsigned int b_mode,
+                               struct rename_conflict_info *ci)
 {
        /* Merge the content and write it out */
        return merge_content(o, path, was_dirty(o, path),
@@ -3135,37 +3157,37 @@ static int process_entry(struct merge_options *o,
                switch (conflict_info->rename_type) {
                case RENAME_NORMAL:
                case RENAME_ONE_FILE_TO_ONE:
-                       clean_merge = conflict_rename_normal(o,
-                                                            path,
-                                                            o_oid, o_mode,
-                                                            a_oid, a_mode,
-                                                            b_oid, b_mode,
-                                                            conflict_info);
+                       clean_merge = handle_rename_normal(o,
+                                                          path,
+                                                          o_oid, o_mode,
+                                                          a_oid, a_mode,
+                                                          b_oid, b_mode,
+                                                          conflict_info);
                        break;
-               case RENAME_DIR:
+               case RENAME_VIA_DIR:
                        clean_merge = 1;
-                       if (conflict_rename_dir(o,
-                                               conflict_info->pair1,
-                                               conflict_info->branch1,
-                                               conflict_info->branch2))
+                       if (handle_rename_via_dir(o,
+                                                 conflict_info->pair1,
+                                                 conflict_info->branch1,
+                                                 conflict_info->branch2))
                                clean_merge = -1;
                        break;
                case RENAME_DELETE:
                        clean_merge = 0;
-                       if (conflict_rename_delete(o,
-                                                  conflict_info->pair1,
-                                                  conflict_info->branch1,
-                                                  conflict_info->branch2))
+                       if (handle_rename_delete(o,
+                                                conflict_info->pair1,
+                                                conflict_info->branch1,
+                                                conflict_info->branch2))
                                clean_merge = -1;
                        break;
                case RENAME_ONE_FILE_TO_TWO:
                        clean_merge = 0;
-                       if (conflict_rename_rename_1to2(o, conflict_info))
+                       if (handle_rename_rename_1to2(o, conflict_info))
                                clean_merge = -1;
                        break;
                case RENAME_TWO_FILES_TO_ONE:
                        clean_merge = 0;
-                       if (conflict_rename_rename_2to1(o, conflict_info))
+                       if (handle_rename_rename_2to1(o, conflict_info))
                                clean_merge = -1;
                        break;
                default:
@@ -3305,8 +3327,8 @@ int merge_trees(struct merge_options *o,
                get_files_dirs(o, merge);
 
                entries = get_unmerged();
-               clean = handle_renames(o, common, head, merge, entries,
-                                      &re_info);
+               clean = detect_and_process_renames(o, common, head, merge,
+                                                  entries, &re_info);
                record_df_conflict_files(o, entries);
                if (clean < 0)
                        goto cleanup;
@@ -3330,7 +3352,7 @@ int merge_trees(struct merge_options *o,
                                    entries->items[i].string);
                }
 
-cleanup:
+       cleanup:
                final_cleanup_renames(&re_info);
 
                string_list_clear(entries, 1);
@@ -3500,14 +3522,14 @@ int merge_recursive_generic(struct merge_options *o,
                        struct commit *base;
                        if (!(base = get_ref(base_list[i], oid_to_hex(base_list[i]))))
                                return err(o, _("Could not parse object '%s'"),
-                                       oid_to_hex(base_list[i]));
+                                          oid_to_hex(base_list[i]));
                        commit_list_insert(base, &ca);
                }
        }
 
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        clean = merge_recursive(o, head_commit, next_commit, ca,
-                       result);
+                               result);
        if (clean < 0) {
                rollback_lock_file(&lock);
                return clean;
diff --git a/negotiator/default.c b/negotiator/default.c
new file mode 100644 (file)
index 0000000..4b78f6b
--- /dev/null
@@ -0,0 +1,176 @@
+#include "cache.h"
+#include "default.h"
+#include "../commit.h"
+#include "../fetch-negotiator.h"
+#include "../prio-queue.h"
+#include "../refs.h"
+#include "../tag.h"
+
+/* Remember to update object flag allocation in object.h */
+#define COMMON         (1U << 2)
+#define COMMON_REF     (1U << 3)
+#define SEEN           (1U << 4)
+#define POPPED         (1U << 5)
+
+static int marked;
+
+struct negotiation_state {
+       struct prio_queue rev_list;
+       int non_common_revs;
+};
+
+static void rev_list_push(struct negotiation_state *ns,
+                         struct commit *commit, int mark)
+{
+       if (!(commit->object.flags & mark)) {
+               commit->object.flags |= mark;
+
+               if (parse_commit(commit))
+                       return;
+
+               prio_queue_put(&ns->rev_list, commit);
+
+               if (!(commit->object.flags & COMMON))
+                       ns->non_common_revs++;
+       }
+}
+
+static int clear_marks(const char *refname, const struct object_id *oid,
+                      int flag, void *cb_data)
+{
+       struct object *o = deref_tag(the_repository, parse_object(the_repository, oid), refname, 0);
+
+       if (o && o->type == OBJ_COMMIT)
+               clear_commit_marks((struct commit *)o,
+                                  COMMON | COMMON_REF | SEEN | POPPED);
+       return 0;
+}
+
+/*
+ * This function marks a rev and its ancestors as common.
+ * In some cases, it is desirable to mark only the ancestors (for example
+ * when only the server does not yet know that they are common).
+ */
+static void mark_common(struct negotiation_state *ns, struct commit *commit,
+               int ancestors_only, int dont_parse)
+{
+       if (commit != NULL && !(commit->object.flags & COMMON)) {
+               struct object *o = (struct object *)commit;
+
+               if (!ancestors_only)
+                       o->flags |= COMMON;
+
+               if (!(o->flags & SEEN))
+                       rev_list_push(ns, commit, SEEN);
+               else {
+                       struct commit_list *parents;
+
+                       if (!ancestors_only && !(o->flags & POPPED))
+                               ns->non_common_revs--;
+                       if (!o->parsed && !dont_parse)
+                               if (parse_commit(commit))
+                                       return;
+
+                       for (parents = commit->parents;
+                                       parents;
+                                       parents = parents->next)
+                               mark_common(ns, parents->item, 0,
+                                           dont_parse);
+               }
+       }
+}
+
+/*
+ * Get the next rev to send, ignoring the common.
+ */
+static const struct object_id *get_rev(struct negotiation_state *ns)
+{
+       struct commit *commit = NULL;
+
+       while (commit == NULL) {
+               unsigned int mark;
+               struct commit_list *parents;
+
+               if (ns->rev_list.nr == 0 || ns->non_common_revs == 0)
+                       return NULL;
+
+               commit = prio_queue_get(&ns->rev_list);
+               parse_commit(commit);
+               parents = commit->parents;
+
+               commit->object.flags |= POPPED;
+               if (!(commit->object.flags & COMMON))
+                       ns->non_common_revs--;
+
+               if (commit->object.flags & COMMON) {
+                       /* do not send "have", and ignore ancestors */
+                       commit = NULL;
+                       mark = COMMON | SEEN;
+               } else if (commit->object.flags & COMMON_REF)
+                       /* send "have", and ignore ancestors */
+                       mark = COMMON | SEEN;
+               else
+                       /* send "have", also for its ancestors */
+                       mark = SEEN;
+
+               while (parents) {
+                       if (!(parents->item->object.flags & SEEN))
+                               rev_list_push(ns, parents->item, mark);
+                       if (mark & COMMON)
+                               mark_common(ns, parents->item, 1, 0);
+                       parents = parents->next;
+               }
+       }
+
+       return &commit->object.oid;
+}
+
+static void known_common(struct fetch_negotiator *n, struct commit *c)
+{
+       if (!(c->object.flags & SEEN)) {
+               rev_list_push(n->data, c, COMMON_REF | SEEN);
+               mark_common(n->data, c, 1, 1);
+       }
+}
+
+static void add_tip(struct fetch_negotiator *n, struct commit *c)
+{
+       n->known_common = NULL;
+       rev_list_push(n->data, c, SEEN);
+}
+
+static const struct object_id *next(struct fetch_negotiator *n)
+{
+       n->known_common = NULL;
+       n->add_tip = NULL;
+       return get_rev(n->data);
+}
+
+static int ack(struct fetch_negotiator *n, struct commit *c)
+{
+       int known_to_be_common = !!(c->object.flags & COMMON);
+       mark_common(n->data, c, 0, 1);
+       return known_to_be_common;
+}
+
+static void release(struct fetch_negotiator *n)
+{
+       clear_prio_queue(&((struct negotiation_state *)n->data)->rev_list);
+       FREE_AND_NULL(n->data);
+}
+
+void default_negotiator_init(struct fetch_negotiator *negotiator)
+{
+       struct negotiation_state *ns;
+       negotiator->known_common = known_common;
+       negotiator->add_tip = add_tip;
+       negotiator->next = next;
+       negotiator->ack = ack;
+       negotiator->release = release;
+       negotiator->data = ns = xcalloc(1, sizeof(*ns));
+       ns->rev_list.compare = compare_commits_by_commit_date;
+
+       if (marked)
+               for_each_ref(clear_marks, NULL);
+       marked = 1;
+}
diff --git a/negotiator/default.h b/negotiator/default.h
new file mode 100644 (file)
index 0000000..d23a8f2
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef NEGOTIATOR_DEFAULT_H
+#define NEGOTIATOR_DEFAULT_H
+
+struct fetch_negotiator;
+
+void default_negotiator_init(struct fetch_negotiator *negotiator);
+
+#endif
index fa5ca9756786307e52004dfc3cb18d11a889dbb9..177b1a4571fb60f75d523124c09c133c2f5c84e6 100644 (file)
--- a/object.h
+++ b/object.h
@@ -57,8 +57,9 @@ struct object_array {
 
 /*
  * object flag allocation:
- * revision.h:               0---------10                                26
- * fetch-pack.c:             0----5
+ * revision.h:               0---------10                              2526
+ * fetch-pack.c:             01
+ * negotiator/default.c:       2--5
  * walker.c:                 0-2
  * upload-pack.c:                4       11----------------19
  * builtin/blame.c:                        12-13
index 953c5dd84dff2d87b639d29b71d844b29f925618..d977e9bacb19ed766dd0a501b6fbcae800bb4ae0 100644 (file)
@@ -361,11 +361,17 @@ static int date_compare(const void *_a, const void *_b)
 
 void bitmap_writer_reuse_bitmaps(struct packing_data *to_pack)
 {
-       if (prepare_bitmap_git() < 0)
+       struct bitmap_index *bitmap_git;
+       if (!(bitmap_git = prepare_bitmap_git()))
                return;
 
        writer.reused = kh_init_sha1();
-       rebuild_existing_bitmaps(to_pack, writer.reused, writer.show_progress);
+       rebuild_existing_bitmaps(bitmap_git, to_pack, writer.reused,
+                                writer.show_progress);
+       /*
+        * NEEDSWORK: rebuild_existing_bitmaps() makes writer.reused reference
+        * some bitmaps in bitmap_git, so we can't free the latter.
+        */
 }
 
 static struct ewah_bitmap *find_reused_bitmap(const unsigned char *sha1)
index 18f8b22aeb422bb81999e1e564006daf8b64f637..f0a1937a1cc5fbb13fc705df8d193a43a0648198 100644 (file)
@@ -25,14 +25,14 @@ struct stored_bitmap {
 };
 
 /*
- * The currently active bitmap index. By design, repositories only have
+ * The active bitmap index for a repository. By design, repositories only have
  * a single bitmap index available (the index for the biggest packfile in
  * the repository), since bitmap indexes need full closure.
  *
  * If there is more than one bitmap index available (e.g. because of alternates),
  * the active bitmap index is the largest one.
  */
-static struct bitmap_index {
+struct bitmap_index {
        /* Packfile to which this bitmap index belongs to */
        struct packed_git *pack;
 
@@ -66,7 +66,7 @@ static struct bitmap_index {
        /* Number of bitmapped commits */
        uint32_t entry_count;
 
-       /* Name-hash cache (or NULL if not present). */
+       /* If not NULL, this is a name-hash cache pointing into map. */
        uint32_t *hashes;
 
        /*
@@ -90,8 +90,7 @@ static struct bitmap_index {
        unsigned int version;
 
        unsigned loaded : 1;
-
-} bitmap_git;
+};
 
 static struct ewah_bitmap *lookup_stored_bitmap(struct stored_bitmap *st)
 {
@@ -259,7 +258,7 @@ static char *pack_bitmap_filename(struct packed_git *p)
        return xstrfmt("%.*s.bitmap", (int)len, p->pack_name);
 }
 
-static int open_pack_bitmap_1(struct packed_git *packfile)
+static int open_pack_bitmap_1(struct bitmap_index *bitmap_git, struct packed_git *packfile)
 {
        int fd;
        struct stat st;
@@ -280,117 +279,122 @@ static int open_pack_bitmap_1(struct packed_git *packfile)
                return -1;
        }
 
-       if (bitmap_git.pack) {
+       if (bitmap_git->pack) {
                warning("ignoring extra bitmap file: %s", packfile->pack_name);
                close(fd);
                return -1;
        }
 
-       bitmap_git.pack = packfile;
-       bitmap_git.map_size = xsize_t(st.st_size);
-       bitmap_git.map = xmmap(NULL, bitmap_git.map_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       bitmap_git.map_pos = 0;
+       bitmap_git->pack = packfile;
+       bitmap_git->map_size = xsize_t(st.st_size);
+       bitmap_git->map = xmmap(NULL, bitmap_git->map_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       bitmap_git->map_pos = 0;
        close(fd);
 
-       if (load_bitmap_header(&bitmap_git) < 0) {
-               munmap(bitmap_git.map, bitmap_git.map_size);
-               bitmap_git.map = NULL;
-               bitmap_git.map_size = 0;
+       if (load_bitmap_header(bitmap_git) < 0) {
+               munmap(bitmap_git->map, bitmap_git->map_size);
+               bitmap_git->map = NULL;
+               bitmap_git->map_size = 0;
                return -1;
        }
 
        return 0;
 }
 
-static int load_pack_bitmap(void)
+static int load_pack_bitmap(struct bitmap_index *bitmap_git)
 {
-       assert(bitmap_git.map && !bitmap_git.loaded);
+       assert(bitmap_git->map && !bitmap_git->loaded);
 
-       bitmap_git.bitmaps = kh_init_sha1();
-       bitmap_git.ext_index.positions = kh_init_sha1_pos();
-       load_pack_revindex(bitmap_git.pack);
+       bitmap_git->bitmaps = kh_init_sha1();
+       bitmap_git->ext_index.positions = kh_init_sha1_pos();
+       load_pack_revindex(bitmap_git->pack);
 
-       if (!(bitmap_git.commits = read_bitmap_1(&bitmap_git)) ||
-               !(bitmap_git.trees = read_bitmap_1(&bitmap_git)) ||
-               !(bitmap_git.blobs = read_bitmap_1(&bitmap_git)) ||
-               !(bitmap_git.tags = read_bitmap_1(&bitmap_git)))
+       if (!(bitmap_git->commits = read_bitmap_1(bitmap_git)) ||
+               !(bitmap_git->trees = read_bitmap_1(bitmap_git)) ||
+               !(bitmap_git->blobs = read_bitmap_1(bitmap_git)) ||
+               !(bitmap_git->tags = read_bitmap_1(bitmap_git)))
                goto failed;
 
-       if (load_bitmap_entries_v1(&bitmap_git) < 0)
+       if (load_bitmap_entries_v1(bitmap_git) < 0)
                goto failed;
 
-       bitmap_git.loaded = 1;
+       bitmap_git->loaded = 1;
        return 0;
 
 failed:
-       munmap(bitmap_git.map, bitmap_git.map_size);
-       bitmap_git.map = NULL;
-       bitmap_git.map_size = 0;
+       munmap(bitmap_git->map, bitmap_git->map_size);
+       bitmap_git->map = NULL;
+       bitmap_git->map_size = 0;
        return -1;
 }
 
-static int open_pack_bitmap(void)
+static int open_pack_bitmap(struct bitmap_index *bitmap_git)
 {
        struct packed_git *p;
        int ret = -1;
 
-       assert(!bitmap_git.map && !bitmap_git.loaded);
+       assert(!bitmap_git->map && !bitmap_git->loaded);
 
        for (p = get_packed_git(the_repository); p; p = p->next) {
-               if (open_pack_bitmap_1(p) == 0)
+               if (open_pack_bitmap_1(bitmap_git, p) == 0)
                        ret = 0;
        }
 
        return ret;
 }
 
-int prepare_bitmap_git(void)
+struct bitmap_index *prepare_bitmap_git(void)
 {
-       if (bitmap_git.loaded)
-               return 0;
+       struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
 
-       if (!open_pack_bitmap())
-               return load_pack_bitmap();
+       if (!open_pack_bitmap(bitmap_git) && !load_pack_bitmap(bitmap_git))
+               return bitmap_git;
 
-       return -1;
+       free_bitmap_index(bitmap_git);
+       return NULL;
 }
 
 struct include_data {
+       struct bitmap_index *bitmap_git;
        struct bitmap *base;
        struct bitmap *seen;
 };
 
-static inline int bitmap_position_extended(const unsigned char *sha1)
+static inline int bitmap_position_extended(struct bitmap_index *bitmap_git,
+                                          const unsigned char *sha1)
 {
-       khash_sha1_pos *positions = bitmap_git.ext_index.positions;
+       khash_sha1_pos *positions = bitmap_git->ext_index.positions;
        khiter_t pos = kh_get_sha1_pos(positions, sha1);
 
        if (pos < kh_end(positions)) {
                int bitmap_pos = kh_value(positions, pos);
-               return bitmap_pos + bitmap_git.pack->num_objects;
+               return bitmap_pos + bitmap_git->pack->num_objects;
        }
 
        return -1;
 }
 
-static inline int bitmap_position_packfile(const unsigned char *sha1)
+static inline int bitmap_position_packfile(struct bitmap_index *bitmap_git,
+                                          const unsigned char *sha1)
 {
-       off_t offset = find_pack_entry_one(sha1, bitmap_git.pack);
+       off_t offset = find_pack_entry_one(sha1, bitmap_git->pack);
        if (!offset)
                return -1;
 
-       return find_revindex_position(bitmap_git.pack, offset);
+       return find_revindex_position(bitmap_git->pack, offset);
 }
 
-static int bitmap_position(const unsigned char *sha1)
+static int bitmap_position(struct bitmap_index *bitmap_git,
+                          const unsigned char *sha1)
 {
-       int pos = bitmap_position_packfile(sha1);
-       return (pos >= 0) ? pos : bitmap_position_extended(sha1);
+       int pos = bitmap_position_packfile(bitmap_git, sha1);
+       return (pos >= 0) ? pos : bitmap_position_extended(bitmap_git, sha1);
 }
 
-static int ext_index_add_object(struct object *object, const char *name)
+static int ext_index_add_object(struct bitmap_index *bitmap_git,
+                               struct object *object, const char *name)
 {
-       struct eindex *eindex = &bitmap_git.ext_index;
+       struct eindex *eindex = &bitmap_git->ext_index;
 
        khiter_t hash_pos;
        int hash_ret;
@@ -413,27 +417,34 @@ static int ext_index_add_object(struct object *object, const char *name)
                bitmap_pos = kh_value(eindex->positions, hash_pos);
        }
 
-       return bitmap_pos + bitmap_git.pack->num_objects;
+       return bitmap_pos + bitmap_git->pack->num_objects;
 }
 
-static void show_object(struct object *object, const char *name, void *data)
+struct bitmap_show_data {
+       struct bitmap_index *bitmap_git;
+       struct bitmap *base;
+};
+
+static void show_object(struct object *object, const char *name, void *data_)
 {
-       struct bitmap *base = data;
+       struct bitmap_show_data *data = data_;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(object->oid.hash);
+       bitmap_pos = bitmap_position(data->bitmap_git, object->oid.hash);
 
        if (bitmap_pos < 0)
-               bitmap_pos = ext_index_add_object(object, name);
+               bitmap_pos = ext_index_add_object(data->bitmap_git, object,
+                                                 name);
 
-       bitmap_set(base, bitmap_pos);
+       bitmap_set(data->base, bitmap_pos);
 }
 
 static void show_commit(struct commit *commit, void *data)
 {
 }
 
-static int add_to_include_set(struct include_data *data,
+static int add_to_include_set(struct bitmap_index *bitmap_git,
+                             struct include_data *data,
                              const unsigned char *sha1,
                              int bitmap_pos)
 {
@@ -445,9 +456,9 @@ static int add_to_include_set(struct include_data *data,
        if (bitmap_get(data->base, bitmap_pos))
                return 0;
 
-       hash_pos = kh_get_sha1(bitmap_git.bitmaps, sha1);
-       if (hash_pos < kh_end(bitmap_git.bitmaps)) {
-               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, hash_pos);
+       hash_pos = kh_get_sha1(bitmap_git->bitmaps, sha1);
+       if (hash_pos < kh_end(bitmap_git->bitmaps)) {
+               struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, hash_pos);
                bitmap_or_ewah(data->base, lookup_stored_bitmap(st));
                return 0;
        }
@@ -461,11 +472,14 @@ static int should_include(struct commit *commit, void *_data)
        struct include_data *data = _data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(commit->object.oid.hash);
+       bitmap_pos = bitmap_position(data->bitmap_git, commit->object.oid.hash);
        if (bitmap_pos < 0)
-               bitmap_pos = ext_index_add_object((struct object *)commit, NULL);
+               bitmap_pos = ext_index_add_object(data->bitmap_git,
+                                                 (struct object *)commit,
+                                                 NULL);
 
-       if (!add_to_include_set(data, commit->object.oid.hash, bitmap_pos)) {
+       if (!add_to_include_set(data->bitmap_git, data, commit->object.oid.hash,
+                               bitmap_pos)) {
                struct commit_list *parent = commit->parents;
 
                while (parent) {
@@ -479,7 +493,8 @@ static int should_include(struct commit *commit, void *_data)
        return 1;
 }
 
-static struct bitmap *find_objects(struct rev_info *revs,
+static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
+                                  struct rev_info *revs,
                                   struct object_list *roots,
                                   struct bitmap *seen)
 {
@@ -501,10 +516,10 @@ static struct bitmap *find_objects(struct rev_info *revs,
                roots = roots->next;
 
                if (object->type == OBJ_COMMIT) {
-                       khiter_t pos = kh_get_sha1(bitmap_git.bitmaps, object->oid.hash);
+                       khiter_t pos = kh_get_sha1(bitmap_git->bitmaps, object->oid.hash);
 
-                       if (pos < kh_end(bitmap_git.bitmaps)) {
-                               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);
+                       if (pos < kh_end(bitmap_git->bitmaps)) {
+                               struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, pos);
                                struct ewah_bitmap *or_with = lookup_stored_bitmap(st);
 
                                if (base == NULL)
@@ -543,7 +558,7 @@ static struct bitmap *find_objects(struct rev_info *revs,
                int pos;
 
                roots = roots->next;
-               pos = bitmap_position(object->oid.hash);
+               pos = bitmap_position(bitmap_git, object->oid.hash);
 
                if (pos < 0 || base == NULL || !bitmap_get(base, pos)) {
                        object->flags &= ~UNINTERESTING;
@@ -556,10 +571,12 @@ static struct bitmap *find_objects(struct rev_info *revs,
 
        if (needs_walk) {
                struct include_data incdata;
+               struct bitmap_show_data show_data;
 
                if (base == NULL)
                        base = bitmap_new();
 
+               incdata.bitmap_git = bitmap_git;
                incdata.base = base;
                incdata.seen = seen;
 
@@ -569,22 +586,27 @@ static struct bitmap *find_objects(struct rev_info *revs,
                if (prepare_revision_walk(revs))
                        die("revision walk setup failed");
 
-               traverse_commit_list(revs, show_commit, show_object, base);
+               show_data.bitmap_git = bitmap_git;
+               show_data.base = base;
+
+               traverse_commit_list(revs, show_commit, show_object,
+                                    &show_data);
        }
 
        return base;
 }
 
-static void show_extended_objects(struct bitmap *objects,
+static void show_extended_objects(struct bitmap_index *bitmap_git,
                                  show_reachable_fn show_reach)
 {
-       struct eindex *eindex = &bitmap_git.ext_index;
+       struct bitmap *objects = bitmap_git->result;
+       struct eindex *eindex = &bitmap_git->ext_index;
        uint32_t i;
 
        for (i = 0; i < eindex->count; ++i) {
                struct object *obj;
 
-               if (!bitmap_get(objects, bitmap_git.pack->num_objects + i))
+               if (!bitmap_get(objects, bitmap_git->pack->num_objects + i))
                        continue;
 
                obj = eindex->objects[i];
@@ -593,7 +615,7 @@ static void show_extended_objects(struct bitmap *objects,
 }
 
 static void show_objects_for_type(
-       struct bitmap *objects,
+       struct bitmap_index *bitmap_git,
        struct ewah_bitmap *type_filter,
        enum object_type object_type,
        show_reachable_fn show_reach)
@@ -604,7 +626,9 @@ static void show_objects_for_type(
        struct ewah_iterator it;
        eword_t filter;
 
-       if (bitmap_git.reuse_objects == bitmap_git.pack->num_objects)
+       struct bitmap *objects = bitmap_git->result;
+
+       if (bitmap_git->reuse_objects == bitmap_git->pack->num_objects)
                return;
 
        ewah_iterator_init(&it, type_filter);
@@ -622,16 +646,16 @@ static void show_objects_for_type(
 
                        offset += ewah_bit_ctz64(word >> offset);
 
-                       if (pos + offset < bitmap_git.reuse_objects)
+                       if (pos + offset < bitmap_git->reuse_objects)
                                continue;
 
-                       entry = &bitmap_git.pack->revindex[pos + offset];
-                       nth_packed_object_oid(&oid, bitmap_git.pack, entry->nr);
+                       entry = &bitmap_git->pack->revindex[pos + offset];
+                       nth_packed_object_oid(&oid, bitmap_git->pack, entry->nr);
 
-                       if (bitmap_git.hashes)
-                               hash = get_be32(bitmap_git.hashes + entry->nr);
+                       if (bitmap_git->hashes)
+                               hash = get_be32(bitmap_git->hashes + entry->nr);
 
-                       show_reach(&oid, object_type, 0, hash, bitmap_git.pack, entry->offset);
+                       show_reach(&oid, object_type, 0, hash, bitmap_git->pack, entry->offset);
                }
 
                pos += BITS_IN_EWORD;
@@ -639,20 +663,21 @@ static void show_objects_for_type(
        }
 }
 
-static int in_bitmapped_pack(struct object_list *roots)
+static int in_bitmapped_pack(struct bitmap_index *bitmap_git,
+                            struct object_list *roots)
 {
        while (roots) {
                struct object *object = roots->item;
                roots = roots->next;
 
-               if (find_pack_entry_one(object->oid.hash, bitmap_git.pack) > 0)
+               if (find_pack_entry_one(object->oid.hash, bitmap_git->pack) > 0)
                        return 1;
        }
 
        return 0;
 }
 
-int prepare_bitmap_walk(struct rev_info *revs)
+struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
 {
        unsigned int i;
 
@@ -662,12 +687,11 @@ int prepare_bitmap_walk(struct rev_info *revs)
        struct bitmap *wants_bitmap = NULL;
        struct bitmap *haves_bitmap = NULL;
 
-       if (!bitmap_git.loaded) {
-               /* try to open a bitmapped pack, but don't parse it yet
-                * because we may not need to use it */
-               if (open_pack_bitmap() < 0)
-                       return -1;
-       }
+       struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
+       /* try to open a bitmapped pack, but don't parse it yet
+        * because we may not need to use it */
+       if (open_pack_bitmap(bitmap_git) < 0)
+               goto cleanup;
 
        for (i = 0; i < revs->pending.nr; ++i) {
                struct object *object = revs->pending.objects[i].item;
@@ -699,26 +723,26 @@ int prepare_bitmap_walk(struct rev_info *revs)
         * in the packfile that has a bitmap, we don't have anything to
         * optimize here
         */
-       if (haves && !in_bitmapped_pack(haves))
-               return -1;
+       if (haves && !in_bitmapped_pack(bitmap_git, haves))
+               goto cleanup;
 
        /* if we don't want anything, we're done here */
        if (!wants)
-               return -1;
+               goto cleanup;
 
        /*
         * now we're going to use bitmaps, so load the actual bitmap entries
         * from disk. this is the point of no return; after this the rev_list
         * becomes invalidated and we must perform the revwalk through bitmaps
         */
-       if (!bitmap_git.loaded && load_pack_bitmap() < 0)
-               return -1;
+       if (!bitmap_git->loaded && load_pack_bitmap(bitmap_git) < 0)
+               goto cleanup;
 
        object_array_clear(&revs->pending);
 
        if (haves) {
                revs->ignore_missing_links = 1;
-               haves_bitmap = find_objects(revs, haves, NULL);
+               haves_bitmap = find_objects(bitmap_git, revs, haves, NULL);
                reset_revision_walk();
                revs->ignore_missing_links = 0;
 
@@ -726,7 +750,7 @@ int prepare_bitmap_walk(struct rev_info *revs)
                        BUG("failed to perform bitmap walk");
        }
 
-       wants_bitmap = find_objects(revs, wants, haves_bitmap);
+       wants_bitmap = find_objects(bitmap_git, revs, wants, haves_bitmap);
 
        if (!wants_bitmap)
                BUG("failed to perform bitmap walk");
@@ -734,13 +758,18 @@ int prepare_bitmap_walk(struct rev_info *revs)
        if (haves_bitmap)
                bitmap_and_not(wants_bitmap, haves_bitmap);
 
-       bitmap_git.result = wants_bitmap;
+       bitmap_git->result = wants_bitmap;
 
        bitmap_free(haves_bitmap);
-       return 0;
+       return bitmap_git;
+
+cleanup:
+       free_bitmap_index(bitmap_git);
+       return NULL;
 }
 
-int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
+int reuse_partial_packfile_from_bitmap(struct bitmap_index *bitmap_git,
+                                      struct packed_git **packfile,
                                       uint32_t *entries,
                                       off_t *up_to)
 {
@@ -750,7 +779,7 @@ int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
         */
        static const double REUSE_PERCENT = 0.9;
 
-       struct bitmap *result = bitmap_git.result;
+       struct bitmap *result = bitmap_git->result;
        uint32_t reuse_threshold;
        uint32_t i, reuse_objects = 0;
 
@@ -770,8 +799,8 @@ int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
                const unsigned char *sha1;
                struct revindex_entry *entry;
 
-               entry = &bitmap_git.reverse_index->revindex[reuse_objects];
-               sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);
+               entry = &bitmap_git->reverse_index->revindex[reuse_objects];
+               sha1 = nth_packed_object_sha1(bitmap_git->pack, entry->nr);
 
                fprintf(stderr, "Failed to reuse at %d (%016llx)\n",
                        reuse_objects, result->words[i]);
@@ -782,48 +811,50 @@ int reuse_partial_packfile_from_bitmap(struct packed_git **packfile,
        if (!reuse_objects)
                return -1;
 
-       if (reuse_objects >= bitmap_git.pack->num_objects) {
-               bitmap_git.reuse_objects = *entries = bitmap_git.pack->num_objects;
+       if (reuse_objects >= bitmap_git->pack->num_objects) {
+               bitmap_git->reuse_objects = *entries = bitmap_git->pack->num_objects;
                *up_to = -1; /* reuse the full pack */
-               *packfile = bitmap_git.pack;
+               *packfile = bitmap_git->pack;
                return 0;
        }
 
-       reuse_threshold = bitmap_popcount(bitmap_git.result) * REUSE_PERCENT;
+       reuse_threshold = bitmap_popcount(bitmap_git->result) * REUSE_PERCENT;
 
        if (reuse_objects < reuse_threshold)
                return -1;
 
-       bitmap_git.reuse_objects = *entries = reuse_objects;
-       *up_to = bitmap_git.pack->revindex[reuse_objects].offset;
-       *packfile = bitmap_git.pack;
+       bitmap_git->reuse_objects = *entries = reuse_objects;
+       *up_to = bitmap_git->pack->revindex[reuse_objects].offset;
+       *packfile = bitmap_git->pack;
 
        return 0;
 }
 
-void traverse_bitmap_commit_list(show_reachable_fn show_reachable)
+void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,
+                                show_reachable_fn show_reachable)
 {
-       assert(bitmap_git.result);
+       assert(bitmap_git->result);
 
-       show_objects_for_type(bitmap_git.result, bitmap_git.commits,
+       show_objects_for_type(bitmap_git, bitmap_git->commits,
                OBJ_COMMIT, show_reachable);
-       show_objects_for_type(bitmap_git.result, bitmap_git.trees,
+       show_objects_for_type(bitmap_git, bitmap_git->trees,
                OBJ_TREE, show_reachable);
-       show_objects_for_type(bitmap_git.result, bitmap_git.blobs,
+       show_objects_for_type(bitmap_git, bitmap_git->blobs,
                OBJ_BLOB, show_reachable);
-       show_objects_for_type(bitmap_git.result, bitmap_git.tags,
+       show_objects_for_type(bitmap_git, bitmap_git->tags,
                OBJ_TAG, show_reachable);
 
-       show_extended_objects(bitmap_git.result, show_reachable);
+       show_extended_objects(bitmap_git, show_reachable);
 
-       bitmap_free(bitmap_git.result);
-       bitmap_git.result = NULL;
+       bitmap_free(bitmap_git->result);
+       bitmap_git->result = NULL;
 }
 
-static uint32_t count_object_type(struct bitmap *objects,
+static uint32_t count_object_type(struct bitmap_index *bitmap_git,
                                  enum object_type type)
 {
-       struct eindex *eindex = &bitmap_git.ext_index;
+       struct bitmap *objects = bitmap_git->result;
+       struct eindex *eindex = &bitmap_git->ext_index;
 
        uint32_t i = 0, count = 0;
        struct ewah_iterator it;
@@ -831,19 +862,19 @@ static uint32_t count_object_type(struct bitmap *objects,
 
        switch (type) {
        case OBJ_COMMIT:
-               ewah_iterator_init(&it, bitmap_git.commits);
+               ewah_iterator_init(&it, bitmap_git->commits);
                break;
 
        case OBJ_TREE:
-               ewah_iterator_init(&it, bitmap_git.trees);
+               ewah_iterator_init(&it, bitmap_git->trees);
                break;
 
        case OBJ_BLOB:
-               ewah_iterator_init(&it, bitmap_git.blobs);
+               ewah_iterator_init(&it, bitmap_git->blobs);
                break;
 
        case OBJ_TAG:
-               ewah_iterator_init(&it, bitmap_git.tags);
+               ewah_iterator_init(&it, bitmap_git->tags);
                break;
 
        default:
@@ -857,32 +888,34 @@ static uint32_t count_object_type(struct bitmap *objects,
 
        for (i = 0; i < eindex->count; ++i) {
                if (eindex->objects[i]->type == type &&
-                       bitmap_get(objects, bitmap_git.pack->num_objects + i))
+                       bitmap_get(objects, bitmap_git->pack->num_objects + i))
                        count++;
        }
 
        return count;
 }
 
-void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees,
+void count_bitmap_commit_list(struct bitmap_index *bitmap_git,
+                             uint32_t *commits, uint32_t *trees,
                              uint32_t *blobs, uint32_t *tags)
 {
-       assert(bitmap_git.result);
+       assert(bitmap_git->result);
 
        if (commits)
-               *commits = count_object_type(bitmap_git.result, OBJ_COMMIT);
+               *commits = count_object_type(bitmap_git, OBJ_COMMIT);
 
        if (trees)
-               *trees = count_object_type(bitmap_git.result, OBJ_TREE);
+               *trees = count_object_type(bitmap_git, OBJ_TREE);
 
        if (blobs)
-               *blobs = count_object_type(bitmap_git.result, OBJ_BLOB);
+               *blobs = count_object_type(bitmap_git, OBJ_BLOB);
 
        if (tags)
-               *tags = count_object_type(bitmap_git.result, OBJ_TAG);
+               *tags = count_object_type(bitmap_git, OBJ_TAG);
 }
 
 struct bitmap_test_data {
+       struct bitmap_index *bitmap_git;
        struct bitmap *base;
        struct progress *prg;
        size_t seen;
@@ -894,7 +927,7 @@ static void test_show_object(struct object *object, const char *name,
        struct bitmap_test_data *tdata = data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(object->oid.hash);
+       bitmap_pos = bitmap_position(tdata->bitmap_git, object->oid.hash);
        if (bitmap_pos < 0)
                die("Object not in bitmap: %s\n", oid_to_hex(&object->oid));
 
@@ -907,7 +940,8 @@ static void test_show_commit(struct commit *commit, void *data)
        struct bitmap_test_data *tdata = data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(commit->object.oid.hash);
+       bitmap_pos = bitmap_position(tdata->bitmap_git,
+                                    commit->object.oid.hash);
        if (bitmap_pos < 0)
                die("Object not in bitmap: %s\n", oid_to_hex(&commit->object.oid));
 
@@ -922,21 +956,22 @@ void test_bitmap_walk(struct rev_info *revs)
        khiter_t pos;
        size_t result_popcnt;
        struct bitmap_test_data tdata;
+       struct bitmap_index *bitmap_git;
 
-       if (prepare_bitmap_git())
+       if (!(bitmap_git = prepare_bitmap_git()))
                die("failed to load bitmap indexes");
 
        if (revs->pending.nr != 1)
                die("you must specify exactly one commit to test");
 
        fprintf(stderr, "Bitmap v%d test (%d entries loaded)\n",
-               bitmap_git.version, bitmap_git.entry_count);
+               bitmap_git->version, bitmap_git->entry_count);
 
        root = revs->pending.objects[0].item;
-       pos = kh_get_sha1(bitmap_git.bitmaps, root->oid.hash);
+       pos = kh_get_sha1(bitmap_git->bitmaps, root->oid.hash);
 
-       if (pos < kh_end(bitmap_git.bitmaps)) {
-               struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);
+       if (pos < kh_end(bitmap_git->bitmaps)) {
+               struct stored_bitmap *st = kh_value(bitmap_git->bitmaps, pos);
                struct ewah_bitmap *bm = lookup_stored_bitmap(st);
 
                fprintf(stderr, "Found bitmap for %s. %d bits / %08x checksum\n",
@@ -957,6 +992,7 @@ void test_bitmap_walk(struct rev_info *revs)
        if (prepare_revision_walk(revs))
                die("revision walk setup failed");
 
+       tdata.bitmap_git = bitmap_git;
        tdata.base = bitmap_new();
        tdata.prg = start_progress("Verifying bitmap entries", result_popcnt);
        tdata.seen = 0;
@@ -970,7 +1006,7 @@ void test_bitmap_walk(struct rev_info *revs)
        else
                fprintf(stderr, "Mismatch!\n");
 
-       bitmap_free(result);
+       free_bitmap_index(bitmap_git);
 }
 
 static int rebuild_bitmap(uint32_t *reposition,
@@ -1004,7 +1040,8 @@ static int rebuild_bitmap(uint32_t *reposition,
        return 0;
 }
 
-int rebuild_existing_bitmaps(struct packing_data *mapping,
+int rebuild_existing_bitmaps(struct bitmap_index *bitmap_git,
+                            struct packing_data *mapping,
                             khash_sha1 *reused_bitmaps,
                             int show_progress)
 {
@@ -1017,10 +1054,7 @@ int rebuild_existing_bitmaps(struct packing_data *mapping,
        khiter_t hash_pos;
        int hash_ret;
 
-       if (prepare_bitmap_git() < 0)
-               return -1;
-
-       num_objects = bitmap_git.pack->num_objects;
+       num_objects = bitmap_git->pack->num_objects;
        reposition = xcalloc(num_objects, sizeof(uint32_t));
 
        for (i = 0; i < num_objects; ++i) {
@@ -1028,8 +1062,8 @@ int rebuild_existing_bitmaps(struct packing_data *mapping,
                struct revindex_entry *entry;
                struct object_entry *oe;
 
-               entry = &bitmap_git.pack->revindex[i];
-               sha1 = nth_packed_object_sha1(bitmap_git.pack, entry->nr);
+               entry = &bitmap_git->pack->revindex[i];
+               sha1 = nth_packed_object_sha1(bitmap_git->pack, entry->nr);
                oe = packlist_find(mapping, sha1, NULL);
 
                if (oe)
@@ -1042,7 +1076,7 @@ int rebuild_existing_bitmaps(struct packing_data *mapping,
        if (show_progress)
                progress = start_progress("Reusing bitmaps", 0);
 
-       kh_foreach_value(bitmap_git.bitmaps, stored, {
+       kh_foreach_value(bitmap_git->bitmaps, stored, {
                if (stored->flags & BITMAP_FLAG_REUSE) {
                        if (!rebuild_bitmap(reposition,
                                            lookup_stored_bitmap(stored),
@@ -1064,3 +1098,21 @@ int rebuild_existing_bitmaps(struct packing_data *mapping,
        bitmap_free(rebuild);
        return 0;
 }
+
+void free_bitmap_index(struct bitmap_index *b)
+{
+       if (!b)
+               return;
+
+       if (b->map)
+               munmap(b->map, b->map_size);
+       ewah_pool_free(b->commits);
+       ewah_pool_free(b->trees);
+       ewah_pool_free(b->blobs);
+       ewah_pool_free(b->tags);
+       kh_destroy_sha1(b->bitmaps);
+       free(b->ext_index.objects);
+       free(b->ext_index.hashes);
+       bitmap_free(b->result);
+       free(b);
+}
index 5ded2f139a6ccdab725ed5e568b1100906f4b736..4555907dee99fbf731b739c584837adbbe8270f1 100644 (file)
@@ -34,13 +34,21 @@ typedef int (*show_reachable_fn)(
        struct packed_git *found_pack,
        off_t found_offset);
 
-int prepare_bitmap_git(void);
-void count_bitmap_commit_list(uint32_t *commits, uint32_t *trees, uint32_t *blobs, uint32_t *tags);
-void traverse_bitmap_commit_list(show_reachable_fn show_reachable);
+struct bitmap_index;
+
+struct bitmap_index *prepare_bitmap_git(void);
+void count_bitmap_commit_list(struct bitmap_index *, uint32_t *commits,
+                             uint32_t *trees, uint32_t *blobs, uint32_t *tags);
+void traverse_bitmap_commit_list(struct bitmap_index *,
+                                show_reachable_fn show_reachable);
 void test_bitmap_walk(struct rev_info *revs);
-int prepare_bitmap_walk(struct rev_info *revs);
-int reuse_partial_packfile_from_bitmap(struct packed_git **packfile, uint32_t *entries, off_t *up_to);
-int rebuild_existing_bitmaps(struct packing_data *mapping, khash_sha1 *reused_bitmaps, int show_progress);
+struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs);
+int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
+                                      struct packed_git **packfile,
+                                      uint32_t *entries, off_t *up_to);
+int rebuild_existing_bitmaps(struct bitmap_index *, struct packing_data *mapping,
+                            khash_sha1 *reused_bitmaps, int show_progress);
+void free_bitmap_index(struct bitmap_index *);
 
 void bitmap_writer_show_progress(int show);
 void bitmap_writer_set_checksum(unsigned char *sha1);
diff --git a/path.h b/path.h
index 5263f40519a3d4093f196513b27b2f8dc19418a4..ed6732e5a22ca8fbceff68437c9df616bb819cc9 100644 (file)
--- a/path.h
+++ b/path.h
@@ -147,7 +147,7 @@ extern void report_linked_checkout_garbage(void);
 /*
  * You can define a static memoized git path like:
  *
- *    static GIT_PATH_FUNC(git_path_foo, "FOO");
+ *    static GIT_PATH_FUNC(git_path_foo, "FOO")
  *
  * or use one of the global ones below.
  */
index cde4fe07db0403d8cb63848842a4db26f62c12c7..2b12da324bc0171c24fd88b6da636f70cd11f214 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1575,7 +1575,7 @@ static void pp_header(struct pretty_print_context *pp,
                }
 
                if (starts_with(line, "parent ")) {
-                       if (linelen != 48)
+                       if (linelen != the_hash_algo->hexsz + 8)
                                die("bad parent line in commit");
                        continue;
                }
@@ -1583,7 +1583,7 @@ static void pp_header(struct pretty_print_context *pp,
                if (!parents_shown) {
                        unsigned num = commit_list_count(commit->parents);
                        /* with enough slop */
-                       strbuf_grow(sb, num * 50 + 20);
+                       strbuf_grow(sb, num * (GIT_MAX_HEXSZ + 10) + 20);
                        add_merge_info(pp, sb, commit);
                        parents_shown = 1;
                }
index 49021ee4467de5a3f12845ab47507a6222cd07ae..600774de68a95ca416b069351cef4724ca7a9cd0 100644 (file)
@@ -1816,7 +1816,7 @@ static int match_name_as_path(const struct ref_filter *filter, const char *refna
                     refname[plen] == '/' ||
                     p[plen-1] == '/'))
                        return 1;
-               if (!wildmatch(p, refname, WM_PATHNAME))
+               if (!wildmatch(p, refname, flags))
                        return 1;
        }
        return 0;
@@ -1871,6 +1871,15 @@ static int for_each_fullref_in_pattern(struct ref_filter *filter,
                return for_each_fullref_in("", cb, cb_data, broken);
        }
 
+       if (filter->ignore_case) {
+               /*
+                * we can't handle case-insensitive comparisons,
+                * so just return everything and let the caller
+                * sort it out.
+                */
+               return for_each_fullref_in("", cb, cb_data, broken);
+       }
+
        if (!filter->name_patterns[0]) {
                /* no patterns; we have to look at everything */
                return for_each_fullref_in("", cb, cb_data, broken);
diff --git a/refs.c b/refs.c
index fcfd3171e83a116d4ba2014b2f88622969f21a2d..457fb78057a9375eb2b2cda44214a1705a28fc1e 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -787,25 +787,21 @@ int delete_ref(const char *msg, const char *refname,
                               old_oid, flags);
 }
 
-int copy_reflog_msg(char *buf, const char *msg)
+void copy_reflog_msg(struct strbuf *sb, const char *msg)
 {
-       char *cp = buf;
        char c;
        int wasspace = 1;
 
-       *cp++ = '\t';
+       strbuf_addch(sb, '\t');
        while ((c = *msg++)) {
                if (wasspace && isspace(c))
                        continue;
                wasspace = isspace(c);
                if (wasspace)
                        c = ' ';
-               *cp++ = c;
+               strbuf_addch(sb, c);
        }
-       while (buf < cp && isspace(cp[-1]))
-               cp--;
-       *cp++ = '\n';
-       return cp - buf;
+       strbuf_rtrim(sb);
 }
 
 int should_autocreate_reflog(const char *refname)
index 55c2ae0bd591f01760cc3824edebf8893689f152..b9eb3aabe6388a6437109439c3ac2ca298853892 100644 (file)
@@ -1582,26 +1582,17 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,
                            const struct object_id *new_oid,
                            const char *committer, const char *msg)
 {
-       int msglen, written;
-       unsigned maxlen, len;
-       char *logrec;
-
-       msglen = msg ? strlen(msg) : 0;
-       maxlen = strlen(committer) + msglen + 100;
-       logrec = xmalloc(maxlen);
-       len = xsnprintf(logrec, maxlen, "%s %s %s\n",
-                       oid_to_hex(old_oid),
-                       oid_to_hex(new_oid),
-                       committer);
-       if (msglen)
-               len += copy_reflog_msg(logrec + len - 1, msg) - 1;
-
-       written = len <= maxlen ? write_in_full(fd, logrec, len) : -1;
-       free(logrec);
-       if (written < 0)
-               return -1;
+       struct strbuf sb = STRBUF_INIT;
+       int ret = 0;
 
-       return 0;
+       strbuf_addf(&sb, "%s %s %s", oid_to_hex(old_oid), oid_to_hex(new_oid), committer);
+       if (msg && *msg)
+               copy_reflog_msg(&sb, msg);
+       strbuf_addch(&sb, '\n');
+       if (write_in_full(fd, sb.buf, sb.len) < 0)
+               ret = -1;
+       strbuf_release(&sb);
+       return ret;
 }
 
 static int files_log_ref_write(struct files_ref_store *refs,
@@ -1676,7 +1667,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
                return -1;
        }
        fd = get_lock_file_fd(&lock->lk);
-       if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) < 0 ||
+       if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
            write_in_full(fd, &term, 1) < 0 ||
            close_ref_gently(lock) < 0) {
                strbuf_addf(err,
@@ -3070,7 +3061,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
                        rollback_lock_file(&reflog_lock);
                } else if (update &&
                           (write_in_full(get_lock_file_fd(&lock->lk),
-                               oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) < 0 ||
+                               oid_to_hex(&cb.last_kept_oid), the_hash_algo->hexsz) < 0 ||
                            write_str_in_full(get_lock_file_fd(&lock->lk), "\n") < 0 ||
                            close_ref_gently(lock) < 0)) {
                        status |= error("couldn't write %s",
index dd834314bd8d5af0315dab19af50f3300a73ad90..04425d6d1e45e2401454e7b89b0ed4b3959926de 100644 (file)
@@ -1,6 +1,8 @@
 #ifndef REFS_REFS_INTERNAL_H
 #define REFS_REFS_INTERNAL_H
 
+#include "iterator.h"
+
 /*
  * Data structures and functions for the internal use of the refs
  * module. Code outside of the refs module should use only the public
@@ -91,11 +93,10 @@ enum peel_status {
 enum peel_status peel_object(const struct object_id *name, struct object_id *oid);
 
 /*
- * Copy the reflog message msg to buf, which has been allocated sufficiently
- * large, while cleaning up the whitespaces.  Especially, convert LF to space,
- * because reflog file is one line per entry.
+ * Copy the reflog message msg to sb while cleaning up the whitespaces.
+ * Especially, convert LF to space, because reflog file is one line per entry.
  */
-int copy_reflog_msg(char *buf, const char *msg);
+void copy_reflog_msg(struct strbuf *sb, const char *msg);
 
 /**
  * Information needed for a single ref update. Set new_oid to the new
index 26b1fbd9a881d2af7d7cc7a098fe182d36502d80..86e6098774d891a3b7045cf1dd9c11959faf13dc 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -1737,6 +1737,7 @@ int get_fetch_map(const struct ref *remote_refs,
                if (refspec->exact_sha1) {
                        ref_map = alloc_ref(name);
                        get_oid_hex(name, &ref_map->old_oid);
+                       ref_map->exact_oid = 1;
                } else {
                        ref_map = get_remote_ref(remote_refs, name);
                }
index 45ecc6cefafdea435d0fee39691f7458b940b877..976292152c020127e6c88d54e5448ef21e0eb4e8 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -73,6 +73,7 @@ struct ref {
                force:1,
                forced_update:1,
                expect_old_sha1:1,
+               exact_oid:1,
                deletion:1;
 
        enum {
index 4dbe406bed2aa13f0685d98f0932f997e88b4972..8bd2e27037d8136ec96d31ed1a41eb45a040e475 100644 (file)
@@ -175,6 +175,7 @@ static void add_pending_object_with_path(struct rev_info *revs,
                strbuf_release(&buf);
                return; /* do not add the commit itself */
        }
+       obj->flags |= USER_GIVEN;
        add_object_array_with_path(obj, name, &revs->pending, mode, path);
 }
 
index bf2239f87689d9486c4b888bed5cba6affa21953..c599c34da91e57572b1c5b315c353d33399e8a09 100644 (file)
@@ -20,8 +20,9 @@
 #define SYMMETRIC_LEFT (1u<<8)
 #define PATCHSAME      (1u<<9)
 #define BOTTOM         (1u<<10)
+#define USER_GIVEN     (1u<<25) /* given directly by the user */
 #define TRACK_LINEAR   (1u<<26)
-#define ALL_REV_FLAGS  (((1u<<11)-1) | TRACK_LINEAR)
+#define ALL_REV_FLAGS  (((1u<<11)-1) | USER_GIVEN | TRACK_LINEAR)
 
 #define DECORATE_SHORT_REFS    1
 #define DECORATE_FULL_REFS     2
index d1d07bed5bb051d980015c85129333effe66391c..5a068fd35116a1e664d7cefbd363e984be6cc0b6 100644 (file)
@@ -63,12 +63,12 @@ static GIT_PATH_FUNC(rebase_path_done, "rebase-merge/done")
  * The file to keep track of how many commands were already processed (e.g.
  * for the prompt).
  */
-static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum");
+static GIT_PATH_FUNC(rebase_path_msgnum, "rebase-merge/msgnum")
 /*
  * The file to keep track of how many commands are to be processed in total
  * (e.g. for the prompt).
  */
-static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end");
+static GIT_PATH_FUNC(rebase_path_msgtotal, "rebase-merge/end")
 /*
  * The commit message that is planned to be used for any changes that
  * need to be committed following a user interaction.
@@ -1864,8 +1864,6 @@ static int prepare_revs(struct replay_opts *opts)
        if (prepare_revision_walk(opts->revs))
                return error(_("revision walk setup failed"));
 
-       if (!opts->revs->commits)
-               return error(_("empty commit set passed"));
        return 0;
 }
 
@@ -2207,6 +2205,7 @@ static int populate_opts_cb(const char *key, const char *value, void *data)
 static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
 {
        int i;
+       char *strategy_opts_string;
 
        strbuf_reset(buf);
        if (!read_oneliner(buf, rebase_path_strategy(), 0))
@@ -2215,7 +2214,11 @@ static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)
        if (!read_oneliner(buf, rebase_path_strategy_opts(), 0))
                return;
 
-       opts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts);
+       strategy_opts_string = buf->buf;
+       if (*strategy_opts_string == ' ')
+               strategy_opts_string++;
+       opts->xopts_nr = split_cmdline(strategy_opts_string,
+                                      (const char ***)&opts->xopts);
        for (i = 0; i < opts->xopts_nr; i++) {
                const char *arg = opts->xopts[i];
 
@@ -2318,6 +2321,10 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
                        short_commit_name(commit), subject_len, subject);
                unuse_commit_buffer(commit, commit_buffer);
        }
+
+       if (!todo_list->nr)
+               return error(_("empty commit set passed"));
+
        return 0;
 }
 
@@ -3219,10 +3226,27 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                        intend_to_amend();
                                return error_failed_squash(item->commit, opts,
                                        item->arg_len, item->arg);
-                       } else if (res && is_rebase_i(opts) && item->commit)
+                       } else if (res && is_rebase_i(opts) && item->commit) {
+                               int to_amend = 0;
+                               struct object_id oid;
+
+                               /*
+                                * If we are rewording and have either
+                                * fast-forwarded already, or are about to
+                                * create a new root commit, we want to amend,
+                                * otherwise we do not.
+                                */
+                               if (item->command == TODO_REWORD &&
+                                   !get_oid("HEAD", &oid) &&
+                                   (!oidcmp(&item->commit->object.oid, &oid) ||
+                                    (opts->have_squash_onto &&
+                                     !oidcmp(&opts->squash_onto, &oid))))
+                                       to_amend = 1;
+
                                return res | error_with_patch(item->commit,
-                                       item->arg, item->arg_len, opts, res,
-                                       item->command == TODO_REWORD);
+                                               item->arg, item->arg_len, opts,
+                                               res, to_amend);
+                       }
                } else if (item->command == TODO_EXEC) {
                        char *end_of_arg = (char *)(item->arg + item->arg_len);
                        int saved = *end_of_arg;
@@ -3636,8 +3660,10 @@ int sequencer_pick_revisions(struct replay_opts *opts)
                if (prepare_revision_walk(opts->revs))
                        return error(_("revision walk setup failed"));
                cmit = get_revision(opts->revs);
-               if (!cmit || get_revision(opts->revs))
-                       return error("BUG: expected exactly one commit from walk");
+               if (!cmit)
+                       return error(_("empty commit set passed"));
+               if (get_revision(opts->revs))
+                       BUG("unexpected extra commit from walk");
                return single_pick(cmit, opts);
        }
 
@@ -3996,7 +4022,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                entry = oidmap_get(&state.commit2label, &commit->object.oid);
 
                if (entry)
-                       fprintf(out, "\n# Branch %s\n", entry->string);
+                       fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
                else
                        fprintf(out, "\n");
 
index c75ef771f8b5c9280466917f8b314d0fe9659884..dfa8a35d68d9b40d4fa30cff15c1f027a201192e 100644 (file)
@@ -336,7 +336,7 @@ out:
 static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1)
 {
        int i;
-       for (i = 0; i < 20; i++) {
+       for (i = 0; i < the_hash_algo->rawsz; i++) {
                static char hex[] = "0123456789abcdef";
                unsigned int val = sha1[i];
                strbuf_addch(buf, hex[val >> 4]);
@@ -1473,7 +1473,7 @@ void *read_object_with_reference(const struct object_id *oid,
                }
                ref_length = strlen(ref_type);
 
-               if (ref_length + GIT_SHA1_HEXSZ > isize ||
+               if (ref_length + the_hash_algo->hexsz > isize ||
                    memcmp(buffer, ref_type, ref_length) ||
                    get_oid_hex((char *) buffer + ref_length, &actual_oid)) {
                        free(buffer);
@@ -2062,9 +2062,9 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,
                namelen = strlen(de->d_name);
                strbuf_setlen(path, baselen);
                strbuf_add(path, de->d_name, namelen);
-               if (namelen == GIT_SHA1_HEXSZ - 2 &&
+               if (namelen == the_hash_algo->hexsz - 2 &&
                    !hex_to_bytes(oid.hash + 1, de->d_name,
-                                 GIT_SHA1_RAWSZ - 1)) {
+                                 the_hash_algo->rawsz - 1)) {
                        if (obj_cb) {
                                r = obj_cb(&oid, path->buf, data);
                                if (r)
index 009faab4aefc34f55daf6fd7e6e63ec0f0ee108a..c9cc1318b7394e86704bda95651c9a4db3015b9a 100644 (file)
@@ -312,7 +312,7 @@ static int init_object_disambiguation(const char *name, int len,
 {
        int i;
 
-       if (len < MINIMUM_ABBREV || len > GIT_SHA1_HEXSZ)
+       if (len < MINIMUM_ABBREV || len > the_hash_algo->hexsz)
                return -1;
 
        memset(ds, 0, sizeof(*ds));
@@ -578,6 +578,8 @@ int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
        struct disambiguate_state ds;
        struct min_abbrev_data mad;
        struct object_id oid_ret;
+       const unsigned hexsz = the_hash_algo->hexsz;
+
        if (len < 0) {
                unsigned long count = approximate_object_count();
                /*
@@ -601,8 +603,8 @@ int find_unique_abbrev_r(char *hex, const struct object_id *oid, int len)
        }
 
        oid_to_hex_r(hex, oid);
-       if (len == GIT_SHA1_HEXSZ || !len)
-               return GIT_SHA1_HEXSZ;
+       if (len == hexsz || !len)
+               return hexsz;
 
        mad.init_len = len;
        mad.cur_len = len;
@@ -708,7 +710,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
        int refs_found = 0;
        int at, reflog_len, nth_prior = 0;
 
-       if (len == GIT_SHA1_HEXSZ && !get_oid_hex(str, oid)) {
+       if (len == the_hash_algo->hexsz && !get_oid_hex(str, oid)) {
                if (warn_ambiguous_refs && warn_on_object_refname_ambiguity) {
                        refs_found = dwim_ref(str, len, &tmp_oid, &real_ref);
                        if (refs_found > 0) {
@@ -752,7 +754,7 @@ static int get_oid_basic(const char *str, int len, struct object_id *oid,
                int detached;
 
                if (interpret_nth_prior_checkout(str, len, &buf) > 0) {
-                       detached = (buf.len == GIT_SHA1_HEXSZ && !get_oid_hex(buf.buf, oid));
+                       detached = (buf.len == the_hash_algo->hexsz && !get_oid_hex(buf.buf, oid));
                        strbuf_release(&buf);
                        if (detached)
                                return 0;
@@ -1653,6 +1655,7 @@ static int get_oid_with_context_1(const char *name,
                        struct commit_list *list = NULL;
 
                        for_each_ref(handle_one_ref, &list);
+                       head_ref(handle_one_ref, &list);
                        commit_list_sort_by_date(&list);
                        return get_oid_oneline(name + 2, oid, list);
                }
index b0716ac585285a971c04952254c34a00aab9f0ef..030556111d197e02bb2b2ed0796e683fe69391d6 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -921,7 +921,7 @@ void strbuf_add_unique_abbrev(struct strbuf *sb, const struct object_id *oid,
                              int abbrev_len)
 {
        int r;
-       strbuf_grow(sb, GIT_SHA1_HEXSZ + 1);
+       strbuf_grow(sb, GIT_MAX_HEXSZ + 1);
        r = find_unique_abbrev_r(sb->buf + sb->len, oid, abbrev_len);
        strbuf_setlen(sb, sb->len + r);
 }
index a0cf0cfe88ee38f35ec33662778ec1bd7036a127..771c4550980e24eb8bf9db14e27c49f983c1ed4b 100644 (file)
@@ -224,18 +224,28 @@ struct string_list_item *string_list_append(struct string_list *list,
                        list->strdup_strings ? xstrdup(string) : (char *)string);
 }
 
+/*
+ * Encapsulate the compare function pointer because ISO C99 forbids
+ * casting from void * to a function pointer and vice versa.
+ */
+struct string_list_sort_ctx
+{
+       compare_strings_fn cmp;
+};
+
 static int cmp_items(const void *a, const void *b, void *ctx)
 {
-       compare_strings_fn cmp = ctx;
+       struct string_list_sort_ctx *sort_ctx = ctx;
        const struct string_list_item *one = a;
        const struct string_list_item *two = b;
-       return cmp(one->string, two->string);
+       return sort_ctx->cmp(one->string, two->string);
 }
 
 void string_list_sort(struct string_list *list)
 {
-       QSORT_S(list->items, list->nr, cmp_items,
-               list->cmp ? list->cmp : strcmp);
+       struct string_list_sort_ctx sort_ctx = {list->cmp ? list->cmp : strcmp};
+
+       QSORT_S(list->items, list->nr, cmp_items, &sort_ctx);
 }
 
 struct string_list_item *unsorted_string_list_lookup(struct string_list *list,
index ca324a9e37d8ce738db417e2cb689cbe0508cff0..fc2c41b947cb471deef42323c83f8b28f42780d6 100644 (file)
@@ -562,7 +562,7 @@ static const struct submodule *config_from(struct submodule_cache *cache,
        parameter.gitmodules_oid = &oid;
        parameter.overwrite = 0;
        git_config_from_mem(parse_config, CONFIG_ORIGIN_SUBMODULE_BLOB, rev.buf,
-                       config, config_size, &parameter);
+                       config, config_size, &parameter, NULL);
        strbuf_release(&rev);
        free(config);
 
@@ -592,6 +592,23 @@ static void submodule_cache_check_init(struct repository *repo)
        submodule_cache_init(repo->submodule_cache);
 }
 
+/*
+ * Note: This function is private for a reason, the '.gitmodules' file should
+ * not be used as as a mechanism to retrieve arbitrary configuration stored in
+ * the repository.
+ *
+ * Runs the provided config function on the '.gitmodules' file found in the
+ * working directory.
+ */
+static void config_from_gitmodules(config_fn_t fn, struct repository *repo, void *data)
+{
+       if (repo->worktree) {
+               char *file = repo_worktree_path(repo, GITMODULES_FILE);
+               git_config_from_file(fn, file, data);
+               free(file);
+       }
+}
+
 static int gitmodules_cb(const char *var, const char *value, void *data)
 {
        struct repository *repo = data;
@@ -609,19 +626,11 @@ void repo_read_gitmodules(struct repository *repo)
 {
        submodule_cache_check_init(repo);
 
-       if (repo->worktree) {
-               char *gitmodules;
-
-               if (repo_read_index(repo) < 0)
-                       return;
-
-               gitmodules = repo_worktree_path(repo, GITMODULES_FILE);
-
-               if (!is_gitmodules_unmerged(repo->index))
-                       git_config_from_file(gitmodules_cb, gitmodules, repo);
+       if (repo_read_index(repo) < 0)
+               return;
 
-               free(gitmodules);
-       }
+       if (!is_gitmodules_unmerged(repo->index))
+               config_from_gitmodules(gitmodules_cb, repo, repo);
 
        repo->submodule_cache->gitmodules_read = 1;
 }
@@ -672,3 +681,45 @@ void submodule_free(struct repository *r)
        if (r->submodule_cache)
                submodule_cache_clear(r->submodule_cache);
 }
+
+struct fetch_config {
+       int *max_children;
+       int *recurse_submodules;
+};
+
+static int gitmodules_fetch_config(const char *var, const char *value, void *cb)
+{
+       struct fetch_config *config = cb;
+       if (!strcmp(var, "submodule.fetchjobs")) {
+               *(config->max_children) = parse_submodule_fetchjobs(var, value);
+               return 0;
+       } else if (!strcmp(var, "fetch.recursesubmodules")) {
+               *(config->recurse_submodules) = parse_fetch_recurse_submodules_arg(var, value);
+               return 0;
+       }
+
+       return 0;
+}
+
+void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules)
+{
+       struct fetch_config config = {
+               .max_children = max_children,
+               .recurse_submodules = recurse_submodules
+       };
+       config_from_gitmodules(gitmodules_fetch_config, the_repository, &config);
+}
+
+static int gitmodules_update_clone_config(const char *var, const char *value,
+                                         void *cb)
+{
+       int *max_jobs = cb;
+       if (!strcmp(var, "submodule.fetchjobs"))
+               *max_jobs = parse_submodule_fetchjobs(var, value);
+       return 0;
+}
+
+void update_clone_config_from_gitmodules(int *max_jobs)
+{
+       config_from_gitmodules(gitmodules_update_clone_config, the_repository, &max_jobs);
+}
index ca1f94e2d2ed90f9ef4c588e27c0d8e15e1724ab..dc7278eea45deedc33635af50b016e9ee28df7d2 100644 (file)
@@ -2,6 +2,7 @@
 #define SUBMODULE_CONFIG_CACHE_H
 
 #include "cache.h"
+#include "config.h"
 #include "hashmap.h"
 #include "submodule.h"
 #include "strbuf.h"
@@ -55,4 +56,15 @@ void submodule_free(struct repository *r);
  */
 int check_submodule_name(const char *name);
 
+/*
+ * Note: these helper functions exist solely to maintain backward
+ * compatibility with 'fetch' and 'update_clone' storing configuration in
+ * '.gitmodules'.
+ *
+ * New helpers to retrieve arbitrary configuration from the '.gitmodules' file
+ * should NOT be added.
+ */
+extern void fetch_config_from_gitmodules(int *max_children, int *recurse_submodules);
+extern void update_clone_config_from_gitmodules(int *max_jobs);
+
 #endif /* SUBMODULE_CONFIG_H */
index 6688dd5d45949597d7d5761c9b88ea9fcceabf2d..6e14547e9e0000e6bf80e9ad21da81795cebcab7 100644 (file)
@@ -1534,6 +1534,18 @@ out:
        return ret;
 }
 
+void submodule_unset_core_worktree(const struct submodule *sub)
+{
+       char *config_path = xstrfmt("%s/modules/%s/config",
+                                   get_git_common_dir(), sub->name);
+
+       if (git_config_set_in_file_gently(config_path, "core.worktree", NULL))
+               warning(_("Could not unset core.worktree setting in submodule '%s'"),
+                         sub->path);
+
+       free(config_path);
+}
+
 static const char *get_super_prefix_or_empty(void)
 {
        const char *s = get_super_prefix();
@@ -1670,7 +1682,7 @@ int submodule_move_head(const char *path,
        argv_array_push(&cp.args, new_head ? new_head : empty_tree_oid_hex());
 
        if (run_command(&cp)) {
-               ret = -1;
+               ret = error(_("Submodule '%s' could not be updated."), path);
                goto out;
        }
 
@@ -1699,6 +1711,8 @@ int submodule_move_head(const char *path,
 
                        if (is_empty_dir(path))
                                rmdir_or_warn(path);
+
+                       submodule_unset_core_worktree(sub);
                }
        }
 out:
index 7856b8a0b3d676cde20095f69a4d000d8cf38615..4644683e6cb39916a942e501eacb8663d89c3f6b 100644 (file)
@@ -121,6 +121,8 @@ extern int submodule_move_head(const char *path,
                               const char *new_head,
                               unsigned flags);
 
+void submodule_unset_core_worktree(const struct submodule *sub);
+
 /*
  * Prepare the "env_array" parameter of a "struct child_process" for executing
  * a submodule by clearing any repo-specific environment variables, but
index 093832fef1541906b735d162a76c8ffa773d236c..6da48a2e0a461e35f31fdd05712ee5c2bb9fda31 100644 (file)
@@ -320,11 +320,11 @@ test_expect_success 'blame -L ,Y (Y == nlines)' '
 
 test_expect_success 'blame -L ,Y (Y == nlines + 1)' '
        n=$(expr $(wc -l <file) + 2) &&
-       test_must_fail $PROG -L,$n file
+       check_count -L,$n A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
 '
 
 test_expect_success 'blame -L ,Y (Y > nlines)' '
-       test_must_fail $PROG -L,12345 file
+       check_count -L,12345 A 1 B 1 B1 1 B2 1 "A U Thor" 1 C 1 D 1 E 1
 '
 
 test_expect_success 'blame -L multiple (disjoint)' '
index e07f0284372ec25c91fd62f0d136c31d80263a0d..d5823f71d8d57ec92404d5cceabd86d5ab86af53 100755 (executable)
@@ -7,22 +7,43 @@ use strict;
 use warnings;
 
 my $exit_code=0;
+my %func;
 
 sub err {
        my $msg = shift;
+       s/^\s+//;
+       s/\s+$//;
+       s/\s+/ /g;
        print "$ARGV:$.: error: $msg: $_\n";
        $exit_code = 1;
 }
 
+# glean names of shell functions
+for my $i (@ARGV) {
+       open(my $f, '<', $i) or die "$0: $i: $!\n";
+       while (<$f>) {
+               $func{$1} = 1 if /^\s*(\w+)\s*\(\)\s*{\s*$/;
+       }
+       close $f;
+}
+
 while (<>) {
        chomp;
+       # stitch together incomplete lines (those ending with "\")
+       while (s/\\$//) {
+               $_ .= readline;
+               chomp;
+       }
+
        /\bsed\s+-i/ and err 'sed -i is not portable';
-       /\becho\s+-[neE]/ and err 'echo with option is not portable (please use printf)';
+       /\becho\s+-[neE]/ and err 'echo with option is not portable (use printf)';
        /^\s*declare\s+/ and err 'arrays/declare not portable';
-       /^\s*[^#]\s*which\s/ and err 'which is not portable (please use type)';
-       /\btest\s+[^=]*==/ and err '"test a == b" is not portable (please use =)';
-       /\bwc -l.*"\s*=/ and err '`"$(wc -l)"` is not portable (please use test_line_count)';
-       /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (please use FOO=bar && export FOO)';
+       /^\s*[^#]\s*which\s/ and err 'which is not portable (use type)';
+       /\btest\s+[^=]*==/ and err '"test a == b" is not portable (use =)';
+       /\bwc -l.*"\s*=/ and err '`"$(wc -l)"` is not portable (use test_line_count)';
+       /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
+       /^\s*([A-Z0-9_]+=(\w+|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
+               err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
        # this resets our $. for each file
        close ARGV if eof;
 }
index d6bcfddf13352b92d6be4d9d5b8e7d7f05cc06ee..f65e301f9ddc13f395b88a4b8cf994506beee292 100644 (file)
@@ -16,8 +16,8 @@ static int cmd_sync(void)
        if ((0 == dwRet) || (dwRet > MAX_PATH))
                return error("Error getting current directory");
 
-       if ((Buffer[0] < 'A') || (Buffer[0] > 'Z'))
-               return error("Invalid drive letter '%c'", Buffer[0]);
+       if (!has_dos_drive_prefix(Buffer))
+               return error("'%s': invalid drive letter", Buffer);
 
        szVolumeAccessPath[4] = Buffer[0];
        hVolWrite = CreateFile(szVolumeAccessPath, GENERIC_READ | GENERIC_WRITE,
index 0f19e53c75bf28fc075e7a220cf8fa78507def7f..30775f986f8067323808f6ccd064067f6c27489b 100644 (file)
@@ -1,3 +1,4 @@
+#include "cache.h"
 #include "pkt-line.h"
 
 static void pack_line(const char *line)
@@ -48,6 +49,36 @@ static void unpack(void)
        }
 }
 
+static void unpack_sideband(void)
+{
+       struct packet_reader reader;
+       packet_reader_init(&reader, 0, NULL, 0,
+                          PACKET_READ_GENTLE_ON_EOF |
+                          PACKET_READ_CHOMP_NEWLINE);
+
+       while (packet_reader_read(&reader) != PACKET_READ_EOF) {
+               int band;
+               int fd;
+
+               switch (reader.status) {
+               case PACKET_READ_EOF:
+                       break;
+               case PACKET_READ_NORMAL:
+                       band = reader.line[0] & 0xff;
+                       if (band < 1 || band > 2)
+                               die("unexpected side band %d", band);
+                       fd = band;
+
+                       write_or_die(fd, reader.line + 1, reader.pktlen - 1);
+                       break;
+               case PACKET_READ_FLUSH:
+                       return;
+               case PACKET_READ_DELIM:
+                       break;
+               }
+       }
+}
+
 int cmd_main(int argc, const char **argv)
 {
        if (argc < 2)
@@ -57,6 +88,8 @@ int cmd_main(int argc, const char **argv)
                pack(argc - 2, argv + 2);
        else if (!strcmp(argv[1], "unpack"))
                unpack();
+       else if (!strcmp(argv[1], "unpack-sideband"))
+               unpack_sideband();
        else
                die("invalid argument '%s'", argv[1]);
 
index 435a37465a702c35a5d53d8809f0ef700ae9ff83..a8729f82325ee7fb9350c42553e11205e6720928 100644 (file)
@@ -132,6 +132,7 @@ prepare_httpd() {
        cp "$TEST_PATH"/passwd "$HTTPD_ROOT_PATH"
        install_script broken-smart-http.sh
        install_script error.sh
+       install_script apply-one-time-sed.sh
 
        ln -s "$LIB_HTTPD_MODULE_PATH" "$HTTPD_ROOT_PATH/modules"
 
@@ -287,3 +288,24 @@ expect_askpass() {
        test_cmp "$TRASH_DIRECTORY/askpass-expect" \
                 "$TRASH_DIRECTORY/askpass-query"
 }
+
+strip_access_log() {
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " "$HTTPD_ROOT_PATH"/access.log
+}
+
+# Requires one argument: the name of a file containing the expected stripped
+# access log entries.
+check_access_log() {
+       sort "$1" >"$1".sorted &&
+       strip_access_log >access.log.stripped &&
+       sort access.log.stripped >access.log.sorted &&
+       if ! test_cmp "$1".sorted access.log.sorted
+       then
+               test_cmp "$1" access.log.stripped
+       fi
+}
index 724d9ae462b78af95298786a1fe26f375023ad55..581c010d8fc4c1184ba17b63b940aaac30d0b0f4 100644 (file)
@@ -111,9 +111,14 @@ Alias /auth/dumb/ www/auth/dumb/
        SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
        SetEnv GIT_HTTP_EXPORT_ALL
 </LocationMatch>
+<LocationMatch /one_time_sed/>
+       SetEnv GIT_EXEC_PATH ${GIT_EXEC_PATH}
+       SetEnv GIT_HTTP_EXPORT_ALL
+</LocationMatch>
 ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
 ScriptAlias /broken_smart/ broken-smart-http.sh/
 ScriptAlias /error/ error.sh/
+ScriptAliasMatch /one_time_sed/(.*) apply-one-time-sed.sh/$1
 <Directory ${GIT_EXEC_PATH}>
        Options FollowSymlinks
 </Directory>
@@ -123,6 +128,9 @@ ScriptAlias /error/ error.sh/
 <Files error.sh>
   Options ExecCGI
 </Files>
+<Files apply-one-time-sed.sh>
+       Options ExecCGI
+</Files>
 <Files ${GIT_EXEC_PATH}/git-http-backend>
        Options ExecCGI
 </Files>
diff --git a/t/lib-httpd/apply-one-time-sed.sh b/t/lib-httpd/apply-one-time-sed.sh
new file mode 100644 (file)
index 0000000..fcef728
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+# If "one-time-sed" exists in $HTTPD_ROOT_PATH, run sed on the HTTP response,
+# using the contents of "one-time-sed" as the sed command to be run. If the
+# response was modified as a result, delete "one-time-sed" so that subsequent
+# HTTP responses are no longer modified.
+#
+# This can be used to simulate the effects of the repository changing in
+# between HTTP request-response pairs.
+if [ -e one-time-sed ]; then
+       "$GIT_EXEC_PATH/git-http-backend" >out
+       sed "$(cat one-time-sed)" <out >out_modified
+
+       if diff out out_modified >/dev/null; then
+               cat out
+       else
+               cat out_modified
+               rm one-time-sed
+       fi
+else
+       "$GIT_EXEC_PATH/git-http-backend"
+fi
index 1f38a85371ab49790d14d3d4b82a0ee6e52789dd..5b56b23166bb3dea2e89cea38a19eb5698dfff53 100755 (executable)
@@ -235,7 +235,7 @@ reset_work_tree_to_interested () {
        then
                mkdir -p submodule_update/.git/modules/sub1/modules &&
                cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
-               GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
+               # core.worktree is unset for sub2 as it is not checked out
        fi &&
        # indicate we are interested in the submodule:
        git -C submodule_update config submodule.sub1.url "bogus" &&
@@ -709,7 +709,8 @@ test_submodule_recursing_with_args_common() {
                        git branch -t remove_sub1 origin/remove_sub1 &&
                        $command remove_sub1 &&
                        test_superproject_content origin/remove_sub1 &&
-                       ! test -e sub1
+                       ! test -e sub1 &&
+                       test_must_fail git config -f .git/modules/sub1/config core.worktree
                )
        '
        # ... absorbing a .git directory along the way.
@@ -755,7 +756,7 @@ test_submodule_recursing_with_args_common() {
                        : >sub1/untrackedfile &&
                        test_must_fail $command replace_sub1_with_file &&
                        test_superproject_content origin/add_sub1 &&
-                       test_submodule_content sub1 origin/add_sub1
+                       test_submodule_content sub1 origin/add_sub1 &&
                        test -f sub1/untracked_file
                )
        '
@@ -781,7 +782,8 @@ test_submodule_recursing_with_args_common() {
                (
                        cd submodule_update &&
                        git branch -t invalid_sub1 origin/invalid_sub1 &&
-                       test_must_fail $command invalid_sub1 &&
+                       test_must_fail $command invalid_sub1 2>err &&
+                       test_i18ngrep sub1 err &&
                        test_superproject_content origin/add_sub1 &&
                        test_submodule_content sub1 origin/add_sub1
                )
@@ -842,7 +844,7 @@ test_submodule_switch_recursing_with_args () {
                        cd submodule_update &&
                        git branch -t add_sub1 origin/add_sub1 &&
                        : >sub1 &&
-                       echo sub1 >.git/info/exclude
+                       echo sub1 >.git/info/exclude &&
                        $command add_sub1 &&
                        test_superproject_content origin/add_sub1 &&
                        test_submodule_content sub1 origin/add_sub1
@@ -969,7 +971,6 @@ test_submodule_forced_switch_recursing_with_args () {
                        rm -rf .git/modules/sub1 &&
                        $command replace_sub1_with_directory &&
                        test_superproject_content origin/replace_sub1_with_directory &&
-                       test_submodule_content sub1 origin/modify_sub1
                        test_git_directory_exists sub1
                )
        '
index af61d083b452c7973a2a703ef35fb57b76af84da..34859fe4a59fdbb48273f4b9e4f16e878b421349 100755 (executable)
@@ -1081,7 +1081,7 @@ test_expect_success 'very long name in the index handled sanely' '
        (
                git ls-files -s path4 |
                sed -e "s/      .*/     /" |
-               tr -d "\012"
+               tr -d "\012" &&
                echo "$a"
        ) | git update-index --index-info &&
        len=$(git ls-files "a*" | wc -c) &&
index 4c865051e7dd3d27eee0e2666b4aaaaa46116d5c..ca85aae51ed1c528d1b93e7fea5f53ecb8095851 100755 (executable)
@@ -408,7 +408,7 @@ is_hidden () {
 test_expect_success MINGW '.git hidden' '
        rm -rf newdir &&
        (
-               unset GIT_DIR GIT_WORK_TREE
+               sane_unset GIT_DIR GIT_WORK_TREE &&
                mkdir newdir &&
                cd newdir &&
                git init &&
@@ -420,7 +420,7 @@ test_expect_success MINGW '.git hidden' '
 test_expect_success MINGW 'bare git dir not hidden' '
        rm -rf newdir &&
        (
-               unset GIT_DIR GIT_WORK_TREE GIT_CONFIG
+               sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
                mkdir newdir &&
                cd newdir &&
                git --bare init
index f19ae4f8ccddacee84e04fa50663672f5f6174c1..5c37c2e1f8c3e96ae72dea1c7a5eef556fb18d00 100755 (executable)
@@ -34,15 +34,15 @@ test_expect_success 'open-quoted pathname' '
 test_expect_success 'setup' '
        mkdir -p a/b/d a/c b &&
        (
-               echo "[attr]notest !test"
-               echo "\" d \"   test=d"
-               echo " e        test=e"
-               echo " e\"      test=e"
-               echo "f test=f"
-               echo "a/i test=a/i"
-               echo "onoff test -test"
-               echo "offon -test test"
-               echo "no notest"
+               echo "[attr]notest !test" &&
+               echo "\" d \"   test=d" &&
+               echo " e        test=e" &&
+               echo " e\"      test=e" &&
+               echo "f test=f" &&
+               echo "a/i test=a/i" &&
+               echo "onoff test -test" &&
+               echo "offon -test test" &&
+               echo "no notest" &&
                echo "A/e/F test=A/e/F"
        ) >.gitattributes &&
        (
@@ -51,7 +51,7 @@ test_expect_success 'setup' '
        ) >a/.gitattributes &&
        (
                echo "h test=a/b/h" &&
-               echo "d/* test=a/b/d/*"
+               echo "d/* test=a/b/d/*" &&
                echo "d/yes notest"
        ) >a/b/.gitattributes &&
        (
@@ -287,7 +287,7 @@ test_expect_success 'bare repository: check that .gitattribute is ignored' '
        (
                cd bare.git &&
                (
-                       echo "f test=f"
+                       echo "f test=f" &&
                        echo "a/i test=a/i"
                ) >.gitattributes &&
                attr_check f unspecified &&
@@ -312,7 +312,7 @@ test_expect_success 'bare repository: test info/attributes' '
        (
                cd bare.git &&
                (
-                       echo "f test=f"
+                       echo "f test=f" &&
                        echo "a/i test=a/i"
                ) >info/attributes &&
                attr_check f f &&
index 9479a4aaabc1a4187fc702f864642ac493508bb9..6a213608cc10fc29691f8bf293b0a34ce1f93c5e 100755 (executable)
@@ -785,7 +785,7 @@ test_expect_success PERL 'missing file in delayed checkout' '
                cd repo &&
                git init &&
                echo "*.a filter=bug" >.gitattributes &&
-               cp "$TEST_ROOT/test.o" missing-delay.a
+               cp "$TEST_ROOT/test.o" missing-delay.a &&
                git add . &&
                git commit -m "test commit"
        ) &&
@@ -807,7 +807,7 @@ test_expect_success PERL 'invalid file in delayed checkout' '
                git init &&
                echo "*.a filter=bug" >.gitattributes &&
                cp "$TEST_ROOT/test.o" invalid-delay.a &&
-               cp "$TEST_ROOT/test.o" unfiltered
+               cp "$TEST_ROOT/test.o" unfiltered &&
                git add . &&
                git commit -m "test commit"
        ) &&
index 0c61268fd22ade66c6cbd91ad743986b33af0d5d..28ea93f509c0a9a932897e45316be9f188ad4259 100755 (executable)
@@ -156,7 +156,7 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi
                return 44;
        }
        EOT
-       (echo p; echo 1; echo; echo s; echo n; echo y; echo q) |
+       test_write_lines p 1 "" s n y q |
        git commit --interactive -m foo &&
        test_cache_tree
 '
index 826cd32e231607e3cd46f9c0314908583c01a093..c13578a635fbcf8b33c1cf912df69afa23e96885 100755 (executable)
@@ -210,10 +210,10 @@ test_expect_success 'D/F' '
        read_tree_u_must_succeed -m -u branch-point side-b side-a &&
        git ls-files -u >actual &&
        (
-               a=$(git rev-parse branch-point:subdir/file2)
-               b=$(git rev-parse side-a:subdir/file2/another)
-               echo "100644 $a 1       subdir/file2"
-               echo "100644 $a 2       subdir/file2"
+               a=$(git rev-parse branch-point:subdir/file2) &&
+               b=$(git rev-parse side-a:subdir/file2/another) &&
+               echo "100644 $a 1       subdir/file2" &&
+               echo "100644 $a 2       subdir/file2" &&
                echo "100644 $b 3       subdir/file2/another"
        ) >expect &&
        test_cmp expect actual
index 074568500a357d3bae69a953aa59155687483a10..83b09e1310676cc4ca092ff30c6da50aa6e635fd 100755 (executable)
@@ -33,7 +33,7 @@ test_expect_success 'reset should remove remnants from a failed merge' '
        git ls-files -s >expect &&
        sha1=$(git rev-parse :new) &&
        (
-               echo "100644 $sha1 1    old"
+               echo "100644 $sha1 1    old" &&
                echo "100644 $sha1 3    old"
        ) | git update-index --index-info &&
        >old &&
@@ -48,7 +48,7 @@ test_expect_success 'two-way reset should remove remnants too' '
        git ls-files -s >expect &&
        sha1=$(git rev-parse :new) &&
        (
-               echo "100644 $sha1 1    old"
+               echo "100644 $sha1 1    old" &&
                echo "100644 $sha1 3    old"
        ) | git update-index --index-info &&
        >old &&
@@ -63,7 +63,7 @@ test_expect_success 'Porcelain reset should remove remnants too' '
        git ls-files -s >expect &&
        sha1=$(git rev-parse :new) &&
        (
-               echo "100644 $sha1 1    old"
+               echo "100644 $sha1 1    old" &&
                echo "100644 $sha1 3    old"
        ) | git update-index --index-info &&
        >old &&
@@ -78,7 +78,7 @@ test_expect_success 'Porcelain checkout -f should remove remnants too' '
        git ls-files -s >expect &&
        sha1=$(git rev-parse :new) &&
        (
-               echo "100644 $sha1 1    old"
+               echo "100644 $sha1 1    old" &&
                echo "100644 $sha1 3    old"
        ) | git update-index --index-info &&
        >old &&
@@ -93,7 +93,7 @@ test_expect_success 'Porcelain checkout -f HEAD should remove remnants too' '
        git ls-files -s >expect &&
        sha1=$(git rev-parse :new) &&
        (
-               echo "100644 $sha1 1    old"
+               echo "100644 $sha1 1    old" &&
                echo "100644 $sha1 3    old"
        ) | git update-index --index-info &&
        >old &&
index 4c50ed955eb18da1a60dd9d635f3c32d081cfb76..cf96016844822cfec9998f107baebf15cb4c70db 100755 (executable)
@@ -23,7 +23,7 @@ test_expect_success setup '
 
 test_expect_success 'multi-read' '
        read_tree_must_succeed initial master side &&
-       (echo a; echo b/c) >expect &&
+       test_write_lines a b/c >expect &&
        git ls-files >actual &&
        test_cmp expect actual
 '
index df3183ea1ab36a46b914333e834093bd50847265..c2df75e4953d897acee5cf590b9c358e5a0b77c2 100755 (executable)
@@ -148,7 +148,7 @@ test_expect_success 'GIT_PREFIX for built-ins' '
        (
                cd dir &&
                echo "change" >two &&
-               GIT_EXTERNAL_DIFF=./diff git diff >../actual
+               GIT_EXTERNAL_DIFF=./diff git diff >../actual &&
                git checkout -- two
        ) &&
        test_cmp expect actual
index f9eb143f43420b0e2f2b864b4f83f78de7886a7b..1a9b21b2934b0a4fae7f74bb8c4da65d32218ca0 100755 (executable)
@@ -108,7 +108,7 @@ test_expect_success 'packsize limit' '
                test-tool genrandom "c" $(( 128 * 1024 )) >mid3 &&
                git add mid1 mid2 mid3 &&
 
-               count=0
+               count=0 &&
                for pi in .git/objects/pack/pack-*.idx
                do
                        test -f "$pi" && count=$(( $count + 1 ))
@@ -116,8 +116,8 @@ test_expect_success 'packsize limit' '
                test $count = 2 &&
 
                (
-                       git hash-object --stdin <mid1
-                       git hash-object --stdin <mid2
+                       git hash-object --stdin <mid1 &&
+                       git hash-object --stdin <mid2 &&
                        git hash-object --stdin <mid3
                ) |
                sort >expect &&
index 03c223708ebb428125dffb4a9ca273629936ff0c..24706ba4125c44f7c32539dc7bfcbb24f3371462 100755 (executable)
@@ -888,7 +888,7 @@ EOF
 
 test_expect_success !MINGW 'get --path copes with unset $HOME' '
        (
-               unset HOME;
+               sane_unset HOME &&
                test_must_fail git config --get --path path.home \
                        >result 2>msg &&
                git config --get --path path.normal >>result &&
index 553e26d9ceb83fc6529beb95ff71ffa0a899a166..8293131001ee0e816406df858dc47bc2fb9be3ce 100755 (executable)
@@ -339,8 +339,8 @@ test_expect_failure 'reflog with non-commit entries displays all entries' '
 '
 
 test_expect_success 'reflog expire operates on symref not referrent' '
-       git branch -l the_symref &&
-       git branch -l referrent &&
+       git branch --create-reflog the_symref &&
+       git branch --create-reflog referrent &&
        git update-ref referrent HEAD &&
        git symbolic-ref refs/heads/the_symref refs/heads/referrent &&
        test_when_finished "rm -f .git/refs/heads/referrent.lock" &&
index 596907758d5d47ae8026098a2ce4acd8c01df6aa..4d62ceef9c15cc981bada757bcf7cfa43f2dd9a5 100755 (executable)
@@ -159,9 +159,9 @@ test_expect_success 'git log -g -p shows diffs vs. parents' '
        git log -1 -p HEAD^ >log.one &&
        git log -1 -p HEAD >log.two &&
        (
-               cat log.one; echo
-               cat log.two; echo
-               cat log.one; echo
+               cat log.one && echo &&
+               cat log.two && echo &&
+               cat log.one && echo &&
                cat log.two
        ) >expect &&
        test_cmp expect actual
index 93c77eac45321cb24823431c4d89dc393049edbf..349f6e10af9ea64a0e874b863f784feab4d12386 100755 (executable)
@@ -123,9 +123,9 @@ test_expect_success 'checkout -b new my-side@{u} forks from the same' '
 
 test_expect_success 'merge my-side@{u} records the correct name' '
 (
-       cd clone || exit
-       git checkout master || exit
-       git branch -D new ;# can fail but is ok
+       cd clone &&
+       git checkout master &&
+       test_might_fail git branch -D new &&
        git branch -t new my-side@{u} &&
        git merge -s ours new@{u} &&
        git show -s --pretty=tformat:%s >actual &&
index 96fe3754c8c96fd65286d4eea44cb65b0edde343..e4d5b56014822ff760d3eab6d7c798f8d932ca45 100755 (executable)
@@ -34,8 +34,8 @@ test_expect_success 'blob and tree' '
                for i in 0 1 2 3 4 5 6 7 8 9
                do
                        echo $i
-               done
-               echo
+               done &&
+               echo &&
                echo b1rwzyc3
        ) >a0blgqsjc &&
 
@@ -222,7 +222,7 @@ test_expect_success 'more history' '
 
        test_might_fail git rm -f a0blgqsjc &&
        (
-               git cat-file blob $side:f5518nwu
+               git cat-file blob $side:f5518nwu &&
                echo j3l0i9s6
        ) >ab2gs879 &&
        git add ab2gs879 &&
index 1e81b33b2e3a23a135c9b9440787a6e0c0deaa22..39133bcbc85239183c3d73596f76f0486aeefcfe 100755 (executable)
@@ -435,7 +435,7 @@ test_expect_success 'writing split index with null sha1 does not write cache tre
        commit=$(git commit-tree $tree -p HEAD <msg) &&
        git update-ref HEAD "$commit" &&
        GIT_ALLOW_NULL_SHA1=1 git reset --hard &&
-       (test-tool dump-cache-tree >cache-tree.out || true) &&
+       test_might_fail test-tool dump-cache-tree >cache-tree.out &&
        test_line_count = 0 cache-tree.out
 '
 
index 9cd0ac4ba3f14dc85b35fa14e20ceb16f191f724..47aeb0b1674c01fd35e3aef759756348026a04bb 100755 (executable)
@@ -20,33 +20,33 @@ test_expect_success PERL 'setup' '
 
 test_expect_success PERL 'saying "n" does nothing' '
        set_and_save_state dir/foo work head &&
-       (echo n; echo n) | git checkout -p &&
+       test_write_lines n n | git checkout -p &&
        verify_saved_state bar &&
        verify_saved_state dir/foo
 '
 
 test_expect_success PERL 'git checkout -p' '
-       (echo n; echo y) | git checkout -p &&
+       test_write_lines n y | git checkout -p &&
        verify_saved_state bar &&
        verify_state dir/foo head head
 '
 
 test_expect_success PERL 'git checkout -p with staged changes' '
        set_state dir/foo work index &&
-       (echo n; echo y) | git checkout -p &&
+       test_write_lines n y | git checkout -p &&
        verify_saved_state bar &&
        verify_state dir/foo index index
 '
 
 test_expect_success PERL 'git checkout -p HEAD with NO staged changes: abort' '
        set_and_save_state dir/foo work head &&
-       (echo n; echo y; echo n) | git checkout -p HEAD &&
+       test_write_lines n y n | git checkout -p HEAD &&
        verify_saved_state bar &&
        verify_saved_state dir/foo
 '
 
 test_expect_success PERL 'git checkout -p HEAD with NO staged changes: apply' '
-       (echo n; echo y; echo y) | git checkout -p HEAD &&
+       test_write_lines n y y | git checkout -p HEAD &&
        verify_saved_state bar &&
        verify_state dir/foo head head
 '
@@ -54,14 +54,14 @@ test_expect_success PERL 'git checkout -p HEAD with NO staged changes: apply' '
 test_expect_success PERL 'git checkout -p HEAD with change already staged' '
        set_state dir/foo index index &&
        # the third n is to get out in case it mistakenly does not apply
-       (echo n; echo y; echo n) | git checkout -p HEAD &&
+       test_write_lines n y n | git checkout -p HEAD &&
        verify_saved_state bar &&
        verify_state dir/foo head head
 '
 
 test_expect_success PERL 'git checkout -p HEAD^' '
        # the third n is to get out in case it mistakenly does not apply
-       (echo n; echo y; echo n) | git checkout -p HEAD^ &&
+       test_write_lines n y n | git checkout -p HEAD^ &&
        verify_saved_state bar &&
        verify_state dir/foo parent parent
 '
@@ -69,7 +69,7 @@ test_expect_success PERL 'git checkout -p HEAD^' '
 test_expect_success PERL 'git checkout -p handles deletion' '
        set_state dir/foo work index &&
        rm dir/foo &&
-       (echo n; echo y) | git checkout -p &&
+       test_write_lines n y | git checkout -p &&
        verify_saved_state bar &&
        verify_state dir/foo index index
 '
@@ -81,21 +81,21 @@ test_expect_success PERL 'git checkout -p handles deletion' '
 
 test_expect_success PERL 'path limiting works: dir' '
        set_state dir/foo work head &&
-       (echo y; echo n) | git checkout -p dir &&
+       test_write_lines y n | git checkout -p dir &&
        verify_saved_state bar &&
        verify_state dir/foo head head
 '
 
 test_expect_success PERL 'path limiting works: -- dir' '
        set_state dir/foo work head &&
-       (echo y; echo n) | git checkout -p -- dir &&
+       test_write_lines y n | git checkout -p -- dir &&
        verify_saved_state bar &&
        verify_state dir/foo head head
 '
 
 test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
        # the third n is to get out in case it mistakenly does not apply
-       (echo y; echo n; echo n) | git checkout -p HEAD^ -- dir &&
+       test_write_lines y n n | git checkout -p HEAD^ -- dir &&
        verify_saved_state bar &&
        verify_state dir/foo parent parent
 '
@@ -103,7 +103,7 @@ test_expect_success PERL 'path limiting works: HEAD^ -- dir' '
 test_expect_success PERL 'path limiting works: foo inside dir' '
        set_state dir/foo work head &&
        # the third n is to get out in case it mistakenly does not apply
-       (echo y; echo n; echo n) | (cd dir && git checkout -p foo) &&
+       test_write_lines y n n | (cd dir && git checkout -p foo) &&
        verify_saved_state bar &&
        verify_state dir/foo head head
 '
index 3e5ac81bd29bf6aded0d0fa643dca448a281d481..26dc3f1fc0c25df1b359b5d56aece9f171344f5a 100755 (executable)
@@ -23,6 +23,12 @@ test_branch_upstream () {
        test_cmp expect.upstream actual.upstream
 }
 
+status_uno_is_clean () {
+       >status.expect &&
+       git status -uno --porcelain >status.actual &&
+       test_cmp status.expect status.actual
+}
+
 test_expect_success 'setup' '
        test_commit my_master &&
        git init repo_a &&
@@ -55,6 +61,7 @@ test_expect_success 'checkout of non-existing branch fails' '
        test_might_fail git branch -D xyzzy &&
 
        test_must_fail git checkout xyzzy &&
+       status_uno_is_clean &&
        test_must_fail git rev-parse --verify refs/heads/xyzzy &&
        test_branch master
 '
@@ -64,15 +71,47 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '
        test_might_fail git branch -D foo &&
 
        test_must_fail git checkout foo &&
+       status_uno_is_clean &&
        test_must_fail git rev-parse --verify refs/heads/foo &&
        test_branch master
 '
 
+test_expect_success 'checkout of branch from multiple remotes fails with advice' '
+       git checkout -B master &&
+       test_might_fail git branch -D foo &&
+       test_must_fail git checkout foo 2>stderr &&
+       test_branch master &&
+       status_uno_is_clean &&
+       test_i18ngrep "^hint: " stderr &&
+       test_must_fail git -c advice.checkoutAmbiguousRemoteBranchName=false \
+               checkout foo 2>stderr &&
+       test_branch master &&
+       status_uno_is_clean &&
+       test_i18ngrep ! "^hint: " stderr &&
+       # Make sure the likes of checkout -p do not print this hint
+       git checkout -p foo 2>stderr &&
+       test_i18ngrep ! "^hint: " stderr &&
+       status_uno_is_clean
+'
+
+test_expect_success 'checkout of branch from multiple remotes succeeds with checkout.defaultRemote #1' '
+       git checkout -B master &&
+       status_uno_is_clean &&
+       test_might_fail git branch -D foo &&
+
+       git -c checkout.defaultRemote=repo_a checkout foo &&
+       status_uno_is_clean &&
+       test_branch foo &&
+       test_cmp_rev remotes/repo_a/foo HEAD &&
+       test_branch_upstream foo repo_a foo
+'
+
 test_expect_success 'checkout of branch from a single remote succeeds #1' '
        git checkout -B master &&
        test_might_fail git branch -D bar &&
 
        git checkout bar &&
+       status_uno_is_clean &&
        test_branch bar &&
        test_cmp_rev remotes/repo_a/bar HEAD &&
        test_branch_upstream bar repo_a bar
@@ -83,6 +122,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '
        test_might_fail git branch -D baz &&
 
        git checkout baz &&
+       status_uno_is_clean &&
        test_branch baz &&
        test_cmp_rev remotes/other_b/baz HEAD &&
        test_branch_upstream baz repo_b baz
@@ -90,6 +130,7 @@ test_expect_success 'checkout of branch from a single remote succeeds #2' '
 
 test_expect_success '--no-guess suppresses branch auto-vivification' '
        git checkout -B master &&
+       status_uno_is_clean &&
        test_might_fail git branch -D bar &&
 
        test_must_fail git checkout --no-guess bar &&
@@ -99,6 +140,7 @@ test_expect_success '--no-guess suppresses branch auto-vivification' '
 
 test_expect_success 'setup more remotes with unconventional refspecs' '
        git checkout -B master &&
+       status_uno_is_clean &&
        git init repo_c &&
        (
                cd repo_c &&
@@ -128,27 +170,33 @@ test_expect_success 'setup more remotes with unconventional refspecs' '
 
 test_expect_success 'checkout of branch from multiple remotes fails #2' '
        git checkout -B master &&
+       status_uno_is_clean &&
        test_might_fail git branch -D bar &&
 
        test_must_fail git checkout bar &&
+       status_uno_is_clean &&
        test_must_fail git rev-parse --verify refs/heads/bar &&
        test_branch master
 '
 
 test_expect_success 'checkout of branch from multiple remotes fails #3' '
        git checkout -B master &&
+       status_uno_is_clean &&
        test_might_fail git branch -D baz &&
 
        test_must_fail git checkout baz &&
+       status_uno_is_clean &&
        test_must_fail git rev-parse --verify refs/heads/baz &&
        test_branch master
 '
 
 test_expect_success 'checkout of branch from a single remote succeeds #3' '
        git checkout -B master &&
+       status_uno_is_clean &&
        test_might_fail git branch -D spam &&
 
        git checkout spam &&
+       status_uno_is_clean &&
        test_branch spam &&
        test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
        test_branch_upstream spam repo_c spam
@@ -156,9 +204,11 @@ test_expect_success 'checkout of branch from a single remote succeeds #3' '
 
 test_expect_success 'checkout of branch from a single remote succeeds #4' '
        git checkout -B master &&
+       status_uno_is_clean &&
        test_might_fail git branch -D eggs &&
 
        git checkout eggs &&
+       status_uno_is_clean &&
        test_branch eggs &&
        test_cmp_rev refs/repo_d/eggs HEAD &&
        test_branch_upstream eggs repo_d eggs
@@ -166,32 +216,38 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' '
 
 test_expect_success 'checkout of branch with a file having the same name fails' '
        git checkout -B master &&
+       status_uno_is_clean &&
        test_might_fail git branch -D spam &&
 
        >spam &&
        test_must_fail git checkout spam &&
+       status_uno_is_clean &&
        test_must_fail git rev-parse --verify refs/heads/spam &&
        test_branch master
 '
 
 test_expect_success 'checkout of branch with a file in subdir having the same name fails' '
        git checkout -B master &&
+       status_uno_is_clean &&
        test_might_fail git branch -D spam &&
 
        >spam &&
        mkdir sub &&
        mv spam sub/spam &&
        test_must_fail git -C sub checkout spam &&
+       status_uno_is_clean &&
        test_must_fail git rev-parse --verify refs/heads/spam &&
        test_branch master
 '
 
 test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
        git checkout -B master &&
+       status_uno_is_clean &&
        test_might_fail git branch -D spam &&
 
        >spam &&
        git checkout spam -- &&
+       status_uno_is_clean &&
        test_branch spam &&
        test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
        test_branch_upstream spam repo_c spam
@@ -200,6 +256,7 @@ test_expect_success 'checkout <branch> -- succeeds, even if a file with the same
 test_expect_success 'loosely defined local base branch is reported correctly' '
 
        git checkout master &&
+       status_uno_is_clean &&
        git branch strict &&
        git branch loose &&
        git commit --allow-empty -m "a bit more" &&
@@ -210,7 +267,9 @@ test_expect_success 'loosely defined local base branch is reported correctly' '
        test_config branch.loose.merge master &&
 
        git checkout strict | sed -e "s/strict/BRANCHNAME/g" >expect &&
+       status_uno_is_clean &&
        git checkout loose | sed -e "s/loose/BRANCHNAME/g" >actual &&
+       status_uno_is_clean &&
 
        test_cmp expect actual
 '
index d2e49f763263333e8e269051686d3b8b2cae44ba..be6e093142cdbeb18fa0ff155f3adf1e7a566ca2 100755 (executable)
@@ -402,6 +402,27 @@ test_expect_success '"add" <path> <branch> dwims' '
        )
 '
 
+test_expect_success '"add" <path> <branch> dwims with checkout.defaultRemote' '
+       test_when_finished rm -rf repo_upstream repo_dwim foo &&
+       setup_remote_repo repo_upstream repo_dwim &&
+       git init repo_dwim &&
+       (
+               cd repo_dwim &&
+               git remote add repo_upstream2 ../repo_upstream &&
+               git fetch repo_upstream2 &&
+               test_must_fail git worktree add ../foo foo &&
+               git -c checkout.defaultRemote=repo_upstream worktree add ../foo foo &&
+               >status.expect &&
+               git status -uno --porcelain >status.actual &&
+               test_cmp status.expect status.actual
+       ) &&
+       (
+               cd foo &&
+               test_branch_upstream foo repo_upstream foo &&
+               test_cmp_rev refs/remotes/repo_upstream/foo refs/heads/foo
+       )
+'
+
 test_expect_success 'git worktree add does not match remote' '
        test_when_finished rm -rf repo_a repo_b foo &&
        setup_remote_repo repo_a repo_b &&
index 332694e7d38083fad18b3e53e4def268d54e9423..0114f052280d4022a3b47e427b2da3df5547f24c 100755 (executable)
@@ -32,7 +32,7 @@ test_expect_success basics '
                test_create_repo xyzzy &&
                cd xyzzy &&
                >file &&
-               git add file
+               git add file &&
                git commit -m "sub initial"
        ) &&
        git add xyzzy &&
index 6a5a3166b1823e751dfe19a28bf54c147dd798b3..17744e8c57b8adbe976e9abe5a8c8350429e4c19 100755 (executable)
@@ -6,12 +6,12 @@ test_description='git add --all'
 
 test_expect_success setup '
        (
-               echo .gitignore
+               echo .gitignore &&
                echo will-remove
        ) >expect &&
        (
-               echo actual
-               echo expect
+               echo actual &&
+               echo expect &&
                echo ignored
        ) >.gitignore &&
        git --literal-pathspecs add --all &&
@@ -25,10 +25,10 @@ test_expect_success setup '
 
 test_expect_success 'git add --all' '
        (
-               echo .gitignore
-               echo not-ignored
-               echo "M .gitignore"
-               echo "A not-ignored"
+               echo .gitignore &&
+               echo not-ignored &&
+               echo "M .gitignore" &&
+               echo "A not-ignored" &&
                echo "D will-remove"
        ) >expect &&
        >ignored &&
index c525656b2c04f8999482496688a0bd47ae8da9e2..afd475613430f6ff7490d9e37d37b76bab934f73 100755 (executable)
@@ -84,7 +84,7 @@ test_expect_success SYMLINKS 'ls-files --others with symlinked submodule' '
        ) &&
        (
                cd super &&
-               "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub
+               "$SHELL_PATH" "$TEST_DIRECTORY/../contrib/workdir/git-new-workdir" ../sub sub &&
                git ls-files --others --exclude-standard >../actual
        ) &&
        echo sub/ >expect &&
index 202ad658b894875b52f015b3dbeeedb7024904a7..e109c3fbfb5f57c35044299502d1ad406210c998 100755 (executable)
@@ -29,7 +29,7 @@ test_expect_success 'overly-long path does not replace another by mistake' '
        printf "$pat" "$blob_a" "$path_a" "$blob_z" "$path_z" |
        git update-index --add --index-info &&
        (
-               echo "$path_a"
+               echo "$path_a" &&
                echo "$path_z"
        ) >expect &&
        git ls-files >actual &&
index 08af596ba6c6b032eb1696c3beaba7c1776b4fd5..64f047332b51cefea37bd5b8b40e863be77dfc77 100755 (executable)
@@ -14,10 +14,10 @@ LAZY_THREAD_COST=2000
 
 test_expect_success 'no buffer overflow in lazy_init_name_hash' '
        (
-           test_seq $LAZY_THREAD_COST | sed "s/^/a_/"
-           echo b/b/b
-           test_seq $LAZY_THREAD_COST | sed "s/^/c_/"
-           test_seq 50 | sed "s/^/d_/" | tr "\n" "/"; echo d
+           test_seq $LAZY_THREAD_COST | sed "s/^/a_/" &&
+           echo b/b/b &&
+           test_seq $LAZY_THREAD_COST | sed "s/^/c_/" &&
+           test_seq 50 | sed "s/^/d_/" | tr "\n" "/" && echo d
        ) |
        sed "s/^/100644 $EMPTY_BLOB     /" |
        git update-index --index-info &&
index 3563e77b374c69a138bdfa3b274b75b6094c55a7..ff641b348a1bacc54fc7c582f4ae0881446a6827 100755 (executable)
@@ -36,15 +36,15 @@ test_expect_success 'setup 1' '
        test_tick &&
        git commit -m "master modifies a and d/e" &&
        c1=$(git rev-parse --verify HEAD) &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o1   a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o1   d/e"
-               echo "100644 $o1 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o1   a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o1   d/e" &&
+               echo "100644 $o1 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -54,15 +54,15 @@ test_expect_success 'setup 2' '
 
        rm -rf [abcd] &&
        git checkout side &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o0   a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o0 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o0   a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o0 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual &&
@@ -75,15 +75,15 @@ test_expect_success 'setup 2' '
        test_tick &&
        git commit -m "side modifies a" &&
        c2=$(git rev-parse --verify HEAD) &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o2   a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o2 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o2   a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o2 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -93,15 +93,15 @@ test_expect_success 'setup 3' '
 
        rm -rf [abcd] &&
        git checkout df-1 &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o0   a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o0 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o0   a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o0 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual &&
@@ -112,15 +112,15 @@ test_expect_success 'setup 3' '
        test_tick &&
        git commit -m "df-1 makes b/c" &&
        c3=$(git rev-parse --verify HEAD) &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o0   a"
-               echo "100644 blob $o3   b/c"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o0 0      a"
-               echo "100644 $o3 0      b/c"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o0   a" &&
+               echo "100644 blob $o3   b/c" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o0 0      a" &&
+               echo "100644 $o3 0      b/c" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -130,15 +130,15 @@ test_expect_success 'setup 4' '
 
        rm -rf [abcd] &&
        git checkout df-2 &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o0   a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o0 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o0   a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o0 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual &&
@@ -149,15 +149,15 @@ test_expect_success 'setup 4' '
        test_tick &&
        git commit -m "df-2 makes a/c" &&
        c4=$(git rev-parse --verify HEAD) &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o4   a/c"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o4 0      a/c"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o4   a/c" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o4 0      a/c" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -167,15 +167,15 @@ test_expect_success 'setup 5' '
 
        rm -rf [abcd] &&
        git checkout remove &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o0   a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o0 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o0   a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o0 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual &&
@@ -190,13 +190,13 @@ test_expect_success 'setup 5' '
        test_tick &&
        git commit -m "remove removes b and modifies a" &&
        c5=$(git rev-parse --verify HEAD) &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o5   a"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o5 0      a"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o5   a" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o5 0      a" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -207,15 +207,15 @@ test_expect_success 'setup 6' '
 
        rm -rf [abcd] &&
        git checkout df-3 &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o0   a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 $o0 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o0   a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 $o0 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o0 0      d/e"
        ) >expected &&
        test_cmp expected actual &&
@@ -226,15 +226,15 @@ test_expect_success 'setup 6' '
        test_tick &&
        git commit -m "df-3 makes d" &&
        c6=$(git rev-parse --verify HEAD) &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o0   a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o6   d"
-               echo "100644 $o0 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 blob $o0   a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o6   d" &&
+               echo "100644 $o0 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o6 0      d"
        ) >expected &&
        test_cmp expected actual
@@ -286,11 +286,11 @@ test_expect_success 'merge-recursive result' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o0 1      a"
-               echo "100644 $o2 2      a"
-               echo "100644 $o1 3      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 $o0 1      a" &&
+               echo "100644 $o2 2      a" &&
+               echo "100644 $o1 3      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -325,10 +325,10 @@ test_expect_success 'merge-recursive remove conflict' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o0 1      a"
-               echo "100644 $o1 2      a"
-               echo "100644 $o5 3      a"
-               echo "100644 $o0 0      c"
+               echo "100644 $o0 1      a" &&
+               echo "100644 $o1 2      a" &&
+               echo "100644 $o5 3      a" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -347,9 +347,9 @@ test_expect_success 'merge-recursive result' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      a"
-               echo "100644 $o3 0      b/c"
-               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      a" &&
+               echo "100644 $o3 0      b/c" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -369,11 +369,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o0 1      a"
-               echo "100644 $o1 2      a"
-               echo "100644 $o4 0      a/c"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 $o0 1      a" &&
+               echo "100644 $o1 2      a" &&
+               echo "100644 $o4 0      a/c" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -393,11 +393,11 @@ test_expect_success 'merge-recursive d/f conflict result the other way' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o0 1      a"
-               echo "100644 $o1 3      a"
-               echo "100644 $o4 0      a/c"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 $o0 1      a" &&
+               echo "100644 $o1 3      a" &&
+               echo "100644 $o4 0      a/c" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -417,11 +417,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
-               echo "100644 $o6 3      d"
-               echo "100644 $o0 1      d/e"
+               echo "100644 $o1 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
+               echo "100644 $o6 3      d" &&
+               echo "100644 $o0 1      d/e" &&
                echo "100644 $o1 2      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -441,11 +441,11 @@ test_expect_success 'merge-recursive d/f conflict result' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
-               echo "100644 $o6 2      d"
-               echo "100644 $o0 1      d/e"
+               echo "100644 $o1 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
+               echo "100644 $o6 2      d" &&
+               echo "100644 $o0 1      d/e" &&
                echo "100644 $o1 3      d/e"
        ) >expected &&
        test_cmp expected actual
@@ -465,13 +465,13 @@ test_expect_success 'reset and bind merge' '
        git read-tree --prefix=M/ master &&
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      M/a"
-               echo "100644 $o0 0      M/b"
-               echo "100644 $o0 0      M/c"
-               echo "100644 $o1 0      M/d/e"
-               echo "100644 $o1 0      a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      M/a" &&
+               echo "100644 $o0 0      M/b" &&
+               echo "100644 $o0 0      M/c" &&
+               echo "100644 $o1 0      M/d/e" &&
+               echo "100644 $o1 0      a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
        ) >expected &&
        test_cmp expected actual &&
@@ -479,17 +479,17 @@ test_expect_success 'reset and bind merge' '
        git read-tree --prefix=a1/ master &&
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      M/a"
-               echo "100644 $o0 0      M/b"
-               echo "100644 $o0 0      M/c"
-               echo "100644 $o1 0      M/d/e"
-               echo "100644 $o1 0      a"
-               echo "100644 $o1 0      a1/a"
-               echo "100644 $o0 0      a1/b"
-               echo "100644 $o0 0      a1/c"
-               echo "100644 $o1 0      a1/d/e"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
+               echo "100644 $o1 0      M/a" &&
+               echo "100644 $o0 0      M/b" &&
+               echo "100644 $o0 0      M/c" &&
+               echo "100644 $o1 0      M/d/e" &&
+               echo "100644 $o1 0      a" &&
+               echo "100644 $o1 0      a1/a" &&
+               echo "100644 $o0 0      a1/b" &&
+               echo "100644 $o0 0      a1/c" &&
+               echo "100644 $o1 0      a1/d/e" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
                echo "100644 $o1 0      d/e"
        ) >expected &&
        test_cmp expected actual &&
@@ -497,21 +497,21 @@ test_expect_success 'reset and bind merge' '
        git read-tree --prefix=z/ master &&
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      M/a"
-               echo "100644 $o0 0      M/b"
-               echo "100644 $o0 0      M/c"
-               echo "100644 $o1 0      M/d/e"
-               echo "100644 $o1 0      a"
-               echo "100644 $o1 0      a1/a"
-               echo "100644 $o0 0      a1/b"
-               echo "100644 $o0 0      a1/c"
-               echo "100644 $o1 0      a1/d/e"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
-               echo "100644 $o1 0      d/e"
-               echo "100644 $o1 0      z/a"
-               echo "100644 $o0 0      z/b"
-               echo "100644 $o0 0      z/c"
+               echo "100644 $o1 0      M/a" &&
+               echo "100644 $o0 0      M/b" &&
+               echo "100644 $o0 0      M/c" &&
+               echo "100644 $o1 0      M/d/e" &&
+               echo "100644 $o1 0      a" &&
+               echo "100644 $o1 0      a1/a" &&
+               echo "100644 $o0 0      a1/b" &&
+               echo "100644 $o0 0      a1/c" &&
+               echo "100644 $o1 0      a1/d/e" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
+               echo "100644 $o1 0      d/e" &&
+               echo "100644 $o1 0      z/a" &&
+               echo "100644 $o0 0      z/b" &&
+               echo "100644 $o0 0      z/c" &&
                echo "100644 $o1 0      z/d/e"
        ) >expected &&
        test_cmp expected actual
@@ -589,8 +589,8 @@ test_expect_success 'merge-recursive simple w/submodule result' '
 
        git ls-files -s >actual &&
        (
-               echo "100644 $o5 0      a"
-               echo "100644 $o0 0      c"
+               echo "100644 $o5 0      a" &&
+               echo "100644 $o0 0      c" &&
                echo "160000 $c1 0      d"
        ) >expected &&
        test_cmp expected actual
@@ -601,13 +601,13 @@ test_expect_success 'merge-recursive copy vs. rename' '
        git merge rename &&
        ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 blob $o0   e"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
-               echo "100644 $o0 0      d/e"
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 blob $o0   e" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
+               echo "100644 $o0 0      d/e" &&
                echo "100644 $o0 0      e"
        ) >expected &&
        test_cmp expected actual
@@ -617,17 +617,17 @@ test_expect_failure 'merge-recursive rename vs. rename/symlink' '
 
        git checkout -f rename &&
        git merge rename-ln &&
-       ( git ls-tree -r HEAD ; git ls-files -s ) >actual &&
+       ( git ls-tree -r HEAD && git ls-files -s ) >actual &&
        (
-               echo "120000 blob $oln  a"
-               echo "100644 blob $o0   b"
-               echo "100644 blob $o0   c"
-               echo "100644 blob $o0   d/e"
-               echo "100644 blob $o0   e"
-               echo "120000 $oln 0     a"
-               echo "100644 $o0 0      b"
-               echo "100644 $o0 0      c"
-               echo "100644 $o0 0      d/e"
+               echo "120000 blob $oln  a" &&
+               echo "100644 blob $o0   b" &&
+               echo "100644 blob $o0   c" &&
+               echo "100644 blob $o0   d/e" &&
+               echo "100644 blob $o0   e" &&
+               echo "120000 $oln 0     a" &&
+               echo "100644 $o0 0      b" &&
+               echo "100644 $o0 0      c" &&
+               echo "100644 $o0 0      d/e" &&
                echo "100644 $o0 0      e"
        ) >expected &&
        test_cmp expected actual
diff --git a/t/t3035-merge-sparse.sh b/t/t3035-merge-sparse.sh
new file mode 100755 (executable)
index 0000000..0c0b433
--- /dev/null
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+test_description='merge with sparse files'
+
+. ./test-lib.sh
+
+# test_file $filename $content
+test_file () {
+       echo "$2" > "$1" &&
+       git add "$1"
+}
+
+# test_commit_this $message_and_tag
+test_commit_this () {
+       git commit -m "$1" &&
+       git tag "$1"
+}
+
+test_expect_success 'setup' '
+       : >empty &&
+       test_file checked-out init &&
+       test_file modify_delete modify_delete_init &&
+       test_commit_this init &&
+       test_file modify_delete modify_delete_theirs &&
+       test_commit_this theirs &&
+       git reset --hard init &&
+       git rm modify_delete &&
+       test_commit_this ours &&
+       git config core.sparseCheckout true &&
+       echo "/checked-out" >.git/info/sparse-checkout &&
+       git reset --hard &&
+       ! git merge theirs
+'
+
+test_expect_success 'reset --hard works after the conflict' '
+       git reset --hard
+'
+
+test_expect_success 'is reset properly' '
+       git status --porcelain -- modify_delete >out &&
+       test_cmp empty out &&
+       test_path_is_missing modify_delete
+'
+
+test_expect_success 'setup: conflict back' '
+       ! git merge theirs
+'
+
+test_expect_success 'Merge abort works after the conflict' '
+       git merge --abort
+'
+
+test_expect_success 'is aborted properly' '
+       git status --porcelain -- modify_delete >out &&
+       test_cmp empty out &&
+       test_path_is_missing modify_delete
+'
+
+test_done
index 2f5f41a012b5f6e38f2b9d631a99648ac8937c2d..f1f09abdd9b254eed1efa6b0d06369f3c8606cf8 100755 (executable)
@@ -21,10 +21,10 @@ test_expect_success setup '
 
 test_expect_success clone '
        git clone "file://$(pwd)/.git" cloned &&
-       (git rev-parse HEAD; git ls-files -s) >expected &&
+       (git rev-parse HEAD && git ls-files -s) >expected &&
        (
                cd cloned &&
-               (git rev-parse HEAD; git ls-files -s) >../actual
+               (git rev-parse HEAD && git ls-files -s) >../actual
        ) &&
        test_cmp expected actual
 '
@@ -40,11 +40,11 @@ test_expect_success advance '
 '
 
 test_expect_success fetch '
-       (git rev-parse HEAD; git ls-files -s) >expected &&
+       (git rev-parse HEAD && git ls-files -s) >expected &&
        (
                cd cloned &&
                git pull &&
-               (git rev-parse HEAD; git ls-files -s) >../actual
+               (git rev-parse HEAD && git ls-files -s) >../actual
        ) &&
        test_cmp expected actual
 '
index e804377f1cbf4f2c82c1aae3d902014d634a2cdc..1e16c6b8ea610c4582e63950b8939205fc228b25 100755 (executable)
@@ -23,7 +23,7 @@ test_expect_success 'ls-tree outside prefix' '
        cat >expect <<-EOF &&
        100644 blob $EMPTY_BLOB ../a[a]/three
        EOF
-       ( cd aa && git ls-tree -r HEAD "../a[a]"; ) >actual &&
+       ( cd aa && git ls-tree -r HEAD "../a[a]" ) >actual &&
        test_cmp expect actual
 '
 
index 08467982f61fd81f0614711b52b9806f2497f3eb..dbca665da41908e7e3014564dc8ddb901030715b 100755 (executable)
@@ -49,9 +49,9 @@ test_expect_success 'git branch HEAD should fail' '
 cat >expect <<EOF
 $ZERO_OID $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000    branch: Created from master
 EOF
-test_expect_success 'git branch -l d/e/f should create a branch and a log' '
+test_expect_success 'git branch --create-reflog d/e/f should create a branch and a log' '
        GIT_COMMITTER_DATE="2005-05-26 23:30" \
-       git branch -l d/e/f &&
+       git -c core.logallrefupdates=false branch --create-reflog d/e/f &&
        test_path_is_file .git/refs/heads/d/e/f &&
        test_path_is_file .git/logs/refs/heads/d/e/f &&
        test_cmp expect .git/logs/refs/heads/d/e/f
@@ -82,7 +82,7 @@ test_expect_success 'git branch -m dumps usage' '
 
 test_expect_success 'git branch -m m broken_symref should work' '
        test_when_finished "git branch -D broken_symref" &&
-       git branch -l m &&
+       git branch --create-reflog m &&
        git symbolic-ref refs/heads/broken_symref refs/heads/i_am_broken &&
        git branch -m m broken_symref &&
        git reflog exists refs/heads/broken_symref &&
@@ -90,13 +90,13 @@ test_expect_success 'git branch -m m broken_symref should work' '
 '
 
 test_expect_success 'git branch -m m m/m should work' '
-       git branch -l m &&
+       git branch --create-reflog m &&
        git branch -m m m/m &&
        git reflog exists refs/heads/m/m
 '
 
 test_expect_success 'git branch -m n/n n should work' '
-       git branch -l n/n &&
+       git branch --create-reflog n/n &&
        git branch -m n/n n &&
        git reflog exists refs/heads/n
 '
@@ -378,9 +378,9 @@ mv .git/config-saved .git/config
 git config branch.s/s.dummy Hello
 
 test_expect_success 'git branch -m s/s s should work when s/t is deleted' '
-       git branch -l s/s &&
+       git branch --create-reflog s/s &&
        git reflog exists refs/heads/s/s &&
-       git branch -l s/t &&
+       git branch --create-reflog s/t &&
        git reflog exists refs/heads/s/t &&
        git branch -d s/t &&
        git branch -m s/s s &&
@@ -444,7 +444,7 @@ test_expect_success 'git branch --copy dumps usage' '
 '
 
 test_expect_success 'git branch -c d e should work' '
-       git branch -l d &&
+       git branch --create-reflog d &&
        git reflog exists refs/heads/d &&
        git config branch.d.dummy Hello &&
        git branch -c d e &&
@@ -459,7 +459,7 @@ test_expect_success 'git branch -c d e should work' '
 '
 
 test_expect_success 'git branch --copy is a synonym for -c' '
-       git branch -l copy &&
+       git branch --create-reflog copy &&
        git reflog exists refs/heads/copy &&
        git config branch.copy.dummy Hello &&
        git branch --copy copy copy-to &&
@@ -486,7 +486,7 @@ test_expect_success 'git branch -c ee ef should copy ee to create branch ef' '
 '
 
 test_expect_success 'git branch -c f/f g/g should work' '
-       git branch -l f/f &&
+       git branch --create-reflog f/f &&
        git reflog exists refs/heads/f/f &&
        git config branch.f/f.dummy Hello &&
        git branch -c f/f g/g &&
@@ -497,7 +497,7 @@ test_expect_success 'git branch -c f/f g/g should work' '
 '
 
 test_expect_success 'git branch -c m2 m2 should work' '
-       git branch -l m2 &&
+       git branch --create-reflog m2 &&
        git reflog exists refs/heads/m2 &&
        git config branch.m2.dummy Hello &&
        git branch -c m2 m2 &&
@@ -506,18 +506,18 @@ test_expect_success 'git branch -c m2 m2 should work' '
 '
 
 test_expect_success 'git branch -c zz zz/zz should fail' '
-       git branch -l zz &&
+       git branch --create-reflog zz &&
        git reflog exists refs/heads/zz &&
        test_must_fail git branch -c zz zz/zz
 '
 
 test_expect_success 'git branch -c b/b b should fail' '
-       git branch -l b/b &&
+       git branch --create-reflog b/b &&
        test_must_fail git branch -c b/b b
 '
 
 test_expect_success 'git branch -C o/q o/p should work when o/p exists' '
-       git branch -l o/q &&
+       git branch --create-reflog o/q &&
        git reflog exists refs/heads/o/q &&
        git reflog exists refs/heads/o/p &&
        git branch -C o/q o/p
@@ -570,10 +570,10 @@ test_expect_success 'git branch -C master5 master5 should work when master is ch
 '
 
 test_expect_success 'git branch -C ab cd should overwrite existing config for cd' '
-       git branch -l cd &&
+       git branch --create-reflog cd &&
        git reflog exists refs/heads/cd &&
        git config branch.cd.dummy CD &&
-       git branch -l ab &&
+       git branch --create-reflog ab &&
        git reflog exists refs/heads/ab &&
        git config branch.ab.dummy AB &&
        git branch -C ab cd &&
@@ -685,7 +685,7 @@ test_expect_success 'renaming a symref is not allowed' '
 '
 
 test_expect_success SYMLINKS 'git branch -m u v should fail when the reflog for u is a symlink' '
-       git branch -l u &&
+       git branch --create-reflog u &&
        mv .git/logs/refs/heads/u real-u &&
        ln -s real-u .git/logs/refs/heads/u &&
        test_must_fail git branch -m u v
index afa27ffe2d869a5e7fdf8318d5b9957208a96c52..724b4b9bc037a229ec4fae9d6e49e0cf47188070 100755 (executable)
@@ -231,9 +231,9 @@ test_expect_success 'timeout if packed-refs.lock exists' '
 test_expect_success 'retry acquiring packed-refs.lock' '
        LOCK=.git/packed-refs.lock &&
        >"$LOCK" &&
-       test_when_finished "wait; rm -f $LOCK" &&
+       test_when_finished "wait && rm -f $LOCK" &&
        {
-               ( sleep 1 ; rm -f $LOCK ) &
+               ( sleep 1 && rm -f $LOCK ) &
        } &&
        git -c core.packedrefstimeout=3000 pack-refs --all --prune
 '
index 2d200fdf36c62c12cbbf2a964d3489edef401257..ac62dc0e8fdc4602fb006919f0c0b62a015964bd 100755 (executable)
@@ -914,7 +914,7 @@ test_expect_success 'git notes copy --stdin' '
                ${indent}
                ${indent}yet another note
        EOF
-       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
        echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
        git notes copy --stdin &&
        git log -2 >actual &&
@@ -939,7 +939,7 @@ test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
        EOF
        test_commit 14th &&
        test_commit 15th &&
-       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
        echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
        git notes copy --for-rewrite=foo &&
        git log -2 >actual &&
@@ -972,7 +972,7 @@ test_expect_success 'git notes copy --for-rewrite (enabled)' '
        EOF
        test_config notes.rewriteMode overwrite &&
        test_config notes.rewriteRef "refs/notes/*" &&
-       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^); \
+       (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
        echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
        git notes copy --for-rewrite=foo &&
        git log -2 >actual &&
@@ -1059,7 +1059,7 @@ test_expect_success 'git notes copy --for-rewrite (append two to one)' '
        git notes add -f -m"append 2" HEAD^^ &&
        test_config notes.rewriteMode concatenate &&
        test_config notes.rewriteRef "refs/notes/*" &&
-       (echo $(git rev-parse HEAD^) $(git rev-parse HEAD);
+       (echo $(git rev-parse HEAD^) $(git rev-parse HEAD) &&
        echo $(git rev-parse HEAD^^) $(git rev-parse HEAD)) |
        git notes copy --for-rewrite=foo &&
        git log -1 >actual &&
index 72d9564747adf2d37ea2e61a6d2e479096fe6508..3996ee013563ce1f91b2a693318b1e07aba96568 100755 (executable)
@@ -200,10 +200,10 @@ test_expect_success 'rebase -q is quiet' '
 
 test_expect_success 'Rebase a commit that sprinkles CRs in' '
        (
-               echo "One"
-               echo "TwoQ"
-               echo "Three"
-               echo "FQur"
+               echo "One" &&
+               echo "TwoQ" &&
+               echo "Three" &&
+               echo "FQur" &&
                echo "Five"
        ) | q_to_cr >CR &&
        git add CR &&
diff --git a/t/t3401-rebase-and-am-rename.sh b/t/t3401-rebase-and-am-rename.sh
new file mode 100755 (executable)
index 0000000..8f83295
--- /dev/null
@@ -0,0 +1,105 @@
+#!/bin/sh
+
+test_description='git rebase + directory rename tests'
+
+. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_expect_success 'setup testcase' '
+       test_create_repo dir-rename &&
+       (
+               cd dir-rename &&
+
+               mkdir x &&
+               test_seq  1 10 >x/a &&
+               test_seq 11 20 >x/b &&
+               test_seq 21 30 >x/c &&
+               test_write_lines a b c d e f g h i >l &&
+               git add x l &&
+               git commit -m "Initial" &&
+
+               git branch O &&
+               git branch A &&
+               git branch B &&
+
+               git checkout A &&
+               git mv x y &&
+               git mv l letters &&
+               git commit -m "Rename x to y, l to letters" &&
+
+               git checkout B &&
+               echo j >>l &&
+               test_seq 31 40 >x/d &&
+               git add l x/d &&
+               git commit -m "Modify l, add x/d"
+       )
+'
+
+test_expect_success 'rebase --interactive: directory rename detected' '
+       (
+               cd dir-rename &&
+
+               git checkout B^0 &&
+
+               set_fake_editor &&
+               FAKE_LINES="1" git rebase --interactive A &&
+
+               git ls-files -s >out &&
+               test_line_count = 5 out &&
+
+               test_path_is_file y/d &&
+               test_path_is_missing x/d
+       )
+'
+
+test_expect_failure 'rebase (am): directory rename detected' '
+       (
+               cd dir-rename &&
+
+               git checkout B^0 &&
+
+               git rebase A &&
+
+               git ls-files -s >out &&
+               test_line_count = 5 out &&
+
+               test_path_is_file y/d &&
+               test_path_is_missing x/d
+       )
+'
+
+test_expect_success 'rebase --merge: directory rename detected' '
+       (
+               cd dir-rename &&
+
+               git checkout B^0 &&
+
+               git rebase --merge A &&
+
+               git ls-files -s >out &&
+               test_line_count = 5 out &&
+
+               test_path_is_file y/d &&
+               test_path_is_missing x/d
+       )
+'
+
+test_expect_failure 'am: directory rename detected' '
+       (
+               cd dir-rename &&
+
+               git checkout A^0 &&
+
+               git format-patch -1 B &&
+
+               git am --3way 0001*.patch &&
+
+               git ls-files -s >out &&
+               test_line_count = 5 out &&
+
+               test_path_is_file y/d &&
+               test_path_is_missing x/d
+       )
+'
+
+test_done
index 488945e0071b1ba4ae988848b5cb42df72737d2b..a1ec501a872b9ae4087c93a5736fcd297bda7fc8 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success setup '
        git commit -a -m"master updates a bit more." &&
 
        git checkout side &&
-       (echo "0 $T" ; cat original) >renamed &&
+       (echo "0 $T" && cat original) >renamed &&
        git add renamed &&
        git update-index --force-remove original &&
        git commit -a -m"side renames and edits." &&
@@ -143,7 +143,7 @@ test_expect_success 'rebase -s funny -Xopt' '
        git checkout -b test-funny master^ &&
        test_commit funny &&
        (
-               PATH=./test-bin:$PATH
+               PATH=./test-bin:$PATH &&
                git rebase -s funny -Xopt master
        ) &&
        test -f funny.was.run
index 352a52e59d1a25d6fe50c5001ba3c84e2fc4a30c..46657ceadd257b4603e5783c02d7879af4e8f3d6 100755 (executable)
@@ -264,11 +264,18 @@ test_expect_success 'retain authorship' '
 '
 
 test_expect_success 'retain authorship w/ conflicts' '
+       oGIT_AUTHOR_NAME=$GIT_AUTHOR_NAME &&
+       test_when_finished "GIT_AUTHOR_NAME=\$oGIT_AUTHOR_NAME" &&
+
        git reset --hard twerp &&
        test_commit a conflict a conflict-a &&
        git reset --hard twerp &&
-       GIT_AUTHOR_NAME=AttributeMe \
+
+       GIT_AUTHOR_NAME=AttributeMe &&
+       export GIT_AUTHOR_NAME &&
        test_commit b conflict b conflict-b &&
+       GIT_AUTHOR_NAME=$oGIT_AUTHOR_NAME &&
+
        set_fake_editor &&
        test_must_fail git rebase -i conflict-a &&
        echo resolved >conflict &&
@@ -509,7 +516,7 @@ test_expect_success 'interrupted squash works as expected' '
        one=$(git rev-parse HEAD~3) &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="1 squash 3 2" git rebase -i HEAD~3 &&
-       (echo one; echo two; echo four) > conflict &&
+       test_write_lines one two four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
        echo resolved > conflict &&
@@ -523,10 +530,10 @@ test_expect_success 'interrupted squash works as expected (case 2)' '
        one=$(git rev-parse HEAD~3) &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="3 squash 1 2" git rebase -i HEAD~3 &&
-       (echo one; echo four) > conflict &&
+       test_write_lines one four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
-       (echo one; echo two; echo four) > conflict &&
+       test_write_lines one two four > conflict &&
        git add conflict &&
        test_must_fail git rebase --continue &&
        echo resolved > conflict &&
@@ -553,15 +560,16 @@ test_expect_success '--continue tries to commit, even for "edit"' '
 '
 
 test_expect_success 'aborted --continue does not squash commits after "edit"' '
+       test_when_finished "git rebase --abort" &&
        old=$(git rev-parse HEAD) &&
        test_tick &&
        set_fake_editor &&
        FAKE_LINES="edit 1" git rebase -i HEAD^ &&
        echo "edited again" > file7 &&
        git add file7 &&
-       test_must_fail env FAKE_COMMIT_MESSAGE=" " git rebase --continue &&
-       test $old = $(git rev-parse HEAD) &&
-       git rebase --abort
+       echo all the things >>conflict &&
+       test_must_fail git rebase --continue &&
+       test $old = $(git rev-parse HEAD)
 '
 
 test_expect_success 'auto-amend only edited commits after "edit"' '
@@ -981,7 +989,35 @@ test_expect_success 'rebase -i --root reword root commit' '
        test -z "$(git show -s --format=%p HEAD^)"
 '
 
+test_expect_success 'rebase -i --root when root has untracked file confilct' '
+       test_when_finished "reset_rebase" &&
+       git checkout -b failing-root-pick A &&
+       echo x >file2 &&
+       git rm file1 &&
+       git commit -m "remove file 1 add file 2" &&
+       echo z >file1 &&
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="1 2" git rebase -i --root &&
+       rm file1 &&
+       git rebase --continue &&
+       test "$(git log -1 --format=%B)" = "remove file 1 add file 2" &&
+       test "$(git rev-list --count HEAD)" = 2
+'
+
+test_expect_success 'rebase -i --root reword root when root has untracked file conflict' '
+       test_when_finished "reset_rebase" &&
+       echo z>file1 &&
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="reword 1 2" \
+               FAKE_COMMIT_MESSAGE="Modified A" git rebase -i --root &&
+       rm file1 &&
+       FAKE_COMMIT_MESSAGE="Reworded A" git rebase --continue &&
+       test "$(git log -1 --format=%B HEAD^)" = "Reworded A" &&
+       test "$(git rev-list --count HEAD)" = 2
+'
+
 test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '
+       git checkout reword-root-branch &&
        git reset --hard &&
        git checkout conflict-branch &&
        set_fake_editor &&
index cb7c6de84abf88bf90ac9716bb24ac91b8f64bf9..da94dddc86d78830664d3a4b910ab157c2db2f0f 100755 (executable)
@@ -77,19 +77,14 @@ test_expect_success 'rebase commit with diff in message' '
 '
 
 test_expect_success 'rebase -m commit with empty message' '
-       test_must_fail git rebase -m master empty-message-merge &&
-       git rebase --abort &&
-       git rebase -m --allow-empty-message master empty-message-merge
+       git rebase -m master empty-message-merge
 '
 
 test_expect_success 'rebase -i commit with empty message' '
        git checkout diff-in-message &&
        set_fake_editor &&
-       test_must_fail env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
-               git rebase -i HEAD^ &&
-       git rebase --abort &&
-       FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
-               git rebase -i --allow-empty-message HEAD^
+       env FAKE_COMMIT_MESSAGE=" " FAKE_LINES="reword 1" \
+               git rebase -i HEAD^
 '
 
 test_done
index 03bf1b8a3b3df2e44ed0f70dc176c25af9ac244a..65ed7db1592d97ae7120e028bc6023446e973a4a 100755 (executable)
@@ -60,7 +60,7 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
        EOF
        chmod +x test-bin/git-merge-funny &&
        (
-               PATH=./test-bin:$PATH
+               PATH=./test-bin:$PATH &&
                test_must_fail git rebase -s funny -Xopt master topic
        ) &&
        test -f funny.was.run &&
@@ -68,7 +68,39 @@ test_expect_success 'rebase --continue remembers merge strategy and options' '
        echo "Resolved" >F2 &&
        git add F2 &&
        (
-               PATH=./test-bin:$PATH
+               PATH=./test-bin:$PATH &&
+               git rebase --continue
+       ) &&
+       test -f funny.was.run
+'
+
+test_expect_success 'rebase -i --continue handles merge strategy and options' '
+       rm -fr .git/rebase-* &&
+       git reset --hard commit-new-file-F2-on-topic-branch &&
+       test_commit "commit-new-file-F3-on-topic-branch-for-dash-i" F3 32 &&
+       test_when_finished "rm -fr test-bin funny.was.run funny.args" &&
+       mkdir test-bin &&
+       cat >test-bin/git-merge-funny <<-EOF &&
+       #!$SHELL_PATH
+       echo "\$@" >>funny.args
+       case "\$1" in --opt) ;; *) exit 2 ;; esac
+       case "\$2" in --foo) ;; *) exit 2 ;; esac
+       case "\$4" in --) ;; *) exit 2 ;; esac
+       shift 2 &&
+       >funny.was.run &&
+       exec git merge-recursive "\$@"
+       EOF
+       chmod +x test-bin/git-merge-funny &&
+       (
+               PATH=./test-bin:$PATH &&
+               test_must_fail git rebase -i -s funny -Xopt -Xfoo master topic
+       ) &&
+       test -f funny.was.run &&
+       rm funny.was.run &&
+       echo "Resolved" >F2 &&
+       git add F2 &&
+       (
+               PATH=./test-bin:$PATH &&
                git rebase --continue
        ) &&
        test -f funny.was.run
diff --git a/t/t3422-rebase-incompatible-options.sh b/t/t3422-rebase-incompatible-options.sh
new file mode 100755 (executable)
index 0000000..bb78a6e
--- /dev/null
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+test_description='test if rebase detects and aborts on incompatible options'
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_seq 2 9 >foo &&
+       git add foo &&
+       git commit -m orig &&
+
+       git branch A &&
+       git branch B &&
+
+       git checkout A &&
+       test_seq 1 9 >foo &&
+       git add foo &&
+       git commit -m A &&
+
+       git checkout B &&
+       echo "q qfoo();" | q_to_tab >>foo &&
+       git add foo &&
+       git commit -m B
+'
+
+#
+# Rebase has lots of useful options like --whitepsace=fix, which are
+# actually all built in terms of flags to git-am.  Since neither
+# --merge nor --interactive (nor any options that imply those two) use
+# git-am, using them together will result in flags like --whitespace=fix
+# being ignored.  Make sure rebase warns the user and aborts instead.
+#
+
+test_rebase_am_only () {
+       opt=$1
+       shift
+       test_expect_success "$opt incompatible with --merge" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --merge A
+       "
+
+       test_expect_success "$opt incompatible with --strategy=ours" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --strategy=ours A
+       "
+
+       test_expect_success "$opt incompatible with --strategy-option=ours" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --strategy-option=ours A
+       "
+
+       test_expect_success "$opt incompatible with --interactive" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --interactive A
+       "
+
+       test_expect_success "$opt incompatible with --exec" "
+               git checkout B^0 &&
+               test_must_fail git rebase $opt --exec 'true' A
+       "
+
+}
+
+test_rebase_am_only --whitespace=fix
+test_rebase_am_only --ignore-whitespace
+test_rebase_am_only --committer-date-is-author-date
+test_rebase_am_only -C4
+
+test_expect_success '--preserve-merges incompatible with --signoff' '
+       git checkout B^0 &&
+       test_must_fail git rebase --preserve-merges --signoff A
+'
+
+test_expect_success '--preserve-merges incompatible with --rebase-merges' '
+       git checkout B^0 &&
+       test_must_fail git rebase --preserve-merges --rebase-merges A
+'
+
+test_expect_success '--rebase-merges incompatible with --strategy' '
+       git checkout B^0 &&
+       test_must_fail git rebase --rebase-merges -s resolve A
+'
+
+test_expect_success '--rebase-merges incompatible with --strategy-option' '
+       git checkout B^0 &&
+       test_must_fail git rebase --rebase-merges -Xignore-space-change A
+'
+
+test_done
diff --git a/t/t3423-rebase-reword.sh b/t/t3423-rebase-reword.sh
new file mode 100755 (executable)
index 0000000..6963750
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='git rebase interactive with rewording'
+
+. ./test-lib.sh
+
+. "$TEST_DIRECTORY"/lib-rebase.sh
+
+test_expect_success 'setup' '
+       test_commit master file-1 test &&
+
+       git checkout -b stuff &&
+
+       test_commit feature_a file-2 aaa &&
+       test_commit feature_b file-2 ddd
+'
+
+test_expect_success 'reword without issues functions as intended' '
+       test_when_finished "reset_rebase" &&
+
+       git checkout stuff^0 &&
+
+       set_fake_editor &&
+       FAKE_LINES="pick 1 reword 2" FAKE_COMMIT_MESSAGE="feature_b_reworded" \
+               git rebase -i -v master &&
+
+       test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
+       test $(git rev-list --count HEAD) = 3
+'
+
+test_expect_success 'reword after a conflict preserves commit' '
+       test_when_finished "reset_rebase" &&
+
+       git checkout stuff^0 &&
+
+       set_fake_editor &&
+       test_must_fail env FAKE_LINES="reword 2" \
+               git rebase -i -v master &&
+
+       git checkout --theirs file-2 &&
+       git add file-2 &&
+       FAKE_COMMIT_MESSAGE="feature_b_reworded" git rebase --continue &&
+
+       test "$(git log -1 --format=%B)" = "feature_b_reworded" &&
+       test $(git rev-list --count HEAD) = 2
+'
+
+test_done
index b42cd66d3a574138148a311b34472506b433c286..3505b6aa14e630a98062558ed30270c98e5a920f 100755 (executable)
@@ -480,11 +480,16 @@ test_expect_success 'malformed instruction sheet 2' '
        test_expect_code 128 git cherry-pick --continue
 '
 
-test_expect_success 'empty commit set' '
+test_expect_success 'empty commit set (no commits to walk)' '
        pristine_detach initial &&
        test_expect_code 128 git cherry-pick base..base
 '
 
+test_expect_success 'empty commit set (culled during walk)' '
+       pristine_detach initial &&
+       test_expect_code 128 git cherry-pick -2 --author=no.such.author base
+'
+
 test_expect_success 'malformed instruction sheet 3' '
        pristine_detach initial &&
        test_expect_code 1 git cherry-pick base..anotherpick &&
index 07af05d7aee65c51245431292a21ec38eef19f53..618750167aed8d99a57b2642713d12406cd2e5f6 100755 (executable)
@@ -156,9 +156,9 @@ test_expect_success 'git add with filemode=0, symlinks=0, and unmerged entries'
 test_expect_success 'git add with filemode=0, symlinks=0 prefers stage 2 over stage 1' '
        git rm --cached -f file symlink &&
        (
-               echo "100644 $(git hash-object -w stage1) 1     file"
-               echo "100755 $(git hash-object -w stage2) 2     file"
-               echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink"
+               echo "100644 $(git hash-object -w stage1) 1     file" &&
+               echo "100755 $(git hash-object -w stage2) 2     file" &&
+               echo "100644 $(printf 1 | git hash-object -w -t blob --stdin) 1 symlink" &&
                echo "120000 $(printf 2 | git hash-object -w -t blob --stdin) 2 symlink"
        ) | git update-index --index-info &&
        git config core.filemode 0 &&
@@ -265,7 +265,7 @@ test_expect_success 'git add to resolve conflicts on otherwise ignored path' '
        git reset --hard &&
        H=$(git rev-parse :1/2/a) &&
        (
-               echo "100644 $H 1       track-this"
+               echo "100644 $H 1       track-this" &&
                echo "100644 $H 3       track-this"
        ) | git update-index --index-info &&
        echo track-this >>.gitignore &&
index 3e9139dca88e83165fc21e6dce06f3243bd49cd7..609fbfdc317137e420fab48e394f0095b0a1df69 100755 (executable)
@@ -46,13 +46,13 @@ test_expect_success 'setup expected' '
 '
 
 test_expect_success 'diff works (initial)' '
-       (echo d; echo 1) | git add -i >output &&
+       test_write_lines d 1 | git add -i >output &&
        sed -ne "/new file/,/content/p" <output >diff &&
        diff_cmp expected diff
 '
 test_expect_success 'revert works (initial)' '
        git add file &&
-       (echo r; echo 1) | git add -i &&
+       test_write_lines r 1 | git add -i &&
        git ls-files >output &&
        ! grep . output
 '
@@ -83,13 +83,13 @@ test_expect_success 'setup expected' '
 '
 
 test_expect_success 'diff works (commit)' '
-       (echo d; echo 1) | git add -i >output &&
+       test_write_lines d 1 | git add -i >output &&
        sed -ne "/^index/,/content/p" <output >diff &&
        diff_cmp expected diff
 '
 test_expect_success 'revert works (commit)' '
        git add file &&
-       (echo r; echo 1) | git add -i &&
+       test_write_lines r 1 | git add -i &&
        git add -i </dev/null >output &&
        grep "unchanged *+3/-0 file" output
 '
@@ -102,7 +102,7 @@ test_expect_success 'setup expected' '
 
 test_expect_success 'dummy edit works' '
        test_set_editor : &&
-       (echo e; echo a) | git add -p &&
+       test_write_lines e a | git add -p &&
        git diff > diff &&
        diff_cmp expected diff
 '
@@ -127,7 +127,7 @@ test_expect_success 'setup fake editor' '
 
 test_expect_success 'bad edit rejected' '
        git reset &&
-       (echo e; echo n; echo d) | git add -p >output &&
+       test_write_lines e n d | git add -p >output &&
        grep "hunk does not apply" output
 '
 
@@ -140,7 +140,7 @@ test_expect_success 'setup patch' '
 
 test_expect_success 'garbage edit rejected' '
        git reset &&
-       (echo e; echo n; echo d) | git add -p >output &&
+       test_write_lines e n d | git add -p >output &&
        grep "hunk does not apply" output
 '
 
@@ -170,7 +170,7 @@ test_expect_success 'setup expected' '
 '
 
 test_expect_success 'real edit works' '
-       (echo e; echo n; echo d) | git add -p &&
+       test_write_lines e n d | git add -p &&
        git diff >output &&
        diff_cmp expected output
 '
index 83744f8c930637560d7d97123a2ffbd38030637e..9546b6f8a4e2fdf0c25f3b463de45a0feae4695c 100755 (executable)
@@ -29,14 +29,14 @@ test_expect_success 'setup' '
 test_expect_success 'saying "n" does nothing' '
        set_state HEAD HEADfile_work HEADfile_index &&
        set_state dir/foo work index &&
-       (echo n; echo n; echo n) | test_must_fail git stash save -p &&
+       test_write_lines n n n | test_must_fail git stash save -p &&
        verify_state HEAD HEADfile_work HEADfile_index &&
        verify_saved_state bar &&
        verify_state dir/foo work index
 '
 
 test_expect_success 'git stash -p' '
-       (echo y; echo n; echo y) | git stash save -p &&
+       test_write_lines y n y | git stash save -p &&
        verify_state HEAD committed HEADfile_index &&
        verify_saved_state bar &&
        verify_state dir/foo head index &&
@@ -51,7 +51,7 @@ test_expect_success 'git stash -p --no-keep-index' '
        set_state HEAD HEADfile_work HEADfile_index &&
        set_state bar bar_work bar_index &&
        set_state dir/foo work index &&
-       (echo y; echo n; echo y) | git stash save -p --no-keep-index &&
+       test_write_lines y n y | git stash save -p --no-keep-index &&
        verify_state HEAD committed committed &&
        verify_state bar bar_work dummy &&
        verify_state dir/foo head head &&
@@ -66,7 +66,7 @@ test_expect_success 'git stash --no-keep-index -p' '
        set_state HEAD HEADfile_work HEADfile_index &&
        set_state bar bar_work bar_index &&
        set_state dir/foo work index &&
-       (echo y; echo n; echo y) | git stash save --no-keep-index -p &&
+       test_write_lines y n y | git stash save --no-keep-index -p &&
        verify_state HEAD committed committed &&
        verify_state dir/foo head head &&
        verify_state bar bar_work dummy &&
index bf4030371a9fc8f5abe7e88d84c98a097b9821dd..c16486a9d41a125610e6280a79e958ebbb792653 100755 (executable)
@@ -180,7 +180,7 @@ test_expect_success 'setup for many rename source candidates' '
        git add "path??" &&
        test_tick &&
        git commit -m "hundred" &&
-       (cat path1; echo new) >new-path &&
+       (cat path1 && echo new) >new-path &&
        echo old >>path1 &&
        git add new-path path1 &&
        git diff -l 4 -C -C --cached --name-status >actual 2>actual.err &&
index 35b35a81c8ce38b3fe29f33412d2e7c9ff1bf68d..b7f25071cfb24408f43634d016ac785bba282614 100755 (executable)
@@ -111,10 +111,10 @@ test_expect_success 'diff-tree -r with wildcard' '
 test_expect_success 'setup submodules' '
        test_tick &&
        git init submod &&
-       ( cd submod && test_commit first; ) &&
+       ( cd submod && test_commit first ) &&
        git add submod &&
        git commit -m first &&
-       ( cd submod && test_commit second; ) &&
+       ( cd submod && test_commit second ) &&
        git add submod &&
        git commit -m second
 '
index 0a8af76aabf5ad79193341d1ffd4be4c9d4cb7a6..6579c81216a9b2e7dc6f2457cc46003dd905aa30 100755 (executable)
@@ -102,10 +102,8 @@ test_expect_success 'apply binary patch' '
 
 test_expect_success 'diff --no-index with binary creation' '
        echo Q | q_to_nul >binary &&
-       (: hide error code from diff, which just indicates differences
-        git diff --binary --no-index /dev/null binary >current ||
-        true
-       ) &&
+       # hide error code from diff, which just indicates differences
+       test_might_fail git diff --binary --no-index /dev/null binary >current &&
        rm binary &&
        git apply --binary <current &&
        echo Q >expected &&
index 028d5507a6b684208ba82292379ec74bc6628574..53880da7bbe85521112ced3ff94ead05587c7e81 100755 (executable)
@@ -1554,13 +1554,15 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
 
 test_expect_success 'format-patch --base' '
        git checkout side &&
-       git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual &&
+       git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&
+       git format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&
        echo >expected &&
        echo "base-commit: $(git rev-parse HEAD~3)" >>expected &&
        echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected &&
        echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected &&
        signature >> expected &&
-       test_cmp expected actual
+       test_cmp expected actual1 &&
+       test_cmp expected actual2
 '
 
 test_expect_success 'format-patch --base errors out when base commit is in revision list' '
index 17df491a3abe84fca63bb899d1162832d13e1847..41facf7abf9bff5cc39419b41998836018257f79 100755 (executable)
@@ -1223,7 +1223,7 @@ test_expect_success 'plain moved code, inside file' '
        test_cmp expected actual
 '
 
-test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
+test_expect_success 'detect blocks of moved code' '
        git reset --hard &&
        cat <<-\EOF >lines.txt &&
                long line 1
@@ -1271,9 +1271,52 @@ test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
        test_config color.diff.newMovedDimmed "normal cyan" &&
        test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
        test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
-       git diff HEAD --no-renames --color-moved=dimmed_zebra --color |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --no-renames --color-moved=blocks --color >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
+       cat <<-\EOF >expected &&
+       <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
+       <BOLD>--- a/lines.txt<RESET>
+       <BOLD>+++ b/lines.txt<RESET>
+       <CYAN>@@ -1,16 +1,16 @@<RESET>
+       <MAGENTA>-long line 1<RESET>
+       <MAGENTA>-long line 2<RESET>
+       <MAGENTA>-long line 3<RESET>
+        line 4<RESET>
+        line 5<RESET>
+        line 6<RESET>
+        line 7<RESET>
+        line 8<RESET>
+        line 9<RESET>
+       <CYAN>+<RESET><CYAN>long line 1<RESET>
+       <CYAN>+<RESET><CYAN>long line 2<RESET>
+       <CYAN>+<RESET><CYAN>long line 3<RESET>
+       <CYAN>+<RESET><CYAN>long line 14<RESET>
+       <CYAN>+<RESET><CYAN>long line 15<RESET>
+       <CYAN>+<RESET><CYAN>long line 16<RESET>
+        line 10<RESET>
+        line 11<RESET>
+        line 12<RESET>
+        line 13<RESET>
+       <MAGENTA>-long line 14<RESET>
+       <MAGENTA>-long line 15<RESET>
+       <MAGENTA>-long line 16<RESET>
+       EOF
+       test_cmp expected actual
+
+'
+
+test_expect_success 'detect permutations inside moved code -- dimmed_zebra' '
+       # reuse setup from test before!
+       test_config color.diff.oldMoved "magenta" &&
+       test_config color.diff.newMoved "cyan" &&
+       test_config color.diff.oldMovedAlternative "blue" &&
+       test_config color.diff.newMovedAlternative "yellow" &&
+       test_config color.diff.oldMovedDimmed "normal magenta" &&
+       test_config color.diff.newMovedDimmed "normal cyan" &&
+       test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
+       test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
+       git diff HEAD --no-renames --color-moved=dimmed_zebra --color >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
@@ -1315,9 +1358,8 @@ test_expect_success 'cmd option assumes configured colored-moved' '
        test_config color.diff.oldMovedAlternativeDimmed "normal blue" &&
        test_config color.diff.newMovedAlternativeDimmed "normal yellow" &&
        test_config diff.colorMoved zebra &&
-       git diff HEAD --no-renames --color-moved --color |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --no-renames --color-moved --color >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
@@ -1395,9 +1437,8 @@ test_expect_success 'move detection ignoring whitespace ' '
        line 4
        line 5
        EOF
-       git diff HEAD --no-renames --color-moved --color |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --no-renames --color-moved --color >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
@@ -1419,9 +1460,9 @@ test_expect_success 'move detection ignoring whitespace ' '
        EOF
        test_cmp expected actual &&
 
-       git diff HEAD --no-renames -w --color-moved --color |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --no-renames --color-moved --color \
+               --color-moved-ws=ignore-all-space >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
@@ -1459,9 +1500,8 @@ test_expect_success 'move detection ignoring whitespace changes' '
        line 5
        EOF
 
-       git diff HEAD --no-renames --color-moved --color |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --no-renames --color-moved --color >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
@@ -1483,9 +1523,9 @@ test_expect_success 'move detection ignoring whitespace changes' '
        EOF
        test_cmp expected actual &&
 
-       git diff HEAD --no-renames -b --color-moved --color |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --no-renames --color-moved --color \
+               --color-moved-ws=ignore-space-change >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
@@ -1526,9 +1566,8 @@ test_expect_success 'move detection ignoring whitespace at eol' '
        # avoid cluttering the output with complaints about our eol whitespace
        test_config core.whitespace -blank-at-eol &&
 
-       git diff HEAD --no-renames --color-moved --color |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --no-renames --color-moved --color >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
@@ -1550,9 +1589,9 @@ test_expect_success 'move detection ignoring whitespace at eol' '
        EOF
        test_cmp expected actual &&
 
-       git diff HEAD --no-renames --ignore-space-at-eol --color-moved --color |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --no-renames --color-moved --color \
+               --color-moved-ws=ignore-space-at-eol >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat <<-\EOF >expected &&
        <BOLD>diff --git a/lines.txt b/lines.txt<RESET>
        <BOLD>--- a/lines.txt<RESET>
@@ -1597,9 +1636,8 @@ test_expect_success '--color-moved block at end of diff output respects MIN_ALNU
        irrelevant_line
        EOF
 
-       git diff HEAD --color-moved=zebra --color --no-renames |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat >expected <<-\EOF &&
        <BOLD>diff --git a/bar b/bar<RESET>
        <BOLD>--- a/bar<RESET>
@@ -1636,9 +1674,8 @@ test_expect_success '--color-moved respects MIN_ALNUM_COUNT' '
        nineteen chars 456789
        EOF
 
-       git diff HEAD --color-moved=zebra --color --no-renames |
-               grep -v "index" |
-               test_decode_color >actual &&
+       git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat >expected <<-\EOF &&
        <BOLD>diff --git a/bar b/bar<RESET>
        <BOLD>--- a/bar<RESET>
@@ -1679,7 +1716,8 @@ test_expect_success '--color-moved treats adjacent blocks as separate for MIN_AL
        7charsA
        EOF
 
-       git diff HEAD --color-moved=zebra --color --no-renames | grep -v "index" | test_decode_color >actual &&
+       git diff HEAD --color-moved=zebra --color --no-renames >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
        cat >expected <<-\EOF &&
        <BOLD>diff --git a/bar b/bar<RESET>
        <BOLD>--- a/bar<RESET>
@@ -1722,7 +1760,146 @@ test_expect_success 'move detection with submodules' '
 
        # nor did we mess with it another way
        git diff --submodule=diff --color | test_decode_color >expect &&
-       test_cmp expect decoded_actual
+       test_cmp expect decoded_actual &&
+       rm -rf bananas &&
+       git submodule deinit bananas
+'
+
+test_expect_success 'only move detection ignores white spaces' '
+       git reset --hard &&
+       q_to_tab <<-\EOF >text.txt &&
+               a long line to exceed per-line minimum
+               another long line to exceed per-line minimum
+               original file
+       EOF
+       git add text.txt &&
+       git commit -m "add text" &&
+       q_to_tab <<-\EOF >text.txt &&
+               Qa long line to exceed per-line minimum
+               Qanother long line to exceed per-line minimum
+               new file
+       EOF
+
+       # Make sure we get a different diff using -w
+       git diff --color --color-moved -w >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
+       q_to_tab <<-\EOF >expected &&
+       <BOLD>diff --git a/text.txt b/text.txt<RESET>
+       <BOLD>--- a/text.txt<RESET>
+       <BOLD>+++ b/text.txt<RESET>
+       <CYAN>@@ -1,3 +1,3 @@<RESET>
+        Qa long line to exceed per-line minimum<RESET>
+        Qanother long line to exceed per-line minimum<RESET>
+       <RED>-original file<RESET>
+       <GREEN>+<RESET><GREEN>new file<RESET>
+       EOF
+       test_cmp expected actual &&
+
+       # And now ignoring white space only in the move detection
+       git diff --color --color-moved \
+               --color-moved-ws=ignore-all-space,ignore-space-change,ignore-space-at-eol >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
+       q_to_tab <<-\EOF >expected &&
+       <BOLD>diff --git a/text.txt b/text.txt<RESET>
+       <BOLD>--- a/text.txt<RESET>
+       <BOLD>+++ b/text.txt<RESET>
+       <CYAN>@@ -1,3 +1,3 @@<RESET>
+       <BOLD;MAGENTA>-a long line to exceed per-line minimum<RESET>
+       <BOLD;MAGENTA>-another long line to exceed per-line minimum<RESET>
+       <RED>-original file<RESET>
+       <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>a long line to exceed per-line minimum<RESET>
+       <BOLD;YELLOW>+<RESET>Q<BOLD;YELLOW>another long line to exceed per-line minimum<RESET>
+       <GREEN>+<RESET><GREEN>new file<RESET>
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'compare whitespace delta across moved blocks' '
+
+       git reset --hard &&
+       q_to_tab <<-\EOF >text.txt &&
+       QIndented
+       QText across
+       Qsome lines
+       QBut! <- this stands out
+       QAdjusting with
+       QQdifferent starting
+       Qwhite spaces
+       QAnother outlier
+       QQQIndented
+       QQQText across
+       QQQfive lines
+       QQQthat has similar lines
+       QQQto previous blocks, but with different indent
+       QQQYetQAnotherQoutlierQ
+       EOF
+
+       git add text.txt &&
+       git commit -m "add text.txt" &&
+
+       q_to_tab <<-\EOF >text.txt &&
+       QQIndented
+       QQText across
+       QQsome lines
+       QQQBut! <- this stands out
+       Adjusting with
+       Qdifferent starting
+       white spaces
+       AnotherQoutlier
+       QQIndented
+       QQText across
+       QQfive lines
+       QQthat has similar lines
+       QQto previous blocks, but with different indent
+       QQYetQAnotherQoutlier
+       EOF
+
+       git diff --color --color-moved --color-moved-ws=allow-indentation-change >actual.raw &&
+       grep -v "index" actual.raw | test_decode_color >actual &&
+
+       q_to_tab <<-\EOF >expected &&
+               <BOLD>diff --git a/text.txt b/text.txt<RESET>
+               <BOLD>--- a/text.txt<RESET>
+               <BOLD>+++ b/text.txt<RESET>
+               <CYAN>@@ -1,14 +1,14 @@<RESET>
+               <BOLD;MAGENTA>-QIndented<RESET>
+               <BOLD;MAGENTA>-QText across<RESET>
+               <BOLD;MAGENTA>-Qsome lines<RESET>
+               <RED>-QBut! <- this stands out<RESET>
+               <BOLD;MAGENTA>-QAdjusting with<RESET>
+               <BOLD;MAGENTA>-QQdifferent starting<RESET>
+               <BOLD;MAGENTA>-Qwhite spaces<RESET>
+               <RED>-QAnother outlier<RESET>
+               <BOLD;MAGENTA>-QQQIndented<RESET>
+               <BOLD;MAGENTA>-QQQText across<RESET>
+               <BOLD;MAGENTA>-QQQfive lines<RESET>
+               <BOLD;MAGENTA>-QQQthat has similar lines<RESET>
+               <BOLD;MAGENTA>-QQQto previous blocks, but with different indent<RESET>
+               <RED>-QQQYetQAnotherQoutlierQ<RESET>
+               <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET>
+               <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET>
+               <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>some lines<RESET>
+               <GREEN>+<RESET>QQQ<GREEN>But! <- this stands out<RESET>
+               <BOLD;CYAN>+<RESET><BOLD;CYAN>Adjusting with<RESET>
+               <BOLD;CYAN>+<RESET>Q<BOLD;CYAN>different starting<RESET>
+               <BOLD;CYAN>+<RESET><BOLD;CYAN>white spaces<RESET>
+               <GREEN>+<RESET><GREEN>AnotherQoutlier<RESET>
+               <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Indented<RESET>
+               <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>Text across<RESET>
+               <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>five lines<RESET>
+               <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>that has similar lines<RESET>
+               <BOLD;CYAN>+<RESET>QQ<BOLD;CYAN>to previous blocks, but with different indent<RESET>
+               <GREEN>+<RESET>QQ<GREEN>YetQAnotherQoutlier<RESET>
+       EOF
+
+       test_cmp expected actual
+'
+
+test_expect_success 'compare whitespace delta incompatible with other space options' '
+       test_must_fail git diff \
+               --color-moved-ws=allow-indentation-change,ignore-all-space \
+               2>err &&
+       test_i18ngrep allow-indentation-change err
 '
 
 test_done
diff --git a/t/t4018/php-abstract-class b/t/t4018/php-abstract-class
new file mode 100644 (file)
index 0000000..5213e12
--- /dev/null
@@ -0,0 +1,4 @@
+abstract class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
diff --git a/t/t4018/php-class b/t/t4018/php-class
new file mode 100644 (file)
index 0000000..7785b63
--- /dev/null
@@ -0,0 +1,4 @@
+class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
diff --git a/t/t4018/php-final-class b/t/t4018/php-final-class
new file mode 100644 (file)
index 0000000..69f5710
--- /dev/null
@@ -0,0 +1,4 @@
+final class RIGHT
+{
+    const FOO = 'ChangeMe';
+}
diff --git a/t/t4018/php-function b/t/t4018/php-function
new file mode 100644 (file)
index 0000000..35717c5
--- /dev/null
@@ -0,0 +1,4 @@
+function RIGHT()
+{
+    return 'ChangeMe';
+}
diff --git a/t/t4018/php-interface b/t/t4018/php-interface
new file mode 100644 (file)
index 0000000..86b49ad
--- /dev/null
@@ -0,0 +1,4 @@
+interface RIGHT
+{
+    public function foo($ChangeMe);
+}
diff --git a/t/t4018/php-method b/t/t4018/php-method
new file mode 100644 (file)
index 0000000..03af1a6
--- /dev/null
@@ -0,0 +1,7 @@
+class Klass
+{
+    public static function RIGHT()
+    {
+        return 'ChangeMe';
+    }
+}
diff --git a/t/t4018/php-trait b/t/t4018/php-trait
new file mode 100644 (file)
index 0000000..65b8c82
--- /dev/null
@@ -0,0 +1,7 @@
+trait RIGHT
+{
+    public function foo($ChangeMe)
+    {
+        return 'foo';
+    }
+}
index 7e76018296c8f0c51b7187c96330d6e0a70a2fe4..6b44ce14933f8ceaa7e5e93eb9eaa0ff2527b66c 100755 (executable)
@@ -127,17 +127,17 @@ test_expect_success setup '
 
        for n in $sample
        do
-               ( zs $n ; echo a ) >file-a$n &&
-               ( echo b; zs $n; echo ) >file-b$n &&
-               ( printf c; zs $n ) >file-c$n &&
-               ( echo d; zs $n ) >file-d$n &&
+               ( zs $n && echo a ) >file-a$n &&
+               ( echo b && zs $n && echo ) >file-b$n &&
+               ( printf c && zs $n ) >file-c$n &&
+               ( echo d && zs $n ) >file-d$n &&
 
                git add file-a$n file-b$n file-c$n file-d$n &&
 
-               ( zs $n ; echo A ) >file-a$n &&
-               ( echo B; zs $n; echo ) >file-b$n &&
-               ( printf C; zs $n ) >file-c$n &&
-               ( echo D; zs $n ) >file-d$n &&
+               ( zs $n && echo A ) >file-a$n &&
+               ( echo B && zs $n && echo ) >file-b$n &&
+               ( printf C && zs $n ) >file-c$n &&
+               ( echo D && zs $n ) >file-d$n &&
 
                expect_pattern $n || return 1
 
index 7a3dbc1ea22fd19a54da8949abc368c112377b19..fa44e788695c6f3226ad17cb6e8f2b8e256814ed 100755 (executable)
@@ -12,12 +12,12 @@ NS="$N$N$N$N$N$N$N$N$N$N$N$N$N"
 test_expect_success setup '
 
        (
-               echo "A $NS"
+               echo "A $NS" &&
                for c in B C D E F G H I J K
                do
                        echo "  $c"
-               done
-               echo "L  $NS"
+               done &&
+               echo "L  $NS" &&
                for c in M N O P Q R S T U V
                do
                        echo "  $c"
@@ -34,7 +34,7 @@ test_expect_success 'hunk header truncation with an overly long line' '
 
        git diff | sed -n -e "s/^.*@@//p" >actual &&
        (
-               echo " A $N$N$N$N$N$N$N$N$N2"
+               echo " A $N$N$N$N$N$N$N$N$N2" &&
                echo " L  $N$N$N$N$N$N$N$N$N1"
        ) >expected &&
        test_cmp actual expected
index 058ee0829ded8163f99325494f326ef6609fee06..4e3499ef84c1bc3bb26857d70bf7f5ba78d5bf9a 100755 (executable)
@@ -498,7 +498,7 @@ test_expect_success 'given commit --submodule=short' '
 test_expect_success 'setup .git file for sm2' '
        (cd sm2 &&
         REAL="$(pwd)/../.real" &&
-        mv .git "$REAL"
+        mv .git "$REAL" &&
         echo "gitdir: $REAL" >.git)
 '
 
@@ -527,7 +527,7 @@ test_expect_success 'diff --submodule with objects referenced by alternates' '
                git commit -m "sub a"
        ) &&
        (cd sub_alt &&
-               sha1_before=$(git rev-parse --short HEAD)
+               sha1_before=$(git rev-parse --short HEAD) &&
                echo b >b &&
                git add b &&
                git commit -m b &&
index 4b168d0ed7ff2bbb1d93895de7fdf3f14bfe334e..0eba4620f039aaa529afa2cf3fbcff2559857b9e 100755 (executable)
@@ -721,7 +721,7 @@ test_expect_success 'given commit' '
 test_expect_success 'setup .git file for sm2' '
        (cd sm2 &&
         REAL="$(pwd)/../.real" &&
-        mv .git "$REAL"
+        mv .git "$REAL" &&
         echo "gitdir: $REAL" >.git)
 '
 
index aff551a1d787477eb2db34d96217f66ca03c435d..66368effd5c04ebeaeba30e30eaf9ba9d7cf262a 100755 (executable)
@@ -27,6 +27,6 @@ test_expect_success 'setup' \
 
 test_expect_success \
        'check if contextually independent diffs for the same file apply' \
-       '( git diff test~2 test~1; git diff test~1 test~0 )| git apply'
+       '( git diff test~2 test~1 && git diff test~1 test~0 )| git apply'
 
 test_done
index 62f335b2d9fe7268f8f6cd878067de8528b5cb75..4c8f3b8e1bdd084a6407d76ff26cff0048e90bdf 100755 (executable)
@@ -25,6 +25,32 @@ test_expect_success '"git log :/a -- " should not be ambiguous' '
        git log :/a --
 '
 
+test_expect_success '"git log :/detached -- " should find a commit only in HEAD' '
+       test_when_finished "git checkout master" &&
+       git checkout --detach &&
+       # Must manually call `test_tick` instead of using `test_commit`,
+       # because the latter additionally creates a tag, which would make
+       # the commit reachable not only via HEAD.
+       test_tick &&
+       git commit --allow-empty -m detached &&
+       test_tick &&
+       git commit --allow-empty -m something-else &&
+       git log :/detached --
+'
+
+test_expect_success '"git log :/detached -- " should not find an orphaned commit' '
+       test_must_fail git log :/detached --
+'
+
+test_expect_success '"git log :/detached -- " should find HEAD only of own worktree' '
+       git worktree add other-tree HEAD &&
+       git -C other-tree checkout --detach &&
+       test_tick &&
+       git -C other-tree commit --allow-empty -m other-detached &&
+       git -C other-tree log :/other-detached -- &&
+       test_must_fail git log :/other-detached --
+'
+
 test_expect_success '"git log -- :/a" should not be ambiguous' '
        git log -- :/a
 '
index d0377fae5c832bcd4df37f3bc2ab4a8708f70251..436b13ad21d06351ef9f9a027689a7e23ca97ba5 100755 (executable)
@@ -60,7 +60,6 @@ test_bad_opts "-L 1:nonexistent" "There is no path"
 test_bad_opts "-L 1:simple" "There is no path"
 test_bad_opts "-L '/foo:b.c'" "argument not .start,end:file"
 test_bad_opts "-L 1000:b.c" "has only.*lines"
-test_bad_opts "-L 1,1000:b.c" "has only.*lines"
 test_bad_opts "-L :b.c" "argument not .start,end:file"
 test_bad_opts "-L :foo:b.c" "no match"
 
@@ -86,12 +85,12 @@ test_expect_success '-L ,Y (Y == nlines)' '
 
 test_expect_success '-L ,Y (Y == nlines + 1)' '
        n=$(expr $(wc -l <b.c) + 1) &&
-       test_must_fail git log -L ,$n:b.c
+       git log -L ,$n:b.c
 '
 
 test_expect_success '-L ,Y (Y == nlines + 2)' '
        n=$(expr $(wc -l <b.c) + 2) &&
-       test_must_fail git log -L ,$n:b.c
+       git log -L ,$n:b.c
 '
 
 test_expect_success '-L with --first-parent and a merge' '
index 2336d09dcc45f3b808c5e5d6a7c43c8129e8cecc..6c620cd5407537e19507bd92a9cd55b2e54001d8 100755 (executable)
@@ -191,7 +191,7 @@ test_expect_success 'survive missing objects/pack directory' '
                mkdir missing-pack &&
                cd missing-pack &&
                git init &&
-               GOP=.git/objects/pack
+               GOP=.git/objects/pack &&
                rm -fr $GOP &&
                git index-pack --stdin --keep=test <../test-3-${packname_3}.pack &&
                test -f $GOP/pack-${packname_3}.pack &&
index bb9b8bb3097c05f6e28c37fa24fa27d5bb5b805d..91d51b35f917de2d8b7fc2ebe5f4b9221169abc0 100755 (executable)
@@ -237,7 +237,7 @@ test_expect_success 'running index-pack in the object store' '
     rm -f .git/objects/pack/* &&
     cp test-1-${pack1}.pack .git/objects/pack/pack-${pack1}.pack &&
     (
-       cd .git/objects/pack
+       cd .git/objects/pack &&
        git index-pack pack-${pack1}.pack
     ) &&
     test -f .git/objects/pack/pack-${pack1}.idx
index 1b0acc383bb216a3f420cd19ff066275a5b4706e..6710c8bc8c4699035bec418258fcaec8a634855c 100755 (executable)
@@ -160,6 +160,22 @@ test_expect_success 'verify blob:limit=1k' '
        test_cmp observed expected
 '
 
+test_expect_success 'verify explicitly specifying oversized blob in input' '
+       git -C r2 ls-files -s large.1000 large.10000 \
+               | awk -f print_2.awk \
+               | sort >expected &&
+       git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
+       HEAD
+       $(git -C r2 rev-parse HEAD:large.10000)
+       EOF
+       git -C r2 index-pack ../filter.pack &&
+       git -C r2 verify-pack -v ../filter.pack \
+               | grep blob \
+               | awk -f print_1.awk \
+               | sort >observed &&
+       test_cmp observed expected
+'
+
 test_expect_success 'verify blob:limit=1m' '
        git -C r2 ls-files -s large.1000 large.10000 \
                | awk -f print_2.awk \
index 77d85aefe7da18a0d7cc2db2baaedacead6b2e97..5947de3d2438ea301e33f2fc3430ee703d1436b9 100755 (executable)
@@ -11,6 +11,11 @@ test_expect_success 'setup full repo' '
        objdir=".git/objects"
 '
 
+test_expect_success 'verify graph with no graph file' '
+       cd "$TRASH_DIRECTORY/full" &&
+       git commit-graph verify
+'
+
 test_expect_success 'write graph with no packs' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write --object-dir . &&
@@ -28,8 +33,8 @@ test_expect_success 'create commits and repack' '
 '
 
 graph_git_two_modes() {
-       git -c core.graph=true $1 >output
-       git -c core.graph=false $1 >expect
+       git -c core.commitGraph=true $1 >output
+       git -c core.commitGraph=false $1 >expect
        test_cmp output expect
 }
 
@@ -200,6 +205,16 @@ test_expect_success 'build graph from commits with append' '
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
 graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
 
+test_expect_success 'build graph using --reachable' '
+       cd "$TRASH_DIRECTORY/full" &&
+       git commit-graph write --reachable &&
+       test_path_is_file $objdir/info/commit-graph &&
+       graph_read_expect "11" "large_edges"
+'
+
+graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
+graph_git_behavior 'append graph, commit 8 vs merge 2' full commits/8 merge/2
+
 test_expect_success 'setup bare repo' '
        cd "$TRASH_DIRECTORY" &&
        git clone --bare --no-local full bare &&
@@ -230,4 +245,190 @@ test_expect_success 'perform fast-forward merge in full repo' '
        test_cmp expect output
 '
 
+test_expect_success 'check that gc computes commit-graph' '
+       cd "$TRASH_DIRECTORY/full" &&
+       git commit --allow-empty -m "blank" &&
+       git commit-graph write --reachable &&
+       cp $objdir/info/commit-graph commit-graph-before-gc &&
+       git reset --hard HEAD~1 &&
+       git config gc.writeCommitGraph true &&
+       git gc &&
+       cp $objdir/info/commit-graph commit-graph-after-gc &&
+       ! test_cmp commit-graph-before-gc commit-graph-after-gc &&
+       git commit-graph write --reachable &&
+       test_cmp commit-graph-after-gc $objdir/info/commit-graph
+'
+
+# the verify tests below expect the commit-graph to contain
+# exactly the commits reachable from the commits/8 branch.
+# If the file changes the set of commits in the list, then the
+# offsets into the binary file will result in different edits
+# and the tests will likely break.
+
+test_expect_success 'git commit-graph verify' '
+       cd "$TRASH_DIRECTORY/full" &&
+       git rev-parse commits/8 | git commit-graph write --stdin-commits &&
+       git commit-graph verify >output
+'
+
+NUM_COMMITS=9
+NUM_OCTOPUS_EDGES=2
+HASH_LEN=20
+GRAPH_BYTE_VERSION=4
+GRAPH_BYTE_HASH=5
+GRAPH_BYTE_CHUNK_COUNT=6
+GRAPH_CHUNK_LOOKUP_OFFSET=8
+GRAPH_CHUNK_LOOKUP_WIDTH=12
+GRAPH_CHUNK_LOOKUP_ROWS=5
+GRAPH_BYTE_OID_FANOUT_ID=$GRAPH_CHUNK_LOOKUP_OFFSET
+GRAPH_BYTE_OID_LOOKUP_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
+                           1 * $GRAPH_CHUNK_LOOKUP_WIDTH))
+GRAPH_BYTE_COMMIT_DATA_ID=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
+                            2 * $GRAPH_CHUNK_LOOKUP_WIDTH))
+GRAPH_FANOUT_OFFSET=$(($GRAPH_CHUNK_LOOKUP_OFFSET + \
+                      $GRAPH_CHUNK_LOOKUP_WIDTH * $GRAPH_CHUNK_LOOKUP_ROWS))
+GRAPH_BYTE_FANOUT1=$(($GRAPH_FANOUT_OFFSET + 4 * 4))
+GRAPH_BYTE_FANOUT2=$(($GRAPH_FANOUT_OFFSET + 4 * 255))
+GRAPH_OID_LOOKUP_OFFSET=$(($GRAPH_FANOUT_OFFSET + 4 * 256))
+GRAPH_BYTE_OID_LOOKUP_ORDER=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 8))
+GRAPH_BYTE_OID_LOOKUP_MISSING=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * 4 + 10))
+GRAPH_COMMIT_DATA_OFFSET=$(($GRAPH_OID_LOOKUP_OFFSET + $HASH_LEN * $NUM_COMMITS))
+GRAPH_BYTE_COMMIT_TREE=$GRAPH_COMMIT_DATA_OFFSET
+GRAPH_BYTE_COMMIT_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN))
+GRAPH_BYTE_COMMIT_EXTRA_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 4))
+GRAPH_BYTE_COMMIT_WRONG_PARENT=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 3))
+GRAPH_BYTE_COMMIT_GENERATION=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 11))
+GRAPH_BYTE_COMMIT_DATE=$(($GRAPH_COMMIT_DATA_OFFSET + $HASH_LEN + 12))
+GRAPH_COMMIT_DATA_WIDTH=$(($HASH_LEN + 16))
+GRAPH_OCTOPUS_DATA_OFFSET=$(($GRAPH_COMMIT_DATA_OFFSET + \
+                            $GRAPH_COMMIT_DATA_WIDTH * $NUM_COMMITS))
+GRAPH_BYTE_OCTOPUS=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4))
+GRAPH_BYTE_FOOTER=$(($GRAPH_OCTOPUS_DATA_OFFSET + 4 * $NUM_OCTOPUS_EDGES))
+
+# usage: corrupt_graph_and_verify <position> <data> <string>
+# Manipulates the commit-graph file at the position
+# by inserting the data, then runs 'git commit-graph verify'
+# and places the output in the file 'err'. Test 'err' for
+# the given string.
+corrupt_graph_and_verify() {
+       pos=$1
+       data="${2:-\0}"
+       grepstr=$3
+       cd "$TRASH_DIRECTORY/full" &&
+       test_when_finished mv commit-graph-backup $objdir/info/commit-graph &&
+       cp $objdir/info/commit-graph commit-graph-backup &&
+       printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
+       test_must_fail git commit-graph verify 2>test_err &&
+       grep -v "^+" test_err >err
+       test_i18ngrep "$grepstr" err
+}
+
+test_expect_success 'detect bad signature' '
+       corrupt_graph_and_verify 0 "\0" \
+               "graph signature"
+'
+
+test_expect_success 'detect bad version' '
+       corrupt_graph_and_verify $GRAPH_BYTE_VERSION "\02" \
+               "graph version"
+'
+
+test_expect_success 'detect bad hash version' '
+       corrupt_graph_and_verify $GRAPH_BYTE_HASH "\02" \
+               "hash version"
+'
+
+test_expect_success 'detect low chunk count' '
+       corrupt_graph_and_verify $GRAPH_BYTE_CHUNK_COUNT "\02" \
+               "missing the .* chunk"
+'
+
+test_expect_success 'detect missing OID fanout chunk' '
+       corrupt_graph_and_verify $GRAPH_BYTE_OID_FANOUT_ID "\0" \
+               "missing the OID Fanout chunk"
+'
+
+test_expect_success 'detect missing OID lookup chunk' '
+       corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ID "\0" \
+               "missing the OID Lookup chunk"
+'
+
+test_expect_success 'detect missing commit data chunk' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATA_ID "\0" \
+               "missing the Commit Data chunk"
+'
+
+test_expect_success 'detect incorrect fanout' '
+       corrupt_graph_and_verify $GRAPH_BYTE_FANOUT1 "\01" \
+               "fanout value"
+'
+
+test_expect_success 'detect incorrect fanout final value' '
+       corrupt_graph_and_verify $GRAPH_BYTE_FANOUT2 "\01" \
+               "fanout value"
+'
+
+test_expect_success 'detect incorrect OID order' '
+       corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_ORDER "\01" \
+               "incorrect OID order"
+'
+
+test_expect_success 'detect OID not in object database' '
+       corrupt_graph_and_verify $GRAPH_BYTE_OID_LOOKUP_MISSING "\01" \
+               "from object database"
+'
+
+test_expect_success 'detect incorrect tree OID' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_TREE "\01" \
+               "root tree OID for commit"
+'
+
+test_expect_success 'detect incorrect parent int-id' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_PARENT "\01" \
+               "invalid parent"
+'
+
+test_expect_success 'detect extra parent int-id' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_EXTRA_PARENT "\00" \
+               "is too long"
+'
+
+test_expect_success 'detect wrong parent' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_WRONG_PARENT "\01" \
+               "commit-graph parent for"
+'
+
+test_expect_success 'detect incorrect generation number' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\070" \
+               "generation for commit"
+'
+
+test_expect_success 'detect incorrect generation number' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_GENERATION "\01" \
+               "non-zero generation number"
+'
+
+test_expect_success 'detect incorrect commit date' '
+       corrupt_graph_and_verify $GRAPH_BYTE_COMMIT_DATE "\01" \
+               "commit date"
+'
+
+test_expect_success 'detect incorrect parent for octopus merge' '
+       corrupt_graph_and_verify $GRAPH_BYTE_OCTOPUS "\01" \
+               "invalid parent"
+'
+
+test_expect_success 'detect invalid checksum hash' '
+       corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
+               "incorrect checksum"
+'
+
+test_expect_success 'git fsck (checks commit-graph)' '
+       cd "$TRASH_DIRECTORY/full" &&
+       git fsck &&
+       corrupt_graph_and_verify $GRAPH_BYTE_FOOTER "\00" \
+               "incorrect checksum" &&
+       test_must_fail git fsck
+'
+
 test_done
index 911eae1bf7518485ace0fbd417e3ea0bbb18a9cf..f1932ea431dca6d5aa2ad71575b7767413eeac18 100755 (executable)
@@ -86,7 +86,7 @@ test_expect_success 'push can be used to delete a ref' '
 test_expect_success 'refuse deleting push with denyDeletes' '
        (
            cd victim &&
-           ( git branch -D extra || : ) &&
+           test_might_fail git branch -D extra &&
            git config receive.denyDeletes true &&
            git branch extra master
        ) &&
@@ -119,7 +119,7 @@ test_expect_success 'override denyDeletes with git -c receive-pack' '
 test_expect_success 'denyNonFastforwards trumps --force' '
        (
            cd victim &&
-           ( git branch -D extra || : ) &&
+           test_might_fail git branch -D extra &&
            git config receive.denyNonFastforwards true
        ) &&
        victim_orig=$(cd victim && git rev-parse --verify master) &&
index 7f278d8ce932f34420c337571ec043ffb5e318e9..b5f886a0e264eed3d3ab54176b01912bbb8badb6 100755 (executable)
@@ -82,13 +82,13 @@ test_expect_success 'hooks ran' '
 '
 
 test_expect_success 'pre-receive hook input' '
-       (echo $commit0 $commit1 refs/heads/master;
+       (echo $commit0 $commit1 refs/heads/master &&
         echo $commit1 $commit0 refs/heads/tofail
        ) | test_cmp - victim.git/pre-receive.stdin
 '
 
 test_expect_success 'update hook arguments' '
-       (echo refs/heads/master $commit0 $commit1;
+       (echo refs/heads/master $commit0 $commit1 &&
         echo refs/heads/tofail $commit1 $commit0
        ) | test_cmp - victim.git/update.args
 '
index 4bda18a662da5a0c36ee007c37d9ded6be3b1832..235fb7686ae0dec21e81b06dd0b220f0705a6b2e 100755 (executable)
@@ -25,8 +25,7 @@ test_expect_success 'non forced push should die not segfault' '
 
        (
                cd another &&
-               git push .. master:master
-               test $? = 1
+               test_must_fail git push .. master:master
        )
 
 '
index 59e80a5ea253607bf83ac4eed670744df950eb81..ff06f99649e4f09b88716ffef27e1aa12519c3a4 100755 (executable)
@@ -6,8 +6,9 @@ test_description='remote push rejects are reported by client'
 
 test_expect_success 'setup' '
        mkdir .git/hooks &&
-       (echo "#!/bin/sh" ; echo "exit 1") >.git/hooks/update &&
-       chmod +x .git/hooks/update &&
+       write_script .git/hooks/update <<-\EOF &&
+       exit 1
+       EOF
        echo 1 >file &&
        git add file &&
        git commit -m 1 &&
index 7a48236e87695d947c7c3f166e1d291cd3b7a05b..9b2a274c71f40ace060069212055a29f9a3c4203 100755 (executable)
@@ -113,7 +113,7 @@ test_expect_success 'git rebase -m' '
 test_expect_success 'git rebase -m --skip' '
        git reset --hard D &&
        clear_hook_input &&
-       test_must_fail git rebase --onto A B &&
+       test_must_fail git rebase -m --onto A B &&
        test_must_fail git rebase --skip &&
        echo D > foo &&
        git add foo &&
index ea6570e81990d73dc07d03c3bb6e6af5c8f029f4..fa03f56a2026b712519f932a8b4fd4a01bee80cc 100755 (executable)
@@ -259,7 +259,7 @@ test_expect_success 'clone shallow object count' '
 test_expect_success 'pull in shallow repo with missing merge base' '
        (
                cd shallow &&
-               git fetch --depth 4 .. A
+               git fetch --depth 4 .. A &&
                test_must_fail git merge --allow-unrelated-histories FETCH_HEAD
        )
 '
@@ -533,19 +533,26 @@ test_expect_success 'test --all wrt tag to non-commits' '
        # are reachable only via created tag references.
        blob=$(echo "hello blob" | git hash-object -t blob -w --stdin) &&
        git tag -a -m "tag -> blob" tag-to-blob $blob &&
- \
+
        tree=$(printf "100644 blob $blob\tfile" | git mktree) &&
        git tag -a -m "tag -> tree" tag-to-tree $tree &&
- \
+
        tree2=$(printf "100644 blob $blob\tfile2" | git mktree) &&
        commit=$(git commit-tree -m "hello commit" $tree) &&
        git tag -a -m "tag -> commit" tag-to-commit $commit &&
- \
+
        blob2=$(echo "hello blob2" | git hash-object -t blob -w --stdin) &&
-       tag=$(printf "object $blob2\ntype blob\ntag tag-to-blob2\n\
-tagger author A U Thor <author@example.com> 0 +0000\n\nhello tag" | git mktag) &&
+       tag=$(git mktag <<-EOF
+               object $blob2
+               type blob
+               tag tag-to-blob2
+               tagger author A U Thor <author@example.com> 0 +0000
+
+               hello tag
+       EOF
+       ) &&
        git tag -a -m "tag -> tag" tag-to-tag $tag &&
- \
+
        # `fetch-pack --all` should succeed fetching all those objects.
        mkdir fetchall &&
        (
@@ -807,6 +814,39 @@ test_expect_success 'fetching deepen' '
        )
 '
 
+test_expect_success 'use ref advertisement to prune "have" lines sent' '
+       rm -rf server client &&
+       git init server &&
+       test_commit -C server both_have_1 &&
+       git -C server tag -d both_have_1 &&
+       test_commit -C server both_have_2 &&
+
+       git clone server client &&
+       test_commit -C server server_has &&
+       test_commit -C client client_has &&
+
+       # In both protocol v0 and v2, ensure that the parent of both_have_2 is
+       # not sent as a "have" line. The client should know that the server has
+       # both_have_2, so it only needs to inform the server that it has
+       # both_have_2, and the server can infer the rest.
+
+       rm -f trace &&
+       cp -r client clientv0 &&
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv0 \
+               fetch origin server_has both_have_2 &&
+       grep "have $(git -C client rev-parse client_has)" trace &&
+       grep "have $(git -C client rev-parse both_have_2)" trace &&
+       ! grep "have $(git -C client rev-parse both_have_2^)" trace &&
+
+       rm -f trace &&
+       cp -r client clientv2 &&
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C clientv2 -c protocol.version=2 \
+               fetch origin server_has both_have_2 &&
+       grep "have $(git -C client rev-parse client_has)" trace &&
+       grep "have $(git -C client rev-parse both_have_2)" trace &&
+       ! grep "have $(git -C client rev-parse both_have_2^)" trace
+'
+
 test_expect_success 'filtering by size' '
        rm -rf server client &&
        test_create_repo server &&
index a6c0178f3af6c3e3f01816197e51e9bca546b00a..11e14a1e0fd591c053e3cb50a1a40e49b2b93e09 100755 (executable)
@@ -348,17 +348,13 @@ URL: $(pwd)/one
 EOF
 
 test_expect_success 'prune --dry-run' '
-       (
-               cd one &&
-               git branch -m side2 side) &&
+       git -C one branch -m side2 side &&
+       test_when_finished "git -C one branch -m side side2" &&
        (
                cd test &&
                git remote prune --dry-run origin >output &&
                git rev-parse refs/remotes/origin/side2 &&
                test_must_fail git rev-parse refs/remotes/origin/side &&
-       (
-               cd ../one &&
-               git branch -m side side2) &&
                test_i18ncmp expect output
        )
 '
@@ -848,7 +844,7 @@ test_expect_success 'migrate a remote from named file in $GIT_DIR/branches (2)'
                git remote rename origin origin &&
                test_path_is_missing .git/branches/origin &&
                test "$(git config remote.origin.url)" = "quux" &&
-               test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin"
+               test "$(git config remote.origin.fetch)" = "refs/heads/foom:refs/heads/origin" &&
                test "$(git config remote.origin.push)" = "HEAD:refs/heads/foom"
        )
 '
index 6a949484d090ea2df02603f9d82bf4f203a799e9..ea020040e858de0c65759e299ce9cf646573fc63 100755 (executable)
@@ -15,7 +15,7 @@ test_expect_success setup '
        git tag mark1.10 &&
        git show-ref --tags -d | sed -e "s/ /   /" >expected.tag &&
        (
-               echo "$(git rev-parse HEAD)     HEAD"
+               echo "$(git rev-parse HEAD)     HEAD" &&
                git show-ref -d | sed -e "s/ /  /"
        ) >expected.all &&
 
@@ -105,7 +105,7 @@ test_expect_success 'use branch.<name>.remote if possible' '
        git clone . other.git &&
        (
                cd other.git &&
-               echo "$(git rev-parse HEAD)     HEAD"
+               echo "$(git rev-parse HEAD)     HEAD" &&
                git show-ref    | sed -e "s/ /  /"
        ) >exp &&
 
index a5077d8b7c596523a3b0e0e5e45385e5d7871b9e..bd8f23e4300218fba33a5bea5effa167a2e9155a 100755 (executable)
@@ -923,7 +923,7 @@ test_expect_success 'push into aliased refs (consistent)' '
        (
                cd child1 &&
                git branch foo &&
-               git symbolic-ref refs/heads/bar refs/heads/foo
+               git symbolic-ref refs/heads/bar refs/heads/foo &&
                git config receive.denyCurrentBranch false
        ) &&
        (
@@ -945,7 +945,7 @@ test_expect_success 'push into aliased refs (inconsistent)' '
        (
                cd child1 &&
                git branch foo &&
-               git symbolic-ref refs/heads/bar refs/heads/foo
+               git symbolic-ref refs/heads/bar refs/heads/foo &&
                git config receive.denyCurrentBranch false
        ) &&
        (
@@ -1011,7 +1011,7 @@ test_expect_success 'push --porcelain rejected' '
        mk_empty testrepo &&
        git push testrepo refs/heads/master:refs/remotes/origin/master &&
        (cd testrepo &&
-               git reset --hard origin/master^
+               git reset --hard origin/master^ &&
                git config receive.denyCurrentBranch true) &&
 
        echo >.git/foo  "To testrepo"  &&
@@ -1025,7 +1025,7 @@ test_expect_success 'push --porcelain --dry-run rejected' '
        mk_empty testrepo &&
        git push testrepo refs/heads/master:refs/remotes/origin/master &&
        (cd testrepo &&
-               git reset --hard origin/master
+               git reset --hard origin/master &&
                git config receive.denyCurrentBranch true) &&
 
        echo >.git/foo  "To testrepo"  &&
@@ -1333,7 +1333,7 @@ test_expect_success 'push --follow-tag only pushes relevant tags' '
                git commit --allow-empty -m "future commit" &&
                git tag -m "future" future &&
                git checkout master &&
-               git for-each-ref refs/heads/master refs/tags/tag >../expect
+               git for-each-ref refs/heads/master refs/tags/tag >../expect &&
                git push --follow-tag ../dst master
        ) &&
        (
index 02f160aae0c414411572a99a148d2b039c24fdd3..c05a661400c10cb7574173b34116362d501481ce 100755 (executable)
@@ -71,7 +71,7 @@ test_expect_success 'push mirror force updates existing branches' '
                git push --mirror up &&
                echo two >foo && git add foo && git commit -m two &&
                git push --mirror up &&
-               git reset --hard HEAD^
+               git reset --hard HEAD^ &&
                git push --mirror up
        ) &&
        master_master=$(cd master && git show-ref -s --verify refs/heads/master) &&
@@ -88,7 +88,7 @@ test_expect_success 'push mirror removes branches' '
                echo one >foo && git add foo && git commit -m one &&
                git branch remove master &&
                git push --mirror up &&
-               git branch -D remove
+               git branch -D remove &&
                git push --mirror up
        ) &&
        (
@@ -170,7 +170,7 @@ test_expect_success 'push mirror force updates existing tags' '
                echo two >foo && git add foo && git commit -m two &&
                git tag -f tmaster master &&
                git push --mirror up &&
-               git reset --hard HEAD^
+               git reset --hard HEAD^ &&
                git tag -f tmaster master &&
                git push --mirror up
        ) &&
@@ -188,7 +188,7 @@ test_expect_success 'push mirror removes tags' '
                echo one >foo && git add foo && git commit -m one &&
                git tag -f tremove master &&
                git push --mirror up &&
-               git tag -d tremove
+               git tag -d tremove &&
                git push --mirror up
        ) &&
        (
@@ -235,7 +235,7 @@ test_expect_success 'remote.foo.mirror adds and removes branches' '
                git branch keep master &&
                git branch remove master &&
                git push up &&
-               git branch -D remove
+               git branch -D remove &&
                git push up
        ) &&
        (
index 59c4b778d3a455b6ef4575a4b07b860d1e84720d..68aa5f0340132e8d3fabfd85aa70f1cdfe755cf3 100755 (executable)
@@ -618,6 +618,18 @@ test_expect_success 'pull --rebase fails on unborn branch with staged changes' '
        )
 '
 
+test_expect_success 'pull --rebase fails on corrupt HEAD' '
+       test_when_finished "rm -rf corrupt" &&
+       git init corrupt &&
+       (
+               cd corrupt &&
+               test_commit one &&
+               obj=$(git rev-parse --verify HEAD | sed "s#^..#&/#") &&
+               rm -f .git/objects/$obj &&
+               test_must_fail git pull --rebase
+       )
+'
+
 test_expect_success 'setup for detecting upstreamed changes' '
        mkdir src &&
        (cd src &&
index 359e03ff836cf6ac087c83c5b7abe7365ecb4447..0f730d7781568861564b44d534bad728095ac71d 100755 (executable)
@@ -379,7 +379,7 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess
                        git config -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive false
                ) &&
                git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err &&
-               git config --unset fetch.recurseSubmodules
+               git config --unset fetch.recurseSubmodules &&
                (
                        cd submodule &&
                        git config --unset -f .gitmodules submodule.subdir/deepsubmodule.fetchRecursive
index 39cb2c1c3489c25545ab3cd69706c0a21378c6b7..e2c37fd9785d2a47f85853a3611006609381c54c 100755 (executable)
@@ -354,7 +354,7 @@ test_expect_success 'push succeeds if submodule has no remote and is on the firs
        git clone a a1 &&
        (
                cd a1 &&
-               git init b
+               git init b &&
                (
                        cd b &&
                        >junk &&
index 943231af9638d14641811ea8196f8afdafdf6871..7045685e2d3942161084ecd04a116faa83f35a44 100755 (executable)
@@ -186,4 +186,47 @@ EOF
        test_cmp expect actual
 '
 
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
+
+test_expect_success 'shallow fetches check connectivity before writing shallow file' '
+       rm -rf "$REPO" client &&
+
+       git init "$REPO" &&
+       test_commit -C "$REPO" one &&
+       test_commit -C "$REPO" two &&
+       test_commit -C "$REPO" three &&
+
+       git init client &&
+
+       # Use protocol v2 to ensure that shallow information is sent exactly
+       # once by the server, since we are planning to manipulate it.
+       git -C "$REPO" config protocol.version 2 &&
+       git -C client config protocol.version 2 &&
+
+       git -C client fetch --depth=2 "$HTTPD_URL/one_time_sed/repo" master:a_branch &&
+
+       # Craft a situation in which the server sends back an unshallow request
+       # with an empty packfile. This is done by refetching with a shorter
+       # depth (to ensure that the packfile is empty), and overwriting the
+       # shallow line in the response with the unshallow line we want.
+       printf "s/0034shallow %s/0036unshallow %s/" \
+              "$(git -C "$REPO" rev-parse HEAD)" \
+              "$(git -C "$REPO" rev-parse HEAD^)" \
+              >"$HTTPD_ROOT_PATH/one-time-sed" &&
+       test_must_fail git -C client fetch --depth=1 "$HTTPD_URL/one_time_sed/repo" \
+               master:a_branch &&
+
+       # Ensure that the one-time-sed script was used.
+       ! test -e "$HTTPD_ROOT_PATH/one-time-sed" &&
+
+       # Ensure that the resulting repo is consistent, despite our failure to
+       # fetch.
+       git -C client fsck
+'
+
+stop_httpd
+
 test_done
index a2af693068fa455838c97df1fefe38bd630ceb2e..a0fc4005e059723dbafb26606548a074554bc961 100755 (executable)
@@ -38,25 +38,16 @@ GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
 POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
 EOF
 test_expect_success 'no empty path components' '
+       # Clear the log, so that it does not affect the "used receive-pack
+       # service" test which reads the log too.
+       test_when_finished ">\"\$HTTPD_ROOT_PATH\"/access.log" &&
+
        # In the URL, add a trailing slash, and see if git appends yet another
        # slash.
        cd "$ROOT_PATH" &&
        git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
 
-       sed -e "
-               s/^.* \"//
-               s/\"//
-               s/ [1-9][0-9]*\$//
-               s/^GET /GET  /
-       " >act <"$HTTPD_ROOT_PATH"/access.log &&
-
-       # Clear the log, so that it does not affect the "used receive-pack
-       # service" test which reads the log too.
-       #
-       # We do this before the actual comparison to ensure the log is cleared.
-       echo > "$HTTPD_ROOT_PATH"/access.log &&
-
-       test_cmp exp act
+       check_access_log exp
 '
 
 test_expect_success 'clone remote repository' '
@@ -124,7 +115,6 @@ test_expect_success 'rejected update prints status' '
 rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
 
 cat >exp <<EOF
-
 GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
 POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
 GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
@@ -138,13 +128,7 @@ GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
 POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
 EOF
 test_expect_success 'used receive-pack service' '
-       sed -e "
-               s/^.* \"//
-               s/\"//
-               s/ [1-9][0-9]*\$//
-               s/^GET /GET  /
-       " >act <"$HTTPD_ROOT_PATH"/access.log &&
-       test_cmp exp act
+       check_access_log exp
 '
 
 test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
index 3480b33007de4d0b3a73afb634aee176f3faa3c7..7079bcf9a0567e926a37031dd56711a0b093d712 100755 (executable)
@@ -178,7 +178,7 @@ test_expect_success 'atomic push obeys update hook preventing a branch to be pus
 test_expect_success 'atomic push is not advertised if configured' '
        mk_repo_pair &&
        (
-               cd upstream
+               cd upstream &&
                git config receive.advertiseatomic 0
        ) &&
        (
index 913089b14452976144878415c05a9a2c743d22bf..3aab44bdcbbc8939c0f80c84f7d44f50bc3ede16 100755 (executable)
@@ -103,13 +103,7 @@ GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
 POST /smart/repo.git/git-upload-pack HTTP/1.1 200
 EOF
 test_expect_success 'used upload-pack service' '
-       sed -e "
-               s/^.* \"//
-               s/\"//
-               s/ [1-9][0-9]*\$//
-               s/^GET /GET  /
-       " >act <"$HTTPD_ROOT_PATH"/access.log &&
-       test_cmp exp act
+       check_access_log exp
 '
 
 test_expect_success 'follow redirects (301)' '
index 84a955770a017e68d0cc1e928929e5a043d64e86..1c49054595c23564cf762ac01ea0a1667a59f2f9 100755 (executable)
@@ -129,13 +129,7 @@ GET  /smart/repo.git/info/refs?service=git-receive-pack HTTP/1.1 403 -
 POST /smart/repo.git/git-receive-pack HTTP/1.1 403 -
 EOF
 test_expect_success 'server request log matches test results' '
-       sed -e "
-               s/^.* \"//
-               s/\"//
-               s/ [1-9][0-9]*\$//
-               s/^GET /GET  /
-       " >act <"$HTTPD_ROOT_PATH"/access.log &&
-       test_cmp exp act
+       check_access_log exp
 '
 
 stop_httpd
index 0b620377448bc3b97d39d2b6beceb4d185edb7ba..ddaa96ac4f44a4e4799aa509b3bd69bc28628d60 100755 (executable)
@@ -618,7 +618,7 @@ hex2oct () {
 test_expect_success 'clone on case-insensitive fs' '
        git init icasefs &&
        (
-               cd icasefs
+               cd icasefs &&
                o=$(git hash-object -w --stdin </dev/null | hex2oct) &&
                t=$(printf "100644 X\0${o}100644 x\0${o}" |
                        git hash-object -w -t tree --stdin) &&
index 3c087e907c4fbe6d8a05854f1d1051a9f9afec7c..af23419ebfc15dc94fd3fa631ea72016ccf2104b 100755 (executable)
@@ -94,7 +94,7 @@ test_expect_success 'clone empty repository' '
         git config receive.denyCurrentBranch warn) &&
        git clone empty empty-clone &&
        test_tick &&
-       (cd empty-clone
+       (cd empty-clone &&
         echo "content" >> foo &&
         git add foo &&
         git commit -m "Initial commit" &&
index df822d9a3e9e7c7b4b7031ffa75716cd7ba6103a..2c6bc07344ccd74447b0cf73186f7818eafc5c94 100755 (executable)
@@ -23,7 +23,7 @@ test_expect_success CLONE_2GB 'setup' '
                printf "blob\nmark :$i\ndata $blobsize\n" &&
                #test-tool genrandom $i $blobsize &&
                printf "%-${blobsize}s" $i &&
-               echo "M 100644 :$i $i" >> commit
+               echo "M 100644 :$i $i" >> commit &&
                i=$(($i+1)) ||
                echo $? > exit-status
         done &&
index cee556536757128861e166ea8e2cab7f975282c6..44d8e80171a646bc6b4172a38763ca6355c94901 100755 (executable)
@@ -154,4 +154,68 @@ test_expect_success 'partial clone with transfer.fsckobjects=1 uses index-pack -
        grep "git index-pack.*--fsck-objects" trace
 '
 
+test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
+       rm -rf src dst &&
+       git init src &&
+       test_commit -C src x &&
+       test_config -C src uploadpack.allowfilter 1 &&
+       test_config -C src uploadpack.allowanysha1inwant 1 &&
+
+       # Create a tag pointing to a blob.
+       BLOB=$(echo blob-contents | git -C src hash-object --stdin -w) &&
+       git -C src tag myblob "$BLOB" &&
+
+       git clone --filter="blob:none" "file://$(pwd)/src" dst 2>err &&
+       ! grep "does not point to a valid object" err &&
+       git -C dst fsck
+'
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+# Converts bytes into a form suitable for inclusion in a sed command. For
+# example, "printf 'ab\r\n' | hex_unpack" results in '\x61\x62\x0d\x0a'.
+sed_escape () {
+       perl -e '$/ = undef; $input = <>; print unpack("H2" x length($input), $input)' |
+               sed 's/\(..\)/\\x\1/g'
+}
+
+test_expect_success 'upon cloning, check that all refs point to objects' '
+       SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+       rm -rf "$SERVER" repo &&
+       test_create_repo "$SERVER" &&
+       test_commit -C "$SERVER" foo &&
+       test_config -C "$SERVER" uploadpack.allowfilter 1 &&
+       test_config -C "$SERVER" uploadpack.allowanysha1inwant 1 &&
+
+       # Create a tag pointing to a blob.
+       BLOB=$(echo blob-contents | git -C "$SERVER" hash-object --stdin -w) &&
+       git -C "$SERVER" tag myblob "$BLOB" &&
+
+       # Craft a packfile not including that blob.
+       git -C "$SERVER" rev-parse HEAD |
+               git -C "$SERVER" pack-objects --stdout >incomplete.pack &&
+
+       # Replace the existing packfile with the crafted one. The protocol
+       # requires that the packfile be sent in sideband 1, hence the extra
+       # \x01 byte at the beginning.
+       printf "1,/packfile/!c %04x\\\\x01%s0000" \
+               "$(($(wc -c <incomplete.pack) + 5))" \
+               "$(sed_escape <incomplete.pack)" \
+               >"$HTTPD_ROOT_PATH/one-time-sed" &&
+
+       # Use protocol v2 because the sed command looks for the "packfile"
+       # section header.
+       test_config -C "$SERVER" protocol.version 2 &&
+       test_must_fail git -c protocol.version=2 clone \
+               --filter=blob:none $HTTPD_URL/one_time_sed/server repo 2>err &&
+
+       grep "did not send all necessary objects" err &&
+
+       # Ensure that the one-time-sed script was used.
+       ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
+'
+
+stop_httpd
+
 test_done
diff --git a/t/t5703-upload-pack-ref-in-want.sh b/t/t5703-upload-pack-ref-in-want.sh
new file mode 100755 (executable)
index 0000000..a73c55a
--- /dev/null
@@ -0,0 +1,377 @@
+#!/bin/sh
+
+test_description='upload-pack ref-in-want'
+
+. ./test-lib.sh
+
+get_actual_refs () {
+       sed -n -e '/wanted-refs/,/0001/{
+               /wanted-refs/d
+               /0001/d
+               p
+               }' <out | test-pkt-line unpack >actual_refs
+}
+
+get_actual_commits () {
+       sed -n -e '/packfile/,/0000/{
+               /packfile/d
+               p
+               }' <out | test-pkt-line unpack-sideband >o.pack &&
+       git index-pack o.pack &&
+       git verify-pack -v o.idx | grep commit | cut -c-40 | sort >actual_commits
+}
+
+check_output () {
+       get_actual_refs &&
+       test_cmp expected_refs actual_refs &&
+       get_actual_commits &&
+       test_cmp expected_commits actual_commits
+}
+
+# c(o/foo) d(o/bar)
+#        \ /
+#         b   e(baz)  f(master)
+#          \__  |  __/
+#             \ | /
+#               a
+test_expect_success 'setup repository' '
+       test_commit a &&
+       git checkout -b o/foo &&
+       test_commit b &&
+       test_commit c &&
+       git checkout -b o/bar b &&
+       test_commit d &&
+       git checkout -b baz a &&
+       test_commit e &&
+       git checkout master &&
+       test_commit f
+'
+
+test_expect_success 'config controls ref-in-want advertisement' '
+       git serve --advertise-capabilities >out &&
+       ! grep -a ref-in-want out &&
+
+       git config uploadpack.allowRefInWant false &&
+       git serve --advertise-capabilities >out &&
+       ! grep -a ref-in-want out &&
+
+       git config uploadpack.allowRefInWant true &&
+       git serve --advertise-capabilities >out &&
+       grep -a ref-in-want out
+'
+
+test_expect_success 'invalid want-ref line' '
+       test-pkt-line pack >in <<-EOF &&
+       command=fetch
+       0001
+       no-progress
+       want-ref refs/heads/non-existent
+       done
+       0000
+       EOF
+
+       test_must_fail git serve --stateless-rpc 2>out <in &&
+       grep "unknown ref" out
+'
+
+test_expect_success 'basic want-ref' '
+       cat >expected_refs <<-EOF &&
+       $(git rev-parse f) refs/heads/master
+       EOF
+       git rev-parse f | sort >expected_commits &&
+
+       test-pkt-line pack >in <<-EOF &&
+       command=fetch
+       0001
+       no-progress
+       want-ref refs/heads/master
+       have $(git rev-parse a)
+       done
+       0000
+       EOF
+
+       git serve --stateless-rpc >out <in &&
+       check_output
+'
+
+test_expect_success 'multiple want-ref lines' '
+       cat >expected_refs <<-EOF &&
+       $(git rev-parse c) refs/heads/o/foo
+       $(git rev-parse d) refs/heads/o/bar
+       EOF
+       git rev-parse c d | sort >expected_commits &&
+
+       test-pkt-line pack >in <<-EOF &&
+       command=fetch
+       0001
+       no-progress
+       want-ref refs/heads/o/foo
+       want-ref refs/heads/o/bar
+       have $(git rev-parse b)
+       done
+       0000
+       EOF
+
+       git serve --stateless-rpc >out <in &&
+       check_output
+'
+
+test_expect_success 'mix want and want-ref' '
+       cat >expected_refs <<-EOF &&
+       $(git rev-parse f) refs/heads/master
+       EOF
+       git rev-parse e f | sort >expected_commits &&
+
+       test-pkt-line pack >in <<-EOF &&
+       command=fetch
+       0001
+       no-progress
+       want-ref refs/heads/master
+       want $(git rev-parse e)
+       have $(git rev-parse a)
+       done
+       0000
+       EOF
+
+       git serve --stateless-rpc >out <in &&
+       check_output
+'
+
+test_expect_success 'want-ref with ref we already have commit for' '
+       cat >expected_refs <<-EOF &&
+       $(git rev-parse c) refs/heads/o/foo
+       EOF
+       >expected_commits &&
+
+       test-pkt-line pack >in <<-EOF &&
+       command=fetch
+       0001
+       no-progress
+       want-ref refs/heads/o/foo
+       have $(git rev-parse c)
+       done
+       0000
+       EOF
+
+       git serve --stateless-rpc >out <in &&
+       check_output
+'
+
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+REPO="$HTTPD_DOCUMENT_ROOT_PATH/repo"
+LOCAL_PRISTINE="$(pwd)/local_pristine"
+
+test_expect_success 'setup repos for change-while-negotiating test' '
+       (
+               git init "$REPO" &&
+               cd "$REPO" &&
+               >.git/git-daemon-export-ok &&
+               test_commit m1 &&
+               git tag -d m1 &&
+
+               # Local repo with many commits (so that negotiation will take
+               # more than 1 request/response pair)
+               git clone "http://127.0.0.1:$LIB_HTTPD_PORT/smart/repo" "$LOCAL_PRISTINE" &&
+               cd "$LOCAL_PRISTINE" &&
+               git checkout -b side &&
+               for i in $(seq 1 33); do test_commit s$i; done &&
+
+               # Add novel commits to upstream
+               git checkout master &&
+               cd "$REPO" &&
+               test_commit m2 &&
+               test_commit m3 &&
+               git tag -d m2 m3
+       ) &&
+       git -C "$LOCAL_PRISTINE" remote set-url origin "http://127.0.0.1:$LIB_HTTPD_PORT/one_time_sed/repo" &&
+       git -C "$LOCAL_PRISTINE" config protocol.version 2
+'
+
+inconsistency () {
+       # Simulate that the server initially reports $2 as the ref
+       # corresponding to $1, and after that, $1 as the ref corresponding to
+       # $1. This corresponds to the real-life situation where the server's
+       # repository appears to change during negotiation, for example, when
+       # different servers in a load-balancing arrangement serve (stateless)
+       # RPCs during a single negotiation.
+       printf "s/%s/%s/" \
+              $(git -C "$REPO" rev-parse $1 | tr -d "\n") \
+              $(git -C "$REPO" rev-parse $2 | tr -d "\n") \
+              >"$HTTPD_ROOT_PATH/one-time-sed"
+}
+
+test_expect_success 'server is initially ahead - no ref in want' '
+       git -C "$REPO" config uploadpack.allowRefInWant false &&
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       inconsistency master 1234567890123456789012345678901234567890 &&
+       test_must_fail git -C local fetch 2>err &&
+       grep "ERR upload-pack: not our ref" err
+'
+
+test_expect_success 'server is initially ahead - ref in want' '
+       git -C "$REPO" config uploadpack.allowRefInWant true &&
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       inconsistency master 1234567890123456789012345678901234567890 &&
+       git -C local fetch &&
+
+       git -C "$REPO" rev-parse --verify master >expected &&
+       git -C local rev-parse --verify refs/remotes/origin/master >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'server is initially behind - no ref in want' '
+       git -C "$REPO" config uploadpack.allowRefInWant false &&
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       inconsistency master "master^" &&
+       git -C local fetch &&
+
+       git -C "$REPO" rev-parse --verify "master^" >expected &&
+       git -C local rev-parse --verify refs/remotes/origin/master >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'server is initially behind - ref in want' '
+       git -C "$REPO" config uploadpack.allowRefInWant true &&
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       inconsistency master "master^" &&
+       git -C local fetch &&
+
+       git -C "$REPO" rev-parse --verify "master" >expected &&
+       git -C local rev-parse --verify refs/remotes/origin/master >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'server loses a ref - ref in want' '
+       git -C "$REPO" config uploadpack.allowRefInWant true &&
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       echo "s/master/raster/" >"$HTTPD_ROOT_PATH/one-time-sed" &&
+       test_must_fail git -C local fetch 2>err &&
+
+       grep "ERR unknown ref refs/heads/raster" err
+'
+
+stop_httpd
+
+REPO="$(pwd)/repo"
+LOCAL_PRISTINE="$(pwd)/local_pristine"
+
+# $REPO
+# c(o/foo) d(o/bar)
+#        \ /
+#         b   e(baz)  f(master)
+#          \__  |  __/
+#             \ | /
+#               a
+#
+# $LOCAL_PRISTINE
+#              s32(side)
+#              |
+#              .
+#              .
+#              |
+#              a(master)
+test_expect_success 'setup repos for fetching with ref-in-want tests' '
+       (
+               git init "$REPO" &&
+               cd "$REPO" &&
+               test_commit a &&
+
+               # Local repo with many commits (so that negotiation will take
+               # more than 1 request/response pair)
+               rm -rf "$LOCAL_PRISTINE" &&
+               git clone "file://$REPO" "$LOCAL_PRISTINE" &&
+               cd "$LOCAL_PRISTINE" &&
+               git checkout -b side &&
+               for i in $(seq 1 33); do test_commit s$i; done &&
+
+               # Add novel commits to upstream
+               git checkout master &&
+               cd "$REPO" &&
+               git checkout -b o/foo &&
+               test_commit b &&
+               test_commit c &&
+               git checkout -b o/bar b &&
+               test_commit d &&
+               git checkout -b baz a &&
+               test_commit e &&
+               git checkout master &&
+               test_commit f
+       ) &&
+       git -C "$REPO" config uploadpack.allowRefInWant true &&
+       git -C "$LOCAL_PRISTINE" config protocol.version 2
+'
+
+test_expect_success 'fetching with exact OID' '
+       test_when_finished "rm -f log" &&
+
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
+               $(git -C "$REPO" rev-parse d):refs/heads/actual &&
+
+       git -C "$REPO" rev-parse "d" >expected &&
+       git -C local rev-parse refs/heads/actual >actual &&
+       test_cmp expected actual &&
+       grep "want $(git -C "$REPO" rev-parse d)" log
+'
+
+test_expect_success 'fetching multiple refs' '
+       test_when_finished "rm -f log" &&
+
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin master baz &&
+
+       git -C "$REPO" rev-parse "master" "baz" >expected &&
+       git -C local rev-parse refs/remotes/origin/master refs/remotes/origin/baz >actual &&
+       test_cmp expected actual &&
+       grep "want-ref refs/heads/master" log &&
+       grep "want-ref refs/heads/baz" log
+'
+
+test_expect_success 'fetching ref and exact OID' '
+       test_when_finished "rm -f log" &&
+
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin \
+               master $(git -C "$REPO" rev-parse b):refs/heads/actual &&
+
+       git -C "$REPO" rev-parse "master" "b" >expected &&
+       git -C local rev-parse refs/remotes/origin/master refs/heads/actual >actual &&
+       test_cmp expected actual &&
+       grep "want $(git -C "$REPO" rev-parse b)" log &&
+       grep "want-ref refs/heads/master" log
+'
+
+test_expect_success 'fetching with wildcard that does not match any refs' '
+       test_when_finished "rm -f log" &&
+
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       git -C local fetch origin refs/heads/none*:refs/heads/* >out &&
+       test_must_be_empty out
+'
+
+test_expect_success 'fetching with wildcard that matches multiple refs' '
+       test_when_finished "rm -f log" &&
+
+       rm -rf local &&
+       cp -r "$LOCAL_PRISTINE" local &&
+       GIT_TRACE_PACKET="$(pwd)/log" git -C local fetch origin refs/heads/o*:refs/heads/o* &&
+
+       git -C "$REPO" rev-parse "o/foo" "o/bar" >expected &&
+       git -C local rev-parse "o/foo" "o/bar" >actual &&
+       test_cmp expected actual &&
+       grep "want-ref refs/heads/o/foo" log &&
+       grep "want-ref refs/heads/o/bar" log
+'
+
+test_done
index 362b1581e092826400d78515cdc8d5cb0f7cfc29..ee5757966f597637a3f9f1602a9ef4bd7ffd908d 100755 (executable)
@@ -96,7 +96,7 @@ test_expect_success 'push new branch with old:new refspec' '
 
 test_expect_success 'push new branch with HEAD:new refspec' '
        (cd local &&
-        git checkout new-name
+        git checkout new-name &&
         git push origin HEAD:new-refspec-2
        ) &&
        compare_refs local HEAD server refs/heads/new-refspec-2
index aa2d360ce35940a176a0ef04ca7ba8560adefb23..44c726ea397cf10a2c522690653ecb696f96cc8b 100755 (executable)
@@ -245,7 +245,7 @@ test_expect_success 'using reflog to find the fork point' '
                        git commit --allow-empty -m "Derived #$count" &&
                        git rev-parse HEAD >derived$count &&
                        git checkout -B base $E || exit 1
-               done
+               done &&
 
                for count in 1 2 3
                do
index 3e692454a719324a7ed3c12238416f45df080efa..7d5bc784721411a3abe475a8ed9f291204072b7d 100755 (executable)
@@ -55,7 +55,7 @@ test_expect_success 'initial merge' '
        git checkout -b work &&
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      git-gui/git-gui.sh"
+               echo "100644 $o1 0      git-gui/git-gui.sh" &&
                echo "100644 $o2 0      git.c"
        ) >expected &&
        test_cmp expected actual
@@ -72,7 +72,7 @@ test_expect_success 'merge update' '
        git pull -s subtree gui master2 &&
        git ls-files -s >actual &&
        (
-               echo "100644 $o3 0      git-gui/git-gui.sh"
+               echo "100644 $o3 0      git-gui/git-gui.sh" &&
                echo "100644 $o2 0      git.c"
        ) >expected &&
        test_cmp expected actual
@@ -88,8 +88,8 @@ test_expect_success 'initial ambiguous subtree' '
        git checkout -b work2 &&
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      git-gui/git-gui.sh"
-               echo "100644 $o1 0      git-gui2/git-gui.sh"
+               echo "100644 $o1 0      git-gui/git-gui.sh" &&
+               echo "100644 $o1 0      git-gui2/git-gui.sh" &&
                echo "100644 $o2 0      git.c"
        ) >expected &&
        test_cmp expected actual
@@ -101,8 +101,8 @@ test_expect_success 'merge using explicit' '
        git pull -Xsubtree=git-gui gui master2 &&
        git ls-files -s >actual &&
        (
-               echo "100644 $o3 0      git-gui/git-gui.sh"
-               echo "100644 $o1 0      git-gui2/git-gui.sh"
+               echo "100644 $o3 0      git-gui/git-gui.sh" &&
+               echo "100644 $o1 0      git-gui2/git-gui.sh" &&
                echo "100644 $o2 0      git.c"
        ) >expected &&
        test_cmp expected actual
@@ -114,8 +114,8 @@ test_expect_success 'merge2 using explicit' '
        git pull -Xsubtree=git-gui2 gui master2 &&
        git ls-files -s >actual &&
        (
-               echo "100644 $o1 0      git-gui/git-gui.sh"
-               echo "100644 $o3 0      git-gui2/git-gui.sh"
+               echo "100644 $o1 0      git-gui/git-gui.sh" &&
+               echo "100644 $o3 0      git-gui2/git-gui.sh" &&
                echo "100644 $o2 0      git.c"
        ) >expected &&
        test_cmp expected actual
index b5621303d67d47146b4af8ee79fe55c338674071..4abe8a06e737be2ea500642762854f6e64632423 100755 (executable)
@@ -72,7 +72,7 @@ test_expect_success 'merge simple rename+criss-cross with no modifications' '
                git rev-parse   >actual     \
                        :2:three   :3:three &&
                git hash-object >>actual    \
-                       three~HEAD three~R2^0
+                       three~HEAD three~R2^0 &&
                test_cmp expect actual
        )
 '
@@ -148,7 +148,7 @@ test_expect_success 'merge criss-cross + rename merges with basic modification'
                git rev-parse   >actual     \
                        :2:three   :3:three &&
                git hash-object >>actual    \
-                       three~HEAD three~R2^0
+                       three~HEAD three~R2^0 &&
                test_cmp expect actual
        )
 '
@@ -228,7 +228,7 @@ test_expect_success 'git detects differently handled merges conflict' '
                        D:new_a  E:new_a &&
                git rev-parse   >actual     \
                        :2:new_a :3:new_a &&
-               test_cmp expect actual
+               test_cmp expect actual &&
 
                git cat-file -p B:new_a >ours &&
                git cat-file -p C:new_a >theirs &&
@@ -345,40 +345,97 @@ test_expect_success 'git detects conflict merging criss-cross+modify/delete, rev
        )
 '
 
+#      SORRY FOR THE SUPER LONG DESCRIPTION, BUT THIS NEXT ONE IS HAIRY
 #
 # criss-cross + d/f conflict via add/add:
 #   Commit A: Neither file 'a' nor directory 'a/' exists.
 #   Commit B: Introduce 'a'
 #   Commit C: Introduce 'a/file'
-#   Commit D: Merge B & C, keeping 'a' and deleting 'a/'
-#
-# Two different later cases:
+#   Commit D1: Merge B & C, keeping 'a'    and deleting 'a/'
 #   Commit E1: Merge B & C, deleting 'a' but keeping 'a/file'
-#   Commit E2: Merge B & C, deleting 'a' but keeping a slightly modified 'a/file'
 #
-#      B   D
+#      B   D1 or D2
 #      o---o
 #     / \ / \
 #  A o   X   ? F
 #     \ / \ /
 #      o---o
-#      C   E1 or E2
-#
-# Merging D & E1 requires we first create a virtual merge base X from
-# merging A & B in memory.  Now, if X could keep both 'a' and 'a/file' in
-# the index, then the merge of D & E1 could be resolved cleanly with both
-# 'a' and 'a/file' removed.  Since git does not currently allow creating
-# such a tree, the best we can do is have X contain both 'a~<unique>' and
-# 'a/file' resulting in the merge of D and E1 having a rename/delete
-# conflict for 'a'.  (Although this merge appears to be unsolvable with git
-# currently, git could do a lot better than it currently does with these
-# d/f conflicts, which is the purpose of this test.)
-#
-# Merge of D & E2 has similar issues for path 'a', but should always result
-# in a modify/delete conflict for path 'a/file'.
-#
-# We run each merge in both directions, to check for directional issues
-# with D/F conflict handling.
+#      C   E1 or E2 or E3
+#
+# I'll describe D2, E2, & E3 (which are alternatives for D1 & E1) more below...
+#
+# Merging D1 & E1 requires we first create a virtual merge base X from
+# merging A & B in memory.  There are several possibilities for the merge-base:
+#   1: Keep both 'a' and 'a/file' (assuming crazy filesystem allowing a tree
+#      with a directory and file at same path): results in merge of D1 & E1
+#      being clean with both files deleted.  Bad (no conflict detected).
+#   2: Keep 'a' but not 'a/file': Merging D1 & E1 is clean and matches E1.  Bad.
+#   3: Keep 'a/file' but not 'a': Merging D1 & E1 is clean and matches D1.  Bad.
+#   4: Keep neither file: Merging D1 & E1 reports the D/F add/add conflict.
+#
+# So 4 sounds good for this case, but if we were to merge D1 & E3, where E3
+# is defined as:
+#   Commit E3: Merge B & C, keeping modified a, and deleting a/
+# then we'd get an add/add conflict for 'a', which seems suboptimal.  A little
+# creativity leads us to an alternate choice:
+#   5: Keep 'a' as 'a~$UNIQUE' and a/file; results:
+#        Merge D1 & E1: rename/delete conflict for 'a'; a/file silently deleted
+#        Merge D1 & E3 is clean, as expected.
+#
+# So choice 5 at least provides some kind of conflict for the original case,
+# and can merge cleanly as expected with D1 and E3.  It also made things just
+# slightly funny for merging D1 and e$, where E4 is defined as:
+#   Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
+# in this case, we'll get a rename/rename(1to2) conflict because a~$UNIQUE
+# gets renamed to 'a' in D1 and to 'a2' in E4.  But that's better than having
+# two files (both 'a' and 'a2') sitting around without the user being notified
+# that we could detect they were related and need to be merged.  Also, choice
+# 5 makes the handling of 'a/file' seem suboptimal.  What if we were to merge
+# D2 and E4, where D2 is:
+#   Commit D2: Merge B & C, renaming 'a'->'a2', keeping 'a/file'
+# This would result in a clean merge with 'a2' having three-way merged
+# contents (good), and deleting 'a/' (bad) -- it doesn't detect the
+# conflict in how the different sides treated a/file differently.
+# Continuing down the creative route:
+#   6: Keep 'a' as 'a~$UNIQUE1' and keep 'a/' as 'a~$UNIQUE2/'; results:
+#        Merge D1 & E1: rename/delete conflict for 'a' and each path under 'a/'.
+#        Merge D1 & E3: clean, as expected.
+#        Merge D1 & E4: rename/rename(1to2) conflict on 'a' vs 'a2'.
+#        Merge D2 & E4: clean for 'a2', rename/delete for a/file
+#
+# Choice 6 could cause rename detection to take longer (providing more targets
+# that need to be searched).  Also, the conflict message for each path under
+# 'a/' might be annoying unless we can detect it at the directory level, print
+# it once, and then suppress it for individual filepaths underneath.
+#
+#
+# As of time of writing, git uses choice 5.  Directory rename detection and
+# rename detection performance improvements might make choice 6 a desirable
+# improvement.  But we can at least document where we fall short for now...
+#
+#
+# Historically, this testcase also used:
+#   Commit E2: Merge B & C, deleting 'a' but keeping slightly modified 'a/file'
+# The merge of D1 & E2 is very similar to D1 & E1 -- it has similar issues for
+# path 'a', but should always result in a modify/delete conflict for path
+# 'a/file'.  These tests ran the two merges
+#   D1 & E1
+#   D1 & E2
+# in both directions, to check for directional issues with D/F conflict
+# handling. Later we added
+#   D1 & E3
+#   D1 & E4
+#   D2 & E4
+# for good measure, though we only ran those one way because we had pretty
+# good confidence in merge-recursive's directional handling of D/F issues.
+#
+# Just to summarize all the intermediate merge commits:
+#   Commit D1: Merge B & C, keeping a    and deleting a/
+#   Commit D2: Merge B & C, renaming a->a2, keeping a/file
+#   Commit E1: Merge B & C, deleting a but keeping a/file
+#   Commit E2: Merge B & C, deleting a but keeping slightly modified a/file
+#   Commit E3: Merge B & C, keeping modified a, and deleting a/
+#   Commit E4: Merge B & C, modifying 'a' and renaming to 'a2', and deleting 'a/'
 #
 
 test_expect_success 'setup differently handled merges of directory/file conflict' '
@@ -395,56 +452,70 @@ test_expect_success 'setup differently handled merges of directory/file conflict
                git branch B &&
                git checkout -b C &&
                mkdir a &&
-               echo 10 >a/file &&
+               test_write_lines a b c d e f g >a/file &&
                git add a/file &&
                test_tick &&
                git commit -m C &&
 
                git checkout B &&
-               echo 5 >a &&
+               test_write_lines 1 2 3 4 5 6 7 >a &&
                git add a &&
                test_tick &&
                git commit -m B &&
 
                git checkout B^0 &&
-               test_must_fail git merge C &&
-               git clean -f &&
-               rm -rf a/ &&
-               echo 5 >a &&
-               git add a &&
-               test_tick &&
-               git commit -m D &&
-               git tag D &&
+               git merge -s ours -m D1 C^0 &&
+               git tag D1 &&
+
+               git checkout B^0 &&
+               test_must_fail git merge C^0 &&
+               git clean -fd &&
+               git rm -rf a/ &&
+               git rm a &&
+               git cat-file -p B:a >a2 &&
+               git add a2 &&
+               git commit -m D2 &&
+               git tag D2 &&
 
                git checkout C^0 &&
-               test_must_fail git merge B &&
-               git clean -f &&
-               git rm --cached a &&
-               echo 10 >a/file &&
-               git add a/file &&
-               test_tick &&
-               git commit -m E1 &&
+               git merge -s ours -m E1 B^0 &&
                git tag E1 &&
 
                git checkout C^0 &&
-               test_must_fail git merge B &&
-               git clean -f &&
-               git rm --cached a &&
-               printf "10\n11\n" >a/file &&
+               git merge -s ours -m E2 B^0 &&
+               test_write_lines a b c d e f g h >a/file &&
                git add a/file &&
-               test_tick &&
-               git commit -m E2 &&
-               git tag E2
+               git commit --amend -C HEAD &&
+               git tag E2 &&
+
+               git checkout C^0 &&
+               test_must_fail git merge B^0 &&
+               git clean -fd &&
+               git rm -rf a/ &&
+               test_write_lines 1 2 3 4 5 6 7 8 >a &&
+               git add a &&
+               git commit -m E3 &&
+               git tag E3 &&
+
+               git checkout C^0 &&
+               test_must_fail git merge B^0 &&
+               git clean -fd &&
+               git rm -rf a/ &&
+               git rm a &&
+               test_write_lines 1 2 3 4 5 6 7 8 >a2 &&
+               git add a2 &&
+               git commit -m E4 &&
+               git tag E4
        )
 '
 
-test_expect_success 'merge of D & E1 fails but has appropriate contents' '
+test_expect_success 'merge of D1 & E1 fails but has appropriate contents' '
        test_when_finished "git -C directory-file reset --hard" &&
        test_when_finished "git -C directory-file clean -fdqx" &&
        (
                cd directory-file &&
 
-               git checkout D^0 &&
+               git checkout D1^0 &&
 
                test_must_fail git merge -s recursive E1^0 &&
 
@@ -463,7 +534,7 @@ test_expect_success 'merge of D & E1 fails but has appropriate contents' '
        )
 '
 
-test_expect_success 'merge of E1 & D fails but has appropriate contents' '
+test_expect_success 'merge of E1 & D1 fails but has appropriate contents' '
        test_when_finished "git -C directory-file reset --hard" &&
        test_when_finished "git -C directory-file clean -fdqx" &&
        (
@@ -471,7 +542,7 @@ test_expect_success 'merge of E1 & D fails but has appropriate contents' '
 
                git checkout E1^0 &&
 
-               test_must_fail git merge -s recursive D^0 &&
+               test_must_fail git merge -s recursive D1^0 &&
 
                git ls-files -s >out &&
                test_line_count = 2 out &&
@@ -488,13 +559,13 @@ test_expect_success 'merge of E1 & D fails but has appropriate contents' '
        )
 '
 
-test_expect_success 'merge of D & E2 fails but has appropriate contents' '
+test_expect_success 'merge of D1 & E2 fails but has appropriate contents' '
        test_when_finished "git -C directory-file reset --hard" &&
        test_when_finished "git -C directory-file clean -fdqx" &&
        (
                cd directory-file &&
 
-               git checkout D^0 &&
+               git checkout D1^0 &&
 
                test_must_fail git merge -s recursive E2^0 &&
 
@@ -506,16 +577,16 @@ test_expect_success 'merge of D & E2 fails but has appropriate contents' '
                test_line_count = 2 out &&
 
                git rev-parse >expect    \
-                       B:a   E2:a/file  c:a/file   A:ignore-me &&
+                       B:a   E2:a/file  C:a/file   A:ignore-me &&
                git rev-parse   >actual   \
                        :2:a  :3:a/file  :1:a/file  :0:ignore-me &&
-               test_cmp expect actual
+               test_cmp expect actual &&
 
                test_path_is_file a~HEAD
        )
 '
 
-test_expect_success 'merge of E2 & D fails but has appropriate contents' '
+test_expect_success 'merge of E2 & D1 fails but has appropriate contents' '
        test_when_finished "git -C directory-file reset --hard" &&
        test_when_finished "git -C directory-file clean -fdqx" &&
        (
@@ -523,7 +594,7 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
 
                git checkout E2^0 &&
 
-               test_must_fail git merge -s recursive D^0 &&
+               test_must_fail git merge -s recursive D1^0 &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
@@ -533,12 +604,87 @@ test_expect_success 'merge of E2 & D fails but has appropriate contents' '
                test_line_count = 2 out &&
 
                git rev-parse >expect    \
-                       B:a   E2:a/file  c:a/file   A:ignore-me &&
+                       B:a   E2:a/file  C:a/file   A:ignore-me &&
                git rev-parse   >actual   \
                        :3:a  :2:a/file  :1:a/file  :0:ignore-me &&
+               test_cmp expect actual &&
+
+               test_path_is_file a~D1^0
+       )
+'
+
+test_expect_success 'merge of D1 & E3 succeeds' '
+       test_when_finished "git -C directory-file reset --hard" &&
+       test_when_finished "git -C directory-file clean -fdqx" &&
+       (
+               cd directory-file &&
+
+               git checkout D1^0 &&
+
+               git merge -s recursive E3^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 2 out &&
+               git ls-files -u >out &&
+               test_line_count = 0 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect    \
+                       A:ignore-me  E3:a &&
+               git rev-parse   >actual   \
+                       :0:ignore-me :0:a &&
                test_cmp expect actual
+       )
+'
+
+test_expect_success 'merge of D1 & E4 notifies user a and a2 are related' '
+       test_when_finished "git -C directory-file reset --hard" &&
+       test_when_finished "git -C directory-file clean -fdqx" &&
+       (
+               cd directory-file &&
+
+               git checkout D1^0 &&
+
+               test_must_fail git merge -s recursive E4^0 &&
 
-               test_path_is_file a~D^0
+               git ls-files -s >out &&
+               test_line_count = 4 out &&
+               git ls-files -u >out &&
+               test_line_count = 3 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect                  \
+                       A:ignore-me  B:a   D1:a  E4:a2 &&
+               git rev-parse   >actual                \
+                       :0:ignore-me :1:a~Temporary\ merge\ branch\ 2  :2:a  :3:a2 &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_failure 'merge of D2 & E4 merges a2s & reports conflict for a/file' '
+       test_when_finished "git -C directory-file reset --hard" &&
+       test_when_finished "git -C directory-file clean -fdqx" &&
+       (
+               cd directory-file &&
+
+               git checkout D2^0 &&
+
+               test_must_fail git merge -s recursive E4^0 &&
+
+               git ls-files -s >out &&
+               test_line_count = 3 out &&
+               git ls-files -u >out &&
+               test_line_count = 1 out &&
+               git ls-files -o >out &&
+               test_line_count = 1 out &&
+
+               git rev-parse >expect                 \
+                       A:ignore-me  E4:a2  D2:a/file &&
+               git rev-parse   >actual               \
+                       :0:ignore-me :0:a2  :2:a/file &&
+               test_cmp expect actual
        )
 '
 
index 1cbd946fc2eb9ab3dcad83c811abb6d18448fc4e..661b633478fae7ceeb50736a4c675e7b95f5042a 100755 (executable)
@@ -352,7 +352,7 @@ test_expect_success 'rename/directory conflict + content merge conflict' '
                        base:file   left-conflict:newfile  right:file &&
                git rev-parse >actual                                 \
                        :1:newfile  :2:newfile             :3:newfile &&
-               test_cmp expect actual
+               test_cmp expect actual &&
 
                test_path_is_file newfile/realfile &&
                test_path_is_file newfile~HEAD
@@ -580,7 +580,7 @@ test_expect_failure 'detect conflict with rename/rename(1to2)/add-source merge'
                        C:a   A:a   B:b   C:C &&
                git rev-parse >actual          \
                        :3:a  :1:a  :2:b  :3:c &&
-               test_cmp expect actual
+               test_cmp expect actual &&
 
                test_path_is_file a &&
                test_path_is_file b &&
@@ -680,13 +680,13 @@ test_expect_success 'rename/rename/add-dest merge still knows about conflicting
                        A:a   C:b   B:b   C:c   B:c &&
                git rev-parse >actual                \
                        :1:a  :2:b  :3:b  :2:c  :3:c &&
-               test_cmp expect actual
+               test_cmp expect actual &&
 
                git rev-parse >expect               \
                        C:c     B:c     C:b     B:b &&
                git hash-object >actual                \
                        c~HEAD  c~B\^0  b~HEAD  b~B\^0 &&
-               test_cmp expect actual
+               test_cmp expect actual &&
 
                test_path_is_missing b &&
                test_path_is_missing c
index 2e28f2908d5571715aeddaa0962bb8adf4e14591..4a71f17edd69a2cb6962cf759aa6ad1b7604c2db 100755 (executable)
@@ -3583,7 +3583,7 @@ test_expect_success '11d-check: Avoid losing not-uptodate with rename + D/F conf
                grep -q stuff z/c &&
                test_seq 1 10 >expected &&
                echo stuff >>expected &&
-               test_cmp expected z/c
+               test_cmp expected z/c &&
 
                git ls-files -s >out &&
                test_line_count = 4 out &&
index fcefffcaece290f4070aceb9ddf6b5579447f104..38e24f787cffb5e645a5b31c8bfa30fb5106bea7 100755 (executable)
@@ -366,7 +366,9 @@ test_expect_success '2c-check: Modify b & add c VS rename b->c' '
 
                git checkout A^0 &&
 
-               GIT_MERGE_VERBOSITY=3 test_must_fail git merge -s recursive B^0 >out 2>err &&
+               GIT_MERGE_VERBOSITY=3 &&
+               export GIT_MERGE_VERBOSITY &&
+               test_must_fail git merge -s recursive B^0 >out 2>err &&
 
                test_i18ngrep "CONFLICT (rename/add): Rename b->c" out &&
                test_i18ngrep ! "Skipped c" out &&
index 48379aa0ee874b0250e2348d47eded09713a0a79..e0496da812f34df16ef7c3417788f9096ddc1e0f 100755 (executable)
@@ -795,4 +795,15 @@ test_expect_success ':remotename and :remoteref' '
        )
 '
 
+test_expect_success 'for-each-ref --ignore-case ignores case' '
+       >expect &&
+       git for-each-ref --format="%(refname)" refs/heads/MASTER >actual &&
+       test_cmp expect actual &&
+
+       echo refs/heads/master >expect &&
+       git for-each-ref --format="%(refname)" --ignore-case \
+               refs/heads/MASTER >actual &&
+       test_cmp expect actual
+'
+
 test_done
index cc3fd2baf2b80817ffc39ab67a988dcf31dbc537..9e59e5a5dd031f036237dafc3ebb1e3854883caa 100755 (executable)
@@ -509,7 +509,7 @@ test_expect_success 'moving nested submodules' '
                touch nested_level1 &&
                git init &&
                git add . &&
-               git commit -m "nested level 1"
+               git commit -m "nested level 1" &&
                git submodule add ../sub_nested_nested &&
                git commit -m "add nested level 2"
        ) &&
index ec4b160ddb9f966044e729f35cc1edfcc79eed14..e23de7d0b5a4da29effb1e59426f857fed0e91e0 100755 (executable)
@@ -107,6 +107,21 @@ test_expect_success 'test that the directory was renamed' '
        test dir/D = "$(cat diroh/D.t)"
 '
 
+V=$(git rev-parse HEAD)
+
+test_expect_success 'populate --state-branch' '
+       git filter-branch --state-branch state -f --tree-filter "touch file || :" HEAD
+'
+
+W=$(git rev-parse HEAD)
+
+test_expect_success 'using --state-branch to skip already rewritten commits' '
+       test_when_finished git reset --hard $V &&
+       git reset --hard $V &&
+       git filter-branch --state-branch state -f --tree-filter "touch file || :" HEAD &&
+       test_cmp_rev $W HEAD
+'
+
 git tag oldD HEAD~4
 test_expect_success 'rewrite one branch, keeping a side branch' '
        git branch modD oldD &&
index 98b7d7b969e4eb3ded40aba1f12d2afc8bad849a..bd10a96727c573bdd53b53079943990d232431c3 100755 (executable)
@@ -19,20 +19,20 @@ test_expect_success PERL 'setup' '
 
 test_expect_success PERL 'saying "n" does nothing' '
        set_and_save_state dir/foo work work &&
-       (echo n; echo n) | git reset -p &&
+       test_write_lines n n | git reset -p &&
        verify_saved_state dir/foo &&
        verify_saved_state bar
 '
 
 test_expect_success PERL 'git reset -p' '
-       (echo n; echo y) | git reset -p >output &&
+       test_write_lines n y | git reset -p >output &&
        verify_state dir/foo work head &&
        verify_saved_state bar &&
        test_i18ngrep "Unstage" output
 '
 
 test_expect_success PERL 'git reset -p HEAD^' '
-       (echo n; echo y) | git reset -p HEAD^ >output &&
+       test_write_lines n y | git reset -p HEAD^ >output &&
        verify_state dir/foo work parent &&
        verify_saved_state bar &&
        test_i18ngrep "Apply" output
@@ -45,20 +45,20 @@ test_expect_success PERL 'git reset -p HEAD^' '
 
 test_expect_success PERL 'git reset -p dir' '
        set_state dir/foo work work &&
-       (echo y; echo n) | git reset -p dir &&
+       test_write_lines y n | git reset -p dir &&
        verify_state dir/foo work head &&
        verify_saved_state bar
 '
 
 test_expect_success PERL 'git reset -p -- foo (inside dir)' '
        set_state dir/foo work work &&
-       (echo y; echo n) | (cd dir && git reset -p -- foo) &&
+       test_write_lines y n | (cd dir && git reset -p -- foo) &&
        verify_state dir/foo work head &&
        verify_saved_state bar
 '
 
 test_expect_success PERL 'git reset -p HEAD^ -- dir' '
-       (echo y; echo n) | git reset -p HEAD^ -- dir &&
+       test_write_lines y n | git reset -p HEAD^ -- dir &&
        verify_state dir/foo work parent &&
        verify_saved_state bar
 '
index ab9da61da391f1b6040076dda2454f602adef7d2..94cb039a03f8378cec55f61a9694feca4a973d29 100755 (executable)
@@ -528,10 +528,10 @@ test_expect_success 'checkout with --merge' '
        cat sample >filf &&
        git checkout -m -- fild file filf &&
        (
-               echo "<<<<<<< ours"
-               echo ourside
-               echo "======="
-               echo theirside
+               echo "<<<<<<< ours" &&
+               echo ourside &&
+               echo "=======" &&
+               echo theirside &&
                echo ">>>>>>> theirs"
        ) >merged &&
        test_cmp expect fild &&
@@ -549,12 +549,12 @@ test_expect_success 'checkout with --merge, in diff3 -m style' '
        cat sample >filf &&
        git checkout -m -- fild file filf &&
        (
-               echo "<<<<<<< ours"
-               echo ourside
-               echo "||||||| base"
-               echo original
-               echo "======="
-               echo theirside
+               echo "<<<<<<< ours" &&
+               echo ourside &&
+               echo "||||||| base" &&
+               echo original &&
+               echo "=======" &&
+               echo theirside &&
                echo ">>>>>>> theirs"
        ) >merged &&
        test_cmp expect fild &&
@@ -572,10 +572,10 @@ test_expect_success 'checkout --conflict=merge, overriding config' '
        cat sample >filf &&
        git checkout --conflict=merge -- fild file filf &&
        (
-               echo "<<<<<<< ours"
-               echo ourside
-               echo "======="
-               echo theirside
+               echo "<<<<<<< ours" &&
+               echo ourside &&
+               echo "=======" &&
+               echo theirside &&
                echo ">>>>>>> theirs"
        ) >merged &&
        test_cmp expect fild &&
@@ -593,12 +593,12 @@ test_expect_success 'checkout --conflict=diff3' '
        cat sample >filf &&
        git checkout --conflict=diff3 -- fild file filf &&
        (
-               echo "<<<<<<< ours"
-               echo ourside
-               echo "||||||| base"
-               echo original
-               echo "======="
-               echo theirside
+               echo "<<<<<<< ours" &&
+               echo ourside &&
+               echo "||||||| base" &&
+               echo original &&
+               echo "=======" &&
+               echo theirside &&
                echo ">>>>>>> theirs"
        ) >merged &&
        test_cmp expect fild &&
@@ -673,7 +673,6 @@ test_expect_success 'custom merge driver with checkout -m' '
                do
                        grep $t arm || exit 1
                done
-               exit 0
        ) &&
 
        mv arm expect &&
index 1bf9789c8a3cfbb1cc8a6ea33e4a0b1a0da0a7cd..a07e8b86de2014fd24bcb4692a744f147ef07787 100755 (executable)
@@ -107,7 +107,7 @@ 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) | \
+       test_write_lines f "*" "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -129,7 +129,7 @@ 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) | \
+       test_write_lines f "part3.* *.out" "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -151,7 +151,7 @@ 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) | \
+       test_write_lines f "* !*.out" "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -173,7 +173,7 @@ 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) | \
+       test_write_lines s "*" "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -195,7 +195,7 @@ 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) | \
+       test_write_lines s "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -217,7 +217,7 @@ 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) | \
+       test_write_lines s 3 "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -239,7 +239,7 @@ 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) | \
+       test_write_lines s "2 3" 5 "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -261,7 +261,7 @@ 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) | \
+       test_write_lines s "3,4 5" "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -282,7 +282,7 @@ test_expect_success 'git clean -id (select - filenames)' '
 
        mkdir -p build docs &&
        touch a.out foo.txt bar.txt baz.txt &&
-       (echo s; echo a.out fo ba bar; echo; echo c) | \
+       test_write_lines s "a.out fo ba bar" "" c |
        git clean -id &&
        test -f Makefile &&
        test ! -f a.out &&
@@ -298,7 +298,7 @@ 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) | \
+       test_write_lines s "1,3-4" 2 "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -320,7 +320,7 @@ 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) | \
+       test_write_lines s "4- 1" "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -342,7 +342,7 @@ 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) | \
+       test_write_lines s "*" "-5- 1 -2" "" c |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -364,7 +364,7 @@ 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) | \
+       test_write_lines a Y y no yes bad "" |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -386,7 +386,7 @@ 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") | \
+       test_write_lines a Y no yes "\04" |
        git clean -id &&
        test -f Makefile &&
        test -f README &&
@@ -408,8 +408,8 @@ 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) | \
+       (cd build/ &&
+        test_write_lines f docs "*.h" "" c |
         git clean -id ..) &&
        test -f Makefile &&
        test -f README &&
@@ -431,9 +431,8 @@ 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) | \
+       (cd build/ &&
+        test_write_lines s ../docs/ ../src/part3.c ../src/part4.c "" c |
         git clean -id ..) &&
        test -f Makefile &&
        test -f README &&
@@ -455,8 +454,8 @@ 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) | \
+       (cd build/ &&
+        test_write_lines a Y y no yes bad "" |
         git clean -id ..) &&
        test -f Makefile &&
        test -f README &&
index 812db137b8db2f6f8457ccb5d6ab27e0215dbe1a..2c2c97e144172a54319ac4d988884407f4186243 100755 (executable)
@@ -171,12 +171,13 @@ test_expect_success 'submodule add to .gitignored path with --force' '
 test_expect_success 'submodule add to reconfigure existing submodule with --force' '
        (
                cd addtest-ignore &&
-               git submodule add --force bogus-url submod &&
-               git submodule add -b initial "$submodurl" submod-branch &&
-               test "bogus-url" = "$(git config -f .gitmodules submodule.submod.url)" &&
-               test "bogus-url" = "$(git config submodule.submod.url)" &&
+               bogus_url="$(pwd)/bogus-url" &&
+               git submodule add --force "$bogus_url" submod &&
+               git submodule add --force -b initial "$submodurl" submod-branch &&
+               test "$bogus_url" = "$(git config -f .gitmodules submodule.submod.url)" &&
+               test "$bogus_url" = "$(git config submodule.submod.url)" &&
                # Restore the url
-               git submodule add --force "$submodurl" submod
+               git submodule add --force "$submodurl" submod &&
                test "$submodurl" = "$(git config -f .gitmodules submodule.submod.url)" &&
                test "$submodurl" = "$(git config submodule.submod.url)"
        )
@@ -818,7 +819,7 @@ test_expect_success '../bar/a/b/c works with relative local path - ../foo/bar.gi
                cp pristine-.git-config .git/config &&
                cp pristine-.gitmodules .gitmodules &&
                mkdir -p a/b/c &&
-               (cd a/b/c; git init) &&
+               (cd a/b/c && git init) &&
                git config remote.origin.url ../foo/bar.git &&
                git submodule add ../bar/a/b/c ./a/b/c &&
                git submodule init &&
@@ -993,6 +994,11 @@ test_expect_success 'submodule deinit should remove the whole submodule section
        rmdir init
 '
 
+test_expect_success 'submodule deinit should unset core.worktree' '
+       test_path_is_file .git/modules/example/config &&
+       test_must_fail git config -f .git/modules/example/config core.worktree
+'
+
 test_expect_success 'submodule deinit from subdirectory' '
        git submodule update --init &&
        git config submodule.example.foo bar &&
index 9e0d31700e0cf8228e0093f3166ec830a5816ba4..f604ef7a7294b893b4e342d086ac67c760582177 100755 (executable)
@@ -865,9 +865,9 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re
         (cd submodule/subsubmodule &&
          git log > ../../expected
         ) &&
-        (cd .git/modules/submodule/modules/subsubmodule
+        (cd .git/modules/submodule/modules/subsubmodule &&
          git log > ../../../../../actual
-        )
+        ) &&
         test_cmp actual expected
        )
 '
@@ -886,7 +886,7 @@ test_expect_success 'submodule update properly revives a moved submodule' '
         git commit -am "pre move" &&
         H2=$(git rev-parse --short HEAD) &&
         git status | sed "s/$H/XXX/" >expect &&
-        H=$(cd submodule2; git rev-parse HEAD) &&
+        H=$(cd submodule2 && git rev-parse HEAD) &&
         git rm --cached submodule2 &&
         rm -rf submodule2 &&
         mkdir -p "moved/sub module" &&
index 08d9add05e0868980f1b59412997e4598e5f9576..34ac28c056bbe8c80178082c770f392bff333fa7 100755 (executable)
@@ -148,7 +148,7 @@ test_expect_success 'preparing second superproject with a nested submodule plus
                cd supersuper &&
                echo "I am super super." >file &&
                git add file &&
-               git commit -m B-super-super-initial
+               git commit -m B-super-super-initial &&
                git submodule add "file://$base_dir/super" subwithsub &&
                git commit -m B-super-super-added &&
                git submodule update --init --recursive &&
index b68c5f5e8510ddbf4e10f6cb608d9e789bef09b4..293e2e1963962e49afa10882439b5106607439a1 100755 (executable)
@@ -176,4 +176,19 @@ test_expect_success 'fsck detects non-blob .gitmodules' '
        )
 '
 
+test_expect_success 'fsck detects corrupt .gitmodules' '
+       git init corrupt &&
+       (
+               cd corrupt &&
+
+               echo "[broken" >.gitmodules &&
+               git add .gitmodules &&
+               git commit -m "broken gitmodules" &&
+
+               git fsck 2>output &&
+               grep gitmodulesParse output &&
+               test_i18ngrep ! "bad config" output
+       )
+'
+
 test_done
index 9dbbd01fc07724e378a864e3fe735269665a9bc3..51646d80197486a19475a144ae8e64b2151b511c 100755 (executable)
@@ -47,7 +47,7 @@ test_expect_success 'paths and -a do not mix' '
 test_expect_success PERL 'can use paths with --interactive' '
        echo bong-o-bong >file &&
        # 2: update, 1:st path, that is all, 7: quit
-       ( echo 2; echo 1; echo; echo 7 ) |
+       test_write_lines 2 1 "" 7 |
        git commit -m foo --interactive file &&
        git reset --hard HEAD^
 '
@@ -293,7 +293,7 @@ test_expect_success PERL 'interactive add' '
 test_expect_success PERL "commit --interactive doesn't change index if editor aborts" '
        echo zoo >file &&
        test_must_fail git diff --exit-code >diff1 &&
-       (echo u ; echo "*" ; echo q) |
+       test_write_lines u "*" q |
        (
                EDITOR=: &&
                export EDITOR &&
@@ -411,8 +411,8 @@ test_expect_success 'sign off (1)' '
        git commit -s -m "thank you" &&
        git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
        (
-               echo thank you
-               echo
+               echo thank you &&
+               echo &&
                git var GIT_COMMITTER_IDENT |
                sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
        ) >expected &&
@@ -430,9 +430,9 @@ test_expect_success 'sign off (2)' '
 $existing" &&
        git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
        (
-               echo thank you
-               echo
-               echo $existing
+               echo thank you &&
+               echo &&
+               echo $existing &&
                git var GIT_COMMITTER_IDENT |
                sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
        ) >expected &&
@@ -450,9 +450,9 @@ test_expect_success 'signoff gap' '
 $alt" &&
        git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
        (
-               echo welcome
-               echo
-               echo $alt
+               echo welcome &&
+               echo &&
+               echo $alt &&
                git var GIT_COMMITTER_IDENT |
                sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
        ) >expected &&
@@ -470,11 +470,11 @@ We have now
 $alt" &&
        git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
        (
-               echo welcome
-               echo
-               echo We have now
-               echo $alt
-               echo
+               echo welcome &&
+               echo &&
+               echo We have now &&
+               echo $alt &&
+               echo &&
                git var GIT_COMMITTER_IDENT |
                sed -e "s/>.*/>/" -e "s/^/Signed-off-by: /"
        ) >expected &&
@@ -491,11 +491,11 @@ non-trailer line
 Myfooter: x" &&
        git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
        (
-               echo subject
-               echo
-               echo non-trailer line
-               echo Myfooter: x
-               echo
+               echo subject &&
+               echo &&
+               echo non-trailer line &&
+               echo Myfooter: x &&
+               echo &&
                echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
        ) >expected &&
        test_cmp expected actual &&
@@ -508,10 +508,10 @@ non-trailer line
 Myfooter: x" &&
        git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
        (
-               echo subject
-               echo
-               echo non-trailer line
-               echo Myfooter: x
+               echo subject &&
+               echo &&
+               echo non-trailer line &&
+               echo Myfooter: x &&
                echo "Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
        ) >expected &&
        test_cmp expected actual
@@ -524,10 +524,10 @@ test_expect_success 'multiple -m' '
        git commit -m "one" -m "two" -m "three" &&
        git cat-file commit HEAD | sed -e "1,/^\$/d" >actual &&
        (
-               echo one
-               echo
-               echo two
-               echo
+               echo one &&
+               echo &&
+               echo two &&
+               echo &&
                echo three
        ) >expected &&
        test_cmp expected actual
index b4b74dbe29dd53ae8029cec20be4b6fef2cd6a1e..943708fb04a114bc020d33cbf21b4e4e62e805bd 100755 (executable)
@@ -193,9 +193,9 @@ test_expect_success 'status with added and untracked file in modified submodule
 
 test_expect_success 'setup .git file for sub' '
        (cd sub &&
-        rm -f new-file
+        rm -f new-file &&
         REAL="$(pwd)/../.real" &&
-        mv .git "$REAL"
+        mv .git "$REAL" &&
         echo "gitdir: $REAL" >.git) &&
         echo .real >>.gitignore &&
         git commit -m "added .real to .gitignore" .gitignore
@@ -209,12 +209,12 @@ test_expect_success 'status with added file in modified submodule with .git file
 
 test_expect_success 'status with a lot of untracked files in the submodule' '
        (
-               cd sub
+               cd sub &&
                i=0 &&
                while test $i -lt 1024
                do
-                       >some-file-$i
-                       i=$(( $i + 1 ))
+                       >some-file-$i &&
+                       i=$(( $i + 1 )) || exit 1
                done
        ) &&
        git status --porcelain sub 2>err.actual &&
index 1a430b9c40d477dd7a399c165585444818f471cf..047156e9d51141a97ad71687c6da036b5ce76548 100755 (executable)
@@ -57,18 +57,18 @@ test_expect_success 'setup' '
 
        git checkout -b delete-base branch1 &&
        mkdir -p a/a &&
-       (echo one; echo two; echo 3; echo 4) >a/a/file.txt &&
+       test_write_lines one two 3 4 >a/a/file.txt &&
        git add a/a/file.txt &&
        git commit -m"base file" &&
        git checkout -b move-to-b delete-base &&
        mkdir -p b/b &&
        git mv a/a/file.txt b/b/file.txt &&
-       (echo one; echo two; echo 4) >b/b/file.txt &&
+       test_write_lines one two 4 >b/b/file.txt &&
        git commit -a -m"move to b" &&
        git checkout -b move-to-c delete-base &&
        mkdir -p c/c &&
        git mv a/a/file.txt c/c/file.txt &&
-       (echo one; echo two; echo 3) >c/c/file.txt &&
+       test_write_lines one two 3 >c/c/file.txt &&
        git commit -a -m"move to c" &&
 
        git checkout -b stash1 master &&
@@ -349,7 +349,7 @@ test_expect_success 'mergetool keeps tempfiles when aborting delete/delete' '
        git checkout -b test$test_count move-to-c &&
        test_config mergetool.keepTemporaries true &&
        test_must_fail git merge move-to-b &&
-       ! (echo a; echo n) | git mergetool a/a/file.txt &&
+       ! test_write_lines a n | git mergetool a/a/file.txt &&
        test -d a/a &&
        cat >expect <<-\EOF &&
        file_BASE_.txt
index 1797f632a388d16f23a47c7693c7a3f0f0deabf6..90bba4fcefd5f32d9200188cef77913b5d5fd40f 100755 (executable)
@@ -99,6 +99,101 @@ do
                test_cmp expected actual
        '
 
+       test_expect_success "grep -w $L (with --column)" '
+               {
+                       echo ${HC}file:5:foo mmap bar
+                       echo ${HC}file:14:foo_mmap bar mmap
+                       echo ${HC}file:5:foo mmap bar_mmap
+                       echo ${HC}file:14:foo_mmap bar mmap baz
+               } >expected &&
+               git grep --column -w -e mmap $H >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep -w $L (with --column, extended OR)" '
+               {
+                       echo ${HC}file:14:foo_mmap bar mmap
+                       echo ${HC}file:19:foo_mmap bar mmap baz
+               } >expected &&
+               git grep --column -w -e mmap$ --or -e baz $H >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep -w $L (with --column, --invert)" '
+               {
+                       echo ${HC}file:1:foo mmap bar
+                       echo ${HC}file:1:foo_mmap bar
+                       echo ${HC}file:1:foo_mmap bar mmap
+                       echo ${HC}file:1:foo mmap bar_mmap
+               } >expected &&
+               git grep --column --invert -w -e baz $H -- file >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep $L (with --column, --invert, extended OR)" '
+               {
+                       echo ${HC}hello_world:6:HeLLo_world
+               } >expected &&
+               git grep --column --invert -e ll --or --not -e _ $H -- hello_world \
+                       >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep $L (with --column, --invert, extended AND)" '
+               {
+                       echo ${HC}hello_world:3:Hello world
+                       echo ${HC}hello_world:3:Hello_world
+                       echo ${HC}hello_world:6:HeLLo_world
+               } >expected &&
+               git grep --column --invert --not -e _ --and --not -e ll $H -- hello_world \
+                       >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep $L (with --column, double-negation)" '
+               {
+                       echo ${HC}file:1:foo_mmap bar mmap baz
+               } >expected &&
+               git grep --column --not \( --not -e foo --or --not -e baz \) $H -- file \
+                       >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep -w $L (with --column, -C)" '
+               {
+                       echo ${HC}file:5:foo mmap bar
+                       echo ${HC}file-foo_mmap bar
+                       echo ${HC}file:14:foo_mmap bar mmap
+                       echo ${HC}file:5:foo mmap bar_mmap
+                       echo ${HC}file:14:foo_mmap bar mmap baz
+               } >expected &&
+               git grep --column -w -C1 -e mmap $H >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep -w $L (with --line-number, --column)" '
+               {
+                       echo ${HC}file:1:5:foo mmap bar
+                       echo ${HC}file:3:14:foo_mmap bar mmap
+                       echo ${HC}file:4:5:foo mmap bar_mmap
+                       echo ${HC}file:5:14:foo_mmap bar mmap baz
+               } >expected &&
+               git grep -n --column -w -e mmap $H >actual &&
+               test_cmp expected actual
+       '
+
+       test_expect_success "grep -w $L (with non-extended patterns, --column)" '
+               {
+                       echo ${HC}file:5:foo mmap bar
+                       echo ${HC}file:10:foo_mmap bar
+                       echo ${HC}file:10:foo_mmap bar mmap
+                       echo ${HC}file:5:foo mmap bar_mmap
+                       echo ${HC}file:10:foo_mmap bar mmap baz
+               } >expected &&
+               git grep --column -w -e bar -e mmap $H >actual &&
+               test_cmp expected actual
+       '
+
        test_expect_success "grep -w $L" '
                {
                        echo ${HC}file:1:foo mmap bar
@@ -845,10 +940,9 @@ test_expect_success 'grep from a subdirectory to search wider area (1)' '
 test_expect_success 'grep from a subdirectory to search wider area (2)' '
        mkdir -p s &&
        (
-               cd s || exit 1
-               ( git grep xxyyzz .. >out ; echo $? >status )
-               ! test -s out &&
-               test 1 = $(cat status)
+               cd s &&
+               test_expect_code 1 git grep xxyyzz .. >out &&
+               ! test -s out
        )
 '
 
index 661f9d430d28834b8a006fa0bcc958f52c95258b..c92a47b6d5b11ab537ce9892ca21feddc19db7d9 100755 (executable)
@@ -216,14 +216,18 @@ test_expect_success 'blame -L with invalid start' '
 '
 
 test_expect_success 'blame -L with invalid end' '
-       test_must_fail git blame -L1,5 tres 2>errors &&
-       test_i18ngrep "has only 2 lines" errors
+       git blame -L1,5 tres >out &&
+       test_line_count = 2 out
 '
 
 test_expect_success 'blame parses <end> part of -L' '
        git blame -L1,1 tres >out &&
-       cat out &&
-       test $(wc -l < out) -eq 1
+       test_line_count = 1 out
+'
+
+test_expect_success 'blame -Ln,-(n+1)' '
+       git blame -L3,-4 nine_lines >out &&
+       test_line_count = 3 out
 '
 
 test_expect_success 'indent of line numbers, nine lines' '
index e80eacbb1b81a319232c0b5f28aab3c82d795a93..b8e919e25d6b4def87a6d66db32b94e8813b1cd7 100755 (executable)
@@ -225,6 +225,8 @@ X-Mailer: X-MAILER-STRING
 In-Reply-To: <unique-message-id@example.com>
 References: <unique-message-id@example.com>
 Reply-To: Reply <reply@example.com>
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -330,7 +332,7 @@ test_expect_success $PREREQ 'Show all headers' '
 
 test_expect_success $PREREQ 'Prompting works' '
        clean_fake_sendmail &&
-       (echo "to@example.com"
+       (echo "to@example.com" &&
         echo ""
        ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
@@ -415,6 +417,7 @@ test_expect_success $PREREQ 'reject long lines' '
                --from="Example <nobody@example.com>" \
                --to=nobody@example.com \
                --smtp-server="$(pwd)/fake.sendmail" \
+               --transfer-encoding=8bit \
                $patches longline.patch \
                2>errors &&
        grep longline.patch errors
@@ -456,6 +459,42 @@ test_expect_success $PREREQ 'allow long lines with --no-validate' '
                2>errors
 '
 
+test_expect_success $PREREQ 'short lines with auto encoding are 8bit' '
+       clean_fake_sendmail &&
+       git send-email \
+               --from="A <author@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               --transfer-encoding=auto \
+               $patches &&
+       grep "Content-Transfer-Encoding: 8bit" msgtxt1
+'
+
+test_expect_success $PREREQ 'long lines with auto encoding are quoted-printable' '
+       clean_fake_sendmail &&
+       git send-email \
+               --from="Example <nobody@example.com>" \
+               --to=nobody@example.com \
+               --smtp-server="$(pwd)/fake.sendmail" \
+               --transfer-encoding=auto \
+               --no-validate \
+               longline.patch &&
+       grep "Content-Transfer-Encoding: quoted-printable" msgtxt1
+'
+
+for enc in auto quoted-printable base64
+do
+       test_expect_success $PREREQ "--validate passes with encoding $enc" '
+               git send-email \
+                       --from="Example <nobody@example.com>" \
+                       --to=nobody@example.com \
+                       --smtp-server="$(pwd)/fake.sendmail" \
+                       --transfer-encoding=$enc \
+                       --validate \
+                       $patches longline.patch
+       '
+done
+
 test_expect_success $PREREQ 'Invalid In-Reply-To' '
        clean_fake_sendmail &&
        git send-email \
@@ -470,8 +509,8 @@ test_expect_success $PREREQ 'Invalid In-Reply-To' '
 
 test_expect_success $PREREQ 'Valid In-Reply-To when prompting' '
        clean_fake_sendmail &&
-       (echo "From Example <from@example.com>"
-        echo "To Example <to@example.com>"
+       (echo "From Example <from@example.com>" &&
+        echo "To Example <to@example.com>" &&
         echo ""
        ) | GIT_SEND_EMAIL_NOTTY=1 git send-email \
                --smtp-server="$(pwd)/fake.sendmail" \
@@ -573,6 +612,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -617,6 +658,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -652,6 +695,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -678,6 +723,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -712,6 +759,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -743,6 +792,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -774,6 +825,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -809,6 +862,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -837,6 +892,8 @@ Subject: [PATCH 1/1] Second.
 Date: DATE-STRING
 Message-Id: MESSAGE-ID-STRING
 X-Mailer: X-MAILER-STRING
+MIME-Version: 1.0
+Content-Transfer-Encoding: 8bit
 
 Result: OK
 EOF
@@ -1966,11 +2023,11 @@ test_expect_success $PREREQ 'invoke hook' '
 
                # Verify error message when a patch is rejected by the hook
                sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch &&
-               git send-email \
+               test_must_fail git send-email \
                        --from="Example <nobody@example.com>" \
                        --to=nobody@example.com \
                        --smtp-server="$(pwd)/../fake.sendmail" \
-                       ../another.patch 2>err
+                       ../another.patch 2>err &&
                test_i18ngrep "rejected by sendemail-validate hook" err
        )
 '
index c937330a5f3a7f6b5c4d8e406564bd88dd0c889b..9af60788443a9e69f3ffa7eca079a15f5bafc457 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success \
        (
                cd import &&
                echo foo >foo &&
-               ln -s foo foo.link
+               ln -s foo foo.link &&
                mkdir -p dir/a/b/c/d/e &&
                echo "deep dir" >dir/a/b/c/d/e/file &&
                mkdir bar &&
index 07bfb63777c80f7e508e632faa9f9ce1823d90ea..8a5c8dc1aad6d036964776d9e3fe2ecd0a893305 100755 (executable)
@@ -149,7 +149,7 @@ test_expect_success 'test show-ignore' "
                svn_cmd up &&
                svn_cmd propset -R svn:ignore '
 no-such-file*
-' .
+' . &&
                svn_cmd commit -m 'propset svn:ignore'
        ) &&
        git svn show-ignore > show-ignore.got &&
index 88241baee32478c7ead525dece88f1e7c6edfa06..8201c3e808a931d07bc7e135f87bcc0499f65e20 100755 (executable)
@@ -22,8 +22,8 @@ esac
 # same value as "svn info" (i.e. the commit timestamp that touched the
 # path most recently); do not expect that field to match.
 test_cmp_info () {
-       sed -e '/^Text Last Updated:/d' "$1" >tmp.expect
-       sed -e '/^Text Last Updated:/d' "$2" >tmp.actual
+       sed -e '/^Text Last Updated:/d' "$1" >tmp.expect &&
+       sed -e '/^Text Last Updated:/d' "$2" >tmp.actual &&
        test_cmp tmp.expect tmp.actual &&
        rm -f tmp.expect tmp.actual
 }
@@ -59,24 +59,24 @@ test_expect_success 'setup repository and import' '
        '
 
 test_expect_success 'info' "
-       (cd svnwc; svn info) > expected.info &&
-       (cd gitwc; git svn info) > actual.info &&
+       (cd svnwc && svn info) > expected.info &&
+       (cd gitwc && git svn info) > actual.info &&
        test_cmp_info expected.info actual.info
        "
 
 test_expect_success 'info --url' '
-       test "$(cd gitwc; git svn info --url)" = "$quoted_svnrepo"
+       test "$(cd gitwc && git svn info --url)" = "$quoted_svnrepo"
        '
 
 test_expect_success 'info .' "
-       (cd svnwc; svn info .) > expected.info-dot &&
-       (cd gitwc; git svn info .) > actual.info-dot &&
+       (cd svnwc && svn info .) > expected.info-dot &&
+       (cd gitwc && git svn info .) > actual.info-dot &&
        test_cmp_info expected.info-dot actual.info-dot
        "
 
 test_expect_success 'info $(pwd)' '
-       (cd svnwc; svn info "$(pwd)") >expected.info-pwd &&
-       (cd gitwc; git svn info "$(pwd)") >actual.info-pwd &&
+       (cd svnwc && svn info "$(pwd)") >expected.info-pwd &&
+       (cd gitwc && git svn info "$(pwd)") >actual.info-pwd &&
        grep -v ^Path: <expected.info-pwd >expected.info-np &&
        grep -v ^Path: <actual.info-pwd >actual.info-np &&
        test_cmp_info expected.info-np actual.info-np &&
@@ -85,8 +85,8 @@ test_expect_success 'info $(pwd)' '
        '
 
 test_expect_success 'info $(pwd)/../___wc' '
-       (cd svnwc; svn info "$(pwd)/../svnwc") >expected.info-pwd &&
-       (cd gitwc; git svn info "$(pwd)/../gitwc") >actual.info-pwd &&
+       (cd svnwc && svn info "$(pwd)/../svnwc") >expected.info-pwd &&
+       (cd gitwc && git svn info "$(pwd)/../gitwc") >actual.info-pwd &&
        grep -v ^Path: <expected.info-pwd >expected.info-np &&
        grep -v ^Path: <actual.info-pwd >actual.info-np &&
        test_cmp_info expected.info-np actual.info-np &&
@@ -95,8 +95,8 @@ test_expect_success 'info $(pwd)/../___wc' '
        '
 
 test_expect_success 'info $(pwd)/../___wc//file' '
-       (cd svnwc; svn info "$(pwd)/../svnwc//file") >expected.info-pwd &&
-       (cd gitwc; git svn info "$(pwd)/../gitwc//file") >actual.info-pwd &&
+       (cd svnwc && svn info "$(pwd)/../svnwc//file") >expected.info-pwd &&
+       (cd gitwc && git svn info "$(pwd)/../gitwc//file") >actual.info-pwd &&
        grep -v ^Path: <expected.info-pwd >expected.info-np &&
        grep -v ^Path: <actual.info-pwd >actual.info-np &&
        test_cmp_info expected.info-np actual.info-np &&
@@ -105,56 +105,56 @@ test_expect_success 'info $(pwd)/../___wc//file' '
        '
 
 test_expect_success 'info --url .' '
-       test "$(cd gitwc; git svn info --url .)" = "$quoted_svnrepo"
+       test "$(cd gitwc && git svn info --url .)" = "$quoted_svnrepo"
        '
 
 test_expect_success 'info file' "
-       (cd svnwc; svn info file) > expected.info-file &&
-       (cd gitwc; git svn info file) > actual.info-file &&
+       (cd svnwc && svn info file) > expected.info-file &&
+       (cd gitwc && git svn info file) > actual.info-file &&
        test_cmp_info expected.info-file actual.info-file
        "
 
 test_expect_success 'info --url file' '
-       test "$(cd gitwc; git svn info --url file)" = "$quoted_svnrepo/file"
+       test "$(cd gitwc && git svn info --url file)" = "$quoted_svnrepo/file"
        '
 
 test_expect_success 'info directory' "
-       (cd svnwc; svn info directory) > expected.info-directory &&
-       (cd gitwc; git svn info directory) > actual.info-directory &&
+       (cd svnwc && svn info directory) > expected.info-directory &&
+       (cd gitwc && git svn info directory) > actual.info-directory &&
        test_cmp_info expected.info-directory actual.info-directory
        "
 
 test_expect_success 'info inside directory' "
-       (cd svnwc/directory; svn info) > expected.info-inside-directory &&
-       (cd gitwc/directory; git svn info) > actual.info-inside-directory &&
+       (cd svnwc/directory && svn info) > expected.info-inside-directory &&
+       (cd gitwc/directory && git svn info) > actual.info-inside-directory &&
        test_cmp_info expected.info-inside-directory actual.info-inside-directory
        "
 
 test_expect_success 'info --url directory' '
-       test "$(cd gitwc; git svn info --url directory)" = "$quoted_svnrepo/directory"
+       test "$(cd gitwc && git svn info --url directory)" = "$quoted_svnrepo/directory"
        '
 
 test_expect_success 'info symlink-file' "
-       (cd svnwc; svn info symlink-file) > expected.info-symlink-file &&
-       (cd gitwc; git svn info symlink-file) > actual.info-symlink-file &&
+       (cd svnwc && svn info symlink-file) > expected.info-symlink-file &&
+       (cd gitwc && git svn info symlink-file) > actual.info-symlink-file &&
        test_cmp_info expected.info-symlink-file actual.info-symlink-file
        "
 
 test_expect_success 'info --url symlink-file' '
-       test "$(cd gitwc; git svn info --url symlink-file)" \
+       test "$(cd gitwc && git svn info --url symlink-file)" \
             = "$quoted_svnrepo/symlink-file"
        '
 
 test_expect_success 'info symlink-directory' "
-       (cd svnwc; svn info symlink-directory) \
+       (cd svnwc && svn info symlink-directory) \
                > expected.info-symlink-directory &&
-       (cd gitwc; git svn info symlink-directory) \
+       (cd gitwc && git svn info symlink-directory) \
                > actual.info-symlink-directory &&
        test_cmp_info expected.info-symlink-directory actual.info-symlink-directory
        "
 
 test_expect_success 'info --url symlink-directory' '
-       test "$(cd gitwc; git svn info --url symlink-directory)" \
+       test "$(cd gitwc && git svn info --url symlink-directory)" \
             = "$quoted_svnrepo/symlink-directory"
        '
 
@@ -169,13 +169,13 @@ test_expect_success 'info added-file' "
                cd svnwc &&
                svn_cmd add added-file > /dev/null
        ) &&
-       (cd svnwc; svn info added-file) > expected.info-added-file &&
-       (cd gitwc; git svn info added-file) > actual.info-added-file &&
+       (cd svnwc && svn info added-file) > expected.info-added-file &&
+       (cd gitwc && git svn info added-file) > actual.info-added-file &&
        test_cmp_info expected.info-added-file actual.info-added-file
        "
 
 test_expect_success 'info --url added-file' '
-       test "$(cd gitwc; git svn info --url added-file)" \
+       test "$(cd gitwc && git svn info --url added-file)" \
             = "$quoted_svnrepo/added-file"
        '
 
@@ -190,15 +190,15 @@ test_expect_success 'info added-directory' "
                cd gitwc &&
                git add added-directory
        ) &&
-       (cd svnwc; svn info added-directory) \
+       (cd svnwc && svn info added-directory) \
                > expected.info-added-directory &&
-       (cd gitwc; git svn info added-directory) \
+       (cd gitwc && git svn info added-directory) \
                > actual.info-added-directory &&
        test_cmp_info expected.info-added-directory actual.info-added-directory
        "
 
 test_expect_success 'info --url added-directory' '
-       test "$(cd gitwc; git svn info --url added-directory)" \
+       test "$(cd gitwc && git svn info --url added-directory)" \
             = "$quoted_svnrepo/added-directory"
        '
 
@@ -213,16 +213,16 @@ test_expect_success 'info added-symlink-file' "
                ln -s added-file added-symlink-file &&
                svn_cmd add added-symlink-file > /dev/null
        ) &&
-       (cd svnwc; svn info added-symlink-file) \
+       (cd svnwc && svn info added-symlink-file) \
                > expected.info-added-symlink-file &&
-       (cd gitwc; git svn info added-symlink-file) \
+       (cd gitwc && git svn info added-symlink-file) \
                > actual.info-added-symlink-file &&
        test_cmp_info expected.info-added-symlink-file \
                actual.info-added-symlink-file
        "
 
 test_expect_success 'info --url added-symlink-file' '
-       test "$(cd gitwc; git svn info --url added-symlink-file)" \
+       test "$(cd gitwc && git svn info --url added-symlink-file)" \
             = "$quoted_svnrepo/added-symlink-file"
        '
 
@@ -237,16 +237,16 @@ test_expect_success 'info added-symlink-directory' "
                ln -s added-directory added-symlink-directory &&
                svn_cmd add added-symlink-directory > /dev/null
        ) &&
-       (cd svnwc; svn info added-symlink-directory) \
+       (cd svnwc && svn info added-symlink-directory) \
                > expected.info-added-symlink-directory &&
-       (cd gitwc; git svn info added-symlink-directory) \
+       (cd gitwc && git svn info added-symlink-directory) \
                > actual.info-added-symlink-directory &&
        test_cmp_info expected.info-added-symlink-directory \
                actual.info-added-symlink-directory
        "
 
 test_expect_success 'info --url added-symlink-directory' '
-       test "$(cd gitwc; git svn info --url added-symlink-directory)" \
+       test "$(cd gitwc && git svn info --url added-symlink-directory)" \
             = "$quoted_svnrepo/added-symlink-directory"
        '
 
@@ -259,13 +259,13 @@ test_expect_success 'info deleted-file' "
                cd svnwc &&
                svn_cmd rm --force file > /dev/null
        ) &&
-       (cd svnwc; svn info file) >expected.info-deleted-file &&
-       (cd gitwc; git svn info file) >actual.info-deleted-file &&
+       (cd svnwc && svn info file) >expected.info-deleted-file &&
+       (cd gitwc && git svn info file) >actual.info-deleted-file &&
        test_cmp_info expected.info-deleted-file actual.info-deleted-file
        "
 
 test_expect_success 'info --url file (deleted)' '
-       test "$(cd gitwc; git svn info --url file)" \
+       test "$(cd gitwc && git svn info --url file)" \
             = "$quoted_svnrepo/file"
        '
 
@@ -278,13 +278,13 @@ test_expect_success 'info deleted-directory' "
                cd svnwc &&
                svn_cmd rm --force directory > /dev/null
        ) &&
-       (cd svnwc; svn info directory) >expected.info-deleted-directory &&
-       (cd gitwc; git svn info directory) >actual.info-deleted-directory &&
+       (cd svnwc && svn info directory) >expected.info-deleted-directory &&
+       (cd gitwc && git svn info directory) >actual.info-deleted-directory &&
        test_cmp_info expected.info-deleted-directory actual.info-deleted-directory
        "
 
 test_expect_success 'info --url directory (deleted)' '
-       test "$(cd gitwc; git svn info --url directory)" \
+       test "$(cd gitwc && git svn info --url directory)" \
             = "$quoted_svnrepo/directory"
        '
 
@@ -297,13 +297,13 @@ test_expect_success 'info deleted-symlink-file' "
                cd svnwc &&
                svn_cmd rm --force symlink-file > /dev/null
        ) &&
-       (cd svnwc; svn info symlink-file) >expected.info-deleted-symlink-file &&
-       (cd gitwc; git svn info symlink-file) >actual.info-deleted-symlink-file &&
+       (cd svnwc && svn info symlink-file) >expected.info-deleted-symlink-file &&
+       (cd gitwc && git svn info symlink-file) >actual.info-deleted-symlink-file &&
        test_cmp_info expected.info-deleted-symlink-file actual.info-deleted-symlink-file
        "
 
 test_expect_success 'info --url symlink-file (deleted)' '
-       test "$(cd gitwc; git svn info --url symlink-file)" \
+       test "$(cd gitwc && git svn info --url symlink-file)" \
             = "$quoted_svnrepo/symlink-file"
        '
 
@@ -316,13 +316,13 @@ test_expect_success 'info deleted-symlink-directory' "
                cd svnwc &&
                svn_cmd rm --force symlink-directory > /dev/null
        ) &&
-       (cd svnwc; svn info symlink-directory) >expected.info-deleted-symlink-directory &&
-       (cd gitwc; git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
+       (cd svnwc && svn info symlink-directory) >expected.info-deleted-symlink-directory &&
+       (cd gitwc && git svn info symlink-directory) >actual.info-deleted-symlink-directory &&
        test_cmp_info expected.info-deleted-symlink-directory actual.info-deleted-symlink-directory
        "
 
 test_expect_success 'info --url symlink-directory (deleted)' '
-       test "$(cd gitwc; git svn info --url symlink-directory)" \
+       test "$(cd gitwc && git svn info --url symlink-directory)" \
             = "$quoted_svnrepo/symlink-directory"
        '
 
@@ -331,27 +331,27 @@ test_expect_success 'info --url symlink-directory (deleted)' '
 
 test_expect_success 'info unknown-file' "
        echo two > gitwc/unknown-file &&
-       (cd gitwc; test_must_fail git svn info unknown-file) \
+       (cd gitwc && test_must_fail git svn info unknown-file) \
                 2> actual.info-unknown-file &&
        grep unknown-file actual.info-unknown-file
        "
 
 test_expect_success 'info --url unknown-file' '
        echo two > gitwc/unknown-file &&
-       (cd gitwc; test_must_fail git svn info --url unknown-file) \
+       (cd gitwc && test_must_fail git svn info --url unknown-file) \
                 2> actual.info-url-unknown-file &&
        grep unknown-file actual.info-url-unknown-file
        '
 
 test_expect_success 'info unknown-directory' "
        mkdir gitwc/unknown-directory svnwc/unknown-directory &&
-       (cd gitwc; test_must_fail git svn info unknown-directory) \
+       (cd gitwc && test_must_fail git svn info unknown-directory) \
                 2> actual.info-unknown-directory &&
        grep unknown-directory actual.info-unknown-directory
        "
 
 test_expect_success 'info --url unknown-directory' '
-       (cd gitwc; test_must_fail git svn info --url unknown-directory) \
+       (cd gitwc && test_must_fail git svn info --url unknown-directory) \
                 2> actual.info-url-unknown-directory &&
        grep unknown-directory actual.info-url-unknown-directory
        '
@@ -361,13 +361,13 @@ test_expect_success 'info unknown-symlink-file' "
                cd gitwc &&
                ln -s unknown-file unknown-symlink-file
        ) &&
-       (cd gitwc; test_must_fail git svn info unknown-symlink-file) \
+       (cd gitwc && test_must_fail git svn info unknown-symlink-file) \
                 2> actual.info-unknown-symlink-file &&
        grep unknown-symlink-file actual.info-unknown-symlink-file
        "
 
 test_expect_success 'info --url unknown-symlink-file' '
-       (cd gitwc; test_must_fail git svn info --url unknown-symlink-file) \
+       (cd gitwc && test_must_fail git svn info --url unknown-symlink-file) \
                 2> actual.info-url-unknown-symlink-file &&
        grep unknown-symlink-file actual.info-url-unknown-symlink-file
        '
@@ -377,13 +377,13 @@ test_expect_success 'info unknown-symlink-directory' "
                cd gitwc &&
                ln -s unknown-directory unknown-symlink-directory
        ) &&
-       (cd gitwc; test_must_fail git svn info unknown-symlink-directory) \
+       (cd gitwc && test_must_fail git svn info unknown-symlink-directory) \
                 2> actual.info-unknown-symlink-directory &&
        grep unknown-symlink-directory actual.info-unknown-symlink-directory
        "
 
 test_expect_success 'info --url unknown-symlink-directory' '
-       (cd gitwc; test_must_fail git svn info --url unknown-symlink-directory) \
+       (cd gitwc && test_must_fail git svn info --url unknown-symlink-directory) \
                 2> actual.info-url-unknown-symlink-directory &&
        grep unknown-symlink-directory actual.info-url-unknown-symlink-directory
        '
index 30013b7bb948640869d5c5208b2feeb08c011b73..9e8fe38e7ef94b7343122b279c94c250dcf48756 100755 (executable)
@@ -7,8 +7,8 @@ test_expect_success 'setup svn repository' '
        svn_cmd checkout "$svnrepo" work.svn &&
        (
                cd work.svn &&
-               echo >file
-               svn_cmd add file
+               echo >file &&
+               svn_cmd add file &&
                svn_cmd commit -m "first commit" file
        )
 '
@@ -17,7 +17,7 @@ test_expect_success 'interact with it via git svn' '
        mkdir work.git &&
        (
                cd work.git &&
-               git svn init "$svnrepo"
+               git svn init "$svnrepo" &&
                git svn fetch &&
 
                echo modification >file &&
index 8dbd6476fa181dc7a2b3f0b6a9dfff57155c9eea..2c213ae65405ecc5253a01369ce4b76f96272f10 100755 (executable)
@@ -51,7 +51,7 @@ do
                git add F &&
                git commit -a -F "$TEST_DIRECTORY"/t3900/$H.txt &&
                E=$(git cat-file commit HEAD | sed -ne "s/^encoding //p") &&
-               test "z$E" = "z$H"
+               test "z$E" = "z$H" &&
                compare_git_head_with "$TEST_DIRECTORY"/t3900/$H.txt
        )
        '
index d8262854bbee335aec293b8f75b9979e60b91171..cb764bcadc72cd954d3727f6fbc4d1e0d1fe6c46 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success 'start import with incomplete authors file' '
 
 test_expect_success 'imported 2 revisions successfully' '
        (
-               cd x
+               cd x &&
                git rev-list refs/remotes/git-svn >actual &&
                test_line_count = 2 actual &&
                git rev-list -1 --pretty=raw refs/remotes/git-svn >actual &&
@@ -42,7 +42,7 @@ EOF
 
 test_expect_success 'continues to import once authors have been added' '
        (
-               cd x
+               cd x &&
                git svn fetch --authors-file=../svn-authors &&
                git rev-list refs/remotes/git-svn >actual &&
                test_line_count = 4 actual &&
index 09ff10cd9b0688522671cac9ee2c8886a8587947..fff49c4100852b28899c6694a33acd3b96b188b8 100755 (executable)
@@ -82,7 +82,7 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
 test_expect_success 'SVN-side change inside of ignored www' '
        (
                cd s &&
-               echo zaq >> www/test_www.txt
+               echo zaq >> www/test_www.txt &&
                svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
                svn_cmd up &&
                svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
@@ -114,8 +114,8 @@ test_expect_success 'update git svn-cloned repo (option ignore)' '
 test_expect_success 'SVN-side change in and out of ignored www' '
        (
                cd s &&
-               echo cvf >> www/test_www.txt
-               echo ygg >> qqq/test_qqq.txt
+               echo cvf >> www/test_www.txt &&
+               echo ygg >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change in and out of ignored www" &&
                svn_cmd up &&
                svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
index 5fa07a369ff06f739a9edea2a8924fc3f19a88ca..067b15bad2508cc01614f8b1a01aeab7cac916dc 100755 (executable)
@@ -7,7 +7,7 @@ test_description='git svn dcommit clobber series'
 test_expect_success 'initialize repo' '
        mkdir import &&
        (cd import &&
-       awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file
+       awk "BEGIN { for (i = 1; i < 64; i++) { print i } }" > file &&
        svn_cmd import -m "initial" . "$svnrepo"
        ) &&
        git svn init "$svnrepo" &&
index 93ef44fae8f28149344b9125530807ed49727e68..027b416720ddf774ffd52e9bd3267b009bd70899 100755 (executable)
@@ -38,7 +38,7 @@ test_expect_success 'import authors with prog and file' '
 
 test_expect_success 'imported 6 revisions successfully' '
        (
-               cd x
+               cd x &&
                git rev-list refs/remotes/git-svn >actual &&
                test_line_count = 6 actual
        )
@@ -46,7 +46,7 @@ test_expect_success 'imported 6 revisions successfully' '
 
 test_expect_success 'authors-prog ran correctly' '
        (
-               cd x
+               cd x &&
                git rev-list -1 --pretty=raw refs/remotes/git-svn~1 >actual &&
                grep "^author ee-foo <ee-foo@example\.com> " actual &&
                git rev-list -1 --pretty=raw refs/remotes/git-svn~2 >actual &&
@@ -62,7 +62,7 @@ test_expect_success 'authors-prog ran correctly' '
 
 test_expect_success 'authors-file overrode authors-prog' '
        (
-               cd x
+               cd x &&
                git rev-list -1 --pretty=raw refs/remotes/git-svn >actual &&
                grep "^author FFFFFFF FFFFFFF <fFf@other\.example\.com> " actual
        )
index 6d3130e61856335dff7004903741a490ae94fc08..5f91c0d68b4582958e2ba5e20e888b9369e6a3f4 100755 (executable)
@@ -21,7 +21,7 @@ test_expect_success 'empty directories exist' '
                do
                        if ! test -d "$i"
                        then
-                               echo >&2 "$i does not exist"
+                               echo >&2 "$i does not exist" &&
                                exit 1
                        fi
                done
@@ -38,7 +38,7 @@ test_expect_success 'option automkdirs set to false' '
                do
                        if test -d "$i"
                        then
-                               echo >&2 "$i exists"
+                               echo >&2 "$i exists" &&
                                exit 1
                        fi
                done
@@ -63,7 +63,7 @@ test_expect_success 'git svn mkdirs recreates empty directories' '
                do
                        if ! test -d "$i"
                        then
-                               echo >&2 "$i does not exist"
+                               echo >&2 "$i does not exist" &&
                                exit 1
                        fi
                done
@@ -79,21 +79,21 @@ test_expect_success 'git svn mkdirs -r works' '
                do
                        if ! test -d "$i"
                        then
-                               echo >&2 "$i does not exist"
+                               echo >&2 "$i does not exist" &&
                                exit 1
                        fi
-               done
+               done &&
 
                if test -d "! !"
                then
-                       echo >&2 "$i should not exist"
+                       echo >&2 "$i should not exist" &&
                        exit 1
-               fi
+               fi &&
 
                git svn mkdirs -r8 &&
                if ! test -d "! !"
                then
-                       echo >&2 "$i not exist"
+                       echo >&2 "$i not exist" &&
                        exit 1
                fi
        )
@@ -115,7 +115,7 @@ test_expect_success 'empty directories in trunk exist' '
                do
                        if ! test -d "$i"
                        then
-                               echo >&2 "$i does not exist"
+                               echo >&2 "$i does not exist" &&
                                exit 1
                        fi
                done
@@ -148,7 +148,7 @@ test_expect_success 'git svn gc-ed files work' '
                        do
                                if ! test -d "$i"
                                then
-                                       echo >&2 "$i does not exist"
+                                       echo >&2 "$i does not exist" &&
                                        exit 1
                                fi
                        done
index a90ff58629341ce41e29a9c06ec453c8d066f31a..d292bf9f55cdbedefca4e3e0777dd1f701dea9be 100755 (executable)
@@ -84,7 +84,7 @@ test_expect_success 'update git svn-cloned repo (option include)' '
 test_expect_success 'SVN-side change inside of ignored www' '
        (
                cd s &&
-               echo zaq >> www/test_www.txt
+               echo zaq >> www/test_www.txt &&
                svn_cmd commit -m "SVN-side change inside of www/test_www.txt" &&
                svn_cmd up &&
                svn_cmd log -v | fgrep "SVN-side change inside of www/test_www.txt"
@@ -116,8 +116,8 @@ test_expect_success 'update git svn-cloned repo (option include)' '
 test_expect_success 'SVN-side change in and out of included qqq' '
        (
                cd s &&
-               echo cvf >> www/test_www.txt
-               echo ygg >> qqq/test_qqq.txt
+               echo cvf >> www/test_www.txt &&
+               echo ygg >> qqq/test_qqq.txt &&
                svn_cmd commit -m "SVN-side change in and out of ignored www" &&
                svn_cmd up &&
                svn_cmd log -v | fgrep "SVN-side change in and out of ignored www"
index 301e7797097cbf8eb39d87fc9535166c9fb5de2a..89f285d082965c0b8f0d8cbee226f3c8e2a75cfd 100755 (executable)
@@ -30,7 +30,7 @@ test_expect_success 'git svn mkdirs recreates empty directories after git svn gc
                do
                        if ! test -d "$i"
                        then
-                               echo >&2 "$i does not exist"
+                               echo >&2 "$i does not exist" &&
                                exit 1
                        fi
                done
index d8464d4218316a51aa0db70bc57919d083d647ac..90346ff4e92ac06d6ca9fd8ea7b5b7d4f1f7f374 100755 (executable)
@@ -12,7 +12,7 @@ test_expect_success 'setup svn repository' '
        svn_cmd checkout "$svnrepo" work.svn &&
        (
                cd work.svn &&
-               echo >file && echo > auto_updated_file
+               echo >file && echo > auto_updated_file &&
                svn_cmd add file auto_updated_file &&
                svn_cmd commit -m "initial commit"
        ) &&
index fa3ef3b1f7408840cabc5eacb1b1851f1c8afaac..a4813c2b09ce9f508fb38b463f8bbcd48851e1d2 100755 (executable)
@@ -39,7 +39,7 @@ test_expect_success 'initialize source svn repo' '
                svn_cmd commit -m trunk &&
                svn_cmd switch "$svnrepo"/branches/branch2 &&
                svn_cmd merge "$svnrepo"/trunk &&
-               svn_cmd commit -m "merge trunk"
+               svn_cmd commit -m "merge trunk" &&
                svn_cmd switch "$svnrepo"/trunk &&
                svn_cmd merge --reintegrate "$svnrepo"/branches/branch2 &&
                svn_cmd commit -m "merge branch2"
index 1319415ba8ec04f2b9d34ec984d3f8afb61fb551..cd61288aa1a9476819bd9b19b888e9e1b75f1da5 100755 (executable)
@@ -187,7 +187,7 @@ test_expect_success \
       git commit -a -m "Update with spaces" &&
       id=$(git rev-list --max-count=1 HEAD) &&
       (cd "$CVSWORK" &&
-      git cvsexportcommit -c $id
+      git cvsexportcommit -c $id &&
       check_entries "G g" "with spaces.png/1.2/-kb|with spaces.txt/1.2/"
       )'
 
@@ -245,7 +245,7 @@ test_expect_success FILEMODE \
       git add G/off &&
       git commit -a -m "Execute test" &&
       (cd "$CVSWORK" &&
-      git cvsexportcommit -c HEAD
+      git cvsexportcommit -c HEAD &&
       test -x G/on &&
       ! test -x G/off
       )'
@@ -303,7 +303,7 @@ test_expect_success 're-commit a removed filename which remains in CVS attic' '
     git add attic_gremlin &&
     git commit -m "Added attic_gremlin" &&
        git cvsexportcommit -w "$CVSWORK" -c HEAD &&
-    (cd "$CVSWORK"; cvs -Q update -d) &&
+    (cd "$CVSWORK" && cvs -Q update -d) &&
     test -f "$CVSWORK/attic_gremlin"
 '
 
index a04de14677adee98bef418f17c6421eda17a358f..bb1c39cfcc9626dd1de998c50f9126b4a88ccc06 100755 (executable)
@@ -80,7 +80,7 @@ test_expect_success 'lookups after checkpoint works' '
                do
                        if test $n -gt 30
                        then
-                               echo >&2 "checkpoint did not update branch"
+                               echo >&2 "checkpoint did not update branch" &&
                                exit 1
                        else
                                n=$(($n + 1))
index 06742748e99fda241ec629e34c0700ae4050692e..a5e5dca75341561894d681410aaeec6e365144fc 100755 (executable)
@@ -328,7 +328,7 @@ test_expect_success 'cvs update (subdirectories)' \
   '(for dir in A A/B A/B/C A/D E; do
       mkdir $dir &&
       echo "test file in $dir" >"$dir/file_in_$(echo $dir|sed -e "s#/# #g")"  &&
-      git add $dir;
+      git add $dir
    done) &&
    git commit -q -m "deep sub directory structure" &&
    git push gitcvs.git >/dev/null &&
@@ -371,7 +371,7 @@ test_expect_success 'cvs update (merge)' \
   'echo Line 0 >expected &&
    for i in 1 2 3 4 5 6 7
    do
-     echo Line $i >>merge
+     echo Line $i >>merge &&
      echo Line $i >>expected
    done &&
    echo Line 8 >>expected &&
@@ -382,7 +382,7 @@ test_expect_success 'cvs update (merge)' \
    GIT_CONFIG="$git_config" cvs -Q update &&
    test "$(echo $(grep merge CVS/Entries|cut -d/ -f2,3,5))" = "merge/1.1/" &&
    test_cmp merge ../merge &&
-   ( echo Line 0; cat merge ) >merge.tmp &&
+   ( echo Line 0 && cat merge ) >merge.tmp &&
    mv merge.tmp merge &&
    cd "$WORKDIR" &&
    echo Line 8 >>merge &&
@@ -410,7 +410,7 @@ do
 done
 
 test_expect_success 'cvs update (conflict merge)' \
-  '( echo LINE 0; cat merge ) >merge.tmp &&
+  '( echo LINE 0 && cat merge ) >merge.tmp &&
    mv merge.tmp merge &&
    git add merge &&
    git commit -q -m "Merge test (conflict)" &&
index 804ce3850ff3f7838035fd316e26ee301b525ff0..5dfee07d9add00d4a68e70c89c8711604f218ef1 100755 (executable)
@@ -135,7 +135,7 @@ test_expect_success PERL 'second update has correct .git/cvs-revisions' '
 
        (cd module-git &&
         git log --format="o_fortuna 1.1 %H" -1 HEAD^^ &&
-        git log --format="o_fortuna 1.2 %H" -1 HEAD^
+        git log --format="o_fortuna 1.2 %H" -1 HEAD^ &&
         git log --format="tick 1.1 %H" -1 HEAD) > expected &&
        test_cmp expected module-git/.git/cvs-revisions
 '
index 1ab76c4246f215cfc0082bdbfd090e1622362140..3f5291b85752e7bc78901da18c2bfa654134eb59 100755 (executable)
@@ -134,7 +134,7 @@ test_expect_success 'clone --changesfile' '
        (
                cd "$git" &&
                git log --oneline p4/master >lines &&
-               test_line_count = 2 lines
+               test_line_count = 2 lines &&
                test_path_is_file file1 &&
                test_path_is_missing file2 &&
                test_path_is_file file3
index 8134ab439b2eb91a3a315cab92b12a2b8b3e5a00..cc53debe1955ece317f994b0e76f2b8470b0f4f6 100755 (executable)
@@ -161,7 +161,7 @@ test_expect_success 'cleanup after failure' '
 test_expect_success 'ktext expansion should not expand multi-line $File::' '
        (
                cd "$cli" &&
-               cat >lv.pm <<-\EOF
+               cat >lv.pm <<-\EOF &&
                my $wanted = sub { my $f = $File::Find::name;
                                    if ( -f && $f =~ /foo/ ) {
                EOF
index decb66ba30871573eb9c252e56599d47095f0fff..602b0a5d5ceafcac6439491b463fee7867e7036f 100755 (executable)
@@ -133,7 +133,7 @@ test_expect_success 'export git tags to p4' '
                p4 labels ... | grep LIGHTWEIGHT_TAG &&
                p4 label -o GIT_TAG_1 | grep "tag created in git:xyzzy" &&
                p4 sync ...@GIT_TAG_1 &&
-               ! test -f main/f10
+               ! test -f main/f10 &&
                p4 sync ...@GIT_TAG_2 &&
                test -f main/f10
        )
index e7e0268e985072b6a7d8c9448dc0c3d9a9057da9..60baa06e27a0a6c45f580738e60f7fe8fa65373a 100755 (executable)
@@ -9,23 +9,11 @@ test_expect_success 'start p4d' '
 '
 
 # We rely on this behavior to detect for p4 move availability.
-test_expect_success 'p4 help unknown returns 1' '
+test_expect_success '"p4 help unknown" errors out' '
        (
                cd "$cli" &&
-               (
-                       p4 help client >errs 2>&1
-                       echo $? >retval
-               )
-               echo 0 >expected &&
-               test_cmp expected retval &&
-               rm retval &&
-               (
-                       p4 help nosuchcommand >errs 2>&1
-                       echo $? >retval
-               )
-               echo 1 >expected &&
-               test_cmp expected retval &&
-               rm retval
+               p4 help client &&
+               ! p4 help nosuchcommand
        )
 '
 
index 37b42d03a2b4f6392c3b84303c52a839caa6befd..eaf03a656329c7b4b9d0ed40c839232a81b310ac 100755 (executable)
@@ -394,7 +394,7 @@ test_expect_success 'cleanup rename after submit cancel' '
        (
                cd "$cli" &&
                test_path_is_missing text2 &&
-               p4 fstat -T action text2 2>&1 | grep "no such file"
+               p4 fstat -T action text2 2>&1 | grep "no such file" &&
                test_path_is_file text &&
                ! p4 fstat -T action text
        )
index 3dc528bb1e0b90c17739b2702afcb8903f8cda10..2ad1b0810df81ecaf5197e4cf860b3524d557c09 100755 (executable)
@@ -30,7 +30,7 @@ test_expect_success 'symlinked directory' '
        (
                cd "$cli" &&
                p4 sync &&
-               test -L some/sub/directory/subdir2
+               test -L some/sub/directory/subdir2 &&
                test_path_is_file some/sub/directory/subdir2/file.t
        )
 
index bbcf14c6646cb4cd9c01d009d196d63a4986a96c..be44c9751aefa5368d6f05d7b1c29f0c8354095f 100755 (executable)
@@ -13,7 +13,7 @@ test_expect_success 'init depot' '
                cd "$cli" &&
                echo file1 >file1 &&
                p4 add file1 &&
-               p4 submit -d "change 1"
+               p4 submit -d "change 1" &&
                echo file2 >file2 &&
                p4 add file2 &&
                p4 submit -d "change 2"
index 9ba892de7abff30a81125521cb5e9084cad1b75f..277d34701201af03d0efbcfe05336b6cfec5483b 100755 (executable)
@@ -26,7 +26,9 @@ test_expect_success 'error handling' '
        ) &&
        p4 passwd -P newpassword &&
        (
-               P4PASSWD=badpassword test_must_fail git p4 clone //depot/foo 2>errmsg &&
+               P4PASSWD=badpassword &&
+               export P4PASSWD &&
+               test_must_fail git p4 clone //depot/foo 2>errmsg &&
                grep -q "failure accessing depot.*P4PASSWD" errmsg
        )
 '
index 3b3a7b66e41024b5a9b939f1c33a9996b8874a12..5ff43b9cbbe1693ac23ac7f020522d16424cb936 100755 (executable)
@@ -1103,7 +1103,7 @@ test_expect_success '__git_complete_refs - remote' '
        master-in-other Z
        EOF
        (
-               cur=
+               cur= &&
                __git_complete_refs --remote=other &&
                print_comp
        ) &&
@@ -1122,7 +1122,7 @@ test_expect_success '__git_complete_refs - track' '
        master-in-other Z
        EOF
        (
-               cur=
+               cur= &&
                __git_complete_refs --track &&
                print_comp
        ) &&
index c3b89ae783d0b5511718be6d6b9d9978dd9131cd..04440685a66d6baff98c1739979c8f866255572b 100755 (executable)
@@ -529,7 +529,7 @@ test_expect_success 'prompt - bash color pc mode - branch name' '
        printf "BEFORE: (${c_green}\${__git_ps1_branch_name}${c_clear}):AFTER\\nmaster" >expected &&
        (
                GIT_PS1_SHOWCOLORHINTS=y &&
-               __git_ps1 "BEFORE:" ":AFTER" >"$actual"
+               __git_ps1 "BEFORE:" ":AFTER" >"$actual" &&
                printf "%s\\n%s" "$PS1" "${__git_ps1_branch_name}" >"$actual"
        ) &&
        test_cmp expected "$actual"
index 1f8ff7e9424e5d780578a6ab00623f814d8f836a..8b5abca29f9492c03c234170ae1c66ba9f0970bb 100644 (file)
@@ -651,14 +651,16 @@ static int connect_helper(struct transport *transport, const char *name,
 }
 
 static int fetch(struct transport *transport,
-                int nr_heads, struct ref **to_fetch)
+                int nr_heads, struct ref **to_fetch,
+                struct ref **fetched_refs)
 {
        struct helper_data *data = transport->data;
        int i, count;
 
        if (process_connect(transport, 0)) {
                do_take_over(transport);
-               return transport->vtable->fetch(transport, nr_heads, to_fetch);
+               return transport->vtable->fetch(transport, nr_heads, to_fetch,
+                                               fetched_refs);
        }
 
        count = 0;
index 1cde6258a73bcf8582b0746d1c44a23b30115dc9..eeb6c340e51eab341b7f089bf2b425697759f3cd 100644 (file)
@@ -36,11 +36,18 @@ struct transport_vtable {
         * Fetch the objects for the given refs. Note that this gets
         * an array, and should ignore the list structure.
         *
+        * The transport *may* provide, in fetched_refs, the list of refs that
+        * it fetched.  If the transport knows anything about the fetched refs
+        * that the caller does not know (for example, shallow status), it
+        * should provide that list of refs and include that information in the
+        * list.
+        *
         * If the transport did not get hashes for refs in
         * get_refs_list(), it should set the old_sha1 fields in the
         * provided refs now.
         **/
-       int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs);
+       int (*fetch)(struct transport *transport, int refs_nr, struct ref **refs,
+                    struct ref **fetched_refs);
 
        /**
         * Push the objects and refs. Send the necessary objects, and
index a32da30dee6f4e38433b68d7c9f7e9404aa58858..fdd813f684f766fe22169537468402677b0eaa71 100644 (file)
@@ -151,7 +151,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
 }
 
 static int fetch_refs_from_bundle(struct transport *transport,
-                              int nr_heads, struct ref **to_fetch)
+                              int nr_heads, struct ref **to_fetch,
+                              struct ref **fetched_refs)
 {
        struct bundle_transport_data *data = transport->data;
        return unbundle(&data->header, data->fd,
@@ -287,7 +288,8 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
 }
 
 static int fetch_refs_via_pack(struct transport *transport,
-                              int nr_heads, struct ref **to_fetch)
+                              int nr_heads, struct ref **to_fetch,
+                              struct ref **fetched_refs)
 {
        int ret = 0;
        struct git_transport_data *data = transport->data;
@@ -348,14 +350,19 @@ static int fetch_refs_via_pack(struct transport *transport,
        data->got_remote_heads = 0;
        data->options.self_contained_and_connected =
                args.self_contained_and_connected;
+       data->options.connectivity_checked = args.connectivity_checked;
 
        if (refs == NULL)
                ret = -1;
        if (report_unmatched_refs(to_fetch, nr_heads))
                ret = -1;
 
+       if (fetched_refs)
+               *fetched_refs = refs;
+       else
+               free_refs(refs);
+
        free_refs(refs_tmp);
-       free_refs(refs);
        free(dest);
        return ret;
 }
@@ -1215,19 +1222,31 @@ const struct ref *transport_get_remote_refs(struct transport *transport,
        return transport->remote_refs;
 }
 
-int transport_fetch_refs(struct transport *transport, struct ref *refs)
+int transport_fetch_refs(struct transport *transport, struct ref *refs,
+                        struct ref **fetched_refs)
 {
        int rc;
        int nr_heads = 0, nr_alloc = 0, nr_refs = 0;
        struct ref **heads = NULL;
+       struct ref *nop_head = NULL, **nop_tail = &nop_head;
        struct ref *rm;
 
        for (rm = refs; rm; rm = rm->next) {
                nr_refs++;
                if (rm->peer_ref &&
                    !is_null_oid(&rm->old_oid) &&
-                   !oidcmp(&rm->peer_ref->old_oid, &rm->old_oid))
+                   !oidcmp(&rm->peer_ref->old_oid, &rm->old_oid)) {
+                       /*
+                        * These need to be reported as fetched, but we don't
+                        * actually need to fetch them.
+                        */
+                       if (fetched_refs) {
+                               struct ref *nop_ref = copy_ref(rm);
+                               *nop_tail = nop_ref;
+                               nop_tail = &nop_ref->next;
+                       }
                        continue;
+               }
                ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
                heads[nr_heads++] = rm;
        }
@@ -1245,7 +1264,11 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
                        heads[nr_heads++] = rm;
        }
 
-       rc = transport->vtable->fetch(transport, nr_heads, heads);
+       rc = transport->vtable->fetch(transport, nr_heads, heads, fetched_refs);
+       if (fetched_refs && nop_head) {
+               *nop_tail = *fetched_refs;
+               *fetched_refs = nop_head;
+       }
 
        free(heads);
        return rc;
index 7792b08582c132e18b495a21b700d9113727c7eb..7a9a7fcaf3ebf4afcd4d181d54de7e0791451fc7 100644 (file)
@@ -18,6 +18,17 @@ struct git_transport_options {
        unsigned deepen_relative : 1;
        unsigned from_promisor : 1;
        unsigned no_dependents : 1;
+
+       /*
+        * If this transport supports connect or stateless-connect,
+        * the corresponding field in struct fetch_pack_args is copied
+        * here after fetching.
+        *
+        * See the definition of connectivity_checked in struct
+        * fetch_pack_args for more information.
+        */
+       unsigned connectivity_checked:1;
+
        int depth;
        const char *deepen_since;
        const struct string_list *deepen_not;
@@ -218,7 +229,8 @@ int transport_push(struct transport *connection,
 const struct ref *transport_get_remote_refs(struct transport *transport,
                                            const struct argv_array *ref_prefixes);
 
-int transport_fetch_refs(struct transport *transport, struct ref *refs);
+int transport_fetch_refs(struct transport *transport, struct ref *refs,
+                        struct ref **fetched_refs);
 void transport_unlock_pack(struct transport *transport);
 int transport_disconnect(struct transport *transport);
 char *transport_anonymize_url(const char *url);
index ecd6e53b8541408add0de076b01932a1ffd631e9..77b37f36fa1bc8fae48231d35514b88d51902488 100644 (file)
@@ -27,8 +27,9 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l
 {
        const char *path;
        unsigned int mode, len;
+       const unsigned hashsz = the_hash_algo->rawsz;
 
-       if (size < 23 || buf[size - 21]) {
+       if (size < hashsz + 3 || buf[size - (hashsz + 1)]) {
                strbuf_addstr(err, _("too-short tree object"));
                return -1;
        }
index 6dee2c77cebdd93dbc357ab3427175ec291c2310..7c643760f81abb9efd1208e9a6e885e5d6ffd4ea 100644 (file)
@@ -20,12 +20,13 @@ static const struct interval zero_width[] = {
 { 0x0730, 0x074A },
 { 0x07A6, 0x07B0 },
 { 0x07EB, 0x07F3 },
+{ 0x07FD, 0x07FD },
 { 0x0816, 0x0819 },
 { 0x081B, 0x0823 },
 { 0x0825, 0x0827 },
 { 0x0829, 0x082D },
 { 0x0859, 0x085B },
-{ 0x08D4, 0x0902 },
+{ 0x08D3, 0x0902 },
 { 0x093A, 0x093A },
 { 0x093C, 0x093C },
 { 0x0941, 0x0948 },
@@ -37,6 +38,7 @@ static const struct interval zero_width[] = {
 { 0x09C1, 0x09C4 },
 { 0x09CD, 0x09CD },
 { 0x09E2, 0x09E3 },
+{ 0x09FE, 0x09FE },
 { 0x0A01, 0x0A02 },
 { 0x0A3C, 0x0A3C },
 { 0x0A41, 0x0A42 },
@@ -63,6 +65,7 @@ static const struct interval zero_width[] = {
 { 0x0BC0, 0x0BC0 },
 { 0x0BCD, 0x0BCD },
 { 0x0C00, 0x0C00 },
+{ 0x0C04, 0x0C04 },
 { 0x0C3E, 0x0C40 },
 { 0x0C46, 0x0C48 },
 { 0x0C4A, 0x0C4D },
@@ -182,6 +185,7 @@ static const struct interval zero_width[] = {
 { 0xA825, 0xA826 },
 { 0xA8C4, 0xA8C5 },
 { 0xA8E0, 0xA8F1 },
+{ 0xA8FF, 0xA8FF },
 { 0xA926, 0xA92D },
 { 0xA947, 0xA951 },
 { 0xA980, 0xA982 },
@@ -219,19 +223,22 @@ static const struct interval zero_width[] = {
 { 0x10A38, 0x10A3A },
 { 0x10A3F, 0x10A3F },
 { 0x10AE5, 0x10AE6 },
+{ 0x10D24, 0x10D27 },
+{ 0x10F46, 0x10F50 },
 { 0x11001, 0x11001 },
 { 0x11038, 0x11046 },
 { 0x1107F, 0x11081 },
 { 0x110B3, 0x110B6 },
 { 0x110B9, 0x110BA },
 { 0x110BD, 0x110BD },
+{ 0x110CD, 0x110CD },
 { 0x11100, 0x11102 },
 { 0x11127, 0x1112B },
 { 0x1112D, 0x11134 },
 { 0x11173, 0x11173 },
 { 0x11180, 0x11181 },
 { 0x111B6, 0x111BE },
-{ 0x111CA, 0x111CC },
+{ 0x111C9, 0x111CC },
 { 0x1122F, 0x11231 },
 { 0x11234, 0x11234 },
 { 0x11236, 0x11237 },
@@ -239,13 +246,14 @@ static const struct interval zero_width[] = {
 { 0x112DF, 0x112DF },
 { 0x112E3, 0x112EA },
 { 0x11300, 0x11301 },
-{ 0x1133C, 0x1133C },
+{ 0x1133B, 0x1133C },
 { 0x11340, 0x11340 },
 { 0x11366, 0x1136C },
 { 0x11370, 0x11374 },
 { 0x11438, 0x1143F },
 { 0x11442, 0x11444 },
 { 0x11446, 0x11446 },
+{ 0x1145E, 0x1145E },
 { 0x114B3, 0x114B8 },
 { 0x114BA, 0x114BA },
 { 0x114BF, 0x114C0 },
@@ -264,8 +272,9 @@ static const struct interval zero_width[] = {
 { 0x1171D, 0x1171F },
 { 0x11722, 0x11725 },
 { 0x11727, 0x1172B },
-{ 0x11A01, 0x11A06 },
-{ 0x11A09, 0x11A0A },
+{ 0x1182F, 0x11837 },
+{ 0x11839, 0x1183A },
+{ 0x11A01, 0x11A0A },
 { 0x11A33, 0x11A38 },
 { 0x11A3B, 0x11A3E },
 { 0x11A47, 0x11A47 },
@@ -285,6 +294,10 @@ static const struct interval zero_width[] = {
 { 0x11D3C, 0x11D3D },
 { 0x11D3F, 0x11D45 },
 { 0x11D47, 0x11D47 },
+{ 0x11D90, 0x11D91 },
+{ 0x11D95, 0x11D95 },
+{ 0x11D97, 0x11D97 },
+{ 0x11EF3, 0x11EF4 },
 { 0x16AF0, 0x16AF4 },
 { 0x16B30, 0x16B36 },
 { 0x16F8F, 0x16F92 },
@@ -355,7 +368,7 @@ static const struct interval double_width[] = {
 { 0x3000, 0x303E },
 { 0x3041, 0x3096 },
 { 0x3099, 0x30FF },
-{ 0x3105, 0x312E },
+{ 0x3105, 0x312F },
 { 0x3131, 0x318E },
 { 0x3190, 0x31BA },
 { 0x31C0, 0x31E3 },
@@ -375,7 +388,7 @@ static const struct interval double_width[] = {
 { 0xFF01, 0xFF60 },
 { 0xFFE0, 0xFFE6 },
 { 0x16FE0, 0x16FE1 },
-{ 0x17000, 0x187EC },
+{ 0x17000, 0x187F1 },
 { 0x18800, 0x18AF2 },
 { 0x1B000, 0x1B11E },
 { 0x1B170, 0x1B2FB },
@@ -410,13 +423,15 @@ static const struct interval double_width[] = {
 { 0x1F6CC, 0x1F6CC },
 { 0x1F6D0, 0x1F6D2 },
 { 0x1F6EB, 0x1F6EC },
-{ 0x1F6F4, 0x1F6F8 },
+{ 0x1F6F4, 0x1F6F9 },
 { 0x1F910, 0x1F93E },
-{ 0x1F940, 0x1F94C },
-{ 0x1F950, 0x1F96B },
-{ 0x1F980, 0x1F997 },
-{ 0x1F9C0, 0x1F9C0 },
-{ 0x1F9D0, 0x1F9E6 },
+{ 0x1F940, 0x1F970 },
+{ 0x1F973, 0x1F976 },
+{ 0x1F97A, 0x1F97A },
+{ 0x1F97C, 0x1F9A2 },
+{ 0x1F9B0, 0x1F9B9 },
+{ 0x1F9C0, 0x1F9C2 },
+{ 0x1F9D0, 0x1F9FF },
 { 0x20000, 0x2FFFD },
 { 0x30000, 0x3FFFD }
 };
index 66741130aeddaec7a913264fb41ef82d87d9b504..cd0680f11e3580ef24e9140dd00a1e38f02557d8 100644 (file)
@@ -1247,7 +1247,7 @@ static void mark_new_skip_worktree(struct exclude_list *el,
                if (select_flag && !(ce->ce_flags & select_flag))
                        continue;
 
-               if (!ce_stage(ce))
+               if (!ce_stage(ce) && !(ce->ce_flags & CE_CONFLICTED))
                        ce->ce_flags |= skip_wt_flag;
                else
                        ce->ce_flags &= ~skip_wt_flag;
index 4ca052d0b6418404a81a86df9e434c5a9ba77ab2..82b393ec31917c0c2bcd904668a60b5aaef00633 100644 (file)
@@ -66,6 +66,7 @@ static const char *pack_objects_hook;
 
 static int filter_capability_requested;
 static int allow_filter;
+static int allow_ref_in_want;
 static struct list_objects_filter_options filter_options;
 
 static void reset_timeout(void)
@@ -1078,6 +1079,8 @@ static int upload_pack_config(const char *var, const char *value, void *unused)
                        return git_config_string(&pack_objects_hook, var, value);
        } else if (!strcmp("uploadpack.allowfilter", var)) {
                allow_filter = git_config_bool(var, value);
+       } else if (!strcmp("uploadpack.allowrefinwant", var)) {
+               allow_ref_in_want = git_config_bool(var, value);
        }
        return parse_hide_refs_config(var, value, "uploadpack");
 }
@@ -1117,6 +1120,7 @@ void upload_pack(struct upload_pack_options *options)
 
 struct upload_pack_data {
        struct object_array wants;
+       struct string_list wanted_refs;
        struct oid_array haves;
 
        struct object_array shallows;
@@ -1138,12 +1142,14 @@ struct upload_pack_data {
 static void upload_pack_data_init(struct upload_pack_data *data)
 {
        struct object_array wants = OBJECT_ARRAY_INIT;
+       struct string_list wanted_refs = STRING_LIST_INIT_DUP;
        struct oid_array haves = OID_ARRAY_INIT;
        struct object_array shallows = OBJECT_ARRAY_INIT;
        struct string_list deepen_not = STRING_LIST_INIT_DUP;
 
        memset(data, 0, sizeof(*data));
        data->wants = wants;
+       data->wanted_refs = wanted_refs;
        data->haves = haves;
        data->shallows = shallows;
        data->deepen_not = deepen_not;
@@ -1152,6 +1158,7 @@ static void upload_pack_data_init(struct upload_pack_data *data)
 static void upload_pack_data_clear(struct upload_pack_data *data)
 {
        object_array_clear(&data->wants);
+       string_list_clear(&data->wanted_refs, 1);
        oid_array_clear(&data->haves);
        object_array_clear(&data->shallows);
        string_list_clear(&data->deepen_not, 0);
@@ -1188,6 +1195,34 @@ static int parse_want(const char *line)
        return 0;
 }
 
+static int parse_want_ref(const char *line, struct string_list *wanted_refs)
+{
+       const char *arg;
+       if (skip_prefix(line, "want-ref ", &arg)) {
+               struct object_id oid;
+               struct string_list_item *item;
+               struct object *o;
+
+               if (read_ref(arg, &oid)) {
+                       packet_write_fmt(1, "ERR unknown ref %s", arg);
+                       die("unknown ref %s", arg);
+               }
+
+               item = string_list_append(wanted_refs, arg);
+               item->util = oiddup(&oid);
+
+               o = parse_object_or_die(&oid, arg);
+               if (!(o->flags & WANTED)) {
+                       o->flags |= WANTED;
+                       add_object_array(o, NULL, &want_obj);
+               }
+
+               return 1;
+       }
+
+       return 0;
+}
+
 static int parse_have(const char *line, struct oid_array *haves)
 {
        const char *arg;
@@ -1213,6 +1248,8 @@ static void process_args(struct packet_reader *request,
                /* process want */
                if (parse_want(arg))
                        continue;
+               if (allow_ref_in_want && parse_want_ref(arg, &data->wanted_refs))
+                       continue;
                /* process have line */
                if (parse_have(arg, &data->haves))
                        continue;
@@ -1355,6 +1392,24 @@ static int process_haves_and_send_acks(struct upload_pack_data *data)
        return ret;
 }
 
+static void send_wanted_ref_info(struct upload_pack_data *data)
+{
+       const struct string_list_item *item;
+
+       if (!data->wanted_refs.nr)
+               return;
+
+       packet_write_fmt(1, "wanted-refs\n");
+
+       for_each_string_list_item(item, &data->wanted_refs) {
+               packet_write_fmt(1, "%s %s\n",
+                                oid_to_hex(item->util),
+                                item->string);
+       }
+
+       packet_delim(1);
+}
+
 static void send_shallow_info(struct upload_pack_data *data)
 {
        /* No shallow info needs to be sent */
@@ -1422,6 +1477,7 @@ int upload_pack_v2(struct repository *r, struct argv_array *keys,
                                state = FETCH_DONE;
                        break;
                case FETCH_SEND_PACK:
+                       send_wanted_ref_info(&data);
                        send_shallow_info(&data);
 
                        packet_write_fmt(1, "packfile\n");
@@ -1442,12 +1498,22 @@ int upload_pack_advertise(struct repository *r,
 {
        if (value) {
                int allow_filter_value;
+               int allow_ref_in_want;
+
                strbuf_addstr(value, "shallow");
+
                if (!repo_config_get_bool(the_repository,
                                         "uploadpack.allowfilter",
                                         &allow_filter_value) &&
                    allow_filter_value)
                        strbuf_addstr(value, " filter");
+
+               if (!repo_config_get_bool(the_repository,
+                                        "uploadpack.allowrefinwant",
+                                        &allow_ref_in_want) &&
+                   allow_ref_in_want)
+                       strbuf_addstr(value, " ref-in-want");
        }
+
        return 1;
 }
index a69241b25ddaff5b61380aa8b451a6fcb833502c..36af25e7f9f4693b196320f56bb0546d7dd2986a 100644 (file)
@@ -114,7 +114,7 @@ PATTERNS("perl",
         "|<<|<>|<=>|>>"),
 PATTERNS("php",
         "^[\t ]*(((public|protected|private|static)[\t ]+)*function.*)$\n"
-        "^[\t ]*(class.*)$",
+        "^[\t ]*((((final|abstract)[\t ]+)?class|interface|trait).*)$",
         /* -- */
         "[a-zA-Z_][a-zA-Z0-9_]*"
         "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
diff --git a/utf8.c b/utf8.c
index d55e20c6415cb53ef66e1edd0a5e7dc470e18052..982217eec930d7495f61917ce5442016a07c5391 100644 (file)
--- a/utf8.c
+++ b/utf8.c
@@ -566,10 +566,10 @@ static int has_bom_prefix(const char *data, size_t len,
        return data && bom && (len >= bom_len) && !memcmp(data, bom, bom_len);
 }
 
-static const char utf16_be_bom[] = {0xFE, 0xFF};
-static const char utf16_le_bom[] = {0xFF, 0xFE};
-static const char utf32_be_bom[] = {0x00, 0x00, 0xFE, 0xFF};
-static const char utf32_le_bom[] = {0xFF, 0xFE, 0x00, 0x00};
+static const char utf16_be_bom[] = {'\xFE', '\xFF'};
+static const char utf16_le_bom[] = {'\xFF', '\xFE'};
+static const char utf32_be_bom[] = {'\0', '\0', '\xFE', '\xFF'};
+static const char utf32_le_bom[] = {'\xFF', '\xFE', '\0', '\0'};
 
 int has_prohibited_utf_bom(const char *enc, const char *data, size_t len)
 {
index 6c0e400f8130743b0c0fef2764bd804451b016a6..6bf2fdbab67fffb4c9ef451b61bbc7ca8f6780ea 100644 (file)
@@ -2340,7 +2340,17 @@ int has_uncommitted_changes(int ignore_submodules)
        if (ignore_submodules)
                rev_info.diffopt.flags.ignore_submodules = 1;
        rev_info.diffopt.flags.quick = 1;
+
        add_head_to_pending(&rev_info);
+       if (!rev_info.pending.nr) {
+               /*
+                * We have no head (or it's corrupt); use the empty tree,
+                * which will complain if the index is non-empty.
+                */
+               struct tree *tree = lookup_tree(the_repository, the_hash_algo->empty_tree);
+               add_pending_object(&rev_info, &tree->object, "");
+       }
+
        diff_setup_done(&rev_info.diffopt);
        result = run_diff_index(&rev_info, 1);
        return diff_result_code(&rev_info.diffopt, result);
index c1937a291126ca5e451763ccdf22bf8cccd0bd42..2356da5f784fbe5670b3f9e8bbc6d6419b4fa9d3 100644 (file)
@@ -52,14 +52,6 @@ extern "C" {
 #define XDL_EMIT_FUNCNAMES (1 << 0)
 #define XDL_EMIT_FUNCCONTEXT (1 << 2)
 
-#define XDL_MMB_READONLY (1 << 0)
-
-#define XDL_MMF_ATOMIC (1 << 0)
-
-#define XDL_BDOP_INS 1
-#define XDL_BDOP_CPY 2
-#define XDL_BDOP_INSB 3
-
 /* merge simplification levels */
 #define XDL_MERGE_MINIMAL 0
 #define XDL_MERGE_EAGER 1
index 0de1ef463bf71b2021ae142a80ac5f87a03a43fc..3e8aff92bc436ee99a698fdd748fb1845e28db8c 100644 (file)
 
 #include "xinclude.h"
 
-
-
 #define XDL_MAX_COST_MIN 256
 #define XDL_HEUR_MIN_COST 256
 #define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
 #define XDL_SNAKE_CNT 20
 #define XDL_K_HEUR 4
 
-
-
 typedef struct s_xdpsplit {
        long i1, i2;
        int min_lo, min_hi;
 } xdpsplit_t;
 
-
-
-
-static long xdl_split(unsigned long const *ha1, long off1, long lim1,
-                     unsigned long const *ha2, long off2, long lim2,
-                     long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
-                     xdalgoenv_t *xenv);
-static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2);
-
-
-
-
-
 /*
  * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
  * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both