]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/nth-packed-object-id'
authorJunio C Hamano <gitster@pobox.com>
Thu, 5 Mar 2020 18:43:03 +0000 (10:43 -0800)
committerJunio C Hamano <gitster@pobox.com>
Thu, 5 Mar 2020 18:43:03 +0000 (10:43 -0800)
Code cleanup to use "struct object_id" more by replacing use of
"char *sha1"

* jk/nth-packed-object-id:
  packfile: drop nth_packed_object_sha1()
  packed_object_info(): use object_id internally for delta base
  packed_object_info(): use object_id for returning delta base
  pack-check: push oid lookup into loop
  pack-check: convert "internal error" die to a BUG()
  pack-bitmap: use object_id when loading on-disk bitmaps
  pack-objects: use object_id struct in pack-reuse code
  pack-objects: convert oe_set_delta_ext() to use object_id
  pack-objects: read delta base oid into object_id struct
  nth_packed_object_oid(): use customary integer return

115 files changed:
.mailmap
Documentation/MyFirstContribution.txt
Documentation/RelNotes/2.26.0.txt
Documentation/config.txt
Documentation/config/branch.txt
Documentation/config/pull.txt
Documentation/config/push.txt
Documentation/config/rebase.txt
Documentation/doc-diff
Documentation/git-check-ignore.txt
Documentation/git-rebase.txt
Documentation/git-sparse-checkout.txt
Documentation/gitcredentials.txt
Makefile
bisect.c
bisect.h
blame.h
builtin/bisect--helper.c
builtin/check-ignore.c
builtin/fetch.c
builtin/pack-objects.c
builtin/pull.c
builtin/rebase.c
builtin/receive-pack.c
builtin/remote.c
builtin/rev-list.c
builtin/show-branch.c
builtin/sparse-checkout.c
builtin/worktree.c
color.c
compat/mingw.c
config.c
config.h
contrib/completion/git-prompt.sh
credential.c
credential.h
dir.c
ewah/bitmap.c
ewah/ewok.h
mailinfo.c
merge-recursive.c
object.c
object.h
pack-bitmap.c
pack-bitmap.h
quote.c
reachable.c
rebase-interactive.c
rebase-interactive.h
rebase.c [new file with mode: 0644]
rebase.h [new file with mode: 0644]
run-command.h
sequencer.c
sequencer.h
strbuf.c
strbuf.h
t/helper/test-config.c
t/helper/test-windows-named-pipe.c
t/lib-log-graph.sh [new file with mode: 0755]
t/perf/p5310-pack-bitmaps.sh
t/t0008-ignores.sh
t/t0021-conversion.sh
t/t0300-credentials.sh
t/t1091-sparse-checkout-builtin.sh
t/t1300-config.sh
t/t1308-config-set.sh
t/t1406-submodule-ref-store.sh
t/t1450-fsck.sh
t/t2107-update-index-basic.sh
t/t2400-worktree-add.sh
t/t2402-worktree-list.sh
t/t3007-ls-files-recurse-submodules.sh
t/t3400-rebase.sh
t/t3401-rebase-and-am-rename.sh
t/t3404-rebase-interactive.sh
t/t3406-rebase-message.sh
t/t3407-rebase-abort.sh
t/t3420-rebase-autostash.sh
t/t3421-rebase-topology-linear.sh
t/t3424-rebase-empty.sh [new file with mode: 0755]
t/t3425-rebase-topology-merges.sh
t/t3427-rebase-subtree.sh
t/t3430-rebase-merges.sh
t/t3432-rebase-fast-forward.sh
t/t3433-rebase-across-mode-change.sh [new file with mode: 0755]
t/t4026-color.sh
t/t4117-apply-reject.sh
t/t4202-log.sh
t/t4214-log-graph-octopus.sh
t/t4215-log-skewed-merges.sh
t/t5150-request-pull.sh
t/t5310-pack-bitmaps.sh
t/t5407-post-rewrite-hook.sh
t/t5409-colorize-remote-messages.sh
t/t5505-remote.sh
t/t5509-fetch-push-namespaces.sh
t/t5510-fetch.sh
t/t5516-fetch-push.sh
t/t5520-pull.sh
t/t5616-partial-clone.sh
t/t6000-rev-list-misc.sh
t/t6047-diff3-conflict-markers.sh
t/t6113-rev-list-bitmap-filters.sh [new file with mode: 0755]
t/t7512-status-help.sh
t/t8003-blame-corner-cases.sh
t/t9106-git-svn-commit-diff-clobber.sh
t/t9300-fast-import.sh
t/t9800-git-p4-basic.sh
t/t9810-git-p4-rcs.sh
t/t9903-bash-prompt.sh
t/test-lib-functions.sh
urlmatch.c
urlmatch.h
worktree.c
worktree.h

index 73fd92e192bd348dd64965fd33feb9d61b11b957..bde7aba756ea74c3af562874ab5c81a829e43c83 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -31,6 +31,7 @@ Brandon Casey <drafnel@gmail.com> <casey@nrlssc.navy.mil>
 Brandon Williams <bwilliams.eng@gmail.com> <bmwill@google.com>
 brian m. carlson <sandals@crustytoothpaste.net>
 brian m. carlson <sandals@crustytoothpaste.net> <sandals@crustytoothpaste.ath.cx>
+brian m. carlson <sandals@crustytoothpaste.net> <bk2204@github.com>
 Bryan Larsen <bryan@larsen.st> <bryan.larsen@gmail.com>
 Bryan Larsen <bryan@larsen.st> <bryanlarsen@yahoo.com>
 Cheng Renquan <crquan@gmail.com>
index 35b9130aa3ee9c0a59c5e151f2ada8fdf1ad95f0..427274df4d9233e61001e478b3c299dd20439a73 100644 (file)
@@ -23,6 +23,42 @@ useful additional context:
 - `Documentation/SubmittingPatches`
 - `Documentation/howto/new-command.txt`
 
+[[getting-help]]
+=== Getting Help
+
+If you get stuck, you can seek help in the following places.
+
+==== git@vger.kernel.org
+
+This is the main Git project mailing list where code reviews, version
+announcements, design discussions, and more take place. Those interested in
+contributing are welcome to post questions here. The Git list requires
+plain-text-only emails and prefers inline and bottom-posting when replying to
+mail; you will be CC'd in all replies to you. Optionally, you can subscribe to
+the list by sending an email to majordomo@vger.kernel.org with "subscribe git"
+in the body. The https://lore.kernel.org/git[archive] of this mailing list is
+available to view in a browser.
+
+==== https://groups.google.com/forum/#!forum/git-mentoring[git-mentoring@googlegroups.com]
+
+This mailing list is targeted to new contributors and was created as a place to
+post questions and receive answers outside of the public eye of the main list.
+Veteran contributors who are especially interested in helping mentor newcomers
+are present on the list. In order to avoid search indexers, group membership is
+required to view messages; anyone can join and no approval is required.
+
+==== https://webchat.freenode.net/#git-devel[#git-devel] on Freenode
+
+This IRC channel is for conversations between Git contributors. If someone is
+currently online and knows the answer to your question, you can receive help
+in real time. Otherwise, you can read the
+https://colabti.org/irclogger/irclogger_logs/git-devel[scrollback] to see
+whether someone answered you. IRC does not allow offline private messaging, so
+if you try to private message someone and then log out of IRC, they cannot
+respond to you. It's better to ask your questions in the channel so that you
+can be answered if you disconnect and so that others can learn from the
+conversation.
+
 [[getting-started]]
 == Getting Started
 
index e8a78b7a19b6a4a2825a983e7b2981c51bc5b41c..30ef134eecf908032f03676ff7d60c39b80f625d 100644 (file)
@@ -35,6 +35,9 @@ UI, Workflows & Features
  * "git config" learned to show in which "scope", in addition to in
    which file, each config setting comes from.
 
+ * The basic 7 colors learned the brighter counterparts
+   (e.g. "brightred").
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -92,6 +95,19 @@ Performance, Internal Implementation, Development Support etc.
  * Memory footprint and performance of "git name-rev" has been
    improved.
 
+ * The object reachability bitmap machinery and the partial cloning
+   machinery were not prepared to work well together, because some
+   object-filtering criteria that partial clones use inherently rely
+   on object traversal, but the bitmap machinery is an optimization
+   to bypass that object traversal.  There however are some cases
+   where they can work together, and they were taught about them.
+
+ * "git rebase" has learned to use the merge backend (i.e. the
+   machinery that drives "rebase -i") by default, while allowing
+   "--apply" option to use the "apply" backend (e.g. the moral
+   equivalent of "format-patch piped to am").  The rebase.backend
+   configuration variable can be set to customize.
+
 
 Fixes since v2.25
 -----------------
@@ -212,6 +228,29 @@ Fixes since v2.25
    thing.
    (merge a7df60cac8 tb/commit-graph-object-dir later to maint).
 
+ * "git remote rename X Y" needs to adjust configuration variables
+   (e.g. branch.<name>.remote) whose value used to be X to Y.
+   branch.<name>.pushRemote is now also updated.
+
+ * Update to doc-diff.
+   (merge 2607d39da3 jk/doc-diff-parallel later to maint).
+
+ * Doc markup fix.
+   (merge 0aa6ce3094 jk/push-option-doc-markup-fix later to maint).
+
+ * "git check-ignore" did not work when the given path is explicitly
+   marked as not ignored with a negative entry in the .gitignore file.
+   (merge 7ec8125fba en/check-ignore later to maint).
+
+ * The merge-recursive machinery failed to refresh the cache entry for
+   a merge result in a couple of places, resulting in an unnecessary
+   merge failure, which has been fixed.
+   (merge fb1c18fc46 en/t3433-rebase-stat-dirty-failure later to maint).
+
+ * Fix for a bug revealed by a recent change to make the protocol v2
+   the default.
+   (merge 3e96c66805 ds/partial-clone-fixes later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge 26f924d50e en/simplify-check-updates-in-unpack-trees later to maint).
    (merge d0d0a357a1 am/update-pathspec-f-f-tests later to maint).
@@ -230,3 +269,9 @@ Fixes since v2.25
    (merge 08809c09aa js/mingw-open-in-gdb later to maint).
    (merge cc4f2eb828 jk/doc-credential-helper later to maint).
    (merge e0020b2f82 es/outside-repo-errmsg-hints later to maint).
+   (merge a2dc43414c es/doc-mentoring later to maint).
+   (merge 539052f42f jk/run-command-formatfix later to maint).
+   (merge 02bbbe9df9 es/worktree-cleanup later to maint).
+   (merge 2ce6d075fa rs/micro-cleanups later to maint).
+   (merge 27f182b3fc rs/blame-typefix-for-fingerprint later to maint).
+   (merge 3c29e21eb0 ma/test-cleanup later to maint).
index 83e7bba8729627d3e329f8a1934ecf46fb30ff1f..08b13ba72be53bf36e05f7095964c5206d795945 100644 (file)
@@ -263,7 +263,9 @@ color::
 +
 The basic colors accepted are `normal`, `black`, `red`, `green`, `yellow`,
 `blue`, `magenta`, `cyan` and `white`.  The first color given is the
-foreground; the second is the background.
+foreground; the second is the background.  All the basic colors except
+`normal` have a bright variant that can be speficied by prefixing the
+color with `bright`, like `brightred`.
 +
 Colors may also be given as numbers between 0 and 255; these use ANSI
 256-color mode (but note that not all terminals may support this).  If
index a592d522a744f99cd2c8f94400bca1203bfe2886..cc5f3249fc58a39da1580b49ad0145c44ec1544f 100644 (file)
@@ -81,15 +81,16 @@ branch.<name>.rebase::
        "git pull" is run. See "pull.rebase" for doing this in a non
        branch-specific manner.
 +
-When `merges`, pass the `--rebase-merges` option to 'git rebase'
+When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase'
 so that the local merge commits are included in the rebase (see
 linkgit:git-rebase[1] for details).
 +
-When `preserve` (deprecated in favor of `merges`), also pass
+When `preserve` (or just 'p', deprecated in favor of `merges`), also pass
 `--preserve-merges` along to 'git rebase' so that locally committed merge
 commits will not be flattened by running 'git pull'.
 +
-When the value is `interactive`, the rebase is run in interactive mode.
+When the value is `interactive` (or just 'i'), the rebase is run in interactive
+mode.
 +
 *NOTE*: this is a possibly dangerous operation; do *not* use
 it unless you understand the implications (see linkgit:git-rebase[1]
index b87cab31b39c4469fc2528c04bd19b6b96912dd7..5404830609569d78dbcd597847c86e4302123b75 100644 (file)
@@ -14,15 +14,16 @@ pull.rebase::
        pull" is run. See "branch.<name>.rebase" for setting this on a
        per-branch basis.
 +
-When `merges`, pass the `--rebase-merges` option to 'git rebase'
+When `merges` (or just 'm'), pass the `--rebase-merges` option to 'git rebase'
 so that the local merge commits are included in the rebase (see
 linkgit:git-rebase[1] for details).
 +
-When `preserve` (deprecated in favor of `merges`), also pass
+When `preserve` (or just 'p', deprecated in favor of `merges`), also pass
 `--preserve-merges` along to 'git rebase' so that locally committed merge
 commits will not be flattened by running 'git pull'.
 +
-When the value is `interactive`, the rebase is run in interactive mode.
+When the value is `interactive` (or just 'i'), the rebase is run in interactive
+mode.
 +
 *NOTE*: this is a possibly dangerous operation; do *not* use
 it unless you understand the implications (see linkgit:git-rebase[1]
index 54871f8213c4292b7e11901831f09a4f18c018fb..0a7aa322a9be695cd9863a519bf3fae5b789a7df 100644 (file)
@@ -80,7 +80,7 @@ higher priority configuration file (e.g. `.git/config` in a
 repository) to clear the values inherited from a lower priority
 configuration files (e.g. `$HOME/.gitconfig`).
 +
---
+----
 
 Example:
 
@@ -97,7 +97,7 @@ repo/.git/config
 
 This will result in only b (a and c are cleared).
 
---
+----
 
 push.recurseSubmodules::
        Make sure all submodule commits used by the revisions to be pushed
index d98e32d812e76f24f6eca49e32af910b1ddd0967..7f7a07d22f86bdfb6b1d12d4625f456b5741bee6 100644 (file)
@@ -5,6 +5,12 @@ rebase.useBuiltin::
        is always used. Setting this will emit a warning, to alert any
        remaining users that setting this now does nothing.
 
+rebase.backend::
+       Default backend to use for rebasing.  Possible choices are
+       'apply' or 'merge'.  In the future, if the merge backend gains
+       all remaining capabilities of the apply backend, this setting
+       may become unused.
+
 rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
        rebase. False by default.
index 88a9b20168b24e95edb8747c6694537b084063fb..1694300e50bfcfa58cc7798614a254fec2e219a5 100755 (executable)
@@ -127,7 +127,7 @@ generate_render_makefile () {
        while read src
        do
                dst=$2/${src#$1/}
-               printf 'all:: %s\n' "$dst"
+               printf 'all: %s\n' "$dst"
                printf '%s: %s\n' "$dst" "$src"
                printf '\t@echo >&2 "  RENDER $(notdir $@)" && \\\n'
                printf '\tmkdir -p $(dir $@) && \\\n'
index 8b2d49c79e1140bb33b63d8c388f4b2c339f7ccf..0c3924a63d2f8625c91cca9ceb391214c3fc3d41 100644 (file)
@@ -30,9 +30,15 @@ OPTIONS
        valid with a single pathname.
 
 -v, --verbose::
-       Also output details about the matching pattern (if any)
-       for each given pathname. For precedence rules within and
-       between exclude sources, see linkgit:gitignore[5].
+       Instead of printing the paths that are excluded, for each path
+       that matches an exclude pattern, print the exclude pattern
+       together with the path.  (Matching an exclude pattern usually
+       means the path is excluded, but if the pattern begins with '!'
+       then it is a negated pattern and matching it means the path is
+       NOT excluded.)
++
+For precedence rules within and between exclude sources, see
+linkgit:gitignore[5].
 
 --stdin::
        Read pathnames from the standard input, one per line,
index 0c4f038dd6077fb47436e82c2ce80b78fa6b9304..8c1f4b826809e922d9864eb0ed88daa737f4c217 100644 (file)
@@ -258,16 +258,45 @@ See also INCOMPATIBLE OPTIONS below.
        original branch. The index and working tree are also left
        unchanged as a result.
 
---keep-empty::
-       Keep the commits that do not change anything from its
-       parents in the result.
+--apply:
+       Use applying strategies to rebase (calling `git-am`
+       internally).  This option may become a no-op in the future
+       once the merge backend handles everything the apply one does.
++
+See also INCOMPATIBLE OPTIONS below.
+
+--empty={drop,keep,ask}::
+       How to handle commits that are not empty to start and are not
+       clean cherry-picks of any upstream commit, but which become
+       empty after rebasing (because they contain a subset of already
+       upstream changes).  With drop (the default), commits that
+       become empty are dropped.  With keep, such commits are kept.
+       With ask (implied by --interactive), the rebase will halt when
+       an empty commit is applied allowing you to choose whether to
+       drop it, edit files more, or just commit the empty changes.
+       Other options, like --exec, will use the default of drop unless
+       -i/--interactive is explicitly specified.
++
+Note that commits which start empty are kept, and commits which are
+clean cherry-picks (as determined by `git log --cherry-mark ...`) are
+always dropped.
 +
 See also INCOMPATIBLE OPTIONS below.
 
+--keep-empty::
+       No-op.  Rebasing commits that started empty (had no change
+       relative to their parent) used to fail and this option would
+       override that behavior, allowing commits with empty changes to
+       be rebased.  Now commits with no changes do not cause rebasing
+       to halt.
++
+See also BEHAVIORAL DIFFERENCES and 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.
+       No-op.  Rebasing commits with an empty message used to fail
+       and this option would override that behavior, allowing commits
+       with empty messages to be rebased.  Now commits with an empty
+       message do not cause rebasing to halt.
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -286,7 +315,7 @@ See also INCOMPATIBLE OPTIONS below.
 --merge::
        Use merging strategies to rebase.  When the recursive (default) merge
        strategy is used, this allows rebase to be aware of renames on the
-       upstream side.
+       upstream side.  This is the default.
 +
 Note that a rebase merge works by replaying each commit from the working
 branch on top of the <upstream> branch.  Because of this, when a merge
@@ -356,7 +385,7 @@ See also INCOMPATIBLE OPTIONS below.
        Ensure at least <n> lines of surrounding context match before
        and after each change.  When fewer lines of surrounding
        context exist they all must match.  By default no context is
-       ever ignored.
+       ever ignored.  Implies --apply.
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -394,8 +423,9 @@ with `--keep-base` in order to drop those commits from your branch.
 
 --ignore-whitespace::
 --whitespace=<option>::
-       These flag are passed to the 'git apply' program
+       These flags are passed to the 'git apply' program
        (see linkgit:git-apply[1]) that applies the patch.
+       Implies --apply.
 +
 See also INCOMPATIBLE OPTIONS below.
 
@@ -539,10 +569,11 @@ INCOMPATIBLE OPTIONS
 
 The following options:
 
+ * --apply
  * --committer-date-is-author-date
  * --ignore-date
- * --whitespace
  * --ignore-whitespace
+ * --whitespace
  * -C
 
 are incompatible with the following options:
@@ -557,6 +588,7 @@ are incompatible with the following options:
  * --interactive
  * --exec
  * --keep-empty
+ * --empty=
  * --edit-todo
  * --root when used in combination with --onto
 
@@ -565,33 +597,127 @@ In addition, the following pairs of options are incompatible:
  * --preserve-merges and --interactive
  * --preserve-merges and --signoff
  * --preserve-merges and --rebase-merges
+ * --preserve-merges and --empty=
  * --keep-base and --onto
  * --keep-base and --root
 
 BEHAVIORAL DIFFERENCES
 -----------------------
 
-There are some subtle differences how the backends behave.
+git rebase has two primary backends: apply and merge.  (The apply
+backend used to known as the 'am' backend, but the name led to
+confusion as it looks like a verb instead of a noun.  Also, the merge
+backend used to be known as the interactive backend, but it is now
+used for non-interactive cases as well.  Both were renamed based on
+lower-level functionality that underpinned each.) There are some
+subtle differences in how these two backends behave:
 
 Empty commits
 ~~~~~~~~~~~~~
 
-The am backend drops any "empty" commits, regardless of 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).
+The apply backend unfortunately drops intentionally empty commits, i.e.
+commits that started empty, though these are rare in practice.  It
+also drops commits that become empty and has no option for controlling
+this behavior.
 
-The interactive backend drops commits by default that
-started empty and halts if it hits a commit that ended up empty.
-The `--keep-empty` option exists for the interactive backend to allow
-it to keep commits that started empty.
+The merge backend keeps intentionally empty commits.  Similar to the
+apply backend, by default the merge backend drops commits that become
+empty unless -i/--interactive is specified (in which case it stops and
+asks the user what to do).  The merge backend also has an
+--empty={drop,keep,ask} option for changing the behavior of handling
+commits that become empty.
 
 Directory rename detection
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-Directory rename heuristics are enabled in the merge and interactive
-backends.  Due to the lack of accurate tree information, directory
-rename detection is disabled in the am backend.
+Due to the lack of accurate tree information (arising from
+constructing fake ancestors with the limited information available in
+patches), directory rename detection is disabled in the apply backend.
+Disabled directory rename detection means that if one side of history
+renames a directory and the other adds new files to the old directory,
+then the new files will be left behind in the old directory without
+any warning at the time of rebasing that you may want to move these
+files into the new directory.
+
+Directory rename detection works with the merge backend to provide you
+warnings in such cases.
+
+Context
+~~~~~~~
+
+The apply backend works by creating a sequence of patches (by calling
+`format-patch` internally), and then applying the patches in sequence
+(calling `am` internally).  Patches are composed of multiple hunks,
+each with line numbers, a context region, and the actual changes.  The
+line numbers have to be taken with some fuzz, since the other side
+will likely have inserted or deleted lines earlier in the file.  The
+context region is meant to help find how to adjust the line numbers in
+order to apply the changes to the right lines.  However, if multiple
+areas of the code have the same surrounding lines of context, the
+wrong one can be picked.  There are real-world cases where this has
+caused commits to be reapplied incorrectly with no conflicts reported.
+Setting diff.context to a larger value may prevent such types of
+problems, but increases the chance of spurious conflicts (since it
+will require more lines of matching context to apply).
+
+The merge backend works with a full copy of each relevant file,
+insulating it from these types of problems.
+
+Labelling of conflicts markers
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+When there are content conflicts, the merge machinery tries to
+annotate each side's conflict markers with the commits where the
+content came from.  Since the apply backend drops the original
+information about the rebased commits and their parents (and instead
+generates new fake commits based off limited information in the
+generated patches), those commits cannot be identified; instead it has
+to fall back to a commit summary.  Also, when merge.conflictStyle is
+set to diff3, the apply backend will use "constructed merge base" to
+label the content from the merge base, and thus provide no information
+about the merge base commit whatsoever.
+
+The merge backend works with the full commits on both sides of history
+and thus has no such limitations.
+
+Hooks
+~~~~~
+
+The apply backend has not traditionally called the post-commit hook,
+while the merge backend has.  However, this was by accident of
+implementation rather than by design.  Both backends should have the
+same behavior, though it is not clear which one is correct.
+
+Interruptability
+~~~~~~~~~~~~~~~~
+
+The apply backend has safety problems with an ill-timed interrupt; if
+the user presses Ctrl-C at the wrong time to try to abort the rebase,
+the rebase can enter a state where it cannot be aborted with a
+subsequent `git rebase --abort`.  The merge backend does not appear to
+suffer from the same shortcoming.  (See
+https://lore.kernel.org/git/20200207132152.GC2868@szeder.dev/ for
+details.)
+
+Miscellaneous differences
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+There are a few more behavioral differences that most folks would
+probably consider inconsequential but which are mentioned for
+completeness:
+
+* Reflog: The two backends will use different wording when describing
+  the changes made in the reflog, though both will make use of the
+  word "rebase".
+
+* Progress, informational, and error messages: The two backends
+  provide slightly different progress and informational messages.
+  Also, the apply backend writes error messages (such as "Your files
+  would be overwritten...") to stdout, while the merge backend writes
+  them to stderr.
+
+* State directories: The two backends keep their state in different
+  directories under .git/
 
 include::merge-strategies.txt[]
 
index a24d90529bd62a99dc46b945b0136e672a1a0374..c0342e53938a4f92398bdd8ceafa57623c2a85ae 100644 (file)
@@ -63,6 +63,13 @@ directories. The input format matches the output of `git ls-tree --name-only`.
 This includes interpreting pathnames that begin with a double quote (") as
 C-style quoted strings.
 
+'add'::
+       Update the sparse-checkout file to include additional patterns.
+       By default, these patterns are read from the command-line arguments,
+       but they can be read from stdin using the `--stdin` option. When
+       `core.sparseCheckoutCone` is enabled, the given patterns are interpreted
+       as directory names as in the 'set' subcommand.
+
 'disable'::
        Disable the `core.sparseCheckout` config setting, and restore the
        working directory to include all files. Leaves the sparse-checkout
index 5b9d14fb1f4b082bde9d36efaf443e0176cd266a..1814d2d23c189c9597205eb2c0ca7f3d0c2306c3 100644 (file)
@@ -131,7 +131,9 @@ context would not match:
 because the hostnames differ. Nor would it match `foo.example.com`; Git
 compares hostnames exactly, without considering whether two hosts are part of
 the same domain. Likewise, a config entry for `http://example.com` would not
-match: Git compares the protocols exactly.
+match: Git compares the protocols exactly.  However, you may use wildcards in
+the domain name and other pattern matching techniques as with the `http.<url>.*`
+options.
 
 If the "pattern" URL does include a path component, then this too must match
 exactly: the context `https://example.com/bar/baz.git` will match a config
index 6134104ae65b713b665a40933b2df1a8c58e2269..9804a0758b2458f0ba3d7130c83d7d22d9570879 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -955,6 +955,7 @@ LIB_OBJS += quote.o
 LIB_OBJS += range-diff.o
 LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
+LIB_OBJS += rebase.o
 LIB_OBJS += rebase-interactive.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
index e81c91d02c07552dd50e82d42ae3e14f81945e01..9154f810f76af3cdbf82b8bb509f47aa392b0e01 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -572,7 +572,7 @@ static int sqrti(int val)
 {
        float d, x = val;
 
-       if (val == 0)
+       if (!val)
                return 0;
 
        do {
@@ -661,11 +661,11 @@ static void bisect_common(struct rev_info *revs)
                mark_edges_uninteresting(revs, NULL, 0);
 }
 
-static void exit_if_skipped_commits(struct commit_list *tried,
+static enum bisect_error error_if_skipped_commits(struct commit_list *tried,
                                    const struct object_id *bad)
 {
        if (!tried)
-               return;
+               return BISECT_OK;
 
        printf("There are only 'skip'ped commits left to test.\n"
               "The first %s commit could be any of:\n", term_bad);
@@ -676,7 +676,8 @@ static void exit_if_skipped_commits(struct commit_list *tried,
        if (bad)
                printf("%s\n", oid_to_hex(bad));
        printf(_("We cannot bisect more!\n"));
-       exit(2);
+
+       return BISECT_ONLY_SKIPPED_LEFT;
 }
 
 static int is_expected_rev(const struct object_id *oid)
@@ -703,9 +704,10 @@ static int is_expected_rev(const struct object_id *oid)
        return res;
 }
 
-static int bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
+static enum bisect_error bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
 {
        char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
+       enum bisect_error res = BISECT_OK;
 
        memcpy(bisect_rev_hex, oid_to_hex(bisect_rev), the_hash_algo->hexsz + 1);
        update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
@@ -715,14 +717,24 @@ static int bisect_checkout(const struct object_id *bisect_rev, int no_checkout)
                update_ref(NULL, "BISECT_HEAD", bisect_rev, NULL, 0,
                           UPDATE_REFS_DIE_ON_ERR);
        } else {
-               int res;
                res = run_command_v_opt(argv_checkout, RUN_GIT_CMD);
                if (res)
-                       exit(res);
+                       /*
+                        * Errors in `run_command()` itself, signaled by res < 0,
+                        * and errors in the child process, signaled by res > 0
+                        * can both be treated as regular BISECT_FAILURE (-1).
+                        */
+                       return -abs(res);
        }
 
        argv_show_branch[1] = bisect_rev_hex;
-       return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
+       res = run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
+       /*
+        * Errors in `run_command()` itself, signaled by res < 0,
+        * and errors in the child process, signaled by res > 0
+        * can both be treated as regular BISECT_FAILURE (-1).
+        */
+       return -abs(res);
 }
 
 static struct commit *get_commit_reference(struct repository *r,
@@ -749,7 +761,7 @@ static struct commit **get_bad_and_good_commits(struct repository *r,
        return rev;
 }
 
-static void handle_bad_merge_base(void)
+static enum bisect_error handle_bad_merge_base(void)
 {
        if (is_expected_rev(current_bad_oid)) {
                char *bad_hex = oid_to_hex(current_bad_oid);
@@ -770,14 +782,14 @@ static void handle_bad_merge_base(void)
                                "between %s and [%s].\n"),
                                bad_hex, term_bad, term_good, bad_hex, good_hex);
                }
-               exit(3);
+               return BISECT_MERGE_BASE_CHECK;
        }
 
        fprintf(stderr, _("Some %s revs are not ancestors of the %s rev.\n"
                "git bisect cannot work properly in this case.\n"
                "Maybe you mistook %s and %s revs?\n"),
                term_good, term_bad, term_good, term_bad);
-       exit(1);
+       return BISECT_FAILED;
 }
 
 static void handle_skipped_merge_base(const struct object_id *mb)
@@ -799,13 +811,18 @@ static void handle_skipped_merge_base(const struct object_id *mb)
  * "check_merge_bases" checks that merge bases are not "bad" (or "new").
  *
  * - If one is "bad" (or "new"), it means the user assumed something wrong
- * and we must exit with a non 0 error code.
+ * and we must return error with a non 0 error code.
  * - If one is "good" (or "old"), that's good, we have nothing to do.
  * - If one is "skipped", we can't know but we should warn.
  * - If we don't know, we should check it out and ask the user to test.
+ * - If a merge base must be tested, on success return
+ * BISECT_INTERNAL_SUCCESS_MERGE_BASE (-11) a special condition
+ * for early success, this will be converted back to 0 in
+ * check_good_are_ancestors_of_bad().
  */
-static void check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
+static enum bisect_error check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
 {
+       enum bisect_error res = BISECT_OK;
        struct commit_list *result;
 
        result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
@@ -813,18 +830,24 @@ static void check_merge_bases(int rev_nr, struct commit **rev, int no_checkout)
        for (; result; result = result->next) {
                const struct object_id *mb = &result->item->object.oid;
                if (oideq(mb, current_bad_oid)) {
-                       handle_bad_merge_base();
+                       res = handle_bad_merge_base();
+                       break;
                } else if (0 <= oid_array_lookup(&good_revs, mb)) {
                        continue;
                } else if (0 <= oid_array_lookup(&skipped_revs, mb)) {
                        handle_skipped_merge_base(mb);
                } else {
                        printf(_("Bisecting: a merge base must be tested\n"));
-                       exit(bisect_checkout(mb, no_checkout));
+                       res = bisect_checkout(mb, no_checkout);
+                       if (!res)
+                               /* indicate early success */
+                               res = BISECT_INTERNAL_SUCCESS_MERGE_BASE;
+                       break;
                }
        }
 
        free_commit_list(result);
+       return res;
 }
 
 static int check_ancestors(struct repository *r, int rev_nr,
@@ -850,43 +873,58 @@ static int check_ancestors(struct repository *r, int rev_nr,
  *
  * If that's not the case, we need to check the merge bases.
  * If a merge base must be tested by the user, its source code will be
- * checked out to be tested by the user and we will exit.
+ * checked out to be tested by the user and we will return.
  */
-static void check_good_are_ancestors_of_bad(struct repository *r,
+
+static enum bisect_error check_good_are_ancestors_of_bad(struct repository *r,
                                            const char *prefix,
                                            int no_checkout)
 {
-       char *filename = git_pathdup("BISECT_ANCESTORS_OK");
+       char *filename;
        struct stat st;
        int fd, rev_nr;
+       enum bisect_error res = BISECT_OK;
        struct commit **rev;
 
        if (!current_bad_oid)
-               die(_("a %s revision is needed"), term_bad);
+               return error(_("a %s revision is needed"), term_bad);
+
+       filename = git_pathdup("BISECT_ANCESTORS_OK");
 
        /* Check if file BISECT_ANCESTORS_OK exists. */
        if (!stat(filename, &st) && S_ISREG(st.st_mode))
                goto done;
 
        /* Bisecting with no good rev is ok. */
-       if (good_revs.nr == 0)
+       if (!good_revs.nr)
                goto done;
 
        /* Check if all good revs are ancestor of the bad rev. */
+
        rev = get_bad_and_good_commits(r, &rev_nr);
        if (check_ancestors(r, rev_nr, rev, prefix))
-               check_merge_bases(rev_nr, rev, no_checkout);
+               res = check_merge_bases(rev_nr, rev, no_checkout);
        free(rev);
 
-       /* Create file BISECT_ANCESTORS_OK. */
-       fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
-       if (fd < 0)
-               warning_errno(_("could not create file '%s'"),
-                             filename);
-       else
-               close(fd);
+       if (!res) {
+               /* Create file BISECT_ANCESTORS_OK. */
+               fd = open(filename, O_CREAT | O_TRUNC | O_WRONLY, 0600);
+               if (fd < 0)
+                       /*
+                        * BISECT_ANCESTORS_OK file is not absolutely necessary,
+                        * the bisection process will continue at the next
+                        * bisection step.
+                        * So, just signal with a warning that something
+                        * might be wrong.
+                        */
+                       warning_errno(_("could not create file '%s'"),
+                               filename);
+               else
+                       close(fd);
+       }
  done:
        free(filename);
+       return res;
 }
 
 /*
@@ -938,18 +976,19 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
 }
 
 /*
- * We use the convention that exiting with an exit code 10 means that
+ * We use the convention that return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND (-10) means
  * the bisection process finished successfully.
- * In this case the calling shell script should exit 0.
- *
+ * In this case the calling function or command should not turn a
+ * BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND return code into an error or a non zero exit code.
  * If no_checkout is non-zero, the bisection process does not
  * checkout the trial commit but instead simply updates BISECT_HEAD.
  */
-int bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
+enum bisect_error bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
 {
        struct rev_info revs;
        struct commit_list *tried;
        int reaches = 0, all = 0, nr, steps;
+       enum bisect_error res = BISECT_OK;
        struct object_id *bisect_rev;
        char *steps_msg;
 
@@ -957,7 +996,9 @@ int bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
        if (read_bisect_refs())
                die(_("reading bisect refs failed"));
 
-       check_good_are_ancestors_of_bad(r, prefix, no_checkout);
+       res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
+       if (res)
+               return res;
 
        bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
        revs.limited = 1;
@@ -969,33 +1010,45 @@ int bisect_next_all(struct repository *r, const char *prefix, int no_checkout)
 
        if (!revs.commits) {
                /*
-                * We should exit here only if the "bad"
+                * We should return error here only if the "bad"
                 * commit is also a "skip" commit.
                 */
-               exit_if_skipped_commits(tried, NULL);
-
+               res = error_if_skipped_commits(tried, NULL);
+               if (res < 0)
+                       return res;
                printf(_("%s was both %s and %s\n"),
                       oid_to_hex(current_bad_oid),
                       term_good,
                       term_bad);
-               exit(1);
+
+               return BISECT_FAILED;
        }
 
        if (!all) {
                fprintf(stderr, _("No testable commit found.\n"
                        "Maybe you started with bad path parameters?\n"));
-               exit(4);
+
+               return BISECT_NO_TESTABLE_COMMIT;
        }
 
        bisect_rev = &revs.commits->item->object.oid;
 
        if (oideq(bisect_rev, current_bad_oid)) {
-               exit_if_skipped_commits(tried, current_bad_oid);
+               res = error_if_skipped_commits(tried, current_bad_oid);
+               if (res)
+                       return res;
                printf("%s is the first %s commit\n", oid_to_hex(bisect_rev),
                        term_bad);
+
                show_diff_tree(r, prefix, revs.commits->item);
-               /* This means the bisection process succeeded. */
-               exit(10);
+               /*
+                * This means the bisection process succeeded.
+                * Using BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND (-10)
+                * so that the call chain can simply check
+                * for negative return values for early returns up
+                * until the cmd_bisect__helper() caller.
+                */
+               return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
        }
 
        nr = all - reaches - 1;
index 4e69a11ea8f3c60fe0c273fc4af98760fd236f09..8bad8d8391546f347ec91324eca21f74bda8e68f 100644 (file)
--- a/bisect.h
+++ b/bisect.h
@@ -31,7 +31,34 @@ struct rev_list_info {
        const char *header_prefix;
 };
 
-int bisect_next_all(struct repository *r,
+/*
+ * enum bisect_error represents the following return codes:
+ * BISECT_OK: success code. Internally, it means that next
+ * commit has been found (and possibly checked out) and it
+ * should be tested.
+ * BISECT_FAILED error code: default error code.
+ * BISECT_ONLY_SKIPPED_LEFT error code: only skipped
+ * commits left to be tested.
+ * BISECT_MERGE_BASE_CHECK error code: merge base check failed.
+ * BISECT_NO_TESTABLE_COMMIT error code: no testable commit found.
+ * BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND early success code:
+ * first term_bad commit found.
+ * BISECT_INTERNAL_SUCCESS_MERGE_BASE early success
+ * code: found merge base that should be tested.
+ * Early success codes BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND and
+ * BISECT_INTERNAL_SUCCESS_MERGE_BASE should be only internal codes.
+ */
+enum bisect_error {
+       BISECT_OK = 0,
+       BISECT_FAILED = -1,
+       BISECT_ONLY_SKIPPED_LEFT = -2,
+       BISECT_MERGE_BASE_CHECK = -3,
+       BISECT_NO_TESTABLE_COMMIT = -4,
+       BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND = -10,
+       BISECT_INTERNAL_SUCCESS_MERGE_BASE = -11
+};
+
+enum bisect_error bisect_next_all(struct repository *r,
                    const char *prefix,
                    int no_checkout);
 
diff --git a/blame.h b/blame.h
index 4a9e1270b036465c23fab5a0e536b9638ca3ce1b..089b181ff27b8c90315ef264e038227cb24c7f86 100644 (file)
--- a/blame.h
+++ b/blame.h
@@ -16,6 +16,8 @@
 #define BLAME_DEFAULT_MOVE_SCORE       20
 #define BLAME_DEFAULT_COPY_SCORE       40
 
+struct fingerprint;
+
 /*
  * One blob in a commit that is being suspected
  */
@@ -52,7 +54,7 @@ struct blame_origin {
        struct blame_entry *suspects;
        mmfile_t file;
        int num_lines;
-       void *fingerprints;
+       struct fingerprint *fingerprints;
        struct object_id blob_oid;
        unsigned short mode;
        /* guilty gets set when shipping any suspects to the final
index 1718df7f09899afb37facf3754556520d361e13c..c1c40b516df7816b2cc0ce5cb20ee8beb7e9e000 100644 (file)
@@ -52,8 +52,8 @@ static void set_terms(struct bisect_terms *terms, const char *bad,
        terms->term_bad = xstrdup(bad);
 }
 
-static const char *vocab_bad = "bad|new";
-static const char *vocab_good = "good|old";
+static const char vocab_bad[] = "bad|new";
+static const char vocab_good[] = "good|old";
 
 /*
  * Check whether the string `term` belongs to the set of strings
@@ -206,31 +206,31 @@ static int bisect_write(const char *state, const char *rev,
        struct object_id oid;
        struct commit *commit;
        FILE *fp = NULL;
-       int retval = 0;
+       int res = 0;
 
        if (!strcmp(state, terms->term_bad)) {
                strbuf_addf(&tag, "refs/bisect/%s", state);
        } else if (one_of(state, terms->term_good, "skip", NULL)) {
                strbuf_addf(&tag, "refs/bisect/%s-%s", state, rev);
        } else {
-               retval = error(_("Bad bisect_write argument: %s"), state);
+               res = error(_("Bad bisect_write argument: %s"), state);
                goto finish;
        }
 
        if (get_oid(rev, &oid)) {
-               retval = error(_("couldn't get the oid of the rev '%s'"), rev);
+               res = error(_("couldn't get the oid of the rev '%s'"), rev);
                goto finish;
        }
 
        if (update_ref(NULL, tag.buf, &oid, NULL, 0,
                       UPDATE_REFS_MSG_ON_ERR)) {
-               retval = -1;
+               res = -1;
                goto finish;
        }
 
        fp = fopen(git_path_bisect_log(), "a");
        if (!fp) {
-               retval = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log());
+               res = error_errno(_("couldn't open the file '%s'"), git_path_bisect_log());
                goto finish;
        }
 
@@ -244,7 +244,7 @@ finish:
        if (fp)
                fclose(fp);
        strbuf_release(&tag);
-       return retval;
+       return res;
 }
 
 static int check_and_set_terms(struct bisect_terms *terms, const char *cmd)
@@ -291,26 +291,14 @@ static const char need_bisect_start_warning[] =
           "You then need to give me at least one %s and %s revision.\n"
           "You can use \"git bisect %s\" and \"git bisect %s\" for that.");
 
-static int bisect_next_check(const struct bisect_terms *terms,
-                            const char *current_term)
+static int decide_next(const struct bisect_terms *terms,
+                      const char *current_term, int missing_good,
+                      int missing_bad)
 {
-       int missing_good = 1, missing_bad = 1, retval = 0;
-       const char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
-       const char *good_glob = xstrfmt("%s-*", terms->term_good);
-
-       if (ref_exists(bad_ref))
-               missing_bad = 0;
-
-       for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
-                            (void *) &missing_good);
-
        if (!missing_good && !missing_bad)
-               goto finish;
-
-       if (!current_term) {
-               retval = -1;
-               goto finish;
-       }
+               return 0;
+       if (!current_term)
+               return -1;
 
        if (missing_good && !missing_bad &&
            !strcmp(current_term, terms->term_good)) {
@@ -321,7 +309,7 @@ static int bisect_next_check(const struct bisect_terms *terms,
                 */
                warning(_("bisecting only with a %s commit"), terms->term_bad);
                if (!isatty(0))
-                       goto finish;
+                       return 0;
                /*
                 * TRANSLATORS: Make sure to include [Y] and [n] in your
                 * translation. The program will only accept English input
@@ -329,21 +317,35 @@ static int bisect_next_check(const struct bisect_terms *terms,
                 */
                yesno = git_prompt(_("Are you sure [Y/n]? "), PROMPT_ECHO);
                if (starts_with(yesno, "N") || starts_with(yesno, "n"))
-                       retval = -1;
-               goto finish;
-       }
-       if (!is_empty_or_missing_file(git_path_bisect_start())) {
-               retval = error(_(need_bad_and_good_revision_warning),
-                              vocab_bad, vocab_good, vocab_bad, vocab_good);
-       } else {
-               retval = error(_(need_bisect_start_warning),
-                              vocab_good, vocab_bad, vocab_good, vocab_bad);
+                       return -1;
+               return 0;
        }
 
-finish:
-       free((void *) good_glob);
-       free((void *) bad_ref);
-       return retval;
+       if (!is_empty_or_missing_file(git_path_bisect_start()))
+               return error(_(need_bad_and_good_revision_warning),
+                            vocab_bad, vocab_good, vocab_bad, vocab_good);
+       else
+               return error(_(need_bisect_start_warning),
+                            vocab_good, vocab_bad, vocab_good, vocab_bad);
+}
+
+static int bisect_next_check(const struct bisect_terms *terms,
+                            const char *current_term)
+{
+       int missing_good = 1, missing_bad = 1;
+       char *bad_ref = xstrfmt("refs/bisect/%s", terms->term_bad);
+       char *good_glob = xstrfmt("%s-*", terms->term_good);
+
+       if (ref_exists(bad_ref))
+               missing_bad = 0;
+
+       for_each_glob_ref_in(mark_good, good_glob, "refs/bisect/",
+                            (void *) &missing_good);
+
+       free(good_glob);
+       free(bad_ref);
+
+       return decide_next(terms, current_term, missing_good, missing_bad);
 }
 
 static int get_terms(struct bisect_terms *terms)
@@ -397,7 +399,7 @@ static int bisect_terms(struct bisect_terms *terms, const char *option)
 
 static int bisect_append_log_quoted(const char **argv)
 {
-       int retval = 0;
+       int res = 0;
        FILE *fp = fopen(git_path_bisect_log(), "a");
        struct strbuf orig_args = STRBUF_INIT;
 
@@ -405,25 +407,25 @@ static int bisect_append_log_quoted(const char **argv)
                return -1;
 
        if (fprintf(fp, "git bisect start") < 1) {
-               retval = -1;
+               res = -1;
                goto finish;
        }
 
        sq_quote_argv(&orig_args, argv);
        if (fprintf(fp, "%s\n", orig_args.buf) < 1)
-               retval = -1;
+               res = -1;
 
 finish:
        fclose(fp);
        strbuf_release(&orig_args);
-       return retval;
+       return res;
 }
 
 static int bisect_start(struct bisect_terms *terms, int no_checkout,
                        const char **argv, int argc)
 {
        int i, has_double_dash = 0, must_write_terms = 0, bad_seen = 0;
-       int flags, pathspec_pos, retval = 0;
+       int flags, pathspec_pos, res = 0;
        struct string_list revs = STRING_LIST_INIT_DUP;
        struct string_list states = STRING_LIST_INIT_DUP;
        struct strbuf start_head = STRBUF_INIT;
@@ -524,7 +526,7 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
                        argv_array_pushl(&argv, "checkout", start_head.buf,
                                         "--", NULL);
                        if (run_command_v_opt(argv.argv, RUN_GIT_CMD)) {
-                               retval = error(_("checking out '%s' failed."
+                               res = error(_("checking out '%s' failed."
                                                 " Try 'git bisect start "
                                                 "<valid-branch>'."),
                                               start_head.buf);
@@ -572,12 +574,12 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
 
        if (no_checkout) {
                if (get_oid(start_head.buf, &oid) < 0) {
-                       retval = error(_("invalid ref: '%s'"), start_head.buf);
+                       res = error(_("invalid ref: '%s'"), start_head.buf);
                        goto finish;
                }
                if (update_ref(NULL, "BISECT_HEAD", &oid, NULL, 0,
                               UPDATE_REFS_MSG_ON_ERR)) {
-                       retval = -1;
+                       res = -1;
                        goto finish;
                }
        }
@@ -589,26 +591,26 @@ static int bisect_start(struct bisect_terms *terms, int no_checkout,
        for (i = 0; i < states.nr; i++)
                if (bisect_write(states.items[i].string,
                                 revs.items[i].string, terms, 1)) {
-                       retval = -1;
+                       res = -1;
                        goto finish;
                }
 
        if (must_write_terms && write_terms(terms->term_bad,
                                            terms->term_good)) {
-               retval = -1;
+               res = -1;
                goto finish;
        }
 
-       retval = bisect_append_log_quoted(argv);
-       if (retval)
-               retval = -1;
+       res = bisect_append_log_quoted(argv);
+       if (res)
+               res = -1;
 
 finish:
        string_list_clear(&revs, 0);
        string_list_clear(&states, 0);
        strbuf_release(&start_head);
        strbuf_release(&bisect_names);
-       return retval;
+       return res;
 }
 
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
@@ -664,7 +666,8 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 
        switch (cmdmode) {
        case NEXT_ALL:
-               return bisect_next_all(the_repository, prefix, no_checkout);
+               res = bisect_next_all(the_repository, prefix, no_checkout);
+               break;
        case WRITE_TERMS:
                if (argc != 2)
                        return error(_("--write-terms requires two arguments"));
@@ -711,5 +714,13 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                return error("BUG: unknown subcommand '%d'", cmdmode);
        }
        free_terms(&terms);
-       return !!res;
+
+       /*
+        * Handle early success
+        * From check_merge_bases > check_good_are_ancestors_of_bad > bisect_next_all
+        */
+       if (res == BISECT_INTERNAL_SUCCESS_MERGE_BASE)
+               res = BISECT_OK;
+
+       return abs(res);
 }
index 5a4f92395f371438cbfc0582c77666622b9c0b3d..ea5d0ae3a6a6fff59424909b8be47aaabb824b7c 100644 (file)
@@ -108,6 +108,9 @@ static int check_ignore(struct dir_struct *dir,
                        int dtype = DT_UNKNOWN;
                        pattern = last_matching_pattern(dir, &the_index,
                                                        full_path, &dtype);
+                       if (!verbose && pattern &&
+                           pattern->flags & PATTERN_FLAG_NEGATIVE)
+                               pattern = NULL;
                }
                if (!quiet && (pattern || show_non_matching))
                        output_pattern(pathspec.items[i].original, pattern);
index ab7e4b1f3e7cf4f96548faf8fb0e4d45b17476d2..bf6bab80fab915242f412de7f0ced4a92d95f930 100644 (file)
@@ -335,6 +335,7 @@ static void find_non_local_tags(const struct ref *refs,
        struct string_list_item *remote_ref_item;
        const struct ref *ref;
        struct refname_hash_entry *item = NULL;
+       const int quick_flags = OBJECT_INFO_QUICK | OBJECT_INFO_SKIP_FETCH_OBJECT;
 
        refname_hash_init(&existing_refs);
        refname_hash_init(&remote_refs);
@@ -353,10 +354,9 @@ static void find_non_local_tags(const struct ref *refs,
                 */
                if (ends_with(ref->name, "^{}")) {
                        if (item &&
-                           !has_object_file_with_flags(&ref->old_oid,
-                                                       OBJECT_INFO_QUICK) &&
+                           !has_object_file_with_flags(&ref->old_oid, quick_flags) &&
                            !oidset_contains(&fetch_oids, &ref->old_oid) &&
-                           !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
+                           !has_object_file_with_flags(&item->oid, quick_flags) &&
                            !oidset_contains(&fetch_oids, &item->oid))
                                clear_item(item);
                        item = NULL;
@@ -370,7 +370,7 @@ static void find_non_local_tags(const struct ref *refs,
                 * fetch.
                 */
                if (item &&
-                   !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
+                   !has_object_file_with_flags(&item->oid, quick_flags) &&
                    !oidset_contains(&fetch_oids, &item->oid))
                        clear_item(item);
 
@@ -391,7 +391,7 @@ static void find_non_local_tags(const struct ref *refs,
         * checked to see if it needs fetching.
         */
        if (item &&
-           !has_object_file_with_flags(&item->oid, OBJECT_INFO_QUICK) &&
+           !has_object_file_with_flags(&item->oid, quick_flags) &&
            !oidset_contains(&fetch_oids, &item->oid))
                clear_item(item);
 
index 73fca2cb17e40f36240854144144eeeabc877590..02aa6ee4808a96f264a861bc49789341179056be 100644 (file)
@@ -3186,7 +3186,7 @@ static int pack_options_allow_reuse(void)
 
 static int get_object_list_from_bitmap(struct rev_info *revs)
 {
-       if (!(bitmap_git = prepare_bitmap_walk(revs)))
+       if (!(bitmap_git = prepare_bitmap_walk(revs, &filter_options)))
                return -1;
 
        if (pack_options_allow_reuse() &&
@@ -3200,7 +3200,8 @@ static int get_object_list_from_bitmap(struct rev_info *revs)
                display_progress(progress_state, nr_result);
        }
 
-       traverse_bitmap_commit_list(bitmap_git, &add_object_entry_from_bitmap);
+       traverse_bitmap_commit_list(bitmap_git, revs,
+                                   &add_object_entry_from_bitmap);
        return 0;
 }
 
@@ -3564,7 +3565,6 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        if (filter_options.choice) {
                if (!pack_to_stdout)
                        die(_("cannot use --filter without --stdout"));
-               use_bitmap_index = 0;
        }
 
        /*
index d4e3e77c8eb6daa0906f2e41ba3cacd7bc0200ba..3e624d1e008588ed063a7a960026287d34b84965 100644 (file)
@@ -15,6 +15,7 @@
 #include "sha1-array.h"
 #include "remote.h"
 #include "dir.h"
+#include "rebase.h"
 #include "refs.h"
 #include "refspec.h"
 #include "revision.h"
 #include "commit-reach.h"
 #include "sequencer.h"
 
-enum rebase_type {
-       REBASE_INVALID = -1,
-       REBASE_FALSE = 0,
-       REBASE_TRUE,
-       REBASE_PRESERVE,
-       REBASE_MERGES,
-       REBASE_INTERACTIVE
-};
-
 /**
  * Parses the value of --rebase. If value is a false value, returns
  * REBASE_FALSE. If value is a true value, returns REBASE_TRUE. If value is
@@ -45,22 +37,9 @@ enum rebase_type {
 static enum rebase_type parse_config_rebase(const char *key, const char *value,
                int fatal)
 {
-       int v = git_parse_maybe_bool(value);
-
-       if (!v)
-               return REBASE_FALSE;
-       else if (v > 0)
-               return REBASE_TRUE;
-       else if (!strcmp(value, "preserve") || !strcmp(value, "p"))
-               return REBASE_PRESERVE;
-       else if (!strcmp(value, "merges") || !strcmp(value, "m"))
-               return REBASE_MERGES;
-       else if (!strcmp(value, "interactive") || !strcmp(value, "i"))
-               return REBASE_INTERACTIVE;
-       /*
-        * Please update _git_config() in git-completion.bash when you
-        * add new rebase modes.
-        */
+       enum rebase_type v = rebase_parse_value(value);
+       if (v != REBASE_INVALID)
+               return v;
 
        if (fatal)
                die(_("Invalid value for %s: %s"), key, value);
index 6154ad8fa516717956deb63a5e4892f0c80de529..7bff0c87bc042835a779c594edaceb3b70926f84 100644 (file)
@@ -44,14 +44,22 @@ static GIT_PATH_FUNC(merge_dir, "rebase-merge")
 
 enum rebase_type {
        REBASE_UNSPECIFIED = -1,
-       REBASE_AM,
+       REBASE_APPLY,
        REBASE_MERGE,
-       REBASE_INTERACTIVE,
        REBASE_PRESERVE_MERGES
 };
 
+enum empty_type {
+       EMPTY_UNSPECIFIED = -1,
+       EMPTY_DROP,
+       EMPTY_KEEP,
+       EMPTY_ASK
+};
+
 struct rebase_options {
        enum rebase_type type;
+       enum empty_type empty;
+       const char *default_backend;
        const char *state_dir;
        struct commit *upstream;
        const char *upstream_name;
@@ -77,7 +85,6 @@ struct rebase_options {
        const char *action;
        int signoff;
        int allow_rerere_autoupdate;
-       int keep_empty;
        int autosquash;
        char *gpg_sign_opt;
        int autostash;
@@ -92,6 +99,8 @@ struct rebase_options {
 
 #define REBASE_OPTIONS_INIT {                          \
                .type = REBASE_UNSPECIFIED,             \
+               .empty = EMPTY_UNSPECIFIED,             \
+               .default_backend = "merge",             \
                .flags = REBASE_NO_QUIET,               \
                .git_am_opts = ARGV_ARRAY_INIT,         \
                .git_format_patch_opt = STRBUF_INIT     \
@@ -110,6 +119,9 @@ static struct replay_opts get_replay_opts(const struct rebase_options *opts)
                replay.allow_rerere_auto = opts->allow_rerere_autoupdate;
        replay.allow_empty = 1;
        replay.allow_empty_message = opts->allow_empty_message;
+       replay.drop_redundant_commits = (opts->empty == EMPTY_DROP);
+       replay.keep_redundant_commits = (opts->empty == EMPTY_KEEP);
+       replay.quiet = !(opts->flags & REBASE_NO_QUIET);
        replay.verbose = opts->flags & REBASE_VERBOSE;
        replay.reschedule_failed_exec = opts->reschedule_failed_exec;
        replay.gpg_sign = xstrdup_or_null(opts->gpg_sign_opt);
@@ -329,8 +341,8 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
 
        argv_array_pushl(&make_script_args, "", revisions, NULL);
        if (opts->restrict_revision)
-               argv_array_push(&make_script_args,
-                               oid_to_hex(&opts->restrict_revision->object.oid));
+               argv_array_pushf(&make_script_args, "^%s",
+                                oid_to_hex(&opts->restrict_revision->object.oid));
 
        ret = sequencer_make_script(the_repository, &todo_list.buf,
                                    make_script_args.argc, make_script_args.argv,
@@ -359,7 +371,7 @@ static int do_interactive_rebase(struct rebase_options *opts, unsigned flags)
        return ret;
 }
 
-static int run_rebase_interactive(struct rebase_options *opts,
+static int run_sequencer_rebase(struct rebase_options *opts,
                                  enum action command)
 {
        unsigned flags = 0;
@@ -367,7 +379,6 @@ static int run_rebase_interactive(struct rebase_options *opts,
 
        git_config_get_bool("rebase.abbreviatecommands", &abbreviate_commands);
 
-       flags |= opts->keep_empty ? TODO_LIST_KEEP_EMPTY : 0;
        flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
        flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
        flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
@@ -431,6 +442,21 @@ static int run_rebase_interactive(struct rebase_options *opts,
        return ret;
 }
 
+static int parse_opt_keep_empty(const struct option *opt, const char *arg,
+                               int unset)
+{
+       struct rebase_options *opts = opt->value;
+
+       BUG_ON_OPT_ARG(arg);
+
+       /*
+        * If we ever want to remap --keep-empty to --empty=keep, insert:
+        *      opts->empty = unset ? EMPTY_UNSPECIFIED : EMPTY_KEEP;
+        */
+       opts->type = REBASE_MERGE;
+       return 0;
+}
+
 static const char * const builtin_rebase_interactive_usage[] = {
        N_("git rebase--interactive [<options>]"),
        NULL
@@ -444,9 +470,13 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
        struct option options[] = {
                OPT_NEGBIT(0, "ff", &opts.flags, N_("allow fast-forward"),
                           REBASE_FORCE),
-               OPT_BOOL(0, "keep-empty", &opts.keep_empty, N_("keep empty commits")),
-               OPT_BOOL(0, "allow-empty-message", &opts.allow_empty_message,
-                        N_("allow commits with empty messages")),
+               { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+                       N_("(DEPRECATED) keep empty commits"),
+                       PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
+                       parse_opt_keep_empty },
+               OPT_BOOL_F(0, "allow-empty-message", &opts.allow_empty_message,
+                          N_("allow commits with empty messages"),
+                          PARSE_OPT_HIDDEN),
                OPT_BOOL(0, "rebase-merges", &opts.rebase_merges, N_("rebase merge commits")),
                OPT_BOOL(0, "rebase-cousins", &opts.rebase_cousins,
                         N_("keep original branch points of cousins")),
@@ -516,28 +546,26 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
                warning(_("--[no-]rebase-cousins has no effect without "
                          "--rebase-merges"));
 
-       return !!run_rebase_interactive(&opts, command);
+       return !!run_sequencer_rebase(&opts, command);
 }
 
-static int is_interactive(struct rebase_options *opts)
+static int is_merge(struct rebase_options *opts)
 {
-       return opts->type == REBASE_INTERACTIVE ||
+       return opts->type == REBASE_MERGE ||
                opts->type == REBASE_PRESERVE_MERGES;
 }
 
-static void imply_interactive(struct rebase_options *opts, const char *option)
+static void imply_merge(struct rebase_options *opts, const char *option)
 {
        switch (opts->type) {
-       case REBASE_AM:
+       case REBASE_APPLY:
                die(_("%s requires an interactive rebase"), option);
                break;
-       case REBASE_INTERACTIVE:
+       case REBASE_MERGE:
        case REBASE_PRESERVE_MERGES:
                break;
-       case REBASE_MERGE:
-               /* we now implement --merge via --interactive */
        default:
-               opts->type = REBASE_INTERACTIVE; /* implied */
+               opts->type = REBASE_MERGE; /* implied */
                break;
        }
 }
@@ -663,8 +691,8 @@ static int rebase_write_basic_state(struct rebase_options *opts)
                   opts->onto ? oid_to_hex(&opts->onto->object.oid) : "");
        write_file(state_dir_path("orig-head", opts), "%s",
                   oid_to_hex(&opts->orig_head));
-       write_file(state_dir_path("quiet", opts), "%s",
-                  opts->flags & REBASE_NO_QUIET ? "" : "t");
+       if (!(opts->flags & REBASE_NO_QUIET))
+               write_file(state_dir_path("quiet", opts), "%s", "");
        if (opts->flags & REBASE_VERBOSE)
                write_file(state_dir_path("verbose", opts), "%s", "");
        if (opts->strategy)
@@ -746,7 +774,7 @@ static int finish_rebase(struct rebase_options *opts)
         * user should see them.
         */
        run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
-       if (opts->type == REBASE_INTERACTIVE) {
+       if (opts->type == REBASE_MERGE) {
                struct replay_opts replay = REPLAY_OPTS_INIT;
 
                replay.action = REPLAY_INTERACTIVE_REBASE;
@@ -1079,8 +1107,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        int status;
        const char *backend, *backend_func;
 
-       if (opts->type == REBASE_INTERACTIVE) {
-               /* Run builtin interactive rebase */
+       if (opts->type == REBASE_MERGE) {
+               /* Run sequencer-based rebase */
                setenv("GIT_CHERRY_PICK_HELP", resolvemsg, 1);
                if (!(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
                        setenv("GIT_SEQUENCE_EDITOR", ":", 1);
@@ -1093,11 +1121,11 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
                        opts->gpg_sign_opt = tmp;
                }
 
-               status = run_rebase_interactive(opts, action);
+               status = run_sequencer_rebase(opts, action);
                goto finished_rebase;
        }
 
-       if (opts->type == REBASE_AM) {
+       if (opts->type == REBASE_APPLY) {
                status = run_am(opts);
                goto finished_rebase;
        }
@@ -1117,8 +1145,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        add_var(&script_snippet, "revisions", opts->revisions);
        add_var(&script_snippet, "restrict_revision", opts->restrict_revision ?
                oid_to_hex(&opts->restrict_revision->object.oid) : NULL);
-       add_var(&script_snippet, "GIT_QUIET",
-               opts->flags & REBASE_NO_QUIET ? "" : "t");
        sq_quote_argv_pretty(&buf, opts->git_am_opts.argv);
        add_var(&script_snippet, "git_am_opt", buf.buf);
        strbuf_release(&buf);
@@ -1136,7 +1162,6 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
                opts->allow_rerere_autoupdate ?
                        opts->allow_rerere_autoupdate == RERERE_AUTOUPDATE ?
                        "--rerere-autoupdate" : "--no-rerere-autoupdate" : "");
-       add_var(&script_snippet, "keep_empty", opts->keep_empty ? "yes" : "");
        add_var(&script_snippet, "autosquash", opts->autosquash ? "t" : "");
        add_var(&script_snippet, "gpg_sign_opt", opts->gpg_sign_opt);
        add_var(&script_snippet, "cmd", opts->cmd);
@@ -1154,7 +1179,7 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
        add_var(&script_snippet, "git_format_patch_opt",
                opts->git_format_patch_opt.buf);
 
-       if (is_interactive(opts) &&
+       if (is_merge(opts) &&
            !(opts->flags & REBASE_INTERACTIVE_EXPLICIT)) {
                strbuf_addstr(&script_snippet,
                              "GIT_SEQUENCE_EDITOR=:; export GIT_SEQUENCE_EDITOR; ");
@@ -1179,8 +1204,8 @@ static int run_specific_rebase(struct rebase_options *opts, enum action action)
 finished_rebase:
        if (opts->dont_finish_rebase)
                ; /* do nothing */
-       else if (opts->type == REBASE_INTERACTIVE)
-               ; /* interactive rebase cleans up after itself */
+       else if (opts->type == REBASE_MERGE)
+               ; /* merge backend cleans up after itself */
        else if (status == 0) {
                if (!file_exists(state_dir_path("stopped-sha", opts)))
                        finish_rebase(opts);
@@ -1238,6 +1263,10 @@ static int rebase_config(const char *var, const char *value, void *data)
                return 0;
        }
 
+       if (!strcmp(var, "rebase.backend")) {
+               return git_config_string(&opts->default_backend, var, value);
+       }
+
        return git_default_config(var, value, data);
 }
 
@@ -1301,6 +1330,18 @@ done:
        return res && is_linear_history(onto, head);
 }
 
+static int parse_opt_am(const struct option *opt, const char *arg, int unset)
+{
+       struct rebase_options *opts = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
+
+       opts->type = REBASE_APPLY;
+
+       return 0;
+}
+
 /* -i followed by -m is still -i */
 static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
 {
@@ -1309,7 +1350,7 @@ static int parse_opt_merge(const struct option *opt, const char *arg, int unset)
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
 
-       if (!is_interactive(opts))
+       if (!is_merge(opts))
                opts->type = REBASE_MERGE;
 
        return 0;
@@ -1324,12 +1365,35 @@ static int parse_opt_interactive(const struct option *opt, const char *arg,
        BUG_ON_OPT_NEG(unset);
        BUG_ON_OPT_ARG(arg);
 
-       opts->type = REBASE_INTERACTIVE;
+       opts->type = REBASE_MERGE;
        opts->flags |= REBASE_INTERACTIVE_EXPLICIT;
 
        return 0;
 }
 
+static enum empty_type parse_empty_value(const char *value)
+{
+       if (!strcasecmp(value, "drop"))
+               return EMPTY_DROP;
+       else if (!strcasecmp(value, "keep"))
+               return EMPTY_KEEP;
+       else if (!strcasecmp(value, "ask"))
+               return EMPTY_ASK;
+
+       die(_("unrecognized empty type '%s'; valid values are \"drop\", \"keep\", and \"ask\"."), value);
+}
+
+static int parse_opt_empty(const struct option *opt, const char *arg, int unset)
+{
+       struct rebase_options *options = opt->value;
+       enum empty_type value = parse_empty_value(arg);
+
+       BUG_ON_OPT_NEG(unset);
+
+       options->empty = value;
+       return 0;
+}
+
 static void NORETURN error_on_missing_default_upstream(void)
 {
        struct branch *current_branch = branch_get(NULL);
@@ -1365,14 +1429,14 @@ static void set_reflog_action(struct rebase_options *options)
        const char *env;
        struct strbuf buf = STRBUF_INIT;
 
-       if (!is_interactive(options))
+       if (!is_merge(options))
                return;
 
        env = getenv(GIT_REFLOG_ACTION_ENVIRONMENT);
        if (env && strcmp("rebase", env))
                return; /* only override it if it is "rebase" */
 
-       strbuf_addf(&buf, "rebase -i (%s)", options->action);
+       strbuf_addf(&buf, "rebase (%s)", options->action);
        setenv(GIT_REFLOG_ACTION_ENVIRONMENT, buf.buf, 1);
        strbuf_release(&buf);
 }
@@ -1410,6 +1474,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        struct object_id squash_onto;
        char *squash_onto_name = NULL;
        int reschedule_failed_exec = -1;
+       int allow_preemptive_ff = 1;
        struct option builtin_rebase_options[] = {
                OPT_STRING(0, "onto", &options.onto_name,
                           N_("revision"),
@@ -1420,7 +1485,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                         N_("allow pre-rebase hook to run")),
                OPT_NEGBIT('q', "quiet", &options.flags,
                           N_("be quiet. implies --no-stat"),
-                          REBASE_NO_QUIET| REBASE_VERBOSE | REBASE_DIFFSTAT),
+                          REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
                OPT_BIT('v', "verbose", &options.flags,
                        N_("display a diffstat of what changed upstream"),
                        REBASE_NO_QUIET | REBASE_VERBOSE | REBASE_DIFFSTAT),
@@ -1461,6 +1526,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                OPT_CMDMODE(0, "show-current-patch", &action,
                            N_("show the patch file being applied or merged"),
                            ACTION_SHOW_CURRENT_PATCH),
+               { OPTION_CALLBACK, 0, "apply", &options, NULL,
+                       N_("use apply strategies to rebase"),
+                       PARSE_OPT_NOARG | PARSE_OPT_NONEG,
+                       parse_opt_am },
                { OPTION_CALLBACK, 'm', "merge", &options, NULL,
                        N_("use merging strategies to rebase"),
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG,
@@ -1474,8 +1543,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                                 "ignoring them"),
                              REBASE_PRESERVE_MERGES, PARSE_OPT_HIDDEN),
                OPT_RERERE_AUTOUPDATE(&options.allow_rerere_autoupdate),
-               OPT_BOOL('k', "keep-empty", &options.keep_empty,
-                        N_("preserve empty commits during rebase")),
+               OPT_CALLBACK_F(0, "empty", &options, N_("{drop,keep,ask}"),
+                              N_("how to handle commits that become empty"),
+                              PARSE_OPT_NONEG, parse_opt_empty),
+               { OPTION_CALLBACK, 'k', "keep-empty", &options, NULL,
+                       N_("(DEPRECATED) keep empty commits"),
+                       PARSE_OPT_NOARG | PARSE_OPT_HIDDEN,
+                       parse_opt_keep_empty },
                OPT_BOOL(0, "autosquash", &options.autosquash,
                         N_("move commits that begin with "
                            "squash!/fixup! under -i")),
@@ -1487,9 +1561,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                OPT_STRING_LIST('x', "exec", &exec, N_("exec"),
                                N_("add exec lines after each commit of the "
                                   "editable list")),
-               OPT_BOOL(0, "allow-empty-message",
-                        &options.allow_empty_message,
-                        N_("allow rebasing commits with empty messages")),
+               OPT_BOOL_F(0, "allow-empty-message",
+                          &options.allow_empty_message,
+                          N_("allow rebasing commits with empty messages"),
+                          PARSE_OPT_HIDDEN),
                {OPTION_STRING, 'r', "rebase-merges", &rebase_merges,
                        N_("mode"),
                        N_("try to rebase merges instead of skipping them"),
@@ -1529,7 +1604,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                die(_("It looks like 'git am' is in progress. Cannot rebase."));
 
        if (is_directory(apply_dir())) {
-               options.type = REBASE_AM;
+               options.type = REBASE_APPLY;
                options.state_dir = apply_dir();
        } else if (is_directory(merge_dir())) {
                strbuf_reset(&buf);
@@ -1541,7 +1616,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        strbuf_reset(&buf);
                        strbuf_addf(&buf, "%s/interactive", merge_dir());
                        if(file_exists(buf.buf)) {
-                               options.type = REBASE_INTERACTIVE;
+                               options.type = REBASE_MERGE;
                                options.flags |= REBASE_INTERACTIVE_EXPLICIT;
                        } else
                                options.type = REBASE_MERGE;
@@ -1581,12 +1656,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                die(_("No rebase in progress?"));
        setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
 
-       if (action == ACTION_EDIT_TODO && !is_interactive(&options))
+       if (action == ACTION_EDIT_TODO && !is_merge(&options))
                die(_("The --edit-todo action can only be used during "
                      "interactive rebase."));
 
        if (trace2_is_enabled()) {
-               if (is_interactive(&options))
+               if (is_merge(&options))
                        trace2_cmd_mode("interactive");
                else if (exec.nr)
                        trace2_cmd_mode("interactive-exec");
@@ -1662,7 +1737,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                goto cleanup;
        }
        case ACTION_QUIT: {
-               if (options.type == REBASE_INTERACTIVE) {
+               if (options.type == REBASE_MERGE) {
                        struct replay_opts replay = REPLAY_OPTS_INIT;
 
                        replay.action = REPLAY_INTERACTIVE_REBASE;
@@ -1711,13 +1786,20 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                    state_dir_base, cmd_live_rebase, buf.buf);
        }
 
+       if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) ||
+           (action != ACTION_NONE) ||
+           (exec.nr > 0) ||
+           options.autosquash) {
+               allow_preemptive_ff = 0;
+       }
+
        for (i = 0; i < options.git_am_opts.argc; i++) {
                const char *option = options.git_am_opts.argv[i], *p;
                if (!strcmp(option, "--committer-date-is-author-date") ||
                    !strcmp(option, "--ignore-date") ||
                    !strcmp(option, "--whitespace=fix") ||
                    !strcmp(option, "--whitespace=strip"))
-                       options.flags |= REBASE_FORCE;
+                       allow_preemptive_ff = 0;
                else if (skip_prefix(option, "-C", &p)) {
                        while (*p)
                                if (!isdigit(*(p++)))
@@ -1737,8 +1819,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (!(options.flags & REBASE_NO_QUIET))
                argv_array_push(&options.git_am_opts, "-q");
 
-       if (options.keep_empty)
-               imply_interactive(&options, "--keep-empty");
+       if (options.empty != EMPTY_UNSPECIFIED)
+               imply_merge(&options, "--empty");
 
        if (gpg_sign) {
                free(options.gpg_sign_opt);
@@ -1748,7 +1830,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (exec.nr) {
                int i;
 
-               imply_interactive(&options, "--exec");
+               imply_merge(&options, "--exec");
 
                strbuf_reset(&buf);
                for (i = 0; i < exec.nr; i++)
@@ -1764,7 +1846,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                else if (strcmp("no-rebase-cousins", rebase_merges))
                        die(_("Unknown mode: %s"), rebase_merges);
                options.rebase_merges = 1;
-               imply_interactive(&options, "--rebase-merges");
+               imply_merge(&options, "--rebase-merges");
        }
 
        if (strategy_options.nr) {
@@ -1783,10 +1865,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        if (options.strategy) {
                options.strategy = xstrdup(options.strategy);
                switch (options.type) {
-               case REBASE_AM:
+               case REBASE_APPLY:
                        die(_("--strategy requires --merge or --interactive"));
                case REBASE_MERGE:
-               case REBASE_INTERACTIVE:
                case REBASE_PRESERVE_MERGES:
                        /* compatible */
                        break;
@@ -1799,47 +1880,65 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        }
 
        if (options.type == REBASE_MERGE)
-               imply_interactive(&options, "--merge");
+               imply_merge(&options, "--merge");
 
        if (options.root && !options.onto_name)
-               imply_interactive(&options, "--root without --onto");
+               imply_merge(&options, "--root without --onto");
 
        if (isatty(2) && options.flags & REBASE_NO_QUIET)
                strbuf_addstr(&options.git_format_patch_opt, " --progress");
 
+       if (options.git_am_opts.argc || options.type == REBASE_APPLY) {
+               /* all am options except -q are compatible only with --apply */
+               for (i = options.git_am_opts.argc - 1; i >= 0; i--)
+                       if (strcmp(options.git_am_opts.argv[i], "-q"))
+                               break;
+
+               if (i >= 0) {
+                       if (is_merge(&options))
+                               die(_("cannot combine apply options with "
+                                     "merge options"));
+                       else
+                               options.type = REBASE_APPLY;
+               }
+       }
+
+       if (options.type == REBASE_UNSPECIFIED) {
+               if (!strcmp(options.default_backend, "merge"))
+                       imply_merge(&options, "--merge");
+               else if (!strcmp(options.default_backend, "apply"))
+                       options.type = REBASE_APPLY;
+               else
+                       die(_("Unknown rebase backend: %s"),
+                           options.default_backend);
+       }
+
        switch (options.type) {
        case REBASE_MERGE:
-       case REBASE_INTERACTIVE:
        case REBASE_PRESERVE_MERGES:
                options.state_dir = merge_dir();
                break;
-       case REBASE_AM:
+       case REBASE_APPLY:
                options.state_dir = apply_dir();
                break;
        default:
-               /* the default rebase backend is `--am` */
-               options.type = REBASE_AM;
-               options.state_dir = apply_dir();
-               break;
+               BUG("options.type was just set above; should be unreachable.");
        }
 
-       if (reschedule_failed_exec > 0 && !is_interactive(&options))
+       if (options.empty == EMPTY_UNSPECIFIED) {
+               if (options.flags & REBASE_INTERACTIVE_EXPLICIT)
+                       options.empty = EMPTY_ASK;
+               else if (exec.nr > 0)
+                       options.empty = EMPTY_KEEP;
+               else
+                       options.empty = EMPTY_DROP;
+       }
+       if (reschedule_failed_exec > 0 && !is_merge(&options))
                die(_("--reschedule-failed-exec requires "
                      "--exec or --interactive"));
        if (reschedule_failed_exec >= 0)
                options.reschedule_failed_exec = reschedule_failed_exec;
 
-       if (options.git_am_opts.argc) {
-               /* all am options except -q are compatible only with --am */
-               for (i = options.git_am_opts.argc - 1; i >= 0; i--)
-                       if (strcmp(options.git_am_opts.argv[i], "-q"))
-                               break;
-
-               if (is_interactive(&options) && i >= 0)
-                       die(_("cannot combine am options with either "
-                             "interactive or merge options"));
-       }
-
        if (options.signoff) {
                if (options.type == REBASE_PRESERVE_MERGES)
                        die("cannot combine '--signoff' with "
@@ -1945,10 +2044,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                /* Is it a local branch? */
                strbuf_reset(&buf);
                strbuf_addf(&buf, "refs/heads/%s", branch_name);
-               if (!read_ref(buf.buf, &options.orig_head))
+               if (!read_ref(buf.buf, &options.orig_head)) {
+                       die_if_checked_out(buf.buf, 1);
                        options.head_name = xstrdup(buf.buf);
                /* If not is it a valid ref (branch or commit)? */
-               else if (!get_oid(branch_name, &options.orig_head))
+               else if (!get_oid(branch_name, &options.orig_head))
                        options.head_name = NULL;
                else
                        die(_("fatal: no such branch/commit '%s'"),
@@ -2045,12 +2145,14 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        /*
         * Check if we are already based on onto with linear history,
         * in which case we could fast-forward without replacing the commits
-        * with new commits recreated by replaying their changes. This
-        * optimization must not be done if this is an interactive rebase.
+        * with new commits recreated by replaying their changes.
+        *
+        * Note that can_fast_forward() initializes merge_base, so we have to
+        * call it before checking allow_preemptive_ff.
         */
        if (can_fast_forward(options.onto, options.upstream, options.restrict_revision,
                    &options.orig_head, &merge_base) &&
-           !is_interactive(&options)) {
+           allow_preemptive_ff) {
                int flag;
 
                if (!(options.flags & REBASE_FORCE)) {
@@ -2133,7 +2235,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                diff_flush(&opts);
        }
 
-       if (is_interactive(&options))
+       if (is_merge(&options))
                goto run_rebase;
 
        /* Detach HEAD and reset the tree */
index 411e0b4d999acb36fb05ebde7598568f2c88dd4b..2cc18bbffdcf41cf2510becf6fcdd74c5946494a 100644 (file)
@@ -27,6 +27,7 @@
 #include "object-store.h"
 #include "protocol.h"
 #include "commit-reach.h"
+#include "worktree.h"
 
 static const char * const receive_pack_usage[] = {
        N_("git receive-pack <git-dir>"),
@@ -816,16 +817,6 @@ static int run_update_hook(struct command *cmd)
        return finish_command(&proc);
 }
 
-static int is_ref_checked_out(const char *ref)
-{
-       if (is_bare_repository())
-               return 0;
-
-       if (!head_name)
-               return 0;
-       return !strcmp(head_name, ref);
-}
-
 static char *refuse_unconfigured_deny_msg =
        N_("By default, updating the current branch in a non-bare repository\n"
           "is denied, because it will make the index and work tree inconsistent\n"
@@ -997,16 +988,26 @@ static const char *push_to_checkout(unsigned char *hash,
                return NULL;
 }
 
-static const char *update_worktree(unsigned char *sha1)
+static const char *update_worktree(unsigned char *sha1, const struct worktree *worktree)
 {
-       const char *retval;
-       const char *work_tree = git_work_tree_cfg ? git_work_tree_cfg : "..";
+       const char *retval, *work_tree, *git_dir = NULL;
        struct argv_array env = ARGV_ARRAY_INIT;
 
+       if (worktree && worktree->path)
+               work_tree = worktree->path;
+       else if (git_work_tree_cfg)
+               work_tree = git_work_tree_cfg;
+       else
+               work_tree = "..";
+
        if (is_bare_repository())
                return "denyCurrentBranch = updateInstead needs a worktree";
+       if (worktree)
+               git_dir = get_worktree_git_dir(worktree);
+       if (!git_dir)
+               git_dir = get_git_dir();
 
-       argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(get_git_dir()));
+       argv_array_pushf(&env, "GIT_DIR=%s", absolute_path(git_dir));
 
        if (!find_hook(push_to_checkout_hook))
                retval = push_to_deploy(sha1, &env, work_tree);
@@ -1026,6 +1027,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        struct object_id *old_oid = &cmd->old_oid;
        struct object_id *new_oid = &cmd->new_oid;
        int do_update_worktree = 0;
+       const struct worktree *worktree = is_bare_repository() ? NULL : find_shared_symref("HEAD", name);
 
        /* only refs/... are allowed */
        if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@@ -1037,7 +1039,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        free(namespaced_name);
        namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
 
-       if (is_ref_checked_out(namespaced_name)) {
+       if (worktree) {
                switch (deny_current_branch) {
                case DENY_IGNORE:
                        break;
@@ -1069,7 +1071,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                        return "deletion prohibited";
                }
 
-               if (head_name && !strcmp(namespaced_name, head_name)) {
+               if (worktree || (head_name && !strcmp(namespaced_name, head_name))) {
                        switch (deny_delete_current) {
                        case DENY_IGNORE:
                                break;
@@ -1118,7 +1120,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        }
 
        if (do_update_worktree) {
-               ret = update_worktree(new_oid->hash);
+               ret = update_worktree(new_oid->hash, find_shared_symref("HEAD", name));
                if (ret)
                        return ret;
        }
index 96bbe828fe20b138d3949ad7080621380c1fe00d..555d4c896c5fba060f3ef65ba591bbdb7d16a355 100644 (file)
@@ -6,6 +6,7 @@
 #include "string-list.h"
 #include "strbuf.h"
 #include "run-command.h"
+#include "rebase.h"
 #include "refs.h"
 #include "refspec.h"
 #include "object-store.h"
@@ -248,9 +249,8 @@ static int add(int argc, const char **argv)
 struct branch_info {
        char *remote_name;
        struct string_list merge;
-       enum {
-               NO_REBASE, NORMAL_REBASE, INTERACTIVE_REBASE, REBASE_MERGES
-       } rebase;
+       enum rebase_type rebase;
+       char *push_remote_name;
 };
 
 static struct string_list branch_list = STRING_LIST_INIT_NODUP;
@@ -264,59 +264,69 @@ static const char *abbrev_ref(const char *name, const char *prefix)
 
 static int config_read_branches(const char *key, const char *value, void *cb)
 {
-       if (starts_with(key, "branch.")) {
-               const char *orig_key = key;
-               char *name;
-               struct string_list_item *item;
-               struct branch_info *info;
-               enum { REMOTE, MERGE, REBASE } type;
-               size_t key_len;
-
-               key += 7;
-               if (strip_suffix(key, ".remote", &key_len)) {
-                       name = xmemdupz(key, key_len);
-                       type = REMOTE;
-               } else if (strip_suffix(key, ".merge", &key_len)) {
-                       name = xmemdupz(key, key_len);
-                       type = MERGE;
-               } else if (strip_suffix(key, ".rebase", &key_len)) {
-                       name = xmemdupz(key, key_len);
-                       type = REBASE;
-               } else
-                       return 0;
+       const char *orig_key = key;
+       char *name;
+       struct string_list_item *item;
+       struct branch_info *info;
+       enum { REMOTE, MERGE, REBASE, PUSH_REMOTE } type;
+       size_t key_len;
 
-               item = string_list_insert(&branch_list, name);
+       if (!starts_with(key, "branch."))
+               return 0;
 
-               if (!item->util)
-                       item->util = xcalloc(1, sizeof(struct branch_info));
-               info = item->util;
-               if (type == REMOTE) {
-                       if (info->remote_name)
-                               warning(_("more than one %s"), orig_key);
-                       info->remote_name = xstrdup(value);
-               } else if (type == MERGE) {
-                       char *space = strchr(value, ' ');
-                       value = abbrev_branch(value);
-                       while (space) {
-                               char *merge;
-                               merge = xstrndup(value, space - value);
-                               string_list_append(&info->merge, merge);
-                               value = abbrev_branch(space + 1);
-                               space = strchr(value, ' ');
-                       }
-                       string_list_append(&info->merge, xstrdup(value));
-               } else {
-                       int v = git_parse_maybe_bool(value);
-                       if (v >= 0)
-                               info->rebase = v;
-                       else if (!strcmp(value, "preserve"))
-                               info->rebase = NORMAL_REBASE;
-                       else if (!strcmp(value, "merges"))
-                               info->rebase = REBASE_MERGES;
-                       else if (!strcmp(value, "interactive"))
-                               info->rebase = INTERACTIVE_REBASE;
+       key += strlen("branch.");
+       if (strip_suffix(key, ".remote", &key_len))
+               type = REMOTE;
+       else if (strip_suffix(key, ".merge", &key_len))
+               type = MERGE;
+       else if (strip_suffix(key, ".rebase", &key_len))
+               type = REBASE;
+       else if (strip_suffix(key, ".pushremote", &key_len))
+               type = PUSH_REMOTE;
+       else
+               return 0;
+       name = xmemdupz(key, key_len);
+
+       item = string_list_insert(&branch_list, name);
+
+       if (!item->util)
+               item->util = xcalloc(1, sizeof(struct branch_info));
+       info = item->util;
+       switch (type) {
+       case REMOTE:
+               if (info->remote_name)
+                       warning(_("more than one %s"), orig_key);
+               info->remote_name = xstrdup(value);
+               break;
+       case MERGE: {
+               char *space = strchr(value, ' ');
+               value = abbrev_branch(value);
+               while (space) {
+                       char *merge;
+                       merge = xstrndup(value, space - value);
+                       string_list_append(&info->merge, merge);
+                       value = abbrev_branch(space + 1);
+                       space = strchr(value, ' ');
                }
+               string_list_append(&info->merge, xstrdup(value));
+               break;
+       }
+       case REBASE:
+               /*
+                * Consider invalid values as false and check the
+                * truth value with >= REBASE_TRUE.
+                */
+               info->rebase = rebase_parse_value(value);
+               break;
+       case PUSH_REMOTE:
+               if (info->push_remote_name)
+                       warning(_("more than one %s"), orig_key);
+               info->push_remote_name = xstrdup(value);
+               break;
+       default:
+               BUG("unexpected type=%d", type);
        }
+
        return 0;
 }
 
@@ -605,6 +615,56 @@ static int migrate_file(struct remote *remote)
        return 0;
 }
 
+struct push_default_info
+{
+       const char *old_name;
+       enum config_scope scope;
+       struct strbuf origin;
+       int linenr;
+};
+
+static int config_read_push_default(const char *key, const char *value,
+       void *cb)
+{
+       struct push_default_info* info = cb;
+       if (strcmp(key, "remote.pushdefault") ||
+           !value || strcmp(value, info->old_name))
+               return 0;
+
+       info->scope = current_config_scope();
+       strbuf_reset(&info->origin);
+       strbuf_addstr(&info->origin, current_config_name());
+       info->linenr = current_config_line();
+
+       return 0;
+}
+
+static void handle_push_default(const char* old_name, const char* new_name)
+{
+       struct push_default_info push_default = {
+               old_name, CONFIG_SCOPE_UNKNOWN, STRBUF_INIT, -1 };
+       git_config(config_read_push_default, &push_default);
+       if (push_default.scope >= CONFIG_SCOPE_COMMAND)
+               ; /* pass */
+       else if (push_default.scope >= CONFIG_SCOPE_LOCAL) {
+               int result = git_config_set_gently("remote.pushDefault",
+                                                  new_name);
+               if (new_name && result && result != CONFIG_NOTHING_SET)
+                       die(_("could not set '%s'"), "remote.pushDefault");
+               else if (!new_name && result && result != CONFIG_NOTHING_SET)
+                       die(_("could not unset '%s'"), "remote.pushDefault");
+       } else if (push_default.scope >= CONFIG_SCOPE_SYSTEM) {
+               /* warn */
+               warning(_("The %s configuration remote.pushDefault in:\n"
+                         "\t%s:%d\n"
+                         "now names the non-existent remote '%s'"),
+                       config_scope_name(push_default.scope),
+                       push_default.origin.buf, push_default.linenr,
+                       old_name);
+       }
+}
+
+
 static int mv(int argc, const char **argv)
 {
        struct option options[] = {
@@ -680,6 +740,11 @@ static int mv(int argc, const char **argv)
                        strbuf_addf(&buf, "branch.%s.remote", item->string);
                        git_config_set(buf.buf, rename.new_name);
                }
+               if (info->push_remote_name && !strcmp(info->push_remote_name, rename.old_name)) {
+                       strbuf_reset(&buf);
+                       strbuf_addf(&buf, "branch.%s.pushremote", item->string);
+                       git_config_set(buf.buf, rename.new_name);
+               }
        }
 
        if (!refspec_updated)
@@ -735,6 +800,9 @@ static int mv(int argc, const char **argv)
                        die(_("creating '%s' failed"), buf.buf);
        }
        string_list_clear(&remote_branches, 1);
+
+       handle_push_default(rename.old_name, rename.new_name);
+
        return 0;
 }
 
@@ -781,6 +849,13 @@ static int rm(int argc, const char **argv)
                                        die(_("could not unset '%s'"), buf.buf);
                        }
                }
+               if (info->push_remote_name && !strcmp(info->push_remote_name, remote->name)) {
+                       strbuf_reset(&buf);
+                       strbuf_addf(&buf, "branch.%s.pushremote", item->string);
+                       result = git_config_set_gently(buf.buf, NULL);
+                       if (result && result != CONFIG_NOTHING_SET)
+                               die(_("could not unset '%s'"), buf.buf);
+               }
        }
 
        /*
@@ -813,6 +888,8 @@ static int rm(int argc, const char **argv)
                strbuf_addf(&buf, "remote.%s", remote->name);
                if (git_config_rename_section(buf.buf, NULL) < 1)
                        return error(_("Could not remove config section '%s'"), buf.buf);
+
+               handle_push_default(remote->name, NULL);
        }
 
        return result;
@@ -943,7 +1020,7 @@ static int add_local_to_show_info(struct string_list_item *branch_item, void *cb
                return 0;
        if ((n = strlen(branch_item->string)) > show_info->width)
                show_info->width = n;
-       if (branch_info->rebase)
+       if (branch_info->rebase >= REBASE_TRUE)
                show_info->any_rebase = 1;
 
        item = string_list_insert(show_info->list, branch_item->string);
@@ -960,16 +1037,16 @@ static int show_local_info_item(struct string_list_item *item, void *cb_data)
        int width = show_info->width + 4;
        int i;
 
-       if (branch_info->rebase && branch_info->merge.nr > 1) {
+       if (branch_info->rebase >= REBASE_TRUE && branch_info->merge.nr > 1) {
                error(_("invalid branch.%s.merge; cannot rebase onto > 1 branch"),
                        item->string);
                return 0;
        }
 
        printf("    %-*s ", show_info->width, item->string);
-       if (branch_info->rebase) {
+       if (branch_info->rebase >= REBASE_TRUE) {
                const char *msg;
-               if (branch_info->rebase == INTERACTIVE_REBASE)
+               if (branch_info->rebase == REBASE_INTERACTIVE)
                        msg = _("rebases interactively onto remote %s");
                else if (branch_info->rebase == REBASE_MERGES)
                        msg = _("rebases interactively (with merges) onto "
index e28d62ec649425627362cc7cbc3f4cf277476363..f520111eda09f1d167524b31919daaea3e5a5a31 100644 (file)
@@ -253,11 +253,26 @@ static int finish_object(struct object *obj, const char *name, void *cb_data)
 static void show_object(struct object *obj, const char *name, void *cb_data)
 {
        struct rev_list_info *info = cb_data;
+       struct rev_info *revs = info->revs;
+
        if (finish_object(obj, name, cb_data))
                return;
        display_progress(progress, ++progress_counter);
        if (info->flags & REV_LIST_QUIET)
                return;
+
+       if (revs->count) {
+               /*
+                * The object count is always accumulated in the .count_right
+                * field for traversal that is not a left-right traversal,
+                * and cmd_rev_list() made sure that a .count request that
+                * wants to count non-commit objects, which is handled by
+                * the show_object() callback, does not ask for .left_right.
+                */
+               revs->count_right++;
+               return;
+       }
+
        if (arg_show_object_names)
                show_object_with_name(stdout, obj, name);
        else
@@ -364,6 +379,79 @@ static inline int parse_missing_action_value(const char *value)
        return 0;
 }
 
+static int try_bitmap_count(struct rev_info *revs,
+                           struct list_objects_filter_options *filter)
+{
+       uint32_t commit_count = 0,
+                tag_count = 0,
+                tree_count = 0,
+                blob_count = 0;
+       int max_count;
+       struct bitmap_index *bitmap_git;
+
+       /* This function only handles counting, not general traversal. */
+       if (!revs->count)
+               return -1;
+
+       /*
+        * A bitmap result can't know left/right, etc, because we don't
+        * actually traverse.
+        */
+       if (revs->left_right || revs->cherry_mark)
+               return -1;
+
+       /*
+        * If we're counting reachable objects, we can't handle a max count of
+        * commits to traverse, since we don't know which objects go with which
+        * commit.
+        */
+       if (revs->max_count >= 0 &&
+           (revs->tag_objects || revs->tree_objects || revs->blob_objects))
+               return -1;
+
+       /*
+        * This must be saved before doing any walking, since the revision
+        * machinery will count it down to zero while traversing.
+        */
+       max_count = revs->max_count;
+
+       bitmap_git = prepare_bitmap_walk(revs, filter);
+       if (!bitmap_git)
+               return -1;
+
+       count_bitmap_commit_list(bitmap_git, &commit_count,
+                                revs->tree_objects ? &tree_count : NULL,
+                                revs->blob_objects ? &blob_count : NULL,
+                                revs->tag_objects ? &tag_count : NULL);
+       if (max_count >= 0 && max_count < commit_count)
+               commit_count = max_count;
+
+       printf("%d\n", commit_count + tree_count + blob_count + tag_count);
+       free_bitmap_index(bitmap_git);
+       return 0;
+}
+
+static int try_bitmap_traversal(struct rev_info *revs,
+                               struct list_objects_filter_options *filter)
+{
+       struct bitmap_index *bitmap_git;
+
+       /*
+        * We can't use a bitmap result with a traversal limit, since the set
+        * of commits we'd get would be essentially random.
+        */
+       if (revs->max_count >= 0)
+               return -1;
+
+       bitmap_git = prepare_bitmap_walk(revs, filter);
+       if (!bitmap_git)
+               return -1;
+
+       traverse_bitmap_commit_list(bitmap_git, revs, &show_object_fast);
+       free_bitmap_index(bitmap_git);
+       return 0;
+}
+
 int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
@@ -521,8 +609,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (revs.show_notes)
                die(_("rev-list does not support display of notes"));
 
-       if (filter_options.choice && use_bitmap_index)
-               die(_("cannot combine --use-bitmap-index with object filtering"));
+       if (revs.count &&
+           (revs.tag_objects || revs.tree_objects || revs.blob_objects) &&
+           (revs.left_right || revs.cherry_mark))
+               die(_("marked counting is incompatible with --objects"));
 
        save_commit_buffer = (revs.verbose_header ||
                              revs.grep_filter.pattern_list ||
@@ -533,28 +623,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (show_progress)
                progress = start_delayed_progress(show_progress, 0);
 
-       if (use_bitmap_index && !revs.prune) {
-               if (revs.count && !revs.left_right && !revs.cherry_mark) {
-                       uint32_t commit_count;
-                       int max_count = revs.max_count;
-                       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) {
-                       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;
-                       }
-               }
+       if (use_bitmap_index) {
+               if (!try_bitmap_count(&revs, &filter_options))
+                       return 0;
+               if (!try_bitmap_traversal(&revs, &filter_options))
+                       return 0;
        }
 
        if (prepare_revision_walk(&revs))
index 35d7f51c236dc0b9a734809a1cee9b04a5f12397..8c90cbb18f072726f03f40f12f6b9731b29da1b6 100644 (file)
@@ -536,7 +536,7 @@ static void append_one_rev(const char *av)
                append_ref(av, &revkey, 0);
                return;
        }
-       if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {
+       if (strpbrk(av, "*?[")) {
                /* glob style match */
                int saved_matches = ref_name_cnt;
 
index 571fed4e3ac90b0a4932d9702d1266493a8d0e0a..740da4b6d54aa1864f8357ca84798a620af9f422 100644 (file)
@@ -18,7 +18,7 @@
 static const char *empty_base = "";
 
 static char const * const builtin_sparse_checkout_usage[] = {
-       N_("git sparse-checkout (init|list|set|disable) <options>"),
+       N_("git sparse-checkout (init|list|set|add|disable) <options>"),
        NULL
 };
 
@@ -394,6 +394,9 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
 
        strbuf_trim_trailing_dir_sep(line);
 
+       if (strbuf_normalize_path(line))
+               die(_("could not normalize path %s"), line->buf);
+
        if (!line->len)
                return;
 
@@ -404,7 +407,7 @@ static void strbuf_to_cone_pattern(struct strbuf *line, struct pattern_list *pl)
 }
 
 static char const * const builtin_sparse_checkout_set_usage[] = {
-       N_("git sparse-checkout set (--stdin | <patterns>)"),
+       N_("git sparse-checkout (set|add) (--stdin | <patterns>)"),
        NULL
 };
 
@@ -412,36 +415,16 @@ static struct sparse_checkout_set_opts {
        int use_stdin;
 } set_opts;
 
-static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
+static void add_patterns_from_input(struct pattern_list *pl,
+                                   int argc, const char **argv)
 {
        int i;
-       struct pattern_list pl;
-       int result;
-       int changed_config = 0;
-
-       static struct option builtin_sparse_checkout_set_options[] = {
-               OPT_BOOL(0, "stdin", &set_opts.use_stdin,
-                        N_("read patterns from standard in")),
-               OPT_END(),
-       };
-
-       repo_read_index(the_repository);
-       require_clean_work_tree(the_repository,
-                               N_("set sparse-checkout patterns"), NULL, 1, 0);
-
-       memset(&pl, 0, sizeof(pl));
-
-       argc = parse_options(argc, argv, prefix,
-                            builtin_sparse_checkout_set_options,
-                            builtin_sparse_checkout_set_usage,
-                            PARSE_OPT_KEEP_UNKNOWN);
-
        if (core_sparse_checkout_cone) {
                struct strbuf line = STRBUF_INIT;
 
-               hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0);
-               hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0);
-               pl.use_cone_patterns = 1;
+               hashmap_init(&pl->recursive_hashmap, pl_hashmap_cmp, NULL, 0);
+               hashmap_init(&pl->parent_hashmap, pl_hashmap_cmp, NULL, 0);
+               pl->use_cone_patterns = 1;
 
                if (set_opts.use_stdin) {
                        struct strbuf unquoted = STRBUF_INIT;
@@ -455,7 +438,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
                                        strbuf_swap(&unquoted, &line);
                                }
 
-                               strbuf_to_cone_pattern(&line, &pl);
+                               strbuf_to_cone_pattern(&line, pl);
                        }
 
                        strbuf_release(&unquoted);
@@ -463,7 +446,7 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
                        for (i = 0; i < argc; i++) {
                                strbuf_setlen(&line, 0);
                                strbuf_addstr(&line, argv[i]);
-                               strbuf_to_cone_pattern(&line, &pl);
+                               strbuf_to_cone_pattern(&line, pl);
                        }
                }
        } else {
@@ -473,13 +456,84 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
                        while (!strbuf_getline(&line, stdin)) {
                                size_t len;
                                char *buf = strbuf_detach(&line, &len);
-                               add_pattern(buf, empty_base, 0, &pl, 0);
+                               add_pattern(buf, empty_base, 0, pl, 0);
                        }
                } else {
                        for (i = 0; i < argc; i++)
-                               add_pattern(argv[i], empty_base, 0, &pl, 0);
+                               add_pattern(argv[i], empty_base, 0, pl, 0);
                }
        }
+}
+
+enum modify_type {
+       REPLACE,
+       ADD,
+};
+
+static void add_patterns_cone_mode(int argc, const char **argv,
+                                  struct pattern_list *pl)
+{
+       struct strbuf buffer = STRBUF_INIT;
+       struct pattern_entry *pe;
+       struct hashmap_iter iter;
+       struct pattern_list existing;
+       char *sparse_filename = get_sparse_checkout_filename();
+
+       add_patterns_from_input(pl, argc, argv);
+
+       memset(&existing, 0, sizeof(existing));
+       existing.use_cone_patterns = core_sparse_checkout_cone;
+
+       if (add_patterns_from_file_to_list(sparse_filename, "", 0,
+                                          &existing, NULL))
+               die(_("unable to load existing sparse-checkout patterns"));
+       free(sparse_filename);
+
+       hashmap_for_each_entry(&existing.recursive_hashmap, &iter, pe, ent) {
+               if (!hashmap_contains_parent(&pl->recursive_hashmap,
+                                       pe->pattern, &buffer) ||
+                   !hashmap_contains_parent(&pl->parent_hashmap,
+                                       pe->pattern, &buffer)) {
+                       strbuf_reset(&buffer);
+                       strbuf_addstr(&buffer, pe->pattern);
+                       insert_recursive_pattern(pl, &buffer);
+               }
+       }
+
+       clear_pattern_list(&existing);
+       strbuf_release(&buffer);
+}
+
+static void add_patterns_literal(int argc, const char **argv,
+                                struct pattern_list *pl)
+{
+       char *sparse_filename = get_sparse_checkout_filename();
+       if (add_patterns_from_file_to_list(sparse_filename, "", 0,
+                                          pl, NULL))
+               die(_("unable to load existing sparse-checkout patterns"));
+       free(sparse_filename);
+       add_patterns_from_input(pl, argc, argv);
+}
+
+static int modify_pattern_list(int argc, const char **argv, enum modify_type m)
+{
+       int result;
+       int changed_config = 0;
+       struct pattern_list pl;
+       memset(&pl, 0, sizeof(pl));
+
+       switch (m) {
+       case ADD:
+               if (core_sparse_checkout_cone)
+                       add_patterns_cone_mode(argc, argv, &pl);
+               else
+                       add_patterns_literal(argc, argv, &pl);
+               break;
+
+       case REPLACE:
+               add_patterns_from_input(&pl, argc, argv);
+               break;
+       }
 
        if (!core_apply_sparse_checkout) {
                set_config(MODE_ALL_PATTERNS);
@@ -496,6 +550,27 @@ static int sparse_checkout_set(int argc, const char **argv, const char *prefix)
        return result;
 }
 
+static int sparse_checkout_set(int argc, const char **argv, const char *prefix,
+                              enum modify_type m)
+{
+       static struct option builtin_sparse_checkout_set_options[] = {
+               OPT_BOOL(0, "stdin", &set_opts.use_stdin,
+                        N_("read patterns from standard in")),
+               OPT_END(),
+       };
+
+       repo_read_index(the_repository);
+       require_clean_work_tree(the_repository,
+                               N_("set sparse-checkout patterns"), NULL, 1, 0);
+
+       argc = parse_options(argc, argv, prefix,
+                            builtin_sparse_checkout_set_options,
+                            builtin_sparse_checkout_set_usage,
+                            PARSE_OPT_KEEP_UNKNOWN);
+
+       return modify_pattern_list(argc, argv, m);
+}
+
 static int sparse_checkout_disable(int argc, const char **argv)
 {
        struct pattern_list pl;
@@ -544,7 +619,9 @@ int cmd_sparse_checkout(int argc, const char **argv, const char *prefix)
                if (!strcmp(argv[0], "init"))
                        return sparse_checkout_init(argc, argv);
                if (!strcmp(argv[0], "set"))
-                       return sparse_checkout_set(argc, argv, prefix);
+                       return sparse_checkout_set(argc, argv, prefix, REPLACE);
+               if (!strcmp(argv[0], "add"))
+                       return sparse_checkout_set(argc, argv, prefix, ADD);
                if (!strcmp(argv[0], "disable"))
                        return sparse_checkout_disable(argc, argv);
        }
index d6bc5263f175cedf42a87423da246c0a2f7f6f81..24f22800f38c759d123d7e307de488f0c8ee852f 100644 (file)
@@ -234,14 +234,7 @@ static void validate_worktree_add(const char *path, const struct add_opts *opts)
                die(_("'%s' already exists"), path);
 
        worktrees = get_worktrees(0);
-       /*
-        * find_worktree()'s suffix matching may undesirably find the main
-        * rather than a linked worktree (for instance, when the basenames
-        * of the main worktree and the one being created are the same).
-        * We're only interested in linked worktrees, so skip the main
-        * worktree with +1.
-        */
-       wt = find_worktree(worktrees + 1, NULL, path);
+       wt = find_worktree_by_path(worktrees, path);
        if (!wt)
                goto done;
 
diff --git a/color.c b/color.c
index ebb222ec3323d45bdcd298208903dff9a90d5c3f..64f52a4f93a21c4abe5e40e8957ec6115ca46c2a 100644 (file)
--- a/color.c
+++ b/color.c
@@ -24,6 +24,14 @@ const char *column_colors_ansi[] = {
        GIT_COLOR_RESET,
 };
 
+enum {
+       COLOR_BACKGROUND_OFFSET = 10,
+       COLOR_FOREGROUND_ANSI = 30,
+       COLOR_FOREGROUND_RGB = 38,
+       COLOR_FOREGROUND_256 = 38,
+       COLOR_FOREGROUND_BRIGHT_ANSI = 90,
+};
+
 /* Ignore the RESET at the end when giving the size */
 const int column_colors_ansi_max = ARRAY_SIZE(column_colors_ansi) - 1;
 
@@ -61,15 +69,38 @@ static int get_hex_color(const char *in, unsigned char *out)
        return 0;
 }
 
-static int parse_color(struct color *out, const char *name, int len)
+/*
+ * If an ANSI color is recognized in "name", fill "out" and return 0.
+ * Otherwise, leave out unchanged and return -1.
+ */
+static int parse_ansi_color(struct color *out, const char *name, int len)
 {
        /* Positions in array must match ANSI color codes */
        static const char * const color_names[] = {
                "black", "red", "green", "yellow",
                "blue", "magenta", "cyan", "white"
        };
-       char *end;
        int i;
+       int color_offset = COLOR_FOREGROUND_ANSI;
+
+       if (strncasecmp(name, "bright", 6) == 0) {
+               color_offset = COLOR_FOREGROUND_BRIGHT_ANSI;
+               name += 6;
+               len -= 6;
+       }
+       for (i = 0; i < ARRAY_SIZE(color_names); i++) {
+               if (match_word(name, len, color_names[i])) {
+                       out->type = COLOR_ANSI;
+                       out->value = i + color_offset;
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+static int parse_color(struct color *out, const char *name, int len)
+{
+       char *end;
        long val;
 
        /* First try the special word "normal"... */
@@ -89,12 +120,8 @@ static int parse_color(struct color *out, const char *name, int len)
        }
 
        /* Then pick from our human-readable color names... */
-       for (i = 0; i < ARRAY_SIZE(color_names); i++) {
-               if (match_word(name, len, color_names[i])) {
-                       out->type = COLOR_ANSI;
-                       out->value = i;
-                       return 0;
-               }
+       if (parse_ansi_color(out, name, len) == 0) {
+               return 0;
        }
 
        /* And finally try a literal 256-color-mode number */
@@ -109,10 +136,15 @@ static int parse_color(struct color *out, const char *name, int len)
                else if (val < 0) {
                        out->type = COLOR_NORMAL;
                        return 0;
-               /* Rewrite low numbers as more-portable standard colors. */
+               /* Rewrite 0-7 as more-portable standard colors. */
                } else if (val < 8) {
                        out->type = COLOR_ANSI;
-                       out->value = val;
+                       out->value = val + COLOR_FOREGROUND_ANSI;
+                       return 0;
+               /* Rewrite 8-15 as more-portable aixterm colors. */
+               } else if (val < 16) {
+                       out->type = COLOR_ANSI;
+                       out->value = val - 8 + COLOR_FOREGROUND_BRIGHT_ANSI;
                        return 0;
                } else if (val < 256) {
                        out->type = COLOR_256;
@@ -166,23 +198,26 @@ int color_parse(const char *value, char *dst)
  * already have the ANSI escape code in it. "out" should have enough
  * space in it to fit any color.
  */
-static char *color_output(char *out, int len, const struct color *c, char type)
+static char *color_output(char *out, int len, const struct color *c, int background)
 {
+       int offset = 0;
+
+       if (background)
+               offset = COLOR_BACKGROUND_OFFSET;
        switch (c->type) {
        case COLOR_UNSPECIFIED:
        case COLOR_NORMAL:
                break;
        case COLOR_ANSI:
-               if (len < 2)
-                       BUG("color parsing ran out of space");
-               *out++ = type;
-               *out++ = '0' + c->value;
+               out += xsnprintf(out, len, "%d", c->value + offset);
                break;
        case COLOR_256:
-               out += xsnprintf(out, len, "%c8;5;%d", type, c->value);
+               out += xsnprintf(out, len, "%d;5;%d", COLOR_FOREGROUND_256 + offset,
+                                c->value);
                break;
        case COLOR_RGB:
-               out += xsnprintf(out, len, "%c8;2;%d;%d;%d", type,
+               out += xsnprintf(out, len, "%d;2;%d;%d;%d",
+                                COLOR_FOREGROUND_RGB + offset,
                                 c->red, c->green, c->blue);
                break;
        }
@@ -279,14 +314,12 @@ int color_parse_mem(const char *value, int value_len, char *dst)
                if (!color_empty(&fg)) {
                        if (sep++)
                                OUT(';');
-                       /* foreground colors are all in the 3x range */
-                       dst = color_output(dst, end - dst, &fg, '3');
+                       dst = color_output(dst, end - dst, &fg, 0);
                }
                if (!color_empty(&bg)) {
                        if (sep++)
                                OUT(';');
-                       /* background colors are all in the 4x range */
-                       dst = color_output(dst, end - dst, &bg, '4');
+                       dst = color_output(dst, end - dst, &bg, 1);
                }
                OUT('m');
        }
index b5230149db53c4c49db930d75146224180d36368..d14065d60ec497131a23549727439d624ff4c710 100644 (file)
@@ -1245,7 +1245,7 @@ static char *path_lookup(const char *cmd, int exe_only)
        int len = strlen(cmd);
        int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe");
 
-       if (strchr(cmd, '/') || strchr(cmd, '\\'))
+       if (strpbrk(cmd, "/\\"))
                return xstrdup(cmd);
 
        path = mingw_getenv("PATH");
index 79b0a56af873fe80e8146082981896b36315f47b..d17d2bd9dcdef8e4f16316090e1265145834926b 100644 (file)
--- a/config.c
+++ b/config.c
@@ -3338,6 +3338,14 @@ enum config_scope current_config_scope(void)
                return current_parsing_scope;
 }
 
+int current_config_line(void)
+{
+       if (current_config_kvi)
+               return current_config_kvi->linenr;
+       else
+               return cf->linenr;
+}
+
 int lookup_config(const char **mapping, int nr_mapping, const char *var)
 {
        int i;
index fe0addb0dc96cd1150c61c5028e0d41424153ee5..9b3773f77826513226608a592e3295d7a6ea5e3f 100644 (file)
--- a/config.h
+++ b/config.h
@@ -309,6 +309,7 @@ int git_config_parse_parameter(const char *, config_fn_t fn, void *data);
 enum config_scope current_config_scope(void);
 const char *current_config_origin_type(void);
 const char *current_config_name(void);
+int current_config_line(void);
 
 /**
  * Include Directives
index 1d510cd47bef49d918704a45e9592e0ffcb7ee0f..014cd7c3cfcc99f837b0bd1a3086b2d32272caf0 100644 (file)
@@ -429,11 +429,7 @@ __git_ps1 ()
                __git_eread "$g/rebase-merge/head-name" b
                __git_eread "$g/rebase-merge/msgnum" step
                __git_eread "$g/rebase-merge/end" total
-               if [ -f "$g/rebase-merge/interactive" ]; then
-                       r="|REBASE-i"
-               else
-                       r="|REBASE-m"
-               fi
+               r="|REBASE"
        else
                if [ -d "$g/rebase-apply" ]; then
                        __git_eread "$g/rebase-apply/next" step
index 62be651b03b55ee4d478706e51ea8606b10739f0..77dfde44e3d136a29f68091878c9f9392af746c0 100644 (file)
@@ -6,6 +6,7 @@
 #include "url.h"
 #include "prompt.h"
 #include "sigchain.h"
+#include "urlmatch.h"
 
 void credential_init(struct credential *c)
 {
@@ -40,7 +41,7 @@ static int credential_config_callback(const char *var, const char *value,
                                      void *data)
 {
        struct credential *c = data;
-       const char *key, *dot;
+       const char *key;
 
        if (!skip_prefix(var, "credential.", &key))
                return 0;
@@ -48,31 +49,16 @@ static int credential_config_callback(const char *var, const char *value,
        if (!value)
                return config_error_nonbool(var);
 
-       dot = strrchr(key, '.');
-       if (dot) {
-               struct credential want = CREDENTIAL_INIT;
-               char *url = xmemdupz(key, dot - key);
-               int matched;
-
-               credential_from_url(&want, url);
-               matched = credential_match(&want, c);
-
-               credential_clear(&want);
-               free(url);
-
-               if (!matched)
-                       return 0;
-               key = dot + 1;
-       }
-
        if (!strcmp(key, "helper")) {
                if (*value)
                        string_list_append(&c->helpers, value);
                else
                        string_list_clear(&c->helpers, 0);
        } else if (!strcmp(key, "username")) {
-               if (!c->username)
+               if (!c->username_from_proto) {
+                       free(c->username);
                        c->username = xstrdup(value);
+               }
        }
        else if (!strcmp(key, "usehttppath"))
                c->use_http_path = git_config_bool(var, value);
@@ -87,11 +73,38 @@ static int proto_is_http(const char *s)
        return !strcmp(s, "https") || !strcmp(s, "http");
 }
 
+static void credential_describe(struct credential *c, struct strbuf *out);
+static void credential_format(struct credential *c, struct strbuf *out);
+
+static int select_all(const struct urlmatch_item *a,
+                     const struct urlmatch_item *b)
+{
+       return 0;
+}
+
 static void credential_apply_config(struct credential *c)
 {
+       char *normalized_url;
+       struct urlmatch_config config = { STRING_LIST_INIT_DUP };
+       struct strbuf url = STRBUF_INIT;
+
        if (c->configured)
                return;
-       git_config(credential_config_callback, c);
+
+       config.section = "credential";
+       config.key = NULL;
+       config.collect_fn = credential_config_callback;
+       config.cascade_fn = NULL;
+       config.select_fn = select_all;
+       config.cb = c;
+
+       credential_format(c, &url);
+       normalized_url = url_normalize(url.buf, &config.url);
+
+       git_config(urlmatch_config_entry, &config);
+       free(normalized_url);
+       strbuf_release(&url);
+
        c->configured = 1;
 
        if (!c->use_http_path && proto_is_http(c->protocol)) {
@@ -112,6 +125,23 @@ static void credential_describe(struct credential *c, struct strbuf *out)
                strbuf_addf(out, "/%s", c->path);
 }
 
+static void credential_format(struct credential *c, struct strbuf *out)
+{
+       if (!c->protocol)
+               return;
+       strbuf_addf(out, "%s://", c->protocol);
+       if (c->username && *c->username) {
+               strbuf_add_percentencode(out, c->username);
+               strbuf_addch(out, '@');
+       }
+       if (c->host)
+               strbuf_addstr(out, c->host);
+       if (c->path) {
+               strbuf_addch(out, '/');
+               strbuf_add_percentencode(out, c->path);
+       }
+}
+
 static char *credential_ask_one(const char *what, struct credential *c,
                                int flags)
 {
@@ -163,6 +193,7 @@ int credential_read(struct credential *c, FILE *fp)
                if (!strcmp(key, "username")) {
                        free(c->username);
                        c->username = xstrdup(value);
+                       c->username_from_proto = 1;
                } else if (!strcmp(key, "password")) {
                        free(c->password);
                        c->password = xstrdup(value);
@@ -349,10 +380,14 @@ void credential_from_url(struct credential *c, const char *url)
        else if (!colon || at <= colon) {
                /* Case (2) */
                c->username = url_decode_mem(cp, at - cp);
+               if (c->username && *c->username)
+                       c->username_from_proto = 1;
                host = at + 1;
        } else {
                /* Case (3) */
                c->username = url_decode_mem(cp, colon - cp);
+               if (c->username && *c->username)
+                       c->username_from_proto = 1;
                c->password = url_decode_mem(colon + 1, at - (colon + 1));
                host = at + 1;
        }
index a5a3ee9bb823e51e785631f90f4e46a6e94a7def..fec7815dd0d60bf398bb8d448ab55a6cd6cee64c 100644 (file)
@@ -118,7 +118,8 @@ struct credential {
        unsigned approved:1,
                 configured:1,
                 quit:1,
-                use_http_path:1;
+                use_http_path:1,
+                username_from_proto:1;
 
        char *username;
        char *password;
diff --git a/dir.c b/dir.c
index 0dc0f113c0595c090f0864786b05aaf857005a73..0ffb1b3302452c2cce0bdaa55e5259d9be168b63 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -688,7 +688,7 @@ static void add_pattern_to_hashsets(struct pattern_list *pl, struct path_pattern
                return;
        }
 
-       if (given->patternlen <= 2 ||
+       if (given->patternlen < 2 ||
            *given->pattern == '*' ||
            strstr(given->pattern, "**")) {
                /* Not a cone pattern. */
index b5fed9621f425b08af27c56be0e3f88a331e314b..d8cec585af97e66e0534c1cebd32f2cdf022d382 100644 (file)
@@ -50,6 +50,14 @@ void bitmap_set(struct bitmap *self, size_t pos)
        self->words[block] |= EWAH_MASK(pos);
 }
 
+void bitmap_unset(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);
index 1b98b57c8b7b623f4e76024b488081d09bf9bad6..011852bef179191b867b96e9d0fb57b099bc60b3 100644 (file)
@@ -174,6 +174,7 @@ struct bitmap {
 struct bitmap *bitmap_new(void);
 struct bitmap *bitmap_word_alloc(size_t word_alloc);
 void bitmap_set(struct bitmap *self, size_t pos);
+void bitmap_unset(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);
index cf92255515d491c8508a885b885707425edf86bb..742fa376ab01142de8bb7410c37657a5ede4a391 100644 (file)
@@ -19,8 +19,7 @@ static void cleanup_space(struct strbuf *sb)
 static void get_sane_name(struct strbuf *out, struct strbuf *name, struct strbuf *email)
 {
        struct strbuf *src = name;
-       if (name->len < 3 || 60 < name->len || strchr(name->buf, '@') ||
-               strchr(name->buf, '<') || strchr(name->buf, '>'))
+       if (name->len < 3 || 60 < name->len || strpbrk(name->buf, "@<>"))
                src = email;
        else if (name == out)
                return;
index aee1769a7ac344c0d0fed681588efce4c09df564..e6f943c5cccf34019f68150e88680d8f48d17059 100644 (file)
@@ -998,10 +998,13 @@ static int update_file_flags(struct merge_options *opt,
                free(buf);
        }
 update_index:
-       if (!ret && update_cache)
-               if (add_cacheinfo(opt, contents, path, 0, update_wd,
+       if (!ret && update_cache) {
+               int refresh = (!opt->priv->call_depth &&
+                              contents->mode != S_IFGITLINK);
+               if (add_cacheinfo(opt, contents, path, 0, refresh,
                                  ADD_CACHE_OK_TO_ADD))
                        return -1;
+       }
        return ret;
 }
 
index c94be9449922de81e9576b8146d0ed08ab5ee9a6..794c86650e9a976a0667cb8103a4ae0a9a0deaf2 100644 (file)
--- a/object.c
+++ b/object.c
@@ -308,6 +308,15 @@ int object_list_contains(struct object_list *list, struct object *obj)
        return 0;
 }
 
+void object_list_free(struct object_list **list)
+{
+       while (*list) {
+               struct object_list *p = *list;
+               *list = p->next;
+               free(p);
+       }
+}
+
 /*
  * A zero-length string to which object_array_entry::name can be
  * initialized without requiring a malloc/free.
index 25f5ab3d54a8b1111b8bb4ce0e4b024894ce8e54..2dbabfca0ab8185f52cca6d3b7c69658e838ae49 100644 (file)
--- a/object.h
+++ b/object.h
@@ -151,6 +151,8 @@ struct object_list *object_list_insert(struct object *item,
 
 int object_list_contains(struct object_list *list, struct object *obj);
 
+void object_list_free(struct object_list **list);
+
 /* Object array handling .. */
 void add_object_array(struct object *obj, const char *name, struct object_array *array);
 void add_object_array_with_path(struct object *obj, const char *name, struct object_array *array, unsigned mode, const char *path);
index 1a067885a1b02cf9f3e26f53dce76dd400a20016..49a8d10d0cf99716ea37b78bec578ca96c17de20 100644 (file)
@@ -12,6 +12,7 @@
 #include "packfile.h"
 #include "repository.h"
 #include "object-store.h"
+#include "list-objects-filter-options.h"
 
 /*
  * An entry on the bitmap index, representing the bitmap for a given
@@ -606,6 +607,7 @@ static struct bitmap *find_objects(struct bitmap_index *bitmap_git,
 }
 
 static void show_extended_objects(struct bitmap_index *bitmap_git,
+                                 struct rev_info *revs,
                                  show_reachable_fn show_reach)
 {
        struct bitmap *objects = bitmap_git->result;
@@ -619,13 +621,44 @@ static void show_extended_objects(struct bitmap_index *bitmap_git,
                        continue;
 
                obj = eindex->objects[i];
+               if ((obj->type == OBJ_BLOB && !revs->blob_objects) ||
+                   (obj->type == OBJ_TREE && !revs->tree_objects) ||
+                   (obj->type == OBJ_TAG && !revs->tag_objects))
+                       continue;
+
                show_reach(&obj->oid, obj->type, 0, eindex->hashes[i], NULL, 0);
        }
 }
 
+static void init_type_iterator(struct ewah_iterator *it,
+                              struct bitmap_index *bitmap_git,
+                              enum object_type type)
+{
+       switch (type) {
+       case OBJ_COMMIT:
+               ewah_iterator_init(it, bitmap_git->commits);
+               break;
+
+       case OBJ_TREE:
+               ewah_iterator_init(it, bitmap_git->trees);
+               break;
+
+       case OBJ_BLOB:
+               ewah_iterator_init(it, bitmap_git->blobs);
+               break;
+
+       case OBJ_TAG:
+               ewah_iterator_init(it, bitmap_git->tags);
+               break;
+
+       default:
+               BUG("object type %d not stored by bitmap type index", type);
+               break;
+       }
+}
+
 static void show_objects_for_type(
        struct bitmap_index *bitmap_git,
-       struct ewah_bitmap *type_filter,
        enum object_type object_type,
        show_reachable_fn show_reach)
 {
@@ -637,7 +670,7 @@ static void show_objects_for_type(
 
        struct bitmap *objects = bitmap_git->result;
 
-       ewah_iterator_init(&it, type_filter);
+       init_type_iterator(&it, bitmap_git, object_type);
 
        for (i = 0; i < objects->word_alloc &&
                        ewah_iterator_next(&filter, &it); i++) {
@@ -682,7 +715,179 @@ static int in_bitmapped_pack(struct bitmap_index *bitmap_git,
        return 0;
 }
 
-struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
+static struct bitmap *find_tip_blobs(struct bitmap_index *bitmap_git,
+                                    struct object_list *tip_objects)
+{
+       struct bitmap *result = bitmap_new();
+       struct object_list *p;
+
+       for (p = tip_objects; p; p = p->next) {
+               int pos;
+
+               if (p->item->type != OBJ_BLOB)
+                       continue;
+
+               pos = bitmap_position(bitmap_git, &p->item->oid);
+               if (pos < 0)
+                       continue;
+
+               bitmap_set(result, pos);
+       }
+
+       return result;
+}
+
+static void filter_bitmap_blob_none(struct bitmap_index *bitmap_git,
+                                   struct object_list *tip_objects,
+                                   struct bitmap *to_filter)
+{
+       struct eindex *eindex = &bitmap_git->ext_index;
+       struct bitmap *tips;
+       struct ewah_iterator it;
+       eword_t mask;
+       uint32_t i;
+
+       /*
+        * The non-bitmap version of this filter never removes
+        * blobs which the other side specifically asked for,
+        * so we must match that behavior.
+        */
+       tips = find_tip_blobs(bitmap_git, tip_objects);
+
+       /*
+        * We can use the blob type-bitmap to work in whole words
+        * for the objects that are actually in the bitmapped packfile.
+        */
+       for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);
+            i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
+            i++) {
+               if (i < tips->word_alloc)
+                       mask &= ~tips->words[i];
+               to_filter->words[i] &= ~mask;
+       }
+
+       /*
+        * Clear any blobs that weren't in the packfile (and so would not have
+        * been caught by the loop above. We'll have to check them
+        * individually.
+        */
+       for (i = 0; i < eindex->count; i++) {
+               uint32_t pos = i + bitmap_git->pack->num_objects;
+               if (eindex->objects[i]->type == OBJ_BLOB &&
+                   bitmap_get(to_filter, pos) &&
+                   !bitmap_get(tips, pos))
+                       bitmap_unset(to_filter, pos);
+       }
+
+       bitmap_free(tips);
+}
+
+static unsigned long get_size_by_pos(struct bitmap_index *bitmap_git,
+                                    uint32_t pos)
+{
+       struct packed_git *pack = bitmap_git->pack;
+       unsigned long size;
+       struct object_info oi = OBJECT_INFO_INIT;
+
+       oi.sizep = &size;
+
+       if (pos < pack->num_objects) {
+               struct revindex_entry *entry = &pack->revindex[pos];
+               if (packed_object_info(the_repository, pack,
+                                      entry->offset, &oi) < 0) {
+                       struct object_id oid;
+                       nth_packed_object_id(&oid, pack, entry->nr);
+                       die(_("unable to get size of %s"), oid_to_hex(&oid));
+               }
+       } else {
+               struct eindex *eindex = &bitmap_git->ext_index;
+               struct object *obj = eindex->objects[pos - pack->num_objects];
+               if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)
+                       die(_("unable to get size of %s"), oid_to_hex(&obj->oid));
+       }
+
+       return size;
+}
+
+static void filter_bitmap_blob_limit(struct bitmap_index *bitmap_git,
+                                    struct object_list *tip_objects,
+                                    struct bitmap *to_filter,
+                                    unsigned long limit)
+{
+       struct eindex *eindex = &bitmap_git->ext_index;
+       struct bitmap *tips;
+       struct ewah_iterator it;
+       eword_t mask;
+       uint32_t i;
+
+       tips = find_tip_blobs(bitmap_git, tip_objects);
+
+       for (i = 0, init_type_iterator(&it, bitmap_git, OBJ_BLOB);
+            i < to_filter->word_alloc && ewah_iterator_next(&mask, &it);
+            i++) {
+               eword_t word = to_filter->words[i] & mask;
+               unsigned offset;
+
+               for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+                       uint32_t pos;
+
+                       if ((word >> offset) == 0)
+                               break;
+                       offset += ewah_bit_ctz64(word >> offset);
+                       pos = i * BITS_IN_EWORD + offset;
+
+                       if (!bitmap_get(tips, pos) &&
+                           get_size_by_pos(bitmap_git, pos) >= limit)
+                               bitmap_unset(to_filter, pos);
+               }
+       }
+
+       for (i = 0; i < eindex->count; i++) {
+               uint32_t pos = i + bitmap_git->pack->num_objects;
+               if (eindex->objects[i]->type == OBJ_BLOB &&
+                   bitmap_get(to_filter, pos) &&
+                   !bitmap_get(tips, pos) &&
+                   get_size_by_pos(bitmap_git, pos) >= limit)
+                       bitmap_unset(to_filter, pos);
+       }
+
+       bitmap_free(tips);
+}
+
+static int filter_bitmap(struct bitmap_index *bitmap_git,
+                        struct object_list *tip_objects,
+                        struct bitmap *to_filter,
+                        struct list_objects_filter_options *filter)
+{
+       if (!filter || filter->choice == LOFC_DISABLED)
+               return 0;
+
+       if (filter->choice == LOFC_BLOB_NONE) {
+               if (bitmap_git)
+                       filter_bitmap_blob_none(bitmap_git, tip_objects,
+                                               to_filter);
+               return 0;
+       }
+
+       if (filter->choice == LOFC_BLOB_LIMIT) {
+               if (bitmap_git)
+                       filter_bitmap_blob_limit(bitmap_git, tip_objects,
+                                                to_filter,
+                                                filter->blob_limit_value);
+               return 0;
+       }
+
+       /* filter choice not handled */
+       return -1;
+}
+
+static int can_filter_bitmap(struct list_objects_filter_options *filter)
+{
+       return !filter_bitmap(NULL, NULL, NULL, filter);
+}
+
+struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
+                                        struct list_objects_filter_options *filter)
 {
        unsigned int i;
 
@@ -692,9 +897,22 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
        struct bitmap *wants_bitmap = NULL;
        struct bitmap *haves_bitmap = NULL;
 
-       struct bitmap_index *bitmap_git = xcalloc(1, sizeof(*bitmap_git));
+       struct bitmap_index *bitmap_git;
+
+       /*
+        * We can't do pathspec limiting with bitmaps, because we don't know
+        * which commits are associated with which object changes (let alone
+        * even which objects are associated with which paths).
+        */
+       if (revs->prune)
+               return NULL;
+
+       if (!can_filter_bitmap(filter))
+               return NULL;
+
        /* try to open a bitmapped pack, but don't parse it yet
         * because we may not need to use it */
+       bitmap_git = xcalloc(1, sizeof(*bitmap_git));
        if (open_pack_bitmap(revs->repo, bitmap_git) < 0)
                goto cleanup;
 
@@ -761,13 +979,20 @@ struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs)
        if (haves_bitmap)
                bitmap_and_not(wants_bitmap, haves_bitmap);
 
+       filter_bitmap(bitmap_git, wants, wants_bitmap, filter);
+
        bitmap_git->result = wants_bitmap;
        bitmap_git->haves = haves_bitmap;
 
+       object_list_free(&wants);
+       object_list_free(&haves);
+
        return bitmap_git;
 
 cleanup:
        free_bitmap_index(bitmap_git);
+       object_list_free(&wants);
+       object_list_free(&haves);
        return NULL;
 }
 
@@ -907,20 +1132,20 @@ int bitmap_walk_contains(struct bitmap_index *bitmap_git,
 }
 
 void traverse_bitmap_commit_list(struct bitmap_index *bitmap_git,
+                                struct rev_info *revs,
                                 show_reachable_fn show_reachable)
 {
        assert(bitmap_git->result);
 
-       show_objects_for_type(bitmap_git, bitmap_git->commits,
-               OBJ_COMMIT, show_reachable);
-       show_objects_for_type(bitmap_git, bitmap_git->trees,
-               OBJ_TREE, show_reachable);
-       show_objects_for_type(bitmap_git, bitmap_git->blobs,
-               OBJ_BLOB, show_reachable);
-       show_objects_for_type(bitmap_git, bitmap_git->tags,
-               OBJ_TAG, show_reachable);
+       show_objects_for_type(bitmap_git, OBJ_COMMIT, show_reachable);
+       if (revs->tree_objects)
+               show_objects_for_type(bitmap_git, OBJ_TREE, show_reachable);
+       if (revs->blob_objects)
+               show_objects_for_type(bitmap_git, OBJ_BLOB, show_reachable);
+       if (revs->tag_objects)
+               show_objects_for_type(bitmap_git, OBJ_TAG, show_reachable);
 
-       show_extended_objects(bitmap_git, show_reachable);
+       show_extended_objects(bitmap_git, revs, show_reachable);
 }
 
 static uint32_t count_object_type(struct bitmap_index *bitmap_git,
@@ -933,26 +1158,7 @@ static uint32_t count_object_type(struct bitmap_index *bitmap_git,
        struct ewah_iterator it;
        eword_t filter;
 
-       switch (type) {
-       case OBJ_COMMIT:
-               ewah_iterator_init(&it, bitmap_git->commits);
-               break;
-
-       case OBJ_TREE:
-               ewah_iterator_init(&it, bitmap_git->trees);
-               break;
-
-       case OBJ_BLOB:
-               ewah_iterator_init(&it, bitmap_git->blobs);
-               break;
-
-       case OBJ_TAG:
-               ewah_iterator_init(&it, bitmap_git->tags);
-               break;
-
-       default:
-               return 0;
-       }
+       init_type_iterator(&it, bitmap_git, type);
 
        while (i < objects->word_alloc && ewah_iterator_next(&filter, &it)) {
                eword_t word = objects->words[i++] & filter;
index bcd03b8993fd7a409cd12a40ddab366b4f6ab22e..1203120c4321c15d488e623385d0ce9633b66477 100644 (file)
@@ -9,6 +9,7 @@
 struct commit;
 struct repository;
 struct rev_info;
+struct list_objects_filter_options;
 
 static const char BITMAP_IDX_SIGNATURE[] = {'B', 'I', 'T', 'M'};
 
@@ -45,9 +46,11 @@ struct bitmap_index *prepare_bitmap_git(struct repository *r);
 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 *,
+                                struct rev_info *revs,
                                 show_reachable_fn show_reachable);
 void test_bitmap_walk(struct rev_info *revs);
-struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs);
+struct bitmap_index *prepare_bitmap_walk(struct rev_info *revs,
+                                        struct list_objects_filter_options *filter);
 int reuse_partial_packfile_from_bitmap(struct bitmap_index *,
                                       struct packed_git **packfile,
                                       uint32_t *entries,
diff --git a/quote.c b/quote.c
index 24a58ba454fdfd04dd441d787ea8202ea772c02f..bcc0dbc50d9b98bb5317398505d6548c98005c2f 100644 (file)
--- a/quote.c
+++ b/quote.c
@@ -55,7 +55,7 @@ void sq_quote_buf_pretty(struct strbuf *dst, const char *src)
        }
 
        for (p = src; *p; p++) {
-               if (!isalpha(*p) && !isdigit(*p) && !strchr(ok_punct, *p)) {
+               if (!isalnum(*p) && !strchr(ok_punct, *p)) {
                        sq_quote_buf(dst, src);
                        return;
                }
index 8f50235b28edd43c3a4e122a1bcae4d1c1b3f99b..77a60c70a5db775d2f783023eeb757c1eb61d8a4 100644 (file)
@@ -223,9 +223,9 @@ void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
        cp.progress = progress;
        cp.count = 0;
 
-       bitmap_git = prepare_bitmap_walk(revs);
+       bitmap_git = prepare_bitmap_walk(revs, NULL);
        if (bitmap_git) {
-               traverse_bitmap_commit_list(bitmap_git, mark_object_seen);
+               traverse_bitmap_commit_list(bitmap_git, revs, mark_object_seen);
                free_bitmap_index(bitmap_git);
                return;
        }
index ac001dea5887a119f839ab4577797f9a9a179174..d86b434b3d20002d023041c63dc43e48747a2351 100644 (file)
@@ -35,7 +35,7 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)
        return MISSING_COMMIT_CHECK_IGNORE;
 }
 
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(int command_count,
                      const char *shortrevisions, const char *shortonto,
                      struct strbuf *buf)
 {
@@ -87,11 +87,6 @@ void append_todo_help(unsigned keep_empty, int command_count,
                        "the rebase will be aborted.\n\n");
 
        strbuf_add_commented_lines(buf, msg, strlen(msg));
-
-       if (!keep_empty) {
-               msg = _("Note that empty commits are commented out");
-               strbuf_add_commented_lines(buf, msg, strlen(msg));
-       }
 }
 
 int edit_todo_list(struct repository *r, struct todo_list *todo_list,
index 4af0c1fcc702ed74ecf60d31a71eeb6154c108f9..dc2cf0ee122c70a9c9b4e614be058ee5c423dd80 100644 (file)
@@ -5,7 +5,7 @@ struct strbuf;
 struct repository;
 struct todo_list;
 
-void append_todo_help(unsigned keep_empty, int command_count,
+void append_todo_help(int command_count,
                      const char *shortrevisions, const char *shortonto,
                      struct strbuf *buf);
 int edit_todo_list(struct repository *r, struct todo_list *todo_list,
diff --git a/rebase.c b/rebase.c
new file mode 100644 (file)
index 0000000..f8137d8
--- /dev/null
+++ b/rebase.c
@@ -0,0 +1,35 @@
+#include "rebase.h"
+#include "config.h"
+
+/*
+ * Parses textual value for pull.rebase, branch.<name>.rebase, etc.
+ * Unrecognised value yields REBASE_INVALID, which traditionally is
+ * treated the same way as REBASE_FALSE.
+ *
+ * The callers that care if (any) rebase is requested should say
+ *   if (REBASE_TRUE <= rebase_parse_value(string))
+ *
+ * The callers that want to differenciate an unrecognised value and
+ * false can do so by treating _INVALID and _FALSE differently.
+ */
+enum rebase_type rebase_parse_value(const char *value)
+{
+       int v = git_parse_maybe_bool(value);
+
+       if (!v)
+               return REBASE_FALSE;
+       else if (v > 0)
+               return REBASE_TRUE;
+       else if (!strcmp(value, "preserve") || !strcmp(value, "p"))
+               return REBASE_PRESERVE;
+       else if (!strcmp(value, "merges") || !strcmp(value, "m"))
+               return REBASE_MERGES;
+       else if (!strcmp(value, "interactive") || !strcmp(value, "i"))
+               return REBASE_INTERACTIVE;
+       /*
+        * Please update _git_config() in git-completion.bash when you
+        * add new rebase modes.
+        */
+
+       return REBASE_INVALID;
+}
diff --git a/rebase.h b/rebase.h
new file mode 100644 (file)
index 0000000..cc723d4
--- /dev/null
+++ b/rebase.h
@@ -0,0 +1,15 @@
+#ifndef REBASE_H
+#define REBASE_H
+
+enum rebase_type {
+       REBASE_INVALID = -1,
+       REBASE_FALSE = 0,
+       REBASE_TRUE,
+       REBASE_PRESERVE,
+       REBASE_MERGES,
+       REBASE_INTERACTIVE
+};
+
+enum rebase_type rebase_parse_value(const char *value);
+
+#endif /* REBASE */
index 592d9dc03584c1690ad36645b65105adcc8d5652..0f3cc73ab6727e9b3805d93e5e4ab4dec3c1d5cb 100644 (file)
@@ -116,7 +116,7 @@ struct child_process {
        unsigned no_stdin:1;
        unsigned no_stdout:1;
        unsigned no_stderr:1;
-    unsigned git_cmd:1; /* if this is to be git sub-command */
+       unsigned git_cmd:1; /* if this is to be git sub-command */
 
        /**
         * If the program cannot be found, the functions return -1 and set
index ba90a513b95f1ddd2b53d6eeafd28ef5d49745e6..7477b15422a7a7e42e1406bd7ecf20b458a93658 100644 (file)
@@ -160,6 +160,8 @@ static GIT_PATH_FUNC(rebase_path_strategy, "rebase-merge/strategy")
 static GIT_PATH_FUNC(rebase_path_strategy_opts, "rebase-merge/strategy_opts")
 static GIT_PATH_FUNC(rebase_path_allow_rerere_autoupdate, "rebase-merge/allow_rerere_autoupdate")
 static GIT_PATH_FUNC(rebase_path_reschedule_failed_exec, "rebase-merge/reschedule-failed-exec")
+static GIT_PATH_FUNC(rebase_path_drop_redundant_commits, "rebase-merge/drop_redundant_commits")
+static GIT_PATH_FUNC(rebase_path_keep_redundant_commits, "rebase-merge/keep_redundant_commits")
 
 static int git_sequencer_config(const char *k, const char *v, void *cb)
 {
@@ -290,7 +292,7 @@ int sequencer_remove_state(struct replay_opts *opts)
                        char *eol = strchr(p, '\n');
                        if (eol)
                                *eol = '\0';
-                       if (delete_ref("(rebase -i) cleanup", p, NULL, 0) < 0) {
+                       if (delete_ref("(rebase) cleanup", p, NULL, 0) < 0) {
                                warning(_("could not delete '%s'"), p);
                                ret = -1;
                        }
@@ -324,7 +326,7 @@ static const char *action_name(const struct replay_opts *opts)
        case REPLAY_PICK:
                return N_("cherry-pick");
        case REPLAY_INTERACTIVE_REBASE:
-               return N_("rebase -i");
+               return N_("rebase");
        }
        die(_("unknown action: %d"), opts->action);
 }
@@ -628,7 +630,7 @@ static int do_recursive_merge(struct repository *r,
                               COMMIT_LOCK | SKIP_IF_UNCHANGED))
                /*
                 * TRANSLATORS: %s will be "revert", "cherry-pick" or
-                * "rebase -i".
+                * "rebase".
                 */
                return error(_("%s: Unable to write new index file"),
                        _(action_name(opts)));
@@ -1485,23 +1487,30 @@ static int is_original_commit_empty(struct commit *commit)
 }
 
 /*
- * Do we run "git commit" with "--allow-empty"?
+ * Should empty commits be allowed?  Return status:
+ *    <0: Error in is_index_unchanged(r) or is_original_commit_empty(commit)
+ *     0: Halt on empty commit
+ *     1: Allow empty commit
+ *     2: Drop empty commit
  */
 static int allow_empty(struct repository *r,
                       struct replay_opts *opts,
                       struct commit *commit)
 {
-       int index_unchanged, empty_commit;
+       int index_unchanged, originally_empty;
 
        /*
-        * Three cases:
+        * Four cases:
         *
         * (1) we do not allow empty at all and error out.
         *
-        * (2) we allow ones that were initially empty, but
-        * forbid the ones that become empty;
+        * (2) we allow ones that were initially empty, and
+        *     just drop the ones that become empty
         *
-        * (3) we allow both.
+        * (3) we allow ones that were initially empty, but
+        *     halt for the ones that become empty;
+        *
+        * (4) we allow both.
         */
        if (!opts->allow_empty)
                return 0; /* let "git commit" barf as necessary */
@@ -1515,13 +1524,15 @@ static int allow_empty(struct repository *r,
        if (opts->keep_redundant_commits)
                return 1;
 
-       empty_commit = is_original_commit_empty(commit);
-       if (empty_commit < 0)
-               return empty_commit;
-       if (!empty_commit)
-               return 0;
-       else
+       originally_empty = is_original_commit_empty(commit);
+       if (originally_empty < 0)
+               return originally_empty;
+       if (originally_empty)
                return 1;
+       else if (opts->drop_redundant_commits)
+               return 2;
+       else
+               return 0;
 }
 
 static struct {
@@ -1732,7 +1743,7 @@ static int do_pick_commit(struct repository *r,
        char *author = NULL;
        struct commit_message msg = { NULL, NULL, NULL, NULL };
        struct strbuf msgbuf = STRBUF_INIT;
-       int res, unborn = 0, reword = 0, allow;
+       int res, unborn = 0, reword = 0, allow, drop_commit;
 
        if (opts->no_commit) {
                /*
@@ -1937,13 +1948,20 @@ static int do_pick_commit(struct repository *r,
                goto leave;
        }
 
+       drop_commit = 0;
        allow = allow_empty(r, opts, commit);
        if (allow < 0) {
                res = allow;
                goto leave;
-       } else if (allow)
+       } else if (allow == 1) {
                flags |= ALLOW_EMPTY;
-       if (!opts->no_commit) {
+       } else if (allow == 2) {
+               drop_commit = 1;
+               fprintf(stderr,
+                       _("dropping %s %s -- patch contents already upstream\n"),
+                       oid_to_hex(&commit->object.oid), msg.subject);
+       } /* else allow == 0 and there's nothing special to do */
+       if (!opts->no_commit && !drop_commit) {
                if (author || command == TODO_REVERT || (flags & AMEND_MSG))
                        res = do_commit(r, msg_file, author, opts, flags);
                else
@@ -2498,6 +2516,12 @@ static int read_populate_opts(struct replay_opts *opts)
                if (file_exists(rebase_path_reschedule_failed_exec()))
                        opts->reschedule_failed_exec = 1;
 
+               if (file_exists(rebase_path_drop_redundant_commits()))
+                       opts->drop_redundant_commits = 1;
+
+               if (file_exists(rebase_path_keep_redundant_commits()))
+                       opts->keep_redundant_commits = 1;
+
                read_strategy_opts(opts, &buf);
                strbuf_release(&buf);
 
@@ -2549,8 +2573,6 @@ static void write_strategy_opts(struct replay_opts *opts)
 int write_basic_state(struct replay_opts *opts, const char *head_name,
                      struct commit *onto, const char *orig_head)
 {
-       const char *quiet = getenv("GIT_QUIET");
-
        if (head_name)
                write_file(rebase_path_head_name(), "%s\n", head_name);
        if (onto)
@@ -2559,8 +2581,8 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
        if (orig_head)
                write_file(rebase_path_orig_head(), "%s\n", orig_head);
 
-       if (quiet)
-               write_file(rebase_path_quiet(), "%s\n", quiet);
+       if (opts->quiet)
+               write_file(rebase_path_quiet(), "%s", "");
        if (opts->verbose)
                write_file(rebase_path_verbose(), "%s", "");
        if (opts->strategy)
@@ -2577,6 +2599,10 @@ int write_basic_state(struct replay_opts *opts, const char *head_name,
                write_file(rebase_path_gpg_sign_opt(), "-S%s\n", opts->gpg_sign);
        if (opts->signoff)
                write_file(rebase_path_signoff(), "--signoff\n");
+       if (opts->drop_redundant_commits)
+               write_file(rebase_path_drop_redundant_commits(), "%s", "");
+       if (opts->keep_redundant_commits)
+               write_file(rebase_path_keep_redundant_commits(), "%s", "");
        if (opts->reschedule_failed_exec)
                write_file(rebase_path_reschedule_failed_exec(), "%s", "");
 
@@ -3176,7 +3202,7 @@ static int do_label(struct repository *r, const char *name, int len)
                return error(_("illegal label name: '%.*s'"), len, name);
 
        strbuf_addf(&ref_name, "refs/rewritten/%.*s", len, name);
-       strbuf_addf(&msg, "rebase -i (label) '%.*s'", len, name);
+       strbuf_addf(&msg, "rebase (label) '%.*s'", len, name);
 
        transaction = ref_store_transaction_begin(refs, &err);
        if (!transaction) {
@@ -4563,7 +4589,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                                   struct rev_info *revs, struct strbuf *out,
                                   unsigned flags)
 {
-       int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
        int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
        int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
        struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
@@ -4626,8 +4651,6 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                if (!to_merge) {
                        /* non-merge commit: easy case */
                        strbuf_reset(&buf);
-                       if (!keep_empty && is_empty)
-                               strbuf_addf(&buf, "%c ", comment_line_char);
                        strbuf_addf(&buf, "%s %s %s", cmd_pick,
                                    oid_to_hex(&commit->object.oid),
                                    oneline.buf);
@@ -4794,7 +4817,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
        struct pretty_print_context pp = {0};
        struct rev_info revs;
        struct commit *commit;
-       int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
        const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
        int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 
@@ -4830,12 +4852,10 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
                return make_script_with_merges(&pp, &revs, out, flags);
 
        while ((commit = get_revision(&revs))) {
-               int is_empty  = is_original_commit_empty(commit);
+               int is_empty = is_original_commit_empty(commit);
 
                if (!is_empty && (commit->object.flags & PATCHSAME))
                        continue;
-               if (!keep_empty && is_empty)
-                       strbuf_addf(out, "%c ", comment_line_char);
                strbuf_addf(out, "%s %s ", insn,
                            oid_to_hex(&commit->object.oid));
                pretty_print_commit(&pp, commit, out);
@@ -4972,7 +4992,7 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
 
        todo_list_to_strbuf(r, todo_list, &buf, num, flags);
        if (flags & TODO_LIST_APPEND_TODO_HELP)
-               append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
+               append_todo_help(count_commands(todo_list),
                                 shortrevisions, shortonto, &buf);
 
        res = write_message(buf.buf, buf.len, file, 0);
index 393571e89a6ce26f6c7655ce081f47faee9c24e5..718a07426dae930dc07417f902fa1ad5682eb337 100644 (file)
@@ -40,6 +40,7 @@ struct replay_opts {
        int allow_rerere_auto;
        int allow_empty;
        int allow_empty_message;
+       int drop_redundant_commits;
        int keep_redundant_commits;
        int verbose;
        int quiet;
@@ -133,7 +134,7 @@ int sequencer_rollback(struct repository *repo, struct replay_opts *opts);
 int sequencer_skip(struct repository *repo, struct replay_opts *opts);
 int sequencer_remove_state(struct replay_opts *opts);
 
-#define TODO_LIST_KEEP_EMPTY (1U << 0)
+/* #define TODO_LIST_KEEP_EMPTY (1U << 0) */ /* No longer used */
 #define TODO_LIST_SHORTEN_IDS (1U << 1)
 #define TODO_LIST_ABBREVIATE_CMDS (1U << 2)
 #define TODO_LIST_REBASE_MERGES (1U << 3)
index f19da55b0783dc2d1bf4cab0e0ce76be5711cc55..bb0065ccaf5b764323cd727dbabe3061bbe328e4 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -479,6 +479,21 @@ void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src)
        }
 }
 
+#define URL_UNSAFE_CHARS " <>\"%{}|\\^`:/?#[]@!$&'()*+,;="
+
+void strbuf_add_percentencode(struct strbuf *dst, const char *src)
+{
+       size_t i, len = strlen(src);
+
+       for (i = 0; i < len; i++) {
+               unsigned char ch = src[i];
+               if (ch <= 0x1F || ch >= 0x7F || strchr(URL_UNSAFE_CHARS, ch))
+                       strbuf_addf(dst, "%%%02X", (unsigned char)ch);
+               else
+                       strbuf_addch(dst, ch);
+       }
+}
+
 size_t strbuf_fread(struct strbuf *sb, size_t size, FILE *f)
 {
        size_t res;
index aae7ac3a82da1adbe4df82e915d47bc14f4bd033..ce8e49c0b2a1427fc9e28c9d0c5c3c6a8e5b66fc 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -378,6 +378,12 @@ size_t strbuf_expand_dict_cb(struct strbuf *sb,
  */
 void strbuf_addbuf_percentquote(struct strbuf *dst, const struct strbuf *src);
 
+/**
+ * Append the contents of a string to a strbuf, percent-encoding any characters
+ * that are needed to be encoded for a URL.
+ */
+void strbuf_add_percentencode(struct strbuf *dst, const char *src);
+
 /**
  * Append the given byte size as a human-readable string (i.e. 12.23 KiB,
  * 3.50 MiB).
index 1e3bc7c8f4a57e131bb40a2044d1cbbabe754211..234c722b485e1ecdf9f00ba56d119c8364a6f949 100644 (file)
@@ -48,6 +48,7 @@ static int iterate_cb(const char *var, const char *value, void *data)
        printf("value=%s\n", value ? value : "(null)");
        printf("origin=%s\n", current_config_origin_type());
        printf("name=%s\n", current_config_name());
+       printf("lno=%d\n", current_config_line());
        printf("scope=%s\n", config_scope_name(current_config_scope()));
 
        return 0;
index b4b752b01aaf558b19879ee3e80be1d55e4b2eaa..ae52183e634425321ce11731804c5d0bb1bebee4 100644 (file)
@@ -19,7 +19,7 @@ int cmd__windows_named_pipe(int argc, const char **argv)
        if (argc < 2)
                goto print_usage;
        filename = argv[1];
-       if (strchr(filename, '/') || strchr(filename, '\\'))
+       if (strpbrk(filename, "/\\"))
                goto print_usage;
        strbuf_addf(&pathname, "//./pipe/%s", filename);
 
diff --git a/t/lib-log-graph.sh b/t/lib-log-graph.sh
new file mode 100755 (executable)
index 0000000..1184cce
--- /dev/null
@@ -0,0 +1,28 @@
+# Helps shared by the test scripts for comparing log graphs.
+
+sanitize_log_output () {
+       sed -e 's/ *$//' \
+           -e 's/commit [0-9a-f]*$/commit COMMIT_OBJECT_NAME/' \
+           -e 's/Merge: [ 0-9a-f]*$/Merge: MERGE_PARENTS/' \
+           -e 's/Merge tag.*/Merge HEADS DESCRIPTION/' \
+           -e 's/Merge commit.*/Merge HEADS DESCRIPTION/' \
+           -e 's/index [0-9a-f]*\.\.[0-9a-f]*/index BEFORE..AFTER/'
+}
+
+lib_test_cmp_graph () {
+       git log --graph "$@" >output &&
+       sed 's/ *$//' >output.sanitized <output &&
+       test_i18ncmp expect output.sanitized
+}
+
+lib_test_cmp_short_graph () {
+       git log --graph --pretty=short "$@" >output &&
+       sanitize_log_output >output.sanitized <output &&
+       test_i18ncmp expect output.sanitized
+}
+
+lib_test_cmp_colored_graph () {
+       git log --graph --color=always "$@" >output.colors.raw &&
+       test_decode_color <output.colors.raw | sed "s/ *\$//" >output.colors &&
+       test_cmp expect.colors output.colors
+}
index 6a3a42531b056285ac3a42cabe97c41146fa4291..7743f4f4c9ff728001b4d9d8201654a288c1ef9b 100755 (executable)
@@ -39,6 +39,28 @@ test_perf 'pack to file (bitmap)' '
        git pack-objects --use-bitmap-index --all pack1b </dev/null >/dev/null
 '
 
+test_perf 'rev-list (commits)' '
+       git rev-list --all --use-bitmap-index >/dev/null
+'
+
+test_perf 'rev-list (objects)' '
+       git rev-list --all --use-bitmap-index --objects >/dev/null
+'
+
+test_perf 'rev-list count with blob:none' '
+       git rev-list --use-bitmap-index --count --objects --all \
+               --filter=blob:none >/dev/null
+'
+
+test_perf 'rev-list count with blob:limit=1k' '
+       git rev-list --use-bitmap-index --count --objects --all \
+               --filter=blob:limit=1k >/dev/null
+'
+
+test_perf 'simulated partial clone' '
+       git pack-objects --stdout --all --filter=blob:none </dev/null >/dev/null
+'
+
 test_expect_success 'create partial bitmap state' '
        # pick a commit to represent the repo tip in the past
        cutoff=$(git rev-list HEAD~100 -1) &&
index 1744cee5e996fe4e03149de3a0fb8065d2aa6d67..370a389e5c5c509b51189e23b81c6ce2d1b79a18 100755 (executable)
@@ -424,9 +424,24 @@ test_expect_success 'local ignore inside a sub-directory with --verbose' '
        )
 '
 
-test_expect_success_multi 'nested include' \
-       'a/b/.gitignore:8:!on*  a/b/one' '
-       test_check_ignore "a/b/one"
+test_expect_success 'nested include of negated pattern' '
+       expect "" &&
+       test_check_ignore "a/b/one" 1
+'
+
+test_expect_success 'nested include of negated pattern with -q' '
+       expect "" &&
+       test_check_ignore "-q a/b/one" 1
+'
+
+test_expect_success 'nested include of negated pattern with -v' '
+       expect "a/b/.gitignore:8:!on*   a/b/one" &&
+       test_check_ignore "-v a/b/one" 0
+'
+
+test_expect_success 'nested include of negated pattern with -v -n' '
+       expect "a/b/.gitignore:8:!on*   a/b/one" &&
+       test_check_ignore "-v -n a/b/one" 0
 '
 
 ############################################################################
@@ -460,7 +475,6 @@ test_expect_success 'cd to ignored sub-directory' '
        expect_from_stdin <<-\EOF &&
                foo
                twoooo
-               ../one
                seven
                ../../one
        EOF
@@ -543,7 +557,6 @@ test_expect_success 'global ignore' '
                globalthree
                a/globalthree
                a/per-repo
-               globaltwo
        EOF
        test_check_ignore "globalone per-repo globalthree a/globalthree a/per-repo not-ignored globaltwo"
 '
@@ -586,17 +599,7 @@ EOF
 cat <<-\EOF >expected-default
        one
        a/one
-       a/b/on
-       a/b/one
-       a/b/one one
-       a/b/one two
-       "a/b/one\"three"
-       a/b/two
        a/b/twooo
-       globaltwo
-       a/globaltwo
-       a/b/globaltwo
-       b/globaltwo
 EOF
 cat <<-EOF >expected-verbose
        .gitignore:1:one        one
@@ -696,8 +699,12 @@ cat <<-EOF >expected-all
        $global_excludes:2:!globaltwo   ../b/globaltwo
        ::      c/not-ignored
 EOF
+cat <<-EOF >expected-default
+../one
+one
+b/twooo
+EOF
 grep -v '^::   ' expected-all >expected-verbose
-sed -e 's/.*   //' expected-verbose >expected-default
 
 broken_c_unquote stdin >stdin0
 
index 6c6d77b51aaacaf90f08ca9f528c244c16e014a1..dc664da551a3e9711df96f7291df4a1c8070d4d7 100755 (executable)
@@ -795,7 +795,6 @@ test_expect_success PERL 'missing file in delayed checkout' '
 
        rm -rf repo-cloned &&
        test_must_fail git clone repo repo-cloned 2>git-stderr.log &&
-       cat git-stderr.log &&
        grep "error: .missing-delay\.a. was not filtered properly" git-stderr.log
 '
 
index 82eaaea0f4954d22861a4733b80aebcc361aca9a..39f097ea9e5094530b981c49193df8df2cbda406 100755 (executable)
@@ -240,6 +240,57 @@ test_expect_success 'do not match configured credential' '
        EOF
 '
 
+test_expect_success 'match multiple configured helpers' '
+       test_config credential.helper "verbatim \"\" \"\"" &&
+       test_config credential.https://example.com.helper "$HELPER" &&
+       check fill <<-\EOF
+       protocol=https
+       host=example.com
+       path=repo.git
+       --
+       protocol=https
+       host=example.com
+       username=foo
+       password=bar
+       --
+       verbatim: get
+       verbatim: protocol=https
+       verbatim: host=example.com
+       EOF
+'
+
+test_expect_success 'match multiple configured helpers with URLs' '
+       test_config credential.https://example.com/repo.git.helper "verbatim \"\" \"\"" &&
+       test_config credential.https://example.com.helper "$HELPER" &&
+       check fill <<-\EOF
+       protocol=https
+       host=example.com
+       path=repo.git
+       --
+       protocol=https
+       host=example.com
+       username=foo
+       password=bar
+       --
+       verbatim: get
+       verbatim: protocol=https
+       verbatim: host=example.com
+       EOF
+'
+
+test_expect_success 'match percent-encoded values' '
+       test_config credential.https://example.com/%2566.git.helper "$HELPER" &&
+       check fill <<-\EOF
+       url=https://example.com/%2566.git
+       --
+       protocol=https
+       host=example.com
+       username=foo
+       password=bar
+       --
+       EOF
+'
+
 test_expect_success 'pull username from config' '
        test_config credential.https://example.com.username foo &&
        check fill <<-\EOF
@@ -255,6 +306,63 @@ test_expect_success 'pull username from config' '
        EOF
 '
 
+test_expect_success 'honors username from URL over helper (URL)' '
+       test_config credential.https://example.com.username bob &&
+       test_config credential.https://example.com.helper "verbatim \"\" bar" &&
+       check fill <<-\EOF
+       url=https://alice@example.com
+       --
+       protocol=https
+       host=example.com
+       username=alice
+       password=bar
+       --
+       verbatim: get
+       verbatim: protocol=https
+       verbatim: host=example.com
+       verbatim: username=alice
+       EOF
+'
+
+test_expect_success 'honors username from URL over helper (components)' '
+       test_config credential.https://example.com.username bob &&
+       test_config credential.https://example.com.helper "verbatim \"\" bar" &&
+       check fill <<-\EOF
+       protocol=https
+       host=example.com
+       username=alice
+       --
+       protocol=https
+       host=example.com
+       username=alice
+       password=bar
+       --
+       verbatim: get
+       verbatim: protocol=https
+       verbatim: host=example.com
+       verbatim: username=alice
+       EOF
+'
+
+test_expect_success 'last matching username wins' '
+       test_config credential.https://example.com/path.git.username bob &&
+       test_config credential.https://example.com.username alice &&
+       test_config credential.https://example.com.helper "verbatim \"\" bar" &&
+       check fill <<-\EOF
+       url=https://example.com/path.git
+       --
+       protocol=https
+       host=example.com
+       username=alice
+       password=bar
+       --
+       verbatim: get
+       verbatim: protocol=https
+       verbatim: host=example.com
+       verbatim: username=alice
+       EOF
+'
+
 test_expect_success 'http paths can be part of context' '
        check fill "verbatim foo bar" <<-\EOF &&
        protocol=https
@@ -289,6 +397,26 @@ test_expect_success 'http paths can be part of context' '
        EOF
 '
 
+test_expect_success 'context uses urlmatch' '
+       test_config "credential.https://*.org.useHttpPath" true &&
+       check fill "verbatim foo bar" <<-\EOF
+       protocol=https
+       host=example.org
+       path=foo.git
+       --
+       protocol=https
+       host=example.org
+       path=foo.git
+       username=foo
+       password=bar
+       --
+       verbatim: get
+       verbatim: protocol=https
+       verbatim: host=example.org
+       verbatim: path=foo.git
+       EOF
+'
+
 test_expect_success 'helpers can abort the process' '
        test_must_fail git \
                -c credential.helper="!f() { echo quit=1; }; f" \
index 7d982096fbf0a25262b975f57f625895eac0aa33..b4c9c32a0372516f020adbcac28e6290c7bbbd78 100755 (executable)
@@ -141,6 +141,21 @@ test_expect_success 'set sparse-checkout using --stdin' '
        check_files repo "a folder1 folder2"
 '
 
+test_expect_success 'add to sparse-checkout' '
+       cat repo/.git/info/sparse-checkout >expect &&
+       cat >add <<-\EOF &&
+       pattern1
+       /folder1/
+       pattern2
+       EOF
+       cat add >>expect &&
+       git -C repo sparse-checkout add --stdin <add &&
+       git -C repo sparse-checkout list >actual &&
+       test_cmp expect actual &&
+       test_cmp expect repo/.git/info/sparse-checkout &&
+       check_files repo "a folder1 folder2"
+'
+
 test_expect_success 'cone mode: match patterns' '
        git -C repo config --worktree core.sparseCheckoutCone true &&
        rm -rf repo/a repo/folder1 repo/folder2 &&
@@ -219,8 +234,52 @@ test_expect_success 'cone mode: set with nested folders' '
        test_cmp repo/.git/info/sparse-checkout expect
 '
 
+test_expect_success 'cone mode: add independent path' '
+       git -C repo sparse-checkout set deep/deeper1 &&
+       git -C repo sparse-checkout add folder1 &&
+       cat >expect <<-\EOF &&
+       /*
+       !/*/
+       /deep/
+       !/deep/*/
+       /deep/deeper1/
+       /folder1/
+       EOF
+       test_cmp expect repo/.git/info/sparse-checkout &&
+       check_files repo a deep folder1
+'
+
+test_expect_success 'cone mode: add sibling path' '
+       git -C repo sparse-checkout set deep/deeper1 &&
+       git -C repo sparse-checkout add deep/deeper2 &&
+       cat >expect <<-\EOF &&
+       /*
+       !/*/
+       /deep/
+       !/deep/*/
+       /deep/deeper1/
+       /deep/deeper2/
+       EOF
+       test_cmp expect repo/.git/info/sparse-checkout &&
+       check_files repo a deep
+'
+
+test_expect_success 'cone mode: add parent path' '
+       git -C repo sparse-checkout set deep/deeper1 folder1 &&
+       git -C repo sparse-checkout add deep &&
+       cat >expect <<-\EOF &&
+       /*
+       !/*/
+       /deep/
+       /folder1/
+       EOF
+       test_cmp expect repo/.git/info/sparse-checkout &&
+       check_files repo a deep folder1
+'
+
 test_expect_success 'revert to old sparse-checkout on bad update' '
        test_when_finished git -C repo reset --hard &&
+       git -C repo sparse-checkout set deep &&
        echo update >repo/deep/deeper2/a &&
        cp repo/.git/info/sparse-checkout expect &&
        test_must_fail git -C repo sparse-checkout set deep/deeper1 2>err &&
@@ -358,10 +417,20 @@ test_expect_success 'pattern-checks: too short' '
        cat >repo/.git/info/sparse-checkout <<-\EOF &&
        /*
        !/*/
-       /a
+       /
        EOF
        check_read_tree_errors repo "a" "disabling cone pattern matching"
 '
+test_expect_success 'pattern-checks: not too short' '
+       cat >repo/.git/info/sparse-checkout <<-\EOF &&
+       /*
+       !/*/
+       /b/
+       EOF
+       git -C repo read-tree -mu HEAD 2>err &&
+       test_must_be_empty err &&
+       check_files repo a
+'
 
 test_expect_success 'pattern-checks: trailing "*"' '
        cat >repo/.git/info/sparse-checkout <<-\EOF &&
@@ -438,4 +507,18 @@ test_expect_success BSLASHPSPEC 'pattern-checks: escaped characters' '
        test_cmp list-expect list-actual
 '
 
+test_expect_success MINGW 'cone mode replaces backslashes with slashes' '
+       git -C repo sparse-checkout set deep\\deeper1 &&
+       cat >expect <<-\EOF &&
+       /*
+       !/*/
+       /deep/
+       !/deep/*/
+       /deep/deeper1/
+       EOF
+       test_cmp expect repo/.git/info/sparse-checkout &&
+       check_files repo a deep &&
+       check_files repo/deep a deeper1
+'
+
 test_done
index 5464c46c185f39cee27da9a28e112bdb7b8960b1..97ebfe1f9d5cea7b37d70d8f7069f00bb5045b8e 100755 (executable)
@@ -1408,6 +1408,8 @@ test_expect_success 'urlmatch favors more specific URLs' '
                cookieFile = /tmp/wildcard.txt
        [http "https://*.example.com/wildcardwithsubdomain"]
                cookieFile = /tmp/wildcardwithsubdomain.txt
+       [http "https://*.example.*"]
+               cookieFile = /tmp/multiwildcard.txt
        [http "https://trailing.example.com"]
                cookieFile = /tmp/trailing.txt
        [http "https://user@*.example.com/"]
@@ -1454,6 +1456,10 @@ test_expect_success 'urlmatch favors more specific URLs' '
 
        echo http.cookiefile /tmp/sub.txt >expect &&
        git config --get-urlmatch HTTP https://user@sub.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/multiwildcard.txt >expect &&
+       git config --get-urlmatch HTTP https://wildcard.example.org >actual &&
        test_cmp expect actual
 '
 
index fba0abe429ace841f720cc8a41281bdf08943053..3a527e3a8438a583bfd8704ad23f9ae2e48ac382 100755 (executable)
@@ -238,8 +238,8 @@ test_expect_success 'error on modifying repo config without repo' '
 
 cmdline_config="'foo.bar=from-cmdline'"
 test_expect_success 'iteration shows correct origins' '
-       echo "[foo]bar = from-repo" >.git/config &&
-       echo "[foo]bar = from-home" >.gitconfig &&
+       printf "[ignore]\n\tthis = please\n[foo]bar = from-repo\n" >.git/config &&
+       printf "[foo]\n\tbar = from-home\n" >.gitconfig &&
        if test_have_prereq MINGW
        then
                # Use Windows path (i.e. *not* $HOME)
@@ -253,18 +253,28 @@ test_expect_success 'iteration shows correct origins' '
        value=from-home
        origin=file
        name=$HOME_GITCONFIG
+       lno=2
        scope=global
 
+       key=ignore.this
+       value=please
+       origin=file
+       name=.git/config
+       lno=2
+       scope=local
+
        key=foo.bar
        value=from-repo
        origin=file
        name=.git/config
+       lno=3
        scope=local
 
        key=foo.bar
        value=from-cmdline
        origin=command line
        name=
+       lno=-1
        scope=command
        EOF
        GIT_CONFIG_PARAMETERS=$cmdline_config test-tool config iterate >actual &&
index d199d872fb1996922a0a77058955c105aef4377c..36b7ef504687f52e50fd4fef1500bc487c6f286d 100755 (executable)
@@ -75,7 +75,7 @@ test_expect_success 'for_each_reflog()' '
 '
 
 test_expect_success 'for_each_reflog_ent()' '
-       $RUN for-each-reflog-ent HEAD >actual && cat actual &&
+       $RUN for-each-reflog-ent HEAD >actual &&
        head -n1 actual | grep first &&
        tail -n2 actual | head -n1 | grep master.to.new
 '
index 02478bc4ece20564a7d5ea27df91cb0695d6e5ec..d09eff503c65aa4f9b6e95f6c1d163f0936e89f9 100755 (executable)
@@ -141,7 +141,6 @@ test_expect_success 'email without @ is okay' '
        git update-ref refs/heads/bogus "$new" &&
        test_when_finished "git update-ref -d refs/heads/bogus" &&
        git fsck 2>out &&
-       cat out &&
        ! grep "commit $new" out
 '
 
index 2242cd098ec4ee0b278c008220d8c2a678b174c7..a30b7ca6bc90c94c5b948b6040e890bc0e569c6f 100755 (executable)
@@ -9,7 +9,6 @@ Tests for command-line parsing and basic operation.
 
 test_expect_success 'update-index --nonsense fails' '
        test_must_fail git update-index --nonsense 2>msg &&
-       cat msg &&
        test -s msg
 '
 
index b5ece19460cde97e6102dc34097fe57fcee77ac9..5a7495474aa8723f77b296813381151ca39b4b8b 100755 (executable)
@@ -570,6 +570,15 @@ test_expect_success '"add" an existing locked but missing worktree' '
        git worktree add --force --force --detach gnoo
 '
 
+test_expect_success '"add" not tripped up by magic worktree matching"' '
+       # if worktree "sub1/bar" exists, "git worktree add bar" in distinct
+       # directory `sub2` should not mistakenly complain that `bar` is an
+       # already-registered worktree
+       mkdir sub1 sub2 &&
+       git -C sub1 --git-dir=../.git worktree add --detach bozo &&
+       git -C sub2 --git-dir=../.git worktree add --detach bozo
+'
+
 test_expect_success FUNNYNAMES 'sanitize generated worktree name' '
        git worktree add --detach ".  weird*..?.lock.lock" &&
        test -d .git/worktrees/---weird-.-
index bb6fb9b12cb7f41cea671a7df830ccc97c592e4f..69ffe865b43965bb84bbe60402fc9c03ecc8ba9b 100755 (executable)
@@ -151,4 +151,10 @@ test_expect_success 'linked worktrees are sorted' '
        test_cmp expected sorted/main/actual
 '
 
+test_expect_success 'worktree path when called in .git directory' '
+       git worktree list >list1&&
+       git -C .git worktree list >list2 &&
+       test_cmp list1 list2
+'
+
 test_done
index 318b5bce7ebeab2ad96097e992e37867b3a7adb5..4a080007137926b5dcc84ee80856bcbcfc71340f 100755 (executable)
@@ -130,7 +130,6 @@ test_expect_success '--recurse-submodules and pathspecs setup' '
 
        git ls-files --recurse-submodules >actual &&
        test_cmp expect actual &&
-       cat actual &&
        git ls-files --recurse-submodules "*" >actual &&
        test_cmp expect actual
 '
index 221b35f2df30846960f74e8cb4d8539f228d6a5f..40d297599584af8fc13b5ddfbb7a991db190c163 100755 (executable)
@@ -143,11 +143,11 @@ test_expect_success 'setup: recover' '
 
 test_expect_success 'Show verbose error when HEAD could not be detached' '
        >B &&
+       test_when_finished "rm -f B" &&
        test_must_fail git rebase topic 2>output.err >output.out &&
        test_i18ngrep "The following untracked working tree files would be overwritten by checkout:" output.err &&
        test_i18ngrep B output.err
 '
-rm -f B
 
 test_expect_success 'fail when upstream arg is missing and not on branch' '
        git checkout topic &&
@@ -165,19 +165,37 @@ test_expect_success 'rebase works with format.useAutoBase' '
        git rebase master
 '
 
-test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg' '
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--merge)' '
        git checkout -b default-base master &&
        git checkout -b default topic &&
        git config branch.default.remote . &&
        git config branch.default.merge refs/heads/default-base &&
-       git rebase &&
+       git rebase --merge &&
        git rev-parse --verify default-base >expect &&
        git rev-parse default~1 >actual &&
        test_cmp expect actual &&
        git checkout default-base &&
        git reset --hard HEAD^ &&
        git checkout default &&
-       git rebase &&
+       git rebase --merge &&
+       git rev-parse --verify default-base >expect &&
+       git rev-parse default~1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'default to common base in @{upstream}s reflog if no upstream arg (--apply)' '
+       git checkout -B default-base master &&
+       git checkout -B default topic &&
+       git config branch.default.remote . &&
+       git config branch.default.merge refs/heads/default-base &&
+       git rebase --apply &&
+       git rev-parse --verify default-base >expect &&
+       git rev-parse default~1 >actual &&
+       test_cmp expect actual &&
+       git checkout default-base &&
+       git reset --hard HEAD^ &&
+       git checkout default &&
+       git rebase --apply &&
        git rev-parse --verify default-base >expect &&
        git rev-parse default~1 >actual &&
        test_cmp expect actual
@@ -206,9 +224,15 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
        test_cmp expect D
 '
 
-test_expect_success 'rebase -q is quiet' '
+test_expect_success 'rebase --apply -q is quiet' '
        git checkout -b quiet topic &&
-       git rebase -q master >output.out 2>&1 &&
+       git rebase --apply -q master >output.out 2>&1 &&
+       test_must_be_empty output.out
+'
+
+test_expect_success 'rebase --merge -q is quiet' '
+       git checkout -B quiet topic &&
+       git rebase --merge -q master >output.out 2>&1 &&
        test_must_be_empty output.out
 '
 
@@ -291,7 +315,7 @@ EOF
        test_cmp From_.msg out
 '
 
-test_expect_success 'rebase --am and --show-current-patch' '
+test_expect_success 'rebase --apply and --show-current-patch' '
        test_create_repo conflict-apply &&
        (
                cd conflict-apply &&
@@ -301,13 +325,13 @@ test_expect_success 'rebase --am and --show-current-patch' '
                echo two >>init.t &&
                git commit -a -m two &&
                git tag two &&
-               test_must_fail git rebase -f --onto init HEAD^ &&
+               test_must_fail git rebase --apply -f --onto init HEAD^ &&
                GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
                grep "show.*$(git rev-parse two)" stderr
        )
 '
 
-test_expect_success 'rebase --am and .gitattributes' '
+test_expect_success 'rebase --apply and .gitattributes' '
        test_create_repo attributes &&
        (
                cd attributes &&
@@ -377,4 +401,22 @@ test_expect_success 'rebase -c rebase.useBuiltin=false warning' '
        test_must_be_empty err
 '
 
+test_expect_success 'switch to branch checked out here' '
+       git checkout master &&
+       git rebase master master
+'
+
+test_expect_success 'switch to branch not checked out' '
+       git checkout master &&
+       git branch other &&
+       git rebase master other
+'
+
+test_expect_success 'refuse to switch to branch checked out elsewhere' '
+       git checkout master &&
+       git worktree add wt &&
+       test_must_fail git -C wt rebase master master 2>err &&
+       test_i18ngrep "already checked out" err
+'
+
 test_done
index a0b9438b2286712c41fb0d5648e8bf29caee8842..f18bae94507587a4dc9118cc3d508902e02c6bc1 100755 (executable)
@@ -52,13 +52,13 @@ test_expect_success 'rebase --interactive: directory rename detected' '
        )
 '
 
-test_expect_failure 'rebase (am): directory rename detected' '
+test_expect_failure 'rebase --apply: directory rename detected' '
        (
                cd dir-rename &&
 
                git checkout B^0 &&
 
-               git -c merge.directoryRenames=true rebase A &&
+               git -c merge.directoryRenames=true rebase --apply A &&
 
                git ls-files -s >out &&
                test_line_count = 5 out &&
index d79a3ef50546dde3dc381a11300dddf0bdf54cee..c5ce3ab760eae5b438dddf20fc1389824f21c7e3 100755 (executable)
@@ -72,15 +72,16 @@ test_expect_success 'rebase --keep-empty' '
        test_line_count = 6 actual
 '
 
-test_expect_success 'rebase -i with empty HEAD' '
+test_expect_success 'rebase -i with empty todo list' '
        cat >expect <<-\EOF &&
        error: nothing to do
        EOF
        (
                set_fake_editor &&
-               test_must_fail env FAKE_LINES="1 exec_true" \
-                       git rebase -i HEAD^ >actual 2>&1
+               test_must_fail env FAKE_LINES="#" \
+                       git rebase -i HEAD^ >output 2>&1
        ) &&
+       tail -n 1 output >actual &&  # Ignore output about changing todo list
        test_i18ncmp expect actual
 '
 
@@ -222,7 +223,7 @@ test_expect_success 'reflog for the branch shows state before rebase' '
 '
 
 test_expect_success 'reflog for the branch shows correct finish message' '
-       printf "rebase -i (finish): refs/heads/branch1 onto %s\n" \
+       printf "rebase (finish): refs/heads/branch1 onto %s\n" \
                "$(git rev-parse branch2)" >expected &&
        git log -g --pretty=%gs -1 refs/heads/branch1 >actual &&
        test_cmp expected actual
@@ -1137,7 +1138,7 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int
        git checkout conflict-branch &&
        (
                set_fake_editor &&
-               test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
+               test_must_fail git rebase -f --apply --onto HEAD~2 HEAD~ &&
                test_must_fail git rebase --edit-todo
        ) &&
        git rebase --abort
@@ -1161,10 +1162,10 @@ test_expect_success 'rebase -i produces readable reflog' '
        git branch -f branch-reflog-test H &&
        git rebase -i --onto I F branch-reflog-test &&
        cat >expect <<-\EOF &&
-       rebase -i (finish): returning to refs/heads/branch-reflog-test
-       rebase -i (pick): H
-       rebase -i (pick): G
-       rebase -i (start): checkout I
+       rebase (finish): returning to refs/heads/branch-reflog-test
+       rebase (pick): H
+       rebase (pick): G
+       rebase (start): checkout I
        EOF
        git reflog -n4 HEAD |
        sed "s/[^:]*: //" >actual &&
index b393e1e9fee88bb9b3df6ecc8544dfa370086e41..61b76f3301982d26b808044bde9ce928f663112e 100755 (executable)
@@ -18,32 +18,29 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'rebase -m' '
-       git rebase -m master >report &&
-       >expect &&
-       sed -n -e "/^Already applied: /p" \
-               -e "/^Committed: /p" report >actual &&
-       test_cmp expect actual
+       git rebase -m master >actual &&
+       test_must_be_empty actual
 '
 
 test_expect_success 'rebase against master twice' '
-       git rebase master >out &&
+       git rebase --apply master >out &&
        test_i18ngrep "Current branch topic is up to date" out
 '
 
 test_expect_success 'rebase against master twice with --force' '
-       git rebase --force-rebase master >out &&
+       git rebase --force-rebase --apply master >out &&
        test_i18ngrep "Current branch topic is up to date, rebase forced" out
 '
 
 test_expect_success 'rebase against master twice from another branch' '
        git checkout topic^ &&
-       git rebase master topic >out &&
+       git rebase --apply master topic >out &&
        test_i18ngrep "Current branch topic is up to date" out
 '
 
 test_expect_success 'rebase fast-forward to master' '
        git checkout topic^ &&
-       git rebase topic >out &&
+       git rebase --apply topic >out &&
        test_i18ngrep "Fast-forwarded HEAD to topic" out
 '
 
@@ -92,7 +89,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
        git checkout -b reflog-topic start &&
        test_commit reflog-to-rebase &&
 
-       git rebase reflog-onto &&
+       git rebase --apply reflog-onto &&
        git log -g --format=%gs -3 >actual &&
        cat >expect <<-\EOF &&
        rebase finished: returning to refs/heads/reflog-topic
@@ -102,7 +99,7 @@ test_expect_success 'GIT_REFLOG_ACTION' '
        test_cmp expect actual &&
 
        git checkout -b reflog-prefix reflog-to-rebase &&
-       GIT_REFLOG_ACTION=change-the-reflog git rebase reflog-onto &&
+       GIT_REFLOG_ACTION=change-the-reflog git rebase --apply reflog-onto &&
        git log -g --format=%gs -3 >actual &&
        cat >expect <<-\EOF &&
        rebase finished: returning to refs/heads/reflog-prefix
index 910f2182843476edf878bfc42f34d868c70d8cf5..97efea0f569714792e82ae7c66a796d6f44ee3be 100755 (executable)
@@ -96,14 +96,14 @@ testrebase() {
        '
 }
 
-testrebase "" .git/rebase-apply
+testrebase " --apply" .git/rebase-apply
 testrebase " --merge" .git/rebase-merge
 
-test_expect_success 'rebase --quit' '
+test_expect_success 'rebase --apply --quit' '
        cd "$work_dir" &&
        # Clean up the state from the previous one
        git reset --hard pre-rebase &&
-       test_must_fail git rebase master &&
+       test_must_fail git rebase --apply master &&
        test_path_is_dir .git/rebase-apply &&
        head_before=$(git rev-parse HEAD) &&
        git rebase --quit &&
index 5f7e73cf83a267cd3dc74917c963352fe9a1856d..b97ea623639486000a2ca26da253775add02356a 100755 (executable)
@@ -34,7 +34,7 @@ test_expect_success setup '
        remove_progress_re="$(printf "s/.*\\r//")"
 '
 
-create_expected_success_am () {
+create_expected_success_apply () {
        cat >expected <<-EOF
        $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
        First, rewinding head to replay your work on top of it...
@@ -44,7 +44,7 @@ create_expected_success_am () {
        EOF
 }
 
-create_expected_success_interactive () {
+create_expected_success_merge () {
        q_to_cr >expected <<-EOF
        $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
        Applied autostash.
@@ -52,7 +52,7 @@ create_expected_success_interactive () {
        EOF
 }
 
-create_expected_failure_am () {
+create_expected_failure_apply () {
        cat >expected <<-EOF
        $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
        First, rewinding head to replay your work on top of it...
@@ -64,7 +64,7 @@ create_expected_failure_am () {
        EOF
 }
 
-create_expected_failure_interactive () {
+create_expected_failure_merge () {
        cat >expected <<-EOF
        $(grep "^Created autostash: [0-9a-f][0-9a-f]*\$" actual)
        Applying autostash resulted in conflicts.
@@ -101,9 +101,9 @@ testrebase () {
 
        test_expect_success "rebase$type --autostash: check output" '
                test_when_finished git branch -D rebased-feature-branch &&
-               suffix=${type#\ --} && suffix=${suffix:-am} &&
-               if test ${suffix} = "merge"; then
-                       suffix=interactive
+               suffix=${type#\ --} && suffix=${suffix:-apply} &&
+               if test ${suffix} = "interactive"; then
+                       suffix=merge
                fi &&
                create_expected_success_$suffix &&
                sed "$remove_progress_re" <actual >actual2 &&
@@ -202,9 +202,9 @@ testrebase () {
 
        test_expect_success "rebase$type: check output with conflicting stash" '
                test_when_finished git branch -D rebased-feature-branch &&
-               suffix=${type#\ --} && suffix=${suffix:-am} &&
-               if test ${suffix} = "merge"; then
-                       suffix=interactive
+               suffix=${type#\ --} && suffix=${suffix:-apply} &&
+               if test ${suffix} = "interactive"; then
+                       suffix=merge
                fi &&
                create_expected_failure_$suffix &&
                sed "$remove_progress_re" <actual >actual2 &&
@@ -234,7 +234,7 @@ test_expect_success "rebase: noop rebase" '
        git checkout feature-branch
 '
 
-testrebase "" .git/rebase-apply
+testrebase " --apply" .git/rebase-apply
 testrebase " --merge" .git/rebase-merge
 testrebase " --interactive" .git/rebase-merge
 
index 325072b0a33e10ad44c3d3d561e207b2a03a13a6..cf8dfd6c203b30b0da4339b099cf6cabcbee04a9 100755 (executable)
@@ -26,7 +26,7 @@ test_run_rebase () {
                test_linear_range 'd e' c..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -50,7 +50,7 @@ test_run_rebase () {
                test_cmp_rev e HEAD
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -66,7 +66,7 @@ test_run_rebase () {
                test_linear_range 'd e' b..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success --fork-point
 test_run_rebase success -m
 test_run_rebase success -i
@@ -83,7 +83,7 @@ test_run_rebase () {
                test_linear_range 'd e' branch-b..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success --fork-point
 test_run_rebase success -m
 test_run_rebase success -i
@@ -98,7 +98,7 @@ test_run_rebase () {
                test_cmp_rev e HEAD
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success --fork-point
 test_run_rebase success -m
 test_run_rebase success -i
@@ -139,7 +139,7 @@ test_run_rebase () {
                test_linear_range 'd i' h..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -154,7 +154,7 @@ test_run_rebase () {
                test_linear_range 'd' h..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -169,7 +169,7 @@ test_run_rebase () {
                test_linear_range 'd i' f..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -184,7 +184,7 @@ test_run_rebase () {
                test_linear_range 'd gp i' h..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -205,17 +205,17 @@ test_expect_success 'setup of linear history for empty commit tests' '
 test_run_rebase () {
        result=$1
        shift
-       test_expect_$result "rebase $* drops empty commit" "
+       test_expect_$result "rebase $* keeps begin-empty commits" "
                reset_rebase &&
-               git rebase $* c l &&
-               test_cmp_rev c HEAD~2 &&
-               test_linear_range 'd l' c..
+               git rebase $* j l &&
+               test_cmp_rev c HEAD~4 &&
+               test_linear_range 'j d k l' c..
        "
 }
-test_run_rebase success ''
+test_run_rebase failure --apply
 test_run_rebase success -m
 test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase success -p
+test_have_prereq !REBASE_P || test_run_rebase failure -p
 
 test_run_rebase () {
        result=$1
@@ -227,10 +227,10 @@ test_run_rebase () {
                test_linear_range 'd k l' c..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
 
 test_run_rebase () {
        result=$1
@@ -242,10 +242,10 @@ test_run_rebase () {
                test_linear_range 'd k l' j..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
-test_have_prereq !REBASE_P || test_run_rebase failure -p
+test_have_prereq !REBASE_P || test_run_rebase success -p
 test_run_rebase success --rebase-merges
 
 #       m
@@ -282,7 +282,7 @@ test_run_rebase () {
                test_linear_range 'x y' c..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -297,7 +297,7 @@ test_run_rebase () {
                test_linear_range 'x y' c..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase failure -p
@@ -312,7 +312,7 @@ test_run_rebase () {
                test_linear_range 'x y' m..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase success -p
@@ -328,7 +328,7 @@ test_run_rebase () {
        "
 }
 
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase failure -p
@@ -343,7 +343,7 @@ test_run_rebase () {
                test_linear_range 'x y' m..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 test_have_prereq !REBASE_P || test_run_rebase failure -p
diff --git a/t/t3424-rebase-empty.sh b/t/t3424-rebase-empty.sh
new file mode 100755 (executable)
index 0000000..98fc2a5
--- /dev/null
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='git rebase of commits that start or become empty'
+
+. ./test-lib.sh
+
+test_expect_success 'setup test repository' '
+       test_write_lines 1 2 3 4 5 6 7 8 9 10 >numbers &&
+       test_write_lines A B C D E F G H I J >letters &&
+       git add numbers letters &&
+       git commit -m A &&
+
+       git branch upstream &&
+       git branch localmods &&
+
+       git checkout upstream &&
+       test_write_lines A B C D E >letters &&
+       git add letters &&
+       git commit -m B &&
+
+       test_write_lines 1 2 3 4 five 6 7 8 9 ten >numbers &&
+       git add numbers &&
+       git commit -m C &&
+
+       git checkout localmods &&
+       test_write_lines 1 2 3 4 five 6 7 8 9 10 >numbers &&
+       git add numbers &&
+       git commit -m C2 &&
+
+       git commit --allow-empty -m D &&
+
+       test_write_lines A B C D E >letters &&
+       git add letters &&
+       git commit -m "Five letters ought to be enough for anybody"
+'
+
+test_expect_failure 'rebase (apply-backend)' '
+       test_when_finished "git rebase --abort" &&
+       git checkout -B testing localmods &&
+       # rebase (--apply) should not drop commits that start empty
+       git rebase --apply upstream &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=drop' '
+       git checkout -B testing localmods &&
+       git rebase --merge --empty=drop upstream &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge uses default of --empty=drop' '
+       git checkout -B testing localmods &&
+       git rebase --merge upstream &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=keep' '
+       git checkout -B testing localmods &&
+       git rebase --merge --empty=keep upstream &&
+
+       test_write_lines D C2 C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --merge --empty=ask' '
+       git checkout -B testing localmods &&
+       test_must_fail git rebase --merge --empty=ask upstream &&
+
+       git rebase --skip &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive --empty=drop' '
+       git checkout -B testing localmods &&
+       git rebase --interactive --empty=drop upstream &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive --empty=keep' '
+       git checkout -B testing localmods &&
+       git rebase --interactive --empty=keep upstream &&
+
+       test_write_lines D C2 C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive --empty=ask' '
+       git checkout -B testing localmods &&
+       test_must_fail git rebase --interactive --empty=ask upstream &&
+
+       git rebase --skip &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'rebase --interactive uses default of --empty=ask' '
+       git checkout -B testing localmods &&
+       test_must_fail git rebase --interactive upstream &&
+
+       git rebase --skip &&
+
+       test_write_lines D C B A >expect &&
+       git log --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_done
index fd8efe84fe8f74d062d73f27fb63d95cc5bed4c8..e42faa44e74f1abe247aa1c16da9d5582123d42c 100755 (executable)
@@ -54,7 +54,7 @@ test_run_rebase () {
                test_linear_range 'n o' e..
        "
 }
-test_run_rebase success ''
+test_run_rebase success --apply
 test_run_rebase success -m
 test_run_rebase success -i
 
@@ -70,7 +70,7 @@ test_run_rebase () {
                test_linear_range "\'"$expected"\'" d..
        "
 }
-test_run_rebase success 'n o e' ''
+test_run_rebase success 'n o e' --apply
 test_run_rebase success 'n o e' -m
 test_run_rebase success 'n o e' -i
 
@@ -86,7 +86,7 @@ test_run_rebase () {
                test_linear_range "\'"$expected"\'" c..
        "
 }
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --apply
 test_run_rebase success 'd n o e' -m
 test_run_rebase success 'd n o e' -i
 
@@ -102,7 +102,7 @@ test_run_rebase () {
                test_linear_range "\'"$expected"\'" c..
        "
 }
-test_run_rebase success 'd n o e' ''
+test_run_rebase success 'd n o e' --apply
 test_run_rebase success 'd n o e' -m
 test_run_rebase success 'd n o e' -i
 
index bec48e6a1f99a5d5db91b392987ff5b64cf16781..79e43a370baca27bdf2945e8d2ec1402516dfcdd 100755 (executable)
@@ -85,23 +85,23 @@ test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --
        verbose test "$(commit_message HEAD)" = "Empty commit"
 '
 
-test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --onto commit' '
        reset_rebase &&
        git checkout -b rebase-onto to-rebase &&
-       test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
+       test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --onto files-master master &&
        : first pick results in no changes &&
-       git rebase --continue &&
+       git rebase --skip &&
        verbose test "$(commit_message HEAD~2)" = "master4" &&
        verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
        verbose test "$(commit_message HEAD)" = "Empty commit"
 '
 
-test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
+test_expect_success 'Rebase -Xsubtree --empty=ask --rebase-merges --onto commit' '
        reset_rebase &&
        git checkout -b rebase-merges-onto to-rebase &&
-       test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
+       test_must_fail git rebase -Xsubtree=files_subtree --empty=ask --rebase-merges --onto files-master --root &&
        : first pick results in no changes &&
-       git rebase --continue &&
+       git rebase --skip &&
        verbose test "$(commit_message HEAD~2)" = "master4" &&
        verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
        verbose test "$(commit_message HEAD)" = "Empty commit"
index e72ca348eadc1746459ea43b9346c9bb5e84b03d..a1bc3e20016b37492c4f351970a7d8f8da44d5e8 100755 (executable)
@@ -20,12 +20,11 @@ Initial setup:
 '
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-rebase.sh
+. "$TEST_DIRECTORY"/lib-log-graph.sh
 
 test_cmp_graph () {
        cat >expect &&
-       git log --graph --boundary --format=%s "$@" >output &&
-       sed "s/ *$//" <output >output.trimmed &&
-       test_cmp expect output.trimmed
+       lib_test_cmp_graph --boundary --format=%s "$@"
 }
 
 test_expect_success 'setup' '
index 92f95b57da1424348b5fd898daf0c7722d4f20d2..6c9d4a13758194dfcf4b005e36f8358ed5a27f80 100755 (executable)
@@ -28,8 +28,10 @@ test_rebase_same_head () {
        shift &&
        cmp_f="$1" &&
        shift &&
-       test_rebase_same_head_ $status_n $what_n $cmp_n "" "$*" &&
-       test_rebase_same_head_ $status_f $what_f $cmp_f " --no-ff" "$*"
+       test_rebase_same_head_ $status_n $what_n $cmp_n " --apply" "$*" &&
+       test_rebase_same_head_ $status_f $what_f $cmp_f " --apply --no-ff" "$*"
+       test_rebase_same_head_ $status_n $what_n $cmp_n " --merge" "$*" &&
+       test_rebase_same_head_ $status_f $what_f $cmp_f " --merge --no-ff" "$*"
 }
 
 test_rebase_same_head_ () {
@@ -44,19 +46,15 @@ test_rebase_same_head_ () {
        test_expect_$status "git rebase$flag $* with $changes is $what with $cmp HEAD" "
                oldhead=\$(git rev-parse HEAD) &&
                test_when_finished 'git reset --hard \$oldhead' &&
+               cp .git/logs/HEAD expect &&
                git rebase$flag $* >stdout &&
                if test $what = work
                then
-                       # Must check this case first, for 'is up to
-                       # date, rebase forced[...]rewinding head' cases
-                       test_i18ngrep 'rewinding head' stdout
+                       old=\$(wc -l <expect) &&
+                       test_line_count '-gt' \$old .git/logs/HEAD
                elif test $what = noop
                then
-                       test_i18ngrep 'is up to date' stdout &&
-                       test_i18ngrep ! 'rebase forced' stdout
-               elif test $what = noop-force
-               then
-                       test_i18ngrep 'is up to date, rebase forced' stdout
+                       test_cmp expect .git/logs/HEAD
                fi &&
                newhead=\$(git rev-parse HEAD) &&
                if test $cmp = same
@@ -71,14 +69,14 @@ test_rebase_same_head_ () {
 
 changes='no changes'
 test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
 test_rebase_same_head success noop same success work same --fork-point master
 test_rebase_same_head success noop same success work diff --fork-point --onto B B
 test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -91,14 +89,14 @@ test_expect_success 'add work same to side' '
 
 changes='our changes'
 test_rebase_same_head success noop same success work same
-test_rebase_same_head success noop same success noop-force same master
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
-test_rebase_same_head success noop same success noop-force same --onto master... master
-test_rebase_same_head success noop same success noop-force same --keep-base master
-test_rebase_same_head success noop same success noop-force same --keep-base
-test_rebase_same_head success noop same success noop-force same --no-fork-point
-test_rebase_same_head success noop same success noop-force same --keep-base --no-fork-point
+test_rebase_same_head success noop same success work same master
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
+test_rebase_same_head success noop same success work same --onto master... master
+test_rebase_same_head success noop same success work same --keep-base master
+test_rebase_same_head success noop same success work same --keep-base
+test_rebase_same_head success noop same success work same --no-fork-point
+test_rebase_same_head success noop same success work same --keep-base --no-fork-point
 test_rebase_same_head success noop same success work same --fork-point master
 test_rebase_same_head success noop same success work diff --fork-point --onto B B
 test_rebase_same_head success noop same success work diff --fork-point --onto B... B
@@ -112,8 +110,8 @@ test_expect_success 'add work same to upstream' '
 '
 
 changes='our and their changes'
-test_rebase_same_head success noop same success noop-force diff --onto B B
-test_rebase_same_head success noop same success noop-force diff --onto B... B
+test_rebase_same_head success noop same success work diff --onto B B
+test_rebase_same_head success noop same success work diff --onto B... B
 test_rebase_same_head success noop same success work diff --onto master... master
 test_rebase_same_head success noop same success work diff --keep-base master
 test_rebase_same_head success noop same success work diff --keep-base
diff --git a/t/t3433-rebase-across-mode-change.sh b/t/t3433-rebase-across-mode-change.sh
new file mode 100755 (executable)
index 0000000..05df964
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+test_description='git rebase across mode change'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir DS &&
+       >DS/whatever &&
+       git add DS &&
+       git commit -m base &&
+
+       git branch side1 &&
+       git branch side2 &&
+
+       git checkout side1 &&
+       git rm -rf DS &&
+       test_ln_s_add unrelated DS &&
+       git commit -m side1 &&
+
+       git checkout side2 &&
+       >unrelated &&
+       git add unrelated &&
+       git commit -m commit1 &&
+
+       echo >>unrelated &&
+       git commit -am commit2
+'
+
+test_expect_success 'rebase changes with the apply backend' '
+       test_when_finished "git rebase --abort || true" &&
+       git checkout -b apply-backend side2 &&
+       git rebase side1
+'
+
+test_expect_success 'rebase changes with the merge backend' '
+       test_when_finished "git rebase --abort || true" &&
+       git checkout -b merge-backend side2 &&
+       git rebase -m side1
+'
+
+test_expect_success 'rebase changes with the merge backend with a delay' '
+       test_when_finished "git rebase --abort || true" &&
+       git checkout -b merge-delay-backend side2 &&
+       git rebase -m --exec "sleep 1" side1
+'
+
+test_done
index 671e951ee5498c7691c5e996c9dfd75dac3e8861..c0b642c1ab03f27dabc58e3b99ba9f8f48bf35d8 100755 (executable)
@@ -30,6 +30,14 @@ test_expect_success 'attribute before color name' '
        color "bold red" "[1;31m"
 '
 
+test_expect_success 'aixterm bright fg color' '
+       color "brightred" "[91m"
+'
+
+test_expect_success 'aixterm bright bg color' '
+       color "green brightblue" "[32;104m"
+'
+
 test_expect_success 'color name before attribute' '
        color "red bold" "[1;31m"
 '
@@ -74,6 +82,10 @@ test_expect_success '0-7 are aliases for basic ANSI color names' '
        color "0 7" "[30;47m"
 '
 
+test_expect_success '8-15 are aliases for aixterm color names' '
+       color "12 13" "[94;105m"
+'
+
 test_expect_success '256 colors' '
        color "254 bold 255" "[1;38;5;254;48;5;255m"
 '
index f7de6f077a6e055b79dfc2ae572f1552c40d2eb3..0ee93fe845afec2aebb50819d3301c3c1972b34c 100755 (executable)
@@ -74,7 +74,7 @@ test_expect_success 'apply with --reject should fail but update the file' '
        test_must_fail git apply --reject patch.1 &&
        test_cmp expected file1 &&
 
-       cat file1.rej &&
+       test_path_is_file file1.rej &&
        test_path_is_missing file2.rej
 '
 
@@ -87,7 +87,7 @@ test_expect_success 'apply with --reject should fail but update the file' '
        test_path_is_missing file1 &&
        test_cmp expected file2 &&
 
-       cat file2.rej &&
+       test_path_is_file file2.rej &&
        test_path_is_missing file1.rej
 
 '
@@ -101,7 +101,7 @@ test_expect_success 'the same test with --verbose' '
        test_path_is_missing file1 &&
        test_cmp expected file2 &&
 
-       cat file2.rej &&
+       test_path_is_file file2.rej &&
        test_path_is_missing file1.rej
 
 '
index 192347a3e1fa50af7e33ef1172c2fbf61120cfa0..4694b6d0ce7170e141c17082285772730e499b3e 100755 (executable)
@@ -5,6 +5,11 @@ test_description='git log'
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
 . "$TEST_DIRECTORY/lib-terminal.sh"
+. "$TEST_DIRECTORY/lib-log-graph.sh"
+
+test_cmp_graph () {
+       lib_test_cmp_graph --format=%s "$@"
+}
 
 test_expect_success setup '
 
@@ -452,8 +457,7 @@ cat > expect <<EOF
 EOF
 
 test_expect_success 'simple log --graph' '
-       git log --graph --pretty=tformat:%s >actual &&
-       test_cmp expect actual
+       test_cmp_graph
 '
 
 cat > expect <<EOF
@@ -467,8 +471,7 @@ cat > expect <<EOF
 EOF
 
 test_expect_success 'simple log --graph --line-prefix="123 "' '
-       git log --graph --line-prefix="123 " --pretty=tformat:%s >actual &&
-       test_cmp expect actual
+       test_cmp_graph --line-prefix="123 "
 '
 
 test_expect_success 'set up merge history' '
@@ -495,9 +498,7 @@ cat > expect <<\EOF
 EOF
 
 test_expect_success 'log --graph with merge' '
-       git log --graph --date-order --pretty=tformat:%s |
-               sed "s/ *\$//" >actual &&
-       test_cmp expect actual
+       test_cmp_graph --date-order
 '
 
 cat > expect <<\EOF
@@ -516,9 +517,7 @@ cat > expect <<\EOF
 EOF
 
 test_expect_success 'log --graph --line-prefix="| | | " with merge' '
-       git log --line-prefix="| | | " --graph --date-order --pretty=tformat:%s |
-               sed "s/ *\$//" >actual &&
-       test_cmp expect actual
+       test_cmp_graph --line-prefix="| | | " --date-order
 '
 
 cat > expect.colors <<\EOF
@@ -538,9 +537,7 @@ EOF
 
 test_expect_success 'log --graph with merge with log.graphColors' '
        test_config log.graphColors " blue,invalid-color, cyan, red  , " &&
-       git log --color=always --graph --date-order --pretty=tformat:%s |
-               test_decode_color | sed "s/ *\$//" >actual &&
-       test_cmp expect.colors actual
+       lib_test_cmp_colored_graph --date-order --format=%s
 '
 
 test_expect_success 'log --raw --graph -m with merge' '
@@ -676,9 +673,7 @@ cat > expect <<\EOF
 EOF
 
 test_expect_success 'log --graph with merge' '
-       git log --graph --date-order --pretty=tformat:%s |
-               sed "s/ *\$//" >actual &&
-       test_cmp expect actual
+       test_cmp_graph --date-order
 '
 
 test_expect_success 'log.decorate configuration' '
@@ -1213,24 +1208,8 @@ cat >expect <<\EOF
   +one
 EOF
 
-sanitize_output () {
-       sed -e 's/ *$//' \
-           -e 's/commit [0-9a-f]*$/commit COMMIT_OBJECT_NAME/' \
-           -e 's/Merge: [ 0-9a-f]*$/Merge: MERGE_PARENTS/' \
-           -e 's/Merge tag.*/Merge HEADS DESCRIPTION/' \
-           -e 's/Merge commit.*/Merge HEADS DESCRIPTION/' \
-           -e 's/, 0 deletions(-)//' \
-           -e 's/, 0 insertions(+)//' \
-           -e 's/ 1 files changed, / 1 file changed, /' \
-           -e 's/, 1 deletions(-)/, 1 deletion(-)/' \
-           -e 's/, 1 insertions(+)/, 1 insertion(+)/' \
-           -e 's/index [0-9a-f]*\.\.[0-9a-f]*/index BEFORE..AFTER/'
-}
-
 test_expect_success 'log --graph with diff and stats' '
-       git log --no-renames --graph --pretty=short --stat -p >actual &&
-       sanitize_output >actual.sanitized <actual &&
-       test_i18ncmp expect actual.sanitized
+       lib_test_cmp_short_graph --no-renames --stat -p
 '
 
 cat >expect <<\EOF
@@ -1505,9 +1484,7 @@ cat >expect <<\EOF
 EOF
 
 test_expect_success 'log --line-prefix="*** " --graph with diff and stats' '
-       git log --line-prefix="*** " --no-renames --graph --pretty=short --stat -p >actual &&
-       sanitize_output >actual.sanitized <actual &&
-       test_i18ncmp expect actual.sanitized
+       lib_test_cmp_short_graph --line-prefix="*** " --no-renames --stat -p
 '
 
 cat >expect <<-\EOF
@@ -1529,9 +1506,7 @@ cat >expect <<-\EOF
 EOF
 
 test_expect_success 'log --graph with --name-status' '
-       git log --graph --format=%s --name-status tangle..reach >actual &&
-       sanitize_output <actual >actual.sanitized &&
-       test_cmp expect actual.sanitized
+       test_cmp_graph --name-status tangle..reach
 '
 
 cat >expect <<-\EOF
@@ -1553,9 +1528,7 @@ cat >expect <<-\EOF
 EOF
 
 test_expect_success 'log --graph with --name-only' '
-       git log --graph --format=%s --name-only tangle..reach >actual &&
-       sanitize_output <actual >actual.sanitized &&
-       test_cmp expect actual.sanitized
+       test_cmp_graph --name-only tangle..reach
 '
 
 test_expect_success 'dotdot is a parent directory' '
index 40d27db674cf1053e8554591d7b697388d13e028..a0803250984040733a327a9e347651c8686e5e0f 100755 (executable)
@@ -3,6 +3,16 @@
 test_description='git log --graph of skewed left octopus merge.'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-log-graph.sh
+
+test_cmp_graph () {
+       cat >expect &&
+       lib_test_cmp_graph --color=never --date-order --format=%s "$@"
+}
+
+test_cmp_colored_graph () {
+       lib_test_cmp_colored_graph --date-order --format=%s "$@"
+}
 
 test_expect_success 'set up merge history' '
        test_commit initial &&
@@ -24,7 +34,7 @@ test_expect_success 'set up merge history' '
 '
 
 test_expect_success 'log --graph with tricky octopus merge, no color' '
-       cat >expect.uncolored <<-\EOF &&
+       test_cmp_graph left octopus-merge <<-\EOF
        * left
        | *-.   octopus-merge
        |/|\ \
@@ -37,9 +47,6 @@ test_expect_success 'log --graph with tricky octopus merge, no color' '
        |/
        * initial
        EOF
-       git log --color=never --graph --date-order --pretty=tformat:%s left octopus-merge >actual.raw &&
-       sed "s/ *\$//" actual.raw >actual &&
-       test_cmp expect.uncolored actual
 '
 
 test_expect_success 'log --graph with tricky octopus merge with colors' '
@@ -57,16 +64,14 @@ test_expect_success 'log --graph with tricky octopus merge with colors' '
        <MAGENTA>|<RESET><MAGENTA>/<RESET>
        * initial
        EOF
-       git log --color=always --graph --date-order --pretty=tformat:%s left octopus-merge >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       test_cmp_colored_graph left octopus-merge
 '
 
 # Repeat the previous two tests with "normal" octopus merge (i.e.,
 # without the first parent skewing to the "left" branch column).
 
 test_expect_success 'log --graph with normal octopus merge, no color' '
-       cat >expect.uncolored <<-\EOF &&
+       test_cmp_graph octopus-merge <<-\EOF
        *---.   octopus-merge
        |\ \ \
        | | | * 4
@@ -78,9 +83,6 @@ test_expect_success 'log --graph with normal octopus merge, no color' '
        |/
        * initial
        EOF
-       git log --color=never --graph --date-order --pretty=tformat:%s octopus-merge >actual.raw &&
-       sed "s/ *\$//" actual.raw >actual &&
-       test_cmp expect.uncolored actual
 '
 
 test_expect_success 'log --graph with normal octopus merge with colors' '
@@ -97,13 +99,11 @@ test_expect_success 'log --graph with normal octopus merge with colors' '
        * initial
        EOF
        test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
-       git log --color=always --graph --date-order --pretty=tformat:%s octopus-merge >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       test_cmp_colored_graph octopus-merge
 '
 
 test_expect_success 'log --graph with normal octopus merge and child, no color' '
-       cat >expect.uncolored <<-\EOF &&
+       test_cmp_graph after-merge <<-\EOF
        * after-merge
        *---.   octopus-merge
        |\ \ \
@@ -116,9 +116,6 @@ test_expect_success 'log --graph with normal octopus merge and child, no color'
        |/
        * initial
        EOF
-       git log --color=never --graph --date-order --pretty=tformat:%s after-merge >actual.raw &&
-       sed "s/ *\$//" actual.raw >actual &&
-       test_cmp expect.uncolored actual
 '
 
 test_expect_success 'log --graph with normal octopus and child merge with colors' '
@@ -136,13 +133,11 @@ test_expect_success 'log --graph with normal octopus and child merge with colors
        * initial
        EOF
        test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
-       git log --color=always --graph --date-order --pretty=tformat:%s after-merge >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       test_cmp_colored_graph after-merge
 '
 
 test_expect_success 'log --graph with tricky octopus merge and its child, no color' '
-       cat >expect.uncolored <<-\EOF &&
+       test_cmp_graph left after-merge <<-\EOF
        * left
        | * after-merge
        | *-.   octopus-merge
@@ -156,9 +151,6 @@ test_expect_success 'log --graph with tricky octopus merge and its child, no col
        |/
        * initial
        EOF
-       git log --color=never --graph --date-order --pretty=tformat:%s left after-merge >actual.raw &&
-       sed "s/ *\$//" actual.raw >actual &&
-       test_cmp expect.uncolored actual
 '
 
 test_expect_success 'log --graph with tricky octopus merge and its child with colors' '
@@ -177,13 +169,11 @@ test_expect_success 'log --graph with tricky octopus merge and its child with co
        <CYAN>|<RESET><CYAN>/<RESET>
        * initial
        EOF
-       git log --color=always --graph --date-order --pretty=tformat:%s left after-merge >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       test_cmp_colored_graph left after-merge
 '
 
 test_expect_success 'log --graph with crossover in octopus merge, no color' '
-       cat >expect.uncolored <<-\EOF &&
+       test_cmp_graph after-4 octopus-merge <<-\EOF
        * after-4
        | *---.   octopus-merge
        | |\ \ \
@@ -200,9 +190,6 @@ test_expect_success 'log --graph with crossover in octopus merge, no color' '
        |/
        * initial
        EOF
-       git log --color=never --graph --date-order --pretty=tformat:%s after-4 octopus-merge >actual.raw &&
-       sed "s/ *\$//" actual.raw >actual &&
-       test_cmp expect.uncolored actual
 '
 
 test_expect_success 'log --graph with crossover in octopus merge with colors' '
@@ -224,13 +211,11 @@ test_expect_success 'log --graph with crossover in octopus merge with colors' '
        <MAGENTA>|<RESET><MAGENTA>/<RESET>
        * initial
        EOF
-       git log --color=always --graph --date-order --pretty=tformat:%s after-4 octopus-merge >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       test_cmp_colored_graph after-4 octopus-merge
 '
 
 test_expect_success 'log --graph with crossover in octopus merge and its child, no color' '
-       cat >expect.uncolored <<-\EOF &&
+       test_cmp_graph after-4 after-merge <<-\EOF
        * after-4
        | * after-merge
        | *---.   octopus-merge
@@ -248,9 +233,6 @@ test_expect_success 'log --graph with crossover in octopus merge and its child,
        |/
        * initial
        EOF
-       git log --color=never --graph --date-order --pretty=tformat:%s after-4 after-merge >actual.raw &&
-       sed "s/ *\$//" actual.raw >actual &&
-       test_cmp expect.uncolored actual
 '
 
 test_expect_success 'log --graph with crossover in octopus merge and its child with colors' '
@@ -273,13 +255,11 @@ test_expect_success 'log --graph with crossover in octopus merge and its child w
        <CYAN>|<RESET><CYAN>/<RESET>
        * initial
        EOF
-       git log --color=always --graph --date-order --pretty=tformat:%s after-4 after-merge >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       test_cmp_colored_graph after-4 after-merge
 '
 
 test_expect_success 'log --graph with unrelated commit and octopus tip, no color' '
-       cat >expect.uncolored <<-\EOF &&
+       test_cmp_graph after-initial octopus-merge <<-\EOF
        * after-initial
        | *---.   octopus-merge
        | |\ \ \
@@ -296,9 +276,6 @@ test_expect_success 'log --graph with unrelated commit and octopus tip, no color
        |/
        * initial
        EOF
-       git log --color=never --graph --date-order --pretty=tformat:%s after-initial octopus-merge >actual.raw &&
-       sed "s/ *\$//" actual.raw >actual &&
-       test_cmp expect.uncolored actual
 '
 
 test_expect_success 'log --graph with unrelated commit and octopus tip with colors' '
@@ -320,13 +297,11 @@ test_expect_success 'log --graph with unrelated commit and octopus tip with colo
        <RED>|<RESET><RED>/<RESET>
        * initial
        EOF
-       git log --color=always --graph --date-order --pretty=tformat:%s after-initial octopus-merge >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       test_cmp_colored_graph after-initial octopus-merge
 '
 
 test_expect_success 'log --graph with unrelated commit and octopus child, no color' '
-       cat >expect.uncolored <<-\EOF &&
+       test_cmp_graph after-initial after-merge <<-\EOF
        * after-initial
        | * after-merge
        | *---.   octopus-merge
@@ -344,9 +319,6 @@ test_expect_success 'log --graph with unrelated commit and octopus child, no col
        |/
        * initial
        EOF
-       git log --color=never --graph --date-order --pretty=tformat:%s after-initial after-merge >actual.raw &&
-       sed "s/ *\$//" actual.raw >actual &&
-       test_cmp expect.uncolored actual
 '
 
 test_expect_success 'log --graph with unrelated commit and octopus child with colors' '
@@ -369,9 +341,7 @@ test_expect_success 'log --graph with unrelated commit and octopus child with co
        <RED>|<RESET><RED>/<RESET>
        * initial
        EOF
-       git log --color=always --graph --date-order --pretty=tformat:%s after-initial after-merge >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       test_cmp_colored_graph after-initial after-merge
 '
 
 test_done
index 1d0d3240ff69c9e8b5261939a8fa903de65cabd5..28d0779a8c599ee9eab9b0b31afe1a57bb558c28 100755 (executable)
@@ -3,12 +3,11 @@
 test_description='git log --graph of skewed merges'
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-log-graph.sh
 
 check_graph () {
        cat >expect &&
-       git log --graph --pretty=tformat:%s "$@" >actual.raw &&
-       sed "s/ *$//" actual.raw >actual &&
-       test_cmp expect actual
+       lib_test_cmp_graph --format=%s "$@"
 }
 
 test_expect_success 'log --graph with merge fusing with its left and right neighbors' '
@@ -306,9 +305,7 @@ test_expect_success 'log --graph with multiple tips and colors' '
        <BLUE>|<RESET><BLUE>/<RESET>
        * 6_A
        EOF
-       git log --color=always --graph --date-order --pretty=tformat:%s 6_1 6_3 6_5 >actual.colors.raw &&
-       test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
-       test_cmp expect.colors actual.colors
+       lib_test_cmp_colored_graph --date-order --pretty=tformat:%s 6_1 6_3 6_5
 '
 
 test_expect_success 'log --graph with multiple tips' '
index 1ad4ecc29a72b6a3e4a7d960865647447a0618d6..c1811ea0f4129447070a7650a0dd95a5b4ef9f46 100755 (executable)
@@ -150,7 +150,6 @@ test_expect_success 'pull request after push' '
                git request-pull initial origin master:for-upstream >../request
        ) &&
        sed -nf read-request.sed <request >digest &&
-       cat digest &&
        {
                read task &&
                read repository &&
@@ -179,7 +178,6 @@ test_expect_success 'request asks HEAD to be pulled' '
                git request-pull initial "$downstream_url" >../request
        ) &&
        sed -nf read-request.sed <request >digest &&
-       cat digest &&
        {
                read task &&
                read repository &&
index 6640329ebf609c5054a6f97919005855790571a6..8318781d2bbf6c345cda1fab26848771918f8a5a 100755 (executable)
@@ -74,16 +74,24 @@ rev_list_tests() {
                test_cmp expect actual
        '
 
-       test_expect_success "enumerate --objects ($state)" '
-               git rev-list --objects --use-bitmap-index HEAD >tmp &&
-               cut -d" " -f1 <tmp >tmp2 &&
-               sort <tmp2 >actual &&
-               git rev-list --objects HEAD >tmp &&
-               cut -d" " -f1 <tmp >tmp2 &&
-               sort <tmp2 >expect &&
+       test_expect_success "counting objects via bitmap ($state)" '
+               git rev-list --count --objects HEAD >expect &&
+               git rev-list --use-bitmap-index --count --objects HEAD >actual &&
                test_cmp expect actual
        '
 
+       test_expect_success "enumerate commits ($state)" '
+               git rev-list --use-bitmap-index HEAD >actual &&
+               git rev-list HEAD >expect &&
+               test_bitmap_traversal --no-confirm-bitmaps expect actual
+       '
+
+       test_expect_success "enumerate --objects ($state)" '
+               git rev-list --objects --use-bitmap-index HEAD >actual &&
+               git rev-list --objects HEAD >expect &&
+               test_bitmap_traversal expect actual
+       '
+
        test_expect_success "bitmap --objects handles non-commit objects ($state)" '
                git rev-list --objects --use-bitmap-index HEAD tagged-blob >actual &&
                grep $blob actual
@@ -99,6 +107,20 @@ test_expect_success 'clone from bitmapped repository' '
        test_cmp expect actual
 '
 
+test_expect_success 'partial clone from bitmapped repository' '
+       test_config uploadpack.allowfilter true &&
+       git clone --no-local --bare --filter=blob:none . partial-clone.git &&
+       (
+               cd partial-clone.git &&
+               pack=$(echo objects/pack/*.pack) &&
+               git verify-pack -v "$pack" >have &&
+               awk "/blob/ { print \$1 }" <have >blobs &&
+               # we expect this single blob because of the direct ref
+               git rev-parse refs/tags/tagged-blob >expect &&
+               test_cmp expect blobs
+       )
+'
+
 test_expect_success 'setup further non-bitmapped commits' '
        test_commit_bulk --id=further 10
 '
index 7344253bfbbcb3f36533db2657f974de97951fde..80750a817e950848f11f334615c2a03b42afe286 100755 (executable)
@@ -53,10 +53,10 @@ test_expect_success 'git commit --amend --no-post-rewrite' '
        test ! -f post-rewrite.data
 '
 
-test_expect_success 'git rebase' '
+test_expect_success 'git rebase --apply' '
        git reset --hard D &&
        clear_hook_input &&
-       test_must_fail git rebase --onto A B &&
+       test_must_fail git rebase --apply --onto A B &&
        echo C > foo &&
        git add foo &&
        git rebase --continue &&
@@ -68,10 +68,10 @@ test_expect_success 'git rebase' '
        verify_hook_input
 '
 
-test_expect_success 'git rebase --skip' '
+test_expect_success 'git rebase --apply --skip' '
        git reset --hard D &&
        clear_hook_input &&
-       test_must_fail git rebase --onto A B &&
+       test_must_fail git rebase --apply --onto A B &&
        test_must_fail git rebase --skip &&
        echo D > foo &&
        git add foo &&
@@ -84,10 +84,10 @@ test_expect_success 'git rebase --skip' '
        verify_hook_input
 '
 
-test_expect_success 'git rebase --skip the last one' '
+test_expect_success 'git rebase --apply --skip the last one' '
        git reset --hard F &&
        clear_hook_input &&
-       test_must_fail git rebase --onto D A &&
+       test_must_fail git rebase --apply --onto D A &&
        git rebase --skip &&
        echo rebase >expected.args &&
        cat >expected.data <<-EOF &&
@@ -128,7 +128,7 @@ test_expect_success 'git rebase -m --skip' '
        verify_hook_input
 '
 
-test_expect_success 'git rebase with implicit use of interactive backend' '
+test_expect_success 'git rebase with implicit use of merge backend' '
        git reset --hard D &&
        clear_hook_input &&
        test_must_fail git rebase --keep-empty --onto A B &&
@@ -143,7 +143,7 @@ test_expect_success 'git rebase with implicit use of interactive backend' '
        verify_hook_input
 '
 
-test_expect_success 'git rebase --skip with implicit use of interactive backend' '
+test_expect_success 'git rebase --skip with implicit use of merge backend' '
        git reset --hard D &&
        clear_hook_input &&
        test_must_fail git rebase --keep-empty --onto A B &&
index 2a8c4496618523ebdbd03eb152a1e8189fafcd0a..5d8f401d8ece8cc76e8c46591ed4883337e57a5f 100755 (executable)
@@ -56,14 +56,13 @@ test_expect_success 'short line' '
 
 test_expect_success 'case-insensitive' '
        git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/case-insensitive 2>output &&
-       cat output &&
        test_decode_color <output >decoded &&
        grep "<BOLD;RED>error<RESET>: error" decoded &&
        grep "<BOLD;RED>ERROR<RESET>: also highlighted" decoded
 '
 
 test_expect_success 'leading space' '
-       git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/leading-space 2>output &&        cat output &&
+       git --git-dir child/.git -c color.remote=always push -f origin HEAD:refs/heads/leading-space 2>output &&
        test_decode_color <output >decoded &&
        grep "  <BOLD;RED>error<RESET>: leading space" decoded
 '
index 883b32efa024d97ac74e9ae5763dc8a218251675..dda81b7d07a2ee7c5ee24f28f5f8a527a3f187da 100755 (executable)
@@ -734,15 +734,53 @@ test_expect_success 'reject adding remote with an invalid name' '
 # the last two ones check if the config is updated.
 
 test_expect_success 'rename a remote' '
+       test_config_global remote.pushDefault origin &&
        git clone one four &&
        (
                cd four &&
+               git config branch.master.pushRemote origin &&
                git remote rename origin upstream &&
                test -z "$(git for-each-ref refs/remotes/origin)" &&
                test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
                test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
                test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
-               test "$(git config branch.master.remote)" = "upstream"
+               test "$(git config branch.master.remote)" = "upstream" &&
+               test "$(git config branch.master.pushRemote)" = "upstream" &&
+               test "$(git config --global remote.pushDefault)" = "origin"
+       )
+'
+
+test_expect_success 'rename a remote renames repo remote.pushDefault' '
+       git clone one four.1 &&
+       (
+               cd four.1 &&
+               git config remote.pushDefault origin &&
+               git remote rename origin upstream &&
+               test "$(git config --local remote.pushDefault)" = "upstream"
+       )
+'
+
+test_expect_success 'rename a remote renames repo remote.pushDefault but ignores global' '
+       test_config_global remote.pushDefault other &&
+       git clone one four.2 &&
+       (
+               cd four.2 &&
+               git config remote.pushDefault origin &&
+               git remote rename origin upstream &&
+               test "$(git config --global remote.pushDefault)" = "other" &&
+               test "$(git config --local remote.pushDefault)" = "upstream"
+       )
+'
+
+test_expect_success 'rename a remote renames repo remote.pushDefault but keeps global' '
+       test_config_global remote.pushDefault origin &&
+       git clone one four.3 &&
+       (
+               cd four.3 &&
+               git config remote.pushDefault origin &&
+               git remote rename origin upstream &&
+               test "$(git config --global remote.pushDefault)" = "origin" &&
+               test "$(git config --local remote.pushDefault)" = "upstream"
        )
 '
 
@@ -784,6 +822,54 @@ test_expect_success 'rename succeeds with existing remote.<target>.prune' '
        git -C four.four remote rename origin upstream
 '
 
+test_expect_success 'remove a remote' '
+       test_config_global remote.pushDefault origin &&
+       git clone one four.five &&
+       (
+               cd four.five &&
+               git config branch.master.pushRemote origin &&
+               git remote remove origin &&
+               test -z "$(git for-each-ref refs/remotes/origin)" &&
+               test_must_fail git config branch.master.remote &&
+               test_must_fail git config branch.master.pushRemote &&
+               test "$(git config --global remote.pushDefault)" = "origin"
+       )
+'
+
+test_expect_success 'remove a remote removes repo remote.pushDefault' '
+       git clone one four.five.1 &&
+       (
+               cd four.five.1 &&
+               git config remote.pushDefault origin &&
+               git remote remove origin &&
+               test_must_fail git config --local remote.pushDefault
+       )
+'
+
+test_expect_success 'remove a remote removes repo remote.pushDefault but ignores global' '
+       test_config_global remote.pushDefault other &&
+       git clone one four.five.2 &&
+       (
+               cd four.five.2 &&
+               git config remote.pushDefault origin &&
+               git remote remove origin &&
+               test "$(git config --global remote.pushDefault)" = "other" &&
+               test_must_fail git config --local remote.pushDefault
+       )
+'
+
+test_expect_success 'remove a remote removes repo remote.pushDefault but keeps global' '
+       test_config_global remote.pushDefault origin &&
+       git clone one four.five.3 &&
+       (
+               cd four.five.3 &&
+               git config remote.pushDefault origin &&
+               git remote remove origin &&
+               test "$(git config --global remote.pushDefault)" = "origin" &&
+               test_must_fail git config --local remote.pushDefault
+       )
+'
+
 cat >remotes_origin <<EOF
 URL: $(pwd)/one
 Push: refs/heads/master:refs/heads/upstream
index 75cbfcc392c8cd71e20d901a7a28bf7f6f73451a..a67f792adf4a2fb846ade40eb26acfcb08ba5d1b 100755 (executable)
@@ -20,7 +20,7 @@ test_expect_success setup '
        ) &&
        commit0=$(cd original && git rev-parse HEAD^) &&
        commit1=$(cd original && git rev-parse HEAD) &&
-       git init pushee &&
+       git init --bare pushee &&
        git init puller
 '
 
@@ -152,4 +152,15 @@ test_expect_success 'clone chooses correct HEAD (v2)' '
        test_cmp expect actual
 '
 
+test_expect_success 'denyCurrentBranch and unborn branch with ref namespace' '
+       (
+               cd original &&
+               git init unborn &&
+               git remote add unborn-namespaced "ext::git --namespace=namespace %s unborn" &&
+               test_must_fail git push unborn-namespaced HEAD:master &&
+               git -C unborn config receive.denyCurrentBranch updateInstead &&
+               git push unborn-namespaced HEAD:master
+       )
+'
+
 test_done
index 566994cede3ef22c0c3927779a53d3923e3d6694..a66dbe0bde0df2851c3736d193707ec7c36bd429 100755 (executable)
@@ -379,7 +379,6 @@ test_expect_success 'fetch from GIT URL with a non-applying branch.<name>.merge
 # the strange name is: a\!'b
 test_expect_success 'quoting of a strangely named repo' '
        test_must_fail git fetch "a\\!'\''b" > result 2>&1 &&
-       cat result &&
        grep "fatal: '\''a\\\\!'\''b'\''" result
 '
 
index f12cbef09728d9c49b0ad66eab77c57043df0596..9ff041a093e71aac932f5e563d4922930bc62ff2 100755 (executable)
@@ -1712,4 +1712,15 @@ test_expect_success 'updateInstead with push-to-checkout hook' '
        )
 '
 
+test_expect_success 'denyCurrentBranch and worktrees' '
+       git worktree add new-wt &&
+       git clone . cloned &&
+       test_commit -C cloned first &&
+       test_config receive.denyCurrentBranch refuse &&
+       test_must_fail git -C cloned push origin HEAD:new-wt &&
+       test_config receive.denyCurrentBranch updateInstead &&
+       git -C cloned push origin HEAD:new-wt &&
+       test_must_fail git -C cloned push --delete origin new-wt
+'
+
 test_done
index 602d996a33b74a4d877a5f31acdf452fa46e1734..2f86fca0428cde001e2e6b3735e215714037059d 100755 (executable)
@@ -277,14 +277,27 @@ test_expect_success '--rebase' '
        test_cmp expect actual
 '
 
-test_expect_success '--rebase fast forward' '
+test_expect_success '--rebase (merge) fast forward' '
        git reset --hard before-rebase &&
        git checkout -b ff &&
        echo another modification >file &&
        git commit -m third file &&
 
        git checkout to-rebase &&
-       git pull --rebase . ff &&
+       git -c rebase.backend=merge pull --rebase . ff &&
+       test_cmp_rev HEAD ff &&
+
+       # The above only validates the result.  Did we actually bypass rebase?
+       git reflog -1 >reflog.actual &&
+       sed "s/^[0-9a-f][0-9a-f]*/OBJID/" reflog.actual >reflog.fuzzy &&
+       echo "OBJID HEAD@{0}: pull --rebase . ff: Fast-forward" >reflog.expected &&
+       test_cmp reflog.expected reflog.fuzzy
+'
+
+test_expect_success '--rebase (am) fast forward' '
+       git reset --hard before-rebase &&
+
+       git -c rebase.backend=apply pull --rebase . ff &&
        test_cmp_rev HEAD ff &&
 
        # The above only validates the result.  Did we actually bypass rebase?
@@ -327,7 +340,7 @@ test_expect_success '--rebase with conflicts shows advice' '
        test_tick &&
        git commit -m "Create conflict" seq.txt &&
        test_must_fail git pull --rebase . seq 2>err >out &&
-       test_i18ngrep "Resolve all conflicts manually" out
+       test_i18ngrep "Resolve all conflicts manually" err
 '
 
 test_expect_success 'failed --rebase shows advice' '
@@ -341,7 +354,7 @@ test_expect_success 'failed --rebase shows advice' '
        git checkout -f -b fails-to-rebase HEAD^ &&
        test_commit v2-without-cr file "2" file2-lf &&
        test_must_fail git pull --rebase . diverging 2>err >out &&
-       test_i18ngrep "Resolve all conflicts manually" out
+       test_i18ngrep "Resolve all conflicts manually" err
 '
 
 test_expect_success '--rebase fails with multiple branches' '
@@ -761,8 +774,10 @@ test_expect_success 'git pull --rebase does not reapply old patches' '
        (
                cd dst &&
                test_must_fail git pull --rebase &&
-               find .git/rebase-apply -name "000*" >patches &&
-               test_line_count = 1 patches
+               cat .git/rebase-merge/done .git/rebase-merge/git-rebase-todo >work &&
+               grep -v -e \# -e ^$ work >patches &&
+               test_line_count = 1 patches &&
+               rm -f work
        )
 '
 
index 9a9178fd2810025322d1ad3048a09a661eaac110..3988a9cea2e74d9f648ef772da9040222248d7ad 100755 (executable)
@@ -384,6 +384,37 @@ test_expect_success 'fetch lazy-fetches only to resolve deltas, protocol v2' '
        grep "want $(cat hash)" trace
 '
 
+# The following two tests must be in this order, or else
+# the first will not fail. It is important that the srv.bare
+# repository did not have tags during clone, but has tags
+# in the fetch.
+
+test_expect_failure 'verify fetch succeeds when asking for new tags' '
+       git clone --filter=blob:none "file://$(pwd)/srv.bare" tag-test &&
+       for i in I J K
+       do
+               test_commit -C src $i &&
+               git -C src branch $i || return 1
+       done &&
+       git -C srv.bare fetch --tags origin +refs/heads/*:refs/heads/* &&
+       git -C tag-test -c protocol.version=2 fetch --tags origin
+'
+
+test_expect_success 'verify fetch downloads only one pack when updating refs' '
+       git clone --filter=blob:none "file://$(pwd)/srv.bare" pack-test &&
+       ls pack-test/.git/objects/pack/*pack >pack-list &&
+       test_line_count = 2 pack-list &&
+       for i in A B C
+       do
+               test_commit -C src $i &&
+               git -C src branch $i || return 1
+       done &&
+       git -C srv.bare fetch origin +refs/heads/*:refs/heads/* &&
+       git -C pack-test fetch origin &&
+       ls pack-test/.git/objects/pack/*pack >pack-list &&
+       test_line_count = 3 pack-list
+'
+
 . "$TEST_DIRECTORY"/lib-httpd.sh
 start_httpd
 
index a0baf9ee43fa12b8b6bc27633569a0c5fb8485fd..3dc1ad8f719926140ce31a9528c8f870977484b1 100755 (executable)
@@ -151,4 +151,16 @@ test_expect_success 'rev-list --end-of-options' '
        test_cmp expect actual
 '
 
+test_expect_success 'rev-list --count' '
+       count=$(git rev-list --count HEAD) &&
+       git rev-list HEAD >actual &&
+       test_line_count = $count actual
+'
+
+test_expect_success 'rev-list --count --objects' '
+       count=$(git rev-list --count --objects HEAD) &&
+       git rev-list --objects HEAD >actual &&
+       test_line_count = $count actual
+'
+
 test_done
index 860542aad00b21304d38b4b294f7efd48356bfd0..f4655bb358ff7fa5c10427d49423fa5eb6849ab8 100755 (executable)
@@ -186,7 +186,7 @@ test_expect_success 'check multiple merge bases' '
        )
 '
 
-test_expect_success 'rebase describes fake ancestor base' '
+test_expect_success 'rebase --merge describes parent of commit being picked' '
        test_create_repo rebase &&
        (
                cd rebase &&
@@ -194,7 +194,16 @@ test_expect_success 'rebase describes fake ancestor base' '
                test_commit master file &&
                git checkout -b side HEAD^ &&
                test_commit side file &&
-               test_must_fail git -c merge.conflictstyle=diff3 rebase master &&
+               test_must_fail git -c merge.conflictstyle=diff3 rebase --merge master &&
+               grep "||||||| parent of" file
+       )
+'
+
+test_expect_success 'rebase --apply describes fake ancestor base' '
+       (
+               cd rebase &&
+               git rebase --abort &&
+               test_must_fail git -c merge.conflictstyle=diff3 rebase --apply master &&
                grep "||||||| constructed merge base" file
        )
 '
diff --git a/t/t6113-rev-list-bitmap-filters.sh b/t/t6113-rev-list-bitmap-filters.sh
new file mode 100755 (executable)
index 0000000..145603f
--- /dev/null
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description='rev-list combining bitmaps and filters'
+. ./test-lib.sh
+
+test_expect_success 'set up bitmapped repo' '
+       # one commit will have bitmaps, the other will not
+       test_commit one &&
+       test_commit much-larger-blob-one &&
+       git repack -adb &&
+       test_commit two &&
+       test_commit much-larger-blob-two
+'
+
+test_expect_success 'filters fallback to non-bitmap traversal' '
+       # use a path-based filter, since they are inherently incompatible with
+       # bitmaps (i.e., this test will never get confused by later code to
+       # combine the features)
+       filter=$(echo "!one" | git hash-object -w --stdin) &&
+       git rev-list --objects --filter=sparse:oid=$filter HEAD >expect &&
+       git rev-list --use-bitmap-index \
+                    --objects --filter=sparse:oid=$filter HEAD >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'blob:none filter' '
+       git rev-list --objects --filter=blob:none HEAD >expect &&
+       git rev-list --use-bitmap-index \
+                    --objects --filter=blob:none HEAD >actual &&
+       test_bitmap_traversal expect actual
+'
+
+test_expect_success 'blob:none filter with specified blob' '
+       git rev-list --objects --filter=blob:none HEAD HEAD:two.t >expect &&
+       git rev-list --use-bitmap-index \
+                    --objects --filter=blob:none HEAD HEAD:two.t >actual &&
+       test_bitmap_traversal expect actual
+'
+
+test_expect_success 'blob:limit filter' '
+       git rev-list --objects --filter=blob:limit=5 HEAD >expect &&
+       git rev-list --use-bitmap-index \
+                    --objects --filter=blob:limit=5 HEAD >actual &&
+       test_bitmap_traversal expect actual
+'
+
+test_expect_success 'blob:limit filter with specified blob' '
+       git rev-list --objects --filter=blob:limit=5 \
+                    HEAD HEAD:much-larger-blob-two.t >expect &&
+       git rev-list --use-bitmap-index \
+                    --objects --filter=blob:limit=5 \
+                    HEAD HEAD:much-larger-blob-two.t >actual &&
+       test_bitmap_traversal expect actual
+'
+
+test_done
index 66d7a627972615f86e805dbc52a178e36fd12616..29518e0949bfc83582c882c67c54bb56f1f0698a 100755 (executable)
@@ -71,10 +71,10 @@ test_expect_success 'prepare for rebase conflicts' '
 '
 
 
-test_expect_success 'status when rebase in progress before resolving conflicts' '
+test_expect_success 'status when rebase --apply in progress before resolving conflicts' '
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD^^) &&
-       test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+       test_must_fail git rebase --apply HEAD^ --onto HEAD^^ &&
        cat >expected <<EOF &&
 rebase in progress; onto $ONTO
 You are currently rebasing branch '\''rebase_conflicts'\'' on '\''$ONTO'\''.
@@ -94,11 +94,11 @@ EOF
 '
 
 
-test_expect_success 'status when rebase in progress before rebase --continue' '
+test_expect_success 'status when rebase --apply in progress before rebase --continue' '
        git reset --hard rebase_conflicts &&
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD^^) &&
-       test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+       test_must_fail git rebase --apply HEAD^ --onto HEAD^^ &&
        echo three >main.txt &&
        git add main.txt &&
        cat >expected <<EOF &&
@@ -688,7 +688,7 @@ EOF
 '
 
 
-test_expect_success 'status when rebase conflicts with statushints disabled' '
+test_expect_success 'status when rebase --apply conflicts with statushints disabled' '
        git reset --hard master &&
        git checkout -b statushints_disabled &&
        test_when_finished "git config --local advice.statushints true" &&
@@ -698,7 +698,7 @@ test_expect_success 'status when rebase conflicts with statushints disabled' '
        test_commit three_statushints main.txt three &&
        test_when_finished "git rebase --abort" &&
        ONTO=$(git rev-parse --short HEAD^^) &&
-       test_must_fail git rebase HEAD^ --onto HEAD^^ &&
+       test_must_fail git rebase --apply HEAD^ --onto HEAD^^ &&
        cat >expected <<EOF &&
 rebase in progress; onto $ONTO
 You are currently rebasing branch '\''statushints_disabled'\'' on '\''$ONTO'\''.
index 1c5fb1d1f8c9cd9062ae44c6069fc530259762d4..9130b887d2cc45db769e1e5cbb0ddb1c32cf4b39 100755 (executable)
@@ -173,7 +173,6 @@ test_expect_success 'blame during cherry-pick with file rename conflict' '
        git show HEAD@{1}:rodent > rodent &&
        git add rodent &&
        git blame -f -C -C1 rodent | sed -e "$pick_fc" >current &&
-       cat current &&
        cat >expected <<-\EOF &&
        mouse-Initial
        mouse-Second
index dbe8deac0d2f28099241957e8944e3f714ee2fde..aec45bca3b7b6fc4561e02ff975f77a5331cb7ce 100755 (executable)
@@ -92,7 +92,8 @@ test_expect_success 'multiple dcommit from git svn will not clobber svn' "
 
 
 test_expect_success 'check that rebase really failed' '
-       test -d .git/rebase-apply
+       git status >output &&
+       grep currently.rebasing output
 '
 
 test_expect_success 'resolve, continue the rebase and dcommit' "
index ae9950a9c20ac48dab9d6c91ef6bd4e2aa372fc7..3e41c58a13689b65bf5a9ed6b2fc6466b303af81 100755 (executable)
@@ -1047,7 +1047,6 @@ test_expect_success 'M: rename root to subdirectory' '
        EOF
        git fast-import <input &&
        git diff-tree -M -r M4^ M4 >actual &&
-       cat actual &&
        compare_diff_raw expect actual
 '
 
index 5856563068c71280065c28d294a7c0a1149f6008..c98c1dfc2308e0affc48d5c2e133b6e5d24e9283 100755 (executable)
@@ -202,7 +202,6 @@ test_expect_success 'exit when p4 fails to produce marshaled output' '
                export PATH &&
                test_expect_code 1 git p4 clone --dest="$git" //depot >errs 2>&1
        ) &&
-       cat errs &&
        test_i18ngrep ! Traceback errs
 '
 
index 57b533dc6fbaa9d9b82bdaf300b46a426c7b43b7..e3836888ec8b322cf1032d9a1804fec915c10b11 100755 (executable)
@@ -294,7 +294,6 @@ test_expect_success 'cope with rcs keyword file deletion' '
                echo "\$Revision\$" >kwdelfile.c &&
                p4 add -t ktext kwdelfile.c &&
                p4 submit -d "Add file to be deleted" &&
-               cat kwdelfile.c &&
                grep 1 kwdelfile.c
        ) &&
        git p4 clone --dest="$git" //depot &&
index 88bc733ad6910ae0a2b11bb5e239f010cb2cdb72..ab5da2cabc400a7a3102bd962a60a08629f4d535 100755 (executable)
@@ -163,7 +163,7 @@ test_expect_success 'prompt - inside bare repository' '
 '
 
 test_expect_success 'prompt - interactive rebase' '
-       printf " (b1|REBASE-i 2/3)" >expected &&
+       printf " (b1|REBASE 2/3)" >expected &&
        write_script fake_editor.sh <<-\EOF &&
                echo "exec echo" >"$1"
                echo "edit $(git log -1 --format="%h")" >>"$1"
@@ -180,7 +180,7 @@ test_expect_success 'prompt - interactive rebase' '
 '
 
 test_expect_success 'prompt - rebase merge' '
-       printf " (b2|REBASE-i 1/3)" >expected &&
+       printf " (b2|REBASE 1/3)" >expected &&
        git checkout b2 &&
        test_when_finished "git checkout master" &&
        test_must_fail git rebase --merge b1 b2 &&
@@ -189,11 +189,11 @@ test_expect_success 'prompt - rebase merge' '
        test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - rebase' '
+test_expect_success 'prompt - rebase am' '
        printf " (b2|REBASE 1/3)" >expected &&
        git checkout b2 &&
        test_when_finished "git checkout master" &&
-       test_must_fail git rebase b1 b2 &&
+       test_must_fail git rebase --apply b1 b2 &&
        test_when_finished "git rebase --abort" &&
        __git_ps1 >"$actual" &&
        test_cmp expected "$actual"
index 284c52d076d1eff4a00f3763708ec7401b66532f..352c213d52e2e4a50808128c027eba7f1ac163df 100644 (file)
@@ -1516,3 +1516,30 @@ test_set_port () {
        port=$(($port + ${GIT_TEST_STRESS_JOB_NR:-0}))
        eval $var=$port
 }
+
+# Compare a file containing rev-list bitmap traversal output to its non-bitmap
+# counterpart. You can't just use test_cmp for this, because the two produce
+# subtly different output:
+#
+#   - regular output is in traversal order, whereas bitmap is split by type,
+#     with non-packed objects at the end
+#
+#   - regular output has a space and the pathname appended to non-commit
+#     objects; bitmap output omits this
+#
+# This function normalizes and compares the two. The second file should
+# always be the bitmap output.
+test_bitmap_traversal () {
+       if test "$1" = "--no-confirm-bitmaps"
+       then
+               shift
+       elif cmp "$1" "$2"
+       then
+               echo >&2 "identical raw outputs; are you sure bitmaps were used?"
+               return 1
+       fi &&
+       cut -d' ' -f1 "$1" | sort >"$1.normalized" &&
+       sort "$2" >"$2.normalized" &&
+       test_cmp "$1.normalized" "$2.normalized" &&
+       rm -f "$1.normalized" "$2.normalized"
+}
index 3e42bd750485d67bb6151ab5f15ac09a0d887502..29272a5c4f4d4a1785b66cb3c3a213c911d0883b 100644 (file)
@@ -557,6 +557,8 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
        const char *key, *dot;
        struct strbuf synthkey = STRBUF_INIT;
        int retval;
+       int (*select_fn)(const struct urlmatch_item *a, const struct urlmatch_item *b) =
+               collect->select_fn ? collect->select_fn : cmp_matches;
 
        if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
                if (collect->cascade_fn)
@@ -587,7 +589,7 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
        if (!item->util) {
                item->util = xcalloc(1, sizeof(matched));
        } else {
-               if (cmp_matches(&matched, item->util) < 0)
+               if (select_fn(&matched, item->util) < 0)
                         /*
                          * Our match is worse than the old one,
                          * we cannot use it.
index eed5f292354cfef466f15f29ef944450e10d4d1a..2407520731f9f0c5250d7bdf5833c56320c10160 100644 (file)
@@ -50,6 +50,15 @@ struct urlmatch_config {
        void *cb;
        int (*collect_fn)(const char *var, const char *value, void *cb);
        int (*cascade_fn)(const char *var, const char *value, void *cb);
+       /*
+        * Compare the two matches, the one just discovered and the existing
+        * best match and return a negative value if the found item is to be
+        * rejected or a non-negative value if it is to be accepted.  If this
+        * field is set to NULL, use the default comparison technique, which
+        * checks to ses if found is better (according to the urlmatch
+        * specificity rules) than existing.
+        */
+       int (*select_fn)(const struct urlmatch_item *found, const struct urlmatch_item *existing);
 };
 
 int urlmatch_config_entry(const char *var, const char *value, void *cb);
index 5b4793caa34e3ab981268662efdc1d840ac53390..eba4fd3a03812f046dadc1baf9d7644ba8dfcce9 100644 (file)
@@ -47,15 +47,13 @@ static void add_head_info(struct worktree *wt)
 static struct worktree *get_main_worktree(void)
 {
        struct worktree *worktree = NULL;
-       struct strbuf path = STRBUF_INIT;
        struct strbuf worktree_path = STRBUF_INIT;
 
        strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
+       strbuf_strip_suffix(&worktree_path, "/.");
        if (!strbuf_strip_suffix(&worktree_path, "/.git"))
                strbuf_strip_suffix(&worktree_path, "/.");
 
-       strbuf_addf(&path, "%s/HEAD", get_git_common_dir());
-
        worktree = xcalloc(1, sizeof(*worktree));
        worktree->path = strbuf_detach(&worktree_path, NULL);
        /*
@@ -69,7 +67,6 @@ static struct worktree *get_main_worktree(void)
                is_bare_repository();
        add_head_info(worktree);
 
-       strbuf_release(&path);
        strbuf_release(&worktree_path);
        return worktree;
 }
@@ -215,7 +212,6 @@ struct worktree *find_worktree(struct worktree **list,
                               const char *arg)
 {
        struct worktree *wt;
-       char *path;
        char *to_free = NULL;
 
        if ((wt = find_worktree_by_suffix(list, arg)))
@@ -223,11 +219,17 @@ struct worktree *find_worktree(struct worktree **list,
 
        if (prefix)
                arg = to_free = prefix_filename(prefix, arg);
-       path = real_pathdup(arg, 0);
-       if (!path) {
-               free(to_free);
+       wt = find_worktree_by_path(list, arg);
+       free(to_free);
+       return wt;
+}
+
+struct worktree *find_worktree_by_path(struct worktree **list, const char *p)
+{
+       char *path = real_pathdup(p, 0);
+
+       if (!path)
                return NULL;
-       }
        for (; *list; list++) {
                const char *wt_path = real_path_if_valid((*list)->path);
 
@@ -235,7 +237,6 @@ struct worktree *find_worktree(struct worktree **list,
                        break;
        }
        free(path);
-       free(to_free);
        return *list;
 }
 
index caecc7a281cc6778447a5c70bc7a756306bbbd3b..d242a6e71c0333f80b76e44976f0c4356f99bec3 100644 (file)
@@ -44,13 +44,29 @@ int submodule_uses_worktrees(const char *path);
 const char *get_worktree_git_dir(const struct worktree *wt);
 
 /*
- * Search a worktree that can be unambiguously identified by
- * "arg". "prefix" must not be NULL.
+ * Search for the worktree identified unambiguously by `arg` -- typically
+ * supplied by the user via the command-line -- which may be a pathname or some
+ * shorthand uniquely identifying a worktree, thus making it convenient for the
+ * user to specify a worktree with minimal typing. For instance, if the last
+ * component (say, "foo") of a worktree's pathname is unique among worktrees
+ * (say, "work/foo" and "work/bar"), it can be used to identify the worktree
+ * unambiguously.
+ *
+ * `prefix` should be the `prefix` handed to top-level Git commands along with
+ * `argc` and `argv`.
+ *
+ * Return the worktree identified by `arg`, or NULL if not found.
  */
 struct worktree *find_worktree(struct worktree **list,
                               const char *prefix,
                               const char *arg);
 
+/*
+ * Return the worktree corresponding to `path`, or NULL if no such worktree
+ * exists.
+ */
+struct worktree *find_worktree_by_path(struct worktree **, const char *path);
+
 /*
  * Return true if the given worktree is the main one.
  */